Skip to content

fix(claude-code): open long Claude transcripts a page at a time - #2428

Merged
johnmatthewtennant merged 11 commits into
mainfrom
jtennant/martha/claude-large-transcripts
Nov 4, 2026
Merged

johnmatthewtennant merged 11 commits into
mainfrom
jtennant/martha/claude-large-transcripts

Conversation

@johnmatthewtennant

@johnmatthewtennant johnmatthewtennant commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Why

Bug fix (AIDA-2457). Opening a long Claude agent's transcript fails with "Could not read the Claude session: .//.jsonl is larger than 50 MB". The plugin read each session file whole through the shared readFile, which caps a read at 50 MB, and long Claude sessions grow past that. One real agent session is 152 MB.

What

A session too large to read whole is read a page at a time from its end, 32 MiB per request, until it has the transcript's latest 50 turns, reaches the file's start or has read two pages. Reads are now bounded by a page, and a session's text by two pages (about 64 MiB), instead of a whole session. Displayed turns, their order and the 50 MB bound itself are unchanged, and sessions under 50 MB read exactly as before.

  • Two declared, read-only plugin processes do the reading: size (wc -c) and read-page (dd of whole MiB blocks, printed as base64). readFile takes an optional process and arguments and keeps its limit, timeout and protections.
  • A line that crosses pages is joined before it is parsed, so a record larger than a page still reads whole.
  • When older turns are left unread, the transcript reports more, and the session's first 4 MiB still decide whose conversation it is.
  • Turns from the conversation's other sessions that are older than a cut session's first kept turn are left too, so the transcript is always a contiguous run of the latest turns. Its notice gives the number of turns shown.
  • Session files are never written.

This is the Claude half of a matching pair. #2259 fixes the same failure for Codex, where one long turn exceeded app-server's 4 MiB frame, by reading each turn's items a page at a time.

Validation

  • A new regression test opens a 55 MB session whose 55 MiB reply spans two pages. On main it fails with "large.jsonl is larger than 50 MB". On this branch it shows both turns, with the reply whole.
  • A second test has 51 turns after a 55 MiB reply. It reads only the last page and the first 4 MiB, shows the latest 50 turns and reports more.
  • A third has three 30 MiB replies and few turns. It stops after two pages and reports more.
  • A fourth adds an older saved session to that one. On the previous head it shows the older session's turn ahead of the gap; on this branch it leaves it.
  • On macOS, the read-page script returned byte-identical pages of a real 152 MB session (cmp against tail | head), returned a 17 MiB final page at its end, and failed with No such file or directory for a missing file. The file's sha256 matched before and after.

Screenshots

Activity tab opening a 55.5 MB session

Activity tab opening a 55.5 MB session

🤖 Generated with Claude Code

A Claude session file over the 50 MB read limit failed to open with
"is larger than 50 MB". Such a file is now read as its first 4 MB,
which says whose conversation it is, and its last 40 MB, which holds
its latest turns, through two new declared read processes. The
transcript reports more turns when it was cut. Session files are only
read, and the shared reader keeps its limit, timeout and protections.

Refs AIDA-2457

Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
johnmatthewtennant and others added 2 commits November 4, 2026 12:35
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
A session over 50 MB was read as its first 4 MB and last 40 MB, so one
whose latest turns span more than that showed fewer of them. It is now
read in 32 MiB pages from its end until it has the latest turns or its
start, as the Codex transcript reads a turn's items a page at a time.

Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
@johnmatthewtennant johnmatthewtennant changed the title fix(claude-code): open Claude transcripts over 50 MB fix(claude-code): open long Claude transcripts a page at a time Nov 4, 2026
Records main as a parent; 5560670 brought in its changes without one.

Signed-off-by: John Tennant <[email protected]>
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>

* origin/main:
  feat(solo): let viewers join read-only spaces (#2301)
  [3/5] Add protection to the Claude harness plugin (#1878)
  fix(settings): keep the bottom padding on long sections (#2361)
  fix(mermaid): cap diagram output size (#2231)
  feat(agents): give plugin agent types the Buzz side of a turn (#2364)
  feat(plugins): pin buzz://plugin-install links to a path and commit (#2315)
  feat(plugins): tint currentColor SVG page icons like host icons (#2314)
  feat(notifications): add a soft notification sound (#1536)
  feat(agents): let agents propose a profile picture (#2267)
  fix(plugins): default reminders and workflows off for fresh installs (#2291)
  Show all reactors in compact, separate emoji capsules (#2179)
  fix(panels): remember page split widths per workspace (#2264)

# Conflicts:
#	src/bundled/claude-code/manifest.json
@johnmatthewtennant
johnmatthewtennant marked this pull request as ready for review November 4, 2026 17:43
@johnmatthewtennant
johnmatthewtennant requested a review from a team as a code owner November 4, 2026 17:43

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 One resource-bound defect remains in large transcript loading.

Comment thread src/bundled/claude-code/transcript.ts Resolved
johnmatthewtennant and others added 2 commits November 4, 2026 13:09
A session over 50 MB with fewer turns than the limit read every page,
holding the whole file and reparsing it after each page. Stop after
two 32 MiB pages and report the older turns as more.

Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>

* origin/main:
  test(agents): refuse a real relay workflow mention in the harness kit (#2444)
  fix(agents): catch up every event watch on the kinds it watches (#1852)
  Keep local artifact collections current (#1507)
  feat(members): add several people to a chat in one write (#2010)

Signed-off-by: John Tennant <[email protected]>

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 One transcript truncation issue remains.

Comment thread src/bundled/claude-code/transcript.ts Resolved
johnmatthewtennant and others added 2 commits November 4, 2026 13:43
A large session read to its page budget can keep fewer than 50 turns.
Turns merged from the conversation's other sessions that are older than
its first kept one would then show around the gap, so they are left, and
the notice gives the count shown rather than promising 50.

Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>

@loganj loganj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Larry and Auto: an independent adversarial review of 5efb784. Source review only, plus targeted probes: Auto ran vitest run src/bundled/claude-code src/buzz-mcp at this head (20 files, 689 tests pass) and two throwaway probe tests; Larry checked the read-page dd/base64 command and its missing-file error on macOS. No typecheck or full app suite run.

The page stitching is correct: it splits only at 0x0A, so no decode runs mid-character, and the held-back partial line is joined back without loss or duplication. A file under 50 MB keeps the whole-file path, and one from 50 MB to 64 MiB is read whole in two pages with cut false. The read-page process adds no power the manifest's claude-sh (bash -c) does not already have, and its arguments are quoted.

Must fix (error): a cut window that starts at a prompt is labelled "New session" (inline at readTranscript).

Everything else is advice, a question or a preference. Approving on the understanding that the newSession fix and its test land before merge.

Notes outside the diff:

  • question: two other whole-file reads of the same session .jsonl still fail above 50 MB: usage (usage.ts:274, through host.readFile and MAX_READ_BYTES), and the protected history reader (runtime.ts:1053, called from the hook at claude.ts:909), which makes every hook of a protected session over 50 MB fail. Both need the whole file, so paging from the end is not their fix. Fine as known follow-ups; worth a line in the PR body.
  • convention: the branch conflicts with main (mergeStateStatus DIRTY) in transcript.ts and transcript.test.ts, after #2088 changed the transcript readers and startedFor. Needs a rebase.
  • advice: tests are missing for newSession on a cut window, a record longer than the page budget, and readSessionTitle paging.

Comment thread src/bundled/claude-code/transcript.ts Resolved
Comment thread src/bundled/claude-code/transcript.ts Resolved
return { text, cut: false, start: async () => text };
} catch (error) {
signal.throwIfAborted();
if (!/larger than 50 MB/.test(error instanceof Error ? error.message : ""))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

advice (perf, simplicity): check the size before the whole-file read.

For every session over 50 MB, this first try streams about 50 MB through base64 and buffers about 67 MB of text before readFile kills it, and only then pages. With wc, two 32 MiB pages and start(), that is about 120 MB read per large session. Spawning size first and paging only above READ_LIMIT removes the wasted read, and removes the coupling to the larger than 50 MB error text.

A simpler alternative, given PAGE_BUDGET = 2: one tail -c 48M "$1" | base64 read that drops the first partial line would replace size, the page loop, the rest stitching and enough, at the cost of a fixed 48 MiB window and no early stop after one page.

Also, for sessions other than the saved one, running start()/startedFor before paging the tail would avoid reading 64 MiB of a file that turns out not to match.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Leaving this as it is for now. Checking the size first adds a wc spawn for every session, and most sessions are small, so the wasted read only happens for the rare session over 50 MB. The tail -c alternative is tempting, but it gives up stopping early after one page. Happy to switch if you'd rather drop the coupling to the error text.

Comment thread src/bundled/claude-code/transcript.ts Resolved
Comment thread src/bundled/claude-code/transcript.ts Resolved
Comment thread src/bundled/claude-code/transcript.ts Resolved
path: string,
signal?: AbortSignal,
protection?: ProcessProtection,
process = protection ? "read-protected" : "read",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convention: readFile now takes six positional parameters, and transcript.ts:319 passes undefined for protection to reach process. An options object for protection, process and args would read more clearly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Agreed it reads better as an options object. I'd rather do that in a follow-up, because readFile has callers outside this plugin.

Comment thread src/bundled/claude-code/tabs.tsx Resolved
johnmatthewtennant and others added 2 commits November 4, 2026 15:25
A paged window that starts at a prompt was labelled "New session",
and a cut session that kept no turns let older sessions' turns through
with a gap. The notice also gets a singular form and says when the
latest turn is too large to show.

Also stops the size process on abort and finishes the readSession doc.

Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
Keeps main's sender names for prompts in the paged transcript.

Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: John Tennant <[email protected]>
@johnmatthewtennant
johnmatthewtennant merged commit 6516d06 into main Nov 4, 2026
41 checks passed
@johnmatthewtennant
johnmatthewtennant deleted the jtennant/martha/claude-large-transcripts branch November 4, 2026 20:43
gilgtm added a commit that referenced this pull request Nov 4, 2026
* origin/main:
  fix: exclude muted channels from sidebar unread scroll indicators (#2335)
  fix(claude-code): open long Claude transcripts a page at a time (#2428)
  feat(solo): share chats as view only (#2375)
  fix(inbox): show Solo responses under All, read live replies in the open conversation, describe toolbar badges (#2275)
  AIDA-2434: Show saved teams in mentions (#2339)
  AIDA-2455: Restore channel scrollbar dragging beside threads (#2436)
  feat(agent-protection): show the owner why a protection provider denied input (#2105)
  fix(search): show slow lookups beside their section headings (#2442)
  Align Bestie DM layout and unread state; speed up thinking shimmer (#2418)
  Send Claude, Pi and Goose history as exact records (#2088)
  fix(agent-activity): align the stopped note and show it after the agent's reply (#2441)
  fix(agents): stop orphaned Attention commands and prevent duplicate runs (#2448)

Signed-off-by: Gil Birman <[email protected]>
Co-authored-by: Claude <[email protected]>

# Conflicts:
#	src/app/shell/PageNavigation.tsx
johnmatthewtennant added a commit that referenced this pull request Nov 4, 2026
)

## Why

Bug fix. Opening a Codex agent's transcript can fail with "Could not
read the Codex session: Codex output frame is too large". The transcript
asked Codex for each turn in one `thread/turns/list` response with every
item (`itemsView: "full"`). One long turn can exceed `AppServer`'s 4 MiB
frame bound, and that closes the connection and fails the whole
transcript, including every turn that would have fit. This happened on a
real agent conversation: one of its 28 matching threads had a 467-item
turn that arrived as a single 4.25 MiB frame, although no item in it was
larger than 34 KB.

## What

The transcript lists turns without their items (`itemsView:
"notLoaded"`), then reads each displayed turn's items through
`thread/items/list`, 50 per request. Frames are now bounded by a page of
items instead of a whole turn. Displayed turns, item order and the 4 MiB
bound itself are unchanged.

A page of items that still exceeds the bound (50 items averaging over 80
KiB) is read again at half the size on a fresh app-server, from the same
cursor, and the rest of the transcript keeps the smaller size, so it
pays that reopen chain once rather than once per turn. Item cursors name
a position in the thread's rollout, so they work on a new process and at
any limit. Only a single item over 4 MiB still fails the transcript, as
before. Pages start at 50 items so a long conversation needs few
requests (12,018 items across the 28 threads above).

This is the Codex half of a matching pair.
[#2428](#2428) fixes the same
failure for Claude, where a session file over 50 MB could not be read,
by reading it a page at a time from its end.

## Validation

- A new regression test reads one turn of 100 commands, 4.4 MiB as a
single frame. It fails on `main` with "Codex output frame is too large"
and passes on this branch, which reads it in two pages of 50 items. A
second test reads 40 items of 200 KiB, where pages of 50 and 25 are over
the bound: it reads them again at 25, then 13, on fresh app-servers and
shows all 40.
- Against Codex 0.166.0's real app-server, the paged items for the
467-item turn were identical to its `itemsView: "full"` items. Replaying
this branch's read sequence over all 28 threads read 12,018 items in 540
requests and 1.2 s, with a largest frame of 1.33 MiB.
- On a fresh app-server process, a cursor from another process with a
smaller limit returned exactly the next items of the 467-item turn.

---------

Signed-off-by: John Tennant <[email protected]>
Co-authored-by: Damon <8a08290d889f1c68921542bfe9631d4cd671be9bea5f1114951f386819e4c10a@buzz.block.builderlab.xyz>
Co-authored-by: Martha <73923046810b68ae182000e74ef7c444383a907df20f81f2ba1008870a6f45a1@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <[email protected]>
wpfleger96 added a commit that referenced this pull request Nov 4, 2026
…-nip-fi

* origin/main:
  feat(inbox): preview recent Inbox activity when hovering the sidebar row (#2347)
  Add collapsible Solo and Team sidebar sections (#2296)
  fix(codex): open long Codex transcripts a page of items at a time (#2259)
  Keep Solo model and effort choices per chat (#2342)
  feat(sidebar): smooth channel reordering with bounded motion (#2108)
  Connect legacy agents from DMs and channel mentions (#2423)
  fix: exclude muted channels from sidebar unread scroll indicators (#2335)
  fix(claude-code): open long Claude transcripts a page at a time (#2428)
  feat(solo): share chats as view only (#2375)

# Conflicts:
#	src/app/shell/PrimarySidebar.tsx
damienrj added a commit to damienrj/buzz-app that referenced this pull request Nov 4, 2026
…-clone-auth-ab8b97

* origin/main: (38 commits)
  feat(identity): decide a missing backup choice from community membership (block#2378)
  Offer Builderlab decisions in FI builds (block#2227)
  feat(inbox): preview recent Inbox activity when hovering the sidebar row (block#2347)
  Add collapsible Solo and Team sidebar sections (block#2296)
  fix(codex): open long Codex transcripts a page of items at a time (block#2259)
  Keep Solo model and effort choices per chat (block#2342)
  feat(sidebar): smooth channel reordering with bounded motion (block#2108)
  Connect legacy agents from DMs and channel mentions (block#2423)
  fix: exclude muted channels from sidebar unread scroll indicators (block#2335)
  fix(claude-code): open long Claude transcripts a page at a time (block#2428)
  feat(solo): share chats as view only (block#2375)
  fix(inbox): show Solo responses under All, read live replies in the open conversation, describe toolbar badges (block#2275)
  AIDA-2434: Show saved teams in mentions (block#2339)
  AIDA-2455: Restore channel scrollbar dragging beside threads (block#2436)
  feat(agent-protection): show the owner why a protection provider denied input (block#2105)
  fix(search): show slow lookups beside their section headings (block#2442)
  Align Bestie DM layout and unread state; speed up thinking shimmer (block#2418)
  Send Claude, Pi and Goose history as exact records (block#2088)
  fix(agent-activity): align the stopped note and show it after the agent's reply (block#2441)
  fix(agents): stop orphaned Attention commands and prevent duplicate runs (block#2448)
  ...

Signed-off-by: damienrj <[email protected]>
johnmatthewtennant added a commit that referenced this pull request Nov 4, 2026
* origin/main: (38 commits)
  fix(browser): load scheme-less address input over https, falling back to http (#2467)
  fix(identity): stop one-click key unlinks and show the linked backed-up key (#2486)
  fix(test): observe coordination transition events (#2297)
  fix(profiles): keep the next view's profile reads through a shrink (#2395)
  fix(channels): show Add and Send message errors above the Manage members list (#2421)
  fix(threads): keep reply hooks off agent activity bubbles (#2422)
  fix(channels): offer only Copy link in a not-joined preview's sidebar actions (#2425)
  test: scope row menu placement checks to active section rows (#2493)
  feat(relay): detach channel windows to a point in history (#2042)
  feat(identity): decide a missing backup choice from community membership (#2378)
  Offer Builderlab decisions in FI builds (#2227)
  feat(inbox): preview recent Inbox activity when hovering the sidebar row (#2347)
  Add collapsible Solo and Team sidebar sections (#2296)
  fix(codex): open long Codex transcripts a page of items at a time (#2259)
  Keep Solo model and effort choices per chat (#2342)
  feat(sidebar): smooth channel reordering with bounded motion (#2108)
  Connect legacy agents from DMs and channel mentions (#2423)
  fix: exclude muted channels from sidebar unread scroll indicators (#2335)
  fix(claude-code): open long Claude transcripts a page at a time (#2428)
  feat(solo): share chats as view only (#2375)
  ...
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.

3 participants