From b977c4281544053bab4ae6630c137b21139d245d Mon Sep 17 00:00:00 2001 From: Aikins Laryea Date: Thu, 5 Nov 2026 15:14:33 +0000 Subject: [PATCH 1/3] docs(integrations): add the openai agents sdk guide Adds a guide for @telemetry-dev/openai-agents, the trace processor for the TypeScript OpenAI Agents SDK, lists it under Frameworks and on the integrations index, and links it from the OpenAI guide with a warning not to combine the two. --- components/IntegrationTitleMark.astro | 1 + docs/integrations/(frameworks)/meta.ts | 1 + .../(frameworks)/openai-agents.mdx | 184 ++++++++++++++++++ .../integrations/(model-providers)/openai.mdx | 4 +- docs/integrations/index.mdx | 3 +- test/routing.test.js | 1 + 6 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 docs/integrations/(frameworks)/openai-agents.mdx diff --git a/components/IntegrationTitleMark.astro b/components/IntegrationTitleMark.astro index 261977d..8b5dda0 100644 --- a/components/IntegrationTitleMark.astro +++ b/components/IntegrationTitleMark.astro @@ -22,6 +22,7 @@ const logos: Record = { "/integrations/mcp": "model-context-protocol", "/integrations/omp": "oh-my-pi", "/integrations/openai": "openai", + "/integrations/openai-agents": "openai", "/integrations/openclaw": "openclaw", "/integrations/opencode": "opencode", "/integrations/openrouter": "openrouter", diff --git a/docs/integrations/(frameworks)/meta.ts b/docs/integrations/(frameworks)/meta.ts index 925e127..da81c4b 100644 --- a/docs/integrations/(frameworks)/meta.ts +++ b/docs/integrations/(frameworks)/meta.ts @@ -8,6 +8,7 @@ export default defineMeta({ "langchain-langgraph", "crewai", "pydantic-ai", + "openai-agents", "eve", "mcp", ], diff --git a/docs/integrations/(frameworks)/openai-agents.mdx b/docs/integrations/(frameworks)/openai-agents.mdx new file mode 100644 index 0000000..90c71d1 --- /dev/null +++ b/docs/integrations/(frameworks)/openai-agents.mdx @@ -0,0 +1,184 @@ +--- +title: OpenAI Agents SDK +description: Trace TypeScript OpenAI Agents SDK runs, model calls, tool calls, and sandbox commands with their exit codes. +lastModified: 2026-11-05 +seo: + title: "OpenAI Agents SDK Tracing and Observability" + description: "Send TypeScript OpenAI Agents SDK traces to telemetry.dev: agents, turns, model calls with token usage and cost, tool calls, and sandbox commands with exit codes and sandbox IDs." +--- + +`@telemetry-dev/openai-agents` sends the runs of the TypeScript [OpenAI Agents SDK](https://openai.github.io/openai-agents-js/) to telemetry.dev: the agents and their turns, each model call with its token usage, each tool call, and each command that a sandbox agent ran, with its exit code and the sandbox it ran in. + +The package is a trace processor for the Agents SDK. It reads the spans the SDK already records, so you don't change your agents or tools. + +## Install + +```package-install +npm i @telemetry-dev/openai-agents @openai/agents +``` + +The package supports `@openai/agents` 0.21.0 and later 0.x releases, and needs Node.js 22 or later, the Agents SDK's minimum. + +## Set the API key + +Create a project at [telemetry.dev](https://telemetry.dev), then create an API key on the project's **Setup** page. Set it where your app runs: + +```sh +export TELEMETRY_DEV_API_KEY=td_live_... +export TELEMETRY_DEV_ENVIRONMENT=development +export OTEL_SERVICE_NAME=support-agent +``` + +Without an API key, the processor does nothing. + +## Add the processor + +Register the processor once, before the first run: + +```ts agent.ts +import { addTraceProcessor, Agent, run } from "@openai/agents"; +import { telemetryDevProcessor } from "@telemetry-dev/openai-agents"; + +addTraceProcessor(telemetryDevProcessor()); + +const agent = new Agent({ name: "Assistant", instructions: "Answer briefly." }); +await run(agent, "What is OpenTelemetry?"); +``` + +`addTraceProcessor` keeps the Agents SDK's default exporter, so traces also reach the OpenAI Traces dashboard. To send them only to telemetry.dev, replace the default exporter instead: + +```ts agent.ts +import { setTraceProcessors } from "@openai/agents"; +import { telemetryDevProcessor } from "@telemetry-dev/openai-agents"; + +setTraceProcessors([telemetryDevProcessor()]); +``` + +`setTraceProcessors` is also how you keep content away from OpenAI. With `addTraceProcessor`, the OpenAI exporter still receives tool arguments and results, Chat Completions inputs and outputs, sandbox commands and working directories, and trace metadata. + +## Check the trace + +Run your agent once and open [Traces](/platform/traces) in your project. A run contains: + +- A root span named after the run's workflow, `Agent workflow` unless you set `workflowName`, and a `task {workflowName}` span under it +- A span for each agent, and a `turn N` span for each of its turns +- A `chat {model}` span for each model call, with its token usage, which telemetry.dev prices +- A span for each tool call, named after the tool +- Handoff, guardrail, and MCP tool-listing spans when the run has them + +A run without a `groupId` is one trace. When runs set a `groupId`, the processor sends it as the session ID (`gen_ai.conversation.id`), and the runs of that `groupId` land in [one trace per session](/sdk/typescript#one-trace-per-session), each run as a root span. + +In short-lived runtimes such as serverless functions, flush at the end of each request: + +```ts +import { getGlobalTraceProvider } from "@openai/agents"; + +await getGlobalTraceProvider().forceFlush(); +``` + +A run that throws never ends its Agents SDK trace. The processor ends its root span after one minute without new spans, or at the next `forceFlush` or `shutdown` of the trace provider. + +## Trace sandbox agents + +Sandbox runs need no extra setup. Each `sandbox.*` span of the Agents SDK, such as `sandbox.create_session` and `sandbox.exec`, arrives with: + +| Attribute | Value | +| --- | --- | +| `sandbox.operation` | The operation, such as `create_session` or `exec` | +| `sandbox.backend` | The sandbox backend, such as `e2b` or `modal`, when the run created or resumed its session | +| `sandbox.id` | The provider's sandbox ID, when you wrap the client as shown below | + +The `exec_command` and `write_stdin` tool spans get `process.exit.code` once the command has exited. A command that exits with a non-zero code is not marked as an error, because many commands, such as `grep`, exit non-zero without failing. + +### Record the provider's sandbox ID + +Wrap the sandbox client with `instrumentSandboxClient()` to record the ID that the provider uses in its own dashboard, logs, and webhooks. The hosted clients are in `@openai/agents-extensions`, and most also need their provider's SDK, such as `e2b` for E2B: + +```package-install +npm i @openai/agents-extensions e2b +``` + +The Docker and local clients come with `@openai/agents`. + +```ts agent.ts +import { run } from "@openai/agents"; +import { SandboxAgent } from "@openai/agents/sandbox"; +import { E2BSandboxClient } from "@openai/agents-extensions/sandbox/e2b"; +import { instrumentSandboxClient } from "@telemetry-dev/openai-agents"; + +const agent = new SandboxAgent({ name: "Coder", instructions: "Run the tests." }); +const client = instrumentSandboxClient(new E2BSandboxClient()); +await run(agent, "Run the tests.", { sandbox: { client } }); +``` + +The session, command, and tool spans of that agent then carry `sandbox.id`. The wrapper reads the ID from the session the client creates or resumes, and not every backend keeps one there: + +| Backend | Client | `sandbox.id` | +| --- | --- | --- | +| E2B | `E2BSandboxClient` | The E2B sandbox ID | +| Modal | `ModalSandboxClient` | The Modal sandbox ID | +| Daytona | `DaytonaSandboxClient` | The Daytona sandbox ID | +| Vercel | `VercelSandboxClient` | The Vercel sandbox ID | +| Cloudflare | `CloudflareSandboxClient` | The Cloudflare sandbox ID | +| Blaxel | `BlaxelSandboxClient` | Not recorded | +| Docker | `DockerSandboxClient` | Not recorded | +| Local | `UnixLocalSandboxClient` | Not recorded | + +All of these clients still get `sandbox.backend`. + +The backend and the ID come from the span that creates or resumes the session. A session that your app passes in with `sandbox: { session }`, or that a resumed run reuses while it is still live in the same process, has no such span, so the spans of that run have neither. + +## Control captured content + +The processor takes the [`init()` options](/sdk/typescript#configure-init) of `@telemetry-dev/sdk`. Use `captureInput` and `captureOutput` to keep content out of telemetry.dev, and `mask` to redact it: + +```ts +addTraceProcessor( + telemetryDevProcessor({ + captureInput: false, + captureOutput: false, + }), +); +``` + +These count as content: + +- Model inputs and outputs +- Tool arguments and results +- The command and working directory of `sandbox.exec`, in `process.command_line` and `process.working_directory` + +With capture off, telemetry.dev still receives span names and timing, model names, response IDs, token usage, turn numbers, agent, tool, handoff, and guardrail names, each agent's tools, handoffs, and output type, whether a guardrail triggered, the MCP server of a tool call and the number of tools an MCP server listed, exit codes, sandbox backends and IDs, error types, the run's `groupId` as the session ID, the Agents SDK trace ID, and the string, number, and boolean values of trace metadata and of the data of custom spans. Trace metadata and custom span data do not go through `mask`. + +Keep the Agents SDK's `traceIncludeSensitiveData` on, which is its default. With it off, the SDK leaves token usage and tool output off its spans, and the Responses API model leaves off the model name too, so telemetry.dev cannot price model calls or read exit codes. Turn content off with `captureInput` and `captureOutput` instead. + +## Don't also instrument the OpenAI client + +The processor already records each model call as a `chat` span. Don't also instrument the OpenAI client that the Agents SDK uses with [`@telemetry-dev/openai`](/integrations/openai): neither a client wrapped with `wrapOpenAI()` and passed to `setDefaultOpenAIClient`, nor `instrumentOpenAI()`, which instruments every client in the process. Each model call would be recorded and priced twice. + +## Options + +`telemetryDevProcessor()` accepts every [`init()` option](/sdk/typescript#configure-init) of `@telemetry-dev/sdk`. The variables you are most likely to set: + +| Variable | Default | Description | +| --- | --- | --- | +| `TELEMETRY_DEV_API_KEY` | None | Ingest API key. Without a key, the processor does nothing. | +| `TELEMETRY_DEV_BASE_URL` | `https://api.telemetry.dev` | Ingest base URL | +| `TELEMETRY_DEV_ENVIRONMENT` | `production` | Environment on exported telemetry | +| `OTEL_SERVICE_NAME` | SDK default | Service name | + +The first processor created in a process sets up the SDK, and later processors ignore their options. Pass SDK options to the processor rather than calling `init()` yourself: the processor replaces a client that an earlier `init()` call made. + +## Limitations + +The Agents SDK shuts the trace provider down on `SIGTERM`, `SIGINT`, and unhandled rejections. The processor then ends every open span so nothing is lost if the process exits. A model call that was in progress at that moment is sent without its token usage. + +For the package source and changelog, see [`@telemetry-dev/openai-agents` on GitHub](https://github.com/telemetry-dev/telemetry/tree/main/packages/openai-agents). + + + + Open the trace explorer and examine the span waterfall. + + + Set the telemetry.dev API key and service name. + + diff --git a/docs/integrations/(model-providers)/openai.mdx b/docs/integrations/(model-providers)/openai.mdx index 02f7c00..a008a4e 100644 --- a/docs/integrations/(model-providers)/openai.mdx +++ b/docs/integrations/(model-providers)/openai.mdx @@ -1,7 +1,7 @@ --- title: OpenAI description: Trace OpenAI chat, Responses API, embedding, media, batch, and stream calls from TypeScript or Python. -lastModified: 2026-10-26 +lastModified: 2026-11-05 seo: title: "OpenAI API Tracing and Token Usage" description: "Trace OpenAI chat completions, Responses API, embedding, and stream calls from TypeScript or Python, with token usage, errors, and time to first chunk." @@ -9,6 +9,8 @@ seo: Wrap your OpenAI client, and each call you make with it becomes a span in telemetry.dev. Your calls and their return values don't change. +To trace runs of the TypeScript OpenAI Agents SDK, see [OpenAI Agents SDK](/integrations/openai-agents); don't wrap the client the Agents SDK uses or call `instrumentOpenAI()` in a process that runs the Agents SDK, or each model call is recorded twice. + ## What you get - Chat messages, Responses API input, system instructions, model parameters, and embedding input diff --git a/docs/integrations/index.mdx b/docs/integrations/index.mdx index 7a4fa6b..174431f 100644 --- a/docs/integrations/index.mdx +++ b/docs/integrations/index.mdx @@ -2,7 +2,7 @@ title: Integrations description: Choose a telemetry.dev integration for your framework, model provider, coding agent, SDK, or OpenTelemetry setup. sidebar: { label: "Overview" } -lastModified: 2026-10-27 +lastModified: 2026-11-05 seo: title: "LLM and AI Agent Integrations" description: "Connect telemetry.dev to the Vercel AI SDK, LangChain, OpenAI, Anthropic, LiteLLM, Claude Code, Cursor, opencode, and more through SDKs or native OpenTelemetry." @@ -23,6 +23,7 @@ Pick the guide for the code that makes your model calls: Trace chains, model calls, and tools through OpenTelemetry. Export CrewAI and model spans with OpenLIT. Use Pydantic AI's native OpenTelemetry instrumentation. + Trace TypeScript Agents SDK runs, tool calls, and sandbox commands. Trace Eve agent-framework runs. Trace official TypeScript MCP SDK v2 transports. diff --git a/test/routing.test.js b/test/routing.test.js index 9c3ea7c..c68b1e4 100644 --- a/test/routing.test.js +++ b/test/routing.test.js @@ -24,6 +24,7 @@ const integrationMarks = { "/docs/integrations/mcp": "/docs/integrations/model-context-protocol.svg", "/docs/integrations/omp": "/docs/integrations/oh-my-pi.svg", "/docs/integrations/openai": "/docs/integrations/openai.svg", + "/docs/integrations/openai-agents": "/docs/integrations/openai.svg", "/docs/integrations/openclaw": "/docs/integrations/openclaw.svg", "/docs/integrations/opencode": "/docs/integrations/opencode.svg", "/docs/integrations/openrouter": "/docs/integrations/openrouter.svg", From 462c83a7284427a164ff37432d4c166d853d5f1e Mon Sep 17 00:00:00 2001 From: Aikins Laryea Date: Thu, 5 Nov 2026 16:37:28 +0000 Subject: [PATCH 2/3] docs(integrations): explain how agents runs group into traces Cover runs inside an active span, propagateAttributes() sessions, and process sessions set on the processor. --- docs/integrations/(frameworks)/openai-agents.mdx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/integrations/(frameworks)/openai-agents.mdx b/docs/integrations/(frameworks)/openai-agents.mdx index 90c71d1..2ba833e 100644 --- a/docs/integrations/(frameworks)/openai-agents.mdx +++ b/docs/integrations/(frameworks)/openai-agents.mdx @@ -66,7 +66,12 @@ Run your agent once and open [Traces](/platform/traces) in your project. A run c - A span for each tool call, named after the tool - Handoff, guardrail, and MCP tool-listing spans when the run has them -A run without a `groupId` is one trace. When runs set a `groupId`, the processor sends it as the session ID (`gen_ai.conversation.id`), and the runs of that `groupId` land in [one trace per session](/sdk/typescript#one-trace-per-session), each run as a root span. +How runs group into traces: + +- A run is one trace by default. +- A run that starts inside an active OpenTelemetry span, such as an HTTP request span, joins that span's trace. +- Runs that share a `groupId` share a session: the processor sends the `groupId` as the session ID (`gen_ai.conversation.id`). Runs inside the same [`propagateAttributes({ sessionId })`](/sdk/typescript#add-user-session-and-metadata) session (from `@telemetry-dev/sdk`) do too; a `groupId` takes precedence. Outside an active span, the runs of a session land in [one trace per session](/sdk/typescript#one-trace-per-session), each run as a root span. +- With `telemetryDevProcessor({ sessionMode: "process" })`, runs without a session of their own share the process's [generated session](/sdk/typescript#process-sessions) in the same way. Use this in a CLI or script that holds one conversation per process, not in a long-running server. In short-lived runtimes such as serverless functions, flush at the end of each request: From 4b5d31a53c45d5e57354e42d8b52e64d975f40c3 Mon Sep 17 00:00:00 2001 From: Aikins Laryea Date: Thu, 5 Nov 2026 16:54:43 +0000 Subject: [PATCH 3/3] docs(integrations): list every session source sent with capture off Name propagateAttributes() and process sessions, and the user ID and metadata that propagateAttributes() sends. --- docs/integrations/(frameworks)/openai-agents.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/(frameworks)/openai-agents.mdx b/docs/integrations/(frameworks)/openai-agents.mdx index 2ba833e..f190c78 100644 --- a/docs/integrations/(frameworks)/openai-agents.mdx +++ b/docs/integrations/(frameworks)/openai-agents.mdx @@ -152,7 +152,7 @@ These count as content: - Tool arguments and results - The command and working directory of `sandbox.exec`, in `process.command_line` and `process.working_directory` -With capture off, telemetry.dev still receives span names and timing, model names, response IDs, token usage, turn numbers, agent, tool, handoff, and guardrail names, each agent's tools, handoffs, and output type, whether a guardrail triggered, the MCP server of a tool call and the number of tools an MCP server listed, exit codes, sandbox backends and IDs, error types, the run's `groupId` as the session ID, the Agents SDK trace ID, and the string, number, and boolean values of trace metadata and of the data of custom spans. Trace metadata and custom span data do not go through `mask`. +With capture off, telemetry.dev still receives span names and timing, model names, response IDs, token usage, turn numbers, agent, tool, handoff, and guardrail names, each agent's tools, handoffs, and output type, whether a guardrail triggered, the MCP server of a tool call and the number of tools an MCP server listed, exit codes, sandbox backends and IDs, error types, the session ID (such as the run's `groupId`, a `propagateAttributes()` session, or the process session), the user ID and metadata of `propagateAttributes()`, the Agents SDK trace ID, and the string, number, and boolean values of trace metadata and of the data of custom spans. Trace metadata, custom span data, and `propagateAttributes()` metadata do not go through `mask`. Keep the Agents SDK's `traceIncludeSensitiveData` on, which is its default. With it off, the SDK leaves token usage and tool output off its spans, and the Responses API model leaves off the model name too, so telemetry.dev cannot price model calls or read exit codes. Turn content off with `captureInput` and `captureOutput` instead.