Skip to content

feat(session): add lifecycle management — storage reclamation, CLI commands, VACUUM support#16201

Open
guazi04 wants to merge 1 commit intoanomalyco:devfrom
guazi04:feat/session-lifecycle
Open

feat(session): add lifecycle management — storage reclamation, CLI commands, VACUUM support#16201
guazi04 wants to merge 1 commit intoanomalyco:devfrom
guazi04:feat/session-lifecycle

Conversation

@guazi04
Copy link

@guazi04 guazi04 commented Mar 5, 2026

Issue for this PR

Partially addresses #16101 (MVP path: steps 1–4)

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Sessions currently have no lifecycle beyond creation. This PR adds the missing pieces from the MVP path outlined in #16101:

Storage reclamation in compactionprune() in compaction.ts previously marked time.compacted on tool parts but never cleared the heavy output and attachments fields. Now it actually clears them (set output to [reclaimed], attachments to []). A retroactive pass in process() catches parts that were marked compacted before this change but still carry stale content. Gated by a new compaction.reclaim config option (default: true). This implements the conservative reclamation level described in #16101.

CLI session management — Four new subcommands under opencode session:

  • archive <id> / unarchive <id> — surfaces the existing setArchived() API that had no CLI exposure
  • stats — shows DB/WAL file sizes, session counts (root/child/archived), message/part counts via single-query SQL aggregates
  • prune — bulk cleanup with --older-than (default 30 days), --children, --vacuum, --dry-run. Uses direct Drizzle queries with batch pagination to avoid listGlobal() ordering bias. Tracks descendants via Set<string> to prevent double-delete.

VACUUM supportDatabase.vacuum() does WAL checkpoint (TRUNCATE) + VACUUM; Database.checkpoint() does checkpoint only. Used by session prune --vacuum.

Data integrity fixremove() previously called children() which filters by project, causing cross-project child sessions to be orphaned on delete. Added childrenAll() that queries by parent_id only, and remove() now uses it for safe cascade deletion.

Not included (future work per #16101):

  • Auto-archival for inactive sessions (proposal 2)
  • Aggressive reclamation mode (proposal 1, level 2)
  • Child session retention period (proposal 3)
  • TUI integration for archive/stats (proposal 4, TUI portion)

How did you verify your code works?

  • Ran opencode session stats against a real vault with ~200 sessions — counts match manual spot-checks
  • Ran opencode session prune --dry-run --older-than 7 to verify candidate selection, then without --dry-run to confirm deletion + vacuum reclaimed ~40 MB
  • Tested archive / unarchive round-trip on multiple sessions
  • Verified compaction reclamation by inspecting part records before/after compaction — output fields correctly replaced with [reclaimed]
  • Confirmed childrenAll() finds children across projects by creating a child session with a different project context

Screenshots / recordings

N/A — CLI-only changes, no UI modifications.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…mmands, VACUUM support

- Clear tool output/attachments after compaction to reclaim storage (conservative mode, configurable via compaction.reclaim)
- Add retroactive reclamation pass for previously-pruned parts
- Expose session archive/unarchive in CLI
- Add `session stats` command showing DB size and session/message/part counts
- Add `session prune` command with --older-than, --children, --vacuum, --dry-run
- Add Database.vacuum() and Database.checkpoint() for disk space reclamation
- Fix cross-project child session cascade deletion via project-agnostic childrenAll()

Closes anomalyco#16101
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.

1 participant