opencode 1.18.30, macOS (Apple Silicon, 36 GB). Bun runtime.
With snapshot tracking on, each user message stores summary.diffs[].patch: the full
unified diff of every working-tree change since the previous turn. In a large working
tree the patches get enormous. One session of 68 messages holds 390 MB of message JSON;
a single message is 330 MB (24,444 file diffs: other agents sharing the same working
directory were unpacking archives during that turn). Across my store, 48 messages hold
2.2 GB (33 sessions); the part table for the same sessions is ~7 MB.
Effect: resuming a session (opencode --session <id>) parses all of it. Measured with
vmmap: 53 MB at start, 5.7 GB resident at 61 s, peak footprint 5.0 GB, settling to ~0.7 GB
after GC while idle. Sending one prompt in that session took the process to 8.2 GB.
With 13 restored sessions resuming together the machine jetsammed (44 GB across opencode
processes).
Related: the event table grows without bound (30.6 GB, 83k rows; message.updated and
message.part.updated copy the full message/part JSON per update), and a 6 h session
wrote 550 GB via WAL checkpoints.
Workarounds that hold: "snapshot": false in config stops new patches; deleting
summary.diffs from the oversized rows (json_remove) and VACUUM took the store from
2.4 GB to 220 MB with the session still exporting intact; the same session then peaks
at 750 MB instead of 5.0 GB.
Suggestions: cap or truncate stored patches (or store diffs out of the message row and
load them lazily), and bound the event table (compact or drop replayed events).
Code paths (v1.18.30 tag; same at HEAD ebb7b76)
packages/opencode/src/session/summary.ts summarize(): loads every message of the
session (sessions.messages, line 116) on each turn, computes snapshot.diffFull(from, to)
(line 98) and writes the result into the user message (target.info.summary = {..., diffs},
line 125) via sessions.updateMessage. Nothing bounds the number of files or the total size.
packages/opencode/src/snapshot/index.ts diffFull: git show of the before and after
content for every added/deleted/modified file between the two snapshot hashes. The only
guard is the per-file limit = 2 * 1024 * 1024 (line 24) at tracking time, so a turn that
touches thousands of files stores thousands of full-content patches.
packages/opencode/src/session/session.ts updateMessage (line 631): publishes
SessionV1.Event.MessageUpdated with the whole info object, diffs included, so the event
store persists a full copy of the payload on every update.
packages/opencode/src/cli/cmd/export.ts (lines 173, 207) is the only reader of
summary.diffs besides the UI "changes" chip; the model request never uses it, which is
why lazy-loading it out of the message row is safe.
opencode 1.18.30, macOS (Apple Silicon, 36 GB). Bun runtime.
With snapshot tracking on, each user message stores summary.diffs[].patch: the full
unified diff of every working-tree change since the previous turn. In a large working
tree the patches get enormous. One session of 68 messages holds 390 MB of message JSON;
a single message is 330 MB (24,444 file diffs: other agents sharing the same working
directory were unpacking archives during that turn). Across my store, 48 messages hold
2.2 GB (33 sessions); the part table for the same sessions is ~7 MB.
Effect: resuming a session (
opencode --session <id>) parses all of it. Measured withvmmap: 53 MB at start, 5.7 GB resident at 61 s, peak footprint 5.0 GB, settling to ~0.7 GB
after GC while idle. Sending one prompt in that session took the process to 8.2 GB.
With 13 restored sessions resuming together the machine jetsammed (44 GB across opencode
processes).
Related: the event table grows without bound (30.6 GB, 83k rows; message.updated and
message.part.updated copy the full message/part JSON per update), and a 6 h session
wrote 550 GB via WAL checkpoints.
Workarounds that hold: "snapshot": false in config stops new patches; deleting
summary.diffs from the oversized rows (json_remove) and VACUUM took the store from
2.4 GB to 220 MB with the session still exporting intact; the same session then peaks
at 750 MB instead of 5.0 GB.
Suggestions: cap or truncate stored patches (or store diffs out of the message row and
load them lazily), and bound the event table (compact or drop replayed events).
Code paths (v1.18.30 tag; same at HEAD ebb7b76)
packages/opencode/src/session/summary.tssummarize(): loads every message of thesession (
sessions.messages, line 116) on each turn, computessnapshot.diffFull(from, to)(line 98) and writes the result into the user message (
target.info.summary = {..., diffs},line 125) via
sessions.updateMessage. Nothing bounds the number of files or the total size.packages/opencode/src/snapshot/index.tsdiffFull:git showof the before and aftercontent for every added/deleted/modified file between the two snapshot hashes. The only
guard is the per-file
limit = 2 * 1024 * 1024(line 24) at tracking time, so a turn thattouches thousands of files stores thousands of full-content patches.
packages/opencode/src/session/session.tsupdateMessage(line 631): publishesSessionV1.Event.MessageUpdatedwith the wholeinfoobject, diffs included, so the eventstore persists a full copy of the payload on every update.
packages/opencode/src/cli/cmd/export.ts(lines 173, 207) is the only reader ofsummary.diffsbesides the UI "changes" chip; the model request never uses it, which iswhy lazy-loading it out of the message row is safe.