Skip to content

Conversation

@ryanwyler
Copy link
Contributor

@ryanwyler ryanwyler commented Jan 15, 2026

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 history for long-running sessions incomplete:

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:

  1. Load conversation history - Loads messages up to the next compaction summary, providing relevant context without overwhelming the UI
  2. 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 breakpoint parameter for full history (undefined = falsy)
  • Uses z.coerce.boolean() pattern consistent with other boolean parameters like roots
  • 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)
image

Loading conversation history
image

Loading full session history
image

@github-actions
Copy link
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on my search, I found several related PRs that are addressing similar functionality:

Potentially Related PRs:

  1. feat(session): bi-directional cursor-based pagination (feat: virtualized scrolling + paginated message loading for long sessions (#8535) #6548)

  2. feat(tui): add configurable message_limit for session history (Cannot scroll to beginning of long conversations (message limit hardcoded to 100) (#6138) #6137)

  3. session: paginate message loading

These PRs should be reviewed to ensure there's no duplicate work, particularly #6656 and #8535 which appear to implement similar pagination/history loading features. The current PR (#8627) may supersede or need to be coordinated with these existing PRs.

@ryanwyler
Copy link
Contributor Author

ryanwyler commented Jan 15, 2026

Comparison with Related PRs

PR #6138 (configurable message_limit)

  • Raises the hardcoded limit from 100 to a configurable value
  • Doesn't actually solve the core problem - just moves the ceiling higher
  • Users with 500+ message sessions would still hit the limit

PR #6656 (cursor pagination)

PR #8535 (bi-directional cursor pagination)

  • Comprehensive implementation: RFC 5005 Link headers, memory bounding, bi-directional loading
  • 500+ lines of changes, 20 tests across 4 files
  • Excellent engineering, but significantly more complex than needed for the core problem

Why Timestamp-Based is More Elegant

This PR uses timestamps as immutable anchors rather than message ID cursors:

Advantages over cursor-based approaches:

  1. Immutable - Timestamps never change, cursors can become invalid
  2. No RFC compliance needed - No Link headers, simpler protocol
  3. Natural concurrency handling - New messages don't invalidate timestamps
  4. Simpler mental model - "messages before this point in time" vs "messages before this cursor"
  5. Future-proof - The ts_before parameter benefits any client (web, mobile)

Why explicit loading vs auto-scroll:

  • Clear user intent (no surprise loads while reading)
  • Two distinct modes: "load context" (stops at compaction) vs "load everything"
  • No memory management complexity needed (user controls growth)
  • 176 lines vs 500+ lines

Server API Enhancement Benefits Web Client

As noted in the PR description, the web client currently reloads all messages when loading more. The ts_before parameter allows incremental loading without re-fetching existing messages - a benefit shared across all clients.

Addressing the Three Issues

This implementation provides an elegant, non-disruptive solution that solves the core problems without the complexity of RFC-compliant pagination or memory management that may not be necessary for most users.

@ariane-emory
Copy link
Contributor

Have not yet tested, but I like the idea, if it works I would totally be in favour.

Little bit suspicious of those 2 failing checks, though. I'm sure you can sort those out, though. ;)

@ryanwyler
Copy link
Contributor Author

Have not yet tested, but I like the idea, if it works I would totally be in favour.

Little bit suspicious of those 2 failing checks, though. I'm sure you can sort those out, though. ;)

The failing tests are due to another commit:

Commit 779610d from PR #7360 by turculaurentiu91, merged January 15, 2026 at 08:12 UTC, uses platform.openLink() on line 300 of packages/desktop/src/index.tsx, but platform isn't created until line 304 inside the render() function. This causes typecheck error: Cannot find name 'platform'.

@ariane-emory
Copy link
Contributor

ariane-emory commented Jan 15, 2026

I haven't looked at the failures too deeply yet. If it's just the CI system spazzing out and throwing a fit - which does occur sometimes - then it could be safe to disregard, just worth taking a look to gain some confidence that that's what's happened.

Implements progressive history loading with two distinct modes for accessing older messages in long-running sessions.

## Implementation

**Server API Enhancement**
Added two optional parameters to Session.messages():
- ts_before: Unix timestamp for loading messages older than a specific point
- breakpoint: Boolean flag controlling whether to stop at compaction summaries

**Client Load Functions**
- loadConversationHistory(): Loads messages up to the next compaction summary
- loadFullSessionHistory(): Loads entire remaining session history without stopping

Both functions use the earliest loaded message timestamp as an anchor point,
eliminating the need for index tracking or offset calculations.

**UI Integration**
Displays "Load more messages" when 100+ messages are present, offering two
clickable options. Toast notifications show the count of messages loaded.
Uses a synthetic message pattern for clean, reactive positioning.

## Design Decisions

**Timestamp-Based Anchoring**
Uses message timestamps as immutable reference points rather than maintaining
counts or offsets. This eliminates state tracking complexity and race conditions.

**Truthiness Pattern for Breakpoint**
The breakpoint parameter uses standard boolean coercion (z.coerce.boolean).
Omitting the parameter (undefined) is falsy and loads everything. Sending true
stops at compaction summaries. This follows the established pattern used by
other boolean parameters like 'roots'.

**Two Loading Modes**
- Conversation history: Stops at compaction summaries to show context
- Full history: Loads all remaining messages for complete reconstruction

**Non-Disruptive**
All parameters are optional. Existing functionality remains unchanged.
Zero breaking changes to any existing code paths.

## Technical Details

The server iterates through messages in reverse chronological order, skipping
messages newer than ts_before. When breakpoint is truthy and a compaction
message is encountered, iteration stops. Results are reversed before returning
to maintain chronological order.

The client prepends loaded messages to the beginning of the existing array,
maintaining sort order with oldest messages first.

## Files Changed

- packages/opencode/src/session/index.ts - Core loading logic
- packages/opencode/src/server/server.ts - HTTP endpoint parameters
- packages/opencode/src/cli/cmd/tui/context/sync.tsx - Load functions
- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx - UI
- packages/sdk/* - Generated SDK types

Total: 176 lines added across 7 files
@ryanwyler ryanwyler force-pushed the feature/tui-load-full-history branch from 225aea3 to f1d8f7d Compare January 15, 2026 21:53
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.

Cannot scroll to beginning of long conversations (message limit hardcoded to 100) (#6138)

2 participants