import type { SessionId, TurnId } from '../../types/ids/index.js'; import type { Message } from '../../types/message/index.js'; import { type RecordPointer, type SessionRecord } from '../../types/session/records.js'; import { type ActiveTurnState } from '../../types/session/turn.js'; import { type LogBytes, type SessionLogEntry, SessionLogIntegrityError } from './chain.js'; import { type ActiveTurnRecord } from './fold.js'; import { type ClaimSessionOptions, type SessionLease, type SessionLeaseStore } from './lease.js'; import type { SpillRef, SpillStore } from './spill.js'; type EnvelopeKey = 'v' | 'id' | 'sessionId' | 'seq' | 'ts' | 'prev' | 'prevText' | 'gen'; type DraftOf = R extends SessionRecord ? Omit : never; /** * A record as a caller appends it: everything but the envelope. The log * assigns `v`, `id`, `sessionId`, `seq`, `ts`, `prev` and `gen`; the caller * gives the `type`, the payload and, inside a turn, the `turnId`. */ export type SessionRecordDraft = DraftOf; /** The payload of a `turn_started` record, as `beginTurn` takes it. */ export type TurnStartedDraft = Omit, 'type'>; /** The last record of a log and the log's size in bytes. */ export interface SessionLogHead { readonly pointer: RecordPointer; readonly gen: number; /** Bytes of the log through the head record (a torn tail is not counted). */ readonly bytes: number; } /** A session's active turn and its state (spec §4.5). */ export interface ActiveTurn extends ActiveTurnRecord { readonly state: ActiveTurnState; } export interface ReadSessionLogOptions { /** * `strict` (the default) throws {@link SessionLogIntegrityError} at the * first break. `tolerant` stops there and reports `intact: false` with * everything before the break. */ readonly mode?: 'strict' | 'tolerant'; /** Resume after this record: its bytes are checked, then the walk continues from it. */ readonly after?: RecordPointer; /** * The record the log must hold at `expectHead.seq`, byte for byte. Anchors * the tail, which no later record vouches for. */ readonly expectHead?: RecordPointer; /** Stop after this seq. */ readonly throughSeq?: number; } /** What a read established, returned when the walk ends. */ export interface SessionLogReadSummary { /** False when a tolerant read stopped at a break. */ readonly intact: boolean; /** The last seq read and verified (0 for an empty log). */ readonly throughSeq: number; readonly head: SessionLogHead | null; /** Bytes after the last complete line: a torn tail, which a writer truncates. */ readonly tornBytes: number; /** The break a tolerant read stopped at. */ readonly break?: SessionLogIntegrityError; } export interface SessionLogRead extends SessionLogReadSummary { readonly entries: readonly SessionLogEntry[]; } export interface BeginTurnOptions { /** * Close an `interrupted` active turn with `turn_failed{failure.code: * 'interrupted'}` before beginning. A `running` or `paused` turn is never * closed this way. */ readonly abandonInterrupted?: boolean; } export interface ActiveTurnOptions { /** The lease the caller holds; the default asks the store for the current one. */ readonly lease?: SessionLease; /** Clock, for tests. */ readonly now?: number; } /** * One session's append-only, hash-chained log: the source of truth for * everything the session did (spec §4). One writer at a time holds its * lease; every append presents it. */ export interface SessionLog { readonly sessionId: SessionId; /** * Take or renew the writer lease. `null` when it is live and this instance * does not hold it — whatever the holder name, so a second instance under * the same name waits like any other. This instance renews its own holding * under the same fence, late or not, unless somebody took the session in * between. A new fence is above the log's highest `gen`. Taking it * repairs a torn tail first (`log_repaired`). */ claim(options: ClaimSessionOptions): Promise; /** Give the lease up; a stale lease releases nothing. */ release(lease: SessionLease): Promise; /** The current holding, or `null` when never claimed. */ lease(): Promise; /** * Append one record under `lease`. Refused with * {@link StaleSessionLeaseError} when the lease is not the current one, * {@link TurnRuleError} when the turn rules forbid the record, and * {@link InvalidSessionRecordError} when it is not a valid record. A body * too large for a record is spilled first. */ append(lease: SessionLease, draft: SessionRecordDraft): Promise; /** * Begin a turn. Throws {@link TurnInProgressError} when a turn is active * (`running`, `paused` or `interrupted`), unless it is `interrupted` and * `abandonInterrupted` is set. */ beginTurn(lease: SessionLease, draft: TurnStartedDraft, options?: BeginTurnOptions): Promise; /** Close a paused or interrupted turn with `turn_failed{failure.code:'abandoned'}`. */ abandonTurn(lease: SessionLease, turnId: TurnId, reason: string): Promise; /** The active turn, if any, and whether it is running, paused or interrupted. */ activeTurn(options?: ActiveTurnOptions): Promise; head(): Promise; /** Walk the log, verifying the chain. Returns the summary when the walk ends. */ read(options?: ReadSessionLogOptions): AsyncGenerator; readAll(options?: ReadSessionLogOptions): Promise; /** The folded conversation (spec §4.5), spills read back. */ messages(options?: { readonly throughSeq?: number; }): Promise; readSpill(ref: SpillRef): Promise; } /** A draft that is not a valid session record once enveloped. */ export declare class InvalidSessionRecordError extends Error { readonly name = "InvalidSessionRecordError"; } /** The log changed under a writer in a way it cannot reconcile (a concurrent writer). */ export declare class SessionLogConflictError extends Error { readonly name = "SessionLogConflictError"; } /** The bytes of one log, as the disk and in-memory backends store them. */ export interface LogMedium extends LogBytes { /** * Append `bytes` so they start at `expectedOffset`. Throws * {@link SessionLogConflictError} if the log is not `expectedOffset` long * when the write is issued, or if the bytes did not land there. */ append(bytes: Uint8Array, expectedOffset: number, sync: boolean): Promise; /** Cut the log to `size` bytes, durably. Refuses if it is not `expectedSize` long. */ truncate(size: number, expectedSize: number): Promise; /** The bytes from `offset` to the end, in chunks. */ stream(offset: number): AsyncIterable; } export interface SessionLogCoreOptions { readonly sessionId: SessionId; readonly medium: LogMedium; readonly leases: SessionLeaseStore; readonly spills: SpillStore; /** Clock for `ts` and turn timings. */ readonly now?: () => number; /** * A `message` or `compaction` record whose line would exceed this many * bytes spills its body first. Default {@link SESSION_RECORD_MAX_BYTES}. */ readonly spillAboveBytes?: number; /** * Which appends are fsynced. `boundaries` (the default): the records * others depend on — `session_started`, the turn lifecycle, checkpoints * and decisions. `all` or `none` as named. */ readonly sync?: 'boundaries' | 'all' | 'none'; } /** * The log's behaviour, shared by both backends so they cannot disagree. A * backend supplies the medium, the lease store and the spill store. */ export declare class SessionLogCore implements SessionLog { #private; readonly sessionId: SessionId; constructor(options: SessionLogCoreOptions); claim(options: ClaimSessionOptions): Promise; /** * In write order: a release waits for the append in flight, so it never * lands between a record's fence check and its bytes. */ release(lease: SessionLease): Promise; lease(): Promise; append(lease: SessionLease, draft: SessionRecordDraft): Promise; beginTurn(lease: SessionLease, draft: TurnStartedDraft, options?: BeginTurnOptions): Promise; abandonTurn(lease: SessionLease, turnId: TurnId, reason: string): Promise; activeTurn(options?: ActiveTurnOptions): Promise; head(): Promise; read(options?: ReadSessionLogOptions): AsyncGenerator; readAll(options?: ReadSessionLogOptions): Promise; messages(options?: { readonly throughSeq?: number; }): Promise; readSpill(ref: SpillRef): Promise; } /** Drain a walk into entries plus its summary. */ export declare function collect(walk: AsyncGenerator): Promise; /** * Walk a log's bytes, verifying the chain as it goes (spec §4.1). Shared by * both backends and by `readSessionLog`. */ export declare function walkSessionLog(medium: Pick, options: ReadSessionLogOptions & { readonly sessionId?: SessionId; }): AsyncGenerator; export {}; //# sourceMappingURL=core.d.ts.map