spawnUtils.ts
utils/swarm/spawnUtils.ts
No strong subsystem tag
147
Lines
5202
Bytes
3
Exports
6
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with general runtime concerns. It contains 147 lines, 6 detected imports, and 3 detected exports.
Important relationships
Detected exports
getTeammateCommandbuildInheritedCliFlagsbuildInheritedEnvVars
Keywords
teammatesflagspushpermissionmodeprocesspropagatemodequotepermissionsteammate_command_env_var
Detected imports
../../bootstrap/state.js../bash/shellQuote.js../bundledMode.js../permissions/PermissionMode.js./backends/teammateModeSnapshot.js./constants.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
/**
* Shared utilities for spawning teammates across different backends.
*/
import {
getChromeFlagOverride,
getFlagSettingsPath,
getInlinePlugins,
getMainLoopModelOverride,
getSessionBypassPermissionsMode,
} from '../../bootstrap/state.js'
import { quote } from '../bash/shellQuote.js'
import { isInBundledMode } from '../bundledMode.js'
import type { PermissionMode } from '../permissions/PermissionMode.js'
import { getTeammateModeFromSnapshot } from './backends/teammateModeSnapshot.js'
import { TEAMMATE_COMMAND_ENV_VAR } from './constants.js'
/**
* Gets the command to use for spawning teammate processes.
* Uses TEAMMATE_COMMAND_ENV_VAR if set, otherwise falls back to the
* current process executable path.
*/
export function getTeammateCommand(): string {
if (process.env[TEAMMATE_COMMAND_ENV_VAR]) {
return process.env[TEAMMATE_COMMAND_ENV_VAR]
}
return isInBundledMode() ? process.execPath : process.argv[1]!
}
/**
* Builds CLI flags to propagate from the current session to spawned teammates.
* This ensures teammates inherit important settings like permission mode,
* model selection, and plugin configuration from their parent.
*
* @param options.planModeRequired - If true, don't inherit bypass permissions (plan mode takes precedence)
* @param options.permissionMode - Permission mode to propagate
*/
export function buildInheritedCliFlags(options?: {
planModeRequired?: boolean
permissionMode?: PermissionMode
}): string {
const flags: string[] = []
const { planModeRequired, permissionMode } = options || {}
// Propagate permission mode to teammates, but NOT if plan mode is required
// Plan mode takes precedence over bypass permissions for safety
if (planModeRequired) {
// Don't inherit bypass permissions when plan mode is required
} else if (
permissionMode === 'bypassPermissions' ||
getSessionBypassPermissionsMode()
) {
flags.push('--dangerously-skip-permissions')
} else if (permissionMode === 'acceptEdits') {
flags.push('--permission-mode acceptEdits')
}
// Propagate --model if explicitly set via CLI
const modelOverride = getMainLoopModelOverride()
if (modelOverride) {
flags.push(`--model ${quote([modelOverride])}`)
}
// Propagate --settings if set via CLI
const settingsPath = getFlagSettingsPath()
if (settingsPath) {
flags.push(`--settings ${quote([settingsPath])}`)
}
// Propagate --plugin-dir for each inline plugin
const inlinePlugins = getInlinePlugins()
for (const pluginDir of inlinePlugins) {
flags.push(`--plugin-dir ${quote([pluginDir])}`)
}
// Propagate --teammate-mode so tmux teammates use the same mode as leader
const sessionMode = getTeammateModeFromSnapshot()
flags.push(`--teammate-mode ${sessionMode}`)
// Propagate --chrome / --no-chrome if explicitly set on the CLI
const chromeFlagOverride = getChromeFlagOverride()
if (chromeFlagOverride === true) {
flags.push('--chrome')
} else if (chromeFlagOverride === false) {
flags.push('--no-chrome')
}
return flags.join(' ')
}
/**
* Environment variables that must be explicitly forwarded to tmux-spawned
* teammates. Tmux may start a new login shell that doesn't inherit the
* parent's env, so we forward any that are set in the current process.
*/
const TEAMMATE_ENV_VARS = [
// API provider selection — without these, teammates default to firstParty
// and send requests to the wrong endpoint (GitHub issue #23561)
'CLAUDE_CODE_USE_BEDROCK',
'CLAUDE_CODE_USE_VERTEX',
'CLAUDE_CODE_USE_FOUNDRY',
// Custom API endpoint
'ANTHROPIC_BASE_URL',
// Config directory override
'CLAUDE_CONFIG_DIR',
// CCR marker — teammates need this for CCR-aware code paths. Auth finds
// its own way via /home/claude/.claude/remote/.oauth_token regardless;
// the FD env var wouldn't help (pipe FDs don't cross tmux).
'CLAUDE_CODE_REMOTE',
// Auto-memory gate (memdir/paths.ts) checks REMOTE && !MEMORY_DIR to
// disable memory on ephemeral CCR filesystems. Forwarding REMOTE alone
// would flip teammates to memory-off when the parent has it on.
'CLAUDE_CODE_REMOTE_MEMORY_DIR',
// Upstream proxy — the parent's MITM relay is reachable from teammates
// (same container network). Forward the proxy vars so teammates route
// customer-configured upstream traffic through the relay for credential
// injection. Without these, teammates bypass the proxy entirely.
'HTTPS_PROXY',
'https_proxy',
'HTTP_PROXY',
'http_proxy',
'NO_PROXY',
'no_proxy',
'SSL_CERT_FILE',
'NODE_EXTRA_CA_CERTS',
'REQUESTS_CA_BUNDLE',
'CURL_CA_BUNDLE',
] as const
/**
* Builds the `env KEY=VALUE ...` string for teammate spawn commands.
* Always includes CLAUDECODE=1 and CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1,
* plus any provider/config env vars that are set in the current process.
*/
export function buildInheritedEnvVars(): string {
const envVars = ['CLAUDECODE=1', 'CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1']
for (const key of TEAMMATE_ENV_VARS) {
const value = process.env[key]
if (value !== undefined && value !== '') {
envVars.push(`${key}=${quote([value])}`)
}
}
return envVars.join(' ')
}