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.
109 lines (108 loc) • 6.39 kB
JavaScript
/**
* kestrel.markets/protocol — the OVERSIGHT contract (protocol v0.3).
*
* The cross-repo seam between the CLI (OSS `kestrel` — `ascii`/Ink Rendering) and
* the Web dashboard (platform `kestrel.markets` — `html` Rendering). ADR-0035: the
* human never places a ticket — the human mode is PM/Pod OVERSIGHT (positions,
* plans, P&L, risk envelope) plus chat. "The agent's ASCII screen and the human's
* HTML chart are two Renderings of one Frame" (CONTEXT.md, *Rendering*) — so if the
* Web dashboard can show a number the CLI cannot, that number was fetched OUTSIDE
* this contract, and the protocol is broken. This module owns the shared model;
* both Renderings consume it, neither invents a value.
*
* Normative source: docs/design/oversight-protocol-contract.md §3–§5 (owner-approved
* 2026-07-14). The model has exactly EIGHT parts and nothing else belongs in it.
*
* HARD CONSTRAINTS (identical to src/protocol/index.ts — see its header):
* - ZERO RUNTIME dependencies. The ONLY imports are intra-protocol type-imports
* (`./session.ts`, `./index.ts` — both dependency-free); this module pulls in NO
* engine / session / blotter / fill / lang / grade / frame / render / ledger /
* cli code and must typecheck with `chdb` uninstalled. The import-graph boundary
* is an automated CI invariant (tests/protocol.boundary.test.ts). Hence the
* acting Kernel is a STRUCTURAL MIRROR of `src/frame/types.ts` `Kernel` — never
* an import of it (§3.2). CONFORMANCE, HONESTLY: today this leaf is a
* HAND-MAINTAINED parallel of `src/frame/types.ts` with NO compiler link — its
* field-for-field correspondence is verified by audit, not yet by a witness. The
* total projector `src/oversight/project.ts` (bead kestrel-telx.2, NOT YET
* LANDED) is the PLANNED conformance point: it will carry a `Record<keyof Kernel,
* true>` key-witness on the `src/frame` side so that a section added to `Kernel`
* breaks the build until this mirror handles it. Until it lands, adding a field
* to `src/frame/types.ts` `Kernel*` and forgetting it here does NOT auto-red — so
* any `Kernel*` edit must be reconciled against this leaf by hand.
* - SHAPES ONLY. No signing keys, no Stripe/commerce logic. Every signed,
* replayable artifact carries an opaque `*Ref` HANDLE, never a key or a signing
* routine (`OversightAct` 'fund' carries `authorizationRef`, never a credential).
* - GENERIC INSTRUMENTS ONLY. No founding-app tickers or strategy names in types,
* comments, or examples. Illustrative fixtures use SPX/SPY/QQQ (ARCHITECTURE §7);
* product symbols ride INSIDE opaque `symbol` fields at runtime.
*
* Version: `major.minor`. This leaf first landed at PROTOCOL_VERSION 0.3 (bumped 0.2 → 0.3, purely
* additive). It now bumps 0.3 → 0.4 for the `ActReceipt` `state` field (kestrel-ysqj): `submitAct`'s
* receipt gains an explicit queued-vs-bound stamp so 'admitted' and 'in force' are distinguishable to a
* remote caller — a shape change to an existing type, hence a minor bump (index.ts owns the constant;
* this note records why it moved). The schema tag `kestrel.session/<PROTOCOL_VERSION>` and the
* content-addressed view/rendering identities track it; the graded-bus conformance root does NOT (it is
* decoupled, ENGINE_VERSION-gated), so only the catalog-records bake re-pins, never a conformance root.
*
* NAMING: index.ts re-exports leaves with `export *` and already exports
* `Position`/`Instrument`/`Order`/`Fill`/`Side`, so every oversight type here is
* PREFIXED (`KernelPosition`, `KernelRestingOrder`, `KernelFill`, …). A collision is
* a compile error, not a style question.
*
* CLOSED-VOCABULARY IDIOM (mandatory): every union ships its runtime tuple + the
* both-ways exhaustiveness guard (`SESSION_DIAGNOSTICS` / `_SessionDiagnosticsExhaustive`
* in session.ts), so a member added to (or removed from) a union without editing its
* tuple BREAKS THE BUILD. That is what makes "an event outside the enum is a protocol
* violation" enforceable rather than aspirational.
*/
export const CALLER_KINDS = ["agent", "human"];
export const CALLER_DETECTIONS = ["flag", "env", "ci", "tty"];
export const MODES = ["sim", "paper", "live"];
export const ATTRIBUTIONS = ["OBS", "CALC", "MODEL"];
export const PLAN_LIFECYCLES = [
"authored", "armed", "fired", "managing", "done",
];
export const PLAN_OUTCOMES = ["filled", "expired", "invalidated"];
export const WAKE_SEVERITIES = ["routine", "elevated", "urgent"];
export const ENGINE_ACTION_KINDS = [
"fired", "cancelled", "rejected", "clamped",
];
export const CLAIM_TYPES = ["predictor", "regime"];
export const AGENT_TIERS = ["strategist", "watcher"];
export const ESCALATION_REASONS = [
"mandate-edge", "brief-flag", "regime", "uncertainty",
];
export const AUTHOR_TIERS = [
"runtime", "watcher", "strategist", "human",
];
export const TURN_DISPOSITIONS = [
"armed", "revised", "pass", "stood-down", "failure",
];
export const OVERSIGHT_ACT_KINDS = [
"allocate", "arm", "de-arm", "coverage", "fund", "de-fund", "pause", "veto",
];
/** The acts that only NARROW authority. These are ALWAYS admitted — de-risking is never gated,
* never queued behind an approval, never refused for want of capital. */
export const NARROWING_ACTS = [
"de-arm", "de-fund", "pause", "veto",
];
export const ACT_STATES = ["queued", "bound"];
export const MESSAGE_AUTHORS = ["owner", "pm", "trader"];
export const INPUT_RESOLUTION_KINDS = [
"grammar", "shortcut", "act", "chat", "parse-error",
];
export const OVERSIGHT_EVENT_TYPES = [
"oversight.snapshot", "oversight.delivery", "oversight.turn", "oversight.act",
"oversight.escalation", "oversight.message", "oversight.journal", "oversight.diagnostic",
"oversight.finalized", "oversight.failed",
];
/** The two TERMINAL event types that end the stream (mirror of the `operation.completed` /
* `operation.failed` pair). `oversight.finalized` ends it cleanly; `oversight.failed` throws. */
export const OVERSIGHT_TERMINAL_TYPES = [
"oversight.finalized", "oversight.failed",
];
/** True iff `t` is a contract event type. Anything else is a protocol violation the client MUST
* fail closed on (mirror of `isSseEventType`). */
export function isOversightEventType(t) {
return OVERSIGHT_EVENT_TYPES.includes(t);
}