> ## Documentation Index
> Fetch the complete documentation index at: https://tracevell.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAI Agents SDK observability

> Trace OpenAI Agents SDK runs in TypeScript and Python: agent turns, tool calls, handoffs, and sub-agents in Tracevell's transcript view.

## Overview

Tracevell is an open-source, OpenTelemetry-native observability platform for the OpenAI Agents SDK ([Python](https://openai.github.io/openai-agents-python/), [TypeScript](https://openai.github.io/openai-agents-js/)). Trace, debug, and monitor every agent turn, tool call, handoff, and sub-agent with a single `Laminar.initialize()` call. Self-host via Helm or use managed cloud.

The OpenAI Agents SDK lets you build agent workflows with `Agent`, `Runner.run` / `run`, function tools, and handoffs between specialists. Tracevell hooks into the OpenAI Agents SDK's built-in trace processor to mirror every agent span into Tracevell, plus adds the system instructions so the full prompt is visible in the trace.

What Tracevell captures:

* The root agent workflow, each `agents.task`, and every `agents.turn` with the model, prompt, response, token counts, latency, and cost.
* Every `function_tool` invocation, with arguments and return value.
* Handoffs between agents, with the destination agent's turns nested under the handoff span.
* Agents invoked as tools (`Agent.as_tool` / `Agent.asTool`), with the sub-agent's full run nested under the tool call.
* Sandbox sessions (Python only) started via `SandboxRunConfig`, including manifest materialization and shell/file tool calls.
* Agent instructions prepended to the input messages on every LLM span.

## Getting started

> [tab] Python
>
> **1. Install**
>
> Ensure you have `lmnr` version `0.7.49` or higher:
>
> ```bash theme={null}
> pip install -U lmnr openai-agents
> ```
>
> **2. Set environment variables**
>
> ```bash theme={null}
> export LMNR_PROJECT_API_KEY=your-tracevell-project-api-key
> export OPENAI_API_KEY=your-openai-api-key
> ```
>
> **3. Initialize Tracevell**
>
> `Laminar.initialize()` auto-instruments the OpenAI Agents SDK when `openai-agents` is importable. No wrapping call is needed.
>
> ```python {4-6} theme={null}
> import asyncio
>
> from agents import Agent, Runner
> from lmnr import Tracevell, observe
>
> Laminar.initialize()
>
> @observe(name="math-homework")
> async def main():
>     agent = Agent(
>         name="MathHelper",
>         instructions="You are a patient math tutor. Explain each step clearly.",
>         model="gpt-5-mini",
>     )
>     result = await Runner.run(agent, "A train leaves Boston at 9am at 60 mph...")
>     print(result.final_output)
>
> if __name__ == "__main__":
>     asyncio.run(main())
> ```
>
> > [!NOTE]
> >
> > Wrapping your entry point in `@observe()` is optional but recommended: it creates a root span that captures inputs and outputs and makes the trace easy to find in the UI.

> [tab] TypeScript
>
> **1. Install**
>
> Ensure you are using `@lmnr-ai/lmnr` version `0.8.21` or higher:
>
> ```bash theme={null}
> npm install @lmnr-ai/lmnr@latest @openai/agents@latest
> # or
> pnpm add @lmnr-ai/lmnr@latest @openai/agents@latest
> ```
>
> **2. Set environment variables**
>
> ```bash theme={null}
> export LMNR_PROJECT_API_KEY=your-tracevell-project-api-key
> export OPENAI_API_KEY=your-openai-api-key
> ```
>
> **3. Initialize Tracevell**
>
> `Laminar.initialize()` auto-instruments `@openai/agents` when it is installed. No wrapping call is needed.
>
> ```typescript {2,4} theme={null}
> import { Agent, run } from '@openai/agents';
> import { Tracevell, observe } from '@lmnr-ai/lmnr';
>
> Laminar.initialize();
>
> await observe({ name: 'math-homework' }, async () => {
>   const agent = new Agent({
>     name: 'MathHelper',
>     instructions: 'You are a patient math tutor. Explain each step clearly.',
>     model: 'gpt-5-mini',
>   });
>   const result = await run(agent, 'A train leaves Boston at 9am at 60 mph...');
>   console.log(result.finalOutput);
> });
> ```
>
> > [!NOTE]
> >
> > If `@openai/agents` is imported before `Laminar.initialize()` in ESM (or if you set `"type": "module"` in `package.json`), pass the module explicitly so the models get patched:
> >
> > ```typescript theme={null}
> > import * as agents from '@openai/agents';
> > import { Tracevell } from '@lmnr-ai/lmnr';
> >
> > Laminar.initialize({ instrumentModules: { openAIAgents: agents } });
> > ```

## See what happened in a trace

Open the trace in Tracevell and you land on the transcript view: each turn reads as a conversation, with the prompt, the model's response, and any tool calls inline with their inputs and outputs. A tree of span names tells you the shape of the run; the transcript tells you what actually happened.

![OpenAI Agents SDK trace in Tracevell, transcript view](https://mintcdn.com/laminarai/RPYdoMdfsv6y-QUL/images/traces/openai-agents-sdk.png?fit=max\&auto=format\&n=RPYdoMdfsv6y-QUL\&q=85\&s=5fb69d5172a859932ecec72eacb6b05e)

Transcript view of a single-agent run. The agent's system instructions are prepended to the input, the model's full response is shown inline, and token counts and cost are attached to the LLM span.

More on the trace UX: [Viewing traces](https://tracevell.com/docs/platform/viewing-traces.md).

## Multi-agent runs with handoffs

The OpenAI Agents SDK models specialization through handoffs: a triage agent routes the user to a specialist via `handoff(other_agent)` / `handoff(otherAgent)`, and the OpenAI Agents SDK exposes each handoff as a `transfer_to_<agent>` tool call. In Tracevell, the destination agent's turns and tool calls nest under the handoff span, so you can follow the full multi-agent conversation in one trace.

> [tab] Python
>
> ```python theme={null}
> import asyncio
>
> from agents import Agent, Runner, function_tool, handoff
> from lmnr import Tracevell, observe
>
> Laminar.initialize()
>
>
> @function_tool
> def cancel_booking(confirmation_code: str) -> str:
>     return f"Booking {confirmation_code} cancelled. Refund in 5-7 business days."
>
>
> @function_tool
> def lookup_loyalty_balance(member_id: str) -> str:
>     return f"Member {member_id}: 48,200 points, Gold tier."
>
>
> booking_agent = Agent(
>     name="BookingAgent",
>     handoff_description="Handles cancellations and loyalty balance lookups.",
>     instructions=(
>         "You handle cancellations and loyalty questions. Use cancel_booking for "
>         "cancellations and lookup_loyalty_balance for points and tier."
>     ),
>     tools=[cancel_booking, lookup_loyalty_balance],
>     model="gpt-5-mini",
> )
>
> triage_agent = Agent(
>     name="TriageAgent",
>     instructions=(
>         "Route the user to the correct specialist. For cancellations or loyalty, "
>         "hand off to BookingAgent. Do not answer specialist topics yourself."
>     ),
>     handoffs=[handoff(booking_agent)],
>     model="gpt-5-mini",
> )
>
>
> @observe(name="airline-support")
> async def main():
>     result = await Runner.run(
>         triage_agent,
>         "Cancel my booking Z9X7K2 and look up loyalty for member M-88421.",
>     )
>     print(result.final_output)
>
>
> if __name__ == "__main__":
>     asyncio.run(main())
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { Agent, handoff, run, tool } from '@openai/agents';
> import { Tracevell, observe } from '@lmnr-ai/lmnr';
> import { z } from 'zod';
>
> Laminar.initialize();
>
> const cancelBooking = tool({
>   name: 'cancel_booking',
>   description: 'Cancel a booking by confirmation code.',
>   parameters: z.object({ confirmation_code: z.string() }),
>   execute: async ({ confirmation_code }) =>
>     `Booking ${confirmation_code} cancelled. Refund in 5-7 business days.`,
> });
>
> const lookupLoyaltyBalance = tool({
>   name: 'lookup_loyalty_balance',
>   description: 'Look up a loyalty member balance and tier.',
>   parameters: z.object({ member_id: z.string() }),
>   execute: async ({ member_id }) => `Member ${member_id}: 48,200 points, Gold tier.`,
> });
>
> const bookingAgent = new Agent({
>   name: 'BookingAgent',
>   handoffDescription: 'Handles cancellations and loyalty balance lookups.',
>   instructions:
>     'You handle cancellations and loyalty questions. Use cancel_booking for ' +
>     'cancellations and lookup_loyalty_balance for points and tier.',
>   tools: [cancelBooking, lookupLoyaltyBalance],
>   model: 'gpt-5-mini',
> });
>
> const triageAgent = new Agent({
>   name: 'TriageAgent',
>   instructions:
>     'Route the user to the correct specialist. For cancellations or loyalty, ' +
>     'hand off to BookingAgent. Do not answer specialist topics yourself.',
>   handoffs: [handoff(bookingAgent)],
>   model: 'gpt-5-mini',
> });
>
> await observe({ name: 'airline-support' }, async () => {
>   const result = await run(
>     triageAgent,
>     'Cancel my booking Z9X7K2 and look up loyalty for member M-88421.',
>   );
>   console.log(result.finalOutput);
> });
> ```

The resulting trace shows the triage turn, the handoff, and the booking agent's tool calls in a single conversation:

![OpenAI Agents SDK multi-agent trace in Tracevell, transcript view](https://mintcdn.com/laminarai/RPYdoMdfsv6y-QUL/images/traces/openai-agents-sdk-multi-agent.png?fit=max\&auto=format\&n=RPYdoMdfsv6y-QUL\&q=85\&s=1770a02ec4d0c2e50572fa0c9076e3d8)

Transcript view of a multi-agent handoff. The first LLM call is the TriageAgent deciding to hand off; the collapsed BookingAgent span shows the sub-agent's final output.

Switch to tree view when you want the full hierarchy at a glance:

![OpenAI Agents SDK multi-agent trace in Tracevell, tree view](https://mintcdn.com/laminarai/RPYdoMdfsv6y-QUL/images/traces/openai-agents-sdk-multi-agent-tree.png?fit=max\&auto=format\&n=RPYdoMdfsv6y-QUL\&q=85\&s=2d96cf98ff2e13520c1f4e9c1ceef319)

Tree view of the same run: TriageAgent's LLM turn, the agents.handoff span, then BookingAgent with its own turns, cancel\_booking, and lookup\_loyalty\_balance tool spans nested underneath.

## Sub-agents as tools

Handoffs transfer the conversation to another agent. **Sub-agents as tools** keep the orchestrator in control: `Agent.as_tool` (Python) and `Agent.asTool` (TypeScript) wrap a specialist as a `FunctionTool` that the parent agent can call like any other tool, then synthesize the sub-agent's output back into the parent's turn. Tracevell nests the sub-agent's full run (its own turns and tool calls) directly under the parent's tool-call span, so you can read the orchestrator-plus-specialist flow in a single trace.

> [tab] Python
>
> ```python theme={null}
> import asyncio
>
> from agents import Agent, Runner, function_tool
> from lmnr import Tracevell, observe
>
> Laminar.initialize()
>
>
> @function_tool
> def search_flights(origin: str, destination: str) -> str:
>     return f"Flights from {origin} to {destination}: UA123 08:00, DL456 14:30."
>
>
> flight_agent = Agent(
>     name="FlightAgent",
>     instructions="Find flights matching the user's origin and destination.",
>     tools=[search_flights],
>     model="gpt-5-mini",
> )
>
> summary_agent = Agent(
>     name="SummaryAgent",
>     instructions="Summarize the user's itinerary in a single short paragraph.",
>     model="gpt-5-mini",
> )
>
> concierge = Agent(
>     name="Concierge",
>     instructions=(
>         "You are a travel concierge. Call book_flight to look up flights, "
>         "then call write_summary to draft a one-paragraph itinerary."
>     ),
>     tools=[
>         flight_agent.as_tool(
>             tool_name="book_flight",
>             tool_description="Search for flights between two cities.",
>         ),
>         summary_agent.as_tool(
>             tool_name="write_summary",
>             tool_description="Write a one-paragraph summary of an itinerary.",
>         ),
>     ],
>     model="gpt-5-mini",
> )
>
>
> @observe(name="travel-concierge")
> async def main():
>     result = await Runner.run(
>         concierge,
>         "I'm flying SFO to JFK next Tuesday. Book something in the morning and summarize it.",
>     )
>     print(result.final_output)
>
>
> if __name__ == "__main__":
>     asyncio.run(main())
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { Agent, run, tool } from '@openai/agents';
> import { Tracevell, observe } from '@lmnr-ai/lmnr';
> import { z } from 'zod';
>
> Laminar.initialize();
>
> const searchFlights = tool({
>   name: 'search_flights',
>   description: 'Search for flights between two cities.',
>   parameters: z.object({ origin: z.string(), destination: z.string() }),
>   execute: async ({ origin, destination }) =>
>     `Flights from ${origin} to ${destination}: UA123 08:00, DL456 14:30.`,
> });
>
> const flightAgent = new Agent({
>   name: 'FlightAgent',
>   instructions: "Find flights matching the user's origin and destination.",
>   tools: [searchFlights],
>   model: 'gpt-5-mini',
> });
>
> const summaryAgent = new Agent({
>   name: 'SummaryAgent',
>   instructions: "Summarize the user's itinerary in a single short paragraph.",
>   model: 'gpt-5-mini',
> });
>
> const concierge = new Agent({
>   name: 'Concierge',
>   instructions:
>     'You are a travel concierge. Call book_flight to look up flights, ' +
>     'then call write_summary to draft a one-paragraph itinerary.',
>   tools: [
>     flightAgent.asTool({
>       toolName: 'book_flight',
>       toolDescription: 'Search for flights between two cities.',
>     }),
>     summaryAgent.asTool({
>       toolName: 'write_summary',
>       toolDescription: 'Write a one-paragraph summary of an itinerary.',
>     }),
>   ],
>   model: 'gpt-5-mini',
> });
>
> await observe({ name: 'travel-concierge' }, async () => {
>   const result = await run(
>     concierge,
>     "I'm flying SFO to JFK next Tuesday. Book something in the morning and summarize it.",
>   );
>   console.log(result.finalOutput);
> });
> ```

In tree view, the Concierge turn calls `book_flight` and `write_summary`; each call expands into the sub-agent's own turns, tool calls, and LLM spans, rather than being flattened to a single tool-return value.

> [!NOTE]
>
> Choose between handoffs and sub-agents-as-tools based on who owns the reply: handoffs hand the conversation to the specialist and its answer is returned to the user; sub-agents-as-tools return a structured result back to the orchestrator, which then keeps writing the user-facing response.

## Running agents in sandboxes (Python)

The Python SDK can execute an agent against a sandboxed workspace (locally on your machine or on a remote provider like E2B, Modal, Daytona, Runloop, Cloudflare, Vercel, or Blaxel) via `SandboxAgent` and `SandboxRunConfig`. Tracevell instruments sandbox sessions and surfaces each manifest materialization step, shell exec, and file read as a span, so you can see exactly what the agent did inside the sandbox alongside the LLM turns that drove it.

> [!NOTE]
>
> Sandboxes are Python-only. The TypeScript SDK does not expose `SandboxAgent` / `SandboxRunConfig` at the time of writing.

### Local sandbox (UnixLocal)

`UnixLocalSandboxClient` runs the agent against your host filesystem with no extra infrastructure: a good starting point for development and tests.

```python theme={null}
import asyncio

from agents import Runner
from agents.run_config import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.entries import GitRepo
from agents.sandbox.sandboxes import UnixLocalSandboxClient
from lmnr import Tracevell, observe

Laminar.initialize()


@observe(name="sandbox-repo-summary")
async def main():
    agent = SandboxAgent(
        name="WorkspaceAssistant",
        instructions="Inspect the sandbox workspace before answering.",
        default_manifest=Manifest(
            entries={
                "repo": GitRepo(repo="openai/openai-agents-python", ref="main"),
            }
        ),
        model="gpt-5-mini",
    )

    result = await Runner.run(
        agent,
        "Inspect the repo README and summarize what this project does.",
        run_config=RunConfig(
            sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()),
        ),
    )
    print(result.final_output)


if __name__ == "__main__":
    asyncio.run(main())
```

### Remote sandbox (E2B)

Swap the client for any remote provider to run the same agent on managed infrastructure. The example below uses [E2B](https://e2b.dev); the pattern is identical for `ModalSandboxClient`, `DaytonaSandboxClient`, `RunloopSandboxClient`, `CloudflareSandboxClient`, `VercelSandboxClient`, and `BlaxelSandboxClient` under `agents.extensions.sandbox.<provider>`.

```bash theme={null}
pip install -U 'openai-agents[e2b]'
export E2B_API_KEY=your-e2b-api-key
```

```python theme={null}
import asyncio

from agents import Runner
from agents.extensions.sandbox.e2b import E2BSandboxClient
from agents.run_config import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.entries import GitRepo
from lmnr import Tracevell, observe

Laminar.initialize()


@observe(name="sandbox-repo-summary-e2b")
async def main():
    agent = SandboxAgent(
        name="WorkspaceAssistant",
        instructions="Inspect the sandbox workspace before answering.",
        default_manifest=Manifest(
            entries={
                "repo": GitRepo(repo="openai/openai-agents-python", ref="main"),
            }
        ),
        model="gpt-5-mini",
    )

    result = await Runner.run(
        agent,
        "Inspect the repo README and summarize what this project does.",
        run_config=RunConfig(
            sandbox=SandboxRunConfig(client=E2BSandboxClient()),
        ),
    )
    print(result.final_output)


if __name__ == "__main__":
    asyncio.run(main())
```

In either case the trace includes a `sandbox.start` / `sandbox.cleanup` span pair around the agent's work, the manifest materialization spans that pulled `repo` into the workspace, and every shell or file operation the agent ran via its sandbox tools.

## Track outcomes with Signals

Traces answer *what happened on this run*. **[Signals](https://tracevell.com/docs/signals/introduction.md) answer the cross-trace question**: *how often does the triage agent answer a cancellation itself instead of handing off, when do tool calls return errors, how many runs exceed three turns without a final answer*. A Signal pairs a plain-language instruction with a JSON output schema. Tracevell runs it live on new traces (Triggers) or backfills it across history (Jobs) and records a structured event every time it matches. From there you [query](https://tracevell.com/docs/platform/sql-editor.md), [cluster](https://tracevell.com/docs/signals/clusters.md), and [alert](https://tracevell.com/docs/signals/alerts.md) on events across every trace.

> [!NOTE]
>
> Every new project ships with a **Failure Detector** Signal that categorizes issues on any trace over 1000 tokens. Open it from the Signals sidebar to see events as soon as your agent traces arrive.

## Query across traces

* **[SQL editor](https://tracevell.com/docs/platform/sql-editor.md)** for ad-hoc queries across traces, spans, signals, and evals.
* **SQL API** for programmatic access from scripts and pipelines.
* **[CLI](https://tracevell.com/docs/platform/cli.md)** (`lmnr-cli sql query`) for terminal-driven queries and piping JSON into shell tools or coding agents.
* **[MCP server](https://tracevell.com/docs/platform/mcp.md)** to query Tracevell directly from Claude Code, Cursor, Codex, or any MCP-aware client.

## Troubleshooting

> [accordion] I don't see any traces in Tracevell
>
> * Confirm `LMNR_PROJECT_API_KEY` is set in the same process that runs the OpenAI Agents SDK.
> * Python: `openai-agents` must be importable when `Laminar.initialize()` runs. Install with `pip install openai-agents`. The integration requires `openai-agents >= 0.7.0` and `lmnr >= 0.7.49`.
> * TypeScript: `@openai/agents` must be installed in the same workspace. The integration requires `@lmnr-ai/lmnr >= 0.8.21`. If you import `@openai/agents` before `Laminar.initialize()` in ESM, pass the module via `instrumentModules: { openAIAgents: agents }`.

> [accordion] Handoffs aren't nested under the source agent
>
> The SDK emits a `transfer_to_<agent>` tool call followed by an `agents.handoff` span, and the destination agent's work lands as a sibling under the same parent task. Open the trace in tree view to see the full structure.

> [accordion] Sub-agents invoked via as\_tool / asTool look flat
>
> Make sure the sub-agent is registered in the orchestrator's `tools` array via `.as_tool(...)` / `.asTool(...)`, not re-exported as a plain function. Each `as_tool` call produces a `FunctionTool` whose `execute` spins up a nested `Runner` run; Tracevell nests the inner run under the parent tool-call span only when the wrapper is the one invoked.

> [accordion] Self-hosting Tracevell
>
> Set `base_url` and the ports of your instance when initializing. For a local OSS deployment:
>
> ```python theme={null}
> Laminar.initialize(
>     base_url="http://localhost",
>     http_port=8000,
>     grpc_port=8001,
> )
> ```
>
> ```typescript theme={null}
> Laminar.initialize({
>   baseUrl: 'http://localhost',
>   httpPort: 8000,
>   port: 8001,
> });
> ```

## What's next

> [card] [Viewing traces](https://tracevell.com/docs/platform/viewing-traces.md)
>
> Read the transcript view, filter, and search across traces.

> [card] [Signals](https://tracevell.com/docs/signals/introduction.md)
>
> Detect behaviors and failures across every run, then query, cluster, and alert on them.

> [card] [SQL editor and MCP server](https://tracevell.com/docs/platform/sql-editor.md)
>
> Query traces programmatically from the UI, API, or your IDE.

> [card] [Tracing structure](https://tracevell.com/docs/tracing/structure/overview.md)
>
> Sessions, metadata, and tags for deeper control.

## Related integrations

> [card] [OpenAI](https://tracevell.com/docs/integrations/openai.md)
>
> Trace the OpenAI SDK directly in TypeScript and Python.

> [card] [Google ADK](https://tracevell.com/docs/integrations/google-adk.md)
>
> Trace Google ADK agents, Gemini calls, and tool calls.

> [card] [Microsoft Agent Framework](https://tracevell.com/docs/integrations/microsoft-agent-framework.md)
>
> Trace Microsoft Agent Framework agents, tools, and workflows.

> [card] [Pydantic AI](https://tracevell.com/docs/integrations/pydantic-ai.md)
>
> Trace Pydantic AI agent runs and typed tool calls.

> [card] [Claude Agent SDK](https://tracevell.com/docs/integrations/claude-agent-sdk.md)
>
> Trace Claude Agent SDK runs and their subagents.

> [card] [All integrations](https://tracevell.com/docs/integrations.md)
>
> Browse every provider, framework, coding agent, and browser integration Tracevell supports.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.