Files
deepseek-harness/packages/session/session-persistence/src/handle.ts
T

118 lines
5.2 KiB
TypeScript

/**
* The per-session storage handle: one open channel onto a stored session's
* append-only event log, returned by `SessionPersistence.create`/`open`.
* @module @deepseek-ai/dsh-session-persistence/handle
*/
import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset, SessionSeedEventState } from '@deepseek-ai/dsh-session'
/**
* Log access granted by an open. `write` is read-write: the session's single
* mutator, which also reads its own log. `read` only observes — it never
* takes ownership and works while another handle or process holds `write`.
*/
export type SessionAccess = 'read' | 'write'
/** Options for {@link SessionHandle.read}. */
export interface SessionHandleReadOptions {
/** Optional cancellation for backend read work. */
readonly signal?: AbortSignal
}
/** One persistence event slice returned by {@link SessionHandle.read}. */
export interface SessionHandleReadResult {
/**
* Whether event values are exclusively owned or shared only after deep
* freezing. Slicing preserves the producer's state even when no events remain.
*/
readonly eventState: SessionSeedEventState
/** Event values in a caller-owned outer array. */
readonly events: readonly SessionEvent[]
}
/** Options for {@link SessionHandle.append}. */
export interface SessionHandleAppendOptions {
/** Optional cancellation observed before the write starts. */
readonly signal?: AbortSignal
}
/** Options for {@link SessionHandle.flush}. */
export interface SessionHandleFlushOptions {
/** Optional cancellation observed before the barrier starts. */
readonly signal?: AbortSignal
}
/**
* One open channel onto a stored session. A handle is single-owner state, not
* a shared service: `read` never backtracks below what this handle already
* observed, a `write` handle reads its own successful appends, and `close()`
* is the one teardown (idempotent, uncancellable; `Symbol.asyncDispose`
* delegates to it). Every operation on a closed handle rejects with
* `SessionHandleClosedError`.
*
* Freshness across handles: once an `append` or `flush` resolves on a write
* handle, every read STARTED afterwards on the same backend instance — on any
* handle, or through `stat`/`list` — observes at least that prefix.
* Reads concurrent with a mutation carry no ordering promise beyond the valid
* contiguous prefix.
*/
export interface SessionHandle extends AsyncDisposable {
/** The stored session this handle addresses. */
readonly id: SessionId
/** The immutable stored header, fixed at `create`/`open`. */
readonly header: SessionHeader
/**
* Exact fork-inherited prefix length stored with the log; `0` when
* `header.isSeeded` is false. Storage metadata paired with the header for
* every body read, never part of the replayable event log.
*/
readonly inheritedEventCount: SessionLogOffset
/** Whether this handle may mutate the log. */
readonly access: SessionAccess
/**
* Read a slice of the valid contiguous logical log. The slice is a legal log
* prefix segment: a torn physical tail is never returned, and repeated reads
* on this handle never observe an older state than a prior read.
* @param offset - first logical event seq to include; defaults to `0`.
* @param length - maximum number of events to return; defaults to the rest
* of the log. An offset at or past the end returns an empty list.
* @param options - optional cancellation.
* @returns the caller-owned outer slice plus the ownership state of its event values.
*/
read(offset?: number, length?: number, options?: SessionHandleReadOptions): Promise<SessionHandleReadResult>
/**
* Append a contiguous batch continuing the current logical end. The first
* event's `seq` MUST equal the stored next-seq; committed events are never
* rewritten. Persistence is best-effort: on resolution the batch is
* accepted, ordered, and visible to reads on this backend instance, but
* only a resolved {@link flush} promises it survives a crash — a backend
* may buffer or batch physical writes behind append. Rejects with
* `SessionReadOnlyError` on a read handle and `SessionOwnershipLostError`
* when write ownership is gone.
* @param events - the contiguous batch, in seq order.
* @param options - optional cancellation observed before the write starts.
*/
append(events: readonly SessionEvent[], options?: SessionHandleAppendOptions): Promise<void>
/**
* The durability barrier — the one operation that promises storage: on
* resolution every acknowledged append is durable and the session is
* materialized for other processes; an empty created session becomes
* durably listable here. Callers that must survive a crash flush; a backend
* whose `append` already persists on resolution treats this as
* materialize-if-needed. Rejects with `SessionReadOnlyError` on a read
* handle.
* @param options - optional cancellation observed before the barrier starts.
*/
flush(options?: SessionHandleFlushOptions): Promise<void>
/**
* Release the handle: a read handle frees local resources; a write handle
* completes pending durability and releases write ownership. Idempotent,
* asynchronous, and deliberately not cancellable.
*/
close(): Promise<void>
}