Skip to content

perf(tui): make session switching independent of transcript length #39380

Closed

Description

@opencode-agent

Problem

Switching between pinned sessions with Ctrl-1/2/3 has a noticeable pause, especially once the sessions have long transcripts. That undermines the quick-switch flow: the shortcut changes the route immediately, but the destination is not immediately usable.

This is a performance follow-up to #36193, not another session-navigation proposal.

What the V2 TUI does today

Checked against origin/v2 at 27e7b0558a:

  • session.quick_switch.N resolves the pinned slot and immediately navigates to the destination session (packages/tui/src/context/local.tsx).
  • On every session ID change, createSessionRows synchronously reduces the destination's cached transcript into render rows, then reconciles those rows (packages/tui/src/routes/session/rows.ts).
  • Session hydration requests the newest 200 messages in one page and keeps them in the shared data store (packages/tui/src/context/data.tsx). A message can expand into many rendered rows because assistant content/tool parts are embedded in it.
  • The session view mounts a SessionRowView for every reduced row in the loaded transcript (packages/tui/src/routes/session/index.tsx). There is no viewport window or incremental transcript mounting on the switch path.
  • The message API itself already supports bounded, cursor-based pagination (packages/server/src/handlers/message.ts and packages/core/src/session.ts), so the missing piece is mainly TUI-side loading/rendering policy rather than a new storage primitive.

This explains why repeatedly switching among already-known sessions can still lag: route lookup is cheap, but transcript reduction/reconciliation/layout work scales with the loaded transcript and its parts.

Desired behavior

Quick-switch latency should be effectively independent of total session length. Switching to an already-open/pinned session should paint the destination chrome and newest visible transcript promptly, while older history loads or mounts only when needed.

Scope

  • Measure the switch path separately: route navigation, transcript fetch/cache lookup, row reduction/reconciliation, and terminal layout/render.
  • Bound work performed before the first destination frame (for example, a recent/visible window rather than all 200 loaded messages and all nested parts).
  • Use the existing cursor pagination to load older history when the user scrolls upward, or otherwise virtualize/window mounted transcript rows.
  • Preserve live updates, sticky-bottom behavior, message navigation, revert boundaries, and full transcript export.
  • Add a representative long-session performance regression check or timing fixture so this does not drift back.

Acceptance criteria

  • Back-to-back Ctrl-1/2/3 switches among long pinned sessions no longer have transcript-length-dependent pauses.
  • The destination's header/composer and newest visible messages appear before older history is fetched or mounted.
  • Scrolling upward can still reach the complete transcript without losing ordering or live event updates.
  • The issue includes before/after timings from a repeatable long-session fixture.

Source discussion: https://slack.com/archives/C0BFMFJ5FJS/p1785255726318509

Activity

  1. bhuvan0808 commented on Aug 5, 2026

    @bhuvan0808

    I'd like to work on this, happy to submit a PR. Let me know if there's a preferred approach @thdxr or any other maintainer.

  2. Dante-dan commented on Sep 29, 2026

    @Dante-dan
    Contributor

    I traced the switch pause to the TUI retaining every older message page loaded while scrolling a session. Re-entering that session reduces the whole cached transcript again, so the work grows with history length.

    I’m working on a bounded cache release for oversized sessions: release the message cache when its view unmounts, clear its pagination cursor so re-entry starts at the newest page, and ignore an older page that resolves after release. Small session caches should stay intact. I’m adapting the approach from the closed, unmerged #45940 and will cover the release threshold and pagination race in tests.

  3. neriousy commented on Nov 13, 2026

    @neriousy
    Member

    Closing as completed. #39568 makes session switching mount the newest 40 rows first and add older ones in chunks. #44656 paginates history, so a session first loads only its latest 20 messages and fetches older pages on scroll. In the PR's measurements, a switch to a 243-row session went from about 167ms to about 43ms. If switching is still slow on a current build, please open a new issue with timings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions