import type { OperationLifecycleLike } from '../types/lifecycle.js'; import type { Principal } from '../types/principal.js'; import { GraphError } from './errors.js'; import type { GraphCheckpoint, GraphCheckpointer, GraphDescription, GraphEvent, GraphStatus, InterruptRequest, PendingInterrupt, RetryPolicy, RunControlLike } from '../types/graph.js'; import type { Store } from '../types/store.js'; /** What a workflow checkpoint holds: the input, every completed step's result, and the output. */ export interface WorkflowState { /** The input the workflow was started with. */ input: I; /** * Each completed step's result, keyed by step name — `name#2` for the second call with a name. A * step whose `onError` supplied its result after its retries ran out also records `recovered`. */ steps: Record; /** The workflow's return value, once it has completed. */ output?: O; } /** What a step's function receives. */ export interface StepContext { /** The step's name. */ readonly name: string; /** The key its result is recorded under: the name, or `name#n` for a repeated name. */ readonly key: string; /** Attempt number, starting at 1. Above 1 only under a retry policy. */ readonly attempt: number; /** Aborted when the run is cancelled or the step exceeds its timeout. */ readonly signal: AbortSignal; /** * Shows the step is still making progress, which refreshes its idle timeout. For long work that * sends nothing else, such as a large upload or a slow tool call. */ heartbeat(): void; } /** Limits on one attempt of a step: its whole run, and the time it may go without progress. */ export interface StepTimeout { /** Fails the attempt when it runs longer than this, in milliseconds. */ runMs?: number; /** * Fails the attempt when this long passes without `context.heartbeat()`, in milliseconds, which * tells a hung step from a slow one that is working. */ idleMs?: number; } /** What a step's `onError` receives once its retries have run out. */ export interface StepFailure { /** The step's name. */ name: string; /** The key its result is recorded under. */ key: string; /** Attempts made. */ attempts: number; /** The last attempt's error. */ error: unknown; } /** How a step's result was recovered, as recorded in the workflow state. */ export interface StepRecovery { /** The error that exhausted the step's retries. */ error: { name: string; message: string; }; /** Attempts made before `onError` decided. */ attempts: number; } /** How one step runs. */ export interface StepOptions { /** Retries a failing attempt, overriding the workflow's default policy. */ retry?: RetryPolicy; /** Aborts the step's signal and fails the attempt when it runs longer than this, in milliseconds. */ timeoutMs?: number; /** Run and idle limits on each attempt. `runMs` is `timeoutMs` by another name; `timeoutMs` wins when both are set. */ timeout?: StepTimeout; /** * Decides what happens once the step's retries run out. What it returns becomes the step's * result, recorded with `recovered`, so a resumed run does not run the step again — a refund after * a charge that kept failing, say. Throwing fails the step with that error. A cancelled run, a * question, or a drain is never passed to it. */ onError?: (failure: StepFailure) => T | Promise; } /** What a workflow function receives besides its input. */ export interface WorkflowContext { /** The thread this run belongs to. */ readonly threadId: string; /** The tenant the run is for, when it has one. */ readonly tenantId?: string; /** Who the run is for, when it was started with a principal: the caller the server authenticated. */ readonly principal?: Readonly; /** Aborted when the run is cancelled. */ readonly signal: AbortSignal; /** Long-term memory, when the workflow was created with a store. In a tenant's run, the tenant's view of it. */ readonly store?: Store; /** * Runs a piece of work once per thread and records its result. * * When the workflow runs again, a step that already finished returns its recorded result without * running. Name steps so the same call has the same name every time; a name used more than once * is numbered in call order. The result must survive JSON serialization. */ step(name: string, run: (context: StepContext) => Promise | T, options?: StepOptions): Promise; /** * Suspends the workflow until a person answers. * * The first time, the run stops with status `awaiting_input` and this question pending. After * `resume()` supplies a value, the workflow runs again from the top — completed steps return their * recorded results — and this call returns the value instead of stopping. */ interrupt(request: InterruptRequest): T; /** Sends anything to the run's `onEvent` listener as a `custom` event. Nothing is recorded. */ emit(data: unknown): void; } /** A workflow function: its input and context in, its output out. */ export type WorkflowFn = (input: I, context: WorkflowContext) => Promise | O; /** Options for `workflow()`. */ export interface WorkflowOptions { /** Names the workflow in checkpoints, events, and diagrams. Defaults to `workflow`. */ name?: string; /** * Where checkpoints go. Defaults to an in-process `MemoryGraphCheckpointer`; pass a persistent one * to survive restarts, or `false` to record nothing, which also disables interrupts and resume. */ checkpointer?: GraphCheckpointer | false; /** Long-term memory handed to the function as `context.store`. */ store?: Store; /** Default retry policy for every step. A step's own policy wins. Defaults to one attempt. */ retry?: RetryPolicy; /** * Defaults for every step: its retries, its run and idle timeouts, and its `onError`. A step's own * options win, field by field. */ stepDefaults?: Pick; /** Steps that may run at once, for a workflow that starts many with `Promise.all`. Defaults to 16. */ maxConcurrency?: number; /** * Runs every invocation as one operation of a client's lifecycle: pass `ai.lifecycle`. Model calls * inside its steps are operations of their own. */ lifecycle?: OperationLifecycleLike; /** Replaces the system clock, for tests. */ now?: () => Date; } /** Options for one run of a workflow. */ export interface WorkflowRunOptions { /** Identifies the run. Reusing one continues that thread. Defaults to a generated id. */ threadId?: string; /** Stops the run. Steps in flight see the signal. */ signal?: AbortSignal; /** Application data recorded on every checkpoint of the run. */ metadata?: Record; /** Receives each step starting, retrying, and finishing, each checkpoint, and custom events. */ onEvent?: (event: GraphEvent) => void; /** * Lets the caller stop the run between steps: pass a `RunControl` and call `drain()`. Steps in * flight finish and are recorded, no new step starts, and the run ends with `GraphDrainedError`; * `continue()` carries it on, on this worker or another. The agent server drains a workflow it * serves this way. */ control?: RunControlLike; /** * The tenant the run is for. Recorded on every checkpoint as `metadata.tenantId`, read as * `context.tenantId`, and scoping `context.store`; another tenant's thread is not found. A run * without one carries on the thread's. */ tenantId?: string; /** * Who the run is for, read as `context.principal`. Its `tenantId` is the run's tenant when * `tenantId` is not given, and a run whose two tenants differ is refused; its `userId` is recorded * on every checkpoint as `metadata.userId`. */ principal?: Principal; } /** One event of a streamed workflow run. */ export type WorkflowEvent = { /** A step finished, or returned its recorded result. */ type: 'step'; /** Checkpoints written so far in the thread. */ step: number; /** The step's name. */ name: string; /** The key its result is recorded under. */ key: string; /** True when the result came from a checkpoint and the step did not run. */ cached: boolean; /** The workflow state after the step. */ state: WorkflowState; /** Always `running`. */ status: 'running'; } | { /** The workflow returned. */ type: 'done'; /** Checkpoints written in the thread. */ step: number; /** The final state. */ state: WorkflowState; /** Always `completed`. */ status: 'completed'; /** What the workflow returned. */ output: O; } | { /** The workflow is waiting for a person, or was stopped by its signal. */ type: 'interrupt'; /** Checkpoints written in the thread. */ step: number; /** The state when it stopped. */ state: WorkflowState; /** `awaiting_input` for a question, `interrupted` for a cancelled run. */ status: 'awaiting_input' | 'interrupted'; /** The first pending question. */ interrupt?: PendingInterrupt; /** Every pending question. */ interrupts?: PendingInterrupt[]; }; /** The outcome of a workflow run. */ export interface WorkflowResult { /** The thread the run belongs to. */ threadId: string; /** `completed`, `awaiting_input`, or `interrupted`. A failed run throws instead. */ status: GraphStatus; /** What the workflow returned, when it completed. */ output?: O; /** The workflow state: input, step results, and output. */ state: WorkflowState; /** Steps whose results are recorded. */ steps: number; /** The first pending question, when it is waiting. */ interrupt?: PendingInterrupt; /** Every pending question. */ interrupts?: PendingInterrupt[]; } /** Raised when a step outlives its run timeout, or goes longer than its idle timeout without progress. */ export declare class WorkflowStepTimeoutError extends GraphError { /** The step. */ readonly stepName: string; /** The limit it exceeded, in milliseconds. */ readonly timeoutMs: number; /** Which limit: `run` for the whole attempt, `idle` for the time without progress. */ readonly kind: 'run' | 'idle'; constructor( /** The step. */ stepName: string, /** The limit it exceeded, in milliseconds. */ timeoutMs: number, /** Which limit: `run` for the whole attempt, `idle` for the time without progress. */ kind?: 'run' | 'idle'); } /** * A durable workflow: a function whose steps are checkpointed, so it survives interrupts, restarts, * and a move to another worker. Create one with `workflow()`. */ export declare class Workflow { private readonly fn; private readonly options; /** The workflow's name. */ readonly name: string; private readonly defaultCheckpointer; private readonly now; constructor(fn: WorkflowFn, options?: WorkflowOptions); /** Runs to the end, or to the first question, and returns the result. */ invoke(input: I, runOptions?: WorkflowRunOptions): Promise>; /** Runs, yielding an event as each step finishes. */ stream(input: I, runOptions?: WorkflowRunOptions): AsyncIterable>; /** Answers the first pending question and runs on. */ resume(threadId: string, value: unknown, runOptions?: WorkflowRunOptions): AsyncIterable>; /** Answers the first pending question, runs on, and returns the result. */ resumeWith(threadId: string, value: unknown, runOptions?: WorkflowRunOptions): Promise>; /** Answers pending questions by id and runs on. */ resumeInterrupts(threadId: string, answers: Record, runOptions?: WorkflowRunOptions): AsyncIterable>; /** * Runs a thread on from its latest checkpoint: after a crash, a failure, or a cancelled run. * Completed steps return their recorded results, so only unfinished work runs again. */ continue(threadId: string, runOptions?: WorkflowRunOptions): AsyncIterable>; /** The latest checkpoint, or the one at `step`. */ state(threadId: string, step?: number): Promise; /** Every checkpoint of a thread, newest first. */ history(threadId: string, limit?: number): Promise; /** The workflow as a one-node graph, so diagrams and the studio can show it. */ describe(): GraphDescription; private checkpointer; private latest; private fresh; private waiting; private answered; private collect; /** Runs the function against a starting checkpoint, streaming events through a queue. */ private execute; private run; private runStep; } /** * Creates a durable workflow from a function. * * ```ts * const refund = workflow(async (input: { orderId: string }, { step, interrupt }) => { * const order = await step('load', () => orders.get(input.orderId)); * if (!interrupt({ reason: `Refund ${order.total}?` })) return { refunded: false }; * await step('refund', () => payments.refund(order.id)); * return { refunded: true }; * }, { checkpointer }); * ``` */ export declare function workflow(fn: WorkflowFn, options?: WorkflowOptions): Workflow;