diff --git a/raw/home-server-101.pdf b/raw/home-server-101.pdf new file mode 100644 index 0000000..1796f09 Binary files /dev/null and b/raw/home-server-101.pdf differ diff --git a/wiki/agent-sdk/_index.md b/wiki/agent-sdk/_index.md index 2a14087..ff20938 100644 --- a/wiki/agent-sdk/_index.md +++ b/wiki/agent-sdk/_index.md @@ -38,3 +38,4 @@ Build production AI agents using the same tools, agent loop, and context managem | [[wiki/agent-sdk/slash-commands\|slash-commands]] | Built-in & custom slash commands via SDK: discovery, /compact, custom .claude/commands/ files, arguments, bash/file interpolation | raw/Slash Commands in the SDK.md | 2026-04-17 | | [[wiki/agent-sdk/streaming-output\|streaming-output]] | Real-time token/tool streaming: StreamEvent structure, text_delta, input_json_delta, UI patterns, limitations | raw/Stream responses in real-time.md | 2026-04-17 | | [[wiki/agent-sdk/streaming-input\|streaming-input]] | Two input modes: AsyncGenerator (streaming, default) vs string (single message); capabilities, limitations, session resuming | raw/Streaming Input.md | 2026-04-17 | +| [[wiki/agent-sdk/subagents\|subagents]] | Spawn subagents programmatically or via filesystem; context isolation, parallelization, tool restrictions, resuming, detection | raw/Subagents in the SDK.md | 2026-04-17 | diff --git a/wiki/agent-sdk/subagents.md b/wiki/agent-sdk/subagents.md new file mode 100644 index 0000000..89ae181 --- /dev/null +++ b/wiki/agent-sdk/subagents.md @@ -0,0 +1,190 @@ +--- +title: "Subagents in the SDK" +aliases: [subagents, sdk-subagents, agent-spawning] +tags: [agent-sdk, subagents, parallelization, context-isolation, python, typescript] +sources: [raw/Subagents in the SDK.md] +created: 2026-04-17 +updated: 2026-04-17 +--- + +# Subagents in the SDK + +Subagents are separate agent instances spawned by a main agent to handle focused subtasks. Core use cases: context isolation, parallel execution, specialized instructions, and tool restriction. + +## Three Ways to Create Subagents + +| Method | When to use | +|--------|-------------| +| **Programmatic** (`agents` param in `query()`) | Recommended for SDK apps — full control, dynamic config | +| **Filesystem-based** (`.claude/agents/*.md`) | Static definitions, loaded at startup | +| **Built-in `general-purpose`** | Available automatically when `Agent` is in `allowedTools` — no definition needed | + +Programmatic agents take precedence over filesystem agents with the same name. + +## Benefits + +### Context Isolation +Each subagent runs in a **fresh conversation**. Only the final message returns to the parent — not intermediate tool calls or reasoning. Parent receives a concise summary, not every file the subagent read. + +### Parallelization +Multiple subagents run concurrently. Example: `style-checker`, `security-scanner`, and `test-coverage` can run simultaneously during a code review. + +### Specialized Instructions +Each subagent has its own system prompt with domain-specific knowledge — without bloating the main agent's prompt. + +### Tool Restrictions +Limit subagents to specific tools. A `doc-reviewer` with only `Read` + `Grep` can analyze but never modify files. + +## AgentDefinition Fields + +| Field | Type | Required | Notes | +|-------|------|----------|-------| +| `description` | string | Yes | Tells Claude when to use this subagent — write clearly | +| `prompt` | string | Yes | Subagent's system prompt | +| `tools` | string[] | No | If omitted, inherits all parent tools | +| `model` | `sonnet\|opus\|haiku\|inherit` | No | Defaults to main model | +| `skills` | string[] | No | Skills available to this subagent | +| `memory` | `user\|project\|local` | No | Python only | +| `mcpServers` | array | No | MCP servers by name or inline config | + +> **Important:** Subagents cannot spawn their own subagents — never include `Agent` in a subagent's `tools`. + +## Python Example + +```python +import asyncio +from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition + +async def main(): + async for message in query( + prompt="Review the authentication module for security issues", + options=ClaudeAgentOptions( + # Agent tool is required — Claude invokes subagents through it + allowed_tools=["Read", "Grep", "Glob", "Agent"], + agents={ + "code-reviewer": AgentDefinition( + description="Expert code review specialist. Use for quality, security, and maintainability reviews.", + prompt="""You are a code review specialist with expertise in security, performance, and best practices. +When reviewing code: +- Identify security vulnerabilities +- Check for performance issues +- Verify adherence to coding standards +- Suggest specific improvements""", + tools=["Read", "Grep", "Glob"], # read-only + model="sonnet", + ), + "test-runner": AgentDefinition( + description="Runs and analyzes test suites. Use for test execution and coverage analysis.", + prompt="You are a test execution specialist. Run tests and provide clear analysis of results.", + tools=["Bash", "Read", "Grep"], + ), + }, + ), + ): + if hasattr(message, "result"): + print(message.result) + +asyncio.run(main()) +``` + +## What Subagents Inherit + +| Receives | Does NOT receive | +|----------|-----------------| +| Own system prompt + Agent tool prompt | Parent's conversation history or tool results | +| Project CLAUDE.md | Skills (unless listed in `AgentDefinition.skills`) | +| Tool definitions (parent's or restricted subset) | Parent's system prompt | + +The only channel from parent → subagent is the **Agent tool's prompt string**. Include file paths, error messages, or decisions directly in that prompt. + +## Invocation + +- **Automatic**: Claude matches tasks to subagents based on `description` field +- **Explicit**: Mention by name in prompt — `"Use the code-reviewer agent to check auth module"` + +## Dynamic Agent Configuration + +Factory pattern for runtime customization: + +```python +def create_security_agent(security_level: str) -> AgentDefinition: + is_strict = security_level == "strict" + return AgentDefinition( + description="Security code reviewer", + prompt=f"You are a {'strict' if is_strict else 'balanced'} security reviewer...", + tools=["Read", "Grep", "Glob"], + model="opus" if is_strict else "sonnet", # more capable model for high-stakes + ) +``` + +## Detecting Subagent Invocation + +Subagents are invoked via the `Agent` tool. Detect by checking `tool_use` blocks where `name` is `"Agent"` (or `"Task"` for SDK < v2.1.63). Messages from inside a subagent include `parent_tool_use_id`. + +```python +for block in message.content: + if getattr(block, "type", None) == "tool_use" and block.name in ("Task", "Agent"): + print(f"Subagent invoked: {block.input.get('subagent_type')}") + +if hasattr(message, "parent_tool_use_id") and message.parent_tool_use_id: + print(" (running inside subagent)") +``` + +## Resuming Subagents + +Resumed subagents retain full conversation history. Flow: + +1. Capture `session_id` from `ResultMessage` during first query +2. Parse `agentId` from message content (appears in Agent tool results) +3. Pass `resume: sessionId` in second query's options +4. Include agent ID in prompt + +```typescript +// Resume example (TypeScript) +for await (const message of query({ + prompt: `Resume agent ${agentId} and list the top 3 most complex endpoints`, + options: { allowedTools: ["Read", "Grep", "Glob", "Agent"], resume: sessionId } +})) { + if ("result" in message) console.log(message.result); +} +``` + +Transcripts persist independently of the main conversation and are cleaned up after `cleanupPeriodDays` (default: 30 days). + +## Common Tool Combinations + +| Use case | Tools | +|----------|-------| +| Read-only analysis | `Read`, `Grep`, `Glob` | +| Test execution | `Bash`, `Read`, `Grep` | +| Code modification | `Read`, `Edit`, `Write`, `Grep`, `Glob` | +| Full access | Omit `tools` field | + +## Troubleshooting + +| Problem | Fix | +|---------|-----| +| Claude not delegating to subagents | Add `Agent` to `allowedTools`; use explicit name in prompt; write clearer `description` | +| Filesystem agents not loading | Restart session — agents are loaded at startup only | +| Windows long prompt failures | Keep prompts concise (<8191 chars) or use filesystem-based agents | + +## Key Takeaways + +- `Agent` tool **must** be in `allowedTools` — subagents are invoked through it +- Subagents **cannot** spawn sub-subagents (no `Agent` in subagent's `tools`) +- Context isolation means only the **final message** returns to parent — include all needed context in the Agent tool prompt +- `description` field drives automatic delegation — write it carefully +- Dynamic factory functions enable runtime customization (different models per security level, etc.) +- Tool name changed from `"Task"` → `"Agent"` in v2.1.63 — check both for compatibility + +## Related Articles + +- [[wiki/agent-sdk/agent-loop|Agent Loop]] — how turns, tool execution, and context management work +- [[wiki/agent-sdk/configure-permissions|Configure Permissions]] — permission modes and subagent inheritance +- [[wiki/agent-sdk/agent-skills-plugins|Agent Skills & Plugins]] — loading skills into subagents +- [[wiki/agent-sdk/hosting-production|Hosting in Production]] — multi-agent deployment patterns +- [[wiki/agent-sdk/sdk-hooks|SDK Hooks]] — intercept tool calls including Agent tool invocations + +## Sources + +- [Subagents in the SDK](https://code.claude.com/docs/en/agent-sdk/subagents) — official Anthropic docs