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
AgentFileHistoryService for main.
- Publish
TurnStarted(turnId=1) then TurnEnded(turnId=1) with no edits.
- Call
captureForActiveTurn('/ws/late.txt') (this is what onSubagentWillExecuteTool does).
- Observe:
history().checkpoints stays []. 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
Agent with run_in_background: true and 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_ID registers only onSubagentWillExecuteTool and returns). Existing test: stays inactive on subagents.
onSubagentWillExecuteTool always forwards to main:
event.waitUntil(main.accessor.get(IAgentFileHistoryService).captureForActiveTurn(path));
- Main only sets
activeTurnId from its own TurnStarted / TurnEnded. After TurnEnded, activeTurnId is cleared.
captureForActiveTurn treats "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
lastEndedTurnId on main TurnEnded.
captureForActiveTurn uses activeTurnId ?? lastEndedTurnId instead 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
endCheckpoint has already run and will not see the new path).
Environment
Contribution
Please /approve if this looks right and I will open the fix PR against this issue.
What version of Kimi Code is running?
0.41.0 (
kimi --version), also currentmain(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 whenactiveTurnIdis undefined:Agentwithrun_in_background: trueis 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,
captureForActiveTurnpins 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):AgentFileHistoryServiceformain.TurnStarted(turnId=1)thenTurnEnded(turnId=1)with no edits.captureForActiveTurn('/ws/late.txt')(this is whatonSubagentWillExecuteTooldoes).history().checkpointsstays[]. The promise resolves. No error is logged.The same drop happens through the real subagent hook: a second service for
agent-1whoseIAgentLifecycleService.handleOf('main')returns the main service, thenonWillExecuteToolfor a Write after the parent turn has ended — main still has no checkpoint for that path.Interactive sketch:
Agentwithrun_in_background: trueand asks it to write a file after some work.What is the expected behavior?
Promise.resolve()with no record.Additional information
Root cause
packages/agent-core-v2/src/features/fileHistory/fileHistoryService.tsagentId !== MAIN_AGENT_IDregisters onlyonSubagentWillExecuteTooland returns). Existing test:stays inactive on subagents.onSubagentWillExecuteToolalways forwards to main: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_backgroundcase):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):
endCheckpointhas already run and will not see the new path).Environment
@moonshot-ai/agent-core-v2onmain(ccf3d5d6and the 0.41.0 line).feat(agent-core-v2): drop the experimental gate from turn-level file history, feat(agent-core-v2): drop the experimental gate from turn-level file history #3525).Contribution
Please
/approveif this looks right and I will open the fix PR against this issue.