import { type ChannelCors } from "#channel/cors.js"; import type { UserContent } from "ai"; import type { ChannelReceiveContext } from "#channel/channel-operations.js"; import type { RouteDefinition } from "#channel/routes.js"; import type { Session, SessionHandle } from "#channel/session.js"; import type { DeliverPayload, SessionAuthContext, TurnPolicy } from "#channel/types.js"; import type { StepInput } from "#harness/types.js"; import type { UploadPolicyInput } from "#public/channels/upload-policy.js"; import type { AudienceContext } from "#shared/conversation-context.js"; import type { ChannelAudience } from "#shared/channel-audience.js"; /** * 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; } /** Runtime context supplied while resolving a channel-owned file URL. */ export interface FetchFileContext { readonly state: Readonly>; /** The session the attachment arrived in, for checking it against the session's caller. */ readonly session: SessionHandle; } export type FetchFileFunction = (url: string, context?: FetchFileContext) => Promise; /** * Input passed to a channel's `receive` callback when another channel or * schedule proactively routes a message to it. */ export interface GenericReceiveInput> { readonly message: string | UserContent; readonly target: Readonly; readonly auth: SessionAuthContext | null; } /** * The object passed to {@link defineChannel}. `routes` is required; `state` * seeds durable adapter state, `context` builds the per-step `channel` argument * for `events` and `deliver`, `events` handle session lifecycle, `receive` * accepts cross-channel handoffs, `fetchFile` stages remote file URLs, * `audience` classifies conversation visibility, and `metadata` projects * custom observability data. * * Generics: `TState` (adapter state), `TCtx` (context factory return type), * `TReceiveTarget` (cross-channel target shape), `TMetadata` (instrumentation * projection). */ export interface GenericChannelDefinition, TMetadata extends Record = Record> { /** Policy used by message sends that do not provide an explicit override. */ readonly turnPolicy?: TurnPolicy; deliver?(payload: DeliverPayload, ctx: TCtx): StepInput | void | Promise; readonly state?: TState; /** * CORS policy for this channel's HTTP routes. `true` enables H3/Nitro's * permissive defaults (`origin`, methods, request headers, and exposed * headers all `"*"`); `false` or omission leaves CORS untouched. Pass an * object for a serializable subset of H3/Nitro CORS options. */ readonly cors?: ChannelCors; /** * Builds the per-step channel context handed to `events` and `deliver`. * Receives the live {@link SessionHandle}, so a factory can close over it to * register late-bound callbacks. eve writes state mutations made inside the * returned context back through `adapter.state`. * * Return the channel-owned context (thread handles, API clients, etc.). The * framework passes it as the `channel` argument to event handlers (with * {@link ChannelContinuationOps} injected) and passes {@link SessionContext} * as a separate `ctx` argument. */ context?(state: NonNullable, session: SessionHandle): TCtx; readonly routes: readonly RouteDefinition[]; receive?(input: GenericReceiveInput, ctx: ChannelReceiveContext): Promise; readonly events?: TEvents; /** * Fetches bytes for a link encountered on a `FilePart.data` by the staging * pipeline. Return bytes / {@link FetchFileResult} to stage the file to the * sandbox, or `null` when the link is not this channel's: eve then downloads * a public `https:` link itself, and any other link reaches the model as a * note. A provider never fetches an attachment. */ readonly fetchFile?: FetchFileFunction; /** * Size cap and media types for every file eve stages for this channel, * checked against the final bytes and the media type they prove, including * links eve downloads itself. A file that breaks the policy reaches the * model as a note. Omit to check nothing beyond your own `fetchFile`; eve's * own downloads then stop at the 25 MB framework default. */ readonly uploadPolicy?: UploadPolicyInput; /** * Channel-owned metadata exposed to instrumentation callbacks. This is the * channel's public observability surface, not a dump of durable adapter state, * so keep it small. Return an object of JSON primitives, arrays, and plain * objects: eve omits `undefined` properties and drops projections containing * values such as `Date` or `Map`. */ readonly metadata?: (state: NonNullable) => TMetadata; /** * Classifies who can observe the originating conversation. The hook runs * after route auth with channel state, the authenticated principal, and * deployment environment. Return `"unknown"` when classification is not * confident; consumers treat it as non-public. */ readonly audience?: (input: AudienceContext) => ChannelAudience; /** * Identifier of the adapter family this channel belongs to. Set by * higher-level wrappers (e.g. `slackChannel` passes `"slack"`) so downstream * consumers can render typed channel chips instead of bucketing everything * under "unknown". * * Authors calling `defineChannel` directly do not need to set this; the * framework defaults to `"http"` for stateless channels and `"defineChannel"` * for stateful ones. */ readonly kindHint?: string; }