Skip to content

fix: strip model-echoed render tags from tool-call arguments on every wire path - #934

Closed
ranxianglei wants to merge 1 commit into
masterfrom
2026-09-18_fix-933-tag-echo-toolargs
Closed

ranxianglei wants to merge 1 commit into
masterfrom
2026-09-18_fix-933-tag-echo-toolargs

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Problem (#933)

Verified chain: kernel renderMessage injects <acp …>mNNNNN</acp> refs into message text (text-only) → model echoes them in output → response-side stripping covered only text fields (content/reasoning/thinking) → tags echoed into tool-call arguments passed through verbatim on every wire format, entered client session storage, and rendered raw in the host TUI. The issue's "move tags to a header channel" main fix is infeasible — the model must read refs from prompt text to cite them in compress calls — so the actionable fix is making the response-side strip structurally complete.

Changes

  1. Structural invariant instead of field enumeration (src/loop/tag-echo-filter.ts): new exports stripAcpTagsDeep / findAcpLikeTagSnippetsDeep walk every string leaf of any payload object. Non-streaming strips now also cover tool args: OpenAI function.arguments, Anthropic tool_use.input (+ streaming partial_json), Responses item arguments and reasoning summary[].text.
  2. Streaming plugin pipes (src/plugin.ts, opencode/pi/omp mode): per-tool-call argument streams for OpenAI delta.tool_calls[].function.arguments and Anthropic input_json_delta.partial_json; per-item streams for Responses function_call_arguments.delta / reasoning_summary_text.delta, plus one-shot strip on .done payloads. Held mid-tag tails flush before done-family/EOF/error events. Also fixed a real bug found while writing the tests: Responses fc delta fragments live in delta, not arguments — the old-style field read would have passed them through raw.
  3. Proxy-mode adapters (src/loop/adapter-*.ts): strip buffered-complete arguments at emission time. adapter-openai additionally reconstructs fragmented raw replays when accumulated args are tag-tainted (a tag spanning fragment boundaries cannot be cut by per-fragment stripping).
  4. Kill switch made global (src/server.ts): ACP_RENDER_NONE=1 was previously honored only on the Responses path + compact rebuilds — exactly the wires opencode 插件模式下 render tag 注入消息正文,模型回显漏网后原样渲染进 TUI(元数据与正文同层问题) #933 hit (chat paths) ignored it. Now honored on all paths (OpenAI chat, Anthropic, count_tokens, [bug] ACP 压缩轮以 stop 终止任务:模型输出压缩摘要后零 toolCalls,需用户手动「继续」(#361 同族) #422 fold-refresh re-request). Documented in CONFIGURATION.md.
  5. Tests (tests/fix-933-tag-echo-toolargs.test.ts, 6 cases): deep-scan invariant over every carrier field × 3 protocols; mid-tag-split streaming through each pipe (tag broken across fragments); non-streaming JSON × 3 protocols; faithful-client replay loop (assistant turn rebuilt from the clean client stream ⇒ next-turn request carries no tags).

Verification

  • npm run typecheck
  • npm test: 1674 pass / 0 fail / 2 skipped (gated e2e) ✓
  • npm run build

Both compression modes reasoned: plugin mode (carrier = agent's compress tool call) gets the pipe-level strips; proxy mode (carrier = acp_summary) gets the adapter-level strips.

Fixes #933

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Reviewing PR #934 now. Initial state:

Per repo convention (§7.2: rebase to CURRENT master before claiming mergeable), I'm rebasing the branch onto origin/master, resolving by union of intent where the two sides touch the same regions, then re-running typecheck + full test suite + build. Will follow up here with the result and CI status.

@ranxianglei
ranxianglei force-pushed the 2026-09-18_fix-933-tag-echo-toolargs branch from 8695aee to a757033 Compare September 18, 2026 08:22
@github-actions

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-09-18_fix-933-tag-echo-toolargs (a757033)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-934

Each push to this PR publishes a new version under the pr-934 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr934.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

This branch now conflicts with master and can't merge as-is. A clean rebase preserving the full change (plus conflict resolution: master's done-family split in plugin.ts, #728 localInputEstimate merged with the ACP_RENDER_NONE gate in server.ts) is up as #935 — please close this one in favor of #935.

ranxianglei added a commit that referenced this pull request Sep 18, 2026
fix: strip model-echoed render tags from tool-call arguments on every wire path (rebase of #934, Fixes #933)
This was referenced Sep 18, 2026
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.

opencode 插件模式下 render tag 注入消息正文,模型回显漏网后原样渲染进 TUI(元数据与正文同层问题)

1 participant