/** * The canonical `SessionEvent` model — ADR 0062, slice 1 (the shared contract). * * This is **Nano's own** agent-session event model: the single schema every * harness dialect (ACP, stream-json, a native normalizer, …) normalizes *into*. * We never adopt an external harness schema as ours — those are ingestion * details owned by the later slices; this union is the stable interface they all * target. * * ## The causal chain * * A session is an append-only log of events. Two orthogonal orderings make the * log both replayable and mergeable: * * - a **monotonic, gap-free `offset`** assigned by the authoritative log on * append (see {@link AppendedSessionEvent}); it is the resume coordinate — * `restore` hands back everything up to a checkpoint offset. * - a **causal `parentId`** the producer stamps: the id of the event this one * logically follows (`null` for the first event of a session). Offset gives a * total order for replay; `parentId` records the *causal* edge, which survives * a compaction that rewrites offsets. * * The producer owns identity (`id`) and causality (`parentId`); the log owns * ordering (`offset`) and fencing (`incarnation`). Keeping those responsibilities * split is what lets a resumed incarnation continue the same causal chain at a * fresh offset without the producer knowing the log's internal cursor. */ /** Discriminates a {@link SessionEvent}. One member per row in the union below. */ export type SessionEventType = "system" | "user" | "assistant" | "reasoning" | "tool-call" | "tool-result" | "compaction" | "usage" | "turn-start" | "turn-end"; /** The set of valid event types, for a runtime membership check at the DB boundary. */ export declare const SESSION_EVENT_TYPES: readonly SessionEventType[]; /** * The fields every event carries regardless of type. `offset` is deliberately * absent — the producer does not assign it; the authoritative log does, yielding * an {@link AppendedSessionEvent}. */ export interface SessionEventEnvelope { /** Producer-assigned unique id for this event (the causal-chain node id). */ readonly id: string; /** The id of the causal predecessor, or `null` for the first event of a session. */ readonly parentId: string | null; } /** A system/instruction message (the harness/system prompt turn). */ export interface SystemMessageEvent extends SessionEventEnvelope { readonly type: "system"; readonly text: string; } /** A user message. */ export interface UserMessageEvent extends SessionEventEnvelope { readonly type: "user"; readonly text: string; } /** An assistant (model) message — the visible answer text. */ export interface AssistantMessageEvent extends SessionEventEnvelope { readonly type: "assistant"; readonly text: string; } /** * Assistant reasoning (chain-of-thought / thinking) for a turn. * * `text` is the human-readable reasoning summary when the provider exposes one. * `providerContinuation` is an **opaque provider reasoning-continuation blob**: * some providers (e.g. encrypted reasoning tokens) return a handle that must be * fed back verbatim to continue reasoning across a resume. Nano never parses, * validates, or transforms it — it stores and replays it as an opaque string so * a re-leased incarnation can resume the model's reasoning exactly. */ export interface ReasoningEvent extends SessionEventEnvelope { readonly type: "reasoning"; readonly text?: string; readonly providerContinuation?: string; } /** A tool/function call the assistant requested. */ export interface ToolCallEvent extends SessionEventEnvelope { readonly type: "tool-call"; /** Correlates this call with its {@link ToolResultEvent}. */ readonly callId: string; readonly name: string; /** The call arguments, as an opaque JSON-serialisable value. */ readonly args: unknown; } /** The result of a previously-emitted {@link ToolCallEvent}. */ export interface ToolResultEvent extends SessionEventEnvelope { readonly type: "tool-result"; /** Matches the originating {@link ToolCallEvent.callId}. */ readonly callId: string; /** `false` when the tool failed; the failure detail lives in `result`. */ readonly ok: boolean; /** The tool output, as an opaque JSON-serialisable value. */ readonly result: unknown; } /** * A compaction or truncation boundary: the events in the (inclusive-exclusive) * offset range `[replacesFrom, replacesTo)` were summarised/dropped to bound * context growth. `summary` is the replacement text (present for compaction, * typically absent for a hard truncation). The original events keep their * offsets in the authoritative log; this marker records that a *replay* should * fold that range into the summary rather than replaying it verbatim. */ export interface CompactionEvent extends SessionEventEnvelope { readonly type: "compaction"; readonly reason: "compaction" | "truncation"; readonly replacesFrom: number; readonly replacesTo: number; readonly summary?: string; } /** A usage/accounting record for a turn (token counts, etc.). */ export interface UsageEvent extends SessionEventEnvelope { readonly type: "usage"; readonly inputTokens: number; readonly outputTokens: number; /** Optional provider model identifier the usage is attributed to. */ readonly model?: string; } /** The start of a turn (a request/response cycle). `turn` is a monotonic index. */ export interface TurnStartEvent extends SessionEventEnvelope { readonly type: "turn-start"; readonly turn: number; } /** The end of a turn matching a prior {@link TurnStartEvent}. */ export interface TurnEndEvent extends SessionEventEnvelope { readonly type: "turn-end"; readonly turn: number; } /** * The canonical session event — a discriminated union over {@link SessionEventType}. * Every harness dialect normalises into exactly this shape. */ export type SessionEvent = SystemMessageEvent | UserMessageEvent | AssistantMessageEvent | ReasoningEvent | ToolCallEvent | ToolResultEvent | CompactionEvent | UsageEvent | TurnStartEvent | TurnEndEvent; /** * A {@link SessionEvent} after the authoritative log has appended it: the same * event plus the log-assigned `offset` (its monotonic resume coordinate) and the * `incarnation` (the generation of the writer that produced it — the fencing * stamp). This is what {@link restore} replays as the mind seed. */ export type AppendedSessionEvent = SessionEvent & { readonly offset: number; readonly incarnation: number; }; /** Raised when a value read back from storage is not a well-formed session event. */ export declare class SessionEventShapeError extends Error { constructor(message: string); } /** * Parse and validate an untyped value (e.g. `JSON.parse` of a stored row) into a * {@link SessionEvent}, reconstructing the exact union member for its `type`. * Throws {@link SessionEventShapeError} on any malformed field. This is the * single trusted boundary between untyped storage and the typed union — it never * uses an `as`-cast to fabricate a shape (see AGENTS.md), it *builds* one field * by field, so a corrupt row fails loudly instead of masquerading as valid. */ export declare function parseSessionEvent(value: unknown): SessionEvent;