Model: openai/gpt-5.4 Generated: 2026-04-01 Book: Claude Code VS OpenCode: Architecture, Design and The Road Ahead Chapter: 5 — Session and Context Management Token Usage: Approx. 1,450 output tokens for this section
5.1 Session Persistence
Session persistence is the hidden spinal cord of a coding agent. Without it, an agent is just a short-lived chatbot with temporary memory. With it, the agent becomes a continuing software worker: it can survive long tasks, resume after interruption, preserve audit trails, and support higher-level behaviors such as compaction, recovery, forked sessions, and background delegation. OpenCode, Claude Code, and Oh-My-OpenCode (OMO) all solve this problem, but they do so with notably different storage philosophies.
OpenCode: relational persistence on SQLite + Drizzle ORM
OpenCode uses a relational storage model. Its session data is stored in SQLite, with Drizzle ORM defining the schema and managing access. The core schema is exposed through storage/schema.ts, which re-exports tables such as SessionTable, MessageTable, PartTable, TodoTable, and PermissionTable from session/session.sql.ts. The database bootstrapping logic in storage/db.ts shows a Bun SQLite database initialized with Drizzle and then migrated through SQL migration files.
This choice matters because OpenCode treats sessions as structured entities, not just logs. A session row contains an id, slug, project_id, directory, title, version, timestamps, optional summary statistics, revert metadata, permission state, and archival state such as time_archived. In other words, OpenCode models a session almost like a first-class business object. The message history is normalized into separate message and part tables, which means the system can query, update, or clean up individual layers of the conversation instead of rewriting one giant blob.
This relational model gives OpenCode several advantages. First, it is queryable. You can ask for all sessions in a project, all messages in a session, all parts in a message, or all todos bound to a session. Second, it supports schema evolution in a disciplined way. storage/db.ts explicitly loads and applies migrations, which means new features can be added without abandoning existing data. Third, it supports richer workflows such as revert, archival, diff summaries, and structured session metadata because those are easy to represent in columns and related tables.
There is also an architectural implication: OpenCode assumes that the session store is part of the application’s internal control plane. Persistence is not merely for debugging; it is an active substrate for the agent runtime.
Claude Code: JSONL transcripts as append-only logs
Claude Code takes a very different approach. Instead of a relational database, it persists sessions as JSONL files under ~/.claude/sessions/-style project storage, with the concrete path logic handled by src/utils/sessionStorage.ts. The active transcript path is computed dynamically, and each session is written as a .jsonl file. A JSONL file, short for JSON Lines, is a simple line-oriented format where each line is one independent JSON object. It is widely used in engineering systems, but it is not one of the classic named formats usually emphasized in CS textbooks like CSV, XML, or normalized SQL tables. Conceptually, JSONL is a stream-friendly logging format rather than a formally standardized database model.
Claude Code’s persisted unit is the TranscriptMessage. In src/types/logs.ts, a transcript message is essentially a serialized message plus metadata such as parentUuid, isSidechain, sessionId, timestamp, optional agent information, and other resume-related fields. The session store also records extra entry types such as summaries, task summaries, worktree state, PR links, and content replacement records. This makes the transcript file a hybrid of conversation log and event log.
The key design principle is append-only persistence. As the conversation evolves, Claude Code can stream new entries into the JSONL file incrementally. That makes writes simple and operationally robust. There is no need to manage multi-table transactions for ordinary transcript growth. The format is also easy to inspect with generic tooling, easy to move between machines, and naturally aligned with replay-oriented recovery.
Just as important is what Claude Code deliberately excludes. sessionStorage.ts explicitly treats progress messages as ephemeral UI state rather than transcript truth. Comments in that file explain that progress entries must not participate in the parent chain, because doing so can fork the conversation graph and break resume behavior. This is a subtle but important distinction: Claude Code separates persistent semantic history from transient runtime chatter.
OMO: OpenCode persistence plus continuation overlays
OMO inherits OpenCode’s core persistence architecture because it is built on top of OpenCode rather than replacing the host runtime. That means its baseline conversation storage remains SQLite plus Drizzle-managed session/message/part state. However, OMO extends the persistence story with continuation-specific features.
The claude-code-session-state feature keeps track of relationships such as main session ID, subagent session membership, and session-to-agent mappings. This is small in code, but conceptually important: OMO needs to know not only that a session exists, but also whether that session belongs to the main thread or to a delegated specialist.
The run-continuation-state feature adds file-based continuation markers. Its storage module writes small JSON files keyed by session ID and records which continuation source is active, when it was updated, and why. OMO also introduces boulder-state tracking for plan continuity. In features/boulder-state/storage.ts, it persists plan identity, start time, associated session IDs, and progress-related information in a .sisyphus state file. This is not just conversation persistence; it is workflow persistence.
So OMO’s persistence model is layered:
- OpenCode relational storage for canonical sessions and messages.
- Auxiliary continuation state for orchestration-specific recovery.
- Plan and agent lineage state for long-running autonomous workflows.
This layering reflects OMO’s broader philosophy: a session is not merely a chat transcript but an execution thread in a multi-agent system.
Relational vs log-based persistence: the real tradeoff
The contrast between OpenCode and Claude Code is not “database good, files bad.” It is a deeper design tradeoff between two persistence metaphors.
The relational model gives structured queries, referential integrity, explicit migrations, and easy support for features like summaries, permissions, reverts, todos, and archival flags. It fits systems that treat sessions as entities with internal structure. The cost is complexity. A relational store requires schema design, migration discipline, and more careful update logic.
The log-based model gives streaming writes, append-only simplicity, human-inspectable artifacts, and natural replay semantics. It fits systems that think in terms of event history and incremental transcript growth. The cost is that queries become harder, compaction and cleanup may require bespoke parsing logic, and “state” often has to be reconstructed by replaying entries or scanning the file.
OpenCode leans toward stateful structure. Claude Code leans toward durable event history. OMO shows a third pattern: hybrid layering, where a structured core is augmented with extra lightweight files for orchestration continuity.
Design lesson
For agent designers, the lesson is clear. If your agent needs search, analytics, revert, permissions, per-part editing, and deep orchestration, a relational substrate is powerful. If your highest priorities are streaming durability, operational simplicity, and portable replay, JSONL-style append-only transcripts are extremely attractive. In practice, the strongest future systems may mix both: an append-only event log for raw truth, plus indexed relational or materialized views for fast control-plane operations.
Session persistence is therefore not a storage afterthought. It encodes how an agent thinks about memory, identity, and time.