UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

783 lines (722 loc) 31 kB
/** * # protocol/oversight-wire — the SNAKE_CASE wire mirror of the oversight contract (kestrel-telx.5). * * The oversight stream (contract §4) is served over the SAME platform HTTP/SSE dialect the * Operation stream is: **snake_case bodies**, an **opaque STRING cursor**, a **closed `event:` * enum**. `src/protocol/oversight.ts` is the camelCase DOMAIN contract both Renderings consume; * this leaf is its wire face, decoded to the domain types AT THE BOUNDARY (contract §4.4). It is * re-exported by {@link ./wire.ts} — the one import path the CLI transport * ({@link ../cli/backend/wire.ts}) already re-exports — so there is exactly ONE definition of the * oversight wire shapes and exactly ONE definition of the SSE vocabulary (the enum itself stays in * `oversight.ts`; nothing here re-declares it). It lives in its own file only because it is long; * every consumer reaches it through `wire.ts`, per the bead. * * ## FAIL CLOSED — a decoder is a GATE, not a cast * Every decoder below reads `unknown` and CHECKS each required field, throwing * {@link OversightWireError} naming the exact path. A missing / mistyped field is a loud protocol * violation, never `undefined` smuggled into a domain type and never a silent default. Nullable * contract fields (`fair`, `plan`, `note`, …) are decoded as EXPLICIT `null` — absent-not-hidden — * and a `null` where the contract requires a value still reds. * * ## ZERO runtime dependencies (OSS-ADR-0046 + tests/protocol.boundary.test.ts) * The only imports are intra-protocol. In particular this leaf holds NO CLI error type: it throws * its own dependency-free {@link OversightWireError}, which the CLI client * ({@link ../cli/backend/oversight-remote.ts}) wraps into a `CliError` with code * `OVERSIGHT_PROTOCOL_VIOLATION` (contract §4.3 rule 1/2). Same discipline as * {@link ../client/sse-transport.ts}: the transport/decoder layer is error-agnostic, the caller owns * the error TAXONOMY. * * ## NOT in the shipped JSON Schema (slice-1 posture) * `scripts/gen-wire-schema.ts` reflects an explicit root list (`WIRE_COMPOSITES`, the ten slice-1 * RESPONSE composites). The oversight shapes are deliberately NOT added to it: the platform route * (§4.1) does not exist yet, and shipping a schema for a route nobody serves would be a gate whose * stated coverage exceeds its actual coverage. Adding them is a one-line follow-up when the route * lands. */ import type { AgentTier, AgentTierStatus, Attribution, AuthorTier, Authorship, BookView, Caller, CallerDetection, CallerKind, EngineActionKind, EscalationReason, KernelBrief, KernelBudget, KernelClaim, KernelEngineAction, KernelField, KernelFill, KernelLeg, KernelMandate, KernelOwnerAct, KernelPlanState, KernelPosition, KernelRestingOrder, KernelVehicleHealth, KernelWake, MessageAuthor, Mode, OrgNode, OversightAct, OversightActRecord, OversightDelivery, OversightEscalation, OversightEvent, OversightEventType, OversightFrame, OversightIdentity, OversightKernel, OversightMessage, OversightTurn, PlanLifecycle, PlanOutcome, PodView, RiskEnvelope, SpectatorFrame, SpectatorPane, TurnDisposition, WakeSeverity, } from "./oversight.ts"; import { AGENT_TIERS, ATTRIBUTIONS, AUTHOR_TIERS, CALLER_DETECTIONS, CALLER_KINDS, ENGINE_ACTION_KINDS, ESCALATION_REASONS, MESSAGE_AUTHORS, MODES, OVERSIGHT_ACT_KINDS, PLAN_LIFECYCLES, PLAN_OUTCOMES, TURN_DISPOSITIONS, WAKE_SEVERITIES, } from "./oversight.ts"; import type { Scope } from "./index.ts"; import type { FailureClass, OperationId, SessionDiagnostic, SessionId, TurnBody, TurnEntry } from "./session.ts"; import { FAILURE_CLASSES, SESSION_DIAGNOSTICS } from "./session.ts"; /* ─────────────────────────── the fail-closed reader ─────────────────────────── */ /** A wire body that does not match the contract. Dependency-free on purpose: the CLI wraps it as a * `CliError{code:"OVERSIGHT_PROTOCOL_VIOLATION"}`, the SDK could wrap it as its own. */ export class OversightWireError extends Error { constructor( readonly path: string, detail: string, ) { super(`oversight wire: ${path}${detail}`); this.name = "OversightWireError"; } } type Rec = Record<string, unknown>; function kindOf(v: unknown): string { if (v === null) return "null"; if (Array.isArray(v)) return "array"; return typeof v; } /** Narrow `v` to a plain object or fail closed. */ function obj(v: unknown, path: string): Rec { if (typeof v !== "object" || v === null || Array.isArray(v)) { throw new OversightWireError(path, `expected an object, got ${kindOf(v)}`); } return v as Rec; } function str(o: Rec, k: string, path: string): string { const v = o[k]; if (typeof v !== "string") throw new OversightWireError(`${path}.${k}`, `expected string, got ${kindOf(v)}`); return v; } function num(o: Rec, k: string, path: string): number { const v = o[k]; if (typeof v !== "number" || !Number.isFinite(v)) { throw new OversightWireError(`${path}.${k}`, `expected a finite number, got ${kindOf(v)}`); } return v; } function bool(o: Rec, k: string, path: string): boolean { const v = o[k]; if (typeof v !== "boolean") throw new OversightWireError(`${path}.${k}`, `expected boolean, got ${kindOf(v)}`); return v; } /** A REQUIRED nullable field: present and either the value or an explicit `null` (absent-not-hidden * — a DROPPED key still reds; only an explicit `null` reads as UNKNOWN). */ function nullable<T>(read: (o: Rec, k: string, path: string) => T) { return (o: Rec, k: string, path: string): T | null => { if (!(k in o)) throw new OversightWireError(`${path}.${k}`, "required (nullable) field is absent — absent-not-hidden"); return o[k] === null ? null : read(o, k, path); }; } const nullStr = nullable(str); const nullNum = nullable(num); /** An OPTIONAL field: absent ⇒ `undefined`; present ⇒ type-checked. */ function optional<T>(read: (o: Rec, k: string, path: string) => T) { return (o: Rec, k: string, path: string): T | undefined => o[k] === undefined ? undefined : read(o, k, path); } /** A closed vocabulary member, checked against its runtime tuple — the wire can never smuggle an * off-enum member in as a bare string. */ function member<T extends string>(tuple: readonly T[]) { return (o: Rec, k: string, path: string): T => { const v = str(o, k, path); if (!(tuple as readonly string[]).includes(v)) { throw new OversightWireError(`${path}.${k}`, `${JSON.stringify(v)} is outside the closed vocabulary [${tuple.join(", ")}]`); } return v as T; }; } function list<T>(o: Rec, k: string, path: string, each: (v: unknown, path: string) => T): readonly T[] { const v = o[k]; if (!Array.isArray(v)) throw new OversightWireError(`${path}.${k}`, `expected array, got ${kindOf(v)}`); return v.map((item, i) => each(item, `${path}.${k}[${i}]`)); } function strList(o: Rec, k: string, path: string): readonly string[] { return list(o, k, path, (v, p) => { if (typeof v !== "string") throw new OversightWireError(p, `expected string, got ${kindOf(v)}`); return v; }); } /** A REQUIRED nullable SUB-OBJECT decoded by `dec`. */ function nullObj<T>(o: Rec, k: string, path: string, dec: (v: unknown, path: string) => T): T | null { if (!(k in o)) throw new OversightWireError(`${path}.${k}`, "required (nullable) section is absent — absent-not-hidden"); return o[k] === null ? null : dec(o[k], `${path}.${k}`); } const callerKind = member<CallerKind>(CALLER_KINDS); const callerDetection = member<CallerDetection>(CALLER_DETECTIONS); const mode = member<Mode>(MODES); const attribution = member<Attribution>(ATTRIBUTIONS); const planLifecycle = member<PlanLifecycle>(PLAN_LIFECYCLES); const planOutcome = member<PlanOutcome>(PLAN_OUTCOMES); const wakeSeverity = member<WakeSeverity>(WAKE_SEVERITIES); const engineActionKind = member<EngineActionKind>(ENGINE_ACTION_KINDS); const agentTier = member<AgentTier>(AGENT_TIERS); const authorTier = member<AuthorTier>(AUTHOR_TIERS); const escalationReason = member<EscalationReason>(ESCALATION_REASONS); const messageAuthor = member<MessageAuthor>(MESSAGE_AUTHORS); const turnDisposition = member<TurnDisposition>(TURN_DISPOSITIONS); const actKind = member(OVERSIGHT_ACT_KINDS); const failureClass = member<FailureClass>(FAILURE_CLASSES); const sessionDiagnostic = member<SessionDiagnostic>(SESSION_DIAGNOSTICS); const side = member<"buy" | "sell">(["buy", "sell"]); /* ────────────────────────────── identity (§3.1) ────────────────────────────── */ export function decodeCaller(v: unknown, path = "caller"): Caller { const o = obj(v, path); return { kind: callerKind(o, "kind", path), harness: nullStr(o, "harness", path), detectedBy: callerDetection(o, "detected_by", path), interactive: bool(o, "interactive", path), }; } export function decodeOversightIdentity(v: unknown, path = "identity"): OversightIdentity { const o = obj(v, path); const operation = optional(str)(o, "operation", path); return { caller: decodeCaller(o["caller"], `${path}.caller`), sessionId: str(o, "session_id", path) as SessionId, ...(operation !== undefined ? { operation: operation as OperationId } : {}), mode: mode(o, "mode", path), // `Scope` is the platform's capability vocabulary, already pinned by the Operation dialect; // it rides through as its string members (no oversight-local re-declaration). scopes: strList(o, "scopes", path) as readonly Scope[], }; } /* ────────────────────────────── the kernel (§3.2) ──────────────────────────── */ export function decodeKernelField(v: unknown, path: string): KernelField { const o = obj(v, path); const source = optional(str)(o, "source", path); const modelVer = optional(str)(o, "model_ver", path); const confidence = optional(num)(o, "confidence", path); const asOfSeq = optional(num)(o, "as_of_seq", path); return { value: num(o, "value", path), attribution: attribution(o, "attribution", path), ...(source !== undefined ? { source } : {}), ...(modelVer !== undefined ? { modelVer } : {}), ...(confidence !== undefined ? { confidence } : {}), ...(asOfSeq !== undefined ? { asOfSeq } : {}), }; } export function decodeKernelLeg(v: unknown, path: string): KernelLeg { const o = obj(v, path); const strike = optional(num)(o, "strike", path); const right = optional(member<"C" | "P">(["C", "P"]))(o, "right", path); // ADR-0017: strike and right are BOTH present (an option) or BOTH absent (spot). A half-populated // leg would render a fictional strike — refuse it. if ((strike === undefined) !== (right === undefined)) { throw new OversightWireError(path, "strike and right must be BOTH present (option) or BOTH absent (spot) — ADR-0017"); } return { symbol: str(o, "symbol", path), ...(strike !== undefined ? { strike } : {}), ...(right !== undefined ? { right } : {}), }; } export function decodeKernelPosition(v: unknown, path: string): KernelPosition { const o = obj(v, path); return { leg: decodeKernelLeg(o["leg"], `${path}.leg`), qty: num(o, "qty", path), basis: num(o, "basis", path), fair: nullNum(o, "fair", path), unrealUsd: nullNum(o, "unreal_usd", path), plan: nullStr(o, "plan", path), claimOwner: nullStr(o, "claim_owner", path), structure: nullStr(o, "structure", path), }; } export function decodeKernelRestingOrder(v: unknown, path: string): KernelRestingOrder { const o = obj(v, path); return { ref: str(o, "ref", path), leg: decodeKernelLeg(o["leg"], `${path}.leg`), side: side(o, "side", path), qty: num(o, "qty", path), px: num(o, "px", path), live: bool(o, "live", path), clamped: bool(o, "clamped", path), note: nullStr(o, "note", path), plan: nullStr(o, "plan", path), }; } export function decodeKernelFill(v: unknown, path: string): KernelFill { const o = obj(v, path); return { leg: decodeKernelLeg(o["leg"], `${path}.leg`), side: side(o, "side", path), qty: num(o, "qty", path), px: num(o, "px", path), plan: nullStr(o, "plan", path), }; } export function decodeKernelPlanState(v: unknown, path: string): KernelPlanState { const o = obj(v, path); return { name: str(o, "name", path), state: planLifecycle(o, "state", path), outcome: "outcome" in o && o["outcome"] === null ? null : planOutcome(o, "outcome", path), note: nullStr(o, "note", path), blockedReason: nullStr(o, "blocked_reason", path), }; } export function decodeRiskEnvelope(v: unknown, path: string): RiskEnvelope { const o = obj(v, path); return { remainingR: num(o, "remaining_r", path), planEnvelope: num(o, "plan_envelope", path), bookEnvelope: num(o, "book_envelope", path), ownerEnvelope: num(o, "owner_envelope", path), }; } export function decodeKernelBudget(v: unknown, path: string): KernelBudget { const o = obj(v, path); return { used: nullNum(o, "used", path), remaining: nullNum(o, "remaining", path), total: nullNum(o, "total", path), maxConcurrentR: nullNum(o, "max_concurrent_r", path), }; } export function decodeKernelWake(v: unknown, path: string): KernelWake { const o = obj(v, path); return { reason: str(o, "reason", path), severity: wakeSeverity(o, "severity", path), deadlineMin: nullNum(o, "deadline_min", path), }; } export function decodeKernelVehicleHealth(v: unknown, path: string): KernelVehicleHealth { const o = obj(v, path); return { symbol: str(o, "symbol", path), bidPresentRate: num(o, "bid_present_rate", path), twoSided: bool(o, "two_sided", path), staleS: num(o, "stale_s", path), dark: bool(o, "dark", path), }; } export function decodeKernelEngineAction(v: unknown, path: string): KernelEngineAction { const o = obj(v, path); return { id: str(o, "id", path), kind: engineActionKind(o, "kind", path), asofSeq: num(o, "asof_seq", path), reason: nullStr(o, "reason", path), }; } export function decodeKernelOwnerAct(v: unknown, path: string): KernelOwnerAct { const o = obj(v, path); return { id: str(o, "id", path), kind: str(o, "kind", path), asofSeq: num(o, "asof_seq", path) }; } export function decodeKernelClaim(v: unknown, path: string): KernelClaim { const o = obj(v, path); return { field: decodeKernelField(o["field"], `${path}.field`), claimType: member<"predictor" | "regime">(["predictor", "regime"])(o, "claim_type", path), }; } export function decodeKernelMandate(v: unknown, path: string): KernelMandate { const o = obj(v, path); return { objective: str(o, "objective", path), rUsd: num(o, "r_usd", path), successCriterion: str(o, "success_criterion", path), riskRule: str(o, "risk_rule", path), }; } export function decodeKernelBrief(v: unknown, path: string): KernelBrief { const o = obj(v, path); return { text: str(o, "text", path), hash: str(o, "hash", path), version: nullStr(o, "version", path) }; } /** The acting kernel. EVERY section is required on the wire (absent-not-hidden, §3.2): an absent * source section arrives as an explicit `null`/`[]`, never as a dropped key. */ export function decodeOversightKernel(v: unknown, path = "kernel"): OversightKernel { const o = obj(v, path); return { mandate: nullObj(o, "mandate", path, decodeKernelMandate), brief: nullObj(o, "brief", path, decodeKernelBrief), wake: nullObj(o, "wake", path, decodeKernelWake), dataHealth: list(o, "data_health", path, decodeKernelVehicleHealth), unavailable: strList(o, "unavailable", path), budgetEnvelope: nullObj(o, "budget_envelope", path, decodeRiskEnvelope), ownerActs: list(o, "owner_acts", path, decodeKernelOwnerAct), engineLog: list(o, "engine_log", path, decodeKernelEngineAction), claims: list(o, "claims", path, decodeKernelClaim), positions: list(o, "positions", path, decodeKernelPosition), resting: list(o, "resting", path, decodeKernelRestingOrder), fillsSinceLast: list(o, "fills_since_last", path, decodeKernelFill), budget: nullObj(o, "budget", path, decodeKernelBudget), plans: list(o, "plans", path, decodeKernelPlanState), }; } /* ─────────────────────────────── the org (§3.3) ────────────────────────────── */ export function decodeAuthorship(v: unknown, path: string): Authorship { const o = obj(v, path); return { tier: authorTier(o, "tier", path), model: nullStr(o, "model", path), version: nullStr(o, "version", path), }; } export function decodeAgentTierStatus(v: unknown, path: string): AgentTierStatus { const o = obj(v, path); const s = obj(o["strategist"], `${path}.strategist`); const w = obj(o["watcher"], `${path}.watcher`); const e = obj(o["escalation"], `${path}.escalation`); return { strategist: { model: nullStr(s, "model", `${path}.strategist`), thesis: nullStr(s, "thesis", `${path}.strategist`), briefHash: nullStr(s, "brief_hash", `${path}.strategist`), lastReframeSeq: nullNum(s, "last_reframe_seq", `${path}.strategist`), }, watcher: { model: nullStr(w, "model", `${path}.watcher`), lastActionSeq: nullNum(w, "last_action_seq", `${path}.watcher`), }, escalation: { pending: bool(e, "pending", `${path}.escalation`), reason: "reason" in e && e["reason"] === null ? null : escalationReason(e, "reason", `${path}.escalation`), atSeq: nullNum(e, "at_seq", `${path}.escalation`), }, configId: nullStr(o, "config_id", path), }; } export function decodeBookView(v: unknown, path: string): BookView { const o = obj(v, path); const cov = obj(o["coverage"], `${path}.coverage`); const st = obj(o["status"], `${path}.status`); return { bookId: str(o, "book_id", path), coverage: { symbols: strList(cov, "symbols", `${path}.coverage`), thesis: str(cov, "thesis", `${path}.coverage`), }, kernel: decodeOversightKernel(o["kernel"], `${path}.kernel`), tiers: decodeAgentTierStatus(o["tiers"], `${path}.tiers`), status: { severity: wakeSeverity(st, "severity", `${path}.status`), deadlineMin: nullNum(st, "deadline_min", `${path}.status`), wakesRemaining: nullNum(st, "wakes_remaining", `${path}.status`), coalesced: num(st, "coalesced", `${path}.status`), }, }; } export function decodePodView(v: unknown, path: string): PodView { const o = obj(v, path); const aggRaw = obj(o["aggregate"], `${path}.aggregate`); const aggregate: Record<string, Record<string, number>> = {}; for (const [childId, facts] of Object.entries(aggRaw)) { const f = obj(facts, `${path}.aggregate.${childId}`); const inner: Record<string, number> = {}; for (const key of Object.keys(f)) inner[key] = num(f, key, `${path}.aggregate.${childId}`); aggregate[childId] = inner; } return { podId: str(o, "pod_id", path), envelope: decodeRiskEnvelope(o["envelope"], `${path}.envelope`), aggregate, children: list(o, "children", path, decodeOrgNode), }; } export function decodeOrgNode(v: unknown, path: string): OrgNode { const o = obj(v, path); const node = member<"book" | "pod">(["book", "pod"])(o, "node", path); return node === "book" ? { node: "book", book: decodeBookView(o["book"], `${path}.book`) } : { node: "pod", pod: decodePodView(o["pod"], `${path}.pod`) }; } /* ──────────────────────────── the Frame (§3.4, §4.2) ───────────────────────── */ export function decodeSpectatorPane(v: unknown, path: string): SpectatorPane { const o = obj(v, path); return { paneId: str(o, "pane_id", path), columns: strList(o, "columns", path), rows: list(o, "rows", path, (row, rp) => { if (!Array.isArray(row)) throw new OversightWireError(rp, `expected a row array, got ${kindOf(row)}`); return row.map((cell, i) => { if (cell === null || typeof cell === "string" || (typeof cell === "number" && Number.isFinite(cell))) { return cell as string | number | null; } throw new OversightWireError(`${rp}[${i}]`, `expected a scalar cell (string | finite number | null), got ${kindOf(cell)}`); }); }), }; } export function decodeSpectatorFrame(v: unknown, path: string): SpectatorFrame { const o = obj(v, path); return { watchlist: strList(o, "watchlist", path), // The ONE permitted wall clock in this contract (§4 invariant 4) — spectator only. asof: str(o, "asof", path), panes: list(o, "panes", path, decodeSpectatorPane), }; } export function decodeOversightMessage(v: unknown, path: string): OversightMessage { const o = obj(v, path); return { seq: num(o, "seq", path), messageId: str(o, "message_id", path), author: messageAuthor(o, "author", path), to: nullStr(o, "to", path), text: str(o, "text", path), }; } export function decodeOversightFrame(v: unknown, path = "frame"): OversightFrame { const o = obj(v, path); return { identity: decodeOversightIdentity(o["identity"], `${path}.identity`), asofSeq: num(o, "asof_seq", path), pod: nullObj(o, "pod", path, decodePodView), spectator: nullObj(o, "spectator", path, decodeSpectatorFrame), pending: nullObj(o, "pending", path, decodeOversightDelivery), messages: list(o, "messages", path, decodeOversightMessage), }; } /* ─────────────────────────── the turn-stream (§3.5) ────────────────────────── */ export function decodeOversightDelivery(v: unknown, path = "delivery"): OversightDelivery { const o = obj(v, path); return { sessionId: str(o, "session_id", path) as SessionId, ordinal: num(o, "ordinal", path), parentHash: str(o, "parent_hash", path), // The RUNTIME's Frame address, carried verbatim — a Rendering never re-hashes the projection. frameRoot: str(o, "frame_root", path), bookId: str(o, "book_id", path), tier: agentTier(o, "tier", path), kernel: decodeOversightKernel(o["kernel"], `${path}.kernel`), }; } export function decodeTurnBody(v: unknown, path: string): TurnBody { const o = obj(v, path); const kind = member<"authored" | "stand-down" | "failure">(["authored", "stand-down", "failure"])(o, "kind", path); switch (kind) { case "authored": return { kind, bytes: str(o, "bytes", path) }; case "stand-down": return { kind, reason: str(o, "reason", path) }; case "failure": return { kind, failureClass: failureClass(o, "failure_class", path) }; } } export function decodeTurnEntry(v: unknown, path: string): TurnEntry { const o = obj(v, path); if (o["kind"] !== "turn") throw new OversightWireError(`${path}.kind`, `expected "turn", got ${JSON.stringify(o["kind"])}`); return { kind: "turn", sessionId: str(o, "session_id", path) as SessionId, ordinal: num(o, "ordinal", path), parentHash: str(o, "parent_hash", path), frameRoot: str(o, "frame_root", path), authoredSha256: str(o, "authored_sha256", path), body: decodeTurnBody(o["body"], `${path}.body`), entryHash: str(o, "entry_hash", path), }; } export function decodeOversightTurn(v: unknown, path = "turn"): OversightTurn { const o = obj(v, path); return { entry: decodeTurnEntry(o["entry"], `${path}.entry`), disposition: turnDisposition(o, "disposition", path), authoredBy: decodeAuthorship(o["authored_by"], `${path}.authored_by`), escalated: "escalated" in o && o["escalated"] === null ? null : escalationReason(o, "escalated", path), }; } export function decodeOversightEscalation(v: unknown, path = "escalation"): OversightEscalation { const o = obj(v, path); return { seq: num(o, "seq", path), bookId: str(o, "book_id", path), // NB: dot-access `o.from`, never the bracket-quoted form — the protocol.boundary import // scanner regex reads a quoted key of that name as an ES import specifier and false-positives. from: decodeAuthorship(o.from, `${path}.from`), to: authorTier(o, "to", path), reason: escalationReason(o, "reason", path), note: nullStr(o, "note", path), answeredBySeq: nullNum(o, "answered_by_seq", path), }; } /* ──────────────────────────────── acts (§3.6) ──────────────────────────────── */ export function decodeOversightAct(v: unknown, path = "act"): OversightAct { const o = obj(v, path); const act = actKind(o, "act", path); switch (act) { case "allocate": return { act, target: str(o, "target", path), envelopeR: num(o, "envelope_r", path) }; case "arm": return { act, target: str(o, "target", path) }; case "de-arm": return { act, target: str(o, "target", path), reason: str(o, "reason", path) }; case "coverage": return { act, bookId: str(o, "book_id", path), symbols: strList(o, "symbols", path), thesis: str(o, "thesis", path), // REQUIRED: instruments alone are not coverage }; case "fund": return { act, ownerEnvelope: num(o, "owner_envelope", path), authorizationRef: str(o, "authorization_ref", path) }; case "de-fund": return { act, ownerEnvelope: num(o, "owner_envelope", path) }; case "pause": return { act, target: str(o, "target", path) }; case "veto": return { act, target: str(o, "target", path), reason: str(o, "reason", path) }; } } export function decodeOversightActRecord(v: unknown, path = "record"): OversightActRecord { const o = obj(v, path); return { seq: num(o, "seq", path), actId: str(o, "act_id", path), act: decodeOversightAct(o["act"], `${path}.act`), authoredBy: decodeAuthorship(o["authored_by"], `${path}.authored_by`), }; } /* ───────────────────────────── the SSE envelope (§4) ───────────────────────── */ /** * The oversight SSE frame's JSON `data:` payload — the mirror of {@link ./wire.ts WireOperationEvent}. * `cursor` echoes the SSE `id:` line (an OPAQUE STRING — §4.3 rule 6: never parsed), `type` is the * closed enum from `oversight.ts` (ONE definition — this shape re-declares nothing), and the * type-specific body rides in `data` as snake_case. */ export interface WireOversightEvent { readonly cursor: string; readonly type: OversightEventType; readonly session_id: string; readonly data?: Record<string, unknown>; } /** * Decode ONE typed oversight event body into the camelCase domain union (contract §4.2 table). * * `type` MUST already have been admitted against `isOversightEventType` (§4.3 rule 1 — the * transport's closed-vocabulary gate); this decoder is TOTAL over the enum, so a member added to * the enum without a case here is a compile error (the `never` exhaustiveness arm), not a silent * pass-through. */ export function decodeOversightEvent(type: OversightEventType, data: unknown): OversightEvent { const path = type; switch (type) { case "oversight.snapshot": return { type, frame: decodeOversightFrame(obj(data, path)["frame"], `${path}.frame`) }; case "oversight.delivery": { const o = obj(data, path); return { type, seq: num(o, "seq", path), delivery: decodeOversightDelivery(o["delivery"], `${path}.delivery`) }; } case "oversight.turn": { const o = obj(data, path); return { type, seq: num(o, "seq", path), turn: decodeOversightTurn(o["turn"], `${path}.turn`) }; } case "oversight.act": return { type, record: decodeOversightActRecord(obj(data, path)["record"], `${path}.record`) }; case "oversight.escalation": return { type, escalation: decodeOversightEscalation(obj(data, path)["escalation"], `${path}.escalation`) }; case "oversight.message": return { type, message: decodeOversightMessage(obj(data, path)["message"], `${path}.message`) }; case "oversight.journal": { const o = obj(data, path); return { type, seq: num(o, "seq", path), bookId: str(o, "book_id", path), authoredBy: decodeAuthorship(o["authored_by"], `${path}.authored_by`), text: str(o, "text", path), }; } case "oversight.diagnostic": { const o = obj(data, path); return { type, diagnostic: sessionDiagnostic(o, "diagnostic", path), ordinal: num(o, "ordinal", path) }; } case "oversight.finalized": { const o = obj(data, path); return { type, seq: num(o, "seq", path), sessionId: str(o, "session_id", path) as SessionId, tipHash: str(o, "tip_hash", path), artifacts: strList(o, "artifacts", path), }; } case "oversight.failed": return { type, reason: str(obj(data, path), "reason", path) }; default: { // fail closed: an enum member with no decoder is a protocol drift, never a silent default. const never: never = type; throw new OversightWireError("type", `no decoder for ${JSON.stringify(never)}`); } } } /** * The BUS-BACKED `seq` of a decoded event, or `null` for the two members that carry none * (`oversight.snapshot` — a projection; `oversight.diagnostic` — nothing landed; `oversight.failed` * — the stream is dying). This is the input to §4.3 rule 4 (monotone seq): the rule can only apply * where the contract says a `seq` exists, so the extraction lives HERE, beside the shapes, rather * than being re-derived by each client. */ export function busSeqOf(e: OversightEvent): number | null { switch (e.type) { case "oversight.delivery": case "oversight.turn": case "oversight.journal": case "oversight.finalized": return e.seq; case "oversight.act": return e.record.seq; case "oversight.escalation": return e.escalation.seq; case "oversight.message": return e.message.seq; case "oversight.snapshot": case "oversight.diagnostic": case "oversight.failed": return null; default: { const never: never = e; throw new OversightWireError("event", `unhandled event ${JSON.stringify(never)}`); } } }