refactor(tui): unify resume replay rendering - #88
Conversation
🦋 Changeset detectedLatest commit: 1a67963 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 |
There was a problem hiding this comment.
💡 Codex Review
kimi-code/apps/kimi-code/src/tui/components/messages/tool-call.ts
Lines 1582 to 1586 in 6d8298e
This replay/display path swallows recognized ExitPlanMode outputs unless parsed feedback text exists, and the header path no longer shows a rejected chip for error outcomes. When args.plan is absent and getPlan() cannot supply a preview, a rejection like "Plan rejected by user. Plan mode remains active." renders as a neutral card with no rejection signal, so users lose the actual decision context.
ℹ️ 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".
| replayEntry( | ||
| context, | ||
| 'status', | ||
| mode === 'manual' ? 'YOLO mode: OFF' : `Permission mode: ${mode}`, |
There was a problem hiding this comment.
Report manual permission mode accurately during replay
The replay notice path currently maps every permission_updated event with mode === 'manual' to "YOLO mode: OFF", which is only correct when the previous mode was actually yolo. For sessions that switch auto -> manual (or emit a manual update without ever enabling YOLO), the transcript becomes misleading and loses the real mode transition. Please preserve prior replay permission state (as the old projection logic did) so manual is shown as "Permission mode: manual" unless it is explicitly a YOLO-off transition.
Useful? React with 👍 / 👎.
| backgroundAgents.add(info.taskId); | ||
| backgroundAgentMetadata.set(info.taskId, { | ||
| agentId: info.taskId, | ||
| parentToolCallId: info.taskId, |
There was a problem hiding this comment.
Track replayed background agents by subagent id
Replay hydration seeds backgroundAgents/backgroundAgentMetadata with taskId (agent-*) instead of the subagent identifier (info.agentId), but live completion/failure handlers look these maps up by event.subagentId. For resumed sessions with in-flight background subagents, the later subagent.completed/subagent.failed events miss the background metadata path, so completion cards are skipped and stale background-agent bookkeeping can persist.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1a679639b6
ℹ️ 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".
| return `${label}${chalk.hex(colors.success)(` · ${chipText}`)}`; | ||
| } | ||
| return `${label}${chalk.hex(colors.error)(' · Rejected')}`; | ||
| return label; |
There was a problem hiding this comment.
Preserve rejection status when no plan box renders
When an ExitPlanMode rejection has no inline/current plan to show (for example plan-file mode if getPlan() returns null/fails, or a replayed/reconstructed card without args.plan), buildPlanPreview() renders no plan box, and buildContent() suppresses the Plan rejected by user... result body. Since this branch now returns only Current plan, the user loses the only visible rejected indicator; keep a header/status fallback for rejected outcomes when no plan box can carry the chip.
Useful? React with 👍 / 👎.
Related Issue
No linked issue. This addresses TUI resume replay drift where replayed history used a separate rendering path from live session events.
Problem
Resume replay rendering had diverged from the normal live rendering flow. The replay code carried its own callback-based projection path, which made Agent and Read grouping behavior harder to keep consistent and caused background task notifications to render with the wrong task wording after resume.
What changed
Refactored TUI resume replay so replay orchestration lives in the KimiTUI session replay flow and drives the same live rendering hooks used by normal session events. Extracted stateless replay helpers for parsing, turn limiting, hook result formatting, background state projection, and snapshot hydration. Replaced the old replay action module and consolidated replay coverage into message replay tests, including plan review replay and bash background task notification wording.
Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.Validation
pnpm exec vitest run apps/kimi-code/test/tui/message-replay.test.ts apps/kimi-code/test/tui/kimi-tui-startup.test.ts apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts apps/kimi-code/test/tui/create-tui-state.test.tspnpm --filter @moonshot-ai/kimi-code run typecheckgit diff --check