feat: gemini-acp text streaming compat#13
Conversation
Gemini-acp's text stream has a few quirks the adapter wasn't handling. This commit makes coding sessions on gemini behave like sessions on Claude Code: 1. **Inline-marker chain-of-thought stripping.** Gemini emits reasoning as plain text with a literal `[Thought: true]` marker at the thought/response boundary. At medium and below we retroactively trim the draft to only the post-marker content so the user sees the response without the thinking dump. High keeps both visible. Adds a `MessageDraft.replaceBuffer` helper for the in-place rewrite. 2. **Finalize text draft on every turn end.** Agents that don't emit `usage`/`session_end` at turn-end (gemini) left the streaming text draft stuck at its 1900-char mid-stream truncation. Register a plugin middleware on the `turn:end` hook that calls a new public `DiscordAdapter.finalizeSessionDraft` so the buffer gets split into chunks regardless of the agent's terminal-event behavior. Also finalize on incoming user message (messageCreate) so the prior turn's draft seals before the next turn's text appends to it. 3. **Discord-specific rendering instruction injected per session.** First-prompt-only middleware on `agent:beforePrompt` prepends a `<system_instruction>` block telling the agent: no markdown tables, use ASCII tables with +---+ borders inside triple-backtick fences, tables ≤90 chars wide, apply silently. Discord-specific (only fires for sourceAdapterId === 'discord'). Adds the `middleware:register` permission to the plugin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| // Reset tracker state for new prompt cycle on existing sessions | ||
| // Reset tracker state and finalize any in-flight draft for existing sessions. | ||
| // Some agents (e.g. gemini) don't emit usage/tool_call events between turns, | ||
| // so a new user message is the only reliable signal that the prior turn ended. |
There was a problem hiding this comment.
Check that this doesn't break with back to back messages
There was a problem hiding this comment.
back to back messages are fine - anything else to be worried about here?
| "and box-drawing or `+---+` style borders, then wrap the whole table in " + | ||
| "triple-backtick code fences. The monospace inside the fence aligns the " + | ||
| "columns correctly.\n" + | ||
| "- Tables MUST be no wider than 90 characters per row. Discord's mobile " + |
There was a problem hiding this comment.
I actually think it's even shorter than this, on my Pixel Fold I can only get 51 characters on folded mode and 71 on unfolded mode. The desktop in full thread view mode is like 91. Thoughts?
|
Clean implementation of both features — the A few minor things worth addressing before merge:
This stacks on #11 — worth merging that one first. No blocking issues here. |
Why
Gemini-acp's text stream has a few quirks that the adapter wasn't handling. Coding sessions on gemini behaved noticeably worse than on Claude Code: chain-of-thought dumped into the response, long messages truncated to 1900 chars, and tables rendered as raw markdown pipes. This PR closes the gap.
What changes
1. Inline-marker chain-of-thought stripping. Gemini emits reasoning as plain text with a literal
[Thought: true]marker at the thought/response boundary. At medium and below we retroactively trim the draft to only the post-marker content so the user sees the response without the thinking dump. High keeps both visible. Adds aMessageDraft.replaceBufferhelper for the in-place rewrite.2. Finalize text draft on every turn end. Agents that don't emit
usage/session_endat turn-end (gemini) left the streaming text draft stuck at its 1900-char mid-stream truncation. Register a plugin middleware on theturn:endhook that calls a new publicDiscordAdapter.finalizeSessionDraftso the buffer gets split into chunks regardless of the agent's terminal-event behavior. Also finalize on incoming user message (messageCreate) so the prior turn's draft seals before the next turn's text appends to it.3. Discord-specific rendering instruction injected per session. First-prompt-only middleware on
agent:beforePromptprepends a<system_instruction>block telling the agent: no markdown tables, use ASCII tables with+---+borders inside triple-backtick fences, tables ≤90 chars wide, apply silently. Discord-specific (only fires forsourceAdapterId === 'discord'). Adds themiddleware:registerpermission to the plugin. (Supersedes #10)Stacking note
Works best in combination with #11 (content-loss split fix) — without that, finalize would still drop content into the void on long responses. Order of merge: #11 first, then this.
Test plan
[Thought:]marker → retro-rewrite is a no-op)🤖 Generated with Claude Code