codeam-cli 2.49.5 → 2.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/index.js +729 -222
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -522,9 +522,9 @@ var _default = makeConfig();
522
522
  var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, clearAll, saveCliConfig, loadCliConfig } = _default;
523
523
 
524
524
  // src/commands/pair-auto.ts
525
- var fs41 = __toESM(require("fs"));
526
- var os34 = __toESM(require("os"));
527
- var path47 = __toESM(require("path"));
525
+ var fs42 = __toESM(require("fs"));
526
+ var os35 = __toESM(require("os"));
527
+ var path48 = __toESM(require("path"));
528
528
  var import_crypto4 = require("crypto");
529
529
 
530
530
  // src/services/telemetry.service.ts
@@ -560,8 +560,8 @@ function createGetModuleFromFilename(basePath = process.argv[1] ? (0, import_pat
560
560
  return decodedFile;
561
561
  };
562
562
  }
563
- function normalizeWindowsPath(path60) {
564
- return path60.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
563
+ function normalizeWindowsPath(path61) {
564
+ return path61.replace(/^[A-Z]:/, "").replace(/\\/g, "/");
565
565
  }
566
566
 
567
567
  // ../../node_modules/@posthog/core/dist/featureFlagUtils.mjs
@@ -3041,9 +3041,9 @@ async function addSourceContext(frames) {
3041
3041
  LRU_FILE_CONTENTS_CACHE.reduce();
3042
3042
  return frames;
3043
3043
  }
3044
- function getContextLinesFromFile(path60, ranges, output) {
3044
+ function getContextLinesFromFile(path61, ranges, output) {
3045
3045
  return new Promise((resolve7) => {
3046
- const stream = (0, import_node_fs.createReadStream)(path60);
3046
+ const stream = (0, import_node_fs.createReadStream)(path61);
3047
3047
  const lineReaded = (0, import_node_readline.createInterface)({
3048
3048
  input: stream
3049
3049
  });
@@ -3058,7 +3058,7 @@ function getContextLinesFromFile(path60, ranges, output) {
3058
3058
  let rangeStart = range[0];
3059
3059
  let rangeEnd = range[1];
3060
3060
  function onStreamError() {
3061
- LRU_FILE_CONTENTS_FS_READ_FAILED.set(path60, 1);
3061
+ LRU_FILE_CONTENTS_FS_READ_FAILED.set(path61, 1);
3062
3062
  lineReaded.close();
3063
3063
  lineReaded.removeAllListeners();
3064
3064
  destroyStreamAndResolve();
@@ -3119,8 +3119,8 @@ function clearLineContext(frame) {
3119
3119
  delete frame.context_line;
3120
3120
  delete frame.post_context;
3121
3121
  }
3122
- function shouldSkipContextLinesForFile(path60) {
3123
- return path60.startsWith("node:") || path60.endsWith(".min.js") || path60.endsWith(".min.cjs") || path60.endsWith(".min.mjs") || path60.startsWith("data:");
3122
+ function shouldSkipContextLinesForFile(path61) {
3123
+ return path61.startsWith("node:") || path61.endsWith(".min.js") || path61.endsWith(".min.cjs") || path61.endsWith(".min.mjs") || path61.startsWith("data:");
3124
3124
  }
3125
3125
  function shouldSkipContextLinesForFrame(frame) {
3126
3126
  if (void 0 !== frame.lineno && frame.lineno > MAX_CONTEXTLINES_LINENO) return true;
@@ -5397,7 +5397,7 @@ function readAnonId() {
5397
5397
  }
5398
5398
  function superProperties() {
5399
5399
  return {
5400
- cliVersion: true ? "2.49.5" : "0.0.0-dev",
5400
+ cliVersion: true ? "2.51.0" : "0.0.0-dev",
5401
5401
  nodeVersion: process.version,
5402
5402
  platform: process.platform,
5403
5403
  arch: process.arch,
@@ -5578,7 +5578,7 @@ var os4 = __toESM(require("os"));
5578
5578
  // package.json
5579
5579
  var package_default = {
5580
5580
  name: "codeam-cli",
5581
- version: "2.49.5",
5581
+ version: "2.51.0",
5582
5582
  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.",
5583
5583
  type: "commonjs",
5584
5584
  main: "dist/index.js",
@@ -5871,6 +5871,28 @@ async function postHeadroomEvent(input) {
5871
5871
  };
5872
5872
  }
5873
5873
  }
5874
+ async function postBeadsEvent(input) {
5875
+ try {
5876
+ await _transport.postJsonAuthed(
5877
+ `${API_BASE}/api/beads/events`,
5878
+ {
5879
+ sessionId: input.sessionId,
5880
+ pluginId: input.pluginId,
5881
+ type: input.type,
5882
+ payload: input.payload ?? {}
5883
+ },
5884
+ input.pluginAuthToken
5885
+ );
5886
+ return { ok: true };
5887
+ } catch (err) {
5888
+ const e = err;
5889
+ return {
5890
+ ok: false,
5891
+ status: typeof e.statusCode === "number" ? e.statusCode : 0,
5892
+ message: e.message || "unknown"
5893
+ };
5894
+ }
5895
+ }
5874
5896
  async function postBeadsProvisioning(input) {
5875
5897
  try {
5876
5898
  await _transport.postJsonAuthed(
@@ -7706,9 +7728,9 @@ function closeAllTerminals() {
7706
7728
  }
7707
7729
 
7708
7730
  // src/commands/start/handlers.ts
7709
- var fs40 = __toESM(require("fs"));
7710
- var os33 = __toESM(require("os"));
7711
- var path46 = __toESM(require("path"));
7731
+ var fs41 = __toESM(require("fs"));
7732
+ var os34 = __toESM(require("os"));
7733
+ var path47 = __toESM(require("path"));
7712
7734
  var import_crypto3 = require("crypto");
7713
7735
  var import_child_process20 = require("child_process");
7714
7736
  var import_which2 = __toESM(require("which"));
@@ -11142,10 +11164,10 @@ function buildForPlatform(platform3) {
11142
11164
  var import_node_crypto4 = require("crypto");
11143
11165
 
11144
11166
  // src/agents/claude/resolver.ts
11145
- function buildClaudeLaunch(extraArgs = [], os40 = createOsStrategy()) {
11146
- const found = os40.findInPath("claude") ?? os40.findInPath("claude-code");
11167
+ function buildClaudeLaunch(extraArgs = [], os41 = createOsStrategy()) {
11168
+ const found = os41.findInPath("claude") ?? os41.findInPath("claude-code");
11147
11169
  if (!found) return null;
11148
- return os40.buildLaunch(found, extraArgs);
11170
+ return os41.buildLaunch(found, extraArgs);
11149
11171
  }
11150
11172
 
11151
11173
  // src/agents/claude/installer.ts
@@ -11738,8 +11760,8 @@ var ClaudeRuntimeStrategy = class {
11738
11760
  meta = getAgent("claude");
11739
11761
  mode = "interactive";
11740
11762
  os;
11741
- constructor(os40) {
11742
- this.os = os40;
11763
+ constructor(os41) {
11764
+ this.os = os41;
11743
11765
  }
11744
11766
  /**
11745
11767
  * Claude Code's react-ink TUI enables bracketed-paste mode at
@@ -12416,8 +12438,8 @@ function codexCredentialLocator() {
12416
12438
  function codexLoginLauncher() {
12417
12439
  return {
12418
12440
  async ensureInstalled() {
12419
- const os40 = createOsStrategy();
12420
- return os40.findInPath("codex") !== null;
12441
+ const os41 = createOsStrategy();
12442
+ return os41.findInPath("codex") !== null;
12421
12443
  },
12422
12444
  launch() {
12423
12445
  return (0, import_node_child_process3.spawn)("codex", ["login"], { stdio: "inherit" });
@@ -12440,8 +12462,8 @@ var CodexRuntimeStrategy = class {
12440
12462
  meta = getAgent("codex");
12441
12463
  mode = "interactive";
12442
12464
  os;
12443
- constructor(os40) {
12444
- this.os = os40;
12465
+ constructor(os41) {
12466
+ this.os = os41;
12445
12467
  }
12446
12468
  async prepareLaunch() {
12447
12469
  let binary = this.os.findInPath("codex");
@@ -12540,12 +12562,12 @@ var CodexRuntimeStrategy = class {
12540
12562
  });
12541
12563
  }
12542
12564
  };
12543
- function resolveNpm(os40) {
12544
- return os40.id === "win32" ? "npm.cmd" : "npm";
12565
+ function resolveNpm(os41) {
12566
+ return os41.id === "win32" ? "npm.cmd" : "npm";
12545
12567
  }
12546
- async function installCodexViaNpm(os40) {
12568
+ async function installCodexViaNpm(os41) {
12547
12569
  return new Promise((resolve7, reject) => {
12548
- const proc = (0, import_node_child_process4.spawn)(resolveNpm(os40), ["install", "-g", "@openai/codex"], {
12570
+ const proc = (0, import_node_child_process4.spawn)(resolveNpm(os41), ["install", "-g", "@openai/codex"], {
12549
12571
  stdio: "inherit"
12550
12572
  });
12551
12573
  proc.on("close", (code) => {
@@ -12562,16 +12584,16 @@ async function installCodexViaNpm(os40) {
12562
12584
  });
12563
12585
  });
12564
12586
  }
12565
- function augmentNpmGlobalBin(os40) {
12587
+ function augmentNpmGlobalBin(os41) {
12566
12588
  try {
12567
- const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os40), ["prefix", "-g"], {
12589
+ const result = (0, import_node_child_process4.spawnSync)(resolveNpm(os41), ["prefix", "-g"], {
12568
12590
  stdio: ["ignore", "pipe", "ignore"]
12569
12591
  });
12570
12592
  if (result.status !== 0) return;
12571
12593
  const prefix = result.stdout.toString().trim();
12572
12594
  if (!prefix) return;
12573
- const binDir = os40.id === "win32" ? prefix : path22.join(prefix, "bin");
12574
- os40.augmentPath([binDir]);
12595
+ const binDir = os41.id === "win32" ? prefix : path22.join(prefix, "bin");
12596
+ os41.augmentPath([binDir]);
12575
12597
  } catch {
12576
12598
  }
12577
12599
  }
@@ -12655,9 +12677,9 @@ var import_node_child_process7 = require("child_process");
12655
12677
  // src/agents/coderabbit/installer.ts
12656
12678
  var import_node_child_process5 = require("child_process");
12657
12679
  var INSTALL_URL = "https://cli.coderabbit.ai/install.sh";
12658
- async function ensureCoderabbitInstalled(os40) {
12659
- if (os40.findInPath("coderabbit")) return true;
12660
- if (os40.id === "win32") {
12680
+ async function ensureCoderabbitInstalled(os41) {
12681
+ if (os41.findInPath("coderabbit")) return true;
12682
+ if (os41.id === "win32") {
12661
12683
  console.error(
12662
12684
  "\n \u2717 CodeRabbit on Windows requires WSL.\n Install the CLI inside your WSL distribution\n (curl -fsSL https://cli.coderabbit.ai/install.sh | sh)\n then re-run `codeam link coderabbit` from WSL.\n"
12663
12685
  );
@@ -12672,8 +12694,8 @@ async function ensureCoderabbitInstalled(os40) {
12672
12694
  proc.on("error", () => resolve7(false));
12673
12695
  });
12674
12696
  if (!ok) return false;
12675
- os40.augmentPath([`${os40.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
12676
- return os40.findInPath("coderabbit") !== null;
12697
+ os41.augmentPath([`${os41.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
12698
+ return os41.findInPath("coderabbit") !== null;
12677
12699
  }
12678
12700
 
12679
12701
  // src/agents/coderabbit/link.ts
@@ -12708,10 +12730,10 @@ function coderabbitCredentialLocator() {
12708
12730
  validate: validateNonEmptyCredential
12709
12731
  };
12710
12732
  }
12711
- function coderabbitLoginLauncher(os40) {
12733
+ function coderabbitLoginLauncher(os41) {
12712
12734
  return {
12713
12735
  async ensureInstalled() {
12714
- return ensureCoderabbitInstalled(os40);
12736
+ return ensureCoderabbitInstalled(os41);
12715
12737
  },
12716
12738
  launch() {
12717
12739
  return (0, import_node_child_process6.spawn)("coderabbit", ["login"], { stdio: "inherit" });
@@ -12734,11 +12756,11 @@ function parseReview(stdout) {
12734
12756
  for (const line of lines) {
12735
12757
  const m = line.match(HUNK_LINE_RE);
12736
12758
  if (!m) continue;
12737
- const [, path60, lineNo, sevToken, message] = m;
12738
- if (!path60 || !lineNo || !message) continue;
12759
+ const [, path61, lineNo, sevToken, message] = m;
12760
+ if (!path61 || !lineNo || !message) continue;
12739
12761
  const cleanedMessage = message.trim().replace(/^[*-]\s+/, "");
12740
12762
  hunks.push({
12741
- path: path60.trim(),
12763
+ path: path61.trim(),
12742
12764
  line: Number(lineNo),
12743
12765
  severity: sevToken ? SEVERITY_MAP[sevToken.toLowerCase()] : void 0,
12744
12766
  message: cleanedMessage
@@ -12757,8 +12779,8 @@ var CoderabbitRuntimeStrategy = class {
12757
12779
  meta = getAgent("coderabbit");
12758
12780
  mode = "batch";
12759
12781
  os;
12760
- constructor(os40) {
12761
- this.os = os40;
12782
+ constructor(os41) {
12783
+ this.os = os41;
12762
12784
  }
12763
12785
  getDefaultArgs() {
12764
12786
  return ["review"];
@@ -12874,10 +12896,10 @@ function cursorCredentialLocator() {
12874
12896
  validate: validateNonEmptyCredential
12875
12897
  };
12876
12898
  }
12877
- function cursorLoginLauncher(os40) {
12899
+ function cursorLoginLauncher(os41) {
12878
12900
  return {
12879
12901
  async ensureInstalled() {
12880
- if (os40.findInPath("cursor-agent")) return true;
12902
+ if (os41.findInPath("cursor-agent")) return true;
12881
12903
  console.error(
12882
12904
  "\n \u2717 cursor-agent binary not on PATH.\n Install Cursor (https://cursor.com/) and ensure the CLI\n plugin is enabled, then re-run `codeam link cursor`.\n"
12883
12905
  );
@@ -12939,8 +12961,8 @@ var CursorRuntimeStrategy = class {
12939
12961
  meta = getAgent("cursor");
12940
12962
  mode = "interactive";
12941
12963
  os;
12942
- constructor(os40) {
12943
- this.os = os40;
12964
+ constructor(os41) {
12965
+ this.os = os41;
12944
12966
  }
12945
12967
  async prepareLaunch() {
12946
12968
  const binary = this.os.findInPath("cursor-agent");
@@ -13079,10 +13101,10 @@ function aiderCredentialLocator() {
13079
13101
  validate: validateNonEmptyCredential
13080
13102
  };
13081
13103
  }
13082
- function aiderLoginLauncher(os40) {
13104
+ function aiderLoginLauncher(os41) {
13083
13105
  return {
13084
13106
  async ensureInstalled() {
13085
- if (os40.findInPath("aider")) return true;
13107
+ if (os41.findInPath("aider")) return true;
13086
13108
  console.error(
13087
13109
  "\n \u2717 aider binary not on PATH.\n Install Aider:\n pip install aider-chat\n then re-run `codeam link aider`.\n"
13088
13110
  );
@@ -13092,7 +13114,7 @@ function aiderLoginLauncher(os40) {
13092
13114
  console.error(
13093
13115
  "\n Aider has no interactive login flow.\n Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your shell,\n or re-run `codeam link aider --api-key=<your-key>`.\n"
13094
13116
  );
13095
- return (0, import_node_child_process9.spawn)(os40.id === "win32" ? "cmd.exe" : "sh", os40.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
13117
+ return (0, import_node_child_process9.spawn)(os41.id === "win32" ? "cmd.exe" : "sh", os41.id === "win32" ? ["/c", "exit", "0"] : ["-c", "exit 0"], {
13096
13118
  stdio: "ignore"
13097
13119
  });
13098
13120
  }
@@ -13164,8 +13186,8 @@ var AiderRuntimeStrategy = class {
13164
13186
  meta = getAgent("aider");
13165
13187
  mode = "interactive";
13166
13188
  os;
13167
- constructor(os40) {
13168
- this.os = os40;
13189
+ constructor(os41) {
13190
+ this.os = os41;
13169
13191
  }
13170
13192
  async prepareLaunch() {
13171
13193
  const binary = this.os.findInPath("aider");
@@ -13294,8 +13316,8 @@ function geminiCredentialLocator() {
13294
13316
  function geminiLoginLauncher() {
13295
13317
  return {
13296
13318
  async ensureInstalled() {
13297
- const os40 = createOsStrategy();
13298
- return os40.findInPath("gemini") !== null;
13319
+ const os41 = createOsStrategy();
13320
+ return os41.findInPath("gemini") !== null;
13299
13321
  },
13300
13322
  launch() {
13301
13323
  return (0, import_node_child_process10.spawn)("gemini", ["auth", "login"], { stdio: "inherit" });
@@ -13328,8 +13350,8 @@ var GeminiRuntimeStrategy = class {
13328
13350
  meta = getAgent("gemini");
13329
13351
  mode = "interactive";
13330
13352
  os;
13331
- constructor(os40) {
13332
- this.os = os40;
13353
+ constructor(os41) {
13354
+ this.os = os41;
13333
13355
  }
13334
13356
  async prepareLaunch() {
13335
13357
  const binary = this.os.findInPath("gemini");
@@ -13428,18 +13450,18 @@ var GeminiRuntimeStrategy = class {
13428
13450
 
13429
13451
  // src/agents/registry.ts
13430
13452
  var runtimeBuilders = {
13431
- claude: (os40) => new ClaudeRuntimeStrategy(os40),
13432
- codex: (os40) => new CodexRuntimeStrategy(os40),
13433
- coderabbit: (os40) => new CoderabbitRuntimeStrategy(os40),
13434
- cursor: (os40) => new CursorRuntimeStrategy(os40),
13435
- aider: (os40) => new AiderRuntimeStrategy(os40),
13436
- gemini: (os40) => new GeminiRuntimeStrategy(os40)
13453
+ claude: (os41) => new ClaudeRuntimeStrategy(os41),
13454
+ codex: (os41) => new CodexRuntimeStrategy(os41),
13455
+ coderabbit: (os41) => new CoderabbitRuntimeStrategy(os41),
13456
+ cursor: (os41) => new CursorRuntimeStrategy(os41),
13457
+ aider: (os41) => new AiderRuntimeStrategy(os41),
13458
+ gemini: (os41) => new GeminiRuntimeStrategy(os41)
13437
13459
  };
13438
13460
  var deployBuilders = {
13439
13461
  claude: () => new ClaudeDeployStrategy(),
13440
13462
  codex: () => new CodexDeployStrategy()
13441
13463
  };
13442
- function createAgentStrategy(agent, os40 = createOsStrategy()) {
13464
+ function createAgentStrategy(agent, os41 = createOsStrategy()) {
13443
13465
  if (!AGENT_REGISTRY[agent]?.enabled) {
13444
13466
  throw new Error(
13445
13467
  `Agent "${agent}" is not supported in this codeam-cli version. Upgrade with 'npm i -g codeam-cli@latest'.`
@@ -13449,10 +13471,10 @@ function createAgentStrategy(agent, os40 = createOsStrategy()) {
13449
13471
  if (!build) {
13450
13472
  throw new Error(`No runtime strategy registered for agent "${agent}"`);
13451
13473
  }
13452
- return build(os40);
13474
+ return build(os41);
13453
13475
  }
13454
- function createInteractiveAgentStrategy(agent, os40 = createOsStrategy()) {
13455
- const s = createAgentStrategy(agent, os40);
13476
+ function createInteractiveAgentStrategy(agent, os41 = createOsStrategy()) {
13477
+ const s = createAgentStrategy(agent, os41);
13456
13478
  if (s.mode !== "interactive") {
13457
13479
  throw new Error(
13458
13480
  `Agent "${agent}" is a batch agent; use createAgentStrategy + .runOneShot for one-shot reviews.`
@@ -14556,6 +14578,14 @@ function mapStatsToSavings(stats, prev, inputPricePerMillion = DEFAULT_INPUT_PRI
14556
14578
  };
14557
14579
  return { next, delta };
14558
14580
  }
14581
+ function defaultGetBudgetEnv() {
14582
+ const raw = process.env["HEADROOM_BUDGET"];
14583
+ if (!raw) return null;
14584
+ const budgetUsd = Number(raw);
14585
+ if (!isFinite(budgetUsd) || budgetUsd <= 0) return null;
14586
+ const budgetPeriod = process.env["HEADROOM_BUDGET_PERIOD"] ?? "daily";
14587
+ return { budgetUsd, budgetPeriod };
14588
+ }
14559
14589
  var HeadroomStatsReporter = class {
14560
14590
  constructor(deps) {
14561
14591
  this.deps = deps;
@@ -14569,14 +14599,22 @@ var HeadroomStatsReporter = class {
14569
14599
  }
14570
14600
  async tick() {
14571
14601
  try {
14602
+ const stats = await this.deps.fetchStats();
14572
14603
  const { next, delta } = mapStatsToSavings(
14573
- await this.deps.fetchStats(),
14604
+ stats,
14574
14605
  this.prev,
14575
14606
  this.deps.inputPricePerMillionUsd ?? DEFAULT_INPUT_PRICE_PER_MILLION
14576
14607
  );
14577
14608
  this.prev = next;
14578
14609
  if (delta.compressionTokens > 0 || delta.compressionSavingsUsd > 0 || delta.rawTokensEst > 0 || delta.cacheReadTokens > 0 || delta.cacheSavingsUsd > 0) {
14579
- await this.deps.postSavings(delta);
14610
+ const getBudgetEnv = this.deps.getBudgetEnv ?? defaultGetBudgetEnv;
14611
+ const budgetEnv = getBudgetEnv();
14612
+ const budget = budgetEnv ? {
14613
+ periodSpendUsd: stats.cost?.cost_with_headroom_usd ?? 0,
14614
+ budgetUsd: budgetEnv.budgetUsd,
14615
+ budgetPeriod: budgetEnv.budgetPeriod
14616
+ } : void 0;
14617
+ await this.deps.postSavings(delta, budget);
14580
14618
  }
14581
14619
  } catch {
14582
14620
  }
@@ -14589,6 +14627,14 @@ var HeadroomStatsReporter = class {
14589
14627
  }
14590
14628
  };
14591
14629
 
14630
+ // src/services/headroom/budget-args.ts
14631
+ function buildBudgetProxyArgs(env) {
14632
+ const budget = env["HEADROOM_BUDGET"];
14633
+ if (!budget) return [];
14634
+ const period = env["HEADROOM_BUDGET_PERIOD"] ?? "daily";
14635
+ return ["--budget", budget, "--budget-period", period];
14636
+ }
14637
+
14592
14638
  // src/lib/updateNotifier.ts
14593
14639
  var fs30 = __toESM(require("fs"));
14594
14640
  var os27 = __toESM(require("os"));
@@ -14747,7 +14793,7 @@ async function autoUpgradeBeforeCriticalCommand() {
14747
14793
  if (process.env.NODE_ENV === "test") return;
14748
14794
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
14749
14795
  if (process.env.CI) return;
14750
- const current = true ? "2.49.5" : null;
14796
+ const current = true ? "2.51.0" : null;
14751
14797
  if (!current) return;
14752
14798
  const cache = readCache();
14753
14799
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -14764,7 +14810,7 @@ function checkForUpdates() {
14764
14810
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
14765
14811
  if (process.env.CI) return;
14766
14812
  if (!process.stdout.isTTY) return;
14767
- const current = true ? "2.49.5" : null;
14813
+ const current = true ? "2.51.0" : null;
14768
14814
  if (!current) return;
14769
14815
  const cache = readCache();
14770
14816
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -14801,7 +14847,7 @@ function maybeStartHeadroomReporter(ctx) {
14801
14847
  const res = await fetch("http://localhost:8787/stats");
14802
14848
  return res.json();
14803
14849
  },
14804
- postSavings: async (delta) => {
14850
+ postSavings: async (delta, budget) => {
14805
14851
  await fetch(ingestUrl, {
14806
14852
  method: "POST",
14807
14853
  headers: {
@@ -14812,7 +14858,12 @@ function maybeStartHeadroomReporter(ctx) {
14812
14858
  sessionId: ctx.sessionId,
14813
14859
  pluginId: ctx.pluginId,
14814
14860
  agentId: process.env["HEADROOM_AGENT"] ?? "claude",
14815
- savings: delta
14861
+ savings: delta,
14862
+ ...budget ? {
14863
+ periodSpendUsd: budget.periodSpendUsd,
14864
+ budgetUsd: budget.budgetUsd,
14865
+ budgetPeriod: budget.budgetPeriod
14866
+ } : {}
14816
14867
  })
14817
14868
  });
14818
14869
  }
@@ -14845,7 +14896,7 @@ function maybeResumeLocalHeadroomReporter(ctx) {
14845
14896
  // Body MUST match HeadroomSavingsDto + PluginAuthGuard (sessionId +
14846
14897
  // pluginId in the body) — same shape as the codespace reporter above and
14847
14898
  // the on-demand `startReporter` in handlers.ts.
14848
- postSavings: async (delta) => {
14899
+ postSavings: async (delta, budget) => {
14849
14900
  await fetch(ingestUrl, {
14850
14901
  method: "POST",
14851
14902
  headers: {
@@ -14856,7 +14907,12 @@ function maybeResumeLocalHeadroomReporter(ctx) {
14856
14907
  sessionId: ctx.sessionId,
14857
14908
  pluginId: ctx.pluginId,
14858
14909
  agentId: agent,
14859
- savings: delta
14910
+ savings: delta,
14911
+ ...budget ? {
14912
+ periodSpendUsd: budget.periodSpendUsd,
14913
+ budgetUsd: budget.budgetUsd,
14914
+ budgetPeriod: budget.budgetPeriod
14915
+ } : {}
14860
14916
  })
14861
14917
  });
14862
14918
  }
@@ -15157,11 +15213,16 @@ function readHeadroomChildEnv() {
15157
15213
  if (typeof parsed !== "object" || parsed === null) return {};
15158
15214
  const o = parsed;
15159
15215
  if (o.enabled === true && typeof o.agent === "string" && o.agent.length > 0 && typeof o.ingestUrl === "string" && o.ingestUrl.length > 0) {
15160
- return {
15216
+ const env = {
15161
15217
  HEADROOM_ENABLED: "1",
15162
15218
  HEADROOM_AGENT: o.agent,
15163
15219
  HEADROOM_SAVINGS_INGEST_URL: o.ingestUrl
15164
15220
  };
15221
+ if (o.budgetEnabled === true && typeof o.budgetUsd === "number") {
15222
+ env["HEADROOM_BUDGET"] = String(o.budgetUsd);
15223
+ env["HEADROOM_BUDGET_PERIOD"] = typeof o.budgetPeriod === "string" ? o.budgetPeriod : "daily";
15224
+ }
15225
+ return env;
15165
15226
  }
15166
15227
  return {};
15167
15228
  } catch {
@@ -15417,11 +15478,16 @@ async function setupHeadroomForSelfHosted(agent, runner = defaultHeadroomRunner,
15417
15478
  }
15418
15479
  onProgress("proxy");
15419
15480
  try {
15420
- const proxy = (0, import_node_child_process14.spawn)("headroom", ["proxy", "--port", "8787"], {
15421
- stdio: "ignore",
15422
- detached: true,
15423
- env: { ...process.env, HEADROOM_KOMPRESS_BACKEND: "onnx_cpu" }
15424
- });
15481
+ const proxyEnv = { ...process.env, HEADROOM_KOMPRESS_BACKEND: "onnx_cpu" };
15482
+ const proxy = (0, import_node_child_process14.spawn)(
15483
+ "headroom",
15484
+ ["proxy", "--port", "8787", ...buildBudgetProxyArgs(proxyEnv)],
15485
+ {
15486
+ stdio: "ignore",
15487
+ detached: true,
15488
+ env: proxyEnv
15489
+ }
15490
+ );
15425
15491
  proxy.once("error", (e) => {
15426
15492
  log.warn(
15427
15493
  "host-agent",
@@ -15445,7 +15511,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process14.s
15445
15511
  detached: false
15446
15512
  });
15447
15513
  function currentCliVersion() {
15448
- return true ? "2.49.5" : null;
15514
+ return true ? "2.51.0" : null;
15449
15515
  }
15450
15516
  function runCmd(cmd, args2, timeoutMs) {
15451
15517
  return new Promise((resolve7) => {
@@ -17126,11 +17192,11 @@ function resolveDoltInstallStrategy(platform3) {
17126
17192
  }
17127
17193
  var DOLT_RELEASE_BASE = "https://github.com/dolthub/dolt/releases/latest/download";
17128
17194
  function doltPlatformTuple(platform3, arch2) {
17129
- const os40 = platform3 === "win32" ? "windows" : platform3 === "darwin" ? "darwin" : "linux";
17195
+ const os41 = platform3 === "win32" ? "windows" : platform3 === "darwin" ? "darwin" : "linux";
17130
17196
  const a = arch2 === "x64" ? "amd64" : arch2 === "arm64" ? "arm64" : null;
17131
17197
  if (!a) return null;
17132
- if (os40 === "windows" && a !== "amd64") return null;
17133
- return `${os40}-${a}`;
17198
+ if (os41 === "windows" && a !== "amd64") return null;
17199
+ return `${os41}-${a}`;
17134
17200
  }
17135
17201
  function resolveDoltTarballStrategy(targetDir, platform3, arch2) {
17136
17202
  const tuple = doltPlatformTuple(platform3, arch2);
@@ -18009,6 +18075,30 @@ async function handleBeadsActionCommand(action, started) {
18009
18075
  });
18010
18076
  }
18011
18077
 
18078
+ // src/beads/config-store.ts
18079
+ var import_node_fs5 = __toESM(require("fs"));
18080
+ var import_node_os3 = __toESM(require("os"));
18081
+ var import_node_path4 = __toESM(require("path"));
18082
+ function beadsConfigPath() {
18083
+ return import_node_path4.default.join(import_node_os3.default.homedir(), ".codeam", "beads-config.json");
18084
+ }
18085
+ function readBeadsEnabled() {
18086
+ try {
18087
+ const raw = import_node_fs5.default.readFileSync(beadsConfigPath(), "utf8");
18088
+ const cfg = JSON.parse(raw);
18089
+ return cfg.enabled !== false;
18090
+ } catch {
18091
+ return true;
18092
+ }
18093
+ }
18094
+ function persistBeadsConfig(cfg) {
18095
+ const file = beadsConfigPath();
18096
+ import_node_fs5.default.mkdirSync(import_node_path4.default.dirname(file), { recursive: true });
18097
+ const tmp = `${file}.tmp`;
18098
+ import_node_fs5.default.writeFileSync(tmp, JSON.stringify(cfg), { mode: 384 });
18099
+ import_node_fs5.default.renameSync(tmp, file);
18100
+ }
18101
+
18012
18102
  // src/beads/wiring.ts
18013
18103
  function beadsKilled() {
18014
18104
  const v = process.env.CODEAM_BEADS_DISABLED;
@@ -18019,6 +18109,10 @@ async function provisionBeadsForStart(ctx) {
18019
18109
  log.trace("beads", "CODEAM_BEADS_DISABLED set \u2014 beads off this run");
18020
18110
  return null;
18021
18111
  }
18112
+ if (readBeadsEnabled() === false) {
18113
+ log.trace("beads", "beads disabled by persisted config \u2014 beads off this run");
18114
+ return null;
18115
+ }
18022
18116
  delete process.env.BEADS_DIR;
18023
18117
  process.env.BEADS_DOLT_SHARED_SERVER = "1";
18024
18118
  if (!ctx.pluginAuthToken) {
@@ -18079,12 +18173,69 @@ function beadsActionFromPayload(payload) {
18079
18173
  };
18080
18174
  }
18081
18175
 
18176
+ // src/beads/configure.ts
18177
+ var _probeSeam = {
18178
+ resolveBd: (cwd) => new BdAdapter({ cwd }).isAvailable(),
18179
+ doltOnPath: () => ensureDoltResolvable(),
18180
+ ping: async (cwd) => {
18181
+ const adapter = new BdAdapter({ cwd });
18182
+ if (!adapter.isAvailable()) return false;
18183
+ const r = await adapter.run(["ping"]);
18184
+ return r.code === 0;
18185
+ },
18186
+ derivePrefix: (cwd) => {
18187
+ try {
18188
+ const { projectKey } = deriveProjectIdentity(cwd);
18189
+ return prefixForProjectKey(projectKey);
18190
+ } catch {
18191
+ return null;
18192
+ }
18193
+ }
18194
+ };
18195
+ async function probeBeadsStatus(cwd = process.cwd()) {
18196
+ const bdAvailable = _probeSeam.resolveBd(cwd);
18197
+ const doltAvailable = _probeSeam.doltOnPath();
18198
+ const serverUp = bdAvailable && doltAvailable ? await _probeSeam.ping(cwd) : false;
18199
+ const prefix = _probeSeam.derivePrefix(cwd);
18200
+ return { bdAvailable, doltAvailable, serverUp, prefix };
18201
+ }
18202
+ async function configureBeads(action, ctx, deps) {
18203
+ if (action === "status") {
18204
+ if (deps.readEnabled() === false) {
18205
+ deps.emit({ type: "beads_status", state: "disabled", running: false });
18206
+ return { enabled: false, running: false };
18207
+ }
18208
+ const p3 = await deps.probe();
18209
+ const running2 = p3.serverUp && p3.bdAvailable;
18210
+ deps.emit({ type: "beads_status", state: running2 ? "enabled" : "disabled", running: running2, ...p3 });
18211
+ return { enabled: running2, running: running2, ...p3 };
18212
+ }
18213
+ if (action === "disable") {
18214
+ deps.persist({ enabled: false });
18215
+ await deps.stopWatcher().catch(() => void 0);
18216
+ await deps.revertAgentHook(ctx.agent).catch(() => void 0);
18217
+ deps.emit({ type: "beads_status", state: "disabled" });
18218
+ return { enabled: false };
18219
+ }
18220
+ deps.persist({ enabled: true });
18221
+ deps.emit({ type: "beads_status", state: "provisioning" });
18222
+ const p2 = await deps.provision();
18223
+ if (!p2.serverUp || !p2.bdAvailable) {
18224
+ deps.emit({ type: "beads_status", state: "error", error: "Beads provisioning failed", ...p2 });
18225
+ return { enabled: false, ...p2 };
18226
+ }
18227
+ await deps.startWatcher().catch(() => void 0);
18228
+ const running = true;
18229
+ deps.emit({ type: "beads_status", state: "enabled", running, ...p2 });
18230
+ return { enabled: true, running, ...p2 };
18231
+ }
18232
+
18082
18233
  // src/commands/start/handlers.ts
18083
18234
  var pendingAttachmentFiles = /* @__PURE__ */ new Set();
18084
18235
  function cleanupAttachmentTempFiles() {
18085
18236
  for (const p2 of pendingAttachmentFiles) {
18086
18237
  try {
18087
- fs40.unlinkSync(p2);
18238
+ fs41.unlinkSync(p2);
18088
18239
  } catch {
18089
18240
  }
18090
18241
  }
@@ -18093,8 +18244,8 @@ function cleanupAttachmentTempFiles() {
18093
18244
  function saveFilesTemp(files) {
18094
18245
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
18095
18246
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
18096
- const tmpPath = path46.join(os33.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
18097
- fs40.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
18247
+ const tmpPath = path47.join(os34.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
18248
+ fs41.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
18098
18249
  pendingAttachmentFiles.add(tmpPath);
18099
18250
  return tmpPath;
18100
18251
  });
@@ -18114,7 +18265,7 @@ var startTask = (ctx, _cmd, parsed) => {
18114
18265
  setTimeout(() => {
18115
18266
  for (const p2 of paths) {
18116
18267
  try {
18117
- fs40.unlinkSync(p2);
18268
+ fs41.unlinkSync(p2);
18118
18269
  } catch {
18119
18270
  }
18120
18271
  pendingAttachmentFiles.delete(p2);
@@ -18315,9 +18466,9 @@ var listFiles = async (ctx, cmd, parsed) => {
18315
18466
  await ctx.relay.sendResult(cmd.id, "completed", result);
18316
18467
  };
18317
18468
  var envReadH = async (ctx, cmd) => {
18318
- const envPath = path46.join(process.cwd(), ".env");
18469
+ const envPath = path47.join(process.cwd(), ".env");
18319
18470
  try {
18320
- const raw = await fs40.promises.readFile(envPath, "utf8");
18471
+ const raw = await fs41.promises.readFile(envPath, "utf8");
18321
18472
  await ctx.relay.sendResult(cmd.id, "completed", {
18322
18473
  exists: true,
18323
18474
  vars: parseDotenv(raw)
@@ -18348,14 +18499,14 @@ var envWriteH = async (ctx, cmd, parsed) => {
18348
18499
  }
18349
18500
  seen.add(v.key);
18350
18501
  }
18351
- const envPath = path46.join(process.cwd(), ".env");
18352
- const tmpPath = path46.join(process.cwd(), ".env.codeam.tmp");
18502
+ const envPath = path47.join(process.cwd(), ".env");
18503
+ const tmpPath = path47.join(process.cwd(), ".env.codeam.tmp");
18353
18504
  try {
18354
- await fs40.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
18355
- await fs40.promises.rename(tmpPath, envPath);
18505
+ await fs41.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
18506
+ await fs41.promises.rename(tmpPath, envPath);
18356
18507
  await ctx.relay.sendResult(cmd.id, "completed", { ok: true, count: vars.length });
18357
18508
  } catch (err) {
18358
- await fs40.promises.rm(tmpPath, { force: true }).catch(() => void 0);
18509
+ await fs41.promises.rm(tmpPath, { force: true }).catch(() => void 0);
18359
18510
  await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
18360
18511
  }
18361
18512
  };
@@ -18373,7 +18524,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18373
18524
  let configuredAgent = rawAgentId;
18374
18525
  if (!configuredAgent) {
18375
18526
  try {
18376
- const raw = JSON.parse(fs40.readFileSync(headroomConfigPath(), "utf8"));
18527
+ const raw = JSON.parse(fs41.readFileSync(headroomConfigPath(), "utf8"));
18377
18528
  configuredAgent = raw.agent ?? "";
18378
18529
  } catch {
18379
18530
  }
@@ -18407,7 +18558,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18407
18558
  persist: persistHeadroomConfig,
18408
18559
  readEnabled: () => {
18409
18560
  try {
18410
- const raw = JSON.parse(fs40.readFileSync(headroomConfigPath(), "utf8"));
18561
+ const raw = JSON.parse(fs41.readFileSync(headroomConfigPath(), "utf8"));
18411
18562
  return raw.enabled === true;
18412
18563
  } catch {
18413
18564
  return false;
@@ -18420,7 +18571,7 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18420
18571
  const res = await fetch("http://localhost:8787/stats");
18421
18572
  return res.json();
18422
18573
  },
18423
- postSavings: async (delta) => {
18574
+ postSavings: async (delta, budget) => {
18424
18575
  if (!opts.ingestUrl) return;
18425
18576
  await fetch(opts.ingestUrl, {
18426
18577
  method: "POST",
@@ -18439,7 +18590,12 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18439
18590
  sessionId: ctx.sessionId,
18440
18591
  pluginId: ctx.pluginId,
18441
18592
  agentId: opts.agent,
18442
- savings: delta
18593
+ savings: delta,
18594
+ ...budget ? {
18595
+ periodSpendUsd: budget.periodSpendUsd,
18596
+ budgetUsd: budget.budgetUsd,
18597
+ budgetPeriod: budget.budgetPeriod
18598
+ } : {}
18443
18599
  })
18444
18600
  });
18445
18601
  }
@@ -18480,6 +18636,148 @@ var headroomConfigureH = async (ctx, cmd, parsed) => {
18480
18636
  });
18481
18637
  await ctx.relay.sendResult(cmd.id, "completed", result);
18482
18638
  };
18639
+ var headroomBudgetH = async (ctx, cmd) => {
18640
+ const payload = cmd.payload;
18641
+ let rawAgentId = ctx.agentId || (typeof payload.agentId === "string" ? payload.agentId : "");
18642
+ if (rawAgentId === "claude_code") rawAgentId = "claude";
18643
+ let payloadAgentId = typeof payload.agentId === "string" ? payload.agentId : "";
18644
+ if (payloadAgentId === "claude_code") payloadAgentId = "claude";
18645
+ if (!rawAgentId || !isHeadroomSupportedAgent(rawAgentId)) {
18646
+ await ctx.relay.sendResult(cmd.id, "completed", { applied: false });
18647
+ return;
18648
+ }
18649
+ if (!payloadAgentId || payloadAgentId !== rawAgentId) {
18650
+ await ctx.relay.sendResult(cmd.id, "completed", { applied: false });
18651
+ return;
18652
+ }
18653
+ let headroomActive = false;
18654
+ try {
18655
+ const raw = JSON.parse(fs41.readFileSync(headroomConfigPath(), "utf8"));
18656
+ headroomActive = raw.enabled === true;
18657
+ } catch {
18658
+ }
18659
+ if (!headroomActive) {
18660
+ await ctx.relay.sendResult(cmd.id, "completed", { applied: false });
18661
+ return;
18662
+ }
18663
+ let existingConfig = { enabled: true };
18664
+ try {
18665
+ existingConfig = JSON.parse(fs41.readFileSync(headroomConfigPath(), "utf8"));
18666
+ } catch {
18667
+ }
18668
+ if (payload.budgetEnabled && payload.budgetUsd != null) {
18669
+ persistHeadroomConfig({
18670
+ ...existingConfig,
18671
+ enabled: existingConfig.enabled ?? true,
18672
+ budgetEnabled: true,
18673
+ budgetUsd: payload.budgetUsd,
18674
+ budgetPeriod: payload.budgetPeriod ?? "daily"
18675
+ });
18676
+ process.env["HEADROOM_BUDGET"] = String(payload.budgetUsd);
18677
+ process.env["HEADROOM_BUDGET_PERIOD"] = payload.budgetPeriod ?? "daily";
18678
+ } else {
18679
+ persistHeadroomConfig({
18680
+ ...existingConfig,
18681
+ enabled: existingConfig.enabled ?? true,
18682
+ budgetEnabled: false,
18683
+ budgetUsd: void 0,
18684
+ budgetPeriod: void 0
18685
+ });
18686
+ delete process.env["HEADROOM_BUDGET"];
18687
+ delete process.env["HEADROOM_BUDGET_PERIOD"];
18688
+ }
18689
+ try {
18690
+ const killer = (0, import_child_process20.spawn)("pkill", ["-TERM", "-f", "headroom.*proxy"], {
18691
+ detached: true,
18692
+ stdio: "ignore"
18693
+ });
18694
+ killer.once("error", () => {
18695
+ });
18696
+ killer.unref();
18697
+ } catch {
18698
+ }
18699
+ try {
18700
+ const proxyEnv = { ...process.env, HEADROOM_KOMPRESS_BACKEND: "onnx_cpu" };
18701
+ const proxy = (0, import_child_process20.spawn)(
18702
+ "headroom",
18703
+ ["proxy", "--port", "8787", ...buildBudgetProxyArgs(proxyEnv)],
18704
+ {
18705
+ stdio: "ignore",
18706
+ detached: true,
18707
+ env: proxyEnv
18708
+ }
18709
+ );
18710
+ proxy.once("error", (e) => {
18711
+ log.warn("headroom-budget", `proxy relaunch error (best-effort): ${e.message}`);
18712
+ });
18713
+ proxy.unref();
18714
+ } catch (e) {
18715
+ log.warn(
18716
+ "headroom-budget",
18717
+ `proxy relaunch failed (best-effort): ${e instanceof Error ? e.message : String(e)}`
18718
+ );
18719
+ }
18720
+ await ctx.relay.sendResult(cmd.id, "completed", { applied: true });
18721
+ };
18722
+ var _beadsEmitChain = Promise.resolve();
18723
+ var beadsConfigureH = async (ctx, cmd, parsed) => {
18724
+ const action = parsed.action;
18725
+ if (action !== "enable" && action !== "disable" && action !== "status") {
18726
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "action must be enable|disable|status" });
18727
+ return;
18728
+ }
18729
+ let rawAgentId = ctx.agentId || (typeof parsed.agentId === "string" ? parsed.agentId : "");
18730
+ if (rawAgentId === "claude_code") rawAgentId = "claude";
18731
+ const agentIds = rawAgentId && isKnownAgentId(rawAgentId) ? [rawAgentId] : [];
18732
+ const deps = {
18733
+ provision: async () => {
18734
+ const r = await provisionBeads({ cwd: process.cwd(), agents: agentIds });
18735
+ return { bdAvailable: r.bdAvailable, doltAvailable: r.doltAvailable, serverUp: r.serverUp, prefix: r.prefix };
18736
+ },
18737
+ probe: async () => probeBeadsStatus(process.cwd()),
18738
+ startWatcher: async () => {
18739
+ if (!ctx.pluginAuthToken) return;
18740
+ const started = await startBeads({
18741
+ sessionId: ctx.sessionId,
18742
+ pluginId: ctx.pluginId,
18743
+ pluginAuthToken: ctx.pluginAuthToken,
18744
+ cwd: process.cwd(),
18745
+ agents: agentIds
18746
+ });
18747
+ if (started) {
18748
+ ctx.beads = started;
18749
+ }
18750
+ },
18751
+ stopWatcher: async () => {
18752
+ if (ctx.beads) {
18753
+ await ctx.beads.watcher.stop();
18754
+ ctx.beads = null;
18755
+ }
18756
+ },
18757
+ revertAgentHook: async (agent) => {
18758
+ log.info("beads", `revertAgentHook: bd has no --remove flag \u2014 leaving ${agent} hook in place (no-op disable path)`);
18759
+ },
18760
+ persist: (cfg) => persistBeadsConfig(cfg),
18761
+ readEnabled: () => readBeadsEnabled(),
18762
+ emit: (event) => {
18763
+ const token = ctx.pluginAuthToken;
18764
+ if (!token) return;
18765
+ _beadsEmitChain = _beadsEmitChain.then(
18766
+ () => postBeadsEvent({
18767
+ sessionId: ctx.sessionId,
18768
+ pluginId: ctx.pluginId,
18769
+ pluginAuthToken: token,
18770
+ type: "beads_status",
18771
+ payload: Object.fromEntries(
18772
+ Object.entries(event).filter(([k2]) => k2 !== "type")
18773
+ )
18774
+ })
18775
+ );
18776
+ }
18777
+ };
18778
+ const result = await configureBeads(action, { agent: rawAgentId, cwd: process.cwd(), pluginAuthToken: ctx.pluginAuthToken }, deps);
18779
+ await ctx.relay.sendResult(cmd.id, "completed", result);
18780
+ };
18483
18781
  var terminalOpenH = async (ctx, cmd, parsed) => {
18484
18782
  const r = openTerminal({
18485
18783
  cols: typeof parsed.cols === "number" ? parsed.cols : void 0,
@@ -18916,8 +19214,8 @@ function normalizeDetectionForSpawn(detection, cwd) {
18916
19214
  if (args2.length === 0) return detection;
18917
19215
  const binName = args2[0];
18918
19216
  if (binName.startsWith("-")) return detection;
18919
- const binPath = path46.join(cwd, "node_modules", ".bin", binName);
18920
- if (!fs40.existsSync(binPath)) return detection;
19217
+ const binPath = path47.join(cwd, "node_modules", ".bin", binName);
19218
+ if (!fs41.existsSync(binPath)) return detection;
18921
19219
  return {
18922
19220
  ...detection,
18923
19221
  command: binPath,
@@ -19465,7 +19763,9 @@ var handlers = {
19465
19763
  save_preview_config: savePreviewConfigH,
19466
19764
  env_read: envReadH,
19467
19765
  env_write: envWriteH,
19468
- headroom_configure: headroomConfigureH
19766
+ headroom_configure: headroomConfigureH,
19767
+ headroom_budget: headroomBudgetH,
19768
+ beads_configure: beadsConfigureH
19469
19769
  };
19470
19770
  async function dispatchCommand(ctx, cmd) {
19471
19771
  if (cmd.type === "beads_action") {
@@ -19733,12 +20033,12 @@ function readTokenFromArgs(args2) {
19733
20033
  }
19734
20034
  const fileFlag = args2.find((a) => a.startsWith("--token-file="));
19735
20035
  if (fileFlag) {
19736
- const path60 = fileFlag.slice("--token-file=".length);
20036
+ const path61 = fileFlag.slice("--token-file=".length);
19737
20037
  try {
19738
- const content = fs41.readFileSync(path60, "utf8").trim();
19739
- if (content.length === 0) fail(`--token-file ${path60} is empty`);
20038
+ const content = fs42.readFileSync(path61, "utf8").trim();
20039
+ if (content.length === 0) fail(`--token-file ${path61} is empty`);
19740
20040
  try {
19741
- fs41.unlinkSync(path60);
20041
+ fs42.unlinkSync(path61);
19742
20042
  } catch {
19743
20043
  }
19744
20044
  return content;
@@ -19764,7 +20064,7 @@ async function claimOnce(token, pluginId, pluginSecretHash) {
19764
20064
  pluginId,
19765
20065
  ideName: "codeam-cli (codespace)",
19766
20066
  ideVersion: process.env.npm_package_version ?? "unknown",
19767
- hostname: os34.hostname(),
20067
+ hostname: os35.hostname(),
19768
20068
  codespaceName: process.env.CODESPACE_NAME ?? "",
19769
20069
  // Current git branch of the codespace's working directory, so the
19770
20070
  // backend can populate `PairedSession.branch` for the codespace pair.
@@ -19825,7 +20125,7 @@ async function claim(token, pluginId, pluginSecretHash) {
19825
20125
  }
19826
20126
  }
19827
20127
  function pairAutoLockPath() {
19828
- return path47.join(os34.homedir(), ".codeam", "pair-auto.lock");
20128
+ return path48.join(os35.homedir(), ".codeam", "pair-auto.lock");
19829
20129
  }
19830
20130
  function isLivePairAuto(pid) {
19831
20131
  if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
@@ -19835,7 +20135,7 @@ function isLivePairAuto(pid) {
19835
20135
  if (e.code !== "EPERM") return false;
19836
20136
  }
19837
20137
  try {
19838
- return fs41.readFileSync(`/proc/${pid}/cmdline`, "utf8").includes("codeam");
20138
+ return fs42.readFileSync(`/proc/${pid}/cmdline`, "utf8").includes("codeam");
19839
20139
  } catch {
19840
20140
  return true;
19841
20141
  }
@@ -19845,24 +20145,24 @@ function isLiveCodeam(pid) {
19845
20145
  }
19846
20146
  function daemonLockPath(sessionId) {
19847
20147
  const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
19848
- return path47.join(os34.homedir(), ".codeam", `daemon-${safe}.lock`);
20148
+ return path48.join(os35.homedir(), ".codeam", `daemon-${safe}.lock`);
19849
20149
  }
19850
20150
  function acquireDaemonLock(sessionId) {
19851
20151
  const lockPath = daemonLockPath(sessionId);
19852
20152
  try {
19853
- fs41.mkdirSync(path47.dirname(lockPath), { recursive: true });
20153
+ fs42.mkdirSync(path48.dirname(lockPath), { recursive: true });
19854
20154
  try {
19855
- fs41.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
20155
+ fs42.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
19856
20156
  } catch (e) {
19857
20157
  if (e.code !== "EEXIST") throw e;
19858
- const holder = Number(fs41.readFileSync(lockPath, "utf8").trim());
20158
+ const holder = Number(fs42.readFileSync(lockPath, "utf8").trim());
19859
20159
  if (holder && holder !== process.pid && isLiveCodeam(holder)) return false;
19860
- fs41.writeFileSync(lockPath, String(process.pid));
20160
+ fs42.writeFileSync(lockPath, String(process.pid));
19861
20161
  }
19862
20162
  const release3 = () => {
19863
20163
  try {
19864
- if (fs41.existsSync(lockPath) && Number(fs41.readFileSync(lockPath, "utf8").trim()) === process.pid) {
19865
- fs41.unlinkSync(lockPath);
20164
+ if (fs42.existsSync(lockPath) && Number(fs42.readFileSync(lockPath, "utf8").trim()) === process.pid) {
20165
+ fs42.unlinkSync(lockPath);
19866
20166
  }
19867
20167
  } catch {
19868
20168
  }
@@ -19884,19 +20184,19 @@ function acquireDaemonLock(sessionId) {
19884
20184
  function acquireSingletonLock() {
19885
20185
  const lockPath = pairAutoLockPath();
19886
20186
  try {
19887
- fs41.mkdirSync(path47.dirname(lockPath), { recursive: true });
20187
+ fs42.mkdirSync(path48.dirname(lockPath), { recursive: true });
19888
20188
  try {
19889
- fs41.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
20189
+ fs42.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
19890
20190
  } catch (e) {
19891
20191
  if (e.code !== "EEXIST") throw e;
19892
- const holder = Number(fs41.readFileSync(lockPath, "utf8").trim());
20192
+ const holder = Number(fs42.readFileSync(lockPath, "utf8").trim());
19893
20193
  if (isLivePairAuto(holder)) return false;
19894
- fs41.writeFileSync(lockPath, String(process.pid));
20194
+ fs42.writeFileSync(lockPath, String(process.pid));
19895
20195
  }
19896
20196
  process.once("exit", () => {
19897
20197
  try {
19898
- if (fs41.existsSync(lockPath) && Number(fs41.readFileSync(lockPath, "utf8").trim()) === process.pid) {
19899
- fs41.unlinkSync(lockPath);
20198
+ if (fs42.existsSync(lockPath) && Number(fs42.readFileSync(lockPath, "utf8").trim()) === process.pid) {
20199
+ fs42.unlinkSync(lockPath);
19900
20200
  }
19901
20201
  } catch {
19902
20202
  }
@@ -20341,19 +20641,19 @@ var AgentService = class _AgentService {
20341
20641
  };
20342
20642
 
20343
20643
  // src/agents/acp/adapters.ts
20344
- var path48 = __toESM(require("path"));
20644
+ var path49 = __toESM(require("path"));
20345
20645
  var require_ = require;
20346
20646
  function resolveBin(pkgName, binName) {
20347
20647
  try {
20348
20648
  const manifestPath = require_.resolve(`${pkgName}/package.json`);
20349
20649
  const manifest = require_(`${pkgName}/package.json`);
20350
- const pkgDir = path48.dirname(manifestPath);
20650
+ const pkgDir = path49.dirname(manifestPath);
20351
20651
  const bin = manifest.bin;
20352
20652
  if (!bin) return null;
20353
- if (typeof bin === "string") return path48.resolve(pkgDir, bin);
20653
+ if (typeof bin === "string") return path49.resolve(pkgDir, bin);
20354
20654
  const target = binName ?? Object.keys(bin)[0];
20355
20655
  if (!target || !bin[target]) return null;
20356
- return path48.resolve(pkgDir, bin[target]);
20656
+ return path49.resolve(pkgDir, bin[target]);
20357
20657
  } catch {
20358
20658
  return null;
20359
20659
  }
@@ -20423,9 +20723,9 @@ function requiresAcp(agent) {
20423
20723
  var import_node_crypto7 = require("crypto");
20424
20724
 
20425
20725
  // src/services/history.service.ts
20426
- var fs42 = __toESM(require("fs"));
20427
- var path49 = __toESM(require("path"));
20428
- var os35 = __toESM(require("os"));
20726
+ var fs43 = __toESM(require("fs"));
20727
+ var path50 = __toESM(require("path"));
20728
+ var os36 = __toESM(require("os"));
20429
20729
  var https7 = __toESM(require("https"));
20430
20730
  var http6 = __toESM(require("http"));
20431
20731
  var import_zod2 = require("zod");
@@ -20452,7 +20752,7 @@ function parseJsonl(filePath) {
20452
20752
  const messages = [];
20453
20753
  let raw;
20454
20754
  try {
20455
- raw = fs42.readFileSync(filePath, "utf8");
20755
+ raw = fs43.readFileSync(filePath, "utf8");
20456
20756
  } catch (err) {
20457
20757
  if (err.code !== "ENOENT") {
20458
20758
  log.warn("history:parseJsonl", `read failed for ${filePath}`, err);
@@ -20593,7 +20893,7 @@ var HistoryService = class _HistoryService {
20593
20893
  return this._quotaPercent === null || Date.now() - this._quotaFetchedAt > ttlMs;
20594
20894
  }
20595
20895
  get projectDir() {
20596
- return this.runtime.resolveHistoryDir(this.cwd) ?? path49.join(os35.homedir(), ".claude", "projects", encodeCwd(this.cwd));
20896
+ return this.runtime.resolveHistoryDir(this.cwd) ?? path50.join(os36.homedir(), ".claude", "projects", encodeCwd(this.cwd));
20597
20897
  }
20598
20898
  /** Set the current Claude conversation ID (extracted from /cost command or session start) */
20599
20899
  setCurrentConversationId(id) {
@@ -20605,7 +20905,7 @@ var HistoryService = class _HistoryService {
20605
20905
  /** Return the current message count in the active conversation. */
20606
20906
  getCurrentMessageCount() {
20607
20907
  if (!this.currentConversationId) return 0;
20608
- const filePath = path49.join(this.projectDir, `${this.currentConversationId}.jsonl`);
20908
+ const filePath = path50.join(this.projectDir, `${this.currentConversationId}.jsonl`);
20609
20909
  return parseJsonl(filePath).length;
20610
20910
  }
20611
20911
  /**
@@ -20616,7 +20916,7 @@ var HistoryService = class _HistoryService {
20616
20916
  const deadline = Date.now() + timeoutMs;
20617
20917
  while (Date.now() < deadline) {
20618
20918
  if (!this.currentConversationId) return null;
20619
- const filePath = path49.join(this.projectDir, `${this.currentConversationId}.jsonl`);
20919
+ const filePath = path50.join(this.projectDir, `${this.currentConversationId}.jsonl`);
20620
20920
  const messages = parseJsonl(filePath);
20621
20921
  if (messages.length > previousCount) {
20622
20922
  for (let i = messages.length - 1; i >= previousCount; i--) {
@@ -20642,16 +20942,16 @@ var HistoryService = class _HistoryService {
20642
20942
  const dir = this.projectDir;
20643
20943
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
20644
20944
  try {
20645
- const files = fs42.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
20945
+ const files = fs43.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
20646
20946
  try {
20647
- const stat3 = fs42.statSync(path49.join(dir, e.name));
20947
+ const stat3 = fs43.statSync(path50.join(dir, e.name));
20648
20948
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
20649
20949
  } catch {
20650
20950
  return { name: e.name, mtime: 0, birthtime: 0 };
20651
20951
  }
20652
20952
  }).filter((f) => f.birthtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
20653
20953
  if (files.length > 0) {
20654
- this.currentConversationId = path49.basename(files[0].name, ".jsonl");
20954
+ this.currentConversationId = path50.basename(files[0].name, ".jsonl");
20655
20955
  }
20656
20956
  } catch {
20657
20957
  }
@@ -20685,13 +20985,13 @@ var HistoryService = class _HistoryService {
20685
20985
  const cutoff = this.bootTimeMs - _HistoryService.BIRTHTIME_GRACE_MS;
20686
20986
  let entries;
20687
20987
  try {
20688
- entries = fs42.readdirSync(dir, { withFileTypes: true });
20988
+ entries = fs43.readdirSync(dir, { withFileTypes: true });
20689
20989
  } catch {
20690
20990
  return null;
20691
20991
  }
20692
20992
  const files = entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => {
20693
20993
  try {
20694
- const stat3 = fs42.statSync(path49.join(dir, e.name));
20994
+ const stat3 = fs43.statSync(path50.join(dir, e.name));
20695
20995
  return { name: e.name, mtime: stat3.mtimeMs, birthtime: stat3.birthtimeMs };
20696
20996
  } catch {
20697
20997
  return { name: e.name, mtime: 0, birthtime: 0 };
@@ -20700,12 +21000,12 @@ var HistoryService = class _HistoryService {
20700
21000
  if (files.length === 0) return null;
20701
21001
  const targetFile = this.currentConversationId ? `${this.currentConversationId}.jsonl` : files[0].name;
20702
21002
  if (!files.some((f) => f.name === targetFile)) return null;
20703
- return this.extractUsageFromFile(path49.join(dir, targetFile));
21003
+ return this.extractUsageFromFile(path50.join(dir, targetFile));
20704
21004
  }
20705
21005
  extractUsageFromFile(filePath) {
20706
21006
  let raw;
20707
21007
  try {
20708
- raw = fs42.readFileSync(filePath, "utf8");
21008
+ raw = fs43.readFileSync(filePath, "utf8");
20709
21009
  } catch {
20710
21010
  return null;
20711
21011
  }
@@ -20750,9 +21050,9 @@ var HistoryService = class _HistoryService {
20750
21050
  let totalCost = 0;
20751
21051
  let files;
20752
21052
  try {
20753
- files = fs42.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
21053
+ files = fs43.readdirSync(projectDir).filter((f) => f.endsWith(".jsonl")).filter((f) => {
20754
21054
  try {
20755
- return fs42.statSync(path49.join(projectDir, f)).mtimeMs >= monthStartMs;
21055
+ return fs43.statSync(path50.join(projectDir, f)).mtimeMs >= monthStartMs;
20756
21056
  } catch {
20757
21057
  return false;
20758
21058
  }
@@ -20763,7 +21063,7 @@ var HistoryService = class _HistoryService {
20763
21063
  for (const file of files) {
20764
21064
  let raw;
20765
21065
  try {
20766
- raw = fs42.readFileSync(path49.join(projectDir, file), "utf8");
21066
+ raw = fs43.readFileSync(path50.join(projectDir, file), "utf8");
20767
21067
  } catch {
20768
21068
  continue;
20769
21069
  }
@@ -20842,7 +21142,7 @@ var HistoryService = class _HistoryService {
20842
21142
  if (this.runtime.resolveHistoryFile) {
20843
21143
  return this.runtime.resolveHistoryFile(this.cwd, sessionId);
20844
21144
  }
20845
- return path49.join(this.projectDir, `${sessionId}.jsonl`);
21145
+ return path50.join(this.projectDir, `${sessionId}.jsonl`);
20846
21146
  }
20847
21147
  /**
20848
21148
  * Parse a conversation's messages from disk, agent-aware. Claude uses the
@@ -20876,7 +21176,7 @@ var HistoryService = class _HistoryService {
20876
21176
  };
20877
21177
  });
20878
21178
  }
20879
- return parseJsonl(path49.join(this.projectDir, `${sessionId}.jsonl`));
21179
+ return parseJsonl(path50.join(this.projectDir, `${sessionId}.jsonl`));
20880
21180
  }
20881
21181
  async loadConversation(sessionId) {
20882
21182
  const messages = this.readConversation(sessionId);
@@ -20932,7 +21232,7 @@ var HistoryService = class _HistoryService {
20932
21232
  if (!filePath) return false;
20933
21233
  let mtimeMs;
20934
21234
  try {
20935
- mtimeMs = fs42.statSync(filePath).mtimeMs;
21235
+ mtimeMs = fs43.statSync(filePath).mtimeMs;
20936
21236
  } catch {
20937
21237
  return false;
20938
21238
  }
@@ -21001,10 +21301,10 @@ var HistoryService = class _HistoryService {
21001
21301
 
21002
21302
  // src/agents/acp/client.ts
21003
21303
  var import_node_child_process16 = require("child_process");
21004
- var fs43 = __toESM(require("fs/promises"));
21304
+ var fs44 = __toESM(require("fs/promises"));
21005
21305
  var fsSync = __toESM(require("fs"));
21006
- var os36 = __toESM(require("os"));
21007
- var path50 = __toESM(require("path"));
21306
+ var os37 = __toESM(require("os"));
21307
+ var path51 = __toESM(require("path"));
21008
21308
  var import_node_stream = require("stream");
21009
21309
 
21010
21310
  // ../../node_modules/@agentclientprotocol/sdk/dist/acp.js
@@ -23837,7 +24137,7 @@ var AcpClient = class {
23837
24137
  },
23838
24138
  readTextFile: async (params) => {
23839
24139
  try {
23840
- const content = await fs43.readFile(params.path, "utf8");
24140
+ const content = await fs44.readFile(params.path, "utf8");
23841
24141
  return applyLineRange(content, params.line ?? null, params.limit ?? null);
23842
24142
  } catch (err) {
23843
24143
  const code = err.code;
@@ -23857,7 +24157,7 @@ var AcpClient = class {
23857
24157
  },
23858
24158
  writeTextFile: async (params) => {
23859
24159
  try {
23860
- await fs43.writeFile(params.path, params.content, "utf8");
24160
+ await fs44.writeFile(params.path, params.content, "utf8");
23861
24161
  return {};
23862
24162
  } catch (err) {
23863
24163
  const code = err.code;
@@ -23906,25 +24206,25 @@ function applyLineRange(content, line, limit) {
23906
24206
  return { content: lines.slice(start2, end).join("\n") };
23907
24207
  }
23908
24208
  function knownAgentBinaryDirs() {
23909
- const home = os36.homedir();
24209
+ const home = os37.homedir();
23910
24210
  const out2 = [];
23911
24211
  out2.push("/tmp/codeam-node20/bin");
23912
24212
  for (const root of [
23913
24213
  "/usr/local/share/nvm/versions/node",
23914
- path50.join(home, ".nvm/versions/node")
24214
+ path51.join(home, ".nvm/versions/node")
23915
24215
  ]) {
23916
24216
  try {
23917
24217
  for (const child of fsSync.readdirSync(root)) {
23918
- out2.push(path50.join(root, child, "bin"));
24218
+ out2.push(path51.join(root, child, "bin"));
23919
24219
  }
23920
24220
  } catch {
23921
24221
  }
23922
24222
  }
23923
- out2.push(path50.join(home, ".volta/bin"));
24223
+ out2.push(path51.join(home, ".volta/bin"));
23924
24224
  out2.push("/usr/local/bin");
23925
24225
  out2.push("/usr/bin");
23926
- out2.push(path50.join(home, ".local/bin"));
23927
- out2.push(path50.join(home, "bin"));
24226
+ out2.push(path51.join(home, ".local/bin"));
24227
+ out2.push(path51.join(home, "bin"));
23928
24228
  return out2.filter((p2) => {
23929
24229
  try {
23930
24230
  return fsSync.statSync(p2).isDirectory();
@@ -23935,7 +24235,7 @@ function knownAgentBinaryDirs() {
23935
24235
  }
23936
24236
  function expandPathForAgentBinaries(existingPath) {
23937
24237
  const existing = new Set(
23938
- existingPath.split(path50.delimiter).filter((p2) => p2.length > 0)
24238
+ existingPath.split(path51.delimiter).filter((p2) => p2.length > 0)
23939
24239
  );
23940
24240
  const additions = [];
23941
24241
  for (const dir of knownAgentBinaryDirs()) {
@@ -23945,7 +24245,7 @@ function expandPathForAgentBinaries(existingPath) {
23945
24245
  }
23946
24246
  }
23947
24247
  if (additions.length === 0) return existingPath;
23948
- return [...additions, existingPath].filter((p2) => p2.length > 0).join(path50.delimiter);
24248
+ return [...additions, existingPath].filter((p2) => p2.length > 0).join(path51.delimiter);
23949
24249
  }
23950
24250
 
23951
24251
  // src/services/streaming/transport.ts
@@ -24337,6 +24637,79 @@ function createOneMRecovery(deps) {
24337
24637
  };
24338
24638
  }
24339
24639
 
24640
+ // src/agents/acp/budgetRecovery.ts
24641
+ var BUDGET_EXCEEDED_RE = /budget exceeded for (\w+) period/i;
24642
+ function looksLikeBudgetExceeded(text) {
24643
+ return BUDGET_EXCEEDED_RE.test(text);
24644
+ }
24645
+ function extractBudgetPeriod(text) {
24646
+ const m = BUDGET_EXCEEDED_RE.exec(text);
24647
+ return m ? m[1] ?? "current" : "current";
24648
+ }
24649
+ var BUDGET_PAUSE_OPTION = "Pause budget this session";
24650
+ var BUDGET_RAISE_OPTION = "Raise budget";
24651
+ function createBudgetRecovery(deps) {
24652
+ let pending = null;
24653
+ const buildQuestion = (period) => `\u{1F4B8} **Headroom budget reached for the ${period} period.**
24654
+
24655
+ You have two options:
24656
+ \u2022 **Pause budget this session** \u2014 removes the spending cap for this session only (the proxy restarts without a budget limit).
24657
+ \u2022 **Raise budget** \u2014 open your agent budget settings to increase the cap.`;
24658
+ return {
24659
+ offer: async (commandId, blocks, detail) => {
24660
+ const period = extractBudgetPeriod(detail);
24661
+ pending = { blocks, period };
24662
+ const question = buildQuestion(period);
24663
+ const options = [BUDGET_PAUSE_OPTION, BUDGET_RAISE_OPTION];
24664
+ await deps.publishText(question);
24665
+ await deps.publishSelectPrompt(question, options);
24666
+ await deps.publishAwaitingAnswer(question, options);
24667
+ deps.appendAgentReply(question);
24668
+ deps.flushHistory();
24669
+ deps.log?.(`budget-exceeded recovery offered period=${period} id=${commandId.slice(0, 8)}`);
24670
+ await deps.sendResult(commandId, "failed", {
24671
+ error: `Headroom budget exceeded (${period}) \u2014 recovery offered`
24672
+ });
24673
+ },
24674
+ tryRecover: async (commandId, optionIndex) => {
24675
+ if (!pending) return false;
24676
+ const { blocks, period } = pending;
24677
+ pending = null;
24678
+ if (optionIndex === 0) {
24679
+ deps.log?.("budget recovery \u2192 pause: relaunching proxy without budget");
24680
+ try {
24681
+ await deps.relaunchProxyWithoutBudget();
24682
+ } catch (err) {
24683
+ deps.log?.(`budget recovery \u2192 relaunch failed (best-effort): ${String(err)}`);
24684
+ }
24685
+ await deps.sendResult(commandId, "completed", {
24686
+ action: "pause",
24687
+ period,
24688
+ note: "Headroom proxy relaunched without budget limit for this session. Send your message again."
24689
+ });
24690
+ const note = "\u2705 **Budget paused for this session.** The proxy is running without a spending cap \u2014 send your message again.";
24691
+ await deps.publishText(note);
24692
+ deps.appendAgentReply(note);
24693
+ deps.flushHistory();
24694
+ deps.log?.(`budget recovery \u2192 pause complete id=${commandId.slice(0, 8)}`);
24695
+ } else {
24696
+ deps.log?.(`budget recovery \u2192 raise: emitting deep-link for agent=${deps.agentId}`);
24697
+ await deps.publishRawChunk({
24698
+ type: "open_agent_budget_settings",
24699
+ agentId: deps.agentId,
24700
+ done: true
24701
+ });
24702
+ await deps.sendResult(commandId, "completed", {
24703
+ action: "raise",
24704
+ agentId: deps.agentId
24705
+ });
24706
+ deps.log?.(`budget recovery \u2192 raise emitted id=${commandId.slice(0, 8)}`);
24707
+ }
24708
+ return true;
24709
+ }
24710
+ };
24711
+ }
24712
+
24340
24713
  // src/agents/acp/reconcileDelta.ts
24341
24714
  function reconcileCumulative(existing, incoming) {
24342
24715
  if (incoming.length === 0) return existing;
@@ -24357,15 +24730,15 @@ function commonPrefixLength(a, b) {
24357
24730
 
24358
24731
  // src/agents/acp/onboarding.ts
24359
24732
  var import_child_process22 = require("child_process");
24360
- var fs44 = __toESM(require("fs"));
24361
- var os37 = __toESM(require("os"));
24362
- var path51 = __toESM(require("path"));
24733
+ var fs45 = __toESM(require("fs"));
24734
+ var os38 = __toESM(require("os"));
24735
+ var path52 = __toESM(require("path"));
24363
24736
  var _onboardingSeam = {
24364
- markerPath: (sessionId) => path51.join(os37.homedir(), ".codeam", "welcomed", `${sessionId}.done`),
24365
- exists: (p2) => fs44.existsSync(p2),
24737
+ markerPath: (sessionId) => path52.join(os38.homedir(), ".codeam", "welcomed", `${sessionId}.done`),
24738
+ exists: (p2) => fs45.existsSync(p2),
24366
24739
  write: (p2) => {
24367
- fs44.mkdirSync(path51.dirname(p2), { recursive: true });
24368
- fs44.writeFileSync(p2, "");
24740
+ fs45.mkdirSync(path52.dirname(p2), { recursive: true });
24741
+ fs45.writeFileSync(p2, "");
24369
24742
  },
24370
24743
  disabled: () => {
24371
24744
  const v = process.env.CODEAM_ONBOARDING_DISABLED;
@@ -24402,7 +24775,7 @@ function resolveRepoName(cwd) {
24402
24775
  if (name) return name;
24403
24776
  }
24404
24777
  }
24405
- const base = path51.basename(cwd || "");
24778
+ const base = path52.basename(cwd || "");
24406
24779
  if (base && !isUuid(base)) return base;
24407
24780
  return "this project";
24408
24781
  }
@@ -24675,13 +25048,28 @@ function extractSelectPrompt(text) {
24675
25048
  };
24676
25049
  }
24677
25050
 
25051
+ // src/agents/acp/inputSuggestion.ts
25052
+ var MIN_LEN = 8;
25053
+ var MAX_LEN = 200;
25054
+ function deriveInputSuggestion(finalText) {
25055
+ if (finalText.length === 0) return null;
25056
+ const lines = finalText.split("\n");
25057
+ let i = lines.length - 1;
25058
+ while (i >= 0 && lines[i].trim() === "") i--;
25059
+ if (i < 0) return null;
25060
+ const lastLine = lines[i].trim();
25061
+ if (!lastLine.endsWith("?")) return null;
25062
+ if (lastLine.length < MIN_LEN || lastLine.length > MAX_LEN) return null;
25063
+ return lastLine;
25064
+ }
25065
+
24678
25066
  // src/services/turn-files/turn-file-aggregator.ts
24679
25067
  var import_crypto5 = require("crypto");
24680
25068
 
24681
25069
  // src/services/turn-files/git-changeset.ts
24682
25070
  var import_child_process23 = require("child_process");
24683
- var fs45 = __toESM(require("fs/promises"));
24684
- var path52 = __toESM(require("path"));
25071
+ var fs46 = __toESM(require("fs/promises"));
25072
+ var path53 = __toESM(require("path"));
24685
25073
  async function collectRepoChangeset(opts) {
24686
25074
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
24687
25075
  if (status2 === null) return null;
@@ -24699,7 +25087,7 @@ async function collectRepoChangeset(opts) {
24699
25087
  let stats;
24700
25088
  if (row.fileStatus === "added" && numstatEntry === void 0) {
24701
25089
  const lineCount = await readUntrackedLineCount(
24702
- path52.join(opts.repoRoot, row.filePath)
25090
+ path53.join(opts.repoRoot, row.filePath)
24703
25091
  );
24704
25092
  stats = { added: lineCount, removed: 0 };
24705
25093
  } else {
@@ -24730,7 +25118,7 @@ function readUntrackedLineCount(absPath) {
24730
25118
  }
24731
25119
  async function defaultReadUntrackedLineCount(absPath) {
24732
25120
  try {
24733
- const content = await fs45.readFile(absPath, "utf8");
25121
+ const content = await fs46.readFile(absPath, "utf8");
24734
25122
  let count = 0;
24735
25123
  let pos = -1;
24736
25124
  while ((pos = content.indexOf("\n", pos + 1)) !== -1) {
@@ -24822,7 +25210,7 @@ function defaultRunGit(cwd, args2) {
24822
25210
  });
24823
25211
  }
24824
25212
  async function discoverRepos(workingDir, maxDepth = 4) {
24825
- const fs49 = await import("fs/promises");
25213
+ const fs50 = await import("fs/promises");
24826
25214
  const out2 = [];
24827
25215
  await walk(workingDir, 0);
24828
25216
  return out2;
@@ -24830,7 +25218,7 @@ async function discoverRepos(workingDir, maxDepth = 4) {
24830
25218
  if (depth > maxDepth) return;
24831
25219
  let entries = [];
24832
25220
  try {
24833
- const dirents = await fs49.readdir(dir, { withFileTypes: true });
25221
+ const dirents = await fs50.readdir(dir, { withFileTypes: true });
24834
25222
  entries = dirents.filter((d3) => !d3.name.startsWith(".") || d3.name === ".git").map((d3) => ({ name: d3.name, isDirectory: d3.isDirectory() }));
24835
25223
  } catch {
24836
25224
  return;
@@ -24841,8 +25229,8 @@ async function discoverRepos(workingDir, maxDepth = 4) {
24841
25229
  if (hasGit) {
24842
25230
  out2.push({
24843
25231
  repoRoot: dir,
24844
- repoPath: path52.relative(workingDir, dir),
24845
- repoName: path52.basename(dir)
25232
+ repoPath: path53.relative(workingDir, dir),
25233
+ repoName: path53.basename(dir)
24846
25234
  });
24847
25235
  return;
24848
25236
  }
@@ -24850,14 +25238,14 @@ async function discoverRepos(workingDir, maxDepth = 4) {
24850
25238
  if (!entry.isDirectory) continue;
24851
25239
  if (entry.name === "node_modules") continue;
24852
25240
  if (entry.name === "dist" || entry.name === "build") continue;
24853
- await walk(path52.join(dir, entry.name), depth + 1);
25241
+ await walk(path53.join(dir, entry.name), depth + 1);
24854
25242
  }
24855
25243
  }
24856
25244
  }
24857
25245
 
24858
25246
  // src/services/turn-files/files-outbox.ts
24859
- var fs46 = __toESM(require("fs/promises"));
24860
- var path53 = __toESM(require("path"));
25247
+ var fs47 = __toESM(require("fs/promises"));
25248
+ var path54 = __toESM(require("path"));
24861
25249
  var import_os7 = require("os");
24862
25250
  var HOME_OUTBOX_DIR = ".codeam/outbox";
24863
25251
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -24890,16 +25278,16 @@ var FilesOutbox = class {
24890
25278
  backoffIndex = 0;
24891
25279
  stopped = false;
24892
25280
  constructor(opts) {
24893
- const base = opts.baseDir ?? path53.join(homeDir(), HOME_OUTBOX_DIR);
24894
- this.filePath = path53.join(base, `${opts.sessionId}.jsonl`);
25281
+ const base = opts.baseDir ?? path54.join(homeDir(), HOME_OUTBOX_DIR);
25282
+ this.filePath = path54.join(base, `${opts.sessionId}.jsonl`);
24895
25283
  this.post = opts.post;
24896
25284
  this.autoSchedule = opts.autoSchedule !== false;
24897
25285
  }
24898
25286
  /** Persist the entry to disk and trigger a flush. Returns once the
24899
25287
  * line is durable on disk (not once the POST succeeds). */
24900
25288
  async enqueue(entry) {
24901
- await fs46.mkdir(path53.dirname(this.filePath), { recursive: true });
24902
- await fs46.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
25289
+ await fs47.mkdir(path54.dirname(this.filePath), { recursive: true });
25290
+ await fs47.appendFile(this.filePath, JSON.stringify(entry) + "\n", "utf8");
24903
25291
  this.backoffIndex = 0;
24904
25292
  if (this.autoSchedule) this.scheduleFlush(0);
24905
25293
  }
@@ -24988,7 +25376,7 @@ var FilesOutbox = class {
24988
25376
  async readAll() {
24989
25377
  let raw = "";
24990
25378
  try {
24991
- raw = await fs46.readFile(this.filePath, "utf8");
25379
+ raw = await fs47.readFile(this.filePath, "utf8");
24992
25380
  } catch {
24993
25381
  return [];
24994
25382
  }
@@ -25012,12 +25400,12 @@ var FilesOutbox = class {
25012
25400
  async rewrite(entries) {
25013
25401
  const tmpPath = `${this.filePath}.${process.pid}.tmp`;
25014
25402
  if (entries.length === 0) {
25015
- await fs46.unlink(this.filePath).catch(() => void 0);
25403
+ await fs47.unlink(this.filePath).catch(() => void 0);
25016
25404
  return;
25017
25405
  }
25018
25406
  const payload = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
25019
- await fs46.writeFile(tmpPath, payload, "utf8");
25020
- await fs46.rename(tmpPath, this.filePath);
25407
+ await fs47.writeFile(tmpPath, payload, "utf8");
25408
+ await fs47.rename(tmpPath, this.filePath);
25021
25409
  }
25022
25410
  };
25023
25411
  function applyJitter(ms) {
@@ -25546,7 +25934,7 @@ var StreamingState = class {
25546
25934
  this.publisher.publishOutput({ type: "text", content: finalText, done: true }),
25547
25935
  flushSc
25548
25936
  ]);
25549
- return;
25937
+ return false;
25550
25938
  }
25551
25939
  log.info(
25552
25940
  "acpRunner",
@@ -25573,6 +25961,7 @@ var StreamingState = class {
25573
25961
  done: true
25574
25962
  });
25575
25963
  await flushSc;
25964
+ return true;
25576
25965
  }
25577
25966
  };
25578
25967
  var PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
@@ -25755,10 +26144,26 @@ async function surfaceStartupFailure(opts) {
25755
26144
  );
25756
26145
  errRelay.start();
25757
26146
  }
26147
+ function budgetBubbleMessage(agent, period) {
26148
+ return `\u{1F4B8} **Headroom budget reached for the ${period} period.**
26149
+
26150
+ The local Headroom proxy rejected the ${agent} request because the configured spending cap was hit. Choose an option below to continue.`;
26151
+ }
26152
+ function buildRelaunchProxyEnv(baseEnv) {
26153
+ const env = { ...baseEnv, HEADROOM_KOMPRESS_BACKEND: "onnx_cpu" };
26154
+ delete env["HEADROOM_BUDGET"];
26155
+ delete env["HEADROOM_BUDGET_PERIOD"];
26156
+ return env;
26157
+ }
25758
26158
  function failureBubble(opts) {
25759
26159
  if (looksLikeAuthFailure(opts.detail) || looksLikeAuthFailure(opts.recentStderr)) {
25760
26160
  return AUTH_FAILURE_MESSAGE;
25761
26161
  }
26162
+ const budgetHaystack = `${opts.detail}
26163
+ ${opts.recentStderr}`;
26164
+ if (looksLikeBudgetExceeded(budgetHaystack)) {
26165
+ return budgetBubbleMessage(opts.agent, extractBudgetPeriod(budgetHaystack));
26166
+ }
25762
26167
  if (looksLikeProviderOutage(opts.detail) || looksLikeProviderOutage(opts.recentStderr)) {
25763
26168
  return providerOutageMessage(opts.agent);
25764
26169
  }
@@ -25791,6 +26196,25 @@ async function reportCredentialInvalid(opts, fetchImpl = fetch) {
25791
26196
  } catch {
25792
26197
  }
25793
26198
  }
26199
+ async function postBudgetReached(opts, fetchImpl = fetch) {
26200
+ const url = `${resolveApiBaseUrl()}/api/sessions/${encodeURIComponent(opts.sessionId)}/headroom-budget-reached`;
26201
+ try {
26202
+ await fetchImpl(url, {
26203
+ method: "POST",
26204
+ headers: {
26205
+ "Content-Type": "application/json",
26206
+ "X-Plugin-Auth-Token": opts.pluginAuthToken
26207
+ },
26208
+ body: JSON.stringify({
26209
+ sessionId: opts.sessionId,
26210
+ pluginId: opts.pluginId,
26211
+ agent: opts.agent,
26212
+ period: opts.period
26213
+ })
26214
+ });
26215
+ } catch {
26216
+ }
26217
+ }
25794
26218
  async function handleGetConversation(args2) {
25795
26219
  const { relay, commandId, jsonlHistory, acpSessionId } = args2;
25796
26220
  try {
@@ -25946,7 +26370,9 @@ async function runAcpSession(opts) {
25946
26370
  flushHistory: () => void history.flush(),
25947
26371
  beginTurn: () => streaming.beginTurn(),
25948
26372
  getCurrentText: () => streaming.getCurrentText(),
25949
- closeTurn: () => streaming.closeTurnWithInteractiveDetection(),
26373
+ closeTurn: async () => {
26374
+ await streaming.closeTurnWithInteractiveDetection();
26375
+ },
25950
26376
  recoverFromFailedTurn: () => recoverFromFailedTurn(client2, streaming),
25951
26377
  reconnectWith1mDisabled,
25952
26378
  promptAgent: (blocks) => client2.prompt(blocks),
@@ -25954,6 +26380,58 @@ async function runAcpSession(opts) {
25954
26380
  describeError,
25955
26381
  log: (msg) => log.info("acpRunner", msg)
25956
26382
  });
26383
+ let _budgetReachedPosted = false;
26384
+ const relaunchProxyWithoutBudget = async () => {
26385
+ const { spawn: spawn32 } = await import("child_process");
26386
+ try {
26387
+ const killer = spawn32("pkill", ["-TERM", "-f", "headroom.*proxy"], {
26388
+ detached: true,
26389
+ stdio: "ignore"
26390
+ });
26391
+ killer.once("error", () => {
26392
+ });
26393
+ killer.unref();
26394
+ } catch {
26395
+ }
26396
+ await new Promise((r) => setTimeout(r, 500));
26397
+ const proxyEnv = buildRelaunchProxyEnv(process.env);
26398
+ try {
26399
+ const proxy = spawn32(
26400
+ "headroom",
26401
+ ["proxy", "--port", "8787"],
26402
+ { stdio: "ignore", detached: true, env: proxyEnv }
26403
+ );
26404
+ proxy.once("error", (e) => {
26405
+ log.warn("acpRunner", `budget recovery proxy relaunch error (best-effort): ${e.message}`);
26406
+ });
26407
+ proxy.unref();
26408
+ } catch (e) {
26409
+ log.warn(
26410
+ "acpRunner",
26411
+ `budget recovery proxy relaunch failed (best-effort): ${e instanceof Error ? e.message : String(e)}`
26412
+ );
26413
+ }
26414
+ await new Promise((r) => setTimeout(r, 3e3));
26415
+ };
26416
+ const budgetRecovery = createBudgetRecovery({
26417
+ publishText: (text) => publisher.publishOutput({ type: "text", content: text, done: true }),
26418
+ publishSelectPrompt: (question, options) => publisher.publishOutput({
26419
+ type: "select_prompt",
26420
+ content: question,
26421
+ options,
26422
+ optionDescriptions: options.map(() => ""),
26423
+ currentIndex: 0,
26424
+ done: true
26425
+ }),
26426
+ publishAwaitingAnswer: (prompt, options) => publisher.publishAwaitingAnswer({ questionId: (0, import_node_crypto7.randomUUID)(), prompt, options }),
26427
+ publishRawChunk: (chunk) => publisher.publishOutput(chunk),
26428
+ sendResult: (commandId, status2, result) => relay.sendResult(commandId, status2, result),
26429
+ appendAgentReply: (text) => history.appendAgentReply(text),
26430
+ flushHistory: () => void history.flush(),
26431
+ relaunchProxyWithoutBudget,
26432
+ agentId: opts.agent,
26433
+ log: (msg) => log.info("acpRunner", msg)
26434
+ });
25957
26435
  showInfo(`Starting ${opts.agent} via ACP adapter (${opts.adapter.requiresAgentBinary})\u2026`);
25958
26436
  let handshake;
25959
26437
  try {
@@ -26063,7 +26541,11 @@ async function runAcpSession(opts) {
26063
26541
  getBeads,
26064
26542
  publisher,
26065
26543
  recentStderr,
26066
- oneMRecovery
26544
+ oneMRecovery,
26545
+ budgetRecovery,
26546
+ { get: () => _budgetReachedPosted, set: (v) => {
26547
+ _budgetReachedPosted = v;
26548
+ } }
26067
26549
  );
26068
26550
  },
26069
26551
  { id: opts.agent, name: opts.agent, displayName: opts.agent }
@@ -26087,7 +26569,7 @@ async function runAcpSession(opts) {
26087
26569
  await new Promise(() => {
26088
26570
  });
26089
26571
  }
26090
- async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, oneMRecovery) {
26572
+ async function handleCommand(cmd, client2, relay, acpSessionId, models, streaming, opts, history, jsonlHistory, agentCaps, turnFiles, getBeads, publisher, recentStderr, oneMRecovery, budgetRecovery, budgetReachedFlag) {
26091
26573
  switch (cmd.type) {
26092
26574
  case "beads_action": {
26093
26575
  const beads = getBeads();
@@ -26147,13 +26629,19 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
26147
26629
  } else if (shouldOfferOneMRecovery({ detail: "", recentStderr: recentStderr.join("\n"), finalText })) {
26148
26630
  await oneMRecovery.offer(cmd.id, blocks);
26149
26631
  } else {
26150
- await streaming.closeTurnWithInteractiveDetection();
26632
+ const emittedSelectPrompt = await streaming.closeTurnWithInteractiveDetection();
26151
26633
  const replyLine = formatAgentReplyLine(finalText);
26152
26634
  if (replyLine.length > 0) {
26153
26635
  showInfo(replyLine);
26154
26636
  }
26155
26637
  history.appendAgentReply(finalText);
26156
26638
  void history.flush();
26639
+ if (!emittedSelectPrompt) {
26640
+ const suggestion = deriveInputSuggestion(finalText);
26641
+ if (suggestion !== null) {
26642
+ void publisher.publishOutput({ type: "input_suggestion", content: suggestion, done: true });
26643
+ }
26644
+ }
26157
26645
  turnFiles.flushTurn().catch((err) => {
26158
26646
  log.warn("acpRunner", `turnFiles.flushTurn failed: ${describeError(err)}`);
26159
26647
  });
@@ -26165,6 +26653,24 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
26165
26653
  const detail = describeError(err);
26166
26654
  log.warn("acpRunner", `prompt failed: ${detail}`);
26167
26655
  await cancelStuckTurn(client2);
26656
+ if (looksLikeBudgetExceeded(`${detail}
26657
+ ${recentStderr.join("\n")}`)) {
26658
+ await streaming.closeAll();
26659
+ if (!budgetReachedFlag.get()) {
26660
+ budgetReachedFlag.set(true);
26661
+ void postBudgetReached({
26662
+ sessionId: opts.sessionId,
26663
+ pluginId: opts.pluginId,
26664
+ pluginAuthToken: opts.pluginAuthToken,
26665
+ agent: opts.agent,
26666
+ period: extractBudgetPeriod(`${detail}
26667
+ ${recentStderr.join("\n")}`)
26668
+ });
26669
+ }
26670
+ await budgetRecovery.offer(cmd.id, blocks, `${detail}
26671
+ ${recentStderr.join("\n")}`);
26672
+ return;
26673
+ }
26168
26674
  if (shouldOfferOneMRecovery({ detail, recentStderr: recentStderr.join("\n"), finalText: "" })) {
26169
26675
  await streaming.closeAll();
26170
26676
  await oneMRecovery.offer(cmd.id, blocks);
@@ -26271,6 +26777,7 @@ async function handleCommand(cmd, client2, relay, acpSessionId, models, streamin
26271
26777
  const index = typeof payload?.index === "number" ? payload.index : 0;
26272
26778
  const offset = typeof payload?.from === "number" ? payload.from : 0;
26273
26779
  const absoluteIndex = index + offset;
26780
+ if (await budgetRecovery.tryRecover(cmd.id, absoluteIndex)) return;
26274
26781
  const result = streaming.resolveSelection(absoluteIndex);
26275
26782
  switch (result.kind) {
26276
26783
  case "resolved":
@@ -27527,15 +28034,15 @@ function fetchQuotaUsage(runtime, historySvc) {
27527
28034
  }
27528
28035
 
27529
28036
  // src/agents/claude/onboarding.ts
27530
- var fs47 = __toESM(require("fs"));
27531
- var os38 = __toESM(require("os"));
27532
- var path54 = __toESM(require("path"));
28037
+ var fs48 = __toESM(require("fs"));
28038
+ var os39 = __toESM(require("os"));
28039
+ var path55 = __toESM(require("path"));
27533
28040
  function ensureClaudeOnboarded() {
27534
28041
  try {
27535
- const file = path54.join(os38.homedir(), ".claude.json");
28042
+ const file = path55.join(os39.homedir(), ".claude.json");
27536
28043
  let config = {};
27537
28044
  try {
27538
- config = JSON.parse(fs47.readFileSync(file, "utf8"));
28045
+ config = JSON.parse(fs48.readFileSync(file, "utf8"));
27539
28046
  } catch {
27540
28047
  }
27541
28048
  if (config.hasCompletedOnboarding === true && typeof config.theme === "string") {
@@ -27546,8 +28053,8 @@ function ensureClaudeOnboarded() {
27546
28053
  if (typeof config.lastOnboardingVersion !== "string") {
27547
28054
  config.lastOnboardingVersion = "2.1.177";
27548
28055
  }
27549
- fs47.mkdirSync(path54.dirname(file), { recursive: true });
27550
- fs47.writeFileSync(file, JSON.stringify(config, null, 2));
28056
+ fs48.mkdirSync(path55.dirname(file), { recursive: true });
28057
+ fs48.writeFileSync(file, JSON.stringify(config, null, 2));
27551
28058
  log.info("claude", "pre-completed Claude onboarding (skip first-run theme picker)");
27552
28059
  } catch (err) {
27553
28060
  log.warn("claude", `ensureClaudeOnboarded failed (non-fatal): ${err.message}`);
@@ -28197,7 +28704,7 @@ var import_picocolors11 = __toESM(require("picocolors"));
28197
28704
  var import_child_process24 = require("child_process");
28198
28705
  var import_util4 = require("util");
28199
28706
  var import_picocolors9 = __toESM(require("picocolors"));
28200
- var path55 = __toESM(require("path"));
28707
+ var path56 = __toESM(require("path"));
28201
28708
  var execFileP6 = (0, import_util4.promisify)(import_child_process24.execFile);
28202
28709
  var MAX_BUFFER = 8 * 1024 * 1024;
28203
28710
  function resetStdinForChild() {
@@ -28686,7 +29193,7 @@ var GitHubCodespacesProvider = class {
28686
29193
  });
28687
29194
  }
28688
29195
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
28689
- const remoteDir = path55.posix.dirname(remotePath);
29196
+ const remoteDir = path56.posix.dirname(remotePath);
28690
29197
  const parts = [
28691
29198
  `mkdir -p ${shellQuote(remoteDir)}`,
28692
29199
  `cat > ${shellQuote(remotePath)}`
@@ -28756,7 +29263,7 @@ function shellQuote(s) {
28756
29263
  // src/services/providers/gitpod.ts
28757
29264
  var import_child_process25 = require("child_process");
28758
29265
  var import_util5 = require("util");
28759
- var path56 = __toESM(require("path"));
29266
+ var path57 = __toESM(require("path"));
28760
29267
  var import_picocolors10 = __toESM(require("picocolors"));
28761
29268
  var execFileP7 = (0, import_util5.promisify)(import_child_process25.execFile);
28762
29269
  var MAX_BUFFER2 = 8 * 1024 * 1024;
@@ -28996,7 +29503,7 @@ var GitpodProvider = class {
28996
29503
  });
28997
29504
  }
28998
29505
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
28999
- const remoteDir = path56.posix.dirname(remotePath);
29506
+ const remoteDir = path57.posix.dirname(remotePath);
29000
29507
  const parts = [
29001
29508
  `mkdir -p ${shellQuote2(remoteDir)}`,
29002
29509
  `cat > ${shellQuote2(remotePath)}`
@@ -29032,7 +29539,7 @@ function shellQuote2(s) {
29032
29539
  // src/services/providers/gitlab-workspaces.ts
29033
29540
  var import_child_process26 = require("child_process");
29034
29541
  var import_util6 = require("util");
29035
- var path57 = __toESM(require("path"));
29542
+ var path58 = __toESM(require("path"));
29036
29543
  var execFileP8 = (0, import_util6.promisify)(import_child_process26.execFile);
29037
29544
  var MAX_BUFFER3 = 8 * 1024 * 1024;
29038
29545
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
@@ -29292,7 +29799,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
29292
29799
  }
29293
29800
  async uploadFile(workspaceId, remotePath, contents, options = {}) {
29294
29801
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
29295
- const remoteDir = path57.posix.dirname(remotePath);
29802
+ const remoteDir = path58.posix.dirname(remotePath);
29296
29803
  const parts = [`mkdir -p ${shellQuote3(remoteDir)}`, `cat > ${shellQuote3(remotePath)}`];
29297
29804
  if (options.mode != null) {
29298
29805
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
@@ -29360,7 +29867,7 @@ function shellQuote3(s) {
29360
29867
  // src/services/providers/railway.ts
29361
29868
  var import_child_process27 = require("child_process");
29362
29869
  var import_util7 = require("util");
29363
- var path58 = __toESM(require("path"));
29870
+ var path59 = __toESM(require("path"));
29364
29871
  var execFileP9 = (0, import_util7.promisify)(import_child_process27.execFile);
29365
29872
  var MAX_BUFFER4 = 8 * 1024 * 1024;
29366
29873
  function resetStdinForChild4() {
@@ -29596,7 +30103,7 @@ var RailwayProvider = class {
29596
30103
  if (!projectId || !serviceId) {
29597
30104
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
29598
30105
  }
29599
- const remoteDir = path58.posix.dirname(remotePath);
30106
+ const remoteDir = path59.posix.dirname(remotePath);
29600
30107
  const parts = [`mkdir -p ${shellQuote4(remoteDir)}`, `cat > ${shellQuote4(remotePath)}`];
29601
30108
  if (options.mode != null) {
29602
30109
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
@@ -30242,8 +30749,8 @@ async function invite() {
30242
30749
  var import_node_dns = require("dns");
30243
30750
  var import_node_util5 = require("util");
30244
30751
  var import_node_crypto8 = require("crypto");
30245
- var fs48 = __toESM(require("fs"));
30246
- var path59 = __toESM(require("path"));
30752
+ var fs49 = __toESM(require("fs"));
30753
+ var path60 = __toESM(require("path"));
30247
30754
  var import_picocolors14 = __toESM(require("picocolors"));
30248
30755
  var dnsResolveP = (0, import_node_util5.promisify)(import_node_dns.resolve);
30249
30756
  async function checkDns(apiBase2) {
@@ -30299,13 +30806,13 @@ async function checkHealth(apiBase2) {
30299
30806
  }
30300
30807
  }
30301
30808
  function checkConfigDir() {
30302
- const dir = path59.join(require("os").homedir(), ".codeam");
30809
+ const dir = path60.join(require("os").homedir(), ".codeam");
30303
30810
  try {
30304
- fs48.mkdirSync(dir, { recursive: true, mode: 448 });
30305
- const probe = path59.join(dir, ".doctor-probe");
30306
- fs48.writeFileSync(probe, "ok", { mode: 384 });
30307
- const read2 = fs48.readFileSync(probe, "utf8");
30308
- fs48.unlinkSync(probe);
30811
+ fs49.mkdirSync(dir, { recursive: true, mode: 448 });
30812
+ const probe = path60.join(dir, ".doctor-probe");
30813
+ fs49.writeFileSync(probe, "ok", { mode: 384 });
30814
+ const read2 = fs49.readFileSync(probe, "utf8");
30815
+ fs49.unlinkSync(probe);
30309
30816
  if (read2 !== "ok") throw new Error("write/read round-trip mismatch");
30310
30817
  return {
30311
30818
  id: "config-dir",
@@ -30345,9 +30852,9 @@ function checkSessions() {
30345
30852
  }
30346
30853
  }
30347
30854
  function checkAgentBinaries() {
30348
- const os40 = createOsStrategy();
30855
+ const os41 = createOsStrategy();
30349
30856
  return getEnabledAgents().map((meta) => {
30350
- const found = os40.findInPath(meta.binaryName);
30857
+ const found = os41.findInPath(meta.binaryName);
30351
30858
  return {
30352
30859
  id: `agent-${meta.id}`,
30353
30860
  label: `Agent binary: ${meta.displayName} (${meta.binaryName})`,
@@ -30369,7 +30876,7 @@ function checkNodePty() {
30369
30876
  detail: "not required on this platform"
30370
30877
  };
30371
30878
  }
30372
- const vendoredPath = path59.join(__dirname, "vendor", "node-pty");
30879
+ const vendoredPath = path60.join(__dirname, "vendor", "node-pty");
30373
30880
  for (const target of [vendoredPath, "node-pty"]) {
30374
30881
  try {
30375
30882
  require(target);
@@ -30411,7 +30918,7 @@ function checkChokidar() {
30411
30918
  }
30412
30919
  async function doctor(args2 = []) {
30413
30920
  const json = args2.includes("--json");
30414
- const cliVersion = true ? "2.49.5" : "0.0.0-dev";
30921
+ const cliVersion = true ? "2.51.0" : "0.0.0-dev";
30415
30922
  const apiBase2 = resolveApiBaseUrl();
30416
30923
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
30417
30924
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -30610,7 +31117,7 @@ async function completion(args2) {
30610
31117
  // src/commands/version.ts
30611
31118
  var import_picocolors15 = __toESM(require("picocolors"));
30612
31119
  function version2() {
30613
- const v = true ? "2.49.5" : "unknown";
31120
+ const v = true ? "2.51.0" : "unknown";
30614
31121
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
30615
31122
  }
30616
31123
 
@@ -30759,10 +31266,10 @@ var EXIT_CODE_NAMES = {
30759
31266
  };
30760
31267
 
30761
31268
  // src/index.ts
30762
- var os39 = __toESM(require("os"));
31269
+ var os40 = __toESM(require("os"));
30763
31270
  if (!process.env.HOME) {
30764
31271
  try {
30765
- const home = os39.homedir();
31272
+ const home = os40.homedir();
30766
31273
  if (home) process.env.HOME = home;
30767
31274
  } catch {
30768
31275
  }