fix(agent-core-v2): invalidate streamed attempt state when the llm requester retries below the turn - #3734
Merged
RealKai42 merged 1 commit intoSep 11, 2026
Conversation
…quester retries below the turn
🦋 Changeset detectedLatest commit: 72147a6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Collaborator
Author
|
@codex review |
commit: |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
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
No tracking issue. Reported via the Mangrove platform (task 201346, container 9123219, kimi 0.42.0,
KIMI_CODE_INFINITE_RETRY=1): a streamingAPIConnectionError: terminatedmid-response led to process exit 1.Problem
With
KIMI_CODE_INFINITE_RETRY=1,AgentLLMRequesterServiceretries failed requests inside its ownfor (;;)loop (and inside the projection-policy resend path) without telling the turn machine.llm.sentis emitted only once, before the loop. When a stream dies after emitting half a tool call (functionpart atindex=0), the retry reuses stream index 0 while the turn'sHistoryAccumulatorandToolCallIdNormalizerstill hold the interrupted attempt's state:index=0hits the staleassignedByIndex[0]mapping and is silently rewritten to the previous attempt's id;[A, A, B];createMachineTools.beginBatchdid not dedupe,flushIfReadydrained the same pending entry twice (pending.get(A)→undefinedon the second read), andrunBatchdereferencedentry.input.toolCall.idoutside itstryblock;void runBatch(...)call had no rejection handler → unhandled rejection → process exit 1.A secondary leak: a duplicate
pending.set(id, ...)overwrote the first entry, leaving its promise pending forever.What changed
Root fix — the turn machine now learns about every below-the-turn retry:
AgentLLMRequesterServiceaccepts a newonAttemptRetryoverride and invokes it before each projection-policy resend and before each infinite-retry backoff sleep.createMachineRequestermaps that callback to a newllm.request.retryingrequester event (LlmRequestEvent/LlmEvent).llm.request.retryinginthinkingwith the same action asllm.sent(extracted asdiscardAttemptStream): roll back and recreate the accumulator and its response normalizer, so each attempt accumulates from zero. Parts of the interrupted attempt already forwarded to the parent machine/UI are not reclaimed — noted indocs/{en,zh}/llm.md.model-requester-implhandles the new event in its event switch (same reset as a re-sent request).Hardening in
agent/loop/machine/tools.tsso a duplicate id can never kill the process again:beginBatchdedupes expected ids;flushIfReadyskips missing pending entries instead of a non-null assertion;executesettles a superseded pending entry with an error result instead of leaking its promise;startBatch, which catches rejections intoonBatchError, and the entry-setup lines moved insiderunBatch'stry.Tests:
test/agent/loop/machineTools.test.ts(new):beginBatchwith duplicate ids runs one batch per unique id and settles the superseded call; executor failures reachonBatchErrorand settle every pending call. Red before the fix (reproduces the exact unhandledTypeError: Cannot read properties of undefined (reading 'input')).test/agent/llmRequester/llmRequesterService.test.ts: unit coverage foronAttemptRetryon both retry paths and the final-error path; an integration test wiring the real service → machine requester → turn machine where attempt 1 streamsfunction(index=0, id=call_a)then fails withAPIConnectionErrorand attempt 2 streamsid=call_b, asserting the finished message has tool call ids['call_b'](duplicate['call_a', 'call_a']before the fix). Fake timers throughout.agent-core-v2: 368 test files / 6569 tests passed,tsc --noEmitclean,oxlint --type-aware0 errors (warnings at baseline),check-no-commentsandcheck-import-boundariesOK.Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.