eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
161 lines (160 loc) • 7.11 kB
TypeScript
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<TState = Record<string, unknown>> {
/**
* 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> = TCtx extends ChannelAdapterContext<infer S> ? S : Record<string, unknown>;
/**
* 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<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessageStreamEvent, {
type: T;
}> 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<T extends HandleMessageStreamEvent["type"], TCtx extends ChannelAdapterContext<any> = ChannelAdapterContext> = (data: EventData<T>, ctx: TCtx) => void | Promise<void>;
/**
* 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<TCtx extends ChannelAdapterContext<any> = ChannelAdapterContext> = {
[K in HandleMessageStreamEvent["type"]]?: EventHandler<K, TCtx>;
};
/**
* 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<Record<string, unknown>>;
export type ChannelInstrumentationMetadataProjector = (state: Record<string, unknown> | undefined) => ChannelInstrumentationMetadata;
/**
* Plain-object channel adapter with durable state, an optional inbound
* delivery hook, event handlers, and optional attachment resolution.
*/
export type ChannelAdapter<TCtx extends ChannelAdapterContext<any> = 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:<name>` 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<string, unknown>;
/**
* 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<StepInput | void>;
/**
* Optional factory that builds the adapter context for this adapter.
*
* @internal
*/
createAdapterContext?(base: ChannelAdapterContext<StateOf<TCtx>>): 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<Buffer | FetchFileResult | null>;
/**
* 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<TCtx>;
/**
* 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<HandleMessageStreamEvent>;
export {};