How Claude Code Agent Teams Actually Works - reverse Claude Code Agent Teams use CC
Metadata
Created: 2026-02-13
Source: Claude Code Docs, reverse engineering of
cli.js(v1.0.34+)Status: Experimental Feature
Date: February 2026
TL;DR
Claude Code Agent Teams lets you spin up multiple Claude Code sessions that work together as a coordinated team β with a lead agent, teammate agents, shared task lists, and inter-agent messaging. But what's really going on under the hood? This post goes beyond the docs: we reverse-engineered the compiled source to understand the filesystem-based IPC, React/Ink polling architecture, three spawn backends, and the 13-operation TeammateTool that makes it all work.

Why This Post Exists
The official docs tell you what Agent Teams does. This post tells you how it does it β by reading the actual compiled source code (cli.js), tracing function calls, and mapping out the internal architecture. If you want to build on top of Agent Teams, extend it with hooks, or just understand the engineering decisions behind multi-agent coordination, this is for you.
The Big Picture Architecture
At its core, Agent Teams is a filesystem-coordinated, polling-based, multi-process orchestration system built on top of Claude Code's existing Task/subagent infrastructure.

The critical design decision: All coordination happens through the local filesystem, not sockets, pipes, or HTTP. Every message, task update, and team config change is a JSON file read/write with file locking. This is simple, debuggable, and works across all spawn backends without protocol negotiation.
Enabling the Feature Gate
Agent Teams is guarded by a feature flag function (lA() in the compiled source) that checks:
Both lA() (feature enabled) and team membership detection (Mz(), which checks if the current process is a teammate) must pass before any team functionality activates. The gate controls everything: tool registration, inbox polling, team context injection, and teammate spawning.
The Seven Core Primitives
The entire system is built on seven primitives that the lead agent uses as tools:
TeamCreate
Initializes team namespace
Creates ~/.claude/teams/{name}/config.json
Task (with team_name)
Spawns an independent teammate
Starts new process + writes to config.json members
TaskCreate
Creates a work item
Writes {id}.json to ~/.claude/tasks/{name}/
TaskUpdate
Changes task state
Updates JSON file with locking
TaskList
Discovers available work
Reads all task JSON files
SendMessage (write)
Direct message to one teammate
Appends to recipient's inbox JSON
SendMessage (broadcast)
Message all teammates
Appends to every teammate's inbox JSON
The Mailbox System β Reverse Engineering IPC
This is the most interesting part of the architecture. Let's trace exactly how Agent A sends a message to Agent B.
Inbox File Structure
Each agent gets a personal inbox file:
Each inbox is a JSON array of messages:
The Write Path β writeToMailbox()
writeToMailbox()From the compiled source, the write path works like this:
The file locking uses lockSync() from a lock library (likely proper-lockfile), creating a .lock companion file. This prevents race conditions when multiple teammates try to write to the same inbox simultaneously.
The Read Path β InboxPoller
The InboxPoller is a React/Ink component (Claude Code's TUI is built with Ink) that runs inside each agent's render loop. On each poll cycle:
The message routing is the key insight: the inbox is a multiplexed channel carrying both human-readable messages and structured protocol messages (serialized as JSON strings inside the text field).
Message Type Hierarchy

The text field does double duty: for regular messages, it's plain text. For protocol messages, it's a JSON string that gets parsed by type-checking functions (isShutdownRequest(), isPlanApprovalResponse(), etc.) β each implemented as a safeParse() call against a Zod schema.
Three Spawn Backends
When the lead spawns a teammate, the system picks one of three backends based on runtime detection:

InProcessBackend (Default)
The in-process backend runs teammates within the same Node.js process as the lead. Each teammate is tracked as an InProcessTeammateTask in the React/Ink AppState:
Switching between teammates uses Shift+Up/Down keyboard shortcuts, which cycle through the selectedIPAgentIndex in AppState. Each teammate gets its own conversation context within the shared process.
PaneBackendExecutor (tmux/iTerm2)
For split-pane mode, the PaneBackendExecutor creates a real terminal pane and sends a CLI command to it:
The teammate process starts as a completely independent Claude Code session. It discovers it's a teammate by checking the --agent-id flag, reads the team config to find the lead, and registers a "Stop" hook to send an idle_notification when it finishes.
Backend Detection Priority
The "auto" teammate mode setting follows this cascade. You can override with "in-process" to force single-terminal mode regardless of available backends.
The Task System β Shared Work Queue
Task File Format
Each task is an individual JSON file:
Self-Claiming Mechanism
When a teammate finishes its current task:
It calls
TaskListto read all task filesFilters for tasks where
status === "pending"andblockedByis empty (all resolved)Attempts to claim by writing
status: "in_progress"andowner: agentNamewith file lockingIf the lock fails (another teammate claimed it first), tries the next task
The file locking prevents double-claiming, but the system relies on eventual consistency β teammates poll the task list rather than receiving push notifications when tasks unblock.
Dependency Resolution
Dependencies are expressed as arrays of task IDs:
When a teammate marks task 1 as completed, task 3 checks if all entries in its blockedBy list are completed. The check happens at poll time β no push notification. This is why "task status can lag" is a documented limitation.
Team Configuration β The Central Registry
The teamAllowedPaths field enables team-wide permission grants β the lead can authorize all teammates to write to specific directories without per-agent permission prompts.
The TeammateTool β 13 Operations
The TeammateTool is the lead's orchestration interface, exposed as a single tool with an operation discriminator:

Each operation maps to filesystem operations:
spawnTeam β creates directories +
config.jsonwrite β appends to one inbox file
broadcast β appends to ALL inbox files (expensive: one write per teammate)
requestShutdown β writes structured message to teammate's inbox
approveShutdown β teammate writes back, lead removes from config
cleanup β checks no active members, then deletes team directory
The Idle Notification Flow
This is the handshake that closes the feedback loop when a teammate finishes:

The idle notification includes an optional summary field β a compressed summary of the teammate's recent conversation, produced by the getLastPeerDmSummary() function. This gives the lead context about what the teammate accomplished without reading their full conversation history.
Hooks Integration β Quality Gates
Two hook events integrate with the team lifecycle:
TeammateIdle Hook
Fires when a teammate is about to go idle. The hook receives the teammate's context and can:
Exit 0: Allow idle (normal)
Exit 2: Send feedback message and keep the teammate working
Use case: Run eslint on the teammate's changes. If linting fails, exit 2 with feedback telling the teammate to fix the issues.
TaskCompleted Hook
Fires when a task is being marked complete. Same exit code semantics:
Exit 0: Allow completion
Exit 2: Reject completion, send feedback
Use case: Run the test suite on affected files. No task can be marked "done" until tests pass.
TeammateInit β The Registration Hook
When a teammate starts (reconstructed from _p8 function in the source):
Reads team config to find the lead agent's name
Applies
teamAllowedPathspermissions to its own sessionIf this agent is NOT the lead, registers a "Stop" hook that sends an
idle_notificationto the lead when the teammate finishes
This registration is why idle notifications work automatically β every teammate registers the hook at startup.
Permission Delegation
One of the more sophisticated features: teammates can delegate permission requests to the lead.

This means permission prompts from all teammates bubble up to the lead's terminal, keeping you as the single approval point. The sandbox_permission_request/response variant handles file system sandbox permissions specifically.
Agent Teams vs. Subagents β The Architecture Difference
Process model
Background async tasks within one session
Independent Claude Code sessions
Context
Share parent's context window
Completely isolated context windows
Communication
Return results to parent only
Peer-to-peer messaging via inbox files
Coordination
Parent manages everything
Shared task list with self-claiming
IPC
In-memory (same process)
Filesystem JSON with file locking
Lifecycle
Managed by parent's abort controller
Independent with shutdown protocol
Spawning
Task({ subagent_type, prompt })
Task({ team_name, name, prompt })
Cost
Lower (shared context)
Higher (each gets full context window)
The architectural analogy: subagents are threads (lightweight, shared memory), agent teams are processes (isolated, communicate via IPC).
Real-World Patterns
Pattern 1: Parallel Specialists
Pattern 2: Competing Hypotheses
Pattern 3: Pipeline with Dependencies
Design Decisions Worth Noting
Why Filesystem IPC?
The filesystem approach has clear advantages for this use case:
Debuggable: You can
catany inbox file to see message historyBackend-agnostic: Works identically for in-process, tmux, and iTerm2 backends
Crash-resilient: Messages persist on disk even if a process dies
No server: No daemon, no port, no protocol negotiation
Cross-platform: Works on macOS, Linux, and WSL
The tradeoff is latency β polling a JSON file is slower than a socket push. But for agent coordination where messages are infrequent (seconds between them, not milliseconds), this is an excellent tradeoff.
Why Polling, Not File Watchers?
The InboxPoller reads the inbox file on each render cycle rather than using fs.watch() or chokidar. This is likely because:
File watchers are notoriously unreliable across platforms
The polling happens naturally as part of the Ink render loop (already on a tick)
No additional resource cleanup needed on process exit
Why JSON, Not SQLite?
Individual task files (one per task) rather than a database means:
No shared database lock contention
Each task can be locked independently
Easy to inspect and debug
No migration story needed
The downside is lack of atomic multi-task operations, which is why "task status can lag" is a documented limitation.
Known Limitations (and Why They Exist)
No session resumption with in-process teammates
In-process teammates are React state β not serialized to disk
Task status can lag
Polling-based, no push notifications for state changes
One team per session
Team config is a singleton in AppState
No nested teams
Teammates check Mz() (isTeammate) and refuse to spawn
Lead is fixed
leadAgentId in config.json is set at creation, never updated
No split panes in VS Code terminal
No tmux support in VS Code's pty implementation
Quick Reference
Enable
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 in settings.json
Start a team
Describe a parallel task in your prompt
Display mode
--teammate-mode in-process or tmux (auto-detected)
Talk to teammate
Shift+Up/Down (in-process) or click pane (split)
Delegate mode
Shift+Tab (restricts lead to coordination-only tools)
Assign tasks
Tell the lead to assign, or let agents self-claim
Require approval
Include "require plan approval" in spawn prompt
Shut down one
"Ask [name] teammate to shut down"
Clean up all
"Clean up the team"
Quality hooks
TeammateIdle (exit 2 = keep working), TaskCompleted (exit 2 = reject)
Key Takeaways
Agent Teams is filesystem-based IPC β JSON inbox files with file locking, no sockets or databases
InboxPoller is a React/Ink component that polls on each render cycle and routes 10+ message types
Three spawn backends (in-process, tmux, iTerm2) detected at runtime with automatic fallback
13 TeammateTool operations provide the full orchestration API: lifecycle, messaging, shutdown, plan approval
Task self-claiming uses file locking to prevent race conditions across concurrent teammates
Permission delegation bubbles all approval requests from teammates up to the lead
Idle notifications are sent automatically via a "Stop" hook registered at teammate startup
The design prioritizes simplicity and debuggability over performance β a smart tradeoff for agent coordination
Sources
Reverse engineering of
cli.jsfrom@anthropic-ai/claude-codenpm package (v1.0.34+)
Last updated