[BUG] An agent started by $.agent.spawn from a $.clock callback can’t reach its own plugin’s tool.call hook
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
A mod registers a tool with $.tool.register and answers it with on('tool.call', { tool: 'mcp__<plugin>__ping' }, ...).
If the mod starts a subagent with $.agent.spawn inside a command.run hook, the subagent calls the tool and the hook answers (pong).
If the same spawn runs from a $.clock.after callback instead, outside any hook, the subagent’s call fails within about 10-150 ms with <plugin> registered the tool ping but no tool.call hook answered this call. The hook never runs: a file write on its first line never happens.
Scope tested
Claude Code 2.1.296 and 2.1.299, on Linux.
Loaded with --plugin-dir, with CLAUDE_CODE_PLUGIN_DIRS, and under --remote-control: same result each time.
An agent the main model starts through the Agent tool reaches the hook fine.
What Should Happen?
a plugin’s tool.call hooks answer calls from every agent in the session, however the agent was started.
Error Messages/Logs
[DEBUG] hooks module timer-spawn-probe@inline tool.call skipped: re-entry (the plugin's own code raised it; origin timer-spawn-probe)
[ERROR] MCP server "timer-spawn-probe" timer-spawn-probe registered the tool ping but no tool.call hook answered this call: ...
[DEBUG] MCP server "timer-spawn-probe": Tool 'ping' failed after 0s: timer-spawn-probe registered the tool ping but no tool.call hook answered this call: ..
Steps to Reproduce
A minimal mod with agents/pinger.md (tools: ToolSearch, mcp__spawnprobe__ping), a tool.call hook that returns { result: 'pong' }, and a /probe command.
In /probe, spawning spawnprobe:pinger directly works.
In /probe, wrapping the same spawn in $.clock.after(500, ...) fails as above.
It fails the same way with general-purpose instead of a plugin agent type.
https://gist.github.com/cbundy/8b1a0fa00dc3b0806e24040d70342be1
agent spawned inside command.run: reaches the hook[DEBUG] hooks module timer-spawn-probe@inline: tool.call nested in timer-spawn-probe#2
agent spawned from $.clock.after: hook skipped
[DEBUG] hooks module timer-spawn-probe@inline tool.call skipped: re-entry (the plugin's own code raised it; origin timer-spawn-probe)
[ERROR] MCP server "timer-spawn-probe" timer-spawn-probe registered the tool ping but no tool.call hook answered this call: ...
[DEBUG] MCP server "timer-spawn-probe": Tool 'ping' failed after 0s: timer-spawn-probe registered the tool ping but no tool.call hook answered this call: ...
Claude Model
Opus
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.299
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Non-interactive/CI environment
Additional Information
Impact: any mod that drives agents from a timer can’t use its own tools as a structured channel back from those agents. Our orchestration mod’s passes run on $.clock.every, so every agent tool call failed: more than 80 in one session. We’ve had to fall back to agents running a validation script through Bash.
Related, possibly worth a docs note: a tool.call hook that runs past its 10 s HookBudget returns the same “no tool.call hook answered this call” message, even though the hook did run. With identical messages the two causes look the same, and it cost us a while to tell them apart. A message that says which happened would help.