Release stage: Generally Available.
Run OpenAI Agents SDK agents as Temporal Workflows. Agent orchestration runs in the Workflow; model calls run as Activities, so they retry durably and are not repeated during Workflow replay.
# Or `pnpm add`/`yarn add`
npm install @temporalio/openai-agents @openai/agents-core @openai/agents-openai openai@openai/agents-core, @openai/agents-openai, and openai are peer dependencies.
Create a Workflow that uses TemporalOpenAIRunner instead of the upstream Runner:
import { Agent } from '@openai/agents-core';
import { TemporalOpenAIRunner } from '@temporalio/openai-agents/workflow';
export async function haikuAgentWorkflow(prompt: string): Promise<string> {
const agent = new Agent({
name: 'Assistant',
instructions: 'You only respond in haikus.',
model: 'gpt-4o-mini',
});
const runner = new TemporalOpenAIRunner();
const result = await runner.run(agent, prompt);
return result.finalOutput ?? '';
}Register OpenAIAgentsPlugin on the Worker so model Activities, Workflow interceptors, sinks, and polyfills are installed:
import { OpenAIProvider } from '@openai/agents-openai';
import { OpenAIAgentsPlugin } from '@temporalio/openai-agents';
import { NativeConnection, Worker } from '@temporalio/worker';
async function main() {
const connection = await NativeConnection.connect();
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
modelParams: { startToCloseTimeout: '30s' },
});
const worker = await Worker.create({
connection,
taskQueue: 'my-task-queue',
workflowsPath: require.resolve('./workflows'),
plugins: [plugin],
});
await worker.run();
}
main();Register the same plugin type on the Client so model parameters and tracing options propagate to new Workflows:
import { OpenAIProvider } from '@openai/agents-openai';
import { Client, Connection } from '@temporalio/client';
import { OpenAIAgentsPlugin } from '@temporalio/openai-agents';
async function main() {
const connection = await Connection.connect();
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
});
const client = new Client({
connection,
plugins: [plugin],
});
const result = await client.workflow.execute('haikuAgentWorkflow', {
args: ['Tell me about recursion in programming.'],
taskQueue: 'my-task-queue',
workflowId: 'haiku-workflow',
});
console.log(result);
}
main();TemporalOpenAIRunner mirrors the OpenAI Agents SDK Runner for Workflow-safe execution. Use run(agent, input, options?) with familiar options such as maxTurns, context, session, and guardrails.
Differences from the upstream runner:
runConfig.modelmust be a model name string. The Worker'smodelProviderresolves it inside the model Activity.signalis not supported. Use Temporal cancellation APIs, such asCancellationScope, to cancel Workflow work.
TemporalOpenAIRunner.run accepts a RunState as the second argument, matching the upstream runner. This supports human approval flows that pause, wait for a Signal or Update, then continue as new.
import { Agent, RunState, tool } from '@openai/agents-core';
import { TemporalOpenAIRunner } from '@temporalio/openai-agents/workflow';
import { condition, continueAsNew, defineSignal, setHandler } from '@temporalio/workflow';
const approveSignal = defineSignal('approve');
interface ApprovalInput {
resumeFromRunState?: string;
}
export async function approvalWorkflow(input: ApprovalInput = {}): Promise<string> {
const action = tool({
name: 'dangerousAction',
description: 'Perform an action that needs approval',
parameters: {
type: 'object' as const,
properties: { reason: { type: 'string' } },
required: ['reason'] as const,
additionalProperties: false as const,
},
needsApproval: true,
execute: async (args) => `did: ${(args as { reason: string }).reason}`,
});
const agent = new Agent({
name: 'Approver',
instructions: 'Use dangerousAction when asked.',
model: 'gpt-4o-mini',
tools: [action],
});
const runner = new TemporalOpenAIRunner();
if (input.resumeFromRunState !== undefined) {
const state = await RunState.fromString(agent, input.resumeFromRunState);
for (const interruption of state.getInterruptions()) {
state.approve(interruption);
}
const resumed = await runner.run(agent, state);
return resumed.finalOutput ?? '';
}
let approved = false;
setHandler(approveSignal, () => {
approved = true;
});
const result = await runner.run(agent, 'please act');
if (result.interruptions.length === 0) return result.finalOutput ?? '';
await condition(() => approved);
await continueAsNew<typeof approvalWorkflow>({ resumeFromRunState: result.state.toString() });
throw new Error('unreachable');
}The agent passed to RunState.fromString must define the same tool names, handoff graph, and MCP servers as the run that produced the serialized state.
Experimental — streaming support is subject to change prior to General Availability.
Pass { stream: true } to run() to stream incremental events as the model responds. The call returns the upstream StreamedRunResult, an async-iterable of agent-SDK stream events. Inside a Workflow, each model call executes as a streaming Activity (invokeModelStreamActivity) that consumes Model.getStreamedResponse and returns the collected list of native model events. The Workflow yields those events deterministically from the Activity's return value — it never polls the live stream, so replay stays deterministic.
External consumers (UIs, tracing pipelines) observe events as they arrive by hosting a WorkflowStream in the Workflow and subscribing with WorkflowStreamClient. The streaming Activity publishes each event to the topic configured on modelParams.streamingTopic. The topic is required for streaming runs; calling run(agent, input, { stream: true }) without a configured topic throws before any Activity is scheduled.
Streaming requires a streamingTopic. Set it on the modelParams of the plugin registered on the Client — the Client interceptor injects the config header into every Workflow-starting call:
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
modelParams: { streamingTopic: 'events', streamingBatchInterval: '100ms' },
});
const client = new Client({ connection, plugins: [plugin] });Workflows started without that Client — by a Schedule or the Temporal UI/CLI — receive no config header. Supply Workflow-authored defaults through the runner's defaultModelParams; unlike the header, these can carry a function-form summary. Host a WorkflowStream in the Workflow to receive the publishes:
import { Agent } from '@openai/agents-core';
import { TemporalOpenAIRunner } from '@temporalio/openai-agents/workflow';
import { WorkflowStream } from '@temporalio/workflow-streams/workflow';
export async function streamingWorkflow(prompt: string): Promise<string> {
new WorkflowStream();
const agent = new Agent({ name: 'Assistant', instructions: '...' });
const runner = new TemporalOpenAIRunner({ defaultModelParams: { streamingTopic: 'events' } });
const result = await runner.run(agent, prompt, { stream: true });
for await (const event of result) {
if (event.type === 'raw_model_stream_event' && event.data.type === 'output_text_delta') {
// event.data.delta — incremental text
}
}
return result.finalOutput ?? '';
}The two layers combine per field: the Client's modelParams overrides the runner's defaultModelParams.
External subscribers receive the unwrapped native model events directly:
import { WorkflowStreamClient } from '@temporalio/workflow-streams/client';
for await (const item of WorkflowStreamClient.create(client, workflowId).topic('events').subscribe()) {
// item.data — native model stream event
}The hosted WorkflowStream retains every published event on the Workflow heap and in history until truncate(upToOffset) is called. A streaming agent publishes many events per run, so a long-running or high-volume Workflow that never truncates will eventually hit Temporal's Workflow history size limits. Call truncate(upToOffset) periodically to keep the heap and history bounded; see @temporalio/workflow-streams for the offset model and batching.
Streaming is incompatible with useLocalActivity, which supports neither Activity heartbeats nor the Workflow Stream signal channel. See @temporalio/workflow-streams for the publisher/subscriber API and delivery semantics.
Use WorkflowSafeMemorySession for conversation history stored on the Workflow heap and rebuilt by replay. This replaces the upstream MemorySession, which is not replay safe because it depends on host process state.
import { Agent } from '@openai/agents-core';
import { TemporalOpenAIRunner, WorkflowSafeMemorySession } from '@temporalio/openai-agents/workflow';
export async function chatWorkflow(prompts: string[]): Promise<string[]> {
const agent = new Agent({
name: 'ChatAgent',
instructions: 'Use the conversation history to answer.',
model: 'gpt-4o-mini',
});
const runner = new TemporalOpenAIRunner();
const session = new WorkflowSafeMemorySession();
const replies: string[] = [];
for (const prompt of prompts) {
const result = await runner.run(agent, prompt, { session });
replies.push(result.finalOutput ?? '');
}
return replies;
}WorkflowSafeMemorySession history lives on the Workflow heap and is rebuilt by replay within a single run. It does not automatically survive continueAsNew — a continued run starts with an empty session. To carry history across a continue-as-new boundary, capture the items and re-seed the new run's session via the constructor's initialItems:
// 1. Before continuing, capture the current history:
const items = await session.getItems();
await continueAsNew(/* ...your Workflow args..., */ items);
// 2. The continued run declares a Workflow parameter to receive those items,
// and re-seeds the session from them:
const session = new WorkflowSafeMemorySession({ initialItems: items });The Worker plugin does five things:
- Registers the model Activity used by Workflow-side model calls.
- Registers Activities for configured MCP server providers.
- Registers Activities for configured sandbox client providers.
- Adds Workflow, Activity, and Nexus interceptors for trace propagation.
- Installs Workflow bundle polyfills required by the OpenAI Agents SDK.
The Client plugin injects the OpenAI Agents config header into Workflow-starting calls. Attach one OpenAIAgentsPlugin instance per Client or Connection configuration. If multiple instances are chained, the last config header wins.
modelParams controls scheduling for the model Activity:
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
modelParams: {
startToCloseTimeout: '30s',
retry: { maximumAttempts: 5, initialInterval: '1s' },
useLocalActivity: false,
},
});See ModelActivityOptions for the public field list. summary can be a string in plugin config; ModelSummaryProvider functions are intentionally not serialized into Workflow headers.
Inline function tools, hosted tools, Activity-backed tools, Nexus operation tools, nested agent tools, and MCP tools can all be used from a Temporal-backed agent. Any tool that does I/O must run outside the Workflow sandbox, usually through an Activity or Nexus Operation.
Use activityAsTool for HTTP calls, database access, file system work, or other I/O. The tool name must match a registered Activity.
import { Agent } from '@openai/agents-core';
import { activityAsTool } from '@temporalio/openai-agents/workflow';
import type * as activities from './activities';
const weatherTool = activityAsTool<typeof activities.getWeather>(
{
name: 'getWeather',
description: 'Get the weather for a city',
parameters: {
type: 'object',
properties: { location: { type: 'string' } },
required: ['location'],
additionalProperties: false,
},
},
{
startToCloseTimeout: '10s',
retryPolicy: { maximumAttempts: 3 },
}
);
const agent = new Agent({
name: 'WeatherAgent',
instructions: 'Use the getWeather tool when asked about weather.',
model: 'gpt-4o-mini',
tools: [weatherTool],
});The generic type is compile-time only. At runtime the Activity is invoked by name through proxyActivities.
For deterministic computation, use tool() from @openai/agents-core directly. Inline tools run in the Workflow sandbox and must not perform I/O, consume nondeterministic randomness, or read wall-clock time beyond Temporal's deterministic replacements.
Hosted tools from @openai/agents-openai, such as webSearchTool(), run server-side through the model provider during the model Activity.
import { Agent, tool } from '@openai/agents-core';
import { webSearchTool } from '@openai/agents-openai';
const addNumbers = tool({
name: 'addNumbers',
description: 'Add two numbers',
parameters: {
type: 'object' as const,
properties: { a: { type: 'number' }, b: { type: 'number' } },
required: ['a', 'b'] as const,
additionalProperties: false as const,
},
execute: async (args) => String((args as { a: number; b: number }).a + (args as { a: number; b: number }).b),
});
const agent = new Agent({
name: 'SearchAgent',
instructions: 'You have web search and arithmetic.',
model: 'gpt-4o-mini',
tools: [addNumbers, webSearchTool()],
});Use nexusOperationAsTool to expose a Nexus Operation as an agent tool. The Workflow starts the Operation through a Nexus client and feeds the stringified result back to the agent.
import { Agent } from '@openai/agents-core';
import { nexusOperationAsTool } from '@temporalio/openai-agents/workflow';
import * as nexus from 'nexus-rpc';
const weatherService = nexus.service('weather', {
getWeather: nexus.operation<{ location: string }, { tempC: number }>(),
});
const weatherTool = nexusOperationAsTool(
weatherService.operations.getWeather,
{
name: 'getWeather',
description: 'Get the weather for a city',
parameters: {
type: 'object',
properties: { location: { type: 'string' } },
required: ['location'],
additionalProperties: false,
},
},
{ service: weatherService, endpoint: 'weather-endpoint' }
);
const agent = new Agent({
name: 'WeatherAgent',
instructions: 'Use the weather tool.',
model: 'gpt-4o-mini',
tools: [weatherTool],
});Use agentAsTool to expose another Agent as a tool while keeping nested model calls durable:
import { Agent } from '@openai/agents-core';
import { agentAsTool } from '@temporalio/openai-agents/workflow';
const specialist = new Agent({
name: 'Specialist',
instructions: 'Answer precisely.',
model: 'gpt-4o-mini',
});
const triage = new Agent({
name: 'Triage',
instructions: 'Delegate specialist questions.',
model: 'gpt-4o-mini',
tools: [
agentAsTool(specialist, {
toolName: 'ask_specialist',
toolDescription: 'Ask the specialist agent',
}),
],
});Nested approval interruptions are not supported. If a nested run pauses for approval, the tool invocation fails with ApplicationFailure type NestedAgentInterruption.
The integration supports stateless and stateful Model Context Protocol servers.
Use stateless servers when each tool call is independent. Register a provider on the Worker:
import { MCPServerStreamableHttp } from '@openai/agents-core';
import { OpenAIProvider } from '@openai/agents-openai';
import { OpenAIAgentsPlugin, StatelessMCPServerProvider } from '@temporalio/openai-agents';
const unitConversionMcp = new StatelessMCPServerProvider(
'unitConversion',
(factoryArgument?) =>
new MCPServerStreamableHttp({ name: 'unitConversion', url: 'https://mcp.example.com/unit-conversion' })
);
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
mcpServerProviders: [unitConversionMcp],
});Reference the same provider name from Workflow code:
import { Agent } from '@openai/agents-core';
import { statelessMcpServer, TemporalOpenAIRunner } from '@temporalio/openai-agents/workflow';
export async function mcpWorkflow(query: string): Promise<string> {
const agent = new Agent({
name: 'UnitConverter',
instructions: 'Use unit conversion tools to answer questions.',
model: 'gpt-4o-mini',
mcpServers: [statelessMcpServer('unitConversion')],
});
const result = await new TemporalOpenAIRunner().run(agent, query);
return result.finalOutput ?? '';
}Use stateful servers when a persistent connection or session is required. The plugin starts a dedicated in-process Worker pinned to a per-run Task Queue and routes MCP operations to it.
import { MCPServerStreamableHttp } from '@openai/agents-core';
import { OpenAIProvider } from '@openai/agents-openai';
import { OpenAIAgentsPlugin, StatefulMCPServerProvider } from '@temporalio/openai-agents';
import { NativeConnection } from '@temporalio/worker';
const connection = await NativeConnection.connect();
const dbMcp = new StatefulMCPServerProvider(
'database',
(factoryArgument?) => new MCPServerStreamableHttp({ name: 'database', url: 'https://mcp.example.com/database' }),
connection
);
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
mcpServerProviders: [dbMcp],
});In the Workflow, call connect() before use and cleanup() in a finally block:
import { Agent } from '@openai/agents-core';
import { statefulMcpServer, TemporalOpenAIRunner } from '@temporalio/openai-agents/workflow';
export async function statefulMcpWorkflow(prompt: string): Promise<string> {
const server = statefulMcpServer('database');
await server.connect();
try {
const agent = new Agent({
name: 'DbAgent',
instructions: 'You have database access.',
model: 'gpt-4o-mini',
mcpServers: [server],
});
const result = await new TemporalOpenAIRunner().run(agent, prompt);
return result.finalOutput ?? '';
} finally {
await server.cleanup();
}
}Dedicated Worker startup and heartbeat failures surface as ApplicationFailure type (exported as DEDICATED_WORKER_FAILURE_TYPE).
Experimental — sandbox support is subject to change prior to General Availability.
An OpenAI Agents SDK SandboxAgent runs commands, edits files, and keeps a workspace inside a sandbox. This plugin dispatches each of those operations as an Activity.
Process handles returned by exec remain usable across Activities only when the sandbox backend can reconnect them after resume; Unix-local and Docker backends cannot.
Register your sandbox backend client under a name on the Worker plugin:
import { UnixLocalSandboxClient } from '@openai/agents-core/sandbox/local';
import { OpenAIProvider } from '@openai/agents-openai';
import { OpenAIAgentsPlugin, SandboxClientProvider } from '@temporalio/openai-agents';
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
sandboxClientProviders: [new SandboxClientProvider('local', new UnixLocalSandboxClient())],
});Reference the same provider name from Workflow code:
import { SandboxAgent, shell } from '@openai/agents-core/sandbox';
import { TemporalOpenAIRunner, temporalSandboxClient } from '@temporalio/openai-agents/workflow';
export async function sandboxWorkflow(prompt: string): Promise<string> {
const agent = new SandboxAgent({
name: 'Coder',
model: 'gpt-4o-mini',
capabilities: [shell()],
});
const result = await new TemporalOpenAIRunner().run(agent, prompt, {
runConfig: { sandbox: { client: temporalSandboxClient('local') } },
});
return result.finalOutput ?? '';
}A credential written into an Activity argument stays in Workflow history. Leave it out of Workflow code and supply it on the Worker instead.
Declare the tool in Workflow code:
import { Agent, hostedMcpTool } from '@openai/agents-core';
const docsTool = hostedMcpTool({ serverLabel: 'docs', serverUrl: 'https://mcp.example.com' });
const agent = new Agent({ name: 'DocsAgent', model: 'gpt-4o-mini', tools: [docsTool] });Supply the credential on the Worker:
import { OpenAIProvider } from '@openai/agents-openai';
import { OpenAIAgentsPlugin } from '@temporalio/openai-agents';
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
hostedToolCredentials: (identity) => {
if (identity.tool !== 'hostedMcp' || identity.serverLabel !== 'docs') return undefined;
const authorization = process.env.DOCS_MCP_TOKEN;
if (!authorization) throw new Error('DOCS_MCP_TOKEN is not set on this Worker');
return { authorization };
},
});For sandbox Manifest environment values, workerEnvValue(name) names an allowlisted Worker environment variable that only the Worker resolves.
import { workerEnvValue } from '@temporalio/openai-agents/workflow';
import { Manifest } from '@openai/agents-core/sandbox';
const manifest = new Manifest({ environment: { DB_PASSWORD: workerEnvValue('WORKER_DB_PASSWORD') } });Allow the variable on the Worker plugin with resolvableWorkerEnvVars: ['WORKER_DB_PASSWORD'].
OpenAI Agents SDK tracing works across Client, Workflow, Activity, Nexus, and MCP boundaries. Workflow replay does not duplicate spans.
Enable the upstream hosted exporter before constructing the plugin:
import { OpenAITracingExporter } from '@openai/agents-openai';
import { addTraceProcessor, BatchTraceProcessor } from '@openai/agents-core';
addTraceProcessor(new BatchTraceProcessor(new OpenAITracingExporter()));Register this processor in the Worker process, not inside Workflow code.
Warning: Using setDefaultOpenAITracingExporter() after constructing the plugin will break plugin internals. Either call it before constructing the plugin, or use addTraceProcessor as described above.
Install @opentelemetry/sdk-trace-base before importing the OTel subpath:
# Or `pnpm add`/`yarn add`
npm install @opentelemetry/sdk-trace-baseimport { trace } from '@opentelemetry/api';
import { OpenAIProvider } from '@openai/agents-openai';
import { OpenAIAgentsPlugin } from '@temporalio/openai-agents';
import { createTracerProvider } from '@temporalio/openai-agents/otel';
trace.setGlobalTracerProvider(createTracerProvider());
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
interceptorOptions: { useOtelInstrumentation: true },
});Use createTracerProvider from this package for replay-safe OTel ID generation. If you need a different provider class, configure it with TemporalIdGenerator and mark it before registering it:
import { trace } from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { markReplaySafeTracerProvider, TemporalIdGenerator } from '@temporalio/openai-agents/otel';
const provider = markReplaySafeTracerProvider(
new NodeTracerProvider({
idGenerator: new TemporalIdGenerator(),
})
);
trace.setGlobalTracerProvider(provider);Only mark providers that use TemporalIdGenerator; the marker opts into the plugin's replay-safety checks.
Set addTemporalSpans: true to emit temporal:* agent-SDK spans for orchestration operations such as Workflow starts, Signals, Queries, Updates, Activities, child Workflows, Nexus Operations, and continue-as-new:
const plugin = new OpenAIAgentsPlugin({
modelProvider: new OpenAIProvider(),
interceptorOptions: { addTemporalSpans: true },
});These spans are agent-SDK spans, so they reach the hosted OpenAI dashboard, custom TracingProcessors, and OTel when enabled.
When using bundleWorkflowCode, pass the plugin to the bundler as well as to the Worker. The bundler hook installs Workflow-side Web API polyfills and registers the Workflow trace interceptor.
import { OpenAIProvider } from '@openai/agents-openai';
import { OpenAIAgentsPlugin } from '@temporalio/openai-agents';
import { bundleWorkflowCode } from '@temporalio/worker';
const plugin = new OpenAIAgentsPlugin({ modelProvider: new OpenAIProvider() });
const bundle = await bundleWorkflowCode({
workflowsPath: require.resolve('./workflows'),
plugins: [plugin],
});Most applications use two import paths: @temporalio/openai-agents in Worker and Client code, and @temporalio/openai-agents/workflow in Workflow code. The other subpaths are for tracing setup or manual Worker wiring.
| Import path | Import from | Use for |
|---|---|---|
@temporalio/openai-agents |
Worker or Client | Plugin setup, MCP providers, model option types |
@temporalio/openai-agents/workflow |
Workflow | Runner, Workflow-safe tools, sessions, MCP handles |
@temporalio/openai-agents/otel |
Worker or Client | Replay-safe OpenTelemetry setup |
@temporalio/openai-agents/workflow-interceptor |
Worker bundling | Manual workflowInterceptorModules wiring without a plugin |
@temporalio/openai-agents exports OpenAIAgentsPlugin, MCP provider classes, SandboxClientProvider, model option types, hosted tool credential types, and DEDICATED_WORKER_FAILURE_TYPE. OpenAIAgentsTraceClientInterceptor is also public for Clients that need manual interceptor wiring instead of plugin registration.
@temporalio/openai-agents/workflow exports Workflow-safe APIs: TemporalOpenAIRunner, WorkflowSafeMemorySession, activityAsTool, nexusOperationAsTool, agentAsTool, statelessMcpServer, statefulMcpServer, temporalSandboxClient, workerEnvValue, related option/definition types, and DEDICATED_WORKER_FAILURE_TYPE.
@temporalio/openai-agents/otel exports createTracerProvider, TemporalIdGenerator, markReplaySafeTracerProvider, isReplaySafeTracerProvider, and TemporalOpenAIAgentsTracerProviderOptions. Install optional peer dependency @opentelemetry/sdk-trace-base only when you use this OTel integration.
@temporalio/openai-agents/workflow-interceptor exports the Workflow trace interceptor module. The plugin registers it automatically; import this path only when assembling Worker interceptor modules yourself.
There is no public TypeScript testing subpath.
Any OpenAI Agents SDK ModelProvider can be passed as modelProvider. The provider runs in the model Activity, never inside the Workflow sandbox.
| Feature | Status | Notes |
|---|---|---|
| Multi-turn agents | Supported | Agent loop runs durably in the Workflow |
| Handoffs | Supported | Agent and handoff() forms |
| Inline function tools | Supported | Must be deterministic |
| Activity-backed tools | Supported | Via activityAsTool() |
| Nexus operation tools | Supported | Via nexusOperationAsTool() |
| Nested agent tools | Supported | Via agentAsTool() |
| Hosted tools | Supported | Executed server-side by the model provider |
| Stateless MCP servers | Supported | Via StatelessMCPServerProvider and statelessMcpServer() |
| Stateful MCP servers | Supported | Via StatefulMCPServerProvider and statefulMcpServer() |
| Sandbox agents | Supported (experimental) | Via SandboxClientProvider and temporalSandboxClient() |
| Sessions | Supported | Via WorkflowSafeMemorySession; upstream MemorySession is rejected |
| Run state and approvals | Supported | Serialize with result.state.toString() and rehydrate with RunState.fromString |
| Guardrails | Supported | Guardrail callbacks must be deterministic |
| Tracing | Supported | OpenAI hosted traces, custom TracingProcessors, OTel, and optional temporal:* spans |
| Agent context | Supported | Activity tools receive a copy |
continueAsNew |
Supported | Plugin config propagates to the continuation |
| Child Workflows | Supported | Plugin config propagates to children |
| Local Activities | Supported | Set useLocalActivity: true in modelParams |
| Model override per run | Supported | runConfig.model accepts a string model name |
| Streaming | Supported (experimental) | run(agent, input, { stream: true }); requires streamingTopic and a hosted WorkflowStream |
| Voice agents | Not supported |