Repository navigation
feat: preserve Claude subagent tool lineage for tracing - #10959
Merged
Merged
Conversation
ysolanky
approved these changes
Oct 11, 2026
Merged
kausmeows
added a commit
that referenced
this pull request
Oct 12, 2026
# Changelog ## New Features: - **Durable External Agent Sessions**: `ClaudeAgent` sessions now resume on any replica. The Claude SDK session id is stored on the Agno session (#10903), and with a database that supports transcript storage (`PostgresDb`, `AsyncPostgresDb`, `SqliteDb`, `AsyncSqliteDb`) every transcript line is mirrored into the new `agno_transcripts` table, so a later turn on another machine resumes from the database instead of a local file. A stored session that cannot be resumed falls back to a fresh SDK session seeded with the Agno history. Claude and Codex non-streaming runs now keep tool calls and results in run history. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/transcript_store.py). (#10904) - **Background Runs & Cancellation for External Agents**: `ClaudeAgent` and `CodexAgent` support `background=True`, durable-queue run ids, resumable indexed SSE and cancellation through `/agents/{id}/runs/{run_id}/cancel`, sharing the background task, slot and event-stream infrastructure of native agents. Claude uses a per-run SDK client and Codex retains turn handles, so the owning process can interrupt either SDK. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/background_cancel.py). (#10905) - **Step Replay for ClaudeAgent**: `ClaudeAgent.continue_run` / `acontinue_run` take the same `continue_from`, `fork` and `input` arguments as `Agent.continue_run`, with `stream=True` and `background=True`. Each tool result is a checkpoint listed by `GET /agents/{id}/runs/{run_id}/checkpoints`; continuing forks the stored SDK transcript at that message, so the model only remembers what happened up to it, and `continue_from="last_user"` replays the turn. A finished run is never rewritten in place: its continuation is a new sibling run. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/continue_from/01_continue_finished_run.py). (#10917) - **Retries for External Agents**: `ClaudeAgent` and `CodexAgent` take `retries`, `delay_between_retries` and `exponential_backoff` with the same defaults as `Agent`. A failed attempt is retried with the same run id and resumes the SDK session or Codex thread it started; limits (`max_turns`, `max_budget_usd`, Codex budget and usage limits) and permanent errors (authentication, billing, bad requests, context window, policy) are not retried. Tool calls from failed attempts are kept exactly once, streaming consumers get a `type: "retry"` warning event between attempts, a retried run records `metadata["attempts"]`, and the durable queue fails a job at once when the agent marks its error non-retryable, including across worker restarts. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/claude_retries.py). (#10937) - **Metrics for External Agents**: Every `ClaudeAgent` and `CodexAgent` run reports tokens, cache usage and cost (Claude) on `RunOutput.metrics`, with one entry per model in `metrics.details["model"]`, plus Agno's wall-clock `duration` and `time_to_first_token`. Completed runs accumulate into the session's `session_metrics`, which AgentOS shows in the sessions list, the session view and `/metrics`. Codex sums every model request in a turn, not only the last one. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/metrics_agentos.py). (#10925) - **Compaction for External Agents**: Claude Code's `/compact` works through the database-backed session store, so a compacted session resumes from its summary on any replica, and `CodexAgent.compact` / `acompact` compact the Codex thread behind an Agno session. Compaction refuses while a run on the session is in flight. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/codex/codex_compaction.py). (#10924) - **Stored Events for External Agents**: `BaseExternalAgent` takes `store_events` and `events_to_skip` like `Agent`. AgentOS turns it on for external agents, so the run view and `/agents/{id}/runs/{run_id}/resume` replay a finished run from the database once the event stream's retention has expired, in order and honouring `last_event_index`. (#10957, #10958) - **Structured Output for External Agents**: `output_schema` on `ClaudeAgent` and `CodexAgent` takes a Pydantic model or a JSON Schema dict, on the agent or per run, and the run's content is the parsed model (or dict) with `content_type` set, as for `Agent`. Claude delivers the object through its structured output tool, which is not recorded as a tool call; Codex receives a strict schema. The schema must describe a JSON object; an array or scalar root is refused before the run. The AgentOS per-request `output_schema` form field now applies to external agents. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/structured_output.py). (#10965) - **Media for External Agents**: Images and files can be attached to `ClaudeAgent` and `CodexAgent` runs, directly and through the AgentOS API and UI. Attachments are staged under `<cwd>/.agno/uploads/<run_id>/`, named in the prompt (Codex also receives images natively), recorded on the run's input, restored for later turns on any replica and removed after the run unless `keep_uploads=True`. Audio and video are rejected with a 400 instead of a server error. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/claude_media.py). (#10926) - **External Agent Identity on Session APIs**: `GET /sessions`, `GET /sessions/{session_id}` and the session run reads carry the same `metadata.sdk` value as the agents API (`"codex"`, `"claude-agent-sdk"`), overlaid at read time from the `agent_data` external agents already persist, so existing sessions are labelled with no migration and stored metadata is untouched. Native, team and workflow sessions are unchanged. `RunSchema` gains a `metadata` field, which also surfaces run metadata such as retry `attempts` and `warnings`. (#11020) - **AnakinTools**: New toolkit for Anakin.io's scrape, crawl, map and search APIs as four flag-gated methods, polling asynchronous jobs until they complete so the model receives page content or links rather than a job id. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/91_tools/anakin_tools.py). (#10650) ## Improvements: - **External Agent APIs**: `ClaudeAgent` and `CodexAgent` take typed native options (tools, permissions, MCP servers, thread and turn options) instead of untyped `_kwargs` dictionaries, which still work with a warning. Adapter constructors are keyword-only, `agent.sdk` names the SDK (API and session metadata emit only `sdk`; the `framework` alias remains on the Python object), and `print_response` / `aprint_response` return the final run and raise on failure. Framework cookbooks were reworked with native SDK comparisons, a sample project and a reproducible reliability kit. (#10926) - **Model Identity for External Agents**: `ClaudeAgent` and `CodexAgent` runs record `model` and `model_provider` like native agents, so they count on the Models Runs card of the AgentOS metrics page. When no model is set on the agent or in its options, `model` defaults to `claude-sonnet-4-6` for Claude and `gpt-5.6-luna` for Codex, as a native model has a default id; the default is what the SDK is asked to run. A model set in options still wins. Previously an unset model left the choice to the CLI. (#11018) - **Queued Turns Per Session**: Queued external-agent turns on one session no longer claim attempts together and burn them on `SessionBusyError`. Each built-in queue store (Postgres, Redis, in-memory) elects the next turn for a session before it spends an attempt or a worker slot. (#10974) - **Harness Cookbooks**: The Claude and Codex cookbooks were refreshed after Phase 1 acceptance testing with typed native options throughout, a corrected reliability runner, and live results recorded in each folder's test log. (#10973) - **One Turn Per Session**: Claude Code and Codex keep one conversation per session on their side, so a second turn started while one is in flight is now refused with `SessionBusyError` (HTTP 409, `error_type: session_busy`) instead of being accepted and failing later. Cancelling a run left pending or running by a crash frees its session. (#10958) - **Subagent Tool Lineage**: Tool calls made by Claude subagents carry `parent_tool_call_id` on `ToolExecution` and on tool events, so tracing shows nested tool spans under the delegating tool. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/continue_from/07_subagent_checkpoints.py). (#10959) - **Development Setup**: `scripts/dev_setup.sh` and `scripts/demo_setup.sh` install the same dependencies, so cookbooks run from either environment. (#10920) ## Bug Fixes: - **Session Busy Keep-Alive**: The 409 for a busy external-agent session is answered by a dedicated `SessionBusyError` handler instead of the catch-all, so the keep-alive connection stays open and the conflict is logged as a warning rather than a traceback. The response body is unchanged and no other exception takes the new path. See [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/session_busy_agentos.py). (#11017) - **Attachment Staging**: Files attached to external-agent runs are staged and restored only under the active workspace, with the harness told when historical paths moved. A failed staging rolls back every file it created, including partial writes and with `keep_uploads=True`, while preexisting files are preserved. Traversal run ids and symlinked upload paths are rejected. (#10971) - **Run Input Validation**: Malformed JSON `images`, `audio`, `videos` and `input_files` on run requests are rejected with HTTP 422 before dispatch instead of vanishing or failing mid-run, and a malformed or scalar-root `output_schema` is rejected instead of being silently dropped. (#11005) - **CodexAgent**: Failed tool calls (a non-zero exit, a declined command, an MCP tool error) are stored with `tool_call_error=True` instead of `null`, on streamed and non-streamed runs. (#10958) - **CodexAgent**: When a thread cannot be resumed, the history replayed into the fresh thread now includes earlier tool calls and their results, truncated to 2000 characters each, instead of only user and assistant text. (#10909) - **External Agent Resume**: `/resume` on a run submitted with `stream=false` reports the run's real status and says to poll the run endpoint, instead of claiming the run completed while it was still running. (#10958) - **Tests**: The partial-tool collision test no longer depends on FastMCP's tool naming. (#10914) Co-authored-by: Sannya Singal <32308435+sannya-singal@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Claude SDK messages include
parent_tool_use_id, but the adapter previously discarded it when creating Agno tool events. This prevented tracing integrations from distinguishing subagent tools from top-level tools.parent_tool_call_idtoToolExecution, including serialization and deserialization.With the corresponding OpenInference update, traces can represent:
tracing update on openinference- Arize-ai/openinference#4018
to test locally use
uv pip install -e ../openinference/python/instrumentation/openinference-instrumentation-agno/Quick cookbook (not adding in the PR files since the openinference update needs to be released first, so the cookbook cant go on main yet.-