zeph-session
Conversation-session persistence for Zeph: an append-only JSONL
event log, deterministic replay, and fork engine, shared by every channel (CLI, TUI, Telegram, ACP,
zeph serve).
[!NOTE] Implements spec-068 (issue #5343): the
SessionEventschema, the JSONL event log with torn-append recovery, theacp_sessionsmetadata store, the deterministic replay engine, theCondensertrait contract and its defaultLlmCondenser, and the eager-copyForkEngine. It is consumed byzeph-core(agent-loopSessionSinkwiring,zeph serveper-session actors,/convcommands) andzeph-acp(session load/list/fork/resume handlers).
Overview
Every conversation-session's history is appended as one line per event to
<data_dir>/<session_id>/events.jsonl — the source of truth. The existing acp_sessions
table (promoted from ACP-only to channel-agnostic, per spec-068 Decision D1) tracks lightweight
queryable metadata (last_seq, status, fork provenance) so sessions list and reconciliation on
open don't require replaying every log.
Replay never calls the LLM or a tool executor: it folds previously recorded events into
agent-ready Messages, which is the correctness guarantee behind byte-identical resume and fork.
Architectural placement
zeph-session mirrors the append-only journal design of zeph-durable (sequential ordering,
single-writer actor model) but is a separate crate — the two record different concerns
(task/step effect-idempotency vs. conversation semantics) at different abstraction levels and use
different storage formats. zeph-session does not depend on zeph-durable, and vice versa.
See specs/068-session-persistence/spec.md and plan.md for the full design and phased rollout.
Module map
| Module | Description |
|---|---|
event |
SessionEvent tagged enum and its SessionEventEnvelope on-disk wrapper |
log |
SessionEventLog — append-only JSONL writer/reader with torn-append truncation (INV-SP-2) |
store |
SessionStore — CRUD over the acp_sessions metadata index |
replay |
ReplayEngine — deterministic fold of an event log into agent-ready messages; never calls the LLM |
condenser |
Condenser trait contract and the non-overlap guard (INV-SP-4) |
llm_condenser |
LlmCondenser — default Condenser, reusing zeph_context::summarization |
fork |
ForkEngine — eager-copy session forking; blobs referenced by UserMessage.image_refs are hard-linked into the child session's blobs directory (falling back to a copy), with referenced hashes validated as bare hex to prevent path traversal |
error |
SessionError — crate-wide error enum |
Usage
The on-disk layout for one session is derived from the data directory and session id:
use Path;
let dir = session_dir;
assert_eq!;
migrate_legacy_session_layout is a one-time startup migration that moves session directories still
sitting at the pre-fix on-disk layout (<data_dir>/sessions/<session_id>/) up one level to the
current layout, returning a MigrationReport (counts migrated vs. skipped-because-destination-exists).
Features
Exactly one storage backend must be selected for the acp_sessions metadata index; sqlite is the default.
| Feature | Default | Description |
|---|---|---|
sqlite |
yes | SQLite backend for zeph-db |
postgres |
no | PostgreSQL backend for zeph-db |
test-utils |
no | Enables testcontainers-based PostgreSQL integration test utilities (implies postgres) |
Installation
License
Licensed under either of MIT or Apache License, Version 2.0 at your option.