Skip to content

Add agent_id/agent_type to tool-lifecycle hook inputs#628

Merged
qing-ant merged 1 commit intoanthropics:mainfrom
james-m-thomas:jthomas/agent-id-on-tool-hooks
Mar 3, 2026
Merged

Add agent_id/agent_type to tool-lifecycle hook inputs#628
qing-ant merged 1 commit intoanthropics:mainfrom
james-m-thomas:jthomas/agent-id-on-tool-hooks

Conversation

@james-m-thomas
Copy link
Copy Markdown
Contributor

@james-m-thomas james-m-thomas commented Mar 3, 2026

What

Surfaces the agent_id / agent_type fields the CLI started sending in v2.1.64 (anthropics/claude-cli-internal#20180) on the four tool-lifecycle hook input types: PreToolUseHookInput, PostToolUseHookInput, PostToolUseFailureHookInput, PermissionRequestHookInput.

Why

When the parent spawns multiple Task sub-agents in parallel, their PreToolUse/PostToolUse hooks interleave over the same control channel with nothing tying each callback to the sub-agent that fired it:

SubagentStart(agent_id=A)
PreToolUse(tool=databricks, tuid=t1)        ← only A is alive, so A ✓
SubagentStart(agent_id=B)
PostToolUse(tuid=t1)
PreToolUse(tool=databricks, tuid=t2)        ← A's follow-up? B's first? ambiguous
SubagentStart(agent_id=C)
PreToolUse(tool=databricks, tuid=t3)        ← A, B, or C? ambiguous

session_id and transcript_path are both parent-level and don't discriminate between sub-agents. Once more than one sub-agent is unblocked, there's no heuristic that can disambiguate — an older sub-agent might be writing its final response or about to fire a follow-up query; both states look identical from the hook stream.

agent_id is the correlation key: it's the same value the CLI emits in that sub-agent's SubagentStart/SubagentStop. For main-thread tool calls it's absent (check .get("agent_id") is None).

Implementation note

The CLI puts these on BaseHookInput, but doing that here would conflict with SubagentStartHookInput/SubagentStopHookInput, which already declare agent_id: str as required — PEP 655 forbids narrowing NotRequiredRequired in a TypedDict subclass. So this uses a total=False mixin applied to exactly the four hook types the CLI populates. Those are also the only callsites where the CLI has a toolUseContext in scope, so it's a wash semantically.

Scope

Types only. CHANGELOG.md and _cli_version.py are intentionally untouched: the auto-release workflow generates changelog entries and keys on the exact chore: bump bundled CLI version to X.Y.Z commit message to trigger a release. Bundling the bump here would silently skip the 2.1.64 auto-release.

The NotRequired fields are inert until the bundled CLI reaches 2.1.64 via the normal bump-commit flow, at which point both sides line up.

Test plan

  • ruff check / ruff format — clean
  • mypy src/claude_agent_sdk/types.py — clean
  • pytest tests/246/246 pass (2 new)
    • PreToolUseHookInput with agent_id present (sub-agent) and absent (main thread) — both type-valid
    • PostToolUseHookInput with agent_id present

Surfaces the sub-agent attribution fields the CLI started sending in
v2.1.64 (anthropics/claude-cli-internal#20180). When multiple Task
sub-agents run in parallel, their PreToolUse/PostToolUse hooks
interleave over the same control channel with nothing tying each
callback to the sub-agent that fired it. agent_id is that correlation
key — it matches the agent_id on that sub-agent's SubagentStart.

Implemented as a _SubagentContextMixin rather than fields on
BaseHookInput: SubagentStartHookInput/SubagentStopHookInput already
declare these as required, and PEP 655 forbids narrowing
NotRequired->Required in a TypedDict subclass. The mixin is applied to
exactly the four tool-lifecycle types the CLI populates (PreToolUse,
PostToolUse, PostToolUseFailure, PermissionRequest).

CHANGELOG and CLI bump intentionally omitted: both are handled by the
auto-release workflow, which keys on the exact bump-commit message.
Including either here would either fail test_changelog.py or silently
skip the 2.1.64 auto-release trigger.
@james-m-thomas james-m-thomas force-pushed the jthomas/agent-id-on-tool-hooks branch from 50642ca to 0b527ca Compare March 3, 2026 20:16
@wolffiex wolffiex self-requested a review March 3, 2026 20:45
@qing-ant qing-ant merged commit 2f1fd38 into anthropics:main Mar 3, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants