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.

178 lines (165 loc) 10.3 kB
/** * # sdk/types — the ONE typed Kestrel SDK contract (kestrel-djm.5) * * The transport-neutral surface a caller writes ONCE and runs LOCAL or REMOTE. Every method returns a * djm.2 PROTOCOL object (never a face-local success/failure state): catalog discovery yields * {@link CatalogEntry}[]; validation yields the client's {@link ValidateOutcome}; a Session interaction * yields the djm.4 {@link Delivery} / {@link SessionResponse} / {@link SessionTranscript}; finalize yields a * protocol {@link Blotter} + tip + conformance root + artifact refs; grade yields the portable * {@link GradeOutcome}. The 402/Offer boundary is DATA on {@link Gated} (the client's exact shape), and an * unknown/off-contract subject is a TYPED refusal (fail-closed) — the SDK speaks the protocol, it does not * invent a vocabulary. * * ZERO-WEIGHT: this module carries NO value import of the local runtime — every djm.2/djm.4 shape is a * TYPE-ONLY import (erased at build), so `src/sdk/types.ts`, `src/sdk/facade.ts`, and the HTTP transport * (`src/sdk/remote.ts`) that consume it stay light (the djm.3 light/heavy seam, pinned by * tests/package.boundary.test.ts). {@link Gated}/{@link GradeOutcome}/{@link ValidateOutcome} are RE-EXPORTED * from the 109 client — one definition, never a fork. * * ── DESIGN FORK LOGGED (hard rules forbid .beads writes — recorded here like djm.4's 7dv.4 note) ── * (1) ONE client interface, TRANSPORT via factory. `createSdk(transport)` (facade.ts) wires a low-level * {@link Transport} seam into the {@link KestrelSdk} facade; `localTransport()` (local.ts, heavy) binds * the djm.4 controller + djm.8 catalog + engine grade in-process; `remoteTransport({ baseUrl, fetch })` * (remote.ts, light) binds the 109 client over the 5rb wire. Minimal shape consistent with djm.4's * `openSession(spec)` factory and 109's `new KestrelClient(opts)` — no third contract. * (2) PROTOCOL OBJECTS, no face-local states (this file's whole point). * (3) SdkFinalized ADDS `conformanceRoot` (= `blotter.session.bus.sha256` = the catalog's pinned root) and * `artifacts` to djm.4's `SessionFinalized`, and narrows `blotter` to the djm.2 PROTOCOL {@link Blotter} * (the generic public view) — the SESSION's rich engine Blotter stays on the local path. Minimal shape * consistent with protocol/session.ts's `FinalizeMessage` (`tipHash` + `artifacts`). */ import type { CatalogEntry, CatalogListing, CatalogPage } from "../protocol/catalog.ts"; import type { Blotter, EventCursor } from "../protocol/index.ts"; import type { SessionId } from "../protocol/session.ts"; import type { AuthoredResponse, BoundResponse, Delivery, SessionResponse, SessionTranscript, } from "../session/controller-types.ts"; // The 402/Offer + verdict + validation shapes the SDK speaks — RE-EXPORTED from the 109 client so both // transports return the SAME types (never forked). These are type-only edges (erased): they add no weight. export type { Gated, GradeOutcome, ValidateOutcome } from "../client/index.ts"; // The djm.4 Session shapes the SDK REUSES verbatim on the incremental surface (also type-only). export type { Delivery, SessionResponse, SessionTranscript, AuthoredResponse, BoundResponse } from "../session/controller-types.ts"; import type { Gated, GradeOutcome, ValidateOutcome } from "../client/index.ts"; /** * WHAT to run: a content-addressed CATALOG subject (the djm.8 pinned entry, resolved by the LOCAL loader or * the server's dataset artifact) or a RAW dataset (a plan `source` over a `dataset` artifact — the paid * boundary rides here). A discriminated union so an unknown subject is a typed refusal, never a silent load. */ export type SessionRef = | { readonly kind: "catalog"; readonly id: string } | { readonly kind: "dataset"; readonly source: string; readonly dataset: string }; /** * The sealed Session (djm.5 finalize): the protocol {@link Blotter} + the `tipHash` (tail of the pentad * chain) + the `conformanceRoot` (= the graded-bus sha256 = `SimRunId`, the catalog-pinned digest) + the * content-addressed `artifacts`. Narrower than djm.4's `SessionFinalized` (which carries the rich engine * Blotter + captured turns): the SDK surface is the PROTOCOL view. */ export interface SdkFinalized { readonly blotter: Blotter; readonly sessionId: SessionId; readonly tipHash: string; /** The graded-bus sha256 (`blotter.session.bus.sha256` = `SimRunId`); the catalog's pinned root. */ readonly conformanceRoot: string; /** Content-addressed handles to the delivered Blotter/Grade artifacts (djm.2 FinalizeMessage.artifacts). */ readonly artifacts: readonly string[]; } /** * The incremental Session surface (djm.4 verbs, transport-neutral): `start` → `advance`/`revise`/`submit` * → `resume` → `finalize`. Every verb returns a djm.2/djm.4 protocol object. Identical shape on LOCAL (wraps * the djm.4 controller) and HTTP (replays the server-computed, catalog-pinned transcript). */ export interface KestrelSession { /** The deterministic Session identity (= genesis hash). Available immediately. */ readonly sessionId: SessionId; /** START: deliver the date-blind OPEN Frame (ordinal OPEN_ORDINAL). */ start(): Promise<Delivery>; /** AUTHORED-OR-STAND-DOWN: commit the pending slot, deliver the next eligible Wake (`next: null` at settle). */ advance(response: AuthoredResponse): Promise<SessionResponse>; /** REVISION: author a superseding document at the current slot (behaviourally identical to advance). */ revise(response: AuthoredResponse): Promise<SessionResponse>; /** EXPLICIT-BINDING turn: reconcile a fully-bound response against the recorded slot (idempotent/typed-refusal). */ submit(response: BoundResponse): Promise<SessionResponse>; /** RESUME: re-derive the committed pentad-chained transcript (nothing to restore). */ resume(after?: EventCursor): Promise<SessionTranscript>; /** FINALIZE: seal the chain → protocol Blotter + tip + conformance root + artifacts. */ finalize(): Promise<SdkFinalized>; } /** A grade over one or more Blotter artifacts (a Blotter's `sessionId` is its artifact id). */ export interface GradeRequest { readonly blotters: readonly string[]; } /** A resolved artifact reference: its `ref`, its `kind`, and the resolved protocol payload. */ export interface ArtifactResult { readonly ref: string; readonly kind: string; readonly value: unknown; } /** Address a durable, resumable control-plane Operation (ADR-0004) for {@link KestrelSdk.resumeOperation}. */ export interface OperationRef { readonly operationId: string; /** Resume strictly after this opaque cursor (absent ⇒ from the beginning). */ readonly after?: EventCursor; } /** The re-derived tail of an Operation: the terminal cursor + the accumulated protocol payload. */ export interface OperationResumption { readonly operationId: string; readonly cursor: EventCursor | null; readonly payload: Readonly<Record<string, unknown>>; } /** * The ONE typed Kestrel client. Covers catalog discovery, validation, incremental Session interaction, * Grade, artifacts, and Operation resume. Transport-agnostic — the SAME object shape whether it wraps the * LOCAL controller or the HTTP client. Its `transportKind` / `version` / `transportVersion` are exposed for * experimental evidence (djm.5 AC5). */ export interface KestrelSdk { /** Which transport backs this client. */ readonly transportKind: Transport["kind"]; /** The SDK contract version (protocol-anchored). */ readonly version: string; /** The bound transport's version tag. */ readonly transportVersion: string; /** Catalog discovery — the canonical {@link CatalogPage} (kestrel-adge): the byte-identical * {@link CatalogListing}[] menu (kestrel-dnxq) PLUS the optional ex-ante {@link CatalogPricing} block. * Returning the WRAPPED page (not a bare array) makes this return type match the served `{ entries, * pricing }` runtime shape — `pricing` is surfaced when the producer serves one, else absent. The listing * CONTENTS are transport-specific (offline sample vs managed catalog); the reproducibility-complete * {@link CatalogEntry} surface resolves at `openSession`/grade, not here. */ catalog(): Promise<CatalogPage>; /** Pure validation → `{ ok, diagnostics }` (no face-local verdict). */ validate(document: string): Promise<ValidateOutcome>; /** * Open an incremental Session over a subject; the 402/Offer boundary is DATA even here. An optional * `document` supplies the customer strategy (Kestrel plan text) the managed backend's author-no-strategy * fence requires (ADR-0012, kestrel-88u2) — the same role the sim verb's `--plans` plays. On the HTTP * transport a catalog subject WITH a document sends it as the `POST /sim` source; a bare openSession keeps * today's behavior (the platform's fence answers — now a legible refusal). The LOCAL transport already * carries the catalog recipe's strategy, so it ignores the argument. */ openSession(subject: SessionRef, document?: string): Promise<Gated<KestrelSession>>; /** Grade one or more Blotter artifacts → the portable {@link GradeOutcome} (may gate). */ grade(request: GradeRequest): Promise<Gated<GradeOutcome>>; /** Resolve a content-addressed artifact reference. */ artifact(ref: string): Promise<ArtifactResult>; /** Resume a durable Operation from its cursor. */ resumeOperation(ref: OperationRef): Promise<OperationResumption>; } /** * The low-level TRANSPORT seam a concrete transport implements and `createSdk` wraps. Identical method set * to {@link KestrelSdk} minus the cross-cutting version fields — the facade adds those. LOCAL and HTTP * transports are behaviourally indistinguishable at this seam (same protocol objects, same digests). */ export interface Transport { readonly kind: "local" | "http"; readonly version: string; catalog(): Promise<CatalogPage>; validate(document: string): Promise<ValidateOutcome>; openSession(subject: SessionRef, document?: string): Promise<Gated<KestrelSession>>; grade(request: GradeRequest): Promise<Gated<GradeOutcome>>; artifact(ref: string): Promise<ArtifactResult>; resumeOperation(ref: OperationRef): Promise<OperationResumption>; }