@integrity-labs/agt-cli 0.15.34 → 0.15.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/agt.js CHANGED
@@ -48,7 +48,7 @@ import {
48
48
  success,
49
49
  table,
50
50
  warn
51
- } from "../chunk-6DIVYBKA.js";
51
+ } from "../chunk-TPR5XCFY.js";
52
52
 
53
53
  // src/bin/agt.ts
54
54
  import { join as join10 } from "path";
@@ -3732,7 +3732,7 @@ import { execFileSync, execSync } from "child_process";
3732
3732
  import { existsSync as existsSync5, realpathSync } from "fs";
3733
3733
  import chalk17 from "chalk";
3734
3734
  import ora15 from "ora";
3735
- var cliVersion = true ? "0.15.34" : "dev";
3735
+ var cliVersion = true ? "0.15.36" : "dev";
3736
3736
  async function fetchLatestVersion() {
3737
3737
  const host2 = getHost();
3738
3738
  if (!host2) return null;
@@ -4181,7 +4181,7 @@ function handleError(err) {
4181
4181
  }
4182
4182
 
4183
4183
  // src/bin/agt.ts
4184
- var cliVersion2 = true ? "0.15.34" : "dev";
4184
+ var cliVersion2 = true ? "0.15.36" : "dev";
4185
4185
  var program = new Command();
4186
4186
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
4187
4187
  program.hook("preAction", (thisCommand) => {
@@ -50,6 +50,9 @@ function buildAllowedTools(mcpServerNames) {
50
50
  function syncClaudeCredsToRoot() {
51
51
  if (platform() !== "linux") return true;
52
52
  if (typeof process.getuid !== "function" || process.getuid() !== 0) return true;
53
+ for (const filename of [".credentials.json", "credentials.json"]) {
54
+ if (existsSync(join("/root/.claude", filename))) return true;
55
+ }
53
56
  let sourcePath = null;
54
57
  try {
55
58
  const entries = readdirSync("/home", { withFileTypes: true });
@@ -178,7 +181,7 @@ function spawnSession(config, session) {
178
181
  if (claudeAuthMode === "subscription") {
179
182
  const credsSynced = syncClaudeCredsToRoot();
180
183
  if (!credsSynced && platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0) {
181
- log(`[persistent-session] No Claude Code credentials found under /home/*. Run 'claude /login' on the host first.`);
184
+ log(`[persistent-session] No Claude Code credentials found under /root/.claude or /home/*. Pair via browser from the host page, or run 'claude /login' on the host.`);
182
185
  }
183
186
  } else {
184
187
  const claudeDir = join(homedir(), ".claude");
@@ -549,4 +552,4 @@ export {
549
552
  stopAllSessionsAndWait,
550
553
  getProjectDir
551
554
  };
552
- //# sourceMappingURL=chunk-66ZLF2MI.js.map
555
+ //# sourceMappingURL=chunk-AFUG4KD3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/persistent-session.ts","../src/lib/mcp-sanitize.ts","../src/lib/claude-tools.ts"],"sourcesContent":["/**\n * Persistent session manager for Claude Code agents.\n *\n * Hybrid approach:\n * - **tmux** for the interactive session (channels like Slack/Telegram\n * require a real TTY that only tmux provides)\n * - **acpx** for task injection (reliable prompt delivery via --no-wait,\n * avoids the tmux send-keys paste-not-submitting issue)\n *\n * On manager restart, detects existing tmux sessions and reattaches\n * without creating duplicates.\n */\n\nimport { spawn, execSync, execFileSync, type ChildProcess } from 'node:child_process';\nimport { join, dirname } from 'node:path';\nimport { homedir, platform } from 'node:os';\nimport { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync, chmodSync, copyFileSync, rmSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { sanitizeMcpJson } from './mcp-sanitize.js';\nimport { buildAllowedTools } from './claude-tools.js';\n\n/**\n * When running as root on Linux, the tmux-spawned claude process reads\n * ~/.claude/.credentials.json from /root. But operators log in via `claude\n * /login` as ssm-user or ec2-user, leaving creds under their own home.\n * Copy the first valid creds file into /root/.claude so claude (running as\n * root inside tmux) finds them. Idempotent — safe to call on every spawn.\n *\n * Returns true if a copy was made (or the file is already up to date),\n * false if no creds could be found at all.\n */\nfunction syncClaudeCredsToRoot(): boolean {\n if (platform() !== 'linux') return true;\n if (typeof process.getuid !== 'function' || process.getuid() !== 0) return true;\n\n // Fast path: pair-via-browser writes creds directly to /root/.claude\n // (the throwaway claude session runs as root). If they're already\n // there, no sync needed.\n for (const filename of ['.credentials.json', 'credentials.json']) {\n if (existsSync(join('/root/.claude', filename))) return true;\n }\n\n // Legacy path: an operator ran `claude /login` interactively as\n // ec2-user. Find any /home/*/.claude credentials and copy them up.\n let sourcePath: string | null = null;\n try {\n const entries = readdirSync('/home', { withFileTypes: true });\n outer: for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n // Both filenames Claude Code has historically used — keep in sync\n // with findClaudeCredentialsPaths() in claude-auth-detect.ts.\n for (const filename of ['.credentials.json', 'credentials.json']) {\n const candidate = join('/home', entry.name, '.claude', filename);\n if (existsSync(candidate)) {\n sourcePath = candidate;\n break outer;\n }\n }\n }\n } catch { /* no /home or unreadable — fall through */ }\n\n if (!sourcePath) return false;\n\n const targetDir = '/root/.claude';\n // Preserve source filename so the resulting file matches what claude's\n // reader expects (it accepts either '.credentials.json' or 'credentials.json').\n const sourceFilename = sourcePath.endsWith('credentials.json') && !sourcePath.endsWith('.credentials.json')\n ? 'credentials.json'\n : '.credentials.json';\n const targetPath = join(targetDir, sourceFilename);\n try {\n if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true, mode: 0o700 });\n copyFileSync(sourcePath, targetPath);\n chmodSync(targetPath, 0o600);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the claude binary to an absolute path. The manager runs under a\n * minimal PATH (cloud-init root env) that doesn't include\n * /home/linuxbrew/.linuxbrew/bin, so a bare `claude` reference in the tmux\n * shell fails immediately — session exits, manager sees it as \"unhealthy\",\n * restarts, loops forever.\n *\n * Cached at first call: claude's location doesn't change between cycles,\n * and `which` spawns aren't free.\n */\nlet cachedClaudePath: string | null = null;\nexport function resolveClaudeBinary(): string {\n if (cachedClaudePath) return cachedClaudePath;\n // Operator override: honour CLAUDE_PATH for non-standard installs.\n const override = process.env.CLAUDE_PATH;\n if (override && existsSync(override)) {\n cachedClaudePath = override;\n return override;\n }\n // Try PATH first — respects an operator's custom install.\n try {\n const out = execSync('which claude 2>/dev/null', { encoding: 'utf-8' }).trim();\n if (out && existsSync(out)) {\n cachedClaudePath = out;\n return out;\n }\n } catch { /* fall through to canonical paths */ }\n const candidates = [\n '/home/linuxbrew/.linuxbrew/bin/claude',\n '/opt/homebrew/bin/claude',\n '/usr/local/bin/claude',\n ];\n for (const p of candidates) {\n if (existsSync(p)) {\n cachedClaudePath = p;\n return p;\n }\n }\n // Last resort — let the shell fail so logs show the missing binary.\n return 'claude';\n}\n\n/**\n * Collect MCP server names from the project .mcp.json to build the\n * --allowedTools pattern for tool isolation.\n */\nfunction collectMcpServerNames(mcpConfigPath: string): string[] {\n if (!existsSync(mcpConfigPath)) return [];\n try {\n const data = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));\n const servers = data.mcpServers as Record<string, unknown> | undefined;\n return servers ? Object.keys(servers) : [];\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// acpx binary resolver (used for task injection only)\n// ---------------------------------------------------------------------------\n\nlet _acpxBin: string | null = null;\nfunction getAcpxBin(): string {\n if (_acpxBin) return _acpxBin;\n\n // Walk up from this file to find node_modules/.bin/acpx.\n // Covers: dev (src/lib → ../../node_modules), built (dist/lib → ../../node_modules),\n // and npm global install (lib/node_modules/@scope/pkg/dist/lib → ../../node_modules).\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n let dir = moduleDir;\n for (let i = 0; i < 6; i++) {\n const candidate = join(dir, 'node_modules', '.bin', 'acpx');\n if (existsSync(candidate)) {\n _acpxBin = candidate;\n return _acpxBin;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n try {\n execSync('which acpx', { stdio: 'ignore' });\n _acpxBin = 'acpx';\n return _acpxBin;\n } catch {\n // acpx not available — injection will fall back to tmux send-keys\n return '';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Types and state\n// ---------------------------------------------------------------------------\n\nexport interface PersistentSessionConfig {\n codeName: string;\n agentId: string;\n projectDir: string;\n mcpConfigPath: string;\n claudeMdPath: string;\n channels: string[];\n devChannels: string[];\n apiHost?: string;\n /**\n * Operator-configured Claude Code auth mode. 'subscription' (default) runs\n * `syncClaudeCredsToRoot()` so claude finds OAuth creds under /root/.claude.\n * 'api_key' puts ANTHROPIC_API_KEY into the spawn env AND deletes any\n * stored OAuth creds so the two auth paths are mutually exclusive.\n */\n claudeAuthMode?: 'subscription' | 'api_key';\n /** Decrypted Anthropic API key. Only used when claudeAuthMode === 'api_key'. */\n anthropicApiKey?: string | null;\n log: (msg: string) => void;\n}\n\nexport interface PersistentSession {\n codeName: string;\n startedAt: number | null;\n restartCount: number;\n status: 'starting' | 'running' | 'stopped' | 'crashed';\n}\n\nconst sessions = new Map<string, PersistentSession>();\n\n// ---------------------------------------------------------------------------\n// Session lifecycle (tmux-based)\n// ---------------------------------------------------------------------------\n\nexport function startPersistentSession(config: PersistentSessionConfig): PersistentSession {\n const existing = sessions.get(config.codeName);\n if (existing && existing.status === 'running') {\n return existing;\n }\n\n // Backoff on repeated crashes\n const restartCount = existing?.restartCount ?? 0;\n if (existing?.status === 'crashed' && existing.startedAt) {\n const backoffMs = Math.min(5000 * Math.pow(2, restartCount), 60_000);\n if (Date.now() - existing.startedAt < backoffMs) {\n return existing;\n }\n }\n\n const session: PersistentSession = {\n codeName: config.codeName,\n startedAt: null,\n restartCount,\n status: 'starting',\n };\n sessions.set(config.codeName, session);\n\n spawnSession(config, session);\n return session;\n}\n\nfunction spawnSession(config: PersistentSessionConfig, session: PersistentSession): void {\n const { codeName, projectDir, mcpConfigPath, claudeMdPath, channels, devChannels, apiHost, log } = config;\n const claudeAuthMode = config.claudeAuthMode ?? 'subscription';\n const tmuxSession = `agt-${codeName}`;\n\n log(`[persistent-session] Starting tmux session '${tmuxSession}' for '${codeName}' (auth=${claudeAuthMode})`);\n\n try {\n sanitizeMcpJson(mcpConfigPath, apiHost);\n\n // Also write acpx config for task injection\n writeAcpxConfig(config);\n\n // Kill any existing tmux session (clean slate)\n try {\n execSync(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: 'ignore' });\n } catch { /* no existing session */ }\n\n // When running as root, claude looks at $HOME/.claude/.credentials.json\n // Auth mode branch (mutually exclusive — never leave both channels armed):\n //\n // subscription: sync OAuth creds from /home/*/.claude into /root/.claude\n // (idempotent). Do NOT set ANTHROPIC_API_KEY in env.\n // api_key: DELETE any /root/.claude creds so claude can't fall\n // back to a stale OAuth session, then inject\n // ANTHROPIC_API_KEY into the spawn env below.\n //\n // Leaving both present is the \"confused deputy\" path: claude's internal\n // precedence between ANTHROPIC_API_KEY and OAuth has changed between\n // versions and is undocumented. Keep exactly one channel live.\n if (claudeAuthMode === 'subscription') {\n const credsSynced = syncClaudeCredsToRoot();\n if (!credsSynced && platform() === 'linux' && typeof process.getuid === 'function' && process.getuid() === 0) {\n log(`[persistent-session] No Claude Code credentials found under /root/.claude or /home/*. Pair via browser from the host page, or run 'claude /login' on the host.`);\n }\n } else {\n // api_key mode — purge subscription creds under the current user's\n // home. Previously this was hardcoded to /root/.claude, which missed\n // non-root runs and macOS dev setups — letting OAuth creds silently\n // override the api_key in those environments. homedir() is what\n // claude-code itself reads, so that's the directory to clear.\n const claudeDir = join(homedir(), '.claude');\n for (const filename of ['.credentials.json', 'credentials.json']) {\n const p = join(claudeDir, filename);\n if (existsSync(p)) {\n try {\n rmSync(p, { force: true });\n log(`[persistent-session] Removed ${p} (api_key mode active — preventing OAuth fallback)`);\n } catch { /* non-fatal */ }\n }\n }\n if (!config.anthropicApiKey) {\n log(`[persistent-session] api_key mode but no anthropicApiKey passed. Session will fail auth.`);\n }\n }\n\n // Build claude args\n const args: string[] = [];\n if (channels.length > 0) args.push('--channels', ...channels);\n if (devChannels.length > 0) args.push('--dangerously-load-development-channels', ...devChannels);\n args.push('--mcp-config', mcpConfigPath);\n if (existsSync(claudeMdPath)) args.push('--system-prompt-file', claudeMdPath);\n args.push('--allow-dangerously-skip-permissions');\n args.push('--dangerously-skip-permissions');\n args.push('--strict-mcp-config');\n args.push('--name', tmuxSession);\n\n // Restrict tools to only the agent's configured MCP servers + built-in tools.\n // Without this, agents inherit the user's personal MCPs (Gmail, Calendar, etc.)\n const mcpServerNames = collectMcpServerNames(mcpConfigPath);\n args.push('--allowedTools', buildAllowedTools(mcpServerNames));\n\n // NOTE: CLAUDE_CODE_SIMPLE=1 blocks account plugins BUT also breaks\n // channel auth (Slack/Telegram require claude.ai OAuth). Instead, rely on\n // --strict-mcp-config + --allowedTools for tool isolation. Account plugins\n // may appear in the tool list but --allowedTools prevents calling them.\n //\n // IS_SANDBOX=1 bypasses claude's refusal to run under root/sudo with\n // --dangerously-skip-permissions. Dedicated EC2 hosts running only\n // agent workloads are effectively sandboxed (org-scoped VPC, no inbound,\n // no other tenants). Without this, the tmux session exits immediately\n // with \"cannot be used with root/sudo privileges for security reasons\".\n let envPrefix = 'IS_SANDBOX=1 ';\n const envIntegrationsPath = join(projectDir, '.env.integrations');\n if (existsSync(envIntegrationsPath)) {\n try {\n const envContent = readFileSync(envIntegrationsPath, 'utf-8');\n const envVars = envContent.split('\\n')\n .filter((line: string) => line && !line.startsWith('#') && line.includes('='))\n .map((line: string) => {\n const eqIdx = line.indexOf('=');\n const key = line.slice(0, eqIdx);\n const value = line.slice(eqIdx + 1);\n // Always quote values to prevent shell injection\n return `${key}=${JSON.stringify(value)}`;\n })\n .join(' ');\n if (envVars) envPrefix = `IS_SANDBOX=1 ${envVars} `;\n } catch { /* non-fatal */ }\n }\n\n // ANTHROPIC_API_KEY is passed via `tmux new-session -e` so it lands in\n // the session shell's env without ever appearing in the claude shell's\n // argv — `ps aux` on the long-running `bash -c \"claude ...\"` process\n // would otherwise expose the raw key for the session's lifetime.\n // The `-e` flag's exposure is bounded to the new-session invocation,\n // which exits in well under a second.\n const tmuxSessionEnvArgs: string[] = [];\n if (claudeAuthMode === 'api_key' && config.anthropicApiKey) {\n tmuxSessionEnvArgs.push('-e', `ANTHROPIC_API_KEY=${config.anthropicApiKey}`);\n }\n\n const initPrompt = 'You are now online. Say \"Ready.\" and wait for incoming messages. Do not run any tools or load any data until a message arrives.';\n const claudeBin = resolveClaudeBinary();\n const claudeCmd = `${envPrefix}${JSON.stringify(claudeBin)} ${JSON.stringify(initPrompt)} ${args.map(a => (a.includes(' ') || a.includes('*')) ? JSON.stringify(a) : a).join(' ')}`;\n\n // Start tmux session with claude in it\n const child = spawn('tmux', [\n 'new-session', '-d', '-s', tmuxSession, '-c', projectDir,\n ...tmuxSessionEnvArgs, claudeCmd,\n ], {\n cwd: projectDir,\n stdio: ['ignore', 'pipe', 'pipe'],\n env: process.env,\n });\n\n child.on('close', (code) => {\n if (code !== 0) {\n log(`[persistent-session] Failed to create tmux session for '${codeName}' (exit ${code})`);\n session.status = 'crashed';\n session.startedAt = Date.now();\n session.restartCount++;\n return;\n }\n log(`[persistent-session] tmux session '${tmuxSession}' created for '${codeName}'`);\n\n // Auto-accept startup dialogs\n acceptDialogs(tmuxSession, codeName, log).catch(() => {});\n });\n\n child.on('error', (err) => {\n log(`[persistent-session] Failed to start tmux for '${codeName}': ${err.message}`);\n session.status = 'crashed';\n session.startedAt = Date.now();\n session.restartCount++;\n });\n\n session.startedAt = Date.now();\n session.status = 'running';\n session.restartCount = 0;\n } catch (err) {\n log(`[persistent-session] Failed to start session for '${codeName}': ${(err as Error).message}`);\n session.status = 'crashed';\n session.startedAt = Date.now();\n session.restartCount++;\n }\n}\n\nasync function acceptDialogs(tmuxSession: string, codeName: string, log: (msg: string) => void): Promise<void> {\n for (let i = 0; i < 15; i++) {\n await new Promise((r) => setTimeout(r, 2000));\n try {\n const screen = execSync(`tmux capture-pane -t ${tmuxSession} -p 2>/dev/null`, { encoding: 'utf-8' });\n\n // First-run theme picker. Has to be dismissed BEFORE the\n // generic \"❯ no Enter to confirm\" exit branch below, since the\n // picker's selected row also renders with `❯`. Accept the\n // highlighted default (Dark mode); operator can change later\n // via /theme.\n if (\n screen.includes('Choose the text style') ||\n (screen.includes('Dark mode') && screen.includes('Light mode'))\n ) {\n execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: 'ignore' });\n log(`[persistent-session] Auto-accepted theme picker for '${codeName}'`);\n continue;\n }\n if (screen.includes('Yes, I trust this folder')) {\n execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: 'ignore' });\n log(`[persistent-session] Auto-accepted workspace trust for '${codeName}'`);\n continue;\n }\n if (screen.includes('I am using this for local development')) {\n execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: 'ignore' });\n log(`[persistent-session] Auto-accepted dev channels for '${codeName}'`);\n continue;\n }\n if (screen.includes('Enter to confirm') && screen.includes('MCP')) {\n execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: 'ignore' });\n log(`[persistent-session] Auto-accepted MCP servers for '${codeName}'`);\n continue;\n }\n if (screen.includes('Yes, I accept') && screen.includes('Bypass Permissions')) {\n execSync(`tmux send-keys -t ${tmuxSession} 2`, { stdio: 'ignore' });\n await new Promise((r) => setTimeout(r, 300));\n execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: 'ignore' });\n log(`[persistent-session] Auto-accepted bypass permissions for '${codeName}'`);\n continue;\n }\n if (screen.includes('❯') && !screen.includes('Enter to confirm')) {\n log(`[persistent-session] Session ready for '${codeName}' — no more dialogs`);\n break;\n }\n } catch { break; }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Task injection (acpx preferred, tmux send-keys fallback)\n// ---------------------------------------------------------------------------\n\nexport async function injectMessage(\n codeName: string,\n type: 'task' | 'chat' | 'system',\n content: string,\n meta?: Record<string, string>,\n log?: (msg: string) => void,\n): Promise<boolean> {\n const _log = log ?? ((_: string) => {});\n const session = sessions.get(codeName);\n if (!session || session.status !== 'running') {\n _log(`[inject] SKIP '${codeName}' — session ${session ? `status=${session.status}` : 'not found in Map'}`);\n return false;\n }\n\n const prefix = meta?.task_name ? `[Task: ${meta.task_name}] ` : '';\n const text = prefix + content;\n const projectDir = getProjectDir(codeName);\n\n // Preferred: use acpx exec for reliable injection (no paste issues).\n // Fire-and-forget — spawn detached so the manager loop isn't blocked.\n const acpx = getAcpxBin();\n if (acpx) {\n try {\n // Write prompt to temp file to avoid shell escaping issues\n const tmpDir = join(projectDir, '.claude');\n mkdirSync(tmpDir, { recursive: true });\n const tmpFile = join(tmpDir, '.agt-inject-prompt.txt');\n writeFileSync(tmpFile, text);\n\n _log(`[inject] acpx exec (fire-and-forget): cwd=${projectDir}, file=${tmpFile}`);\n const child = spawn(acpx, ['claude', 'exec', '-f', tmpFile], {\n cwd: projectDir,\n stdio: 'ignore',\n detached: true,\n });\n child.on('error', (err) => {\n _log(`[inject] acpx spawn error for '${codeName}': ${err.message}`);\n });\n child.unref();\n return true;\n } catch (err) {\n _log(`[inject] acpx exec failed for '${codeName}': ${(err as Error).message}`);\n // Fall through to tmux\n }\n } else {\n _log(`[inject] acpx binary not found — falling back to tmux send-keys`);\n }\n\n // Fallback: tmux send-keys (may have paste issues with long text)\n // Use execFileSync to avoid shell injection — text passed as literal arg\n try {\n execFileSync('tmux', ['send-keys', '-t', `agt-${codeName}`, text, 'Enter'], { stdio: 'ignore' });\n // tmux send-keys doesn't guarantee submission — return false so caller\n // doesn't advance scheduler state on an unverified keystroke\n _log(`[inject] tmux send-keys sent for '${codeName}' — unverified (returning false)`);\n return false;\n } catch (err) {\n _log(`[inject] tmux send-keys failed for '${codeName}': ${(err as Error).message}`);\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Session management\n// ---------------------------------------------------------------------------\n\nexport function stopPersistentSession(codeName: string, log: (msg: string) => void): void {\n const session = sessions.get(codeName);\n if (!session) return;\n\n log(`[persistent-session] Stopping session for '${codeName}'`);\n session.status = 'stopped';\n\n try {\n execSync(`tmux kill-session -t agt-${codeName} 2>/dev/null`, { stdio: 'ignore' });\n } catch { /* session may already be dead */ }\n\n // Also close any acpx session\n try {\n const acpx = getAcpxBin();\n if (acpx) {\n execFileSync(acpx, ['claude', 'sessions', 'close', `agt-${codeName}`], {\n cwd: getProjectDir(codeName),\n timeout: 5_000,\n stdio: 'ignore',\n });\n }\n } catch { /* non-fatal */ }\n\n sessions.delete(codeName);\n}\n\nexport function getSessionState(codeName: string): PersistentSession | null {\n return sessions.get(codeName) ?? null;\n}\n\n/**\n * Check if a persistent session is healthy.\n * Uses tmux has-session to check if the tmux session exists.\n * Also detects sessions from previous manager runs (not in the Map).\n */\nexport function isSessionHealthy(codeName: string): boolean {\n const tmuxSession = `agt-${codeName}`;\n\n // Check if tmux session exists\n try {\n execSync(`tmux has-session -t ${tmuxSession} 2>/dev/null`, { stdio: 'ignore' });\n } catch {\n // tmux session doesn't exist — mark as crashed but don't increment\n // restartCount here (that happens in spawnSession on actual failure)\n const session = sessions.get(codeName);\n if (session && session.status === 'running') {\n session.status = 'crashed';\n }\n return false;\n }\n\n // tmux session exists — ensure it's tracked in the Map\n if (!sessions.has(codeName)) {\n sessions.set(codeName, {\n codeName,\n startedAt: Date.now(),\n restartCount: 0,\n status: 'running',\n });\n }\n\n const session = sessions.get(codeName)!;\n if (session.status !== 'running') {\n session.status = 'running';\n }\n\n return true;\n}\n\nexport function resetRestartCount(codeName: string): void {\n const session = sessions.get(codeName);\n if (session) session.restartCount = 0;\n}\n\n// ---------------------------------------------------------------------------\n// Diagnostics — collect session health info for remote debugging\n// ---------------------------------------------------------------------------\n\nexport interface SessionDiagnostics {\n codeName: string;\n status: 'running' | 'starting' | 'stopped' | 'crashed' | 'unknown';\n startedAt: string | null;\n restartCount: number;\n tmuxAlive: boolean;\n screenCapture: string | null; // last N lines from tmux pane\n launchArgs: string | null; // process args\n channelStatus: string | null; // extracted from screen capture\n}\n\nexport function collectDiagnostics(codeNames: string[]): SessionDiagnostics[] {\n return codeNames.map((codeName) => {\n const session = sessions.get(codeName);\n const tmuxSession = `agt-${codeName}`;\n let tmuxAlive = false;\n let screenCapture: string | null = null;\n let launchArgs: string | null = null;\n let channelStatus: string | null = null;\n\n // Check tmux session (execFileSync to avoid shell injection)\n try {\n execFileSync('tmux', ['has-session', '-t', tmuxSession], { stdio: 'ignore' });\n tmuxAlive = true;\n } catch { /* session doesn't exist */ }\n\n // Capture last 30 lines from tmux pane\n if (tmuxAlive) {\n try {\n screenCapture = execFileSync('tmux', ['capture-pane', '-t', tmuxSession, '-p', '-S', '-30'], {\n encoding: 'utf-8',\n timeout: 3000,\n }).trim();\n } catch { /* non-fatal */ }\n }\n\n // Get process args via ps (safe — no user input in command)\n try {\n const psOutput = execFileSync('ps', ['aux'], { encoding: 'utf-8', timeout: 3000 });\n const line = psOutput.split('\\n').find((l) => l.includes(`agt-${codeName}`) && !l.includes('grep'));\n if (line) {\n const match = line.match(/claude\\s+.*/);\n launchArgs = match ? match[0].slice(0, 500) : null;\n }\n } catch { /* non-fatal */ }\n\n // Extract channel status from screen capture.\n // Only check the last 5 lines for current state — startup errors\n // may linger in scroll history but the agent could be healthy now.\n if (screenCapture) {\n const recentLines = screenCapture.split('\\n').slice(-5).join('\\n');\n const isIdle = recentLines.includes('❯');\n\n if (isIdle) {\n // Agent is at prompt — channels are likely working\n // Check full capture for persistent errors only\n if (screenCapture.includes('Channels require claude.ai authentication')) {\n channelStatus = 'error: auth required';\n } else {\n channelStatus = 'ok';\n }\n } else if (recentLines.includes('CHANNEL_ERROR') || recentLines.includes('CLOSED')) {\n channelStatus = 'error: disconnected';\n } else if (recentLines.includes('no MCP server configured')) {\n channelStatus = 'error: MCP server not found';\n } else if (recentLines.includes('ignored')) {\n channelStatus = 'error: channels ignored';\n } else {\n channelStatus = 'ok';\n }\n }\n\n return {\n codeName,\n status: tmuxAlive\n ? (session?.status ?? 'running')\n : (session?.status === 'running' ? 'crashed' : session?.status ?? 'unknown'),\n startedAt: session?.startedAt ? new Date(session.startedAt).toISOString() : null,\n restartCount: session?.restartCount ?? 0,\n tmuxAlive,\n screenCapture: screenCapture ? screenCapture.slice(-2000) : null, // limit size\n launchArgs,\n channelStatus,\n };\n });\n}\n\nexport function stopAllSessions(log: (msg: string) => void): void {\n for (const codeName of sessions.keys()) {\n stopPersistentSession(codeName, log);\n }\n}\n\nexport async function stopAllSessionsAndWait(\n log: (msg: string) => void,\n opts: { timeoutMs: number },\n): Promise<void> {\n const codeNames = [...sessions.keys()];\n if (codeNames.length === 0) return;\n\n for (const codeName of codeNames) {\n stopPersistentSession(codeName, log);\n }\n\n await new Promise<void>((resolve) => setTimeout(resolve, Math.min(opts.timeoutMs, 2000)));\n}\n\nexport function getProjectDir(codeName: string): string {\n return join(homedir(), '.augmented', codeName, 'project');\n}\n\n// ---------------------------------------------------------------------------\n// acpx config (needed for prompt-based injection)\n// ---------------------------------------------------------------------------\n\nfunction writeAcpxConfig(config: PersistentSessionConfig): void {\n const {\n projectDir,\n mcpConfigPath,\n claudeMdPath,\n channels,\n devChannels,\n anthropicApiKey,\n } = config;\n const claudeAuthMode = config.claudeAuthMode ?? 'subscription';\n\n const claudeArgs: string[] = [];\n if (channels.length > 0) claudeArgs.push('--channels', ...channels);\n if (devChannels.length > 0) claudeArgs.push('--dangerously-load-development-channels', ...devChannels);\n claudeArgs.push('--mcp-config', mcpConfigPath);\n if (existsSync(claudeMdPath)) claudeArgs.push('--system-prompt-file', claudeMdPath);\n claudeArgs.push('--allow-dangerously-skip-permissions');\n claudeArgs.push('--dangerously-skip-permissions');\n claudeArgs.push('--strict-mcp-config');\n\n // Tool isolation for acpx exec (same as tmux session)\n const mcpServerNames2 = collectMcpServerNames(mcpConfigPath);\n claudeArgs.push('--allowedTools', buildAllowedTools(mcpServerNames2));\n\n // Write a wrapper script that sources .env.integrations then runs the ACP\n // adapter. This avoids ENAMETOOLONG from inlining long tokens (e.g. Xero\n // JWTs) into the command string, and works around acpx not supporting an\n // `env` field on agent configs.\n const acpCmd = `npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.map(a => (a.includes(' ') || a.includes('*')) ? JSON.stringify(a) : a).join(' ')}`;\n const envIntegrationsPath = join(projectDir, '.env.integrations');\n const wrapperPath = join(projectDir, '.claude', 'acpx-agent.sh');\n const wrapperLines = ['#!/usr/bin/env bash'];\n if (existsSync(envIntegrationsPath)) {\n wrapperLines.push(`set -a`, `source ${JSON.stringify(envIntegrationsPath)}`, `set +a`);\n }\n // Mirror the tmux-session auth branch: when mode=api_key we've purged the\n // OAuth creds under /root/.claude, so ACP task injections (acpx) also need\n // ANTHROPIC_API_KEY or every injected task fails auth. JSON.stringify is\n // shell-safe under bash for sk-ant-* tokens (no $/` chars).\n if (claudeAuthMode === 'api_key' && anthropicApiKey) {\n wrapperLines.push(`export ANTHROPIC_API_KEY=${JSON.stringify(anthropicApiKey)}`);\n }\n wrapperLines.push(`exec ${acpCmd}`);\n mkdirSync(join(projectDir, '.claude'), { recursive: true });\n writeFileSync(wrapperPath, wrapperLines.join('\\n') + '\\n', { mode: 0o755 });\n\n const acpxConfig = {\n defaultAgent: 'claude',\n defaultPermissions: 'approve-all',\n agents: {\n claude: {\n command: wrapperPath,\n },\n },\n };\n\n writeFileSync(join(projectDir, '.acpxrc.json'), JSON.stringify(acpxConfig, null, 2));\n}\n","/**\n * Sanitize a Claude Code .mcp.json file for compatibility.\n *\n * Fixes:\n * 1. Relative proxy URLs (e.g., /mcp-proxy/...) — resolved to absolute if\n * apiHost is provided, otherwise removed.\n * 2. URL-based entries (type: \"sse\") — converted to mcp-remote stdio bridge\n * since Claude Code doesn't support SSE MCP servers natively.\n *\n * Returns true if the file was modified.\n */\n\nimport { readFileSync, writeFileSync } from 'node:fs';\n\nexport function sanitizeMcpJson(\n mcpConfigPath: string,\n apiHost?: string,\n): boolean {\n try {\n const mcpRaw = JSON.parse(readFileSync(mcpConfigPath, 'utf-8'));\n const servers = mcpRaw.mcpServers as Record<string, Record<string, unknown>> | undefined;\n if (!servers) return false;\n\n let changed = false;\n for (const [key, val] of Object.entries(servers)) {\n if (typeof val?.url !== 'string') continue;\n\n // Resolve relative URLs\n if (val.url.startsWith('/')) {\n if (apiHost) {\n val.url = `${apiHost}${val.url}`;\n changed = true;\n } else {\n delete servers[key];\n changed = true;\n continue;\n }\n }\n\n // Convert URL-based entries to mcp-remote stdio bridge\n // Claude Code doesn't support type: \"sse\" natively\n const url = val.url as string;\n delete val.url;\n delete val.type;\n val.command = 'npx';\n val.args = ['-y', 'mcp-remote', url, '--allow-http'];\n changed = true;\n }\n\n if (changed) writeFileSync(mcpConfigPath, JSON.stringify(mcpRaw, null, 2));\n return changed;\n } catch {\n return false;\n }\n}\n","// Shared helper for building Claude Code's --allowedTools string (ENG-4487).\n//\n// The manager spawns claude in three modes: persistent tmux session, acpx\n// exec wrapper, and one-shot `claude -p` for scheduled tasks + webapp direct\n// chat. Each site used to hand-roll its own allowedTools list, which drifted:\n// the one-shot paths forgot Skill and Agent, so integration skills under\n// .claude/skills/integration-... were silently invisible during scheduled-task\n// execution. Agents produced apologetic \"no data sources connected\" outputs\n// when the skills were actually on disk and their API keys were in env\n// vars — they just couldn't call the Skill tool.\n//\n// Invariant: every Claude Code invocation the manager spawns must include\n// Skill and Agent. Their absence disables integration-skill activation and\n// subagent dispatch without warning. Keep that list in one place so a new\n// spawn site physically cannot miss them.\n\n// Order is stable for test snapshots.\nconst BASE_TOOLS = ['Bash', 'Read', 'Write', 'Edit', 'Grep', 'Glob', 'Agent', 'Skill'] as const;\n\n// Build the comma-separated allowedTools string for a Claude Code spawn.\n// Each MCP server name becomes a wildcard pattern matching every tool that\n// server exposes; plus the eight base built-ins.\nexport function buildAllowedTools(mcpServerNames: readonly string[]): string {\n // Claude Code's allowedTools patterns use underscore-separated names; MCP\n // server IDs in .mcp.json can use hyphens (e.g. direct-chat), so normalise.\n const mcpPatterns = mcpServerNames.map((name) => `mcp__${name.replace(/-/g, '_')}__*`);\n return [...mcpPatterns, ...BASE_TOOLS].join(',');\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","readFileSync","writeFileSync","session"]}
@@ -7347,4 +7347,4 @@ export {
7347
7347
  managerInstallCommand,
7348
7348
  managerUninstallCommand
7349
7349
  };
7350
- //# sourceMappingURL=chunk-6DIVYBKA.js.map
7350
+ //# sourceMappingURL=chunk-TPR5XCFY.js.map