import type { ContextAccessor } from "#context/key.js"; import type { StepInput } from "#harness/types.js"; import type { HandleMessageStreamEvent } from "#protocol/message.js"; import type { SessionHandle } from "#channel/session.js"; import type { DeliverPayload } from "#channel/types.js"; /** * Context available to every adapter handler (`deliver` and event handlers). * * `state` is a mutable reference to the adapter's durable state. The runtime * auto-snapshots it at step boundaries — no manual `serialize()` needed. * * `ctx` provides read/write access to durable context keys (the same * {@link ContextAccessor} that tools and providers use). * * `session` is a live handle to the current session — id, auth, * continuation token, plus an imperative {@link SessionHandle.setContinuationToken} * for channels that need to re-key the session mid-turn (e.g. Slack's * auto-anchor on first post). */ export interface ChannelAdapterContext> { /** * Mutable adapter state. Auto-serialized at step boundaries via * JSON snapshot. The adapter reads and writes this directly. */ state: TState; /** * Read/write access to durable context keys. Seed keys here for tools * to read during the turn. */ readonly ctx: ContextAccessor; /** * Live handle to the current session. */ readonly session: SessionHandle; } /** * Extracts the state type from a {@link ChannelAdapterContext} generic. * Used internally so adapter-level types can declare their state shape * once (via `TCtx`) and have helpers narrow consistently. */ type StateOf = TCtx extends ChannelAdapterContext ? S : Record; /** * Extracts the `data` field type from a stream event by its `type` discriminant. * Events that carry no `data` field (e.g. `session.completed`) resolve to `undefined`. */ type EventData = Extract extends { data: infer D; } ? D : undefined; /** * A single outbound event handler. Receives the event's `data` (not the full * envelope) and the adapter context. Void return — side effects only. */ type EventHandler = ChannelAdapterContext> = (data: EventData, ctx: TCtx) => void | Promise; /** * Map of outbound event handlers keyed by event type. The adapter declares * handlers for the event types it cares about. For undeclared types, the * framework's default handlers run. */ export type ChannelEventHandlers = ChannelAdapterContext> = { [K in HandleMessageStreamEvent["type"]]?: EventHandler; }; /** * Enriched return shape from a channel's {@link ChannelAdapter.fetchFile} * function. Return a bare {@link Buffer} when only bytes are known, or * this record when the fetch discovers a more accurate `mediaType` or * `filename` (e.g. from an HTTP `Content-Type` header). * * When fields are provided, staging prefers them over the values the * channel populated at ingestion time. */ export interface FetchFileResult { readonly bytes: Buffer; readonly mediaType?: string; readonly filename?: string; } export type ChannelInstrumentationMetadata = Readonly>; export type ChannelInstrumentationMetadataProjector = (state: Record | undefined) => ChannelInstrumentationMetadata; /** * Plain-object channel adapter with durable state, an optional inbound * delivery hook, event handlers, and optional attachment resolution. */ export type ChannelAdapter = ChannelAdapterContext> = { /** * Stable durable identifier for serialization across step boundaries. * Must be unique across all adapters visible to one runtime bundle. * * Defaults to {@link HTTP_ADAPTER_KIND} (`"http"`) for the canonical * session channel and every behaviorless authored channel, or is derived * from the route file path as `channel:` once * `runtime/resolve-channel.ts` rewrites authored adapters that carry * behavior. */ readonly kind: string; /** * Initial state shape. Auto-serialized at step boundaries via JSON * snapshot. On rehydration, the runtime restores the serialized state * and re-attaches the adapter's behavior functions from the registry. */ readonly state?: Record; /** * Inbound hook: fires once per delivery before the harness starts. * * Return a {@link StepInput} to override the input the harness sees, or * return void to use the default payload projection. */ deliver?(payload: DeliverPayload, ctx: TCtx): StepInput | void | Promise; /** * Optional factory that builds the adapter context for this adapter. * * @internal */ createAdapterContext?(base: ChannelAdapterContext>): TCtx; /** * Fetches bytes for a URL encountered in `FilePart.data`. * * Called by the staging pipeline when it encounters a `URL` object * on a `FilePart`. Return `null` to let the URL pass through to the * model provider (e.g. public images). Return bytes or * {@link FetchFileResult} to stage the file to the sandbox. * * Credentials should be captured in the closure at channel * construction time. */ readonly fetchFile?: (url: string) => Promise; /** * Framework-owned observability projection for the active channel. * * Channel implementations decide which state fields are safe and * meaningful to expose to instrumentation callbacks. The harness reads * the resulting projection through a seed context key rather than * inspecting adapter state directly. */ readonly instrumentation?: { readonly metadata?: ChannelInstrumentationMetadataProjector; }; } & ChannelEventHandlers; /** * Produces the default {@link StepInput} when no custom adapter `deliver` * hook is defined. * * Passes through both `message` and `inputResponses` from the delivery * payload so tool-approval responses (and other HITL replies) reach the * harness even when the channel has no bespoke deliver logic. */ export declare function defaultDeliverResult(payload: DeliverPayload): StepInput | undefined; /** * Returns the durable kind for an adapter. */ export declare function getAdapterKind(adapter: ChannelAdapter): string; /** * Calls an adapter's event handler for a given event. Returns the event * unchanged (adapters don't transform events — they perform side effects). * * Throwing handlers are logged and swallowed so a downstream delivery * failure does not corrupt the event stream write path. */ export declare function callAdapterEventHandler(adapter: ChannelAdapter, event: HandleMessageStreamEvent, ctx: ChannelAdapterContext): Promise; export {};