WorkflowAgent

API Reference for the WorkflowAgent class.

Creates a durable, resumable AI agent for use inside a workflow. WorkflowAgent handles the agent loop, tool schema serialization across workflow step boundaries, and built-in tool approval flows.

Unlike ToolLoopAgent from the ai package, WorkflowAgent is designed to survive process restarts, pause for human approval, and integrate with the Workflow DevKit's step mechanism.

WorkflowAgent supports runtimeContext for shared agent state and toolsContext for per-tool context. Because these values can cross workflow and step boundaries, keep them serializable durable data. Unlike ToolLoopAgent, do not place functions, class instances, symbols, database clients, or SDK clients in context; pass identifiers or configuration and recreate non-serializable resources inside step functions.

import { WorkflowAgent } from '@ai-sdk/workflow';
import { tool } from 'ai';
import { z } from 'zod';

const agent = new WorkflowAgent({
  model: 'anthropic/claude-sonnet-5.5',
  instructions: 'You are a helpful assistant.',
  tools: {
    weather: tool({
      description: 'Get the weather in a location',
      inputSchema: z.object({
        location: z.string(),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 72,
      }),
    }),
  },
});

const result = await agent.stream({
  messages: [
    {
      role: 'user',
      content: [{ type: 'text', text: 'What is the weather in NYC?' }],
    },
  ],
});

console.log(result.messages);

To see WorkflowAgent in action, check out these examples.

Import

import { WorkflowAgent } from "@ai-sdk/workflow"

Constructor

Parameters

id?:

string
The id of the agent.

model:

LanguageModel
The language model to use. A string compatible with the Vercel AI Gateway (e.g., 'anthropic/claude-sonnet-5.5') or a provider instance (e.g., openai('gpt-6-astra')).

instructions?:

Instructions
Instructions for the agent, used as the system prompt. Supports provider-specific options (e.g., caching) when using the SystemModelMessage form.

tools?:

Record<string, Tool>
A set of tools the agent can call. Keys are tool names. Tools are serialized to JSON Schema across workflow step boundaries and validated with Ajv at runtime.

toolChoice?:

ToolChoice
Tool call selection strategy. Options: 'auto' | 'none' | 'required' | { type: 'tool', toolName: string }. Default: 'auto'.

stopWhen?:

StopCondition | StopCondition[]
Default stop condition for the agent loop. When omitted, generate defaults to 20 steps, while stream has no maximum step count. Use isStepCount() to bound execution. Per-call values override this default.

activeTools?:

ActiveTools<TTools>
Default set of active tools. Limits which tools the model can call without changing tool call and result types. Per-stream values override this default.

output?:

OutputSpecification
Default structured output specification. Per-stream values override this default.

repairToolCall?:

ToolCallRepairFunction
Default function to repair tool calls that fail to parse. Mark the function with 'use step' when used in a workflow. Per-stream values override this default.

experimental_download?:

DownloadFunction
Default custom download function for URLs. Per-stream values override this default.

experimental_sandbox?:

Experimental_SandboxSession
Default sandbox session passed to tool descriptions and execution as experimental_sandbox, and exposed to prepareStep. Per-stream values override this default.

experimental_toolApprovalSecret?:

WorkflowToolApprovalSecret
Workflow-safe reference to the environment variable containing the secret used to HMAC-sign tool approval requests and verify approved message history before tool execution. Only the environment variable name crosses workflow boundaries; the secret is read inside signing and verification steps. Per-stream values override this default.

prepareStep?:

PrepareStepCallback
Callback called before each step in the agent loop. Use it to modify settings, manage context, inject messages dynamically, or override experimental_sandbox for the current step. Receives step number, previous steps, messages, context, and sandbox.

prepareCall?:

PrepareCallCallback
Callback called once before the agent loop starts. Use it to transform model, instructions, tools configuration, or other settings based on runtime context. Cannot override tools (bound at construction for type safety).

runtimeContext?:

Context
Default shared runtime context for every stream call. Flows through prepareStep, lifecycle callbacks, and step results. Per-stream values override this default. Must be serializable when used in workflows.

toolsContext?:

InferToolSetContext<TTools>
Default per-tool context map for every stream call. Each tool receives only its own validated entry as context. Per-stream values override this default. Must be serializable when used in workflows.

telemetry?:

TelemetryOptions
Telemetry configuration with options for enabling/disabling telemetry, setting a function ID, and recording inputs/outputs.

onStart?:

WorkflowAgentOnStartCallback
Callback called when the agent starts streaming, before any LLM calls. Receives the model, messages, runtime context, and tools context. If also specified in stream(), both callbacks fire (constructor first). Takes precedence over experimental_onStart when both are provided in the constructor.
GenerateTextStartEvent

model:

LanguageModel
The model being used for the generation.

messages:

Array<ModelMessage>
The messages being sent to the model.

runtimeContext:

Context
The shared runtime context for the agent loop.

toolsContext:

InferToolSetContext<Tools>
The per-tool context map for the agent loop.

experimental_onStart?:

WorkflowAgentOnStartCallback
Deprecated alias for onStart. Used only when onStart is not provided in the constructor.

onStepStart?:

WorkflowAgentOnStepStartCallback
Callback called before each step (LLM call) begins. Receives step number, model, messages, previous steps, runtime context, and tools context. If also specified in stream(), both callbacks fire (constructor first). Takes precedence over experimental_onStepStart when both are provided in the constructor.
GenerateTextStepStartEvent

model:

LanguageModel
The model being used for this step.

messages:

Array<ModelMessage>
The messages that will be sent to the model for this step.

steps:

ReadonlyArray<StepResult>
Results from all previously finished steps.

runtimeContext:

Context
The shared runtime context for this step.

toolsContext:

InferToolSetContext<Tools>
The per-tool context map for this step.

experimental_onStepStart?:

WorkflowAgentOnStepStartCallback
Deprecated alias for onStepStart. Used only when onStepStart is not provided in the constructor.

onToolExecutionStart?:

WorkflowAgentOnToolExecutionStartCallback
Callback called right before a tool's execute function runs. If also specified in stream(), both callbacks fire (constructor first). Experimental (can break in patch releases).
WorkflowAgentToolExecutionStartEvent

toolCall:

{ type: "tool-call"; toolCallId: string; toolName: string; input: unknown }
The tool call being executed. For concrete tool sets, the tool name and input are correlated.

stepNumber:

number
The current step number, starting at zero.

messages:

Array<ModelMessage>
Messages that were sent to the language model to initiate the response that contained the tool call.

toolContext:

InferToolContext<TOOLS[NAME]> | undefined
The validated context for the tool call. For concrete tool sets, each event union member pairs it with the corresponding tool name.

onToolExecutionEnd?:

WorkflowAgentOnToolExecutionEndCallback
Callback called right after a tool's execute function completes or errors. Check success to determine whether output or error is available. If also specified in stream(), both callbacks fire (constructor first). Experimental (can break in patch releases).
WorkflowAgentToolExecutionEndEvent

toolCall:

{ type: "tool-call"; toolCallId: string; toolName: string; input: unknown }
The tool call that was executed. For concrete tool sets, the tool name and input are correlated.

stepNumber:

number
The current step number, starting at zero.

durationMs:

number
Tool execution time in milliseconds. Workflow agents use durationMs; AI SDK Core generation callbacks use toolExecutionMs.

messages:

Array<ModelMessage>
Messages that were sent to the language model to initiate the response that contained the tool call.

toolContext:

InferToolContext<TOOLS[NAME]> | undefined
The validated context for the tool call. For concrete tool sets, each event union member pairs it with the corresponding tool name.

success:

boolean
Whether the tool execution succeeded. Discriminates between the output and error event variants.

output?:

InferToolOutput<TOOLS[NAME]>
The tool output. Available when success is true and correlated with the configured tool.

error?:

unknown
The tool execution error. Available when success is false.

onStepEnd?:

WorkflowAgentOnStepEndCallback
Callback invoked after each agent step completes. If also specified in stream(), both callbacks fire (constructor first).

onStepFinish?:

WorkflowAgentOnStepFinishCallback
Deprecated. Use onStepEnd instead. This alias is only used as a fallback when onStepEnd is not provided.

onEnd?:

WorkflowAgentOnEndCallback
Callback called when all agent steps are finished and the response is complete. Receives steps, messages, text, finish reason, total usage, and context. If also specified in stream(), both callbacks fire (constructor first).

maxOutputTokens?:

number
Maximum number of tokens the model is allowed to generate.

temperature?:

number
Sampling temperature, controls randomness.

topP?:

number
Top-p (nucleus) sampling parameter.

topK?:

number
Top-k sampling parameter.

presencePenalty?:

number
Presence penalty parameter.

frequencyPenalty?:

number
Frequency penalty parameter.

stopSequences?:

string[]
Custom token sequences which stop the model output.

seed?:

number
Seed for deterministic generation (if supported).

maxRetries?:

number
How many times to retry retryable model-call failures. Set to 0 to disable retries. Retry-After response headers are respected, and durable workflow step retries are not stacked. Default: 2.

headers?:

Record<string, string | undefined>
Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.

providerOptions?:

ProviderOptions
Additional provider-specific configuration.

Properties

id:

string | undefined
The id of the agent. Used for telemetry identification. Read-only.

tools:

Record<string, Tool>
The tool set configured for this agent. Read-only.

Methods

generate()

Runs the durable agent loop using the model's non-streaming doGenerate() method. Model calls and tool execution use the same orchestration as stream().

const result = await agent.generate({ prompt: 'Summarize the findings.' });
console.log(result.text);
console.log(result.usage);

Accepts WorkflowAgentGenerateOptions, sharing prompt/messages, instructions, generation settings, tools and runtime context, preparation, stopping, output, and lifecycle options with stream(). It does not accept writable, includeRawChunks, experimental_transform, sendFinish, or preventClose. Without a configured stop condition, generation stops after at most 20 steps. stream() retains its existing unbounded default.

The include option accepts requestBody, responseBody, and requestMessages booleans, all defaulting to false. Enable them only when the additional request/response data is needed. Response IDs, timestamps, model IDs, headers, and provider metadata are retained when supplied by the model.

Returns

Returns Promise<WorkflowAgentGenerateResult> with the core GenerateTextResult semantics:

  • text, finishReason, rawFinishReason, and finalStep describe the final step.
  • content, files, sources, warnings, tool calls, and tool results cover all steps.
  • usage and totalUsage sum all steps while retaining token details and unknown counts.
  • responseMessages includes generated messages, even when prepareStep replaces the input history.
  • output defaults to text, or follows the constructor/per-call output specification. Reading it throws NoOutputGeneratedError when output is unavailable, such as a final tool-calls response.

Inside a workflow, timeout bounds model calls, including retries and dispatch after suspension. It does not cancel a hook that a tool is waiting on. Use the Workflow runtime’s run.cancel() to stop a suspended run. See deadlines, cancellation, and replay.

Generation failures reject. The end callback runs before complete-output parsing; a parsing failure can therefore reject after onEnd has run. Existing Workflow callback shapes and preparation precedence apply; they are not all identical to core's callback APIs.

Use the result API inside workflow code and return selected serializable fields (for example, text and usage) through run.returnValue. Returning the whole result class is not supported. Performance data currently measures the model response; complete tool/step timing is not yet reported.

Approval requests appear in content and responseMessages without requiring a writable. To continue, append responseMessages to the original history and add a tool message containing the user's tool-approval-response. Preserve the request signature and tool-call input; use the same experimental_toolApprovalSecret environment-variable reference on continuation. Local approvals are validated before execution, and provider-executed approval responses are forwarded to the provider. See approval round trips.

stream()

Runs the agent loop, streaming responses and executing tool calls as needed. Returns a promise resolving to a WorkflowAgentStreamResult.

const result = await agent.stream({
  messages: [{ role: 'user', content: [{ type: 'text', text: 'Hello' }] }],
});

prompt:

string | Array<ModelMessage>
A prompt string or a list of messages. You can either use prompt or messages but not both.

messages:

Array<ModelMessage>
The conversation messages to process. You can either use prompt or messages but not both.

writable?:

WritableStream<ModelCallStreamPart>
A writable stream that receives raw model stream parts in real-time. Convert to UI message chunks at the response boundary using createModelCallToUIChunkTransform().

instructions?:

Instructions
Override the agent instructions for this call.

system?:

string
Deprecated. Use instructions instead.

stopWhen?:

StopCondition | StopCondition[]
Condition(s) for ending the agent loop. When omitted and no constructor-level condition is configured, WorkflowAgent has no maximum step count and continues until natural completion. Use isStepCount() to bound execution.

toolChoice?:

ToolChoice
Override the tool choice strategy for this call. Default: 'auto'.

activeTools?:

ActiveTools<TTools>
Limits the subset of tools available for this call without changing tool call and result types.

output?:

OutputSpecification
Structured output specification. Use Output.object({ schema }) for typed objects or Output.text() for text.

timeout?:

number
Model-call deadline in milliseconds. Inside a workflow, the absolute deadline is checked at dispatch and retry and remains in effect across suspension. It does not cancel a tool waiting on a Workflow hook; use run.cancel() to stop the suspended run.

sendFinish?:

boolean
Whether to send a 'finish' chunk to the writable stream when streaming completes. Default: true.

preventClose?:

boolean
Whether to prevent the writable stream from being closed after streaming completes. Default: false.

includeRawChunks?:

boolean
Include raw, unprocessed chunks from the provider in the stream. Default: false.

repairToolCall?:

ToolCallRepairFunction
Callback to attempt automatic recovery when a tool call cannot be parsed. Mark the function with 'use step' when used in a workflow.

experimental_transform?:

StreamTextTransform | Array<StreamTextTransform>
Factories for stream transformations, applied in order to raw provider events before model processing and writable output. Each factory must return a TransformStream synchronously and maintain the stream structure. Mark each factory with 'use step' when used in a workflow.

experimental_download?:

DownloadFunction
Custom download function for fetching files/URLs.

experimental_sandbox?:

Experimental_SandboxSession
Sandbox session passed to tool descriptions and execution as experimental_sandbox, and exposed to prepareStep. Overrides the constructor default.

experimental_toolApprovalSecret?:

WorkflowToolApprovalSecret
Workflow-safe reference to the environment variable containing the secret used to HMAC-sign tool approval requests and verify approved message history before tool execution. Only the environment variable name crosses workflow boundaries; the secret is read inside signing and verification steps. Overrides the constructor default.

telemetry?:

TelemetryOptions
Per-call telemetry configuration.

runtimeContext?:

Context
Shared runtime context for this stream call. Overrides the constructor default and flows through prepareStep, lifecycle callbacks, and step results. Must be serializable when used in workflows.

toolsContext?:

InferToolSetContext<TTools>
Per-tool context map for this stream call. Overrides the constructor default. Each tool receives only its own validated entry as context. Must be serializable when used in workflows.

prepareStep?:

PrepareStepCallback
Per-call prepareStep override. Receives the initial instructions and messages alongside the current step state.

onStart?:

WorkflowAgentOnStartCallback
Per-call onStart callback. If also specified in the constructor, both fire (constructor first). Takes precedence over experimental_onStart when both are provided in this call.

experimental_onStart?:

WorkflowAgentOnStartCallback
Deprecated alias for the per-call onStart callback. Used only when onStart is not provided in this call.

onStepStart?:

WorkflowAgentOnStepStartCallback
Per-call onStepStart callback. If also specified in the constructor, both fire (constructor first). Takes precedence over experimental_onStepStart when both are provided in this call.

experimental_onStepStart?:

WorkflowAgentOnStepStartCallback
Deprecated alias for the per-call onStepStart callback. Used only when onStepStart is not provided in this call.

onToolExecutionStart?:

WorkflowAgentOnToolExecutionStartCallback
Per-call onToolExecutionStart callback. If also specified in the constructor, both fire (constructor first).

onToolExecutionEnd?:

WorkflowAgentOnToolExecutionEndCallback
Per-call onToolExecutionEnd callback. If also specified in the constructor, both fire (constructor first).

onStepEnd?:

WorkflowAgentOnStepEndCallback
Per-call onStepEnd callback. If also specified in the constructor, both fire (constructor first).

onStepFinish?:

WorkflowAgentOnStepFinishCallback
Deprecated. Use onStepEnd instead. This alias is only used as a fallback when onStepEnd is not provided.

onEnd?:

WorkflowAgentOnEndCallback
Per-call onEnd callback. If also specified in the constructor, both fire (constructor first).

onError?:

WorkflowAgentOnErrorCallback
Callback invoked when an error occurs during streaming.

onAbort?:

WorkflowAgentOnAbortCallback
Callback invoked when the operation is aborted. Receives all previously finished steps.

Returns

Returns a Promise<WorkflowAgentStreamResult> with the following properties:

messages:

Array<ModelMessage>
The final messages including all tool calls and results.

steps:

Array<StepResult>
Details for all steps taken by the agent.

toolCalls:

Array<ToolCall>
Tool calls from the last step, including unexecuted calls (e.g., tools requiring approval).

toolResults:

Array<ToolResult>
Tool results from the last step. Only includes results for tools that were executed.

error:

unknown | undefined
The original value from a model stream error part. The property is present when an error part was emitted, even if its value is undefined; use 'error' in result to distinguish that case.

output:

OUTPUT
The structured output if an output specification was provided.

repairToolCall in a workflow

repairToolCall runs inside the model step. When you use it in a workflow, define the callback as a function with a 'use step' directive. You can set it on the constructor or pass it to stream().

Workflow saves a reference to the registered function and loads its implementation inside the model step. Keep any values captured by the function serializable. An ordinary inline callback causes a serialization error when it crosses this step boundary.

The callback can be asynchronous. Return the repaired tool call, or null if the repair cannot be completed. See tool call repair for repair strategies.

experimental_transform in a workflow

Pass a transform factory, or an array of factories, to stream(). Mark each factory with 'use step' so Workflow can load it inside the model step. The factory must return a TransformStream synchronously. Keep any captured values serializable.

import type { LanguageModelV4StreamPart } from '@ai-sdk/provider';
import { WorkflowAgent } from '@ai-sdk/workflow';
import { getWritable } from 'workflow';

function uppercaseText() {
  'use step';

  return new TransformStream<
    LanguageModelV4StreamPart,
    LanguageModelV4StreamPart
  >({
    transform(part, controller) {
      controller.enqueue(
        part.type === 'text-delta'
          ? { ...part, delta: part.delta.toUpperCase() }
          : part,
      );
    },
  });
}

export async function uppercaseWorkflow() {
  'use workflow';

  const agent = new WorkflowAgent({ model: 'openai/gpt-5' });

  return agent.stream({
    messages: [{ role: 'user', content: 'Write a short greeting.' }],
    writable: getWritable(),
    experimental_transform: uppercaseText,
  });
}

Utilities

createModelCallToUIChunkTransform(options?)

Creates a TransformStream that converts raw ModelCallStreamPart chunks (written by the agent to the writable stream) into UIMessageChunk objects suitable for client consumption.

import { createModelCallToUIChunkTransform } from '@ai-sdk/workflow';

return createUIMessageStreamResponse({
  stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
});

When resuming with a WorkflowChatTransport cursor, replay the raw workflow stream from index 0 and pass the non-negative UI chunk index to the transform:

const readable = run
  .getReadable({ startIndex: 0 })
  .pipeThrough(createModelCallToUIChunkTransform({ uiStartIndex: startIndex }));

uiStartIndex must be a non-negative safe integer. Raw model stream parts and UI message chunks are not one-to-one, so do not pass a UI chunk index to getReadable. Negative tail indexes require a durable stream that already stores UIMessageChunk objects.

toUIMessageChunk()

Converts a single ModelCallStreamPart to a UIMessageChunk. Returns undefined for parts that don't map to UI chunks.

import { toUIMessageChunk } from '@ai-sdk/workflow';

const uiChunk = toUIMessageChunk(modelCallPart);

Types

ActiveTools

type ActiveTools<TTools extends ToolSet> =
  | ReadonlyArray<keyof TTools & string>
  | undefined;

Limits a workflow agent call to the listed tool names. undefined means no tool restriction is applied.

InferWorkflowAgentUIMessage

Infers the UI message type for a WorkflowAgent instance. Optionally accepts a second type argument for custom message metadata.

import { WorkflowAgent, InferWorkflowAgentUIMessage } from '@ai-sdk/workflow';

const agent = new WorkflowAgent({
  model: 'anthropic/claude-sonnet-5.5',
  tools: { weather: weatherTool },
});

type MyAgentUIMessage = InferWorkflowAgentUIMessage<typeof agent>;

InferWorkflowAgentTools

Infers the tool set type of a WorkflowAgent instance.

import { WorkflowAgent, InferWorkflowAgentTools } from '@ai-sdk/workflow';

type MyTools = InferWorkflowAgentTools<typeof myAgent>;

Examples

Basic Agent with Tools

import { WorkflowAgent } from '@ai-sdk/workflow';
import { tool } from 'ai';
import { z } from 'zod';

const agent = new WorkflowAgent({
  model: 'anthropic/claude-sonnet-5.5',
  instructions: 'You are a helpful assistant.',
  tools: {
    weather: tool({
      description: 'Get weather for a location',
      inputSchema: z.object({
        location: z.string(),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 72,
        condition: 'sunny',
      }),
    }),
  },
});

const result = await agent.stream({
  messages: [
    {
      role: 'user',
      content: [{ type: 'text', text: 'What is the weather in NYC?' }],
    },
  ],
});

console.log(result.messages);
console.log(result.steps);

Agent in a Workflow with Durable Tools

workflow/agent-chat.ts
import { WorkflowAgent, type ModelCallStreamPart } from '@ai-sdk/workflow';
import { convertToModelMessages, tool, type UIMessage } from 'ai';
import { getWritable } from 'workflow';
import { z } from 'zod';

// Tool execute functions marked with 'use step' become durable workflow steps
// with automatic retries and persistence
async function searchFlightsStep(input: {
  origin: string;
  destination: string;
}) {
  'use step';
  const response = await fetch(`https://api.flights.example/search?...`);
  return response.json();
}

export async function chat(messages: UIMessage[]) {
  'use workflow';

  const modelMessages = await convertToModelMessages(messages);

  const agent = new WorkflowAgent({
    model: 'anthropic/claude-sonnet-5.5',
    instructions: 'You are a flight booking assistant.',
    tools: {
      searchFlights: tool({
        description: 'Search for available flights',
        inputSchema: z.object({
          origin: z.string(),
          destination: z.string(),
        }),
        execute: searchFlightsStep,
      }),
    },
  });

  const result = await agent.stream({
    messages: modelMessages,
    writable: getWritable<ModelCallStreamPart>(),
  });

  return { messages: result.messages };
}
app/api/chat/route.ts
import { createModelCallToUIChunkTransform } from '@ai-sdk/workflow';
import { createUIMessageStreamResponse, type UIMessage } from 'ai';
import { start } from 'workflow/api';
import { chat } from '@/workflow/agent-chat';

export async function POST(request: Request) {
  const { messages }: { messages: UIMessage[] } = await request.json();

  const run = await start(chat, [messages]);

  return createUIMessageStreamResponse({
    stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
  });
}

Agent with Structured Output

import { WorkflowAgent, Output } from '@ai-sdk/workflow';
import { z } from 'zod';

const analysisAgent = new WorkflowAgent({
  model: 'anthropic/claude-sonnet-5.5',
});

const result = await analysisAgent.stream({
  messages: [
    {
      role: 'user',
      content: [
        {
          type: 'text',
          text: 'Analyze: "The product exceeded my expectations!"',
        },
      ],
    },
  ],
  output: Output.object({
    schema: z.object({
      sentiment: z.enum(['positive', 'negative', 'neutral']),
      score: z.number(),
      summary: z.string(),
    }),
  }),
});

console.log(result.output);
// { sentiment: 'positive', score: 9, summary: '...' }

Agent with Tool Approval

For WorkflowAgent, tool approval is configured on the tool definition with needsApproval. For generateText, streamText, and ToolLoopAgent, use toolApproval instead.

import { WorkflowAgent } from '@ai-sdk/workflow';
import { tool } from 'ai';
import { z } from 'zod';

const agent = new WorkflowAgent({
  model: 'anthropic/claude-sonnet-5.5',
  experimental_toolApprovalSecret: {
    environmentVariable: 'TOOL_APPROVAL_SECRET',
  },
  tools: {
    bookFlight: tool({
      description: 'Book a flight',
      inputSchema: z.object({
        flightId: z.string(),
        passengerName: z.string(),
      }),
      needsApproval: true, // Pauses the agent until user approves
      execute: bookFlightStep,
    }),
  },
});

When experimental_toolApprovalSecret is configured, each approval request is signed over its approval ID, tool call ID, tool name, and validated input. Replayed approvals with a missing or invalid signature do not execute the tool. The signature is preserved in the durable stream and UI message history, while only the environment variable name crosses workflow boundaries. Signing and verification steps read the raw secret from their local environment and do not serialize it. A stream-level reference overrides the constructor value.

Agent with Lifecycle Callbacks

import { WorkflowAgent } from '@ai-sdk/workflow';

const agent = new WorkflowAgent({
  model: 'anthropic/claude-sonnet-5.5',
  tools: { weather: weatherTool },

  // Agent-wide callbacks
  onStepEnd({ usage }) {
    console.log('Tokens used:', usage.totalTokens);
  },
});

const result = await agent.stream({
  messages,

  // Per-call callbacks (both fire)
  async onStepEnd({ usage }) {
    await trackUsage(usage);
  },

  onEnd({ steps, totalUsage }) {
    console.log(
      `Done in ${steps.length} steps, ${totalUsage.totalTokens} tokens`,
    );
  },
});