import type { CheckpointId, MessageId, TurnId } from '../../types/ids/index.js'; import type { Message } from '../../types/message/index.js'; import type { SessionRecord } from '../../types/session/records.js'; import type { SpillRef } from './spill.js'; /** * Folds over a session log's records: the conversation a model sees, and * which turn (if any) is active. * * ## The message fold (spec §4.5) * * The context is, in order: * * 1. the latest `compaction`'s summary; * 2. then the messages it kept (`keptMessageIds`, plus any `pinned`); * 3. then every message after its `replacesSeqRange`; * 4. with every `message_replaced` applied. * * A replacement changes a message's content in place and keeps its id and * position. It applies whenever its target is in the context, including a * target that a compaction keeps. So a guardrail's rewrite, a review's * override or a structured-output answer is what every reader of the fold * sees; the model's raw text stays in the log for audit only. * * Only `message`, `message_replaced` and `compaction` records move the fold. * The live compaction events (`compaction_completed` and the rest) are * reports, not instructions. */ /** One message of the folded context. */ export interface FoldedMessage { /** Absent for a compaction summary message, which has no record of its own. */ readonly messageId?: MessageId; /** Seq of the record that put the message in the log (the compaction's, for a summary). */ readonly seq: number; readonly message: Message; /** Set when the record's content is a preview and the full message is in a spill. */ readonly spill?: SpillRef; /** Set when a `message_replaced` changed this message. */ readonly replacedAtSeq?: number; } /** A compaction whose summary was spilled; the fold resolves it on demand. */ export interface SpilledSummary { readonly seq: number; readonly spill: SpillRef; } /** * The message fold, applied one record at a time. * * Synchronous and pure: spilled bodies are left as references * ({@link FoldedMessage.spill}, {@link SessionMessageFold.spilledSummary}); * {@link foldSessionMessages} resolves them. */ export declare class SessionMessageFold { #private; get throughSeq(): number; get spilledSummary(): SpilledSummary | undefined; apply(record: SessionRecord): void; /** The folded context, oldest first. A spilled summary is not included; see {@link foldSessionMessages}. */ entries(): FoldedMessage[]; messages(): Message[]; } export interface FoldSessionMessagesOptions { /** Fold only records with `seq <= throughSeq` (a checkpoint's context). */ readonly throughSeq?: number; /** Reads a spilled body. Required when the log holds a spill; the fold refuses without it. */ readonly readSpill?: (ref: SpillRef) => Promise; } /** A spill the fold met with no way to read it. */ export declare class SpillUnavailableError extends Error { readonly name = "SpillUnavailableError"; } /** * The conversation a session log holds: the fold of every record (or of the * records through `throughSeq`), spilled bodies read back and checked. */ export declare function foldSessionMessages(records: Iterable | AsyncIterable, options?: FoldSessionMessagesOptions): Promise; /** The active turn as the log records it, before any lease is consulted. */ export interface ActiveTurnRecord { readonly turnId: TurnId; /** Seq of its `turn_started`. */ readonly startedSeq: number; readonly startedAt: string; /** Set while its last segment record is `turn_paused`. */ readonly paused: boolean; readonly pausedCheckpointId?: CheckpointId; /** * The `gen` of its latest `turn_started` or `turn_resuming`: the lease * holding that is running it. A lease at a higher fence did not start it. */ readonly ownerGen: number; } /** A record that the turn rules refuse at append time. */ export declare class TurnRuleError extends Error { readonly name = "TurnRuleError"; } /** * Tracks the session's one active turn (spec §4.5), record by record. * * `apply` in strict mode throws {@link TurnRuleError} for a record the turn * rules forbid — the writer uses it to refuse an append before it is written. * A reader applies records tolerantly: what is in the log is what happened. */ export declare class SessionTurnState { #private; get active(): ActiveTurnRecord | undefined; /** The last turn that started, active or not. */ get lastTurnId(): TurnId | undefined; /** Whether `session_started` has been applied. */ get started(): boolean; /** Throws {@link TurnRuleError} if `record` may not follow the records applied so far. */ check(record: Pick): void; apply(record: SessionRecord, options?: { readonly strict?: boolean; }): void; } //# sourceMappingURL=fold.d.ts.map