MoonshotAI / MoonshotAI/kimi-code
Background subagent writes are silently dropped from file history after the parent turn ends
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
What version of Kimi Code is running?
0.41.0 (kimi --version), also current main (ccf3d5d6)
Which open platform/subscription were you using?
Not relevant (engine-local file-history / rewind path)
Which model were you using?
Not relevant
What platform is your computer?
Linux x86_64 (reproduced with a focused unit test; the defect is host-independent)
What issue are you seeing?
Turn-level file history silently drops Write/Edit snapshots from a background subagent once the parent (main) turn has ended.
Subagent file mutations are forwarded to the main agent's captureForActiveTurn(). That method returns immediately when activeTurnId is undefined:
captureForActiveTurn(path: string): Promise<void> {
const turnId = this.activeTurnId;
if (turnId === undefined) return Promise.resolve();
return this.enqueue(() => this.capture(path, turnId));
}
Agent with run_in_background: true is the intended path for long-running workers. Those workers commonly keep writing after the parent turn has already completed. File history / rewind then reports success (no error) while the files never appear in any checkpoint.
Related (same API, different timing): if the user has already started a new main turn when the background write arrives, captureForActiveTurn pins the snapshot to that new turn, not the turn that spawned the subagent. Rewind of the original turn misses the files; rewind of the later turn restores unrelated worker output.
What steps can reproduce the bug?
Deterministic unit reproduction (fails on unpatched main, passes with the last-ended fallback):
- Construct
AgentFileHistoryServiceformain. - Publish
TurnStarted(turnId=1)thenTurnEnded(turnId=1)with no edits. - Call
captureForActiveTurn('/ws/late.txt')(this is whatonSubagentWillExecuteTooldoes). - Observe:
history().checkpointsstays[]. The promise resolves. No error is logged.
The same drop happens through the real subagent hook: a second service for agent-1 whose IAgentLifecycleService.handleOf('main') returns the main service, then onWillExecuteTool for a Write after the parent turn has ended — main still has no checkpoint for that path.
Interactive sketch:
- Start a session, send a prompt that launches
Agentwithrun_in_background: trueand asks it to write a file after some work. - Let the parent turn finish (the background worker is still running).
- After the worker writes the file, inspect turn-level file history / rewind for the parent turn.
- The worker's write is missing. Creating a brand-new session is the only way to get a clean history going forward.
What is the expected behavior?
- A background subagent Write/Edit that lands after the parent turn ends must still produce a file-history snapshot (at least a start checkpoint on the last ended main turn), not
Promise.resolve()with no record. - Failures to snapshot should be visible (log / error), not silent success.
- Ideally, captures from a given subagent stay bound to the spawn turn, not whichever main turn happens to be active when the write arrives.
Additional information
Root cause
packages/agent-core-v2/src/features/fileHistory/fileHistoryService.ts
- Subagents do not keep their own history (
agentId !== MAIN_AGENT_IDregisters onlyonSubagentWillExecuteTooland returns). Existing test:stays inactive on subagents. onSubagentWillExecuteToolalways forwards to main:
event.waitUntil(main.accessor.get(IAgentFileHistoryService).captureForActiveTurn(path));
- Main only sets
activeTurnIdfrom its ownTurnStarted/TurnEnded. AfterTurnEnded,activeTurnIdis cleared. captureForActiveTurntreats "no active turn" as success and records nothing.
This is a state-machine / identity bug: subagent writes have no durable turn to attach to after the parent goes idle, and the API swallows that.
Suggested fix
Minimum (unblocks the idle / run_in_background case):
- Remember
lastEndedTurnIdon mainTurnEnded. captureForActiveTurnusesactiveTurnId ?? lastEndedTurnIdinstead of no-op'ing.
That is enough for the unit tests above. Remaining follow-up (not required for the idle drop):
- Bind each subagent to the main turn that spawned it, so a later user prompt cannot steal those snapshots.
- After a late capture on an already-ended turn, also record an end-of-turn after-image (today
endCheckpointhas already run and will not see the new path).
Environment
- Engine:
@moonshot-ai/agent-core-v2onmain(ccf3d5d6and the 0.41.0 line). - File history is no longer experimental (
feat(agent-core-v2): drop the experimental gate from turn-level file history, #3525).
Contribution
- I am willing to submit a PR for this bug fix myself (please wait for maintainer approval in this issue first)
Please /approve if this looks right and I will open the fix PR against this issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/agent-core-v2/src/features/fileHistory/fileHistoryService.ts and inspect captureForActiveTurn, TurnStarted/TurnEnded handling, and onSubagentWillExecuteTool. Run the deterministic unit reproduction described in the issue, then verify that a late background-subagent write creates a checkpoint and that snapshot failures are visible rather than silently succeeding.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100