Repository navigation
[FEATURE]: Session Lifecycle Management — unified storage reclamation, auto-archival, and child session cleanup #16101
Description
Feature hasn't been suggested before.
- I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Problem
OpenCode sessions grow unboundedly. There is no TTL, no auto-cleanup, no storage cap, and no post-compaction reclamation. Over time this leads to significant disk usage that users have reported across multiple issues.
Real-world data from 3 weeks of active use:
| Metric | Value |
|---|---|
Database size (opencode.db) |
774 MB |
| Sessions | 1,812 (248 root, 1,564 child) |
| Messages | 50,818 |
| Parts (PartTable rows) | 176,596 (626 MB = 81% of DB) |
| Archived sessions | 0 (no auto-archival exists) |
| Pruned parts still retaining full output | 2,689 (14.15 MB wasted) |
86% of all sessions are child sessions (from task() calls), and they hold 75% of total part storage.
Scope: DB growth vs. filesystem artifacts
This issue focuses on SQLite database growth (opencode.db + WAL/SHM). Filesystem-level storage leaks like tmp_pack_* file accumulation (#8749, #14811) are a separate problem with different root causes (snapshot system). Both contribute to disk pressure, but they require different solutions.
Current State
OpenCode has some building blocks, but they don't form a complete lifecycle:
| Capability | Exists? | Gap |
|---|---|---|
| Prune (mark old tool outputs) | ✅ compaction.ts:59-99 |
Sets time.compacted on ToolStateCompleted parts but never clears output or attachments — data stays in DB |
| Compaction (LLM summarization) | ✅ | Summary is tiny (~0.07 MB for 166 compactions) but pre-compaction parts are never reclaimed |
| Archive (mark session archived) | ✅ Session.setArchived() + REST API |
Not surfaced in TUI or CLI — users can't archive sessions without hitting the API directly |
| Remove (delete session + cascade children) | ✅ Session.remove() |
Manual only, no batch/auto cleanup |
| Auto-cleanup / TTL | ❌ | Nothing exists |
| Child session lifecycle | ❌ | No independent cleanup; only cascade-deleted with parent |
| Storage statistics / management UI | ❌ | No way to see DB size or per-session storage usage |
| SQLite VACUUM | ❌ | Never called — deleted/cleared data doesn't free disk space (passive WAL checkpoint exists at startup but only flushes, does not truncate WAL or reclaim row space) |
Related Issues
This problem has surfaced across many issues, each addressing a piece:
- [FEATURE]: Subagent sessions accumulate without cleanup option - causes storage bloat and UI clutter #5734 — Subagent sessions accumulate without cleanup option — storage bloat
- Does OpenCode automatically clean up old session/conversation data? #4980 — Does OpenCode automatically clean up old session data?
- [FEATURE]: implement smart pruning #7459 — Implement smart pruning
- [FEATURE]:Session compaction includes already-summarized messages, wasting tokens #12479 — Compaction includes already-summarized messages, wasting tokens
- [FEATURE]: "Compact above" - Partial Compaction #9896 — "Compact above" (partial compaction)
- If disk space runs out your active session becomes corrupted #7607 — Disk space runs out → session corrupted
More related issues and PRs
Storage growth (filesystem-level, separate root cause):
- [BUG] tmp_pack Files Consuming 180GB on Ubuntu 24.04 LTS #8749 — tmp_pack files consuming 180GB
- Snapshot system leaks tmp_pack_* files indefinitely, filling disk at ~9.5 GB/hour #14811 — Snapshot system leaks tmp_pack_* files at ~9.5 GB/hour
- [BUG/PERF] Severe Memory Leak and Disk Swell leading to System Kernel Panic (macOS) #12687 — Memory leak and disk swell → kernel panic
Related open PRs:
- feat(cli): add session prune command for cleanup #12631 —
feat(cli): add session prune command - feat(opencode): prune old tool outputs before compaction #14827 —
feat: prune old tool outputs before compaction - fix: clear tool output and attachments when pruning to prevent memory leak #7049 —
fix: clear tool output and attachments when pruning - feat(session): add storage interface for pluggable session backends #15922 —
feat: pluggable session storage backends - fix(memory): add TTL, LRU eviction, and ring buffers to prevent memory leaks #12259 —
fix: add TTL, LRU eviction, ring buffers
None of these take a holistic view. This issue aims to unify them under a coherent session lifecycle management design.
Proposal
1. Post-Compaction Storage Reclamation
After compaction produces a summary, the original parts before the compaction point have served their purpose — the summary is usually sufficient for the model to continue.
The reclamation target is ToolStateCompleted parts — specifically their output (string) and attachments (FilePart[]) fields, which make up the vast majority of storage. Part rows and metadata (tool name, input args, timestamps) would be preserved.
Two levels of reclamation:
Conservative: Clear output and attachments from tool parts that already have time.compacted set. This is a natural extension of the current prune logic — prune already identifies these parts as disposable and the rendering layer already checks time.compacted (see message-v2.ts:637-638), it just doesn't follow through on clearing the stored content.
Aggressive: After compaction completes, clear output and attachments from all ToolStateCompleted parts before the compaction point — not just the pruned ones. The compaction summary usually captures the full context needed to continue. This approach is proven in practice by oh-my-opencode's compaction strategy, which produces comprehensive summaries including child session records, task state, and key discoveries — and the model continues working effectively.
Whether to apply conservative or aggressive reclamation could be a user-configurable option, extending the existing config.compaction namespace (which already has auto and prune booleans).
In my database, parts account for 626 MB (81% of 774 MB). The compaction summaries total just 0.07 MB. The potential savings are orders of magnitude.
2. Auto-Archival for Inactive Sessions
Sessions that haven't been active for N days could be auto-archived. The time_archived column already exists in the schema but is never set automatically.
This would require adding a new configuration key (e.g., in the config.compaction or a new config.session namespace) since no session lifecycle config exists today. The auto-archive check could run at startup or on a periodic interval.
3. Child Session Lifecycle
Child sessions (86% of all sessions in my database) are created by task() and hold 75% of storage. Once a child session completes and returns its result to the parent, its detailed parts are rarely needed again.
Note: the parent session already captures the child's result in its own tool part output. The child's SubtaskPart only holds input metadata (prompt, description, agent) — the actual result lives in the parent's tool output. This means the child's full part history is largely redundant once the parent acknowledges the result.
Suggestion: Completed child sessions could have their parts reclaimed after a configurable retention period (e.g., 24h or N days). This preserves the ability to inspect recent child sessions for debugging while reclaiming storage from older ones. Immediate deletion is too aggressive — users may want to review child session details shortly after completion.
4. Session Management Tooling
Users currently have no visibility into storage usage and limited management options:
archive— supported viaSession.setArchived()+ REST API, but not surfaced in CLI or TUI — should be exposed asopencode session archiveand a TUI action- No storage statistics — a command like
opencode session statscould show DB size, session count, largest sessions - No batch cleanup —
opencode session prune(see PR feat(cli): add session prune command for cleanup #12631) for removing old/archived sessions and reclaiming part storage - No VACUUM — SQLite does not automatically reclaim disk space from deleted rows. After batch cleanup, running
VACUUMwould reclaim disk space. Optionally,PRAGMA wal_checkpoint(TRUNCATE)could also be offered to shrink a large WAL file when the DB is idle. (Currently OpenCode only runs a passive checkpoint at startup viadb.ts:83, which flushes WAL contents but does not truncate the file.)
Non-goals / Invariants
- Preserve recent transcript UX — reclamation should not affect sessions the user is actively viewing or recently used. A retention window ensures users can still scroll back and inspect recent tool outputs.
- Preserve
task_idresume capability — child session data should be retained long enough fortask_id-based resume to work. The retention period in Proposal feat: model config persistence #3 addresses this. - Don't VACUUM during active sessions — VACUUM requires exclusive access. It should only be triggered by explicit user action or at shutdown.
- Reclamation is opt-in or gradual — start with the conservative approach; let users opt into aggressive reclamation.
MVP Path
A practical incremental path:
- Land existing PRs — fix: clear tool output and attachments when pruning to prevent memory leak #7049 and feat(opencode): prune old tool outputs before compaction #14827 already clear tool output on prune. Getting these merged would immediately address the "prune marks but doesn't clear" gap.
- Expose archive in TUI/CLI — small surface area change, high user impact.
- Add
opencode session stats— give users visibility into storage before asking them to manage it. - Add checkpoint/VACUUM to cleanup flow — ensure deleted data actually frees disk space.
- Then iterate — auto-archival, child session lifecycle, and aggressive reclamation can build on this foundation.
Background
I discovered this problem while building a plugin that reads session history across projects for weekly work summaries. The 774 MB database (3 weeks, 10 projects) made it clear that session storage needs active lifecycle management. The plugin use case also highlighted that Session.listGlobal() is only accessible via an experimental API route — cross-project session access could benefit from first-class support.
Summary
The core ask is: sessions should have a lifecycle, not just a birth. The building blocks exist (prune, archive, remove, compaction) but they aren't connected into a coherent lifecycle. Adding post-compaction reclamation, auto-archival, child session cleanup, and management tooling would address the storage growth problem systematically rather than piecemeal.
Activity
[-][DESIGN]: Session Lifecycle Management — unified storage reclamation, auto-archival, and child session cleanup[/-][+][FEATURE]: Session Lifecycle Management — unified storage reclamation, auto-archival, and child session cleanup[/+]github-actions commentedon Mar 5, 2026
Thanks for updating the issue! It now meets our contributing guidelines. The
needs:compliancelabel has been removed.fix: clear tool output and attachments during compaction prune
feat(session): add lifecycle management — storage reclamation, CLI co…
feat(session): add lifecycle management — storage reclamation, CLI co…
feat(session): add lifecycle management — storage reclamation, CLI co…
blain3white commentedon Jun 9, 2026
This maps closely to what I'm building in Clean My Agent — local-first lifecycle management for OpenCode sessions including archival, safe deletion, and export. Posting in case it's useful as a reference while the native feature is designed.
gigimento commentedon Jun 17, 2026
Same issue here — my opencode.db grew to 1.25 GB causing OOM crash in the Desktop (Electron) app. The part table alone was 152 MB with base64-encoded attachments (PDFs/images) from long sessions. Had to manually delete sessions and VACUUM to reclaim ~1 GB. An auto-retention policy with configurable maxSizeMB / maxAgeDays would have prevented this entirely. Also, session delete should VACUUM automatically.
claell commentedon Jul 25, 2026
Strong +1 on the storage-reclamation and stats parts of this. On the TTL / auto-cleanup part, I'd ask that it ship opt-in and off by default.
Reclaiming pruned tool outputs, VACUUM, and clearing compacted part payloads are all lossless — they solve most of the 774 MB in your table without touching anything a user might want to read again. Time-based deletion of whole sessions is a different category and should be treated as such.
The cautionary case is Claude Code, which defaults to deleting session transcripts after 30 days with no prompt and no recovery: anthropics/claude-code#59248 (labelled
data-loss) and anthropics/claude-code#62476. Many users treat session history as durable working knowledge and have no idea a sweep exists until it has already run.Suggested:
retention_daysunset by default;opencode session prune --older-than 30d --dry-runas the primary interface; and if auto-prune is ever enabled, surface it explicitly on first run.steve02081504 commentedon Sep 18, 2026
Concrete requirement from a daily user (Windows), adding to the TTL / auto-cleanup part of this issue:
Auto-delete sessions idle for longer than N days.
Trigger should be based on inactivity (
time.updated) rather thantime.created— a session that is still being resumed must not be swept.Ships opt-in and off by default (agreeing with @claell above). Recommended config shape, under a new session lifecycle namespace or
config.compaction:{ "session": { "retention_days": 30, // unset/0 = disabled (default: disabled) "retention_mode": "archive" // "archive" | "delete" } }Check can run on startup and/or on a periodic interval, in the background, with no interactive prompt.
Deletion should cascade to child sessions (as
Session.remove()already does) and runVACUUMafterwards, otherwise disk space is not actually reclaimed.A non-destructive CLI surface would help too, e.g.
opencode session prune --older-than 30d --dry-run.Current workaround is a manual loop over
opencode session list --format json(which exposesupdated/createdin epoch ms) +opencode session delete <id>, but there is no batch/auto equivalent.opencode session listalso has no age filter, so a script must parse all sessions itself.Dante-dan commentedon Sep 28, 2026
I have been following this issue for a while, and I have a concrete need for opt-in cleanup. I also saw the recent session prune PRs #51254 and #51255, so I do not want to duplicate their manual CLI deletion flow.
For a focused follow-up, could the core team review this approach before implementation?
This is the specific behavior I need in my own use. I am happy to prepare a PR against this issue once the core team has reviewed the design. Would you approve this scope, or suggest changes before implementation?
steve02081504 commentedon Sep 28, 2026
Update on my earlier requirement: I ended up implementing this in fount's code shell instead — activity-based retention with configurable archive/delete, cascade deletion, and VACUUM after cleanup — so I no longer need to watch this issue. Still rooting for a native version here for everyone else.
AlexeyShirshov commentedon Sep 30, 2026
I'd like to contribute an archive action to the existing TUI session list, without replacing the dialog.
Archive browsing and restoration would be outside this patch.
Would this scope be acceptable?
s3ntin3l8 commentedon Oct 17, 2026
Another data point from a long-running host (opencode 2.0.29), in case it helps prioritise:
opencode.dbis 17 GB, butPRAGMA freelist_countxpage_sizeshows ~84% of the pages (~15 GB) are on the freelist; live data is only ~2.7 GB.auto_vacuumis 0 andjournal_modeis wal, so the file never shrinks on its own and the space is only recoverable with a manualVACUUM.partrows and ~126ksession_messagerows.session/message/parttables are still populated (~1.5 GB) even though every v1 session id also exists insession_v2.opencode session delete <id>is per session, which is impractical at this scale.Something like an
auto_vacuum=INCREMENTALsetting (or a periodic vacuum when idle), an age-based archive/delete command, and dropping the v1 tables after migration would each address a part of this.