fix(agent-core-v2): deduplicate repeated tool call ids in the loop machine requester - #3582
Merged
sailist merged 1 commit intoSep 6, 2026
Conversation
|
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
Follow-up to #3580 (no separate issue; internal robustness hardening found during post-merge review of the loop machine adapter).
Problem
The turn state machine assumes tool call ids are unique within one assistant response. That invariant is normally guaranteed upstream by the requester service's id normalizer, but the machine itself does not defend it: if a response reaches the machine with two tool calls sharing one id (e.g. a requester implementation that bypasses the normalizer), several id-keyed structures corrupt at once — the agent machine's
turnToolsmap and spawned tool actors overwrite each other, and the loop machine tools adapter'sflushIfReadypushes anundefinedentry intorunBatch, which throws aTypeErrorbefore itstryblock. The observable result is an unhandled rejection,batchInFlightstucktrue, and one of the two calls silently never executing while the turn reports success.What changed
packages/agent-core-v2/src/agent/loop/machine/requester.ts: the requester delta bridge now runs every streamedfunctionpart id through aToolCallIdResponseNormalizer(the same class the requester service uses). The first occurrence keeps the raw id; repeats are remapped toid__2,id__3, ... exactly like the upstream normalizer._streamIndexis left untouched, sotool_call_partargument deltas still route to the right call.turn.spawnTools,beginBatch, tool execution, history, and wire projections. On the normal path ids are already unique and the remap is a no-op.Verification: negative reproduction (unhandled rejection + lost call before the fix), then a scratch harness driving the real machine with a normalizer-bypassing fake requester — both the no-index and
_streamIndex+ argument-delta scenarios now execute both calls ascall_dup/call_dup__2with correctly paired tool result messages;test/agent/loop65/65, package typecheck, lint (no-comments / import-boundaries / oxlint), and a full remote suite (20590 passed, 0 failed) on the pre-rebase stack, with focused tests re-run after rebasing onto the merged #3580.Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.