eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
516 lines (515 loc) • 26 kB
TypeScript
import type * as memory from "#instrumentation/memory.js";
import type { InstrumentationStateSlot } from "#instrumentation/state.js";
import type { RuntimeTraceContext } from "#protocol/message.js";
import type { ChannelAudience } from "#shared/channel-audience.js";
import type { InstrumentationDecision } from "#shared/instrumentation-decision.js";
import type { TraceCaptureContext, TraceCapturePolicy } from "#shared/trace-policy.js";
/**
* Stable eve identity for one model attempt. Retries share `stepIndex` and
* differ by `attemptIndex`, so `step.attempt.*` fires once per attempt while
* protocol `step.*` events and the resolver hook fire once per step.
*/
export interface InstrumentationAttemptScope {
readonly channelAudience?: ChannelAudience;
readonly attemptId: string;
readonly attemptIndex: number;
readonly functionId?: string;
readonly rootSessionId?: string;
readonly sessionId: string;
readonly stepIndex: number;
readonly turnId: string;
}
/** The model SDK operation an attempt runs through. */
export interface InstrumentationOperationRef {
readonly modelId: string;
readonly operationId: string;
readonly provider: string;
}
export interface InstrumentationModelRef {
readonly modelId: string;
readonly provider: string;
}
/** Token usage for one model call. A field is absent when the provider omits it. */
export interface InstrumentationUsage {
readonly inputTokenDetails?: {
readonly cacheReadTokens?: number;
readonly cacheWriteTokens?: number;
};
readonly inputTokens?: number;
readonly outputTokens?: number;
}
/** Final model input for one call. Message shape stays opaque to this layer. */
export interface InstrumentationModelInput {
readonly instructions?: unknown;
readonly messages: readonly unknown[];
}
/**
* The model response parts eve records. A kind outside this union is dropped
* when the bridge maps a response, so widening the union is what makes a new
* kind reachable by a provider.
*/
export type InstrumentationContentPart = {
readonly type: "text";
readonly text: string;
} | {
readonly type: "reasoning";
readonly text: string;
} | {
readonly type: "tool-call";
readonly callId: string;
readonly input: unknown;
readonly toolName: string;
} | {
readonly type: "tool-result";
readonly callId: string;
readonly input: unknown;
readonly output: unknown;
readonly toolName: string;
} | {
readonly type: "tool-error";
readonly callId: string;
readonly error: unknown;
readonly input: unknown;
readonly toolName: string;
};
/**
* What eve dispatched an action as. The model sees every action as a tool, so
* this is the only thing that separates a subagent or remote-agent call from an
* ordinary tool in a trace.
*/
export type InstrumentationActionKind = "load-skill" | "remote-agent-call" | "subagent-call" | "tool-call";
/**
* How one action ended.
*
* `type` survives a provider that declined content, so whether the tool errored
* is answerable without seeing what it returned.
*/
export type InstrumentationActionOutput = {
readonly type: "result";
readonly output?: unknown;
} | {
readonly type: "error";
readonly error?: unknown;
};
/**
* Every event carries an `idempotencyKey` naming the operation it is about: a
* start and its terminal share one, and two operations never collide.
*
* Every part is identity eve reconstructs on replay — session and turn ids,
* `scope.attemptId` (itself `session:turn:step:attempt`), AI SDK step number,
* and durable runtime-action call ids. A provider writing rows can use the key
* as its row id and be idempotent by construction.
*/
export declare function sessionIdempotencyKey(sessionId: string): string;
export declare function turnIdempotencyKey(sessionId: string, turnId: string): string;
export declare function attemptIdempotencyKey(scope: InstrumentationAttemptScope): string;
/** One model call occurs per AI SDK step within an eve attempt. */
export declare function modelCallIdempotencyKey(scope: InstrumentationAttemptScope, stepNumber: number): string;
export declare function toolCallIdempotencyKey(scope: InstrumentationAttemptScope, callId: string, stepNumber: number): string;
export declare function inputIdempotencyKey(sessionId: string, turnId: string, requestId: string): string;
export declare function channelDeliveryIdempotencyKey(sessionId: string, deliveryId: string): string;
export interface InstrumentationChannelDeliveryRef {
readonly channelAudience?: ChannelAudience;
readonly channelKind: string;
readonly channelName: string;
readonly deliveryId: string;
readonly requestId?: string;
readonly requestTraceContext?: InstrumentationTraceContext;
}
/** Known framework delivery input; adapter-specific fields are never projected. */
export interface InstrumentationChannelDeliveryInput {
readonly context?: readonly string[];
readonly inputResponses?: readonly InstrumentationInputResponse[];
readonly message?: unknown;
readonly outputSchema?: unknown;
}
interface InstrumentationChannelDeliveryScope {
readonly agentName?: string;
readonly delivery: InstrumentationChannelDeliveryRef;
readonly idempotencyKey: string;
readonly rootSessionId: string;
readonly sequence?: number;
readonly sessionId: string;
readonly traceSeed?: InstrumentationTraceSeed;
readonly turnId?: string;
}
export interface InstrumentationChannelDeliveryStartedEvent extends InstrumentationChannelDeliveryScope {
readonly parentTraceContext?: InstrumentationTraceContext;
readonly type: "channel.delivery.started";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly input?: InstrumentationChannelDeliveryInput;
}
export type InstrumentationChannelDeliveryOutcome = "cancelled" | "completed" | "failed";
export interface InstrumentationChannelDeliveryTerminalEvent extends InstrumentationChannelDeliveryScope {
readonly type: "channel.delivery.cancelled" | "channel.delivery.completed" | "channel.delivery.failed";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly error?: unknown;
readonly errorCode?: string;
readonly outcome: InstrumentationChannelDeliveryOutcome;
}
/** Framework-owned reason an agent suspended for user input. */
export type InstrumentationInputKind = "question" | "session-limit" | "tool-approval";
export interface InstrumentationInputOption {
readonly description?: string;
readonly id: string;
readonly label: string;
readonly style?: "danger" | "default" | "primary";
}
/** User-facing input request content projected without runtime-owned types. */
export interface InstrumentationInputRequest {
readonly allowFreeform?: boolean;
readonly display?: "confirmation" | "select" | "text";
readonly options?: readonly InstrumentationInputOption[];
readonly prompt: string;
}
/** User response content projected without runtime-owned types. */
export interface InstrumentationInputResponse {
readonly optionId?: string;
readonly text?: string;
}
export type InstrumentationInputOutcome = "answered" | "approved" | "cancelled" | "denied" | "failed" | "ignored" | "invalid";
export interface InstrumentationInputRequestedEvent {
readonly type: "input.requested";
readonly action: {
readonly callId: string;
readonly name: string;
};
readonly idempotencyKey: string;
readonly kind: InstrumentationInputKind;
/** Content. Absent unless this provider's trace policy records this direction. */
readonly request?: InstrumentationInputRequest;
readonly requestId: string;
readonly scope: InstrumentationAttemptScope;
}
export interface InstrumentationInputResolvedEvent {
readonly type: "input.resolved";
readonly error?: unknown;
readonly idempotencyKey: string;
readonly kind: InstrumentationInputKind;
readonly outcome: InstrumentationInputOutcome;
readonly requestId: string;
/** Content. Absent unless this provider's trace policy records this direction. */
readonly response?: InstrumentationInputResponse;
readonly scope: InstrumentationAttemptScope;
}
/** Runtime action call IDs are durable and unique within one session. */
export declare function actionIdempotencyKey(sessionId: string, turnId: string, callId: string): string;
export interface InstrumentationStepAttemptStartedEvent {
readonly type: "step.attempt.started";
readonly idempotencyKey: string;
readonly operation: InstrumentationOperationRef;
readonly runtimeContext?: Readonly<Record<string, unknown>>;
readonly scope: InstrumentationAttemptScope;
}
export interface InstrumentationSessionStartedEvent {
readonly type: "session.started";
readonly agentName?: string;
readonly channelKind?: string;
readonly channelType?: string;
readonly channelAudience?: ChannelAudience;
readonly idempotencyKey: string;
readonly parentLineage?: InstrumentationParentLineage;
readonly parentTraceContext?: InstrumentationTraceContext;
readonly rootSessionId: string;
readonly scheduleId?: string;
readonly sessionId: string;
readonly title?: string;
readonly traceSeed?: InstrumentationTraceSeed;
}
export type InstrumentationTraceContext = RuntimeTraceContext;
export interface InstrumentationTraceSeed extends InstrumentationTraceContext {
readonly decision?: InstrumentationDecision;
}
export declare const INSTRUMENTATION_PRINCIPAL_TYPES: readonly ["anonymous", "app", "local-dev", "none", "other", "runtime", "service", "unknown", "user"];
export type InstrumentationPrincipalType = (typeof INSTRUMENTATION_PRINCIPAL_TYPES)[number];
export declare function isInstrumentationPrincipalType(value: unknown): value is InstrumentationPrincipalType;
export interface InstrumentationPrincipalSummary {
readonly id?: string;
readonly type: InstrumentationPrincipalType;
}
/**
* Which tool call dispatched a subagent child. The trace structure alone
* cannot say: one turn's children all parent to the same window.
*/
export interface InstrumentationParentLineage {
readonly callId: string;
readonly sessionId: string;
readonly subagentName?: string;
readonly turnId: string;
}
/**
* A session transition that carries no failure.
*
* `session.waiting` sits here rather than with the failed shape because it is
* not terminal: the session suspends awaiting input or approval and may resume
* with a new turn.
*/
export interface InstrumentationSessionSettledEvent {
readonly type: "session.completed" | "session.waiting";
readonly idempotencyKey: string;
readonly sessionId: string;
readonly turnId?: string;
}
export interface InstrumentationSessionFailedEvent {
readonly type: "session.failed";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly error?: unknown;
readonly idempotencyKey: string;
readonly sessionId: string;
readonly turnId?: string;
}
export type InstrumentationSessionTransitionEvent = InstrumentationSessionSettledEvent | InstrumentationSessionFailedEvent;
export interface InstrumentationTurnStartedEvent {
readonly type: "turn.started";
readonly agentName?: string;
readonly currentPrincipal?: InstrumentationPrincipalSummary;
readonly idempotencyKey: string;
readonly initiatorPrincipal?: InstrumentationPrincipalSummary;
readonly parentLineage?: InstrumentationParentLineage;
readonly parentTraceContext?: InstrumentationTraceContext;
readonly rootSessionId: string;
readonly sequence: number;
readonly sessionId: string;
readonly turnId: string;
}
/**
* A turn that ended without a failure.
*
* `turn.cancelled` sits here rather than with the failed shape because
* cancellation is not an error: the harness settles a cancelled turn as
* `turn.cancelled` → `session.waiting`, with no failure surfaced anywhere.
*/
export interface InstrumentationTurnSettledEvent {
readonly type: "turn.cancelled" | "turn.completed";
readonly idempotencyKey: string;
readonly sessionId: string;
readonly turnId: string;
}
export interface InstrumentationTurnFailedEvent {
readonly type: "turn.failed";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly error?: unknown;
readonly idempotencyKey: string;
readonly sessionId: string;
readonly turnId: string;
}
export type InstrumentationTurnTerminalEvent = InstrumentationTurnSettledEvent | InstrumentationTurnFailedEvent;
export interface InstrumentationStepAttemptCompletedEvent {
readonly type: "step.attempt.completed";
readonly idempotencyKey: string;
readonly scope: InstrumentationAttemptScope;
}
export interface InstrumentationStepAttemptFailedEvent {
readonly type: "step.attempt.failed";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly error?: unknown;
readonly idempotencyKey: string;
readonly scope: InstrumentationAttemptScope;
}
export type InstrumentationStepAttemptTerminalEvent = InstrumentationStepAttemptCompletedEvent | InstrumentationStepAttemptFailedEvent;
/**
* Provider metadata for one completed attempt, as reported by the AI SDK
* (`StepResult.providerMetadata`). Carries Vercel AI Gateway cost data when
* the request went through the gateway; absent for other providers.
*/
export interface InstrumentationStepAttemptMetadataEvent {
readonly type: "step.attempt.metadata";
readonly idempotencyKey: string;
readonly scope: InstrumentationAttemptScope;
readonly providerMetadata: Readonly<Record<string, unknown>>;
}
export interface InstrumentationModelCallStartedEvent {
readonly type: "model.call.started";
readonly idempotencyKey: string;
/** Content. Absent unless this provider's trace policy records this direction. */
readonly input?: InstrumentationModelInput;
readonly model: InstrumentationModelRef;
readonly runtimeContext?: Readonly<Record<string, unknown>>;
readonly scope: InstrumentationAttemptScope;
}
export interface InstrumentationModelCallCompletedEvent {
readonly type: "model.call.completed";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly content?: readonly InstrumentationContentPart[];
readonly finishReason: string;
readonly idempotencyKey: string;
readonly responseModelId?: string;
readonly responseId?: string;
readonly scope: InstrumentationAttemptScope;
readonly usage: InstrumentationUsage;
}
export interface InstrumentationModelCallFailedEvent {
readonly type: "model.call.failed";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly error?: unknown;
readonly idempotencyKey: string;
readonly scope: InstrumentationAttemptScope;
}
export type InstrumentationModelCallTerminalEvent = InstrumentationModelCallCompletedEvent | InstrumentationModelCallFailedEvent;
export type InstrumentationToolOutput = InstrumentationActionOutput;
export interface InstrumentationToolCallStartedEvent {
readonly type: "tool.call.started";
readonly callId: string;
readonly idempotencyKey: string;
readonly input: unknown;
readonly scope: InstrumentationAttemptScope;
readonly toolName: string;
}
export interface InstrumentationToolCallCompletedEvent {
readonly type: "tool.call.completed";
readonly idempotencyKey: string;
readonly output: InstrumentationToolOutput;
readonly scope: InstrumentationAttemptScope;
}
export interface InstrumentationToolCallFailedEvent {
readonly type: "tool.call.failed";
/** Content. Absent unless this provider's trace policy records this direction. */
readonly error?: unknown;
readonly idempotencyKey: string;
readonly scope: InstrumentationAttemptScope;
}
export type InstrumentationToolCallTerminalEvent = InstrumentationToolCallCompletedEvent | InstrumentationToolCallFailedEvent;
/**
* One thing the agent did on the model's behalf. `kind` is what separates a
* subagent or remote-agent call from an ordinary tool; `name` is the name the
* model called, which is the tool name for every kind.
*/
export interface InstrumentationActionStartedEvent {
readonly type: "action.started";
readonly callId: string;
readonly idempotencyKey: string;
/** Content. Absent unless this provider's trace policy records this direction. */
readonly input?: unknown;
/** Whether this action owns a durable workflow body. */
readonly isWorkflowTool?: boolean;
readonly kind: InstrumentationActionKind;
readonly name: string;
readonly scope: InstrumentationAttemptScope;
}
export type InstrumentationActionOutcome = "abandoned" | "cancelled" | "completed" | "failed" | "rejected";
export interface InstrumentationActionCompletedEvent {
readonly type: "action.completed";
readonly acceptedAtMs?: number;
readonly idempotencyKey: string;
readonly outcome: "completed";
readonly output: InstrumentationActionOutput;
readonly scope: InstrumentationAttemptScope;
readonly usage?: InstrumentationUsage;
}
export interface InstrumentationActionFailedEvent {
readonly type: "action.failed";
readonly acceptedAtMs?: number;
/** Content. Absent unless this provider's trace policy records this direction. */
readonly error?: unknown;
readonly errorCode?: string;
readonly idempotencyKey: string;
readonly outcome: Exclude<InstrumentationActionOutcome, "completed">;
readonly scope: InstrumentationAttemptScope;
}
export type InstrumentationActionTerminalEvent = InstrumentationActionCompletedEvent | InstrumentationActionFailedEvent;
/** The second argument to every handler. */
export interface InstrumentationHandlerContext {
/** Durable state scoped to this provider and this operation. */
readonly state: InstrumentationStateSlot;
}
/**
* The AI SDK can omit a model terminal when an incomplete stream closes. A
* handler can use `ctx.state` for durable correlation when a terminal arrives,
* but providers must scope live resources to the attempt and release anything
* still open when the step attempt terminates.
*/
export type InstrumentationEventHandler<TEvent> = (event: TEvent, ctx: InstrumentationHandlerContext) => void | PromiseLike<void>;
/** Internal normalized provider shape consumed by the instrumentation bus. */
export interface InstrumentationProviderDefinition {
readonly name: string;
/** Durable state identity, separate from the human-readable log name. */
readonly stateNamespace?: string;
/** Internal provider-specific projection applied after capture filtering. */
readonly projectEvent?: (event: InstrumentationEvent) => InstrumentationEvent | PromiseLike<InstrumentationEvent>;
/** Provider-specific event admission and directional content policy. */
readonly tracePolicy?: TraceCapturePolicy;
readonly events?: {
readonly "channel.delivery.started"?: InstrumentationEventHandler<InstrumentationChannelDeliveryStartedEvent>;
readonly "channel.delivery.cancelled"?: InstrumentationEventHandler<InstrumentationChannelDeliveryTerminalEvent>;
readonly "channel.delivery.completed"?: InstrumentationEventHandler<InstrumentationChannelDeliveryTerminalEvent>;
readonly "channel.delivery.failed"?: InstrumentationEventHandler<InstrumentationChannelDeliveryTerminalEvent>;
readonly "step.attempt.started"?: InstrumentationEventHandler<InstrumentationStepAttemptStartedEvent>;
readonly "step.attempt.completed"?: InstrumentationEventHandler<InstrumentationStepAttemptCompletedEvent>;
readonly "step.attempt.failed"?: InstrumentationEventHandler<InstrumentationStepAttemptFailedEvent>;
readonly "step.attempt.metadata"?: InstrumentationEventHandler<InstrumentationStepAttemptMetadataEvent>;
readonly "model.call.started"?: InstrumentationEventHandler<InstrumentationModelCallStartedEvent>;
readonly "model.call.completed"?: InstrumentationEventHandler<InstrumentationModelCallCompletedEvent>;
readonly "model.call.failed"?: InstrumentationEventHandler<InstrumentationModelCallFailedEvent>;
readonly "memory.operation.started"?: InstrumentationEventHandler<memory.InstrumentationMemoryOperationStartedEvent>;
readonly "memory.operation.completed"?: InstrumentationEventHandler<memory.InstrumentationMemoryOperationCompletedEvent>;
readonly "memory.operation.failed"?: InstrumentationEventHandler<memory.InstrumentationMemoryOperationFailedEvent>;
readonly "input.requested"?: InstrumentationEventHandler<InstrumentationInputRequestedEvent>;
readonly "input.resolved"?: InstrumentationEventHandler<InstrumentationInputResolvedEvent>;
readonly "session.completed"?: InstrumentationEventHandler<InstrumentationSessionSettledEvent>;
readonly "session.failed"?: InstrumentationEventHandler<InstrumentationSessionFailedEvent>;
readonly "session.started"?: InstrumentationEventHandler<InstrumentationSessionStartedEvent>;
readonly "session.waiting"?: InstrumentationEventHandler<InstrumentationSessionSettledEvent>;
readonly "action.started"?: InstrumentationEventHandler<InstrumentationActionStartedEvent>;
readonly "action.completed"?: InstrumentationEventHandler<InstrumentationActionCompletedEvent>;
readonly "action.failed"?: InstrumentationEventHandler<InstrumentationActionFailedEvent>;
readonly "tool.call.started"?: InstrumentationEventHandler<InstrumentationToolCallStartedEvent>;
readonly "tool.call.completed"?: InstrumentationEventHandler<InstrumentationToolCallCompletedEvent>;
readonly "tool.call.failed"?: InstrumentationEventHandler<InstrumentationToolCallFailedEvent>;
readonly "turn.cancelled"?: InstrumentationEventHandler<InstrumentationTurnSettledEvent>;
readonly "turn.completed"?: InstrumentationEventHandler<InstrumentationTurnSettledEvent>;
readonly "turn.failed"?: InstrumentationEventHandler<InstrumentationTurnFailedEvent>;
readonly "turn.started"?: InstrumentationEventHandler<InstrumentationTurnStartedEvent>;
};
/** Drains anything buffered. Driven by the runtime, not by the bus. */
readonly flush?: () => void | PromiseLike<void>;
/** Releases resources when the process is going away. */
readonly shutdown?: () => void | PromiseLike<void>;
}
type InstrumentationProviderInput = Omit<InstrumentationProviderDefinition, "name"> & {
readonly name?: string;
};
export interface InstrumentationDispatchGroups {
readonly serialBefore?: readonly InstrumentationProviderDefinition[];
readonly parallel?: readonly InstrumentationProviderDefinition[];
readonly serialAfter?: readonly InstrumentationProviderDefinition[];
}
export type InstrumentationHooksInput = readonly InstrumentationProviderInput[] | InstrumentationDispatchGroups;
/** Events that pair a start with its terminal under one `idempotencyKey`. */
export type InstrumentationCorrelatedEvent = InstrumentationChannelDeliveryStartedEvent | InstrumentationChannelDeliveryTerminalEvent | InstrumentationInputRequestedEvent | InstrumentationInputResolvedEvent | InstrumentationActionStartedEvent | InstrumentationActionTerminalEvent | memory.InstrumentationMemoryOperationStartedEvent | memory.InstrumentationMemoryOperationTerminalEvent | InstrumentationModelCallStartedEvent | InstrumentationModelCallTerminalEvent | InstrumentationToolCallStartedEvent | InstrumentationToolCallTerminalEvent;
export type InstrumentationPointEvent = InstrumentationStepAttemptStartedEvent | InstrumentationStepAttemptMetadataEvent | InstrumentationStepAttemptTerminalEvent | InstrumentationSessionStartedEvent | InstrumentationSessionTransitionEvent | InstrumentationTurnStartedEvent | InstrumentationTurnTerminalEvent;
export type InstrumentationEvent = InstrumentationCorrelatedEvent | InstrumentationPointEvent;
/** Trusted framework operation for activating context around AI SDK execution. */
export type InstrumentationContextRunner = <T>(operation: InstrumentationExecutionOperation, execute: () => PromiseLike<T>) => PromiseLike<T>;
/** Stable identity supplied only to a trusted framework context runner. */
export type InstrumentationExecutionOperation = {
readonly idempotencyKey: string;
readonly scope: InstrumentationAttemptScope;
readonly type: "tool.call";
} | {
readonly idempotencyKey: string;
readonly scope: InstrumentationAttemptScope;
readonly type: "model.call";
} | memory.InstrumentationMemoryExecutionOperation;
/** Provider-neutral hook operations consumed by the AI SDK bridge. */
export interface InstrumentationHooks {
/**
* Whether any provider admitted by this bound trace requests content.
*
* False means nothing downstream can read what was said, so the publisher
* should not serialize it in the first place. This is the only way the
* projection is skipped rather than merely withheld.
*/
readonly capturesContent: boolean;
/** Input-content demand for publishers that can project directions separately. */
readonly capturesInputs?: boolean;
/** Output-content demand for publishers that can project directions separately. */
readonly capturesOutputs?: boolean;
readonly forTrace?: (trace: TraceCaptureContext) => InstrumentationHooks;
publish(event: InstrumentationEvent): Promise<void>;
}
export interface CreateInstrumentationHooksOptions {
readonly handlerTimeoutMs?: number;
}
/** Creates failure-isolated hooks backed by normalized dispatch groups. */
export declare function createInstrumentationHooks(input: InstrumentationHooksInput, options?: CreateInstrumentationHooksOptions): InstrumentationHooks;
export {};