/** * L1 — the adapter contract. * * One interface every provider implements. `stream()` returns an * ASYNC ITERABLE of events — never a settled promise of a list, and never a * callback emitter. An async iterable is incremental (the consumer sees each * event as it happens), interruptible (AbortSignal), and replayable (the * events carry `seq`). The three properties that let a ReAct loop stream * without buffering and without losing its place. * * WHY not a `Promise`: a buffered adapter is what made the * reference implementation's streaming a second-class feature bolted onto * a batch loop. The contract shape IS the architecture — see ADR-0001. * * Adapters translate provider wire events INTO `Event`. They never see tool * handlers (only `ToolSpec` projections) — the kernel is the only thing that * runs tools. Provider-private fields (thinking blocks, cache_control, * reasoning_content) are digested HERE and never leak into the union. */ import { type Event } from "./events.js"; import type { Message, ToolSpec } from "./messages.js"; /** * What the kernel accepts as a cancellation signal: a REAL AbortSignal * (the universal host type — every Node and browser host has one) or a * minimal structural stub for tests and exotic hosts. `AbortSignal` itself * is not structurally assignable to a hand-rolled interface (its listener * and options shapes are richer), so the union is the honest contract: * real signals pass without casts, stubs stay possible. */ export type AbortSignalLike = AbortSignal | AbortSignalStub; /** Minimal structural stand-in: `aborted` + `addEventListener` + `removeEventListener`. */ export interface AbortSignalStub { readonly aborted: boolean; addEventListener(type: string, listener: (this: AbortSignalStub, ev: unknown) => void, options?: { once?: boolean; }): void; removeEventListener(type: string, listener: (this: AbortSignalStub, ev: unknown) => void, options?: { once?: boolean; }): void; } export interface StreamOptions { readonly model: string; readonly messages: readonly Message[]; /** Provider-level system prompt. The kernel never composes prompts. */ readonly systemPrompt?: string; readonly tools?: readonly ToolSpec[]; readonly maxTokens?: number; readonly temperature?: number; /** XP-1: the RESOLVED reasoning setting — native wire values only (the * runtime's matrix resolves and refuses; adapters serialize per * dialect). Absent = provider defaults, byte-identical to pre-XP-1. */ readonly reasoning?: { readonly thinking?: "adaptive" | "enabled" | "disabled"; readonly effort?: string; }; readonly signal?: AbortSignalLike; } /** * The NARROW event set an adapter may produce (round 5). Everything else in the * union is kernel-owned — `terminal`, `tool_execution_*`, `permission_*`, * `user_input`, `compacted`, `summarized`, `uncertain_pending`, * `user_input_replaced`, `assistant_start`/`assistant_end` — and a * provider that yields any of those is FORGING kernel state. The type * narrows the adapter contract; the loop ALSO enforces it at runtime (JS * and third-party adapters are not trusted — see kernel/loop.ts). */ export type AdapterEvent = Extract; /** Runtime whitelist backing the narrowed type — the loop's trust gate. */ export declare const ADAPTER_EVENT_TYPES: ReadonlySet; /** * round 5(P1-8): the trust gate validates STRUCTURE, not just the type name. * A third-party adapter can emit a legal type with illegal fields (a stop * without a reason, a usage with known:true and no token, an array tool * input) — persisted, that would poison the next load. The gate reuses the * same per-variant validator the store relies on (isKisoEvent), so there * is exactly ONE set of rules: an invalid event is a forgery, never * appended, and the turn ends with an invalid_request terminal. */ export declare function isAdapterEvent(value: Event): value is AdapterEvent; export interface Adapter { stream(options: StreamOptions): AsyncIterable; }