withAgent function creates an agent span and gives you an AgentContext object to log planning steps, instrument tool calls, and record handoffs to other agents. In Python, with_agent is a context manager that does the same thing. All LLM calls made inside the block automatically inherit the agent’s trace context — no additional wiring is required.
The withAgent / with_agent function
withAgent(options, fn) starts an agent trace, runs your workflow function with an AgentContext, and automatically links any nested LLM calls to the same trace. with_agent(...) does the same as a context manager, yielding the AgentContext to the with block.
Python’s
trace_tool() calls fn() synchronously and does not await it — pass a plain sync callable (a lambda or a regular function), as shown above. There is no async variant of trace_tool in the Python SDK today. TypeScript’s traceTool() accepts either a sync or async (Promise-returning) function and awaits it internally.AgentOptions / with_agent() parameters
In TypeScript, withAgent takes a single AgentOptions object. In Python, with_agent() takes the same fields as keyword arguments directly — there is no options object.
string
required
Display name for this agent. Appears in the trace view and the agent registry. Same in both SDKs.
string
default:"specialist"
Role label for this agent. Common values:
"coordinator", "specialist", "planner". Used to distinguish orchestrators from workers in multi-agent traces.TypeScript defaults to "specialist" when omitted. Python’s role keyword has no default — it stays None if you don’t pass it.string
default:"custom"
Framework powering this agent. Examples:
"custom", "langchain", "google-adk", "openai-assistants".TypeScript defaults to "custom" when omitted. Python’s framework keyword has no default — it stays None if you don’t pass it.ToolDefinition[]
Tool definitions available to this agent. Each object should have a
name and a description. These appear in the tool discovery view and are linked to tool call spans.In Python, pass a plain list of dict objects with the same name/description shape — there is no ToolDefinition class to construct.string
TypeScript only. Free-form version string attached to the agent span (
agent_version). There is no equivalent keyword on Python’s with_agent().string
TypeScript only. Free-form description attached to the agent span (
agent_description). There is no equivalent keyword on Python’s with_agent().Record<string, unknown>
TypeScript only. Arbitrary key-value metadata attached to the agent span. There is no equivalent keyword on Python’s
with_agent() — Python’s agent-start event does not carry a metadata field.AgentContext methods
The agent object passed to your function (TypeScript) or yielded by the with block (Python) exposes three methods: logPlan/log_plan, traceTool/trace_tool, and delegateTo/delegate_to.
agent.logPlan(steps) / agent.log_plan(steps)
Records a planning span with the list of steps the agent intends to take. Call this after deciding what to do and before executing.
agent.traceTool(name, args, fn) / agent.trace_tool(name, args, fn)
Wraps a function call and records a tool span containing the tool name, input arguments, and return value.
status: "error" and the error is re-thrown/re-raised in both SDKs.
Two implementation details differ between the SDKs’ tool spans:
- Tool definition lookup. TypeScript’s
traceTool()matchesnameagainst thetoolsarray passed towithAgentand attaches the matching entry astool_definitionson the span. Python’strace_tool()does not currently do this lookup, so the tool span it emits has notool_definitions. - Operation naming. The
operationfield on the emitted span istool.<name>in TypeScript but<agentName>.tool.<name>in Python.
tools_used, tool_call_args, tool_call_result (or error_message on failure), latency_ms, and status fields.agent.delegateTo(targetName, reason?) / agent.delegate_to(target_agent_name, reason=None)
Records a handoff span indicating that this agent is delegating work to another agent. The reason string is stored as delegation_reason on the span.
Span kinds emitted
withAgent/with_agent and their methods produce four distinct span kinds, each visible as a separate row in the trace flame graph:
Nested agents
Agents can be nested inside each other. The outer agent block sets a trace context that is automatically inherited by the inner one (viaAsyncLocalStorage-based context in TypeScript, contextvars in Python). The inner agent span records the outer agent’s ID as parent_agent_id, building a parent-child hierarchy visible in the agent registry.
TypeScript additionally sets
delegation_reason to "root" or "delegated" automatically on the agent-start event, based on whether a parent agent is already active in context. Python’s with_agent start event does not set delegation_reason — it is only populated there when you call agent.delegate_to().Cross-service agent context propagation
Nested agents work automatically when both agents run in the same process. When one agent delegates to another agent running in a different service — for example, a coordinator agent in one Node.js service calling an HTTP endpoint that runs a specialist agent in another service — the trace context has to be carried across the network boundary too, or the two services will show up as unrelated traces.injectAgentContext and extractAgentContext carry the current W3C trace context (plus optional delegation metadata) over outgoing HTTP headers via Baggage, so the receiving service’s spans link back to the sender’s trace.
Sending side — inject context into outgoing request headers before calling the downstream agent:
TypeScript
TypeScript
Record<string, string>
required
On the sending side, an outgoing headers object that is mutated in place — W3C
traceparent and baggage entries are added. On the receiving side, the incoming headers to read them back from.string
Injecting side only, optional. Stored in baggage as
agent.delegation.reason — why the work is being handed off to the downstream service.string
Injecting side only, optional. Stored in baggage as
agent.task — a description of the task being delegated.extractAgentContext(headers) returns an OpenTelemetry Context. Activate it with context.with() as shown above, or pass it as the third argument to tracer.startSpan(), so that any spans created on the receiving side — including a nested withAgent — attach to the sender’s trace instead of starting a new one.
Python: trace_tool_fn (Python-only)
The Python SDK additionally exports trace_tool_fn(name, fn), a helper that instruments a raw tool function once at registration time instead of wrapping each call inline. This is useful when handing plain functions to a tool registry (for example, an ADK function-calling loop) where you want every invocation traced without changing the call site.
Python
str
required
Tool name recorded on the emitted span (
operation: tool.<name>, tools_used: [name]).Callable
required
The raw function to instrument.
trace_tool_fn returns a wrapped callable with the same signature — calling it runs fn, enqueues a tool span with latency and status, and re-raises on error.Because
trace_tool_fn is not a method on an AgentContext, the span it emits picks up trace_id, span_id, user_id, session_id, and tags from whatever context is active (via get_current_context()) — including a surrounding with_agent block — but it does not set agent_id, agent_name, agent_role, or agent_framework on the event, even when called from inside one. Use AgentContext.trace_tool() if you need the tool span attributed to a specific agent.trace_tool_fn is Python-only — there is no equivalent in the TypeScript SDK. In TypeScript, use agent.traceTool() (documented above) to wrap a tool call inline inside a withAgent block.
