Skip to content

fix(responses): preserve whitespace in tool-call arguments (#244) - #245

Merged
ltmoerdani merged 1 commit into
mainfrom
fix/issue-244-arguments-whitespace
Sep 23, 2026
Merged

ltmoerdani merged 1 commit into
mainfrom
fix/issue-244-arguments-whitespace

Conversation

@ltmoerdani

Copy link
Copy Markdown
Owner

Closes #244

Problem

All whitespace is stripped from tool calls and responses on OpenAI models (both OpenCode Go and Zen) — every word concatenated (thisiswhattheresponselookslike), and corrupted tool-call arguments send the model into reasoning loops. Text-only conversations are also affected because agent-mode output flows through tool calls.

Root cause

Regression class of #192 (fixed in PR #194). That fix moved text/reasoning deltas to firstStringRaw() but deliberately left arguments_delta on the trimming firstString() under the assumption that arguments tolerate whitespace stripping.

That assumption was wrong. Responses API argument fragments are arbitrary JSON slices that can split inside string values ("hello wo + rld"), so per-fragment .trim() destroyed boundary whitespace and corrupted tool-call input on every GPT-family model (all route to /v1/responses).

Verified against official sources:

  • openai-node's response-accumulator.ts accumulates via raw output.arguments += event.delta — no trim, no falsy filtering
  • openai-node's own test mocks split argument streams on whitespace boundaries — exactly the case trimming destroys
  • response.function_call_arguments.done carries the final authoritative arguments string

Reporter diagnostics confirmed every affected request hit the responses endpoint (HTTP 200, silent corruption) with the repeat-request/cache-hit pattern of an agent retrying mangled tool calls.

Fix (3 layers)

Layer File Change
Root cause src/core/routing.ts function_call_arguments.delta → firstStringRaw() (no trim)
Repair net src/core/routing.ts New function_call_arguments.done handler → emits final arguments tagged argumentsDone: true
Self-healing src/toolCallAccumulator.ts argumentsDone delta REPLACES pending arguments instead of appending — heals any gateway-side delta mis-join

Tests

5 new regression tests: fragment split mid-string-value, done-event mapping, done-without-args, replace semantics, append-only path unaffected. 489/489 pass, npm run compile + typecheck clean.

Docs

  • docs/issues/107-20260924-issue244-tool-call-arguments-whitespace.md (new)
  • docs/issues/83 annotated — superseded assumption noted
  • CHANGELOG [0.7.7], devlog, ARCHITECTURE-MAP
  • Version bump 0.7.7 (VSIX built + verified locally)

Notes for merge

Per repo policy: merge commit (gh pr merge --merge), no squash.

response.function_call_arguments.delta fragments went through the
trimming firstString() helper — the same per-chunk-.trim() bug class as
#192, but on a path the #192 fix deliberately left alone. Responses API
argument fragments are arbitrary JSON slices that can split inside
string values, so per-fragment trimming corrupted tool-call input on
every GPT-family model (all route to /v1/responses) on both Go and Zen,
sending the model into reasoning loops.

Three-layer fix:
- root cause: fragments now go through firstStringRaw()
- new response.function_call_arguments.done handler emits the final
  authoritative arguments tagged argumentsDone: true
- ToolCallAccumulator treats argumentsDone as REPLACE instead of append,
  healing any gateway-side delta mis-join

Verified against openai-node's own accumulator (raw += event.delta) and
the official done-event semantics. 5 regression tests; 489/489 pass.

Docs: docs/issues/107, doc 83 annotated (superseded assumption),
CHANGELOG 0.7.7, devlog, ARCHITECTURE-MAP. Version bump 0.7.7.

closes #244
@ltmoerdani
ltmoerdani merged commit 239056f into main Sep 23, 2026
2 checks passed
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.

[BUG] All whitespace is stripped from tool calls and responses when using OpenAI models

1 participant