Files
SunnyShu 1b7aa90c97 fix(agents): preserve tool-call pairing after mid-turn abort (#116642)
* [AI] fix(agents): emit aborted tool results for skipped tool calls on mid-turn abort

When an abort fires mid-batch in executeToolCalls (after the assistant
message with tool_use is committed but before all tool_results are
written), the sequential and parallel dispatch loops break out and skip
the remaining tool calls. The committed assistant turn retains N tool_use
blocks but only M < N tool_results land in context.messages, leaving
orphaned tool_use that corrupts retries/continuation and triggers provider
400 errors on providers that do not synthesize missing results
(allowSyntheticToolResults=false, e.g. openai-completions/DeepSeek).

Emit aborted tool results (createErrorToolResult("Operation aborted")) for
the skipped tail in both executeToolCallsSequential and
executeToolCallsParallel so every tool_use keeps a paired tool_result.
This complements the existing write-side guard (which only covers
synthetic-enabled providers) and the persisted replay repair.

The aborted tail outcomes are routed through finalizeToolCallOutcome (via
a shared finalizeAbortedToolCall helper) so config.afterToolOutcome hooks
(audit, redaction, metadata, error-normalization) observe these skipped
calls just like every immediate or executed outcome, instead of bypassing
the outcome contract. Regression tests assert afterToolOutcome fires for
every skipped call in both dispatch modes.

Fixes #116379

Co-Authored-By: Maas <noreply@anthropic.com>

* [AI] fix(agents): emit tool_execution_start before aborted end for skipped calls

The abort-tail backfill added in #116379 emits tool_execution_end (and a
paired tool_result) for tool calls the dispatch loop never reached, but it
skipped the matching tool_execution_start. Channel/client subscribers that
pair start→end events received an end for an unknown tool-call id during
abort recovery.

Emit tool_execution_start for each skipped call before its aborted end/result,
mirroring the start event every dispatched (including immediate non-executed)
call already emits. Covers both sequential and parallel dispatch, with
regression assertions that every skipped call has a start before its end and
that start/end counts stay paired.

Co-Authored-By: Maas <noreply@anthropic.com>

* fix(agents): complete aborted tool tails safely

Fixes #116379

---------

Co-authored-by: Maas <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-01 03:30:13 +08:00
..