You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
v1 TUI sync hardcodes limit:100 — api.state.session.messages() returns truncated data for long sessions #31513
Sessions with >100 messages silently return truncated data via api.state.session.messages(). Plugins (cache-hit, etc.) see incorrect statistics because the API only returns the last 100 messages.
The root cause is in packages/opencode/src/cli/cmd/tui/context/sync.tsx:
// line 559sdk.client.session.messages({ sessionID,limit: 100}),// lines 580-581constremoved=infos.slice(0,-100)constvisible=infos.slice(-100)
This hardcoded window means any session exceeding 100 messages is silently truncated.
Concrete example — a real opus-4-6 session with 284 messages (234 assistant + 50 user):
Full (DB session table)
API (messages(), last 100)
Loss
tokens_cache_read
138,989,149
59,585,432
−57%
tokens_input
329
118
−64%
tokens_output
91,895
32,749
−64%
cost
$80.61
$31.17
−61%
The cache-hit plugin correctly aggregates whatever the API returns — the data is simply incomplete.
This issue is distinct from #30587 and not fully addressed by #26861.
#30587 is about the TUI scroll buffer — users can't scroll to old messages. A UI problem.
This issue (#31513) is about the plugin API — api.state.session.messages() returns truncated data regardless of scroll state. Plugins call this API once and get at most 100 messages, with no way to trigger full load.
#26861 replaces the hardcoded limit: 100 with INITIAL_PAGE_SIZE = 100 and adds lazy-scroll loading (50/page). This fixes the scroll experience in #30587, but the initial page size remains 100. For plugins consuming the messages API, they still only see 100 messages on first call — they can't scroll or paginate.
What's needed beyond #26861: either raise/remove INITIAL_PAGE_SIZE, or provide an explicit "full load" path for programmatic consumers.
Confirmed this affects opencode-cache-hit — same session, same cap.
Workaround: switched from api.state.session.messages() (capped at 100)
to api.state.session.get() which returns database-level aggregates
(cost, tokens.{input,output,cache.*}) computed from all messages by
the session projector, not affected by the TUI sync limit.
Before/after (284-message session):
messages()
session.get()
cache read
59.6M tok
139.0M tok
cost
~39% actual
full
Covered by opencode-cache-hit v0.2.2. This is a plugin-side workaround —
the root cause in sync.tsx (hardcoded limit:100) still needs fixing
for plugins that iterate per-message fields.
Quick checklist for other plugin authors: if your Session type has cost? and tokens?, you can swap now without waiting for the
pagination PR.
Activity
github-actions commentedon Jun 9, 2026
This issue might be a duplicate of existing issues. Please check:
limit:100truncates old messages, breaking scroll and fork-from-history in long sessionsGET /session/:id/messageendpoint to address the fixed truncationzhumengzhu commentedon Jun 9, 2026
This issue is distinct from #30587 and not fully addressed by #26861.
#30587 is about the TUI scroll buffer — users can't scroll to old messages. A UI problem.
This issue (#31513) is about the plugin API —
api.state.session.messages()returns truncated data regardless of scroll state. Plugins call this API once and get at most 100 messages, with no way to trigger full load.#26861 replaces the hardcoded
limit: 100withINITIAL_PAGE_SIZE = 100and adds lazy-scroll loading (50/page). This fixes the scroll experience in #30587, but the initial page size remains 100. For plugins consuming the messages API, they still only see 100 messages on first call — they can't scroll or paginate.What's needed beyond #26861: either raise/remove
INITIAL_PAGE_SIZE, or provide an explicit "full load" path for programmatic consumers.zhumengzhu commentedon Jun 10, 2026
Confirmed this affects opencode-cache-hit — same session, same cap.
Workaround: switched from
api.state.session.messages()(capped at 100)to
api.state.session.get()which returns database-level aggregates(
cost,tokens.{input,output,cache.*}) computed from all messages bythe session projector, not affected by the TUI sync limit.
Before/after (284-message session):
Covered by opencode-cache-hit v0.2.2. This is a plugin-side workaround —
the root cause in
sync.tsx(hardcodedlimit:100) still needs fixingfor plugins that iterate per-message fields.
Quick checklist for other plugin authors: if your
Sessiontype hascost?andtokens?, you can swap now without waiting for thepagination PR.
github-actions commentedon Aug 10, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.