Skip to content
Draft
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@temporalio/envconfig": "workspace:*",
"@temporalio/interceptors-opentelemetry": "workspace:*",
"@temporalio/nexus": "workspace:*",
"@temporalio/openai-agents": "workspace:*",
"@temporalio/nyc-test-coverage": "workspace:*",
"@temporalio/plugin": "workspace:*",
"@temporalio/proto": "workspace:*",
Expand Down
99 changes: 99 additions & 0 deletions packages/openai-agents/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
{
"name": "@temporalio/openai-agents",
"version": "1.16.0",
"description": "Temporal OpenAI Agents SDK integration package",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"import": "./lib/index.js",
"require": "./lib/index.js",
"default": "./lib/index.js"
},
"./workflow": {
"types": "./lib/workflow.d.ts",
"import": "./lib/workflow.js",
"require": "./lib/workflow.js",
"default": "./lib/workflow.js"
},
"./load-polyfills": {
"types": "./lib/workflow/load-polyfills.d.ts",
"import": "./lib/workflow/load-polyfills.js",
"require": "./lib/workflow/load-polyfills.js",
"default": "./lib/workflow/load-polyfills.js"
},
"./lib/workflow": {
"types": "./lib/workflow.d.ts",
"import": "./lib/workflow.js",
"require": "./lib/workflow.js",
"default": "./lib/workflow.js"
},
"./lib/load-polyfills": {
"types": "./lib/workflow/load-polyfills.d.ts",
"import": "./lib/workflow/load-polyfills.js",
"require": "./lib/workflow/load-polyfills.js",
"default": "./lib/workflow/load-polyfills.js"
},
"./lib/index": {
"types": "./lib/index.d.ts",
"import": "./lib/index.js",
"require": "./lib/index.js",
"default": "./lib/index.js"
},
"./testing": {
"types": "./lib/worker/testing.d.ts",
"import": "./lib/worker/testing.js",
"require": "./lib/worker/testing.js",
"default": "./lib/worker/testing.js"
},
"./lib/testing": {
"types": "./lib/worker/testing.d.ts",
"import": "./lib/worker/testing.js",
"require": "./lib/worker/testing.js",
"default": "./lib/worker/testing.js"
}
},
"keywords": [
"temporal",
"workflow",
"ai",
"openai",
"agents"
],
"author": "Temporal Technologies Inc. <sdk@temporal.io>",
"license": "MIT",
"dependencies": {
"@opentelemetry/api": "^1.9.0",
"@temporalio/activity": "workspace:*",
"@temporalio/common": "workspace:*",
"@temporalio/plugin": "workspace:*",
"@temporalio/workflow": "workspace:*",
"@ungap/structured-clone": "^1.3.0",
"headers-polyfill": "^4.0.3",
"web-streams-polyfill": "^4.2.0"
},
"peerDependencies": {
"@openai/agents-core": "~0.3.0",
"@openai/agents-openai": "~0.3.0"
},
"engines": {
"node": ">= 20.0.0"
},
"bugs": {
"url": "https://github.com/temporalio/sdk-typescript/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/temporalio/sdk-typescript.git",
"directory": "packages/openai-agents"
},
"homepage": "https://github.com/temporalio/sdk-typescript/tree/main/packages/openai-agents",
"publishConfig": {
"access": "public"
},
"files": [
"src",
"lib"
]
}
19 changes: 19 additions & 0 deletions packages/openai-agents/src/common/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { TemporalFailure } from '@temporalio/common';

export function unwrapTemporalFailure(error: unknown): TemporalFailure | undefined {
const visited = new Set<unknown>();
const stack: unknown[] = [error];
while (stack.length > 0) {
const current = stack.pop();
if (!current || typeof current !== 'object' || visited.has(current)) continue;
visited.add(current);
if (current instanceof TemporalFailure) return current;
if (current instanceof AggregateError) {
for (const inner of current.errors) {
stack.push(inner);
}
}
stack.push((current as any).cause);
}
return undefined;
}
42 changes: 42 additions & 0 deletions packages/openai-agents/src/common/model-activity-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Agent, AgentInputItem } from '@openai/agents-core';
import { ActivityCancellationType, type Duration, type Priority, type RetryPolicy } from '@temporalio/common';

export type { AgentInputItem } from '@openai/agents-core';

export interface ModelSummaryProvider {
/** Generate a human-readable summary for the model activity shown in the Temporal UI. */
provide(
agent: Agent<any, any> | undefined,
instructions: string | undefined,
input: string | AgentInputItem[]
): string;
}

export interface ModelActivityOptions {
/** Task queue for the model activity. Defaults to the current worker's task queue. */
taskQueue?: string;
/** Maximum total time from schedule to completion, including retries. */
scheduleToCloseTimeout?: Duration;
/** Maximum time the activity can wait in the task queue before a worker picks it up. */
scheduleToStartTimeout?: Duration;
/** Maximum time for a single activity execution attempt. @default '60s' */
startToCloseTimeout?: Duration;
/** Interval for heartbeat checks. The activity must heartbeat within this period. */
heartbeatTimeout?: Duration;
/** Retry policy for the model activity. Defaults to the server-defined policy. */
retryPolicy?: RetryPolicy;
/** Use local activities instead of regular activities. Avoids a server round-trip but lacks independent retry. @default false */
useLocalActivity?: boolean;
/** Activity summary shown in the Temporal UI. String for static text, ModelSummaryProvider for dynamic. */
summaryOverride?: string | ModelSummaryProvider;
/** How cancellation propagates from the workflow to the activity. @default ActivityCancellationType.TRY_CANCEL */
cancellationType?: ActivityCancellationType;
/** Priority for the model activity. Omit to use server defaults. */
priority?: Priority;
}

export const DEFAULT_MODEL_ACTIVITY_OPTIONS: ModelActivityOptions = {
startToCloseTimeout: '60s',
useLocalActivity: false,
cancellationType: ActivityCancellationType.TRY_CANCEL,
};
57 changes: 57 additions & 0 deletions packages/openai-agents/src/common/serialized-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { ModelSettings, SerializedHandoff, SerializedOutputType, SerializedTool } from '@openai/agents-core';

/** Current wire protocol version. Activity validates this on every invocation. */
export const WIRE_VERSION = 1;

// Note: Some fields (modelSettings, tools, outputType, handoffs) reference upstream types directly.
// We trust these to remain JSON-safe; if upstream adds a non-serializable field, bump WIRE_VERSION
// and either project or exclude.

/** Recursive JSON-safe type replacing upstream `unknown` fields on the wire. */
export type JsonValue = null | string | number | boolean | JsonValue[] | { [k: string]: JsonValue };

/** JSON-serializable projection of upstream `ModelRequest`, sent workflow → activity. */
export interface SerializedModelRequest {
__wireVersion: typeof WIRE_VERSION;
systemInstructions?: string;
input: JsonValue;
modelSettings: ModelSettings;
tools: SerializedTool[];
toolsExplicitlyProvided?: boolean;
outputType: SerializedOutputType;
handoffs: SerializedHandoff[];
prompt?: JsonValue;
previousResponseId?: string;
conversationId?: string;
// tracing is ModelTracing (boolean | 'enabled_without_data') — an enablement flag, not trace context.
// OTel span context for activity-under-generation nesting is tracked separately (see TemporalTracingProcessor).
tracing: JsonValue;
overridePromptModel?: boolean;
// Excluded by design (must stay in this comment for future contributors):
// signal — AbortSignal; not serializable. Temporal cancellation provides equivalent.
}

/** JSON-serializable projection of upstream `ModelResponse`, returned activity → workflow. */
export interface SerializedModelResponse {
__wireVersion: typeof WIRE_VERSION;
usage: JsonValue;
output: JsonValue[];
responseId?: string;
/**
* Provider-specific metadata. Upstream type is `Record<string, any>`.
*
* **Coercion warning:** Temporal's JSON codec serializes this field as-is. Non-JSON-primitive
* values (Date, Map, Set, class instances) will be silently coerced — e.g., Date becomes an
* ISO 8601 string. Code consuming this field on the workflow side will receive the coerced
* form, not the original type. If your model provider populates providerData with non-JSON
* types, handle the coerced representation explicitly.
*/
providerData?: Record<string, JsonValue>;
// All upstream ModelResponse fields are present, with types narrowed to JSON-safe equivalents (Usage → JsonValue, etc.).
}

/** Activity input envelope: model name + serialized request. */
export interface InvokeModelActivityInput {
modelName: string;
request: SerializedModelRequest;
}
39 changes: 39 additions & 0 deletions packages/openai-agents/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @temporalio/openai-agents — Temporal integration for the OpenAI Agents SDK.
*
* Deferred (not in this package):
* - StatefulMCPServerProvider — persistent MCP server connections across worker lifecycle
* - nexusOperationAsTool — TS SDK lacks executeNexusOperation; add when available
* - testing.AgentEnvironment — richer test harness beyond FakeModel
* - workflowFailureExceptionTypes registration (TS SDK doesn't support)
*/

// Main entry — all public exports (plugin, activities, workflow utilities, testing namespace, errors)

export { OpenAIAgentsPlugin } from './worker/plugin';
export type { OpenAIAgentsPluginOptions } from './worker/plugin';
export { toSerializedModelResponse } from './worker/activities';
export { StatelessMCPServerProvider } from './worker/mcp-provider';
export type { StatelessMCPServerFactory, MCPToolDefinition, MCPCallToolResult } from './worker/mcp-provider';
export {
WIRE_VERSION,
type SerializedModelRequest,
type SerializedModelResponse,
type InvokeModelActivityInput,
type JsonValue,
} from './common/serialized-model';
export type { ModelActivityOptions, ModelSummaryProvider, AgentInputItem } from './common/model-activity-options';
export { DEFAULT_MODEL_ACTIVITY_OPTIONS } from './common/model-activity-options';
export { ToolSerializationError } from './workflow/tools';
export type { ActivityToolDefinition, ActivityAsToolOptions, JsonObjectSchema } from './workflow/tools';
export type { StatelessMcpServerOptions, TemporalMCPServer, MCPPromptDefinition } from './workflow/mcp-client';
export {
isInWorkflow,
isReplaying,
TemporalTracingProcessor,
ensureTracingProcessorRegistered,
} from './workflow/tracing';
export type { TemporalTracingProcessorOptions } from './workflow/tracing';
export type { TemporalOpenAIRunnerOptions } from './workflow/runner';

export * as testing from './worker/testing';
9 changes: 9 additions & 0 deletions packages/openai-agents/src/testing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export {
FakeModel,
FakeModelProvider,
textResponse,
toolCallResponse,
handoffResponse,
multiToolCallResponse,
ResponseBuilders,
} from './worker/testing';
Loading
Loading