codeam-cli 2.64.0 → 2.65.1

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 +38 -0
  2. package/dist/index.js +1557 -965
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2791,6 +2791,15 @@ function resolveApiBaseUrl() {
2791
2791
 
2792
2792
  // ../../packages/shared/src/types/agent-squad.ts
2793
2793
  var HANDOFF_FENCE_TAG = "codeam-handoff";
2794
+ var SQUAD_CONFIGURE_COMMAND = "squad_configure";
2795
+ var SQUAD_STATS_COMMAND = "squad_stats";
2796
+ var SQUAD_HOP_BUDGET_DEFAULT = 3;
2797
+ var SQUAD_HOP_BUDGET_MIN = 1;
2798
+ var SQUAD_HOP_BUDGET_MAX = 10;
2799
+ function clampHopBudget(value) {
2800
+ if (typeof value !== "number" || !Number.isFinite(value)) return SQUAD_HOP_BUDGET_DEFAULT;
2801
+ return Math.min(SQUAD_HOP_BUDGET_MAX, Math.max(SQUAD_HOP_BUDGET_MIN, Math.round(value)));
2802
+ }
2794
2803
  var SQUAD_SPECIALTIES = {
2795
2804
  claude: "deep reasoning, refactors, and multi-step architecture work",
2796
2805
  codex: "fast, focused implementation and test fixing",
@@ -3257,6 +3266,22 @@ function makeConfig(baseDir) {
3257
3266
  s.agent = agent;
3258
3267
  save(c2);
3259
3268
  }
3269
+ function setSquadAuto2(pluginId, value) {
3270
+ const stored = {
3271
+ enabled: value.enabled === true,
3272
+ hopBudget: clampHopBudget(value.hopBudget)
3273
+ };
3274
+ const c2 = load();
3275
+ const s = c2.sessions.find((x) => x.pluginId === pluginId);
3276
+ if (!s) return null;
3277
+ s.squadAuto = stored;
3278
+ save(c2);
3279
+ return stored;
3280
+ }
3281
+ function getSquadAuto2(pluginId) {
3282
+ const s = load().sessions.find((x) => x.pluginId === pluginId);
3283
+ return s?.squadAuto ?? null;
3284
+ }
3260
3285
  function clearAll2() {
3261
3286
  try {
3262
3287
  fs3.unlinkSync(file);
@@ -3269,7 +3294,7 @@ function makeConfig(baseDir) {
3269
3294
  function loadCliConfig2() {
3270
3295
  return load();
3271
3296
  }
3272
- return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, getActiveSessionForAgent: getActiveSessionForAgent2, setDisable1mContext: setDisable1mContext2, setSessionAgent: setSessionAgent2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
3297
+ return { getConfig: getConfig2, ensurePluginId: ensurePluginId2, addSession: addSession2, removeSession: removeSession2, setActiveSession: setActiveSession2, getActiveSession: getActiveSession2, getActiveSessionForAgent: getActiveSessionForAgent2, setDisable1mContext: setDisable1mContext2, setSessionAgent: setSessionAgent2, setSquadAuto: setSquadAuto2, getSquadAuto: getSquadAuto2, clearAll: clearAll2, saveCliConfig: saveCliConfig2, loadCliConfig: loadCliConfig2 };
3273
3298
  }
3274
3299
  var CODESPACE_ENV_KEYS = [
3275
3300
  "PREVIEW_TUNNEL_TOKEN",
@@ -3293,12 +3318,12 @@ function loadCodespaceEnv() {
3293
3318
  }
3294
3319
  }
3295
3320
  var _default = makeConfig();
3296
- var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, setSessionAgent, clearAll, saveCliConfig, loadCliConfig } = _default;
3321
+ var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, setSessionAgent, setSquadAuto, getSquadAuto, clearAll, saveCliConfig, loadCliConfig } = _default;
3297
3322
 
3298
3323
  // src/commands/pair-auto.ts
3299
- var fs63 = __toESM(require("fs"));
3300
- var os53 = __toESM(require("os"));
3301
- var path68 = __toESM(require("path"));
3324
+ var fs64 = __toESM(require("fs"));
3325
+ var os54 = __toESM(require("os"));
3326
+ var path69 = __toESM(require("path"));
3302
3327
  var import_crypto4 = require("crypto");
3303
3328
 
3304
3329
  // src/services/telemetry.service.ts
@@ -8048,7 +8073,7 @@ function readAnonId() {
8048
8073
  }
8049
8074
  function superProperties() {
8050
8075
  return {
8051
- cliVersion: true ? "2.64.0" : "0.0.0-dev",
8076
+ cliVersion: true ? "2.65.1" : "0.0.0-dev",
8052
8077
  nodeVersion: process.version,
8053
8078
  platform: process.platform,
8054
8079
  arch: process.arch,
@@ -8229,7 +8254,7 @@ var os4 = __toESM(require("os"));
8229
8254
  // package.json
8230
8255
  var package_default = {
8231
8256
  name: "codeam-cli",
8232
- version: "2.64.0",
8257
+ version: "2.65.1",
8233
8258
  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.",
8234
8259
  type: "commonjs",
8235
8260
  main: "dist/index.js",
@@ -9681,7 +9706,7 @@ var CommandRelayService = class _CommandRelayService {
9681
9706
  // fresh + clear the "CLI update available" banner after a self-update
9682
9707
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9683
9708
  // pair/reconnect). Older backends ignore the extra field.
9684
- ..."2.64.0" ? { ideVersion: "2.64.0" } : {}
9709
+ ..."2.65.1" ? { ideVersion: "2.65.1" } : {}
9685
9710
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9686
9711
  }
9687
9712
  /**
@@ -11073,11 +11098,11 @@ function closeAllTerminals() {
11073
11098
  }
11074
11099
 
11075
11100
  // src/commands/start/handlers.ts
11076
- var fs62 = __toESM(require("fs"));
11077
- var os52 = __toESM(require("os"));
11078
- var path67 = __toESM(require("path"));
11101
+ var fs63 = __toESM(require("fs"));
11102
+ var os53 = __toESM(require("os"));
11103
+ var path68 = __toESM(require("path"));
11079
11104
  var import_crypto3 = require("crypto");
11080
- var import_child_process25 = require("child_process");
11105
+ var import_child_process26 = require("child_process");
11081
11106
 
11082
11107
  // src/lib/payload.ts
11083
11108
  var import_zod = require("zod");
@@ -11907,8 +11932,8 @@ function findGitRoot2(startDir) {
11907
11932
 
11908
11933
  // src/commands/link.ts
11909
11934
  var import_node_crypto8 = require("crypto");
11910
- var fs31 = __toESM(require("fs"));
11911
- var path36 = __toESM(require("path"));
11935
+ var fs32 = __toESM(require("fs"));
11936
+ var path37 = __toESM(require("path"));
11912
11937
  var import_chokidar = __toESM(require("chokidar"));
11913
11938
  var import_picocolors2 = __toESM(require("picocolors"));
11914
11939
 
@@ -16518,6 +16543,18 @@ var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
16518
16543
  var INSTALL_TIMEOUT_MS = 15e4;
16519
16544
  var PREREQ_INSTALL_TIMEOUT_MS = 12e4;
16520
16545
  var REQUIRED_TOOLS = ["unzip", "git"];
16546
+ function defaultRunVersionProbe(binary, env) {
16547
+ const r = (0, import_node_child_process7.spawnSync)(binary, ["--version"], { stdio: "ignore", timeout: 15e3, env });
16548
+ return !r.error && r.status === 0;
16549
+ }
16550
+ function probeRuns(binary, env, deps) {
16551
+ const probe = deps.runVersionProbe ?? defaultRunVersionProbe;
16552
+ try {
16553
+ return probe(binary, env);
16554
+ } catch {
16555
+ return false;
16556
+ }
16557
+ }
16521
16558
  function manualInstallHint(runner, tools) {
16522
16559
  const pkgs = tools.join(" ");
16523
16560
  const argv = osPackageInstallArgv(detectPackageManager(runner), [...tools]);
@@ -16652,22 +16689,27 @@ async function ensureCoderabbitInstalled(os66, deps = {}) {
16652
16689
  log.info("coderabbit", "CodeRabbit CLI not found \u2014 installing via the official script");
16653
16690
  const run = deps.runInstallScript ?? defaultRunInstallScript;
16654
16691
  const { code, output } = await run(env);
16655
- if (code !== 0) {
16656
- const detail = summarizeInstallFailure(output);
16692
+ os66.augmentPath([`${os66.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
16693
+ const binary = os66.findInPath("coderabbit");
16694
+ const detail = summarizeInstallFailure(output);
16695
+ if (binary !== null) {
16696
+ if (code === 0) return { ok: true };
16697
+ if (probeRuns(binary, env, deps)) return { ok: true };
16657
16698
  return {
16658
16699
  ok: false,
16659
- error: detail ? `CodeRabbit CLI install failed: ${detail}` : "CodeRabbit CLI install failed \u2014 check this machine's network egress and try again."
16700
+ error: detail ? `CodeRabbit CLI install failed and the installed binary does not run: ${detail}` : "CodeRabbit CLI install failed \u2014 the installed `coderabbit` binary does not run. Re-run the install and check this machine's network egress."
16660
16701
  };
16661
16702
  }
16662
- os66.augmentPath([`${os66.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
16663
- if (os66.findInPath("coderabbit") === null) {
16664
- const detail = summarizeInstallFailure(output);
16703
+ if (code !== 0) {
16665
16704
  return {
16666
16705
  ok: false,
16667
- error: detail ? `CodeRabbit CLI install did not produce a binary: ${detail}` : "CodeRabbit CLI install reported success but no `coderabbit` binary was found on PATH."
16706
+ error: detail ? `CodeRabbit CLI install failed: ${detail}` : "CodeRabbit CLI install failed \u2014 check this machine's network egress and try again."
16668
16707
  };
16669
16708
  }
16670
- return { ok: true };
16709
+ return {
16710
+ ok: false,
16711
+ error: detail ? `CodeRabbit CLI install did not produce a binary: ${detail}` : "CodeRabbit CLI install reported success but no `coderabbit` binary was found on PATH."
16712
+ };
16671
16713
  }
16672
16714
 
16673
16715
  // src/agents/coderabbit/link.ts
@@ -17556,6 +17598,198 @@ function detectAiderSelector(_lines) {
17556
17598
  return null;
17557
17599
  }
17558
17600
 
17601
+ // src/agents/acp/agent-binary.ts
17602
+ var import_fs = __toESM(require("fs"));
17603
+ var import_os4 = __toESM(require("os"));
17604
+ var import_path4 = __toESM(require("path"));
17605
+ var import_child_process13 = require("child_process");
17606
+ function currentPlatformKey() {
17607
+ return `${process.platform}-${process.arch}`;
17608
+ }
17609
+ var SDK_PKG = "@anthropic-ai/claude-agent-sdk";
17610
+ function resolveSdkDirViaRequire(req = require) {
17611
+ try {
17612
+ return import_path4.default.dirname(req.resolve(`${SDK_PKG}/package.json`));
17613
+ } catch {
17614
+ }
17615
+ try {
17616
+ let dir = import_path4.default.dirname(req.resolve(SDK_PKG));
17617
+ for (let i = 0; i < 5; i++) {
17618
+ if (import_path4.default.basename(dir) === "claude-agent-sdk") return dir;
17619
+ const parent = import_path4.default.dirname(dir);
17620
+ if (parent === dir) break;
17621
+ dir = parent;
17622
+ }
17623
+ return null;
17624
+ } catch {
17625
+ return null;
17626
+ }
17627
+ }
17628
+ function defaultSdkDir() {
17629
+ return resolveSdkDirViaRequire();
17630
+ }
17631
+ function resolveClaudeNativeBinary(deps = {}) {
17632
+ const existsSync29 = deps.existsSync ?? import_fs.default.existsSync;
17633
+ const platformKey = deps.platformKey ?? currentPlatformKey();
17634
+ const sdkDir = deps.sdkDir !== void 0 ? deps.sdkDir : defaultSdkDir();
17635
+ if (!sdkDir) return null;
17636
+ const scopeDir = import_path4.default.dirname(sdkDir);
17637
+ const binName = platformKey.startsWith("win32-") ? "claude.exe" : "claude";
17638
+ const candidate = import_path4.default.join(scopeDir, `claude-agent-sdk-${platformKey}`, binName);
17639
+ return existsSync29(candidate) ? candidate : null;
17640
+ }
17641
+ var realSleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
17642
+ async function waitForClaudeNativeBinary(opts = {}) {
17643
+ const timeoutMs = opts.timeoutMs ?? 18e4;
17644
+ const pollMs = opts.pollMs ?? 500;
17645
+ const now = opts.now ?? Date.now;
17646
+ const sleep5 = opts.sleep ?? realSleep;
17647
+ const deps = {
17648
+ sdkDir: opts.sdkDir,
17649
+ platformKey: opts.platformKey,
17650
+ existsSync: opts.existsSync
17651
+ };
17652
+ const deadline = now() + timeoutMs;
17653
+ let found = resolveClaudeNativeBinary(deps);
17654
+ if (found) return found;
17655
+ while (now() < deadline) {
17656
+ await sleep5(pollMs);
17657
+ found = resolveClaudeNativeBinary(deps);
17658
+ if (found) return found;
17659
+ }
17660
+ return resolveClaudeNativeBinary(deps);
17661
+ }
17662
+ function isCommandOnPath(cmd, probe = defaultWhich) {
17663
+ return probe(cmd);
17664
+ }
17665
+ function defaultWhich(cmd) {
17666
+ const finder = process.platform === "win32" ? "where" : "which";
17667
+ try {
17668
+ const res = (0, import_child_process13.spawnSync)(finder, [cmd], { stdio: "ignore" });
17669
+ return res.status === 0;
17670
+ } catch {
17671
+ return false;
17672
+ }
17673
+ }
17674
+ async function waitForCommandOnPath(cmd, opts = {}) {
17675
+ const timeoutMs = opts.timeoutMs ?? 18e4;
17676
+ const pollMs = opts.pollMs ?? 500;
17677
+ const now = opts.now ?? Date.now;
17678
+ const sleep5 = opts.sleep ?? realSleep;
17679
+ const probe = opts.probe;
17680
+ const check = () => isCommandOnPath(cmd, probe);
17681
+ const deadline = now() + timeoutMs;
17682
+ if (check()) return true;
17683
+ while (now() < deadline) {
17684
+ await sleep5(pollMs);
17685
+ if (check()) return true;
17686
+ }
17687
+ return check();
17688
+ }
17689
+ function augmentUserLocalBinPaths() {
17690
+ const home = import_os4.default.homedir();
17691
+ const candidates = [
17692
+ // XDG-style per-user bin — npm's default global-prefix bin dir on most
17693
+ // Linux setups (`npm config set prefix ~/.local` or an nvm-less
17694
+ // per-user npm), and where curl-based agent installers commonly land.
17695
+ import_path4.default.join(home, ".local", "bin"),
17696
+ // Common explicit npm global-prefix conventions seen in the wild
17697
+ // (`npm config set prefix ~/.npm-global`, and Debian/Fedora's
17698
+ // `~/.local/share/npm` layout for `npm config set prefix
17699
+ // ~/.local/share/npm`).
17700
+ import_path4.default.join(home, ".npm-global", "bin"),
17701
+ import_path4.default.join(home, ".local", "share", "npm", "bin")
17702
+ ];
17703
+ const parts = (process.env.PATH ?? "").split(import_path4.default.delimiter).filter((p2) => p2.length > 0);
17704
+ const existing = new Set(parts);
17705
+ const additions = candidates.filter((dir) => !existing.has(dir));
17706
+ if (additions.length === 0) return;
17707
+ process.env.PATH = [...additions, ...parts].join(import_path4.default.delimiter);
17708
+ }
17709
+ function resolveCursorAgentBinary(deps = {}) {
17710
+ const existsSync29 = deps.existsSync ?? import_fs.default.existsSync;
17711
+ const platform3 = deps.platform ?? process.platform;
17712
+ const env = deps.env ?? process.env;
17713
+ if (platform3 === "win32") {
17714
+ const localAppData = env.LOCALAPPDATA;
17715
+ if (!localAppData) return null;
17716
+ const exe = import_path4.default.win32.join(localAppData, "cursor-agent", "cursor-agent.exe");
17717
+ return existsSync29(exe) ? exe : null;
17718
+ }
17719
+ const home = deps.homedir ?? import_os4.default.homedir();
17720
+ const unix = import_path4.default.posix.join(home, ".local", "bin", "cursor-agent");
17721
+ return existsSync29(unix) ? unix : null;
17722
+ }
17723
+ async function waitForCursorAgent(opts = {}) {
17724
+ const timeoutMs = opts.timeoutMs ?? 18e4;
17725
+ const pollMs = opts.pollMs ?? 500;
17726
+ const now = opts.now ?? Date.now;
17727
+ const sleep5 = opts.sleep ?? realSleep;
17728
+ const check = () => resolveCursorAgentBinary(opts) !== null || isCommandOnPath("cursor-agent", opts.probe);
17729
+ const deadline = now() + timeoutMs;
17730
+ if (check()) return true;
17731
+ while (now() < deadline) {
17732
+ await sleep5(pollMs);
17733
+ if (check()) return true;
17734
+ }
17735
+ return check();
17736
+ }
17737
+ var ADAPTER_MODULE_LOAD_ERROR_RE = /ERR_MODULE_NOT_FOUND|Cannot find module|ERR_REQUIRE_ESM|ERR_UNKNOWN_BUILTIN_MODULE|ERR_UNSUPPORTED_DIR_IMPORT|ERR_PACKAGE_PATH_NOT_EXPORTED|ERR_INVALID_PACKAGE_CONFIG|ERR_INVALID_PACKAGE_TARGET|ERR_INVALID_MODULE_SPECIFIER|node:internal\/modules\/|Directory import|SyntaxError|Unexpected (token|end|identifier)|missing \) after argument list/i;
17738
+ var PERMANENT_ADAPTER_STARTUP_RE = /IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist|not eligible for Gemini Code Assist|Error authenticating|ProjectIdRequiredError/i;
17739
+ function probeAdapterModuleGraph(command2, args2, opts = {}) {
17740
+ const livenessMs = opts.livenessMs ?? 400;
17741
+ const spawnFn = opts.spawnFn ?? import_child_process13.spawn;
17742
+ return new Promise((resolve9) => {
17743
+ let settled = false;
17744
+ let stderr = "";
17745
+ let timer;
17746
+ let child;
17747
+ const finish = (r) => {
17748
+ if (settled) return;
17749
+ settled = true;
17750
+ if (timer) clearTimeout(timer);
17751
+ try {
17752
+ child?.kill("SIGKILL");
17753
+ } catch {
17754
+ }
17755
+ resolve9(r);
17756
+ };
17757
+ try {
17758
+ child = spawnFn(command2, [...args2], { stdio: ["pipe", "ignore", "pipe"] });
17759
+ } catch {
17760
+ resolve9("ok");
17761
+ return;
17762
+ }
17763
+ child.stderr?.on("data", (d3) => {
17764
+ stderr += d3.toString();
17765
+ if (stderr.length > 8192) stderr = stderr.slice(-8192);
17766
+ });
17767
+ child.on("error", () => finish("ok"));
17768
+ child.on("exit", (code) => {
17769
+ if (code !== 0 && code !== null && !PERMANENT_ADAPTER_STARTUP_RE.test(stderr)) {
17770
+ finish("transient");
17771
+ } else {
17772
+ finish("ok");
17773
+ }
17774
+ });
17775
+ timer = setTimeout(() => finish("ok"), livenessMs);
17776
+ });
17777
+ }
17778
+ async function waitForAdapterModuleGraph(command2, args2, opts = {}) {
17779
+ const timeoutMs = opts.timeoutMs ?? 18e4;
17780
+ const pollMs = opts.pollMs ?? 500;
17781
+ const now = opts.now ?? Date.now;
17782
+ const sleep5 = opts.sleep ?? realSleep;
17783
+ const probeOpts = { livenessMs: opts.livenessMs, spawnFn: opts.spawnFn };
17784
+ const deadline = now() + timeoutMs;
17785
+ if (await probeAdapterModuleGraph(command2, args2, probeOpts) === "ok") return true;
17786
+ while (now() < deadline) {
17787
+ await sleep5(pollMs);
17788
+ if (await probeAdapterModuleGraph(command2, args2, probeOpts) === "ok") return true;
17789
+ }
17790
+ return false;
17791
+ }
17792
+
17559
17793
  // src/agents/aider/runtime.ts
17560
17794
  var AIDER_CONTEXT_WINDOW = 2e5;
17561
17795
  var AIDER_MODELS = [
@@ -17574,6 +17808,7 @@ var AiderRuntimeStrategy = class {
17574
17808
  this.os = os66;
17575
17809
  }
17576
17810
  async prepareLaunch() {
17811
+ augmentUserLocalBinPaths();
17577
17812
  const binary = this.os.findInPath("aider");
17578
17813
  if (!binary) {
17579
17814
  throw new Error(
@@ -17645,19 +17880,19 @@ var import_node_crypto6 = require("crypto");
17645
17880
  var import_node_child_process13 = require("child_process");
17646
17881
 
17647
17882
  // src/agents/gemini/local-token.ts
17648
- var fs28 = __toESM(require("fs"));
17649
- var os25 = __toESM(require("os"));
17650
- var path33 = __toESM(require("path"));
17883
+ var fs29 = __toESM(require("fs"));
17884
+ var os26 = __toESM(require("os"));
17885
+ var path34 = __toESM(require("path"));
17651
17886
  function geminiCredentialsPath() {
17652
- return path33.join(os25.homedir(), ".gemini", "oauth_creds.json");
17887
+ return path34.join(os26.homedir(), ".gemini", "oauth_creds.json");
17653
17888
  }
17654
17889
  function geminiCredentialsPaths() {
17655
17890
  return [geminiCredentialsPath()];
17656
17891
  }
17657
17892
  async function extractLocalGeminiToken() {
17658
17893
  const file = geminiCredentialsPath();
17659
- if (!fs28.existsSync(file)) return null;
17660
- const credential = fs28.readFileSync(file, "utf8").trim();
17894
+ if (!fs29.existsSync(file)) return null;
17895
+ const credential = fs29.readFileSync(file, "utf8").trim();
17661
17896
  if (credential.length === 0) return null;
17662
17897
  return { method: "oauth", credential, source: "flat-file" };
17663
17898
  }
@@ -17713,20 +17948,20 @@ function geminiLoginLauncher() {
17713
17948
  }
17714
17949
 
17715
17950
  // src/agents/gemini/history.ts
17716
- var fs29 = __toESM(require("fs"));
17717
- var os26 = __toESM(require("os"));
17718
- var path34 = __toESM(require("path"));
17719
- var GEMINI_ROOT = path34.join(os26.homedir(), ".gemini");
17951
+ var fs30 = __toESM(require("fs"));
17952
+ var os27 = __toESM(require("os"));
17953
+ var path35 = __toESM(require("path"));
17954
+ var GEMINI_ROOT = path35.join(os27.homedir(), ".gemini");
17720
17955
  function projectNameForCwd(cwd, root) {
17721
17956
  try {
17722
17957
  const parsed = JSON.parse(
17723
- fs29.readFileSync(path34.join(root, "projects.json"), "utf8")
17958
+ fs30.readFileSync(path35.join(root, "projects.json"), "utf8")
17724
17959
  );
17725
17960
  const map = parsed.projects;
17726
17961
  if (map && typeof map === "object") {
17727
17962
  if (typeof map[cwd] === "string") return map[cwd];
17728
17963
  try {
17729
- const rp = fs29.realpathSync(cwd);
17964
+ const rp = fs30.realpathSync(cwd);
17730
17965
  if (typeof map[rp] === "string") return map[rp];
17731
17966
  } catch {
17732
17967
  }
@@ -17735,7 +17970,7 @@ function projectNameForCwd(cwd, root) {
17735
17970
  } catch (err) {
17736
17971
  log.debug("gemini", `projects.json unreadable at ${root} \u2014 using basename fallback`, err);
17737
17972
  }
17738
- return path34.basename(cwd) || null;
17973
+ return path35.basename(cwd) || null;
17739
17974
  }
17740
17975
  function resolveHistoryDir5(cwd, root = GEMINI_ROOT) {
17741
17976
  const name = projectNameForCwd(cwd, root);
@@ -17743,8 +17978,8 @@ function resolveHistoryDir5(cwd, root = GEMINI_ROOT) {
17743
17978
  log.warn("gemini", `resolveHistoryDir \u2014 could not derive a project name for cwd=${cwd}`);
17744
17979
  return null;
17745
17980
  }
17746
- const dir = path34.join(root, "tmp", name, "chats");
17747
- if (!fs29.existsSync(dir)) {
17981
+ const dir = path35.join(root, "tmp", name, "chats");
17982
+ if (!fs30.existsSync(dir)) {
17748
17983
  log.debug("gemini", `resolveHistoryDir \u2014 chats dir not present yet: ${dir} (project=${name})`);
17749
17984
  return null;
17750
17985
  }
@@ -17756,7 +17991,7 @@ function resolveHistoryFile4(cwd, sessionId, root = GEMINI_ROOT) {
17756
17991
  if (!dir) return null;
17757
17992
  let files;
17758
17993
  try {
17759
- files = fs29.readdirSync(dir).filter((f) => f.startsWith("session-") && f.endsWith(".jsonl"));
17994
+ files = fs30.readdirSync(dir).filter((f) => f.startsWith("session-") && f.endsWith(".jsonl"));
17760
17995
  } catch (err) {
17761
17996
  log.warn("gemini", `resolveHistoryFile \u2014 cannot read chats dir ${dir} (id8=${id8})`, err);
17762
17997
  return null;
@@ -17768,12 +18003,12 @@ function resolveHistoryFile4(cwd, sessionId, root = GEMINI_ROOT) {
17768
18003
  const byName = files.find((f) => id8.length > 0 && f.includes(id8));
17769
18004
  if (byName) {
17770
18005
  log.info("gemini", `resolveHistoryFile \u2014 matched by filename id8=${id8}: ${byName}`);
17771
- return path34.join(dir, byName);
18006
+ return path35.join(dir, byName);
17772
18007
  }
17773
18008
  for (const f of files) {
17774
- const full = path34.join(dir, f);
18009
+ const full = path35.join(dir, f);
17775
18010
  try {
17776
- const firstLine = fs29.readFileSync(full, "utf8").split("\n", 1)[0] ?? "";
18011
+ const firstLine = fs30.readFileSync(full, "utf8").split("\n", 1)[0] ?? "";
17777
18012
  const hdr = JSON.parse(firstLine);
17778
18013
  if (hdr.sessionId === sessionId) {
17779
18014
  log.info("gemini", `resolveHistoryFile \u2014 matched by header sessionId: ${f}`);
@@ -17786,7 +18021,7 @@ function resolveHistoryFile4(cwd, sessionId, root = GEMINI_ROOT) {
17786
18021
  let newest = null;
17787
18022
  for (const f of files) {
17788
18023
  try {
17789
- const mtime = fs29.statSync(path34.join(dir, f)).mtimeMs;
18024
+ const mtime = fs30.statSync(path35.join(dir, f)).mtimeMs;
17790
18025
  if (!newest || mtime > newest.mtime) newest = { file: f, mtime };
17791
18026
  } catch {
17792
18027
  }
@@ -17795,7 +18030,7 @@ function resolveHistoryFile4(cwd, sessionId, root = GEMINI_ROOT) {
17795
18030
  "gemini",
17796
18031
  `resolveHistoryFile \u2014 no id8=${id8} match among ${files.length} file(s) in ${dir}; falling back to newest=${newest?.file ?? "none"}`
17797
18032
  );
17798
- return newest ? path34.join(dir, newest.file) : null;
18033
+ return newest ? path35.join(dir, newest.file) : null;
17799
18034
  }
17800
18035
  function extractGeminiText(content) {
17801
18036
  const fromParts = (arr) => Array.isArray(arr) ? arr.map((p2) => {
@@ -17823,7 +18058,7 @@ function isSyntheticGeminiTurn(text) {
17823
18058
  function parseHistoryFile5(filePath) {
17824
18059
  let raw;
17825
18060
  try {
17826
- raw = fs29.readFileSync(filePath, "utf8");
18061
+ raw = fs30.readFileSync(filePath, "utf8");
17827
18062
  } catch (err) {
17828
18063
  log.warn("gemini", `parseHistoryFile \u2014 cannot read ${filePath}`, err);
17829
18064
  return [];
@@ -17864,7 +18099,7 @@ function parseHistoryFile5(filePath) {
17864
18099
  }
17865
18100
  log.trace(
17866
18101
  "gemini",
17867
- `parseHistoryFile \u2014 ${out2.length} turn(s) from ${path34.basename(filePath)}` + (malformed > 0 ? ` (${malformed} malformed line(s) skipped)` : "")
18102
+ `parseHistoryFile \u2014 ${out2.length} turn(s) from ${path35.basename(filePath)}` + (malformed > 0 ? ` (${malformed} malformed line(s) skipped)` : "")
17868
18103
  );
17869
18104
  return out2;
17870
18105
  }
@@ -18008,42 +18243,42 @@ var import_node_os3 = require("os");
18008
18243
  var import_node_path4 = require("path");
18009
18244
 
18010
18245
  // src/agents/kimi/history.ts
18011
- var fs30 = __toESM(require("fs"));
18012
- var os27 = __toESM(require("os"));
18013
- var path35 = __toESM(require("path"));
18246
+ var fs31 = __toESM(require("fs"));
18247
+ var os28 = __toESM(require("os"));
18248
+ var path36 = __toESM(require("path"));
18014
18249
  var import_node_crypto7 = require("crypto");
18015
18250
  function kimiHome() {
18016
- return process.env.KIMI_CODE_HOME || path35.join(os27.homedir(), ".kimi-code");
18251
+ return process.env.KIMI_CODE_HOME || path36.join(os28.homedir(), ".kimi-code");
18017
18252
  }
18018
18253
  function workDirKey(cwd) {
18019
18254
  const hash = (0, import_node_crypto7.createHash)("sha256").update(cwd).digest("hex").slice(0, 12);
18020
- return `wd_${path35.basename(cwd)}_${hash}`;
18255
+ return `wd_${path36.basename(cwd)}_${hash}`;
18021
18256
  }
18022
18257
  function resolveHistoryDir6(cwd) {
18023
- const dir = path35.join(kimiHome(), "sessions", workDirKey(cwd));
18024
- if (!fs30.existsSync(dir)) {
18258
+ const dir = path36.join(kimiHome(), "sessions", workDirKey(cwd));
18259
+ if (!fs31.existsSync(dir)) {
18025
18260
  log.debug("kimi", `resolveHistoryDir \u2014 session bucket not present yet: ${dir}`);
18026
18261
  return null;
18027
18262
  }
18028
18263
  return dir;
18029
18264
  }
18030
- var WIRE_REL = path35.join("agents", "main", "wire.jsonl");
18265
+ var WIRE_REL = path36.join("agents", "main", "wire.jsonl");
18031
18266
  function resolveHistoryFile5(cwd, sessionId) {
18032
18267
  const fromIndex = sessionDirFromIndex(sessionId);
18033
18268
  if (fromIndex) {
18034
- const p2 = path35.join(fromIndex, WIRE_REL);
18035
- if (fs30.existsSync(p2)) return p2;
18269
+ const p2 = path36.join(fromIndex, WIRE_REL);
18270
+ if (fs31.existsSync(p2)) return p2;
18036
18271
  }
18037
- const bucket2 = path35.join(kimiHome(), "sessions", workDirKey(cwd));
18038
- const computed = path35.join(bucket2, sessionId, WIRE_REL);
18039
- if (fs30.existsSync(computed)) return computed;
18272
+ const bucket2 = path36.join(kimiHome(), "sessions", workDirKey(cwd));
18273
+ const computed = path36.join(bucket2, sessionId, WIRE_REL);
18274
+ if (fs31.existsSync(computed)) return computed;
18040
18275
  const scanned = scanBucketsForSession(sessionId);
18041
18276
  if (scanned) return scanned;
18042
18277
  return null;
18043
18278
  }
18044
18279
  function sessionDirFromIndex(sessionId) {
18045
18280
  try {
18046
- const raw = fs30.readFileSync(path35.join(kimiHome(), "session_index.jsonl"), "utf8");
18281
+ const raw = fs31.readFileSync(path36.join(kimiHome(), "session_index.jsonl"), "utf8");
18047
18282
  for (const line of raw.split("\n")) {
18048
18283
  if (!line.includes(sessionId)) continue;
18049
18284
  try {
@@ -18059,22 +18294,22 @@ function sessionDirFromIndex(sessionId) {
18059
18294
  return null;
18060
18295
  }
18061
18296
  function scanBucketsForSession(sessionId) {
18062
- const sessionsRoot = path35.join(kimiHome(), "sessions");
18297
+ const sessionsRoot = path36.join(kimiHome(), "sessions");
18063
18298
  let buckets;
18064
18299
  try {
18065
- buckets = fs30.readdirSync(sessionsRoot, { withFileTypes: true });
18300
+ buckets = fs31.readdirSync(sessionsRoot, { withFileTypes: true });
18066
18301
  } catch {
18067
18302
  return null;
18068
18303
  }
18069
18304
  for (const b of buckets) {
18070
18305
  if (!b.isDirectory()) continue;
18071
- const candidate = path35.join(sessionsRoot, b.name, sessionId, WIRE_REL);
18072
- if (fs30.existsSync(candidate)) return candidate;
18306
+ const candidate = path36.join(sessionsRoot, b.name, sessionId, WIRE_REL);
18307
+ if (fs31.existsSync(candidate)) return candidate;
18073
18308
  }
18074
18309
  return null;
18075
18310
  }
18076
18311
  async function discoverSessionId2(cwd, opts) {
18077
- const bucket2 = path35.join(kimiHome(), "sessions", workDirKey(cwd));
18312
+ const bucket2 = path36.join(kimiHome(), "sessions", workDirKey(cwd));
18078
18313
  const floor = opts.sinceMs - 2e3;
18079
18314
  const deadline = Date.now() + (opts.timeoutMs ?? 15e3);
18080
18315
  for (; ; ) {
@@ -18087,7 +18322,7 @@ async function discoverSessionId2(cwd, opts) {
18087
18322
  function newestSessionSince(bucket2, floorMs) {
18088
18323
  let entries;
18089
18324
  try {
18090
- entries = fs30.readdirSync(bucket2, { withFileTypes: true });
18325
+ entries = fs31.readdirSync(bucket2, { withFileTypes: true });
18091
18326
  } catch {
18092
18327
  return null;
18093
18328
  }
@@ -18097,7 +18332,7 @@ function newestSessionSince(bucket2, floorMs) {
18097
18332
  if (!e.isDirectory() || !e.name.startsWith("session_")) continue;
18098
18333
  let mtime;
18099
18334
  try {
18100
- mtime = fs30.statSync(path35.join(bucket2, e.name)).mtimeMs;
18335
+ mtime = fs31.statSync(path36.join(bucket2, e.name)).mtimeMs;
18101
18336
  } catch {
18102
18337
  continue;
18103
18338
  }
@@ -18118,7 +18353,7 @@ function joinTextParts(parts) {
18118
18353
  function parseHistoryFile6(filePath) {
18119
18354
  let raw;
18120
18355
  try {
18121
- raw = fs30.readFileSync(filePath, "utf8");
18356
+ raw = fs31.readFileSync(filePath, "utf8");
18122
18357
  } catch {
18123
18358
  return [];
18124
18359
  }
@@ -18496,7 +18731,7 @@ function parseLinkArgs(args2) {
18496
18731
  if (apiKeyFileArg) {
18497
18732
  const filePath = apiKeyFileArg.slice("--api-key-file=".length);
18498
18733
  try {
18499
- apiKey = fs31.readFileSync(path36.resolve(filePath), "utf8").trim();
18734
+ apiKey = fs32.readFileSync(path37.resolve(filePath), "utf8").trim();
18500
18735
  } catch (err) {
18501
18736
  throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
18502
18737
  }
@@ -18623,7 +18858,7 @@ async function link(args2 = []) {
18623
18858
  return;
18624
18859
  }
18625
18860
  if (parsed.tokenFile) {
18626
- const credential = fs31.readFileSync(path36.resolve(parsed.tokenFile), "utf8").trim();
18861
+ const credential = fs32.readFileSync(path37.resolve(parsed.tokenFile), "utf8").trim();
18627
18862
  if (!credential) {
18628
18863
  showError(`--token-file ${parsed.tokenFile} is empty.`);
18629
18864
  process.exit(1);
@@ -18842,13 +19077,13 @@ async function linkDryRunPreflight(ctx) {
18842
19077
  // src/agents/coderabbit/configure.ts
18843
19078
  var import_node_child_process17 = require("child_process");
18844
19079
  var import_node_fs6 = require("fs");
18845
- var path38 = __toESM(require("path"));
19080
+ var path39 = __toESM(require("path"));
18846
19081
 
18847
19082
  // src/agents/coderabbit/oauth.ts
18848
19083
  var import_node_child_process16 = require("child_process");
18849
- var fs32 = __toESM(require("fs"));
18850
- var os28 = __toESM(require("os"));
18851
- var path37 = __toESM(require("path"));
19084
+ var fs33 = __toESM(require("fs"));
19085
+ var os29 = __toESM(require("os"));
19086
+ var path38 = __toESM(require("path"));
18852
19087
  function parseCoderabbitAuthEvent(line) {
18853
19088
  const l = line.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, "").trim();
18854
19089
  if (!l || l[0] !== "{") return null;
@@ -18897,8 +19132,8 @@ function resolvePython() {
18897
19132
  function spawnCoderabbitLoginProc(cmd, args2) {
18898
19133
  const python = resolvePython();
18899
19134
  if (python) {
18900
- const helper = path37.join(os28.tmpdir(), "codeam-cr-pty.py");
18901
- fs32.writeFileSync(helper, PYTHON_PTY_HELPER, { mode: 420 });
19135
+ const helper = path38.join(os29.tmpdir(), "codeam-cr-pty.py");
19136
+ fs33.writeFileSync(helper, PYTHON_PTY_HELPER, { mode: 420 });
18902
19137
  return (0, import_node_child_process16.spawn)(python, [helper, cmd, ...args2], {
18903
19138
  stdio: ["pipe", "pipe", "pipe"],
18904
19139
  env: { ...process.env, TERM: "xterm-256color", COLUMNS: "220", LINES: "50" },
@@ -19009,7 +19244,7 @@ function runCoderabbitOAuthLogin(deps) {
19009
19244
  });
19010
19245
  }
19011
19246
  function coderabbitDir(home) {
19012
- return path37.join(home ?? os28.homedir(), ".coderabbit");
19247
+ return path38.join(home ?? os29.homedir(), ".coderabbit");
19013
19248
  }
19014
19249
  var NON_CREDENTIAL = /* @__PURE__ */ new Set(["doctor.json", "machine-id"]);
19015
19250
  function snapshotCredentialDir(home) {
@@ -19017,14 +19252,14 @@ function snapshotCredentialDir(home) {
19017
19252
  const snap = {};
19018
19253
  let entries;
19019
19254
  try {
19020
- entries = fs32.readdirSync(dir, { withFileTypes: true });
19255
+ entries = fs33.readdirSync(dir, { withFileTypes: true });
19021
19256
  } catch {
19022
19257
  return snap;
19023
19258
  }
19024
19259
  for (const e of entries) {
19025
19260
  if (!e.isFile() || NON_CREDENTIAL.has(e.name)) continue;
19026
19261
  try {
19027
- const st3 = fs32.statSync(path37.join(dir, e.name));
19262
+ const st3 = fs33.statSync(path38.join(dir, e.name));
19028
19263
  snap[e.name] = `${st3.mtimeMs}:${st3.size}`;
19029
19264
  } catch {
19030
19265
  }
@@ -19042,7 +19277,7 @@ function diffCapturedCredential(before, home) {
19042
19277
  }
19043
19278
  if (!best) return null;
19044
19279
  try {
19045
- return { file: best.file, contents: fs32.readFileSync(path37.join(dir, best.file), "utf8") };
19280
+ return { file: best.file, contents: fs33.readFileSync(path38.join(dir, best.file), "utf8") };
19046
19281
  } catch {
19047
19282
  return null;
19048
19283
  }
@@ -19119,7 +19354,7 @@ function collectChangedFiles(cwd) {
19119
19354
  return [...byPath.values()];
19120
19355
  }
19121
19356
  function restoreCoderabbitOauthBlob(os66, value) {
19122
- const dir = path38.join(os66.homeDir(), ".coderabbit");
19357
+ const dir = path39.join(os66.homeDir(), ".coderabbit");
19123
19358
  (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
19124
19359
  let file = "auth.json";
19125
19360
  let contents = value.trim();
@@ -19127,13 +19362,13 @@ function restoreCoderabbitOauthBlob(os66, value) {
19127
19362
  try {
19128
19363
  const parsed = JSON.parse(contents);
19129
19364
  if (typeof parsed.file === "string" && typeof parsed.contents === "string") {
19130
- file = path38.basename(parsed.file);
19365
+ file = path39.basename(parsed.file);
19131
19366
  contents = parsed.contents;
19132
19367
  }
19133
19368
  } catch {
19134
19369
  }
19135
19370
  }
19136
- (0, import_node_fs6.writeFileSync)(path38.join(dir, file), contents, { mode: 384 });
19371
+ (0, import_node_fs6.writeFileSync)(path39.join(dir, file), contents, { mode: 384 });
19137
19372
  }
19138
19373
  function installFailureMessage(result) {
19139
19374
  return result.error ?? "CodeRabbit CLI could not be installed";
@@ -19149,10 +19384,10 @@ async function configureCoderabbit(input, deps = {}) {
19149
19384
  const home = os66.homeDir();
19150
19385
  os66.augmentPath(
19151
19386
  os66.id === "win32" ? [
19152
- path38.join(home, ".local", "bin"),
19153
- path38.join(process.env.APPDATA ?? path38.join(home, "AppData", "Roaming"), "npm"),
19154
- path38.join(home, "scoop", "shims")
19155
- ] : [path38.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
19387
+ path39.join(home, ".local", "bin"),
19388
+ path39.join(process.env.APPDATA ?? path39.join(home, "AppData", "Roaming"), "npm"),
19389
+ path39.join(home, "scoop", "shims")
19390
+ ] : [path39.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
19156
19391
  );
19157
19392
  const installed2 = os66.findInPath("coderabbit") !== null;
19158
19393
  const base = () => ({
@@ -19461,9 +19696,9 @@ function defaultRunGh(args2) {
19461
19696
 
19462
19697
  // src/commands/host-agent.ts
19463
19698
  var import_node_child_process25 = require("child_process");
19464
- var os41 = __toESM(require("os"));
19465
- var fs45 = __toESM(require("fs"));
19466
- var path49 = __toESM(require("path"));
19699
+ var os42 = __toESM(require("os"));
19700
+ var fs46 = __toESM(require("fs"));
19701
+ var path50 = __toESM(require("path"));
19467
19702
 
19468
19703
  // src/integrations/manifest.ts
19469
19704
  var import_node_fs8 = __toESM(require("fs"));
@@ -19586,14 +19821,14 @@ function persistOrClearSkillsFromPayload(skills) {
19586
19821
  }
19587
19822
 
19588
19823
  // src/commands/host/agent-install.ts
19589
- var import_child_process13 = require("child_process");
19590
- var os32 = __toESM(require("os"));
19824
+ var import_child_process14 = require("child_process");
19825
+ var os33 = __toESM(require("os"));
19591
19826
  function runAgentInstallScript(script, opts = {}) {
19592
19827
  const timeoutMs = opts.timeoutMs ?? 18e4;
19593
19828
  const scope = opts.logScope ?? "agent-install";
19594
19829
  return new Promise((resolve9) => {
19595
- const home = process.env.HOME || os32.homedir();
19596
- const child = (0, import_child_process13.spawn)("sh", ["-c", script], {
19830
+ const home = process.env.HOME || os33.homedir();
19831
+ const child = (0, import_child_process14.spawn)("sh", ["-c", script], {
19597
19832
  env: { ...process.env, HOME: home },
19598
19833
  stdio: ["ignore", "pipe", "pipe"]
19599
19834
  });
@@ -19658,14 +19893,14 @@ function describeReason(reason) {
19658
19893
  }
19659
19894
 
19660
19895
  // src/commands/host/host-client.ts
19661
- var fs36 = __toESM(require("fs"));
19662
- var os33 = __toESM(require("os"));
19663
- var path41 = __toESM(require("path"));
19896
+ var fs37 = __toESM(require("fs"));
19897
+ var os34 = __toESM(require("os"));
19898
+ var path42 = __toESM(require("path"));
19664
19899
  var import_node_crypto9 = require("crypto");
19665
19900
  function sampleCpuTimes() {
19666
19901
  let idle = 0;
19667
19902
  let total = 0;
19668
- for (const cpu of os33.cpus()) {
19903
+ for (const cpu of os34.cpus()) {
19669
19904
  const t2 = cpu.times;
19670
19905
  idle += t2.idle;
19671
19906
  total += t2.user + t2.nice + t2.sys + t2.idle + t2.irq;
@@ -19684,8 +19919,8 @@ var MetricsCollector = class {
19684
19919
  const prev = this.prevCpu;
19685
19920
  this.prevCpu = current2;
19686
19921
  if (!prev) {
19687
- const cores = os33.cpus().length || 1;
19688
- const proxy = os33.loadavg()[0] / cores * 100;
19922
+ const cores = os34.cpus().length || 1;
19923
+ const proxy = os34.loadavg()[0] / cores * 100;
19689
19924
  return Math.min(100, Math.max(0, Math.round(proxy)));
19690
19925
  }
19691
19926
  const idleDelta = current2.idle - prev.idle;
@@ -19698,8 +19933,8 @@ var MetricsCollector = class {
19698
19933
  collect() {
19699
19934
  return {
19700
19935
  cpuPct: this.cpuPct(),
19701
- ramUsedMb: Math.round((os33.totalmem() - os33.freemem()) / 1048576),
19702
- ramTotalMb: Math.round(os33.totalmem() / 1048576),
19936
+ ramUsedMb: Math.round((os34.totalmem() - os34.freemem()) / 1048576),
19937
+ ramTotalMb: Math.round(os34.totalmem() / 1048576),
19703
19938
  latencyMs: this.lastLatencyMs
19704
19939
  };
19705
19940
  }
@@ -19708,19 +19943,19 @@ function apiBase() {
19708
19943
  return process.env.CODEAM_API_URL ?? resolveApiBaseUrl();
19709
19944
  }
19710
19945
  function hostIdentityPath() {
19711
- return path41.join(os33.homedir(), ".codeam", "host-agent.json");
19946
+ return path42.join(os34.homedir(), ".codeam", "host-agent.json");
19712
19947
  }
19713
19948
  function collectOsInfo() {
19714
19949
  return {
19715
- distro: os33.platform(),
19716
- arch: os33.arch(),
19717
- kernel: os33.release(),
19950
+ distro: os34.platform(),
19951
+ arch: os34.arch(),
19952
+ kernel: os34.release(),
19718
19953
  nodeVersion: process.versions.node
19719
19954
  };
19720
19955
  }
19721
19956
  function loadHostIdentity() {
19722
19957
  try {
19723
- const raw = fs36.readFileSync(hostIdentityPath(), "utf8");
19958
+ const raw = fs37.readFileSync(hostIdentityPath(), "utf8");
19724
19959
  const parsed = JSON.parse(raw);
19725
19960
  if (typeof parsed === "object" && parsed !== null && typeof parsed.hostId === "string" && typeof parsed.hostToken === "string" && typeof parsed.controlPluginId === "string") {
19726
19961
  const p2 = parsed;
@@ -19739,8 +19974,8 @@ function loadHostIdentity() {
19739
19974
  }
19740
19975
  function saveHostIdentity(identity) {
19741
19976
  const file = hostIdentityPath();
19742
- fs36.mkdirSync(path41.dirname(file), { recursive: true, mode: 448 });
19743
- fs36.writeFileSync(file, JSON.stringify(identity, null, 2), {
19977
+ fs37.mkdirSync(path42.dirname(file), { recursive: true, mode: 448 });
19978
+ fs37.writeFileSync(file, JSON.stringify(identity, null, 2), {
19744
19979
  encoding: "utf8",
19745
19980
  mode: 384
19746
19981
  });
@@ -19785,7 +20020,7 @@ function isTerminalEnrollError(err) {
19785
20020
  }
19786
20021
  function deleteHostIdentity() {
19787
20022
  try {
19788
- fs36.rmSync(hostIdentityPath(), { force: true });
20023
+ fs37.rmSync(hostIdentityPath(), { force: true });
19789
20024
  } catch {
19790
20025
  }
19791
20026
  }
@@ -19809,7 +20044,7 @@ async function postJson(pathname, body) {
19809
20044
  function resolveHostLabel(label) {
19810
20045
  const explicit = label?.trim();
19811
20046
  const envLabel = process.env.CODEAM_HOST_LABEL?.trim();
19812
- const resolved = explicit || envLabel || os33.hostname();
20047
+ const resolved = explicit || envLabel || os34.hostname();
19813
20048
  return resolved.slice(0, 80);
19814
20049
  }
19815
20050
  async function redeemEnrollToken(token, label) {
@@ -19920,17 +20155,17 @@ async function reportDeployProgress(auth, deployId, step, message, sessionId) {
19920
20155
  }
19921
20156
 
19922
20157
  // src/commands/host/workspace.ts
19923
- var fs37 = __toESM(require("fs"));
19924
- var os34 = __toESM(require("os"));
19925
- var path42 = __toESM(require("path"));
20158
+ var fs38 = __toESM(require("fs"));
20159
+ var os35 = __toESM(require("os"));
20160
+ var path43 = __toESM(require("path"));
19926
20161
  var import_node_child_process20 = require("child_process");
19927
20162
  var import_node_util4 = require("util");
19928
20163
  var execFileP4 = (0, import_node_util4.promisify)(import_node_child_process20.execFile);
19929
20164
  function isAbsolutePathTarget(target) {
19930
- return path42.isAbsolute(target);
20165
+ return path43.isAbsolute(target);
19931
20166
  }
19932
20167
  function selfHostedWorkspaceRoot() {
19933
- return path42.join(os34.homedir(), ".codeam", "self-hosted");
20168
+ return path43.join(os35.homedir(), ".codeam", "self-hosted");
19934
20169
  }
19935
20170
  function nonInteractiveGitEnv() {
19936
20171
  return {
@@ -20000,15 +20235,15 @@ async function configureGitCredentials(dest, repoRef, cloneToken, provider = "gi
20000
20235
  const isGitlab = provider === "gitlab";
20001
20236
  if (isGitlab ? !gitlabProjectPath(repoRef.trim()) : !githubOwnerRepo(repoRef.trim())) return;
20002
20237
  if (!cloneToken) return;
20003
- const credFile = path42.join(dest, ".git", "codeam-credentials");
20238
+ const credFile = path43.join(dest, ".git", "codeam-credentials");
20004
20239
  const credLine = isGitlab ? `https://oauth2:${cloneToken}@gitlab.com
20005
20240
  ` : `https://x-access-token:${cloneToken}@github.com
20006
20241
  `;
20007
- fs37.writeFileSync(credFile, credLine, { mode: 384 });
20242
+ fs38.writeFileSync(credFile, credLine, { mode: 384 });
20008
20243
  restrictToOwner(credFile);
20009
20244
  const env = nonInteractiveGitEnv();
20010
20245
  const git2 = (args2) => execFileP4("git", ["-C", dest, ...args2], { timeout: 3e4, env });
20011
- const credFilePosix = credFile.split(path42.sep).join("/");
20246
+ const credFilePosix = credFile.split(path43.sep).join("/");
20012
20247
  await git2(["config", "--local", "--replace-all", "credential.helper", ""]).catch(() => {
20013
20248
  });
20014
20249
  await git2([
@@ -20041,17 +20276,17 @@ function maskToken(text, cloneToken) {
20041
20276
  }
20042
20277
  async function prepareWorkspace(repoOrPath, deployId, cloneToken, provider = "github", branch) {
20043
20278
  if (isAbsolutePathTarget(repoOrPath)) {
20044
- if (!fs37.existsSync(repoOrPath)) {
20279
+ if (!fs38.existsSync(repoOrPath)) {
20045
20280
  throw new Error(`deploy target path does not exist: ${repoOrPath}`);
20046
20281
  }
20047
20282
  return repoOrPath;
20048
20283
  }
20049
- const dest = path42.join(selfHostedWorkspaceRoot(), deployId);
20050
- if (fs37.existsSync(path42.join(dest, ".git"))) {
20284
+ const dest = path43.join(selfHostedWorkspaceRoot(), deployId);
20285
+ if (fs38.existsSync(path43.join(dest, ".git"))) {
20051
20286
  if (cloneToken) await configureGitCredentials(dest, repoOrPath, cloneToken, provider);
20052
20287
  return dest;
20053
20288
  }
20054
- fs37.mkdirSync(selfHostedWorkspaceRoot(), { recursive: true, mode: 448 });
20289
+ fs38.mkdirSync(selfHostedWorkspaceRoot(), { recursive: true, mode: 448 });
20055
20290
  const cloneUrl = repoCloneUrl(repoOrPath, cloneToken, provider);
20056
20291
  const cloneArgs = branch ? ["clone", "--depth", "1", "--branch", branch, cloneUrl, dest] : ["clone", "--depth", "1", cloneUrl, dest];
20057
20292
  try {
@@ -20069,9 +20304,9 @@ async function prepareWorkspace(repoOrPath, deployId, cloneToken, provider = "gi
20069
20304
  }
20070
20305
 
20071
20306
  // src/commands/host/agent-provisioning.ts
20072
- var fs38 = __toESM(require("fs"));
20073
- var os35 = __toESM(require("os"));
20074
- var path43 = __toESM(require("path"));
20307
+ var fs39 = __toESM(require("fs"));
20308
+ var os36 = __toESM(require("os"));
20309
+ var path44 = __toESM(require("path"));
20075
20310
  var PUBLIC_TO_INTERNAL_AGENT = {
20076
20311
  claude_code: "claude",
20077
20312
  claude: "claude",
@@ -20088,22 +20323,22 @@ function toInternalAgentId(publicAgentId) {
20088
20323
  return PUBLIC_TO_INTERNAL_AGENT[publicAgentId] ?? null;
20089
20324
  }
20090
20325
  function ensureDir(dir) {
20091
- fs38.mkdirSync(dir, { recursive: true, mode: 448 });
20326
+ fs39.mkdirSync(dir, { recursive: true, mode: 448 });
20092
20327
  }
20093
20328
  function writeFile0600(filePath, contents) {
20094
- ensureDir(path43.dirname(filePath));
20095
- fs38.writeFileSync(filePath, contents, { encoding: "utf8", mode: 384 });
20329
+ ensureDir(path44.dirname(filePath));
20330
+ fs39.writeFileSync(filePath, contents, { encoding: "utf8", mode: 384 });
20096
20331
  restrictToOwner(filePath);
20097
20332
  }
20098
20333
  function rmIfExists(filePath) {
20099
20334
  try {
20100
- fs38.rmSync(filePath, { force: true });
20335
+ fs39.rmSync(filePath, { force: true });
20101
20336
  } catch {
20102
20337
  }
20103
20338
  }
20104
20339
  var claudeProvisioner = {
20105
20340
  write(auth, home) {
20106
- const credentialsJson = path43.join(home, ".claude", ".credentials.json");
20341
+ const credentialsJson = path44.join(home, ".claude", ".credentials.json");
20107
20342
  if (auth.kind === "api_key") {
20108
20343
  rmIfExists(credentialsJson);
20109
20344
  return { ANTHROPIC_API_KEY: auth.value };
@@ -20115,8 +20350,8 @@ var claudeProvisioner = {
20115
20350
  } else {
20116
20351
  rmIfExists(credentialsJson);
20117
20352
  }
20118
- const claudeJson = path43.join(home, ".claude.json");
20119
- if (!fs38.existsSync(claudeJson)) {
20353
+ const claudeJson = path44.join(home, ".claude.json");
20354
+ if (!fs39.existsSync(claudeJson)) {
20120
20355
  writeFile0600(
20121
20356
  claudeJson,
20122
20357
  JSON.stringify({ hasCompletedOnboarding: true, customApiKeyResponses: { approved: [] } })
@@ -20127,7 +20362,7 @@ var claudeProvisioner = {
20127
20362
  };
20128
20363
  var codexProvisioner = {
20129
20364
  write(auth, home) {
20130
- const authJson = path43.join(home, ".codex", "auth.json");
20365
+ const authJson = path44.join(home, ".codex", "auth.json");
20131
20366
  if (auth.kind === "api_key") {
20132
20367
  rmIfExists(authJson);
20133
20368
  return { OPENAI_API_KEY: auth.value };
@@ -20138,8 +20373,8 @@ var codexProvisioner = {
20138
20373
  };
20139
20374
  var geminiProvisioner = {
20140
20375
  write(auth, home) {
20141
- const settingsJson = path43.join(home, ".gemini", "settings.json");
20142
- const oauthCreds = path43.join(home, ".gemini", "oauth_creds.json");
20376
+ const settingsJson = path44.join(home, ".gemini", "settings.json");
20377
+ const oauthCreds = path44.join(home, ".gemini", "oauth_creds.json");
20143
20378
  if (auth.kind === "api_key") {
20144
20379
  rmIfExists(oauthCreds);
20145
20380
  writeFile0600(settingsJson, '{"security":{"auth":{"selectedType":"gemini-api-key"}}}');
@@ -20152,7 +20387,7 @@ var geminiProvisioner = {
20152
20387
  };
20153
20388
  var cursorProvisioner = {
20154
20389
  write(auth, home) {
20155
- const authJson = path43.join(home, ".config", "cursor", "auth.json");
20390
+ const authJson = path44.join(home, ".config", "cursor", "auth.json");
20156
20391
  if (auth.kind === "api_key") {
20157
20392
  rmIfExists(authJson);
20158
20393
  return { CURSOR_API_KEY: auth.value };
@@ -20194,22 +20429,22 @@ max_context_size = 262144
20194
20429
  var kimiProvisioner = {
20195
20430
  write(auth, home) {
20196
20431
  const credentialsFiles = [
20197
- path43.join(home, ".kimi", "credentials", "kimi-code.json"),
20198
- path43.join(home, ".kimi-code", "credentials", "kimi-code.json")
20432
+ path44.join(home, ".kimi", "credentials", "kimi-code.json"),
20433
+ path44.join(home, ".kimi-code", "credentials", "kimi-code.json")
20199
20434
  ];
20200
20435
  if (auth.kind === "api_key") {
20201
20436
  credentialsFiles.forEach(rmIfExists);
20202
20437
  return { KIMI_API_KEY: auth.value };
20203
20438
  }
20204
20439
  credentialsFiles.forEach((f) => writeFile0600(f, auth.value));
20205
- writeFile0600(path43.join(home, ".kimi-code", "config.toml"), KIMI_MANAGED_CONFIG_TOML);
20440
+ writeFile0600(path44.join(home, ".kimi-code", "config.toml"), KIMI_MANAGED_CONFIG_TOML);
20206
20441
  return {};
20207
20442
  }
20208
20443
  };
20209
20444
  var coderabbitProvisioner = {
20210
20445
  write(auth, home) {
20211
- const dir = path43.join(home, ".coderabbit");
20212
- const authJson = path43.join(dir, "auth.json");
20446
+ const dir = path44.join(home, ".coderabbit");
20447
+ const authJson = path44.join(dir, "auth.json");
20213
20448
  if (auth.kind === "api_key") {
20214
20449
  rmIfExists(authJson);
20215
20450
  return { CODERABBIT_API_KEY: auth.value };
@@ -20221,13 +20456,13 @@ var coderabbitProvisioner = {
20221
20456
  try {
20222
20457
  const parsed = JSON.parse(value);
20223
20458
  if (typeof parsed.file === "string" && typeof parsed.contents === "string") {
20224
- file = path43.basename(parsed.file);
20459
+ file = path44.basename(parsed.file);
20225
20460
  contents = parsed.contents;
20226
20461
  }
20227
20462
  } catch {
20228
20463
  }
20229
20464
  }
20230
- writeFile0600(path43.join(dir, file), contents);
20465
+ writeFile0600(path44.join(dir, file), contents);
20231
20466
  return {};
20232
20467
  }
20233
20468
  };
@@ -20255,7 +20490,7 @@ var UnsupportedAgentError = class extends Error {
20255
20490
  this.agentId = agentId;
20256
20491
  }
20257
20492
  };
20258
- function provisionAgentCredentials(publicAgentId, auth, homeDir2 = os35.homedir()) {
20493
+ function provisionAgentCredentials(publicAgentId, auth, homeDir2 = os36.homedir()) {
20259
20494
  const internal = toInternalAgentId(publicAgentId);
20260
20495
  if (!internal) throw new UnsupportedAgentError(publicAgentId);
20261
20496
  const provisioner = PROVISIONERS[internal];
@@ -20265,11 +20500,11 @@ function provisionAgentCredentials(publicAgentId, auth, homeDir2 = os35.homedir(
20265
20500
 
20266
20501
  // src/commands/host/git-tooling.ts
20267
20502
  var import_node_child_process21 = require("child_process");
20268
- var fs39 = __toESM(require("fs"));
20269
- var os36 = __toESM(require("os"));
20270
- var path44 = __toESM(require("path"));
20503
+ var fs40 = __toESM(require("fs"));
20504
+ var os37 = __toESM(require("os"));
20505
+ var path45 = __toESM(require("path"));
20271
20506
  function codeamBinDir() {
20272
- return process.env.CODEAM_BIN_DIR ?? path44.join(os36.homedir(), ".codeam", "bin");
20507
+ return process.env.CODEAM_BIN_DIR ?? path45.join(os37.homedir(), ".codeam", "bin");
20273
20508
  }
20274
20509
  var FALLBACK_GH_VERSION = "2.62.0";
20275
20510
  var RELEASE_API = "https://api.github.com/repos/cli/cli/releases/latest";
@@ -20301,7 +20536,7 @@ async function download(url2, dest) {
20301
20536
  const res = await fetch(url2, { headers: { "User-Agent": "codeam-cli" } });
20302
20537
  if (!res.ok || !res.body) return false;
20303
20538
  const buf = Buffer.from(await res.arrayBuffer());
20304
- fs39.writeFileSync(dest, buf);
20539
+ fs40.writeFileSync(dest, buf);
20305
20540
  return true;
20306
20541
  } catch {
20307
20542
  return false;
@@ -20324,8 +20559,8 @@ async function ensureGhCli(runner, token, deps = {}) {
20324
20559
  const version3 = await resolveVersionFn(token);
20325
20560
  const asset = `gh_${version3}_${osToken}_${arch2}`;
20326
20561
  const url2 = `https://github.com/cli/cli/releases/download/v${version3}/${asset}.${ext}`;
20327
- const tmpRoot = fs39.mkdtempSync(path44.join(os36.tmpdir(), "codeam-gh-"));
20328
- const archive = path44.join(tmpRoot, `${asset}.${ext}`);
20562
+ const tmpRoot = fs40.mkdtempSync(path45.join(os37.tmpdir(), "codeam-gh-"));
20563
+ const archive = path45.join(tmpRoot, `${asset}.${ext}`);
20329
20564
  if (!await downloadFn(url2, archive)) {
20330
20565
  log.warn("host-agent", "gh download failed \u2014 skipping (git pull/push still work via the credential helper)");
20331
20566
  return null;
@@ -20335,16 +20570,16 @@ async function ensureGhCli(runner, token, deps = {}) {
20335
20570
  log.warn("host-agent", `gh archive extraction failed (code=${String(extract.code)}) \u2014 skipping`);
20336
20571
  return null;
20337
20572
  }
20338
- const extractedBin = path44.join(tmpRoot, asset, "bin", binaryName);
20339
- if (!fs39.existsSync(extractedBin)) {
20573
+ const extractedBin = path45.join(tmpRoot, asset, "bin", binaryName);
20574
+ if (!fs40.existsSync(extractedBin)) {
20340
20575
  log.warn("host-agent", "gh binary not found in the extracted archive \u2014 skipping");
20341
20576
  return null;
20342
20577
  }
20343
20578
  const binDir = codeamBinDir();
20344
- fs39.mkdirSync(binDir, { recursive: true });
20345
- const target = path44.join(binDir, binaryName);
20346
- fs39.copyFileSync(extractedBin, target);
20347
- fs39.chmodSync(target, 493);
20579
+ fs40.mkdirSync(binDir, { recursive: true });
20580
+ const target = path45.join(binDir, binaryName);
20581
+ fs40.copyFileSync(extractedBin, target);
20582
+ fs40.chmodSync(target, 493);
20348
20583
  log.info("host-agent", `gh installed to ${target} (v${version3})`);
20349
20584
  return target;
20350
20585
  } catch (e) {
@@ -20404,8 +20639,8 @@ async function ensureGlabCli(runner, deps = {}) {
20404
20639
  const version3 = await resolveLatestGlabVersion();
20405
20640
  const asset = `glab_${version3}_${osToken}_${arch2}`;
20406
20641
  const url2 = `https://gitlab.com/gitlab-org/cli/-/releases/v${version3}/downloads/${asset}.${ext}`;
20407
- const tmpRoot = fs39.mkdtempSync(path44.join(os36.tmpdir(), "codeam-glab-"));
20408
- const archive = path44.join(tmpRoot, `${asset}.${ext}`);
20642
+ const tmpRoot = fs40.mkdtempSync(path45.join(os37.tmpdir(), "codeam-glab-"));
20643
+ const archive = path45.join(tmpRoot, `${asset}.${ext}`);
20409
20644
  if (!await downloadFn(url2, archive)) {
20410
20645
  log.warn("host-agent", "glab download failed \u2014 skipping (git push/pull still work)");
20411
20646
  return null;
@@ -20416,20 +20651,20 @@ async function ensureGlabCli(runner, deps = {}) {
20416
20651
  return null;
20417
20652
  }
20418
20653
  const candidates = [
20419
- path44.join(tmpRoot, "bin", binaryName),
20420
- path44.join(tmpRoot, asset, "bin", binaryName),
20421
- path44.join(tmpRoot, binaryName)
20654
+ path45.join(tmpRoot, "bin", binaryName),
20655
+ path45.join(tmpRoot, asset, "bin", binaryName),
20656
+ path45.join(tmpRoot, binaryName)
20422
20657
  ];
20423
- const extractedBin = candidates.find((c2) => fs39.existsSync(c2));
20658
+ const extractedBin = candidates.find((c2) => fs40.existsSync(c2));
20424
20659
  if (!extractedBin) {
20425
20660
  log.warn("host-agent", "glab binary not found in the extracted archive \u2014 skipping");
20426
20661
  return null;
20427
20662
  }
20428
20663
  const binDir = codeamBinDir();
20429
- fs39.mkdirSync(binDir, { recursive: true });
20430
- const target = path44.join(binDir, binaryName);
20431
- fs39.copyFileSync(extractedBin, target);
20432
- fs39.chmodSync(target, 493);
20664
+ fs40.mkdirSync(binDir, { recursive: true });
20665
+ const target = path45.join(binDir, binaryName);
20666
+ fs40.copyFileSync(extractedBin, target);
20667
+ fs40.chmodSync(target, 493);
20433
20668
  log.info("host-agent", `glab installed to ${target} (v${version3})`);
20434
20669
  return target;
20435
20670
  } catch (e) {
@@ -20626,24 +20861,24 @@ var HeadroomStatsReporter = class {
20626
20861
  };
20627
20862
 
20628
20863
  // src/commands/host/headroom-bootstrap.ts
20629
- var fs41 = __toESM(require("fs"));
20630
- var path46 = __toESM(require("path"));
20631
- var os38 = __toESM(require("os"));
20864
+ var fs42 = __toESM(require("fs"));
20865
+ var path47 = __toESM(require("path"));
20866
+ var os39 = __toESM(require("os"));
20632
20867
 
20633
20868
  // src/commands/host/headroom-config.ts
20634
- var fs40 = __toESM(require("fs"));
20635
- var os37 = __toESM(require("os"));
20636
- var path45 = __toESM(require("path"));
20869
+ var fs41 = __toESM(require("fs"));
20870
+ var os38 = __toESM(require("os"));
20871
+ var path46 = __toESM(require("path"));
20637
20872
  function headroomConfigPath() {
20638
- return path45.join(os37.homedir(), ".codeam", "headroom-config.json");
20873
+ return path46.join(os38.homedir(), ".codeam", "headroom-config.json");
20639
20874
  }
20640
20875
  function persistHeadroomConfig(config) {
20641
20876
  try {
20642
20877
  const file = headroomConfigPath();
20643
- fs40.mkdirSync(path45.dirname(file), { recursive: true, mode: 448 });
20878
+ fs41.mkdirSync(path46.dirname(file), { recursive: true, mode: 448 });
20644
20879
  const tmp = `${file}.tmp-${process.pid}`;
20645
- fs40.writeFileSync(tmp, JSON.stringify(config, null, 2), { encoding: "utf8", mode: 384 });
20646
- fs40.renameSync(tmp, file);
20880
+ fs41.writeFileSync(tmp, JSON.stringify(config, null, 2), { encoding: "utf8", mode: 384 });
20881
+ fs41.renameSync(tmp, file);
20647
20882
  restrictToOwner(file);
20648
20883
  } catch (err) {
20649
20884
  log.warn(
@@ -20653,21 +20888,21 @@ function persistHeadroomConfig(config) {
20653
20888
  }
20654
20889
  }
20655
20890
  function agentSettingsPath(kind) {
20656
- const home = os37.homedir();
20657
- if (kind === "claude") return path45.join(home, ".claude", "settings.json");
20658
- if (kind === "codex") return path45.join(home, ".codex", "auth.json");
20659
- if (kind === "copilot") return path45.join(home, ".config", "github-copilot", "hosts.json");
20891
+ const home = os38.homedir();
20892
+ if (kind === "claude") return path46.join(home, ".claude", "settings.json");
20893
+ if (kind === "codex") return path46.join(home, ".codex", "auth.json");
20894
+ if (kind === "copilot") return path46.join(home, ".config", "github-copilot", "hosts.json");
20660
20895
  return null;
20661
20896
  }
20662
20897
  function backupAgentHeadroomConfig(kind) {
20663
20898
  const src = agentSettingsPath(kind);
20664
20899
  if (!src) return;
20665
20900
  try {
20666
- if (!fs40.existsSync(src)) return;
20667
- const dest = path45.join(os37.homedir(), ".codeam", `headroom-backup-${kind}.json`);
20668
- fs40.mkdirSync(path45.dirname(dest), { recursive: true, mode: 448 });
20669
- fs40.copyFileSync(src, dest);
20670
- fs40.chmodSync(dest, 384);
20901
+ if (!fs41.existsSync(src)) return;
20902
+ const dest = path46.join(os38.homedir(), ".codeam", `headroom-backup-${kind}.json`);
20903
+ fs41.mkdirSync(path46.dirname(dest), { recursive: true, mode: 448 });
20904
+ fs41.copyFileSync(src, dest);
20905
+ fs41.chmodSync(dest, 384);
20671
20906
  log.info("host-agent", `headroom config backup: ${src} \u2192 ${dest}`);
20672
20907
  } catch (err) {
20673
20908
  log.warn(
@@ -20679,12 +20914,12 @@ function backupAgentHeadroomConfig(kind) {
20679
20914
  function restoreAgentHeadroomConfig(kind) {
20680
20915
  const dest = agentSettingsPath(kind);
20681
20916
  if (!dest) return false;
20682
- const src = path45.join(os37.homedir(), ".codeam", `headroom-backup-${kind}.json`);
20683
- if (!fs40.existsSync(src)) return false;
20917
+ const src = path46.join(os38.homedir(), ".codeam", `headroom-backup-${kind}.json`);
20918
+ if (!fs41.existsSync(src)) return false;
20684
20919
  try {
20685
- fs40.mkdirSync(path45.dirname(dest), { recursive: true, mode: 448 });
20686
- fs40.copyFileSync(src, dest);
20687
- fs40.chmodSync(dest, 384);
20920
+ fs41.mkdirSync(path46.dirname(dest), { recursive: true, mode: 448 });
20921
+ fs41.copyFileSync(src, dest);
20922
+ fs41.chmodSync(dest, 384);
20688
20923
  log.info("host-agent", `headroom config restored: ${src} \u2192 ${dest}`);
20689
20924
  return true;
20690
20925
  } catch (err) {
@@ -20697,7 +20932,7 @@ function restoreAgentHeadroomConfig(kind) {
20697
20932
  }
20698
20933
  function readHeadroomChildEnv() {
20699
20934
  try {
20700
- const raw = fs40.readFileSync(headroomConfigPath(), "utf8");
20935
+ const raw = fs41.readFileSync(headroomConfigPath(), "utf8");
20701
20936
  const parsed = JSON.parse(raw);
20702
20937
  if (typeof parsed !== "object" || parsed === null) return {};
20703
20938
  const o = parsed;
@@ -20733,49 +20968,49 @@ function bundledClaudeBinDir() {
20733
20968
  const roots = /* @__PURE__ */ new Set();
20734
20969
  let dir = __dirname;
20735
20970
  for (let i = 0; i < 6; i++) {
20736
- roots.add(path46.join(dir, "node_modules"));
20737
- const parent = path46.dirname(dir);
20971
+ roots.add(path47.join(dir, "node_modules"));
20972
+ const parent = path47.dirname(dir);
20738
20973
  if (parent === dir) break;
20739
20974
  dir = parent;
20740
20975
  }
20741
20976
  try {
20742
20977
  const main2 = require.resolve("@anthropic-ai/claude-agent-sdk");
20743
- const marker = `${path46.sep}@anthropic-ai${path46.sep}`;
20978
+ const marker = `${path47.sep}@anthropic-ai${path47.sep}`;
20744
20979
  const idx = main2.lastIndexOf(marker);
20745
20980
  if (idx !== -1) roots.add(main2.slice(0, idx));
20746
20981
  } catch {
20747
20982
  }
20748
20983
  for (const nm of roots) {
20749
- const atAnthropic = path46.join(nm, "@anthropic-ai");
20984
+ const atAnthropic = path47.join(nm, "@anthropic-ai");
20750
20985
  let entries;
20751
20986
  try {
20752
- entries = fs41.readdirSync(atAnthropic);
20987
+ entries = fs42.readdirSync(atAnthropic);
20753
20988
  } catch {
20754
20989
  continue;
20755
20990
  }
20756
20991
  for (const entry of entries) {
20757
20992
  if (!entry.startsWith("claude-agent-sdk-")) continue;
20758
- const bin = path46.join(atAnthropic, entry, "claude");
20759
- if (fs41.existsSync(bin)) return path46.dirname(bin);
20993
+ const bin = path47.join(atAnthropic, entry, "claude");
20994
+ if (fs42.existsSync(bin)) return path47.dirname(bin);
20760
20995
  }
20761
20996
  }
20762
20997
  return null;
20763
20998
  }
20764
20999
  async function getFreeDiskBytes(dir) {
20765
21000
  try {
20766
- const s = await fs41.promises.statfs(dir);
21001
+ const s = await fs42.promises.statfs(dir);
20767
21002
  return s.bsize * s.bavail;
20768
21003
  } catch {
20769
21004
  return null;
20770
21005
  }
20771
21006
  }
20772
21007
  function headroomModelsCached() {
20773
- const hubDir = process.env.HUGGINGFACE_HUB_CACHE || path46.join(
20774
- process.env.HF_HOME || path46.join(os38.homedir(), ".cache", "huggingface"),
21008
+ const hubDir = process.env.HUGGINGFACE_HUB_CACHE || path47.join(
21009
+ process.env.HF_HOME || path47.join(os39.homedir(), ".cache", "huggingface"),
20775
21010
  "hub"
20776
21011
  );
20777
21012
  return HEADROOM_MODELS.every(
20778
- (m) => fs41.existsSync(path46.join(hubDir, `models--${m.repo.replace("/", "--")}`))
21013
+ (m) => fs42.existsSync(path47.join(hubDir, `models--${m.repo.replace("/", "--")}`))
20779
21014
  );
20780
21015
  }
20781
21016
  async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner, opts = {}) {
@@ -20844,7 +21079,7 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
20844
21079
  if (initKind === "claude") {
20845
21080
  const claudeDir = bundledClaudeBinDir();
20846
21081
  if (claudeDir) {
20847
- initEnv.PATH = `${claudeDir}${path46.delimiter}${process.env["PATH"] ?? ""}`;
21082
+ initEnv.PATH = `${claudeDir}${path47.delimiter}${process.env["PATH"] ?? ""}`;
20848
21083
  log.info("host-agent", `headroom init: bundled claude on PATH (${claudeDir})`);
20849
21084
  } else {
20850
21085
  log.warn("host-agent", "headroom init: bundled claude binary not found \u2014 init may fail");
@@ -20881,19 +21116,19 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
20881
21116
  }
20882
21117
 
20883
21118
  // src/commands/host/house-proxy-config.ts
20884
- var fs42 = __toESM(require("fs"));
20885
- var os39 = __toESM(require("os"));
20886
- var path47 = __toESM(require("path"));
21119
+ var fs43 = __toESM(require("fs"));
21120
+ var os40 = __toESM(require("os"));
21121
+ var path48 = __toESM(require("path"));
20887
21122
  function houseProxyConfigPath() {
20888
- return path47.join(os39.homedir(), ".codeam", "house-proxy.json");
21123
+ return path48.join(os40.homedir(), ".codeam", "house-proxy.json");
20889
21124
  }
20890
21125
  function persistHouseProxyConfig(config) {
20891
21126
  try {
20892
21127
  const file = houseProxyConfigPath();
20893
- fs42.mkdirSync(path47.dirname(file), { recursive: true, mode: 448 });
21128
+ fs43.mkdirSync(path48.dirname(file), { recursive: true, mode: 448 });
20894
21129
  const tmp = `${file}.tmp-${process.pid}`;
20895
- fs42.writeFileSync(tmp, JSON.stringify(config, null, 2), { encoding: "utf8", mode: 384 });
20896
- fs42.renameSync(tmp, file);
21130
+ fs43.writeFileSync(tmp, JSON.stringify(config, null, 2), { encoding: "utf8", mode: 384 });
21131
+ fs43.renameSync(tmp, file);
20897
21132
  restrictToOwner(file);
20898
21133
  } catch (err) {
20899
21134
  log.warn(
@@ -20904,13 +21139,13 @@ function persistHouseProxyConfig(config) {
20904
21139
  }
20905
21140
  function clearHouseProxyConfig() {
20906
21141
  try {
20907
- fs42.rmSync(houseProxyConfigPath(), { force: true });
21142
+ fs43.rmSync(houseProxyConfigPath(), { force: true });
20908
21143
  } catch {
20909
21144
  }
20910
21145
  }
20911
21146
  function readHouseProxyChildEnv() {
20912
21147
  try {
20913
- const raw = fs42.readFileSync(houseProxyConfigPath(), "utf8");
21148
+ const raw = fs43.readFileSync(houseProxyConfigPath(), "utf8");
20914
21149
  const parsed = JSON.parse(raw);
20915
21150
  if (typeof parsed !== "object" || parsed === null) return {};
20916
21151
  const o = parsed;
@@ -20943,9 +21178,9 @@ function readHouseProxyChildEnv() {
20943
21178
  var import_node_child_process23 = require("child_process");
20944
21179
 
20945
21180
  // src/lib/updateNotifier.ts
20946
- var fs43 = __toESM(require("fs"));
20947
- var os40 = __toESM(require("os"));
20948
- var path48 = __toESM(require("path"));
21181
+ var fs44 = __toESM(require("fs"));
21182
+ var os41 = __toESM(require("os"));
21183
+ var path49 = __toESM(require("path"));
20949
21184
  var https6 = __toESM(require("https"));
20950
21185
  var import_node_child_process22 = require("child_process");
20951
21186
  var import_picocolors3 = __toESM(require("picocolors"));
@@ -20954,12 +21189,12 @@ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
20954
21189
  var TTL_MS = 24 * 60 * 60 * 1e3;
20955
21190
  var REQUEST_TIMEOUT_MS = 1500;
20956
21191
  function cachePath() {
20957
- const dir = path48.join(os40.homedir(), ".codeam");
20958
- return path48.join(dir, "update-check.json");
21192
+ const dir = path49.join(os41.homedir(), ".codeam");
21193
+ return path49.join(dir, "update-check.json");
20959
21194
  }
20960
21195
  function readCache() {
20961
21196
  try {
20962
- const raw = fs43.readFileSync(cachePath(), "utf8");
21197
+ const raw = fs44.readFileSync(cachePath(), "utf8");
20963
21198
  const parsed = JSON.parse(raw);
20964
21199
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
20965
21200
  return parsed;
@@ -20970,10 +21205,10 @@ function readCache() {
20970
21205
  function writeCache(cache) {
20971
21206
  try {
20972
21207
  const file = cachePath();
20973
- fs43.mkdirSync(path48.dirname(file), { recursive: true });
21208
+ fs44.mkdirSync(path49.dirname(file), { recursive: true });
20974
21209
  const tmp = `${file}.${process.pid}.tmp`;
20975
- fs43.writeFileSync(tmp, JSON.stringify(cache));
20976
- fs43.renameSync(tmp, file);
21210
+ fs44.writeFileSync(tmp, JSON.stringify(cache));
21211
+ fs44.renameSync(tmp, file);
20977
21212
  } catch {
20978
21213
  }
20979
21214
  }
@@ -21047,8 +21282,8 @@ function isLinkedInstall() {
21047
21282
  timeout: 2e3
21048
21283
  }).trim();
21049
21284
  if (!root) return false;
21050
- const pkgPath = path48.join(root, PKG_NAME);
21051
- return fs43.lstatSync(pkgPath).isSymbolicLink();
21285
+ const pkgPath = path49.join(root, PKG_NAME);
21286
+ return fs44.lstatSync(pkgPath).isSymbolicLink();
21052
21287
  } catch {
21053
21288
  return false;
21054
21289
  }
@@ -21084,7 +21319,7 @@ function maybeAutoUpdate(currentVersion, latest) {
21084
21319
  return;
21085
21320
  }
21086
21321
  try {
21087
- fs43.unlinkSync(cachePath());
21322
+ fs44.unlinkSync(cachePath());
21088
21323
  } catch {
21089
21324
  }
21090
21325
  process.stderr.write(` ${import_picocolors3.default.green("\u2713")} Updated. Resuming session...
@@ -21100,7 +21335,7 @@ async function autoUpgradeBeforeCriticalCommand() {
21100
21335
  if (process.env.NODE_ENV === "test") return;
21101
21336
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21102
21337
  if (process.env.CI) return;
21103
- const current2 = true ? "2.64.0" : null;
21338
+ const current2 = true ? "2.65.1" : null;
21104
21339
  if (!current2) return;
21105
21340
  const cache = readCache();
21106
21341
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21117,7 +21352,7 @@ function checkForUpdates() {
21117
21352
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
21118
21353
  if (process.env.CI) return;
21119
21354
  if (!process.stdout.isTTY) return;
21120
- const current2 = true ? "2.64.0" : null;
21355
+ const current2 = true ? "2.65.1" : null;
21121
21356
  if (!current2) return;
21122
21357
  const cache = readCache();
21123
21358
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -21137,7 +21372,7 @@ var SELF_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
21137
21372
  var SELF_UPDATE_VIEW_TIMEOUT_MS = 3e4;
21138
21373
  var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
21139
21374
  function currentCliVersion() {
21140
- return true ? "2.64.0" : null;
21375
+ return true ? "2.65.1" : null;
21141
21376
  }
21142
21377
  function runCmd(cmd, args2, timeoutMs) {
21143
21378
  return new Promise((resolve9) => {
@@ -21204,7 +21439,7 @@ async function runSelfUpdate() {
21204
21439
 
21205
21440
  // src/commands/host/teardown.ts
21206
21441
  var import_node_child_process24 = require("child_process");
21207
- var fs44 = __toESM(require("fs"));
21442
+ var fs45 = __toESM(require("fs"));
21208
21443
  var defaultDisableService = () => {
21209
21444
  try {
21210
21445
  (0, import_node_child_process24.execFileSync)("systemctl", ["disable", "--now", "codeam-host-agent"], { stdio: "ignore" });
@@ -21213,7 +21448,7 @@ var defaultDisableService = () => {
21213
21448
  };
21214
21449
  var defaultTeardownHeadroom = () => {
21215
21450
  try {
21216
- const kind = JSON.parse(fs44.readFileSync(headroomConfigPath(), "utf8")).agent;
21451
+ const kind = JSON.parse(fs45.readFileSync(headroomConfigPath(), "utf8")).agent;
21217
21452
  if (kind) {
21218
21453
  (0, import_node_child_process24.execFileSync)("headroom", ["unwrap", kind], { stdio: "ignore", timeout: 15e3 });
21219
21454
  }
@@ -21281,8 +21516,8 @@ function maybeResumeLocalHeadroomReporter(ctx) {
21281
21516
  if (process.env["HEADROOM_ENABLED"] === "1") return null;
21282
21517
  try {
21283
21518
  const file = headroomConfigPath();
21284
- if (!fs45.existsSync(file)) return null;
21285
- const cfg = JSON.parse(fs45.readFileSync(file, "utf8"));
21519
+ if (!fs46.existsSync(file)) return null;
21520
+ const cfg = JSON.parse(fs46.readFileSync(file, "utf8"));
21286
21521
  if (!cfg?.enabled) return null;
21287
21522
  const agent = cfg.agent ?? "claude";
21288
21523
  const ingestUrl = `${resolveApiBaseUrl()}/api/sessions/${ctx.sessionId}/headroom-savings`;
@@ -21869,13 +22104,13 @@ var HostAgentSupervisor = class {
21869
22104
  const relay = this.relay;
21870
22105
  if (!relay) return;
21871
22106
  const raw = cmd.payload?.path;
21872
- const target = typeof raw === "string" && raw.trim() ? path49.resolve(raw.trim()) : os41.homedir();
22107
+ const target = typeof raw === "string" && raw.trim() ? path50.resolve(raw.trim()) : os42.homedir();
21873
22108
  try {
21874
- const dirents = await fs45.promises.readdir(target, { withFileTypes: true });
22109
+ const dirents = await fs46.promises.readdir(target, { withFileTypes: true });
21875
22110
  const entries = dirents.filter((d3) => !d3.name.startsWith(".")).map((d3) => ({ name: d3.name, isDir: d3.isDirectory() })).sort(
21876
22111
  (a, b) => a.isDir === b.isDir ? a.name.localeCompare(b.name) : a.isDir ? -1 : 1
21877
22112
  );
21878
- const parent = path49.dirname(target);
22113
+ const parent = path50.dirname(target);
21879
22114
  await relay.sendResult(cmd.id, "completed", {
21880
22115
  path: target,
21881
22116
  // null at the filesystem root so the UI can hide the ".." affordance.
@@ -22108,14 +22343,14 @@ var HostAgentSupervisor = class {
22108
22343
  API_TIMEOUT_MS: "3000000",
22109
22344
  CODEAM_AUTO_TOKEN: payload.autoPairToken
22110
22345
  };
22111
- const houseConfigDir = path49.join(
22112
- os41.homedir(),
22346
+ const houseConfigDir = path50.join(
22347
+ os42.homedir(),
22113
22348
  ".codeam",
22114
22349
  "house-claude",
22115
22350
  payload.deployId
22116
22351
  );
22117
22352
  try {
22118
- fs45.mkdirSync(houseConfigDir, { recursive: true, mode: 448 });
22353
+ fs46.mkdirSync(houseConfigDir, { recursive: true, mode: 448 });
22119
22354
  } catch {
22120
22355
  }
22121
22356
  childEnv.CLAUDE_CONFIG_DIR = houseConfigDir;
@@ -22141,7 +22376,7 @@ var HostAgentSupervisor = class {
22141
22376
  report("installing", "installing agent CLI");
22142
22377
  await this.runAgentInstall(payload.agentInstallScript);
22143
22378
  }
22144
- const home = process.env.HOME || os41.homedir();
22379
+ const home = process.env.HOME || os42.homedir();
22145
22380
  childEnv.PATH = `${home}/.local/bin:${process.env.PATH ?? ""}`;
22146
22381
  if (payload.cloneToken) {
22147
22382
  try {
@@ -22149,13 +22384,13 @@ var HostAgentSupervisor = class {
22149
22384
  if ((payload.repoProvider ?? "github") === "gitlab") {
22150
22385
  const glabCmd = await ensureGlabCli(defaultGitToolingRunner);
22151
22386
  if (glabCmd) {
22152
- childEnv.PATH = `${codeamBinDir()}${path49.delimiter}${childEnv.PATH}`;
22387
+ childEnv.PATH = `${codeamBinDir()}${path50.delimiter}${childEnv.PATH}`;
22153
22388
  await ensureGlabAuth(defaultGitToolingRunner, glabCmd, payload.cloneToken);
22154
22389
  }
22155
22390
  } else {
22156
22391
  const ghCmd = await ensureGhCli(defaultGitToolingRunner, payload.cloneToken);
22157
22392
  if (ghCmd) {
22158
- childEnv.PATH = `${codeamBinDir()}${path49.delimiter}${childEnv.PATH}`;
22393
+ childEnv.PATH = `${codeamBinDir()}${path50.delimiter}${childEnv.PATH}`;
22159
22394
  await ensureGhAuth(defaultGitToolingRunner, ghCmd, payload.cloneToken);
22160
22395
  }
22161
22396
  }
@@ -22175,7 +22410,7 @@ var HostAgentSupervisor = class {
22175
22410
  }
22176
22411
  if (payload.headroomEnabled && payload.headroomAgent && payload.headroomSavingsIngestUrl && isHeadroomSupportedAgent(payload.headroomAgent)) {
22177
22412
  report("headroom", "setting up Headroom proxy");
22178
- const freeBytes = await this.getFreeDisk(os41.homedir());
22413
+ const freeBytes = await this.getFreeDisk(os42.homedir());
22179
22414
  const alreadyInstalled = this.isHeadroomInstalled();
22180
22415
  if (!alreadyInstalled && freeBytes !== null && freeBytes < HEADROOM_MIN_FREE_DISK_BYTES) {
22181
22416
  const freeGb = (freeBytes / 1e9).toFixed(1);
@@ -22234,9 +22469,9 @@ var HostAgentSupervisor = class {
22234
22469
  );
22235
22470
  if (!payload.suppressOnboardingWelcome) {
22236
22471
  try {
22237
- const cfgDir = childEnv.CLAUDE_CONFIG_DIR || path49.join(os41.homedir(), ".claude");
22238
- const projectDir = path49.join(cfgDir, "projects", encodeCwd(cwd));
22239
- const hasPriorConversation = fs45.existsSync(projectDir) && fs45.readdirSync(projectDir).some((f) => f.endsWith(".jsonl"));
22472
+ const cfgDir = childEnv.CLAUDE_CONFIG_DIR || path50.join(os42.homedir(), ".claude");
22473
+ const projectDir = path50.join(cfgDir, "projects", encodeCwd(cwd));
22474
+ const hasPriorConversation = fs46.existsSync(projectDir) && fs46.readdirSync(projectDir).some((f) => f.endsWith(".jsonl"));
22240
22475
  if (hasPriorConversation) {
22241
22476
  childEnv.CODEAM_RESUME_LATEST = "1";
22242
22477
  log.info(
@@ -22335,7 +22570,7 @@ var HostAgentSupervisor = class {
22335
22570
  if (this.children.size > 0) return;
22336
22571
  const session = getActiveSession();
22337
22572
  if (!session || !session.pluginId || !session.pollSecret || !session.agent) return;
22338
- const cwd = session.cwd && fs45.existsSync(session.cwd) ? session.cwd : process.cwd();
22573
+ const cwd = session.cwd && fs46.existsSync(session.cwd) ? session.cwd : process.cwd();
22339
22574
  const proc = this.resumeSpawner(
22340
22575
  { ...readHeadroomChildEnv(), ...readHouseProxyChildEnv() },
22341
22576
  cwd
@@ -22432,12 +22667,12 @@ var HostAgentSupervisor = class {
22432
22667
  this.children.delete(deployId);
22433
22668
  }
22434
22669
  const dirs = [
22435
- path49.join(selfHostedWorkspaceRoot(), deployId),
22436
- path49.join(os41.homedir(), ".codeam", "house-claude", deployId)
22670
+ path50.join(selfHostedWorkspaceRoot(), deployId),
22671
+ path50.join(os42.homedir(), ".codeam", "house-claude", deployId)
22437
22672
  ];
22438
22673
  for (const dir of dirs) {
22439
22674
  try {
22440
- fs45.rmSync(dir, { recursive: true, force: true });
22675
+ fs46.rmSync(dir, { recursive: true, force: true });
22441
22676
  } catch (err) {
22442
22677
  log.warn(
22443
22678
  "host-agent",
@@ -22574,10 +22809,10 @@ async function configureHeadroom(action, ctx, deps) {
22574
22809
  }
22575
22810
 
22576
22811
  // src/services/headroom/budget-relaunch.ts
22577
- var fs46 = __toESM(require("fs"));
22578
- var os42 = __toESM(require("os"));
22579
- var path50 = __toESM(require("path"));
22580
- var import_child_process14 = require("child_process");
22812
+ var fs47 = __toESM(require("fs"));
22813
+ var os43 = __toESM(require("os"));
22814
+ var path51 = __toESM(require("path"));
22815
+ var import_child_process15 = require("child_process");
22581
22816
  function amendDeploymentManifestBudget(manifest, budget) {
22582
22817
  const rawArgs = manifest.proxy_args ?? [];
22583
22818
  const strippedArgs = [];
@@ -22610,7 +22845,7 @@ function amendDeploymentManifestBudget(manifest, budget) {
22610
22845
  return { ...manifest, proxy_args: newArgs, base_env: newEnv };
22611
22846
  }
22612
22847
  function findHeadroomDeployments(homeDir2, deps) {
22613
- const deployDir = path50.join(homeDir2, ".headroom", "deploy");
22848
+ const deployDir = path51.join(homeDir2, ".headroom", "deploy");
22614
22849
  let profiles;
22615
22850
  try {
22616
22851
  profiles = deps.readDir(deployDir);
@@ -22619,7 +22854,7 @@ function findHeadroomDeployments(homeDir2, deps) {
22619
22854
  }
22620
22855
  const results = [];
22621
22856
  for (const profile of profiles) {
22622
- const manifestPath = path50.join(deployDir, profile, "manifest.json");
22857
+ const manifestPath = path51.join(deployDir, profile, "manifest.json");
22623
22858
  let raw;
22624
22859
  try {
22625
22860
  raw = deps.readJson(manifestPath);
@@ -22651,12 +22886,12 @@ async function applyBudgetToHeadroom(budget, deps) {
22651
22886
  }
22652
22887
  function writeManifestReal(manifestPath, manifest) {
22653
22888
  const tmp = manifestPath + ".codeam.tmp";
22654
- fs46.writeFileSync(tmp, JSON.stringify(manifest, null, 2) + "\n", { mode: 384 });
22655
- fs46.renameSync(tmp, manifestPath);
22889
+ fs47.writeFileSync(tmp, JSON.stringify(manifest, null, 2) + "\n", { mode: 384 });
22890
+ fs47.renameSync(tmp, manifestPath);
22656
22891
  }
22657
22892
  function restartDeploymentReal(profile) {
22658
22893
  try {
22659
- const proc = (0, import_child_process14.spawn)("headroom", ["install", "restart", "--profile", profile], {
22894
+ const proc = (0, import_child_process15.spawn)("headroom", ["install", "restart", "--profile", profile], {
22660
22895
  detached: true,
22661
22896
  stdio: "ignore"
22662
22897
  });
@@ -22687,11 +22922,11 @@ function spawnProxyReal2(_budget) {
22687
22922
  );
22688
22923
  }
22689
22924
  function makeRealApplyBudgetDeps() {
22690
- const homeDir2 = os42.homedir();
22925
+ const homeDir2 = os43.homedir();
22691
22926
  return {
22692
22927
  findDeployments: () => findHeadroomDeployments(homeDir2, {
22693
- readDir: (dir) => fs46.readdirSync(dir),
22694
- readJson: (filePath) => JSON.parse(fs46.readFileSync(filePath, "utf8"))
22928
+ readDir: (dir) => fs47.readdirSync(dir),
22929
+ readJson: (filePath) => JSON.parse(fs47.readFileSync(filePath, "utf8"))
22695
22930
  }),
22696
22931
  writeManifest: writeManifestReal,
22697
22932
  restartDeployment: restartDeploymentReal,
@@ -22824,47 +23059,47 @@ async function readUsageReport(deps = {}) {
22824
23059
  }
22825
23060
 
22826
23061
  // src/agents/acp/guardrail-config.ts
22827
- var fs47 = __toESM(require("fs"));
22828
- var path51 = __toESM(require("path"));
22829
- var os43 = __toESM(require("os"));
23062
+ var fs48 = __toESM(require("fs"));
23063
+ var path52 = __toESM(require("path"));
23064
+ var os44 = __toESM(require("os"));
22830
23065
  var current = null;
22831
- function guardrailConfigPath(homeDir2 = os43.homedir()) {
22832
- return path51.join(homeDir2, ".codeam", "guardrails.json");
23066
+ function guardrailConfigPath(homeDir2 = os44.homedir()) {
23067
+ return path52.join(homeDir2, ".codeam", "guardrails.json");
22833
23068
  }
22834
- function loadGuardrailPolicy(homeDir2 = os43.homedir()) {
23069
+ function loadGuardrailPolicy(homeDir2 = os44.homedir()) {
22835
23070
  try {
22836
- current = normalizeGuardrailPolicy(JSON.parse(fs47.readFileSync(guardrailConfigPath(homeDir2), "utf8")));
23071
+ current = normalizeGuardrailPolicy(JSON.parse(fs48.readFileSync(guardrailConfigPath(homeDir2), "utf8")));
22837
23072
  } catch {
22838
23073
  current = { ...DEFAULT_GUARDRAIL_POLICY };
22839
23074
  }
22840
23075
  return current;
22841
23076
  }
22842
- function getGuardrailPolicy(homeDir2 = os43.homedir()) {
23077
+ function getGuardrailPolicy(homeDir2 = os44.homedir()) {
22843
23078
  return current ?? loadGuardrailPolicy(homeDir2);
22844
23079
  }
22845
- function setGuardrailPolicy(raw, homeDir2 = os43.homedir()) {
23080
+ function setGuardrailPolicy(raw, homeDir2 = os44.homedir()) {
22846
23081
  const next = normalizeGuardrailPolicy(raw);
22847
23082
  current = next;
22848
23083
  try {
22849
23084
  const p2 = guardrailConfigPath(homeDir2);
22850
- fs47.mkdirSync(path51.dirname(p2), { recursive: true });
22851
- fs47.writeFileSync(p2, JSON.stringify(next, null, 2));
23085
+ fs48.mkdirSync(path52.dirname(p2), { recursive: true });
23086
+ fs48.writeFileSync(p2, JSON.stringify(next, null, 2));
22852
23087
  } catch {
22853
23088
  }
22854
23089
  return next;
22855
23090
  }
22856
23091
 
22857
23092
  // src/services/preview/port-registry.ts
22858
- var fs48 = __toESM(require("fs"));
22859
- var os44 = __toESM(require("os"));
22860
- var path52 = __toESM(require("path"));
22861
- var import_child_process15 = require("child_process");
23093
+ var fs49 = __toESM(require("fs"));
23094
+ var os45 = __toESM(require("os"));
23095
+ var path53 = __toESM(require("path"));
23096
+ var import_child_process16 = require("child_process");
22862
23097
  function registryPath() {
22863
- return path52.join(os44.homedir(), ".codeam", "preview-ports.json");
23098
+ return path53.join(os45.homedir(), ".codeam", "preview-ports.json");
22864
23099
  }
22865
23100
  function readRegistry() {
22866
23101
  try {
22867
- const raw = fs48.readFileSync(registryPath(), "utf8");
23102
+ const raw = fs49.readFileSync(registryPath(), "utf8");
22868
23103
  const parsed = JSON.parse(raw);
22869
23104
  if (parsed && typeof parsed === "object") return parsed;
22870
23105
  } catch {
@@ -22874,10 +23109,10 @@ function readRegistry() {
22874
23109
  function writeRegistry(reg) {
22875
23110
  try {
22876
23111
  const file = registryPath();
22877
- fs48.mkdirSync(path52.dirname(file), { recursive: true });
23112
+ fs49.mkdirSync(path53.dirname(file), { recursive: true });
22878
23113
  const tmp = `${file}.tmp`;
22879
- fs48.writeFileSync(tmp, JSON.stringify(reg), "utf8");
22880
- fs48.renameSync(tmp, file);
23114
+ fs49.writeFileSync(tmp, JSON.stringify(reg), "utf8");
23115
+ fs49.renameSync(tmp, file);
22881
23116
  } catch (err) {
22882
23117
  log.warn("preview", `port-registry write failed: ${err instanceof Error ? err.message : String(err)}`);
22883
23118
  }
@@ -22905,7 +23140,7 @@ function groupAlive(pgid) {
22905
23140
  }
22906
23141
  function killGroup(pgid) {
22907
23142
  if (process.platform === "win32") {
22908
- (0, import_child_process15.spawnSync)("taskkill", ["/F", "/T", "/PID", String(pgid)], { stdio: "ignore" });
23143
+ (0, import_child_process16.spawnSync)("taskkill", ["/F", "/T", "/PID", String(pgid)], { stdio: "ignore" });
22909
23144
  return;
22910
23145
  }
22911
23146
  try {
@@ -22942,8 +23177,8 @@ function reclaimOwnOrphanPort(port) {
22942
23177
  }
22943
23178
 
22944
23179
  // src/services/preview/host-allow.ts
22945
- var import_fs = require("fs");
22946
- var path53 = __toESM(require("path"));
23180
+ var import_fs2 = require("fs");
23181
+ var path54 = __toESM(require("path"));
22947
23182
  var NEXT_ALLOWED_ORIGINS = [
22948
23183
  "*.trycloudflare.com",
22949
23184
  "*.preview.codeagent-mobile.com",
@@ -22963,11 +23198,11 @@ var MARKER_DIR = ".codeam";
22963
23198
  var MARKER_FILE = "preview-host-allow.json";
22964
23199
  var ORIG_INFIX = ".codeam-orig";
22965
23200
  function markerPath(cwd) {
22966
- return path53.join(cwd, MARKER_DIR, MARKER_FILE);
23201
+ return path54.join(cwd, MARKER_DIR, MARKER_FILE);
22967
23202
  }
22968
23203
  async function fileExists(p2) {
22969
23204
  try {
22970
- await import_fs.promises.access(p2);
23205
+ await import_fs2.promises.access(p2);
22971
23206
  return true;
22972
23207
  } catch {
22973
23208
  return false;
@@ -22976,13 +23211,13 @@ async function fileExists(p2) {
22976
23211
  async function findConfigFile(cwd, framework) {
22977
23212
  const base = CONFIG_BASENAMES[framework];
22978
23213
  for (const ext of CONFIG_EXTS) {
22979
- if (await fileExists(path53.join(cwd, `${base}${ext}`))) return `${base}${ext}`;
23214
+ if (await fileExists(path54.join(cwd, `${base}${ext}`))) return `${base}${ext}`;
22980
23215
  }
22981
23216
  return null;
22982
23217
  }
22983
23218
  async function dependsOn(cwd, pkgName) {
22984
23219
  try {
22985
- const raw = await import_fs.promises.readFile(path53.join(cwd, "package.json"), "utf8");
23220
+ const raw = await import_fs2.promises.readFile(path54.join(cwd, "package.json"), "utf8");
22986
23221
  const pkg = JSON.parse(raw);
22987
23222
  return !!(pkg.dependencies?.[pkgName] ?? pkg.devDependencies?.[pkgName]);
22988
23223
  } catch {
@@ -22990,11 +23225,11 @@ async function dependsOn(cwd, pkgName) {
22990
23225
  }
22991
23226
  }
22992
23227
  async function isEsmConfig(cwd, configFile) {
22993
- const ext = path53.extname(configFile);
23228
+ const ext = path54.extname(configFile);
22994
23229
  if (ext === ".mjs" || ext === ".mts" || ext === ".ts") return true;
22995
23230
  if (ext === ".cjs") return false;
22996
23231
  try {
22997
- const raw = await import_fs.promises.readFile(path53.join(cwd, "package.json"), "utf8");
23232
+ const raw = await import_fs2.promises.readFile(path54.join(cwd, "package.json"), "utf8");
22998
23233
  return JSON.parse(raw).type === "module";
22999
23234
  } catch {
23000
23235
  return false;
@@ -23077,12 +23312,12 @@ function shimFor(framework, origBasename, esm) {
23077
23312
  return framework === "next" ? nextShim(origBasename, esm) : viteShim(origBasename, esm);
23078
23313
  }
23079
23314
  async function writeMarker(cwd, marker) {
23080
- await import_fs.promises.mkdir(path53.join(cwd, MARKER_DIR), { recursive: true });
23081
- await import_fs.promises.writeFile(markerPath(cwd), JSON.stringify(marker, null, 2), "utf8");
23315
+ await import_fs2.promises.mkdir(path54.join(cwd, MARKER_DIR), { recursive: true });
23316
+ await import_fs2.promises.writeFile(markerPath(cwd), JSON.stringify(marker, null, 2), "utf8");
23082
23317
  }
23083
23318
  async function readMarker(cwd) {
23084
23319
  try {
23085
- const parsed = JSON.parse(await import_fs.promises.readFile(markerPath(cwd), "utf8"));
23320
+ const parsed = JSON.parse(await import_fs2.promises.readFile(markerPath(cwd), "utf8"));
23086
23321
  return parsed && parsed.configFile ? parsed : null;
23087
23322
  } catch {
23088
23323
  return null;
@@ -23092,20 +23327,20 @@ async function restorePreviewHostAllow(cwd) {
23092
23327
  const marker = await readMarker(cwd);
23093
23328
  if (!marker) return;
23094
23329
  try {
23095
- const configAbs = path53.join(cwd, marker.configFile);
23330
+ const configAbs = path54.join(cwd, marker.configFile);
23096
23331
  if (marker.backupFile) {
23097
- const backupAbs = path53.join(cwd, marker.backupFile);
23332
+ const backupAbs = path54.join(cwd, marker.backupFile);
23098
23333
  if (await fileExists(backupAbs)) {
23099
- await import_fs.promises.rm(configAbs, { force: true });
23100
- await import_fs.promises.rename(backupAbs, configAbs);
23334
+ await import_fs2.promises.rm(configAbs, { force: true });
23335
+ await import_fs2.promises.rename(backupAbs, configAbs);
23101
23336
  }
23102
23337
  } else {
23103
- await import_fs.promises.rm(configAbs, { force: true });
23338
+ await import_fs2.promises.rm(configAbs, { force: true });
23104
23339
  }
23105
23340
  } catch (err) {
23106
23341
  log.warn("preview", `host-allow restore failed: ${err instanceof Error ? err.message : err}`);
23107
23342
  } finally {
23108
- await import_fs.promises.rm(markerPath(cwd), { force: true }).catch(() => void 0);
23343
+ await import_fs2.promises.rm(markerPath(cwd), { force: true }).catch(() => void 0);
23109
23344
  }
23110
23345
  }
23111
23346
  async function applyPreviewHostAllow(cwd) {
@@ -23127,16 +23362,16 @@ async function applyPreviewHostAllow(cwd) {
23127
23362
  }
23128
23363
  if (!framework) return;
23129
23364
  if (existing) {
23130
- const ext = path53.extname(existing);
23365
+ const ext = path54.extname(existing);
23131
23366
  const origBasename = `${CONFIG_BASENAMES[framework]}${ORIG_INFIX}${ext}`;
23132
23367
  const esm = await isEsmConfig(cwd, existing);
23133
- await import_fs.promises.rename(path53.join(cwd, existing), path53.join(cwd, origBasename));
23134
- await import_fs.promises.writeFile(path53.join(cwd, existing), shimFor(framework, origBasename, esm), "utf8");
23368
+ await import_fs2.promises.rename(path54.join(cwd, existing), path54.join(cwd, origBasename));
23369
+ await import_fs2.promises.writeFile(path54.join(cwd, existing), shimFor(framework, origBasename, esm), "utf8");
23135
23370
  await writeMarker(cwd, { framework, configFile: existing, backupFile: origBasename });
23136
23371
  log.info("preview", `host-allow: wrapped ${existing} (${framework}) for tunnel access`);
23137
23372
  } else {
23138
23373
  const configFile = `${CONFIG_BASENAMES[framework]}.mjs`;
23139
- await import_fs.promises.writeFile(path53.join(cwd, configFile), shimFor(framework, null, true), "utf8");
23374
+ await import_fs2.promises.writeFile(path54.join(cwd, configFile), shimFor(framework, null, true), "utf8");
23140
23375
  await writeMarker(cwd, { framework, configFile, backupFile: null });
23141
23376
  log.info("preview", `host-allow: created ${configFile} (${framework}) for tunnel access`);
23142
23377
  }
@@ -23146,14 +23381,14 @@ async function applyPreviewHostAllow(cwd) {
23146
23381
  }
23147
23382
 
23148
23383
  // src/services/preview/cloudflared.ts
23149
- var import_child_process16 = require("child_process");
23150
- var import_fs2 = require("fs");
23384
+ var import_child_process17 = require("child_process");
23385
+ var import_fs3 = require("fs");
23151
23386
  var import_promises = __toESM(require("fs/promises"));
23152
- var import_os9 = __toESM(require("os"));
23153
- var import_path4 = __toESM(require("path"));
23387
+ var import_os10 = __toESM(require("os"));
23388
+ var import_path5 = __toESM(require("path"));
23154
23389
  var import_promises2 = require("stream/promises");
23155
23390
  var import_which = __toESM(require("which"));
23156
- var CACHED_BINARY = import_path4.default.join(import_os9.default.homedir(), ".codeam", "bin", "cloudflared");
23391
+ var CACHED_BINARY = import_path5.default.join(import_os10.default.homedir(), ".codeam", "bin", "cloudflared");
23157
23392
  async function resolveCloudflared(opts = {}) {
23158
23393
  try {
23159
23394
  return await (0, import_which.default)("cloudflared");
@@ -23177,7 +23412,7 @@ async function resolveCloudflared(opts = {}) {
23177
23412
  }
23178
23413
  async function downloadCloudflared(target) {
23179
23414
  const url2 = downloadUrlForPlatform();
23180
- await import_promises.default.mkdir(import_path4.default.dirname(target), { recursive: true });
23415
+ await import_promises.default.mkdir(import_path5.default.dirname(target), { recursive: true });
23181
23416
  const response = await fetch(url2);
23182
23417
  if (!response.ok || !response.body) {
23183
23418
  throw new Error(
@@ -23188,10 +23423,10 @@ async function downloadCloudflared(target) {
23188
23423
  const tmp = `${target}.download.tgz`;
23189
23424
  await (0, import_promises2.pipeline)(
23190
23425
  response.body,
23191
- (0, import_fs2.createWriteStream)(tmp)
23426
+ (0, import_fs3.createWriteStream)(tmp)
23192
23427
  );
23193
23428
  try {
23194
- await extractTgz(tmp, import_path4.default.dirname(target));
23429
+ await extractTgz(tmp, import_path5.default.dirname(target));
23195
23430
  await import_promises.default.access(target);
23196
23431
  await import_promises.default.chmod(target, 493);
23197
23432
  } catch (err) {
@@ -23205,7 +23440,7 @@ async function downloadCloudflared(target) {
23205
23440
  }
23206
23441
  await (0, import_promises2.pipeline)(
23207
23442
  response.body,
23208
- (0, import_fs2.createWriteStream)(target, { mode: 493 })
23443
+ (0, import_fs3.createWriteStream)(target, { mode: 493 })
23209
23444
  );
23210
23445
  }
23211
23446
  async function isExecutableBinary(p2) {
@@ -23223,7 +23458,7 @@ async function isExecutableBinary(p2) {
23223
23458
  }
23224
23459
  function extractTgz(tgzPath, destDir) {
23225
23460
  return new Promise((resolve9, reject) => {
23226
- const child = (0, import_child_process16.spawn)("tar", ["-xzf", tgzPath, "-C", destDir], { stdio: "ignore" });
23461
+ const child = (0, import_child_process17.spawn)("tar", ["-xzf", tgzPath, "-C", destDir], { stdio: "ignore" });
23227
23462
  child.on("error", (err) => reject(err));
23228
23463
  child.on(
23229
23464
  "exit",
@@ -23253,11 +23488,11 @@ function decodeTunnelToken(token) {
23253
23488
  }
23254
23489
  async function spawnNamedTunnel(bin, token, port) {
23255
23490
  const creds = decodeTunnelToken(token);
23256
- const credDir = import_path4.default.join(import_os9.default.homedir(), ".codeam");
23491
+ const credDir = import_path5.default.join(import_os10.default.homedir(), ".codeam");
23257
23492
  await import_promises.default.mkdir(credDir, { recursive: true });
23258
- const credFile = import_path4.default.join(credDir, `tunnel-${creds.TunnelID}.json`);
23493
+ const credFile = import_path5.default.join(credDir, `tunnel-${creds.TunnelID}.json`);
23259
23494
  await import_promises.default.writeFile(credFile, JSON.stringify(creds), { mode: 384 });
23260
- return (0, import_child_process16.spawn)(
23495
+ return (0, import_child_process17.spawn)(
23261
23496
  bin,
23262
23497
  [
23263
23498
  "tunnel",
@@ -23273,17 +23508,17 @@ async function spawnNamedTunnel(bin, token, port) {
23273
23508
  }
23274
23509
 
23275
23510
  // src/services/preview/codespace.ts
23276
- var import_child_process17 = require("child_process");
23511
+ var import_child_process18 = require("child_process");
23277
23512
  var import_util3 = require("util");
23278
- var execFileP5 = (0, import_util3.promisify)(import_child_process17.execFile);
23513
+ var execFileP5 = (0, import_util3.promisify)(import_child_process18.execFile);
23279
23514
 
23280
23515
  // src/services/preview/config-file.ts
23281
23516
  var import_promises3 = __toESM(require("fs/promises"));
23282
- var import_path5 = __toESM(require("path"));
23517
+ var import_path6 = __toESM(require("path"));
23283
23518
  var CONFIG_DIR = ".codeam";
23284
23519
  var CONFIG_FILE = "preview.json";
23285
23520
  function configPath(cwd) {
23286
- return import_path5.default.join(cwd, CONFIG_DIR, CONFIG_FILE);
23521
+ return import_path6.default.join(cwd, CONFIG_DIR, CONFIG_FILE);
23287
23522
  }
23288
23523
  var REQUIRED_FIELDS = [
23289
23524
  "framework",
@@ -23314,7 +23549,7 @@ async function readPreviewConfig(cwd) {
23314
23549
  }
23315
23550
  async function writePreviewConfig(cwd, detection) {
23316
23551
  const filePath = configPath(cwd);
23317
- await import_promises3.default.mkdir(import_path5.default.dirname(filePath), { recursive: true });
23552
+ await import_promises3.default.mkdir(import_path6.default.dirname(filePath), { recursive: true });
23318
23553
  await import_promises3.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
23319
23554
  }
23320
23555
 
@@ -23466,14 +23701,14 @@ async function waitForPortListening(port, opts) {
23466
23701
  }
23467
23702
 
23468
23703
  // src/services/preview/provision-deps.ts
23469
- var import_fs3 = require("fs");
23470
- var import_path6 = __toESM(require("path"));
23704
+ var import_fs4 = require("fs");
23705
+ var import_path7 = __toESM(require("path"));
23471
23706
 
23472
23707
  // src/services/preview/run-setup.ts
23473
- var import_child_process18 = require("child_process");
23708
+ var import_child_process19 = require("child_process");
23474
23709
  function runSetupCommand(cmd, args2, cwd, env, opts) {
23475
23710
  return new Promise((resolve9) => {
23476
- const child = (0, import_child_process18.spawn)(cmd, args2, {
23711
+ const child = (0, import_child_process19.spawn)(cmd, args2, {
23477
23712
  cwd,
23478
23713
  env: { ...process.env, ...env ?? {} },
23479
23714
  stdio: ["ignore", "pipe", "pipe"]
@@ -23614,37 +23849,37 @@ function pickMigrationScript(scripts) {
23614
23849
  }
23615
23850
  async function exists(p2) {
23616
23851
  try {
23617
- await import_fs3.promises.access(p2);
23852
+ await import_fs4.promises.access(p2);
23618
23853
  return true;
23619
23854
  } catch {
23620
23855
  return false;
23621
23856
  }
23622
23857
  }
23623
23858
  async function firstExisting(cwd, names) {
23624
- for (const n of names) if (await exists(import_path6.default.join(cwd, n))) return n;
23859
+ for (const n of names) if (await exists(import_path7.default.join(cwd, n))) return n;
23625
23860
  return null;
23626
23861
  }
23627
23862
  async function ensureEnvFile(cwd, generated) {
23628
- if (await exists(import_path6.default.join(cwd, ".env"))) {
23863
+ if (await exists(import_path7.default.join(cwd, ".env"))) {
23629
23864
  log.info("provision", ".env already present \u2014 leaving it untouched");
23630
23865
  return;
23631
23866
  }
23632
23867
  const sample = await firstExisting(cwd, ENV_SAMPLES);
23633
23868
  if (sample) {
23634
- const body = await import_fs3.promises.readFile(import_path6.default.join(cwd, sample), "utf8");
23635
- await import_fs3.promises.writeFile(import_path6.default.join(cwd, ".env"), body);
23869
+ const body = await import_fs4.promises.readFile(import_path7.default.join(cwd, sample), "utf8");
23870
+ await import_fs4.promises.writeFile(import_path7.default.join(cwd, ".env"), body);
23636
23871
  log.info("provision", `wrote .env from ${sample}`);
23637
23872
  return;
23638
23873
  }
23639
23874
  if (generated.length > 0) {
23640
23875
  const body = "# Generated by codeam \u2014 points at the auto-provisioned local services.\n" + generated.flatMap((s) => s.envLines).join("\n") + "\n";
23641
- await import_fs3.promises.writeFile(import_path6.default.join(cwd, ".env"), body);
23876
+ await import_fs4.promises.writeFile(import_path7.default.join(cwd, ".env"), body);
23642
23877
  log.info("provision", `generated .env for ${generated.map((s) => s.name).join("+")}`);
23643
23878
  }
23644
23879
  }
23645
23880
  async function readPackageJson(cwd) {
23646
23881
  try {
23647
- return JSON.parse(await import_fs3.promises.readFile(import_path6.default.join(cwd, "package.json"), "utf8"));
23882
+ return JSON.parse(await import_fs4.promises.readFile(import_path7.default.join(cwd, "package.json"), "utf8"));
23648
23883
  } catch {
23649
23884
  return null;
23650
23885
  }
@@ -23684,10 +23919,10 @@ async function provisionProjectDependencies(cwd) {
23684
23919
  } else if (pkg) {
23685
23920
  generated = detectServicesFromDeps(pkg);
23686
23921
  if (generated.length > 0) {
23687
- const dir = import_path6.default.join(cwd, ".codeam", "provision");
23688
- await import_fs3.promises.mkdir(dir, { recursive: true });
23689
- const file = import_path6.default.join(dir, "compose.generated.yaml");
23690
- await import_fs3.promises.writeFile(file, renderComposeYaml(generated));
23922
+ const dir = import_path7.default.join(cwd, ".codeam", "provision");
23923
+ await import_fs4.promises.mkdir(dir, { recursive: true });
23924
+ const file = import_path7.default.join(dir, "compose.generated.yaml");
23925
+ await import_fs4.promises.writeFile(file, renderComposeYaml(generated));
23691
23926
  log.info(
23692
23927
  "provision",
23693
23928
  `no compose in repo \u2014 generated ${generated.map((s) => s.name).join("+")}`
@@ -23708,12 +23943,12 @@ async function provisionProjectDependencies(cwd) {
23708
23943
  }
23709
23944
 
23710
23945
  // src/services/preview/setup-deps.ts
23711
- var import_fs4 = __toESM(require("fs"));
23712
- var import_path7 = __toESM(require("path"));
23946
+ var import_fs5 = __toESM(require("fs"));
23947
+ var import_path8 = __toESM(require("path"));
23713
23948
  function detectMissingNodeDeps(cwd) {
23714
- if (!import_fs4.default.existsSync(import_path7.default.join(cwd, "package.json"))) return null;
23715
- if (import_fs4.default.existsSync(import_path7.default.join(cwd, "node_modules"))) return null;
23716
- if (import_fs4.default.existsSync(import_path7.default.join(cwd, "yarn.lock"))) {
23949
+ if (!import_fs5.default.existsSync(import_path8.default.join(cwd, "package.json"))) return null;
23950
+ if (import_fs5.default.existsSync(import_path8.default.join(cwd, "node_modules"))) return null;
23951
+ if (import_fs5.default.existsSync(import_path8.default.join(cwd, "yarn.lock"))) {
23717
23952
  return { cmd: "yarn", args: ["install"] };
23718
23953
  }
23719
23954
  return { cmd: "npm", args: ["install", "--legacy-peer-deps"] };
@@ -23817,20 +24052,20 @@ function activePreviewSessionIds() {
23817
24052
  }
23818
24053
 
23819
24054
  // src/services/preview/start-orchestrator.ts
23820
- var import_child_process20 = require("child_process");
23821
- var fs56 = __toESM(require("fs"));
23822
- var path60 = __toESM(require("path"));
24055
+ var import_child_process21 = require("child_process");
24056
+ var fs57 = __toESM(require("fs"));
24057
+ var path61 = __toESM(require("path"));
23823
24058
  var import_which2 = __toESM(require("which"));
23824
24059
 
23825
24060
  // src/services/project-env/index.ts
23826
- var import_fs5 = require("fs");
23827
- var path59 = __toESM(require("path"));
24061
+ var import_fs6 = require("fs");
24062
+ var path60 = __toESM(require("path"));
23828
24063
 
23829
24064
  // src/beads/project-key.ts
23830
- var import_child_process19 = require("child_process");
24065
+ var import_child_process20 = require("child_process");
23831
24066
  var crypto2 = __toESM(require("crypto"));
23832
- var fs54 = __toESM(require("fs"));
23833
- var path58 = __toESM(require("path"));
24067
+ var fs55 = __toESM(require("fs"));
24068
+ var path59 = __toESM(require("path"));
23834
24069
  function normalizeOrigin(raw) {
23835
24070
  const trimmed = raw.trim();
23836
24071
  if (!trimmed) return null;
@@ -23856,17 +24091,17 @@ function normalizeOrigin(raw) {
23856
24091
  return `${host2}/${pathPart}`;
23857
24092
  }
23858
24093
  function findRepoRoot(cwd) {
23859
- let dir = path58.resolve(cwd);
24094
+ let dir = path59.resolve(cwd);
23860
24095
  const seen = /* @__PURE__ */ new Set();
23861
24096
  for (let i = 0; i < 256; i++) {
23862
24097
  if (seen.has(dir)) return null;
23863
24098
  seen.add(dir);
23864
24099
  try {
23865
- const stat3 = fs54.statSync(path58.join(dir, ".git"), { throwIfNoEntry: false });
24100
+ const stat3 = fs55.statSync(path59.join(dir, ".git"), { throwIfNoEntry: false });
23866
24101
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
23867
24102
  } catch {
23868
24103
  }
23869
- const parent = path58.dirname(dir);
24104
+ const parent = path59.dirname(dir);
23870
24105
  if (parent === dir) return null;
23871
24106
  dir = parent;
23872
24107
  }
@@ -23874,10 +24109,10 @@ function findRepoRoot(cwd) {
23874
24109
  }
23875
24110
  var _execSeam2 = {
23876
24111
  exec: (file, args2, opts) => {
23877
- const out2 = (0, import_child_process19.execFileSync)(file, args2, opts);
24112
+ const out2 = (0, import_child_process20.execFileSync)(file, args2, opts);
23878
24113
  return typeof out2 === "string" ? out2 : out2.toString("utf8");
23879
24114
  },
23880
- realpath: (p2) => fs54.realpathSync(p2)
24115
+ realpath: (p2) => fs55.realpathSync(p2)
23881
24116
  };
23882
24117
  function readOrigin(cwd) {
23883
24118
  try {
@@ -23906,20 +24141,20 @@ function deriveProjectIdentity(cwd = process.cwd()) {
23906
24141
  } catch {
23907
24142
  }
23908
24143
  const hash = crypto2.createHash("sha256").update(real).digest("hex");
23909
- return { projectKey: `path:${hash}`, projectLabel: path58.basename(real) || "project" };
24144
+ return { projectKey: `path:${hash}`, projectLabel: path59.basename(real) || "project" };
23910
24145
  }
23911
24146
 
23912
24147
  // src/services/project-env/index.ts
23913
24148
  async function readIfExists(p2) {
23914
24149
  try {
23915
- return await import_fs5.promises.readFile(p2, "utf8");
24150
+ return await import_fs6.promises.readFile(p2, "utf8");
23916
24151
  } catch {
23917
24152
  return null;
23918
24153
  }
23919
24154
  }
23920
24155
  async function syncProjectEnvUp(cwd, ctx) {
23921
24156
  if (!ctx.pluginAuthToken) return;
23922
- const content = await readIfExists(path59.join(cwd, ".env"));
24157
+ const content = await readIfExists(path60.join(cwd, ".env"));
23923
24158
  if (content == null) return;
23924
24159
  try {
23925
24160
  const { projectKey, projectLabel } = deriveProjectIdentity(cwd);
@@ -23941,7 +24176,7 @@ async function syncProjectEnvUp(cwd, ctx) {
23941
24176
  }
23942
24177
  async function restoreProjectEnvIfMissing(cwd, ctx) {
23943
24178
  if (!ctx.pluginAuthToken) return false;
23944
- const envPath = path59.join(cwd, ".env");
24179
+ const envPath = path60.join(cwd, ".env");
23945
24180
  if (await readIfExists(envPath) != null) return false;
23946
24181
  try {
23947
24182
  const { projectKey, projectLabel } = deriveProjectIdentity(cwd);
@@ -23952,9 +24187,9 @@ async function restoreProjectEnvIfMissing(cwd, ctx) {
23952
24187
  projectKey
23953
24188
  });
23954
24189
  if (!stored) return false;
23955
- const tmp = path59.join(cwd, ".env.codeam-restore.tmp");
23956
- await import_fs5.promises.writeFile(tmp, stored.content, { encoding: "utf8", mode: 384 });
23957
- await import_fs5.promises.rename(tmp, envPath);
24190
+ const tmp = path60.join(cwd, ".env.codeam-restore.tmp");
24191
+ await import_fs6.promises.writeFile(tmp, stored.content, { encoding: "utf8", mode: 384 });
24192
+ await import_fs6.promises.rename(tmp, envPath);
23958
24193
  log.info("project-env", `restored .env (${stored.keyCount} vars) for ${projectLabel}`);
23959
24194
  return true;
23960
24195
  } catch (err) {
@@ -24029,8 +24264,8 @@ function normalizeDetectionForSpawn(detection, cwd) {
24029
24264
  if (args2.length === 0) return detection;
24030
24265
  const binName = args2[0];
24031
24266
  if (binName.startsWith("-")) return detection;
24032
- const binPath = path60.join(cwd, "node_modules", ".bin", binName);
24033
- if (!fs56.existsSync(binPath)) return detection;
24267
+ const binPath = path61.join(cwd, "node_modules", ".bin", binName);
24268
+ if (!fs57.existsSync(binPath)) return detection;
24034
24269
  return {
24035
24270
  ...detection,
24036
24271
  command: binPath,
@@ -24246,7 +24481,7 @@ async function startDevServer(ctx) {
24246
24481
  await applyPreviewHostAllow(cwd);
24247
24482
  const spawnable = normalizeDetectionForSpawn(detection, cwd);
24248
24483
  emitProgress("BOOT_SEQUENCE", `${spawnable.command} ${spawnable.args.join(" ")}`);
24249
- const devServer = (0, import_child_process20.spawn)(spawnable.command, spawnable.args, {
24484
+ const devServer = (0, import_child_process21.spawn)(spawnable.command, spawnable.args, {
24250
24485
  cwd,
24251
24486
  env: { ...process.env, ...spawnable.env ?? {} },
24252
24487
  stdio: ["ignore", "pipe", "pipe"],
@@ -24371,7 +24606,7 @@ async function establishTunnel(ctx, dev) {
24371
24606
  `cloudflared quick tunnel (retry ${attempt}/${MAX_TUNNEL_ATTEMPTS})`
24372
24607
  );
24373
24608
  }
24374
- const candidate = (0, import_child_process20.spawn)(
24609
+ const candidate = (0, import_child_process21.spawn)(
24375
24610
  bin,
24376
24611
  ["tunnel", "--url", `http://localhost:${detection.port}`],
24377
24612
  { stdio: ["ignore", "pipe", "pipe"] }
@@ -24402,10 +24637,10 @@ async function establishTunnel(ctx, dev) {
24402
24637
  }
24403
24638
 
24404
24639
  // src/beads/bd-adapter.ts
24405
- var import_child_process21 = require("child_process");
24406
- var fs57 = __toESM(require("fs"));
24407
- var os46 = __toESM(require("os"));
24408
- var path61 = __toESM(require("path"));
24640
+ var import_child_process22 = require("child_process");
24641
+ var fs58 = __toESM(require("fs"));
24642
+ var os47 = __toESM(require("os"));
24643
+ var path62 = __toESM(require("path"));
24409
24644
  var BD_PACKAGE = "@beads/bd";
24410
24645
  function resolveBundledBdBinary() {
24411
24646
  return _resolveSeam.resolveBundled();
@@ -24417,11 +24652,11 @@ function _defaultResolveBundled() {
24417
24652
  } catch {
24418
24653
  return null;
24419
24654
  }
24420
- const binDir = path61.join(path61.dirname(pkgJsonPath), "bin");
24655
+ const binDir = path62.join(path62.dirname(pkgJsonPath), "bin");
24421
24656
  const binaryName = process.platform === "win32" ? "bd.exe" : "bd";
24422
- const binaryPath = path61.join(binDir, binaryName);
24657
+ const binaryPath = path62.join(binDir, binaryName);
24423
24658
  try {
24424
- fs57.accessSync(binaryPath, fs57.constants.F_OK);
24659
+ fs58.accessSync(binaryPath, fs58.constants.F_OK);
24425
24660
  return binaryPath;
24426
24661
  } catch {
24427
24662
  return null;
@@ -24431,13 +24666,13 @@ function resolveBdOnPath() {
24431
24666
  return _resolveSeam.resolveOnPath();
24432
24667
  }
24433
24668
  function _defaultResolveOnPath() {
24434
- const dirs = (process.env.PATH ?? "").split(path61.delimiter).filter(Boolean);
24669
+ const dirs = (process.env.PATH ?? "").split(path62.delimiter).filter(Boolean);
24435
24670
  const candidates = process.platform === "win32" ? ["bd.exe", "bd.cmd", "bd"] : ["bd"];
24436
24671
  for (const dir of dirs) {
24437
24672
  for (const candidate of candidates) {
24438
- const full = path61.join(dir, candidate);
24673
+ const full = path62.join(dir, candidate);
24439
24674
  try {
24440
- fs57.accessSync(full, fs57.constants.F_OK);
24675
+ fs58.accessSync(full, fs58.constants.F_OK);
24441
24676
  return full;
24442
24677
  } catch {
24443
24678
  }
@@ -24456,7 +24691,7 @@ function _defaultSpawn(binaryPath, args2, opts) {
24456
24691
  return new Promise((resolve9) => {
24457
24692
  let proc;
24458
24693
  try {
24459
- proc = (0, import_child_process21.spawn)(binaryPath, args2, { cwd: opts.cwd, env: opts.env });
24694
+ proc = (0, import_child_process22.spawn)(binaryPath, args2, { cwd: opts.cwd, env: opts.env });
24460
24695
  } catch (err) {
24461
24696
  resolve9({ code: -1, stdout: "", stderr: err.message });
24462
24697
  return;
@@ -24522,7 +24757,7 @@ var BdAdapter = class {
24522
24757
  const env = { ...process.env };
24523
24758
  if (!env.HOME) {
24524
24759
  try {
24525
- const home = os46.homedir();
24760
+ const home = os47.homedir();
24526
24761
  if (home) env.HOME = home;
24527
24762
  } catch {
24528
24763
  }
@@ -24613,13 +24848,13 @@ function coerceIssue(row, projectKey) {
24613
24848
  }
24614
24849
 
24615
24850
  // src/beads/provisioner.ts
24616
- var import_child_process24 = require("child_process");
24617
- var fs59 = __toESM(require("fs"));
24618
- var os48 = __toESM(require("os"));
24619
- var path63 = __toESM(require("path"));
24851
+ var import_child_process25 = require("child_process");
24852
+ var fs60 = __toESM(require("fs"));
24853
+ var os49 = __toESM(require("os"));
24854
+ var path64 = __toESM(require("path"));
24620
24855
 
24621
24856
  // src/beads/install-bd.ts
24622
- var import_child_process22 = require("child_process");
24857
+ var import_child_process23 = require("child_process");
24623
24858
  var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
24624
24859
  var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
24625
24860
  function resolveInstallStrategy(platform3) {
@@ -24650,7 +24885,7 @@ function _defaultInstallSpawn(strategy) {
24650
24885
  return new Promise((resolve9) => {
24651
24886
  let proc;
24652
24887
  try {
24653
- proc = (0, import_child_process22.spawn)(strategy.command, strategy.args, { env: process.env });
24888
+ proc = (0, import_child_process23.spawn)(strategy.command, strategy.args, { env: process.env });
24654
24889
  } catch (err) {
24655
24890
  resolve9({ ok: false, code: -1, stderr: err.message });
24656
24891
  return;
@@ -24680,10 +24915,10 @@ async function installBd(platform3 = process.platform) {
24680
24915
  }
24681
24916
 
24682
24917
  // src/beads/install-dolt.ts
24683
- var import_child_process23 = require("child_process");
24684
- var fs58 = __toESM(require("fs"));
24685
- var os47 = __toESM(require("os"));
24686
- var path62 = __toESM(require("path"));
24918
+ var import_child_process24 = require("child_process");
24919
+ var fs59 = __toESM(require("fs"));
24920
+ var os48 = __toESM(require("os"));
24921
+ var path63 = __toESM(require("path"));
24687
24922
  var DOLT_INSTALL_SH_URL = "https://github.com/dolthub/dolt/releases/latest/download/install.sh";
24688
24923
  var DOLT_MSI_URL = "https://github.com/dolthub/dolt/releases/latest/download/dolt-windows-amd64.msi";
24689
24924
  function resolveDoltInstallStrategy(platform3) {
@@ -24772,14 +25007,14 @@ async function installDoltToDir(targetDir, platform3 = process.platform, arch2 =
24772
25007
  return result;
24773
25008
  }
24774
25009
  var _doltPathSeam = {
24775
- homedir: () => os47.homedir(),
25010
+ homedir: () => os48.homedir(),
24776
25011
  getPath: () => process.env.PATH ?? "",
24777
25012
  setPath: (p2) => {
24778
25013
  process.env.PATH = p2;
24779
25014
  },
24780
25015
  exists: (p2) => {
24781
25016
  try {
24782
- fs58.accessSync(p2, fs58.constants.F_OK);
25017
+ fs59.accessSync(p2, fs59.constants.F_OK);
24783
25018
  return true;
24784
25019
  } catch {
24785
25020
  return false;
@@ -24790,7 +25025,7 @@ function doltBinaryNames(platform3) {
24790
25025
  return platform3 === "win32" ? ["dolt.exe", "dolt.cmd", "dolt"] : ["dolt"];
24791
25026
  }
24792
25027
  function knownDoltDirs(platform3) {
24793
- const P3 = platform3 === "win32" ? path62.win32 : path62.posix;
25028
+ const P3 = platform3 === "win32" ? path63.win32 : path63.posix;
24794
25029
  const home = _doltPathSeam.homedir();
24795
25030
  if (platform3 === "win32") {
24796
25031
  return [
@@ -24806,7 +25041,7 @@ function knownDoltDirs(platform3) {
24806
25041
  ].filter(Boolean);
24807
25042
  }
24808
25043
  function ensureDoltResolvable(platform3 = process.platform) {
24809
- const P3 = platform3 === "win32" ? path62.win32 : path62.posix;
25044
+ const P3 = platform3 === "win32" ? path63.win32 : path63.posix;
24810
25045
  const delim = platform3 === "win32" ? ";" : ":";
24811
25046
  const names = doltBinaryNames(platform3);
24812
25047
  const pathDirs = _doltPathSeam.getPath().split(delim).filter(Boolean);
@@ -24842,7 +25077,7 @@ function _defaultDoltInstallSpawn(strategy) {
24842
25077
  };
24843
25078
  let proc;
24844
25079
  try {
24845
- proc = (0, import_child_process23.spawn)(strategy.command, strategy.args, {
25080
+ proc = (0, import_child_process24.spawn)(strategy.command, strategy.args, {
24846
25081
  env: process.env,
24847
25082
  stdio: ["ignore", "pipe", "pipe"]
24848
25083
  });
@@ -24983,17 +25218,17 @@ var _provisionSeam = {
24983
25218
  };
24984
25219
  var _linkSeam = {
24985
25220
  platform: () => process.platform,
24986
- homedir: () => os48.homedir(),
25221
+ homedir: () => os49.homedir(),
24987
25222
  isWritableDir: (dir) => {
24988
25223
  try {
24989
- fs59.accessSync(dir, fs59.constants.W_OK);
25224
+ fs60.accessSync(dir, fs60.constants.W_OK);
24990
25225
  return true;
24991
25226
  } catch {
24992
25227
  return false;
24993
25228
  }
24994
25229
  },
24995
25230
  ensureDir: (dir) => {
24996
- fs59.mkdirSync(dir, { recursive: true });
25231
+ fs60.mkdirSync(dir, { recursive: true });
24997
25232
  },
24998
25233
  /**
24999
25234
  * A directory to symlink `bd` into so the AGENT's shell + Claude Code's
@@ -25014,9 +25249,9 @@ var _linkSeam = {
25014
25249
  * which `linkBdOntoPath` creates if missing.
25015
25250
  */
25016
25251
  cliBinDir: () => {
25017
- const pathDirs = (process.env.PATH ?? "").split(path63.delimiter).filter(Boolean);
25252
+ const pathDirs = (process.env.PATH ?? "").split(path64.delimiter).filter(Boolean);
25018
25253
  const home = _linkSeam.homedir();
25019
- const localBin = home ? path63.join(home, ".local", "bin") : null;
25254
+ const localBin = home ? path64.join(home, ".local", "bin") : null;
25020
25255
  if (localBin) {
25021
25256
  try {
25022
25257
  _linkSeam.ensureDir(localBin);
@@ -25026,16 +25261,16 @@ var _linkSeam = {
25026
25261
  const candidates = [];
25027
25262
  if (localBin) candidates.push(localBin);
25028
25263
  try {
25029
- candidates.push(path63.dirname(process.execPath));
25264
+ candidates.push(path64.dirname(process.execPath));
25030
25265
  } catch {
25031
25266
  }
25032
25267
  candidates.push("/usr/local/bin");
25033
25268
  const entry = process.argv[1];
25034
25269
  if (entry) {
25035
25270
  try {
25036
- candidates.push(path63.dirname(fs59.realpathSync(entry)));
25271
+ candidates.push(path64.dirname(fs60.realpathSync(entry)));
25037
25272
  } catch {
25038
- candidates.push(path63.dirname(entry));
25273
+ candidates.push(path64.dirname(entry));
25039
25274
  }
25040
25275
  }
25041
25276
  const onPathWritable = candidates.find(
@@ -25047,20 +25282,20 @@ var _linkSeam = {
25047
25282
  /** Current symlink target at `linkPath`, or null when absent / not a link. */
25048
25283
  readlink: (linkPath) => {
25049
25284
  try {
25050
- return fs59.readlinkSync(linkPath);
25285
+ return fs60.readlinkSync(linkPath);
25051
25286
  } catch {
25052
25287
  return null;
25053
25288
  }
25054
25289
  },
25055
- unlink: (linkPath) => fs59.unlinkSync(linkPath),
25056
- symlink: (target, linkPath) => fs59.symlinkSync(target, linkPath)
25290
+ unlink: (linkPath) => fs60.unlinkSync(linkPath),
25291
+ symlink: (target, linkPath) => fs60.symlinkSync(target, linkPath)
25057
25292
  };
25058
25293
  function linkBdOntoPath(binaryPath) {
25059
25294
  if (_linkSeam.platform() === "win32") return;
25060
25295
  const binDir = _linkSeam.cliBinDir();
25061
25296
  if (!binDir) return;
25062
25297
  _linkSeam.ensureDir(binDir);
25063
- const linkPath = path63.join(binDir, "bd");
25298
+ const linkPath = path64.join(binDir, "bd");
25064
25299
  if (linkPath === binaryPath) return;
25065
25300
  const current2 = _linkSeam.readlink(linkPath);
25066
25301
  if (current2 === binaryPath) return;
@@ -25069,7 +25304,7 @@ function linkBdOntoPath(binaryPath) {
25069
25304
  log.info("beads", `linked bd onto PATH: ${linkPath} -> ${binaryPath}`);
25070
25305
  }
25071
25306
  function setGitBeadsRole() {
25072
- (0, import_child_process24.execFileSync)("git", ["config", "--global", "beads.role", "contributor"], {
25307
+ (0, import_child_process25.execFileSync)("git", ["config", "--global", "beads.role", "contributor"], {
25073
25308
  stdio: "ignore"
25074
25309
  });
25075
25310
  }
@@ -25255,7 +25490,7 @@ function dedupeRecipes(agents) {
25255
25490
 
25256
25491
  // src/beads/watcher.ts
25257
25492
  var crypto4 = __toESM(require("crypto"));
25258
- var path64 = __toESM(require("path"));
25493
+ var path65 = __toESM(require("path"));
25259
25494
  var API_BASE6 = resolveApiBaseUrl();
25260
25495
  var DEBOUNCE_MS2 = 400;
25261
25496
  var ZERO_SUMMARY = {
@@ -25279,7 +25514,7 @@ var BeadsWatcher = class {
25279
25514
  constructor(opts) {
25280
25515
  this.opts = opts;
25281
25516
  this.bd = opts.adapter ?? new BdAdapter({ cwd: opts.cwd, beadsDir: opts.beadsDir });
25282
- this.feedPath = opts.feedPath ?? path64.join(opts.cwd ?? process.cwd(), ".beads", "last-touched");
25517
+ this.feedPath = opts.feedPath ?? path65.join(opts.cwd ?? process.cwd(), ".beads", "last-touched");
25283
25518
  this.apiBase = opts.apiBaseUrl ?? API_BASE6;
25284
25519
  }
25285
25520
  opts;
@@ -25778,8 +26013,8 @@ function cleanupAttachmentTempFiles() {
25778
26013
  function saveFilesTemp(files) {
25779
26014
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
25780
26015
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
25781
- const tmpPath = path67.join(os52.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
25782
- fs62.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
26016
+ const tmpPath = path68.join(os53.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
26017
+ fs63.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
25783
26018
  pendingAttachmentFiles.add(tmpPath);
25784
26019
  return tmpPath;
25785
26020
  });
@@ -25924,7 +26159,7 @@ var sessionTerminated = async (ctx, cmd) => {
25924
26159
  }
25925
26160
  quiet(() => ctx.agent.kill());
25926
26161
  try {
25927
- const proc = (0, import_child_process25.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
26162
+ const proc = (0, import_child_process26.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
25928
26163
  detached: true,
25929
26164
  stdio: "ignore"
25930
26165
  });
@@ -25943,7 +26178,7 @@ var shutdownSession = async (ctx, cmd) => {
25943
26178
  quiet(() => ctx.agent.kill());
25944
26179
  if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
25945
26180
  try {
25946
- const stopProc = (0, import_child_process25.spawn)(
26181
+ const stopProc = (0, import_child_process26.spawn)(
25947
26182
  "bash",
25948
26183
  ["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
25949
26184
  { detached: true, stdio: "ignore" }
@@ -25953,7 +26188,7 @@ var shutdownSession = async (ctx, cmd) => {
25953
26188
  }
25954
26189
  }
25955
26190
  try {
25956
- const proc = (0, import_child_process25.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
26191
+ const proc = (0, import_child_process26.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
25957
26192
  detached: true,
25958
26193
  stdio: "ignore"
25959
26194
  });
@@ -25997,10 +26232,10 @@ var listFiles = async (ctx, cmd, parsed) => {
25997
26232
  await ctx.relay.sendResult(cmd.id, "completed", result);
25998
26233
  };
25999
26234
  var envReadH = async (ctx, cmd) => {
26000
- const envPath = path67.join(process.cwd(), ".env");
26235
+ const envPath = path68.join(process.cwd(), ".env");
26001
26236
  await restoreProjectEnvIfMissing(process.cwd(), ctx);
26002
26237
  try {
26003
- const raw = await fs62.promises.readFile(envPath, "utf8");
26238
+ const raw = await fs63.promises.readFile(envPath, "utf8");
26004
26239
  await ctx.relay.sendResult(cmd.id, "completed", {
26005
26240
  exists: true,
26006
26241
  vars: parseDotenv(raw)
@@ -26031,15 +26266,15 @@ var envWriteH = async (ctx, cmd, parsed) => {
26031
26266
  }
26032
26267
  seen.add(v.key);
26033
26268
  }
26034
- const envPath = path67.join(process.cwd(), ".env");
26035
- const tmpPath = path67.join(process.cwd(), ".env.codeam.tmp");
26269
+ const envPath = path68.join(process.cwd(), ".env");
26270
+ const tmpPath = path68.join(process.cwd(), ".env.codeam.tmp");
26036
26271
  try {
26037
- await fs62.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
26038
- await fs62.promises.rename(tmpPath, envPath);
26272
+ await fs63.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
26273
+ await fs63.promises.rename(tmpPath, envPath);
26039
26274
  await ctx.relay.sendResult(cmd.id, "completed", { ok: true, count: vars.length });
26040
26275
  void syncProjectEnvUp(process.cwd(), ctx);
26041
26276
  } catch (err) {
26042
- await fs62.promises.rm(tmpPath, { force: true }).catch(() => void 0);
26277
+ await fs63.promises.rm(tmpPath, { force: true }).catch(() => void 0);
26043
26278
  await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
26044
26279
  }
26045
26280
  };
@@ -26088,7 +26323,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
26088
26323
  let configuredAgent = rawAgentId;
26089
26324
  if (!configuredAgent) {
26090
26325
  try {
26091
- const raw = JSON.parse(fs62.readFileSync(headroomConfigPath(), "utf8"));
26326
+ const raw = JSON.parse(fs63.readFileSync(headroomConfigPath(), "utf8"));
26092
26327
  configuredAgent = raw.agent ?? "";
26093
26328
  } catch {
26094
26329
  }
@@ -26122,7 +26357,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
26122
26357
  persist: persistHeadroomConfig,
26123
26358
  readEnabled: () => {
26124
26359
  try {
26125
- const raw = JSON.parse(fs62.readFileSync(headroomConfigPath(), "utf8"));
26360
+ const raw = JSON.parse(fs63.readFileSync(headroomConfigPath(), "utf8"));
26126
26361
  return raw.enabled === true;
26127
26362
  } catch {
26128
26363
  return false;
@@ -26444,7 +26679,7 @@ var headroomBudgetH = async (ctx, cmd) => {
26444
26679
  }
26445
26680
  let headroomActive = false;
26446
26681
  try {
26447
- const raw = JSON.parse(fs62.readFileSync(headroomConfigPath(), "utf8"));
26682
+ const raw = JSON.parse(fs63.readFileSync(headroomConfigPath(), "utf8"));
26448
26683
  headroomActive = raw.enabled === true;
26449
26684
  } catch {
26450
26685
  }
@@ -26454,7 +26689,7 @@ var headroomBudgetH = async (ctx, cmd) => {
26454
26689
  }
26455
26690
  let existingConfig = { enabled: true };
26456
26691
  try {
26457
- existingConfig = JSON.parse(fs62.readFileSync(headroomConfigPath(), "utf8"));
26692
+ existingConfig = JSON.parse(fs63.readFileSync(headroomConfigPath(), "utf8"));
26458
26693
  } catch {
26459
26694
  }
26460
26695
  if (payload.budgetEnabled && payload.budgetUsd != null) {
@@ -26558,7 +26793,7 @@ var defaultCliUpdateDeps = {
26558
26793
  // Only a truly local `codeam start` (neither marker) re-execs in place below.
26559
26794
  isSupervised: () => process.env["CODEAM_AUTO_APPROVE"] === "1" || process.env["CODESPACES"] === "true",
26560
26795
  relaunch: (args2) => {
26561
- const child = (0, import_child_process25.spawnSync)("codeam", args2, { stdio: "inherit", env: process.env });
26796
+ const child = (0, import_child_process26.spawnSync)("codeam", args2, { stdio: "inherit", env: process.env });
26562
26797
  process.exit(child.status ?? 0);
26563
26798
  }
26564
26799
  };
@@ -26567,9 +26802,9 @@ var CLI_UPDATE_MAX_ATTEMPTS = 3;
26567
26802
  function buildNpmInstallInvocation(opts) {
26568
26803
  const entryScript = opts?.entryScript ?? process.argv[1] ?? "";
26569
26804
  const execPath = opts?.execPath ?? process.execPath;
26570
- const exists2 = opts?.existsSync ?? fs62.existsSync;
26805
+ const exists2 = opts?.existsSync ?? fs63.existsSync;
26571
26806
  const platform3 = opts?.platform ?? process.platform;
26572
- const p2 = platform3 === "win32" ? path67.win32 : path67.posix;
26807
+ const p2 = platform3 === "win32" ? path68.win32 : path68.posix;
26573
26808
  const normalized = entryScript.split(/[\\/]/).join("/");
26574
26809
  const marker = "/lib/node_modules/codeam-cli/";
26575
26810
  const markerIdx = normalized.indexOf(marker);
@@ -26591,7 +26826,7 @@ function isPermissionError(stderr) {
26591
26826
  function resolveGlobalNodeModulesDir(opts) {
26592
26827
  const entryScript = opts?.entryScript ?? process.argv[1] ?? "";
26593
26828
  const platform3 = opts?.platform ?? process.platform;
26594
- const p2 = platform3 === "win32" ? path67.win32 : path67.posix;
26829
+ const p2 = platform3 === "win32" ? path68.win32 : path68.posix;
26595
26830
  const marker = "/lib/node_modules/codeam-cli/";
26596
26831
  const markerIdx = entryScript.split(/[\\/]/).join("/").indexOf(marker);
26597
26832
  if (markerIdx <= 0) return null;
@@ -26601,9 +26836,9 @@ function resolveGlobalNodeModulesDir(opts) {
26601
26836
  var STALE_STAGING_AGE_MS = CLI_UPDATE_INSTALL_TIMEOUT_MS;
26602
26837
  function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
26603
26838
  if (!nodeModulesDir) return 0;
26604
- const readdirSync15 = deps?.readdirSync ?? fs62.readdirSync;
26605
- const statSync17 = deps?.statSync ?? fs62.statSync;
26606
- const rmSync9 = deps?.rmSync ?? fs62.rmSync;
26839
+ const readdirSync15 = deps?.readdirSync ?? fs63.readdirSync;
26840
+ const statSync17 = deps?.statSync ?? fs63.statSync;
26841
+ const rmSync9 = deps?.rmSync ?? fs63.rmSync;
26607
26842
  let removed = 0;
26608
26843
  let entries;
26609
26844
  try {
@@ -26613,7 +26848,7 @@ function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
26613
26848
  }
26614
26849
  for (const name of entries) {
26615
26850
  if (!/^\.codeam-cli-/.test(name)) continue;
26616
- const full = path67.join(nodeModulesDir, name);
26851
+ const full = path68.join(nodeModulesDir, name);
26617
26852
  try {
26618
26853
  const st3 = statSync17(full);
26619
26854
  if (now - st3.mtimeMs < STALE_STAGING_AGE_MS) continue;
@@ -26639,7 +26874,7 @@ async function runNpmInstallLatest() {
26639
26874
  const useShell = process.platform === "win32";
26640
26875
  const command2 = useShell && invocation.command.includes(" ") ? `"${invocation.command}"` : invocation.command;
26641
26876
  const result = await new Promise((resolve9) => {
26642
- (0, import_child_process25.execFile)(
26877
+ (0, import_child_process26.execFile)(
26643
26878
  command2,
26644
26879
  invocation.args,
26645
26880
  { timeout: CLI_UPDATE_INSTALL_TIMEOUT_MS, shell: useShell },
@@ -27253,13 +27488,13 @@ async function dispatchCommand(ctx, cmd) {
27253
27488
  }
27254
27489
 
27255
27490
  // src/commands/start/keep-alive.ts
27256
- var import_child_process26 = require("child_process");
27491
+ var import_child_process27 = require("child_process");
27257
27492
  function buildKeepAlive(ctx) {
27258
27493
  let timer = null;
27259
27494
  async function setIdleTimeout(minutes) {
27260
27495
  if (!ctx.inCodespace || !ctx.codespaceName) return;
27261
27496
  await new Promise((resolve9) => {
27262
- const proc = (0, import_child_process26.spawn)(
27497
+ const proc = (0, import_child_process27.spawn)(
27263
27498
  "gh",
27264
27499
  [
27265
27500
  "api",
@@ -27493,7 +27728,7 @@ function resolveTokenValue(args2) {
27493
27728
  if (fileFlag) {
27494
27729
  const path91 = fileFlag.slice("--token-file=".length);
27495
27730
  try {
27496
- const content = fs63.readFileSync(path91, "utf8").trim();
27731
+ const content = fs64.readFileSync(path91, "utf8").trim();
27497
27732
  if (content.length === 0) fail(`--token-file ${path91} is empty`);
27498
27733
  rmIfExistsQuiet(path91);
27499
27734
  return content;
@@ -27524,7 +27759,7 @@ async function claimOnce(token, pluginId, pluginSecretHash) {
27524
27759
  pluginId,
27525
27760
  ideName: "codeam-cli (codespace)",
27526
27761
  ideVersion: process.env.npm_package_version ?? "unknown",
27527
- hostname: os53.hostname(),
27762
+ hostname: os54.hostname(),
27528
27763
  codespaceName: process.env.CODESPACE_NAME ?? "",
27529
27764
  // Current git branch of the codespace's working directory, so the
27530
27765
  // backend can populate `PairedSession.branch` for the codespace pair.
@@ -27585,7 +27820,7 @@ async function claim(token, pluginId, pluginSecretHash) {
27585
27820
  }
27586
27821
  }
27587
27822
  function pairAutoLockPath() {
27588
- return path68.join(os53.homedir(), ".codeam", "pair-auto.lock");
27823
+ return path69.join(os54.homedir(), ".codeam", "pair-auto.lock");
27589
27824
  }
27590
27825
  function isLivePairAuto(pid) {
27591
27826
  if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
@@ -27595,7 +27830,7 @@ function isLivePairAuto(pid) {
27595
27830
  if (e.code !== "EPERM") return false;
27596
27831
  }
27597
27832
  try {
27598
- return fs63.readFileSync(`/proc/${pid}/cmdline`, "utf8").includes("codeam");
27833
+ return fs64.readFileSync(`/proc/${pid}/cmdline`, "utf8").includes("codeam");
27599
27834
  } catch {
27600
27835
  return true;
27601
27836
  }
@@ -27605,24 +27840,24 @@ function isLiveCodeam(pid) {
27605
27840
  }
27606
27841
  function daemonLockPath(sessionId) {
27607
27842
  const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
27608
- return path68.join(os53.homedir(), ".codeam", `daemon-${safe}.lock`);
27843
+ return path69.join(os54.homedir(), ".codeam", `daemon-${safe}.lock`);
27609
27844
  }
27610
27845
  function acquireDaemonLock(sessionId) {
27611
27846
  const lockPath = daemonLockPath(sessionId);
27612
27847
  try {
27613
- fs63.mkdirSync(path68.dirname(lockPath), { recursive: true });
27848
+ fs64.mkdirSync(path69.dirname(lockPath), { recursive: true });
27614
27849
  try {
27615
- fs63.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
27850
+ fs64.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
27616
27851
  } catch (e) {
27617
27852
  if (e.code !== "EEXIST") throw e;
27618
- const holder = Number(fs63.readFileSync(lockPath, "utf8").trim());
27853
+ const holder = Number(fs64.readFileSync(lockPath, "utf8").trim());
27619
27854
  if (holder && holder !== process.pid && isLiveCodeam(holder)) return false;
27620
- fs63.writeFileSync(lockPath, String(process.pid));
27855
+ fs64.writeFileSync(lockPath, String(process.pid));
27621
27856
  }
27622
27857
  const release3 = () => {
27623
27858
  try {
27624
- if (fs63.existsSync(lockPath) && Number(fs63.readFileSync(lockPath, "utf8").trim()) === process.pid) {
27625
- fs63.unlinkSync(lockPath);
27859
+ if (fs64.existsSync(lockPath) && Number(fs64.readFileSync(lockPath, "utf8").trim()) === process.pid) {
27860
+ fs64.unlinkSync(lockPath);
27626
27861
  }
27627
27862
  } catch {
27628
27863
  }
@@ -27644,19 +27879,19 @@ function acquireDaemonLock(sessionId) {
27644
27879
  function acquireSingletonLock() {
27645
27880
  const lockPath = pairAutoLockPath();
27646
27881
  try {
27647
- fs63.mkdirSync(path68.dirname(lockPath), { recursive: true });
27882
+ fs64.mkdirSync(path69.dirname(lockPath), { recursive: true });
27648
27883
  try {
27649
- fs63.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
27884
+ fs64.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
27650
27885
  } catch (e) {
27651
27886
  if (e.code !== "EEXIST") throw e;
27652
- const holder = Number(fs63.readFileSync(lockPath, "utf8").trim());
27887
+ const holder = Number(fs64.readFileSync(lockPath, "utf8").trim());
27653
27888
  if (isLivePairAuto(holder)) return false;
27654
- fs63.writeFileSync(lockPath, String(process.pid));
27889
+ fs64.writeFileSync(lockPath, String(process.pid));
27655
27890
  }
27656
27891
  process.once("exit", () => {
27657
27892
  try {
27658
- if (fs63.existsSync(lockPath) && Number(fs63.readFileSync(lockPath, "utf8").trim()) === process.pid) {
27659
- fs63.unlinkSync(lockPath);
27893
+ if (fs64.existsSync(lockPath) && Number(fs64.readFileSync(lockPath, "utf8").trim()) === process.pid) {
27894
+ fs64.unlinkSync(lockPath);
27660
27895
  }
27661
27896
  } catch {
27662
27897
  }
@@ -28110,178 +28345,6 @@ var AgentService = class _AgentService {
28110
28345
  // src/agents/acp/adapters.ts
28111
28346
  var path70 = __toESM(require("path"));
28112
28347
 
28113
- // src/agents/acp/agent-binary.ts
28114
- var import_fs6 = __toESM(require("fs"));
28115
- var import_os11 = __toESM(require("os"));
28116
- var import_path8 = __toESM(require("path"));
28117
- var import_child_process27 = require("child_process");
28118
- function currentPlatformKey() {
28119
- return `${process.platform}-${process.arch}`;
28120
- }
28121
- var SDK_PKG = "@anthropic-ai/claude-agent-sdk";
28122
- function resolveSdkDirViaRequire(req = require) {
28123
- try {
28124
- return import_path8.default.dirname(req.resolve(`${SDK_PKG}/package.json`));
28125
- } catch {
28126
- }
28127
- try {
28128
- let dir = import_path8.default.dirname(req.resolve(SDK_PKG));
28129
- for (let i = 0; i < 5; i++) {
28130
- if (import_path8.default.basename(dir) === "claude-agent-sdk") return dir;
28131
- const parent = import_path8.default.dirname(dir);
28132
- if (parent === dir) break;
28133
- dir = parent;
28134
- }
28135
- return null;
28136
- } catch {
28137
- return null;
28138
- }
28139
- }
28140
- function defaultSdkDir() {
28141
- return resolveSdkDirViaRequire();
28142
- }
28143
- function resolveClaudeNativeBinary(deps = {}) {
28144
- const existsSync29 = deps.existsSync ?? import_fs6.default.existsSync;
28145
- const platformKey = deps.platformKey ?? currentPlatformKey();
28146
- const sdkDir = deps.sdkDir !== void 0 ? deps.sdkDir : defaultSdkDir();
28147
- if (!sdkDir) return null;
28148
- const scopeDir = import_path8.default.dirname(sdkDir);
28149
- const binName = platformKey.startsWith("win32-") ? "claude.exe" : "claude";
28150
- const candidate = import_path8.default.join(scopeDir, `claude-agent-sdk-${platformKey}`, binName);
28151
- return existsSync29(candidate) ? candidate : null;
28152
- }
28153
- var realSleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
28154
- async function waitForClaudeNativeBinary(opts = {}) {
28155
- const timeoutMs = opts.timeoutMs ?? 18e4;
28156
- const pollMs = opts.pollMs ?? 500;
28157
- const now = opts.now ?? Date.now;
28158
- const sleep5 = opts.sleep ?? realSleep;
28159
- const deps = {
28160
- sdkDir: opts.sdkDir,
28161
- platformKey: opts.platformKey,
28162
- existsSync: opts.existsSync
28163
- };
28164
- const deadline = now() + timeoutMs;
28165
- let found = resolveClaudeNativeBinary(deps);
28166
- if (found) return found;
28167
- while (now() < deadline) {
28168
- await sleep5(pollMs);
28169
- found = resolveClaudeNativeBinary(deps);
28170
- if (found) return found;
28171
- }
28172
- return resolveClaudeNativeBinary(deps);
28173
- }
28174
- function isCommandOnPath(cmd, probe = defaultWhich) {
28175
- return probe(cmd);
28176
- }
28177
- function defaultWhich(cmd) {
28178
- const finder = process.platform === "win32" ? "where" : "which";
28179
- try {
28180
- const res = (0, import_child_process27.spawnSync)(finder, [cmd], { stdio: "ignore" });
28181
- return res.status === 0;
28182
- } catch {
28183
- return false;
28184
- }
28185
- }
28186
- async function waitForCommandOnPath(cmd, opts = {}) {
28187
- const timeoutMs = opts.timeoutMs ?? 18e4;
28188
- const pollMs = opts.pollMs ?? 500;
28189
- const now = opts.now ?? Date.now;
28190
- const sleep5 = opts.sleep ?? realSleep;
28191
- const probe = opts.probe;
28192
- const check = () => isCommandOnPath(cmd, probe);
28193
- const deadline = now() + timeoutMs;
28194
- if (check()) return true;
28195
- while (now() < deadline) {
28196
- await sleep5(pollMs);
28197
- if (check()) return true;
28198
- }
28199
- return check();
28200
- }
28201
- function resolveCursorAgentBinary(deps = {}) {
28202
- const existsSync29 = deps.existsSync ?? import_fs6.default.existsSync;
28203
- const platform3 = deps.platform ?? process.platform;
28204
- const env = deps.env ?? process.env;
28205
- if (platform3 === "win32") {
28206
- const localAppData = env.LOCALAPPDATA;
28207
- if (!localAppData) return null;
28208
- const exe = import_path8.default.win32.join(localAppData, "cursor-agent", "cursor-agent.exe");
28209
- return existsSync29(exe) ? exe : null;
28210
- }
28211
- const home = deps.homedir ?? import_os11.default.homedir();
28212
- const unix = import_path8.default.posix.join(home, ".local", "bin", "cursor-agent");
28213
- return existsSync29(unix) ? unix : null;
28214
- }
28215
- async function waitForCursorAgent(opts = {}) {
28216
- const timeoutMs = opts.timeoutMs ?? 18e4;
28217
- const pollMs = opts.pollMs ?? 500;
28218
- const now = opts.now ?? Date.now;
28219
- const sleep5 = opts.sleep ?? realSleep;
28220
- const check = () => resolveCursorAgentBinary(opts) !== null || isCommandOnPath("cursor-agent", opts.probe);
28221
- const deadline = now() + timeoutMs;
28222
- if (check()) return true;
28223
- while (now() < deadline) {
28224
- await sleep5(pollMs);
28225
- if (check()) return true;
28226
- }
28227
- return check();
28228
- }
28229
- var ADAPTER_MODULE_LOAD_ERROR_RE = /ERR_MODULE_NOT_FOUND|Cannot find module|ERR_REQUIRE_ESM|ERR_UNKNOWN_BUILTIN_MODULE|ERR_UNSUPPORTED_DIR_IMPORT|ERR_PACKAGE_PATH_NOT_EXPORTED|ERR_INVALID_PACKAGE_CONFIG|ERR_INVALID_PACKAGE_TARGET|ERR_INVALID_MODULE_SPECIFIER|node:internal\/modules\/|Directory import|SyntaxError|Unexpected (token|end|identifier)|missing \) after argument list/i;
28230
- var PERMANENT_ADAPTER_STARTUP_RE = /IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist|not eligible for Gemini Code Assist|Error authenticating|ProjectIdRequiredError/i;
28231
- function probeAdapterModuleGraph(command2, args2, opts = {}) {
28232
- const livenessMs = opts.livenessMs ?? 400;
28233
- const spawnFn = opts.spawnFn ?? import_child_process27.spawn;
28234
- return new Promise((resolve9) => {
28235
- let settled = false;
28236
- let stderr = "";
28237
- let timer;
28238
- let child;
28239
- const finish = (r) => {
28240
- if (settled) return;
28241
- settled = true;
28242
- if (timer) clearTimeout(timer);
28243
- try {
28244
- child?.kill("SIGKILL");
28245
- } catch {
28246
- }
28247
- resolve9(r);
28248
- };
28249
- try {
28250
- child = spawnFn(command2, [...args2], { stdio: ["pipe", "ignore", "pipe"] });
28251
- } catch {
28252
- resolve9("ok");
28253
- return;
28254
- }
28255
- child.stderr?.on("data", (d3) => {
28256
- stderr += d3.toString();
28257
- if (stderr.length > 8192) stderr = stderr.slice(-8192);
28258
- });
28259
- child.on("error", () => finish("ok"));
28260
- child.on("exit", (code) => {
28261
- if (code !== 0 && code !== null && !PERMANENT_ADAPTER_STARTUP_RE.test(stderr)) {
28262
- finish("transient");
28263
- } else {
28264
- finish("ok");
28265
- }
28266
- });
28267
- timer = setTimeout(() => finish("ok"), livenessMs);
28268
- });
28269
- }
28270
- async function waitForAdapterModuleGraph(command2, args2, opts = {}) {
28271
- const timeoutMs = opts.timeoutMs ?? 18e4;
28272
- const pollMs = opts.pollMs ?? 500;
28273
- const now = opts.now ?? Date.now;
28274
- const sleep5 = opts.sleep ?? realSleep;
28275
- const probeOpts = { livenessMs: opts.livenessMs, spawnFn: opts.spawnFn };
28276
- const deadline = now() + timeoutMs;
28277
- if (await probeAdapterModuleGraph(command2, args2, probeOpts) === "ok") return true;
28278
- while (now() < deadline) {
28279
- await sleep5(pollMs);
28280
- if (await probeAdapterModuleGraph(command2, args2, probeOpts) === "ok") return true;
28281
- }
28282
- return false;
28283
- }
28284
-
28285
28348
  // src/agents/kimi/installer.ts
28286
28349
  var import_node_child_process27 = require("child_process");
28287
28350
  var import_node_os10 = require("os");
@@ -28430,7 +28493,10 @@ var REGISTRY = {
28430
28493
  args: [bin],
28431
28494
  requiresAgentBinary: "codex",
28432
28495
  // codex ships via `npm install -g @openai/codex` → PATH binary.
28433
- waitForBinary: (o) => waitForCommandOnPath("codex", o)
28496
+ waitForBinary: (o) => {
28497
+ augmentUserLocalBinPaths();
28498
+ return waitForCommandOnPath("codex", o);
28499
+ }
28434
28500
  };
28435
28501
  },
28436
28502
  // Cursor speaks ACP NATIVELY via `cursor-agent acp` ("Start the Cursor
@@ -28456,7 +28522,30 @@ var REGISTRY = {
28456
28522
  command: resolveCursorAgentBinary() ?? "cursor-agent",
28457
28523
  args: ["acp"],
28458
28524
  requiresAgentBinary: "cursor-agent",
28459
- waitForBinary: (o) => waitForCursorAgent(o)
28525
+ // Stale-PATH guard — same class as codex/gemini above, kept here so the
28526
+ // PROBE and the SPAWN agree about what "ready" means.
28527
+ //
28528
+ // On an in-session switch the adapter spec is resolved BEFORE the install
28529
+ // (`ensureAgentBinaryForSwitch`'s first step), so with no cursor-agent on
28530
+ // disk yet `resolveCursorAgentBinary()` returns null and the spec is
28531
+ // PERMANENTLY CACHED with the bare name `cursor-agent`. The installer then
28532
+ // drops the binary in ~/.local/bin, which a systemd-minimal PATH lacks,
28533
+ // yet `waitForCursorAgent` still reports READY (it probes the absolute
28534
+ // install location).
28535
+ //
28536
+ // ⚠️ Scope, honestly: this is defence in depth, NOT a live outage fix.
28537
+ // `AcpClient.start` already spawns with `expandPathForAgentBinaries(...)`
28538
+ // (client.ts), which folds the same dirs in, so the production ACP spawn
28539
+ // very likely resolved anyway. What was genuinely wrong is that a probe
28540
+ // could report ready while a *plain* spawn from the same process could not
28541
+ // find the binary — the real-install gate
28542
+ // (`__tests__/integration/agent-install.int.test.ts`) spawns without that
28543
+ // expansion and caught the divergence. Aligning the probe with codex/gemini
28544
+ // costs one idempotent call and removes the discrepancy.
28545
+ waitForBinary: (o) => {
28546
+ augmentUserLocalBinPaths();
28547
+ return waitForCursorAgent(o);
28548
+ }
28460
28549
  }),
28461
28550
  // Gemini speaks ACP natively via `gemini --acp` — no npm adapter
28462
28551
  // package, just the user-installed `gemini` binary on PATH. Same
@@ -28474,7 +28563,12 @@ var REGISTRY = {
28474
28563
  // codeam was launched from.
28475
28564
  args: ["--skip-trust", "--acp"],
28476
28565
  requiresAgentBinary: "gemini",
28477
- waitForBinary: (o) => waitForCommandOnPath("gemini", o)
28566
+ // gemini ships via `npm install -g @google/gemini-cli` → PATH binary,
28567
+ // same stale-PATH exposure as codex above.
28568
+ waitForBinary: (o) => {
28569
+ augmentUserLocalBinPaths();
28570
+ return waitForCommandOnPath("gemini", o);
28571
+ }
28478
28572
  }),
28479
28573
  // Kimi Code (Moonshot) speaks ACP natively via `kimi acp` — a stdio
28480
28574
  // JSON-RPC server that answers `initialize` (agentInfo `Kimi Code CLI`,
@@ -28558,12 +28652,292 @@ async function resolveAcpAdapterWithRetry(agent, opts = {}) {
28558
28652
  var import_node_crypto11 = require("crypto");
28559
28653
 
28560
28654
  // src/services/history.service.ts
28561
- var fs65 = __toESM(require("fs"));
28562
- var path71 = __toESM(require("path"));
28563
- var os55 = __toESM(require("os"));
28655
+ var fs66 = __toESM(require("fs"));
28656
+ var path72 = __toESM(require("path"));
28657
+ var os56 = __toESM(require("os"));
28564
28658
  var https7 = __toESM(require("https"));
28565
28659
  var http6 = __toESM(require("http"));
28566
28660
  var import_zod2 = require("zod");
28661
+
28662
+ // src/agents/acp/squad-roster.ts
28663
+ var fs65 = __toESM(require("fs"));
28664
+ var os55 = __toESM(require("os"));
28665
+ var path71 = __toESM(require("path"));
28666
+ var PROMPT_MAX = 500;
28667
+ var REPLY_SUMMARY_MAX = 1e3;
28668
+ var PREAMBLE_MAX = 2e3;
28669
+ var BRIEFING_DEFAULT_MAX = 8e3;
28670
+ var GENERIC_SPECIALTY = "general implementation tasks";
28671
+ function clip(s, max) {
28672
+ return s.length > max ? s.slice(0, max) : s;
28673
+ }
28674
+ function defaultMember() {
28675
+ return {
28676
+ acpSessionId: null,
28677
+ provisioned: false,
28678
+ binaryVerified: false,
28679
+ lastTurnIndex: 0,
28680
+ contextTextFallback: false
28681
+ };
28682
+ }
28683
+ function journalPathFor(homeDir2, sessionId) {
28684
+ return path71.join(homeDir2, ".codeam", `squad-journal-${sessionId}.json`);
28685
+ }
28686
+ function loadJournal(journalPath) {
28687
+ try {
28688
+ const raw = JSON.parse(fs65.readFileSync(journalPath, "utf-8"));
28689
+ return Array.isArray(raw.turns) ? raw.turns : [];
28690
+ } catch {
28691
+ return [];
28692
+ }
28693
+ }
28694
+ var SquadState = class {
28695
+ /** Set by the caller after fetchSquadRoster; null until then. */
28696
+ roster = null;
28697
+ /**
28698
+ * Autonomous chained handoffs (P2-2, PRO). Seeded from the persisted
28699
+ * `SavedSession.squadAuto` at session start and rewritten by
28700
+ * `squad_configure`. Read-only to callers — mutate via {@link setAuto}, which
28701
+ * clamps the budget and re-arms the chain.
28702
+ */
28703
+ autoConfig = { enabled: false, hopBudget: SQUAD_HOP_BUDGET_DEFAULT };
28704
+ /**
28705
+ * Hops left in the CURRENT chain. Reset to the budget on every USER-initiated
28706
+ * prompt (so a user turn always interrupts and re-arms) and decremented by
28707
+ * each self-accepted handoff.
28708
+ */
28709
+ hopsLeft = 0;
28710
+ /** Lifetime handoff counters for `squad_stats` (in-memory, this process). */
28711
+ handoffCounters = { proposed: 0, accepted: 0, auto: 0 };
28712
+ journalPath;
28713
+ turns;
28714
+ members = /* @__PURE__ */ new Map();
28715
+ constructor(opts) {
28716
+ this.journalPath = journalPathFor(opts.homeDir ?? os55.homedir(), opts.sessionId);
28717
+ this.turns = loadJournal(this.journalPath);
28718
+ if (opts.auto) this.setAuto(opts.auto);
28719
+ }
28720
+ get auto() {
28721
+ return this.autoConfig;
28722
+ }
28723
+ /** Apply a new mode (clamping the budget) and re-arm the chain. */
28724
+ setAuto(value) {
28725
+ this.autoConfig = {
28726
+ enabled: value.enabled === true,
28727
+ hopBudget: clampHopBudget(value.hopBudget)
28728
+ };
28729
+ this.resetHops();
28730
+ return this.autoConfig;
28731
+ }
28732
+ hopsRemaining() {
28733
+ return this.hopsLeft;
28734
+ }
28735
+ /** Re-arm the chain — called at the start of every USER-initiated turn. */
28736
+ resetHops() {
28737
+ this.hopsLeft = this.autoConfig.enabled ? this.autoConfig.hopBudget : 0;
28738
+ }
28739
+ /** Spend one hop on a self-accepted handoff. */
28740
+ consumeHop() {
28741
+ if (this.hopsLeft > 0) this.hopsLeft -= 1;
28742
+ }
28743
+ countProposal(opts) {
28744
+ this.handoffCounters.proposed += 1;
28745
+ if (opts.auto) this.handoffCounters.auto += 1;
28746
+ }
28747
+ countAccepted() {
28748
+ this.handoffCounters.accepted += 1;
28749
+ }
28750
+ /**
28751
+ * Per-member activity for the `squad_stats` relay command, derived from the
28752
+ * journal (the same shared history the delta briefing reads) plus the
28753
+ * in-memory handoff counters. `filesTouched` counts DISTINCT paths — a member
28754
+ * that edited the same file across three turns touched ONE file.
28755
+ */
28756
+ stats() {
28757
+ const byAgent = /* @__PURE__ */ new Map();
28758
+ for (const t2 of this.turns) {
28759
+ let row = byAgent.get(t2.agentId);
28760
+ if (!row) {
28761
+ row = { turns: 0, files: /* @__PURE__ */ new Set() };
28762
+ byAgent.set(t2.agentId, row);
28763
+ }
28764
+ row.turns += 1;
28765
+ for (const f of t2.filesTouched) row.files.add(f);
28766
+ }
28767
+ const members = [...byAgent.entries()].map(([agentId, row]) => ({
28768
+ agentId,
28769
+ turns: row.turns,
28770
+ filesTouched: row.files.size
28771
+ }));
28772
+ return { members, handoffs: { ...this.handoffCounters }, sinceTurn: 1 };
28773
+ }
28774
+ /** Returns (creating on first access) the mutable per-agent state. */
28775
+ member(agentId) {
28776
+ let m = this.members.get(agentId);
28777
+ if (!m) {
28778
+ m = defaultMember();
28779
+ this.members.set(agentId, m);
28780
+ }
28781
+ return m;
28782
+ }
28783
+ /** Appends a journal entry and persists (fire-and-forget durability). */
28784
+ recordTurn(entry) {
28785
+ const turn = {
28786
+ turn: this.turns.length + 1,
28787
+ agentId: entry.agentId,
28788
+ prompt: clip(entry.prompt, PROMPT_MAX),
28789
+ replySummary: clip(entry.replySummary, REPLY_SUMMARY_MAX),
28790
+ filesTouched: entry.filesTouched
28791
+ };
28792
+ this.turns.push(turn);
28793
+ this.persist();
28794
+ }
28795
+ turnCount() {
28796
+ return this.turns.length;
28797
+ }
28798
+ entriesSince(turnIndex) {
28799
+ return this.turns.filter((t2) => t2.turn > turnIndex);
28800
+ }
28801
+ persist() {
28802
+ try {
28803
+ fs65.mkdirSync(path71.dirname(this.journalPath), { recursive: true, mode: 448 });
28804
+ fs65.writeFileSync(this.journalPath, JSON.stringify({ turns: this.turns }), { mode: 384 });
28805
+ } catch {
28806
+ }
28807
+ }
28808
+ };
28809
+ function specialtyFor(agentId) {
28810
+ return SQUAD_SPECIALTIES[agentId] ?? GENERIC_SPECIALTY;
28811
+ }
28812
+ var TEAM_PREAMBLE_MARKER = "[Team context]";
28813
+ var TEAM_PREAMBLE_LINES = [
28814
+ "[Team context] You are the active agent in a CodeAgent Mobile session where the user",
28815
+ "has a squad of agents and can pass work between them. Your available teammates:",
28816
+ "If a task clearly fits a teammate better than you, you MAY propose a handoff by ending",
28817
+ `your reply with a fenced code block tagged ${HANDOFF_FENCE_TAG} containing ONE JSON object:`,
28818
+ '{"to":"<teammate id>","reason":"<one sentence>","prompt":"<the prompt they should run>"}',
28819
+ "Propose at most one handoff per reply, only when genuinely better, and never announce",
28820
+ "the block in prose \u2014 the app renders it as a card the user can accept."
28821
+ ];
28822
+ var TEAM_PREAMBLE_BULLET_RE = /^- .+ — best at: /;
28823
+ var BRIEFING_MARKER = "[Team update]";
28824
+ var BRIEFING_HEADER = "[Team update] While you were away, other agents worked on this session:";
28825
+ var BRIEFING_FOOTER = "Continue from the CURRENT state of the working tree.";
28826
+ function buildTeamPreamble(roster, currentAgent, opts) {
28827
+ const others = roster.agents.filter((a) => a.agentId !== currentAgent);
28828
+ if (others.length === 0) return null;
28829
+ const lines = [
28830
+ TEAM_PREAMBLE_LINES[0],
28831
+ TEAM_PREAMBLE_LINES[1],
28832
+ ...others.map((a) => `- ${a.displayName} \u2014 best at: ${specialtyFor(a.agentId)}`)
28833
+ ];
28834
+ if (opts.handoffInstructions) {
28835
+ lines.push(...TEAM_PREAMBLE_LINES.slice(2));
28836
+ }
28837
+ return clip(lines.join("\n"), PREAMBLE_MAX);
28838
+ }
28839
+ function renderJournalEntry(e) {
28840
+ const filesClause = e.filesTouched.length > 0 ? ` (files: ${e.filesTouched.join(", ")})` : "";
28841
+ return `- turn ${e.turn} (${e.agentId}): ${e.prompt} \u2192 ${e.replySummary}${filesClause}`;
28842
+ }
28843
+ function buildDeltaBriefing(entries, maxChars = BRIEFING_DEFAULT_MAX) {
28844
+ if (entries.length === 0) return null;
28845
+ const header = BRIEFING_HEADER;
28846
+ const footer = BRIEFING_FOOTER;
28847
+ const envelope = header.length + 1 + footer.length + 1;
28848
+ const sorted = [...entries].sort((a, b) => a.turn - b.turn);
28849
+ const lines = [];
28850
+ let bodyLen = 0;
28851
+ for (let i = sorted.length - 1; i >= 0; i--) {
28852
+ const line = renderJournalEntry(sorted[i]);
28853
+ const addedLen = line.length + (lines.length > 0 ? 1 : 0);
28854
+ if (lines.length > 0 && envelope + bodyLen + addedLen > maxChars) break;
28855
+ lines.unshift(line);
28856
+ bodyLen += addedLen;
28857
+ }
28858
+ return [header, lines.join("\n"), footer].join("\n");
28859
+ }
28860
+
28861
+ // src/agents/acp/squad-context.ts
28862
+ var SQUAD_CONTEXT_URI = "codeam://squad-context";
28863
+ var HANDOFF_MARKER = "[Session handoff]";
28864
+ var HANDOFF_TERMINATOR = "--- End of handoff context ---";
28865
+ function buildSquadContextBlock(text) {
28866
+ return {
28867
+ type: "resource",
28868
+ resource: { uri: SQUAD_CONTEXT_URI, mimeType: "text/plain", text }
28869
+ };
28870
+ }
28871
+ function isSquadContextBlock(block) {
28872
+ return block.type === "resource" && block.resource.uri === SQUAD_CONTEXT_URI;
28873
+ }
28874
+ function looksLikeUnsupportedPromptShape(err) {
28875
+ const code = err?.code;
28876
+ if (code === -32602) return true;
28877
+ const message = err instanceof Error ? err.message : String(err ?? "");
28878
+ if (/invalid[ _]params|-32602/i.test(message)) return true;
28879
+ return /\bresource\b/i.test(message) && /unsupported|not supported/i.test(message);
28880
+ }
28881
+ function skipWhile(lines, start2, keepGoing) {
28882
+ let i = start2;
28883
+ while (i < lines.length && keepGoing(lines[i])) i++;
28884
+ return i;
28885
+ }
28886
+ function endOfTeamPreamble(lines, start2) {
28887
+ if (lines[start2] !== TEAM_PREAMBLE_LINES[0]) return -1;
28888
+ if (lines[start2 + 1] !== TEAM_PREAMBLE_LINES[1]) return -1;
28889
+ return skipWhile(
28890
+ lines,
28891
+ start2 + 2,
28892
+ (line) => TEAM_PREAMBLE_BULLET_RE.test(line) || TEAM_PREAMBLE_LINES.includes(line)
28893
+ );
28894
+ }
28895
+ function endOfTerminatedBlock(lines, start2, terminator) {
28896
+ for (let i = start2; i < lines.length; i++) {
28897
+ if (lines[i].trimEnd() === terminator) return i + 1;
28898
+ }
28899
+ return -1;
28900
+ }
28901
+ function stripSquadContext(text) {
28902
+ if (!text.includes(HANDOFF_MARKER) && !text.includes(BRIEFING_MARKER) && !text.includes(TEAM_PREAMBLE_MARKER)) {
28903
+ return text;
28904
+ }
28905
+ const lines = text.split("\n");
28906
+ const kept = [];
28907
+ let i = 0;
28908
+ let stripped = false;
28909
+ while (i < lines.length) {
28910
+ const line = lines[i];
28911
+ if (line.startsWith(HANDOFF_MARKER)) {
28912
+ const end = endOfTerminatedBlock(lines, i, HANDOFF_TERMINATOR);
28913
+ if (end !== -1) {
28914
+ i = end;
28915
+ stripped = true;
28916
+ continue;
28917
+ }
28918
+ } else if (line.startsWith(BRIEFING_MARKER)) {
28919
+ const end = endOfTerminatedBlock(lines, i, BRIEFING_FOOTER);
28920
+ if (end !== -1) {
28921
+ i = end;
28922
+ stripped = true;
28923
+ continue;
28924
+ }
28925
+ } else if (line.startsWith(TEAM_PREAMBLE_MARKER)) {
28926
+ const end = endOfTeamPreamble(lines, i);
28927
+ if (end !== -1) {
28928
+ i = end;
28929
+ stripped = true;
28930
+ continue;
28931
+ }
28932
+ }
28933
+ kept.push(line);
28934
+ i++;
28935
+ }
28936
+ if (!stripped) return text;
28937
+ return kept.join("\n").trim();
28938
+ }
28939
+
28940
+ // src/services/history.service.ts
28567
28941
  var historyRecordSchema = import_zod2.z.object({
28568
28942
  type: import_zod2.z.string().optional(),
28569
28943
  timestamp: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
@@ -28583,11 +28957,24 @@ function extractText3(content) {
28583
28957
  return "";
28584
28958
  }
28585
28959
  var CONVERSATION_BATCH_SIZE = 30;
28960
+ function scrubSquadContext(messages) {
28961
+ const out2 = [];
28962
+ for (const m of messages) {
28963
+ const text = stripSquadContext(m.text);
28964
+ if (text === m.text) {
28965
+ out2.push(m);
28966
+ continue;
28967
+ }
28968
+ if (text.length === 0) continue;
28969
+ out2.push({ ...m, text });
28970
+ }
28971
+ return out2;
28972
+ }
28586
28973
  function parseJsonl(filePath) {
28587
28974
  const messages = [];
28588
28975
  let raw;
28589
28976
  try {
28590
- raw = fs65.readFileSync(filePath, "utf8");
28977
+ raw = fs66.readFileSync(filePath, "utf8");
28591
28978
  } catch (err) {
28592
28979
  if (err.code !== "ENOENT") {
28593
28980
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -28728,7 +29115,7 @@ var HistoryService = class _HistoryService {
28728
29115
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
28729
29116
  }
28730
29117
  get projectDir() {
28731
- return this.runtime.resolveHistoryDir(this.cwd) ?? path71.join(os55.homedir(), ".claude", "projects", encodeCwd(this.cwd));
29118
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path72.join(os56.homedir(), ".claude", "projects", encodeCwd(this.cwd));
28732
29119
  }
28733
29120
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
28734
29121
  setCurrentConversationId(id) {
@@ -28740,7 +29127,7 @@ var HistoryService = class _HistoryService {
28740
29127
  /** Return the current message count in the active conversation. */
28741
29128
  getCurrentMessageCount() {
28742
29129
  if (!this.currentConversationId) return 0;
28743
- const filePath = path71.join(this.projectDir, `${this.currentConversationId}.jsonl`);
29130
+ const filePath = path72.join(this.projectDir, `${this.currentConversationId}.jsonl`);
28744
29131
  return parseJsonl(filePath).length;
28745
29132
  }
28746
29133
  /**
@@ -28751,7 +29138,7 @@ var HistoryService = class _HistoryService {
28751
29138
  const deadline = Date.now() + timeoutMs;
28752
29139
  while (Date.now() < deadline) {
28753
29140
  if (!this.currentConversationId) return null;
28754
- const filePath = path71.join(this.projectDir, `${this.currentConversationId}.jsonl`);
29141
+ const filePath = path72.join(this.projectDir, `${this.currentConversationId}.jsonl`);
28755
29142
  const messages = parseJsonl(filePath);
28756
29143
  if (messages.length > previousCount) {
28757
29144
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -28777,16 +29164,16 @@ var HistoryService = class _HistoryService {
28777
29164
  const dir = this.projectDir;
28778
29165
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
28779
29166
  try {
28780
- const files = fs65.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
29167
+ const files = fs66.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
28781
29168
  try {
28782
- const stat3 = fs65.statSync(path71.join(dir, e.name));
29169
+ const stat3 = fs66.statSync(path72.join(dir, e.name));
28783
29170
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
28784
29171
  } catch {
28785
29172
  return { name: e.name, mtime: 0, birthtime: 0 };
28786
29173
  }
28787
29174
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
28788
29175
  if (files.length > 0) {
28789
- this.currentConversationId = path71.basename(files[0].name, ".jsonl");
29176
+ this.currentConversationId = path72.basename(files[0].name, ".jsonl");
28790
29177
  }
28791
29178
  } catch {
28792
29179
  }
@@ -28820,13 +29207,13 @@ var HistoryService = class _HistoryService {
28820
29207
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
28821
29208
  let entries;
28822
29209
  try {
28823
- entries = fs65.readdirSync(dir, { withFileTypes: true });
29210
+ entries = fs66.readdirSync(dir, { withFileTypes: true });
28824
29211
  } catch {
28825
29212
  return null;
28826
29213
  }
28827
29214
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
28828
29215
  try {
28829
- const stat3 = fs65.statSync(path71.join(dir, e.name));
29216
+ const stat3 = fs66.statSync(path72.join(dir, e.name));
28830
29217
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
28831
29218
  } catch {
28832
29219
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -28835,12 +29222,12 @@ var HistoryService = class _HistoryService {
28835
29222
  if (files.length === 0) return null;
28836
29223
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
28837
29224
  if (!files.some((f) => f.name === targetFile)) return null;
28838
- return this.extractUsageFromFile(path71.join(dir, targetFile));
29225
+ return this.extractUsageFromFile(path72.join(dir, targetFile));
28839
29226
  }
28840
29227
  extractUsageFromFile(filePath) {
28841
29228
  let raw;
28842
29229
  try {
28843
- raw = fs65.readFileSync(filePath, "utf8");
29230
+ raw = fs66.readFileSync(filePath, "utf8");
28844
29231
  } catch {
28845
29232
  return null;
28846
29233
  }
@@ -28885,9 +29272,9 @@ var HistoryService = class _HistoryService {
28885
29272
  let totalCost = 0;
28886
29273
  let files;
28887
29274
  try {
28888
- files = fs65.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
29275
+ files = fs66.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
28889
29276
  try {
28890
- return fs65.statSync(path71.join(projectDir, f)).mtimeMs >= monthStartMs;
29277
+ return fs66.statSync(path72.join(projectDir, f)).mtimeMs >= monthStartMs;
28891
29278
  } catch {
28892
29279
  return false;
28893
29280
  }
@@ -28898,7 +29285,7 @@ var HistoryService = class _HistoryService {
28898
29285
  for (const file of files) {
28899
29286
  let raw;
28900
29287
  try {
28901
- raw = fs65.readFileSync(path71.join(projectDir, file), "utf8");
29288
+ raw = fs66.readFileSync(path72.join(projectDir, file), "utf8");
28902
29289
  } catch {
28903
29290
  continue;
28904
29291
  }
@@ -28977,7 +29364,7 @@ var HistoryService = class _HistoryService {
28977
29364
  if (this.runtime.resolveHistoryFile) {
28978
29365
  return this.runtime.resolveHistoryFile(this.cwd, sessionId);
28979
29366
  }
28980
- return path71.join(this.projectDir, `${sessionId}.jsonl`);
29367
+ return path72.join(this.projectDir, `${sessionId}.jsonl`);
28981
29368
  }
28982
29369
  /**
28983
29370
  * Parse a conversation's messages from disk, agent-aware. Claude uses the
@@ -28989,6 +29376,10 @@ var HistoryService = class _HistoryService {
28989
29376
  * convention as parseJsonl.
28990
29377
  */
28991
29378
  readConversation(sessionId) {
29379
+ const agentId = this.runtime.id;
29380
+ return scrubSquadContext(this.readConversationRaw(sessionId)).map((m) => ({ ...m, agentId }));
29381
+ }
29382
+ readConversationRaw(sessionId) {
28992
29383
  if (this.runtime.resolveHistoryFile) {
28993
29384
  const filePath = this.runtime.resolveHistoryFile(this.cwd, sessionId);
28994
29385
  if (!filePath) return [];
@@ -29011,7 +29402,7 @@ var HistoryService = class _HistoryService {
29011
29402
  };
29012
29403
  });
29013
29404
  }
29014
- return parseJsonl(path71.join(this.projectDir, `${sessionId}.jsonl`));
29405
+ return parseJsonl(path72.join(this.projectDir, `${sessionId}.jsonl`));
29015
29406
  }
29016
29407
  async loadConversation(sessionId) {
29017
29408
  const messages = this.readConversation(sessionId);
@@ -29079,7 +29470,7 @@ var HistoryService = class _HistoryService {
29079
29470
  if (!filePath) return false;
29080
29471
  let mtimeMs;
29081
29472
  try {
29082
- mtimeMs = fs65.statSync(filePath).mtimeMs;
29473
+ mtimeMs = fs66.statSync(filePath).mtimeMs;
29083
29474
  } catch {
29084
29475
  return false;
29085
29476
  }
@@ -29154,10 +29545,10 @@ var HistoryService = class _HistoryService {
29154
29545
 
29155
29546
  // src/agents/acp/client.ts
29156
29547
  var import_node_child_process29 = require("child_process");
29157
- var fs66 = __toESM(require("fs/promises"));
29548
+ var fs67 = __toESM(require("fs/promises"));
29158
29549
  var fsSync = __toESM(require("fs"));
29159
- var os57 = __toESM(require("os"));
29160
- var path73 = __toESM(require("path"));
29550
+ var os58 = __toESM(require("os"));
29551
+ var path74 = __toESM(require("path"));
29161
29552
  var import_node_stream = require("stream");
29162
29553
 
29163
29554
  // ../../node_modules/@agentclientprotocol/sdk/dist/schema/index.js
@@ -33183,8 +33574,8 @@ function createIdleTimeout(idleMs, makeError, activeIdleMs = idleMs) {
33183
33574
  }
33184
33575
 
33185
33576
  // src/agents/acp/internal-paths.ts
33186
- var path72 = __toESM(require("path"));
33187
- var os56 = __toESM(require("os"));
33577
+ var path73 = __toESM(require("path"));
33578
+ var os57 = __toESM(require("os"));
33188
33579
  var INTERNAL_TOKENS = [".codeam", "house-claude"];
33189
33580
  var SELF_HOSTED_WORKSPACE_RE = /\.codeam[/\\]self-hosted/gi;
33190
33581
  function textReferencesInternal(text) {
@@ -33192,13 +33583,13 @@ function textReferencesInternal(text) {
33192
33583
  const scrubbed = text.replace(SELF_HOSTED_WORKSPACE_RE, " ");
33193
33584
  return INTERNAL_TOKENS.some((t2) => scrubbed.includes(t2));
33194
33585
  }
33195
- function pathIsInternal(p2, homeDir2 = os56.homedir()) {
33586
+ function pathIsInternal(p2, homeDir2 = os57.homedir()) {
33196
33587
  if (!p2) return false;
33197
- const abs = path72.resolve(p2);
33198
- const home = path72.resolve(homeDir2);
33199
- const within = (root) => abs === root || abs.startsWith(root + path72.sep);
33200
- if (within(path72.join(home, ".codeam", "self-hosted"))) return false;
33201
- return within(path72.join(home, ".codeam")) || within(path72.join(home, ".beads")) || abs === path72.join(home, ".codeam-host.log") || abs.includes(`${path72.sep}house-claude${path72.sep}`) || abs.endsWith(`${path72.sep}house-claude`);
33588
+ const abs = path73.resolve(p2);
33589
+ const home = path73.resolve(homeDir2);
33590
+ const within = (root) => abs === root || abs.startsWith(root + path73.sep);
33591
+ if (within(path73.join(home, ".codeam", "self-hosted"))) return false;
33592
+ return within(path73.join(home, ".codeam")) || within(path73.join(home, ".beads")) || abs === path73.join(home, ".codeam-host.log") || abs.includes(`${path73.sep}house-claude${path73.sep}`) || abs.endsWith(`${path73.sep}house-claude`);
33202
33593
  }
33203
33594
  function toolCallReferencesInternal(call) {
33204
33595
  if (textReferencesInternal(call.title)) return true;
@@ -34157,7 +34548,7 @@ var AcpClient = class {
34157
34548
  throw new RequestError(-32002, GUARDRAIL_SECRET_READ_BLOCK_REASON, { uri: params.path });
34158
34549
  }
34159
34550
  try {
34160
- const content = await fs66.readFile(params.path, "utf8");
34551
+ const content = await fs67.readFile(params.path, "utf8");
34161
34552
  return applyLineRange(content, params.line ?? null, params.limit ?? null);
34162
34553
  } catch (err) {
34163
34554
  const code = err.code;
@@ -34180,7 +34571,7 @@ var AcpClient = class {
34180
34571
  throw new RequestError(-32002, INTERNAL_BLOCK_REASON, { uri: params.path });
34181
34572
  }
34182
34573
  try {
34183
- await fs66.writeFile(params.path, params.content, "utf8");
34574
+ await fs67.writeFile(params.path, params.content, "utf8");
34184
34575
  return {};
34185
34576
  } catch (err) {
34186
34577
  const code = err.code;
@@ -34240,29 +34631,29 @@ function applyLineRange(content, line, limit) {
34240
34631
  return { content: lines.slice(start2, end).join("\n") };
34241
34632
  }
34242
34633
  function knownAgentBinaryDirs() {
34243
- const home = os57.homedir();
34634
+ const home = os58.homedir();
34244
34635
  const out2 = [];
34245
34636
  out2.push("/tmp/codeam-node20/bin");
34246
34637
  for (const root of [
34247
34638
  "/usr/local/share/nvm/versions/node",
34248
- path73.join(home, ".nvm/versions/node")
34639
+ path74.join(home, ".nvm/versions/node")
34249
34640
  ]) {
34250
34641
  try {
34251
34642
  for (const child of fsSync.readdirSync(root)) {
34252
- out2.push(path73.join(root, child, "bin"));
34643
+ out2.push(path74.join(root, child, "bin"));
34253
34644
  }
34254
34645
  } catch {
34255
34646
  }
34256
34647
  }
34257
- out2.push(path73.join(home, ".volta/bin"));
34648
+ out2.push(path74.join(home, ".volta/bin"));
34258
34649
  out2.push("/usr/local/bin");
34259
34650
  out2.push("/usr/bin");
34260
- out2.push(path73.join(home, ".local/bin"));
34261
- out2.push(path73.join(home, "bin"));
34651
+ out2.push(path74.join(home, ".local/bin"));
34652
+ out2.push(path74.join(home, "bin"));
34262
34653
  if (process.platform === "win32") {
34263
34654
  const { LOCALAPPDATA, APPDATA } = process.env;
34264
- if (LOCALAPPDATA) out2.push(path73.join(LOCALAPPDATA, "cursor-agent"));
34265
- if (APPDATA) out2.push(path73.join(APPDATA, "npm"));
34655
+ if (LOCALAPPDATA) out2.push(path74.join(LOCALAPPDATA, "cursor-agent"));
34656
+ if (APPDATA) out2.push(path74.join(APPDATA, "npm"));
34266
34657
  }
34267
34658
  return out2.filter((p2) => {
34268
34659
  try {
@@ -34274,7 +34665,7 @@ function knownAgentBinaryDirs() {
34274
34665
  }
34275
34666
  function expandPathForAgentBinaries(existingPath) {
34276
34667
  const existing = new Set(
34277
- existingPath.split(path73.delimiter).filter((p2) => p2.length > 0)
34668
+ existingPath.split(path74.delimiter).filter((p2) => p2.length > 0)
34278
34669
  );
34279
34670
  const additions = [];
34280
34671
  for (const dir of knownAgentBinaryDirs()) {
@@ -34284,7 +34675,7 @@ function expandPathForAgentBinaries(existingPath) {
34284
34675
  }
34285
34676
  }
34286
34677
  if (additions.length === 0) return existingPath;
34287
- return [...additions, existingPath].filter((p2) => p2.length > 0).join(path73.delimiter);
34678
+ return [...additions, existingPath].filter((p2) => p2.length > 0).join(path74.delimiter);
34288
34679
  }
34289
34680
 
34290
34681
  // src/agents/acp/headroom-budget-proxy.ts
@@ -34366,7 +34757,7 @@ async function ensureAgentBinaryForSwitch(agentId, installScript, deps = {}) {
34366
34757
  };
34367
34758
  }
34368
34759
  log.info("switchAgent", `installing ${agentId} binary (missing on PATH)`);
34369
- const res = await runInstall(installScript, { logScope: "switchAgent" });
34760
+ const res = await runInstall(installScript, { logScope: "switchAgent", timeoutMs: 3e5 });
34370
34761
  if (!res.ok) {
34371
34762
  return {
34372
34763
  ok: false,
@@ -34374,6 +34765,22 @@ async function ensureAgentBinaryForSwitch(agentId, installScript, deps = {}) {
34374
34765
  };
34375
34766
  }
34376
34767
  if (await spec.waitForBinary({ timeoutMs: 3e4 })) return { ok: true };
34768
+ log.info(
34769
+ "switchAgent",
34770
+ "install probe failed \u2014 retrying install once (half-finished bin link class)"
34771
+ );
34772
+ deps.onRetry?.();
34773
+ const retryRes = await runInstall(installScript, {
34774
+ logScope: "switchAgent",
34775
+ timeoutMs: 3e5
34776
+ });
34777
+ if (!retryRes.ok) {
34778
+ return {
34779
+ ok: false,
34780
+ error: retryRes.timedOut ? `${displayName(agentId)} install timed out.` : `${displayName(agentId)} install failed.`
34781
+ };
34782
+ }
34783
+ if (await spec.waitForBinary({ timeoutMs: 3e4 })) return { ok: true };
34377
34784
  return {
34378
34785
  ok: false,
34379
34786
  error: `${displayName(agentId)} installed but its binary never appeared on PATH.`
@@ -34407,7 +34814,11 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
34407
34814
  const from = deps.currentAgent();
34408
34815
  const target = resolveSwitchTarget(rawAgentId, from);
34409
34816
  if (!target.ok) {
34410
- return { ok: false, agentId: typeof rawAgentId === "string" ? rawAgentId : "", error: target.error };
34817
+ return {
34818
+ ok: false,
34819
+ agentId: typeof rawAgentId === "string" ? rawAgentId : "",
34820
+ error: target.error
34821
+ };
34411
34822
  }
34412
34823
  const agentId = target.agentId;
34413
34824
  const emitStatus = (status2) => deps.postEvent("switch_agent_status", { ...status2 });
@@ -34438,7 +34849,9 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
34438
34849
  }
34439
34850
  if (!fastPath.skipInstall) {
34440
34851
  void emitStep("install");
34441
- const bin = await deps.ensureBinary(agentId, installScript);
34852
+ const bin = await deps.ensureBinary(agentId, installScript, () => {
34853
+ void emitStep("install");
34854
+ });
34442
34855
  if (!bin.ok) return fail2(bin.error);
34443
34856
  }
34444
34857
  void emitStep("restart");
@@ -34454,7 +34867,9 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
34454
34867
  `Switching to ${displayName(agentId)} failed and ${displayName(from)} couldn't be restored \u2014 restart the session.`
34455
34868
  );
34456
34869
  }
34457
- return fail2(`Couldn't start ${displayName(agentId)} \u2014 the session stays on ${displayName(from)}.`);
34870
+ return fail2(
34871
+ `Couldn't start ${displayName(agentId)} \u2014 the session stays on ${displayName(from)}.`
34872
+ );
34458
34873
  }
34459
34874
  try {
34460
34875
  deps.persistAgent(agentId);
@@ -34467,121 +34882,6 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
34467
34882
  return { ok: true, agentId };
34468
34883
  }
34469
34884
 
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
-
34585
34885
  // src/services/streaming/transport.ts
34586
34886
  var http7 = __toESM(require("http"));
34587
34887
  var https8 = __toESM(require("https"));
@@ -34881,6 +35181,13 @@ var AcpPublisher = class {
34881
35181
  * `mode: 'replace'` so a re-send overrides any stale persisted
34882
35182
  * version — important for the ACP path because each turn we ship
34883
35183
  * the cumulative messages, not a delta.
35184
+ *
35185
+ * Each message may carry its OWN `agentId` — the agent that PRODUCED that
35186
+ * turn. The top-level `agentId` only keys the backend's per-agent
35187
+ * conversation bucket, so without the per-message field a multi-agent
35188
+ * session's reloaded history collapsed to whichever agent happened to be
35189
+ * active (the v1 limitation, codeagent-egai). Additive: older backends
35190
+ * ignore it, older CLIs simply omit it.
34884
35191
  */
34885
35192
  async pushConversation(args2) {
34886
35193
  const url2 = `${this.apiBase}/api/sessions/conversation`;
@@ -35101,8 +35408,19 @@ function stripHandoffFences(text) {
35101
35408
  }
35102
35409
  return restore(stripFences(masked));
35103
35410
  }
35104
- function handoffFenceStart(text) {
35105
- return text.indexOf(FENCE_OPEN);
35411
+ function handoffFenceStartMasked(text) {
35412
+ const { masked } = maskOuterFences(text);
35413
+ if (masked.indexOf(FENCE_OPEN) === -1) return -1;
35414
+ const spans = [];
35415
+ for (const m of text.matchAll(OUTER_FENCE_RE)) {
35416
+ const start2 = m.index ?? 0;
35417
+ spans.push({ start: start2, end: start2 + m[0].length });
35418
+ }
35419
+ const insideSpan = (idx) => spans.some((s) => idx >= s.start && idx < s.end);
35420
+ for (let i = text.indexOf(FENCE_OPEN); i !== -1; i = text.indexOf(FENCE_OPEN, i + 1)) {
35421
+ if (!insideSpan(i)) return i;
35422
+ }
35423
+ return -1;
35106
35424
  }
35107
35425
 
35108
35426
  // src/agents/acp/onboarding.ts
@@ -35882,11 +36200,34 @@ var TurnFileAggregator = class {
35882
36200
  */
35883
36201
  baselineByKey = /* @__PURE__ */ new Map();
35884
36202
  baselineCaptured = false;
36203
+ /**
36204
+ * The file paths (relative to their repo root) that the MOST RECENT
36205
+ * `flushTurn()` call found novel — i.e. what it just enqueued (or would
36206
+ * have enqueued had the batch not exactly matched the baseline). Updated
36207
+ * unconditionally on every flush, including a no-op one (empties back
36208
+ * out), so `peekTurnPaths()` never returns paths from more than one
36209
+ * flush cycle ago. Not touched by the baseline-capture flush (it returns
36210
+ * before `novel` is computed), which is correct — pre-pair files aren't
36211
+ * "this session's" changes.
36212
+ */
36213
+ lastTurnPaths = [];
35885
36214
  /** Stop the outbox scheduler. Idempotent. */
35886
36215
  stop() {
35887
36216
  this.stopped = true;
35888
36217
  this.outbox.stop();
35889
36218
  }
36219
+ /**
36220
+ * Non-destructive read of the paths the MOST RECENT `flushTurn()` call
36221
+ * found novel — does NOT trigger a scan or clear anything itself. The
36222
+ * squad journal (`recordSquadTurn` in `command-handlers.ts`) `await`s
36223
+ * `flushTurn()` for the turn it's about to record BEFORE calling this, so
36224
+ * the read reflects THAT turn's paths, not a stale one left over from
36225
+ * whatever flushed previously. Empty on a session's very first turn
36226
+ * (still capturing the pre-pair baseline) or a chat-only turn.
36227
+ */
36228
+ peekTurnPaths() {
36229
+ return [...this.lastTurnPaths];
36230
+ }
35890
36231
  /**
35891
36232
  * Run the discovery + git collection + POST pipeline for one
35892
36233
  * turn. Errors are swallowed (logged) so an agent never blocks on a
@@ -35927,6 +36268,7 @@ var TurnFileAggregator = class {
35927
36268
  if (!base) return true;
35928
36269
  return base.linesAdded !== f.linesAdded || base.linesRemoved !== f.linesRemoved || base.fileStatus !== f.fileStatus;
35929
36270
  });
36271
+ this.lastTurnPaths = novel.map((f) => f.filePath);
35930
36272
  if (novel.length === 0) {
35931
36273
  log.trace(
35932
36274
  "turnFiles",
@@ -35946,10 +36288,7 @@ var TurnFileAggregator = class {
35946
36288
  await this.outbox.enqueue(entry);
35947
36289
  }
35948
36290
  } catch (err) {
35949
- log.warn(
35950
- "turnFiles",
35951
- `flushTurn failed: ${err.message ?? String(err)}`
35952
- );
36291
+ log.warn("turnFiles", `flushTurn failed: ${err.message ?? String(err)}`);
35953
36292
  }
35954
36293
  }
35955
36294
  /**
@@ -36945,6 +37284,56 @@ async function detectRepoStack(cwd, runtime) {
36945
37284
  }
36946
37285
  }
36947
37286
 
37287
+ // src/agents/acp/coderabbit-mention.ts
37288
+ var CODERABBIT_AGENT_ID = "coderabbit";
37289
+ var CODERABBIT_NOT_LINKED_MESSAGE = "Link CodeRabbit in Profile \u203A Your Squad first.";
37290
+ var CODERABBIT_NO_CUSTOM_INSTRUCTIONS_NOTICE = "CodeRabbit reviews your current changes \u2014 custom instructions aren't supported yet.";
37291
+ function hasCustomInstructions(prompt) {
37292
+ return prompt.replace(/@coderabbit\b/gi, "").trim().length > 0;
37293
+ }
37294
+ function composeReviewOutput(markdown, custom) {
37295
+ const body = markdown.trim().length > 0 ? markdown.trim() : "CodeRabbit found no issues to report.";
37296
+ return custom ? `${CODERABBIT_NO_CUSTOM_INSTRUCTIONS_NOTICE}
37297
+
37298
+ ${body}` : body;
37299
+ }
37300
+ function defaultRunReview(input) {
37301
+ return new CoderabbitRuntimeStrategy(createOsStrategy()).runOneShot(input);
37302
+ }
37303
+ async function runCoderabbitMentionReview(deps = {}) {
37304
+ const configure = deps.configure ?? configureCoderabbit;
37305
+ const runReview = deps.runReview ?? defaultRunReview;
37306
+ try {
37307
+ const status2 = await configure({ action: "status" });
37308
+ if (!status2.loggedIn) {
37309
+ const cred = deps.fetchCredential ? await deps.fetchCredential() : null;
37310
+ if (!cred) return { ok: false, error: CODERABBIT_NOT_LINKED_MESSAGE };
37311
+ const provisioned = await configure({
37312
+ action: "provision",
37313
+ provisionCredential: cred
37314
+ });
37315
+ if (!provisioned.loggedIn) {
37316
+ return { ok: false, error: provisioned.error ?? CODERABBIT_NOT_LINKED_MESSAGE };
37317
+ }
37318
+ }
37319
+ const result = await configure({ action: "review" }, { runReview });
37320
+ if (result.error) return { ok: false, error: result.error };
37321
+ return { ok: true, markdown: result.review?.markdown ?? "" };
37322
+ } catch (err) {
37323
+ return {
37324
+ ok: false,
37325
+ error: err instanceof Error ? err.message : "CodeRabbit review failed"
37326
+ };
37327
+ }
37328
+ }
37329
+ function logMentionOutcome(result) {
37330
+ if (result.ok) {
37331
+ log.info("acpRunner", `squad: coderabbit review completed (${result.markdown.length} chars)`);
37332
+ } else {
37333
+ log.warn("acpRunner", `squad: coderabbit review failed: ${result.error}`);
37334
+ }
37335
+ }
37336
+
36948
37337
  // src/agents/acp/command-handlers.ts
36949
37338
  var import_node_child_process31 = require("child_process");
36950
37339
 
@@ -37143,39 +37532,72 @@ async function routeSquadTask(ctx, target) {
37143
37532
  }
37144
37533
  return { ok: true };
37145
37534
  }
37146
- function prefixSquadContext(ctx, blocks) {
37535
+ function collectSquadContext(ctx) {
37536
+ const pieces = [];
37537
+ const { squad, opts } = ctx;
37538
+ if (squad) {
37539
+ const member = squad.member(opts.agent);
37540
+ const roster = squad.roster;
37541
+ if (roster && member.lastTurnIndex === 0) {
37542
+ const preamble = buildTeamPreamble(roster, opts.agent, {
37543
+ handoffInstructions: roster.handoffsEnabled === true
37544
+ });
37545
+ if (preamble) pieces.push(preamble);
37546
+ }
37547
+ if (squad.turnCount() > member.lastTurnIndex) {
37548
+ const otherEntries = squad.entriesSince(member.lastTurnIndex).filter((e) => e.agentId !== opts.agent);
37549
+ const briefing = buildDeltaBriefing(otherEntries);
37550
+ if (briefing) pieces.push(briefing);
37551
+ }
37552
+ }
37147
37553
  if (ctx.pendingHandoff?.current) {
37148
- blocks.unshift({ type: "text", text: ctx.pendingHandoff.current });
37554
+ pieces.push(ctx.pendingHandoff.current);
37149
37555
  ctx.pendingHandoff.current = null;
37150
37556
  }
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 });
37557
+ return pieces;
37558
+ }
37559
+ function squadContextMode(ctx) {
37560
+ return ctx.squad?.member(ctx.opts.agent).contextTextFallback === true ? "text" : "resource";
37561
+ }
37562
+ function applySquadContext(blocks, pieces, mode) {
37563
+ if (pieces.length === 0) return;
37564
+ if (mode === "resource") {
37565
+ blocks.unshift(buildSquadContextBlock(pieces.join("\n\n")));
37566
+ return;
37158
37567
  }
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 });
37568
+ for (let i = pieces.length - 1; i >= 0; i--) blocks.unshift({ type: "text", text: pieces[i] });
37569
+ }
37570
+ async function promptWithContextFallback(ctx, client3, blocks, pieces) {
37571
+ try {
37572
+ return await client3.prompt(blocks);
37573
+ } catch (err) {
37574
+ if (pieces.length === 0 || blocks.length === 0 || !isSquadContextBlock(blocks[0]) || !looksLikeUnsupportedPromptShape(err)) {
37575
+ throw err;
37576
+ }
37577
+ log.warn(
37578
+ "acpRunner",
37579
+ `squad: ${ctx.opts.agent} rejected the native squad-context resource block (${describeError(err)}) \u2014 retrying once with legacy text blocks`
37580
+ );
37581
+ if (ctx.squad) ctx.squad.member(ctx.opts.agent).contextTextFallback = true;
37582
+ blocks.shift();
37583
+ applySquadContext(blocks, pieces, "text");
37584
+ return await client3.prompt(blocks);
37165
37585
  }
37166
37586
  }
37167
37587
  function recordSquadTurn(ctx, prompt, replySummary) {
37168
- const { squad, opts } = ctx;
37588
+ const { squad, opts, turnFiles } = ctx;
37169
37589
  if (!squad) return;
37170
37590
  squad.recordTurn({
37171
37591
  agentId: opts.agent,
37172
37592
  prompt,
37173
37593
  replySummary,
37174
37594
  // 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: []
37595
+ // outbox POST). The caller (`startTaskH`) AWAITS `turnFiles.flushTurn()`
37596
+ // for THIS turn before calling recordSquadTurn precisely so
37597
+ // `peekTurnPaths()` reflects THIS turn's novel files, not a stale read
37598
+ // of whatever the aggregator's PREVIOUS flush happened to find. Capped
37599
+ // so a pathological turn (mass refactor) doesn't bloat the journal.
37600
+ filesTouched: turnFiles.peekTurnPaths().slice(0, 20)
37179
37601
  });
37180
37602
  squad.member(opts.agent).lastTurnIndex = squad.turnCount();
37181
37603
  }
@@ -37190,45 +37612,112 @@ function resolvePendingProposal(ctx, requestedAgentId) {
37190
37612
  if (!slot || !pending) return;
37191
37613
  slot.current = null;
37192
37614
  const accepted = requestedAgentId === pending.toAgentId;
37615
+ if (accepted) ctx.squad?.countAccepted();
37193
37616
  log.info(
37194
37617
  "acpRunner",
37195
37618
  `squad: handoff ${pending.proposalId} ${accepted ? "accepted" : "declined"}`
37196
37619
  );
37197
- void ctx.postSquadEvent?.("handoff_resolved", {
37198
- proposalId: pending.proposalId,
37199
- accepted
37200
- });
37620
+ const resolution = { proposalId: pending.proposalId, accepted };
37621
+ void ctx.postSquadEvent?.("handoff_resolved", { ...resolution });
37622
+ }
37623
+ function proposalIdFor(commandId, hop) {
37624
+ const id = hop <= 1 ? `hp-${commandId}` : `hp-${commandId}-h${hop}`;
37625
+ return id.length > 128 ? id.slice(0, 128) : id;
37201
37626
  }
37202
- function emitHandoffProposal(ctx, proposal) {
37627
+ function emitHandoffProposal(ctx, proposal, hop) {
37203
37628
  const { squad, pendingProposal, postSquadEvent, opts, cmd } = ctx;
37204
- if (!proposal || !pendingProposal || !postSquadEvent) return;
37205
- if (squad?.roster?.handoffsEnabled !== true) return;
37629
+ if (!proposal || !pendingProposal || !postSquadEvent) return null;
37630
+ if (squad?.roster?.handoffsEnabled !== true) return null;
37206
37631
  if (pendingProposal.current) {
37207
37632
  log.info("acpRunner", "squad: dropping handoff proposal \u2014 one is already pending");
37208
- return;
37633
+ return null;
37209
37634
  }
37635
+ const auto = squad.auto.enabled && squad.hopsRemaining() > 0;
37636
+ if (auto) squad.consumeHop();
37210
37637
  const record2 = {
37211
- proposalId: `hp-${cmd.id}`,
37638
+ proposalId: proposalIdFor(cmd.id, hop),
37212
37639
  fromAgentId: opts.agent,
37213
37640
  toAgentId: proposal.to,
37214
37641
  reason: proposal.reason,
37215
- prompt: proposal.prompt
37642
+ prompt: proposal.prompt,
37643
+ ...auto ? { auto: true, hopsRemaining: squad.hopsRemaining() } : {}
37216
37644
  };
37217
- pendingProposal.current = record2;
37218
- log.info("acpRunner", `squad: handoff proposed ${opts.agent} \u2192 ${record2.toAgentId}`);
37645
+ squad.countProposal({ auto });
37646
+ log.info(
37647
+ "acpRunner",
37648
+ `squad: handoff proposed ${opts.agent} \u2192 ${record2.toAgentId}${auto ? " (auto)" : ""}`
37649
+ );
37219
37650
  void postSquadEvent("handoff_proposed", { ...record2 });
37651
+ if (!auto) {
37652
+ pendingProposal.current = record2;
37653
+ return null;
37654
+ }
37655
+ return record2;
37656
+ }
37657
+ function resolveAutoHandoff(ctx, record2) {
37658
+ ctx.squad?.countAccepted();
37659
+ const resolution = {
37660
+ proposalId: record2.proposalId,
37661
+ accepted: true,
37662
+ auto: true
37663
+ };
37664
+ void ctx.postSquadEvent?.("handoff_resolved", { ...resolution });
37665
+ }
37666
+ async function runCoderabbitMention(ctx, promptText) {
37667
+ const { cmd, relay, streaming, opts, turnFiles } = ctx;
37668
+ const userText = promptText.length > 0 ? promptText : `@${CODERABBIT_AGENT_ID}`;
37669
+ await streaming.beginTurn();
37670
+ ctx.history.appendUserPrompt(userText);
37671
+ log.info("acpRunner", `squad: coderabbit one-shot review id=${cmd.id.slice(0, 8)}`);
37672
+ const review = await runCoderabbitMentionReview({
37673
+ fetchCredential: () => fetchProvisionCredential({
37674
+ agentId: CODERABBIT_AGENT_ID,
37675
+ sessionId: opts.sessionId,
37676
+ pluginId: opts.pluginId,
37677
+ pluginAuthToken: opts.pluginAuthToken
37678
+ })
37679
+ });
37680
+ logMentionOutcome(review);
37681
+ if (!review.ok) {
37682
+ await streaming.closeWithBubble(review.error);
37683
+ ctx.history.appendAgentReply(review.error, CODERABBIT_AGENT_ID);
37684
+ void ctx.history.flush();
37685
+ await relay.sendResult(cmd.id, "failed", { error: review.error });
37686
+ return;
37687
+ }
37688
+ const output = composeReviewOutput(review.markdown, hasCustomInstructions(promptText));
37689
+ await streaming.closeWithBubble(output);
37690
+ ctx.history.appendAgentReply(output, CODERABBIT_AGENT_ID);
37691
+ void ctx.history.flush();
37692
+ await turnFiles.flushTurn().catch((err) => {
37693
+ log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37694
+ });
37695
+ recordCoderabbitTurn(ctx, userText, output);
37696
+ await relay.sendResult(cmd.id, "completed", { agentId: CODERABBIT_AGENT_ID });
37697
+ }
37698
+ function recordCoderabbitTurn(ctx, prompt, replySummary) {
37699
+ ctx.squad?.recordTurn({
37700
+ agentId: CODERABBIT_AGENT_ID,
37701
+ prompt,
37702
+ replySummary,
37703
+ filesTouched: []
37704
+ });
37220
37705
  }
37221
37706
  async function startTaskH(ctx) {
37222
37707
  const { cmd, relay, streaming, opts, turnFiles, publisher, recentStderr, budgetReachedFlag } = ctx;
37223
37708
  const payload = cmd.payload;
37709
+ const requestedAgentId = typeof payload?.agentId === "string" ? payload.agentId.trim() : "";
37710
+ resolvePendingProposal(ctx, requestedAgentId);
37711
+ if (requestedAgentId === CODERABBIT_AGENT_ID) {
37712
+ await runCoderabbitMention(ctx, (payload?.prompt ?? "").trim());
37713
+ return;
37714
+ }
37224
37715
  const blocks = buildAcpPromptBlocks(payload ?? {});
37225
37716
  if (blocks.length === 0) {
37226
37717
  log.warn("acpRunner", "start_task with empty prompt + no attachments; ignoring");
37227
37718
  await relay.sendResult(cmd.id, "failed", { error: "empty prompt" });
37228
37719
  return;
37229
37720
  }
37230
- const requestedAgentId = typeof payload?.agentId === "string" ? payload.agentId.trim() : "";
37231
- resolvePendingProposal(ctx, requestedAgentId);
37232
37721
  if (requestedAgentId.length > 0 && requestedAgentId !== opts.agent) {
37233
37722
  const routed = await routeSquadTask(ctx, requestedAgentId);
37234
37723
  if (!routed.ok) {
@@ -37237,7 +37726,6 @@ async function startTaskH(ctx) {
37237
37726
  return;
37238
37727
  }
37239
37728
  }
37240
- const { client: client3, history, budgetRecovery } = ctx;
37241
37729
  const promptText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
37242
37730
  const imageCount = blocks.filter((b) => b.type === "image").length;
37243
37731
  log.info(
@@ -37249,133 +37737,214 @@ async function startTaskH(ctx) {
37249
37737
  showInfo(echoLine);
37250
37738
  }
37251
37739
  await streaming.beginTurn();
37252
- history.appendUserPrompt(promptText);
37740
+ ctx.history.appendUserPrompt(promptText);
37253
37741
  maybePrefaceAgentStandard(blocks, opts.agent, opts.sessionId);
37254
- prefixSquadContext(ctx, blocks);
37255
- let turnClosed = false;
37256
- try {
37257
- const reply = await client3.prompt(blocks);
37258
- const finalText = streaming.getCurrentText();
37259
- if (agentHooks(opts.agent)?.classifyCompletedReply?.(finalText) === "upgrade_required") {
37260
- await streaming.closeWithBubble(CURSOR_UPGRADE_MESSAGE);
37261
- turnClosed = true;
37262
- history.appendAgentReply(CURSOR_UPGRADE_MESSAGE);
37263
- void history.flush();
37264
- log.info("acpRunner", `start_task \u2190 cursor-plan-upgrade-required id=${cmd.id.slice(0, 8)}`);
37265
- await relay.sendResult(cmd.id, "failed", { error: "cursor plan upgrade required" });
37266
- } else if (replyIsHouseAgentLimit(finalText)) {
37267
- const houseBubble = houseAgentLimitMessage(finalText);
37268
- await streaming.closeWithBubble(houseBubble);
37269
- turnClosed = true;
37270
- history.appendAgentReply(houseBubble);
37271
- void history.flush();
37272
- turnFiles.flushTurn().catch((err) => {
37273
- log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37274
- });
37275
- log.info("acpRunner", `start_task \u2190 house-agent-limit id=${cmd.id.slice(0, 8)}`);
37276
- await relay.sendResult(cmd.id, "failed", {
37277
- error: "house agent usage ceiling / temporarily unavailable"
37278
- });
37279
- } else if (replyIsAuthFailure(finalText)) {
37280
- await streaming.closeWithBubble(AUTH_FAILURE_MESSAGE);
37281
- turnClosed = true;
37282
- history.appendAgentReply(AUTH_FAILURE_MESSAGE);
37283
- void history.flush();
37284
- turnFiles.flushTurn().catch((err) => {
37285
- log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37286
- });
37287
- void reportCredentialInvalid(opts);
37288
- log.info("acpRunner", `start_task \u2190 auth-failure-in-reply id=${cmd.id.slice(0, 8)}`);
37289
- await relay.sendResult(cmd.id, "failed", { error: "agent reply reported auth failure" });
37290
- } else if (shouldOfferOneMRecovery({ detail: "", recentStderr: recentStderr.join("\n"), finalText })) {
37291
- await streaming.closeWithBubble(ONE_M_CREDITS_MESSAGE);
37292
- turnClosed = true;
37293
- history.appendAgentReply(ONE_M_CREDITS_MESSAGE);
37294
- void history.flush();
37295
- turnFiles.flushTurn().catch((err) => {
37296
- log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37297
- });
37298
- void reportCredentialInvalid(opts);
37299
- log.info("acpRunner", `start_task \u2190 1m-credits-reconnect id=${cmd.id.slice(0, 8)}`);
37300
- await relay.sendResult(cmd.id, "failed", {
37301
- error: "agent reply reported 1M-context usage-credits gate"
37302
- });
37303
- } else {
37304
- await streaming.closeTurnWithInteractiveDetection();
37305
- turnClosed = true;
37306
- const { cleanText, proposal } = extractHandoffProposal(
37307
- finalText,
37308
- opts.agent,
37309
- handoffTargets(ctx)
37310
- );
37311
- const replyLine = formatAgentReplyLine(cleanText);
37312
- if (replyLine.length > 0) {
37313
- showInfo(replyLine);
37314
- }
37315
- history.appendAgentReply(cleanText);
37316
- void history.flush();
37317
- recordSquadTurn(ctx, promptText, cleanText);
37318
- emitHandoffProposal(ctx, proposal);
37319
- void publisher.publishOutput({
37320
- type: "input_suggestion",
37321
- content: ACP_QUICK_REPLIES,
37322
- done: true
37323
- });
37324
- turnFiles.flushTurn().catch((err) => {
37325
- log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37326
- });
37327
- log.info("acpRunner", `start_task \u2190 done stopReason=${reply.stopReason ?? "?"} id=${cmd.id.slice(0, 8)}`);
37328
- await relay.sendResult(cmd.id, "completed", { stopReason: reply.stopReason });
37329
- }
37330
- } catch (err) {
37331
- if (turnClosed) {
37332
- log.warn(
37333
- "acpRunner",
37334
- `post-close ack failed (turn already delivered) id=${cmd.id.slice(0, 8)}: ${describeError(err)}`
37335
- );
37336
- return;
37337
- }
37338
- const hadText = streaming.hasVisibleProgress();
37339
- const detail = describeError(err);
37340
- log.warn("acpRunner", `prompt failed: ${detail}`);
37341
- await cancelStuckTurn(client3);
37342
- if (looksLikeBudgetExceeded(`${detail}
37343
- ${recentStderr.join("\n")}`)) {
37344
- await streaming.closeAll();
37345
- if (!budgetReachedFlag.get()) {
37346
- budgetReachedFlag.set(true);
37347
- void postBudgetReached({
37348
- sessionId: opts.sessionId,
37349
- pluginId: opts.pluginId,
37350
- pluginAuthToken: opts.pluginAuthToken,
37351
- agent: opts.agent,
37352
- period: extractBudgetPeriod(`${detail}
37353
- ${recentStderr.join("\n")}`)
37742
+ const squadContext = collectSquadContext(ctx);
37743
+ applySquadContext(blocks, squadContext, squadContextMode(ctx));
37744
+ ctx.squad?.resetHops();
37745
+ let turnBlocks = blocks;
37746
+ let turnPieces = squadContext;
37747
+ let turnPrompt = promptText;
37748
+ let hop = 1;
37749
+ for (; ; ) {
37750
+ const { client: client3, history, budgetRecovery } = ctx;
37751
+ let turnClosed = false;
37752
+ try {
37753
+ const reply = await promptWithContextFallback(ctx, client3, turnBlocks, turnPieces);
37754
+ const finalText = streaming.getCurrentText();
37755
+ if (agentHooks(opts.agent)?.classifyCompletedReply?.(finalText) === "upgrade_required") {
37756
+ await streaming.closeWithBubble(CURSOR_UPGRADE_MESSAGE);
37757
+ turnClosed = true;
37758
+ history.appendAgentReply(CURSOR_UPGRADE_MESSAGE);
37759
+ void history.flush();
37760
+ log.info("acpRunner", `start_task \u2190 cursor-plan-upgrade-required id=${cmd.id.slice(0, 8)}`);
37761
+ await relay.sendResult(cmd.id, "failed", { error: "cursor plan upgrade required" });
37762
+ return;
37763
+ } else if (replyIsHouseAgentLimit(finalText)) {
37764
+ const houseBubble = houseAgentLimitMessage(finalText);
37765
+ await streaming.closeWithBubble(houseBubble);
37766
+ turnClosed = true;
37767
+ history.appendAgentReply(houseBubble);
37768
+ void history.flush();
37769
+ turnFiles.flushTurn().catch((err) => {
37770
+ log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37771
+ });
37772
+ log.info("acpRunner", `start_task \u2190 house-agent-limit id=${cmd.id.slice(0, 8)}`);
37773
+ await relay.sendResult(cmd.id, "failed", {
37774
+ error: "house agent usage ceiling / temporarily unavailable"
37775
+ });
37776
+ return;
37777
+ } else if (replyIsAuthFailure(finalText)) {
37778
+ await streaming.closeWithBubble(AUTH_FAILURE_MESSAGE);
37779
+ turnClosed = true;
37780
+ history.appendAgentReply(AUTH_FAILURE_MESSAGE);
37781
+ void history.flush();
37782
+ turnFiles.flushTurn().catch((err) => {
37783
+ log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37784
+ });
37785
+ void reportCredentialInvalid(opts);
37786
+ log.info("acpRunner", `start_task \u2190 auth-failure-in-reply id=${cmd.id.slice(0, 8)}`);
37787
+ await relay.sendResult(cmd.id, "failed", { error: "agent reply reported auth failure" });
37788
+ return;
37789
+ } else if (shouldOfferOneMRecovery({ detail: "", recentStderr: recentStderr.join("\n"), finalText })) {
37790
+ await streaming.closeWithBubble(ONE_M_CREDITS_MESSAGE);
37791
+ turnClosed = true;
37792
+ history.appendAgentReply(ONE_M_CREDITS_MESSAGE);
37793
+ void history.flush();
37794
+ turnFiles.flushTurn().catch((err) => {
37795
+ log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37796
+ });
37797
+ void reportCredentialInvalid(opts);
37798
+ log.info("acpRunner", `start_task \u2190 1m-credits-reconnect id=${cmd.id.slice(0, 8)}`);
37799
+ await relay.sendResult(cmd.id, "failed", {
37800
+ error: "agent reply reported 1M-context usage-credits gate"
37801
+ });
37802
+ return;
37803
+ } else {
37804
+ await streaming.closeTurnWithInteractiveDetection();
37805
+ turnClosed = true;
37806
+ const { cleanText, proposal } = extractHandoffProposal(
37807
+ finalText,
37808
+ opts.agent,
37809
+ handoffTargets(ctx)
37810
+ );
37811
+ const replyLine = formatAgentReplyLine(cleanText);
37812
+ if (replyLine.length > 0) {
37813
+ showInfo(replyLine);
37814
+ }
37815
+ history.appendAgentReply(cleanText);
37816
+ void history.flush();
37817
+ const flush = turnFiles.flushTurn().catch((err) => {
37818
+ log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
37354
37819
  });
37820
+ if (ctx.squad) await flush;
37821
+ recordSquadTurn(ctx, turnPrompt, cleanText);
37822
+ const autoHop = emitHandoffProposal(ctx, proposal, hop);
37823
+ if (autoHop) {
37824
+ const routed = await routeSquadTask(ctx, autoHop.toAgentId);
37825
+ if (routed.ok) {
37826
+ resolveAutoHandoff(ctx, autoHop);
37827
+ hop += 1;
37828
+ turnPrompt = autoHop.prompt;
37829
+ turnPieces = collectSquadContext(ctx);
37830
+ turnBlocks = [{ type: "text", text: turnPrompt }];
37831
+ maybePrefaceAgentStandard(turnBlocks, opts.agent, opts.sessionId);
37832
+ applySquadContext(turnBlocks, turnPieces, squadContextMode(ctx));
37833
+ await streaming.beginTurn();
37834
+ ctx.history.appendUserPrompt(turnPrompt);
37835
+ continue;
37836
+ }
37837
+ log.warn(
37838
+ "acpRunner",
37839
+ `squad: auto-handoff to ${autoHop.toAgentId} failed: ${routed.error}`
37840
+ );
37841
+ }
37842
+ void publisher.publishOutput({
37843
+ type: "input_suggestion",
37844
+ content: ACP_QUICK_REPLIES,
37845
+ done: true
37846
+ });
37847
+ log.info(
37848
+ "acpRunner",
37849
+ `start_task \u2190 done stopReason=${reply.stopReason ?? "?"} id=${cmd.id.slice(0, 8)}`
37850
+ );
37851
+ await relay.sendResult(cmd.id, "completed", { stopReason: reply.stopReason });
37852
+ return;
37853
+ }
37854
+ } catch (err) {
37855
+ if (turnClosed) {
37856
+ log.warn(
37857
+ "acpRunner",
37858
+ `post-close ack failed (turn already delivered) id=${cmd.id.slice(0, 8)}: ${describeError(err)}`
37859
+ );
37860
+ return;
37355
37861
  }
37356
- await budgetRecovery.offer(cmd.id, blocks, `${detail}
37862
+ const hadText = streaming.hasVisibleProgress();
37863
+ const detail = describeError(err);
37864
+ log.warn("acpRunner", `prompt failed: ${detail}`);
37865
+ await cancelStuckTurn(client3);
37866
+ if (looksLikeBudgetExceeded(`${detail}
37867
+ ${recentStderr.join("\n")}`)) {
37868
+ await streaming.closeAll();
37869
+ if (!budgetReachedFlag.get()) {
37870
+ budgetReachedFlag.set(true);
37871
+ void postBudgetReached({
37872
+ sessionId: opts.sessionId,
37873
+ pluginId: opts.pluginId,
37874
+ pluginAuthToken: opts.pluginAuthToken,
37875
+ agent: opts.agent,
37876
+ period: extractBudgetPeriod(`${detail}
37877
+ ${recentStderr.join("\n")}`)
37878
+ });
37879
+ }
37880
+ await budgetRecovery.offer(cmd.id, turnBlocks, `${detail}
37357
37881
  ${recentStderr.join("\n")}`);
37882
+ return;
37883
+ }
37884
+ const bubble = failureBubble({
37885
+ detail,
37886
+ recentStderr: recentStderr.join("\n"),
37887
+ hadText,
37888
+ agent: opts.agent
37889
+ });
37890
+ if (bubble) {
37891
+ await streaming.closeWithBubble(bubble);
37892
+ history.appendAgentReply(bubble);
37893
+ void history.flush();
37894
+ } else {
37895
+ await streaming.closeAll();
37896
+ }
37897
+ if (bubble === AUTH_FAILURE_MESSAGE || bubble === ONE_M_CREDITS_MESSAGE) {
37898
+ void reportCredentialInvalid(opts);
37899
+ }
37900
+ await relay.sendResult(cmd.id, "failed", { error: detail });
37358
37901
  return;
37359
37902
  }
37360
- const bubble = failureBubble({
37361
- detail,
37362
- recentStderr: recentStderr.join("\n"),
37363
- hadText,
37364
- agent: opts.agent
37903
+ }
37904
+ }
37905
+ async function squadConfigureH(ctx) {
37906
+ const { cmd, relay, opts, squad } = ctx;
37907
+ if (!squad) {
37908
+ await relay.sendResult(cmd.id, "failed", {
37909
+ error: "Agent Squad is not available on this session."
37365
37910
  });
37366
- if (bubble) {
37367
- await streaming.closeWithBubble(bubble);
37368
- history.appendAgentReply(bubble);
37369
- void history.flush();
37370
- } else {
37371
- await streaming.closeAll();
37372
- }
37373
- if (bubble === AUTH_FAILURE_MESSAGE || bubble === ONE_M_CREDITS_MESSAGE) {
37374
- void reportCredentialInvalid(opts);
37375
- }
37376
- await relay.sendResult(cmd.id, "failed", { error: detail });
37911
+ return;
37377
37912
  }
37378
- return;
37913
+ const payload = cmd.payload;
37914
+ if (payload?.action === "set") {
37915
+ const applied = squad.setAuto({
37916
+ enabled: payload.autoHandoffs === true,
37917
+ hopBudget: clampHopBudget(payload.hopBudget)
37918
+ });
37919
+ setSquadAuto(opts.pluginId, applied);
37920
+ log.info(
37921
+ "acpRunner",
37922
+ `squad: auto handoffs ${applied.enabled ? "ON" : "OFF"} budget=${applied.hopBudget}`
37923
+ );
37924
+ const result = { ...applied, hopsRemaining: squad.hopsRemaining() };
37925
+ await relay.sendResult(cmd.id, "completed", { ...result });
37926
+ return;
37927
+ }
37928
+ if (payload?.action === "status") {
37929
+ const result = {
37930
+ ...squad.auto,
37931
+ hopsRemaining: squad.hopsRemaining()
37932
+ };
37933
+ await relay.sendResult(cmd.id, "completed", { ...result });
37934
+ return;
37935
+ }
37936
+ await relay.sendResult(cmd.id, "failed", { error: "squad_configure: unknown action" });
37937
+ }
37938
+ async function squadStatsH(ctx) {
37939
+ const { cmd, relay, squad } = ctx;
37940
+ if (!squad) {
37941
+ await relay.sendResult(cmd.id, "failed", {
37942
+ error: "Agent Squad is not available on this session."
37943
+ });
37944
+ return;
37945
+ }
37946
+ const stats = squad.stats();
37947
+ await relay.sendResult(cmd.id, "completed", { ...stats });
37379
37948
  }
37380
37949
  async function groupMentionTaskH(ctx) {
37381
37950
  const { cmd, client: client3, relay, streaming, opts, history } = ctx;
@@ -37388,6 +37957,7 @@ async function groupMentionTaskH(ctx) {
37388
37957
  });
37389
37958
  return;
37390
37959
  }
37960
+ ctx.squad?.resetHops();
37391
37961
  await streaming.beginTurn();
37392
37962
  history.appendUserPrompt(promptText);
37393
37963
  let response = "";
@@ -37782,7 +38352,10 @@ async function integrationsSyncH(ctx) {
37782
38352
  attached: manifest.integrations.map((e) => e.id)
37783
38353
  });
37784
38354
  } catch (err) {
37785
- log.warn("acpRunner", `integrations_sync failed (tools apply next restart): ${describeError(err)}`);
38355
+ log.warn(
38356
+ "acpRunner",
38357
+ `integrations_sync failed (tools apply next restart): ${describeError(err)}`
38358
+ );
37786
38359
  await relay.sendResult(cmd.id, "completed", { synced: false, error: describeError(err) });
37787
38360
  }
37788
38361
  }
@@ -37852,7 +38425,9 @@ var ACP_COMMAND_HANDLERS = {
37852
38425
  skills_configure: skillsConfigureH2,
37853
38426
  pack_start: packStartH,
37854
38427
  pack_action: packActionH,
37855
- pack_status: packStatusH
38428
+ pack_status: packStatusH,
38429
+ [SQUAD_CONFIGURE_COMMAND]: squadConfigureH,
38430
+ [SQUAD_STATS_COMMAND]: squadStatsH
37856
38431
  };
37857
38432
  async function dispatchAcpCommand(ctx) {
37858
38433
  const handler = ACP_COMMAND_HANDLERS[ctx.cmd.type];
@@ -38117,7 +38692,7 @@ var StreamingState = class {
38117
38692
  let fenceCut = -1;
38118
38693
  if (delta.kind === "text") {
38119
38694
  this.recomputeText();
38120
- fenceCut = handoffFenceStart(this.text);
38695
+ fenceCut = handoffFenceStartMasked(this.text);
38121
38696
  const visibleText = fenceCut === -1 ? this.text : this.text.slice(0, fenceCut).trimEnd();
38122
38697
  void this.publisher.publishOutput({ type: "text", content: visibleText, done: false });
38123
38698
  }
@@ -38165,11 +38740,12 @@ var StreamingState = class {
38165
38740
  * {@link getCurrentText} keeps returning the RAW text so the turn-close
38166
38741
  * extraction can parse the proposal out of it.
38167
38742
  *
38168
- * Masked-aware (`stripHandoffFences`), unlike the live-stream `append()`
38169
- * cut (`handoffFenceStart`, unmasked): a TERMINAL frame is the one that
38743
+ * Masked-aware (`stripHandoffFences`), same as the live-stream `append()`
38744
+ * cut (`handoffFenceStartMasked`): a TERMINAL frame is the one that
38170
38745
  * 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.
38746
+ * 4+-backtick block here would permanently truncate the bubble and an
38747
+ * unmasked live cut would truncate the LIVE view for the rest of the turn
38748
+ * the moment the quoted example's fence-open marker streams in.
38173
38749
  */
38174
38750
  visible(text) {
38175
38751
  return stripHandoffFences(text);
@@ -38345,16 +38921,24 @@ var AcpHistory = class {
38345
38921
  id: (0, import_node_crypto11.randomUUID)(),
38346
38922
  role: "user",
38347
38923
  text,
38348
- timestamp: Date.now()
38924
+ timestamp: Date.now(),
38925
+ agentId: this.opts.agent
38349
38926
  });
38350
38927
  }
38351
- appendAgentReply(text) {
38928
+ /**
38929
+ * `agentId` overrides the producing agent for a turn this session's RESIDENT
38930
+ * agent did not author — today only the `@coderabbit` one-shot review, which
38931
+ * runs the batch reviewer inside the session without ever swapping. Defaults
38932
+ * to the resident agent.
38933
+ */
38934
+ appendAgentReply(text, agentId = this.opts.agent) {
38352
38935
  if (text.length === 0) return;
38353
38936
  this.messages.push({
38354
38937
  id: (0, import_node_crypto11.randomUUID)(),
38355
38938
  role: "agent",
38356
38939
  text,
38357
- timestamp: Date.now()
38940
+ timestamp: Date.now(),
38941
+ agentId
38358
38942
  });
38359
38943
  }
38360
38944
  /**
@@ -38694,6 +39278,8 @@ async function runAcpSession(opts) {
38694
39278
  pluginAuthToken: opts.pluginAuthToken,
38695
39279
  agentId: opts.agent
38696
39280
  });
39281
+ void turnFiles.flushTurn().catch(() => {
39282
+ });
38697
39283
  const REPO_DIRTY_FLUSH_DEBOUNCE_MS = 2e3;
38698
39284
  let repoDirtyTimer = null;
38699
39285
  const fileWatcher = new FileWatcherService({
@@ -38739,9 +39325,12 @@ async function runAcpSession(opts) {
38739
39325
  publisher,
38740
39326
  recentStderr,
38741
39327
  budgetRecovery,
38742
- { get: () => _budgetReachedPosted, set: (v) => {
38743
- _budgetReachedPosted = v;
38744
- } },
39328
+ {
39329
+ get: () => _budgetReachedPosted,
39330
+ set: (v) => {
39331
+ _budgetReachedPosted = v;
39332
+ }
39333
+ },
38745
39334
  // resume_session re-points the runner's active conversation: the
38746
39335
  // relay callback reads `acpSessionId` per command, so every FUTURE
38747
39336
  // get_conversation / upload / one-shot serves the RESUMED id — not
@@ -38765,7 +39354,10 @@ async function runAcpSession(opts) {
38765
39354
  );
38766
39355
  const HANDOFF_MAX_CHARS = 16e3;
38767
39356
  const pendingHandoff = { current: null };
38768
- const squad = new SquadState({ sessionId: opts.sessionId });
39357
+ const squad = new SquadState({
39358
+ sessionId: opts.sessionId,
39359
+ auto: getSquadAuto(opts.pluginId)
39360
+ });
38769
39361
  const refreshSquadRoster = () => {
38770
39362
  void fetchSquadRoster({
38771
39363
  sessionId: opts.sessionId,
@@ -38849,7 +39441,7 @@ async function runAcpSession(opts) {
38849
39441
  provisionCredential: (agentId, auth) => {
38850
39442
  switchCredentialEnv = provisionAgentCredentials(agentId, auth);
38851
39443
  },
38852
- ensureBinary: (agentId, installScript) => ensureAgentBinaryForSwitch(agentId, installScript),
39444
+ ensureBinary: (agentId, installScript, onRetry) => ensureAgentBinaryForSwitch(agentId, installScript, { onRetry }),
38853
39445
  swapRuntime: relaunchWith,
38854
39446
  revertRuntime: relaunchWith,
38855
39447
  persistAgent: (agentId) => setSessionAgent(opts.pluginId, agentId),
@@ -43765,7 +44357,7 @@ function checkChokidar() {
43765
44357
  }
43766
44358
  async function doctor(args2 = []) {
43767
44359
  const json = args2.includes("--json");
43768
- const cliVersion = true ? "2.64.0" : "0.0.0-dev";
44360
+ const cliVersion = true ? "2.65.1" : "0.0.0-dev";
43769
44361
  const apiBase2 = resolveApiBaseUrl();
43770
44362
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
43771
44363
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -44156,7 +44748,7 @@ async function mcpRun(args2) {
44156
44748
  // src/commands/version.ts
44157
44749
  var import_picocolors15 = __toESM(require("picocolors"));
44158
44750
  function version2() {
44159
- const v = true ? "2.64.0" : "unknown";
44751
+ const v = true ? "2.65.1" : "unknown";
44160
44752
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
44161
44753
  }
44162
44754