codeam-cli 2.75.45 → 2.75.47

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 +12 -0
  2. package/dist/index.js +718 -697
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3763,9 +3763,9 @@ var _default = makeConfig();
3763
3763
  var { getConfig, ensurePluginId, addSession, removeSession, setActiveSession, getActiveSession, getActiveSessionForAgent, setDisable1mContext, setSessionAgent, setSquadAuto, getSquadAuto, clearAll, saveCliConfig, loadCliConfig } = _default;
3764
3764
 
3765
3765
  // src/commands/pair-auto.ts
3766
- var fs52 = __toESM(require("fs"));
3767
- var os40 = __toESM(require("os"));
3768
- var path60 = __toESM(require("path"));
3766
+ var fs54 = __toESM(require("fs"));
3767
+ var os42 = __toESM(require("os"));
3768
+ var path62 = __toESM(require("path"));
3769
3769
  var import_crypto3 = require("crypto");
3770
3770
 
3771
3771
  // src/services/telemetry.service.ts
@@ -10577,7 +10577,7 @@ function readAnonId() {
10577
10577
  }
10578
10578
  function superProperties() {
10579
10579
  return {
10580
- cliVersion: true ? "2.75.45" : "0.0.0-dev",
10580
+ cliVersion: true ? "2.75.47" : "0.0.0-dev",
10581
10581
  nodeVersion: process.version,
10582
10582
  platform: process.platform,
10583
10583
  arch: process.arch,
@@ -10822,7 +10822,7 @@ var http = __toESM(require("http"));
10822
10822
  // package.json
10823
10823
  var package_default = {
10824
10824
  name: "codeam-cli",
10825
- version: "2.75.45",
10825
+ version: "2.75.47",
10826
10826
  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.",
10827
10827
  type: "commonjs",
10828
10828
  main: "dist/index.js",
@@ -12156,7 +12156,7 @@ var CommandRelayService = class _CommandRelayService {
12156
12156
  // fresh + clear the "CLI update available" banner after a self-update
12157
12157
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
12158
12158
  // pair/reconnect). Older backends ignore the extra field.
12159
- ..."2.75.45" ? { ideVersion: "2.75.45" } : {}
12159
+ ..."2.75.47" ? { ideVersion: "2.75.47" } : {}
12160
12160
  };
12161
12161
  try {
12162
12162
  const res = await _postJson(`${API_BASE2}/api/plugin/heartbeat`, body);
@@ -13632,9 +13632,9 @@ function closeAllTerminals() {
13632
13632
  }
13633
13633
 
13634
13634
  // src/commands/start/handlers.ts
13635
- var fs51 = __toESM(require("fs"));
13636
- var os39 = __toESM(require("os"));
13637
- var path59 = __toESM(require("path"));
13635
+ var fs53 = __toESM(require("fs"));
13636
+ var os41 = __toESM(require("os"));
13637
+ var path61 = __toESM(require("path"));
13638
13638
  var import_crypto2 = require("crypto");
13639
13639
  var import_child_process25 = require("child_process");
13640
13640
 
@@ -17638,6 +17638,8 @@ async function spawnAndCapture(cmd, args2, opts = {}) {
17638
17638
  clearTimeout(timer);
17639
17639
  activeChildren.delete(child);
17640
17640
  if (code !== 0) {
17641
+ const failedOut = stdout.trim();
17642
+ if (failedOut) opts.onFailedOutput?.(failedOut);
17641
17643
  settle(null);
17642
17644
  return;
17643
17645
  }
@@ -18143,6 +18145,7 @@ var ClaudeRuntimeStrategy = class {
18143
18145
  if (!launch) return null;
18144
18146
  return spawnAndCapture(launch.cmd, launch.args, {
18145
18147
  onStderr: opts?.onStderr,
18148
+ onFailedOutput: opts?.onFailedOutput,
18146
18149
  cwd: opts?.cwd,
18147
18150
  timeoutMs: opts?.timeoutMs
18148
18151
  });
@@ -19230,6 +19233,7 @@ var CodexRuntimeStrategy = class {
19230
19233
  const launch = this.os.buildLaunch(binary, ["exec", prompt]);
19231
19234
  return spawnAndCapture(launch.cmd, launch.args, {
19232
19235
  onStderr: opts?.onStderr,
19236
+ onFailedOutput: opts?.onFailedOutput,
19233
19237
  cwd: opts?.cwd,
19234
19238
  timeoutMs: opts?.timeoutMs
19235
19239
  });
@@ -20335,6 +20339,7 @@ var CursorRuntimeStrategy = class {
20335
20339
  const launch = this.os.buildLaunch(binary, ["--print", "--force", "--trust", prompt]);
20336
20340
  return spawnAndCapture(launch.cmd, launch.args, {
20337
20341
  onStderr: opts?.onStderr,
20342
+ onFailedOutput: opts?.onFailedOutput,
20338
20343
  cwd: opts?.cwd,
20339
20344
  timeoutMs: opts?.timeoutMs
20340
20345
  });
@@ -21138,6 +21143,7 @@ var GeminiRuntimeStrategy = class {
21138
21143
  const launch = this.os.buildLaunch(binary, ["--skip-trust", "-p", prompt]);
21139
21144
  return spawnAndCapture(launch.cmd, launch.args, {
21140
21145
  onStderr: opts?.onStderr,
21146
+ onFailedOutput: opts?.onFailedOutput,
21141
21147
  cwd: opts?.cwd,
21142
21148
  timeoutMs: opts?.timeoutMs
21143
21149
  });
@@ -21416,6 +21422,7 @@ var KimiRuntimeStrategy = class {
21416
21422
  const launch = this.os.buildLaunch(binary, ["-p", prompt, "--output-format", "text"]);
21417
21423
  return spawnAndCapture(launch.cmd, launch.args, {
21418
21424
  onStderr: opts?.onStderr,
21425
+ onFailedOutput: opts?.onFailedOutput,
21419
21426
  cwd: opts?.cwd,
21420
21427
  timeoutMs: opts?.timeoutMs
21421
21428
  });
@@ -21536,6 +21543,7 @@ var OpencodeRuntimeStrategy = class {
21536
21543
  const launch = this.os.buildLaunch(binary, ["run", prompt]);
21537
21544
  return spawnAndCapture(launch.cmd, launch.args, {
21538
21545
  onStderr: opts?.onStderr,
21546
+ onFailedOutput: opts?.onFailedOutput,
21539
21547
  cwd: opts?.cwd,
21540
21548
  timeoutMs: opts?.timeoutMs
21541
21549
  });
@@ -22676,17 +22684,548 @@ function setGuardrailPolicy(raw, homeDir2 = os28.homedir()) {
22676
22684
  return next;
22677
22685
  }
22678
22686
 
22679
- // src/services/preview/port-registry.ts
22680
- var fs33 = __toESM(require("fs"));
22681
- var os29 = __toESM(require("os"));
22687
+ // src/agents/acp/oneMContextRecovery.ts
22688
+ var ONE_M_CONTEXT_CREDITS_RE = /usage credits required for 1m context/i;
22689
+ function looksLike1mContextCreditsError(text) {
22690
+ return ONE_M_CONTEXT_CREDITS_RE.test(text);
22691
+ }
22692
+ function shouldOfferOneMRecovery(opts) {
22693
+ return looksLike1mContextCreditsError(opts.detail) || looksLike1mContextCreditsError(opts.recentStderr) || looksLike1mContextCreditsError(opts.finalText);
22694
+ }
22695
+
22696
+ // src/commands/host/house-proxy-config.ts
22697
+ var fs34 = __toESM(require("fs"));
22698
+ var os30 = __toESM(require("os"));
22682
22699
  var path40 = __toESM(require("path"));
22700
+
22701
+ // src/lib/restrict-to-owner.ts
22702
+ var import_node_fs7 = __toESM(require("fs"));
22703
+ var import_node_os4 = __toESM(require("os"));
22704
+ var import_node_child_process18 = require("child_process");
22705
+ var BROAD_WINDOWS_SIDS = [
22706
+ "*S-1-1-0",
22707
+ "*S-1-5-11",
22708
+ "*S-1-5-32-545",
22709
+ "*S-1-5-32-544",
22710
+ "*S-1-5-18"
22711
+ ];
22712
+ function restrictToOwner(filePath) {
22713
+ try {
22714
+ if (process.platform === "win32") {
22715
+ const username = import_node_os4.default.userInfo().username;
22716
+ (0, import_node_child_process18.execFileSync)(
22717
+ "icacls",
22718
+ [
22719
+ filePath,
22720
+ "/inheritance:r",
22721
+ "/grant:r",
22722
+ `${username}:F`,
22723
+ ...BROAD_WINDOWS_SIDS.flatMap((sid) => ["/remove", sid])
22724
+ ],
22725
+ { stdio: "ignore" }
22726
+ );
22727
+ } else {
22728
+ import_node_fs7.default.chmodSync(filePath, 384);
22729
+ }
22730
+ } catch {
22731
+ }
22732
+ }
22733
+
22734
+ // src/commands/host/house-proxy-config.ts
22735
+ function houseProxyConfigPath() {
22736
+ return path40.join(os30.homedir(), ".codeam", "house-proxy.json");
22737
+ }
22738
+ var DEPLOY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
22739
+ function deployHouseProxyConfigPath(deployId) {
22740
+ if (!DEPLOY_ID_RE.test(deployId) || deployId.includes("..")) return null;
22741
+ return path40.join(os30.homedir(), ".codeam", "house-proxy", `${deployId}.json`);
22742
+ }
22743
+ function writeJson0600(file, value) {
22744
+ fs34.mkdirSync(path40.dirname(file), { recursive: true, mode: 448 });
22745
+ const tmp = `${file}.tmp-${process.pid}`;
22746
+ fs34.writeFileSync(tmp, JSON.stringify(value, null, 2), { encoding: "utf8", mode: 384 });
22747
+ fs34.renameSync(tmp, file);
22748
+ restrictToOwner(file);
22749
+ }
22750
+ function persistHouseProxyConfig(config, deployId) {
22751
+ try {
22752
+ const perDeploy = deployId ? deployHouseProxyConfigPath(deployId) : null;
22753
+ if (perDeploy) writeJson0600(perDeploy, config);
22754
+ writeJson0600(houseProxyConfigPath(), config);
22755
+ } catch (err) {
22756
+ log.warn(
22757
+ "host-agent",
22758
+ `failed to persist house-proxy config (best-effort): ${err instanceof Error ? err.message : String(err)}`
22759
+ );
22760
+ }
22761
+ }
22762
+ function clearHouseProxyConfig(deployId) {
22763
+ try {
22764
+ fs34.rmSync(houseProxyConfigPath(), { force: true });
22765
+ const perDeploy = deployId ? deployHouseProxyConfigPath(deployId) : null;
22766
+ if (perDeploy) writeJson0600(perDeploy, { none: true });
22767
+ } catch {
22768
+ }
22769
+ }
22770
+ function readHouseProxyChildEnv(deployId) {
22771
+ try {
22772
+ const perDeploy = deployId ? deployHouseProxyConfigPath(deployId) : null;
22773
+ const file = perDeploy && fs34.existsSync(perDeploy) ? perDeploy : houseProxyConfigPath();
22774
+ const raw = fs34.readFileSync(file, "utf8");
22775
+ const parsed = JSON.parse(raw);
22776
+ if (typeof parsed !== "object" || parsed === null) return {};
22777
+ const o = parsed;
22778
+ if (o.none === true) return {};
22779
+ if (typeof o.baseUrl !== "string" || !o.baseUrl) return {};
22780
+ if (typeof o.token !== "string" || !o.token) return {};
22781
+ return buildHouseProxyChildEnv({
22782
+ baseUrl: o.baseUrl,
22783
+ token: o.token,
22784
+ openRouter: o.openRouter === true,
22785
+ ...typeof o.claudeConfigDir === "string" && o.claudeConfigDir ? { claudeConfigDir: o.claudeConfigDir } : {},
22786
+ ...typeof o.model === "string" && o.model ? { model: o.model } : {},
22787
+ ...typeof o.managedAgentId === "string" && o.managedAgentId ? { managedAgentId: o.managedAgentId } : {}
22788
+ });
22789
+ } catch {
22790
+ return {};
22791
+ }
22792
+ }
22793
+ var HOUSE_MODEL_CONTEXT_TOKENS = "512000";
22794
+ function buildHouseProxyChildEnv(cfg) {
22795
+ const env = {
22796
+ ANTHROPIC_BASE_URL: cfg.baseUrl,
22797
+ ANTHROPIC_AUTH_TOKEN: cfg.token,
22798
+ // ⚠️ MAX_CONTEXT_TOKENS, not (only) AUTO_COMPACT_WINDOW — and this is
22799
+ // HOUSE-AGENT ONLY: it lives in this builder precisely so a user's own
22800
+ // Claude Code (a real Anthropic model claude knows) is never touched.
22801
+ //
22802
+ // claude does not know `MiniMax-M3`, so it falls back to its
22803
+ // unknown-model default (200k). `CLAUDE_CODE_AUTO_COMPACT_WINDOW` is then
22804
+ // applied as `Math.min(assumedWindow, env)` — it can only SHRINK the
22805
+ // window, never grow it — so the 512000 we used to set here was inert:
22806
+ // claude kept believing 200k. Meanwhile a session with a dozen linked
22807
+ // integrations carries ~175k tokens of MCP tool schemas on EVERY turn
22808
+ // (measured: 475 tools ≈ 698 KB on a real box), so the context started
22809
+ // ~88% full and a 28-character prompt triggered a compaction, then the
22810
+ // next small tool result triggered another: "Autocompact is thrashing:
22811
+ // the context refilled to the limit within 3 turns", 13 compactions in
22812
+ // one session (rafaelph90.br@gmail.com, 2026-09-03).
22813
+ //
22814
+ // claude's own unknown-model notice names the fix: "set
22815
+ // CLAUDE_CODE_MAX_CONTEXT_TOKENS to its real window" — that variable is
22816
+ // what `wL()` returns for an unrecognised model. MiniMax-M3 is a 1M-context
22817
+ // model with a guaranteed minimum of 512K, so 512000 here is the
22818
+ // conservative truth, not a wish. AUTO_COMPACT_WINDOW stays as the
22819
+ // compaction threshold beneath it.
22820
+ CLAUDE_CODE_MAX_CONTEXT_TOKENS: HOUSE_MODEL_CONTEXT_TOKENS,
22821
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: HOUSE_MODEL_CONTEXT_TOKENS,
22822
+ API_TIMEOUT_MS: "3000000"
22823
+ };
22824
+ if (cfg.openRouter === true) {
22825
+ env.ANTHROPIC_API_KEY = "";
22826
+ } else {
22827
+ const model = typeof cfg.model === "string" && cfg.model ? cfg.model : "MiniMax-M3";
22828
+ env.ANTHROPIC_MODEL = model;
22829
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL = model;
22830
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL = model;
22831
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL = model;
22832
+ }
22833
+ if (typeof cfg.claudeConfigDir === "string" && cfg.claudeConfigDir) {
22834
+ env.CLAUDE_CONFIG_DIR = cfg.claudeConfigDir;
22835
+ }
22836
+ if (isManagedProviderId(cfg.managedAgentId)) {
22837
+ env[MANAGED_AGENT_ENV] = cfg.managedAgentId;
22838
+ }
22839
+ return env;
22840
+ }
22841
+ function houseRailWireId(env) {
22842
+ const managed = env[MANAGED_AGENT_ENV];
22843
+ return isManagedProviderId(managed) ? managed : HOUSE_AGENT_ID;
22844
+ }
22845
+ var HOUSE_PROXY_ENV_KEYS = [
22846
+ "ANTHROPIC_BASE_URL",
22847
+ "ANTHROPIC_AUTH_TOKEN",
22848
+ "ANTHROPIC_API_KEY",
22849
+ "ANTHROPIC_MODEL",
22850
+ "ANTHROPIC_DEFAULT_SONNET_MODEL",
22851
+ "ANTHROPIC_DEFAULT_OPUS_MODEL",
22852
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL",
22853
+ "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
22854
+ "CLAUDE_CODE_MAX_CONTEXT_TOKENS",
22855
+ "API_TIMEOUT_MS",
22856
+ "CLAUDE_CONFIG_DIR",
22857
+ MANAGED_AGENT_ENV
22858
+ ];
22859
+ function clearHouseProxyEnvOverrides() {
22860
+ const out2 = {};
22861
+ for (const key of HOUSE_PROXY_ENV_KEYS) out2[key] = void 0;
22862
+ return out2;
22863
+ }
22864
+ function isHouseProxyEnv(env) {
22865
+ return typeof env.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN.length > 0 && (env.ANTHROPIC_BASE_URL ?? "").includes("/api/v1/agent-proxy");
22866
+ }
22867
+ function currentRailWireId(env = process.env) {
22868
+ return isHouseProxyEnv(env) ? houseRailWireId(env) : null;
22869
+ }
22870
+ function pickHouseProxyEnv(env) {
22871
+ const out2 = {};
22872
+ for (const key of HOUSE_PROXY_ENV_KEYS) {
22873
+ const v = env[key];
22874
+ if (typeof v === "string") out2[key] = v;
22875
+ }
22876
+ return out2;
22877
+ }
22878
+
22879
+ // src/lib/provider-routing.ts
22880
+ var fs35 = __toESM(require("fs"));
22881
+ var os31 = __toESM(require("os"));
22882
+ var path41 = __toESM(require("path"));
22883
+ var PROVIDER_BASE_URL_ENV_KEYS = [
22884
+ "ANTHROPIC_BASE_URL",
22885
+ "OPENAI_BASE_URL",
22886
+ "OPENAI_API_BASE",
22887
+ "GEMINI_API_BASE",
22888
+ "GOOGLE_GEMINI_BASE_URL"
22889
+ ];
22890
+ var ROUTING_DEFAULT = "default";
22891
+ function redactUrlToOrigin(raw) {
22892
+ if (typeof raw !== "string") return null;
22893
+ const trimmed = raw.trim();
22894
+ if (!trimmed) return null;
22895
+ try {
22896
+ const u2 = new URL(trimmed);
22897
+ if (!u2.host) return "invalid-url";
22898
+ return `${u2.protocol}//${u2.host}`;
22899
+ } catch {
22900
+ return "invalid-url";
22901
+ }
22902
+ }
22903
+ function readOpencodeProviderOrigins(opts = {}) {
22904
+ const home = opts.homeDir ?? os31.homedir();
22905
+ const readFile7 = opts.readFile ?? ((f) => fs35.readFileSync(f, "utf8"));
22906
+ const candidates = [];
22907
+ if (opts.cwd) {
22908
+ candidates.push(path41.join(opts.cwd, "opencode.json"), path41.join(opts.cwd, "opencode.jsonc"));
22909
+ }
22910
+ candidates.push(
22911
+ path41.join(home, ".config", "opencode", "opencode.json"),
22912
+ path41.join(home, ".config", "opencode", "opencode.jsonc")
22913
+ );
22914
+ const out2 = [];
22915
+ const seen2 = /* @__PURE__ */ new Set();
22916
+ for (const file of candidates) {
22917
+ let text;
22918
+ try {
22919
+ text = readFile7(file);
22920
+ } catch {
22921
+ continue;
22922
+ }
22923
+ let parsed;
22924
+ try {
22925
+ parsed = JSON.parse(text.replace(/^\s*\/\/.*$/gm, ""));
22926
+ } catch {
22927
+ continue;
22928
+ }
22929
+ const providers = parsed?.provider;
22930
+ if (!providers || typeof providers !== "object") continue;
22931
+ for (const [id, cfg] of Object.entries(providers)) {
22932
+ if (seen2.has(id)) continue;
22933
+ const options = cfg?.options;
22934
+ const base = options?.baseURL ?? options?.baseUrl ?? options?.base_url;
22935
+ const origin = redactUrlToOrigin(base);
22936
+ if (!origin) continue;
22937
+ seen2.add(id);
22938
+ out2.push({ provider: id, origin });
22939
+ }
22940
+ }
22941
+ return out2;
22942
+ }
22943
+ function describeProviderRouting(env, opts = {}) {
22944
+ const routed = {};
22945
+ for (const key of PROVIDER_BASE_URL_ENV_KEYS) {
22946
+ routed[key] = redactUrlToOrigin(env[key]) ?? ROUTING_DEFAULT;
22947
+ }
22948
+ let opencode = [];
22949
+ try {
22950
+ opencode = readOpencodeProviderOrigins(opts);
22951
+ } catch {
22952
+ }
22953
+ const houseKeys = HOUSE_PROXY_ENV_KEYS.filter(
22954
+ (k2) => typeof env[k2] === "string" && env[k2].length > 0
22955
+ );
22956
+ const codeamKeys = Object.keys(env).filter((k2) => k2.startsWith("CODEAM_") && typeof env[k2] === "string" && env[k2].length > 0).sort();
22957
+ return { env: routed, opencode, houseProxy: isHouseProxyEnv(env), houseKeys, codeamKeys };
22958
+ }
22959
+ function formatProviderRouting(r, label) {
22960
+ const parts = [
22961
+ label ? `agent=${label}` : null,
22962
+ `anthropic=${r.env.ANTHROPIC_BASE_URL}`,
22963
+ `openai=${r.env.OPENAI_BASE_URL !== ROUTING_DEFAULT ? r.env.OPENAI_BASE_URL : r.env.OPENAI_API_BASE}`,
22964
+ `gemini=${r.env.GEMINI_API_BASE !== ROUTING_DEFAULT ? r.env.GEMINI_API_BASE : r.env.GOOGLE_GEMINI_BASE_URL}`,
22965
+ `opencode=[${r.opencode.map((p2) => `${p2.provider}=${p2.origin}`).join(",")}]`,
22966
+ `houseProxy=${r.houseProxy ? "yes" : "no"}`,
22967
+ `houseKeys=[${r.houseKeys.join(",")}]`,
22968
+ `codeamKeys=[${r.codeamKeys.join(",")}]`
22969
+ ];
22970
+ return parts.filter((p2) => p2 !== null).join(" ");
22971
+ }
22972
+ var PROVIDER_BILLING_RE = /(?:api error|http|status)[:\s]+402\b|\b402\b[^\n]{0,40}(?:payment required|insufficient|credits?|balance)|insufficient (?:credits?|balance|funds)|payment required/i;
22973
+ function matchesProviderBilling(text) {
22974
+ return PROVIDER_BILLING_RE.test(text);
22975
+ }
22976
+ function scrubSecrets(text) {
22977
+ return text.replace(/\b(?:sk|pk|rk|key|token)[-_][A-Za-z0-9_-]{12,}/gi, "[redacted]").replace(/bearer\s+[A-Za-z0-9._-]{12,}/gi, "Bearer [redacted]").replace(/\b[A-Za-z0-9_-]{40,}\b/g, "[redacted]");
22978
+ }
22979
+ function classifyProviderBilling(text, env) {
22980
+ if (!matchesProviderBilling(text)) return null;
22981
+ const line = text.split("\n").find((l) => PROVIDER_BILLING_RE.test(l)) ?? text;
22982
+ return {
22983
+ marker: isHouseProxyEnv(env) ? "provider_billing_house" : "provider_billing_external",
22984
+ anthropicHost: redactUrlToOrigin(env.ANTHROPIC_BASE_URL) ?? ROUTING_DEFAULT,
22985
+ snippet: scrubSecrets(line.trim()).slice(0, 160)
22986
+ };
22987
+ }
22988
+
22989
+ // src/agents/acp/agent-hooks.ts
22990
+ function replyIsCursorUpgradeRequired(finalText) {
22991
+ const t2 = finalText.trim().toLowerCase();
22992
+ if (t2.length === 0 || t2.length > 200) return false;
22993
+ return t2.includes("upgrade your plan to continue") || t2.includes("upgrade your plan") && t2.includes("continue");
22994
+ }
22995
+ function isGeminiIneligibleTier(haystack2) {
22996
+ return /IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist|not eligible for Gemini Code Assist/i.test(
22997
+ haystack2
22998
+ );
22999
+ }
23000
+ var ACP_AGENT_HOOKS = {
23001
+ claude: {
23002
+ statusPage: { vendor: "Anthropic", url: "https://status.anthropic.com" },
23003
+ // Freeze the SDK-bundled Claude Code binary at the version this CLI (and its
23004
+ // pinned ACP adapter) was tested against. Without this the binary silently
23005
+ // self-updates to @latest and can drift PAST the adapter — a newer Claude
23006
+ // Code (v2.1.211) started streaming its TUI chrome (What's-new banner, status
23007
+ // line, box-drawing) into the ACP text channel, which paints as a broken chat
23008
+ // on mobile. A version regression must now reach users only through a gated
23009
+ // codeam-cli release, never silently via auto-update.
23010
+ startupExtraEnv: () => ({
23011
+ DISABLE_AUTOUPDATER: "1",
23012
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
23013
+ })
23014
+ },
23015
+ codex: {
23016
+ statusPage: { vendor: "OpenAI", url: "https://status.openai.com" },
23017
+ startupExtraEnv: ({ autoApprovePermissions }) => autoApprovePermissions ? { INITIAL_AGENT_MODE: "agent-full-access" } : {}
23018
+ },
23019
+ gemini: {
23020
+ statusPage: { vendor: "Google", url: "https://status.cloud.google.com" },
23021
+ classifyStartupFailure: (haystack2) => isGeminiIneligibleTier(haystack2) ? "ineligible_tier" : null
23022
+ },
23023
+ copilot: {
23024
+ statusPage: { vendor: "GitHub", url: "https://www.githubstatus.com" }
23025
+ },
23026
+ cursor: {
23027
+ statusPage: { vendor: "Cursor", url: "https://status.cursor.com" },
23028
+ classifyCompletedReply: (finalText) => replyIsCursorUpgradeRequired(finalText) ? "upgrade_required" : null
23029
+ }
23030
+ };
23031
+ function agentHooks(agent) {
23032
+ return ACP_AGENT_HOOKS[agent];
23033
+ }
23034
+
23035
+ // src/agents/acp/failure-messages.ts
23036
+ function describeError(err) {
23037
+ if (err instanceof Error) return err.message;
23038
+ return String(err);
23039
+ }
23040
+ var AUTH_FAILURE_RE = /invalid authentication credentials|failed to authenticate|authentication[_ ](?:error|required)|please run \/login|\bunauthorized\b|\binvalid x-api-key\b|oauth (?:token|session) (?:expired|revoked)|(?:session|token|credentials?|oauth)[^\n]{0,40}could not be refreshed|(?:api error|http|status)[:\s]+401|\b401\b[^\n]{0,40}(?:unauthor|authenticat|credential|api[_ ]?key|login)/i;
23041
+ var HOUSE_AGENT_LIMIT_RE = /HOUSE_AGENT_CEILING|CodeAgent Cloud[^\n]{0,60}(?:usage )?ceiling|CodeAgent Cloud is temporarily (?:unavailable|disabled)/i;
23042
+ function looksLikeHouseAgentLimit(text) {
23043
+ return HOUSE_AGENT_LIMIT_RE.test(text);
23044
+ }
23045
+ var LOCAL_PROXY_RE = /(?:127\.0\.0\.1|localhost|\[::1\]):\d+/i;
23046
+ var CONNECTION_ERROR_RE = /ECONNREFUSED|ECONNRESET|ETIMEDOUT|EHOSTUNREACH|socket hang up|fetch failed|network error|connection (?:refused|reset|closed)/i;
23047
+ function looksLikeLocalProxyUnavailable(text) {
23048
+ return LOCAL_PROXY_RE.test(text) && CONNECTION_ERROR_RE.test(text);
23049
+ }
23050
+ function looksLikeAuthFailure(text) {
23051
+ if (looksLikeHouseAgentLimit(text)) return false;
23052
+ if (looksLikeLocalProxyUnavailable(text)) return false;
23053
+ return AUTH_FAILURE_RE.test(text);
23054
+ }
23055
+ function replyIsAuthFailure(finalText) {
23056
+ const t2 = finalText.trim();
23057
+ return t2.length > 0 && t2.length <= 200 && looksLikeAuthFailure(t2);
23058
+ }
23059
+ function replyIsHouseAgentLimit(finalText) {
23060
+ const t2 = finalText.trim();
23061
+ return t2.length > 0 && t2.length <= 400 && looksLikeHouseAgentLimit(t2);
23062
+ }
23063
+ var BYO_PROVIDER_BILLING_RE = /(?:api error|http|status)[:\s]+402\b[^\n]{0,80}(?:insufficient (?:credits?|balance)|requires more credits)|\b402\b[^\n]{0,40}(?:insufficient (?:credits?|balance)|requires more credits)|insufficient (?:credits?|balance)[^\n]{0,40}\b402\b/i;
23064
+ var PROVIDER_CREDITS_PHRASE_RE = /\brequires more credits\b|\binsufficient credits\.?\s+add more\b|\bcredit balance is too low\b|\binsufficient_quota\b|\bexceeded your current quota\b/i;
23065
+ function looksLikeByoProviderBilling(text, env = currentAgentEnv()) {
23066
+ return !isHouseProxyEnv(env) && (BYO_PROVIDER_BILLING_RE.test(text) || PROVIDER_CREDITS_PHRASE_RE.test(text));
23067
+ }
23068
+ function replyIsByoProviderBilling(finalText, env = currentAgentEnv()) {
23069
+ const t2 = finalText.trim();
23070
+ return t2.length > 0 && t2.length <= 600 && looksLikeByoProviderBilling(t2, env);
23071
+ }
23072
+ var PROVIDER_HOST_NAMES = [
23073
+ [/(^|\.)openrouter\.ai$/i, "OpenRouter"],
23074
+ [/(^|\.)deepinfra\.com$/i, "DeepInfra"],
23075
+ [/(^|\.)anthropic\.com$/i, "Anthropic"],
23076
+ [/(^|\.)openai\.com$/i, "OpenAI"],
23077
+ [/(^|\.)googleapis\.com$/i, "Google"],
23078
+ [/(^|\.)minimax(?:i)?\.(?:io|com|chat)$/i, "MiniMax"],
23079
+ [/(^|\.)moonshot\.(?:ai|cn)$/i, "Moonshot"],
23080
+ [/(^|\.)x\.ai$/i, "xAI"],
23081
+ [/(^|\.)mistral\.ai$/i, "Mistral"],
23082
+ [/(^|\.)groq\.com$/i, "Groq"],
23083
+ [/(^|\.)together\.(?:ai|xyz)$/i, "Together"]
23084
+ ];
23085
+ var AGENT_DEFAULT_PROVIDER = {
23086
+ claude: "Anthropic",
23087
+ codex: "OpenAI",
23088
+ gemini: "Google"
23089
+ };
23090
+ function byoProviderName(opts) {
23091
+ const env = opts.env ?? currentAgentEnv();
23092
+ for (const key of PROVIDER_BASE_URL_ENV_KEYS) {
23093
+ const origin = redactUrlToOrigin(env[key]);
23094
+ if (!origin || origin === "invalid-url") continue;
23095
+ const host2 = new URL(origin).hostname;
23096
+ for (const [re2, name] of PROVIDER_HOST_NAMES) if (re2.test(host2)) return name;
23097
+ return host2;
23098
+ }
23099
+ return opts.agent && AGENT_DEFAULT_PROVIDER[opts.agent] || "provider";
23100
+ }
23101
+ function byoProviderBillingMessage(provider) {
23102
+ return `\u{1F4B3} **Your ${provider} account has no credits left.** Top up at your provider, or switch this session to another agent.`;
23103
+ }
23104
+ function completedReplyFailureBubble(opts) {
23105
+ const env = opts.env ?? currentAgentEnv();
23106
+ if (replyIsHouseAgentLimit(opts.finalText)) return houseAgentLimitMessage(opts.finalText);
23107
+ if (replyIsByoProviderBilling(opts.finalText, env)) {
23108
+ return byoProviderBillingMessage(byoProviderName({ env, agent: opts.agent }));
23109
+ }
23110
+ if (replyIsAuthFailure(opts.finalText)) return AUTH_FAILURE_MESSAGE;
23111
+ return null;
23112
+ }
23113
+ function houseAgentLimitMessage(text) {
23114
+ if (/temporarily (?:unavailable|disabled)/i.test(text)) {
23115
+ return "\u23F3 **Our agent service is temporarily unavailable \u2014 this one\u2019s on us, not your account.**\n\nThere\u2019s nothing to buy and nothing owed, and it isn\u2019t a problem with your login, so re-authenticating won\u2019t help. Send your message again in a bit, or connect your own agent (Claude Code, Codex, Cursor, Gemini) in **Profile \u203A Agents** to keep going now.";
23116
+ }
23117
+ const canUpgrade = /upgrade to pro/i.test(text);
23118
+ return "\u{1F4CA} **You\u2019ve reached today\u2019s agent usage limit.**\n\nThe free agent has a daily usage ceiling that resets at midnight UTC. This is a usage limit, not a problem with your login \u2014 re-authenticating won\u2019t change it. " + (canUpgrade ? "To keep going now, upgrade to **Pro** for a higher ceiling, or connect your own agent (Claude, Codex, \u2026) in **Profile \u203A Agents** to run without this limit." : "To keep going now, connect your own agent (Claude, Codex, \u2026) in **Profile \u203A Agents** to run without this limit, or wait for the reset.");
23119
+ }
23120
+ var AUTH_FAILURE_MESSAGE = (
23121
+ // ⚠️ Do NOT name a status code here. This bubble fires for several
23122
+ // different failures, and the old hard-coded "(API 401)" told users the API
23123
+ // had answered 401 when it never had — it also sent a live investigation
23124
+ // hunting for 401s in proxy logs that were serving 200s throughout
23125
+ // (2026-08-24). Say what happened, not a number we did not see.
23126
+ "\u{1F512} **Authentication failed \u2014 your agent credentials are invalid or expired.**\n\nTap [Re-authenticate this agent](codeam://reauth) to renew your credentials in Profile \u203A Agents, then send your message again."
23127
+ );
23128
+ var CURSOR_UPGRADE_MESSAGE = "\u26A1 **Cursor needs a paid plan to run the agent.**\n\nThe headless Cursor Agent requires Cursor **Pro** \u2014 your Free plan\u2019s included usage does NOT cover Agent runs, even with quota left. This is your Cursor account (not CodeAgent). Upgrade, then send your message again:\n\n[Upgrade to Cursor Pro \u2192](https://cursor.com/dashboard)";
23129
+ var ONE_M_CREDITS_MESSAGE = "\u{1F504} **Reconnect your Claude subscription to continue.**\n\nClaude requested 1M-context but your account doesn\u2019t have the usage credits for it on this credential. Reconnecting refreshes your subscription so the agent can keep going \u2014 disabling 1M context won\u2019t fix a credits gate.\n\nTap [Reconnect this agent](codeam://reauth) to reconnect your Claude subscription in Profile \u203A Agents, then send your message again.";
23130
+ var TURN_FAILURE_MESSAGE = "\u26A0\uFE0F **The agent hit an error and couldn\u2019t finish this turn.** Please send your message again.";
23131
+ function emptyReplyMessage(agent) {
23132
+ const label = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].displayName : agent;
23133
+ const binary = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].binaryName : agent;
23134
+ return `\u26A0\uFE0F **${label} returned an empty reply.** Its provider may be having issues \u2014 check the agent's login/subscription on this machine (e.g. run \`${binary} -p "test"\` there).`;
23135
+ }
23136
+ var PROVIDER_OUTAGE_RE = /overloaded_error|\boverloaded\b|service[ _]unavailable|temporarily[ _]unavailable|(?:api error|http|status)[:\s]+(?:529|503|502|504)\b|\b(?:529|503|502|504)\b[^\n]{0,40}(?:overload|unavailable|gateway|upstream|server error)|bad gateway|gateway time-?out|upstream (?:error|connect|timeout)/i;
23137
+ function looksLikeProviderOutage(text) {
23138
+ return PROVIDER_OUTAGE_RE.test(text);
23139
+ }
23140
+ function agentStatusPage(agent, railWireId) {
23141
+ if (railWireId) return null;
23142
+ const a = (agent ?? "").toLowerCase();
23143
+ const id = a.includes("claude") || a.includes("anthropic") ? "claude" : a.includes("codex") || a.includes("openai") ? "codex" : a.includes("gemini") || a.includes("google") ? "gemini" : a.includes("copilot") ? "copilot" : a.includes("cursor") ? "cursor" : null;
23144
+ return id ? agentHooks(id)?.statusPage ?? null : null;
23145
+ }
23146
+ function providerOutageMessage(agent, railWireId) {
23147
+ if (railWireId) {
23148
+ const who2 = isManagedProviderId(railWireId) ? `${MANAGED_PROVIDER_DISPLAY_NAMES[railWireId]} ` : "";
23149
+ return `\u{1F310} **CodeAgent's agent service is having a disruption \u2014 this isn't your session.**
23150
+
23151
+ Your ${who2}agent couldn't finish this turn because our provider returned an overload error. It usually clears up on its own \u2014 just send your message again in a bit.`;
23152
+ }
23153
+ const info = agentStatusPage(agent);
23154
+ const who = info ? info.vendor : "The agent provider";
23155
+ return `\u{1F310} **${who} is having a service disruption \u2014 this isn\u2019t your session.**
23156
+
23157
+ The agent provider returned an overload/outage error, so this turn couldn\u2019t finish. It usually clears up on its own \u2014 just send your message again in a bit.` + (info ? `
23158
+
23159
+ Follow the status here: [${info.url}](${info.url})` : "");
23160
+ }
23161
+ function startupFailureMessage(agent, detail, recentStderr, railWireId) {
23162
+ const haystack2 = `${detail}
23163
+ ${recentStderr}`;
23164
+ if (agentHooks(agent)?.classifyStartupFailure?.(haystack2) === "ineligible_tier") {
23165
+ return [
23166
+ "\u26A0\uFE0F **Gemini couldn't start \u2014 your Google account isn't eligible.**",
23167
+ "",
23168
+ "Google discontinued free **\u201CLogin with Google\u201D** access to the Gemini CLI on 2026-06-18. The free / individual tier (and Google AI Pro/Ultra) can no longer authenticate here.",
23169
+ "",
23170
+ "To use Gemini from CodeAgent, re-link it in **Profile \u203A Agents** with either:",
23171
+ "\u2022 a **Gemini API key** (AI Studio \u2014 free quota available), or",
23172
+ "\u2022 a paid **Gemini Code Assist (Standard/Enterprise)** subscription."
23173
+ ].join("\n");
23174
+ }
23175
+ if (looksLikeAuthFailure(haystack2)) return AUTH_FAILURE_MESSAGE;
23176
+ if (looksLikeProviderOutage(haystack2)) return providerOutageMessage(agent, railWireId);
23177
+ const tail = recentStderr.split("\n").filter(Boolean).slice(-3).join("\n");
23178
+ return [`\u26A0\uFE0F The ${agent} agent failed to start.`, "", tail ? `Details:
23179
+ ${tail}` : detail].join(
23180
+ "\n"
23181
+ );
23182
+ }
23183
+ function startupCredentialInvalidReason(agent, detail, recentStderr) {
23184
+ const haystack2 = `${detail}
23185
+ ${recentStderr}`;
23186
+ return agentHooks(agent)?.classifyStartupFailure?.(haystack2) === "ineligible_tier" ? "ineligible_tier" : null;
23187
+ }
23188
+ function failureBubble(opts) {
23189
+ const env = opts.env ?? currentAgentEnv();
23190
+ if (looksLikeHouseAgentLimit(opts.detail) || looksLikeHouseAgentLimit(opts.recentStderr)) {
23191
+ return houseAgentLimitMessage(`${opts.detail}
23192
+ ${opts.recentStderr}`);
23193
+ }
23194
+ if (looksLikeByoProviderBilling(opts.detail, env) || looksLikeByoProviderBilling(opts.recentStderr, env)) {
23195
+ return byoProviderBillingMessage(byoProviderName({ env, agent: opts.agent }));
23196
+ }
23197
+ if (looksLikeAuthFailure(opts.detail) || looksLikeAuthFailure(opts.recentStderr)) {
23198
+ return AUTH_FAILURE_MESSAGE;
23199
+ }
23200
+ if (looksLike1mContextCreditsError(opts.detail) || looksLike1mContextCreditsError(opts.recentStderr)) {
23201
+ return ONE_M_CREDITS_MESSAGE;
23202
+ }
23203
+ if (looksLikeProviderOutage(opts.detail) || looksLikeProviderOutage(opts.recentStderr)) {
23204
+ return providerOutageMessage(opts.agent, opts.railWireId);
23205
+ }
23206
+ if (!opts.hadText) return TURN_FAILURE_MESSAGE;
23207
+ return null;
23208
+ }
23209
+ var WINDOWS_CONTROL_C_EXIT = 3221225786;
23210
+ function adapterExitMessage(opts) {
23211
+ if (opts.code === WINDOWS_CONTROL_C_EXIT) return null;
23212
+ if (opts.signal === "SIGINT") return null;
23213
+ if (opts.authFail) return AUTH_FAILURE_MESSAGE;
23214
+ if (opts.outageFail) return providerOutageMessage(opts.agent, opts.railWireId);
23215
+ return `Agent adapter exited unexpectedly (code=${opts.code ?? "null"} signal=${opts.signal ?? "null"}).`;
23216
+ }
23217
+
23218
+ // src/services/preview/port-registry.ts
23219
+ var fs36 = __toESM(require("fs"));
23220
+ var os32 = __toESM(require("os"));
23221
+ var path42 = __toESM(require("path"));
22683
23222
  var import_child_process14 = require("child_process");
22684
23223
  function registryPath() {
22685
- return path40.join(os29.homedir(), ".codeam", "preview-ports.json");
23224
+ return path42.join(os32.homedir(), ".codeam", "preview-ports.json");
22686
23225
  }
22687
23226
  function readRegistry() {
22688
23227
  try {
22689
- const raw = fs33.readFileSync(registryPath(), "utf8");
23228
+ const raw = fs36.readFileSync(registryPath(), "utf8");
22690
23229
  const parsed = JSON.parse(raw);
22691
23230
  if (parsed && typeof parsed === "object") return parsed;
22692
23231
  } catch {
@@ -22696,10 +23235,10 @@ function readRegistry() {
22696
23235
  function writeRegistry(reg) {
22697
23236
  try {
22698
23237
  const file = registryPath();
22699
- fs33.mkdirSync(path40.dirname(file), { recursive: true });
23238
+ fs36.mkdirSync(path42.dirname(file), { recursive: true });
22700
23239
  const tmp = `${file}.tmp`;
22701
- fs33.writeFileSync(tmp, JSON.stringify(reg), "utf8");
22702
- fs33.renameSync(tmp, file);
23240
+ fs36.writeFileSync(tmp, JSON.stringify(reg), "utf8");
23241
+ fs36.renameSync(tmp, file);
22703
23242
  } catch (err) {
22704
23243
  log.warn("preview", `port-registry write failed: ${err instanceof Error ? err.message : String(err)}`);
22705
23244
  }
@@ -22765,7 +23304,7 @@ function reclaimOwnOrphanPort(port) {
22765
23304
 
22766
23305
  // src/services/preview/host-allow.ts
22767
23306
  var import_fs2 = require("fs");
22768
- var path41 = __toESM(require("path"));
23307
+ var path43 = __toESM(require("path"));
22769
23308
  var NEXT_ALLOWED_ORIGINS = [
22770
23309
  "*.trycloudflare.com",
22771
23310
  "*.preview.codeagent-mobile.com",
@@ -22785,7 +23324,7 @@ var MARKER_DIR = ".codeam";
22785
23324
  var MARKER_FILE = "preview-host-allow.json";
22786
23325
  var ORIG_INFIX = ".codeam-orig";
22787
23326
  function markerPath(cwd) {
22788
- return path41.join(cwd, MARKER_DIR, MARKER_FILE);
23327
+ return path43.join(cwd, MARKER_DIR, MARKER_FILE);
22789
23328
  }
22790
23329
  async function fileExists(p2) {
22791
23330
  try {
@@ -22798,13 +23337,13 @@ async function fileExists(p2) {
22798
23337
  async function findConfigFile(cwd, framework) {
22799
23338
  const base = CONFIG_BASENAMES[framework];
22800
23339
  for (const ext of CONFIG_EXTS) {
22801
- if (await fileExists(path41.join(cwd, `${base}${ext}`))) return `${base}${ext}`;
23340
+ if (await fileExists(path43.join(cwd, `${base}${ext}`))) return `${base}${ext}`;
22802
23341
  }
22803
23342
  return null;
22804
23343
  }
22805
23344
  async function dependsOn(cwd, pkgName) {
22806
23345
  try {
22807
- const raw = await import_fs2.promises.readFile(path41.join(cwd, "package.json"), "utf8");
23346
+ const raw = await import_fs2.promises.readFile(path43.join(cwd, "package.json"), "utf8");
22808
23347
  const pkg = JSON.parse(raw);
22809
23348
  return !!(pkg.dependencies?.[pkgName] ?? pkg.devDependencies?.[pkgName]);
22810
23349
  } catch {
@@ -22812,11 +23351,11 @@ async function dependsOn(cwd, pkgName) {
22812
23351
  }
22813
23352
  }
22814
23353
  async function isEsmConfig(cwd, configFile) {
22815
- const ext = path41.extname(configFile);
23354
+ const ext = path43.extname(configFile);
22816
23355
  if (ext === ".mjs" || ext === ".mts" || ext === ".ts") return true;
22817
23356
  if (ext === ".cjs") return false;
22818
23357
  try {
22819
- const raw = await import_fs2.promises.readFile(path41.join(cwd, "package.json"), "utf8");
23358
+ const raw = await import_fs2.promises.readFile(path43.join(cwd, "package.json"), "utf8");
22820
23359
  return JSON.parse(raw).type === "module";
22821
23360
  } catch {
22822
23361
  return false;
@@ -22907,7 +23446,7 @@ async function isOurShim(abs) {
22907
23446
  }
22908
23447
  }
22909
23448
  async function writeMarker(cwd, marker) {
22910
- await import_fs2.promises.mkdir(path41.join(cwd, MARKER_DIR), { recursive: true });
23449
+ await import_fs2.promises.mkdir(path43.join(cwd, MARKER_DIR), { recursive: true });
22911
23450
  await import_fs2.promises.writeFile(markerPath(cwd), JSON.stringify(marker, null, 2), "utf8");
22912
23451
  }
22913
23452
  async function readMarker(cwd) {
@@ -22922,9 +23461,9 @@ async function restorePreviewHostAllow(cwd) {
22922
23461
  const marker = await readMarker(cwd);
22923
23462
  if (!marker) return;
22924
23463
  try {
22925
- const configAbs = path41.join(cwd, marker.configFile);
23464
+ const configAbs = path43.join(cwd, marker.configFile);
22926
23465
  if (marker.backupFile) {
22927
- const backupAbs = path41.join(cwd, marker.backupFile);
23466
+ const backupAbs = path43.join(cwd, marker.backupFile);
22928
23467
  if (await fileExists(backupAbs)) {
22929
23468
  await import_fs2.promises.rm(configAbs, { force: true });
22930
23469
  await import_fs2.promises.rename(backupAbs, configAbs);
@@ -22957,26 +23496,26 @@ async function applyPreviewHostAllow(cwd) {
22957
23496
  }
22958
23497
  if (!framework) return;
22959
23498
  if (existing) {
22960
- const ext = path41.extname(existing);
23499
+ const ext = path43.extname(existing);
22961
23500
  const origBasename = `${CONFIG_BASENAMES[framework]}${ORIG_INFIX}${ext}`;
22962
- if (await isOurShim(path41.join(cwd, existing))) {
22963
- const origAbs = path41.join(cwd, origBasename);
23501
+ if (await isOurShim(path43.join(cwd, existing))) {
23502
+ const origAbs = path43.join(cwd, origBasename);
22964
23503
  if (!await fileExists(origAbs) || await isOurShim(origAbs)) {
22965
23504
  log.warn("preview", `host-allow: ${existing} is a leftover shim with no original \u2014 skipped`);
22966
23505
  return;
22967
23506
  }
22968
- await import_fs2.promises.rm(path41.join(cwd, existing), { force: true });
22969
- await import_fs2.promises.rename(origAbs, path41.join(cwd, existing));
23507
+ await import_fs2.promises.rm(path43.join(cwd, existing), { force: true });
23508
+ await import_fs2.promises.rename(origAbs, path43.join(cwd, existing));
22970
23509
  log.info("preview", `host-allow: restored ${existing} from a leftover ${origBasename}`);
22971
23510
  }
22972
23511
  const esm = await isEsmConfig(cwd, existing);
22973
- await import_fs2.promises.rename(path41.join(cwd, existing), path41.join(cwd, origBasename));
22974
- await import_fs2.promises.writeFile(path41.join(cwd, existing), shimFor(framework, origBasename, esm), "utf8");
23512
+ await import_fs2.promises.rename(path43.join(cwd, existing), path43.join(cwd, origBasename));
23513
+ await import_fs2.promises.writeFile(path43.join(cwd, existing), shimFor(framework, origBasename, esm), "utf8");
22975
23514
  await writeMarker(cwd, { framework, configFile: existing, backupFile: origBasename });
22976
23515
  log.info("preview", `host-allow: wrapped ${existing} (${framework}) for tunnel access`);
22977
23516
  } else {
22978
23517
  const configFile = `${CONFIG_BASENAMES[framework]}.mjs`;
22979
- await import_fs2.promises.writeFile(path41.join(cwd, configFile), shimFor(framework, null, true), "utf8");
23518
+ await import_fs2.promises.writeFile(path43.join(cwd, configFile), shimFor(framework, null, true), "utf8");
22980
23519
  await writeMarker(cwd, { framework, configFile, backupFile: null });
22981
23520
  log.info("preview", `host-allow: created ${configFile} (${framework}) for tunnel access`);
22982
23521
  }
@@ -22986,8 +23525,8 @@ async function applyPreviewHostAllow(cwd) {
22986
23525
  }
22987
23526
 
22988
23527
  // src/services/preview/build-heal.ts
22989
- var fs35 = __toESM(require("fs"));
22990
- var path42 = __toESM(require("path"));
23528
+ var fs38 = __toESM(require("fs"));
23529
+ var path44 = __toESM(require("path"));
22991
23530
  var BUILD_MARKER_NAME = "BUILD_ID";
22992
23531
  var DEFAULT_DEBOUNCE_MS = 1500;
22993
23532
  var DEFAULT_MAX_RESTARTS = 3;
@@ -22996,7 +23535,7 @@ function isBuildHealSupported(framework) {
22996
23535
  }
22997
23536
  function defaultWatchDir(dir, onEvent) {
22998
23537
  try {
22999
- const w3 = fs35.watch(dir, { persistent: false }, (_eventType, filename) => onEvent(filename));
23538
+ const w3 = fs38.watch(dir, { persistent: false }, (_eventType, filename) => onEvent(filename));
23000
23539
  w3.on("error", () => {
23001
23540
  try {
23002
23541
  w3.close();
@@ -23019,7 +23558,7 @@ function resetBuildHealState(sessionId) {
23019
23558
  }
23020
23559
  function readMarker2(markerPath2) {
23021
23560
  try {
23022
- return fs35.readFileSync(markerPath2, "utf8").trim();
23561
+ return fs38.readFileSync(markerPath2, "utf8").trim();
23023
23562
  } catch {
23024
23563
  return null;
23025
23564
  }
@@ -23028,8 +23567,8 @@ function watchForBuildClobber(deps) {
23028
23567
  const watchDir = deps.watchDir ?? defaultWatchDir;
23029
23568
  const debounceMs = deps.debounceMs ?? DEFAULT_DEBOUNCE_MS;
23030
23569
  const maxRestarts = deps.maxRestarts ?? DEFAULT_MAX_RESTARTS;
23031
- const nextDir = path42.join(deps.cwd, ".next");
23032
- const markerPath2 = path42.join(nextDir, BUILD_MARKER_NAME);
23570
+ const nextDir = path44.join(deps.cwd, ".next");
23571
+ const markerPath2 = path44.join(nextDir, BUILD_MARKER_NAME);
23033
23572
  let stopped = false;
23034
23573
  let debounceTimer = null;
23035
23574
  let dirUnwatch = null;
@@ -23087,12 +23626,12 @@ function watchForBuildClobber(deps) {
23087
23626
  if (filename === null || filename === BUILD_MARKER_NAME) onMarkerTouched();
23088
23627
  });
23089
23628
  };
23090
- if (fs35.existsSync(nextDir)) {
23629
+ if (fs38.existsSync(nextDir)) {
23091
23630
  attachDirWatch();
23092
23631
  } else {
23093
23632
  cwdUnwatch = watchDir(deps.cwd, (filename) => {
23094
23633
  if (stopped || dirUnwatch) return;
23095
- if ((filename === ".next" || filename === null) && fs35.existsSync(nextDir)) {
23634
+ if ((filename === ".next" || filename === null) && fs38.existsSync(nextDir)) {
23096
23635
  cwdUnwatch?.();
23097
23636
  cwdUnwatch = null;
23098
23637
  attachDirWatch();
@@ -23319,7 +23858,7 @@ function quoteIfNeeded(value) {
23319
23858
 
23320
23859
  // src/services/preview/expo.ts
23321
23860
  var http6 = __toESM(require("http"));
23322
- var path45 = __toESM(require("path"));
23861
+ var path47 = __toESM(require("path"));
23323
23862
  var EXPO_PACKAGER_PROXY_URL_ENV = "EXPO_PACKAGER_PROXY_URL";
23324
23863
  function stripExpoTunnelFlag(args2) {
23325
23864
  return args2.filter((a) => a !== "--tunnel" && !a.startsWith("--tunnel="));
@@ -23328,7 +23867,7 @@ function ensureExpoPortArg(command2, args2, port) {
23328
23867
  const hasPort = args2.some((a) => a === "--port" || a.startsWith("--port="));
23329
23868
  if (hasPort) return [...args2];
23330
23869
  const out2 = [...args2];
23331
- if (path45.basename(command2) === "npm" && !out2.includes("--")) out2.push("--");
23870
+ if (path47.basename(command2) === "npm" && !out2.includes("--")) out2.push("--");
23332
23871
  out2.push("--port", String(port));
23333
23872
  return out2;
23334
23873
  }
@@ -23457,6 +23996,9 @@ function describeOneShotAgentError(stderr) {
23457
23996
  if (!detail) return null;
23458
23997
  return `Preview detection couldn't run \u2014 the agent answered ${m[1]}: ${detail}`;
23459
23998
  }
23999
+ function previewProviderCreditsMessage(provider) {
24000
+ return `Preview detection couldn't run \u2014 your ${provider} account has no credits left. Top up at your provider, or switch this session to another agent.`;
24001
+ }
23460
24002
  var RAW_EXCERPT_LIMIT = 1e3;
23461
24003
  function describeDetectionFailure(raw) {
23462
24004
  const text = raw?.trim() ?? "";
@@ -23949,8 +24491,8 @@ function isJsInstallCommand(cmd, args2) {
23949
24491
  }
23950
24492
 
23951
24493
  // src/services/preview/script-candidates.ts
23952
- var fs40 = __toESM(require("fs"));
23953
- var path48 = __toESM(require("path"));
24494
+ var fs43 = __toESM(require("fs"));
24495
+ var path50 = __toESM(require("path"));
23954
24496
  var EXCLUDE_RE = /(^|:)(deploy|publish|release|build|test|e2e|lint|format|typecheck|type-check|clean|prebuild|postinstall|preinstall|prepare|install|migrate|seed|db|prisma|codegen|storybook-build)(:|$)/i;
23955
24497
  var EXCLUDE_BODY_RE = /\b(run:android|run:ios|gradlew|fastlane|xcodebuild|eas build|eas submit)\b/i;
23956
24498
  var MAX_APPS = 40;
@@ -23968,7 +24510,7 @@ function frameworkFor(pkg, body) {
23968
24510
  }
23969
24511
  function readPkg(file) {
23970
24512
  try {
23971
- return JSON.parse(fs40.readFileSync(file, "utf8"));
24513
+ return JSON.parse(fs43.readFileSync(file, "utf8"));
23972
24514
  } catch {
23973
24515
  return null;
23974
24516
  }
@@ -23978,7 +24520,7 @@ function expandGlob(root, pattern) {
23978
24520
  if (!clean.endsWith("/*")) return [clean];
23979
24521
  const base = clean.slice(0, -2);
23980
24522
  try {
23981
- return fs40.readdirSync(path48.join(root, base), { withFileTypes: true }).filter((d3) => d3.isDirectory() && !d3.name.startsWith(".") && d3.name !== "node_modules").map((d3) => path48.join(base, d3.name));
24523
+ return fs43.readdirSync(path50.join(root, base), { withFileTypes: true }).filter((d3) => d3.isDirectory() && !d3.name.startsWith(".") && d3.name !== "node_modules").map((d3) => path50.join(base, d3.name));
23982
24524
  } catch {
23983
24525
  return [];
23984
24526
  }
@@ -23987,7 +24529,7 @@ function workspaceDirs(root, rootPkg) {
23987
24529
  const ws = rootPkg?.workspaces;
23988
24530
  const patterns = Array.isArray(ws) ? ws : ws?.packages ?? [];
23989
24531
  try {
23990
- const pnpm = fs40.readFileSync(path48.join(root, "pnpm-workspace.yaml"), "utf8");
24532
+ const pnpm = fs43.readFileSync(path50.join(root, "pnpm-workspace.yaml"), "utf8");
23991
24533
  for (const m of pnpm.matchAll(/^\s*-\s*['"]?([^'"\n]+)['"]?\s*$/gm)) patterns.push(m[1]);
23992
24534
  } catch {
23993
24535
  }
@@ -23998,14 +24540,14 @@ function workspaceDirs(root, rootPkg) {
23998
24540
  }
23999
24541
  function listScriptCandidates(root) {
24000
24542
  const out2 = [];
24001
- const rootPkg = readPkg(path48.join(root, "package.json"));
24543
+ const rootPkg = readPkg(path50.join(root, "package.json"));
24002
24544
  const add = (pkg, appDir) => {
24003
24545
  for (const [script, body] of Object.entries(pkg.scripts ?? {})) {
24004
24546
  if (out2.length >= MAX_CANDIDATES) return;
24005
24547
  if (EXCLUDE_RE.test(script) || EXCLUDE_BODY_RE.test(body)) continue;
24006
24548
  const fw = frameworkFor(pkg, body);
24007
24549
  out2.push({
24008
- app: pkg.name ?? (appDir === "." ? path48.basename(root) : appDir),
24550
+ app: pkg.name ?? (appDir === "." ? path50.basename(root) : appDir),
24009
24551
  appDir,
24010
24552
  script,
24011
24553
  body,
@@ -24019,8 +24561,8 @@ function listScriptCandidates(root) {
24019
24561
  };
24020
24562
  if (rootPkg) add(rootPkg, ".");
24021
24563
  for (const dir of workspaceDirs(root, rootPkg)) {
24022
- const pkg = readPkg(path48.join(root, dir, "package.json"));
24023
- if (pkg) add(pkg, dir.split(path48.sep).join("/"));
24564
+ const pkg = readPkg(path50.join(root, dir, "package.json"));
24565
+ if (pkg) add(pkg, dir.split(path50.sep).join("/"));
24024
24566
  }
24025
24567
  return out2;
24026
24568
  }
@@ -24202,19 +24744,19 @@ function activePreviewSessionIds() {
24202
24744
 
24203
24745
  // src/services/preview/start-orchestrator.ts
24204
24746
  var import_child_process20 = require("child_process");
24205
- var fs43 = __toESM(require("fs"));
24206
- var path51 = __toESM(require("path"));
24747
+ var fs46 = __toESM(require("fs"));
24748
+ var path53 = __toESM(require("path"));
24207
24749
  var import_which3 = __toESM(require("which"));
24208
24750
 
24209
24751
  // src/services/project-env/index.ts
24210
24752
  var import_fs6 = require("fs");
24211
- var path50 = __toESM(require("path"));
24753
+ var path52 = __toESM(require("path"));
24212
24754
 
24213
24755
  // src/beads/project-key.ts
24214
24756
  var import_child_process19 = require("child_process");
24215
24757
  var crypto2 = __toESM(require("crypto"));
24216
- var fs41 = __toESM(require("fs"));
24217
- var path49 = __toESM(require("path"));
24758
+ var fs44 = __toESM(require("fs"));
24759
+ var path51 = __toESM(require("path"));
24218
24760
  function normalizeOrigin(raw) {
24219
24761
  const trimmed = raw.trim();
24220
24762
  if (!trimmed) return null;
@@ -24240,17 +24782,17 @@ function normalizeOrigin(raw) {
24240
24782
  return `${host2}/${pathPart}`;
24241
24783
  }
24242
24784
  function findRepoRoot(cwd) {
24243
- let dir = path49.resolve(cwd);
24785
+ let dir = path51.resolve(cwd);
24244
24786
  const seen2 = /* @__PURE__ */ new Set();
24245
24787
  for (let i = 0; i < 256; i++) {
24246
24788
  if (seen2.has(dir)) return null;
24247
24789
  seen2.add(dir);
24248
24790
  try {
24249
- const stat3 = fs41.statSync(path49.join(dir, ".git"), { throwIfNoEntry: false });
24791
+ const stat3 = fs44.statSync(path51.join(dir, ".git"), { throwIfNoEntry: false });
24250
24792
  if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
24251
24793
  } catch {
24252
24794
  }
24253
- const parent = path49.dirname(dir);
24795
+ const parent = path51.dirname(dir);
24254
24796
  if (parent === dir) return null;
24255
24797
  dir = parent;
24256
24798
  }
@@ -24261,7 +24803,7 @@ var _execSeam3 = {
24261
24803
  const out2 = (0, import_child_process19.execFileSync)(file, args2, opts);
24262
24804
  return typeof out2 === "string" ? out2 : out2.toString("utf8");
24263
24805
  },
24264
- realpath: (p2) => fs41.realpathSync(p2)
24806
+ realpath: (p2) => fs44.realpathSync(p2)
24265
24807
  };
24266
24808
  function readOrigin(cwd) {
24267
24809
  try {
@@ -24290,7 +24832,7 @@ function deriveProjectIdentity(cwd = process.cwd()) {
24290
24832
  } catch {
24291
24833
  }
24292
24834
  const hash = crypto2.createHash("sha256").update(real).digest("hex");
24293
- return { projectKey: `path:${hash}`, projectLabel: path49.basename(real) || "project" };
24835
+ return { projectKey: `path:${hash}`, projectLabel: path51.basename(real) || "project" };
24294
24836
  }
24295
24837
 
24296
24838
  // src/services/project-env/index.ts
@@ -24303,7 +24845,7 @@ async function readIfExists(p2) {
24303
24845
  }
24304
24846
  async function syncProjectEnvUp(cwd, ctx) {
24305
24847
  if (!ctx.pluginAuthToken) return;
24306
- const content = await readIfExists(path50.join(cwd, ".env"));
24848
+ const content = await readIfExists(path52.join(cwd, ".env"));
24307
24849
  if (content == null) return;
24308
24850
  try {
24309
24851
  const { projectKey, projectLabel } = deriveProjectIdentity(cwd);
@@ -24325,7 +24867,7 @@ async function syncProjectEnvUp(cwd, ctx) {
24325
24867
  }
24326
24868
  async function restoreProjectEnvIfMissing(cwd, ctx) {
24327
24869
  if (!ctx.pluginAuthToken) return false;
24328
- const envPath = path50.join(cwd, ".env");
24870
+ const envPath = path52.join(cwd, ".env");
24329
24871
  if (await readIfExists(envPath) != null) return false;
24330
24872
  try {
24331
24873
  const { projectKey, projectLabel } = deriveProjectIdentity(cwd);
@@ -24336,7 +24878,7 @@ async function restoreProjectEnvIfMissing(cwd, ctx) {
24336
24878
  projectKey
24337
24879
  });
24338
24880
  if (!stored) return false;
24339
- const tmp = path50.join(cwd, ".env.codeam-restore.tmp");
24881
+ const tmp = path52.join(cwd, ".env.codeam-restore.tmp");
24340
24882
  await import_fs6.promises.writeFile(tmp, stored.content, { encoding: "utf8", mode: 384 });
24341
24883
  await import_fs6.promises.rename(tmp, envPath);
24342
24884
  log.info("project-env", `restored .env (${stored.keyCount} vars) for ${projectLabel}`);
@@ -25119,8 +25661,8 @@ function normalizeDetectionForSpawn(detection, cwd) {
25119
25661
  if (args2.length === 0) return detection;
25120
25662
  const binName = args2[0];
25121
25663
  if (binName.startsWith("-")) return detection;
25122
- const binPath = path51.join(cwd, "node_modules", ".bin", binName);
25123
- if (!fs43.existsSync(binPath)) return detection;
25664
+ const binPath = path53.join(cwd, "node_modules", ".bin", binName);
25665
+ if (!fs46.existsSync(binPath)) return detection;
25124
25666
  return {
25125
25667
  ...detection,
25126
25668
  command: binPath,
@@ -25531,9 +26073,9 @@ async function bringUpPublicUrl(ctx, tunnelPort) {
25531
26073
 
25532
26074
  // src/beads/bd-adapter.ts
25533
26075
  var import_child_process21 = require("child_process");
25534
- var fs44 = __toESM(require("fs"));
25535
- var os31 = __toESM(require("os"));
25536
- var path52 = __toESM(require("path"));
26076
+ var fs47 = __toESM(require("fs"));
26077
+ var os34 = __toESM(require("os"));
26078
+ var path54 = __toESM(require("path"));
25537
26079
  var BD_PACKAGE = "@beads/bd";
25538
26080
  function resolveBundledBdBinary() {
25539
26081
  return _resolveSeam.resolveBundled();
@@ -25545,11 +26087,11 @@ function _defaultResolveBundled() {
25545
26087
  } catch {
25546
26088
  return null;
25547
26089
  }
25548
- const binDir = path52.join(path52.dirname(pkgJsonPath), "bin");
26090
+ const binDir = path54.join(path54.dirname(pkgJsonPath), "bin");
25549
26091
  const binaryName = process.platform === "win32" ? "bd.exe" : "bd";
25550
- const binaryPath = path52.join(binDir, binaryName);
26092
+ const binaryPath = path54.join(binDir, binaryName);
25551
26093
  try {
25552
- fs44.accessSync(binaryPath, fs44.constants.F_OK);
26094
+ fs47.accessSync(binaryPath, fs47.constants.F_OK);
25553
26095
  return binaryPath;
25554
26096
  } catch {
25555
26097
  return null;
@@ -25559,13 +26101,13 @@ function resolveBdOnPath() {
25559
26101
  return _resolveSeam.resolveOnPath();
25560
26102
  }
25561
26103
  function _defaultResolveOnPath() {
25562
- const dirs = (process.env.PATH ?? "").split(path52.delimiter).filter(Boolean);
26104
+ const dirs = (process.env.PATH ?? "").split(path54.delimiter).filter(Boolean);
25563
26105
  const candidates = process.platform === "win32" ? ["bd.exe", "bd.cmd", "bd"] : ["bd"];
25564
26106
  for (const dir of dirs) {
25565
26107
  for (const candidate of candidates) {
25566
- const full = path52.join(dir, candidate);
26108
+ const full = path54.join(dir, candidate);
25567
26109
  try {
25568
- fs44.accessSync(full, fs44.constants.F_OK);
26110
+ fs47.accessSync(full, fs47.constants.F_OK);
25569
26111
  return full;
25570
26112
  } catch {
25571
26113
  }
@@ -25650,7 +26192,7 @@ var BdAdapter = class {
25650
26192
  const env = { ...process.env };
25651
26193
  if (!env.HOME) {
25652
26194
  try {
25653
- const home = os31.homedir();
26195
+ const home = os34.homedir();
25654
26196
  if (home) env.HOME = home;
25655
26197
  } catch {
25656
26198
  }
@@ -25742,9 +26284,9 @@ function coerceIssue(row, projectKey) {
25742
26284
 
25743
26285
  // src/beads/provisioner.ts
25744
26286
  var import_child_process24 = require("child_process");
25745
- var fs46 = __toESM(require("fs"));
25746
- var os33 = __toESM(require("os"));
25747
- var path54 = __toESM(require("path"));
26287
+ var fs49 = __toESM(require("fs"));
26288
+ var os36 = __toESM(require("os"));
26289
+ var path56 = __toESM(require("path"));
25748
26290
 
25749
26291
  // src/beads/install-bd.ts
25750
26292
  var import_child_process22 = require("child_process");
@@ -25809,9 +26351,9 @@ async function installBd(platform3 = process.platform) {
25809
26351
 
25810
26352
  // src/beads/install-dolt.ts
25811
26353
  var import_child_process23 = require("child_process");
25812
- var fs45 = __toESM(require("fs"));
25813
- var os32 = __toESM(require("os"));
25814
- var path53 = __toESM(require("path"));
26354
+ var fs48 = __toESM(require("fs"));
26355
+ var os35 = __toESM(require("os"));
26356
+ var path55 = __toESM(require("path"));
25815
26357
  var DOLT_INSTALL_SH_URL = "https://github.com/dolthub/dolt/releases/latest/download/install.sh";
25816
26358
  var DOLT_MSI_URL = "https://github.com/dolthub/dolt/releases/latest/download/dolt-windows-amd64.msi";
25817
26359
  function resolveDoltInstallStrategy(platform3) {
@@ -25900,14 +26442,14 @@ async function installDoltToDir(targetDir, platform3 = process.platform, arch2 =
25900
26442
  return result;
25901
26443
  }
25902
26444
  var _doltPathSeam = {
25903
- homedir: () => os32.homedir(),
26445
+ homedir: () => os35.homedir(),
25904
26446
  getPath: () => process.env.PATH ?? "",
25905
26447
  setPath: (p2) => {
25906
26448
  process.env.PATH = p2;
25907
26449
  },
25908
26450
  exists: (p2) => {
25909
26451
  try {
25910
- fs45.accessSync(p2, fs45.constants.F_OK);
26452
+ fs48.accessSync(p2, fs48.constants.F_OK);
25911
26453
  return true;
25912
26454
  } catch {
25913
26455
  return false;
@@ -25918,7 +26460,7 @@ function doltBinaryNames(platform3) {
25918
26460
  return platform3 === "win32" ? ["dolt.exe", "dolt.cmd", "dolt"] : ["dolt"];
25919
26461
  }
25920
26462
  function knownDoltDirs(platform3) {
25921
- const P3 = platform3 === "win32" ? path53.win32 : path53.posix;
26463
+ const P3 = platform3 === "win32" ? path55.win32 : path55.posix;
25922
26464
  const home = _doltPathSeam.homedir();
25923
26465
  if (platform3 === "win32") {
25924
26466
  return [
@@ -25934,7 +26476,7 @@ function knownDoltDirs(platform3) {
25934
26476
  ].filter(Boolean);
25935
26477
  }
25936
26478
  function ensureDoltResolvable(platform3 = process.platform) {
25937
- const P3 = platform3 === "win32" ? path53.win32 : path53.posix;
26479
+ const P3 = platform3 === "win32" ? path55.win32 : path55.posix;
25938
26480
  const delim = platform3 === "win32" ? ";" : ":";
25939
26481
  const names = doltBinaryNames(platform3);
25940
26482
  const pathDirs = _doltPathSeam.getPath().split(delim).filter(Boolean);
@@ -26128,17 +26670,17 @@ function remoteHasDoltData(cwd) {
26128
26670
  }
26129
26671
  var _linkSeam = {
26130
26672
  platform: () => process.platform,
26131
- homedir: () => os33.homedir(),
26673
+ homedir: () => os36.homedir(),
26132
26674
  isWritableDir: (dir) => {
26133
26675
  try {
26134
- fs46.accessSync(dir, fs46.constants.W_OK);
26676
+ fs49.accessSync(dir, fs49.constants.W_OK);
26135
26677
  return true;
26136
26678
  } catch {
26137
26679
  return false;
26138
26680
  }
26139
26681
  },
26140
26682
  ensureDir: (dir) => {
26141
- fs46.mkdirSync(dir, { recursive: true });
26683
+ fs49.mkdirSync(dir, { recursive: true });
26142
26684
  },
26143
26685
  /**
26144
26686
  * A directory to symlink `bd` into so the AGENT's shell + Claude Code's
@@ -26159,9 +26701,9 @@ var _linkSeam = {
26159
26701
  * which `linkBdOntoPath` creates if missing.
26160
26702
  */
26161
26703
  cliBinDir: () => {
26162
- const pathDirs = (process.env.PATH ?? "").split(path54.delimiter).filter(Boolean);
26704
+ const pathDirs = (process.env.PATH ?? "").split(path56.delimiter).filter(Boolean);
26163
26705
  const home = _linkSeam.homedir();
26164
- const localBin = home ? path54.join(home, ".local", "bin") : null;
26706
+ const localBin = home ? path56.join(home, ".local", "bin") : null;
26165
26707
  if (localBin) {
26166
26708
  try {
26167
26709
  _linkSeam.ensureDir(localBin);
@@ -26171,16 +26713,16 @@ var _linkSeam = {
26171
26713
  const candidates = [];
26172
26714
  if (localBin) candidates.push(localBin);
26173
26715
  try {
26174
- candidates.push(path54.dirname(process.execPath));
26716
+ candidates.push(path56.dirname(process.execPath));
26175
26717
  } catch {
26176
26718
  }
26177
26719
  candidates.push("/usr/local/bin");
26178
26720
  const entry = process.argv[1];
26179
26721
  if (entry) {
26180
26722
  try {
26181
- candidates.push(path54.dirname(fs46.realpathSync(entry)));
26723
+ candidates.push(path56.dirname(fs49.realpathSync(entry)));
26182
26724
  } catch {
26183
- candidates.push(path54.dirname(entry));
26725
+ candidates.push(path56.dirname(entry));
26184
26726
  }
26185
26727
  }
26186
26728
  const onPathWritable = candidates.find(
@@ -26192,20 +26734,20 @@ var _linkSeam = {
26192
26734
  /** Current symlink target at `linkPath`, or null when absent / not a link. */
26193
26735
  readlink: (linkPath) => {
26194
26736
  try {
26195
- return fs46.readlinkSync(linkPath);
26737
+ return fs49.readlinkSync(linkPath);
26196
26738
  } catch {
26197
26739
  return null;
26198
26740
  }
26199
26741
  },
26200
- unlink: (linkPath) => fs46.unlinkSync(linkPath),
26201
- symlink: (target, linkPath) => fs46.symlinkSync(target, linkPath)
26742
+ unlink: (linkPath) => fs49.unlinkSync(linkPath),
26743
+ symlink: (target, linkPath) => fs49.symlinkSync(target, linkPath)
26202
26744
  };
26203
26745
  function linkBdOntoPath(binaryPath) {
26204
26746
  if (_linkSeam.platform() === "win32") return;
26205
26747
  const binDir = _linkSeam.cliBinDir();
26206
26748
  if (!binDir) return;
26207
26749
  _linkSeam.ensureDir(binDir);
26208
- const linkPath = path54.join(binDir, "bd");
26750
+ const linkPath = path56.join(binDir, "bd");
26209
26751
  if (linkPath === binaryPath) return;
26210
26752
  const current2 = _linkSeam.readlink(linkPath);
26211
26753
  if (current2 === binaryPath) return;
@@ -26419,16 +26961,16 @@ function dedupeRecipes(agents) {
26419
26961
  }
26420
26962
  function ensureLocalGitExclude(cwd, patterns) {
26421
26963
  try {
26422
- const gitDir = path54.join(cwd, ".git");
26423
- if (!fs46.statSync(gitDir).isDirectory()) return;
26424
- const file = path54.join(gitDir, "info", "exclude");
26425
- fs46.mkdirSync(path54.dirname(file), { recursive: true });
26426
- const current2 = fs46.existsSync(file) ? fs46.readFileSync(file, "utf8") : "";
26964
+ const gitDir = path56.join(cwd, ".git");
26965
+ if (!fs49.statSync(gitDir).isDirectory()) return;
26966
+ const file = path56.join(gitDir, "info", "exclude");
26967
+ fs49.mkdirSync(path56.dirname(file), { recursive: true });
26968
+ const current2 = fs49.existsSync(file) ? fs49.readFileSync(file, "utf8") : "";
26427
26969
  const have = new Set(current2.split(/\r?\n/).map((l) => l.trim()));
26428
26970
  const missing = patterns.filter((p2) => !have.has(p2));
26429
26971
  if (missing.length === 0) return;
26430
26972
  const prefix = current2.length > 0 && !current2.endsWith("\n") ? "\n" : "";
26431
- fs46.appendFileSync(file, `${prefix}# CodeAgent: beads state stays local
26973
+ fs49.appendFileSync(file, `${prefix}# CodeAgent: beads state stays local
26432
26974
  ${missing.join("\n")}
26433
26975
  `);
26434
26976
  } catch {
@@ -26437,7 +26979,7 @@ ${missing.join("\n")}
26437
26979
 
26438
26980
  // src/beads/watcher.ts
26439
26981
  var crypto4 = __toESM(require("crypto"));
26440
- var path55 = __toESM(require("path"));
26982
+ var path57 = __toESM(require("path"));
26441
26983
  var API_BASE5 = resolveApiBaseUrl();
26442
26984
  var DEBOUNCE_MS2 = 400;
26443
26985
  var ZERO_SUMMARY = {
@@ -26461,7 +27003,7 @@ var BeadsWatcher = class {
26461
27003
  constructor(opts) {
26462
27004
  this.opts = opts;
26463
27005
  this.bd = opts.adapter ?? new BdAdapter({ cwd: opts.cwd, beadsDir: opts.beadsDir });
26464
- this.feedPath = opts.feedPath ?? path55.join(opts.cwd ?? process.cwd(), ".beads", "last-touched");
27006
+ this.feedPath = opts.feedPath ?? path57.join(opts.cwd ?? process.cwd(), ".beads", "last-touched");
26465
27007
  this.apiBase = opts.apiBaseUrl ?? API_BASE5;
26466
27008
  }
26467
27009
  opts;
@@ -26715,41 +27257,6 @@ async function handleBeadsActionCommand(action, started) {
26715
27257
  var import_node_fs8 = __toESM(require("fs"));
26716
27258
  var import_node_os5 = __toESM(require("os"));
26717
27259
  var import_node_path6 = __toESM(require("path"));
26718
-
26719
- // src/lib/restrict-to-owner.ts
26720
- var import_node_fs7 = __toESM(require("fs"));
26721
- var import_node_os4 = __toESM(require("os"));
26722
- var import_node_child_process18 = require("child_process");
26723
- var BROAD_WINDOWS_SIDS = [
26724
- "*S-1-1-0",
26725
- "*S-1-5-11",
26726
- "*S-1-5-32-545",
26727
- "*S-1-5-32-544",
26728
- "*S-1-5-18"
26729
- ];
26730
- function restrictToOwner(filePath) {
26731
- try {
26732
- if (process.platform === "win32") {
26733
- const username = import_node_os4.default.userInfo().username;
26734
- (0, import_node_child_process18.execFileSync)(
26735
- "icacls",
26736
- [
26737
- filePath,
26738
- "/inheritance:r",
26739
- "/grant:r",
26740
- `${username}:F`,
26741
- ...BROAD_WINDOWS_SIDS.flatMap((sid) => ["/remove", sid])
26742
- ],
26743
- { stdio: "ignore" }
26744
- );
26745
- } else {
26746
- import_node_fs7.default.chmodSync(filePath, 384);
26747
- }
26748
- } catch {
26749
- }
26750
- }
26751
-
26752
- // src/beads/config-store.ts
26753
27260
  function beadsConfigPath() {
26754
27261
  return import_node_path6.default.join(import_node_os5.default.homedir(), ".codeam", "beads-config.json");
26755
27262
  }
@@ -27036,8 +27543,8 @@ function cleanupAttachmentTempFiles() {
27036
27543
  function saveFilesTemp(files) {
27037
27544
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
27038
27545
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
27039
- const tmpPath = path59.join(os39.tmpdir(), `codeam-${(0, import_crypto2.randomUUID)()}-${safeName}`);
27040
- fs51.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
27546
+ const tmpPath = path61.join(os41.tmpdir(), `codeam-${(0, import_crypto2.randomUUID)()}-${safeName}`);
27547
+ fs53.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
27041
27548
  pendingAttachmentFiles.add(tmpPath);
27042
27549
  return tmpPath;
27043
27550
  });
@@ -27272,10 +27779,10 @@ var listFiles = async (ctx, cmd, parsed) => {
27272
27779
  await ctx.relay.sendResult(cmd.id, "completed", result);
27273
27780
  };
27274
27781
  var envReadH = async (ctx, cmd) => {
27275
- const envPath = path59.join(process.cwd(), ".env");
27782
+ const envPath = path61.join(process.cwd(), ".env");
27276
27783
  await restoreProjectEnvIfMissing(process.cwd(), ctx);
27277
27784
  try {
27278
- const raw = await fs51.promises.readFile(envPath, "utf8");
27785
+ const raw = await fs53.promises.readFile(envPath, "utf8");
27279
27786
  await ctx.relay.sendResult(cmd.id, "completed", {
27280
27787
  exists: true,
27281
27788
  vars: parseDotenv(raw)
@@ -27304,7 +27811,7 @@ var envParseH = async (ctx, cmd, parsed) => {
27304
27811
  const existing = /* @__PURE__ */ new Map();
27305
27812
  if (!replace) {
27306
27813
  try {
27307
- const raw = await fs51.promises.readFile(path59.join(process.cwd(), ".env"), "utf8");
27814
+ const raw = await fs53.promises.readFile(path61.join(process.cwd(), ".env"), "utf8");
27308
27815
  for (const v of parseDotenv(raw)) existing.set(v.key, v.value);
27309
27816
  } catch {
27310
27817
  }
@@ -27344,15 +27851,15 @@ var envWriteH = async (ctx, cmd, parsed) => {
27344
27851
  }
27345
27852
  seen2.add(v.key);
27346
27853
  }
27347
- const envPath = path59.join(process.cwd(), ".env");
27348
- const tmpPath = path59.join(process.cwd(), ".env.codeam.tmp");
27854
+ const envPath = path61.join(process.cwd(), ".env");
27855
+ const tmpPath = path61.join(process.cwd(), ".env.codeam.tmp");
27349
27856
  try {
27350
- await fs51.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
27351
- await fs51.promises.rename(tmpPath, envPath);
27857
+ await fs53.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
27858
+ await fs53.promises.rename(tmpPath, envPath);
27352
27859
  await ctx.relay.sendResult(cmd.id, "completed", { ok: true, count: vars.length });
27353
27860
  void syncProjectEnvUp(process.cwd(), ctx);
27354
27861
  } catch (err) {
27355
- await fs51.promises.rm(tmpPath, { force: true }).catch(() => void 0);
27862
+ await fs53.promises.rm(tmpPath, { force: true }).catch(() => void 0);
27356
27863
  await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
27357
27864
  }
27358
27865
  };
@@ -27684,9 +28191,9 @@ var CLI_UPDATE_MAX_ATTEMPTS = 3;
27684
28191
  function buildNpmInstallInvocation(opts) {
27685
28192
  const entryScript = opts?.entryScript ?? process.argv[1] ?? "";
27686
28193
  const execPath = opts?.execPath ?? process.execPath;
27687
- const exists2 = opts?.existsSync ?? fs51.existsSync;
28194
+ const exists2 = opts?.existsSync ?? fs53.existsSync;
27688
28195
  const platform3 = opts?.platform ?? process.platform;
27689
- const p2 = platform3 === "win32" ? path59.win32 : path59.posix;
28196
+ const p2 = platform3 === "win32" ? path61.win32 : path61.posix;
27690
28197
  const normalized = entryScript.split(/[\\/]/).join("/");
27691
28198
  const marker = "/lib/node_modules/codeam-cli/";
27692
28199
  const markerIdx = normalized.indexOf(marker);
@@ -27708,7 +28215,7 @@ function isPermissionError(stderr) {
27708
28215
  function resolveGlobalNodeModulesDir(opts) {
27709
28216
  const entryScript = opts?.entryScript ?? process.argv[1] ?? "";
27710
28217
  const platform3 = opts?.platform ?? process.platform;
27711
- const p2 = platform3 === "win32" ? path59.win32 : path59.posix;
28218
+ const p2 = platform3 === "win32" ? path61.win32 : path61.posix;
27712
28219
  const marker = "/lib/node_modules/codeam-cli/";
27713
28220
  const markerIdx = entryScript.split(/[\\/]/).join("/").indexOf(marker);
27714
28221
  if (markerIdx <= 0) return null;
@@ -27718,9 +28225,9 @@ function resolveGlobalNodeModulesDir(opts) {
27718
28225
  var STALE_STAGING_AGE_MS = CLI_UPDATE_INSTALL_TIMEOUT_MS;
27719
28226
  function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
27720
28227
  if (!nodeModulesDir) return 0;
27721
- const readdirSync16 = deps?.readdirSync ?? fs51.readdirSync;
27722
- const statSync16 = deps?.statSync ?? fs51.statSync;
27723
- const rmSync10 = deps?.rmSync ?? fs51.rmSync;
28228
+ const readdirSync16 = deps?.readdirSync ?? fs53.readdirSync;
28229
+ const statSync16 = deps?.statSync ?? fs53.statSync;
28230
+ const rmSync10 = deps?.rmSync ?? fs53.rmSync;
27724
28231
  let removed = 0;
27725
28232
  let entries;
27726
28233
  try {
@@ -27730,7 +28237,7 @@ function sweepStaleCliStagingDirs(nodeModulesDir, now = Date.now(), deps) {
27730
28237
  }
27731
28238
  for (const name of entries) {
27732
28239
  if (!/^\.codeam-cli-/.test(name)) continue;
27733
- const full = path59.join(nodeModulesDir, name);
28240
+ const full = path61.join(nodeModulesDir, name);
27734
28241
  try {
27735
28242
  const st3 = statSync16(full);
27736
28243
  if (now - st3.mtimeMs < STALE_STAGING_AGE_MS) continue;
@@ -28102,11 +28609,13 @@ async function runDetectOneShot(generate, timeoutMs = PREVIEW_DETECT_TIMEOUT_MS)
28102
28609
  const onStderr = (chunk) => {
28103
28610
  stderr = (stderr + chunk).slice(-4e3);
28104
28611
  };
28612
+ const onFailedOutput = (stdout) => onStderr(`
28613
+ ${stdout}`);
28105
28614
  const deadline = new Promise((resolve12) => {
28106
28615
  timer = setTimeout(() => resolve12({ raw: null, timedOut: true, stderr }), timeoutMs);
28107
28616
  timer.unref?.();
28108
28617
  });
28109
- const oneShot = generate(PREVIEW_DETECT_PROMPT, { timeoutMs, onStderr }).then(
28618
+ const oneShot = generate(PREVIEW_DETECT_PROMPT, { timeoutMs, onStderr, onFailedOutput }).then(
28110
28619
  (raw) => ({ raw, timedOut: false, stderr }),
28111
28620
  (err) => {
28112
28621
  log.info("preview", `detect: generateOneShot threw: ${String(err)}`);
@@ -28170,6 +28679,15 @@ async function resolvePreviewDetection(args2) {
28170
28679
  --- salida cruda del agente (acotada) ---
28171
28680
  ${failure?.rawExcerpt ?? ""}`
28172
28681
  );
28682
+ if (looksLikeByoProviderBilling(`${raw ?? ""}
28683
+ ${stderr}`)) {
28684
+ log.info("preview", "detect: provider out of credits \u2014 emitting preview_error");
28685
+ emit2(USER_EVENTS.PREVIEW_ERROR, {
28686
+ stage: "detection",
28687
+ message: previewProviderCreditsMessage(byoProviderName({ agent: runtime.id }))
28688
+ });
28689
+ return null;
28690
+ }
28173
28691
  emit2(USER_EVENTS.PREVIEW_ERROR, {
28174
28692
  stage: "detection",
28175
28693
  // El mensaje sale del diagnostico: decir "JSON invalido" cuando el
@@ -28765,7 +29283,7 @@ function resolveTokenValue(args2) {
28765
29283
  if (fileFlag) {
28766
29284
  const path98 = fileFlag.slice("--token-file=".length);
28767
29285
  try {
28768
- const content = fs52.readFileSync(path98, "utf8").trim();
29286
+ const content = fs54.readFileSync(path98, "utf8").trim();
28769
29287
  if (content.length === 0) fail(`--token-file ${path98} is empty`);
28770
29288
  rmIfExistsQuiet(path98);
28771
29289
  return content;
@@ -28859,12 +29377,12 @@ async function claim(token, pluginId, pluginSecretHash) {
28859
29377
  }
28860
29378
  }
28861
29379
  function pairAutoLockPath() {
28862
- return path60.join(os40.homedir(), ".codeam", "pair-auto.lock");
29380
+ return path62.join(os42.homedir(), ".codeam", "pair-auto.lock");
28863
29381
  }
28864
29382
  var procRoot = "/proc";
28865
29383
  function readProcFile(rel) {
28866
29384
  try {
28867
- return fs52.readFileSync(path60.join(procRoot, rel), "utf8");
29385
+ return fs54.readFileSync(path62.join(procRoot, rel), "utf8");
28868
29386
  } catch {
28869
29387
  return void 0;
28870
29388
  }
@@ -28885,7 +29403,7 @@ function currentBootId() {
28885
29403
  function readLockRecord(lockPath) {
28886
29404
  let raw;
28887
29405
  try {
28888
- raw = fs52.readFileSync(lockPath, "utf8");
29406
+ raw = fs54.readFileSync(lockPath, "utf8");
28889
29407
  } catch {
28890
29408
  return void 0;
28891
29409
  }
@@ -28934,7 +29452,7 @@ function isLiveLockHolder(rec) {
28934
29452
  }
28935
29453
  function releaseOwnLock(lockPath) {
28936
29454
  try {
28937
- if (readLockRecord(lockPath)?.pid === process.pid) fs52.unlinkSync(lockPath);
29455
+ if (readLockRecord(lockPath)?.pid === process.pid) fs54.unlinkSync(lockPath);
28938
29456
  } catch {
28939
29457
  }
28940
29458
  }
@@ -28958,7 +29476,7 @@ function snapshotProcessTree(root) {
28958
29476
  const children = /* @__PURE__ */ new Map();
28959
29477
  let entries = [];
28960
29478
  try {
28961
- entries = fs52.readdirSync(procRoot).filter((e) => /^\d+$/.test(e));
29479
+ entries = fs54.readdirSync(procRoot).filter((e) => /^\d+$/.test(e));
28962
29480
  } catch {
28963
29481
  }
28964
29482
  for (const e of entries) {
@@ -28993,18 +29511,18 @@ function signalProcessTree(refs, signal) {
28993
29511
  }
28994
29512
  function daemonLockPath(sessionId) {
28995
29513
  const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
28996
- return path60.join(os40.homedir(), ".codeam", `daemon-${safe}.lock`);
29514
+ return path62.join(os42.homedir(), ".codeam", `daemon-${safe}.lock`);
28997
29515
  }
28998
29516
  function acquireDaemonLock(sessionId) {
28999
29517
  const lockPath = daemonLockPath(sessionId);
29000
29518
  try {
29001
- fs52.mkdirSync(path60.dirname(lockPath), { recursive: true });
29519
+ fs54.mkdirSync(path62.dirname(lockPath), { recursive: true });
29002
29520
  try {
29003
- fs52.writeFileSync(lockPath, ownLockBody(), { flag: "wx" });
29521
+ fs54.writeFileSync(lockPath, ownLockBody(), { flag: "wx" });
29004
29522
  } catch (e) {
29005
29523
  if (e.code !== "EEXIST") throw e;
29006
29524
  if (isLiveLockHolder(readLockRecord(lockPath))) return false;
29007
- fs52.writeFileSync(lockPath, ownLockBody());
29525
+ fs54.writeFileSync(lockPath, ownLockBody());
29008
29526
  }
29009
29527
  const release3 = () => releaseOwnLock(lockPath);
29010
29528
  process.once("exit", release3);
@@ -29024,13 +29542,13 @@ function acquireDaemonLock(sessionId) {
29024
29542
  function acquireSingletonLock() {
29025
29543
  const lockPath = pairAutoLockPath();
29026
29544
  try {
29027
- fs52.mkdirSync(path60.dirname(lockPath), { recursive: true });
29545
+ fs54.mkdirSync(path62.dirname(lockPath), { recursive: true });
29028
29546
  try {
29029
- fs52.writeFileSync(lockPath, ownLockBody(), { flag: "wx" });
29547
+ fs54.writeFileSync(lockPath, ownLockBody(), { flag: "wx" });
29030
29548
  } catch (e) {
29031
29549
  if (e.code !== "EEXIST") throw e;
29032
29550
  if (isLiveLockHolder(readLockRecord(lockPath))) return false;
29033
- fs52.writeFileSync(lockPath, ownLockBody());
29551
+ fs54.writeFileSync(lockPath, ownLockBody());
29034
29552
  }
29035
29553
  process.once("exit", () => releaseOwnLock(lockPath));
29036
29554
  return true;
@@ -29447,7 +29965,7 @@ var AgentService = class _AgentService {
29447
29965
  };
29448
29966
 
29449
29967
  // src/agents/acp/adapters.ts
29450
- var path61 = __toESM(require("path"));
29968
+ var path63 = __toESM(require("path"));
29451
29969
 
29452
29970
  // src/agents/kimi/installer.ts
29453
29971
  var import_node_child_process19 = require("child_process");
@@ -29567,13 +30085,13 @@ function resolveBin(pkgName, binName) {
29567
30085
  try {
29568
30086
  const manifestPath = require_.resolve(`${pkgName}/package.json`);
29569
30087
  const manifest = require_(`${pkgName}/package.json`);
29570
- const pkgDir = path61.dirname(manifestPath);
30088
+ const pkgDir = path63.dirname(manifestPath);
29571
30089
  const bin = manifest.bin;
29572
30090
  if (!bin) return null;
29573
- if (typeof bin === "string") return path61.resolve(pkgDir, bin);
30091
+ if (typeof bin === "string") return path63.resolve(pkgDir, bin);
29574
30092
  const target = binName ?? Object.keys(bin)[0];
29575
30093
  if (!target || !bin[target]) return null;
29576
- return path61.resolve(pkgDir, bin[target]);
30094
+ return path63.resolve(pkgDir, bin[target]);
29577
30095
  } catch {
29578
30096
  return null;
29579
30097
  }
@@ -29753,25 +30271,25 @@ async function resolveAcpAdapterWithRetry(agent, opts = {}) {
29753
30271
  }
29754
30272
 
29755
30273
  // src/commands/host/workspace.ts
29756
- var fs53 = __toESM(require("fs"));
29757
- var os41 = __toESM(require("os"));
29758
- var path62 = __toESM(require("path"));
30274
+ var fs55 = __toESM(require("fs"));
30275
+ var os43 = __toESM(require("os"));
30276
+ var path64 = __toESM(require("path"));
29759
30277
  var import_node_child_process21 = require("child_process");
29760
30278
  var import_node_util5 = require("util");
29761
30279
  var execFileP5 = (0, import_node_util5.promisify)(import_node_child_process21.execFile);
29762
30280
  function isAbsolutePathTarget(target) {
29763
- return path62.isAbsolute(target);
30281
+ return path64.isAbsolute(target);
29764
30282
  }
29765
30283
  function selfHostedWorkspaceRoot() {
29766
- return path62.join(os41.homedir(), ".codeam", "self-hosted");
30284
+ return path64.join(os43.homedir(), ".codeam", "self-hosted");
29767
30285
  }
29768
30286
  function deployIdFromWorkspace(cwd) {
29769
30287
  if (!cwd) return null;
29770
30288
  const root = selfHostedWorkspaceRoot();
29771
- const resolved = path62.resolve(cwd);
29772
- const rel = path62.relative(root, resolved);
29773
- if (!rel || rel.startsWith("..") || path62.isAbsolute(rel)) return null;
29774
- const parts = rel.split(path62.sep).filter(Boolean);
30289
+ const resolved = path64.resolve(cwd);
30290
+ const rel = path64.relative(root, resolved);
30291
+ if (!rel || rel.startsWith("..") || path64.isAbsolute(rel)) return null;
30292
+ const parts = rel.split(path64.sep).filter(Boolean);
29775
30293
  if (parts.length !== 1) return null;
29776
30294
  return parts[0];
29777
30295
  }
@@ -29843,15 +30361,15 @@ async function configureGitCredentials(dest, repoRef, cloneToken, provider = "gi
29843
30361
  const isGitlab = provider === "gitlab";
29844
30362
  if (isGitlab ? !gitlabProjectPath(repoRef.trim()) : !githubOwnerRepo(repoRef.trim())) return;
29845
30363
  if (!cloneToken) return;
29846
- const credFile = path62.join(dest, ".git", "codeam-credentials");
30364
+ const credFile = path64.join(dest, ".git", "codeam-credentials");
29847
30365
  const credLine = isGitlab ? `https://oauth2:${cloneToken}@gitlab.com
29848
30366
  ` : `https://x-access-token:${cloneToken}@github.com
29849
30367
  `;
29850
- fs53.writeFileSync(credFile, credLine, { mode: 384 });
30368
+ fs55.writeFileSync(credFile, credLine, { mode: 384 });
29851
30369
  restrictToOwner(credFile);
29852
30370
  const env = nonInteractiveGitEnv();
29853
30371
  const git2 = (args2) => execFileP5("git", ["-C", dest, ...args2], { timeout: 3e4, env });
29854
- const credFilePosix = credFile.split(path62.sep).join("/");
30372
+ const credFilePosix = credFile.split(path64.sep).join("/");
29855
30373
  await git2(["config", "--local", "--replace-all", "credential.helper", ""]).catch(() => {
29856
30374
  });
29857
30375
  await git2([
@@ -29884,17 +30402,17 @@ function maskToken(text, cloneToken) {
29884
30402
  }
29885
30403
  async function prepareWorkspace(repoOrPath, deployId, cloneToken, provider = "github", branch) {
29886
30404
  if (isAbsolutePathTarget(repoOrPath)) {
29887
- if (!fs53.existsSync(repoOrPath)) {
30405
+ if (!fs55.existsSync(repoOrPath)) {
29888
30406
  throw new Error(`deploy target path does not exist: ${repoOrPath}`);
29889
30407
  }
29890
30408
  return repoOrPath;
29891
30409
  }
29892
- const dest = path62.join(selfHostedWorkspaceRoot(), deployId);
29893
- if (fs53.existsSync(path62.join(dest, ".git"))) {
30410
+ const dest = path64.join(selfHostedWorkspaceRoot(), deployId);
30411
+ if (fs55.existsSync(path64.join(dest, ".git"))) {
29894
30412
  if (cloneToken) await configureGitCredentials(dest, repoOrPath, cloneToken, provider);
29895
30413
  return dest;
29896
30414
  }
29897
- fs53.mkdirSync(selfHostedWorkspaceRoot(), { recursive: true, mode: 448 });
30415
+ fs55.mkdirSync(selfHostedWorkspaceRoot(), { recursive: true, mode: 448 });
29898
30416
  const cloneUrl = repoCloneUrl(repoOrPath, cloneToken, provider);
29899
30417
  const cloneArgs = branch ? ["clone", "--depth", "1", "--branch", branch, cloneUrl, dest] : ["clone", "--depth", "1", cloneUrl, dest];
29900
30418
  try {
@@ -29917,266 +30435,6 @@ var fs72 = __toESM(require("fs"));
29917
30435
  var os56 = __toESM(require("os"));
29918
30436
  var path83 = __toESM(require("path"));
29919
30437
 
29920
- // src/lib/provider-routing.ts
29921
- var fs55 = __toESM(require("fs"));
29922
- var os43 = __toESM(require("os"));
29923
- var path64 = __toESM(require("path"));
29924
-
29925
- // src/commands/host/house-proxy-config.ts
29926
- var fs54 = __toESM(require("fs"));
29927
- var os42 = __toESM(require("os"));
29928
- var path63 = __toESM(require("path"));
29929
- function houseProxyConfigPath() {
29930
- return path63.join(os42.homedir(), ".codeam", "house-proxy.json");
29931
- }
29932
- var DEPLOY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
29933
- function deployHouseProxyConfigPath(deployId) {
29934
- if (!DEPLOY_ID_RE.test(deployId) || deployId.includes("..")) return null;
29935
- return path63.join(os42.homedir(), ".codeam", "house-proxy", `${deployId}.json`);
29936
- }
29937
- function writeJson0600(file, value) {
29938
- fs54.mkdirSync(path63.dirname(file), { recursive: true, mode: 448 });
29939
- const tmp = `${file}.tmp-${process.pid}`;
29940
- fs54.writeFileSync(tmp, JSON.stringify(value, null, 2), { encoding: "utf8", mode: 384 });
29941
- fs54.renameSync(tmp, file);
29942
- restrictToOwner(file);
29943
- }
29944
- function persistHouseProxyConfig(config, deployId) {
29945
- try {
29946
- const perDeploy = deployId ? deployHouseProxyConfigPath(deployId) : null;
29947
- if (perDeploy) writeJson0600(perDeploy, config);
29948
- writeJson0600(houseProxyConfigPath(), config);
29949
- } catch (err) {
29950
- log.warn(
29951
- "host-agent",
29952
- `failed to persist house-proxy config (best-effort): ${err instanceof Error ? err.message : String(err)}`
29953
- );
29954
- }
29955
- }
29956
- function clearHouseProxyConfig(deployId) {
29957
- try {
29958
- fs54.rmSync(houseProxyConfigPath(), { force: true });
29959
- const perDeploy = deployId ? deployHouseProxyConfigPath(deployId) : null;
29960
- if (perDeploy) writeJson0600(perDeploy, { none: true });
29961
- } catch {
29962
- }
29963
- }
29964
- function readHouseProxyChildEnv(deployId) {
29965
- try {
29966
- const perDeploy = deployId ? deployHouseProxyConfigPath(deployId) : null;
29967
- const file = perDeploy && fs54.existsSync(perDeploy) ? perDeploy : houseProxyConfigPath();
29968
- const raw = fs54.readFileSync(file, "utf8");
29969
- const parsed = JSON.parse(raw);
29970
- if (typeof parsed !== "object" || parsed === null) return {};
29971
- const o = parsed;
29972
- if (o.none === true) return {};
29973
- if (typeof o.baseUrl !== "string" || !o.baseUrl) return {};
29974
- if (typeof o.token !== "string" || !o.token) return {};
29975
- return buildHouseProxyChildEnv({
29976
- baseUrl: o.baseUrl,
29977
- token: o.token,
29978
- openRouter: o.openRouter === true,
29979
- ...typeof o.claudeConfigDir === "string" && o.claudeConfigDir ? { claudeConfigDir: o.claudeConfigDir } : {},
29980
- ...typeof o.model === "string" && o.model ? { model: o.model } : {},
29981
- ...typeof o.managedAgentId === "string" && o.managedAgentId ? { managedAgentId: o.managedAgentId } : {}
29982
- });
29983
- } catch {
29984
- return {};
29985
- }
29986
- }
29987
- var HOUSE_MODEL_CONTEXT_TOKENS = "512000";
29988
- function buildHouseProxyChildEnv(cfg) {
29989
- const env = {
29990
- ANTHROPIC_BASE_URL: cfg.baseUrl,
29991
- ANTHROPIC_AUTH_TOKEN: cfg.token,
29992
- // ⚠️ MAX_CONTEXT_TOKENS, not (only) AUTO_COMPACT_WINDOW — and this is
29993
- // HOUSE-AGENT ONLY: it lives in this builder precisely so a user's own
29994
- // Claude Code (a real Anthropic model claude knows) is never touched.
29995
- //
29996
- // claude does not know `MiniMax-M3`, so it falls back to its
29997
- // unknown-model default (200k). `CLAUDE_CODE_AUTO_COMPACT_WINDOW` is then
29998
- // applied as `Math.min(assumedWindow, env)` — it can only SHRINK the
29999
- // window, never grow it — so the 512000 we used to set here was inert:
30000
- // claude kept believing 200k. Meanwhile a session with a dozen linked
30001
- // integrations carries ~175k tokens of MCP tool schemas on EVERY turn
30002
- // (measured: 475 tools ≈ 698 KB on a real box), so the context started
30003
- // ~88% full and a 28-character prompt triggered a compaction, then the
30004
- // next small tool result triggered another: "Autocompact is thrashing:
30005
- // the context refilled to the limit within 3 turns", 13 compactions in
30006
- // one session (rafaelph90.br@gmail.com, 2026-09-03).
30007
- //
30008
- // claude's own unknown-model notice names the fix: "set
30009
- // CLAUDE_CODE_MAX_CONTEXT_TOKENS to its real window" — that variable is
30010
- // what `wL()` returns for an unrecognised model. MiniMax-M3 is a 1M-context
30011
- // model with a guaranteed minimum of 512K, so 512000 here is the
30012
- // conservative truth, not a wish. AUTO_COMPACT_WINDOW stays as the
30013
- // compaction threshold beneath it.
30014
- CLAUDE_CODE_MAX_CONTEXT_TOKENS: HOUSE_MODEL_CONTEXT_TOKENS,
30015
- CLAUDE_CODE_AUTO_COMPACT_WINDOW: HOUSE_MODEL_CONTEXT_TOKENS,
30016
- API_TIMEOUT_MS: "3000000"
30017
- };
30018
- if (cfg.openRouter === true) {
30019
- env.ANTHROPIC_API_KEY = "";
30020
- } else {
30021
- const model = typeof cfg.model === "string" && cfg.model ? cfg.model : "MiniMax-M3";
30022
- env.ANTHROPIC_MODEL = model;
30023
- env.ANTHROPIC_DEFAULT_SONNET_MODEL = model;
30024
- env.ANTHROPIC_DEFAULT_OPUS_MODEL = model;
30025
- env.ANTHROPIC_DEFAULT_HAIKU_MODEL = model;
30026
- }
30027
- if (typeof cfg.claudeConfigDir === "string" && cfg.claudeConfigDir) {
30028
- env.CLAUDE_CONFIG_DIR = cfg.claudeConfigDir;
30029
- }
30030
- if (isManagedProviderId(cfg.managedAgentId)) {
30031
- env[MANAGED_AGENT_ENV] = cfg.managedAgentId;
30032
- }
30033
- return env;
30034
- }
30035
- function houseRailWireId(env) {
30036
- const managed = env[MANAGED_AGENT_ENV];
30037
- return isManagedProviderId(managed) ? managed : HOUSE_AGENT_ID;
30038
- }
30039
- var HOUSE_PROXY_ENV_KEYS = [
30040
- "ANTHROPIC_BASE_URL",
30041
- "ANTHROPIC_AUTH_TOKEN",
30042
- "ANTHROPIC_API_KEY",
30043
- "ANTHROPIC_MODEL",
30044
- "ANTHROPIC_DEFAULT_SONNET_MODEL",
30045
- "ANTHROPIC_DEFAULT_OPUS_MODEL",
30046
- "ANTHROPIC_DEFAULT_HAIKU_MODEL",
30047
- "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
30048
- "CLAUDE_CODE_MAX_CONTEXT_TOKENS",
30049
- "API_TIMEOUT_MS",
30050
- "CLAUDE_CONFIG_DIR",
30051
- MANAGED_AGENT_ENV
30052
- ];
30053
- function clearHouseProxyEnvOverrides() {
30054
- const out2 = {};
30055
- for (const key of HOUSE_PROXY_ENV_KEYS) out2[key] = void 0;
30056
- return out2;
30057
- }
30058
- function isHouseProxyEnv(env) {
30059
- return typeof env.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN.length > 0 && (env.ANTHROPIC_BASE_URL ?? "").includes("/api/v1/agent-proxy");
30060
- }
30061
- function currentRailWireId(env = process.env) {
30062
- return isHouseProxyEnv(env) ? houseRailWireId(env) : null;
30063
- }
30064
- function pickHouseProxyEnv(env) {
30065
- const out2 = {};
30066
- for (const key of HOUSE_PROXY_ENV_KEYS) {
30067
- const v = env[key];
30068
- if (typeof v === "string") out2[key] = v;
30069
- }
30070
- return out2;
30071
- }
30072
-
30073
- // src/lib/provider-routing.ts
30074
- var PROVIDER_BASE_URL_ENV_KEYS = [
30075
- "ANTHROPIC_BASE_URL",
30076
- "OPENAI_BASE_URL",
30077
- "OPENAI_API_BASE",
30078
- "GEMINI_API_BASE",
30079
- "GOOGLE_GEMINI_BASE_URL"
30080
- ];
30081
- var ROUTING_DEFAULT = "default";
30082
- function redactUrlToOrigin(raw) {
30083
- if (typeof raw !== "string") return null;
30084
- const trimmed = raw.trim();
30085
- if (!trimmed) return null;
30086
- try {
30087
- const u2 = new URL(trimmed);
30088
- if (!u2.host) return "invalid-url";
30089
- return `${u2.protocol}//${u2.host}`;
30090
- } catch {
30091
- return "invalid-url";
30092
- }
30093
- }
30094
- function readOpencodeProviderOrigins(opts = {}) {
30095
- const home = opts.homeDir ?? os43.homedir();
30096
- const readFile7 = opts.readFile ?? ((f) => fs55.readFileSync(f, "utf8"));
30097
- const candidates = [];
30098
- if (opts.cwd) {
30099
- candidates.push(path64.join(opts.cwd, "opencode.json"), path64.join(opts.cwd, "opencode.jsonc"));
30100
- }
30101
- candidates.push(
30102
- path64.join(home, ".config", "opencode", "opencode.json"),
30103
- path64.join(home, ".config", "opencode", "opencode.jsonc")
30104
- );
30105
- const out2 = [];
30106
- const seen2 = /* @__PURE__ */ new Set();
30107
- for (const file of candidates) {
30108
- let text;
30109
- try {
30110
- text = readFile7(file);
30111
- } catch {
30112
- continue;
30113
- }
30114
- let parsed;
30115
- try {
30116
- parsed = JSON.parse(text.replace(/^\s*\/\/.*$/gm, ""));
30117
- } catch {
30118
- continue;
30119
- }
30120
- const providers = parsed?.provider;
30121
- if (!providers || typeof providers !== "object") continue;
30122
- for (const [id, cfg] of Object.entries(providers)) {
30123
- if (seen2.has(id)) continue;
30124
- const options = cfg?.options;
30125
- const base = options?.baseURL ?? options?.baseUrl ?? options?.base_url;
30126
- const origin = redactUrlToOrigin(base);
30127
- if (!origin) continue;
30128
- seen2.add(id);
30129
- out2.push({ provider: id, origin });
30130
- }
30131
- }
30132
- return out2;
30133
- }
30134
- function describeProviderRouting(env, opts = {}) {
30135
- const routed = {};
30136
- for (const key of PROVIDER_BASE_URL_ENV_KEYS) {
30137
- routed[key] = redactUrlToOrigin(env[key]) ?? ROUTING_DEFAULT;
30138
- }
30139
- let opencode = [];
30140
- try {
30141
- opencode = readOpencodeProviderOrigins(opts);
30142
- } catch {
30143
- }
30144
- const houseKeys = HOUSE_PROXY_ENV_KEYS.filter(
30145
- (k2) => typeof env[k2] === "string" && env[k2].length > 0
30146
- );
30147
- const codeamKeys = Object.keys(env).filter((k2) => k2.startsWith("CODEAM_") && typeof env[k2] === "string" && env[k2].length > 0).sort();
30148
- return { env: routed, opencode, houseProxy: isHouseProxyEnv(env), houseKeys, codeamKeys };
30149
- }
30150
- function formatProviderRouting(r, label) {
30151
- const parts = [
30152
- label ? `agent=${label}` : null,
30153
- `anthropic=${r.env.ANTHROPIC_BASE_URL}`,
30154
- `openai=${r.env.OPENAI_BASE_URL !== ROUTING_DEFAULT ? r.env.OPENAI_BASE_URL : r.env.OPENAI_API_BASE}`,
30155
- `gemini=${r.env.GEMINI_API_BASE !== ROUTING_DEFAULT ? r.env.GEMINI_API_BASE : r.env.GOOGLE_GEMINI_BASE_URL}`,
30156
- `opencode=[${r.opencode.map((p2) => `${p2.provider}=${p2.origin}`).join(",")}]`,
30157
- `houseProxy=${r.houseProxy ? "yes" : "no"}`,
30158
- `houseKeys=[${r.houseKeys.join(",")}]`,
30159
- `codeamKeys=[${r.codeamKeys.join(",")}]`
30160
- ];
30161
- return parts.filter((p2) => p2 !== null).join(" ");
30162
- }
30163
- var PROVIDER_BILLING_RE = /(?:api error|http|status)[:\s]+402\b|\b402\b[^\n]{0,40}(?:payment required|insufficient|credits?|balance)|insufficient (?:credits?|balance|funds)|payment required/i;
30164
- function matchesProviderBilling(text) {
30165
- return PROVIDER_BILLING_RE.test(text);
30166
- }
30167
- function scrubSecrets(text) {
30168
- return text.replace(/\b(?:sk|pk|rk|key|token)[-_][A-Za-z0-9_-]{12,}/gi, "[redacted]").replace(/bearer\s+[A-Za-z0-9._-]{12,}/gi, "Bearer [redacted]").replace(/\b[A-Za-z0-9_-]{40,}\b/g, "[redacted]");
30169
- }
30170
- function classifyProviderBilling(text, env) {
30171
- if (!matchesProviderBilling(text)) return null;
30172
- const line = text.split("\n").find((l) => PROVIDER_BILLING_RE.test(l)) ?? text;
30173
- return {
30174
- marker: isHouseProxyEnv(env) ? "provider_billing_house" : "provider_billing_external",
30175
- anthropicHost: redactUrlToOrigin(env.ANTHROPIC_BASE_URL) ?? ROUTING_DEFAULT,
30176
- snippet: scrubSecrets(line.trim()).slice(0, 160)
30177
- };
30178
- }
30179
-
30180
30438
  // src/agents/acp/provider-billing-signal.ts
30181
30439
  var seen = /* @__PURE__ */ new Set();
30182
30440
  function noteProviderBillingSignal(opts) {
@@ -37494,7 +37752,7 @@ function resolveRepoName(cwd) {
37494
37752
  function buildOnboardingWelcome(cwd) {
37495
37753
  const repo = resolveRepoName(cwd);
37496
37754
  return [
37497
- `Welcome to CodeAgent Mobile! \u{1F44B} You're now driving this agent from your phone \u2014 and it comes fully wired, zero setup:`,
37755
+ `Welcome to CodeAgent Mobile! \u{1F44B} You're now driving this agent from your phone or the web \u2014 and it comes fully wired, zero setup:`,
37498
37756
  "",
37499
37757
  "- **Native repo context, persistent memory, and an issue tracker** \u2014 powered by Beads, out of the box.",
37500
37758
  "- **A full IDE** (Monaco) \u2014 edit code, live preview, and rich visual output, not just a file viewer.",
@@ -38540,243 +38798,6 @@ function withTimeout(p2, ms) {
38540
38798
  });
38541
38799
  }
38542
38800
 
38543
- // src/agents/acp/oneMContextRecovery.ts
38544
- var ONE_M_CONTEXT_CREDITS_RE = /usage credits required for 1m context/i;
38545
- function looksLike1mContextCreditsError(text) {
38546
- return ONE_M_CONTEXT_CREDITS_RE.test(text);
38547
- }
38548
- function shouldOfferOneMRecovery(opts) {
38549
- return looksLike1mContextCreditsError(opts.detail) || looksLike1mContextCreditsError(opts.recentStderr) || looksLike1mContextCreditsError(opts.finalText);
38550
- }
38551
-
38552
- // src/agents/acp/agent-hooks.ts
38553
- function replyIsCursorUpgradeRequired(finalText) {
38554
- const t2 = finalText.trim().toLowerCase();
38555
- if (t2.length === 0 || t2.length > 200) return false;
38556
- return t2.includes("upgrade your plan to continue") || t2.includes("upgrade your plan") && t2.includes("continue");
38557
- }
38558
- function isGeminiIneligibleTier(haystack2) {
38559
- return /IneligibleTierError|UNSUPPORTED_CLIENT|no longer supported for Gemini Code Assist|not eligible for Gemini Code Assist/i.test(
38560
- haystack2
38561
- );
38562
- }
38563
- var ACP_AGENT_HOOKS = {
38564
- claude: {
38565
- statusPage: { vendor: "Anthropic", url: "https://status.anthropic.com" },
38566
- // Freeze the SDK-bundled Claude Code binary at the version this CLI (and its
38567
- // pinned ACP adapter) was tested against. Without this the binary silently
38568
- // self-updates to @latest and can drift PAST the adapter — a newer Claude
38569
- // Code (v2.1.211) started streaming its TUI chrome (What's-new banner, status
38570
- // line, box-drawing) into the ACP text channel, which paints as a broken chat
38571
- // on mobile. A version regression must now reach users only through a gated
38572
- // codeam-cli release, never silently via auto-update.
38573
- startupExtraEnv: () => ({
38574
- DISABLE_AUTOUPDATER: "1",
38575
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
38576
- })
38577
- },
38578
- codex: {
38579
- statusPage: { vendor: "OpenAI", url: "https://status.openai.com" },
38580
- startupExtraEnv: ({ autoApprovePermissions }) => autoApprovePermissions ? { INITIAL_AGENT_MODE: "agent-full-access" } : {}
38581
- },
38582
- gemini: {
38583
- statusPage: { vendor: "Google", url: "https://status.cloud.google.com" },
38584
- classifyStartupFailure: (haystack2) => isGeminiIneligibleTier(haystack2) ? "ineligible_tier" : null
38585
- },
38586
- copilot: {
38587
- statusPage: { vendor: "GitHub", url: "https://www.githubstatus.com" }
38588
- },
38589
- cursor: {
38590
- statusPage: { vendor: "Cursor", url: "https://status.cursor.com" },
38591
- classifyCompletedReply: (finalText) => replyIsCursorUpgradeRequired(finalText) ? "upgrade_required" : null
38592
- }
38593
- };
38594
- function agentHooks(agent) {
38595
- return ACP_AGENT_HOOKS[agent];
38596
- }
38597
-
38598
- // src/agents/acp/failure-messages.ts
38599
- function describeError(err) {
38600
- if (err instanceof Error) return err.message;
38601
- return String(err);
38602
- }
38603
- var AUTH_FAILURE_RE = /invalid authentication credentials|failed to authenticate|authentication[_ ](?:error|required)|please run \/login|\bunauthorized\b|\binvalid x-api-key\b|oauth (?:token|session) (?:expired|revoked)|(?:session|token|credentials?|oauth)[^\n]{0,40}could not be refreshed|(?:api error|http|status)[:\s]+401|\b401\b[^\n]{0,40}(?:unauthor|authenticat|credential|api[_ ]?key|login)/i;
38604
- var HOUSE_AGENT_LIMIT_RE = /HOUSE_AGENT_CEILING|CodeAgent Cloud[^\n]{0,60}(?:usage )?ceiling|CodeAgent Cloud is temporarily (?:unavailable|disabled)/i;
38605
- function looksLikeHouseAgentLimit(text) {
38606
- return HOUSE_AGENT_LIMIT_RE.test(text);
38607
- }
38608
- var LOCAL_PROXY_RE = /(?:127\.0\.0\.1|localhost|\[::1\]):\d+/i;
38609
- var CONNECTION_ERROR_RE = /ECONNREFUSED|ECONNRESET|ETIMEDOUT|EHOSTUNREACH|socket hang up|fetch failed|network error|connection (?:refused|reset|closed)/i;
38610
- function looksLikeLocalProxyUnavailable(text) {
38611
- return LOCAL_PROXY_RE.test(text) && CONNECTION_ERROR_RE.test(text);
38612
- }
38613
- function looksLikeAuthFailure(text) {
38614
- if (looksLikeHouseAgentLimit(text)) return false;
38615
- if (looksLikeLocalProxyUnavailable(text)) return false;
38616
- return AUTH_FAILURE_RE.test(text);
38617
- }
38618
- function replyIsAuthFailure(finalText) {
38619
- const t2 = finalText.trim();
38620
- return t2.length > 0 && t2.length <= 200 && looksLikeAuthFailure(t2);
38621
- }
38622
- function replyIsHouseAgentLimit(finalText) {
38623
- const t2 = finalText.trim();
38624
- return t2.length > 0 && t2.length <= 400 && looksLikeHouseAgentLimit(t2);
38625
- }
38626
- var BYO_PROVIDER_BILLING_RE = /(?:api error|http|status)[:\s]+402\b[^\n]{0,80}(?:insufficient (?:credits?|balance)|requires more credits)|\b402\b[^\n]{0,40}(?:insufficient (?:credits?|balance)|requires more credits)|insufficient (?:credits?|balance)[^\n]{0,40}\b402\b/i;
38627
- function looksLikeByoProviderBilling(text, env = process.env) {
38628
- return !isHouseProxyEnv(env) && BYO_PROVIDER_BILLING_RE.test(text);
38629
- }
38630
- function replyIsByoProviderBilling(finalText, env = process.env) {
38631
- const t2 = finalText.trim();
38632
- return t2.length > 0 && t2.length <= 600 && looksLikeByoProviderBilling(t2, env);
38633
- }
38634
- var PROVIDER_HOST_NAMES = [
38635
- [/(^|\.)openrouter\.ai$/i, "OpenRouter"],
38636
- [/(^|\.)deepinfra\.com$/i, "DeepInfra"],
38637
- [/(^|\.)anthropic\.com$/i, "Anthropic"],
38638
- [/(^|\.)openai\.com$/i, "OpenAI"],
38639
- [/(^|\.)googleapis\.com$/i, "Google"],
38640
- [/(^|\.)minimax(?:i)?\.(?:io|com|chat)$/i, "MiniMax"],
38641
- [/(^|\.)moonshot\.(?:ai|cn)$/i, "Moonshot"],
38642
- [/(^|\.)x\.ai$/i, "xAI"],
38643
- [/(^|\.)mistral\.ai$/i, "Mistral"],
38644
- [/(^|\.)groq\.com$/i, "Groq"],
38645
- [/(^|\.)together\.(?:ai|xyz)$/i, "Together"]
38646
- ];
38647
- var AGENT_DEFAULT_PROVIDER = {
38648
- claude: "Anthropic",
38649
- codex: "OpenAI",
38650
- gemini: "Google"
38651
- };
38652
- function byoProviderName(opts) {
38653
- const env = opts.env ?? process.env;
38654
- for (const key of PROVIDER_BASE_URL_ENV_KEYS) {
38655
- const origin = redactUrlToOrigin(env[key]);
38656
- if (!origin || origin === "invalid-url") continue;
38657
- const host2 = new URL(origin).hostname;
38658
- for (const [re2, name] of PROVIDER_HOST_NAMES) if (re2.test(host2)) return name;
38659
- return host2;
38660
- }
38661
- return opts.agent && AGENT_DEFAULT_PROVIDER[opts.agent] || "provider";
38662
- }
38663
- function byoProviderBillingMessage(provider) {
38664
- return `\u{1F4B3} **Your ${provider} account has no credits left.** Top up at your provider, or switch this session to another agent.`;
38665
- }
38666
- function completedReplyFailureBubble(opts) {
38667
- const env = opts.env ?? process.env;
38668
- if (replyIsHouseAgentLimit(opts.finalText)) return houseAgentLimitMessage(opts.finalText);
38669
- if (replyIsByoProviderBilling(opts.finalText, env)) {
38670
- return byoProviderBillingMessage(byoProviderName({ env, agent: opts.agent }));
38671
- }
38672
- if (replyIsAuthFailure(opts.finalText)) return AUTH_FAILURE_MESSAGE;
38673
- return null;
38674
- }
38675
- function houseAgentLimitMessage(text) {
38676
- if (/temporarily (?:unavailable|disabled)/i.test(text)) {
38677
- return "\u23F3 **Our agent service is temporarily unavailable \u2014 this one\u2019s on us, not your account.**\n\nThere\u2019s nothing to buy and nothing owed, and it isn\u2019t a problem with your login, so re-authenticating won\u2019t help. Send your message again in a bit, or connect your own agent (Claude Code, Codex, Cursor, Gemini) in **Profile \u203A Agents** to keep going now.";
38678
- }
38679
- const canUpgrade = /upgrade to pro/i.test(text);
38680
- return "\u{1F4CA} **You\u2019ve reached today\u2019s agent usage limit.**\n\nThe free agent has a daily usage ceiling that resets at midnight UTC. This is a usage limit, not a problem with your login \u2014 re-authenticating won\u2019t change it. " + (canUpgrade ? "To keep going now, upgrade to **Pro** for a higher ceiling, or connect your own agent (Claude, Codex, \u2026) in **Profile \u203A Agents** to run without this limit." : "To keep going now, connect your own agent (Claude, Codex, \u2026) in **Profile \u203A Agents** to run without this limit, or wait for the reset.");
38681
- }
38682
- var AUTH_FAILURE_MESSAGE = (
38683
- // ⚠️ Do NOT name a status code here. This bubble fires for several
38684
- // different failures, and the old hard-coded "(API 401)" told users the API
38685
- // had answered 401 when it never had — it also sent a live investigation
38686
- // hunting for 401s in proxy logs that were serving 200s throughout
38687
- // (2026-08-24). Say what happened, not a number we did not see.
38688
- "\u{1F512} **Authentication failed \u2014 your agent credentials are invalid or expired.**\n\nTap [Re-authenticate this agent](codeam://reauth) to renew your credentials in Profile \u203A Agents, then send your message again."
38689
- );
38690
- var CURSOR_UPGRADE_MESSAGE = "\u26A1 **Cursor needs a paid plan to run the agent.**\n\nThe headless Cursor Agent requires Cursor **Pro** \u2014 your Free plan\u2019s included usage does NOT cover Agent runs, even with quota left. This is your Cursor account (not CodeAgent). Upgrade, then send your message again:\n\n[Upgrade to Cursor Pro \u2192](https://cursor.com/dashboard)";
38691
- var ONE_M_CREDITS_MESSAGE = "\u{1F504} **Reconnect your Claude subscription to continue.**\n\nClaude requested 1M-context but your account doesn\u2019t have the usage credits for it on this credential. Reconnecting refreshes your subscription so the agent can keep going \u2014 disabling 1M context won\u2019t fix a credits gate.\n\nTap [Reconnect this agent](codeam://reauth) to reconnect your Claude subscription in Profile \u203A Agents, then send your message again.";
38692
- var TURN_FAILURE_MESSAGE = "\u26A0\uFE0F **The agent hit an error and couldn\u2019t finish this turn.** Please send your message again.";
38693
- function emptyReplyMessage(agent) {
38694
- const label = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].displayName : agent;
38695
- const binary = isKnownAgentId(agent) ? AGENT_REGISTRY[agent].binaryName : agent;
38696
- return `\u26A0\uFE0F **${label} returned an empty reply.** Its provider may be having issues \u2014 check the agent's login/subscription on this machine (e.g. run \`${binary} -p "test"\` there).`;
38697
- }
38698
- var PROVIDER_OUTAGE_RE = /overloaded_error|\boverloaded\b|service[ _]unavailable|temporarily[ _]unavailable|(?:api error|http|status)[:\s]+(?:529|503|502|504)\b|\b(?:529|503|502|504)\b[^\n]{0,40}(?:overload|unavailable|gateway|upstream|server error)|bad gateway|gateway time-?out|upstream (?:error|connect|timeout)/i;
38699
- function looksLikeProviderOutage(text) {
38700
- return PROVIDER_OUTAGE_RE.test(text);
38701
- }
38702
- function agentStatusPage(agent, railWireId) {
38703
- if (railWireId) return null;
38704
- const a = (agent ?? "").toLowerCase();
38705
- const id = a.includes("claude") || a.includes("anthropic") ? "claude" : a.includes("codex") || a.includes("openai") ? "codex" : a.includes("gemini") || a.includes("google") ? "gemini" : a.includes("copilot") ? "copilot" : a.includes("cursor") ? "cursor" : null;
38706
- return id ? agentHooks(id)?.statusPage ?? null : null;
38707
- }
38708
- function providerOutageMessage(agent, railWireId) {
38709
- if (railWireId) {
38710
- const who2 = isManagedProviderId(railWireId) ? `${MANAGED_PROVIDER_DISPLAY_NAMES[railWireId]} ` : "";
38711
- return `\u{1F310} **CodeAgent's agent service is having a disruption \u2014 this isn't your session.**
38712
-
38713
- Your ${who2}agent couldn't finish this turn because our provider returned an overload error. It usually clears up on its own \u2014 just send your message again in a bit.`;
38714
- }
38715
- const info = agentStatusPage(agent);
38716
- const who = info ? info.vendor : "The agent provider";
38717
- return `\u{1F310} **${who} is having a service disruption \u2014 this isn\u2019t your session.**
38718
-
38719
- The agent provider returned an overload/outage error, so this turn couldn\u2019t finish. It usually clears up on its own \u2014 just send your message again in a bit.` + (info ? `
38720
-
38721
- Follow the status here: [${info.url}](${info.url})` : "");
38722
- }
38723
- function startupFailureMessage(agent, detail, recentStderr, railWireId) {
38724
- const haystack2 = `${detail}
38725
- ${recentStderr}`;
38726
- if (agentHooks(agent)?.classifyStartupFailure?.(haystack2) === "ineligible_tier") {
38727
- return [
38728
- "\u26A0\uFE0F **Gemini couldn't start \u2014 your Google account isn't eligible.**",
38729
- "",
38730
- "Google discontinued free **\u201CLogin with Google\u201D** access to the Gemini CLI on 2026-06-18. The free / individual tier (and Google AI Pro/Ultra) can no longer authenticate here.",
38731
- "",
38732
- "To use Gemini from CodeAgent, re-link it in **Profile \u203A Agents** with either:",
38733
- "\u2022 a **Gemini API key** (AI Studio \u2014 free quota available), or",
38734
- "\u2022 a paid **Gemini Code Assist (Standard/Enterprise)** subscription."
38735
- ].join("\n");
38736
- }
38737
- if (looksLikeAuthFailure(haystack2)) return AUTH_FAILURE_MESSAGE;
38738
- if (looksLikeProviderOutage(haystack2)) return providerOutageMessage(agent, railWireId);
38739
- const tail = recentStderr.split("\n").filter(Boolean).slice(-3).join("\n");
38740
- return [`\u26A0\uFE0F The ${agent} agent failed to start.`, "", tail ? `Details:
38741
- ${tail}` : detail].join(
38742
- "\n"
38743
- );
38744
- }
38745
- function startupCredentialInvalidReason(agent, detail, recentStderr) {
38746
- const haystack2 = `${detail}
38747
- ${recentStderr}`;
38748
- return agentHooks(agent)?.classifyStartupFailure?.(haystack2) === "ineligible_tier" ? "ineligible_tier" : null;
38749
- }
38750
- function failureBubble(opts) {
38751
- const env = opts.env ?? process.env;
38752
- if (looksLikeHouseAgentLimit(opts.detail) || looksLikeHouseAgentLimit(opts.recentStderr)) {
38753
- return houseAgentLimitMessage(`${opts.detail}
38754
- ${opts.recentStderr}`);
38755
- }
38756
- if (looksLikeByoProviderBilling(opts.detail, env) || looksLikeByoProviderBilling(opts.recentStderr, env)) {
38757
- return byoProviderBillingMessage(byoProviderName({ env, agent: opts.agent }));
38758
- }
38759
- if (looksLikeAuthFailure(opts.detail) || looksLikeAuthFailure(opts.recentStderr)) {
38760
- return AUTH_FAILURE_MESSAGE;
38761
- }
38762
- if (looksLike1mContextCreditsError(opts.detail) || looksLike1mContextCreditsError(opts.recentStderr)) {
38763
- return ONE_M_CREDITS_MESSAGE;
38764
- }
38765
- if (looksLikeProviderOutage(opts.detail) || looksLikeProviderOutage(opts.recentStderr)) {
38766
- return providerOutageMessage(opts.agent, opts.railWireId);
38767
- }
38768
- if (!opts.hadText) return TURN_FAILURE_MESSAGE;
38769
- return null;
38770
- }
38771
- var WINDOWS_CONTROL_C_EXIT = 3221225786;
38772
- function adapterExitMessage(opts) {
38773
- if (opts.code === WINDOWS_CONTROL_C_EXIT) return null;
38774
- if (opts.signal === "SIGINT") return null;
38775
- if (opts.authFail) return AUTH_FAILURE_MESSAGE;
38776
- if (opts.outageFail) return providerOutageMessage(opts.agent, opts.railWireId);
38777
- return `Agent adapter exited unexpectedly (code=${opts.code ?? "null"} signal=${opts.signal ?? "null"}).`;
38778
- }
38779
-
38780
38801
  // src/agents/acp/backend-reports.ts
38781
38802
  async function reportCredentialInvalid(opts, fetchImpl = fetch) {
38782
38803
  const url2 = `${resolveApiBaseUrl()}/api/plugin/agents/${encodeURIComponent(opts.agent)}/credential-invalid`;
@@ -48963,7 +48984,7 @@ async function autoUpgradeBeforeCriticalCommand() {
48963
48984
  if (process.env.NODE_ENV === "test") return;
48964
48985
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48965
48986
  if (process.env.CI) return;
48966
- const current2 = true ? "2.75.45" : null;
48987
+ const current2 = true ? "2.75.47" : null;
48967
48988
  if (!current2) return;
48968
48989
  const cache = readCache();
48969
48990
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -48980,7 +49001,7 @@ function checkForUpdates() {
48980
49001
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
48981
49002
  if (process.env.CI) return;
48982
49003
  if (!process.stdout.isTTY) return;
48983
- const current2 = true ? "2.75.45" : null;
49004
+ const current2 = true ? "2.75.47" : null;
48984
49005
  if (!current2) return;
48985
49006
  const cache = readCache();
48986
49007
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -49003,7 +49024,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
49003
49024
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
49004
49025
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
49005
49026
  function currentCliVersion() {
49006
- return true ? "2.75.45" : null;
49027
+ return true ? "2.75.47" : null;
49007
49028
  }
49008
49029
  async function installedVersion(deps) {
49009
49030
  const ls = await deps.run(
@@ -51058,7 +51079,7 @@ function checkChokidar() {
51058
51079
  }
51059
51080
  async function doctor(args2 = []) {
51060
51081
  const json = args2.includes("--json");
51061
- const cliVersion = true ? "2.75.45" : "0.0.0-dev";
51082
+ const cliVersion = true ? "2.75.47" : "0.0.0-dev";
51062
51083
  const apiBase2 = resolveApiBaseUrl();
51063
51084
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
51064
51085
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -51582,7 +51603,7 @@ async function mcpWarm(argv = [], opts = {}) {
51582
51603
  // src/commands/version.ts
51583
51604
  var import_picocolors15 = __toESM(require("picocolors"));
51584
51605
  function version2() {
51585
- const v = true ? "2.75.45" : "unknown";
51606
+ const v = true ? "2.75.47" : "unknown";
51586
51607
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
51587
51608
  }
51588
51609