/** * `CleoSessionStorage` — durable Pi `SessionStorage` over `cleo.db` (T11761 · S3 · T11899). * * The flag-ON, durable half of the Pi session-persistence seam. It implements * Pi's tree-structured `SessionStorage` interface * (`@earendil-works/pi-agent-core`) over the PROJECT-scope consolidated * `cleo.db`, with ZERO write authority: * * - **All durable writes go through {@link withWriterLease}** (`writer-lease.ts`, * PROJECT scope + `bulk` lane) — the daemon remains the sole arbitrated * Drizzle writer. This adapter NEVER opens a raw DB writer (Gate 3): it calls * the store-layer accessor ({@link import('../../store/pi-session-store.js')}), * which extracts the native handle the chokepoint already holds. * - **Identity is daemon-stamped, never minted.** The owning `sessionId` is * supplied at construction from `resolveCurrentSessionId` / `CLEO_SESSION_ID` * (resolved in `pi-agent-adapter.ts`). Unlike `InMemorySessionStorage`, this * storage NEVER calls `uuidv7()` to mint an id. * * {@link InMemorySessionStorage} (from `pi-agent-core`) remains the flag-OFF * default that the S2 adapter body uses; this durable storage activates only when * the Pi-runner flag is enabled AND a durable session is requested. * * The tree semantics mirror the upstream `InMemorySessionStorage` reference * (memory-storage.ts) byte-for-byte — `appendEntry` advances the leaf via * `leafIdAfterEntry`, `setLeafId` appends a synthetic `leaf` entry, label-cache * and `getPathToRoot` walk parent chains — so a session tree written here reads * back identical to the in-memory implementation. The ONLY difference is the * backing store (durable cleo.db rows vs in-RAM arrays) and the lease boundary. * * @module * @task T11899 * @task T11761 * @epic T10403 */ import type { SessionMetadata, SessionStorage, SessionTreeEntry } from '@earendil-works/pi-agent-core'; /** * Construction options for {@link CleoSessionStorage}. */ export interface CleoSessionStorageOptions { /** * The daemon-stamped owning session id. MUST be a real, env-resolved id — * never a freshly-minted one. The S2 adapter reads it from * `resolveCurrentSessionId` / `CLEO_SESSION_ID` and passes it here. */ readonly sessionId: string; /** * Project working directory for `cleo.db` resolution. Defaults to `cwd` inside * the store accessor. */ readonly cwd?: string; /** * Wall-clock provider (injectable for deterministic tests). Defaults to * `() => new Date().toISOString()`. */ readonly now?: () => string; } /** * Durable Pi `SessionStorage` backed by the PROJECT-scope `cleo.db`. * * Every mutating method ({@link appendEntry}, {@link setLeafId}) runs its write * inside `withWriterLease('project', 'bulk', …)`; read methods operate directly * on the shared native handle (no lease — readers do not contend the writer * arbitration row). The session id is fixed at construction and never minted. */ export declare class CleoSessionStorage implements SessionStorage { #private; /** * @param options - The daemon-stamped session id + optional cwd / clock. */ constructor(options: CleoSessionStorageOptions); /** * Return the session metadata. The `id` is ALWAYS the daemon-stamped id this * storage was constructed with (never minted); `createdAt` is the persisted * leaf-row anchor, lazily created on first read so a never-written session * still yields stable metadata. * * @returns The session metadata. */ getMetadata(): Promise; /** * Return the active leaf id, or `null` when none is set. * * @returns The leaf id or `null`. * @throws {SessionError} `invalid_session` when the recorded leaf entry is missing. */ getLeafId(): Promise; /** * Persist a leaf entry that records the active session-tree leaf. * * Mirrors the upstream reference: appends a synthetic `leaf` entry (parented at * the current leaf) THEN advances the leaf pointer to `leafId`. The whole * mutation runs inside ONE leased section. * * @param leafId - The new active leaf entry id, or `null` to reset to root. * @throws {SessionError} `not_found` when `leafId` names no existing entry. */ setLeafId(leafId: string | null): Promise; /** * Allocate a fresh, unused entry id for this session. * * Read-only (no persistence until {@link appendEntry}) — matches the upstream * `createEntryId`. * * @returns A fresh unused entry id. */ createEntryId(): Promise; /** * Append a tree entry and advance the leaf pointer (leased write). * * @param entry - The entry to persist. */ appendEntry(entry: SessionTreeEntry): Promise; /** * Return an entry by id, or `undefined` when absent. * * @param id - The entry id. * @returns The entry or `undefined`. */ getEntry(id: string): Promise; /** * Return all entries of a given type in append order. * * @param type - The entry-type discriminator to filter on. * @returns The matching entries. */ findEntries(type: TType): Promise>>; /** * Return the most-recent label applied to an entry, or `undefined`. * * Computed from the `label` entries (last write wins; an empty label clears), * mirroring the upstream label cache. * * @param id - The target entry id. * @returns The label or `undefined`. */ getLabel(id: string): Promise; /** * Walk from a leaf to the root, returning the path in root→leaf order. * * @param leafId - The leaf to walk from, or `null` for an empty path. * @returns The root→leaf entry path. * @throws {SessionError} `not_found` / `invalid_session` on a broken chain. */ getPathToRoot(leafId: string | null): Promise; /** * Return all entries for this session in stable append order. * * @returns The ordered entries. */ getEntries(): Promise; } //# sourceMappingURL=pi-session-storage.d.ts.map