tnega 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/agent.js +2207 -513
- package/dist/agent.js.map +4 -4
- package/dist/bin.js +942 -469
- package/dist/bin.js.map +3 -3
- package/dist/cli-runtime.js +943 -469
- package/dist/cli-runtime.js.map +3 -3
- package/dist/coding-agent.js +1623 -162
- package/dist/coding-agent.js.map +4 -4
- package/dist/eval.js +422 -158
- package/dist/eval.js.map +3 -3
- package/dist/evolve.js +512 -326
- package/dist/evolve.js.map +4 -4
- package/dist/index.js +951 -469
- package/dist/index.js.map +3 -3
- package/dist/llm.js +47 -3
- package/dist/llm.js.map +3 -3
- package/dist/session.js +91 -5
- package/dist/session.js.map +2 -2
- package/dist/types/packages/agent/src/inbox-durable.d.ts +5 -0
- package/dist/types/packages/agent/src/live.d.ts +24 -8
- package/dist/types/packages/agent/src/service.d.ts +8 -2
- package/dist/types/packages/agent/src/types.d.ts +40 -2
- package/dist/types/packages/cli/src/config.d.ts +2 -0
- package/dist/types/packages/cli/src/index.d.ts +1 -0
- package/dist/types/packages/llm/src/types.d.ts +2 -0
- package/dist/types/packages/session/src/index.d.ts +68 -7
- package/dist/types/packages/session/src/invariant.d.ts +2 -0
- package/dist/types/src/index.d.ts +2 -2
- package/dist/web/assets/{index-45pokiBH.js → index-Bq0i29_u.js} +30 -30
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
package/dist/session.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../packages/session/src/index.ts", "../packages/session/src/invariant.ts"],
|
|
4
|
-
"sourcesContent": ["import { randomUUID } from 'node:crypto'\r\nimport { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises'\r\nimport { dirname, resolve } from 'node:path'\r\nimport type { Context } from '@tnega/core'\r\nimport { checkSessionInvariants, type SessionInvariantFailure } from './invariant.js'\r\n\r\n/**\r\n * v7 derives model-visible messages from the *folded surface* instead of from\r\n * raw file order, so compaction can stay strictly append-only.\r\n *\r\n * v6 rewrote the log on every real compaction: the kept tail was physically\r\n * reordered after the checkpoint (and every seq renumbered) purely so that a\r\n * file-order projection would equal the surface. v7 removes that coupling:\r\n * `deriveMessages()` walks the surface nodes in surface position order (a\r\n * replacement node may carry a *higher* seq than the nodes that follow it),\r\n * so compaction appends `compaction/start` \u2192 `checkpoint` (prefix +\r\n * `surfaceOp.replace` range) \u2192 `compaction/end` and never rewrites history.\r\n * To keep every surface node self-describing, `assistant/message` now carries\r\n * its own `toolCalls` (the assistant turn is not reassembled from separate\r\n * `tool/call` events at projection time).\r\n *\r\n * v6 logs are rejected (no migration in the pre-release window).\r\n */\r\nexport const SESSION_FORMAT_VERSION = 7\r\n\r\n/** Serialized tool schema, structurally compatible with @tnega/tools ToolSchema. */\r\nexport interface ToolSchemaSnapshot {\r\n name: string\r\n description: string\r\n parameters?: Record<string, unknown>\r\n}\r\n\r\n/** Call configuration recorded beside a request so it can be reconstructed. */\r\nexport interface LlmCallConfig {\r\n provider?: string\r\n model?: string\r\n maxTokens?: number\r\n temperature?: number\r\n reasoningEffort?: string\r\n}\r\n\r\nexport class SessionFormatError extends Error {\r\n override name = 'SessionFormatError'\r\n}\r\n\r\nconst liveSessions = new Map<string, SessionLog>()\r\n\r\nfunction sessionFileKey(file: string): string {\r\n return resolve(file)\r\n}\r\n\r\nexport type ModelRole = 'system' | 'user' | 'assistant' | 'tool'\r\n\r\nexport interface ModelToolCall {\r\n id: string\r\n name: string\r\n arguments: unknown\r\n}\r\n\r\nexport interface ModelMessage {\r\n role: ModelRole\r\n content: string\r\n name?: string\r\n tool_call_id?: string\r\n tool_calls?: ModelToolCall[]\r\n toolOk?: boolean\r\n toolError?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface UserMessagePayload {\r\n content: string\r\n name?: string\r\n parentId?: string\r\n}\r\n\r\nexport interface AssistantMessagePayload {\r\n content: string\r\n name?: string\r\n parentId?: string\r\n interrupted?: boolean\r\n /**\r\n * The tool requests this assistant turn made, when it made any. The model\r\n * transcript is derived node-by-node from the surface, so an assistant turn\r\n * must carry its calls itself rather than being reassembled from the\r\n * separate (log-only) `tool/call` events that follow it.\r\n */\r\n toolCalls?: ModelToolCall[]\r\n}\r\n\r\nexport interface AssistantChunkPayload {\r\n id: string\r\n content: string\r\n index?: number\r\n}\r\n\r\nexport interface SystemMessagePayload {\r\n content: string\r\n name?: string\r\n parentId?: string\r\n}\r\n\r\nexport type MessageEventPayload =\r\n | UserMessagePayload\r\n | AssistantMessagePayload\r\n | SystemMessagePayload\r\n\r\nexport type MessageEventType = 'user/message' | 'assistant/message' | 'system/message'\r\n\r\nexport interface CompactionStartPayload {\r\n boundary?: number\r\n keep?: number\r\n tokensBefore?: number\r\n}\r\n\r\nexport interface CompactionEndPayload {\r\n checkpointId?: string\r\n keep?: number\r\n}\r\n\r\nexport interface ToolCallPayload {\r\n id: string\r\n name: string\r\n arguments: unknown\r\n /** Exact raw arguments JSON as produced by the model; preserves serialization. */\r\n argRaw?: string\r\n turn?: number\r\n step?: number\r\n}\r\n\r\nexport interface ToolResultErrorPayload {\r\n name?: string\r\n message: string\r\n stack?: string\r\n}\r\n\r\nexport interface ToolResultPayload {\r\n id: string\r\n toolCallId: string\r\n name: string\r\n ok: boolean\r\n durationMs?: number\r\n output?: unknown\r\n error?: ToolResultErrorPayload\r\n argRaw?: string\r\n turn?: number\r\n step?: number\r\n}\r\n\r\nexport type CancelCause =\r\n | { type: 'user' }\r\n | { type: 'parent' }\r\n | { type: 'disposed' }\r\n | { type: 'abort'; message?: string }\r\n | { type: 'timeout'; timeoutMs: number }\r\n\r\n/**\r\n * Why a turn ended \u2014 the durable machine outcome, distinct from the\r\n * per-step model `finishReason`. Written on `turn/end`.\r\n */\r\nexport type TurnEndReason =\r\n | { kind: 'completed' }\r\n | { kind: 'aborted'; cause: CancelCause }\r\n | { kind: 'blocked' }\r\n | { kind: 'error'; error: ToolResultErrorPayload }\r\n | { kind: 'max-tokens' }\r\n | { kind: 'max-steps' }\r\n | { kind: 'max-turns' }\r\n | { kind: 'interrupted' }\r\n\r\nexport interface TurnStartPayload {\r\n turn: number\r\n input?: unknown\r\n reason?: string\r\n}\r\n\r\nexport interface TurnEndPayload {\r\n turn: number\r\n finishReason?: string\r\n /** The typed durable reason this turn ended. Supersedes `finishReason`. */\r\n reason?: TurnEndReason\r\n output?: string\r\n steps?: number\r\n interrupted?: boolean\r\n cancelCause?: CancelCause\r\n error?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface StepStartPayload {\r\n turn: number\r\n step: number\r\n}\r\n\r\nexport interface StepEndPayload {\r\n turn: number\r\n step: number\r\n finishReason?: string\r\n toolCalls?: number\r\n interrupted?: boolean\r\n cancelCause?: CancelCause\r\n error?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface LLMRetryPayload {\r\n retryId: string\r\n retry: number\r\n delayMs?: number\r\n failure?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface LLMRetryStartedPayload {\r\n retryId: string\r\n retry: number\r\n}\r\n\r\n/** How a `request/header` snapshot enters the log. */\r\nexport type RequestHeaderReason =\r\n | 'initial'\r\n | 'resume'\r\n | 'change'\r\n | 'series'\r\n | 'change-series'\r\n\r\nexport interface RequestHeaderPayload {\r\n reason: RequestHeaderReason\r\n config?: LlmCallConfig\r\n /** Rendered system prompt; absent for a system-less request. */\r\n system?: string\r\n /** Assembled tool schemas; absent for a tool-less request. */\r\n tools?: ToolSchemaSnapshot[]\r\n /** True when this snapshot also begins a new model-message series. */\r\n startsSeries?: boolean\r\n}\r\n\r\nexport interface RequestContextPayload {\r\n provider?: string\r\n model?: string\r\n contextWindow?: number\r\n}\r\n\r\nexport interface CheckpointPayload {\r\n /** The compacted prefix: messages the model keeps seeing after compaction. */\r\n messages: ModelMessage[]\r\n summary?: string\r\n tokensBefore?: number\r\n /**\r\n * The surface span this checkpoint shadows, when written by a v7\r\n * compaction: `start`/`end` are the seqs of the first and last *surface\r\n * nodes* being replaced (surface positions, so `start` may be numerically\r\n * greater than `end` after an earlier compaction). The fold removes every\r\n * live node between those two positions and inserts this checkpoint there;\r\n * the kept tail stays in place and the log is never rewritten.\r\n * `'replace'` (bare string) is the legacy v5 form and shadows nothing\r\n * structurally; projection treats it as a full-surface seed.\r\n */\r\n surfaceOp?: { op: 'replace'; start: number; end: number } | 'replace'\r\n /** @deprecated v0.2 keeps raw events in place; use messages instead. */\r\n snapshot?: SessionEvent[]\r\n}\r\n\r\nexport type PlanItemStatus = 'pending' | 'done' | 'failed'\r\n\r\nexport interface PlanItemPayload {\r\n id: string\r\n title: string\r\n status: PlanItemStatus\r\n detail?: string\r\n}\r\n\r\nexport interface PlanPayload {\r\n items: PlanItemPayload[]\r\n status?: 'pending' | 'running' | 'done' | 'failed'\r\n summary?: string\r\n}\r\n\r\nexport interface InboxSplicePayload {\r\n target: 'next-turn' | 'next-step'\r\n index?: number\r\n deleteCount?: number\r\n inserted?: Array<{\r\n id: string\r\n content: string\r\n /** Structured message payload preserved across restart when present. */\r\n payload?: unknown\r\n mode?: 'followup' | 'steer'\r\n }>\r\n}\r\n\r\n/** A durable change to session display metadata (title, mode, agentType). */\r\nexport interface MetaPatchPayload {\r\n /** Keys changed by this event; only present keys are touched. */\r\n fields: Array<'title' | 'agentType' | 'mode'>\r\n title?: string\r\n agentType?: AgentType\r\n mode?: SessionMode\r\n}\r\n\r\nexport type AgentType = 'general' | 'coding'\r\n\r\nexport type SessionMode = 'auto' | 'plan' | 'execute'\r\n\r\nexport type SessionEventType =\r\n | MessageEventType\r\n | 'assistant/chunk'\r\n | 'tool/call'\r\n | 'tool/result'\r\n | 'request/header'\r\n | 'request/context'\r\n | 'agent/inbox/spliced'\r\n | 'plan'\r\n | 'checkpoint'\r\n | 'compaction/start'\r\n | 'compaction/end'\r\n | 'meta'\r\n | 'meta/patch'\r\n | 'llm/retry'\r\n | 'llm/retry-started'\r\n | 'turn/start'\r\n | 'turn/end'\r\n | 'step/start'\r\n | 'step/end'\r\n\r\nexport interface SessionEventBase<T extends SessionEventType, P> {\r\n id: string\r\n seq: number\r\n ts: number\r\n type: T\r\n payload: P\r\n /** Surface placement; required on message-producing events. */\r\n surfaceOp?: SurfaceOp\r\n /** Seqs of earlier raw events this event derives from. */\r\n sourceEventSeqs?: number[]\r\n}\r\n\r\n/**\r\n * The message-producing subset of event types. Every variant is a surface\r\n * node that carries `surfaceOp` and participates in the ordered fold.\r\n * `system/message` is included because tnega keeps the system prompt on the\r\n * transcript (OpenAI-style), so it must be replaceable and derivable like any\r\n * other message rather than living only in `request/header`.\r\n */\r\nexport type SurfaceEventType =\r\n | 'user/message'\r\n | 'system/message'\r\n | 'assistant/message'\r\n | 'tool/result'\r\n\r\nexport type SurfaceOp = 'append' | { op: 'replace'; start: number; end: number }\r\n\r\nexport type SessionEvent =\r\n | SessionEventBase<'user/message', UserMessagePayload>\r\n | SessionEventBase<'assistant/message', AssistantMessagePayload>\r\n | SessionEventBase<'assistant/chunk', AssistantChunkPayload>\r\n | SessionEventBase<'system/message', SystemMessagePayload>\r\n | SessionEventBase<'tool/call', ToolCallPayload>\r\n | SessionEventBase<'tool/result', ToolResultPayload>\r\n | SessionEventBase<'request/header', RequestHeaderPayload>\r\n | SessionEventBase<'request/context', RequestContextPayload>\r\n | SessionEventBase<'agent/inbox/spliced', InboxSplicePayload>\r\n | SessionEventBase<'plan', PlanPayload>\r\n | SessionEventBase<'checkpoint', CheckpointPayload>\r\n | SessionEventBase<'compaction/start', CompactionStartPayload>\r\n | SessionEventBase<'compaction/end', CompactionEndPayload>\r\n | SessionEventBase<'meta', Record<string, unknown>>\r\n | SessionEventBase<'meta/patch', MetaPatchPayload>\r\n | SessionEventBase<'llm/retry', LLMRetryPayload>\r\n | SessionEventBase<'llm/retry-started', LLMRetryStartedPayload>\r\n | SessionEventBase<'turn/start', TurnStartPayload>\r\n | SessionEventBase<'turn/end', TurnEndPayload>\r\n | SessionEventBase<'step/start', StepStartPayload>\r\n | SessionEventBase<'step/end', StepEndPayload>\r\n\r\nfunction isMessageEventType(type: SessionEventType): type is MessageEventType {\r\n return type === 'user/message'\r\n || type === 'assistant/message'\r\n || type === 'system/message'\r\n}\r\n\r\nfunction isMessageEvent(\r\n event: SessionEvent,\r\n): event is Extract<SessionEvent, { type: MessageEventType }> {\r\n return isMessageEventType(event.type)\r\n}\r\n\r\nexport interface SessionConfig {\r\n file: string\r\n broadcast?: SessionBroadcast\r\n}\r\n\r\nexport interface CompactOptions {\r\n keep?: number\r\n keepTokens?: number\r\n summary?: string\r\n tokensBefore?: number\r\n messages?: ModelMessage[]\r\n}\r\n\r\nexport type ReplayReducer<T> = (state: T, event: SessionEvent) => T | Promise<T>\r\n\r\nexport type SessionBroadcast = (\r\n type: 'event' | 'flush',\r\n payload: unknown,\r\n) => void\r\n\r\nexport interface ContextUsage {\r\n tokens: number\r\n limit: number\r\n ratio: number\r\n}\r\n\r\nexport const DEFAULT_CONTEXT_LIMIT = 128_000\r\n\r\nfunction isSessionEvent(value: unknown): value is SessionEvent {\r\n if (typeof value !== 'object' || value === null) return false\r\n const record = value as Record<string, unknown>\r\n return typeof record.id === 'string'\r\n && typeof record.seq === 'number'\r\n && typeof record.ts === 'number'\r\n && typeof record.type === 'string'\r\n && 'payload' in record\r\n}\r\n\r\nfunction clone<T>(value: T): T {\r\n return structuredClone(value)\r\n}\r\n\r\nfunction previousSurfaceSeq(nodes: readonly number[]): number | undefined {\r\n return nodes.at(-1)\r\n}\r\n\r\n/**\r\n * Project an event list to model messages, in the list's own order. Each\r\n * surface event is mapped through {@link deriveEventMessage}; a `checkpoint`\r\n * splices its compacted prefix in at its position. This is the building block\r\n * both {@link deriveSurfaceMessages} (surface order) and callers holding a\r\n * compact slice (e.g. the events a summarizer will read) share.\r\n */\r\nexport function projectEvents(events: readonly SessionEvent[]): ModelMessage[] {\r\n const messages: ModelMessage[] = []\r\n for (const event of events) {\r\n if (event.type === 'checkpoint') {\r\n messages.splice(0, messages.length, ...clone(event.payload.messages))\r\n continue\r\n }\r\n const message = deriveEventMessage(event)\r\n if (message) messages.push(message)\r\n }\r\n return messages\r\n}\r\n\r\nexport function isAppendSurfaceEvent(event: SessionEvent): boolean {\r\n return isSurfaceEventType(event.type) && event.surfaceOp === 'append'\r\n}\r\n\r\nexport function isSurfaceEventType(type: SessionEventType): type is SurfaceEventType {\r\n return type === 'user/message'\r\n || type === 'system/message'\r\n || type === 'assistant/message'\r\n || type === 'tool/result'\r\n}\r\n\r\n/** A v7 checkpoint shadows a surface span; legacy ('replace') shadows nothing structurally. */\r\nexport function checkpointShadowRange(\r\n event: SessionEvent,\r\n): { start: number; end: number } | undefined {\r\n if (event.type !== 'checkpoint') return undefined\r\n const op = event.payload.surfaceOp\r\n if (typeof op === 'object' && op !== null && op.op === 'replace') {\r\n return { start: op.start, end: op.end }\r\n }\r\n return undefined\r\n}\r\n\r\n/** Detached current surface nodes plus every replacement's shadowed ranges. */\r\nexport interface SurfaceFoldReplacement {\r\n seq: number\r\n start: number\r\n end: number\r\n shadowedSeqs: number[]\r\n}\r\n\r\nexport interface SurfaceFoldResult {\r\n nodes: number[]\r\n replacements: SurfaceFoldReplacement[]\r\n}\r\n\r\n/**\r\n * Fold a log's surface operations into the current surface node order.\r\n *\r\n * The surface is *positional*: a replacement removes the live nodes between\r\n * the positions of `start`/`end` and inserts the replacing event's seq there,\r\n * so surface order is independent of raw seq order \u2014 after compaction the\r\n * checkpoint node may carry a higher seq than the kept tail that follows it.\r\n */\r\nexport function foldSurface(events: readonly SessionEvent[]): SurfaceFoldResult {\r\n const nodes: number[] = []\r\n const replacements: SurfaceFoldReplacement[] = []\r\n\r\n const applyReplace = (seq: number, start: number, end: number): void => {\r\n const startIndex = nodes.indexOf(start)\r\n if (startIndex < 0) {\r\n // The shadowed head is already gone (defensive; our writer always\r\n // shadows live nodes). Keep the prefix visible by anchoring at the tail.\r\n nodes.push(seq)\r\n return\r\n }\r\n const endIndex = nodes.indexOf(end, startIndex)\r\n if (endIndex < 0) {\r\n nodes.push(seq)\r\n return\r\n }\r\n const shadowed = nodes.splice(startIndex, endIndex - startIndex + 1)\r\n nodes.splice(startIndex, 0, seq)\r\n replacements.push({ seq, start, end, shadowedSeqs: shadowed })\r\n }\r\n\r\n for (const event of events) {\r\n if (event.type === 'checkpoint') {\r\n const range = checkpointShadowRange(event)\r\n if (!range) {\r\n // A range-less checkpoint is a prefix seed: it contributes its own\r\n // node at its surface position (used when compaction runs before any\r\n // durable history exists to shadow).\r\n nodes.push(event.seq)\r\n continue\r\n }\r\n applyReplace(event.seq, range.start, range.end)\r\n continue\r\n }\r\n if (!isSurfaceEventType(event.type)) continue\r\n const op = event.surfaceOp ?? 'append'\r\n if (op === 'append') {\r\n nodes.push(event.seq)\r\n continue\r\n }\r\n applyReplace(event.seq, op.start, op.end)\r\n }\r\n return { nodes, replacements }\r\n}\r\n\r\n/**\r\n * Derive one surface node's model-visible message. A content-less assistant\r\n * turn (e.g. a max-token cutoff with no output and no tool calls) is skipped\r\n * so it never enters a provider transcript; `tool/result` yields the `tool`\r\n * role message that pairs with the assistant's `toolCalls`.\r\n */\r\nexport function deriveEventMessage(event: SessionEvent): ModelMessage | null {\r\n switch (event.type) {\r\n case 'user/message': {\r\n const message: ModelMessage = { role: 'user', content: event.payload.content }\r\n if (event.payload.name) message.name = event.payload.name\r\n return message\r\n }\r\n case 'system/message': {\r\n const message: ModelMessage = { role: 'system', content: event.payload.content }\r\n if (event.payload.name) message.name = event.payload.name\r\n return message\r\n }\r\n case 'assistant/message': {\r\n const toolCalls = event.payload.toolCalls\r\n if (!event.payload.content && !(toolCalls && toolCalls.length)) return null\r\n const message: ModelMessage = { role: 'assistant', content: event.payload.content }\r\n if (event.payload.name) message.name = event.payload.name\r\n if (toolCalls?.length) message.tool_calls = clone(toolCalls)\r\n return message\r\n }\r\n case 'tool/result': {\r\n const failed = !event.payload.ok\r\n const message: ModelMessage = {\r\n role: 'tool',\r\n content: failed\r\n ? `error: ${event.payload.error?.message ?? 'unknown'}`\r\n : stringify(event.payload.output),\r\n tool_call_id: event.payload.toolCallId,\r\n }\r\n message.name = event.payload.name\r\n if (failed) {\r\n message.toolOk = false\r\n if (event.payload.error) message.toolError = event.payload.error\r\n }\r\n return message\r\n }\r\n default:\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Derive the model transcript strictly from the folded surface: walk the\r\n * surface nodes in surface position order (so a checkpoint's prefix is placed\r\n * where it replaced history, whatever its raw seq), mapping each node through\r\n * {@link deriveEventMessage}. This is the single source of the model's view;\r\n * raw log order never influences it.\r\n */\r\nexport function deriveSurfaceMessages(events: readonly SessionEvent[]): ModelMessage[] {\r\n const { nodes } = foldSurface(events)\r\n const bySeq = new Map(events.map(event => [event.seq, event] as const))\r\n const messages: ModelMessage[] = []\r\n for (const seq of nodes) {\r\n const event = bySeq.get(seq)\r\n if (!event) continue\r\n if (event.type === 'checkpoint') {\r\n messages.splice(0, messages.length, ...clone(event.payload.messages))\r\n continue\r\n }\r\n const message = deriveEventMessage(event)\r\n if (message) messages.push(message)\r\n }\r\n return messages\r\n}\r\n\r\n/**\r\n * Project the log into a human-facing transcript (the web view). Unlike the\r\n * model surface, a compaction does **not** hide the history it summarized:\r\n * shadowed messages are kept in place, and each live checkpoint is retained\r\n * as a marker exactly where it replaced them (with nested checkpoints\r\n * expanded recursively). Readers see the full conversation; the model keeps\r\n * deriving from the folded surface via {@link deriveSurfaceMessages}.\r\n */\r\nexport function transcriptEvents(events: readonly SessionEvent[]): SessionEvent[] {\r\n const bySeq = new Map(events.map(event => [event.seq, event] as const))\r\n const { nodes, replacements } = foldSurface(events)\r\n\r\n const shadowedOf = new Map<number, SessionEvent[]>()\r\n for (const replacement of replacements) {\r\n shadowedOf.set(replacement.seq, replacement.shadowedSeqs\r\n .map(seq => bySeq.get(seq))\r\n .filter((event): event is SessionEvent => event !== undefined))\r\n }\r\n\r\n const isVisible = (event: SessionEvent): boolean => (\r\n event.type !== 'system/message' && isSurfaceEventType(event.type)\r\n )\r\n\r\n const skeleton: SessionEvent[] = []\r\n const appendHistory = (checkpointSeq: number): void => {\r\n for (const shadowed of shadowedOf.get(checkpointSeq) ?? []) {\r\n if (shadowed.type === 'checkpoint') appendHistory(shadowed.seq)\r\n else if (isVisible(shadowed)) skeleton.push(shadowed)\r\n }\r\n }\r\n for (const seq of nodes) {\r\n const event = bySeq.get(seq)\r\n if (!event) continue\r\n if (event.type === 'checkpoint') appendHistory(event.seq)\r\n skeleton.push(event)\r\n }\r\n\r\n // Declared tool calls travel on their assistant message, so the matching\r\n // log-only `tool/call` records would double-render: drop those, keep the\r\n // legacy ones that have no assistant declaration.\r\n const declaredCalls = new Set<string>()\r\n for (const event of events) {\r\n if (event.type === 'assistant/message') {\r\n for (const call of event.payload.toolCalls ?? []) declaredCalls.add(call.id)\r\n }\r\n }\r\n const inSkeleton = new Set(skeleton.map(event => event.id))\r\n const structure = events\r\n .filter(event => !inSkeleton.has(event.id))\r\n .filter(event => event.type !== 'compaction/start' && event.type !== 'compaction/end')\r\n .filter(event => event.type !== 'checkpoint') // superseded markers: history shown via the live chain\r\n .filter(event => !(event.type === 'tool/call' && declaredCalls.has(event.payload.id)))\r\n\r\n const ordered = [...skeleton]\r\n for (const event of structure) {\r\n let index = ordered.length\r\n for (let cursor = ordered.length - 1; cursor >= 0; cursor -= 1) {\r\n if (ordered[cursor]!.seq <= event.seq) {\r\n index = cursor + 1\r\n break\r\n }\r\n }\r\n ordered.splice(index, 0, event)\r\n }\r\n return ordered\r\n}\r\n\r\nexport function foldRequestHeader(events: readonly SessionEvent[]): RequestHeaderPayload | undefined {\r\n let header: RequestHeaderPayload | undefined\r\n for (const event of events) {\r\n if (event.type === 'request/header') header = clone(event.payload)\r\n }\r\n return header\r\n}\r\n\r\nexport function foldRequestContext(events: readonly SessionEvent[]): RequestContextPayload | undefined {\r\n let context: RequestContextPayload | undefined\r\n for (const event of events) {\r\n if (event.type === 'request/context') context = clone(event.payload)\r\n }\r\n return context\r\n}\r\n\r\n/**\r\n * Session display metadata, rebuilt from the immutable events: the head\r\n * `meta` base followed by every `meta/patch` in seq order. No mutable\r\n * side file or in-place rewrite is involved, so title/mode/agentType stay\r\n * replayable and fork-clean like every other durable fact.\r\n */\r\nexport function foldSessionMeta(events: readonly SessionEvent[]): {\r\n title?: string\r\n agentType?: AgentType\r\n mode?: SessionMode\r\n} {\r\n const meta: { title?: string; agentType?: AgentType; mode?: SessionMode } = {}\r\n for (const event of events) {\r\n if (event.type === 'meta') {\r\n const payload = event.payload as Record<string, unknown>\r\n if (typeof payload.title === 'string') meta.title = payload.title\r\n if (payload.agentType === 'general' || payload.agentType === 'coding') {\r\n meta.agentType = payload.agentType\r\n }\r\n if (payload.mode === 'auto' || payload.mode === 'plan' || payload.mode === 'execute') {\r\n meta.mode = payload.mode\r\n }\r\n continue\r\n }\r\n if (event.type === 'meta/patch') {\r\n const patch = event.payload\r\n if (patch.fields.includes('title') && typeof patch.title === 'string') {\r\n meta.title = patch.title\r\n }\r\n if (patch.fields.includes('agentType') && patch.agentType !== undefined) {\r\n meta.agentType = patch.agentType\r\n }\r\n if (patch.fields.includes('mode') && patch.mode !== undefined) {\r\n meta.mode = patch.mode\r\n }\r\n }\r\n }\r\n return meta\r\n}\r\n\r\n/** Convenience wrapper: fold session metadata from a `SessionLog`'s events. */\r\nexport function foldMetaFromLog(log: { read(): Promise<SessionEvent[]> }): Promise<{\r\n title?: string\r\n agentType?: AgentType\r\n mode?: SessionMode\r\n}> {\r\n return log.read().then(foldSessionMeta)\r\n}\r\n\r\nfunction stringify(value: unknown): string {\r\n if (typeof value === 'string') return value\r\n try {\r\n return JSON.stringify(value) ?? String(value)\r\n } catch {\r\n return String(value)\r\n }\r\n}\r\n\r\nexport function estimateMessageTokens(messages: readonly ModelMessage[]): number {\r\n let tokens = 0\r\n for (const message of messages) {\r\n tokens += Math.ceil(message.content.length / 4)\r\n for (const call of message.tool_calls ?? []) {\r\n const raw = JSON.stringify(call.arguments ?? {}) ?? ''\r\n tokens += Math.ceil(raw.length / 4)\r\n }\r\n }\r\n return tokens\r\n}\r\n\r\nexport function estimateEventTokens(event: SessionEvent): number {\r\n switch (event.type) {\r\n case 'user/message':\r\n case 'system/message':\r\n return Math.ceil(event.payload.content.length / 4)\r\n case 'assistant/message': {\r\n let tokens = Math.ceil(event.payload.content.length / 4)\r\n for (const call of event.payload.toolCalls ?? []) {\r\n const raw = JSON.stringify(call.arguments ?? {}) ?? ''\r\n tokens += Math.ceil(raw.length / 4)\r\n }\r\n return tokens\r\n }\r\n case 'assistant/chunk':\r\n return Math.ceil(event.payload.content.length / 4)\r\n case 'tool/call': {\r\n const raw = JSON.stringify(event.payload.arguments ?? {}) ?? ''\r\n return Math.ceil(raw.length / 4)\r\n }\r\n case 'tool/result': {\r\n const raw = event.payload.ok\r\n ? stringify(event.payload.output)\r\n : event.payload.error?.message ?? 'error'\r\n return Math.ceil(raw.length / 4)\r\n }\r\n case 'plan':\r\n return 0\r\n case 'checkpoint':\r\n return estimateMessageTokens(event.payload.messages)\r\n case 'compaction/start':\r\n case 'compaction/end':\r\n case 'meta':\r\n case 'meta/patch':\r\n return 0\r\n case 'llm/retry':\r\n case 'llm/retry-started':\r\n return 0\r\n case 'request/header':\r\n case 'request/context':\r\n case 'agent/inbox/spliced':\r\n return 0\r\n case 'turn/start':\r\n case 'turn/end':\r\n case 'step/start':\r\n case 'step/end':\r\n return 0\r\n }\r\n}\r\n\r\nexport function suffixStartIndexForTokens(\r\n events: readonly SessionEvent[],\r\n targetTokens: number,\r\n): number {\r\n if (!events.length || targetTokens <= 0) return 0\r\n let tokens = 0\r\n let candidate = events.length\r\n for (let index = events.length - 1; index >= 0; index -= 1) {\r\n tokens += estimateEventTokens(events[index]!)\r\n if (tokens >= targetTokens) {\r\n candidate = index\r\n break\r\n }\r\n }\r\n if (candidate === events.length) return 0\r\n let cut = candidate\r\n while (cut < events.length) {\r\n const event = events[cut]\r\n if (event?.type === 'user/message') return cut\r\n cut += 1\r\n }\r\n return candidate\r\n}\r\n\r\nexport function safeCompactSplit(\r\n events: readonly SessionEvent[],\r\n splitIndex: number,\r\n): number {\r\n const index = Math.max(0, Math.min(splitIndex, events.length))\r\n const first = events[index]\r\n if (first?.type === 'tool/result') {\r\n const callId = first.payload.toolCallId\r\n for (let cursor = index - 1; cursor >= 0; cursor -= 1) {\r\n const candidate = events[cursor]\r\n if (candidate?.type === 'tool/call' && candidate.payload.id === callId) {\r\n return cursor\r\n }\r\n }\r\n }\r\n\r\n let openCalls = 0\r\n for (let cursor = 0; cursor <= events.length; cursor += 1) {\r\n if (cursor >= index && openCalls === 0) return cursor\r\n const event = events[cursor]\r\n if (!event) continue\r\n if (event.type === 'tool/call') openCalls += 1\r\n if (event.type === 'tool/result') openCalls = Math.max(0, openCalls - 1)\r\n }\r\n return index\r\n}\r\n\r\nexport function repairUnclosed(\r\n events: readonly SessionEvent[],\r\n): SessionEvent[] {\r\n const openCalls: Extract<SessionEvent, { type: 'tool/call' }>[] = []\r\n const openSteps: Extract<SessionEvent, { type: 'step/start' }>[] = []\r\n const openTurns: Extract<SessionEvent, { type: 'turn/start' }>[] = []\r\n for (const event of events) {\r\n switch (event.type) {\r\n case 'tool/call':\r\n openCalls.push(event)\r\n break\r\n case 'tool/result': {\r\n const callIndex = openCalls.findIndex(\r\n call => call.payload.id === event.payload.toolCallId,\r\n )\r\n if (callIndex >= 0) openCalls.splice(callIndex, 1)\r\n else openCalls.pop()\r\n break\r\n }\r\n case 'step/start':\r\n openSteps.push(event)\r\n break\r\n case 'step/end':\r\n openSteps.pop()\r\n break\r\n case 'turn/start':\r\n openTurns.push(event)\r\n break\r\n case 'turn/end':\r\n openTurns.pop()\r\n break\r\n }\r\n }\r\n\r\n if (!openCalls.length && !openSteps.length && !openTurns.length) return []\r\n\r\n const synthetic: SessionEvent[] = []\r\n let nextSeq = (events.at(-1)?.seq ?? 0) + 1\r\n let nextTs = (events.at(-1)?.ts ?? Date.now()) + 1\r\n const push = (type: SessionEventType, payload: SessionEvent['payload']): void => {\r\n synthetic.push({\r\n id: randomUUID(),\r\n seq: nextSeq,\r\n ts: nextTs,\r\n type: type as SessionEvent['type'],\r\n payload: clone(payload),\r\n } as SessionEvent)\r\n nextSeq += 1\r\n nextTs += 1\r\n }\r\n\r\n for (const call of openCalls) {\r\n push('tool/result', {\r\n id: call.payload.id,\r\n toolCallId: call.payload.id,\r\n name: call.payload.name,\r\n ok: false,\r\n error: {\r\n name: 'SessionInterruptedError',\r\n message: 'session interrupted before tool result was recorded',\r\n },\r\n })\r\n }\r\n for (const step of openSteps) {\r\n push('step/end', {\r\n turn: step.payload.turn,\r\n step: step.payload.step,\r\n finishReason: 'interrupted',\r\n interrupted: true,\r\n })\r\n }\r\n for (let i = 0; i < openTurns.length; i++) {\r\n const turn = openTurns[i]!.payload.turn\r\n push('turn/end', {\r\n turn,\r\n finishReason: 'interrupted',\r\n interrupted: true,\r\n })\r\n }\r\n return synthetic\r\n}\r\n\r\nexport function resolveCompactKeep(\r\n events: readonly SessionEvent[],\r\n options: CompactOptions,\r\n): number {\r\n if (\r\n typeof options.keepTokens === 'number'\r\n && Number.isFinite(options.keepTokens)\r\n && options.keepTokens > 0\r\n ) {\r\n return events.length - suffixStartIndexForTokens(events, options.keepTokens)\r\n }\r\n const keep = typeof options.keep === 'number' && Number.isFinite(options.keep) && options.keep > 0\r\n ? Math.floor(options.keep)\r\n : 0\r\n return Math.min(keep, events.length)\r\n}\r\n\r\nexport function estimateContextUsage(\r\n messages: readonly ModelMessage[],\r\n limit = DEFAULT_CONTEXT_LIMIT,\r\n): ContextUsage {\r\n const tokens = estimateMessageTokens(messages)\r\n return {\r\n tokens,\r\n limit,\r\n ratio: limit > 0 ? tokens / limit : 0,\r\n }\r\n}\r\n\r\nexport class SessionLog {\r\n private _events: SessionEvent[] = []\r\n private _surface: ModelMessage[] = []\r\n private _surfaceNodes: number[] = []\r\n private _requestHeader: RequestHeaderPayload | undefined\r\n private _requestContext: RequestContextPayload | undefined\r\n private _loaded = false\r\n private _nextSeq = 1\r\n private _queue: Promise<unknown> = Promise.resolve()\r\n private _writeTail: Promise<void> = Promise.resolve()\r\n private _pending: SessionEvent[] = []\r\n private _drainScheduled = false\r\n private _writeError: unknown\r\n\r\n constructor(\r\n readonly file: string,\r\n private _broadcast?: SessionBroadcast,\r\n ) {}\r\n\r\n init(): Promise<void> {\r\n return this._run(async () => {\r\n const key = sessionFileKey(this.file)\r\n if (!liveSessions.has(key)) liveSessions.set(key, this)\r\n try {\r\n await this._ensureLoaded()\r\n await this._drainWrite()\r\n if (this._writeError) {\r\n const error = this._writeError\r\n this._writeError = undefined\r\n throw error\r\n }\r\n } catch (error) {\r\n if (liveSessions.get(key) === this) liveSessions.delete(key)\r\n throw error\r\n }\r\n })\r\n }\r\n\r\n append(type: 'user/message', payload: UserMessagePayload): Promise<SessionEvent>\r\n append(type: 'assistant/message', payload: AssistantMessagePayload): Promise<SessionEvent>\r\n append(type: 'assistant/chunk', payload: AssistantChunkPayload): Promise<SessionEvent>\r\n append(type: 'system/message', payload: SystemMessagePayload): Promise<SessionEvent>\r\n append(type: 'tool/call', payload: ToolCallPayload): Promise<SessionEvent>\r\n append(type: 'tool/result', payload: ToolResultPayload): Promise<SessionEvent>\r\n append(type: 'request/header', payload: RequestHeaderPayload): Promise<SessionEvent>\r\n append(type: 'request/context', payload: RequestContextPayload): Promise<SessionEvent>\r\n append(type: 'agent/inbox/spliced', payload: InboxSplicePayload): Promise<SessionEvent>\r\n append(type: 'plan', payload: PlanPayload): Promise<SessionEvent>\r\n append(type: 'checkpoint', payload: CheckpointPayload): Promise<SessionEvent>\r\n append(type: 'compaction/start', payload: CompactionStartPayload): Promise<SessionEvent>\r\n append(type: 'compaction/end', payload: CompactionEndPayload): Promise<SessionEvent>\r\n append(type: 'meta', payload: Record<string, unknown>): Promise<SessionEvent>\r\n append(type: 'meta/patch', payload: MetaPatchPayload): Promise<SessionEvent>\r\n append(type: 'llm/retry', payload: LLMRetryPayload): Promise<SessionEvent>\r\n append(type: 'llm/retry-started', payload: LLMRetryStartedPayload): Promise<SessionEvent>\r\n append(type: 'turn/start', payload: TurnStartPayload): Promise<SessionEvent>\r\n append(type: 'turn/end', payload: TurnEndPayload): Promise<SessionEvent>\r\n append(type: 'step/start', payload: StepStartPayload): Promise<SessionEvent>\r\n append(type: 'step/end', payload: StepEndPayload): Promise<SessionEvent>\r\n append(type: SessionEventType, payload: SessionEvent['payload']): Promise<SessionEvent> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const event = this._buildEvent(type, payload)\r\n this._commitEvent(event)\r\n return event\r\n })\r\n }\r\n\r\n private _buildEvent(\r\n type: SessionEventType,\r\n payload: SessionEvent['payload'],\r\n ): SessionEvent {\r\n const eventPayload = clone(payload)\r\n const event = {\r\n id: randomUUID(),\r\n seq: this._nextSeq,\r\n ts: Date.now(),\r\n type: type as SessionEvent['type'],\r\n payload: eventPayload,\r\n } as SessionEvent\r\n if (isMessageEventType(type)) {\r\n for (let index = this._events.length - 1; index >= 0; index -= 1) {\r\n const previous = this._events[index]\r\n if (previous && isMessageEvent(previous)) {\r\n const messagePayload = eventPayload as MessageEventPayload\r\n messagePayload.parentId ??= previous.id\r\n break\r\n }\r\n }\r\n event.surfaceOp ??= 'append'\r\n const previous = previousSurfaceSeq(this._surfaceNodes)\r\n event.sourceEventSeqs ??= previous === undefined ? [] : [previous]\r\n } else if (type === 'request/header') {\r\n event.sourceEventSeqs ??= []\r\n } else if (type === 'request/context') {\r\n event.sourceEventSeqs ??= []\r\n } else if (type === 'agent/inbox/spliced') {\r\n event.sourceEventSeqs ??= []\r\n }\r\n this._nextSeq += 1\r\n return event\r\n }\r\n\r\n private _commitEvent(event: SessionEvent): void {\r\n this._events.push(event)\r\n this._refreshSurface()\r\n if (event.type === 'request/header') this._requestHeader = clone(event.payload)\r\n if (event.type === 'request/context') this._requestContext = clone(event.payload)\r\n this._broadcast?.('event', event)\r\n this._enqueue(event)\r\n }\r\n\r\n /**\r\n * Rebuild both surface views from the committed log. The nodes come from the\r\n * positional {@link foldSurface} fold; the messages come from\r\n * {@link deriveSurfaceMessages}, which walks those nodes in surface order \u2014\r\n * the model view is a pure function of the surface, never of raw file order.\r\n */\r\n private _refreshSurface(): void {\r\n this._surfaceNodes = foldSurface(this._events).nodes\r\n this._surface = deriveSurfaceMessages(this._events)\r\n }\r\n\r\n private _enqueue(event: SessionEvent): void {\r\n this._pending.push(event)\r\n if (this._drainScheduled) return\r\n this._drainScheduled = true\r\n setImmediate(() => {\r\n this._drainScheduled = false\r\n void this._drainWrite()\r\n })\r\n }\r\n\r\n private _drainWrite(): Promise<void> {\r\n const events = this._pending.splice(0)\r\n if (!events.length) return this._writeTail\r\n const write = this._writeTail.then(() => appendFile(\r\n this.file,\r\n `${events.map(event => JSON.stringify(event)).join('\\n')}\\n`,\r\n 'utf8',\r\n ))\r\n this._writeTail = write.catch((error) => {\r\n this._writeError ??= error\r\n })\r\n return write.then(() => undefined, () => undefined)\r\n }\r\n\r\n flush(): Promise<number> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n await this._drainWrite()\r\n if (this._writeError) {\r\n const error = this._writeError\r\n this._writeError = undefined\r\n throw error\r\n }\r\n const seq = this._nextSeq - 1\r\n this._broadcast?.('flush', { file: this.file, seq })\r\n return seq\r\n })\r\n }\r\n\r\n async lineage(messageId: string): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return this._resolveLineage(messageId)\r\n })\r\n }\r\n\r\n /** The next turn number: one more than the highest recorded `turn/start`. */\r\n async nextTurn(): Promise<number> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n let max = 0\r\n for (const event of this._events) {\r\n if (event.type === 'turn/start') max = Math.max(max, event.payload.turn)\r\n }\r\n return max + 1\r\n })\r\n }\r\n\r\n async forkAt(messageId: string): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const lineage = this._resolveLineage(messageId)\r\n const lineageIds = new Set(lineage.map(event => event.id))\r\n const targetIndex = this._events.findIndex(\r\n event => event.id === messageId && isMessageEvent(event),\r\n )\r\n if (targetIndex < 0) {\r\n throw new Error(`message not found: ${messageId}`)\r\n }\r\n const selected: SessionEvent[] = []\r\n let skippedMessage = false\r\n for (let index = 0; index <= targetIndex; index += 1) {\r\n const event = this._events[index]!\r\n if (isMessageEvent(event)) {\r\n if (!lineageIds.has(event.id)) {\r\n skippedMessage = true\r\n continue\r\n }\r\n skippedMessage = false\r\n selected.push(clone(event))\r\n continue\r\n }\r\n if (\r\n event.type === 'meta'\r\n || event.type === 'llm/retry'\r\n || event.type === 'llm/retry-started'\r\n || event.type.startsWith('turn/')\r\n || event.type.startsWith('step/')\r\n ) {\r\n continue\r\n }\r\n if (event.type === 'checkpoint') {\r\n selected.push(clone(event))\r\n continue\r\n }\r\n if (!skippedMessage) {\r\n selected.push(clone(event))\r\n }\r\n }\r\n return selected\r\n })\r\n }\r\n\r\n read(): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return this._events.map(event => clone(event))\r\n })\r\n }\r\n\r\n deriveMessages(): Promise<ModelMessage[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return clone(this._surface)\r\n })\r\n }\r\n\r\n /** The ordered surface events, after compaction replacements are applied. */\r\n surfaceEvents(): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const bySeq = new Map(this._events.map(event => [event.seq, event] as const))\r\n return this._surfaceNodes\r\n .map(seq => bySeq.get(seq))\r\n .filter((event): event is SessionEvent => event !== undefined)\r\n .map(event => clone(event))\r\n })\r\n }\r\n\r\n /** The latest `request/header` snapshot, or undefined before the first one. */\r\n requestHeader(): RequestHeaderPayload | undefined {\r\n return this._requestHeader ? clone(this._requestHeader) : undefined\r\n }\r\n\r\n /** The latest resolved route metadata, or undefined before the first one. */\r\n requestContext(): RequestContextPayload | undefined {\r\n return this._requestContext ? clone(this._requestContext) : undefined\r\n }\r\n\r\n /** Display metadata folded from the durable `meta` + `meta/patch` events. */\r\n meta(): Promise<{ title?: string; agentType?: AgentType; mode?: SessionMode }> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return foldSessionMeta(this._events)\r\n })\r\n }\r\n\r\n estimateContext(limit = DEFAULT_CONTEXT_LIMIT): Promise<ContextUsage> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return estimateContextUsage(this._surface, limit)\r\n })\r\n }\r\n\r\n replay(): Promise<SessionEvent[]>\r\n replay<T>(reduce: ReplayReducer<T>, initial: T): Promise<T>\r\n replay<T>(reduce?: ReplayReducer<T>, initial?: T): Promise<SessionEvent[] | T> {\r\n return this.read().then((events) => {\r\n if (!reduce) return events\r\n let state = initial as T\r\n let pending: Promise<void> = Promise.resolve()\r\n for (const event of events) {\r\n pending = pending.then(async () => {\r\n state = await reduce(state, event)\r\n })\r\n }\r\n return pending.then(() => state)\r\n })\r\n }\r\n\r\n /** Structural violations in the loaded event stream (empty = balanced). */\r\n runInvariants(): Promise<SessionInvariantFailure[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return checkSessionInvariants(this._events)\r\n })\r\n }\r\n\r\n /**\r\n * Compact the session without ever rewriting history.\r\n *\r\n * The surface (not the raw log) is the unit of compaction. A kept suffix \u2014\r\n * measured in tokens via `keepTokens`, or in nodes via `keep`, and always\r\n * snapped to start on a fresh `user/message` turn so a tool result never\r\n * outlives the assistant call it answers \u2014 stays visible; every earlier\r\n * surface node is shadowed by the checkpoint's `surfaceOp.replace` range.\r\n * The three compaction events are appended like any other event, so seqs\r\n * stay immutable, live observers see the transition, and a crash can only\r\n * leave an orphaned `compaction/start` rather than a torn rewrite.\r\n *\r\n * Returns the new event count, or the unchanged count when there is no\r\n * useful surface span to shadow.\r\n */\r\n compact(options: CompactOptions = {}): Promise<number> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const events = this._events\r\n const bySeq = new Map(events.map(event => [event.seq, event] as const))\r\n const surfaceEvents = foldSurface(events).nodes\r\n .map(seq => bySeq.get(seq))\r\n .filter((event): event is SessionEvent => event !== undefined)\r\n\r\n const keep = resolveCompactKeep(surfaceEvents, options)\r\n let split = Math.max(0, surfaceEvents.length - keep)\r\n // The retained tail must open on a fresh user turn: shadowing an\r\n // assistant step while keeping its tool results would leave an orphan\r\n // tool message at the head of the transcript. Prefer the next user\r\n // boundary; when the candidate sits inside the final turn, keep that\r\n // whole turn instead of cutting it.\r\n if (split < surfaceEvents.length && surfaceEvents[split]!.type !== 'user/message') {\r\n let next = split\r\n while (next < surfaceEvents.length && surfaceEvents[next]!.type !== 'user/message') next += 1\r\n if (next < surfaceEvents.length) {\r\n split = next\r\n } else {\r\n let lastUser = surfaceEvents.length - 1\r\n while (lastUser >= 0 && surfaceEvents[lastUser]!.type !== 'user/message') lastUser -= 1\r\n split = lastUser >= 0 ? lastUser : surfaceEvents.length\r\n }\r\n }\r\n const shadowed = surfaceEvents.slice(0, split)\r\n if (!options.messages?.length) return this._events.length\r\n\r\n // A compaction needs a summary to write. When there is history to\r\n // shadow the checkpoint replaces that head span; when there is none (a\r\n // budget trip before anything is durable) the checkpoint seeds the\r\n // surface with the compacted prefix instead.\r\n const range = shadowed.length\r\n ? { start: shadowed[0]!.seq, end: shadowed[shadowed.length - 1]!.seq }\r\n : undefined\r\n const boundary = split\r\n this._commitEvent(this._buildEvent('compaction/start', {\r\n ...(boundary > 0 ? { boundary } : {}),\r\n ...(options.keep !== undefined ? { keep: options.keep } : {}),\r\n ...(options.tokensBefore !== undefined ? { tokensBefore: options.tokensBefore } : {}),\r\n }))\r\n const checkpoint = this._buildEvent('checkpoint', {\r\n messages: clone(options.messages),\r\n ...(range ? { surfaceOp: { op: 'replace', start: range.start, end: range.end } } : {}),\r\n ...(options.summary ? { summary: options.summary } : {}),\r\n ...(options.tokensBefore !== undefined ? { tokensBefore: options.tokensBefore } : {}),\r\n })\r\n this._commitEvent(checkpoint)\r\n this._commitEvent(this._buildEvent('compaction/end', {\r\n checkpointId: checkpoint.id,\r\n ...(options.keep !== undefined ? { keep: options.keep } : {}),\r\n }))\r\n return this._events.length\r\n })\r\n }\r\n\r\n close(): Promise<void> {\r\n return this._queue.then(async () => {\r\n await this._drainWrite()\r\n if (this._writeError) {\r\n const error = this._writeError\r\n this._writeError = undefined\r\n throw error\r\n }\r\n const key = sessionFileKey(this.file)\r\n if (liveSessions.get(key) === this) liveSessions.delete(key)\r\n })\r\n }\r\n\r\n private _run<T>(task: () => Promise<T>): Promise<T> {\r\n const next = this._queue.then(task, task)\r\n this._queue = next.then(() => undefined, () => undefined)\r\n return next\r\n }\r\n\r\n private async _ensureLoaded(): Promise<void> {\r\n if (this._loaded) return\r\n const key = sessionFileKey(this.file)\r\n const owner = liveSessions.get(key)\r\n if (owner && owner !== this && owner._loaded) {\r\n this._events = owner._events.map(event => clone(event))\r\n this._surface = clone(owner._surface)\r\n this._surfaceNodes = [...owner._surfaceNodes]\r\n this._requestHeader = owner._requestHeader ? clone(owner._requestHeader) : undefined\r\n this._requestContext = owner._requestContext ? clone(owner._requestContext) : undefined\r\n this._nextSeq = owner._nextSeq\r\n this._loaded = true\r\n return\r\n }\r\n await mkdir(dirname(resolve(this.file)), { recursive: true })\r\n const read = await this._read()\r\n if (!read.existing) {\r\n const meta: SessionEvent = {\r\n id: randomUUID(),\r\n seq: 1,\r\n ts: Date.now(),\r\n type: 'meta',\r\n payload: { formatVersion: SESSION_FORMAT_VERSION },\r\n }\r\n await writeFile(this.file, `${JSON.stringify(meta)}\\n`, 'utf8')\r\n this._events = [meta]\r\n this._refreshSurface()\r\n this._nextSeq = 2\r\n this._loaded = true\r\n return\r\n }\r\n this._events = read.events\r\n this._refreshSurface()\r\n this._requestHeader = foldRequestHeader(this._events)\r\n this._requestContext = foldRequestContext(this._events)\r\n this._nextSeq = (this._events.at(-1)?.seq ?? 0) + 1\r\n this._loaded = true\r\n }\r\n\r\n private async _read(): Promise<{ existing: boolean; events: SessionEvent[] }> {\r\n let text: string\r\n try {\r\n text = await readFile(this.file, 'utf8')\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return { existing: false, events: [] }\r\n }\r\n throw error\r\n }\r\n if (!text.trim()) return { existing: false, events: [] }\r\n\r\n const lines = text.split('\\n')\r\n const events: SessionEvent[] = []\r\n let torn = false\r\n for (const line of lines) {\r\n if (!line.trim()) continue\r\n if (torn) continue\r\n let parsed: unknown\r\n try {\r\n parsed = JSON.parse(line)\r\n } catch {\r\n torn = true\r\n continue\r\n }\r\n if (!isSessionEvent(parsed)) {\r\n torn = true\r\n continue\r\n }\r\n events.push(parsed)\r\n }\r\n\r\n this._assertFormat(events)\r\n\r\n const key = sessionFileKey(this.file)\r\n const owner = liveSessions.get(key)\r\n if (owner && owner !== this) {\r\n // A live writer owns this log; never synthesize closures over its active turn.\r\n return { existing: true, events }\r\n }\r\n\r\n const synthetic = repairUnclosed(events)\r\n if (!torn && !synthetic.length) return { existing: true, events }\r\n\r\n const repaired = [...events, ...synthetic]\r\n await writeFile(\r\n this.file,\r\n `${repaired.map(event => JSON.stringify(event)).join('\\n')}\\n`,\r\n 'utf8',\r\n )\r\n return { existing: true, events: repaired }\r\n }\r\n\r\n private _assertFormat(events: readonly SessionEvent[]): void {\r\n let version: number | undefined\r\n for (const event of events) {\r\n if (event.type !== 'meta') continue\r\n const value = (event.payload as Record<string, unknown>).formatVersion\r\n if (typeof value === 'number') version = value\r\n }\r\n if (version === undefined) {\r\n throw new SessionFormatError(\r\n `session log is missing formatVersion; expected ${SESSION_FORMAT_VERSION}`,\r\n )\r\n }\r\n if (version !== SESSION_FORMAT_VERSION) {\r\n throw new SessionFormatError(\r\n `unsupported session format version ${version}; expected ${SESSION_FORMAT_VERSION}`,\r\n )\r\n }\r\n }\r\n\r\n private _resolveLineage(messageId: string): SessionEvent[] {\r\n const messages = this._events.filter(\r\n (event): event is Extract<SessionEvent, { type: MessageEventType }> => isMessageEvent(event),\r\n )\r\n const byId = new Map<string, Extract<SessionEvent, { type: MessageEventType }>>(\r\n messages.map(event => [event.id, event] as const),\r\n )\r\n if (!byId.has(messageId)) {\r\n throw new Error(`message not found: ${messageId}`)\r\n }\r\n const chain: SessionEvent[] = []\r\n const seen = new Set<string>()\r\n let cursorId: string | undefined = messageId\r\n while (cursorId && byId.has(cursorId) && !seen.has(cursorId)) {\r\n seen.add(cursorId)\r\n const event: Extract<SessionEvent, { type: MessageEventType }> = byId.get(cursorId)!\r\n chain.unshift(clone(event))\r\n const parentId: string | undefined = event.payload.parentId\r\n if (parentId && byId.has(parentId)) {\r\n cursorId = parentId\r\n continue\r\n }\r\n const index = messages.findIndex(message => message.id === cursorId)\r\n cursorId = index > 0 ? messages[index - 1]!.id : undefined\r\n }\r\n return chain\r\n }\r\n}\r\n\r\nexport const session = {\r\n name: 'session',\r\n apply: async (ctx: Context, config: SessionConfig) => {\r\n const log = new SessionLog(config.file, config.broadcast ?? ((type, payload) => {\r\n if (type === 'event') ctx.emit('session/event', payload)\r\n else ctx.emit('session/flush', payload)\r\n }))\r\n await log.init()\r\n ctx.provide('session', log)\r\n return () => log.close()\r\n },\r\n}\r\n\r\nexport * from './invariant.js'\r\n\r\nexport const name = '@tnega/session'\r\n", "/**\r\n * Session invariant companion.\r\n *\r\n * Pure structural checks over a session event stream, mirroring the DSH\r\n * \"each package ships a ./invariant\" discipline. They assert the relationships\r\n * the event-sourcing layer promises:\r\n *\r\n * - durable lifecycle structures (turn / step / tool call) stay balanced \u2014\r\n * every `turn/start` has a `turn/end`, every `tool/call` a `tool/result`;\r\n * - seq stays strictly monotonic;\r\n * - message events that reach the model surface stay append-only unless they\r\n * belong to a compaction replacement.\r\n *\r\n * The checks are pure over `SessionEvent[]`, so they run equally on a full\r\n * log, on a single newly-committed event, or in tests. Recovery (repairing an\r\n * interrupted log into a closed one) is `repairUnclosed`; these checks assert\r\n * that the result is actually legal.\r\n */\r\n\r\nimport type { SessionEvent, ToolResultPayload } from './index.js'\r\n\r\nexport interface SessionInvariantFailure {\r\n name: string\r\n detail: string\r\n}\r\n\r\n/** Balanced `turn/start` \u2194 `turn/end` per turn number. */\r\nexport function checkBalancedTurns(events: readonly SessionEvent[]): SessionInvariantFailure[] {\r\n const opens = new Map<number, number>()\r\n const failures: SessionInvariantFailure[] = []\r\n for (const event of events) {\r\n if (event.type === 'turn/start') {\r\n opens.set(event.payload.turn, (opens.get(event.payload.turn) ?? 0) + 1)\r\n } else if (event.type === 'turn/end') {\r\n const remaining = opens.get(event.payload.turn) ?? 0\r\n if (remaining <= 0) {\r\n failures.push({\r\n name: 'turn/end-without-start',\r\n detail: `turn/end for turn ${event.payload.turn} has no matching turn/start`,\r\n })\r\n } else {\r\n opens.set(event.payload.turn, remaining - 1)\r\n }\r\n }\r\n }\r\n for (const [turn, count] of opens) {\r\n for (let index = 0; index < count; index += 1) {\r\n failures.push({\r\n name: 'turn/start-without-end',\r\n detail: `turn/start ${turn} has no matching turn/end`,\r\n })\r\n }\r\n }\r\n return failures\r\n}\r\n\r\n/** Balanced `step/start` \u2194 `step/end` per (turn, step). */\r\nexport function checkBalancedSteps(events: readonly SessionEvent[]): SessionInvariantFailure[] {\r\n const open = new Set<string>()\r\n const failures: SessionInvariantFailure[] = []\r\n for (const event of events) {\r\n if (event.type === 'step/start') {\r\n const key = `${event.payload.turn}:${event.payload.step}`\r\n if (open.has(key)) {\r\n failures.push({\r\n name: 'duplicate-step-start',\r\n detail: `step/start for ${key} appears twice without a matching step/end`,\r\n })\r\n }\r\n open.add(key)\r\n } else if (event.type === 'step/end') {\r\n const key = `${event.payload.turn}:${event.payload.step}`\r\n if (!open.delete(key)) {\r\n failures.push({\r\n name: 'step/end-without-start',\r\n detail: `step/end for ${key} has no matching step/start`,\r\n })\r\n }\r\n }\r\n }\r\n for (const key of open) {\r\n failures.push({\r\n name: 'step/start-without-end',\r\n detail: `step/start ${key} has no matching step/end`,\r\n })\r\n }\r\n return failures\r\n}\r\n\r\n/** Balanced `tool/call` \u2194 `tool/result` per call id. */\r\nexport function checkBalancedToolCalls(events: readonly SessionEvent[]): SessionInvariantFailure[] {\r\n const open = new Set<string>()\r\n const failures: SessionInvariantFailure[] = []\r\n for (const event of events) {\r\n if (event.type === 'tool/call') {\r\n open.add(event.payload.id)\r\n } else if (event.type === 'tool/result') {\r\n const payload = event.payload as ToolResultPayload\r\n const id = payload.toolCallId\r\n if (!open.delete(id)) {\r\n failures.push({\r\n name: 'tool/result-without-call',\r\n detail: `tool/result for call ${id} has no matching tool/call`,\r\n })\r\n }\r\n }\r\n }\r\n for (const id of open) {\r\n failures.push({\r\n name: 'tool/call-without-result',\r\n detail: `tool/call ${id} has no matching tool/result`,\r\n })\r\n }\r\n return failures\r\n}\r\n\r\n/** seq is strictly monotonic (an append-only log never rewinds). */\r\nexport function checkMonotonicSeq(events: readonly SessionEvent[]): SessionInvariantFailure[] {\r\n const failures: SessionInvariantFailure[] = []\r\n for (let index = 1; index < events.length; index += 1) {\r\n const previous = events[index - 1]\r\n const current = events[index]\r\n if (current && previous && current.seq <= previous.seq) {\r\n failures.push({\r\n name: 'non-monotonic-seq',\r\n detail: `seq ${current.seq} follows ${previous.seq} out of order at index ${index}`,\r\n })\r\n }\r\n }\r\n return failures\r\n}\r\n\r\n/** Aggregate all structural session checks over an event stream. */\r\nexport function checkSessionInvariants(\r\n events: readonly SessionEvent[],\r\n): SessionInvariantFailure[] {\r\n return [\r\n ...checkBalancedTurns(events),\r\n ...checkBalancedSteps(events),\r\n ...checkBalancedToolCalls(events),\r\n ...checkMonotonicSeq(events),\r\n ]\r\n}\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { randomUUID } from 'node:crypto'\r\nimport { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises'\r\nimport { dirname, resolve } from 'node:path'\r\nimport type { Context } from '@tnega/core'\r\nimport { checkSessionInvariants, type SessionInvariantFailure } from './invariant.js'\r\n\r\n/**\r\n * v7 derives model-visible messages from the *folded surface* instead of from\r\n * raw file order, so compaction can stay strictly append-only.\r\n *\r\n * v6 rewrote the log on every real compaction: the kept tail was physically\r\n * reordered after the checkpoint (and every seq renumbered) purely so that a\r\n * file-order projection would equal the surface. v7 removes that coupling:\r\n * `deriveMessages()` walks the surface nodes in surface position order (a\r\n * replacement node may carry a *higher* seq than the nodes that follow it),\r\n * so compaction appends `compaction/start` \u2192 `checkpoint` (prefix +\r\n * `surfaceOp.replace` range) \u2192 `compaction/end` and never rewrites history.\r\n * To keep every surface node self-describing, `assistant/message` now carries\r\n * its own `toolCalls` (the assistant turn is not reassembled from separate\r\n * `tool/call` events at projection time).\r\n *\r\n * v10 extends the all-queue splice with explicit prefix delete counts, so a\n * mixed next-step/next-turn claim commits as one fact. v9 and older logs are\n * rejected without in-place migration.\n *\n * v9 adds an atomic `agent/inbox/spliced` clear target for both pending inbox\n * queues, so cancellation cannot durably remove only one queue.\n *\n * v8 adds terminal, log-only `assistant/attempt` records for attempts without\n * a surface message. Stream chunks are normalized before entering Session.\n */\nexport const SESSION_FORMAT_VERSION = 10\n\r\n/** Serialized tool schema, structurally compatible with @tnega/tools ToolSchema. */\r\nexport interface ToolSchemaSnapshot {\r\n name: string\r\n description: string\r\n parameters?: Record<string, unknown>\r\n}\r\n\r\n/** Call configuration recorded beside a request so it can be reconstructed. */\r\nexport interface LlmCallConfig {\r\n provider?: string\r\n model?: string\r\n maxTokens?: number\r\n temperature?: number\r\n reasoningEffort?: string\r\n}\r\n\r\nexport class SessionFormatError extends Error {\r\n override name = 'SessionFormatError'\r\n}\r\n\r\nconst liveSessions = new Map<string, SessionLog>()\r\n\r\nfunction sessionFileKey(file: string): string {\r\n return resolve(file)\r\n}\r\n\r\nexport type ModelRole = 'system' | 'user' | 'assistant' | 'tool'\r\n\r\nexport interface ModelToolCall {\r\n id: string\r\n name: string\r\n arguments: unknown\r\n}\r\n\r\nexport interface ModelMessage {\r\n role: ModelRole\r\n content: string\r\n name?: string\r\n tool_call_id?: string\r\n tool_calls?: ModelToolCall[]\r\n toolOk?: boolean\r\n toolError?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface UserMessagePayload {\r\n content: string\r\n name?: string\r\n parentId?: string\r\n}\r\n\r\nexport interface AssistantMessagePayload {\n content: string\n /** Committed normalized stream for reconnect consumers; not model history. */\n stream?: AssistantStreamRecord[]\n name?: string\r\n parentId?: string\r\n interrupted?: boolean\r\n /**\r\n * The tool requests this assistant turn made, when it made any. The model\r\n * transcript is derived node-by-node from the surface, so an assistant turn\r\n * must carry its calls itself rather than being reassembled from the\r\n * separate (log-only) `tool/call` events that follow it.\r\n */\r\n toolCalls?: ModelToolCall[]\r\n}\r\n\r\nexport interface AssistantChunkPayload {\n id: string\r\n content: string\r\n index?: number\r\n}\n\n/** Session-owned normalized model output; never a provider wire payload. */\nexport type AssistantStreamChunk =\n | { type: 'message_start'; id: string; model?: string }\n | { type: 'message_delta'; id: string; delta: string }\n | { type: 'toolcall_start'; id: string; index: number; name: string }\n | { type: 'toolcall_end'; id: string; index: number; name: string; arguments: unknown }\n | { type: 'message_stop'; id: string; finishReason: AssistantStreamFinishReason }\n | { type: 'stream_error'; error: ToolResultErrorPayload }\n\nexport type AssistantStreamFinishReason =\n | 'stop'\n | 'tool_calls'\n | 'length'\n | 'max_turns'\n | 'max_steps'\n | 'error'\n | 'cancelled'\n\n/** One delivered chunk with its timestamp; array order preserves delivery order. */\nexport interface AssistantStreamRecord {\n time: number\n chunk: AssistantStreamChunk\n}\n\n/** Terminal settlement of an attempt that committed no assistant/message. */\nexport interface AssistantAttemptPayload {\n turn: number\n step: number\n /** May be empty when the request fails before delivering any chunks. */\n stream: AssistantStreamRecord[]\n}\n\r\nexport interface SystemMessagePayload {\r\n content: string\r\n name?: string\r\n parentId?: string\r\n}\r\n\r\nexport type MessageEventPayload =\r\n | UserMessagePayload\r\n | AssistantMessagePayload\r\n | SystemMessagePayload\r\n\r\nexport type MessageEventType = 'user/message' | 'assistant/message' | 'system/message'\r\n\r\nexport interface CompactionStartPayload {\r\n boundary?: number\r\n keep?: number\r\n tokensBefore?: number\r\n}\r\n\r\nexport interface CompactionEndPayload {\r\n checkpointId?: string\r\n keep?: number\r\n}\r\n\r\nexport interface ToolCallPayload {\r\n id: string\r\n name: string\r\n arguments: unknown\r\n /** Exact raw arguments JSON as produced by the model; preserves serialization. */\r\n argRaw?: string\r\n turn?: number\r\n step?: number\r\n}\r\n\r\nexport interface ToolResultErrorPayload {\r\n name?: string\r\n message: string\r\n stack?: string\r\n}\r\n\r\nexport interface ToolResultPayload {\r\n id: string\r\n toolCallId: string\r\n name: string\r\n ok: boolean\r\n durationMs?: number\r\n output?: unknown\r\n error?: ToolResultErrorPayload\r\n argRaw?: string\r\n turn?: number\r\n step?: number\r\n}\r\n\r\nexport type CancelCause =\r\n | { type: 'user' }\r\n | { type: 'parent' }\r\n | { type: 'disposed' }\r\n | { type: 'abort'; message?: string }\r\n | { type: 'timeout'; timeoutMs: number }\r\n\r\n/**\r\n * Why a turn ended \u2014 the durable machine outcome, distinct from the\r\n * per-step model `finishReason`. Written on `turn/end`.\r\n */\r\nexport type TurnEndReason =\r\n | { kind: 'completed' }\r\n | { kind: 'aborted'; cause: CancelCause }\r\n | { kind: 'blocked' }\r\n | { kind: 'error'; error: ToolResultErrorPayload }\r\n | { kind: 'max-tokens' }\r\n | { kind: 'max-steps' }\r\n | { kind: 'max-turns' }\r\n | { kind: 'interrupted' }\r\n\r\nexport interface TurnStartPayload {\r\n turn: number\r\n input?: unknown\r\n reason?: string\r\n}\r\n\r\nexport interface TurnEndPayload {\r\n turn: number\r\n finishReason?: string\r\n /** The typed durable reason this turn ended. Supersedes `finishReason`. */\r\n reason?: TurnEndReason\r\n output?: string\r\n steps?: number\r\n interrupted?: boolean\r\n cancelCause?: CancelCause\r\n error?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface StepStartPayload {\r\n turn: number\r\n step: number\r\n}\r\n\r\nexport interface StepEndPayload {\r\n turn: number\r\n step: number\r\n finishReason?: string\r\n toolCalls?: number\r\n interrupted?: boolean\r\n cancelCause?: CancelCause\r\n error?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface LLMRetryPayload {\r\n retryId: string\r\n retry: number\r\n delayMs?: number\r\n failure?: ToolResultErrorPayload\r\n}\r\n\r\nexport interface LLMRetryStartedPayload {\r\n retryId: string\r\n retry: number\r\n}\r\n\r\n/** How a `request/header` snapshot enters the log. */\r\nexport type RequestHeaderReason =\r\n | 'initial'\r\n | 'resume'\r\n | 'change'\r\n | 'series'\r\n | 'change-series'\r\n\r\nexport interface RequestHeaderPayload {\r\n reason: RequestHeaderReason\r\n config?: LlmCallConfig\r\n /** Rendered system prompt; absent for a system-less request. */\r\n system?: string\r\n /** Assembled tool schemas; absent for a tool-less request. */\r\n tools?: ToolSchemaSnapshot[]\r\n /** True when this snapshot also begins a new model-message series. */\r\n startsSeries?: boolean\r\n}\r\n\r\nexport interface RequestContextPayload {\r\n provider?: string\r\n model?: string\r\n contextWindow?: number\r\n}\r\n\r\nexport interface CheckpointPayload {\r\n /** The compacted prefix: messages the model keeps seeing after compaction. */\r\n messages: ModelMessage[]\r\n summary?: string\r\n tokensBefore?: number\r\n /**\r\n * The surface span this checkpoint shadows, when written by a v7\r\n * compaction: `start`/`end` are the seqs of the first and last *surface\r\n * nodes* being replaced (surface positions, so `start` may be numerically\r\n * greater than `end` after an earlier compaction). The fold removes every\r\n * live node between those two positions and inserts this checkpoint there;\r\n * the kept tail stays in place and the log is never rewritten.\r\n * `'replace'` (bare string) is the legacy v5 form and shadows nothing\r\n * structurally; projection treats it as a full-surface seed.\r\n */\r\n surfaceOp?: { op: 'replace'; start: number; end: number } | 'replace'\r\n /** @deprecated v0.2 keeps raw events in place; use messages instead. */\r\n snapshot?: SessionEvent[]\r\n}\r\n\r\nexport type PlanItemStatus = 'pending' | 'done' | 'failed'\r\n\r\nexport interface PlanItemPayload {\r\n id: string\r\n title: string\r\n status: PlanItemStatus\r\n detail?: string\r\n}\r\n\r\nexport interface PlanPayload {\r\n items: PlanItemPayload[]\r\n status?: 'pending' | 'running' | 'done' | 'failed'\r\n summary?: string\r\n}\r\n\r\nexport type InboxSplicePayload = {\n target: 'all'\n /** Prefix removals from both queues; omitted means clear both queues. */\n deleteCounts?: { nextTurn: number; nextStep: number }\n} | {\n target: 'next-turn' | 'next-step'\n index?: number\r\n deleteCount?: number\r\n inserted?: Array<{\r\n id: string\r\n content: string\r\n /** Structured message payload preserved across restart when present. */\r\n payload?: unknown\r\n mode?: 'followup' | 'steer'\r\n }>\r\n}\r\n\r\n/** A durable change to session display metadata (title, mode, agentType). */\r\nexport interface MetaPatchPayload {\r\n /** Keys changed by this event; only present keys are touched. */\r\n fields: Array<'title' | 'agentType' | 'mode'>\r\n title?: string\r\n agentType?: AgentType\r\n mode?: SessionMode\r\n}\r\n\r\nexport type AgentType = 'general' | 'coding'\r\n\r\nexport type SessionMode = 'auto' | 'plan' | 'execute'\r\n\r\nexport type SessionEventType =\r\n | MessageEventType\r\n | 'assistant/chunk'\n | 'assistant/attempt'\n | 'tool/call'\r\n | 'tool/result'\r\n | 'request/header'\r\n | 'request/context'\r\n | 'agent/inbox/spliced'\r\n | 'plan'\r\n | 'checkpoint'\r\n | 'compaction/start'\r\n | 'compaction/end'\r\n | 'meta'\r\n | 'meta/patch'\r\n | 'llm/retry'\r\n | 'llm/retry-started'\r\n | 'turn/start'\r\n | 'turn/end'\r\n | 'step/start'\r\n | 'step/end'\r\n\r\nexport interface SessionEventBase<T extends SessionEventType, P> {\r\n id: string\r\n seq: number\r\n ts: number\r\n type: T\r\n payload: P\r\n /** Surface placement; required on message-producing events. */\r\n surfaceOp?: SurfaceOp\r\n /** Seqs of earlier raw events this event derives from. */\r\n sourceEventSeqs?: number[]\r\n}\r\n\r\n/**\r\n * The message-producing subset of event types. Every variant is a surface\r\n * node that carries `surfaceOp` and participates in the ordered fold.\r\n * `system/message` is included because tnega keeps the system prompt on the\r\n * transcript (OpenAI-style), so it must be replaceable and derivable like any\r\n * other message rather than living only in `request/header`.\r\n */\r\nexport type SurfaceEventType =\r\n | 'user/message'\r\n | 'system/message'\r\n | 'assistant/message'\r\n | 'tool/result'\r\n\r\nexport type SurfaceOp = 'append' | { op: 'replace'; start: number; end: number }\r\n\r\nexport type SessionEvent =\r\n | SessionEventBase<'user/message', UserMessagePayload>\r\n | SessionEventBase<'assistant/message', AssistantMessagePayload>\r\n | SessionEventBase<'assistant/chunk', AssistantChunkPayload>\n | SessionEventBase<'assistant/attempt', AssistantAttemptPayload>\n | SessionEventBase<'system/message', SystemMessagePayload>\r\n | SessionEventBase<'tool/call', ToolCallPayload>\r\n | SessionEventBase<'tool/result', ToolResultPayload>\r\n | SessionEventBase<'request/header', RequestHeaderPayload>\r\n | SessionEventBase<'request/context', RequestContextPayload>\r\n | SessionEventBase<'agent/inbox/spliced', InboxSplicePayload>\r\n | SessionEventBase<'plan', PlanPayload>\r\n | SessionEventBase<'checkpoint', CheckpointPayload>\r\n | SessionEventBase<'compaction/start', CompactionStartPayload>\r\n | SessionEventBase<'compaction/end', CompactionEndPayload>\r\n | SessionEventBase<'meta', Record<string, unknown>>\r\n | SessionEventBase<'meta/patch', MetaPatchPayload>\r\n | SessionEventBase<'llm/retry', LLMRetryPayload>\r\n | SessionEventBase<'llm/retry-started', LLMRetryStartedPayload>\r\n | SessionEventBase<'turn/start', TurnStartPayload>\r\n | SessionEventBase<'turn/end', TurnEndPayload>\r\n | SessionEventBase<'step/start', StepStartPayload>\r\n | SessionEventBase<'step/end', StepEndPayload>\r\n\r\nfunction isMessageEventType(type: SessionEventType): type is MessageEventType {\r\n return type === 'user/message'\r\n || type === 'assistant/message'\r\n || type === 'system/message'\r\n}\r\n\r\nfunction isMessageEvent(\r\n event: SessionEvent,\r\n): event is Extract<SessionEvent, { type: MessageEventType }> {\r\n return isMessageEventType(event.type)\r\n}\r\n\r\nexport interface SessionConfig {\r\n file: string\r\n broadcast?: SessionBroadcast\r\n}\r\n\r\nexport interface CompactOptions {\r\n keep?: number\r\n keepTokens?: number\r\n summary?: string\r\n tokensBefore?: number\r\n messages?: ModelMessage[]\r\n}\r\n\r\nexport type ReplayReducer<T> = (state: T, event: SessionEvent) => T | Promise<T>\r\n\r\nexport type SessionBroadcast = (\r\n type: 'event' | 'flush',\r\n payload: unknown,\r\n) => void\r\n\r\nexport interface ContextUsage {\r\n tokens: number\r\n limit: number\r\n ratio: number\r\n}\r\n\r\nexport const DEFAULT_CONTEXT_LIMIT = 128_000\r\n\r\nfunction isSessionEvent(value: unknown): value is SessionEvent {\r\n if (typeof value !== 'object' || value === null) return false\r\n const record = value as Record<string, unknown>\r\n return typeof record.id === 'string'\r\n && typeof record.seq === 'number'\r\n && typeof record.ts === 'number'\r\n && typeof record.type === 'string'\r\n && 'payload' in record\r\n}\r\n\r\n/** Replay committed streams in raw durable order, including shadowed messages. */\nexport function assistantStreams(events: readonly SessionEvent[]): AssistantStreamRecord[][] {\n return events.flatMap(event => {\n if (event.type !== 'assistant/message' && event.type !== 'assistant/attempt') return []\n return event.payload.stream === undefined ? [] : [clone(event.payload.stream)]\n })\n}\n\nfunction clone<T>(value: T): T {\n return structuredClone(value)\r\n}\r\n\r\nfunction previousSurfaceSeq(nodes: readonly number[]): number | undefined {\r\n return nodes.at(-1)\r\n}\r\n\r\n/**\r\n * Project an event list to model messages, in the list's own order. Each\r\n * surface event is mapped through {@link deriveEventMessage}; a `checkpoint`\r\n * splices its compacted prefix in at its position. This is the building block\r\n * both {@link deriveSurfaceMessages} (surface order) and callers holding a\r\n * compact slice (e.g. the events a summarizer will read) share.\r\n */\r\nexport function projectEvents(events: readonly SessionEvent[]): ModelMessage[] {\r\n const messages: ModelMessage[] = []\r\n for (const event of events) {\r\n if (event.type === 'checkpoint') {\r\n messages.splice(0, messages.length, ...clone(event.payload.messages))\r\n continue\r\n }\r\n const message = deriveEventMessage(event)\r\n if (message) messages.push(message)\r\n }\r\n return messages\r\n}\r\n\r\nexport function isAppendSurfaceEvent(event: SessionEvent): boolean {\r\n return isSurfaceEventType(event.type) && event.surfaceOp === 'append'\r\n}\r\n\r\nexport function isSurfaceEventType(type: SessionEventType): type is SurfaceEventType {\r\n return type === 'user/message'\r\n || type === 'system/message'\r\n || type === 'assistant/message'\r\n || type === 'tool/result'\r\n}\r\n\r\n/** A v7 checkpoint shadows a surface span; legacy ('replace') shadows nothing structurally. */\r\nexport function checkpointShadowRange(\r\n event: SessionEvent,\r\n): { start: number; end: number } | undefined {\r\n if (event.type !== 'checkpoint') return undefined\r\n const op = event.payload.surfaceOp\r\n if (typeof op === 'object' && op !== null && op.op === 'replace') {\r\n return { start: op.start, end: op.end }\r\n }\r\n return undefined\r\n}\r\n\r\n/** Detached current surface nodes plus every replacement's shadowed ranges. */\r\nexport interface SurfaceFoldReplacement {\r\n seq: number\r\n start: number\r\n end: number\r\n shadowedSeqs: number[]\r\n}\r\n\r\nexport interface SurfaceFoldResult {\r\n nodes: number[]\r\n replacements: SurfaceFoldReplacement[]\r\n}\r\n\r\n/**\r\n * Fold a log's surface operations into the current surface node order.\r\n *\r\n * The surface is *positional*: a replacement removes the live nodes between\r\n * the positions of `start`/`end` and inserts the replacing event's seq there,\r\n * so surface order is independent of raw seq order \u2014 after compaction the\r\n * checkpoint node may carry a higher seq than the kept tail that follows it.\r\n */\r\nexport function foldSurface(events: readonly SessionEvent[]): SurfaceFoldResult {\r\n const nodes: number[] = []\r\n const replacements: SurfaceFoldReplacement[] = []\r\n\r\n const applyReplace = (seq: number, start: number, end: number): void => {\r\n const startIndex = nodes.indexOf(start)\r\n if (startIndex < 0) {\r\n // The shadowed head is already gone (defensive; our writer always\r\n // shadows live nodes). Keep the prefix visible by anchoring at the tail.\r\n nodes.push(seq)\r\n return\r\n }\r\n const endIndex = nodes.indexOf(end, startIndex)\r\n if (endIndex < 0) {\r\n nodes.push(seq)\r\n return\r\n }\r\n const shadowed = nodes.splice(startIndex, endIndex - startIndex + 1)\r\n nodes.splice(startIndex, 0, seq)\r\n replacements.push({ seq, start, end, shadowedSeqs: shadowed })\r\n }\r\n\r\n for (const event of events) {\r\n if (event.type === 'checkpoint') {\r\n const range = checkpointShadowRange(event)\r\n if (!range) {\r\n // A range-less checkpoint is a prefix seed: it contributes its own\r\n // node at its surface position (used when compaction runs before any\r\n // durable history exists to shadow).\r\n nodes.push(event.seq)\r\n continue\r\n }\r\n applyReplace(event.seq, range.start, range.end)\r\n continue\r\n }\r\n if (!isSurfaceEventType(event.type)) continue\r\n const op = event.surfaceOp ?? 'append'\r\n if (op === 'append') {\r\n nodes.push(event.seq)\r\n continue\r\n }\r\n applyReplace(event.seq, op.start, op.end)\r\n }\r\n return { nodes, replacements }\r\n}\r\n\r\n/**\r\n * Derive one surface node's model-visible message. A content-less assistant\r\n * turn (e.g. a max-token cutoff with no output and no tool calls) is skipped\r\n * so it never enters a provider transcript; `tool/result` yields the `tool`\r\n * role message that pairs with the assistant's `toolCalls`.\r\n */\r\nexport function deriveEventMessage(event: SessionEvent): ModelMessage | null {\r\n switch (event.type) {\r\n case 'user/message': {\r\n const message: ModelMessage = { role: 'user', content: event.payload.content }\r\n if (event.payload.name) message.name = event.payload.name\r\n return message\r\n }\r\n case 'system/message': {\r\n const message: ModelMessage = { role: 'system', content: event.payload.content }\r\n if (event.payload.name) message.name = event.payload.name\r\n return message\r\n }\r\n case 'assistant/message': {\r\n const toolCalls = event.payload.toolCalls\r\n if (!event.payload.content && !(toolCalls && toolCalls.length)) return null\r\n const message: ModelMessage = { role: 'assistant', content: event.payload.content }\r\n if (event.payload.name) message.name = event.payload.name\r\n if (toolCalls?.length) message.tool_calls = clone(toolCalls)\r\n return message\r\n }\r\n case 'tool/result': {\r\n const failed = !event.payload.ok\r\n const message: ModelMessage = {\r\n role: 'tool',\r\n content: failed\r\n ? `error: ${event.payload.error?.message ?? 'unknown'}`\r\n : stringify(event.payload.output),\r\n tool_call_id: event.payload.toolCallId,\r\n }\r\n message.name = event.payload.name\r\n if (failed) {\r\n message.toolOk = false\r\n if (event.payload.error) message.toolError = event.payload.error\r\n }\r\n return message\r\n }\r\n default:\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Derive the model transcript strictly from the folded surface: walk the\r\n * surface nodes in surface position order (so a checkpoint's prefix is placed\r\n * where it replaced history, whatever its raw seq), mapping each node through\r\n * {@link deriveEventMessage}. This is the single source of the model's view;\r\n * raw log order never influences it.\r\n */\r\nexport function deriveSurfaceMessages(events: readonly SessionEvent[]): ModelMessage[] {\r\n const { nodes } = foldSurface(events)\r\n const bySeq = new Map(events.map(event => [event.seq, event] as const))\r\n const messages: ModelMessage[] = []\r\n for (const seq of nodes) {\r\n const event = bySeq.get(seq)\r\n if (!event) continue\r\n if (event.type === 'checkpoint') {\r\n messages.splice(0, messages.length, ...clone(event.payload.messages))\r\n continue\r\n }\r\n const message = deriveEventMessage(event)\r\n if (message) messages.push(message)\r\n }\r\n return messages\r\n}\r\n\r\n/**\r\n * Project the log into a human-facing transcript (the web view). Unlike the\r\n * model surface, a compaction does **not** hide the history it summarized:\r\n * shadowed messages are kept in place, and each live checkpoint is retained\r\n * as a marker exactly where it replaced them (with nested checkpoints\r\n * expanded recursively). Readers see the full conversation; the model keeps\r\n * deriving from the folded surface via {@link deriveSurfaceMessages}.\r\n */\r\nexport function transcriptEvents(events: readonly SessionEvent[]): SessionEvent[] {\r\n const bySeq = new Map(events.map(event => [event.seq, event] as const))\r\n const { nodes, replacements } = foldSurface(events)\r\n\r\n const shadowedOf = new Map<number, SessionEvent[]>()\r\n for (const replacement of replacements) {\r\n shadowedOf.set(replacement.seq, replacement.shadowedSeqs\r\n .map(seq => bySeq.get(seq))\r\n .filter((event): event is SessionEvent => event !== undefined))\r\n }\r\n\r\n const isVisible = (event: SessionEvent): boolean => (\r\n event.type !== 'system/message' && isSurfaceEventType(event.type)\r\n )\r\n\r\n const skeleton: SessionEvent[] = []\r\n const appendHistory = (checkpointSeq: number): void => {\r\n for (const shadowed of shadowedOf.get(checkpointSeq) ?? []) {\r\n if (shadowed.type === 'checkpoint') appendHistory(shadowed.seq)\r\n else if (isVisible(shadowed)) skeleton.push(shadowed)\r\n }\r\n }\r\n for (const seq of nodes) {\r\n const event = bySeq.get(seq)\r\n if (!event) continue\r\n if (event.type === 'checkpoint') appendHistory(event.seq)\r\n skeleton.push(event)\r\n }\r\n\r\n // Declared tool calls travel on their assistant message, so the matching\r\n // log-only `tool/call` records would double-render: drop those, keep the\r\n // legacy ones that have no assistant declaration.\r\n const declaredCalls = new Set<string>()\r\n for (const event of events) {\r\n if (event.type === 'assistant/message') {\r\n for (const call of event.payload.toolCalls ?? []) declaredCalls.add(call.id)\r\n }\r\n }\r\n const inSkeleton = new Set(skeleton.map(event => event.id))\r\n const structure = events\r\n .filter(event => !inSkeleton.has(event.id))\r\n .filter(event => event.type !== 'compaction/start' && event.type !== 'compaction/end')\r\n .filter(event => event.type !== 'checkpoint') // superseded markers: history shown via the live chain\r\n .filter(event => !(event.type === 'tool/call' && declaredCalls.has(event.payload.id)))\r\n\r\n const ordered = [...skeleton]\n for (const event of structure) {\n let index = 0\n for (let cursor = ordered.length - 1; cursor >= 0; cursor -= 1) {\r\n if (ordered[cursor]!.seq <= event.seq) {\r\n index = cursor + 1\r\n break\r\n }\r\n }\r\n ordered.splice(index, 0, event)\r\n }\r\n return ordered\r\n}\r\n\r\nexport function foldRequestHeader(events: readonly SessionEvent[]): RequestHeaderPayload | undefined {\r\n let header: RequestHeaderPayload | undefined\r\n for (const event of events) {\r\n if (event.type === 'request/header') header = clone(event.payload)\r\n }\r\n return header\r\n}\r\n\r\nexport function foldRequestContext(events: readonly SessionEvent[]): RequestContextPayload | undefined {\r\n let context: RequestContextPayload | undefined\r\n for (const event of events) {\r\n if (event.type === 'request/context') context = clone(event.payload)\r\n }\r\n return context\r\n}\r\n\r\n/**\r\n * Session display metadata, rebuilt from the immutable events: the head\r\n * `meta` base followed by every `meta/patch` in seq order. No mutable\r\n * side file or in-place rewrite is involved, so title/mode/agentType stay\r\n * replayable and fork-clean like every other durable fact.\r\n */\r\nexport function foldSessionMeta(events: readonly SessionEvent[]): {\r\n title?: string\r\n agentType?: AgentType\r\n mode?: SessionMode\r\n} {\r\n const meta: { title?: string; agentType?: AgentType; mode?: SessionMode } = {}\r\n for (const event of events) {\r\n if (event.type === 'meta') {\r\n const payload = event.payload as Record<string, unknown>\r\n if (typeof payload.title === 'string') meta.title = payload.title\r\n if (payload.agentType === 'general' || payload.agentType === 'coding') {\r\n meta.agentType = payload.agentType\r\n }\r\n if (payload.mode === 'auto' || payload.mode === 'plan' || payload.mode === 'execute') {\r\n meta.mode = payload.mode\r\n }\r\n continue\r\n }\r\n if (event.type === 'meta/patch') {\r\n const patch = event.payload\r\n if (patch.fields.includes('title') && typeof patch.title === 'string') {\r\n meta.title = patch.title\r\n }\r\n if (patch.fields.includes('agentType') && patch.agentType !== undefined) {\r\n meta.agentType = patch.agentType\r\n }\r\n if (patch.fields.includes('mode') && patch.mode !== undefined) {\r\n meta.mode = patch.mode\r\n }\r\n }\r\n }\r\n return meta\r\n}\r\n\r\n/** Convenience wrapper: fold session metadata from a `SessionLog`'s events. */\r\nexport function foldMetaFromLog(log: { read(): Promise<SessionEvent[]> }): Promise<{\r\n title?: string\r\n agentType?: AgentType\r\n mode?: SessionMode\r\n}> {\r\n return log.read().then(foldSessionMeta)\r\n}\r\n\r\nfunction stringify(value: unknown): string {\r\n if (typeof value === 'string') return value\r\n try {\r\n return JSON.stringify(value) ?? String(value)\r\n } catch {\r\n return String(value)\r\n }\r\n}\r\n\r\nexport function estimateMessageTokens(messages: readonly ModelMessage[]): number {\r\n let tokens = 0\r\n for (const message of messages) {\r\n tokens += Math.ceil(message.content.length / 4)\r\n for (const call of message.tool_calls ?? []) {\r\n const raw = JSON.stringify(call.arguments ?? {}) ?? ''\r\n tokens += Math.ceil(raw.length / 4)\r\n }\r\n }\r\n return tokens\r\n}\r\n\r\nexport function estimateEventTokens(event: SessionEvent): number {\r\n switch (event.type) {\r\n case 'user/message':\r\n case 'system/message':\r\n return Math.ceil(event.payload.content.length / 4)\r\n case 'assistant/message': {\r\n let tokens = Math.ceil(event.payload.content.length / 4)\r\n for (const call of event.payload.toolCalls ?? []) {\r\n const raw = JSON.stringify(call.arguments ?? {}) ?? ''\r\n tokens += Math.ceil(raw.length / 4)\r\n }\r\n return tokens\r\n }\r\n case 'assistant/chunk':\r\n return Math.ceil(event.payload.content.length / 4)\r\n case 'tool/call': {\r\n const raw = JSON.stringify(event.payload.arguments ?? {}) ?? ''\r\n return Math.ceil(raw.length / 4)\r\n }\r\n case 'tool/result': {\r\n const raw = event.payload.ok\r\n ? stringify(event.payload.output)\r\n : event.payload.error?.message ?? 'error'\r\n return Math.ceil(raw.length / 4)\r\n }\r\n case 'plan':\n case 'assistant/attempt':\n return 0\n case 'checkpoint':\r\n return estimateMessageTokens(event.payload.messages)\r\n case 'compaction/start':\r\n case 'compaction/end':\r\n case 'meta':\r\n case 'meta/patch':\r\n return 0\r\n case 'llm/retry':\r\n case 'llm/retry-started':\r\n return 0\r\n case 'request/header':\r\n case 'request/context':\r\n case 'agent/inbox/spliced':\r\n return 0\r\n case 'turn/start':\r\n case 'turn/end':\r\n case 'step/start':\r\n case 'step/end':\r\n return 0\r\n }\r\n}\r\n\r\nexport function suffixStartIndexForTokens(\r\n events: readonly SessionEvent[],\r\n targetTokens: number,\r\n): number {\r\n if (!events.length || targetTokens <= 0) return 0\r\n let tokens = 0\r\n let candidate = events.length\r\n for (let index = events.length - 1; index >= 0; index -= 1) {\r\n tokens += estimateEventTokens(events[index]!)\r\n if (tokens >= targetTokens) {\r\n candidate = index\r\n break\r\n }\r\n }\r\n if (candidate === events.length) return 0\r\n let cut = candidate\r\n while (cut < events.length) {\r\n const event = events[cut]\r\n if (event?.type === 'user/message') return cut\r\n cut += 1\r\n }\r\n return candidate\r\n}\r\n\r\nexport function safeCompactSplit(\r\n events: readonly SessionEvent[],\r\n splitIndex: number,\r\n): number {\r\n const index = Math.max(0, Math.min(splitIndex, events.length))\r\n const first = events[index]\r\n if (first?.type === 'tool/result') {\r\n const callId = first.payload.toolCallId\r\n for (let cursor = index - 1; cursor >= 0; cursor -= 1) {\r\n const candidate = events[cursor]\r\n if (candidate?.type === 'tool/call' && candidate.payload.id === callId) {\r\n return cursor\r\n }\r\n }\r\n }\r\n\r\n let openCalls = 0\r\n for (let cursor = 0; cursor <= events.length; cursor += 1) {\r\n if (cursor >= index && openCalls === 0) return cursor\r\n const event = events[cursor]\r\n if (!event) continue\r\n if (event.type === 'tool/call') openCalls += 1\r\n if (event.type === 'tool/result') openCalls = Math.max(0, openCalls - 1)\r\n }\r\n return index\r\n}\r\n\r\nexport function repairUnclosed(\r\n events: readonly SessionEvent[],\r\n): SessionEvent[] {\r\n const openCalls: Extract<SessionEvent, { type: 'tool/call' }>[] = []\r\n const openSteps: Extract<SessionEvent, { type: 'step/start' }>[] = []\r\n const openTurns: Extract<SessionEvent, { type: 'turn/start' }>[] = []\r\n for (const event of events) {\r\n switch (event.type) {\r\n case 'tool/call':\r\n openCalls.push(event)\r\n break\r\n case 'tool/result': {\r\n const callIndex = openCalls.findIndex(\r\n call => call.payload.id === event.payload.toolCallId,\r\n )\r\n if (callIndex >= 0) openCalls.splice(callIndex, 1)\r\n else openCalls.pop()\r\n break\r\n }\r\n case 'step/start':\r\n openSteps.push(event)\r\n break\r\n case 'step/end':\r\n openSteps.pop()\r\n break\r\n case 'turn/start':\r\n openTurns.push(event)\r\n break\r\n case 'turn/end':\r\n openTurns.pop()\r\n break\r\n }\r\n }\r\n\r\n if (!openCalls.length && !openSteps.length && !openTurns.length) return []\r\n\r\n const synthetic: SessionEvent[] = []\r\n let nextSeq = (events.at(-1)?.seq ?? 0) + 1\r\n let nextTs = (events.at(-1)?.ts ?? Date.now()) + 1\r\n const push = (type: SessionEventType, payload: SessionEvent['payload']): void => {\r\n synthetic.push({\r\n id: randomUUID(),\r\n seq: nextSeq,\r\n ts: nextTs,\r\n type: type as SessionEvent['type'],\r\n payload: clone(payload),\r\n } as SessionEvent)\r\n nextSeq += 1\r\n nextTs += 1\r\n }\r\n\r\n for (const call of openCalls) {\r\n push('tool/result', {\r\n id: call.payload.id,\r\n toolCallId: call.payload.id,\r\n name: call.payload.name,\r\n ok: false,\r\n error: {\r\n name: 'SessionInterruptedError',\r\n message: 'session interrupted before tool result was recorded',\r\n },\r\n })\r\n }\r\n for (const step of openSteps) {\r\n push('step/end', {\r\n turn: step.payload.turn,\r\n step: step.payload.step,\r\n finishReason: 'interrupted',\r\n interrupted: true,\r\n })\r\n }\r\n for (let i = 0; i < openTurns.length; i++) {\r\n const turn = openTurns[i]!.payload.turn\r\n push('turn/end', {\r\n turn,\r\n finishReason: 'interrupted',\r\n interrupted: true,\r\n })\r\n }\r\n return synthetic\r\n}\r\n\r\nexport function resolveCompactKeep(\r\n events: readonly SessionEvent[],\r\n options: CompactOptions,\r\n): number {\r\n if (\r\n typeof options.keepTokens === 'number'\r\n && Number.isFinite(options.keepTokens)\r\n && options.keepTokens > 0\r\n ) {\r\n return events.length - suffixStartIndexForTokens(events, options.keepTokens)\r\n }\r\n const keep = typeof options.keep === 'number' && Number.isFinite(options.keep) && options.keep > 0\r\n ? Math.floor(options.keep)\r\n : 0\r\n return Math.min(keep, events.length)\r\n}\r\n\r\nexport function estimateContextUsage(\r\n messages: readonly ModelMessage[],\r\n limit = DEFAULT_CONTEXT_LIMIT,\r\n): ContextUsage {\r\n const tokens = estimateMessageTokens(messages)\r\n return {\r\n tokens,\r\n limit,\r\n ratio: limit > 0 ? tokens / limit : 0,\r\n }\r\n}\r\n\r\nexport class SessionLog {\r\n private _events: SessionEvent[] = []\r\n private _surface: ModelMessage[] = []\r\n private _surfaceNodes: number[] = []\r\n private _requestHeader: RequestHeaderPayload | undefined\r\n private _requestContext: RequestContextPayload | undefined\r\n private _loaded = false\r\n private _nextSeq = 1\r\n private _queue: Promise<unknown> = Promise.resolve()\r\n private _writeTail: Promise<void> = Promise.resolve()\r\n private _pending: SessionEvent[] = []\r\n private _drainScheduled = false\r\n private _writeError: unknown\r\n\r\n constructor(\n readonly file: string,\n private _broadcast?: SessionBroadcast,\n publicationContext?: Context,\n ) {\n if (!this._broadcast && publicationContext) {\n this._broadcast = (type, payload) => {\n if (type === 'event') publicationContext.emit('session/event', payload)\n else publicationContext.emit('session/flush', payload)\n }\n }\n }\n\r\n init(): Promise<void> {\r\n return this._run(async () => {\r\n const key = sessionFileKey(this.file)\r\n if (!liveSessions.has(key)) liveSessions.set(key, this)\r\n try {\r\n await this._ensureLoaded()\r\n await this._drainWrite()\r\n if (this._writeError) {\r\n const error = this._writeError\r\n this._writeError = undefined\r\n throw error\r\n }\r\n } catch (error) {\r\n if (liveSessions.get(key) === this) liveSessions.delete(key)\r\n throw error\r\n }\r\n })\r\n }\r\n\r\n append(type: 'user/message', payload: UserMessagePayload): Promise<SessionEvent>\r\n append(type: 'assistant/message', payload: AssistantMessagePayload): Promise<SessionEvent>\r\n append(type: 'assistant/chunk', payload: AssistantChunkPayload): Promise<SessionEvent>\n append(type: 'assistant/attempt', payload: AssistantAttemptPayload): Promise<SessionEvent>\n append(type: 'system/message', payload: SystemMessagePayload): Promise<SessionEvent>\r\n append(type: 'tool/call', payload: ToolCallPayload): Promise<SessionEvent>\r\n append(type: 'tool/result', payload: ToolResultPayload): Promise<SessionEvent>\r\n append(type: 'request/header', payload: RequestHeaderPayload): Promise<SessionEvent>\r\n append(type: 'request/context', payload: RequestContextPayload): Promise<SessionEvent>\r\n append(type: 'agent/inbox/spliced', payload: InboxSplicePayload): Promise<SessionEvent>\r\n append(type: 'plan', payload: PlanPayload): Promise<SessionEvent>\r\n append(type: 'checkpoint', payload: CheckpointPayload): Promise<SessionEvent>\r\n append(type: 'compaction/start', payload: CompactionStartPayload): Promise<SessionEvent>\r\n append(type: 'compaction/end', payload: CompactionEndPayload): Promise<SessionEvent>\r\n append(type: 'meta', payload: Record<string, unknown>): Promise<SessionEvent>\r\n append(type: 'meta/patch', payload: MetaPatchPayload): Promise<SessionEvent>\r\n append(type: 'llm/retry', payload: LLMRetryPayload): Promise<SessionEvent>\r\n append(type: 'llm/retry-started', payload: LLMRetryStartedPayload): Promise<SessionEvent>\r\n append(type: 'turn/start', payload: TurnStartPayload): Promise<SessionEvent>\r\n append(type: 'turn/end', payload: TurnEndPayload): Promise<SessionEvent>\r\n append(type: 'step/start', payload: StepStartPayload): Promise<SessionEvent>\r\n append(type: 'step/end', payload: StepEndPayload): Promise<SessionEvent>\r\n append(type: SessionEventType, payload: SessionEvent['payload']): Promise<SessionEvent> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const event = this._buildEvent(type, payload)\r\n this._commitEvent(event)\r\n return event\r\n })\r\n }\r\n\r\n private _buildEvent(\r\n type: SessionEventType,\r\n payload: SessionEvent['payload'],\r\n ): SessionEvent {\r\n const eventPayload = clone(payload)\r\n const event = {\r\n id: randomUUID(),\r\n seq: this._nextSeq,\r\n ts: Date.now(),\r\n type: type as SessionEvent['type'],\r\n payload: eventPayload,\r\n } as SessionEvent\r\n if (isMessageEventType(type)) {\r\n for (let index = this._events.length - 1; index >= 0; index -= 1) {\r\n const previous = this._events[index]\r\n if (previous && isMessageEvent(previous)) {\r\n const messagePayload = eventPayload as MessageEventPayload\r\n messagePayload.parentId ??= previous.id\r\n break\r\n }\r\n }\r\n event.surfaceOp ??= 'append'\r\n const previous = previousSurfaceSeq(this._surfaceNodes)\r\n event.sourceEventSeqs ??= previous === undefined ? [] : [previous]\r\n } else if (type === 'request/header') {\r\n event.sourceEventSeqs ??= []\r\n } else if (type === 'request/context') {\r\n event.sourceEventSeqs ??= []\r\n } else if (type === 'agent/inbox/spliced') {\r\n event.sourceEventSeqs ??= []\r\n }\r\n this._nextSeq += 1\r\n return event\r\n }\r\n\r\n private _commitEvent(event: SessionEvent): void {\r\n this._events.push(event)\r\n this._refreshSurface()\r\n if (event.type === 'request/header') this._requestHeader = clone(event.payload)\r\n if (event.type === 'request/context') this._requestContext = clone(event.payload)\r\n this._broadcast?.('event', event)\r\n this._enqueue(event)\r\n }\r\n\r\n /**\r\n * Rebuild both surface views from the committed log. The nodes come from the\r\n * positional {@link foldSurface} fold; the messages come from\r\n * {@link deriveSurfaceMessages}, which walks those nodes in surface order \u2014\r\n * the model view is a pure function of the surface, never of raw file order.\r\n */\r\n private _refreshSurface(): void {\r\n this._surfaceNodes = foldSurface(this._events).nodes\r\n this._surface = deriveSurfaceMessages(this._events)\r\n }\r\n\r\n private _enqueue(event: SessionEvent): void {\r\n this._pending.push(event)\r\n if (this._drainScheduled) return\r\n this._drainScheduled = true\r\n setImmediate(() => {\r\n this._drainScheduled = false\r\n void this._drainWrite()\r\n })\r\n }\r\n\r\n private _drainWrite(): Promise<void> {\r\n const events = this._pending.splice(0)\r\n if (!events.length) return this._writeTail\r\n const write = this._writeTail.then(() => appendFile(\r\n this.file,\r\n `${events.map(event => JSON.stringify(event)).join('\\n')}\\n`,\r\n 'utf8',\r\n ))\r\n this._writeTail = write.catch((error) => {\r\n this._writeError ??= error\r\n })\r\n return write.then(() => undefined, () => undefined)\r\n }\r\n\r\n flush(): Promise<number> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n await this._drainWrite()\r\n if (this._writeError) {\r\n const error = this._writeError\r\n this._writeError = undefined\r\n throw error\r\n }\r\n const seq = this._nextSeq - 1\r\n this._broadcast?.('flush', { file: this.file, seq })\r\n return seq\r\n })\r\n }\r\n\r\n async lineage(messageId: string): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return this._resolveLineage(messageId)\r\n })\r\n }\r\n\r\n /** The next turn number: one more than the highest recorded `turn/start`. */\r\n async nextTurn(): Promise<number> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n let max = 0\r\n for (const event of this._events) {\r\n if (event.type === 'turn/start') max = Math.max(max, event.payload.turn)\r\n }\r\n return max + 1\r\n })\r\n }\r\n\r\n async forkAt(messageId: string): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const lineage = this._resolveLineage(messageId)\r\n const lineageIds = new Set(lineage.map(event => event.id))\r\n const targetIndex = this._events.findIndex(\r\n event => event.id === messageId && isMessageEvent(event),\r\n )\r\n if (targetIndex < 0) {\r\n throw new Error(`message not found: ${messageId}`)\r\n }\r\n const selected: SessionEvent[] = []\r\n let skippedMessage = false\r\n for (let index = 0; index <= targetIndex; index += 1) {\r\n const event = this._events[index]!\r\n if (isMessageEvent(event)) {\r\n if (!lineageIds.has(event.id)) {\r\n skippedMessage = true\r\n continue\r\n }\r\n skippedMessage = false\r\n selected.push(clone(event))\r\n continue\r\n }\r\n if (\r\n event.type === 'meta'\r\n || event.type === 'llm/retry'\r\n || event.type === 'llm/retry-started'\n // Message forks omit the owning lifecycle, including its attempt ledger.\n || event.type === 'assistant/attempt'\n || event.type.startsWith('turn/')\n || event.type.startsWith('step/')\r\n ) {\r\n continue\r\n }\r\n if (event.type === 'checkpoint') {\r\n selected.push(clone(event))\r\n continue\r\n }\r\n if (!skippedMessage) {\r\n selected.push(clone(event))\r\n }\r\n }\r\n return selected\r\n })\r\n }\r\n\r\n read(): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return this._events.map(event => clone(event))\r\n })\r\n }\r\n\r\n deriveMessages(): Promise<ModelMessage[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return clone(this._surface)\r\n })\r\n }\r\n\r\n /** The ordered surface events, after compaction replacements are applied. */\r\n surfaceEvents(): Promise<SessionEvent[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const bySeq = new Map(this._events.map(event => [event.seq, event] as const))\r\n return this._surfaceNodes\r\n .map(seq => bySeq.get(seq))\r\n .filter((event): event is SessionEvent => event !== undefined)\r\n .map(event => clone(event))\r\n })\r\n }\r\n\r\n /** The latest `request/header` snapshot, or undefined before the first one. */\r\n requestHeader(): RequestHeaderPayload | undefined {\r\n return this._requestHeader ? clone(this._requestHeader) : undefined\r\n }\r\n\r\n /** The latest resolved route metadata, or undefined before the first one. */\r\n requestContext(): RequestContextPayload | undefined {\r\n return this._requestContext ? clone(this._requestContext) : undefined\r\n }\r\n\r\n /** Display metadata folded from the durable `meta` + `meta/patch` events. */\r\n meta(): Promise<{ title?: string; agentType?: AgentType; mode?: SessionMode }> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return foldSessionMeta(this._events)\r\n })\r\n }\r\n\r\n estimateContext(limit = DEFAULT_CONTEXT_LIMIT): Promise<ContextUsage> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return estimateContextUsage(this._surface, limit)\r\n })\r\n }\r\n\r\n replay(): Promise<SessionEvent[]>\r\n replay<T>(reduce: ReplayReducer<T>, initial: T): Promise<T>\r\n replay<T>(reduce?: ReplayReducer<T>, initial?: T): Promise<SessionEvent[] | T> {\r\n return this.read().then((events) => {\r\n if (!reduce) return events\r\n let state = initial as T\r\n let pending: Promise<void> = Promise.resolve()\r\n for (const event of events) {\r\n pending = pending.then(async () => {\r\n state = await reduce(state, event)\r\n })\r\n }\r\n return pending.then(() => state)\r\n })\r\n }\r\n\r\n /** Structural violations in the loaded event stream (empty = balanced). */\r\n runInvariants(): Promise<SessionInvariantFailure[]> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n return checkSessionInvariants(this._events)\r\n })\r\n }\r\n\r\n /**\r\n * Compact the session without ever rewriting history.\r\n *\r\n * The surface (not the raw log) is the unit of compaction. A kept suffix \u2014\r\n * measured in tokens via `keepTokens`, or in nodes via `keep`, and always\r\n * snapped to start on a fresh `user/message` turn so a tool result never\r\n * outlives the assistant call it answers \u2014 stays visible; every earlier\r\n * surface node is shadowed by the checkpoint's `surfaceOp.replace` range.\r\n * The three compaction events are appended like any other event, so seqs\r\n * stay immutable, live observers see the transition, and a crash can only\r\n * leave an orphaned `compaction/start` rather than a torn rewrite.\r\n *\r\n * Returns the new event count, or the unchanged count when there is no\r\n * useful surface span to shadow.\r\n */\r\n compact(options: CompactOptions = {}): Promise<number> {\r\n return this._run(async () => {\r\n await this._ensureLoaded()\r\n const events = this._events\r\n const bySeq = new Map(events.map(event => [event.seq, event] as const))\r\n const surfaceEvents = foldSurface(events).nodes\r\n .map(seq => bySeq.get(seq))\r\n .filter((event): event is SessionEvent => event !== undefined)\r\n\r\n const keep = resolveCompactKeep(surfaceEvents, options)\r\n let split = Math.max(0, surfaceEvents.length - keep)\r\n // The retained tail must open on a fresh user turn: shadowing an\r\n // assistant step while keeping its tool results would leave an orphan\r\n // tool message at the head of the transcript. Prefer the next user\r\n // boundary; when the candidate sits inside the final turn, keep that\r\n // whole turn instead of cutting it.\r\n if (split < surfaceEvents.length && surfaceEvents[split]!.type !== 'user/message') {\r\n let next = split\r\n while (next < surfaceEvents.length && surfaceEvents[next]!.type !== 'user/message') next += 1\r\n if (next < surfaceEvents.length) {\r\n split = next\r\n } else {\r\n let lastUser = surfaceEvents.length - 1\r\n while (lastUser >= 0 && surfaceEvents[lastUser]!.type !== 'user/message') lastUser -= 1\r\n split = lastUser >= 0 ? lastUser : surfaceEvents.length\r\n }\r\n }\r\n const shadowed = surfaceEvents.slice(0, split)\r\n if (!options.messages?.length) return this._events.length\r\n\r\n // A compaction needs a summary to write. When there is history to\r\n // shadow the checkpoint replaces that head span; when there is none (a\r\n // budget trip before anything is durable) the checkpoint seeds the\r\n // surface with the compacted prefix instead.\r\n const range = shadowed.length\r\n ? { start: shadowed[0]!.seq, end: shadowed[shadowed.length - 1]!.seq }\r\n : undefined\r\n const boundary = split\r\n this._commitEvent(this._buildEvent('compaction/start', {\r\n ...(boundary > 0 ? { boundary } : {}),\r\n ...(options.keep !== undefined ? { keep: options.keep } : {}),\r\n ...(options.tokensBefore !== undefined ? { tokensBefore: options.tokensBefore } : {}),\r\n }))\r\n const checkpoint = this._buildEvent('checkpoint', {\r\n messages: clone(options.messages),\r\n ...(range ? { surfaceOp: { op: 'replace', start: range.start, end: range.end } } : {}),\r\n ...(options.summary ? { summary: options.summary } : {}),\r\n ...(options.tokensBefore !== undefined ? { tokensBefore: options.tokensBefore } : {}),\r\n })\r\n this._commitEvent(checkpoint)\r\n this._commitEvent(this._buildEvent('compaction/end', {\r\n checkpointId: checkpoint.id,\r\n ...(options.keep !== undefined ? { keep: options.keep } : {}),\r\n }))\r\n return this._events.length\r\n })\r\n }\r\n\r\n close(): Promise<void> {\r\n return this._queue.then(async () => {\r\n await this._drainWrite()\r\n if (this._writeError) {\r\n const error = this._writeError\r\n this._writeError = undefined\r\n throw error\r\n }\r\n const key = sessionFileKey(this.file)\r\n if (liveSessions.get(key) === this) liveSessions.delete(key)\r\n })\r\n }\r\n\r\n private _run<T>(task: () => Promise<T>): Promise<T> {\r\n const next = this._queue.then(task, task)\r\n this._queue = next.then(() => undefined, () => undefined)\r\n return next\r\n }\r\n\r\n private async _ensureLoaded(): Promise<void> {\r\n if (this._loaded) return\r\n const key = sessionFileKey(this.file)\r\n const owner = liveSessions.get(key)\r\n if (owner && owner !== this && owner._loaded) {\r\n this._events = owner._events.map(event => clone(event))\r\n this._surface = clone(owner._surface)\r\n this._surfaceNodes = [...owner._surfaceNodes]\r\n this._requestHeader = owner._requestHeader ? clone(owner._requestHeader) : undefined\r\n this._requestContext = owner._requestContext ? clone(owner._requestContext) : undefined\r\n this._nextSeq = owner._nextSeq\r\n this._loaded = true\r\n return\r\n }\r\n await mkdir(dirname(resolve(this.file)), { recursive: true })\r\n const read = await this._read()\r\n if (!read.existing) {\r\n const meta: SessionEvent = {\r\n id: randomUUID(),\r\n seq: 1,\r\n ts: Date.now(),\r\n type: 'meta',\r\n payload: { formatVersion: SESSION_FORMAT_VERSION },\r\n }\r\n await writeFile(this.file, `${JSON.stringify(meta)}\\n`, 'utf8')\r\n this._events = [meta]\r\n this._refreshSurface()\r\n this._nextSeq = 2\r\n this._loaded = true\r\n return\r\n }\r\n this._events = read.events\r\n this._refreshSurface()\r\n this._requestHeader = foldRequestHeader(this._events)\r\n this._requestContext = foldRequestContext(this._events)\r\n this._nextSeq = (this._events.at(-1)?.seq ?? 0) + 1\r\n this._loaded = true\r\n }\r\n\r\n private async _read(): Promise<{ existing: boolean; events: SessionEvent[] }> {\r\n let text: string\r\n try {\r\n text = await readFile(this.file, 'utf8')\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return { existing: false, events: [] }\r\n }\r\n throw error\r\n }\r\n if (!text.trim()) return { existing: false, events: [] }\r\n\r\n const lines = text.split('\\n')\r\n const events: SessionEvent[] = []\r\n let torn = false\r\n for (const line of lines) {\r\n if (!line.trim()) continue\r\n if (torn) continue\r\n let parsed: unknown\r\n try {\r\n parsed = JSON.parse(line)\r\n } catch {\r\n torn = true\r\n continue\r\n }\r\n if (!isSessionEvent(parsed)) {\r\n torn = true\r\n continue\r\n }\r\n events.push(parsed)\r\n }\r\n\r\n this._assertFormat(events)\r\n\r\n const key = sessionFileKey(this.file)\r\n const owner = liveSessions.get(key)\r\n if (owner && owner !== this) {\r\n // A live writer owns this log; never synthesize closures over its active turn.\r\n return { existing: true, events }\r\n }\r\n\r\n const synthetic = repairUnclosed(events)\r\n if (!torn && !synthetic.length) return { existing: true, events }\r\n\r\n const repaired = [...events, ...synthetic]\r\n await writeFile(\r\n this.file,\r\n `${repaired.map(event => JSON.stringify(event)).join('\\n')}\\n`,\r\n 'utf8',\r\n )\r\n return { existing: true, events: repaired }\r\n }\r\n\r\n private _assertFormat(events: readonly SessionEvent[]): void {\r\n let version: number | undefined\r\n for (const event of events) {\r\n if (event.type !== 'meta') continue\r\n const value = (event.payload as Record<string, unknown>).formatVersion\r\n if (typeof value === 'number') version = value\r\n }\r\n if (version === undefined) {\r\n throw new SessionFormatError(\r\n `session log is missing formatVersion; expected ${SESSION_FORMAT_VERSION}`,\r\n )\r\n }\r\n if (version !== SESSION_FORMAT_VERSION) {\r\n throw new SessionFormatError(\r\n `unsupported session format version ${version}; expected ${SESSION_FORMAT_VERSION}`,\r\n )\r\n }\r\n }\r\n\r\n private _resolveLineage(messageId: string): SessionEvent[] {\r\n const messages = this._events.filter(\r\n (event): event is Extract<SessionEvent, { type: MessageEventType }> => isMessageEvent(event),\r\n )\r\n const byId = new Map<string, Extract<SessionEvent, { type: MessageEventType }>>(\r\n messages.map(event => [event.id, event] as const),\r\n )\r\n if (!byId.has(messageId)) {\r\n throw new Error(`message not found: ${messageId}`)\r\n }\r\n const chain: SessionEvent[] = []\r\n const seen = new Set<string>()\r\n let cursorId: string | undefined = messageId\r\n while (cursorId && byId.has(cursorId) && !seen.has(cursorId)) {\r\n seen.add(cursorId)\r\n const event: Extract<SessionEvent, { type: MessageEventType }> = byId.get(cursorId)!\r\n chain.unshift(clone(event))\r\n const parentId: string | undefined = event.payload.parentId\r\n if (parentId && byId.has(parentId)) {\r\n cursorId = parentId\r\n continue\r\n }\r\n const index = messages.findIndex(message => message.id === cursorId)\r\n cursorId = index > 0 ? messages[index - 1]!.id : undefined\r\n }\r\n return chain\r\n }\r\n}\r\n\r\nexport const session = {\r\n name: 'session',\r\n apply: async (ctx: Context, config: SessionConfig) => {\r\n const log = new SessionLog(config.file, config.broadcast ?? ((type, payload) => {\r\n if (type === 'event') ctx.emit('session/event', payload)\r\n else ctx.emit('session/flush', payload)\r\n }))\r\n await log.init()\r\n ctx.provide('session', log)\r\n return () => log.close()\r\n },\r\n}\r\n\r\nexport * from './invariant.js'\r\n\r\nexport const name = '@tnega/session'\r\n", "/**\r\n * Session invariant companion.\r\n *\r\n * Pure structural checks over a session event stream, mirroring the DSH\r\n * \"each package ships a ./invariant\" discipline. They assert the relationships\r\n * the event-sourcing layer promises:\r\n *\r\n * - durable lifecycle structures (turn / step / tool call) stay balanced \u2014\r\n * every `turn/start` has a `turn/end`, every `tool/call` a `tool/result`;\r\n * - seq stays strictly monotonic;\r\n * - message events that reach the model surface stay append-only unless they\r\n * belong to a compaction replacement.\r\n *\r\n * The checks are pure over `SessionEvent[]`, so they run equally on a full\r\n * log, on a single newly-committed event, or in tests. Recovery (repairing an\r\n * interrupted log into a closed one) is `repairUnclosed`; these checks assert\r\n * that the result is actually legal.\r\n */\r\n\r\nimport type { SessionEvent, ToolResultPayload } from './index.js'\r\n\r\nexport interface SessionInvariantFailure {\r\n name: string\r\n detail: string\r\n}\r\n\r\n/** Balanced `turn/start` \u2194 `turn/end` per turn number. */\r\nexport function checkBalancedTurns(events: readonly SessionEvent[]): SessionInvariantFailure[] {\r\n const opens = new Map<number, number>()\r\n const failures: SessionInvariantFailure[] = []\r\n for (const event of events) {\r\n if (event.type === 'turn/start') {\r\n opens.set(event.payload.turn, (opens.get(event.payload.turn) ?? 0) + 1)\r\n } else if (event.type === 'turn/end') {\r\n const remaining = opens.get(event.payload.turn) ?? 0\r\n if (remaining <= 0) {\r\n failures.push({\r\n name: 'turn/end-without-start',\r\n detail: `turn/end for turn ${event.payload.turn} has no matching turn/start`,\r\n })\r\n } else {\r\n opens.set(event.payload.turn, remaining - 1)\r\n }\r\n }\r\n }\r\n for (const [turn, count] of opens) {\r\n for (let index = 0; index < count; index += 1) {\r\n failures.push({\r\n name: 'turn/start-without-end',\r\n detail: `turn/start ${turn} has no matching turn/end`,\r\n })\r\n }\r\n }\r\n return failures\r\n}\r\n\r\n/** Balanced `step/start` \u2194 `step/end` per (turn, step). */\r\nexport function checkBalancedSteps(events: readonly SessionEvent[]): SessionInvariantFailure[] {\r\n const open = new Set<string>()\r\n const failures: SessionInvariantFailure[] = []\r\n for (const event of events) {\r\n if (event.type === 'step/start') {\r\n const key = `${event.payload.turn}:${event.payload.step}`\r\n if (open.has(key)) {\r\n failures.push({\r\n name: 'duplicate-step-start',\r\n detail: `step/start for ${key} appears twice without a matching step/end`,\r\n })\r\n }\r\n open.add(key)\r\n } else if (event.type === 'step/end') {\r\n const key = `${event.payload.turn}:${event.payload.step}`\r\n if (!open.delete(key)) {\r\n failures.push({\r\n name: 'step/end-without-start',\r\n detail: `step/end for ${key} has no matching step/start`,\r\n })\r\n }\r\n }\r\n }\r\n for (const key of open) {\r\n failures.push({\r\n name: 'step/start-without-end',\r\n detail: `step/start ${key} has no matching step/end`,\r\n })\r\n }\r\n return failures\r\n}\r\n\r\n/** Balanced `tool/call` \u2194 `tool/result` per call id. */\r\nexport function checkBalancedToolCalls(events: readonly SessionEvent[]): SessionInvariantFailure[] {\r\n const open = new Set<string>()\r\n const failures: SessionInvariantFailure[] = []\r\n for (const event of events) {\r\n if (event.type === 'tool/call') {\r\n open.add(event.payload.id)\r\n } else if (event.type === 'tool/result') {\r\n const payload = event.payload as ToolResultPayload\r\n const id = payload.toolCallId\r\n if (!open.delete(id)) {\r\n failures.push({\r\n name: 'tool/result-without-call',\r\n detail: `tool/result for call ${id} has no matching tool/call`,\r\n })\r\n }\r\n }\r\n }\r\n for (const id of open) {\r\n failures.push({\r\n name: 'tool/call-without-result',\r\n detail: `tool/call ${id} has no matching tool/result`,\r\n })\r\n }\r\n return failures\r\n}\r\n\r\n/** seq is strictly monotonic (an append-only log never rewinds). */\r\nexport function checkMonotonicSeq(events: readonly SessionEvent[]): SessionInvariantFailure[] {\n const failures: SessionInvariantFailure[] = []\r\n for (let index = 1; index < events.length; index += 1) {\r\n const previous = events[index - 1]\r\n const current = events[index]\r\n if (current && previous && current.seq <= previous.seq) {\r\n failures.push({\r\n name: 'non-monotonic-seq',\r\n detail: `seq ${current.seq} follows ${previous.seq} out of order at index ${index}`,\r\n })\r\n }\r\n }\r\n return failures\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)\n}\n\nfunction isNonNegativeInteger(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\n}\n\n/** Tool arguments must survive JSONL persistence without losing values. */\nfunction isJsonValue(value: unknown, ancestors = new Set<object>()): boolean {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return true\n if (typeof value === 'number') return Number.isFinite(value)\n if (typeof value !== 'object' || ancestors.has(value)) return false\n if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype\n && Object.getPrototypeOf(value) !== null) return false\n ancestors.add(value)\n const valid = (Array.isArray(value) ? Array.from(value) : Object.values(value))\n .every(member => isJsonValue(member, ancestors))\n ancestors.delete(value)\n return valid\n}\n\nfunction isStreamRecord(value: unknown): boolean {\n if (!isRecord(value) || !isNonNegativeInteger(value.time) || !isRecord(value.chunk)) return false\n const chunk = value.chunk\n if (chunk.type === 'stream_error') {\n return isRecord(chunk.error)\n && typeof chunk.error.message === 'string'\n && (chunk.error.name === undefined || typeof chunk.error.name === 'string')\n && (chunk.error.stack === undefined || typeof chunk.error.stack === 'string')\n }\n if (typeof chunk.id !== 'string') return false\n switch (chunk.type) {\n case 'message_start':\n return chunk.model === undefined || typeof chunk.model === 'string'\n case 'message_delta':\n return typeof chunk.delta === 'string'\n case 'toolcall_start':\n case 'toolcall_end':\n return isNonNegativeInteger(chunk.index) && typeof chunk.name === 'string'\n && (chunk.type === 'toolcall_start' || isJsonValue(chunk.arguments))\n case 'message_stop':\n return chunk.finishReason === 'stop' || chunk.finishReason === 'tool_calls'\n || chunk.finishReason === 'length' || chunk.finishReason === 'max_turns'\n || chunk.finishReason === 'max_steps' || chunk.finishReason === 'error'\n || chunk.finishReason === 'cancelled'\n default:\n return false\n }\n}\n\n/** A terminal attempt belongs to an open durable step, never to a live attempt id. */\nexport function checkAssistantAttempts(events: readonly SessionEvent[]): SessionInvariantFailure[] {\n const turns = new Set<number>()\n const steps = new Map<string, number>()\n const failures: SessionInvariantFailure[] = []\n for (const event of events) {\n if (event.type === 'turn/start') turns.add(event.payload.turn)\n if (event.type === 'turn/end') {\n turns.delete(event.payload.turn)\n for (const [key, turn] of steps) {\n if (turn === event.payload.turn) steps.delete(key)\n }\n }\n if (event.type === 'step/start' && turns.has(event.payload.turn)) {\n steps.set(`${event.payload.turn}:${event.payload.step}`, event.payload.turn)\n }\n if (event.type === 'step/end') steps.delete(`${event.payload.turn}:${event.payload.step}`)\n if (event.type !== 'assistant/attempt') continue\n const payload: unknown = event.payload\n if (!isRecord(payload) || !isNonNegativeInteger(payload.turn) || !isNonNegativeInteger(payload.step)\n || !turns.has(payload.turn) || !steps.has(`${payload.turn}:${payload.step}`)) {\n failures.push({\n name: 'assistant/attempt-without-open-step',\n detail: `assistant/attempt at seq ${event.seq} has no matching open turn and step`,\n })\n }\n if (!isRecord(payload) || !Array.isArray(payload.stream) || !payload.stream.every(isStreamRecord)) {\n failures.push({\n name: 'assistant/attempt-invalid-stream',\n detail: `assistant/attempt at seq ${event.seq} must contain an ordered array of normalized stream records`,\n })\n }\n }\n return failures\n}\n\r\n/** Aggregate all structural session checks over an event stream. */\r\nexport function checkSessionInvariants(\r\n events: readonly SessionEvent[],\r\n): SessionInvariantFailure[] {\r\n return [\r\n ...checkBalancedTurns(events),\r\n ...checkBalancedSteps(events),\r\n ...checkBalancedToolCalls(events),\r\n ...checkMonotonicSeq(events),\n ...checkAssistantAttempts(events),\n ]\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["previous"]
|
|
7
7
|
}
|
|
@@ -9,6 +9,7 @@ export declare class DurableInbox {
|
|
|
9
9
|
private _session;
|
|
10
10
|
private _nextTurn;
|
|
11
11
|
private _nextStep;
|
|
12
|
+
private _operationTail;
|
|
12
13
|
constructor(_session: SessionLog);
|
|
13
14
|
get size(): number;
|
|
14
15
|
insert(input: {
|
|
@@ -26,6 +27,8 @@ export declare class DurableInbox {
|
|
|
26
27
|
* nothing is pending.
|
|
27
28
|
*/
|
|
28
29
|
claimBatch(): Promise<DurableInboxMessage[]>;
|
|
30
|
+
/** Claim every input waiting for the next step, without consuming a turn. */
|
|
31
|
+
claimNextStep(): Promise<DurableInboxMessage[]>;
|
|
29
32
|
clear(): Promise<void>;
|
|
30
33
|
/** Insert a message at an explicit target/index boundary. */
|
|
31
34
|
insertAt(input: {
|
|
@@ -48,6 +51,8 @@ export declare class DurableInbox {
|
|
|
48
51
|
nextTurn: readonly DurableInboxMessage[];
|
|
49
52
|
nextStep: readonly DurableInboxMessage[];
|
|
50
53
|
};
|
|
54
|
+
/** Keep the entire read → append → apply transition ordered, including claims. */
|
|
55
|
+
private _run;
|
|
51
56
|
private _find;
|
|
52
57
|
static restore(session: SessionLog): Promise<DurableInbox>;
|
|
53
58
|
private _applyAll;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Context } from "../../core/src/index.js";
|
|
2
2
|
import { SessionLog } from "../../session/src/index.js";
|
|
3
|
-
import { type DurableInboxMessage } from './inbox-durable.js';
|
|
3
|
+
import { type DurableInboxMessage, type DurableTarget } from './inbox-durable.js';
|
|
4
4
|
import type { AgentCancelCause, AgentContextBudget, AgentHooks, AgentInput, AgentStreamEvent, LLMAdapter } from './types.js';
|
|
5
5
|
export interface AgentSessionMeta {
|
|
6
6
|
agentId: string;
|
|
@@ -29,6 +29,21 @@ export type AgentStatus = 'idle' | 'running';
|
|
|
29
29
|
export interface AgentCancelOptions {
|
|
30
30
|
keepInbox?: boolean;
|
|
31
31
|
}
|
|
32
|
+
/** Public API intent for a newly inserted inbox message. */
|
|
33
|
+
export type AgentInboxInsertionIntent = 'followup' | 'steer' | 'inject';
|
|
34
|
+
/** Target carried by an inbox insertion observation. */
|
|
35
|
+
export type AgentInboxInsertedTarget = AgentInboxInsertionIntent | DurableTarget;
|
|
36
|
+
export interface AgentInboxInsertedEvent {
|
|
37
|
+
id: string;
|
|
38
|
+
agent: LiveAgent;
|
|
39
|
+
/**
|
|
40
|
+
* New messages report their public API intent. Replacements report the
|
|
41
|
+
* durable queue target that contained the replaced message.
|
|
42
|
+
*/
|
|
43
|
+
target: AgentInboxInsertedTarget;
|
|
44
|
+
input: DurableInboxMessage;
|
|
45
|
+
inboxSeq: number;
|
|
46
|
+
}
|
|
32
47
|
export interface LiveAgent {
|
|
33
48
|
readonly id: string;
|
|
34
49
|
readonly meta: AgentSessionMeta;
|
|
@@ -42,14 +57,15 @@ export interface LiveAgent {
|
|
|
42
57
|
readonly ctx: Context;
|
|
43
58
|
/** The scoped context setup composes before publication. */
|
|
44
59
|
readonly agentCtx: Context;
|
|
45
|
-
followup(input: AgentInput): void
|
|
46
|
-
steer(input: AgentInput): void
|
|
47
|
-
replaceMessage(messageId: string, input: AgentInput): void
|
|
48
|
-
removeMessage(messageId: string): void
|
|
49
|
-
|
|
60
|
+
followup(input: AgentInput): Promise<void>;
|
|
61
|
+
steer(input: AgentInput): Promise<void>;
|
|
62
|
+
replaceMessage(messageId: string, input: AgentInput): Promise<void>;
|
|
63
|
+
removeMessage(messageId: string): Promise<void>;
|
|
64
|
+
/** Durably stage model-visible input for the next step without waking idle work. */
|
|
65
|
+
inject(input: AgentInput): Promise<void>;
|
|
50
66
|
send(input: AgentInput, options?: {
|
|
51
67
|
mode?: 'followup' | 'steer';
|
|
52
|
-
}): void
|
|
68
|
+
}): Promise<void>;
|
|
53
69
|
cancel(cause: AgentCancelCause, options?: AgentCancelOptions): void;
|
|
54
70
|
whenIdle(): Promise<void>;
|
|
55
71
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Context } from "../../core/src/index.js";
|
|
2
2
|
import { type SessionLog } from "../../session/src/index.js";
|
|
3
|
-
import type { AgentContextBudget, AgentHooks, AgentInput, AgentRunOptions, AgentRunResult, AgentStreamEvent, LLMAdapter } from './types.js';
|
|
3
|
+
import type { AgentContextBudget, AgentHooks, AgentInput, AgentNextStepClaimer, AgentRunOptions, AgentRunResult, AgentStreamEvent, LLMAdapter } from './types.js';
|
|
4
4
|
export declare class AgentError extends Error {
|
|
5
5
|
name: string;
|
|
6
6
|
}
|
|
@@ -26,20 +26,25 @@ export interface AgentConfig {
|
|
|
26
26
|
llm?: LLMAdapter;
|
|
27
27
|
/** Bind the loop to a specific session instead of the ctx-provided singleton. */
|
|
28
28
|
session?: SessionLog;
|
|
29
|
-
/**
|
|
29
|
+
/** Compatibility diagnostic flag; request replayability is always enforced. */
|
|
30
30
|
assertReplayable?: boolean;
|
|
31
31
|
maxTurns?: number;
|
|
32
32
|
maxSteps?: number;
|
|
33
33
|
inbox?: AgentInbox;
|
|
34
34
|
hooks?: AgentHooks;
|
|
35
35
|
contextBudget?: AgentContextBudget;
|
|
36
|
+
/** Optional live-agent seam for work admitted after the first step. */
|
|
37
|
+
claimNextStep?: AgentNextStepClaimer;
|
|
36
38
|
}
|
|
37
39
|
export declare class AgentService {
|
|
38
40
|
private ctx;
|
|
39
41
|
private config;
|
|
42
|
+
private settleAttempt;
|
|
40
43
|
readonly inbox: AgentInbox;
|
|
41
44
|
private _seriesStarted;
|
|
42
45
|
private _persistedRequest;
|
|
46
|
+
private _streamRevision;
|
|
47
|
+
private _attempt;
|
|
43
48
|
constructor(ctx: Context, config?: AgentConfig);
|
|
44
49
|
/** Call when a request begins an explicit new model-message series. */
|
|
45
50
|
startSeries(): void;
|
|
@@ -53,6 +58,7 @@ export declare class AgentService {
|
|
|
53
58
|
private _enforceContextBudget;
|
|
54
59
|
private _persistStepInput;
|
|
55
60
|
private _assertReplayable;
|
|
61
|
+
private _claimNextStepMessages;
|
|
56
62
|
private _initialMessages;
|
|
57
63
|
private _extendMessages;
|
|
58
64
|
private _session;
|