fix: collapse session fork to one api

This commit is contained in:
Hypatia May
2026-07-06 13:57:59 +08:00
parent 37f3aedc0b
commit 37aac7f313
10 changed files with 284 additions and 177 deletions

View File

@@ -9,8 +9,7 @@ Creates and holds event-sourced `Session` instances. Persistence is intentionall
### Public API
- `ctx.sessions.create(id?: SessionId, options?: { seed?: SessionEvent[]; meta?: { cwd?: string; parentSession?: SessionId; createdAt?: number; seedLength?: number } }): Session` — Create a session. `options.seed` replays/forks an existing event log; `options.meta` attaches creation metadata (validated absolute `cwd`, `parentSession` lineage, seed boundary) as the immutable `SessionHeader`. The store fills `version`/`id` and defaults `createdAt` to now; a caller reconstructing a persisted session passes the original `createdAt` and persisted `seedLength` to preserve them. Disposed with the calling fiber.
- `ctx.sessions.snapshot(source: Session | SessionId): SessionForkSeed` — Resolve a live session object or id, reject non-boundary logs, and return a deep-cloned seed plus `parentSession` / `seedLength` metadata. Use this when the caller will pass the seed/meta into another creation path instead of creating a detached session immediately.
- `ctx.sessions.fork({ source, sessionId? }): Session` — Convenience wrapper around `snapshot(source)` + `create(sessionId, { seed, meta })`; creates a live child session with lineage metadata.
- `ctx.sessions.fork({ source, boundary?, childSessionId? }): Session` — Resolve a live session object or id, select a seed through the inclusive `boundary` event seq (default: current last event), require that selected prefix to be turn-enclosed, and create a live child session with lineage metadata.
- `ctx.sessions.get(id: SessionId): Session | undefined`
- `ctx.sessions.list(): Session[]`
@@ -69,9 +68,9 @@ Every `SessionEvent` carries two optional top-level fields (structural metadata)
### Extension points
- Persistence plugins: subscribe to `session/event` (write-behind) and drain on `session/flush` (awaited) and fiber dispose. A durable backend reads the log and reloads it into a live session; the metadata seam (`SessionHeader`, `session.header`) is what such a backend stores beside the log.
- Replay/fork: `ctx.sessions.create(id, { seed })` seeds a new session with an existing event log. The surface rebuilds deterministically from `surfaceOp` markers in the seeded events. The seed is validated to the SAME invariants `append` enforces — including that every surface-eligible event (`SurfaceEventType`) carries a `surfaceOp` marker — so a marker-less message event is rejected at construction rather than silently vanishing from `deriveMessages()` (the surface is the sole derivation path) on resume. Ordinary live-session forks use `ctx.sessions.snapshot()` to validate an empty or `turn/end` boundary and build reusable seed metadata, or `ctx.sessions.fork()` to create the child session immediately.
- Replay/fork: `ctx.sessions.create(id, { seed })` seeds a new session with an existing event log. The surface rebuilds deterministically from `surfaceOp` markers in the seeded events. The seed is validated to the SAME invariants `append` enforces — including that every surface-eligible event (`SurfaceEventType`) carries a `surfaceOp` marker — so a marker-less message event is rejected at construction rather than silently vanishing from `deriveMessages()` (the surface is the sole derivation path) on resume. Ordinary live-session forks use `ctx.sessions.fork({ source, boundary?, childSessionId? })`, where `boundary` is the inclusive source event seq to fork through.
- Compaction: the `dsh-compact-basic` plugin appends a `user/message` with `surfaceOp: { op: 'replace', start, end }` to shadow old surface nodes behind a summary checkpoint.
### What is NOT here (TODO)
- **Session branching/tree** (pi-style entry tree) — deferred unless needed beyond turn-boundary `snapshot()` / `fork()`.
- **Session branching/tree** (pi-style entry tree) — deferred unless needed beyond boundary-based `fork()`.