Welcome to the second chapter of the Shared project tutorial!
In the previous chapter, Team Context & Identity Management, we learned how to give our agents unique names, IDs, and colors. Now that our agent has an ID badge, we need a way to actually bring them into existence and give them a job.
Imagine you run a company. When you hire a new employee, you don't just point at a desk and say "Go." You need a Hiring Manager to handle the onboarding logistics:
The Agent Spawning Orchestrator is that Hiring Manager. It acts as a central dispatcher. It takes a simple request ("I want a Coder agent") and handles all the complex setup required to launch a fully functional agent process.
The orchestrator's job can be broken down into three specific tasks.
The system needs a "Job Description." This includes the agent's name, their initial instructions (prompt), and what kind of permissions they should have.
Not all agents run the same way.
The orchestrator checks your settings and decides which "Backend" to use.
When a new agent spawns, it needs to inherit settings from the main application. The orchestrator packs a "backpack" of environment variables and CLI flags (like --dangerously-skip-permissions) so the new agent behaves correctly.
Before looking at the code, let's visualize the "Hiring Process."
The heart of this system is in spawnMultiAgent.ts. The main entry point is a function called spawnTeammate.
We start with spawnTeammate. Its only job is to receive the configuration and pass it to the main handler.
// spawnMultiAgent.ts
export async function spawnTeammate(
config: SpawnTeammateConfig,
context: ToolUseContext,
): Promise<{ data: SpawnOutput }> {
// Pass the request to the main logic handler
return handleSpawn(config, context)
}
Explanation: This is the public face of the Hiring Manager. Other parts of the code call this simple function without worrying about how the spawning happens.
The handleSpawn function determines where the agent will live. It checks if we are allowed to run "In-Process" (background) agents or if we need a visible terminal pane.
async function handleSpawn(input: SpawnInput, context: ToolUseContext) {
// Check if we are configured to run agents inside the main process
if (isInProcessEnabled()) {
return handleSpawnInProcess(input, context)
}
// If not, use the visual split-pane method (Tmux or iTerm)
const useSplitPane = input.use_splitpane !== false
if (useSplitPane) {
return handleSpawnSplitPane(input, context)
}
// Legacy fallback
return handleSpawnSeparateWindow(input, context)
}
Explanation: This acts like a traffic switch. It routes the request to the correct specialist. We will cover the specific handlers in Execution Backend Strategies.
One of the most critical jobs of the Orchestrator is ensuring the new agent inherits settings from the parent. For example, if you are running in "Auto-Approve" mode, your sub-agents should probably be in that mode too.
The function buildInheritedCliFlags constructs the command line arguments for the new agent.
function buildInheritedCliFlags(options: { permissionMode?: PermissionMode }) {
const flags: string[] = []
// If the parent allows bypassing permissions, pass that flag to the child
if (options.permissionMode === 'bypassPermissions') {
flags.push('--dangerously-skip-permissions')
}
// If the user specified a specific AI model, pass that along too
const modelOverride = getMainLoopModelOverride()
if (modelOverride) {
flags.push(`--model ${quote([modelOverride])}`)
}
return flags.join(' ')
}
Explanation: This ensures consistency. It prevents a situation where the main agent is smart (using Claude 3.5 Sonnet) but spawns a "dumb" agent (using a default model) because it forgot to pass the configuration down.
Finally, inside the specific handlers (like handleSpawnSplitPane), the Orchestrator assembles the final command string. It combines the Binary (the executable), the Identity (from Chapter 1), and the Backpack (Flags).
// Inside handleSpawnSplitPane...
// 1. Where is the program?
const binaryPath = getTeammateCommand()
// 2. Who is this agent? (Identity)
const identityArgs = `--agent-id ${quote([teammateId])} --team-name ${quote([teamName])}`
// 3. What settings do they need? (The Backpack)
const flagsStr = buildInheritedCliFlags({ ... })
// 4. Combine into one executable command
const spawnCommand = `env ${envVars} ${binaryPath} ${identityArgs} ${flagsStr}`
Explanation: This spawnCommand is the final product of the Orchestrator. It is a complete, executable shell command that defines exactly who the agent is and how it should behave.
In this chapter, we explored the Agent Spawning Orchestrator:
spawnTeammate.The Orchestrator has now prepared the command and selected the destination. But how does that command actually get executed? How do we talk to a terminal window or a background process?
We will answer that in the next chapter.
Next Chapter: Execution Backend Strategies
Generated by Code IQ