Bug Description
stripHallucinations runs at the start of each createChatMessageTransformHandler cycle (line 7444), before injectMessageIds (line 7468). But it operates on ALL messages — including prior turns that already had <dcp-message-id>mXXXX</dcp-message-id> appended by the previous cycle's injectMessageIds.
If a prior assistant message contains <dcp-message-id> anywhere in its text content (e.g., the model mentioned the tag name), DCP_PAIRED_TAG_REGEX treats the injected closing tag at the end of the message as the closing match. Everything between the in-content opening tag and the injected closing tag is deleted.
Reproduced with node:
const DCP_PAIRED_TAG_REGEX = /<dcp[^>]*>[\s\S]*?<\/dcp[^>]*>/gi;
const DCP_UNPAIRED_TAG_REGEX = /<\/?dcp[^>]*>/gi;
const input =
'The tag called `<dcp-message-id>` is used to track messages. ' +
'This text should survive.\n\n' +
'<dcp-message-id>m0369</dcp-message-id>';
const result = input
.replace(DCP_PAIRED_TAG_REGEX, '')
.replace(DCP_UNPAIRED_TAG_REGEX, '');
// result: "The tag called `"
// Everything after the in-content tag is gone.
Expected Behavior
Content after an in-text DCP tag mention should not be deleted. Only hallucinated tag pairs that are entirely within the generated content should be stripped.
Debug Context Logs
{
"role": "assistant",
"parts": [
{
"type": "text",
"text": "The tag called `"
}
]
}
*(Full content truncated — all text after the tag mention is gone, including the rest of the sentence and subsequent paragraphs)*
Tool Call Details
No response
DCP Version
3.1.12
Opencode Version
1.15.12
Model
Claude Sonnet 4
Additional Context
Add one pre-processing step in stripHallucinationsFromString to remove the legitimately injected message ID suffix before running the paired regex. The injected tag can't then act as a false closing match. injectMessageIds re-adds it later in the same cycle.
// New constant:
const INJECTED_TAG_SUFFIX_REGEX = /\n<dcp-message-id>m\d+<\/dcp-message-id>\s*$/;
// Updated function (one new line, existing replaces unchanged):
var stripHallucinationsFromString = (text) => {
const safe = text.replace(INJECTED_TAG_SUFFIX_REGEX, "");
return safe.replace(DCP_PAIRED_TAG_REGEX, "").replace(DCP_UNPAIRED_TAG_REGEX, "");
};
This is additive — the existing two .replace() calls are unchanged.
Bug Description
stripHallucinationsruns at the start of eachcreateChatMessageTransformHandlercycle (line 7444), beforeinjectMessageIds(line 7468). But it operates on ALL messages — including prior turns that already had<dcp-message-id>mXXXX</dcp-message-id>appended by the previous cycle'sinjectMessageIds.If a prior assistant message contains
<dcp-message-id>anywhere in its text content (e.g., the model mentioned the tag name),DCP_PAIRED_TAG_REGEXtreats the injected closing tag at the end of the message as the closing match. Everything between the in-content opening tag and the injected closing tag is deleted.Reproduced with node:
Expected Behavior
Content after an in-text DCP tag mention should not be deleted. Only hallucinated tag pairs that are entirely within the generated content should be stripped.
Debug Context Logs
{ "role": "assistant", "parts": [ { "type": "text", "text": "The tag called `" } ] } *(Full content truncated — all text after the tag mention is gone, including the rest of the sentence and subsequent paragraphs)*Tool Call Details
No response
DCP Version
3.1.12
Opencode Version
1.15.12
Model
Claude Sonnet 4
Additional Context
Add one pre-processing step in
stripHallucinationsFromStringto remove the legitimately injected message ID suffix before running the paired regex. The injected tag can't then act as a false closing match.injectMessageIdsre-adds it later in the same cycle.This is additive — the existing two
.replace()calls are unchanged.