diff --git a/internal/toolcall/toolcalls_parse.go b/internal/toolcall/toolcalls_parse.go
index f15c130..772b297 100644
--- a/internal/toolcall/toolcalls_parse.go
+++ b/internal/toolcall/toolcalls_parse.go
@@ -216,6 +216,7 @@ func updateCDATAStateForStrip(inCDATA bool, cdataFenceMarker, line string) (bool
pos := 0
state := inCDATA
fenceMarker := cdataFenceMarker
+ lineForFence := line
if !state {
start := strings.Index(lower[pos:], "`,
+ ` <|DSML|invoke name="Bash">`,
+ ` <|DSML|parameter name="command">|DSML|parameter>`,
+ ` |DSML|invoke>`,
+ `|DSML|tool_calls>`,
+ "```",
+ "tail",
+ }, "\n")
+ text := ``
+
+ calls := ParseToolCalls(text, []string{"Write"})
+ if len(calls) != 1 {
+ t.Fatalf("expected one compact CDATA call, got %#v", calls)
+ }
+ if calls[0].Input["content"] != content {
+ t.Fatalf("expected compact CDATA content to survive, got %#v", calls[0].Input["content"])
+ }
+}
+
func TestParseToolCallsPreservesSimpleCDATAInlineMarkupAsText(t *testing.T) {
text := `urgent]]>`
calls := ParseToolCalls(text, []string{"Write"})
diff --git a/internal/toolstream/tool_sieve_xml_test.go b/internal/toolstream/tool_sieve_xml_test.go
index ab1aa38..ce2ee77 100644
--- a/internal/toolstream/tool_sieve_xml_test.go
+++ b/internal/toolstream/tool_sieve_xml_test.go
@@ -331,6 +331,51 @@ func TestProcessToolSieveKeepsExtremeHereDocCDATAUntilOuterClose(t *testing.T) {
}
}
+func TestProcessToolSieveKeepsCompactCDATAWithImmediateFencedDSML(t *testing.T) {
+ var state State
+ content := strings.Join([]string{
+ "```xml",
+ `<|DSML|tool_calls>`,
+ ` <|DSML|invoke name="Bash">`,
+ ` <|DSML|parameter name="command">|DSML|parameter>`,
+ ` |DSML|invoke>`,
+ `|DSML|tool_calls>`,
+ "```",
+ "tail",
+ }, "\n")
+ chunks := []string{
+ ``,
+ }
+
+ var events []Event
+ for _, c := range chunks {
+ events = append(events, ProcessChunk(&state, c, []string{"Write"})...)
+ }
+ events = append(events, Flush(&state, []string{"Write"})...)
+
+ var textContent strings.Builder
+ var gotContent string
+ toolCalls := 0
+ for _, evt := range events {
+ textContent.WriteString(evt.Content)
+ if len(evt.ToolCalls) > 0 {
+ toolCalls += len(evt.ToolCalls)
+ gotContent, _ = evt.ToolCalls[0].Input["content"].(string)
+ }
+ }
+ if toolCalls != 1 {
+ t.Fatalf("expected one compact CDATA tool call, got %d events=%#v", toolCalls, events)
+ }
+ if textContent.Len() != 0 {
+ t.Fatalf("expected no leaked text, got %q", textContent.String())
+ }
+ if gotContent != content {
+ t.Fatalf("expected compact CDATA content to survive, got len=%d want=%d", len(gotContent), len(content))
+ }
+}
+
func TestProcessToolSieveFallsBackWhenCDATANeverCloses(t *testing.T) {
var state State
chunks := []string{