Skip to content

fix(acp): route <think> reasoning content to agent_thought_chunk - #108

Merged
alpibrupa merged 1 commit into
mainfrom
fix-acp-think-routing
Sep 4, 2026
Merged

fix(acp): route <think> reasoning content to agent_thought_chunk#108
alpibrupa merged 1 commit into
mainfrom
fix-acp-think-routing

Conversation

@alpibrupa

Copy link
Copy Markdown
Contributor

Summary

  • session/prompt streamed every text delta as agent_message_chunk, so a provider's <think>...</think> reasoning content leaked into the assistant's visible message instead of the ACP schema's dedicated agent_thought_chunk update kind.
  • Adds per-session in-think state (file-based at /tmp/lex-acp-think-<session_id>, since run_turn_streaming_with_provider's callback type is fixed at (d.Step) -> [io] Unit shared across mcp_main.lex/TUI/client_protocol.lex and can't be widened to [io, sql] for just this caller) and a recursive splitter (split_think) that segments each streamed chunk across <think>/</think> boundaries, routing Thought segments to agent_thought_chunk and Msg segments to agent_message_chunk.
  • Also strips a duplicated leading <think> marker — some providers double-emit the opening tag — caught via live testing against a real reasoning model.

Fixes #107

Test plan

  • lex check src/server/client_protocol.lex — ok
  • lex fmt --check src/server/client_protocol.lex — formatted
  • Full repo sweep: lex check over every tracked .lex file — all pass
  • lex test — 4/4 passed
  • Unit-level: 8 hand-traced split_think cases (plain text, close-then-open, tag spanning a chunk boundary, open-with-no-close, close-with-no-open, doubled leading <think>, and the exact literal <think>The boundary chunk from the original bug report) — all correct
  • Live ACP client test (real initializesession/newsession/promptsession/close flow against a local reasoning model over litellm): before the fix, 100% of streamed text — including reasoning — arrived as agent_message_chunk; after the fix, reasoning arrives as genuine agent_thought_chunk frames and zero <think>/</think> substrings leak into any chunk's text across a 244-frame run

session/prompt was streaming everything as agent_message_chunk, so a
provider's <think>...</think> reasoning leaked into the assistant's
visible message instead of the ACP schema's dedicated
agent_thought_chunk update kind. Track in-think state per session
(file-based, since run_turn_streaming_with_provider's callback type is
fixed at (d.Step) -> [io] Unit and can't be widened to [io, sql] for
just this caller), split each streamed chunk on the tags, and route
Thought/Msg segments to the right update kind. Also strips a
duplicated leading <think> (some providers double-emit the opening
marker), verified against the literal boundary chunk that surfaced it
in live testing.

Fixes #107
@alpibrupa
alpibrupa merged commit 6915e77 into main Sep 4, 2026
1 check passed
@alpibrupa
alpibrupa deleted the fix-acp-think-routing branch September 4, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ACP: reasoning-model <think> content streams as agent_message_chunk instead of agent_thought_chunk

1 participant