Summary
When compress.permission: \"allow\" is set, DCP injects <dcp-message-id>mNNNN</dcp-message-id> tags into part.state.output of completed tool results (via appendToAllToolParts in lib/messages/inject/inject.ts). OpenCode's XML renderer processes tool result content using XML-like <parameter> tags. When the injected DCP tag ends up inside tool result output, the renderer partially parses it and leaves a residual fragment visible in the rendered response.
Observed artifact
At the end of assistant responses that terminate with tool calls, the following appears in the UI:
```
m0031
```
The </parameter> is OpenCode's XML renderer garbling the </dcp-message-id> closing tag from inside the tool output string.
Root cause
In lib/messages/inject/inject.ts, the injection priority for assistant messages is:
appendToAllToolParts — appends tag directly into part.state.output (string)
appendToLastTextPart — fallback to last text part
- Synthetic text part inserted before first tool
Because appendToAllToolParts succeeds when tool parts have status: \"completed\" with string output, the DCP tag is embedded in the tool result text that OpenCode's renderer subsequently processes as XML.
Environment
- Plugin version:
@tarquinen/opencode-dcp@3.1.7
- OpenCode version: latest
pruneNotificationType: \"toast\" (already changed from \"chat\")
Suggested fix
Instead of injecting into part.state.output, consider:
- Using a separate metadata field on the part (e.g.,
part.metadata.dcpMessageId) that the renderer never displays
- Skipping tool parts entirely and always falling through to
appendToLastTextPart or the synthetic text part (inserted before the first tool part), which won't be processed by the tool XML renderer
- Injecting into a non-rendered field of the message object itself rather than any content-bearing part
Option 2 is the minimal change: remove or bypass appendToAllToolParts in the assistant message injection path, letting the synthetic text part (inserted before the first tool) carry the ID instead. This text part is not rendered through the tool XML pipeline.
Workaround
Setting compress.permission: \"deny\" stops tag injection but also disables DCP system prompt, all /dcp CLI commands, nudges, and breaks the compress tool entirely — too destructive to be a viable workaround.
Summary
When
compress.permission: \"allow\"is set, DCP injects<dcp-message-id>mNNNN</dcp-message-id>tags intopart.state.outputof completed tool results (viaappendToAllToolPartsinlib/messages/inject/inject.ts). OpenCode's XML renderer processes tool result content using XML-like<parameter>tags. When the injected DCP tag ends up inside tool result output, the renderer partially parses it and leaves a residual fragment visible in the rendered response.Observed artifact
At the end of assistant responses that terminate with tool calls, the following appears in the UI:
```
m0031
```
The
</parameter>is OpenCode's XML renderer garbling the</dcp-message-id>closing tag from inside the tool output string.Root cause
In
lib/messages/inject/inject.ts, the injection priority for assistant messages is:appendToAllToolParts— appends tag directly intopart.state.output(string)appendToLastTextPart— fallback to last text partBecause
appendToAllToolPartssucceeds when tool parts havestatus: \"completed\"with string output, the DCP tag is embedded in the tool result text that OpenCode's renderer subsequently processes as XML.Environment
@tarquinen/opencode-dcp@3.1.7pruneNotificationType: \"toast\"(already changed from\"chat\")Suggested fix
Instead of injecting into
part.state.output, consider:part.metadata.dcpMessageId) that the renderer never displaysappendToLastTextPartor the synthetic text part (inserted before the first tool part), which won't be processed by the tool XML rendererOption 2 is the minimal change: remove or bypass
appendToAllToolPartsin the assistant message injection path, letting the synthetic text part (inserted before the first tool) carry the ID instead. This text part is not rendered through the tool XML pipeline.Workaround
Setting
compress.permission: \"deny\"stops tag injection but also disables DCP system prompt, all/dcpCLI commands, nudges, and breaks the compress tool entirely — too destructive to be a viable workaround.