feat: add /undo slash command and keep replay in sync#277
Conversation
🦋 Changeset detectedLatest commit: fd6bd63 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
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 |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c373a7819
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (message.role === 'user') { | ||
| removedUserCount++; | ||
| if (removedUserCount >= count) break; |
There was a problem hiding this comment.
Count only real user prompts when undoing
When a non-user-origin role: 'user' message is at the end of history, such as a background task completion notification appended with origin.kind === 'background_task', this condition treats it as the prompt to undo and stops before removing the actual last visible user prompt. The TUI removes the last TranscriptEntry with kind === 'user', so after /undo in that scenario the prompt disappears from the transcript but remains in the model context and persisted history.
Useful? React with 👍 / 👎.
| const entries = this.state.transcriptEntries; | ||
| const lastUserIndex = entries.findLastIndex((e) => e.kind === 'user'); |
There was a problem hiding this comment.
Treat skill activations as undoable turns
When the last turn was a user-slash skill activation, the transcript entry is kind: 'skill_activation', not kind: 'user', so this search either reports “Nothing to undo” or selects an earlier prompt. In the latter case the TUI removes the earlier prompt while core context.undo(1) removes the skill activation from history, leaving the visible transcript and model context out of sync.
Useful? React with 👍 / 👎.
…n turns in TUI undo
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 421adaf23d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
|
||
| this.state.ui.requestRender(); | ||
|
|
||
| void session.undoHistory(1); |
There was a problem hiding this comment.
Handle undo RPC failures before mutating the transcript
If session.undoHistory(1) rejects here (for example because the core/session RPC is gone or persistence fails), the TUI has already spliced the visible transcript and requested a render, while the core context and persisted history still contain the prompt. Other send paths attach a .catch(...) and call failSessionRequest/show an error; this path leaves the UI and model history out of sync with no user-visible failure.
Useful? React with 👍 / 👎.
| undoHistory({ agentId, ...payload }: AgentScopedPayload<UndoHistoryPayload>) { | ||
| return this.getAgent(agentId).undoHistory(payload); |
There was a problem hiding this comment.
Refresh session metadata after undoing main-agent prompts
When /undo removes a main-agent prompt, this method only forwards to the agent and never mirrors the metadata maintenance done by prompt()/activateSkill(). After undoing the last or only prompt, SessionMeta.lastPrompt (and the auto-generated title for an untitled session) can still point at text that is no longer in context, so the session picker/continued session metadata advertises an undone prompt.
Useful? React with 👍 / 👎.
|
|
||
| this.agent.replayBuilder.removeLastMessages(removedCount); | ||
|
|
||
| this.openSteps.clear(); |
There was a problem hiding this comment.
Reset injection bookkeeping after removing injected reminders
When the undone turn contained dynamic injections added in beforeStep (for example plugin session-start, plan-mode, or auto-permission reminders), this loop removes those origin.kind === 'injection' messages from history but leaves each injector's bookkeeping untouched. On the next turn the injectors can still think the reminder exists (or that the permission mode was already announced), so the model proceeds without required session/plan/permission instructions that were just removed from context.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e0675eac7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| 'context.append_loop_event': { event: LoopRecordedEvent }; | ||
| 'context.clear': {}; | ||
| 'context.apply_compaction': CompactionResult; | ||
| 'context.undo': { count: number }; |
There was a problem hiding this comment.
Apply undo records in the vis context projector
When sessions containing this new persisted context.undo record are opened in the visual debugger, apps/vis/server/src/lib/context-projector.ts still falls through default for this record type (I checked the switch: it handles append/clear/apply_compaction but not undo). As a result, the projected context shown by the vis /context endpoint keeps prompts and assistant/tool messages that agent-core removed during replay, so debugging an undone session displays stale conversation state.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e0675eac7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e296ec1dad
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ebc40722c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Related Issue
N/A – this is a quick feature/fix combination.
Problem
What changed
/undoslash command (apps/kimi-code) that pops the last user prompt and its subsequent assistant/tool exchange from the transcript.ReplayBuilder.removeLastMessages(packages/agent-core) so the replay record set can be trimmed to match the undone history.ContextMemory.undoto call the new replay-builder method, keeping in-memory history and replay records consistent.context.undoand asserts both context history and replay messages are correctly trimmed.Checklist