Repository navigation
fix(usage): read Claude transcripts over 50 MB a page at a time - #3498
Conversation
The Usage page failed every read of an agent's Claude Code sessions when one transcript, usually a long subagent's, was larger than the app's 50 MB file read: the reader passed that error up, so the agent showed "Couldn't read the sessions" instead of its usage. Such a file is now read with the plugin's declared read-page process in 8 MiB pages and digested a page of whole lines at a time, so its usage still counts and only a page's text is held at once. A file gone before its pages are read is a gap, as for the app's read. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Riley Crane <[email protected]>
rileycrane
left a comment
There was a problem hiding this comment.
🤖 Independent accounting/failure review at 0be4861: no must-fix defect found in this scope. Incremental parsing preserves session attribution, keyed request replacement/order, unkeyed requests, titles, times, and gaps. Six synthetic probes with the actual usage module and manifest processes passed: paged/full equality for three EOF shapes with cross-page duplicate updates and multibyte text; permission/I/O failures on page two reject; deletion on page two discards partial subagent counts and reports a gap. This does not validate native desktop rendering or peak memory/resource bounds. One optional regression-coverage suggestion is inline.
rileycrane
left a comment
There was a problem hiding this comment.
🤖 Carl's adversarial resource/boundary review at 0be4861: one MUST-FIX line-bound defect is inline. Reading transcripts larger than 50 MiB is reasonable, and reusing the existing read-page process is a sound scoped approach. The shared 50 MiB cap is an application policy around whole-buffer reads, originally derived from upload policy (96360af, 52615f7); I found no hard Tauri ceiling at precisely that size in those owners/history. Raising it globally would increase allocation exposure across all plugin readers and change the documented host.readFile contract, so it is unnecessary here. The old upload-limit explanation is also stale as a universal rule: ordinary files now allow 100 MiB and the global limit is 500 MiB.
The one-page memory claim needs correcting: carryover can approach 50 MiB, with additional combined buffers, decoded strings, parsed JSON, base64 intermediates, and retained observations. A synthetic 51 MiB single-record file reached about 662 MiB maximum RSS in a fresh Node process running the actual usage module and manifest commands; the fixture was written in reused 1 MiB buffers. This is Node evidence, not a desktop/WebKit measurement, and eight readers can run together. No desktop Usage-screen or full-suite run was performed in this read-only review. Minimalness/elegance: 9/10; correctness: 8/10 until the inline guard defect is repaired.
The paged read checked the 50 MB line limit only on the bytes carried to the next page, so a line completed within a page, or left unfinished at the end of the file, was decoded and parsed at any size. Each line is now measured before it is decoded. One larger than the app's read is left unread and counted as an unreadable line, so the session reports a gap and the rest of the file still counts; a line longer than a page past the limit is dropped as it is read rather than held. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Riley Crane <[email protected]>
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 Reviewed at a8d65b6. No blocking findings.
- Paging leaves results unchanged:
seenandunkeyedcarry across pages, and the made-up keys for requests without ids stay unique becauseseen.sizeonly grows. Session start, title, times and spawned subagent names come out the same as from a whole-file read. Text is decoded only on whole-line boundaries, so a multi-byte character can't be split across pages. - The line limit holds: only the line carried in from the previous page can be over
READ_LIMIT, and that case is checked before any text is decoded. - Page boundaries: I added throwaway tests for a file that ends exactly on an 8 MiB page boundary (macOS
ddreturns an empty last page there), a dropped oversized line whose newline is the first byte of a page, and a line exactly at the limit. All three behave correctly.
…when-ready * origin/main: (21 commits) fix(agents): report a conversation wake that fails before its turn starts (#2192) fix(usage): read Claude transcripts over 50 MB a page at a time (#3498) feat(agents): preview where interrupted agents were working before resume (#3494) perf(messages): close the editor and delete dialog on submit (#3493) feat(messages): quote selected message text into the composer (#2392) perf(toast): scope docked clearance to the toast host (#3489) fix(unread): undo exact captured ordinary reads conditionally (#3473) fix(solo): keep navigation responsive during chat deletion (#3488) fix(channels): keep custom thread tab names until the tab closes (#3458) perf(relay): verify cached history in the native host (#3426) fix(settings): refine claude and typesafe key setup dialogs (#3391) Update bundled help for Bestie and agent settings (#3194) feat(profiles): share and show a local time zone (#3332) Revert "feat(link-previews): unfurl web links below messages by default" (#3480) Show disabled plugin chats in Solo (#3456) perf(messages): host one context menu per message list (#3465) test(claude): keep the quit-release test copy above the minimum version (#3477) fix(profiles): restore verified names from the device cache (#3382) fix(mentions): show addressed agents first in Solo audience picker (#2856) perf: count member access once and scope find highlights to painting (#3464) ...
…n-on-github * origin/main: (21 commits) fix(agents): report a conversation wake that fails before its turn starts (#2192) fix(usage): read Claude transcripts over 50 MB a page at a time (#3498) feat(agents): preview where interrupted agents were working before resume (#3494) perf(messages): close the editor and delete dialog on submit (#3493) feat(messages): quote selected message text into the composer (#2392) perf(toast): scope docked clearance to the toast host (#3489) fix(unread): undo exact captured ordinary reads conditionally (#3473) fix(solo): keep navigation responsive during chat deletion (#3488) fix(channels): keep custom thread tab names until the tab closes (#3458) perf(relay): verify cached history in the native host (#3426) fix(settings): refine claude and typesafe key setup dialogs (#3391) Update bundled help for Bestie and agent settings (#3194) feat(profiles): share and show a local time zone (#3332) Revert "feat(link-previews): unfurl web links below messages by default" (#3480) Show disabled plugin chats in Solo (#3456) perf(messages): host one context menu per message list (#3465) test(claude): keep the quit-release test copy above the minimum version (#3477) fix(profiles): restore verified names from the device cache (#3382) fix(mentions): show addressed agents first in Solo audience picker (#2856) perf: count member access once and scope find highlights to painting (#3464) ...
* origin/main: (31 commits) Let plugin panels name their tab with panels.useTabTitle (#3251) fix(agents): report a conversation wake that fails before its turn starts (#2192) fix(usage): read Claude transcripts over 50 MB a page at a time (#3498) feat(agents): preview where interrupted agents were working before resume (#3494) perf(messages): close the editor and delete dialog on submit (#3493) feat(messages): quote selected message text into the composer (#2392) perf(toast): scope docked clearance to the toast host (#3489) fix(unread): undo exact captured ordinary reads conditionally (#3473) fix(solo): keep navigation responsive during chat deletion (#3488) fix(channels): keep custom thread tab names until the tab closes (#3458) perf(relay): verify cached history in the native host (#3426) fix(settings): refine claude and typesafe key setup dialogs (#3391) Update bundled help for Bestie and agent settings (#3194) feat(profiles): share and show a local time zone (#3332) Revert "feat(link-previews): unfurl web links below messages by default" (#3480) Show disabled plugin chats in Solo (#3456) perf(messages): host one context menu per message list (#3465) test(claude): keep the quit-release test copy above the minimum version (#3477) fix(profiles): restore verified names from the device cache (#3382) fix(mentions): show addressed agents first in Solo audience picker (#2856) ...
…ve-client * origin/main: feat(solo): smooth chat recency reordering (#3502) Reject self-mentions that cannot wake another session (#3513) feat(status): offer don't clear for every status (#3515) Let plugin panels name their tab with panels.useTabTitle (#3251) fix(agents): report a conversation wake that fails before its turn starts (#2192) fix(usage): read Claude transcripts over 50 MB a page at a time (#3498) feat(agents): preview where interrupted agents were working before resume (#3494) perf(messages): close the editor and delete dialog on submit (#3493) feat(messages): quote selected message text into the composer (#2392) perf(toast): scope docked clearance to the toast host (#3489) fix(unread): undo exact captured ordinary reads conditionally (#3473) Signed-off-by: Philip Azar <[email protected]>
Problem
Settings → Usage shows an error for an agent instead of its usage when any one of its Claude Code transcripts is over 50 MB:
The app's native file read refuses files over 50 MB (
src-tauri/src/host_files.rs).readUsageinsrc/bundled/claude-code/usage.tspassed every read error up except "no such file". So one long subagent transcript failed the read for all of that agent's sessions. Long-running subagents pass 50 MB in practice. The one that surfaced this was 74 MB.Fix
When the app's read reports a file is over 50 MB,
readUsagereads it instead with the plugin's existingread-pageprocess (dd+base64, already in the manifest and used for large chat transcripts intranscript.ts). It reads 8 MiB pages and digests whole lines a page at a time:digestbecomes an incrementaldigester(). A whole file goes through it once, giving the same start, title, times, observation keys and order, unreadable-line count and spawned names as before.Non-goal: Codex rollouts over 50 MB (
src/bundled/codex/usage.ts) have the same limit through a different reader. That fix is left for a separate change.Tests
usage.test.tscases, run against the manifest's real processes:origin/mainwith the "larger than 50 MB" error.bin/pnpm checkpasses.bin/pnpm exec vitest run(full suite): 21,782 passed, 1 failed, at a8d65b6. The failure isbrowser-host/media-preparation.integration.test.mjs(tiled HEIC preparation). It does not reach the changed files, and it also failed on an earlierorigin/mainrun (17da600).readUsageread-only against a real~/.claude/projectsholding a 74 MB subagent transcript, with the app's 50 MB limit simulated:origin/mainit fails with the reported error.Not yet exercised in the running desktop app's Usage page.
🤖 Generated with Claude Code