The explore agent has returned a comprehensive report on the persistence layer architecture. This is exactly what the user requested - a detailed breakdown of how the project concept relates to sessions, checkpointing, worktrees, and sandboxes, with specific focus on:
1. Data structures of the persistence layer
2. How they grow with respect to the primary agent's Request context
3. How knowledge is sectioned in hierarchies for each active agent
The report is thorough and well-structured. I should present this to the user in a clear, organized manner, highlighting the key findings.
Persistence Layer Architecture Breakdown
I've completed a comprehensive exploration of the @packages/opencode/src/project/ persistence layer. Here's what I found:
🏗️ Architecture Summary
The system uses a hybrid storage model:
| Storage Type |
Technology |
Purpose |
| Relational |
SQLite (Drizzle ORM) |
Projects, Sessions, Messages, Parts, Permissions |
| File-based |
JSON files |
Session diffs, share metadata |
| Git-based |
Bare Git repos |
Checkpoints/snapshots of worktree state |
📊 Core Data Hierarchy
Project (1)
├── id: git root commit hash
├── worktree: /path/to/repo
├── sandboxes: string[] (additional directories)
├── permission: Ruleset
└── Session (N)
├── id, slug, parentID (for forks)
├── directory: working dir
├── permission?: Ruleset (override)
└── Message (N)
├── role: "user" | "assistant"
├── agent: string (which agent handled this)
├── model: { providerID, modelID }
├── tokens, cost
└── Part (N)
├── type: "text" | "tool" | "reasoning" | "step-start" | "step-finish" | "snapshot" | "patch" | "compaction" | "subtask"
└── state: pending | running | completed | error (for tools)
🔗 Key Relationships
Project ↔ Sessions
- Cardinality: 1:N with cascade delete
- Storage:
project_id FK in session table
- Location:
~/.local/share/opencode/opencode.db
Sessions ↔ Checkpoints (Snapshots)
- Mechanism: Git tree hashes stored in
step-start/step-finish parts
- Location:
~/.local/share/opencode/snapshot/{project_id}/ (bare git repo)
- Trigger: Every reasoning step boundary
- Retention: 7-day auto-prune via scheduled GC
Project ↔ Worktrees ↔ Sandboxes
Project.Info = {
worktree: string, // Primary git worktree
sandboxes: string[], // Additional isolated directories
}
- Worktree: Main git repository root
- Sandboxes: Additional directories for multi-root workspaces
- Session.directory: Determines which sandbox/worktree a session operates in
📈 Growth Model: How Storage Expands Per Request
Per User Request (one turn):
| Component |
Size Estimate |
Growth Trigger |
| User Message |
~200-500 bytes |
1 per request |
| User Parts |
~100B + content |
Text/file attachments |
| Assistant Message |
~500 bytes |
1 per response |
| Assistant Parts |
Variable |
Text, tools, reasoning |
| Snapshots |
~1-100 KB |
Per step boundary |
| Tool Parts |
~200-500B + I/O |
Per tool execution |
Compaction Strategy
Overflow Detection:
const usable = model.limit.input - reserved // reserved ≈ 20K tokens
if (tokenCount >= usable) → trigger compaction
Pruning Algorithm:
- Scan messages backwards
- Accumulate tool output token estimates
- After 40K tokens, mark old tool outputs for pruning
- If >20K found, compact them
- Render as
"[Old tool result content cleared]"
🧠 Agent Knowledge Hierarchy
Critical Finding: Agents Are Stateless
| Aspect |
Behavior |
| Per-agent persistence |
None - no dedicated storage |
| Agent tracking |
Message.agent field records which agent handled each turn |
| Knowledge sharing |
All messages visible to all agents within a session |
| Isolation |
None - any agent reads full session history |
| Permissions |
Merged: defaults → config → user overrides → session override |
Built-in Agents
| Agent |
Mode |
Purpose |
build |
primary |
Default agent (full permissions) |
plan |
primary |
Planning mode (deny edits except plan files) |
general |
subagent |
Complex task delegation |
explore |
subagent |
Codebase exploration |
compaction |
hidden |
Session summarization |
title |
hidden |
Title generation |
summary |
hidden |
Summarization |
💾 Storage Locations
~/.local/share/opencode/
├── opencode.db # SQLite database (all tables)
├── storage/
│ ├── session_diff/{id}.json # Session-level diffs
│ └── share/{id}.json # Share metadata
└── snapshot/{project_id}/ # Bare git repos per project
└── (git objects for checkpoints)
⚠️ Open Questions & Potential Issues
- Snapshot Orphaning: No explicit cleanup when sessions deleted; relies on git gc
- Sandbox Usage: Unclear when sessions use sandboxes vs. primary worktree
- Fork Divergence: Forked sessions reference parent snapshots; unclear how divergence is tracked
- Compaction Races: What happens if user sends message during compaction?
- Permission Inheritance: When does session permission override project default?
📁 Key Files for Deep Dives
| Concern |
Primary Files |
| Project lifecycle |
src/project/project.ts, src/project/instance.ts |
| Session management |
src/session/index.ts, src/session/processor.ts |
| Message/Part types |
src/session/message-v2.ts |
| Compaction logic |
src/session/compaction.ts |
| Snapshots |
src/snapshot/index.ts |
| Schema |
src/storage/schema.sql.ts |
Would you like me to dive deeper into any specific area? For example:
- Trace the exact flow of a request through the persistence layer
- Analyze the compaction algorithm in detail
- Examine how snapshots are created and restored
- Review the permission merging logic