qwen-code/packages
Dinesh Palanisamy adfdd33aeb
fix(anthropic): sanitize tool_use.id / tool_result.tool_use_id (#8164)
* fix(anthropic): don't strip a trailing tool_use with no subsequent message

Fixes #8159

cleanOrphanedToolCalls treated any assistant tool_use with no matching
tool_result as an orphan and stripped it -- including a tool_use in the
very last message, where there is no subsequent message to have found a
result in yet. "No result yet" is not the same as "no result ever": the
tool may simply not have finished executing, or the conversion may be
happening for a reason other than sending the completed turn to
Anthropic (token counting, a resumed/replayed session snapshot, a retry
issued before tool execution completes, ...).

Silently deleting a currently-active tool_use corrupts the assistant's
most recent turn, and the damage compounds when that turn also carries a
signed extended-thinking block: the block's signature is computed over
the full sibling content of the turn, so removing the tool_use next to
it invalidates the signature and replaying the mutated turn produces
Anthropic 400 "thinking blocks in the latest assistant message cannot be
modified" -- a genuinely confusing error for something the client did to
its own outgoing request.

Fix: when an assistant tool_use is in the last message of the array (no
message follows it at all), treat its tool_use blocks as valid/unresolved
rather than scanning for a match that can't exist yet. A tool_use is only
condemned as orphaned when a subsequent message was actually scanned and
found to lack a matching tool_result.

Also fixed one existing test that unintentionally pinned the buggy
behavior as expected output ("cleans orphaned tool_use blocks without
matching tool_result" used a fixture with no subsequent message at all,
which is the trailing case, not a genuine orphan) -- added a real next
message with unrelated content so it now exercises an actual orphan.
Added a new regression test for the trailing case.

Verification:
- New/updated unit tests in converter.test.ts (73 tests, was 72).
- Full anthropicContentGenerator/ suite: 193 tests pass.
- tsc --noEmit -p packages/core/tsconfig.json and eslint clean for
  touched files.

* fix(anthropic): sanitize tool_use.id / tool_result.tool_use_id

Fixes #8160

The Anthropic converter passed Part.functionCall.id / FunctionResponse.id
straight through as tool_use.id / tool_result.tool_use_id with only an
empty-string-style fallback, never validating or sanitizing against
Anthropic's accepted character set.

Anthropic validates both fields against ^[a-zA-Z0-9_-]+$ server-side and
rejects anything else -- including the empty string, since `+` requires
at least one character. functionCall.id is the Gemini lingua-franca's own
id field with no such constraint, so it can legitimately carry another
provider's id scheme, a composite/namespaced id, or be entirely absent.

Live-verified against the real Anthropic Messages API (via our corporate
LiteLLM proxy in front of Vertex, model claude-sonnet-4-6):
- A tool_use.id containing characters outside [a-zA-Z0-9_-] (e.g.
  `call:abc.def/ghi?jkl`) 400s: "String should match pattern
  '^[a-zA-Z0-9_-]+$'".
- An empty-string tool_result.tool_use_id (the exact fallback the old
  code emitted) 400s the same way.
- The sanitized replacement (`call_abc_def_ghi_jkl`) round-trips
  successfully (HTTP 200) with the pair still linked.

Fix: resolveToolUseId() sanitizes non-conforming characters to `_`,
generates a real fallback id (never an empty string) when the source is
missing, and memoizes the source id -> sanitized id mapping per request
(toolIdMap) so a tool_use/tool_result pair sharing a source id still
resolves to the same wire id after sanitization. Uniqueness is enforced
across the whole request (usedToolIds) rather than the previous
per-Content-object counter, which fixes a latent collision: two
different Content objects each missing an id previously both started
their local counter at 0, producing duplicate fallback ids like `tool_0`
in two different assistant turns.

State is scoped to a single convertGeminiRequestToAnthropic call (reset
via resetToolIdState) since the converter instance is long-lived across
requests (constructed once per generator, not once per call).

Also merges in the trailing-tool_use fix from #8163 (fixes #8159): one
of the new tests here needs the model's missing-id tool_use to survive
as the final message in order to observe the generated fallback id,
which depends on that fix. This PR's net diff will shrink to just the
sanitization change once #8163 merges.

Verification:
- New tests in converter.test.ts covering: bad-char sanitization with
  linkage preserved, non-empty fallback generation (not empty string),
  no id collisions across two missing-id calls in one request, and
  same-source-id consistency across tool_use/tool_result in different
  messages.
- Full anthropicContentGenerator/ suite: 197 tests pass.
- Live proxy verification as described above.
- tsc --noEmit -p packages/core/tsconfig.json and eslint clean for
  touched files.

---------

Co-authored-by: Palanisamy, Dinesh <Dinesh.Palanisamy@netapp.com>
2026-07-31 10:18:58 +00:00
..
acp-bridge chore(release): v0.21.2 (#8200) 2026-07-31 05:22:46 +00:00
audio-capture chore(release): v0.21.2 (#8200) 2026-07-31 05:22:46 +00:00
channels fix(github-channel): retry definite no-write deliveries (#8087) 2026-07-31 08:06:28 +00:00
chrome-extension chore(release): v0.21.2 (#8200) 2026-07-31 05:22:46 +00:00
cli fix(cli): prevent silent VP-mode crash by adding uncaughtException handler and error visibility (#8088) 2026-07-31 08:39:55 +00:00
core fix(anthropic): sanitize tool_use.id / tool_result.tool_use_id (#8164) 2026-07-31 10:18:58 +00:00
cua-driver fix(mcp): add opt-in model payload filtering (#7413) 2026-07-21 09:49:04 +00:00
desktop fix(daemon): harden Todo Stop Guard continuations (#7821) 2026-07-28 06:51:40 +00:00
mobile-mcp chore(release): v0.20.1 (#7461) 2026-07-22 02:00:22 +00:00
sdk-java fix(sdk-java): Harden daemon transport reliability (#7603) 2026-07-24 04:22:05 +00:00
sdk-python fix(sdk-python): validate max_tool_calls and max_subagent_depth as integers (#7548) 2026-07-23 08:45:13 +00:00
sdk-typescript feat(web-shell): add contextual task panels (#7929) 2026-07-30 13:45:30 +00:00
vscode-ide-companion feat(anthropic): support extended 1h cache_control TTL (#8048) 2026-07-31 08:33:42 +00:00
web-shell fix(web-shell): localize built-in agent type names in zh-CN (#8209) 2026-07-31 09:43:29 +00:00
web-templates chore(release): v0.21.2 (#8200) 2026-07-31 05:22:46 +00:00
webui chore(release): v0.21.2 (#8200) 2026-07-31 05:22:46 +00:00
zed-extension