Skip to content

[FEATURE]: show and load the messages the TUI hides in long sessions #53642

Closed as not planned

Description

@Nowaker

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

The TUI keeps at most 100 messages of a session loaded. Opening a session fetches the newest 100 (session.messages({ limit: 100 })), and every new message past 100 drops the oldest one from the store (packages/tui/src/context/sync.tsx). The limit counts messages only; every part of a kept message stays loaded, whatever its size. Nothing else limits what the transcript renders.

That limit is reasonable, but today it is invisible and final:

  • Nothing in the transcript says that messages are missing. Scrolling to the top of a long session lands on some message in the middle of the work, which reads as if the session started there.
  • The first prompt, which usually says what the whole session is about, is the first thing to go.
  • There is no way to see the older messages again from the TUI, short of exporting the session.
  • The 100 is hard-coded, so a user on a small machine can't lower it and a user who wants more scrollback can't raise it.

Proposal:

  1. A divider wherever messages are hidden. A horizontal rule across the transcript with the count centered on it, ──── 1,234 messages hidden ────, and below it the actions that load them: load 50 above, load 50 below and load all. A gap of 50 or fewer offers only load all 23. The actions are clickable and are also commands in the palette ("Load 50 hidden messages above the divider", "... below the divider", "Load all hidden messages", /load-hidden), with keybinds that default to none (messages_hidden_above, messages_hidden_below, messages_hidden_all).
  2. Keep the first prompt. When older messages are hidden, the session's first prompt stays at the top, above the divider. tui.json transcript.keep_first_prompt, default true. This is a visible change from today; it could default to false if you'd rather keep the current look.
  3. Configurable limit. tui.json transcript.max_messages, default 100, the current value.

"Above" fills the gap from its older edge: the messages right after the first prompt, or the session's very first messages when no prompt is pinned. "Below" fills it from its newer edge, extending the recent messages backwards. load all loads the rest and stops dropping messages for that session. Loading 50 grows the window by what was loaded, so new output does not immediately drop what you just loaded.

Loading keeps the reader's place: if the view is scrolled away from the divider, the block at the top of the viewport stays on the same row while the loaded messages are inserted above it.

API

GET /session/:id/message already pages backwards with limit and an opaque before cursor (X-Next-Cursor). The first prompt and "above" need the other direction, and the divider needs a count, so the same route gains:

  • after: messages strictly newer than a cursor, oldest first, X-Next-Cursor continuing forward;
  • order=asc: without an anchor, page from the oldest message instead of the newest;
  • before and after also accept a message ID, so a client can anchor on a message it already has instead of holding a cursor that goes stale when messages are dropped;
  • X-Total-Count on paged responses.

before with after, after with order=desc and before with order=asc are rejected with 400. Requests without limit are unchanged. The experimental /api/session/:id/message route has ascending order, but it reads the session_message projection, which sessions older than the event tables don't have.

The TUI asks for the first prompt (limit=1&order=asc) only when the newest page doesn't cover the whole session, so short sessions make exactly the request they make today.

Activity

  1. github-actions commented on Oct 7, 2026

    @github-actions
    Contributor

    This issue might overlap with existing issues. Please check:

    For keybind-related issues, please also check our pinned keybinds documentation: #4997

  2. opencode-agent commented on Oct 7, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed proposal, @Nowaker!

    The code you describe (session.messages({ limit: 100 }) in packages/tui/src/context/sync.tsx, which drops the oldest message once there are more than 100, and the before/X-Next-Cursor paging on GET /session/:id/message) is the OpenCode v1 TUI (opencode-ai, versions 0.x/1.x). v1 now only gets fixes for critical bugs such as data loss, security problems or crashes, so a feature like this is unlikely to be added there. Your older messages are still stored and can be exported.

    Please try v2 (@opencode/cli, run with opencode2). Its TUI works differently here: when you scroll up to the oldest loaded message, it fetches the previous page and keeps your place (see #44597), so older messages aren't permanently out of reach. If something you propose is still missing in v2, such as a visible marker for hidden messages or keeping the first prompt pinned, please reply here or open a new issue that describes what you see in v2. And if you think the v1 behavior is critical, let us know why.

  3. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    Closing this as not planned. The proposal targets the v1 TUI (opencode-ai, 0.x/1.x), which now only gets fixes for critical bugs, and we haven't heard back since the last comment. Thanks again for the detailed write-up, @Nowaker.

    In v2 (@opencode/cli), the TUI already loads older messages page by page as you scroll up (see #44597). If something from your proposal is still missing there, such as a visible marker for hidden messages or keeping the first prompt pinned, please open a new issue describing what you see in v2.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions