Skip to content

fix(agent-core-v2): deduplicate repeated tool call ids in the loop machine requester - #3582

Merged
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:fix-loop-machine-duplicate-toolcall-id
Sep 6, 2026
Merged

fix(agent-core-v2): deduplicate repeated tool call ids in the loop machine requester#3582
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:fix-loop-machine-duplicate-toolcall-id

Conversation

@sailist

@sailist sailist commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

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 turnTools map and spawned tool actors overwrite each other, and the loop machine tools adapter's flushIfReady pushes an undefined entry into runBatch, which throws a TypeError before its try block. The observable result is an unhandled rejection, batchInFlight stuck true, 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 streamed function part id through a ToolCallIdResponseNormalizer (the same class the requester service uses). The first occurrence keeps the raw id; repeats are remapped to id__2, id__3, ... exactly like the upstream normalizer. _streamIndex is left untouched, so tool_call_part argument deltas still route to the right call.
  • The bridge is the machine's only tool call entry point (the assistant message is folded from deltas), so remapping there makes ids consistent end to end: turn.spawnTools, beginBatch, tool execution, history, and wire projections. On the normal path ids are already unique and the remap is a no-op.
  • No changeset: the trigger is unreachable in the normal request flow, so the change is not user-perceivable.

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 as call_dup/call_dup__2 with correctly paired tool result messages; test/agent/loop 65/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

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: the issue must have a maintainer's /approve).
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: c20e539

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@sailist
sailist merged commit 00265b0 into MoonshotAI:main Sep 6, 2026
15 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.

1 participant