InProcessTeammate
- Entity ID:
ent-20260410-ccf7ed3ab424 - Type:
execution-model - Scope:
shared - Status:
active
Description
One of three agent execution models (alongside LocalAgentTask and RemoteAgentTask). InProcessTeammate runs a child agent inside the same Node.js process as the leader, using Node's AsyncLocalStorage from the async_hooks module to isolate each teammate's identity context. Unlike pane-based teammates (tmux/iTerm2) which spawn separate OS processes, in-process teammates share the leader's process resources -- API client, MCP connections, file-state caches -- while maintaining logically isolated execution contexts.
The system uses two layered AsyncLocalStorage instances: TeammateContext (in teammateContext.ts) carries the teammate's identity (agentId, agentName, teamName, color, planModeRequired, parentSessionId) and lifecycle AbortController; AgentContext (in agentContext.ts) carries analytics attribution metadata (agentType, invokingRequestId, invocationKind). Both are entered via runWithTeammateContext() and runWithAgentContext() at the top of each teammate's agent loop, so all downstream async operations -- API calls, tool use, telemetry -- resolve to the correct identity without parameter drilling.
The execution lifecycle follows a continuous prompt loop: the teammate receives an initial prompt, runs the standard runAgent() loop (identical to what the AgentTool uses for subagents), transitions to idle on completion, then polls for the next message or shutdown request. This makes teammates persistent actors rather than one-shot agents. The loop only exits on AbortController signal or when the model approves a shutdown request.
In-process mode is selected automatically when no pane backend (tmux/iTerm2) is available, or can be forced via the teammateMode: 'in-process' setting. It is always used for non-interactive sessions (-p mode). When pane-backend detection fails at spawn time, the system falls back to in-process mode and records the fallback so subsequent spawns stay consistent.
Key claims
-
Context isolation via AsyncLocalStorage, not process boundaries. Each teammate runs in the leader's Node.js process.
teammateContextStorage(anAsyncLocalStorage<TeammateContext>instance) ensures thatgetTeammateContext(),getAgentId(),getTeamName(), and related identity functions resolve to the correct teammate within concurrent async chains. This prevents the global-state overwrite problem that would occur if identity were stored in a shared module-level variable. (src/utils/teammateContext.ts) -
Identity resolution priority: AsyncLocalStorage > dynamicTeamContext > env vars. The helper functions in
teammate.tscheck the AsyncLocalStorage store first (in-process teammates), then the module-leveldynamicTeamContextobject (tmux teammates that joined at runtime via CLI args), then environment variables. This three-tier priority ensures the correct identity is returned regardless of execution model. (src/utils/teammate.ts) -
Independent AbortController per teammate. Each teammate gets its own
AbortControllercreated at spawn time, deliberately NOT linked to the leader's query abort. This means teammates continue running when the leader's current query is interrupted (e.g., user presses Escape on a leader prompt). A second, per-turncurrentWorkAbortControllerallows Escape to stop the current work iteration without killing the whole teammate. (src/utils/swarm/spawnInProcess.ts,src/utils/swarm/inProcessRunner.ts) -
Shares API client and MCP connections with the leader. The
toolUseContextpassed from the leader is reused (withmessages: []stripped to avoid pinning the parent's conversation in memory). The teammate callsrunAgent()which internally callsquery(), sharing the same API infrastructure. (src/utils/swarm/backends/InProcessBackend.ts, lines 118-129) -
UI message cap of 50.
task.messages(the AppState mirror used for the zoomed transcript dialog) is capped atTEAMMATE_MESSAGES_UI_CAP = 50entries. Older messages are dropped. The full conversation lives in the localallMessagesarray insiderunInProcessTeammate()and is persisted to the agent transcript on disk. This cap was introduced after BQ analysis showed ~20MB RSS per agent at 500+ turns and a whale session that launched 292 agents in 2 minutes, reaching 36.8GB. (src/tasks/InProcessTeammateTask/types.ts) -
Auto-compaction of conversation history. When the accumulated
allMessagestoken count exceeds the auto-compact threshold for the model, the runner compacts the conversation usingcompactConversation()with an isolatedToolUseContextclone (to avoid clearing the main session's readFileState cache). Content replacement state is also reset on compaction since old tool_use_ids become irrelevant. (src/utils/swarm/inProcessRunner.ts, lines 1072-1126) -
File-based mailbox for communication. Despite running in the same process, in-process teammates use the same file-based mailbox system as pane-based teammates (
writeToMailbox,readMailbox). Messages are polled every 500ms during idle. The runner prioritizes shutdown requests over regular messages, and team-lead messages over peer messages, to prevent starvation. (src/utils/swarm/inProcessRunner.ts,src/utils/teammateMailbox.ts) -
In-memory pending message queue for transcript interaction. When a user views a teammate's transcript and types a message, it is injected via
injectUserMessageToTeammate()intotask.pendingUserMessages. The idle poll loop checks this queue on every iteration (before checking the file mailbox), providing near-instant delivery for interactive messages. (src/tasks/InProcessTeammateTask/InProcessTeammateTask.tsx,src/utils/swarm/inProcessRunner.ts) -
Tool access: async agent tools + team coordination tools. Teammates receive the standard
ASYNC_AGENT_ALLOWED_TOOLSset (Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, Notebook, Skill, Worktree tools) plusIN_PROCESS_TEAMMATE_ALLOWED_TOOLS(SendMessage, TaskCreate, TaskGet, TaskList, TaskUpdate, and optionally Cron tools). Blocked tools include AgentTool (prevents recursion), TaskOutput, ExitPlanMode, EnterPlanMode, AskUserQuestion, and TaskStop. Team-essential tools (SendMessage, TeamCreate, TeamDelete, TaskCreate/Get/List/Update) are force-injected even when a custom agent definition specifies an explicit tool list. (src/constants/tools.ts,src/utils/swarm/inProcessRunner.ts) -
Permission delegation to leader UI. When a teammate encounters an
'ask'permission result, it uses the leader'sToolUseConfirmdialog queue (with a colored worker badge showing the teammate's name). The teammate's tool use appears in the leader's permission prompt UI with the same specialized components (BashPermissionRequest, FileEditToolDiff, etc.) as the leader's own tools. Falls back to the file-based mailbox permission system when the UI bridge is unavailable. For bash commands, the classifier auto-approval is awaited (rather than raced) before showing the dialog. (src/utils/swarm/inProcessRunner.ts,createInProcessCanUseTool) -
Graceful shutdown via model decision. Shutdown requests are not auto-approved. The leader sends a shutdown request to the teammate's mailbox, and
shutdownRequestedis set on the task state. The request is passed to the model as a<teammate-message>XML prompt, and the model decides whether to approve (exit) or reject (continue working). This differs fromkill()which immediately aborts via the AbortController. (src/utils/swarm/backends/InProcessBackend.ts,src/utils/swarm/inProcessRunner.ts) -
Task list integration for work assignment. During idle, the runner calls
tryClaimNextTask()to check the team's task list for unclaimed pending tasks. If found, it atomically claims the task, sets it toin_progress, and formats it as a prompt. This enables leader-less work distribution where teammates self-serve from a shared task queue. (src/utils/swarm/inProcessRunner.ts) -
System prompt: full leader prompt + teammate addendum. Teammates receive the full system prompt (same as the main agent) plus
TEAMMATE_SYSTEM_PROMPT_ADDENDUMwhich instructs them to useSendMessagetool for communication (plain text responses are not visible to the team). Custom agent definitions can append or replace the system prompt viasystemPromptMode. (src/utils/swarm/teammatePromptAddendum.ts,src/utils/swarm/inProcessRunner.ts) -
Perfetto tracing integration. Teammates are registered/unregistered in the Perfetto trace hierarchy for visualization of the agent tree. (
src/utils/swarm/spawnInProcess.ts) -
Task state type:
in_process_teammate. Registered inAppState.tasksasInProcessTeammateTaskStatewith status lifecycle:running->idle(isIdle flag) ->completed|failed|killed. The task state includesonIdleCallbacksfor efficient notification without polling (used bywaitForTeammatesToBecomeIdle). (src/tasks/InProcessTeammateTask/types.ts)
Relations
- implements
TeammateExecutorinterface -- the unified backend abstraction for teammate lifecycle (spawn, sendMessage, terminate, kill, isActive), shared with PaneBackendExecutor (tmux/iTerm2). (src/utils/swarm/backends/types.ts) - peer-of
LocalAgentTask(ent-20260410-524a894e3356) -- another execution model; LocalAgentTask runs as a separate OS process for full isolation, InProcessTeammate shares the process for lower overhead. - peer-of
RemoteAgentTask(ent-20260410-3bfef9371d5f) -- another execution model; RemoteAgentTask runs in Anthropic's cloud infrastructure. - uses
runAgent()-- the same agent execution loop used by AgentTool for subagents, ensuring behavioral parity. (src/tools/AgentTool/runAgent.ts) - uses
AsyncLocalStorage-- Node.js primitive fromasync_hooksfor per-async-chain context isolation. - selected-by
getTeammateExecutor()inregistry.ts-- the factory that returns eitherInProcessBackendorPaneBackendExecutorbased on environment detection and configuration. - spawned-by
handleSpawn()inspawnMultiAgent.ts-- the top-level spawn dispatcher that routes to in-process or pane-based backends.
Sources
src/utils/teammateContext.ts-- TeammateContext type definition, AsyncLocalStorage instance,runWithTeammateContext(),createTeammateContext()src/utils/agentContext.ts-- AgentContext (SubagentContext | TeammateAgentContext), second AsyncLocalStorage layer for analyticssrc/utils/swarm/spawnInProcess.ts--spawnInProcessTeammate()spawn logic,killInProcessTeammate()force-kill logicsrc/utils/swarm/inProcessRunner.ts--runInProcessTeammate()continuous prompt loop,startInProcessTeammate()fire-and-forget entry point, permission delegation, mailbox polling, auto-compaction, task claimingsrc/utils/swarm/backends/InProcessBackend.ts--InProcessBackendclass implementingTeammateExecutor, spawn/sendMessage/terminate/kill/isActive methodssrc/utils/swarm/backends/types.ts--TeammateExecutorinterface,BackendTypeunion ('tmux' | 'iterm2' | 'in-process'),TeammateSpawnConfigsrc/utils/swarm/backends/registry.ts--isInProcessEnabled()detection logic,getTeammateExecutor()factory, fallback behaviorsrc/tasks/InProcessTeammateTask/InProcessTeammateTask.tsx-- Task interface implementation,requestTeammateShutdown(),appendTeammateMessage(),injectUserMessageToTeammate()src/tasks/InProcessTeammateTask/types.ts--InProcessTeammateTaskStatetype,TeammateIdentity,TEAMMATE_MESSAGES_UI_CAP(50),appendCappedMessage()src/utils/teammate.ts-- identity resolution helpers (priority: AsyncLocalStorage > dynamicTeamContext > env vars),isTeammate(),isTeamLead(),waitForTeammatesToBecomeIdle()src/utils/inProcessTeammateHelpers.ts-- plan approval handling, permission response detection helperssrc/utils/swarm/teammatePromptAddendum.ts--TEAMMATE_SYSTEM_PROMPT_ADDENDUMconstantsrc/constants/tools.ts--IN_PROCESS_TEAMMATE_ALLOWED_TOOLS,ASYNC_AGENT_ALLOWED_TOOLS,ALL_AGENT_DISALLOWED_TOOLSsrc/tools/shared/spawnMultiAgent.ts-- top-levelhandleSpawn()dispatcher,handleSpawnInProcess()in-process path