Skip to content

fix(agent-core): roll the todo store back on undo - #3072

Open
dhnihaoya wants to merge 2 commits into
MoonshotAI:mainfrom
dhnihaoya:fix/undo-rolls-back-todo-store
Open

fix(agent-core): roll the todo store back on undo#3072
dhnihaoya wants to merge 2 commits into
MoonshotAI:mainfrom
dhnihaoya:fix/undo-rolls-back-todo-store

Conversation

@dhnihaoya

Copy link
Copy Markdown

Related Issue

Resolve #3058

Problem

See linked issue. In short: /undo removed the conversation turns but left the todo list at the post-undo state — on the legacy v1 engine the store was never rolled back and the TUI's post-undo panel refresh silently no-op'd (getTodos threw NOT_IMPLEMENTED), and v2 sessions resumed through the SDK's v1 wire fold hydrated the todo panel with the undone (newest) todos.

What changed

  • ToolManager keeps checkpoint snapshots of the tool store, taken when each real user input (undo anchor) enters history via the pushHistory funnel — the single path shared by live appends, replay restore, and deferred flushes — and reseeded as a baseline at compaction and /clear (store contents survive, matching today's behavior and v2). Context.undo rolls the store back by the number of anchors it actually removed, before any undo_limit throw.
  • Restores go through updateStore, so the live path appends compensating tools.update_store records while replay restore stays record-suppressed — the append-only wire, the transcript's last-write-wins fold, and resume all stay self-consistent without truncation. Pre-fix wires self-heal on the next resume.
  • The v1 engine now serves agent.getTodos (AgentAPI → rpcMethodsSessionAPIImplKimiCore → SDK base client), replacing the NOT_IMPLEMENTED throw; the v2 client override is untouched. The TUI's post-undo todo refresh works unchanged.
  • This mirrors agent-core-v2's .undoable() checkpoint semantics with the same anchor predicate (isRealUserInput ≡ v2's isUndoAnchor), keeping engine behavior aligned.

Tests (all in existing files): undo rolls the store back + resume roundtrip, compensating record on the wire, todos first written in the undone turn cleared, compaction-baseline restore on partial undo, background notifications skipped, /clear reseeding; legacy and compensating wire shapes replayed on resume; SDK-level getTodos after undoHistory. Full suites green: agent-core 4192 passed, node-sdk 388 passed.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • 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.

Context.undo now rolls the agent tool store (todos) back to match the
spliced history: ToolManager keeps checkpoint snapshots taken when each
real user input enters history (the pushHistory funnel, shared by live
and replay paths) and reseeded at compaction/clear; undo restores
through updateStore so the live path appends compensating
tools.update_store records while replay restore stays record-suppressed.

The v1 engine also serves agent.getTodos now (AgentAPI -> rpcMethods ->
SessionAPIImpl -> KimiCore -> SDK base client), replacing the
NOT_IMPLEMENTED throw, so the TUI's post-undo todo refresh works on the
legacy engine. Folding v2-session wires through a v1 agent resumes with
the rolled-back store as well.
@changeset-bot

changeset-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8bf4033

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17e91844c1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +170 to +172
const pops = Math.min(removedUserTurns, this.storeCheckpoints.length - 1);
if (pops <= 0) return;
const target = this.storeCheckpoints[this.storeCheckpoints.length - pops]!;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject undo counts beyond retained todo checkpoints

When a session has more than 99 undo anchors since the last clear/compaction, callers can still request a larger /undo count because availability is based on context history, not this checkpoint limit. In that case pops is capped to the retained window and target becomes the oldest retained checkpoint, so undoing 100+ prompts restores todos from a turn that was just removed and then persists that wrong state via the compensating tools.update_store; please either retain/precheck the full undo depth or restore a safe baseline when the requested undo exceeds the retained checkpoints.

Useful? React with 👍 / 👎.

…ered

Review feedback: with checkpoints trimmed to a fixed window, an undo
deeper than the window restored the store snapshot of a turn the undo
just removed and persisted it via the compensating record. The stack is
1:1 with live history anchors (pushes on anchor appends, pops on undo,
reseeded on clear/compaction), so it is already bounded by history and
needs no cap; add a regression test with distinct per-anchor values.
@dhnihaoya

Copy link
Copy Markdown
Author

Addressed in 8bf4033: dropped the checkpoint cap entirely. The stack is 1:1 with live history anchors (pushed on anchor appends via pushHistory, popped by undo, reseeded on clear/compaction), so it is already bounded by history itself and every undo depth stays covered — there is no window to fall outside of. Added a regression test with a distinct store value per anchor across 120 anchors; the capped version restores the wrong snapshot, the uncapped one restores the pre-first-anchor state.

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.

/undo 后 TodoList 不回滚,仍显示被撤销回合写入的待办状态

1 participant