Skip to content

feat(server-utils): Parent the tool spans of an Anthropic session runner under one span per session #25768

Open

Description

@RulaKhaled

#25554 gives each tool call of a Managed Agents session runner an execute_tool span, but nothing parents them. When EnvironmentWorker drives the runners from its own poll loop (lib/environments/worker.mjs, for await (const _ of runner)), no user span is active, so every tool call becomes its own root segment. The agent.tool_use event has no session id and tool objects are shared across runners, so the spans can't be tied back to a session either. The same happens when beta.sessions.events.toolRunner() is iterated outside a span.

Anthropic's own OTel tracing already groups them. EnvironmentWorker runs each work item inside an anthropic.environment.work span that carries anthropic.session.id, and the session runner parents its tool spans to the context active when its loop starts. We could do the same: start one span per runner and set gen_ai.conversation.id from runner.sessionId, so its tool spans nest under it.

Open questions:

  • Which op and name the span should have, and whether invoke_agent fits a session runner.
  • Where to start it. The tool calls run from the runner's background loop, which begins on the first next(), not when [Symbol.asyncIterator]() is called.
  • How a span that lasts a whole session (hours, with reconnects and idle waits) behaves under span streaming.
  • What happens when the user already has a span active around the loop.

Follow-up to #25554, raised in review by @isaacs.

Activity

  1. linear-code commented on Oct 27, 2026

    @linear-code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions