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
Edit tool persists full-file before/after snapshots per call; unbounded part-table growth makes large sessions slow at every prompt #40816
The edit tool stores the entire file content twice (before + after) in each tool part's metadata.filediff, and the session prompt pipeline hydrates every part of the session on every prompt (stream() pages through all messages + hydrate() loads all parts). On long-lived sessions this compounds into hundreds of MB of DB data and a multi-second, memory-spiking load per prompt.
Evidence — two real sessions (Windows, opencode 1.18.13)
Session
Messages
Parts
Tool parts
edit parts
edit bytes
Session DB weight
A (35-day session)
7,741
31,253
6,911
1,383
194 MB
225.8 MB
B (25-day session)
8,259
32,041
7,552
1,577
171.8 MB
208.2 MB
Single edit part anatomy (largest example):
filediff: { file, before: 214,358 chars, after: 214,698 chars, additions, deletions }
total part JSON: 513,383 bytes ← for ONE small edit to a ~210 KB file
The full-file snapshot is stored with no patch string — the compact unified diff exists in metadata.diff (a few KB) but the two full copies are persisted regardless.
Per-prompt load:packages/opencode/src/session/message-v2.ts — stream() (line 469) pages ALL messages (50/page loop, ~155 queries for session A) and hydrate() (line 98) does inArray(PartTable.message_id, ids) over all parts, then filterCompacted() converts everything to model messages. This happens before any plugin messages.transform compression. Observed: ~1.9k CPU-seconds + ~1.2 GB RSS on a session with 7.7k messages while idle-ish; spike visible during prompt assembly.
Why it didn't used to be an issue
Growth curve: a few hundred messages hydrate in ms. At 8k messages × 32k parts (with ~190 MB of edit snapshots), the same load takes seconds and allocates hundreds of MB transiently. Nothing "breaks" — it degrades linearly with session size, and edit-heavy sessions hit the pain point first.
Suggested fix directions
packages/opencode/src/tool/edit.ts (~line 181): cap or omit the full-file before/after in metadata.filediff for large files (e.g. store only the unified diff/patch, or truncate snapshots beyond a size threshold). The compact diff already carries the change; patch is what Snapshot.FileDiff's schema (packages/schema/src/file-diff.ts) actually models as required-ish.
packages/opencode/src/session/message-v2.tsstream()/hydrate(): bound the prompt-build load (e.g. hydrate only messages within the context budget, or skip hydrating tool-part metadata for old messages). Compaction already exists but only trims the model-visible tail — stream() still reads the whole history.
Consider a storage hygiene pass: old tool parts' metadata.filediff could be dropped by compaction (compaction.prune marks but doesn't reclaim).
Repro
Long edit-heavy session (thousands of messages), resume it, prompt once; watch RSS + CPU during prompt assembly.
opencode.dbpart table: SELECT json_extract(data,'$.type') t, COUNT(*), SUM(LENGTH(data)) FROM part GROUP BY t — tool dwarfs everything; edit-typed parts dominate.
Note: this was diagnosed on 1.18.13; the same edit.ts filediff producer and message-v2.ts hydrate path are present on dev.
This issue might be a duplicate of existing issues. Please check:
perf(core): avoid hydrating discarded compacted session parts #35570: Reports the same stream()/hydrate() full-session hydration cost in message-v2.ts, with near-identical profiling data (197 MB of part JSON, RSS spikes, 3 963 messages → 134 after compaction). That issue focuses on avoiding hydration of discarded compacted parts; this issue adds the complementary root cause that edit tool filediff metadata is what inflates those parts to begin with.
Memory Megathread #20695: Memory Megathread — the general tracking issue for memory/performance problems across sessions.
If your issue is specifically about the edit tool storing full before/after snapshots (the storage side), it may be worth noting there as a distinct root cause. If it is primarily about the hydration overhead (the load side), #35570 covers that path closely.
Adding concrete numbers from a local context-bloat investigation — this is the top cause of context-window compaction on this host.
Evidence (mined from the local session DB):
Single edit tool part reached 16.4 MB (editing a ~7.8 MB runtime-bundle file) because the result carries full state.metadata.filediff.before + .after (~2x file size).
Lifetime edit-tool result content across all sessions: 284.6 MB — the largest per-tool contributor (~34% of ~843 MB stored tool content). Token cost is higher than byte cost since the same content is retokenized every turn.
Worst session hit 18 compactions (76 parts/compaction — context filling almost every turn); a ~120 KB config doc edited 30+ times in one session re-injected ~230 KB of before+after each time.
Why it's disproportionately bad: unlike other large outputs (bash/read), an edit's before+after is largely identical to content already in context (the caller just supplied old+new strings), so it's near-pure redundancy that accelerates compaction without adding information.
Suggested fix (confirming the original ask): stop persisting full filediff.before/.after into the model-facing message; keep only the diff field (already present) or cap snapshots to changed lines ±N. Store full before/after server-side for undo/revert only.
Workaround meanwhile implemented on our side: a pre-edit size guard (edit_guard.py) that blocks editing files > 100 KB and warns > 50 KB, plus a context_bloat_audit.py that flags edit-echo + compaction storms on demand.
Activity
github-actions commentedon Aug 6, 2026
This issue might be a duplicate of existing issues. Please check:
stream()/hydrate()full-session hydration cost inmessage-v2.ts, with near-identical profiling data (197 MB of part JSON, RSS spikes, 3 963 messages → 134 after compaction). That issue focuses on avoiding hydration of discarded compacted parts; this issue adds the complementary root cause thatedittoolfilediffmetadata is what inflates those parts to begin with.If your issue is specifically about the
edittool storing fullbefore/aftersnapshots (the storage side), it may be worth noting there as a distinct root cause. If it is primarily about the hydration overhead (the load side), #35570 covers that path closely.suppg02-sudo commentedon Aug 10, 2026
Adding concrete numbers from a local context-bloat investigation — this is the top cause of context-window compaction on this host.
Evidence (mined from the local session DB):
state.metadata.filediff.before+.after(~2x file size).Why it's disproportionately bad: unlike other large outputs (bash/read), an edit's before+after is largely identical to content already in context (the caller just supplied old+new strings), so it's near-pure redundancy that accelerates compaction without adding information.
Suggested fix (confirming the original ask): stop persisting full filediff.before/.after into the model-facing message; keep only the diff field (already present) or cap snapshots to changed lines ±N. Store full before/after server-side for undo/revert only.
Workaround meanwhile implemented on our side: a pre-edit size guard (edit_guard.py) that blocks editing files > 100 KB and warns > 50 KB, plus a context_bloat_audit.py that flags edit-echo + compaction storms on demand.