Skip to content

fix(tui): keep long sessions responsive and bound resumed history#1976

Merged
liruifengv merged 7 commits into
mainfrom
fix/long-session-responsiveness
Jul 21, 2026
Merged

fix(tui): keep long sessions responsive and bound resumed history#1976
liruifengv merged 7 commits into
mainfrom
fix/long-session-responsiveness

Conversation

@liruifengv

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is described below, based on a real session debug export.

Problem

A multi-day session (53 user turns, ~4.9k tool calls, single turns with 1,100+ steps and 400+ assistant text blocks, 100MB of wire history) made the CLI progressively laggy with the node process pinned at 100% CPU, and resume took seconds while flooding the screen:

  • Every rendered frame (spinner tick every 80–120ms, streaming flush every 50ms) re-truncated, re-normalized, and re-compared every transcript line, so frame cost grew with history length without bound.
  • Grouping a second Read/Agent call, removing swarm progress, and similar structural updates invalidated the whole transcript tree, forcing a full cold re-render (markdown + highlighting) of every mounted message.
  • Step merging collapsed thinking/tool steps but never assistant messages, so a single long turn could mount hundreds of markdown components.
  • Resume transferred the entire replay over the RPC boundary (~100MB, JSON round-tripped in-process) even though the TUI renders only the last 10 user turns.
  • Goal sessions were worst of all: replay "turns" only counted real user input, so a 100-round goal fell entirely inside the 10-turn window and resume replayed the whole run from round 0 — including the goal driver's long synthetic continuation prompts rendered as user bubbles.

What changed

Five focused commits (details in each message):

  1. fix(pi-tui) renderer fast path — keep the previous frame's raw lines, processed output, and per-line kitty image ids; a line whose raw string reference is unchanged reuses its processed output verbatim. Steady-state frame over a 30k-line transcript: 8.6ms → 0.56ms (~15x), with a vitest bench guarding the fast path. Behavior is byte-identical: reuse only hits on reference equality at unchanged width; a miss degrades to the old path.
  2. fix(tui) invalidate storms — remove tree-wide transcriptContainer.invalidate() from six hot paths (Read/Agent grouping, swarm progress removal, MCP status finalize, replay tool-call removal, /undo). The container's per-child reference-checked cache already handles structural changes; tree invalidation stays for theme switches.
  3. fix(tui) assistant folding — a running turn keeps its last 20 assistant messages; a finished turn folds to its conclusion tail of 2; older ones collapse into the step summary line (… thinking N times, call M tools, K messages). Both knobs are env-tunable and default-off-able; entries are preserved.
  4. fix(session) bounded resumeresumeSession accepts an optional replayTurnLimit; the turn-boundary predicate moves into agent-core as the single source of truth (limitAgentReplayByTurns, re-exported through the SDK); the CLI passes its existing 10-turn limit so resume transfers only the tail instead of the full history.
  5. fix(session) goal rounds as turnsgoal_continuation prompts count as replay turn boundaries (matching the goal system's own turn counting), so a 100-round goal resumes with its most recent 10 rounds; the synthetic continuation prompt no longer renders as a user bubble on replay (it is never shown live either).

Verification:

  • pi-tui 736, apps 2340, agent-core 3932, node-sdk 227 tests green; new tests cover the frame fast path, folding caps, replay trimming, and goal-round boundaries.
  • End-to-end tmux checks with real and synthetic session wires: a 54-turn session resumes to its last 10 turns with folding summaries; a synthetic 25-round goal session renders only rounds 15–24 with zero continuation bubbles (61 lines total).

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.

@changeset-bot

changeset-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cf511ff

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

This PR includes changesets to release 4 packages
Name Type
@moonshot-ai/kimi-code Patch
@moonshot-ai/kimi-code-sdk Patch
@moonshot-ai/agent-core Patch
@moonshot-ai/pi-tui 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

@pkg-pr-new

pkg-pr-new Bot commented Jul 20, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@cf511ff
npx https://pkg.pr.new/@moonshot-ai/kimi-code@cf511ff

commit: cf511ff

@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: cc1b646ffd

ℹ️ 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".

Comment thread apps/kimi-code/src/tui/controllers/session-replay.ts Outdated
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@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: 191958dd96

ℹ️ 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".

Comment thread packages/agent-core/src/rpc/core-impl.ts
Each frame previously re-truncated, re-normalized, and re-compared every
transcript line, so steady-state frames (spinner ticks, streaming flushes)
cost O(total lines x chars) and pegged one core in long sessions. Keep the
previous frame's raw lines, processed output, and per-line kitty image ids;
a line whose raw string reference is unchanged reuses its processed output
verbatim, and image-id consumers read the cache instead of re-scanning text.
Grouping a second Read/Agent call, removing swarm progress, finalizing an
MCP status row, replay tool-call removal, and /undo each invalidated the
entire transcript tree, forcing every mounted message to re-render (markdown
lexing + code highlighting) on the next frame. The container's render cache
already validates per child by reference, so structural child-list changes
are picked up without a tree-wide invalidate; reserve it for global style
changes such as theme switches.
Step merging only collapsed thinking/tool steps, so assistant text blocks
accumulated without bound inside a turn (hundreds of markdown components in
a single long turn, all re-processed every frame). A running turn now keeps
its last 20 assistant messages (KIMI_CODE_TUI_KEEP_RECENT_ASSISTANT), and a
finished turn folds down to its conclusion tail of 2
(KIMI_CODE_TUI_KEEP_RECENT_ASSISTANT_COMPLETED); older ones collapse into
the step summary line with a message count. Entries are kept, so expand
behavior is unchanged.
Resume used to return every agent's full replay over the RPC boundary (a
long session can reach ~100MB, serialized and parsed several times on an
in-process call), while the TUI only renders the last 10 user turns. The
resume payload now accepts an optional replayTurnLimit, the turn-boundary
predicate moves into agent-core as the single source of truth
(limitAgentReplayByTurns, re-exported through the SDK), and the CLI passes
its existing 10-turn limit so resume transfers just the tail.
Replay turn boundaries only matched real user input, so a 100-round goal
(a handful of user prompts plus 100 system-trigger continuations) fell
entirely inside the 10-turn replay window and resumed by rendering the whole
run from the start. The goal driver already fires one synthetic prompt per
goal turn and counts those as turns itself, so replay trimming now treats
goal_continuation prompts as turn boundaries and keeps the most recent 10
rounds. The continuation prompt is model-facing and hidden live; replay no
longer renders it as a user bubble either, while still advancing the replay
turn so each round groups separately.
Suppressing goal continuation bubbles removed every turn-boundary
component from goal-session replays, so mergeAllTurnSteps found no turn
edges and skipped step/assistant folding entirely — a single oversized
goal round still mounted all of its tool cards and assistant messages.
Mount an invisible ReplayTurnBoundaryComponent for each hidden
continuation: it renders zero lines but keeps the turn edges discoverable
to folding and window trimming, matching replay trimming's per-round turn
semantics.
@liruifengv
liruifengv force-pushed the fix/long-session-responsiveness branch from 95076ae to cf511ff Compare July 21, 2026 07:12
@liruifengv
liruifengv merged commit e458323 into main Jul 21, 2026
14 checks passed
@liruifengv
liruifengv deleted the fix/long-session-responsiveness branch July 21, 2026 07:20
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.

1 participant