codeam-cli 2.63.1 → 2.64.0

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/index.js +714 -284
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2789,6 +2789,17 @@ function resolveApiBaseUrl() {
2789
2789
  return DEFAULT_API_BASE_URL;
2790
2790
  }
2791
2791
 
2792
+ // ../../packages/shared/src/types/agent-squad.ts
2793
+ var HANDOFF_FENCE_TAG = "codeam-handoff";
2794
+ var SQUAD_SPECIALTIES = {
2795
+ claude: "deep reasoning, refactors, and multi-step architecture work",
2796
+ codex: "fast, focused implementation and test fixing",
2797
+ cursor: "multi-file edits and codebase-wide changes",
2798
+ gemini: "large-context analysis across big files and logs",
2799
+ kimi: "long-context code reading and summarization",
2800
+ opencode: "general implementation tasks"
2801
+ };
2802
+
2792
2803
  // ../../packages/shared/src/headroom/manifest.ts
2793
2804
  var HEADROOM_PROXY_PORT = 8787;
2794
2805
  var HEADROOM_PIP_COMPANIONS = [
@@ -2921,6 +2932,9 @@ var USER_EVENTS = {
2921
2932
  // the backend re-publishes them on the per-user SSE bus (mirrored in repo A).
2922
2933
  SWITCH_AGENT_PROGRESS: "switch_agent_progress",
2923
2934
  SWITCH_AGENT_STATUS: "switch_agent_status",
2935
+ // Agent Squad — the CLI posts these to /api/agent-switch/events; PRO-gated.
2936
+ HANDOFF_PROPOSED: "handoff_proposed",
2937
+ HANDOFF_RESOLVED: "handoff_resolved",
2924
2938
  // VCS / PR Command Center — the backend publishes this after an agent finishes
2925
2939
  // reviewing a PR (verdict + comment count + findings), driving the mobile
2926
2940
  // completion screen + push. Mirrored in repo A's app-shared events.ts.
@@ -3125,11 +3139,11 @@ function quiet(fn) {
3125
3139
  log.debug(TAG, "ignored sync error", err);
3126
3140
  }
3127
3141
  }
3128
- function rmIfExistsQuiet(path90) {
3142
+ function rmIfExistsQuiet(path91) {
3129
3143
  try {
3130
- fs2.rmSync(path90, { force: true });
3144
+ fs2.rmSync(path91, { force: true });
3131
3145
  } catch (err) {
3132
- log.debug(TAG, `rmIfExists failed for ${path90}`, err);
3146
+ log.debug(TAG, `rmIfExists failed for ${path91}`, err);
3133
3147
  }
3134
3148
  }
3135
3149
  function killQuiet(target, signal = "SIGTERM") {
@@ -3320,8 +3334,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
3320
3334
  return decodedFile;
3321
3335
  };
3322
3336
  }
3323
- function normalizeWindowsPath(path90) {
3324
- return path90.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
3337
+ function normalizeWindowsPath(path91) {
3338
+ return path91.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
3325
3339
  }
3326
3340
 
3327
3341
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -5801,9 +5815,9 @@ async function addSourceContext(frames) {
5801
5815
  LRU_FILE_CONTENTS_CACHE.reduce();
5802
5816
  return frames;
5803
5817
  }
5804
- function getContextLinesFromFile(path90, ranges, output) {
5818
+ function getContextLinesFromFile(path91, ranges, output) {
5805
5819
  return new Promise((resolve9) => {
5806
- const stream = (0, import_node_fs.createReadStream)(path90);
5820
+ const stream = (0, import_node_fs.createReadStream)(path91);
5807
5821
  const lineReaded = (0, import_node_readline.createInterface)({
5808
5822
  input: stream
5809
5823
  });
@@ -5818,7 +5832,7 @@ function getContextLinesFromFile(path90, ranges, output) {
5818
5832
  let rangeStart = range[0];
5819
5833
  let rangeEnd = range[1];
5820
5834
  function onStreamError() {
5821
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path90, 1);
5835
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path91, 1);
5822
5836
  lineReaded.close();
5823
5837
  lineReaded.removeAllListeners();
5824
5838
  destroyStreamAndResolve();
@@ -5879,8 +5893,8 @@ function clearLineContext(frame) {
5879
5893
  delete frame.context_line;
5880
5894
  delete frame.post_context;
5881
5895
  }
5882
- function shouldSkipContextLinesForFile(path90) {
5883
- return path90.startsWith("node:") || path90.endsWith(".min.js") || path90.endsWith(".min.cjs") || path90.endsWith(".min.mjs") || path90.startsWith("data:");
5896
+ function shouldSkipContextLinesForFile(path91) {
5897
+ return path91.startsWith("node:") || path91.endsWith(".min.js") || path91.endsWith(".min.cjs") || path91.endsWith(".min.mjs") || path91.startsWith("data:");
5884
5898
  }
5885
5899
  function shouldSkipContextLinesForFrame(frame) {
5886
5900
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -8034,7 +8048,7 @@ function readAnonId() {
8034
8048
  }
8035
8049
  function superProperties() {
8036
8050
  return {
8037
- cliVersion: true ? "2.63.1" : "0.0.0-dev",
8051
+ cliVersion: true ? "2.64.0" : "0.0.0-dev",
8038
8052
  nodeVersion: process.version,
8039
8053
  platform: process.platform,
8040
8054
  arch: process.arch,
@@ -8215,7 +8229,7 @@ var os4 = __toESM(require("os"));
8215
8229
  // package.json
8216
8230
  var package_default = {
8217
8231
  name: "codeam-cli",
8218
- version: "2.63.1",
8232
+ version: "2.64.0",
8219
8233
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
8220
8234
  type: "commonjs",
8221
8235
  main: "dist/index.js",
@@ -8611,6 +8625,20 @@ async function fetchProvisionCredential(input) {
8611
8625
  return null;
8612
8626
  }
8613
8627
  }
8628
+ async function fetchSquadRoster(input) {
8629
+ try {
8630
+ const res = await _transport.postJsonAuthed(
8631
+ `${API_BASE}/api/plugin/agents/roster`,
8632
+ { sessionId: input.sessionId, pluginId: input.pluginId },
8633
+ input.pluginAuthToken
8634
+ );
8635
+ const data = res?.data;
8636
+ if (!data || !Array.isArray(data.agents)) return null;
8637
+ return { agents: data.agents, handoffsEnabled: data.handoffsEnabled === true };
8638
+ } catch {
8639
+ return null;
8640
+ }
8641
+ }
8614
8642
  async function postAgentSwitchEvent(input) {
8615
8643
  try {
8616
8644
  await _transport.postJsonAuthed(
@@ -8773,9 +8801,7 @@ async function _postJsonAuthed(url2, body, pluginAuthToken) {
8773
8801
  });
8774
8802
  res.on("end", () => {
8775
8803
  if (res.statusCode && res.statusCode >= 400) {
8776
- reject(
8777
- makeHttpError(res.statusCode, res.headers["retry-after"], responseBody)
8778
- );
8804
+ reject(makeHttpError(res.statusCode, res.headers["retry-after"], responseBody));
8779
8805
  return;
8780
8806
  }
8781
8807
  try {
@@ -9655,7 +9681,7 @@ var CommandRelayService = class _CommandRelayService {
9655
9681
  // fresh + clear the "CLI update available" banner after a self-update
9656
9682
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9657
9683
  // pair/reconnect). Older backends ignore the extra field.
9658
- ..."2.63.1" ? { ideVersion: "2.63.1" } : {}
9684
+ ..."2.64.0" ? { ideVersion: "2.64.0" } : {}
9659
9685
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9660
9686
  }
9661
9687
  /**
@@ -9680,7 +9706,8 @@ var CommandRelayService = class _CommandRelayService {
9680
9706
  ];
9681
9707
  _postJson(`${API_BASE2}/api/plugin/agents`, {
9682
9708
  pluginId: this.pluginId,
9683
- agents
9709
+ agents,
9710
+ capabilities: { squad: true }
9684
9711
  }).then(() => {
9685
9712
  this.agentsRegistered = true;
9686
9713
  }).catch(() => {
@@ -9899,10 +9926,10 @@ var WINDOWS_LEGACY_JUNCTIONS = [
9899
9926
  /[\\/]Start Menu([\\/]|$)/i,
9900
9927
  /[\\/]Templates([\\/]|$)/i
9901
9928
  ];
9902
- function isUnsafeWindowsWatchRoot(dir, homedir52) {
9929
+ function isUnsafeWindowsWatchRoot(dir, homedir53) {
9903
9930
  const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
9904
9931
  const cwd = norm(dir);
9905
- const home = norm(homedir52);
9932
+ const home = norm(homedir53);
9906
9933
  if (cwd === home) return true;
9907
9934
  if (/^[a-z]:$/.test(cwd)) return true;
9908
9935
  const sysRoots = [
@@ -14558,10 +14585,10 @@ function buildForPlatform(platform3) {
14558
14585
  var import_node_crypto4 = require("crypto");
14559
14586
 
14560
14587
  // src/agents/claude/resolver.ts
14561
- function buildClaudeLaunch(extraArgs = [], os65 = createOsStrategy()) {
14562
- const found = os65.findInPath("claude") ?? os65.findInPath("claude-code");
14588
+ function buildClaudeLaunch(extraArgs = [], os66 = createOsStrategy()) {
14589
+ const found = os66.findInPath("claude") ?? os66.findInPath("claude-code");
14563
14590
  if (!found) return null;
14564
- return os65.buildLaunch(found, extraArgs);
14591
+ return os66.buildLaunch(found, extraArgs);
14565
14592
  }
14566
14593
 
14567
14594
  // src/agents/claude/installer.ts
@@ -15151,8 +15178,8 @@ var ClaudeRuntimeStrategy = class {
15151
15178
  meta = getAgent("claude");
15152
15179
  mode = "interactive";
15153
15180
  os;
15154
- constructor(os65) {
15155
- this.os = os65;
15181
+ constructor(os66) {
15182
+ this.os = os66;
15156
15183
  }
15157
15184
  /**
15158
15185
  * Claude Code's react-ink TUI enables bracketed-paste mode at
@@ -15932,8 +15959,8 @@ function codexCredentialLocator() {
15932
15959
  function codexLoginLauncher() {
15933
15960
  return {
15934
15961
  async ensureInstalled() {
15935
- const os65 = createOsStrategy();
15936
- return os65.findInPath("codex") !== null;
15962
+ const os66 = createOsStrategy();
15963
+ return os66.findInPath("codex") !== null;
15937
15964
  },
15938
15965
  launch() {
15939
15966
  return (0, import_node_child_process4.spawn)("codex", ["login"], { stdio: "inherit" });
@@ -15956,8 +15983,8 @@ var CodexRuntimeStrategy = class {
15956
15983
  meta = getAgent("codex");
15957
15984
  mode = "interactive";
15958
15985
  os;
15959
- constructor(os65) {
15960
- this.os = os65;
15986
+ constructor(os66) {
15987
+ this.os = os66;
15961
15988
  }
15962
15989
  async prepareLaunch() {
15963
15990
  let binary = this.os.findInPath("codex");
@@ -16066,12 +16093,12 @@ var CodexRuntimeStrategy = class {
16066
16093
  });
16067
16094
  }
16068
16095
  };
16069
- function resolveNpm(os65) {
16070
- return os65.id === "win32" ? "npm.cmd" : "npm";
16096
+ function resolveNpm(os66) {
16097
+ return os66.id === "win32" ? "npm.cmd" : "npm";
16071
16098
  }
16072
- async function installCodexViaNpm(os65) {
16099
+ async function installCodexViaNpm(os66) {
16073
16100
  return new Promise((resolve9, reject) => {
16074
- const proc = (0, import_node_child_process5.spawn)(resolveNpm(os65), ["install", "-g", "@openai/codex"], {
16101
+ const proc = (0, import_node_child_process5.spawn)(resolveNpm(os66), ["install", "-g", "@openai/codex"], {
16075
16102
  stdio: "inherit"
16076
16103
  });
16077
16104
  proc.on("close", (code) => {
@@ -16088,16 +16115,16 @@ async function installCodexViaNpm(os65) {
16088
16115
  });
16089
16116
  });
16090
16117
  }
16091
- function augmentNpmGlobalBin(os65) {
16118
+ function augmentNpmGlobalBin(os66) {
16092
16119
  try {
16093
- const result = (0, import_node_child_process5.spawnSync)(resolveNpm(os65), ["prefix", "-g"], {
16120
+ const result = (0, import_node_child_process5.spawnSync)(resolveNpm(os66), ["prefix", "-g"], {
16094
16121
  stdio: ["ignore", "pipe", "ignore"]
16095
16122
  });
16096
16123
  if (result.status !== 0) return;
16097
16124
  const prefix = result.stdout.toString().trim();
16098
16125
  if (!prefix) return;
16099
- const binDir = os65.id === "win32" ? prefix : path25.join(prefix, "bin");
16100
- os65.augmentPath([binDir]);
16126
+ const binDir = os66.id === "win32" ? prefix : path25.join(prefix, "bin");
16127
+ os66.augmentPath([binDir]);
16101
16128
  } catch {
16102
16129
  }
16103
16130
  }
@@ -16496,10 +16523,10 @@ function manualInstallHint(runner, tools) {
16496
16523
  const argv = osPackageInstallArgv(detectPackageManager(runner), [...tools]);
16497
16524
  return argv ? `\`sudo ${argv.join(" ")}\`` : `your package manager (e.g. \`${pkgs}\`)`;
16498
16525
  }
16499
- function writeUnzipShim(os65, runner) {
16526
+ function writeUnzipShim(os66, runner) {
16500
16527
  const python = ["python3", "python"].find((p2) => runner.which(p2));
16501
16528
  if (!python) return null;
16502
- const dir = os65.scratchPath("codeam-cr-prereq");
16529
+ const dir = os66.scratchPath("codeam-cr-prereq");
16503
16530
  const shim = path27.join(dir, "unzip");
16504
16531
  const script = `#!/bin/sh
16505
16532
  set -e
@@ -16521,9 +16548,9 @@ exec ${python} -m zipfile -e "$archive" "$dest"
16521
16548
  (0, import_node_fs5.chmodSync)(shim, 448);
16522
16549
  return dir;
16523
16550
  }
16524
- async function ensureInstallPrerequisites(os65, deps = {}) {
16551
+ async function ensureInstallPrerequisites(os66, deps = {}) {
16525
16552
  const runner = deps.runner ?? defaultHeadroomRunner;
16526
- const missing = REQUIRED_TOOLS.filter((t2) => os65.findInPath(t2) === null);
16553
+ const missing = REQUIRED_TOOLS.filter((t2) => os66.findInPath(t2) === null);
16527
16554
  if (missing.length === 0) return { ok: true, extraPath: [] };
16528
16555
  log.info(
16529
16556
  "coderabbit",
@@ -16542,10 +16569,10 @@ async function ensureInstallPrerequisites(os65, deps = {}) {
16542
16569
  );
16543
16570
  }
16544
16571
  }
16545
- const stillMissing = REQUIRED_TOOLS.filter((t2) => os65.findInPath(t2) === null);
16572
+ const stillMissing = REQUIRED_TOOLS.filter((t2) => os66.findInPath(t2) === null);
16546
16573
  if (stillMissing.length === 0) return { ok: true, extraPath: [] };
16547
16574
  if (stillMissing.length === 1 && stillMissing[0] === "unzip") {
16548
- const shimDir = writeUnzipShim(os65, runner);
16575
+ const shimDir = writeUnzipShim(os66, runner);
16549
16576
  if (shimDir) {
16550
16577
  log.info("coderabbit", "unzip is unavailable \u2014 using a scoped python zipfile shim");
16551
16578
  return { ok: true, extraPath: [shimDir] };
@@ -16608,15 +16635,15 @@ function summarizeInstallFailure(output) {
16608
16635
  }
16609
16636
  return lines.length > 0 ? lines[lines.length - 1] : null;
16610
16637
  }
16611
- async function ensureCoderabbitInstalled(os65, deps = {}) {
16612
- if (os65.findInPath("coderabbit")) return { ok: true };
16613
- if (os65.id === "win32") {
16638
+ async function ensureCoderabbitInstalled(os66, deps = {}) {
16639
+ if (os66.findInPath("coderabbit")) return { ok: true };
16640
+ if (os66.id === "win32") {
16614
16641
  return {
16615
16642
  ok: false,
16616
16643
  error: "CodeRabbit on Windows requires WSL. Install the CLI inside your WSL distribution (curl -fsSL https://cli.coderabbit.ai/install.sh | sh), then try again."
16617
16644
  };
16618
16645
  }
16619
- const prereq = await ensureInstallPrerequisites(os65, deps);
16646
+ const prereq = await ensureInstallPrerequisites(os66, deps);
16620
16647
  if (!prereq.ok) return { ok: false, error: prereq.error };
16621
16648
  const env = { ...process.env };
16622
16649
  if (prereq.extraPath.length > 0) {
@@ -16632,8 +16659,8 @@ async function ensureCoderabbitInstalled(os65, deps = {}) {
16632
16659
  error: detail ? `CodeRabbit CLI install failed: ${detail}` : "CodeRabbit CLI install failed \u2014 check this machine's network egress and try again."
16633
16660
  };
16634
16661
  }
16635
- os65.augmentPath([`${os65.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
16636
- if (os65.findInPath("coderabbit") === null) {
16662
+ os66.augmentPath([`${os66.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
16663
+ if (os66.findInPath("coderabbit") === null) {
16637
16664
  const detail = summarizeInstallFailure(output);
16638
16665
  return {
16639
16666
  ok: false,
@@ -16675,10 +16702,10 @@ function coderabbitCredentialLocator() {
16675
16702
  validate: validateNonEmptyCredential
16676
16703
  };
16677
16704
  }
16678
- function coderabbitLoginLauncher(os65) {
16705
+ function coderabbitLoginLauncher(os66) {
16679
16706
  return {
16680
16707
  async ensureInstalled() {
16681
- const result = await ensureCoderabbitInstalled(os65);
16708
+ const result = await ensureCoderabbitInstalled(os66);
16682
16709
  if (!result.ok && result.error) console.error(`
16683
16710
  \u2717 ${result.error}
16684
16711
  `);
@@ -16738,8 +16765,8 @@ function pickLine(obj) {
16738
16765
  function toHunk(raw, groupSeverity) {
16739
16766
  if (!raw || typeof raw !== "object") return null;
16740
16767
  const o = raw;
16741
- const path90 = asString(pick(o, ["file_path", "filePath", "file", "path", "filename", "fileName"])) ?? asString(pick(o, ["location"])?.path);
16742
- if (!path90) return null;
16768
+ const path91 = asString(pick(o, ["file_path", "filePath", "file", "path", "filename", "fileName"])) ?? asString(pick(o, ["location"])?.path);
16769
+ if (!path91) return null;
16743
16770
  const message = asString(
16744
16771
  pick(o, [
16745
16772
  "comment",
@@ -16756,7 +16783,7 @@ function toHunk(raw, groupSeverity) {
16756
16783
  const severity = normSeverity(pick(o, ["severity", "level", "priority", "impact"])) ?? normSeverity(groupSeverity);
16757
16784
  const locObj = pick(o, ["location"]) ?? o;
16758
16785
  return {
16759
- path: path90.trim(),
16786
+ path: path91.trim(),
16760
16787
  line: pickLine(o) ?? pickLine(locObj),
16761
16788
  severity,
16762
16789
  message: (title && message ? `${title}: ${message}` : title || message).trim() || "(no message)"
@@ -16839,10 +16866,10 @@ function parsePlain(stdout) {
16839
16866
  for (const line of stdout.split(/\r?\n/)) {
16840
16867
  const m = line.match(HUNK_LINE_RE);
16841
16868
  if (!m) continue;
16842
- const [, path90, lineNo, sevToken, message] = m;
16843
- if (!path90 || !lineNo || !message) continue;
16869
+ const [, path91, lineNo, sevToken, message] = m;
16870
+ if (!path91 || !lineNo || !message) continue;
16844
16871
  hunks.push({
16845
- path: path90.trim(),
16872
+ path: path91.trim(),
16846
16873
  line: Number(lineNo),
16847
16874
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
16848
16875
  message: message.trim().replace(/^[*-]\s+/, "")
@@ -16902,8 +16929,8 @@ var CoderabbitRuntimeStrategy = class {
16902
16929
  meta = getAgent("coderabbit");
16903
16930
  mode = "batch";
16904
16931
  os;
16905
- constructor(os65) {
16906
- this.os = os65;
16932
+ constructor(os66) {
16933
+ this.os = os66;
16907
16934
  }
16908
16935
  getDefaultArgs() {
16909
16936
  return ["review", "--agent"];
@@ -17174,10 +17201,10 @@ function cursorCredentialLocator() {
17174
17201
  validate: validateNonEmptyCredential
17175
17202
  };
17176
17203
  }
17177
- function cursorLoginLauncher(os65) {
17204
+ function cursorLoginLauncher(os66) {
17178
17205
  return {
17179
17206
  async ensureInstalled() {
17180
- if (os65.findInPath("cursor-agent")) return true;
17207
+ if (os66.findInPath("cursor-agent")) return true;
17181
17208
  console.error(
17182
17209
  "\n \u2717 cursor-agent binary not on PATH.\n Install Cursor (https://cursor.com/) and ensure the CLI\n plugin is enabled, then re-run `codeam link cursor`.\n"
17183
17210
  );
@@ -17241,8 +17268,8 @@ var CursorRuntimeStrategy = class {
17241
17268
  meta = getAgent("cursor");
17242
17269
  mode = "interactive";
17243
17270
  os;
17244
- constructor(os65) {
17245
- this.os = os65;
17271
+ constructor(os66) {
17272
+ this.os = os66;
17246
17273
  }
17247
17274
  async prepareLaunch() {
17248
17275
  const binary = this.os.findInPath("cursor-agent");
@@ -17458,10 +17485,10 @@ function aiderCredentialLocator() {
17458
17485
  validate: validateNonEmptyCredential
17459
17486
  };
17460
17487
  }
17461
- function aiderLoginLauncher(os65) {
17488
+ function aiderLoginLauncher(os66) {
17462
17489
  return {
17463
17490
  async ensureInstalled() {
17464
- if (os65.findInPath("aider")) return true;
17491
+ if (os66.findInPath("aider")) return true;
17465
17492
  console.error(
17466
17493
  "\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
17467
17494
  );
@@ -17471,7 +17498,7 @@ function aiderLoginLauncher(os65) {
17471
17498
  console.error(
17472
17499
  "\n Aider has no interactive login flow.\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your shell,\n or re-run `codeam link aider --api-key=<your-key>`.\n"
17473
17500
  );
17474
- return (0, import_node_child_process12.spawn)(os65.id === "win32" ? "cmd.exe" : "sh", os65.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
17501
+ return (0, import_node_child_process12.spawn)(os66.id === "win32" ? "cmd.exe" : "sh", os66.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
17475
17502
  stdio: "ignore"
17476
17503
  });
17477
17504
  }
@@ -17543,8 +17570,8 @@ var AiderRuntimeStrategy = class {
17543
17570
  meta = getAgent("aider");
17544
17571
  mode = "interactive";
17545
17572
  os;
17546
- constructor(os65) {
17547
- this.os = os65;
17573
+ constructor(os66) {
17574
+ this.os = os66;
17548
17575
  }
17549
17576
  async prepareLaunch() {
17550
17577
  const binary = this.os.findInPath("aider");
@@ -17676,8 +17703,8 @@ function geminiCredentialLocator() {
17676
17703
  function geminiLoginLauncher() {
17677
17704
  return {
17678
17705
  async ensureInstalled() {
17679
- const os65 = createOsStrategy();
17680
- return os65.findInPath("gemini") !== null;
17706
+ const os66 = createOsStrategy();
17707
+ return os66.findInPath("gemini") !== null;
17681
17708
  },
17682
17709
  launch() {
17683
17710
  return (0, import_node_child_process13.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
@@ -17867,8 +17894,8 @@ var GeminiRuntimeStrategy = class {
17867
17894
  meta = getAgent("gemini");
17868
17895
  mode = "interactive";
17869
17896
  os;
17870
- constructor(os65) {
17871
- this.os = os65;
17897
+ constructor(os66) {
17898
+ this.os = os66;
17872
17899
  }
17873
17900
  async prepareLaunch() {
17874
17901
  const binary = this.os.findInPath("gemini");
@@ -18159,8 +18186,8 @@ var KimiRuntimeStrategy = class {
18159
18186
  meta = getAgent("kimi");
18160
18187
  mode = "interactive";
18161
18188
  os;
18162
- constructor(os65) {
18163
- this.os = os65;
18189
+ constructor(os66) {
18190
+ this.os = os66;
18164
18191
  }
18165
18192
  async prepareLaunch() {
18166
18193
  const binary = this.os.findInPath("kimi");
@@ -18302,8 +18329,8 @@ var OpencodeRuntimeStrategy = class {
18302
18329
  meta = getAgent("opencode");
18303
18330
  mode = "interactive";
18304
18331
  os;
18305
- constructor(os65) {
18306
- this.os = os65;
18332
+ constructor(os66) {
18333
+ this.os = os66;
18307
18334
  }
18308
18335
  async prepareLaunch() {
18309
18336
  const binary = this.os.findInPath("opencode");
@@ -18387,20 +18414,20 @@ var OpencodeRuntimeStrategy = class {
18387
18414
 
18388
18415
  // src/agents/registry.ts
18389
18416
  var runtimeBuilders = {
18390
- claude: (os65) => new ClaudeRuntimeStrategy(os65),
18391
- codex: (os65) => new CodexRuntimeStrategy(os65),
18392
- coderabbit: (os65) => new CoderabbitRuntimeStrategy(os65),
18393
- cursor: (os65) => new CursorRuntimeStrategy(os65),
18394
- aider: (os65) => new AiderRuntimeStrategy(os65),
18395
- gemini: (os65) => new GeminiRuntimeStrategy(os65),
18396
- kimi: (os65) => new KimiRuntimeStrategy(os65),
18397
- opencode: (os65) => new OpencodeRuntimeStrategy(os65)
18417
+ claude: (os66) => new ClaudeRuntimeStrategy(os66),
18418
+ codex: (os66) => new CodexRuntimeStrategy(os66),
18419
+ coderabbit: (os66) => new CoderabbitRuntimeStrategy(os66),
18420
+ cursor: (os66) => new CursorRuntimeStrategy(os66),
18421
+ aider: (os66) => new AiderRuntimeStrategy(os66),
18422
+ gemini: (os66) => new GeminiRuntimeStrategy(os66),
18423
+ kimi: (os66) => new KimiRuntimeStrategy(os66),
18424
+ opencode: (os66) => new OpencodeRuntimeStrategy(os66)
18398
18425
  };
18399
18426
  var deployBuilders = {
18400
18427
  claude: () => new ClaudeDeployStrategy(),
18401
18428
  codex: () => new CodexDeployStrategy()
18402
18429
  };
18403
- function createAgentStrategy(agent, os65 = createOsStrategy()) {
18430
+ function createAgentStrategy(agent, os66 = createOsStrategy()) {
18404
18431
  if (!AGENT_REGISTRY[agent]?.enabled) {
18405
18432
  throw new Error(
18406
18433
  `Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
@@ -18410,10 +18437,10 @@ function createAgentStrategy(agent, os65 = createOsStrategy()) {
18410
18437
  if (!build) {
18411
18438
  throw new Error(`No runtime strategy registered for agent "${agent}"`);
18412
18439
  }
18413
- return build(os65);
18440
+ return build(os66);
18414
18441
  }
18415
- function createInteractiveAgentStrategy(agent, os65 = createOsStrategy()) {
18416
- const s = createAgentStrategy(agent, os65);
18442
+ function createInteractiveAgentStrategy(agent, os66 = createOsStrategy()) {
18443
+ const s = createAgentStrategy(agent, os66);
18417
18444
  if (s.mode !== "interactive") {
18418
18445
  throw new Error(
18419
18446
  `Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
@@ -19091,8 +19118,8 @@ function collectChangedFiles(cwd) {
19091
19118
  }
19092
19119
  return [...byPath.values()];
19093
19120
  }
19094
- function restoreCoderabbitOauthBlob(os65, value) {
19095
- const dir = path38.join(os65.homeDir(), ".coderabbit");
19121
+ function restoreCoderabbitOauthBlob(os66, value) {
19122
+ const dir = path38.join(os66.homeDir(), ".coderabbit");
19096
19123
  (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
19097
19124
  let file = "auth.json";
19098
19125
  let contents = value.trim();
@@ -19112,26 +19139,26 @@ function installFailureMessage(result) {
19112
19139
  return result.error ?? "CodeRabbit CLI could not be installed";
19113
19140
  }
19114
19141
  async function configureCoderabbit(input, deps = {}) {
19115
- const os65 = deps.os ?? createOsStrategy();
19142
+ const os66 = deps.os ?? createOsStrategy();
19116
19143
  const ensureInstalled = deps.ensureInstalled ?? ensureCoderabbitInstalled;
19117
19144
  const isLoggedIn = deps.isLoggedIn ?? defaultIsLoggedIn;
19118
19145
  const runOAuth = deps.runOAuthLogin ?? runCoderabbitOAuthLogin;
19119
19146
  const snapshot = deps.snapshotDir ?? (() => snapshotCredentialDir());
19120
19147
  const capture2 = deps.captureCredential ?? ((b) => diffCapturedCredential(b));
19121
19148
  const loginWithApiKey = deps.loginWithApiKey ?? defaultLoginWithApiKey;
19122
- const home = os65.homeDir();
19123
- os65.augmentPath(
19124
- os65.id === "win32" ? [
19149
+ const home = os66.homeDir();
19150
+ os66.augmentPath(
19151
+ os66.id === "win32" ? [
19125
19152
  path38.join(home, ".local", "bin"),
19126
19153
  path38.join(process.env.APPDATA ?? path38.join(home, "AppData", "Roaming"), "npm"),
19127
19154
  path38.join(home, "scoop", "shims")
19128
19155
  ] : [path38.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
19129
19156
  );
19130
- const installed2 = os65.findInPath("coderabbit") !== null;
19157
+ const installed2 = os66.findInPath("coderabbit") !== null;
19131
19158
  const base = () => ({
19132
19159
  action: input.action,
19133
19160
  supported: true,
19134
- installed: os65.findInPath("coderabbit") !== null,
19161
+ installed: os66.findInPath("coderabbit") !== null,
19135
19162
  loggedIn: false
19136
19163
  });
19137
19164
  if (input.action === "status") {
@@ -19145,7 +19172,7 @@ async function configureCoderabbit(input, deps = {}) {
19145
19172
  const key = (input.apiKey ?? "").trim();
19146
19173
  if (!key) return { ...res2, error: "No API key provided" };
19147
19174
  if (!res2.installed) {
19148
- const inst = await ensureInstalled(os65);
19175
+ const inst = await ensureInstalled(os66);
19149
19176
  res2.installed = inst.ok;
19150
19177
  if (!inst.ok) return { ...res2, error: installFailureMessage(inst) };
19151
19178
  }
@@ -19169,7 +19196,7 @@ async function configureCoderabbit(input, deps = {}) {
19169
19196
  }
19170
19197
  if (!res2.installed) {
19171
19198
  deps.onEvent?.({ kind: "installing" });
19172
- const inst = await ensureInstalled(os65);
19199
+ const inst = await ensureInstalled(os66);
19173
19200
  res2.installed = inst.ok;
19174
19201
  if (!inst.ok) return { ...res2, error: installFailureMessage(inst) };
19175
19202
  }
@@ -19186,7 +19213,7 @@ async function configureCoderabbit(input, deps = {}) {
19186
19213
  return { ...res2, loggedIn: true, linked: true };
19187
19214
  }
19188
19215
  try {
19189
- restoreCoderabbitOauthBlob(os65, cred.credential);
19216
+ restoreCoderabbitOauthBlob(os66, cred.credential);
19190
19217
  } catch (err) {
19191
19218
  return {
19192
19219
  ...res2,
@@ -19210,7 +19237,7 @@ async function configureCoderabbit(input, deps = {}) {
19210
19237
  const res2 = base();
19211
19238
  if (!installed2) {
19212
19239
  deps.onEvent?.({ kind: "installing" });
19213
- const inst = await ensureInstalled(os65);
19240
+ const inst = await ensureInstalled(os66);
19214
19241
  res2.installed = inst.ok;
19215
19242
  if (!inst.ok) return { ...res2, error: installFailureMessage(inst) };
19216
19243
  }
@@ -19250,7 +19277,7 @@ async function configureCoderabbit(input, deps = {}) {
19250
19277
  }
19251
19278
  const res = base();
19252
19279
  if (!res.installed) {
19253
- const inst = await ensureInstalled(os65);
19280
+ const inst = await ensureInstalled(os66);
19254
19281
  res.installed = inst.ok;
19255
19282
  if (!inst.ok) return { ...res, error: installFailureMessage(inst) };
19256
19283
  }
@@ -21073,7 +21100,7 @@ async function autoUpgradeBeforeCriticalCommand() {
21073
21100
  if (process.env.NODE_ENV === "test") return;
21074
21101
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21075
21102
  if (process.env.CI) return;
21076
- const current2 = true ? "2.63.1" : null;
21103
+ const current2 = true ? "2.64.0" : null;
21077
21104
  if (!current2) return;
21078
21105
  const cache = readCache();
21079
21106
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21090,7 +21117,7 @@ function checkForUpdates() {
21090
21117
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21091
21118
  if (process.env.CI) return;
21092
21119
  if (!process.stdout.isTTY) return;
21093
- const current2 = true ? "2.63.1" : null;
21120
+ const current2 = true ? "2.64.0" : null;
21094
21121
  if (!current2) return;
21095
21122
  const cache = readCache();
21096
21123
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21110,7 +21137,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
21110
21137
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
21111
21138
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
21112
21139
  function currentCliVersion() {
21113
- return true ? "2.63.1" : null;
21140
+ return true ? "2.64.0" : null;
21114
21141
  }
21115
21142
  function runCmd(cmd, args2, timeoutMs) {
21116
21143
  return new Promise((resolve9) => {
@@ -22779,7 +22806,7 @@ async function defaultFetchJson(url2) {
22779
22806
  async function readUsageReport(deps = {}) {
22780
22807
  const port = deps.port ?? HEADROOM_PROXY_PORT;
22781
22808
  const base = `http://127.0.0.1:${port}`;
22782
- const get2 = deps.fetchJson ?? ((path90) => defaultFetchJson(`${base}${path90}`));
22809
+ const get2 = deps.fetchJson ?? ((path91) => defaultFetchJson(`${base}${path91}`));
22783
22810
  let history;
22784
22811
  try {
22785
22812
  history = await get2("/stats-history");
@@ -24696,11 +24723,11 @@ function resolveDoltInstallStrategy(platform3) {
24696
24723
  }
24697
24724
  var DOLT_RELEASE_BASE = "https://github.com/dolthub/dolt/releases/latest/download";
24698
24725
  function doltPlatformTuple(platform3, arch2) {
24699
- const os65 = platform3 === "win32" ? "windows" : platform3 === "darwin" ? "darwin" : "linux";
24726
+ const os66 = platform3 === "win32" ? "windows" : platform3 === "darwin" ? "darwin" : "linux";
24700
24727
  const a = arch2 === "x64" ? "amd64" : arch2 === "arm64" ? "arm64" : null;
24701
24728
  if (!a) return null;
24702
- if (os65 === "windows" && a !== "amd64") return null;
24703
- return `${os65}-${a}`;
24729
+ if (os66 === "windows" && a !== "amd64") return null;
24730
+ return `${os66}-${a}`;
24704
24731
  }
24705
24732
  function resolveDoltTarballStrategy(targetDir, platform3, arch2) {
24706
24733
  const tuple = doltPlatformTuple(platform3, arch2);
@@ -25761,8 +25788,14 @@ function dispatchPrompt(ctx, prompt) {
25761
25788
  ctx.outputSvc.newTurn();
25762
25789
  ctx.agent.sendCommand(prompt);
25763
25790
  }
25764
- var startTask = (ctx, _cmd, parsed) => {
25791
+ var startTask = async (ctx, cmd, parsed) => {
25765
25792
  const { prompt, files } = parsed;
25793
+ if (parsed.agentId && parsed.agentId !== ctx.agentId) {
25794
+ await ctx.relay.sendResult(cmd.id, "failed", {
25795
+ error: "Switching agents isn't supported on this session."
25796
+ });
25797
+ return;
25798
+ }
25766
25799
  const effectivePrompt = prompt ?? "";
25767
25800
  if (files && files.length > 0) {
25768
25801
  const paths = saveFilesTemp(files);
@@ -26346,7 +26379,7 @@ var vcsAgentReviewH = async (ctx, cmd, parsed) => {
26346
26379
  });
26347
26380
  const token = ctx.pluginAuthToken;
26348
26381
  void (async () => {
26349
- const os65 = createOsStrategy();
26382
+ const os66 = createOsStrategy();
26350
26383
  try {
26351
26384
  const report = await reviewPullRequest(
26352
26385
  {
@@ -26355,7 +26388,7 @@ var vcsAgentReviewH = async (ctx, cmd, parsed) => {
26355
26388
  baseBranch: parsed.baseBranch
26356
26389
  },
26357
26390
  {
26358
- runReview: (input) => new CoderabbitRuntimeStrategy(os65).runOneShot(input),
26391
+ runReview: (input) => new CoderabbitRuntimeStrategy(os66).runOneShot(input),
26359
26392
  runGh: (args2) => defaultRunGh(args2),
26360
26393
  postReport: async (r) => {
26361
26394
  if (!token) return;
@@ -27458,11 +27491,11 @@ function resolveTokenValue(args2) {
27458
27491
  }
27459
27492
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
27460
27493
  if (fileFlag) {
27461
- const path90 = fileFlag.slice("--token-file=".length);
27494
+ const path91 = fileFlag.slice("--token-file=".length);
27462
27495
  try {
27463
- const content = fs63.readFileSync(path90, "utf8").trim();
27464
- if (content.length === 0) fail(`--token-file ${path90} is empty`);
27465
- rmIfExistsQuiet(path90);
27496
+ const content = fs63.readFileSync(path91, "utf8").trim();
27497
+ if (content.length === 0) fail(`--token-file ${path91} is empty`);
27498
+ rmIfExistsQuiet(path91);
27466
27499
  return content;
27467
27500
  } catch (err) {
27468
27501
  fail(`Could not read --token-file: ${err.message}`);
@@ -34370,7 +34403,7 @@ function makeSerializedSwitchEmitter(post2) {
34370
34403
  return chain;
34371
34404
  };
34372
34405
  }
34373
- async function performAgentSwitch(deps, rawAgentId) {
34406
+ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
34374
34407
  const from = deps.currentAgent();
34375
34408
  const target = resolveSwitchTarget(rawAgentId, from);
34376
34409
  if (!target.ok) {
@@ -34386,22 +34419,28 @@ async function performAgentSwitch(deps, rawAgentId) {
34386
34419
  };
34387
34420
  log.info("switchAgent", `switch requested ${from} \u2192 ${agentId}`);
34388
34421
  void emitStatus({ state: "switching", agentId, fromAgentId: from });
34389
- void emitStep("credential");
34390
- const cred = await deps.fetchCredential(agentId);
34391
- if (!cred) {
34392
- return fail2(
34393
- `No linked credential for ${displayName(agentId)}. Link it in Profile \u203A Agents first.`
34394
- );
34422
+ let installScript;
34423
+ if (!fastPath.skipProvision) {
34424
+ void emitStep("credential");
34425
+ const cred = await deps.fetchCredential(agentId);
34426
+ if (!cred) {
34427
+ return fail2(
34428
+ `No linked credential for ${displayName(agentId)}. Link it in Profile \u203A Agents first.`
34429
+ );
34430
+ }
34431
+ try {
34432
+ deps.provisionCredential(agentId, toAgentAuth(cred.method, cred.credential));
34433
+ } catch (err) {
34434
+ log.warn("switchAgent", `credential provisioning failed: ${err.message}`);
34435
+ return fail2(`Couldn't write the ${displayName(agentId)} credential on this machine.`);
34436
+ }
34437
+ installScript = cred.installScript;
34395
34438
  }
34396
- try {
34397
- deps.provisionCredential(agentId, toAgentAuth(cred.method, cred.credential));
34398
- } catch (err) {
34399
- log.warn("switchAgent", `credential provisioning failed: ${err.message}`);
34400
- return fail2(`Couldn't write the ${displayName(agentId)} credential on this machine.`);
34439
+ if (!fastPath.skipInstall) {
34440
+ void emitStep("install");
34441
+ const bin = await deps.ensureBinary(agentId, installScript);
34442
+ if (!bin.ok) return fail2(bin.error);
34401
34443
  }
34402
- void emitStep("install");
34403
- const bin = await deps.ensureBinary(agentId, cred.installScript);
34404
- if (!bin.ok) return fail2(bin.error);
34405
34444
  void emitStep("restart");
34406
34445
  try {
34407
34446
  await deps.swapRuntime(agentId);
@@ -34428,6 +34467,121 @@ async function performAgentSwitch(deps, rawAgentId) {
34428
34467
  return { ok: true, agentId };
34429
34468
  }
34430
34469
 
34470
+ // src/agents/acp/squad-roster.ts
34471
+ var fs67 = __toESM(require("fs"));
34472
+ var os58 = __toESM(require("os"));
34473
+ var path74 = __toESM(require("path"));
34474
+ var PROMPT_MAX = 500;
34475
+ var REPLY_SUMMARY_MAX = 1e3;
34476
+ var PREAMBLE_MAX = 2e3;
34477
+ var BRIEFING_DEFAULT_MAX = 8e3;
34478
+ var GENERIC_SPECIALTY = "general implementation tasks";
34479
+ function clip(s, max) {
34480
+ return s.length > max ? s.slice(0, max) : s;
34481
+ }
34482
+ function defaultMember() {
34483
+ return { acpSessionId: null, provisioned: false, binaryVerified: false, lastTurnIndex: 0 };
34484
+ }
34485
+ function journalPathFor(homeDir2, sessionId) {
34486
+ return path74.join(homeDir2, ".codeam", `squad-journal-${sessionId}.json`);
34487
+ }
34488
+ function loadJournal(journalPath) {
34489
+ try {
34490
+ const raw = JSON.parse(fs67.readFileSync(journalPath, "utf-8"));
34491
+ return Array.isArray(raw.turns) ? raw.turns : [];
34492
+ } catch {
34493
+ return [];
34494
+ }
34495
+ }
34496
+ var SquadState = class {
34497
+ /** Set by the caller after fetchSquadRoster; null until then. */
34498
+ roster = null;
34499
+ journalPath;
34500
+ turns;
34501
+ members = /* @__PURE__ */ new Map();
34502
+ constructor(opts) {
34503
+ this.journalPath = journalPathFor(opts.homeDir ?? os58.homedir(), opts.sessionId);
34504
+ this.turns = loadJournal(this.journalPath);
34505
+ }
34506
+ /** Returns (creating on first access) the mutable per-agent state. */
34507
+ member(agentId) {
34508
+ let m = this.members.get(agentId);
34509
+ if (!m) {
34510
+ m = defaultMember();
34511
+ this.members.set(agentId, m);
34512
+ }
34513
+ return m;
34514
+ }
34515
+ /** Appends a journal entry and persists (fire-and-forget durability). */
34516
+ recordTurn(entry) {
34517
+ const turn = {
34518
+ turn: this.turns.length + 1,
34519
+ agentId: entry.agentId,
34520
+ prompt: clip(entry.prompt, PROMPT_MAX),
34521
+ replySummary: clip(entry.replySummary, REPLY_SUMMARY_MAX),
34522
+ filesTouched: entry.filesTouched
34523
+ };
34524
+ this.turns.push(turn);
34525
+ this.persist();
34526
+ }
34527
+ turnCount() {
34528
+ return this.turns.length;
34529
+ }
34530
+ entriesSince(turnIndex) {
34531
+ return this.turns.filter((t2) => t2.turn > turnIndex);
34532
+ }
34533
+ persist() {
34534
+ try {
34535
+ fs67.mkdirSync(path74.dirname(this.journalPath), { recursive: true, mode: 448 });
34536
+ fs67.writeFileSync(this.journalPath, JSON.stringify({ turns: this.turns }), { mode: 384 });
34537
+ } catch {
34538
+ }
34539
+ }
34540
+ };
34541
+ function specialtyFor(agentId) {
34542
+ return SQUAD_SPECIALTIES[agentId] ?? GENERIC_SPECIALTY;
34543
+ }
34544
+ function buildTeamPreamble(roster, currentAgent, opts) {
34545
+ const others = roster.agents.filter((a) => a.agentId !== currentAgent);
34546
+ if (others.length === 0) return null;
34547
+ const lines = [
34548
+ "[Team context] You are the active agent in a CodeAgent Mobile session where the user",
34549
+ "has a squad of agents and can pass work between them. Your available teammates:",
34550
+ ...others.map((a) => `- ${a.displayName} \u2014 best at: ${specialtyFor(a.agentId)}`)
34551
+ ];
34552
+ if (opts.handoffInstructions) {
34553
+ lines.push(
34554
+ "If a task clearly fits a teammate better than you, you MAY propose a handoff by ending",
34555
+ `your reply with a fenced code block tagged ${HANDOFF_FENCE_TAG} containing ONE JSON object:`,
34556
+ '{"to":"<teammate id>","reason":"<one sentence>","prompt":"<the prompt they should run>"}',
34557
+ "Propose at most one handoff per reply, only when genuinely better, and never announce",
34558
+ "the block in prose \u2014 the app renders it as a card the user can accept."
34559
+ );
34560
+ }
34561
+ return clip(lines.join("\n"), PREAMBLE_MAX);
34562
+ }
34563
+ function renderJournalEntry(e) {
34564
+ const filesClause = e.filesTouched.length > 0 ? ` (files: ${e.filesTouched.join(", ")})` : "";
34565
+ return `- turn ${e.turn} (${e.agentId}): ${e.prompt} \u2192 ${e.replySummary}${filesClause}`;
34566
+ }
34567
+ function buildDeltaBriefing(entries, maxChars = BRIEFING_DEFAULT_MAX) {
34568
+ if (entries.length === 0) return null;
34569
+ const header = "[Team update] While you were away, other agents worked on this session:";
34570
+ const footer = "Continue from the CURRENT state of the working tree.";
34571
+ const envelope = header.length + 1 + footer.length + 1;
34572
+ const sorted = [...entries].sort((a, b) => a.turn - b.turn);
34573
+ const lines = [];
34574
+ let bodyLen = 0;
34575
+ for (let i = sorted.length - 1; i >= 0; i--) {
34576
+ const line = renderJournalEntry(sorted[i]);
34577
+ const addedLen = line.length + (lines.length > 0 ? 1 : 0);
34578
+ if (lines.length > 0 && envelope + bodyLen + addedLen > maxChars) break;
34579
+ lines.unshift(line);
34580
+ bodyLen += addedLen;
34581
+ }
34582
+ return [header, lines.join("\n"), footer].join("\n");
34583
+ }
34584
+
34431
34585
  // src/services/streaming/transport.ts
34432
34586
  var http7 = __toESM(require("http"));
34433
34587
  var https8 = __toESM(require("https"));
@@ -34873,17 +35027,95 @@ function commonPrefixLength(a, b) {
34873
35027
  return i;
34874
35028
  }
34875
35029
 
35030
+ // src/agents/acp/handoff-protocol.ts
35031
+ var REASON_MAX = 1e3;
35032
+ var PROMPT_MAX2 = 8e3;
35033
+ var FENCE_OPEN = "```" + HANDOFF_FENCE_TAG;
35034
+ var FENCE_RE = new RegExp("```" + HANDOFF_FENCE_TAG + "\\s*\\n([\\s\\S]*?)\\n?```", "g");
35035
+ var OUTER_FENCE_RE = /(`{4,})[\s\S]*?\1/g;
35036
+ var outerFencePlaceholder = (i) => `@@HANDOFF_MASK_${i}@@`;
35037
+ function maskOuterFences(text) {
35038
+ const spans = [];
35039
+ const masked = text.replace(OUTER_FENCE_RE, (m) => {
35040
+ const token = outerFencePlaceholder(spans.length);
35041
+ spans.push(m);
35042
+ return token;
35043
+ });
35044
+ const restore = (s) => spans.reduce((acc, span, i) => acc.split(outerFencePlaceholder(i)).join(span), s);
35045
+ return { masked, restore };
35046
+ }
35047
+ function parseProposal(raw, currentAgent, validTargets) {
35048
+ let parsed;
35049
+ try {
35050
+ parsed = JSON.parse(raw);
35051
+ } catch {
35052
+ log.debug("handoffProtocol", "dropped proposal: malformed JSON");
35053
+ return null;
35054
+ }
35055
+ if (typeof parsed !== "object" || parsed === null) {
35056
+ log.debug("handoffProtocol", "dropped proposal: not a JSON object");
35057
+ return null;
35058
+ }
35059
+ const { to, reason, prompt } = parsed;
35060
+ if (typeof to !== "string" || to.length === 0) {
35061
+ log.debug("handoffProtocol", 'dropped proposal: missing/invalid "to"');
35062
+ return null;
35063
+ }
35064
+ if (!validTargets.has(to)) {
35065
+ log.debug("handoffProtocol", `dropped proposal: unknown target "${to}"`);
35066
+ return null;
35067
+ }
35068
+ if (to === currentAgent) {
35069
+ log.debug("handoffProtocol", "dropped proposal: target is the current agent");
35070
+ return null;
35071
+ }
35072
+ if (typeof reason !== "string" || reason.length === 0 || reason.length > REASON_MAX) {
35073
+ log.debug("handoffProtocol", 'dropped proposal: invalid "reason"');
35074
+ return null;
35075
+ }
35076
+ if (typeof prompt !== "string" || prompt.length === 0 || prompt.length > PROMPT_MAX2) {
35077
+ log.debug("handoffProtocol", 'dropped proposal: invalid "prompt"');
35078
+ return null;
35079
+ }
35080
+ return { to, reason, prompt };
35081
+ }
35082
+ function stripFences(masked) {
35083
+ const stripped = masked.replace(FENCE_RE, "");
35084
+ return stripped.replace(/(?:\r?\n){3,}/g, "\n\n").trim();
35085
+ }
35086
+ function extractHandoffProposal(text, currentAgent, validTargets) {
35087
+ const { masked, restore } = maskOuterFences(text);
35088
+ const matches = [...masked.matchAll(FENCE_RE)];
35089
+ if (matches.length === 0) {
35090
+ return { cleanText: text, proposal: null };
35091
+ }
35092
+ const last = matches[matches.length - 1];
35093
+ const proposal = parseProposal(last[1].trim(), currentAgent, validTargets);
35094
+ const cleanText = restore(stripFences(masked));
35095
+ return { cleanText, proposal };
35096
+ }
35097
+ function stripHandoffFences(text) {
35098
+ const { masked, restore } = maskOuterFences(text);
35099
+ if (masked.search(FENCE_RE) === -1) {
35100
+ return text;
35101
+ }
35102
+ return restore(stripFences(masked));
35103
+ }
35104
+ function handoffFenceStart(text) {
35105
+ return text.indexOf(FENCE_OPEN);
35106
+ }
35107
+
34876
35108
  // src/agents/acp/onboarding.ts
34877
35109
  var import_child_process28 = require("child_process");
34878
- var fs67 = __toESM(require("fs"));
34879
- var os58 = __toESM(require("os"));
34880
- var path74 = __toESM(require("path"));
35110
+ var fs68 = __toESM(require("fs"));
35111
+ var os59 = __toESM(require("os"));
35112
+ var path75 = __toESM(require("path"));
34881
35113
  var _onboardingSeam = {
34882
- markerPath: (sessionId) => path74.join(os58.homedir(), ".codeam", "welcomed", `${sessionId}.done`),
34883
- exists: (p2) => fs67.existsSync(p2),
35114
+ markerPath: (sessionId) => path75.join(os59.homedir(), ".codeam", "welcomed", `${sessionId}.done`),
35115
+ exists: (p2) => fs68.existsSync(p2),
34884
35116
  write: (p2) => {
34885
- fs67.mkdirSync(path74.dirname(p2), { recursive: true });
34886
- fs67.writeFileSync(p2, "");
35117
+ fs68.mkdirSync(path75.dirname(p2), { recursive: true });
35118
+ fs68.writeFileSync(p2, "");
34887
35119
  },
34888
35120
  disabled: () => {
34889
35121
  const v = process.env.CODEAM_ONBOARDING_DISABLED;
@@ -34920,7 +35152,7 @@ function resolveRepoName(cwd) {
34920
35152
  if (name) return name;
34921
35153
  }
34922
35154
  }
34923
- const base = path74.basename(cwd || "");
35155
+ const base = path75.basename(cwd || "");
34924
35156
  if (base && !isUuid(base)) return base;
34925
35157
  return "this project";
34926
35158
  }
@@ -35178,13 +35410,13 @@ var import_crypto5 = require("crypto");
35178
35410
 
35179
35411
  // src/services/turn-files/git-changeset.ts
35180
35412
  var import_child_process29 = require("child_process");
35181
- var fs69 = __toESM(require("fs/promises"));
35182
- var path76 = __toESM(require("path"));
35413
+ var fs70 = __toESM(require("fs/promises"));
35414
+ var path77 = __toESM(require("path"));
35183
35415
 
35184
35416
  // src/services/turn-files/review-ignore.ts
35185
35417
  var import_ignore2 = __toESM(require("ignore"));
35186
- var fs68 = __toESM(require("fs"));
35187
- var path75 = __toESM(require("path"));
35418
+ var fs69 = __toESM(require("fs"));
35419
+ var path76 = __toESM(require("path"));
35188
35420
  var CURATED_REVIEW_IGNORE = [
35189
35421
  // Google Cloud SDK (the incident) — installs a huge python tree.
35190
35422
  "google-cloud-sdk/",
@@ -35223,7 +35455,7 @@ var CURATED_REVIEW_IGNORE = [
35223
35455
  function makeReviewIgnore(repoRoot) {
35224
35456
  const ig = (0, import_ignore2.default)().add(CURATED_REVIEW_IGNORE);
35225
35457
  try {
35226
- const custom = fs68.readFileSync(path75.join(repoRoot, ".codeam", "reviewignore"), "utf8");
35458
+ const custom = fs69.readFileSync(path76.join(repoRoot, ".codeam", "reviewignore"), "utf8");
35227
35459
  ig.add(custom);
35228
35460
  } catch {
35229
35461
  }
@@ -35268,7 +35500,7 @@ async function collectRepoChangeset(opts) {
35268
35500
  let stats;
35269
35501
  if (!truncated && row.fileStatus === "added" && numstatEntry === void 0) {
35270
35502
  const lineCount = await readUntrackedLineCount(
35271
- path76.join(opts.repoRoot, row.filePath)
35503
+ path77.join(opts.repoRoot, row.filePath)
35272
35504
  );
35273
35505
  stats = { added: lineCount, removed: 0 };
35274
35506
  } else {
@@ -35299,7 +35531,7 @@ function readUntrackedLineCount(absPath) {
35299
35531
  }
35300
35532
  async function defaultReadUntrackedLineCount(absPath) {
35301
35533
  try {
35302
- const content = await fs69.readFile(absPath, "utf8");
35534
+ const content = await fs70.readFile(absPath, "utf8");
35303
35535
  let count = 0;
35304
35536
  let pos = -1;
35305
35537
  while ((pos = content.indexOf("\n", pos + 1)) !== -1) {
@@ -35391,7 +35623,7 @@ function defaultRunGit(cwd, args2) {
35391
35623
  });
35392
35624
  }
35393
35625
  async function discoverRepos(workingDir, maxDepth = 4) {
35394
- const fs79 = await import("fs/promises");
35626
+ const fs80 = await import("fs/promises");
35395
35627
  const out2 = [];
35396
35628
  await walk(workingDir, 0);
35397
35629
  return out2;
@@ -35399,7 +35631,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
35399
35631
  if (depth > maxDepth) return;
35400
35632
  let entries = [];
35401
35633
  try {
35402
- const dirents = await fs79.readdir(dir, { withFileTypes: true });
35634
+ const dirents = await fs80.readdir(dir, { withFileTypes: true });
35403
35635
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
35404
35636
  } catch {
35405
35637
  return;
@@ -35410,8 +35642,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
35410
35642
  if (hasGit) {
35411
35643
  out2.push({
35412
35644
  repoRoot: dir,
35413
- repoPath: path76.relative(workingDir, dir),
35414
- repoName: path76.basename(dir)
35645
+ repoPath: path77.relative(workingDir, dir),
35646
+ repoName: path77.basename(dir)
35415
35647
  });
35416
35648
  return;
35417
35649
  }
@@ -35419,14 +35651,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
35419
35651
  if (!entry.isDirectory) continue;
35420
35652
  if (entry.name === "node_modules") continue;
35421
35653
  if (entry.name === "dist" || entry.name === "build") continue;
35422
- await walk(path76.join(dir, entry.name), depth + 1);
35654
+ await walk(path77.join(dir, entry.name), depth + 1);
35423
35655
  }
35424
35656
  }
35425
35657
  }
35426
35658
 
35427
35659
  // src/services/turn-files/files-outbox.ts
35428
- var fs70 = __toESM(require("fs/promises"));
35429
- var path77 = __toESM(require("path"));
35660
+ var fs71 = __toESM(require("fs/promises"));
35661
+ var path78 = __toESM(require("path"));
35430
35662
  var import_os12 = require("os");
35431
35663
  var HOME_OUTBOX_DIR = ".codeam/outbox";
35432
35664
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -35459,16 +35691,16 @@ var FilesOutbox = class {
35459
35691
  backoffIndex = 0;
35460
35692
  stopped = false;
35461
35693
  constructor(opts) {
35462
- const base = opts.baseDir ?? path77.join(homeDir(), HOME_OUTBOX_DIR);
35463
- this.filePath = path77.join(base, `${opts.sessionId}.jsonl`);
35694
+ const base = opts.baseDir ?? path78.join(homeDir(), HOME_OUTBOX_DIR);
35695
+ this.filePath = path78.join(base, `${opts.sessionId}.jsonl`);
35464
35696
  this.post = opts.post;
35465
35697
  this.autoSchedule = opts.autoSchedule !== false;
35466
35698
  }
35467
35699
  /** Persist the entry to disk and trigger a flush. Returns once the
35468
35700
  * line is durable on disk (not once the POST succeeds). */
35469
35701
  async enqueue(entry) {
35470
- await fs70.mkdir(path77.dirname(this.filePath), { recursive: true });
35471
- await fs70.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
35702
+ await fs71.mkdir(path78.dirname(this.filePath), { recursive: true });
35703
+ await fs71.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
35472
35704
  this.backoffIndex = 0;
35473
35705
  if (this.autoSchedule) this.scheduleFlush(0);
35474
35706
  }
@@ -35559,7 +35791,7 @@ var FilesOutbox = class {
35559
35791
  async readAll() {
35560
35792
  let raw = "";
35561
35793
  try {
35562
- raw = await fs70.readFile(this.filePath, "utf8");
35794
+ raw = await fs71.readFile(this.filePath, "utf8");
35563
35795
  } catch {
35564
35796
  return [];
35565
35797
  }
@@ -35583,12 +35815,12 @@ var FilesOutbox = class {
35583
35815
  async rewrite(entries) {
35584
35816
  const tmpPath = `${this.filePath}.${process.pid}.tmp`;
35585
35817
  if (entries.length === 0) {
35586
- await fs70.unlink(this.filePath).catch(() => void 0);
35818
+ await fs71.unlink(this.filePath).catch(() => void 0);
35587
35819
  return;
35588
35820
  }
35589
35821
  const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
35590
- await fs70.writeFile(tmpPath, payload, "utf8");
35591
- await fs70.rename(tmpPath, this.filePath);
35822
+ await fs71.writeFile(tmpPath, payload, "utf8");
35823
+ await fs71.rename(tmpPath, this.filePath);
35592
35824
  }
35593
35825
  };
35594
35826
  function applyJitter(ms) {
@@ -36044,8 +36276,8 @@ async function postBudgetReached(opts, fetchImpl = fetch) {
36044
36276
 
36045
36277
  // src/packs/gates.ts
36046
36278
  var import_node_child_process30 = require("child_process");
36047
- var fs71 = __toESM(require("fs"));
36048
- var path78 = __toESM(require("path"));
36279
+ var fs72 = __toESM(require("fs"));
36280
+ var path79 = __toESM(require("path"));
36049
36281
  var defaultCommandRunner = (file, args2, cwd, timeoutMs) => new Promise((resolve9) => {
36050
36282
  (0, import_node_child_process30.execFile)(
36051
36283
  file,
@@ -36082,14 +36314,14 @@ var NO_TEST_PLACEHOLDER = 'echo "Error: no test specified"';
36082
36314
  var CHECKS_TIMEOUT_MS = 5 * 6e4;
36083
36315
  function detectChecksCommand(cwd) {
36084
36316
  try {
36085
- const cfg = JSON.parse(fs71.readFileSync(path78.join(cwd, ".codeam", "pack.json"), "utf8"));
36317
+ const cfg = JSON.parse(fs72.readFileSync(path79.join(cwd, ".codeam", "pack.json"), "utf8"));
36086
36318
  if (typeof cfg.checksCommand === "string" && cfg.checksCommand.trim().length > 0) {
36087
36319
  return cfg.checksCommand.trim();
36088
36320
  }
36089
36321
  } catch {
36090
36322
  }
36091
36323
  try {
36092
- const pkg = JSON.parse(fs71.readFileSync(path78.join(cwd, "package.json"), "utf8"));
36324
+ const pkg = JSON.parse(fs72.readFileSync(path79.join(cwd, "package.json"), "utf8"));
36093
36325
  const test = pkg.scripts?.test;
36094
36326
  if (typeof test === "string" && test.trim().length > 0 && !test.includes(NO_TEST_PLACEHOLDER)) {
36095
36327
  return "npm test";
@@ -36107,39 +36339,39 @@ ${res.stderr}`.trim();
36107
36339
  }
36108
36340
 
36109
36341
  // src/packs/run-store.ts
36110
- var fs72 = __toESM(require("fs"));
36111
- var path79 = __toESM(require("path"));
36342
+ var fs73 = __toESM(require("fs"));
36343
+ var path80 = __toESM(require("path"));
36112
36344
  var crypto5 = __toESM(require("crypto"));
36113
36345
  function packsDir(cwd) {
36114
- return path79.join(cwd, ".codeam", "packs");
36346
+ return path80.join(cwd, ".codeam", "packs");
36115
36347
  }
36116
36348
  function runDir(cwd, runId) {
36117
- return path79.join(packsDir(cwd), runId);
36349
+ return path80.join(packsDir(cwd), runId);
36118
36350
  }
36119
36351
  function newRunId() {
36120
36352
  return `pk_${Date.now().toString(36)}_${crypto5.randomBytes(4).toString("hex")}`;
36121
36353
  }
36122
36354
  function writeJsonAtomic(file, value) {
36123
- fs72.mkdirSync(path79.dirname(file), { recursive: true });
36355
+ fs73.mkdirSync(path80.dirname(file), { recursive: true });
36124
36356
  const tmp = `${file}.tmp`;
36125
- fs72.writeFileSync(tmp, JSON.stringify(value, null, 2));
36126
- fs72.renameSync(tmp, file);
36357
+ fs73.writeFileSync(tmp, JSON.stringify(value, null, 2));
36358
+ fs73.renameSync(tmp, file);
36127
36359
  }
36128
36360
  function saveRun(cwd, state) {
36129
- writeJsonAtomic(path79.join(runDir(cwd, state.runId), "run.json"), state);
36361
+ writeJsonAtomic(path80.join(runDir(cwd, state.runId), "run.json"), state);
36130
36362
  }
36131
36363
  function saveStageHandoff(cwd, runId, stageIndex, role, handoff) {
36132
36364
  const name = `${String(stageIndex + 1).padStart(2, "0")}-${role}.json`;
36133
- writeJsonAtomic(path79.join(runDir(cwd, runId), name), handoff);
36365
+ writeJsonAtomic(path80.join(runDir(cwd, runId), name), handoff);
36134
36366
  }
36135
36367
  function loadLatestRun(cwd) {
36136
36368
  try {
36137
36369
  const dir = packsDir(cwd);
36138
- const entries = fs72.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
36370
+ const entries = fs73.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
36139
36371
  for (let i = entries.length - 1; i >= 0; i--) {
36140
- const file = path79.join(dir, entries[i], "run.json");
36372
+ const file = path80.join(dir, entries[i], "run.json");
36141
36373
  try {
36142
- const parsed = JSON.parse(fs72.readFileSync(file, "utf8"));
36374
+ const parsed = JSON.parse(fs73.readFileSync(file, "utf8"));
36143
36375
  if (parsed && typeof parsed.runId === "string") return parsed;
36144
36376
  } catch {
36145
36377
  }
@@ -36150,17 +36382,17 @@ function loadLatestRun(cwd) {
36150
36382
  }
36151
36383
  function ensureLedgerIgnored(cwd) {
36152
36384
  try {
36153
- const gitDir = path79.join(cwd, ".git");
36154
- if (!fs72.existsSync(gitDir)) return;
36155
- const exclude = path79.join(gitDir, "info", "exclude");
36385
+ const gitDir = path80.join(cwd, ".git");
36386
+ if (!fs73.existsSync(gitDir)) return;
36387
+ const exclude = path80.join(gitDir, "info", "exclude");
36156
36388
  let existing = "";
36157
36389
  try {
36158
- existing = fs72.readFileSync(exclude, "utf8");
36390
+ existing = fs73.readFileSync(exclude, "utf8");
36159
36391
  } catch {
36160
36392
  }
36161
36393
  if (existing.includes(".codeam/packs/")) return;
36162
- fs72.mkdirSync(path79.dirname(exclude), { recursive: true });
36163
- fs72.writeFileSync(exclude, `${existing.trimEnd()}
36394
+ fs73.mkdirSync(path80.dirname(exclude), { recursive: true });
36395
+ fs73.writeFileSync(exclude, `${existing.trimEnd()}
36164
36396
  .codeam/packs/
36165
36397
  `.trimStart());
36166
36398
  } catch {
@@ -36751,35 +36983,35 @@ function buildAcpPromptBlocks(payload) {
36751
36983
  }
36752
36984
 
36753
36985
  // src/agents/agent-standard.ts
36754
- var fs74 = __toESM(require("fs"));
36755
- var path81 = __toESM(require("path"));
36756
- var os59 = __toESM(require("os"));
36757
- function ensureAgentStandard(homeDir2 = os59.homedir()) {
36986
+ var fs75 = __toESM(require("fs"));
36987
+ var path82 = __toESM(require("path"));
36988
+ var os60 = __toESM(require("os"));
36989
+ function ensureAgentStandard(homeDir2 = os60.homedir()) {
36758
36990
  try {
36759
- const file = path81.join(homeDir2, ".claude", "CLAUDE.md");
36991
+ const file = path82.join(homeDir2, ".claude", "CLAUDE.md");
36760
36992
  let existing = "";
36761
36993
  try {
36762
- existing = fs74.readFileSync(file, "utf8");
36994
+ existing = fs75.readFileSync(file, "utf8");
36763
36995
  } catch {
36764
36996
  }
36765
36997
  if (existing.includes(AGENT_STANDARD_MARKER)) return;
36766
- fs74.mkdirSync(path81.dirname(file), { recursive: true });
36998
+ fs75.mkdirSync(path82.dirname(file), { recursive: true });
36767
36999
  const next = existing.trim() ? `${existing.trimEnd()}
36768
37000
 
36769
37001
  ${AGENT_STANDARD_BLOCK}
36770
37002
  ` : `${AGENT_STANDARD_BLOCK}
36771
37003
  `;
36772
- fs74.writeFileSync(file, next);
37004
+ fs75.writeFileSync(file, next);
36773
37005
  } catch {
36774
37006
  }
36775
37007
  }
36776
37008
  var _agentStandardSeam = {
36777
37009
  isLocalSession: () => isLocalSession(),
36778
- markerPath: (sessionId) => path81.join(os59.homedir(), ".codeam", "agent-standard", `${sessionId}.done`),
36779
- exists: (p2) => fs74.existsSync(p2),
37010
+ markerPath: (sessionId) => path82.join(os60.homedir(), ".codeam", "agent-standard", `${sessionId}.done`),
37011
+ exists: (p2) => fs75.existsSync(p2),
36780
37012
  write: (p2) => {
36781
- fs74.mkdirSync(path81.dirname(p2), { recursive: true });
36782
- fs74.writeFileSync(p2, "");
37013
+ fs75.mkdirSync(path82.dirname(p2), { recursive: true });
37014
+ fs75.writeFileSync(p2, "");
36783
37015
  }
36784
37016
  };
36785
37017
  function isClaude(agent) {
@@ -36883,20 +37115,111 @@ async function beadsActionH(ctx) {
36883
37115
  }
36884
37116
  return;
36885
37117
  }
37118
+ function rebindSessionHandles(ctx, outcome) {
37119
+ Object.assign(ctx, outcome.handles);
37120
+ return outcome;
37121
+ }
37122
+ async function routeSquadTask(ctx, target) {
37123
+ const { squad, routeToAgent } = ctx;
37124
+ if (!routeToAgent) {
37125
+ return { ok: false, error: "Routing to another agent is not supported on this session." };
37126
+ }
37127
+ const roster = squad?.roster;
37128
+ if (roster && !roster.agents.some((a) => a.agentId === target)) {
37129
+ return { ok: false, error: `Unknown agent '${target}' \u2014 not in your squad.` };
37130
+ }
37131
+ const member = squad?.member(target);
37132
+ const fastPathArmed = Boolean(member && (member.provisioned || member.binaryVerified));
37133
+ let { result } = rebindSessionHandles(ctx, await routeToAgent(target));
37134
+ if (!result.ok && fastPathArmed) {
37135
+ log.warn(
37136
+ "acpRunner",
37137
+ `squad: fast-path route to ${target} failed (${result.error}) \u2014 retrying full path`
37138
+ );
37139
+ ({ result } = rebindSessionHandles(ctx, await routeToAgent(target, { skipFastPath: true })));
37140
+ }
37141
+ if (!result.ok) {
37142
+ return { ok: false, error: result.error ?? `Couldn't switch to ${target}.` };
37143
+ }
37144
+ return { ok: true };
37145
+ }
37146
+ function prefixSquadContext(ctx, blocks) {
37147
+ if (ctx.pendingHandoff?.current) {
37148
+ blocks.unshift({ type: "text", text: ctx.pendingHandoff.current });
37149
+ ctx.pendingHandoff.current = null;
37150
+ }
37151
+ const { squad, opts } = ctx;
37152
+ if (!squad) return;
37153
+ const member = squad.member(opts.agent);
37154
+ if (squad.turnCount() > member.lastTurnIndex) {
37155
+ const otherEntries = squad.entriesSince(member.lastTurnIndex).filter((e) => e.agentId !== opts.agent);
37156
+ const briefing = buildDeltaBriefing(otherEntries);
37157
+ if (briefing) blocks.unshift({ type: "text", text: briefing });
37158
+ }
37159
+ const roster = squad.roster;
37160
+ if (roster && member.lastTurnIndex === 0) {
37161
+ const preamble = buildTeamPreamble(roster, opts.agent, {
37162
+ handoffInstructions: roster.handoffsEnabled === true
37163
+ });
37164
+ if (preamble) blocks.unshift({ type: "text", text: preamble });
37165
+ }
37166
+ }
37167
+ function recordSquadTurn(ctx, prompt, replySummary) {
37168
+ const { squad, opts } = ctx;
37169
+ if (!squad) return;
37170
+ squad.recordTurn({
37171
+ agentId: opts.agent,
37172
+ prompt,
37173
+ replySummary,
37174
+ // TurnFileAggregator owns per-turn file changesets end-to-end (git diff →
37175
+ // outbox POST) and exposes no path list, and its flush is fire-and-forget,
37176
+ // so there is nothing accurate to attribute synchronously here. The
37177
+ // briefing simply omits the files clause rather than guessing.
37178
+ filesTouched: []
37179
+ });
37180
+ squad.member(opts.agent).lastTurnIndex = squad.turnCount();
37181
+ }
37182
+ function handoffTargets(ctx) {
37183
+ const roster = ctx.squad?.roster;
37184
+ if (!roster) return /* @__PURE__ */ new Set();
37185
+ return new Set(roster.agents.map((a) => a.agentId).filter((id) => id !== ctx.opts.agent));
37186
+ }
37187
+ function resolvePendingProposal(ctx, requestedAgentId) {
37188
+ const slot = ctx.pendingProposal;
37189
+ const pending = slot?.current;
37190
+ if (!slot || !pending) return;
37191
+ slot.current = null;
37192
+ const accepted = requestedAgentId === pending.toAgentId;
37193
+ log.info(
37194
+ "acpRunner",
37195
+ `squad: handoff ${pending.proposalId} ${accepted ? "accepted" : "declined"}`
37196
+ );
37197
+ void ctx.postSquadEvent?.("handoff_resolved", {
37198
+ proposalId: pending.proposalId,
37199
+ accepted
37200
+ });
37201
+ }
37202
+ function emitHandoffProposal(ctx, proposal) {
37203
+ const { squad, pendingProposal, postSquadEvent, opts, cmd } = ctx;
37204
+ if (!proposal || !pendingProposal || !postSquadEvent) return;
37205
+ if (squad?.roster?.handoffsEnabled !== true) return;
37206
+ if (pendingProposal.current) {
37207
+ log.info("acpRunner", "squad: dropping handoff proposal \u2014 one is already pending");
37208
+ return;
37209
+ }
37210
+ const record2 = {
37211
+ proposalId: `hp-${cmd.id}`,
37212
+ fromAgentId: opts.agent,
37213
+ toAgentId: proposal.to,
37214
+ reason: proposal.reason,
37215
+ prompt: proposal.prompt
37216
+ };
37217
+ pendingProposal.current = record2;
37218
+ log.info("acpRunner", `squad: handoff proposed ${opts.agent} \u2192 ${record2.toAgentId}`);
37219
+ void postSquadEvent("handoff_proposed", { ...record2 });
37220
+ }
36886
37221
  async function startTaskH(ctx) {
36887
- const {
36888
- cmd,
36889
- client: client3,
36890
- relay,
36891
- streaming,
36892
- opts,
36893
- history,
36894
- turnFiles,
36895
- publisher,
36896
- recentStderr,
36897
- budgetRecovery,
36898
- budgetReachedFlag
36899
- } = ctx;
37222
+ const { cmd, relay, streaming, opts, turnFiles, publisher, recentStderr, budgetReachedFlag } = ctx;
36900
37223
  const payload = cmd.payload;
36901
37224
  const blocks = buildAcpPromptBlocks(payload ?? {});
36902
37225
  if (blocks.length === 0) {
@@ -36904,6 +37227,17 @@ async function startTaskH(ctx) {
36904
37227
  await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
36905
37228
  return;
36906
37229
  }
37230
+ const requestedAgentId = typeof payload?.agentId === "string" ? payload.agentId.trim() : "";
37231
+ resolvePendingProposal(ctx, requestedAgentId);
37232
+ if (requestedAgentId.length > 0 && requestedAgentId !== opts.agent) {
37233
+ const routed = await routeSquadTask(ctx, requestedAgentId);
37234
+ if (!routed.ok) {
37235
+ log.warn("acpRunner", `start_task routing to ${requestedAgentId} failed: ${routed.error}`);
37236
+ await relay.sendResult(cmd.id, "failed", { error: routed.error });
37237
+ return;
37238
+ }
37239
+ }
37240
+ const { client: client3, history, budgetRecovery } = ctx;
36907
37241
  const promptText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
36908
37242
  const imageCount = blocks.filter((b) => b.type === "image").length;
36909
37243
  log.info(
@@ -36917,10 +37251,7 @@ async function startTaskH(ctx) {
36917
37251
  await streaming.beginTurn();
36918
37252
  history.appendUserPrompt(promptText);
36919
37253
  maybePrefaceAgentStandard(blocks, opts.agent, opts.sessionId);
36920
- if (ctx.pendingHandoff?.current) {
36921
- blocks.unshift({ type: "text", text: ctx.pendingHandoff.current });
36922
- ctx.pendingHandoff.current = null;
36923
- }
37254
+ prefixSquadContext(ctx, blocks);
36924
37255
  let turnClosed = false;
36925
37256
  try {
36926
37257
  const reply = await client3.prompt(blocks);
@@ -36972,12 +37303,19 @@ async function startTaskH(ctx) {
36972
37303
  } else {
36973
37304
  await streaming.closeTurnWithInteractiveDetection();
36974
37305
  turnClosed = true;
36975
- const replyLine = formatAgentReplyLine(finalText);
37306
+ const { cleanText, proposal } = extractHandoffProposal(
37307
+ finalText,
37308
+ opts.agent,
37309
+ handoffTargets(ctx)
37310
+ );
37311
+ const replyLine = formatAgentReplyLine(cleanText);
36976
37312
  if (replyLine.length > 0) {
36977
37313
  showInfo(replyLine);
36978
37314
  }
36979
- history.appendAgentReply(finalText);
37315
+ history.appendAgentReply(cleanText);
36980
37316
  void history.flush();
37317
+ recordSquadTurn(ctx, promptText, cleanText);
37318
+ emitHandoffProposal(ctx, proposal);
36981
37319
  void publisher.publishOutput({
36982
37320
  type: "input_suggestion",
36983
37321
  content: ACP_QUICK_REPLIES,
@@ -37776,14 +38114,18 @@ var StreamingState = class {
37776
38114
  }
37777
38115
  const cumulativeContent = reconcileCumulative(existing?.content ?? "", delta.delta);
37778
38116
  this.streamingChunks.set(chunkId, { kind: delta.kind, content: cumulativeContent });
38117
+ let fenceCut = -1;
37779
38118
  if (delta.kind === "text") {
37780
38119
  this.recomputeText();
37781
- void this.publisher.publishOutput({ type: "text", content: this.text, done: false });
38120
+ fenceCut = handoffFenceStart(this.text);
38121
+ const visibleText = fenceCut === -1 ? this.text : this.text.slice(0, fenceCut).trimEnd();
38122
+ void this.publisher.publishOutput({ type: "text", content: visibleText, done: false });
37782
38123
  }
38124
+ const visibleChunkContent = delta.kind === "text" && fenceCut !== -1 ? cumulativeContent.slice(0, fenceCut).trimEnd() : cumulativeContent;
37783
38125
  void this.publisher.publishStreamingChunk({
37784
38126
  chunkId,
37785
38127
  kind: delta.kind,
37786
- content: cumulativeContent,
38128
+ content: visibleChunkContent,
37787
38129
  isFinal: false
37788
38130
  });
37789
38131
  }
@@ -37813,8 +38155,27 @@ var StreamingState = class {
37813
38155
  * spuriously and strand the runner with a free-form pending state
37814
38156
  * the user can't see / answer.
37815
38157
  */
38158
+ /**
38159
+ * Presentation-only view of a text buffer: everything BEFORE a
38160
+ * ```codeam-handoff fence. The fence is protocol litter the app renders as
38161
+ * a proposal card (see `handoff-protocol.ts`) — it must never reach the
38162
+ * user, and the TERMINAL frames (`done:true` / `isFinal:true`) are the ones
38163
+ * that persist, so suppressing it only while streaming would still leave it
38164
+ * pinned on the finished bubble. Internal state is untouched:
38165
+ * {@link getCurrentText} keeps returning the RAW text so the turn-close
38166
+ * extraction can parse the proposal out of it.
38167
+ *
38168
+ * Masked-aware (`stripHandoffFences`), unlike the live-stream `append()`
38169
+ * cut (`handoffFenceStart`, unmasked): a TERMINAL frame is the one that
38170
+ * PERSISTS, so cutting on a fence quoted as an example inside a
38171
+ * 4+-backtick block here would permanently truncate the bubble. The live
38172
+ * cut stays unmasked/cheap — a mid-stream example is never terminal.
38173
+ */
38174
+ visible(text) {
38175
+ return stripHandoffFences(text);
38176
+ }
37816
38177
  async closeAll() {
37817
- const finalText = this.text;
38178
+ const finalText = this.visible(this.text);
37818
38179
  this.text = "";
37819
38180
  await Promise.all([
37820
38181
  this.publisher.publishOutput({ type: "text", content: finalText, done: true }),
@@ -37858,7 +38219,9 @@ var StreamingState = class {
37858
38219
  ([chunkId, { kind, content }]) => this.publisher.publishStreamingChunk({
37859
38220
  chunkId,
37860
38221
  kind,
37861
- content,
38222
+ // Terminal frame — same fence suppression the live deltas apply, so
38223
+ // a proposal fence never survives on the finalised bubble.
38224
+ content: kind === "text" ? this.visible(content) : content,
37862
38225
  isFinal: true
37863
38226
  })
37864
38227
  )
@@ -37885,7 +38248,7 @@ var StreamingState = class {
37885
38248
  * text done:true chunk with the full cumulative).
37886
38249
  */
37887
38250
  async closeTurnWithInteractiveDetection() {
37888
- const finalText = this.text;
38251
+ const finalText = this.visible(this.text);
37889
38252
  this.text = "";
37890
38253
  const flushSc = this.flushStreamingChunks();
37891
38254
  const extracted = extractSelectPrompt(finalText);
@@ -38388,13 +38751,32 @@ async function runAcpSession(opts) {
38388
38751
  acpSessionId = id;
38389
38752
  },
38390
38753
  switchAgentForSession,
38391
- pendingHandoff
38754
+ pendingHandoff,
38755
+ // Agent Squad: roster/journal state, the @-mention route, the
38756
+ // single-slot proposal, and the SAME serialized event chain the
38757
+ // switch uses (so proposal events can't overtake switch events).
38758
+ squad,
38759
+ routeToAgent,
38760
+ pendingProposal,
38761
+ emitSwitchEvent
38392
38762
  );
38393
38763
  },
38394
38764
  { id: opts.agent, name: opts.agent, displayName: opts.agent }
38395
38765
  );
38396
38766
  const HANDOFF_MAX_CHARS = 16e3;
38397
38767
  const pendingHandoff = { current: null };
38768
+ const squad = new SquadState({ sessionId: opts.sessionId });
38769
+ const refreshSquadRoster = () => {
38770
+ void fetchSquadRoster({
38771
+ sessionId: opts.sessionId,
38772
+ pluginId: opts.pluginId,
38773
+ pluginAuthToken: opts.pluginAuthToken
38774
+ }).then((roster) => {
38775
+ if (roster) squad.roster = roster;
38776
+ });
38777
+ };
38778
+ refreshSquadRoster();
38779
+ const pendingProposal = { current: null };
38398
38780
  let switchCredentialEnv = {};
38399
38781
  const relaunchWith = async (nextAgent) => {
38400
38782
  try {
@@ -38404,6 +38786,7 @@ async function runAcpSession(opts) {
38404
38786
  await streaming.closeAll();
38405
38787
  const prevAgent = opts.agent;
38406
38788
  const transcript = history.recentTranscript(HANDOFF_MAX_CHARS);
38789
+ squad.member(prevAgent).acpSessionId = acpSessionId;
38407
38790
  await client3.stop();
38408
38791
  const adapter = await resolveAcpAdapterWithRetry(nextAgent);
38409
38792
  if (!adapter) throw new Error(`no ACP adapter available for ${nextAgent}`);
@@ -38453,31 +38836,74 @@ async function runAcpSession(opts) {
38453
38836
  payload
38454
38837
  })
38455
38838
  );
38456
- const switchAgentForSession = (rawAgentId) => performAgentSwitch(
38457
- {
38458
- currentAgent: () => opts.agent,
38459
- postEvent: emitSwitchEvent,
38460
- fetchCredential: (agentId) => fetchProvisionCredential({
38461
- agentId,
38462
- sessionId: opts.sessionId,
38463
- pluginId: opts.pluginId,
38464
- pluginAuthToken: opts.pluginAuthToken,
38465
- includeInstallScript: true
38466
- }),
38467
- provisionCredential: (agentId, auth) => {
38468
- switchCredentialEnv = provisionAgentCredentials(agentId, auth);
38469
- },
38470
- ensureBinary: (agentId, installScript) => ensureAgentBinaryForSwitch(agentId, installScript),
38471
- swapRuntime: relaunchWith,
38472
- revertRuntime: relaunchWith,
38473
- persistAgent: (agentId) => setSessionAgent(opts.pluginId, agentId),
38474
- reannounce: (agentId) => {
38475
- relay.setAgentMeta({ id: agentId, name: agentId, displayName: agentId });
38476
- relay.reannounceAgents();
38477
- }
38839
+ const switchDeps = {
38840
+ currentAgent: () => opts.agent,
38841
+ postEvent: emitSwitchEvent,
38842
+ fetchCredential: (agentId) => fetchProvisionCredential({
38843
+ agentId,
38844
+ sessionId: opts.sessionId,
38845
+ pluginId: opts.pluginId,
38846
+ pluginAuthToken: opts.pluginAuthToken,
38847
+ includeInstallScript: true
38848
+ }),
38849
+ provisionCredential: (agentId, auth) => {
38850
+ switchCredentialEnv = provisionAgentCredentials(agentId, auth);
38478
38851
  },
38479
- rawAgentId
38480
- );
38852
+ ensureBinary: (agentId, installScript) => ensureAgentBinaryForSwitch(agentId, installScript),
38853
+ swapRuntime: relaunchWith,
38854
+ revertRuntime: relaunchWith,
38855
+ persistAgent: (agentId) => setSessionAgent(opts.pluginId, agentId),
38856
+ reannounce: (agentId) => {
38857
+ relay.setAgentMeta({ id: agentId, name: agentId, displayName: agentId });
38858
+ relay.reannounceAgents();
38859
+ }
38860
+ };
38861
+ const switchAgentForSession = async (rawAgentId) => {
38862
+ const result = await performAgentSwitch(switchDeps, rawAgentId);
38863
+ if (result.ok) refreshSquadRoster();
38864
+ return result;
38865
+ };
38866
+ const sessionHandles = () => ({
38867
+ client: client3,
38868
+ acpSessionId,
38869
+ history,
38870
+ jsonlHistory,
38871
+ agentCaps,
38872
+ budgetRecovery
38873
+ });
38874
+ const routeToAgent = async (target, routeOpts = {}) => {
38875
+ const m = squad.member(target);
38876
+ const fast = routeOpts.skipFastPath !== true;
38877
+ const result = await performAgentSwitch(switchDeps, target, {
38878
+ skipProvision: fast && m.provisioned,
38879
+ skipInstall: fast && m.binaryVerified
38880
+ });
38881
+ if (!result.ok) {
38882
+ if (fast) {
38883
+ m.provisioned = false;
38884
+ m.binaryVerified = false;
38885
+ }
38886
+ return { result, handles: sessionHandles() };
38887
+ }
38888
+ m.provisioned = true;
38889
+ m.binaryVerified = true;
38890
+ if (m.acpSessionId && agentCaps?.loadSession) {
38891
+ try {
38892
+ await client3.loadSession(m.acpSessionId);
38893
+ acpSessionId = m.acpSessionId;
38894
+ history.switchActiveSession(m.acpSessionId);
38895
+ pendingHandoff.current = null;
38896
+ log.info(
38897
+ "acpRunner",
38898
+ `squad: resumed ${target}'s conversation ${m.acpSessionId.slice(0, 8)}`
38899
+ );
38900
+ } catch (err) {
38901
+ log.warn("acpRunner", `squad: resume for ${target} failed: ${describeError(err)}`);
38902
+ }
38903
+ }
38904
+ refreshSquadRoster();
38905
+ return { result, handles: sessionHandles() };
38906
+ };
38481
38907
  await onboardingWelcomeDone;
38482
38908
  relay.start();
38483
38909
  void createWakeCredentialProbe({
@@ -38508,7 +38934,7 @@ async function runAcpSession(opts) {
38508
38934
  await new Promise(() => {
38509
38935
  });
38510
38936
  }
38511
- async function handleCommand(cmd, client3, relay, acpSessionId, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, budgetRecovery, budgetReachedFlag, onActiveSessionChanged, switchAgent, pendingHandoff) {
38937
+ async function handleCommand(cmd, client3, relay, acpSessionId, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, budgetRecovery, budgetReachedFlag, onActiveSessionChanged, switchAgent, pendingHandoff, squad, routeToAgent, pendingProposal, postSquadEvent) {
38512
38938
  const session = {
38513
38939
  client: client3,
38514
38940
  relay,
@@ -38526,7 +38952,11 @@ async function handleCommand(cmd, client3, relay, acpSessionId, streaming, opts,
38526
38952
  budgetReachedFlag,
38527
38953
  onActiveSessionChanged,
38528
38954
  switchAgent,
38529
- pendingHandoff
38955
+ pendingHandoff,
38956
+ squad,
38957
+ routeToAgent,
38958
+ pendingProposal,
38959
+ postSquadEvent
38530
38960
  };
38531
38961
  await dispatchAcpCommand(assembleAcpCommandContext(session, cmd));
38532
38962
  }
@@ -39586,9 +40016,9 @@ function startClaudeCredentialSync(opts) {
39586
40016
  }
39587
40017
 
39588
40018
  // src/beads/workflow-hint.ts
39589
- var fs75 = __toESM(require("fs"));
39590
- var path82 = __toESM(require("path"));
39591
- var os60 = __toESM(require("os"));
40019
+ var fs76 = __toESM(require("fs"));
40020
+ var path83 = __toESM(require("path"));
40021
+ var os61 = __toESM(require("os"));
39592
40022
  var BEADS_HINT_MARKER = "<!-- codeam:beads-workflow -->";
39593
40023
  var BEADS_HINT = `${BEADS_HINT_MARKER}
39594
40024
  # Beads (bd) \u2014 task tracking + persistent memory (ALWAYS use it)
@@ -39602,22 +40032,22 @@ This environment uses **bd (beads)** for issue/task tracking and persistent memo
39602
40032
  - \`bd ready\` (available work) \xB7 \`bd show <id>\` \xB7 \`bd update <id> --claim\` \xB7 \`bd close <id>\`.
39603
40033
  - Use \`bd remember "..."\` for persistent knowledge \u2014 do NOT use MEMORY.md files.
39604
40034
  ${BEADS_HINT_MARKER}`;
39605
- function ensureBeadsWorkflowHint(homeDir2 = os60.homedir()) {
40035
+ function ensureBeadsWorkflowHint(homeDir2 = os61.homedir()) {
39606
40036
  try {
39607
- const file = path82.join(homeDir2, ".claude", "CLAUDE.md");
40037
+ const file = path83.join(homeDir2, ".claude", "CLAUDE.md");
39608
40038
  let existing = "";
39609
40039
  try {
39610
- existing = fs75.readFileSync(file, "utf8");
40040
+ existing = fs76.readFileSync(file, "utf8");
39611
40041
  } catch {
39612
40042
  }
39613
40043
  if (existing.includes(BEADS_HINT_MARKER)) return;
39614
- fs75.mkdirSync(path82.dirname(file), { recursive: true });
40044
+ fs76.mkdirSync(path83.dirname(file), { recursive: true });
39615
40045
  const next = existing.trim() ? `${existing.trimEnd()}
39616
40046
 
39617
40047
  ${BEADS_HINT}
39618
40048
  ` : `${BEADS_HINT}
39619
40049
  `;
39620
- fs75.writeFileSync(file, next);
40050
+ fs76.writeFileSync(file, next);
39621
40051
  } catch {
39622
40052
  }
39623
40053
  }
@@ -39989,7 +40419,7 @@ var AcpDriver = class {
39989
40419
  };
39990
40420
 
39991
40421
  // src/baton/transcript-mirror.ts
39992
- var fs76 = __toESM(require("fs"));
40422
+ var fs77 = __toESM(require("fs"));
39993
40423
  var TranscriptMirror = class {
39994
40424
  constructor(deps) {
39995
40425
  this.deps = deps;
@@ -40056,7 +40486,7 @@ var TranscriptMirror = class {
40056
40486
  }
40057
40487
  };
40058
40488
  function defaultWatch(file, onChange) {
40059
- const w3 = fs76.watch(file, { persistent: false }, () => onChange());
40489
+ const w3 = fs77.watch(file, { persistent: false }, () => onChange());
40060
40490
  return () => w3.close();
40061
40491
  }
40062
40492
 
@@ -40354,16 +40784,16 @@ function keepDeviceAwake(deps = {}) {
40354
40784
  }
40355
40785
 
40356
40786
  // src/agents/claude/onboarding.ts
40357
- var fs77 = __toESM(require("fs"));
40358
- var os62 = __toESM(require("os"));
40359
- var path83 = __toESM(require("path"));
40787
+ var fs78 = __toESM(require("fs"));
40788
+ var os63 = __toESM(require("os"));
40789
+ var path84 = __toESM(require("path"));
40360
40790
  var ONBOARDING_VERSION_SENTINEL = "9999.0.0";
40361
40791
  function ensureClaudeOnboarded(cwd) {
40362
40792
  try {
40363
- const file = path83.join(os62.homedir(), ".claude.json");
40793
+ const file = path84.join(os63.homedir(), ".claude.json");
40364
40794
  let config = {};
40365
40795
  try {
40366
- config = JSON.parse(fs77.readFileSync(file, "utf8"));
40796
+ config = JSON.parse(fs78.readFileSync(file, "utf8"));
40367
40797
  } catch {
40368
40798
  }
40369
40799
  let changed = false;
@@ -40388,8 +40818,8 @@ function ensureClaudeOnboarded(cwd) {
40388
40818
  }
40389
40819
  }
40390
40820
  if (!changed) return;
40391
- fs77.mkdirSync(path83.dirname(file), { recursive: true });
40392
- fs77.writeFileSync(file, JSON.stringify(config, null, 2));
40821
+ fs78.mkdirSync(path84.dirname(file), { recursive: true });
40822
+ fs78.writeFileSync(file, JSON.stringify(config, null, 2));
40393
40823
  log.info(
40394
40824
  "claude",
40395
40825
  `pre-completed Claude onboarding${cwd ? ` + trusted workspace ${cwd}` : ""}`
@@ -41121,7 +41551,7 @@ var import_picocolors11 = __toESM(require("picocolors"));
41121
41551
  var import_child_process30 = require("child_process");
41122
41552
  var import_util4 = require("util");
41123
41553
  var import_picocolors9 = __toESM(require("picocolors"));
41124
- var path84 = __toESM(require("path"));
41554
+ var path85 = __toESM(require("path"));
41125
41555
  var execFileP6 = (0, import_util4.promisify)(import_child_process30.execFile);
41126
41556
  var MAX_BUFFER = 8 * 1024 * 1024;
41127
41557
  function resetStdinForChild() {
@@ -41610,7 +42040,7 @@ var GitHubCodespacesProvider = class {
41610
42040
  });
41611
42041
  }
41612
42042
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
41613
- const remoteDir = path84.posix.dirname(remotePath);
42043
+ const remoteDir = path85.posix.dirname(remotePath);
41614
42044
  const parts = [
41615
42045
  `mkdir -p ${shellQuote(remoteDir)}`,
41616
42046
  `cat > ${shellQuote(remotePath)}`
@@ -41680,7 +42110,7 @@ function shellQuote(s) {
41680
42110
  // src/services/providers/gitpod.ts
41681
42111
  var import_child_process31 = require("child_process");
41682
42112
  var import_util5 = require("util");
41683
- var path85 = __toESM(require("path"));
42113
+ var path86 = __toESM(require("path"));
41684
42114
  var import_picocolors10 = __toESM(require("picocolors"));
41685
42115
  var execFileP7 = (0, import_util5.promisify)(import_child_process31.execFile);
41686
42116
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -41920,7 +42350,7 @@ var GitpodProvider = class {
41920
42350
  });
41921
42351
  }
41922
42352
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
41923
- const remoteDir = path85.posix.dirname(remotePath);
42353
+ const remoteDir = path86.posix.dirname(remotePath);
41924
42354
  const parts = [
41925
42355
  `mkdir -p ${shellQuote2(remoteDir)}`,
41926
42356
  `cat > ${shellQuote2(remotePath)}`
@@ -41956,7 +42386,7 @@ function shellQuote2(s) {
41956
42386
  // src/services/providers/gitlab-workspaces.ts
41957
42387
  var import_child_process32 = require("child_process");
41958
42388
  var import_util6 = require("util");
41959
- var path86 = __toESM(require("path"));
42389
+ var path87 = __toESM(require("path"));
41960
42390
  var execFileP8 = (0, import_util6.promisify)(import_child_process32.execFile);
41961
42391
  var MAX_BUFFER3 = 8 * 1024 * 1024;
41962
42392
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -42216,7 +42646,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
42216
42646
  }
42217
42647
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
42218
42648
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
42219
- const remoteDir = path86.posix.dirname(remotePath);
42649
+ const remoteDir = path87.posix.dirname(remotePath);
42220
42650
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
42221
42651
  if (options.mode != null) {
42222
42652
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -42284,7 +42714,7 @@ function shellQuote3(s) {
42284
42714
  // src/services/providers/railway.ts
42285
42715
  var import_child_process33 = require("child_process");
42286
42716
  var import_util7 = require("util");
42287
- var path87 = __toESM(require("path"));
42717
+ var path88 = __toESM(require("path"));
42288
42718
  var execFileP9 = (0, import_util7.promisify)(import_child_process33.execFile);
42289
42719
  var MAX_BUFFER4 = 8 * 1024 * 1024;
42290
42720
  function resetStdinForChild4() {
@@ -42520,7 +42950,7 @@ var RailwayProvider = class {
42520
42950
  if (!projectId || !serviceId) {
42521
42951
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
42522
42952
  }
42523
- const remoteDir = path87.posix.dirname(remotePath);
42953
+ const remoteDir = path88.posix.dirname(remotePath);
42524
42954
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
42525
42955
  if (options.mode != null) {
42526
42956
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -43166,8 +43596,8 @@ async function invite() {
43166
43596
  var import_node_dns = require("dns");
43167
43597
  var import_node_util5 = require("util");
43168
43598
  var import_node_crypto13 = require("crypto");
43169
- var fs78 = __toESM(require("fs"));
43170
- var path88 = __toESM(require("path"));
43599
+ var fs79 = __toESM(require("fs"));
43600
+ var path89 = __toESM(require("path"));
43171
43601
  var import_picocolors14 = __toESM(require("picocolors"));
43172
43602
  var dnsResolveP = (0, import_node_util5.promisify)(import_node_dns.resolve);
43173
43603
  async function checkDns(apiBase2) {
@@ -43223,13 +43653,13 @@ async function checkHealth(apiBase2) {
43223
43653
  }
43224
43654
  }
43225
43655
  function checkConfigDir() {
43226
- const dir = path88.join(require("os").homedir(), ".codeam");
43656
+ const dir = path89.join(require("os").homedir(), ".codeam");
43227
43657
  try {
43228
- fs78.mkdirSync(dir, { recursive: true, mode: 448 });
43229
- const probe = path88.join(dir, ".doctor-probe");
43230
- fs78.writeFileSync(probe, "ok", { mode: 384 });
43231
- const read2 = fs78.readFileSync(probe, "utf8");
43232
- fs78.unlinkSync(probe);
43658
+ fs79.mkdirSync(dir, { recursive: true, mode: 448 });
43659
+ const probe = path89.join(dir, ".doctor-probe");
43660
+ fs79.writeFileSync(probe, "ok", { mode: 384 });
43661
+ const read2 = fs79.readFileSync(probe, "utf8");
43662
+ fs79.unlinkSync(probe);
43233
43663
  if (read2 !== "ok") throw new Error("write/read round-trip mismatch");
43234
43664
  return {
43235
43665
  id: "config-dir",
@@ -43269,9 +43699,9 @@ function checkSessions() {
43269
43699
  }
43270
43700
  }
43271
43701
  function checkAgentBinaries() {
43272
- const os65 = createOsStrategy();
43702
+ const os66 = createOsStrategy();
43273
43703
  return getEnabledAgents().map((meta) => {
43274
- const found = os65.findInPath(meta.binaryName);
43704
+ const found = os66.findInPath(meta.binaryName);
43275
43705
  return {
43276
43706
  id: `agent-${meta.id}`,
43277
43707
  label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
@@ -43293,7 +43723,7 @@ function checkNodePty() {
43293
43723
  detail: "not required on this platform"
43294
43724
  };
43295
43725
  }
43296
- const vendoredPath = path88.join(__dirname, "vendor", "node-pty");
43726
+ const vendoredPath = path89.join(__dirname, "vendor", "node-pty");
43297
43727
  for (const target of [vendoredPath, "node-pty"]) {
43298
43728
  try {
43299
43729
  require(target);
@@ -43335,7 +43765,7 @@ function checkChokidar() {
43335
43765
  }
43336
43766
  async function doctor(args2 = []) {
43337
43767
  const json = args2.includes("--json");
43338
- const cliVersion = true ? "2.63.1" : "0.0.0-dev";
43768
+ const cliVersion = true ? "2.64.0" : "0.0.0-dev";
43339
43769
  const apiBase2 = resolveApiBaseUrl();
43340
43770
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
43341
43771
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -43726,7 +44156,7 @@ async function mcpRun(args2) {
43726
44156
  // src/commands/version.ts
43727
44157
  var import_picocolors15 = __toESM(require("picocolors"));
43728
44158
  function version2() {
43729
- const v = true ? "2.63.1" : "unknown";
44159
+ const v = true ? "2.64.0" : "unknown";
43730
44160
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
43731
44161
  }
43732
44162
 
@@ -43875,10 +44305,10 @@ var EXIT_CODE_NAMES = {
43875
44305
  };
43876
44306
 
43877
44307
  // src/index.ts
43878
- var os64 = __toESM(require("os"));
44308
+ var os65 = __toESM(require("os"));
43879
44309
  if (!process.env.HOME) {
43880
44310
  try {
43881
- const home = os64.homedir();
44311
+ const home = os65.homedir();
43882
44312
  if (home) process.env.HOME = home;
43883
44313
  } catch {
43884
44314
  }