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". |
Merged
5 tasks
7723qqq
added a commit
to 7723qqq/kimi-code
that referenced
this pull request
Sep 15, 2026
…ment audit The matrix carried claims that do not match the code: native/glob.rs and native/bash.rs were named as the Glob and Bash implementations (they are a 55-line matcher helper and timeout constants), read_media.rs and core_tool_defs.rs were placed outside src/tools/, compaction/micro.rs was "354 lines + 8 tests" (326 + 7), server/remote_control.rs was 1339 lines (1317), agent-core-v2 was 1018 files (1544), the reverse RPC set was "10 of 11" (9, one of them dead), the lib suite was 2107 tests (2349), and the claim that packages/kosong/src/providers/* was deleted is false — two model metadata modules remain with live consumers. Two alignment claims were overstated and are now marked as partial: the injection board omitted the permission_mode reminder entirely, and the ACP board omitted the non-ACP stopReason values, the missing $/cancel_request handling, the uncalled terminal/kill, the silently dropped additionalDirectories, and the Bash rerouting that hardcodes the shell and passes cwd=None. Section 6 records the audit method, the delta ratchet, the shipped fixes and the open work items (permission-mode reminder, MoonshotAI#3734, the eight unverified behavior commits, MoonshotAI#3532, and the ACP findings).
11 tasks
7723qqq
added a commit
to 7723qqq/kimi-code
that referenced
this pull request
Sep 17, 2026
Brings the fork's Rust engine up to the behaviors ported from upstream since 0.42.0, and moves the version to 2.0.0 to follow upstream (never bumped independently). Ports landed here, each recorded in packages/kimi-agent/ROADMAP.md §6.2 and scripts/upstream-v2-delta-allowlist.json with its own evidence: - MoonshotAI#3688 MCP media originals are preserved instead of flattened to a text preview: src/mcp/output.rs adds the content-block conversion and the result-to-delivery path, FileStore gains content-addressed writing, and the attachment reference is resolvable by Read/ReadMediaFile. - MoonshotAI#3734 streamed attempt state is invalidated when the LLM requester retries: a turn-scoped tool-call id ledger, streamed tool-call deltas from all three wire protocols, and an attempt guard that rolls back on every failure exit. - MoonshotAI#3697 steer interrupts a background-task wait — plus the blocking wait it needed. The engine's WaitFor had been routed through a synchronous local store, so a 5s wait returned in 6ms reporting a timeout; the toolset now waits on its own task runner, and a steer signal ends that wait early without cancelling the turn or its tasks. WaitFor gains the wait-any form. - MoonshotAI#3846 an MCP tool call returning 401 flips the server to needs-auth. - MCP servers connect once per process, starting at startup. - AI session titles are generated through the managed chat_title tool. - The abort fix in the preceding commit. Also refreshes the ROADMAP evidence blocks, the upstream delta allowlist, the bilingual docs those behaviors surface in, and the TUI rendering for the new wait status.
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.