AgentTool Deep Dive: Sub‑Agent Generation and Recursive Safeguards

This article dissects Claude Code's AgentTool, explaining how it spawns full‑featured sub‑agents, routes execution through three distinct paths, shares Anthropic Prompt Cache via Fork, and protects against infinite recursion with a dual‑layer safeguard while isolating worktrees for safe parallel file operations.

James' Growth Diary
James' Growth Diary
James' Growth Diary
AgentTool Deep Dive: Sub‑Agent Generation and Recursive Safeguards

What AgentTool Is

AgentTool is the core multi‑agent engine of Claude Code. When a user asks, for example, “search bugs in three modules in parallel,” Claude does not run three separate searches itself; it silently dispatches three cloned agents via a single call to AgentTool.

The entry point lives in src/tools/AgentTool/AgentTool.tsx around line 387. Its schema accepts a prompt (task description) and optional fields subagent_type, name, and run_in_background. Invoking Agent(prompt="fix bug") triggers the full execution chain:

AI 生成 tool_use: { prompt: "修复 bug", subagent_type: "Explore" }
  ↓
AgentTool.call()                             ← 入口(AgentTool.tsx:387)
  ├── 解析 effectiveType(fork vs 命名 agent vs GP 回退)
  ├── filterDeniedAgents()                    ← 权限过滤
  ├── assembleToolPool(workerPermissionContext) ← 独立组装工具池
  ├── createAgentWorktree()                   ← 可选 worktree 隔离
  ↓
runAgent()                                    ← 核心执行(runAgent.ts)
  ├── getAgentSystemPrompt()                  ← 构建 agent 专属 system prompt
  ├── executeSubagentStartHooks()             ← Hook 注入
  └── query()                                 ← 进入标准 agentic loop

Design Point: Full Query Loop for Sub‑Agents

Each sub‑agent runs the complete query() loop using the same engine as the main agent. It is not a simplified copy; it has an independent tool pool and permission context, making it a fully fledged agent instance.

Three Execution Paths

Named Agent (when subagent_type is set): uses the agent’s own getSystemPrompt(), assembles an independent tool pool, and receives only the task description as context.

Fork Subprocess (Fork enabled and no type specified): inherits the parent’s full system prompt and tool pool, and gets the full conversation history as context.

General‑Purpose Fallback (Fork disabled and no type specified): falls back to the generic GP agent with its own system prompt and a freshly assembled tool pool.

02 Fork Path – Prompt‑Cache Sharing Engineering

Fork solves the problem of sharing Anthropic’s expensive Prompt Cache across parallel sub‑agents. The cache hits only when the request prefix is identical. Fork constructs messages so that the first N‑1 blocks are identical and only the last block differs:

// forkSubagent.ts:93 — all fork subprocesses share the same placeholder result
const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'

// buildForkedMessages() creates two messages:
// [assistant(parent tool_use block) ← identical, cache 100% hit]
// [user([placeholder_results..., {type: "text", text: subprocess command}])]
// Only the final user block varies, keeping cache hits high.

Fork is enabled only when three conditions are met: the feature flag FORK_SUBAGENT is on, the system is not in Coordinator mode, and the session is interactive. If any condition fails, the engine silently degrades to the General‑Purpose agent.

03 Recursive Protection – Two Safeguards

To prevent infinite recursion when a forked sub‑agent calls AgentTool again, Claude Code employs two lines of defense:

Runtime check : the sub‑process’s querySource is marked as 'agent:builtin:fork'. The main loop refuses to spawn a new fork if it sees this marker.

Persistent tag : a <fork‑boilerplate> tag is injected into the message history. Before starting a fork, the loop scans messages for this tag; finding it aborts the second fork.

The combination covers both normal execution (runtime marker) and edge cases where the runtime state might be lost (persistent tag).

04 Named Agent Tool‑Pool Isolation

When subagent_type is set, the tool pool is assembled independently from the parent:

// AgentTool.tsx tool‑pool assembly
const workerPermissionContext = {...appState.toolPermissionContext, mode: selectedAgent.permissionMode ?? 'acceptEdits'}
const workerTools = assembleToolPool(workerPermissionContext, appState.mcp.tools)
// Further filtering in runAgent.ts
const resolvedTools = useExactTools ? availableTools : resolveAgentTools(agentDefinition, availableTools, isAsync).resolvedTools

This ensures minimal permissions for the sub‑agent while still inheriting globally connected MCP tools.

Built‑in Agents Overview

Explore – Model: Haiku (lightweight), Permission: read‑only, Tools: Read/Grep/Glob, Purpose: codebase search.

Plan – Model: inherits parent, Permission: read‑only, Tools: limited, Purpose: plan‑mode information gathering.

General‑purpose – Model: inherits parent, Permission: full, Tools: all, Purpose: complex generic tasks.

statusline‑setup – Model: inherits parent, Permission: limited, Tools: limited, Purpose: status‑line configuration.

05 Model Resolution and Worktree Isolation

Model selection follows a four‑step priority chain (source: src/utils/model/agent.ts):

1. CLAUDE_CODE_SUBAGENT_MODEL env var ← global override
2. model parameter passed to AgentTool ← call‑time override
3. model frontmatter in agent definition ← e.g., "sonnet", "haiku", "inherit"
4. inherit parent conversation model ← getDefaultSubagentModel() returns "inherit"

The special value inherit is not a simple pass‑through; it is resolved by getRuntimeMainLoopModel() to map plan‑mode aliases like opusplan→Opus correctly.

If an agent definition sets isolation: "worktree", the sub‑agent runs inside its own Git worktree, preventing file‑level conflicts when multiple agents modify the same repository:

const slug = `agent-${earlyAgentId.slice(0,8)}`
worktreeInfo = await createAgentWorktree(slug)
// Lifecycle: create worktree → CWD override → sub‑agent work → cleanup

06 Five Transferable Engineering Insights

Sub‑agents run the full query() loop, gaining identical tool execution, error recovery, and token‑budget control at the cost of higher memory/CPU.

Isolated tool pools implement least‑privilege security; e.g., the Explore agent only receives three read‑only tools.

Fork’s cache sharing yields a 40‑60% increase in Prompt Cache hit rate, delivering measurable API‑cost savings.

Dual‑guard recursion (runtime querySource + persistent <fork‑boilerplate>) outperforms single‑line defenses.

Named agents exemplify “role‑based AI” by coupling distinct system prompts, tool sets, and permission modes.

Conclusion

AgentTool is Claude Code’s most complex single tool, yet its design remains disciplined. It offers three execution paths (named, fork, GP), a double‑layer recursion safeguard, independent tool‑pool assembly for minimal permissions, genuine Prompt Cache sharing via Fork, worktree isolation for safe parallel file operations, and a nuanced model‑inheritance mechanism.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

forkClaude CodeAI toolingsub-agentprompt cacheAgentToolrecursive protectionworktree isolation
James' Growth Diary
Written by

James' Growth Diary

I am James, focusing on AI Agent learning and growth. I continuously update two series: “AI Agent Mastery Path,” which systematically outlines core theories and practices of agents, and “Claude Code Design Philosophy,” which deeply analyzes the design thinking behind top AI tools. Helping you build a solid foundation in the AI era.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.