feat(tui): load conversation and session history
Resolves #6137, #4918, #7380 Related to #6548
Problem
Users cannot access message history beyond the initial 100 messages loaded on session init. This makes OpenCode unusable for long-running sessions where:
- Users need to reference earlier context (issue #7380)
- Sessions span hundreds of interactions (issue #4918)
- Important information from early messages becomes inaccessible (issue #6137)
Solution
This solution does not disrupt the natural 100 message limit for the TUI client maintaining the current message roll-off strategy in place.
Functional Core: ~38 lines (22% of changes)
- Server logic: 18 lines
- Client logic: 20 lines The rest is either:
- UI presentation (buttons, colors, hover states)
- Auto-generated SDK types (non-disruptive additional options for Session.messages())
- Comments and structure
Elegant, non-disruptive implementation that adds on-demand message loading with two modes accessed via a "Load more messages" UI when 100+ messages are present:
- Load conversation history - Loads messages up to the next compaction summary, providing relevant context without overwhelming the UI
- Load full session history - Loads all remaining messages for complete session reconstruction
Only pulls the missing messages - does not reload the entire session, only the missing messages due to the ts_before implementation.
Zero breaking changes - All parameters optional, existing functionality completely unchanged.
Message roll-off maintained - There is a system in the client today that once the messages reach over 100 messages, one message is rolled-off with each new message. This roll-off strategy works perfectly with this feature. A user can load their conversation or their session history and continue their session and the last message will roll off as a new message comes in. If for some reason the user wants to bring back the earliest messages again, they can click the load button at the top to load them all back in, but this natural pruning system is important to maintain and this solution fits perfectly with the entire existing system.
Implementation
Uses timestamp-based anchoring (immutable reference points) rather than offset/count tracking, eliminating state management complexity and race conditions.
Server API Enhancement - Added optional parameters to Session.messages():
-
ts_before: Unix timestamp for loading messages older than a specific point -
breakpoint: Boolean controlling whether to stop at compaction summaries
This enhances the robustness of the server messaging system by providing precise temporal queries rather than simple limits. The ts_before parameter acts as an immutable anchor point that naturally handles concurrent updates and message insertion without index drift.
Client - Two load functions that prepend older messages to the existing array, maintaining chronological order. Toast notifications show message counts.
Total addition: 176 lines across 7 files
Technical Details
- Server logic handles reverse iteration with optional breakpoint stopping
- Client omits
breakpointparameter for full history (undefined = falsy) - Uses
z.coerce.boolean()pattern consistent with other boolean parameters likeroots - Timestamps are immutable - no race conditions or state tracking needed
Testing
Verified with sessions containing 1000+ messages:
- Conversation history stops at compaction summaries ✅
- Full session history loads all remaining messages ✅
- Accurate message counts in toast notifications ✅
- No disruption to real-time message updates ✅
Additional Consideration
The server Session.messages() enhancement with ts_before & breakpoint would greatly benefit the web client implementation, which currently reloads all messages when loading more. The timestamp-based approach would allow incremental loading without re-fetching existing messages.
Screenshots
Option to load conversation (next breakpoint) or full session (all breakpoints)
Loading conversation history
Loading full session history