import type { ModelMessage } from "ai"; import type { SessionAuth, SessionPredecessor } from "#context/keys.js"; import type { UnstampedMessageStreamEvent } from "#protocol/message.js"; import type { ConversationContext } from "#shared/conversation-context.js"; /** * Stream event types allowed for dynamic tool resolvers. Dispatch * supports any event; this extract restricts the public surface until * more events are validated. */ export type DynamicToolEventName = Extract; export declare const ALLOWED_DYNAMIC_TOOL_EVENTS: ReadonlySet; /** * Instructions and skills are restricted to session/turn boundaries. * Keeping their resolved context stable within a turn avoids changing the * model input between tool-loop steps. */ export declare const ALLOWED_DYNAMIC_INSTRUCTION_EVENTS: ReadonlySet; export declare const ALLOWED_DYNAMIC_SKILL_EVENTS: ReadonlySet; export declare const ALLOWED_DYNAMIC_CONNECTION_EVENTS: ReadonlySet; /** * Context passed to a dynamic resolver's event handler. * * Exposes read-only session identity, auth, and channel metadata. State * is not exposed here; resolvers read it through `defineState` handles or * the session context inside tool `execute` functions. */ export interface DynamicResolveContext { /** Active cancellation signal when resolving a dynamic model. */ readonly abortSignal?: AbortSignal; /** Effective model for this resolver, or `null` before dynamic model selection. */ readonly model: { readonly id: string; } | null; readonly session: { readonly id: string; readonly auth: SessionAuth; readonly schedule?: import("#context/session-schedule.js").SessionSchedule; /** * Present when eve started this session in place of a stranded session, * one that another eve version built. It names the earlier session, whose recorded stream * `sessions.attach(predecessor.sessionId)` from `eve/server` reads. */ readonly predecessor?: SessionPredecessor; }; /** Channel metadata for the request that triggered this resolve. */ readonly channel: { /** Channel type that produced the request (e.g. `"slack"`, `"http"`), when known. */ readonly kind?: string; /** Channel-owned resume handle for the conversation, when the channel supplies one. */ readonly continuationToken?: string; /** Free-form channel-specific metadata attached to the request. */ readonly metadata?: Readonly>; }; /** * Immutable classification and execution context for the active conversation. * Absent on sessions persisted before this context key existed. */ readonly conversation?: ConversationContext; /** Conversation history visible at this resolve point, oldest first. */ readonly messages: readonly ModelMessage[]; } /** * Base event handler map accepted by `defineDynamic`. Intentionally * wide so it accepts both tool-returning and skill-returning handlers: * the slot directory (tools/ vs skills/) determines the required return, * validated at runtime by the respective resolver. */ export type DynamicEvents = { readonly [K in DynamicToolEventName]?: (event: unknown, ctx: DynamicResolveContext) => TResult | Promise; }; type DynamicEventMapHandler = Extract, (...args: never[]) => unknown>; type DynamicEventMapResult = Awaited>>; /** * Marker discriminator for a `defineDynamic({ events })` export. */ export declare const DYNAMIC_SENTINEL_KIND: "eve:dynamic"; /** * Return value of `defineDynamic`: the runtime shape of a dynamic export, * stamped with a sentinel kind the compiler/normalizer detects. */ export type DynamicSentinel = { readonly kind: typeof DYNAMIC_SENTINEL_KIND; readonly events: DynamicEvents; }; /** * Defines a dynamic resolver evaluated at runtime from stream-event * handlers. It is shared across tools, skills, connections, and agent definitions; * the directory it is authored in (not this function) decides what each * handler must return and which events are honored. The file's path-derived * slug names the single-entry case; a `Record` return names each * entry by its bare key, prefixed with the mount namespace for an extension's * resolver. Return `null` to contribute nothing for that event. * * Per-slot return shape: * - `agent/tools/`: return a single `defineTool(...)`, a * `Record`, or `null`. * - `agent/skills/`: return a single `defineSkill(...)`, a * `Record`, or `null`. * - `agent/connections/`: return one connection definition, a * `Record`, or `null`. * - `agent/subagents//agent.ts`: return `defineAgent(...)` to configure * and expose the subagent, or `null` to omit it. * * Per-slot events: tools resolvers run at `session.started`, * `turn.started`, and `step.started`. Skills and connection resolvers run only * at `session.started` and `turn.started`; the runtime never invokes a * handler keyed on `step.started` in that slot. Dynamic subagents run at * `session.started` and `turn.started` only. * * ```ts * import { defineDynamic, defineTool } from "eve/tools"; * import { z } from "zod"; * * export default defineDynamic({ * events: { * "session.started": async (event, ctx) => ({ * export: defineTool({ * description: "Export data", * inputSchema: z.object({ format: z.string() }), * async execute(input) { * return doExport(input.format); * }, * }), * }), * }, * }); * ``` * * A single return is named after the file slug. A map names each entry by its * bare key — there is no automatic slug prefix, so namespace keys yourself * (e.g. `team__playbook`) when a bare name might collide. Tool names must match * the tool filename charset, and a name under a connection's `__` prefix * is rejected. A dynamic tool/skill whose name matches an authored one * overrides it; two dynamic resolvers emitting the same name is an error. */ export declare function defineDynamic(definition: { readonly events: TEvents; }): DynamicSentinel>; export declare function assertResolverOnlyDynamicSentinel(sentinel: DynamicSentinel, message: string): void; export declare function isDynamicSentinel(value: unknown): value is DynamicSentinel; export {};