openmausbot 0.1.92 → 0.1.93

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 (104) hide show
  1. package/dist/assets/{DesktopViewer-Do6lBa4C.js → DesktopViewer-CKNR3SEW.js} +1 -1
  2. package/dist/assets/{abnfDiagram-O67JEVCF-Cd3X7Hxc.js → abnfDiagram-O67JEVCF-Ckrm1I1X.js} +1 -1
  3. package/dist/assets/{arc-DIYc7Ymx.js → arc-BBdBuCgB.js} +1 -1
  4. package/dist/assets/{architectureDiagram-NJMV4G6O-DBmRGVyP.js → architectureDiagram-NJMV4G6O-CDdA3Hjs.js} +1 -1
  5. package/dist/assets/{blockDiagram-BEXU5L5S-CPFePSEE.js → blockDiagram-BEXU5L5S-AUrsx1YR.js} +1 -1
  6. package/dist/assets/{c4Diagram-YGBWAQC7-DD_DO1qy.js → c4Diagram-YGBWAQC7-BEqQ0Unh.js} +1 -1
  7. package/dist/assets/channel-BU94VpxE.js +1 -0
  8. package/dist/assets/{chunk-2Q5K7J3B-BNEeNSjX.js → chunk-2Q5K7J3B-CEEuvd4C.js} +1 -1
  9. package/dist/assets/{chunk-3FUC2YCW-BbinLaqH.js → chunk-3FUC2YCW-DyANCiHl.js} +2 -2
  10. package/dist/assets/{chunk-5VM5RSS4-BBgYrgDR.js → chunk-5VM5RSS4-Dx_Slpw9.js} +1 -1
  11. package/dist/assets/{chunk-GTNCS2PH-ezXj-kA-.js → chunk-GTNCS2PH-Dl7h7iiD.js} +1 -1
  12. package/dist/assets/{chunk-GWA4HPMP-9FxZ3diV.js → chunk-GWA4HPMP-BLx07SPb.js} +1 -1
  13. package/dist/assets/{chunk-JWPE2WC7-CO-40JpI.js → chunk-JWPE2WC7-BMxDj3n7.js} +1 -1
  14. package/dist/assets/{chunk-NETBCI7D-BEhjslYE.js → chunk-NETBCI7D-CN66PT4H.js} +1 -1
  15. package/dist/assets/{chunk-WEXAMYUT-CNeq4n7_.js → chunk-WEXAMYUT-6HwhsDKw.js} +1 -1
  16. package/dist/assets/{chunk-XXDRQBXY-OsCY7KSi.js → chunk-XXDRQBXY-gWnLt7SN.js} +1 -1
  17. package/dist/assets/{classDiagram-v2-NBCMYWYE-CloRUEtk.js → classDiagram-v2-NBCMYWYE-CMS5l2ea.js} +1 -1
  18. package/dist/assets/{cose-bilkent-JH36ORCC-D1BGZhp4.js → cose-bilkent-JH36ORCC-CV7sQwIG.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-CUf4T2CN.js → cynefin-OW5HDTMX-CSyl-7vl.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-VND7K2PF-DPEZrFGA.js → cynefinDiagram-VND7K2PF-BCLKL6az.js} +1 -1
  21. package/dist/assets/{dagre-6A5THRUB-bDe6GIv5.js → dagre-6A5THRUB-DiaZs6S1.js} +1 -1
  22. package/dist/assets/{diagram-22UHCM2B-CdcUQz55.js → diagram-22UHCM2B-BeTtmt3I.js} +1 -1
  23. package/dist/assets/{diagram-3UASUU5V-BZpqGg7c.js → diagram-3UASUU5V-CsdBG-QQ.js} +1 -1
  24. package/dist/assets/{diagram-ATOU4E4O-DvGM6ugp.js → diagram-ATOU4E4O-BczvdbR1.js} +1 -1
  25. package/dist/assets/{diagram-CDSNMT55-DSTmMm0s.js → diagram-CDSNMT55-DHTTTMhi.js} +1 -1
  26. package/dist/assets/{diagram-MLGK6HIB-15cucnMn.js → diagram-MLGK6HIB-RTjHyd8z.js} +1 -1
  27. package/dist/assets/{diagram-MPIPVDR6-tMbuaE6n.js → diagram-MPIPVDR6-BDenHEH_.js} +1 -1
  28. package/dist/assets/{ebnfDiagram-ZINNZB2B-BYxYzjHI.js → ebnfDiagram-ZINNZB2B-BSiRSbtL.js} +1 -1
  29. package/dist/assets/{elk-276RUBZZ-v6bj852W.js → elk-276RUBZZ-BCAElss-.js} +1 -1
  30. package/dist/assets/{erDiagram-OPXOYQCR-5lBBlnd2.js → erDiagram-OPXOYQCR-C4_1ZPJe.js} +1 -1
  31. package/dist/assets/{flowDiagram-KWPJA3E3-CZyl1zIj.js → flowDiagram-KWPJA3E3-Bcb4Z75S.js} +1 -1
  32. package/dist/assets/{ganttDiagram-FUAMR5RP-BiAI-ltZ.js → ganttDiagram-FUAMR5RP-BiPOzxzb.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-X574FWY7-BAZISkj3.js → gitGraphDiagram-X574FWY7-B1wtF3ZS.js} +1 -1
  34. package/dist/assets/index-Biji-bfL.css +1 -0
  35. package/dist/assets/{index-DGBaEXrM.js → index-BkYwilkG.js} +1 -1
  36. package/dist/assets/index-DZlhoES0.js +597 -0
  37. package/dist/assets/{infoDiagram-VRGFBTTK-B6ooOy-Z.js → infoDiagram-VRGFBTTK-A19No71U.js} +1 -1
  38. package/dist/assets/{ishikawaDiagram-OU5B5YK6-BZ9ksi-B.js → ishikawaDiagram-OU5B5YK6-BQsqJ_cV.js} +1 -1
  39. package/dist/assets/{journeyDiagram-ZHPQQLJL-DWqlzIEe.js → journeyDiagram-ZHPQQLJL-DAO0pVan.js} +1 -1
  40. package/dist/assets/{kanban-definition-PNTS6WVX-JX6piDwL.js → kanban-definition-PNTS6WVX-NepmKEe2.js} +1 -1
  41. package/dist/assets/{linear-CmOWzX0P.js → linear-bq1PTDTW.js} +1 -1
  42. package/dist/assets/{mermaid.core-CF1zwoQ-.js → mermaid.core-s4LTbQle.js} +5 -5
  43. package/dist/assets/{mindmap-definition-NLK3R4M7-CGh2K4fW.js → mindmap-definition-NLK3R4M7-LtdbgHFH.js} +1 -1
  44. package/dist/assets/{pegDiagram-GJSIUBJH-CDwYN0s7.js → pegDiagram-GJSIUBJH-tdbaCxiK.js} +1 -1
  45. package/dist/assets/{pieDiagram-5QR66LMP-1rfjJocB.js → pieDiagram-5QR66LMP-DvkbpW8l.js} +1 -1
  46. package/dist/assets/{quadrantDiagram-O4NWA36T-tKPvPiok.js → quadrantDiagram-O4NWA36T-Do4KaH-E.js} +1 -1
  47. package/dist/assets/{railroadDiagram-XR7U4H2S-BHLm5vXR.js → railroadDiagram-XR7U4H2S-BQWULOAs.js} +1 -1
  48. package/dist/assets/{requirementDiagram-PLB6GJNP-CB45STp7.js → requirementDiagram-PLB6GJNP-w9kei1FU.js} +1 -1
  49. package/dist/assets/{sankeyDiagram-IPEJSGJF-55QA_huC.js → sankeyDiagram-IPEJSGJF-D2gRy0e8.js} +1 -1
  50. package/dist/assets/{sequenceDiagram-PO4LG4MO-BckFmQHn.js → sequenceDiagram-PO4LG4MO-a2sO5_Eo.js} +1 -1
  51. package/dist/assets/{sizeCapture-INFHLROL-BFCOJF11.js → sizeCapture-INFHLROL-CiPJmFEB.js} +1 -1
  52. package/dist/assets/{stateDiagram-v2-GCMORJYK-CZvmiVVN.js → stateDiagram-v2-GCMORJYK-DAkUUlL7.js} +1 -1
  53. package/dist/assets/{swimlanes-2SLR337P-BpDUCEKy.js → swimlanes-2SLR337P-DANf2R8Y.js} +1 -1
  54. package/dist/assets/swimlanesDiagram-TC7HE7FX-CdHxG8aM.js +8 -0
  55. package/dist/assets/{timeline-definition-EJHVYXUP-BpxHq6IK.js → timeline-definition-EJHVYXUP-BKpQmzrT.js} +1 -1
  56. package/dist/assets/{usecaseDiagram-POWQR4AR-Cj66vm02.js → usecaseDiagram-POWQR4AR-BtI7qLN-.js} +1 -1
  57. package/dist/assets/{vennDiagram-UO4OBE2U-C9nRkWE_.js → vennDiagram-UO4OBE2U-gXJXrAG_.js} +1 -1
  58. package/dist/assets/{wardleyDiagram-VNRHLVJA-D-me_nWj.js → wardleyDiagram-VNRHLVJA-GU8Po5-n.js} +1 -1
  59. package/dist/assets/{xychartDiagram-PMCCYNJV-Dc_fL4mk.js → xychartDiagram-PMCCYNJV-BF4J30g6.js} +1 -1
  60. package/dist/index.html +2 -2
  61. package/dist-server/companion/src/routes.js +24 -1
  62. package/dist-server/container-mcp.js +6 -1
  63. package/dist-server/drivers/agents-proxy.js +2 -0
  64. package/dist-server/index.js +1722 -906
  65. package/dist-server/local-computer-proxy.js +5 -1
  66. package/dist-server/mcp-gate.js +5 -1
  67. package/dist-server/openmausbot.js +763 -93
  68. package/dist-server/pair-cli.js +763 -93
  69. package/dist-server/server/boat.js +3 -3
  70. package/dist-server/server/cloud-owner.js +1 -1
  71. package/dist-server/server/computer-control.js +6 -0
  72. package/dist-server/server/config.js +14 -1
  73. package/dist-server/server/container-computer.js +43 -32
  74. package/dist-server/server/digest.js +12 -2
  75. package/dist-server/server/drivers/acp/core.js +172 -12
  76. package/dist-server/server/drivers/acp/cursor.js +13 -1
  77. package/dist-server/server/drivers/acp/quiet-status.js +257 -0
  78. package/dist-server/server/drivers/acp/qwen.js +18 -0
  79. package/dist-server/server/drivers/builtIn.js +2 -0
  80. package/dist-server/server/drivers/cerebras.js +108 -0
  81. package/dist-server/server/drivers/chat-mcp-tools.js +20 -3
  82. package/dist-server/server/drivers/claude.js +17 -3
  83. package/dist-server/server/drivers/openai-chat.js +26 -2
  84. package/dist-server/server/drivers/prompt-split.js +2 -0
  85. package/dist-server/server/env-path.js +6 -2
  86. package/dist-server/server/index.js +210 -57
  87. package/dist-server/server/local-vm-stop-reason.js +26 -0
  88. package/dist-server/server/provider-key-check.js +2 -1
  89. package/dist-server/server/routes/desktop-viewer.js +50 -6
  90. package/dist-server/server/routines.js +6 -3
  91. package/dist-server/server/store.js +54 -0
  92. package/dist-server/server/vps-computer.js +1 -1
  93. package/dist-server/server/workspace-backup.js +22 -0
  94. package/dist-server/server/workspace.js +1 -1
  95. package/dist-server/shared/approval-mode.js +1 -1
  96. package/dist-server/shared/redact.js +1 -0
  97. package/dist-server/shared/tool-name.js +22 -0
  98. package/dist-server/vps-container-mcp.js +6 -1
  99. package/dist-server/workspace-backup.worker.js +14 -0
  100. package/package.json +1 -1
  101. package/dist/assets/channel-Dtk5Mtic.js +0 -1
  102. package/dist/assets/index-Cdmjrl28.css +0 -1
  103. package/dist/assets/index-DR7YPMkW.js +0 -597
  104. package/dist/assets/swimlanesDiagram-TC7HE7FX-M0GYW9Y3.js +0 -8
@@ -1140,6 +1140,8 @@ var init_redact = __esm({
1140
1140
  // xai (grok)
1141
1141
  /\bgsk_[A-Za-z0-9]{40,}/g,
1142
1142
  // groq
1143
+ /\bcsk-[A-Za-z0-9]{32,}/g,
1144
+ // cerebras
1143
1145
  /\bhf_[A-Za-z0-9]{30,}/g,
1144
1146
  // hugging face
1145
1147
  /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g,
@@ -17575,7 +17577,7 @@ function defaultSetupIo(input = process.stdin, output = process.stdout) {
17575
17577
  `);
17576
17578
  };
17577
17579
  const rich = () => output.isTTY === true && process.env.TERM !== "dumb" && process.env.NO_COLOR === void 0 && (output.columns ?? 80) >= 30 && (output.rows ?? 24) >= 8;
17578
- const run2 = (prompt) => {
17580
+ const run3 = (prompt) => {
17579
17581
  if (!input.isTTY || !input.setRawMode) throw new Error("Setup needs an interactive terminal.");
17580
17582
  const raw = input.isRaw === true;
17581
17583
  const flowing = input.readableFlowing === true;
@@ -17636,14 +17638,14 @@ function defaultSetupIo(input = process.stdin, output = process.stdout) {
17636
17638
  }
17637
17639
  })();
17638
17640
  };
17639
- const ask = (question) => run2((context) => rich() ? text2({ ...context, message: displayText(question) }) : plainText(displayText(question), false, context));
17640
- const secret = (question) => run2((context) => rich() ? password({ ...context, message: displayText(question), mask: "*" }) : plainText(displayText(question), true, context));
17641
+ const ask = (question) => run3((context) => rich() ? text2({ ...context, message: displayText(question) }) : plainText(displayText(question), false, context));
17642
+ const secret = (question) => run3((context) => rich() ? password({ ...context, message: displayText(question), mask: "*" }) : plainText(displayText(question), true, context));
17641
17643
  const choose = async (question, options, defaultIndex = 0) => {
17642
17644
  if (!options.length) throw new Error("There are no choices available.");
17643
17645
  if (!Number.isInteger(defaultIndex) || defaultIndex < 0 || defaultIndex >= options.length) {
17644
17646
  throw new Error("The default choice is not available.");
17645
17647
  }
17646
- if (rich()) return run2((context) => select({
17648
+ if (rich()) return run3((context) => select({
17647
17649
  ...context,
17648
17650
  message: displayText(question),
17649
17651
  options: options.map((label, value) => ({ value, label: displayText(label) })),
@@ -17671,7 +17673,7 @@ function defaultSetupIo(input = process.stdin, output = process.stdout) {
17671
17673
  }
17672
17674
  };
17673
17675
  const confirm2 = async (question, defaultYes = false) => {
17674
- if (rich()) return run2((context) => confirm({ ...context, message: displayText(question), initialValue: defaultYes }));
17676
+ if (rich()) return run3((context) => confirm({ ...context, message: displayText(question), initialValue: defaultYes }));
17675
17677
  for (; ; ) {
17676
17678
  const answer = (await ask(`${question} ${defaultYes ? "[Y/n]" : "[y/N]"}: `)).trim();
17677
17679
  if (!answer) return defaultYes;
@@ -18627,8 +18629,8 @@ var init_new_bot_defaults = __esm({
18627
18629
  }
18628
18630
  }
18629
18631
  if (value.type === "interval" && value.window) {
18630
- const minutes = (time3) => Number(time3.slice(0, 2)) * 60 + Number(time3.slice(3));
18631
- if (minutes(value.window.end) - minutes(value.window.start) < value.everyMinutes) {
18632
+ const minutes2 = (time3) => Number(time3.slice(0, 2)) * 60 + Number(time3.slice(3));
18633
+ if (minutes2(value.window.end) - minutes2(value.window.start) < value.everyMinutes) {
18632
18634
  ctx.addIssue({ code: "custom", message: "The interval must fit inside its time window" });
18633
18635
  }
18634
18636
  }
@@ -19078,6 +19080,8 @@ function loadConfig() {
19078
19080
  }
19079
19081
  cfg.mistral = { ...cfg.mistral };
19080
19082
  if (process.env.MISTRAL_API_KEY !== void 0) cfg.mistral.key = process.env.MISTRAL_API_KEY;
19083
+ cfg.cerebras = { ...cfg.cerebras };
19084
+ if (process.env.CEREBRAS_API_KEY !== void 0) cfg.cerebras.key = process.env.CEREBRAS_API_KEY;
19081
19085
  cfg.xai = { ...cfg.xai };
19082
19086
  if (process.env.XAI_API_KEY !== void 0) cfg.xai.key = process.env.XAI_API_KEY;
19083
19087
  cfg.anthropic = { ...cfg.anthropic };
@@ -19138,7 +19142,7 @@ function saveConfig(patch, options = {}) {
19138
19142
  const before = configSaveListeners.size ? structuredClone(disk) : disk;
19139
19143
  const storedProfiles = storedBrowserProfilesSchema.safeParse(disk.browserProfiles);
19140
19144
  if (storedProfiles.success) disk.browserProfiles = storedProfiles.data;
19141
- for (const key of ["xai", "anthropic", "mistral", "openai", "openrouter", "openaiCompat", "composio", "box", "opencodeGo", "tts", "decider", "imageGen", "profile", "rooms", "threads", "context", "memory", "localVm", "features", "cloudOverflow", "budgets", "billing", "decisions", "onboarding", "browserEngine", "newBots"]) {
19145
+ for (const key of ["xai", "anthropic", "mistral", "cerebras", "openai", "openrouter", "openaiCompat", "composio", "box", "opencodeGo", "tts", "decider", "imageGen", "profile", "rooms", "threads", "context", "memory", "localVm", "features", "cloudOverflow", "budgets", "billing", "decisions", "onboarding", "browserEngine", "newBots"]) {
19142
19146
  const section = checkedPatch[key];
19143
19147
  if (!section) continue;
19144
19148
  const current = jsonObjectSchema.safeParse(disk[key]);
@@ -19251,6 +19255,8 @@ function instanceOwnsRouting(cfg, entry, routingDefaults) {
19251
19255
  return own(config2.key) || own(entry.environment?.OPENAI_COMPAT_API_KEY) || own(entry.environment?.[compatKeyEnv]) || own(config2.apiKeyEnv) && config2.apiKeyEnv !== compatKeyEnv || ownUrl(routingDefaults?.url || cfg.openaiCompat?.url || process.env.OPENAI_COMPAT_URL || "https://openrouter.ai/api/v1");
19252
19256
  case "mistral":
19253
19257
  return own(entry.environment?.MISTRAL_API_KEY) || ownUrl("https://api.mistral.ai/v1");
19258
+ case "cerebras":
19259
+ return own(entry.environment?.CEREBRAS_API_KEY) || ownUrl("https://api.cerebras.ai/v1");
19254
19260
  case "grok":
19255
19261
  return own(entry.environment?.XAI_API_KEY) || own(config2.apiKeyEnv) && config2.apiKeyEnv !== "XAI_API_KEY" || ownUrl("https://api.x.ai/v1", cfg.xai?.url);
19256
19262
  default:
@@ -19260,6 +19266,7 @@ function instanceOwnsRouting(cfg, entry, routingDefaults) {
19260
19266
  function injectedEnvironment(cfg, instanceId, driver) {
19261
19267
  const environment = /* @__PURE__ */ new Map();
19262
19268
  if (driver === "mistral" && cfg.mistral?.key) environment.set("MISTRAL_API_KEY", cfg.mistral.key);
19269
+ if (driver === "cerebras" && cfg.cerebras?.key) environment.set("CEREBRAS_API_KEY", cfg.cerebras.key);
19263
19270
  if (driver === "grok" && cfg.xai?.key) environment.set("XAI_API_KEY", cfg.xai.key);
19264
19271
  const anthropicHere = driver === "claudeAgent" && Boolean(cfg.anthropic?.key) && (cfg.anthropic?.everyClaudeBot === false ? instanceId === CLAUDE_API_INSTANCE : instanceId !== CLAUDE_API_INSTANCE);
19265
19272
  if (anthropicHere) environment.set("ANTHROPIC_API_KEY", cfg.anthropic.key);
@@ -19292,6 +19299,7 @@ function instanceConfigs(cfg) {
19292
19299
  computer: { driver: "boxAgent" },
19293
19300
  openaiCompat: { driver: "openai-compat" },
19294
19301
  mistral: { driver: "mistral" },
19302
+ cerebras: { driver: "cerebras" },
19295
19303
  ...API_KEY_FLEET,
19296
19304
  qwen: { driver: "qwenAgent" },
19297
19305
  hermes: { driver: "hermesAgent" },
@@ -19307,6 +19315,7 @@ function instanceConfigs(cfg) {
19307
19315
  cursor: { driver: "cursorAgent" },
19308
19316
  openaiCompat: { driver: "openai-compat" },
19309
19317
  mistral: { driver: "mistral" },
19318
+ cerebras: { driver: "cerebras" },
19310
19319
  ...API_KEY_FLEET,
19311
19320
  ...CUSTOM_ONLY
19312
19321
  };
@@ -19573,6 +19582,7 @@ var init_config = __esm({
19573
19582
  phone: external_exports.enum(["ios", "android"]).optional()
19574
19583
  }).optional(),
19575
19584
  mistral: external_exports.object({ key: optionalText }).optional(),
19585
+ cerebras: external_exports.object({ key: optionalText }).optional(),
19576
19586
  xai: external_exports.object({ key: optionalText, url: optionalText }).optional(),
19577
19587
  /** Anthropic API key for Claude Code billed per token, handed only to
19578
19588
  * Claude instances; `url` only for a proxy or a test double. Never a
@@ -19720,6 +19730,7 @@ var init_config = __esm({
19720
19730
  WORKSPACE_CREDENTIAL_ENV = [
19721
19731
  "XAI_API_KEY",
19722
19732
  "MISTRAL_API_KEY",
19733
+ "CEREBRAS_API_KEY",
19723
19734
  "OMB_ANTHROPIC_API_KEY",
19724
19735
  "OMB_ANTHROPIC_API_URL",
19725
19736
  "OMB_HOSTED_MODEL_TOKEN",
@@ -19763,6 +19774,7 @@ var init_config = __esm({
19763
19774
  "OPENCODE_API_KEY",
19764
19775
  "XAI_API_KEY",
19765
19776
  "MISTRAL_API_KEY",
19777
+ "CEREBRAS_API_KEY",
19766
19778
  "CURSOR_API_KEY",
19767
19779
  "CURSOR_AUTH_TOKEN"
19768
19780
  ];
@@ -20241,6 +20253,8 @@ function windowsKnownDirs() {
20241
20253
  // x.ai installer
20242
20254
  join11(localAppData, "agy", "bin"),
20243
20255
  // Antigravity installer
20256
+ // Cursor installer: cursor-agent.* and its `agent` copies (MOCA-272)
20257
+ join11(localAppData, "cursor-agent"),
20244
20258
  join11(home, ".local", "bin"),
20245
20259
  // claude native installer
20246
20260
  join11(home, ".claude", "local"),
@@ -20248,7 +20262,9 @@ function windowsKnownDirs() {
20248
20262
  // Factory droid installer (%USERPROFILE%\bin)
20249
20263
  join11(home, ".bun", "bin"),
20250
20264
  join11(home, ".deno", "bin"),
20251
- join11(home, "go", "bin")
20265
+ join11(home, "go", "bin"),
20266
+ join11(process.env.ProgramFiles ?? "C:\\Program Files", "Docker", "Docker", "resources", "bin")
20267
+ // Docker Desktop installer
20252
20268
  ];
20253
20269
  }
20254
20270
  function registerPathDir(dir) {
@@ -20518,12 +20534,12 @@ function killCliTree(child, timeoutMs = 5e3) {
20518
20534
  const pid = child.pid;
20519
20535
  if (!pid) return Promise.resolve(true);
20520
20536
  if (!Number.isInteger(pid) || pid <= 1 || pid === process.pid) return Promise.resolve(false);
20521
- const run2 = stopCliTree(child, pid, Number.isFinite(timeoutMs) ? Math.max(0, timeoutMs) : 5e3);
20522
- stopping.set(child, run2);
20523
- void run2.then((stopped) => {
20537
+ const run3 = stopCliTree(child, pid, Number.isFinite(timeoutMs) ? Math.max(0, timeoutMs) : 5e3);
20538
+ stopping.set(child, run3);
20539
+ void run3.then((stopped) => {
20524
20540
  if (!stopped) stopping.delete(child);
20525
20541
  });
20526
- return run2;
20542
+ return run3;
20527
20543
  }
20528
20544
  async function stopCliTree(child, pid, timeoutMs) {
20529
20545
  const exited = () => child.exitCode !== null || child.signalCode !== null;
@@ -20636,9 +20652,9 @@ function installNpmEngine(pkg, options = {}) {
20636
20652
  const key = `${options.baseDir ?? DATA_DIR} ${pkg}`;
20637
20653
  const existing = running.get(key);
20638
20654
  if (existing) return existing;
20639
- const run2 = installOnce(pkg, options).finally(() => running.delete(key));
20640
- running.set(key, run2);
20641
- return run2;
20655
+ const run3 = installOnce(pkg, options).finally(() => running.delete(key));
20656
+ running.set(key, run3);
20657
+ return run3;
20642
20658
  }
20643
20659
  async function installOnce(pkg, options) {
20644
20660
  const prefix = enginesPrefix(options.baseDir);
@@ -21307,7 +21323,9 @@ function readPromptSplitReceipt(scope, key) {
21307
21323
  return {
21308
21324
  stable: record3.stable,
21309
21325
  volatile: record3.volatile,
21310
- ...typeof record3.turnsSinceFull === "number" ? { turnsSinceFull: record3.turnsSinceFull } : {}
21326
+ ...typeof record3.turnsSinceFull === "number" ? { turnsSinceFull: record3.turnsSinceFull } : {},
21327
+ ...typeof record3.lastUsed === "number" && record3.lastUsed > 0 ? { lastUsed: record3.lastUsed } : {},
21328
+ ...typeof record3.peakUsed === "number" && record3.peakUsed > 0 ? { peakUsed: record3.peakUsed } : {}
21311
21329
  };
21312
21330
  }
21313
21331
  }
@@ -21349,6 +21367,205 @@ var init_prompt_split = __esm({
21349
21367
  }
21350
21368
  });
21351
21369
 
21370
+ // server/drivers/acp/quiet-status.ts
21371
+ import { execFile as execFile4 } from "node:child_process";
21372
+ import { closeSync as closeSync4, fstatSync, openSync as openSync4, readSync as readSync2 } from "node:fs";
21373
+ function parseQwenLogLine(line) {
21374
+ if (line.includes("[RETRY]") && /\bAttempt \d+ failed\b/.test(line)) {
21375
+ const attempt = /\bAttempt (\d+) failed/.exec(line);
21376
+ const status = /\bwith status (\d{3})\b/.exec(line);
21377
+ const explicit = /explicit delay of (\d+)\s*ms/.exec(line);
21378
+ const seconds = /Retrying in (\d+)\s*s\b/.exec(line);
21379
+ return {
21380
+ kind: "retry",
21381
+ attempt: attempt ? Number(attempt[1]) : null,
21382
+ status: status ? Number(status[1]) : null,
21383
+ delayMs: explicit ? Number(explicit[1]) : seconds ? Number(seconds[1]) * 1e3 : null
21384
+ };
21385
+ }
21386
+ if (/\[compaction\] hard-tier rescue triggered|Reactive compression|tryCompress/.test(line) && !/succeeded|failed|skipped|stopped/.test(line)) {
21387
+ return { kind: "compressing" };
21388
+ }
21389
+ const gate = /\[compaction\] cheap-gate NOOP: effectiveTokens=(\d+), auto=(\d+)/.exec(line);
21390
+ if (gate) return { kind: "context", tokens: Number(gate[1]), threshold: Number(gate[2]) };
21391
+ return null;
21392
+ }
21393
+ function psTimeMs(raw) {
21394
+ const m2 = /^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+(?:\.\d+)?)$/.exec(raw.trim());
21395
+ if (!m2) return null;
21396
+ const [, d, h2, min, s2] = m2;
21397
+ return ((Number(d ?? 0) * 24 + Number(h2 ?? 0)) * 60 + Number(min)) * 6e4 + Number(s2) * 1e3;
21398
+ }
21399
+ function treePids(rows, root) {
21400
+ const kids = /* @__PURE__ */ new Map();
21401
+ for (const r2 of rows) {
21402
+ const list = kids.get(r2.ppid);
21403
+ if (list) list.push(r2.pid);
21404
+ else kids.set(r2.ppid, [r2.pid]);
21405
+ }
21406
+ const out = [];
21407
+ const queue = [root];
21408
+ while (queue.length && out.length < 512) {
21409
+ const pid = queue.shift();
21410
+ if (out.includes(pid)) continue;
21411
+ out.push(pid);
21412
+ queue.push(...kids.get(pid) ?? []);
21413
+ }
21414
+ return out;
21415
+ }
21416
+ async function samplePosix(root) {
21417
+ const table2 = await run2("ps", ["-A", "-o", "pid=,ppid=,time="]);
21418
+ if (table2 === null) return { cpuMs: null, connections: null };
21419
+ const rows = [];
21420
+ for (const line of table2.split("\n")) {
21421
+ const [pid, ppid, time3] = line.trim().split(/\s+/);
21422
+ if (!pid || !ppid || !time3) continue;
21423
+ rows.push({ pid: Number(pid), ppid: Number(ppid), cpuMs: psTimeMs(time3) });
21424
+ }
21425
+ const pids = treePids(rows, root);
21426
+ if (!rows.some((r2) => r2.pid === root)) return { cpuMs: null, connections: null };
21427
+ let cpuMs = 0;
21428
+ for (const r2 of rows) if (pids.includes(r2.pid)) cpuMs += r2.cpuMs ?? 0;
21429
+ const lsof = await run2("lsof", ["-nP", "-a", "-p", pids.join(","), "-iTCP", "-sTCP:ESTABLISHED", "-Fn"]);
21430
+ const connections = lsof === null ? null : lsof.split("\n").filter((l) => l.startsWith("n")).length;
21431
+ return { cpuMs, connections };
21432
+ }
21433
+ async function sampleWindows(root) {
21434
+ const script = `$ErrorActionPreference='SilentlyContinue';$all=Get-CimInstance Win32_Process|Select-Object ProcessId,ParentProcessId,KernelModeTime,UserModeTime;$ids=@(${root});$q=@(${root});while($q.Count){$p=$q[0];$q=@($q|Select-Object -Skip 1);foreach($c in $all|Where-Object{$_.ParentProcessId -eq $p}){if($ids -notcontains $c.ProcessId){$ids+=$c.ProcessId;$q+=$c.ProcessId}}};$cpu=($all|Where-Object{$ids -contains $_.ProcessId}|ForEach-Object{[double]$_.KernelModeTime+[double]$_.UserModeTime}|Measure-Object -Sum).Sum/10000;$n=@(Get-NetTCPConnection -State Established|Where-Object{$ids -contains $_.OwningProcess}).Count;Write-Output "$cpu $n"`;
21435
+ const out = await run2("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], 8e3);
21436
+ const [cpu, n3] = (out ?? "").trim().split(/\s+/);
21437
+ const cpuMs = Number(cpu);
21438
+ const connections = Number(n3);
21439
+ return {
21440
+ cpuMs: cpu !== void 0 && Number.isFinite(cpuMs) ? cpuMs : null,
21441
+ connections: n3 !== void 0 && Number.isFinite(connections) ? connections : null
21442
+ };
21443
+ }
21444
+ function sampleProcessTree(pid) {
21445
+ return (process.platform === "win32" ? sampleWindows(pid) : samplePosix(pid)).catch(() => ({ cpuMs: null, connections: null }));
21446
+ }
21447
+ function classifyQuiet(input) {
21448
+ const { logged, sample, previous } = input;
21449
+ if (logged?.kind === "retry") return { kind: "retrying", status: logged.status, delayMs: logged.delayMs, attempt: logged.attempt };
21450
+ if (logged?.kind === "compressing") return { kind: "compressing" };
21451
+ const nearFullContext = input.context !== null && input.context.threshold > 0 && input.context.tokens >= input.context.threshold * 0.9;
21452
+ if (sample.connections !== null && sample.connections > 0) return { kind: "waiting-model", nearFullContext };
21453
+ const cpuDelta = sample.cpuMs !== null && previous?.cpuMs != null ? sample.cpuMs - previous.cpuMs : null;
21454
+ if (cpuDelta !== null && input.sinceMs > 0 && cpuDelta > BUSY_CPU_MS_PER_MIN * (input.sinceMs / 6e4)) return { kind: "busy" };
21455
+ if (sample.connections === 0 && cpuDelta !== null) return { kind: "no-signs" };
21456
+ return { kind: "unknown" };
21457
+ }
21458
+ function quietKey(state) {
21459
+ if (state.kind === "retrying") return `retrying:${state.attempt ?? "?"}:${state.status ?? "?"}`;
21460
+ if (state.kind === "waiting-model") return `waiting-model:${state.nearFullContext}`;
21461
+ return state.kind;
21462
+ }
21463
+ function describeQuiet(name, state, quietMs, stopAfterMs) {
21464
+ const quiet = minutes(quietMs);
21465
+ switch (state.kind) {
21466
+ case "retrying": {
21467
+ const why = state.status === 429 ? "hit a rate limit (HTTP 429)" : state.status !== null && state.status >= 500 ? `got a server error from its model provider (HTTP ${state.status})` : state.status !== null ? `got an error from its model provider (HTTP ${state.status})` : "had a failed model request";
21468
+ const next = state.delayMs !== null ? ` Next try in ${Math.max(1, Math.round(state.delayMs / 1e3))} s.` : "";
21469
+ const attempt = state.attempt !== null ? ` (attempt ${state.attempt + 1})` : "";
21470
+ return `${name} ${why} and is retrying${attempt}.${next} It is still working.`;
21471
+ }
21472
+ case "compressing":
21473
+ return `${name} is compressing its conversation history to make room. This can take a few minutes; it is still working.`;
21474
+ case "waiting-model":
21475
+ return state.nearFullContext ? `${name} is waiting on its model: no reply for ${quiet}, but its request is still open. Its history is nearly full, so it may be compressing it.` : `${name} is waiting on its model: no reply for ${quiet}, but its request is still open. Slow models and rate limits look like this.`;
21476
+ case "busy":
21477
+ return `${name} is busy on this computer but has sent nothing for ${quiet}.`;
21478
+ case "between-requests":
21479
+ return `${name} has sent nothing for ${quiet} and has no request open to its model right now. It may be waiting before a retry. Still waiting; press Stop to end the turn.`;
21480
+ case "no-signs":
21481
+ return `${name} has sent nothing for ${quiet}, has no open request to its model, and is not using the CPU. It may be stuck. Press Stop to end the turn; otherwise it is stopped after ${minutes(stopAfterMs)} of silence.`;
21482
+ case "unknown":
21483
+ return `${name} has sent nothing for ${quiet}. Still waiting; press Stop to end the turn.`;
21484
+ }
21485
+ }
21486
+ function lastSeenPhrase(state) {
21487
+ switch (state.kind) {
21488
+ case "retrying":
21489
+ return "retrying a failed model request";
21490
+ case "compressing":
21491
+ return "compressing its history";
21492
+ case "waiting-model":
21493
+ return "waiting on its model with a request open";
21494
+ case "busy":
21495
+ return "busy on this computer";
21496
+ case "between-requests":
21497
+ return "no request open to its model";
21498
+ case "no-signs":
21499
+ return "no open request and no CPU use";
21500
+ case "unknown":
21501
+ return "no readable process state";
21502
+ }
21503
+ }
21504
+ var LogTail, run2, BUSY_CPU_MS_PER_MIN, minutes;
21505
+ var init_quiet_status = __esm({
21506
+ "server/drivers/acp/quiet-status.ts"() {
21507
+ "use strict";
21508
+ LogTail = class {
21509
+ offset = null;
21510
+ partial = "";
21511
+ path;
21512
+ maxBytes;
21513
+ constructor(path3, maxBytes = 256 * 1024) {
21514
+ this.path = path3;
21515
+ this.maxBytes = maxBytes;
21516
+ }
21517
+ read() {
21518
+ let fd;
21519
+ try {
21520
+ fd = openSync4(this.path, "r");
21521
+ } catch {
21522
+ if (this.offset === null) this.offset = 0;
21523
+ return [];
21524
+ }
21525
+ try {
21526
+ const size = fstatSync(fd).size;
21527
+ if (this.offset === null) {
21528
+ this.offset = size;
21529
+ return [];
21530
+ }
21531
+ if (size < this.offset) {
21532
+ this.offset = 0;
21533
+ this.partial = "";
21534
+ }
21535
+ if (size === this.offset) return [];
21536
+ const start = Math.max(this.offset, size - this.maxBytes);
21537
+ if (start > this.offset) this.partial = "";
21538
+ const buf = Buffer.alloc(size - start);
21539
+ readSync2(fd, buf, 0, buf.length, start);
21540
+ this.offset = size;
21541
+ const text4 = this.partial + buf.toString("utf8");
21542
+ const lines = text4.split(/\r?\n/);
21543
+ this.partial = lines.pop() ?? "";
21544
+ return lines.filter((l) => l.trim());
21545
+ } catch {
21546
+ return [];
21547
+ } finally {
21548
+ try {
21549
+ closeSync4(fd);
21550
+ } catch {
21551
+ }
21552
+ }
21553
+ }
21554
+ };
21555
+ run2 = (file2, args, timeoutMs = 4e3) => new Promise((resolve14) => {
21556
+ execFile4(file2, args, { timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024, windowsHide: true }, (err, stdout2) => {
21557
+ if (err && !(typeof stdout2 === "string" && err.code === 1)) resolve14(null);
21558
+ else resolve14(String(stdout2));
21559
+ });
21560
+ });
21561
+ BUSY_CPU_MS_PER_MIN = 3e3;
21562
+ minutes = (ms) => {
21563
+ if (ms < 9e4) return `${Math.max(1, Math.round(ms / 1e3))} s`;
21564
+ return `${Math.round(ms / 6e4)} min`;
21565
+ };
21566
+ }
21567
+ });
21568
+
21352
21569
  // shared/approval-mode.ts
21353
21570
  function supportsApprovalMode(driverKind, mode) {
21354
21571
  if (mode === "custom") return driverKind === "codex";
@@ -21366,7 +21583,8 @@ function supportsApprovalMode(driverKind, mode) {
21366
21583
  "openai-compat",
21367
21584
  "grok",
21368
21585
  "minimax",
21369
- "mistral"
21586
+ "mistral",
21587
+ "cerebras"
21370
21588
  ].includes(driverKind ?? "");
21371
21589
  }
21372
21590
  var init_approval_mode = __esm({
@@ -21601,7 +21819,7 @@ var init_ask_question = __esm({
21601
21819
  });
21602
21820
 
21603
21821
  // server/thread-log-rotation.ts
21604
- import { closeSync as closeSync4, fstatSync, openSync as openSync4, readSync as readSync2, renameSync as renameSync5, rmSync as rmSync5, statSync as statSync3, writeSync } from "node:fs";
21822
+ import { closeSync as closeSync5, fstatSync as fstatSync2, openSync as openSync5, readSync as readSync3, renameSync as renameSync5, rmSync as rmSync5, statSync as statSync3, writeSync } from "node:fs";
21605
21823
  function currentThreadLogCap() {
21606
21824
  try {
21607
21825
  return capProvider();
@@ -21623,20 +21841,20 @@ function capThreadLog(file2, maxBytes) {
21623
21841
  function trimToTail(file2, keepBytes) {
21624
21842
  const staging = `${file2}.trim`;
21625
21843
  try {
21626
- const source = openSync4(file2, "r");
21844
+ const source = openSync5(file2, "r");
21627
21845
  try {
21628
- const size = fstatSync(source).size;
21846
+ const size = fstatSync2(source).size;
21629
21847
  const start = Math.max(0, size - keepBytes);
21630
21848
  const window = Buffer.allocUnsafe(size - start);
21631
21849
  let filled = 0;
21632
21850
  while (filled < window.length) {
21633
- const count = readSync2(source, window, filled, window.length - filled, start + filled);
21851
+ const count = readSync3(source, window, filled, window.length - filled, start + filled);
21634
21852
  if (count === 0) return;
21635
21853
  filled += count;
21636
21854
  }
21637
21855
  const firstNewline = window.indexOf(10);
21638
21856
  if (firstNewline === -1 || firstNewline + 1 >= window.length) return;
21639
- const target = openSync4(staging, "w", 384);
21857
+ const target = openSync5(staging, "w", 384);
21640
21858
  try {
21641
21859
  let written = firstNewline + 1;
21642
21860
  while (written < window.length) {
@@ -21645,10 +21863,10 @@ function trimToTail(file2, keepBytes) {
21645
21863
  written += count;
21646
21864
  }
21647
21865
  } finally {
21648
- closeSync4(target);
21866
+ closeSync5(target);
21649
21867
  }
21650
21868
  } finally {
21651
- closeSync4(source);
21869
+ closeSync5(source);
21652
21870
  }
21653
21871
  renameSync5(staging, file2);
21654
21872
  } catch {
@@ -22104,7 +22322,7 @@ function createAcpDriver(support11) {
22104
22322
  }
22105
22323
  const DRIVER_KIND8 = support11.driverKind;
22106
22324
  const SOURCE = support11.nativeSource;
22107
- const decodeConfig8 = decodeAcpConfig(support11.defaultCli);
22325
+ const decodeConfig9 = decodeAcpConfig(support11.defaultCli);
22108
22326
  const DENY_TIMEOUT_NOTE3 = "OpenMausBot: nobody answered this permission request in time. Skip this action and finish what you can without it.";
22109
22327
  return {
22110
22328
  driverKind: DRIVER_KIND8,
@@ -22115,8 +22333,8 @@ function createAcpDriver(support11) {
22115
22333
  },
22116
22334
  install: support11.install,
22117
22335
  models: support11.models,
22118
- decodeConfig: decodeConfig8,
22119
- defaultConfig: () => decodeConfig8({}),
22336
+ decodeConfig: decodeConfig9,
22337
+ defaultConfig: () => decodeConfig9({}),
22120
22338
  async create(input) {
22121
22339
  const { instanceId, config: config2 } = input;
22122
22340
  const childEnv = (activeConfig = config2) => {
@@ -22268,11 +22486,85 @@ function createAcpDriver(support11) {
22268
22486
  }
22269
22487
  return servers;
22270
22488
  };
22489
+ const startQuietWatch = (threadId, session, current) => {
22490
+ const noticeAfterMs = envOr("OMB_ACP_QUIET_NOTICE_MS", 6e4);
22491
+ const tickMs = envOr("OMB_ACP_QUIET_TICK_MS", 15e3);
22492
+ const logPath = support11.statusLog && session.sessionId ? support11.statusLog.path(session.env, session.sessionId) : null;
22493
+ const tail2 = logPath ? new LogTail(logPath) : null;
22494
+ tail2?.read();
22495
+ let logged = null;
22496
+ let context = null;
22497
+ let previous = null;
22498
+ let previousAt = 0;
22499
+ let shownKey = null;
22500
+ let noSignsSince = null;
22501
+ let notices = 0;
22502
+ let probing = false;
22503
+ let state = null;
22504
+ const tick = async () => {
22505
+ if (probing || current.state.settled || session.current !== current) return;
22506
+ const now = Date.now();
22507
+ for (const line of tail2?.read() ?? []) {
22508
+ session.acp.touch();
22509
+ const signal = support11.statusLog.parse(line);
22510
+ if (signal?.kind === "context") context = { tokens: signal.tokens, threshold: signal.threshold };
22511
+ else if (signal) logged = { signal, at: now };
22512
+ }
22513
+ const quietMs = now - session.acp.lastInboundAt;
22514
+ if (quietMs < noticeAfterMs || current.asks.size || current.runningTools.size) {
22515
+ if (quietMs < noticeAfterMs) logged = null;
22516
+ previous = null;
22517
+ shownKey = null;
22518
+ noSignsSince = null;
22519
+ state = null;
22520
+ return;
22521
+ }
22522
+ if (logged?.signal.kind === "retry" && now - logged.at > (logged.signal.delayMs ?? 6e4) + 12e4) logged = null;
22523
+ probing = true;
22524
+ const sample = session.child.pid ? await sampleProcessTree(session.child.pid) : { cpuMs: null, connections: null };
22525
+ probing = false;
22526
+ if (current.state.settled || session.current !== current) return;
22527
+ let next = classifyQuiet({
22528
+ logged: logged?.signal ?? null,
22529
+ context,
22530
+ sample,
22531
+ previous,
22532
+ sinceMs: previous ? now - previousAt : 0
22533
+ });
22534
+ const first = previous === null;
22535
+ previous = sample;
22536
+ previousAt = now;
22537
+ if (first && next.kind === "unknown") return;
22538
+ if (next.kind === "no-signs") {
22539
+ noSignsSince ??= now;
22540
+ if (now - noSignsSince < envOr("OMB_ACP_STUCK_AFTER_MS", 18e4)) next = { kind: "between-requests" };
22541
+ } else {
22542
+ noSignsSince = null;
22543
+ }
22544
+ state = next;
22545
+ const key = quietKey(next);
22546
+ if (key === shownKey || notices >= 12) return;
22547
+ shownKey = key;
22548
+ notices += 1;
22549
+ emit({
22550
+ ...base2(threadId, current.turnId),
22551
+ type: "runtime.notice",
22552
+ message: describeQuiet(support11.displayName, next, quietMs, promptIdleTimeoutMs())
22553
+ });
22554
+ };
22555
+ const timer = setInterval(() => {
22556
+ void tick();
22557
+ }, tickMs);
22558
+ timer.unref?.();
22559
+ current.stopQuietWatch = () => clearInterval(timer);
22560
+ return { lastState: () => state };
22561
+ };
22271
22562
  const settle = (threadId, session, ok, stopReason) => {
22272
22563
  const current = session.current;
22273
22564
  if (!current || current.state.settled) return;
22274
22565
  current.state.settled = true;
22275
22566
  current.acknowledge();
22567
+ current.stopQuietWatch?.();
22276
22568
  if (current.interruptTimer) clearTimeout(current.interruptTimer);
22277
22569
  for (const finish of current.asks.values()) finish("cancel", "system");
22278
22570
  session.acp.failAll(new Error("turn settled"));
@@ -22337,7 +22629,7 @@ function createAcpDriver(support11) {
22337
22629
  return;
22338
22630
  }
22339
22631
  rpcPending.delete(id);
22340
- const error51 = new Error(idleMessage ?? `${method} stopped responding`);
22632
+ const error51 = new Error((typeof idleMessage === "function" ? idleMessage() : idleMessage) ?? `${method} stopped responding`);
22341
22633
  Object.assign(error51, { acpPromptStall: true });
22342
22634
  reject(error51);
22343
22635
  }, idleMs);
@@ -22361,9 +22653,16 @@ function createAcpDriver(support11) {
22361
22653
  });
22362
22654
  send({ jsonrpc: "2.0", id, method, params });
22363
22655
  });
22656
+ let lastInboundAt = Date.now();
22364
22657
  const acp = {
22365
22658
  send,
22366
22659
  request,
22660
+ get lastInboundAt() {
22661
+ return lastInboundAt;
22662
+ },
22663
+ touch: () => {
22664
+ for (const p2 of rpcPending.values()) p2.armIdle();
22665
+ },
22367
22666
  failAll: (error51) => {
22368
22667
  for (const p2 of rpcPending.values()) {
22369
22668
  if (p2.timer) clearTimeout(p2.timer);
@@ -22615,12 +22914,23 @@ function createAcpDriver(support11) {
22615
22914
  }
22616
22915
  break;
22617
22916
  }
22917
+ case "usage_update": {
22918
+ const used = u4.used ?? u4.usage?.used;
22919
+ if (typeof used !== "number" || !(used > 0)) break;
22920
+ if (current.state.usagePeak !== null && used < current.state.usagePeak * ACP_COMPACTION_COLLAPSE_RATIO) {
22921
+ current.state.usageCompacted = true;
22922
+ }
22923
+ if (current.state.usagePeak === null || used > current.state.usagePeak) current.state.usagePeak = used;
22924
+ current.state.usageLast = used;
22925
+ break;
22926
+ }
22618
22927
  }
22619
22928
  };
22620
22929
  const session = {
22621
22930
  child,
22622
22931
  acp,
22623
22932
  launch,
22933
+ env,
22624
22934
  cwd,
22625
22935
  contractKey,
22626
22936
  sessionKey: null,
@@ -22650,6 +22960,7 @@ function createAcpDriver(support11) {
22650
22960
  continue;
22651
22961
  }
22652
22962
  appendNative(threadId, { dir: "in", source: SOURCE, msg: nativeLogMessage(msg) });
22963
+ lastInboundAt = Date.now();
22653
22964
  for (const p2 of rpcPending.values()) p2.armIdle();
22654
22965
  if (msg.id !== void 0 && (msg.result !== void 0 || msg.error !== void 0)) {
22655
22966
  const pend = rpcPending.get(msg.id);
@@ -22784,7 +23095,7 @@ function createAcpDriver(support11) {
22784
23095
  sessions.set(threadId, session);
22785
23096
  }
22786
23097
  const request = (method, params, timeoutMs, receive, idleMs, idleMessage) => session.acp.request(method, params, timeoutMs, receive, idleMs, idleMessage);
22787
- const state = { settled: false, promptSent: false, text: "", producedItem: false, startupActivity: false, stopped: false };
23098
+ const state = { settled: false, promptSent: false, text: "", producedItem: false, startupActivity: false, stopped: false, usagePeak: null, usageLast: null, usageCompacted: false };
22788
23099
  let acknowledge = () => {
22789
23100
  };
22790
23101
  let rejectStartup = (_error) => {
@@ -22845,6 +23156,7 @@ function createAcpDriver(support11) {
22845
23156
  asks,
22846
23157
  runningTools: /* @__PURE__ */ new Set(),
22847
23158
  interruptTimer: null,
23159
+ stopQuietWatch: null,
22848
23160
  flushAssistantText,
22849
23161
  receiveModelVariants
22850
23162
  };
@@ -22869,6 +23181,7 @@ function createAcpDriver(support11) {
22869
23181
  emit({ ...base2(threadId, turnId), type: "turn.started" });
22870
23182
  session.current = current;
22871
23183
  (async () => {
23184
+ let pendingSplitReceipt = null;
22872
23185
  try {
22873
23186
  const handshake = async () => {
22874
23187
  for (const child of retiring.get(threadId) ?? []) {
@@ -23071,20 +23384,21 @@ ${fallback}` : null;
23071
23384
  emitSessionStarted();
23072
23385
  const halves = promptHalves(turn);
23073
23386
  let promptInput = promptTurn;
23074
- let pendingSplitReceipt = null;
23075
23387
  if (halves.stable !== null) {
23076
23388
  const receiptKey = JSON.stringify([threadId, sessionId]);
23389
+ const previousReceipt = readPromptSplitReceipt(DRIVER_KIND8, receiptKey);
23390
+ state.usagePeak = previousReceipt?.peakUsed ?? previousReceipt?.lastUsed ?? null;
23077
23391
  const composed = splitSessionPrompt(
23078
23392
  halves.stable,
23079
23393
  halves.volatile,
23080
- readPromptSplitReceipt(DRIVER_KIND8, receiptKey),
23394
+ previousReceipt,
23081
23395
  promptTurn.system,
23082
23396
  promptTurn.text,
23083
23397
  Boolean(turn.mentionTurn),
23084
23398
  ACP_PROMPT_RE_ANCHOR_TURNS
23085
23399
  );
23086
23400
  promptInput = { ...promptTurn, system: "", text: composed.text };
23087
- pendingSplitReceipt = { key: receiptKey, receipt: composed.receipt };
23401
+ pendingSplitReceipt = { key: receiptKey, receipt: composed.receipt, previous: previousReceipt };
23088
23402
  }
23089
23403
  const text4 = support11.buildPromptText ? support11.buildPromptText(promptInput) : promptInput.system ? `${promptInput.system}
23090
23404
 
@@ -23100,16 +23414,34 @@ ${promptInput.text}` : promptInput.text;
23100
23414
  state.promptSent = true;
23101
23415
  acknowledge();
23102
23416
  const promptIdleMs = promptIdleTimeoutMs();
23417
+ const quiet = startQuietWatch(threadId, session, current);
23103
23418
  const result = await request(
23104
23419
  "session/prompt",
23105
23420
  { sessionId, prompt: [{ type: "text", text: text4 }, ...imageBlocks] },
23106
23421
  void 0,
23107
23422
  void 0,
23108
23423
  promptIdleMs,
23109
- `${DRIVER_KIND8} sent nothing for ${Math.round(promptIdleMs / 1e3)} s with no tool running, so the turn was stopped as stuck. Send the message again to retry. On a self-hosted server, OPENMAUS_ACP_PROMPT_IDLE_TIMEOUT_MS sets this limit (0 turns it off).`
23424
+ () => {
23425
+ const last = quiet.lastState();
23426
+ return `${DRIVER_KIND8} sent nothing for ${formatQuietLimit(promptIdleMs)} with no tool running, so the turn was stopped as stuck${last ? ` (last seen: ${lastSeenPhrase(last)})` : ""}. Send the message again to retry. On a self-hosted server, OPENMAUS_ACP_PROMPT_IDLE_TIMEOUT_MS sets this limit (0 turns it off).`;
23427
+ }
23110
23428
  );
23111
23429
  if (pendingSplitReceipt) {
23112
- writePromptSplitReceipt(DRIVER_KIND8, pendingSplitReceipt.key, pendingSplitReceipt.receipt);
23430
+ if (state.usageCompacted) {
23431
+ deletePromptSplitReceipt(DRIVER_KIND8, pendingSplitReceipt.key);
23432
+ } else {
23433
+ const previousLastUsed = typeof pendingSplitReceipt.previous?.lastUsed === "number" ? pendingSplitReceipt.previous.lastUsed : void 0;
23434
+ const lastUsed = state.usageLast ?? previousLastUsed;
23435
+ writePromptSplitReceipt(
23436
+ DRIVER_KIND8,
23437
+ pendingSplitReceipt.key,
23438
+ {
23439
+ ...pendingSplitReceipt.receipt,
23440
+ ...lastUsed === void 0 ? {} : { lastUsed },
23441
+ ...state.usagePeak === null ? {} : { peakUsed: state.usagePeak }
23442
+ }
23443
+ );
23444
+ }
23113
23445
  }
23114
23446
  const usage = result?.usage ?? result?._meta ?? {};
23115
23447
  if (typeof usage.inputTokens === "number" || typeof usage.outputTokens === "number") {
@@ -23138,6 +23470,9 @@ ${promptInput.text}` : promptInput.text;
23138
23470
  settle(threadId, session, false, reason ?? "failed");
23139
23471
  }
23140
23472
  } catch (e) {
23473
+ if (pendingSplitReceipt && state.promptSent && state.usageCompacted) {
23474
+ deletePromptSplitReceipt(DRIVER_KIND8, pendingSplitReceipt.key);
23475
+ }
23141
23476
  if (!state.settled) {
23142
23477
  const message3 = e instanceof Error ? e.message : String(e);
23143
23478
  const code = support11.classifyError?.(e);
@@ -23264,7 +23599,7 @@ ${promptInput.text}` : promptInput.text;
23264
23599
  }
23265
23600
  };
23266
23601
  }
23267
- var ACCOUNT_ERROR_CODES, isAccountError, envOr, INIT_TIMEOUT, SESSION_CONFIG_TIMEOUT, NEW_SESSION_TIMEOUT, LOAD_SESSION_TIMEOUT, ACP_PROMPT_RE_ANCHOR_TURNS, promptIdleTimeoutMs, CLIENT_FILE_MAX_BYTES, TOOL_LOG_TEXT_LIMIT;
23602
+ var ACCOUNT_ERROR_CODES, isAccountError, envOr, INIT_TIMEOUT, SESSION_CONFIG_TIMEOUT, NEW_SESSION_TIMEOUT, LOAD_SESSION_TIMEOUT, ACP_PROMPT_RE_ANCHOR_TURNS, ACP_COMPACTION_COLLAPSE_RATIO, DEFAULT_ACP_PROMPT_IDLE_MS, promptIdleTimeoutMs, formatQuietLimit, CLIENT_FILE_MAX_BYTES, TOOL_LOG_TEXT_LIMIT;
23268
23603
  var init_core3 = __esm({
23269
23604
  "server/drivers/acp/core.ts"() {
23270
23605
  "use strict";
@@ -23272,6 +23607,7 @@ var init_core3 = __esm({
23272
23607
  init_local_inject();
23273
23608
  init_prompt_split();
23274
23609
  init_procs();
23610
+ init_quiet_status();
23275
23611
  init_contracts();
23276
23612
  init_env_path();
23277
23613
  init_approval_mode();
@@ -23297,12 +23633,15 @@ var init_core3 = __esm({
23297
23633
  NEW_SESSION_TIMEOUT = envOr("OPENMAUS_ACP_NEW_SESSION_TIMEOUT_MS", 3e5);
23298
23634
  LOAD_SESSION_TIMEOUT = envOr("OPENMAUS_ACP_LOAD_SESSION_TIMEOUT_MS", 12e4);
23299
23635
  ACP_PROMPT_RE_ANCHOR_TURNS = 8;
23636
+ ACP_COMPACTION_COLLAPSE_RATIO = 0.6;
23637
+ DEFAULT_ACP_PROMPT_IDLE_MS = 15 * 6e4;
23300
23638
  promptIdleTimeoutMs = () => {
23301
23639
  const raw = process.env.OPENMAUS_ACP_PROMPT_IDLE_TIMEOUT_MS;
23302
- if (raw === void 0) return 18e4;
23640
+ if (raw === void 0) return DEFAULT_ACP_PROMPT_IDLE_MS;
23303
23641
  const ms = Number(raw);
23304
23642
  return Number.isFinite(ms) && ms > 0 ? ms : 0;
23305
23643
  };
23644
+ formatQuietLimit = (ms) => ms >= 12e4 && ms % 6e4 === 0 ? `${ms / 6e4} min` : `${Math.round(ms / 1e3)} s`;
23306
23645
  CLIENT_FILE_MAX_BYTES = 8 * 1024 * 1024;
23307
23646
  TOOL_LOG_TEXT_LIMIT = 64e3;
23308
23647
  }
@@ -24909,10 +25248,10 @@ var init_boatagent = __esm({
24909
25248
  if (runPromptId) {
24910
25249
  const status = await api2(`/boxes/${boxId}/prompts/${runPromptId}`).catch(() => null);
24911
25250
  appendNative(threadId, { dir: "in", source: "box.prompt.status", msg: status });
24912
- const run2 = status?.promptRun ?? status?.prompt ?? status ?? {};
24913
- const state = String(run2?.status ?? "");
25251
+ const run3 = status?.promptRun ?? status?.prompt ?? status ?? {};
25252
+ const state = String(run3?.status ?? "");
24914
25253
  if (/completed|succeeded|done|finished/i.test(state)) {
24915
- const result = run2?.result ?? run2?.output ?? lastText;
25254
+ const result = run3?.result ?? run3?.output ?? lastText;
24916
25255
  if (typeof result === "string" && result.trim() && result !== lastText) {
24917
25256
  ingest(result);
24918
25257
  }
@@ -24923,7 +25262,7 @@ var init_boatagent = __esm({
24923
25262
  return await finishRun(true, null);
24924
25263
  }
24925
25264
  if (/failed|error|cancelled|interrupted/i.test(state)) {
24926
- const runError = [run2?.error, run2?.failureReason, run2?.message].find((v2) => typeof v2 === "string" && v2.trim());
25265
+ const runError = [run3?.error, run3?.failureReason, run3?.message].find((v2) => typeof v2 === "string" && v2.trim());
24927
25266
  if (problem || runError || /failed|error/i.test(state)) throw new Error(problem ?? runError ?? `the box run ${state}`);
24928
25267
  return await finishRun(false, state);
24929
25268
  }
@@ -25427,9 +25766,9 @@ import { chmodSync as chmodSync4, mkdirSync as mkdirSync10, mkdtempSync as mkdte
25427
25766
  import { createServer as createNetServer } from "node:net";
25428
25767
  import { homedir as homedir9, tmpdir as tmpdir3 } from "node:os";
25429
25768
  import { join as join22, dirname as dirname10, isAbsolute as isAbsolute5, normalize } from "node:path";
25430
- function claudeAuthStatus(cli, env, run2 = execCli) {
25769
+ function claudeAuthStatus(cli, env, run3 = execCli) {
25431
25770
  return new Promise((resolve14) => {
25432
- run2(cli, ["auth", "status", "--json"], { timeout: 8e3, maxBuffer: 65536, env }, (_error, stdout2) => {
25771
+ run3(cli, ["auth", "status", "--json"], { timeout: 8e3, maxBuffer: 65536, env }, (_error, stdout2) => {
25433
25772
  try {
25434
25773
  const status = JSON.parse(stdout2);
25435
25774
  if (!status || typeof status !== "object" || !("loggedIn" in status) || status.loggedIn !== true) {
@@ -26089,6 +26428,8 @@ var init_claude = __esm({
26089
26428
  await refreshModels();
26090
26429
  let cliVersion = null;
26091
26430
  let cliVersionChecked = false;
26431
+ let cliHasAutocompact = null;
26432
+ let cliHelpVersion = null;
26092
26433
  const readCliVersion = (env) => new Promise((resolve14) => {
26093
26434
  execCli(
26094
26435
  config2.cli,
@@ -26227,7 +26568,7 @@ var init_claude = __esm({
26227
26568
  if (claudeCliSupports(cliVersion, "--setting-sources")) args.push("--setting-sources", "project");
26228
26569
  }
26229
26570
  const compactWindow = autoCompactWindow(turnEnvironment);
26230
- if (compactWindow && claudeCliSupports(cliVersion, "--autocompact")) {
26571
+ if (compactWindow && cliHasAutocompact === true) {
26231
26572
  args.push("--autocompact", compactWindow);
26232
26573
  }
26233
26574
  if (turn.refreshSystemPrompt && cliVersionChecked && claudeCliSupports(cliVersion, "--system-prompt-snapshot")) {
@@ -26950,6 +27291,13 @@ var init_claude = __esm({
26950
27291
  if (!version2) return { state: "unavailable", reason: `\`${config2.cli}\` CLI not found` };
26951
27292
  cliVersion = parseClaudeCliVersion(version2);
26952
27293
  cliVersionChecked = true;
27294
+ if (version2 !== cliHelpVersion) {
27295
+ const help = await new Promise((resolve14) => {
27296
+ execCli(config2.cli, ["--help"], { timeout: 8e3, env }, (err, stdout2) => resolve14(err ? null : stdout2));
27297
+ });
27298
+ cliHasAutocompact = help === null ? null : /^\s*--autocompact\b/m.test(help);
27299
+ cliHelpVersion = version2;
27300
+ }
26953
27301
  const update = claudeCliUpdate(version2, config2.cli);
26954
27302
  const warning = claudeInheritWarning(env);
26955
27303
  if (config2.requireApiKey) {
@@ -27184,11 +27532,11 @@ var init_codex_mcp_names = __esm({
27184
27532
  // server/environment.ts
27185
27533
  import { randomUUID as randomUUID11 } from "node:crypto";
27186
27534
  import {
27187
- closeSync as closeSync5,
27535
+ closeSync as closeSync6,
27188
27536
  fsyncSync as fsyncSync3,
27189
27537
  linkSync as linkSync2,
27190
27538
  mkdirSync as mkdirSync11,
27191
- openSync as openSync5,
27539
+ openSync as openSync6,
27192
27540
  readFileSync as readFileSync10,
27193
27541
  unlinkSync as unlinkSync4,
27194
27542
  writeFileSync as writeFileSync6
@@ -27223,10 +27571,10 @@ function loadEnvironmentId(dataDir) {
27223
27571
  const temporaryFile = join24(directory, `.environment-id.${process.pid}.${randomUUID11()}.tmp`);
27224
27572
  let descriptor;
27225
27573
  try {
27226
- descriptor = openSync5(temporaryFile, "wx", 384);
27574
+ descriptor = openSync6(temporaryFile, "wx", 384);
27227
27575
  writeFileSync6(descriptor, id + "\n", "utf8");
27228
27576
  fsyncSync3(descriptor);
27229
- closeSync5(descriptor);
27577
+ closeSync6(descriptor);
27230
27578
  descriptor = void 0;
27231
27579
  try {
27232
27580
  linkSync2(temporaryFile, file2);
@@ -27238,7 +27586,7 @@ function loadEnvironmentId(dataDir) {
27238
27586
  throw new Error(`The environment identity at ${file2} disappeared while it was being created.`);
27239
27587
  }
27240
27588
  } finally {
27241
- if (descriptor !== void 0) closeSync5(descriptor);
27589
+ if (descriptor !== void 0) closeSync6(descriptor);
27242
27590
  try {
27243
27591
  unlinkSync4(temporaryFile);
27244
27592
  } catch {
@@ -28234,7 +28582,7 @@ var init_webapi = __esm({
28234
28582
 
28235
28583
  // server/drivers/chatgpt-plan-auth.ts
28236
28584
  import { createHash as createHash9, randomBytes as randomBytes3, randomUUID as randomUUID12, timingSafeEqual } from "node:crypto";
28237
- import { chmodSync as chmodSync5, closeSync as closeSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync12, openSync as openSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "node:fs";
28585
+ import { chmodSync as chmodSync5, closeSync as closeSync7, lstatSync as lstatSync2, mkdirSync as mkdirSync12, openSync as openSync7, readFileSync as readFileSync11, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "node:fs";
28238
28586
  import { createServer as createServer2 } from "node:http";
28239
28587
  import { dirname as dirname12, isAbsolute as isAbsolute6, join as join25 } from "node:path";
28240
28588
  import { setTimeout as delay } from "node:timers/promises";
@@ -28267,7 +28615,7 @@ async function locked(directory, operation) {
28267
28615
  let fd;
28268
28616
  for (; ; ) {
28269
28617
  try {
28270
- fd = openSync6(path3, "wx", 384);
28618
+ fd = openSync7(path3, "wx", 384);
28271
28619
  break;
28272
28620
  } catch (error51) {
28273
28621
  if (error51.code !== "EEXIST") throw new Error("ChatGPT credentials could not be locked safely.");
@@ -28279,7 +28627,7 @@ async function locked(directory, operation) {
28279
28627
  writeFileSync7(fd, String(process.pid));
28280
28628
  return await operation();
28281
28629
  } finally {
28282
- closeSync6(fd);
28630
+ closeSync7(fd);
28283
28631
  unlinkSync5(path3);
28284
28632
  }
28285
28633
  }
@@ -39571,7 +39919,7 @@ var init_boat = __esm({
39571
39919
  'for name in chrome google-chrome chromium chromium-browser; do pid=$(pgrep -o -x "$name" 2>/dev/null || true); [ -z "$pid" ] || kill -TERM "$pid" 2>/dev/null || true; done',
39572
39920
  "for i in 1 2 3 4 5 6 7 8; do if ! pgrep -x chrome >/dev/null 2>&1 && ! pgrep -x google-chrome >/dev/null 2>&1 && ! pgrep -x chromium >/dev/null 2>&1 && ! pgrep -x chromium-browser >/dev/null 2>&1; then break; fi; sleep 0.25; done"
39573
39921
  ].join("; ");
39574
- INCLUDED_BOAT_UNAVAILABLE = "Cloud Pro's included cloud computers aren't available right now. Try again later.";
39922
+ INCLUDED_BOAT_UNAVAILABLE = "The cloud computers included with your Cloud plan aren't available right now. Try again later.";
39575
39923
  PANEL_PATH = "/tmp/ogb-panel.jpg";
39576
39924
  PANEL_FRAME_WIDTH = 1920;
39577
39925
  PANEL_FRAME_QUALITY = 85;
@@ -39788,6 +40136,13 @@ function boundedText(value) {
39788
40136
  return `${bytes.subarray(0, end).toString()}
39789
40137
  [MCP result truncated at 50KB; request less output.]`;
39790
40138
  }
40139
+ function omitBlankDefaults(builtInBrowser, tool2, args) {
40140
+ if (!builtInBrowser || !object3(args)) return;
40141
+ for (const field of BUILT_IN_BROWSER_BLANK_MEANS_OMITTED[tool2] ?? []) {
40142
+ const value = args[field];
40143
+ if (typeof value === "string" && !value.trim()) delete args[field];
40144
+ }
40145
+ }
39791
40146
  async function mountChatTools(integrations, signal, computerUse = false) {
39792
40147
  const servers = [];
39793
40148
  if (computerUse && integrations?.computer) servers.push(["computer", integrations.computer]);
@@ -39824,11 +40179,11 @@ async function mountChatTools(integrations, signal, computerUse = false) {
39824
40179
  if (signal.aborted || closed) throw aborted2();
39825
40180
  const client = "boxId" in descriptor ? new ChatBoatClient(descriptor) : new ChatMcpClient(descriptor, computerUse);
39826
40181
  clients.push(client);
39827
- return { name, client, tools: await client.tools(signal) };
40182
+ return { name, client, builtInBrowser: descriptor === integrations?.browser, tools: await client.tools(signal) };
39828
40183
  }));
39829
40184
  for (const mount of mounts) {
39830
40185
  if (mount.status === "rejected") throw mount.reason;
39831
- const { name: server, client, tools } = mount.value;
40186
+ const { name: server, client, builtInBrowser, tools } = mount.value;
39832
40187
  const originalNames = /* @__PURE__ */ new Set();
39833
40188
  for (const tool2 of tools) {
39834
40189
  if (!object3(tool2) || typeof tool2.name !== "string" || !tool2.name.trim() || originalNames.has(tool2.name)) throw new Error("MCP server advertised an invalid or duplicate tool name");
@@ -39854,7 +40209,7 @@ async function mountChatTools(integrations, signal, computerUse = false) {
39854
40209
  const suffix = `_${index}`;
39855
40210
  name = base2.slice(0, 64 - suffix.length) + suffix;
39856
40211
  }
39857
- registered.set(name, { client, name: tool2.name, schema: schema4 });
40212
+ registered.set(name, { client, builtInBrowser, name: tool2.name, schema: schema4 });
39858
40213
  definitions.push({ type: "function", function: { name, description, parameters } });
39859
40214
  if (Buffer.byteLength(JSON.stringify(definitions)) > CATALOG_BYTES) throw new Error("MCP tool catalog exceeds the 1MB limit");
39860
40215
  }
@@ -39868,6 +40223,7 @@ async function mountChatTools(integrations, signal, computerUse = false) {
39868
40223
  if (closed || signal.aborted) throw new ChatToolSessionError("MCP session closed");
39869
40224
  const tool2 = registered.get(name);
39870
40225
  if (!tool2) throw new Error("The requested tool was not advertised for this turn");
40226
+ omitBlankDefaults(tool2.builtInBrowser, tool2.name, args);
39871
40227
  if (!object3(args) || !tool2.schema(args)) throw new Error("Tool arguments do not match the advertised input schema; use its required fields and types");
39872
40228
  };
39873
40229
  return {
@@ -39908,7 +40264,7 @@ async function mountChatTools(integrations, signal, computerUse = false) {
39908
40264
  }
39909
40265
  };
39910
40266
  }
39911
- var import_ajv, import__, import_ajv_formats, ChatToolSessionError, STARTUP_MS, CALL_MS, FRAME_BYTES, OUTPUT_BYTES, TOOL_COUNT, MAX_PAGES, SCHEMA_BYTES, CATALOG_BYTES, MAX_FRAMES, ChatMcpClient, validatorOptions;
40267
+ var import_ajv, import__, import_ajv_formats, ChatToolSessionError, STARTUP_MS, CALL_MS, FRAME_BYTES, OUTPUT_BYTES, TOOL_COUNT, MAX_PAGES, SCHEMA_BYTES, CATALOG_BYTES, MAX_FRAMES, ChatMcpClient, validatorOptions, BUILT_IN_BROWSER_BLANK_MEANS_OMITTED;
39912
40268
  var init_chat_mcp_tools = __esm({
39913
40269
  "server/drivers/chat-mcp-tools.ts"() {
39914
40270
  "use strict";
@@ -40104,6 +40460,9 @@ var init_chat_mcp_tools = __esm({
40104
40460
  strictTypes: false,
40105
40461
  strictTuples: false
40106
40462
  };
40463
+ BUILT_IN_BROWSER_BLANK_MEANS_OMITTED = {
40464
+ agent_browser_read: ["url"]
40465
+ };
40107
40466
  }
40108
40467
  });
40109
40468
 
@@ -40289,6 +40648,11 @@ function providerError(json2) {
40289
40648
  }
40290
40649
  return null;
40291
40650
  }
40651
+ function announcesAction(text4) {
40652
+ const reply = text4.trim();
40653
+ if (!reply || reply.length > 400 || reply.includes("?")) return false;
40654
+ return /(?:[:\u2014\u2013]|\.\.\.|\u2026)$/.test(reply) || /^(?:checking|let me|i'll|i will|i'm going to|i am going to|opening|pulling|looking|searching|navigating|reading|fetching|loading|now (?:i'll|let me|opening|checking|pulling|reading))\b/i.test(reply);
40655
+ }
40292
40656
  function rejectsToolsParameter(status, body) {
40293
40657
  if (status !== 400 && status !== 422) return false;
40294
40658
  let envelope;
@@ -40487,6 +40851,7 @@ function createOpenAIChatRuntime(options) {
40487
40851
  if (idleTimer) clearTimeout(idleTimer);
40488
40852
  }
40489
40853
  };
40854
+ const seesImages = (turn) => options.imageInput?.(turn.model || options.models().default) ?? true;
40490
40855
  const messagesFor = (turn) => {
40491
40856
  const halves = promptHalves(turn);
40492
40857
  const note = halves.stable !== null ? volatileContextNote(halves.volatile, false) : "";
@@ -40498,7 +40863,9 @@ function createOpenAIChatRuntime(options) {
40498
40863
  role: message3.role,
40499
40864
  content: message3.text
40500
40865
  })),
40501
- { role: "user", content: options.computerUse ? chatUserContent(userTurn) : userTurn.text }
40866
+ { role: "user", content: !options.computerUse ? userTurn.text : seesImages(turn) ? chatUserContent(userTurn) : userTurn.images?.length ? `${userTurn.text}
40867
+
40868
+ ${TEXT_ONLY_ATTACHMENT_NOTE}` : userTurn.text }
40502
40869
  ];
40503
40870
  };
40504
40871
  const sendTurn = async (turn) => {
@@ -40589,6 +40956,7 @@ function createOpenAIChatRuntime(options) {
40589
40956
  let toolFailed = false;
40590
40957
  const denials = [];
40591
40958
  const seenCalls = /* @__PURE__ */ new Set();
40959
+ let nudged = false;
40592
40960
  try {
40593
40961
  tools = await mountChatTools(options.tools === false ? void 0 : turn.integrations, abort.signal, options.computerUse);
40594
40962
  let optionalQuestionOnly = options.tools !== false && tools.definitions.length === 0;
@@ -40664,6 +41032,11 @@ function createOpenAIChatRuntime(options) {
40664
41032
  if (completion.finishReason && completion.finishReason !== "stop") {
40665
41033
  throw new ChatProtocolError(`provider did not finish the response (${completion.finishReason})`);
40666
41034
  }
41035
+ if (options.nudgeAnnouncedAction && !nudged && tools.definitions.length && announcesAction(completion.text)) {
41036
+ nudged = true;
41037
+ messages.push({ role: "assistant", content: completion.text }, { role: "user", content: NUDGE_ANNOUNCED_ACTION });
41038
+ continue;
41039
+ }
40667
41040
  if (toolFailed) {
40668
41041
  stopReason = "tool_error";
40669
41042
  throw new ChatProtocolError("One or more tool operations failed or were denied. See the tool results; the final response is not an execution receipt.");
@@ -40681,7 +41054,7 @@ function createOpenAIChatRuntime(options) {
40681
41054
  role: "assistant",
40682
41055
  content: completion.text || null,
40683
41056
  tool_calls: completion.toolCalls,
40684
- ...completion.protocolReasoning && !reasoningReplayRejected.has(model) ? { reasoning_content: completion.protocolReasoning } : {},
41057
+ ...completion.protocolReasoning && !reasoningReplayRejected.has(model) ? { [options.reasoningReplayField ?? "reasoning_content"]: completion.protocolReasoning } : {},
40685
41058
  ...completion.protocolReasoningDetails.length ? { reasoning_details: completion.protocolReasoningDetails } : {}
40686
41059
  });
40687
41060
  const screenshotParts = [];
@@ -40739,6 +41112,11 @@ function createOpenAIChatRuntime(options) {
40739
41112
  started = true;
40740
41113
  if (allowed) {
40741
41114
  result = await tools.execute(call.function.name, args, abort.signal);
41115
+ if (result.images?.length && !seesImages(turn)) {
41116
+ const { images: _dropped, ...rest } = result;
41117
+ result = { ...rest, text: `${rest.text}
41118
+ ${TEXT_ONLY_SCREENSHOT_NOTE}` };
41119
+ }
40742
41120
  if (result.images?.length) {
40743
41121
  try {
40744
41122
  assertImageTransport(options.apiUrl);
@@ -40881,7 +41259,7 @@ function createOpenAIChatRuntime(options) {
40881
41259
  }
40882
41260
  };
40883
41261
  }
40884
- var usageFrom, asError, UnsupportedChatToolsError, UnsupportedReasoningReplayError;
41262
+ var usageFrom, asError, UnsupportedChatToolsError, NUDGE_ANNOUNCED_ACTION, TEXT_ONLY_ATTACHMENT_NOTE, TEXT_ONLY_SCREENSHOT_NOTE, UnsupportedReasoningReplayError;
40885
41263
  var init_openai_chat = __esm({
40886
41264
  "server/drivers/openai-chat.ts"() {
40887
41265
  "use strict";
@@ -40900,6 +41278,9 @@ var init_openai_chat = __esm({
40900
41278
  asError = (value) => value instanceof Error ? value : new Error(String(value));
40901
41279
  UnsupportedChatToolsError = class extends Error {
40902
41280
  };
41281
+ NUDGE_ANNOUNCED_ACTION = "You said what you would do next but called no tool. Do it now with your tools, or reply with your final answer if nothing is left to do.";
41282
+ TEXT_ONLY_ATTACHMENT_NOTE = "[The person attached image(s), but this model cannot see images. Say so if the request depends on them.]";
41283
+ TEXT_ONLY_SCREENSHOT_NOTE = "[Screenshot not shown: this model cannot see images. Read the page with a snapshot or a text tool instead.]";
40903
41284
  UnsupportedReasoningReplayError = class extends Error {
40904
41285
  };
40905
41286
  }
@@ -41934,8 +42315,15 @@ function resolveCursorAcpModelId(available, wanted) {
41934
42315
  const base2 = (id) => id.split("[")[0].trim().toLowerCase();
41935
42316
  const exact = ids.find((m2) => m2.modelId.toLowerCase() === want);
41936
42317
  if (exact) return exact.modelId;
41937
- const byBase = ids.find((m2) => base2(m2.modelId) === want);
42318
+ const isFast = (id) => /[[,]\s*fast\s*=\s*true\s*[\],]/i.test(id);
42319
+ const sameBase = ids.filter((m2) => base2(m2.modelId) === want);
42320
+ const byBase = sameBase.find((m2) => !isFast(m2.modelId)) ?? sameBase[0];
41938
42321
  if (byBase) return byBase.modelId;
42322
+ if (want.endsWith("-fast")) {
42323
+ const stem = want.slice(0, -"-fast".length);
42324
+ const fast = ids.find((m2) => base2(m2.modelId) === stem && isFast(m2.modelId));
42325
+ if (fast) return fast.modelId;
42326
+ }
41939
42327
  const byName = ids.find((m2) => (m2.name ?? "").trim().toLowerCase() === want);
41940
42328
  if (byName) return byName.modelId;
41941
42329
  if (want === "auto" || want === "default") {
@@ -42084,27 +42472,27 @@ function decodeCursorAuthText(text4) {
42084
42472
  if (/login successful|logged in|signed in|authenticated/.test(normalized)) return true;
42085
42473
  return null;
42086
42474
  }
42087
- function execText(run2, cli, args, env) {
42475
+ function execText(run3, cli, args, env) {
42088
42476
  return new Promise((resolve14) => {
42089
- run2(cli, args, { timeout: EXEC_TIMEOUT_MS, env }, (err, stdout2) => {
42477
+ run3(cli, args, { timeout: EXEC_TIMEOUT_MS, env }, (err, stdout2) => {
42090
42478
  if (err) return resolve14(null);
42091
42479
  resolve14(String(stdout2 ?? ""));
42092
42480
  });
42093
42481
  });
42094
42482
  }
42095
- async function probeCursorAuth(cli, env, run2 = execCli) {
42483
+ async function probeCursorAuth(cli, env, run3 = execCli) {
42096
42484
  if (nonBlank2(env.CURSOR_API_KEY) || nonBlank2(env.CURSOR_AUTH_TOKEN)) return true;
42097
42485
  for (const args of [["status", "--format", "json"], ["status"]]) {
42098
- const stdout2 = await execText(run2, cli, [...args], env);
42486
+ const stdout2 = await execText(run3, cli, [...args], env);
42099
42487
  if (stdout2 == null) continue;
42100
42488
  const decoded = decodeCursorAuthStatus(firstJsonValue(stdout2)) ?? decodeCursorAuthText(stdout2);
42101
42489
  if (decoded !== null) return decoded;
42102
42490
  }
42103
42491
  return false;
42104
42492
  }
42105
- async function fetchCursorModels(cli, env, run2 = execCli) {
42493
+ async function fetchCursorModels(cli, env, run3 = execCli) {
42106
42494
  for (const args of [["models"], ["--list-models"]]) {
42107
- const stdout2 = await execText(run2, cli, [...args], env);
42495
+ const stdout2 = await execText(run3, cli, [...args], env);
42108
42496
  if (stdout2 == null) continue;
42109
42497
  const fromText = decodeCursorModelText(stdout2);
42110
42498
  if (fromText) return fromText;
@@ -42127,8 +42515,8 @@ function classifyCursorError(error51) {
42127
42515
  }
42128
42516
  return void 0;
42129
42517
  }
42130
- function createCursorAgentDriver(run2 = execCli) {
42131
- return createAcpDriver(support6(run2));
42518
+ function createCursorAgentDriver(run3 = execCli) {
42519
+ return createAcpDriver(support6(run3));
42132
42520
  }
42133
42521
  var STATIC_CURSOR_MODELS, SLUG3, EXEC_TIMEOUT_MS, nonBlank2, support6, CursorAgentDriver;
42134
42522
  var init_cursor = __esm({
@@ -42150,7 +42538,7 @@ var init_cursor = __esm({
42150
42538
  SLUG3 = /^[a-z0-9][a-z0-9._:+-]*$/i;
42151
42539
  EXEC_TIMEOUT_MS = 8e3;
42152
42540
  nonBlank2 = (value) => Boolean(value?.trim());
42153
- support6 = (run2) => ({
42541
+ support6 = (run3) => ({
42154
42542
  driverKind: "cursorAgent",
42155
42543
  displayName: "Cursor",
42156
42544
  models: STATIC_CURSOR_MODELS,
@@ -42177,13 +42565,13 @@ var init_cursor = __esm({
42177
42565
  "acp"
42178
42566
  ],
42179
42567
  credentialEnv: ["CURSOR_API_KEY", "CURSOR_AUTH_TOKEN"],
42180
- resolveModels: (environment, config2) => fetchCursorModels(config2.cli || "cursor-agent", environment, run2),
42568
+ resolveModels: (environment, config2) => fetchCursorModels(config2.cli || "cursor-agent", environment, run3),
42181
42569
  // Prefer the advertised ACP method. An already-signed-in CLI should accept
42182
42570
  // cursor_login without a browser; a missing method rides the ambient login
42183
42571
  // (CURSOR_API_KEY / `cursor-agent login`) instead of failing the turn.
42184
42572
  pickAuthMethod: (methods) => methods.some((m2) => m2.id === "cursor_login") ? "cursor_login" : null,
42185
42573
  authFailure: "continue",
42186
- isAuthenticated: (env, config2) => probeCursorAuth(config2.cli || "cursor-agent", env, run2),
42574
+ isAuthenticated: (env, config2) => probeCursorAuth(config2.cli || "cursor-agent", env, run3),
42187
42575
  classifyError: classifyCursorError,
42188
42576
  async configureSession({ request, sessionId, turn, sessionModels }) {
42189
42577
  if (!turn.model) return;
@@ -42266,7 +42654,7 @@ var init_attachments = __esm({
42266
42654
  });
42267
42655
 
42268
42656
  // server/drivers/acp/opencode-go.ts
42269
- import { execFile as execFile4 } from "node:child_process";
42657
+ import { execFile as execFile5 } from "node:child_process";
42270
42658
  import { existsSync as existsSync15, mkdirSync as mkdirSync19, readFileSync as readFileSync21, statSync as statSync5, writeFileSync as writeFileSync13 } from "node:fs";
42271
42659
  import { homedir as homedir16 } from "node:os";
42272
42660
  import { dirname as dirname14, isAbsolute as isAbsolute9, join as join38, relative as relative2, resolve as resolvePath, sep } from "node:path";
@@ -42411,7 +42799,7 @@ function runOpenCodeModelsVerbose(cli, environment) {
42411
42799
  function runGit(directory, args) {
42412
42800
  const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_")));
42413
42801
  return new Promise((resolveRun, rejectRun) => {
42414
- execFile4("git", [
42802
+ execFile5("git", [
42415
42803
  "-c",
42416
42804
  "user.name=OpenMaus",
42417
42805
  "-c",
@@ -43094,6 +43482,12 @@ function resolveQwenTurnModel(model, env) {
43094
43482
  if (!matches.length) throw new Error("This Qwen model is no longer configured. Refresh models and select it again.");
43095
43483
  return matches[0].id;
43096
43484
  }
43485
+ function qwenDebugLogPath(env, sessionId) {
43486
+ const off = ["", "0", "false", "off", "no"].includes((env.QWEN_DEBUG_LOG_FILE ?? "").trim().toLowerCase());
43487
+ if (off || !/^[\w-]{1,128}$/.test(sessionId)) return null;
43488
+ const base2 = env.QWEN_RUNTIME_DIR || env.QWEN_HOME || harnessHome("qwen", env);
43489
+ return join39(base2, "debug", `${sessionId}.txt`);
43490
+ }
43097
43491
  function qwenApprovalArgs(fullAuto, approvalMode) {
43098
43492
  if (fullAuto) return ["--yolo"];
43099
43493
  if (approvalMode === "auto") return ["--approval-mode", "auto"];
@@ -43106,6 +43500,7 @@ var init_qwen = __esm({
43106
43500
  "use strict";
43107
43501
  init_contracts();
43108
43502
  init_env_path();
43503
+ init_quiet_status();
43109
43504
  init_local_inject();
43110
43505
  init_core3();
43111
43506
  EMPTY = { default: "", options: [] };
@@ -43140,7 +43535,14 @@ var init_qwen = __esm({
43140
43535
  isAuthenticated: () => true,
43141
43536
  buildPromptText: (turn) => turn.system ? `${turn.system}
43142
43537
 
43143
- ${turn.text}` : turn.text
43538
+ ${turn.text}` : turn.text,
43539
+ // Qwen says nothing over ACP while it retries a rate limit or compresses
43540
+ // its history, but its debug log does. Turn the log on (unless the person
43541
+ // turned it off) and read it while a prompt is quiet.
43542
+ transformEnv: (env) => {
43543
+ if (env.QWEN_DEBUG_LOG_FILE === void 0) env.QWEN_DEBUG_LOG_FILE = "1";
43544
+ },
43545
+ statusLog: { path: qwenDebugLogPath, parse: parseQwenLogLine }
43144
43546
  };
43145
43547
  QwenAgentDriver = createAcpDriver(support8);
43146
43548
  }
@@ -51506,6 +51908,120 @@ var init_mistral = __esm({
51506
51908
  }
51507
51909
  });
51508
51910
 
51911
+ // server/drivers/cerebras.ts
51912
+ function decodeConfig8(raw) {
51913
+ const config2 = configSchema2.parse(raw ?? {});
51914
+ config2.url = config2.url.replace(/\/+$/, "");
51915
+ const url2 = new URL(config2.url);
51916
+ if (url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname))) {
51917
+ throw new Error("Cerebras requires HTTPS, except for a local test endpoint.");
51918
+ }
51919
+ return config2;
51920
+ }
51921
+ var DEFAULT_URL3, DEFAULT_MODELS3, configSchema2, modelCard2, TEXT_ONLY_MODELS, CerebrasDriver;
51922
+ var init_cerebras = __esm({
51923
+ "server/drivers/cerebras.ts"() {
51924
+ "use strict";
51925
+ init_zod();
51926
+ init_openai_chat();
51927
+ DEFAULT_URL3 = "https://api.cerebras.ai/v1";
51928
+ DEFAULT_MODELS3 = {
51929
+ default: "gpt-oss-120b",
51930
+ options: [
51931
+ { id: "gpt-oss-120b", label: "GPT OSS 120B", contextWindow: 65536 },
51932
+ { id: "qwen-3.8-27b", label: "Qwen3.8 27B", contextWindow: 65536 }
51933
+ ]
51934
+ };
51935
+ configSchema2 = external_exports.object({
51936
+ url: external_exports.string().trim().url().default(DEFAULT_URL3),
51937
+ model: external_exports.string().trim().min(1).optional(),
51938
+ tools: external_exports.boolean().optional()
51939
+ });
51940
+ modelCard2 = external_exports.object({ id: external_exports.string().min(1) });
51941
+ TEXT_ONLY_MODELS = /^(?:gpt-oss|llama3\.1-8b|llama-3\.3-70b)/i;
51942
+ CerebrasDriver = {
51943
+ driverKind: "cerebras",
51944
+ metadata: { displayName: "Cerebras (API)", supportsMultipleInstances: true, access: "api" },
51945
+ models: DEFAULT_MODELS3,
51946
+ install: {
51947
+ docsUrl: "https://cloud.cerebras.ai/",
51948
+ settings: "connections",
51949
+ signInCommand: "Save a Cerebras API key in Settings \u2192 Connections, or set CEREBRAS_API_KEY on the server."
51950
+ },
51951
+ decodeConfig: decodeConfig8,
51952
+ defaultConfig: () => decodeConfig8({}),
51953
+ async create(input) {
51954
+ const { config: config2 } = input;
51955
+ const apiKey = (input.environment.CEREBRAS_API_KEY ?? process.env.CEREBRAS_API_KEY ?? "").trim();
51956
+ const known = new Map(DEFAULT_MODELS3.options.map((option) => [option.id, option]));
51957
+ const withConfiguredModel = (options) => {
51958
+ const preferred = config2.model ?? DEFAULT_MODELS3.default;
51959
+ if (config2.model && !options.some((option) => option.id === config2.model)) {
51960
+ options = [{ id: config2.model, label: config2.model }, ...options];
51961
+ }
51962
+ return { default: options.some((option) => option.id === preferred) ? preferred : options[0].id, options };
51963
+ };
51964
+ let catalog2 = withConfiguredModel(DEFAULT_MODELS3.options);
51965
+ const refreshModels = async () => {
51966
+ if (!apiKey) return;
51967
+ try {
51968
+ const response = await fetch(`${config2.url}/models`, {
51969
+ headers: { authorization: `Bearer ${apiKey}` },
51970
+ redirect: "error",
51971
+ signal: AbortSignal.timeout(8e3)
51972
+ });
51973
+ if (!response.ok) return;
51974
+ const json2 = await response.json();
51975
+ const rows = Array.isArray(json2) ? json2 : external_exports.object({ data: external_exports.array(external_exports.unknown()) }).parse(json2).data;
51976
+ const options = [];
51977
+ const seen = /* @__PURE__ */ new Set();
51978
+ for (const row of rows) {
51979
+ const parsed = modelCard2.safeParse(row);
51980
+ if (!parsed.success || !parsed.data.id.trim() || seen.has(parsed.data.id)) continue;
51981
+ seen.add(parsed.data.id);
51982
+ options.push(known.get(parsed.data.id) ?? { id: parsed.data.id, label: parsed.data.id });
51983
+ }
51984
+ if (options.length) catalog2 = withConfiguredModel(options);
51985
+ } catch {
51986
+ }
51987
+ };
51988
+ if (apiKey) void refreshModels();
51989
+ return createOpenAIChatRuntime({
51990
+ input,
51991
+ driverKind: "cerebras",
51992
+ apiKey,
51993
+ apiUrl: config2.url,
51994
+ tools: config2.tools,
51995
+ models: () => catalog2,
51996
+ refreshModels,
51997
+ reasoning: true,
51998
+ reasoningReplayField: "reasoning",
51999
+ computerUse: true,
52000
+ imageInput: (model) => !TEXT_ONLY_MODELS.test(model),
52001
+ nudgeAnnouncedAction: true,
52002
+ requestBody: (model, messages, stream) => ({
52003
+ model,
52004
+ messages,
52005
+ stream,
52006
+ ...stream ? { stream_options: { include_usage: true } } : {}
52007
+ }),
52008
+ httpErrorLabel: "Cerebras",
52009
+ missingKeyError: "Save a Cerebras API key in Settings \u2192 Connections, or set CEREBRAS_API_KEY.",
52010
+ unavailableReason: "No Cerebras API key \u2014 open Settings \u2192 Connections.",
52011
+ timeoutMs: 18e4,
52012
+ billing: "metered",
52013
+ includeUsageInCompleted: true,
52014
+ nativeLog: {
52015
+ source: "cerebras.chat.completions",
52016
+ outgoing: (_turn, messages, model) => ({ model, messageCount: messages.length }),
52017
+ incoming: ({ text: text4, reasoning, usage }) => ({ textLength: text4.length, reasoningLength: reasoning.length, usage })
52018
+ }
52019
+ });
52020
+ }
52021
+ };
52022
+ }
52023
+ });
52024
+
51509
52025
  // server/drivers/minimax.ts
51510
52026
  import { readFileSync as readFileSync25 } from "node:fs";
51511
52027
  import { homedir as homedir19 } from "node:os";
@@ -51517,14 +52033,14 @@ function normalizedApiUrl(value) {
51517
52033
  function loadLocalMiniMaxConfig(home = homedir19()) {
51518
52034
  try {
51519
52035
  const raw = localConfigSchema.parse(JSON.parse(readFileSync25(join42(home, ".mmx", "config.json"), "utf8")));
51520
- const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL3;
52036
+ const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL4;
51521
52037
  return {
51522
52038
  apiKey: raw.api_key?.trim() ?? "",
51523
52039
  url: normalizedApiUrl(configuredUrl),
51524
52040
  defaultModel: raw.default_text_model?.trim() ?? ""
51525
52041
  };
51526
52042
  } catch {
51527
- return { apiKey: "", url: DEFAULT_URL3, defaultModel: "" };
52043
+ return { apiKey: "", url: DEFAULT_URL4, defaultModel: "" };
51528
52044
  }
51529
52045
  }
51530
52046
  function decodeMinimaxConfig(raw) {
@@ -51534,10 +52050,10 @@ function decodeMinimaxConfig(raw) {
51534
52050
  const config2 = parsed.success ? parsed.data : {};
51535
52051
  return {
51536
52052
  ...tools !== void 0 ? { tools } : {},
51537
- url: normalizedApiUrl(config2.url?.trim() || process.env.MINIMAX_BASE_URL?.trim() || DEFAULT_URL3)
52053
+ url: normalizedApiUrl(config2.url?.trim() || process.env.MINIMAX_BASE_URL?.trim() || DEFAULT_URL4)
51538
52054
  };
51539
52055
  }
51540
- var DRIVER_KIND7, API_KEY_ENV, DEFAULT_URL3, CN_URL, MODELS4, localConfigSchema, driverConfigSchema, MinimaxDriver;
52056
+ var DRIVER_KIND7, API_KEY_ENV, DEFAULT_URL4, CN_URL, MODELS4, localConfigSchema, driverConfigSchema, MinimaxDriver;
51541
52057
  var init_minimax = __esm({
51542
52058
  "server/drivers/minimax.ts"() {
51543
52059
  "use strict";
@@ -51545,7 +52061,7 @@ var init_minimax = __esm({
51545
52061
  init_openai_chat();
51546
52062
  DRIVER_KIND7 = "minimax";
51547
52063
  API_KEY_ENV = "MINIMAX_API_KEY";
51548
- DEFAULT_URL3 = "https://api.minimax.io/v1";
52064
+ DEFAULT_URL4 = "https://api.minimax.io/v1";
51549
52065
  CN_URL = "https://api.minimaxi.com/v1";
51550
52066
  MODELS4 = {
51551
52067
  default: "MiniMax-M3",
@@ -51583,7 +52099,7 @@ var init_minimax = __esm({
51583
52099
  async create(input) {
51584
52100
  const local = loadLocalMiniMaxConfig();
51585
52101
  const apiKey = input.environment[API_KEY_ENV]?.trim() || process.env[API_KEY_ENV]?.trim() || local.apiKey;
51586
- const apiUrl = input.config.url === DEFAULT_URL3 && local.url !== DEFAULT_URL3 ? local.url : input.config.url;
52102
+ const apiUrl = input.config.url === DEFAULT_URL4 && local.url !== DEFAULT_URL4 ? local.url : input.config.url;
51587
52103
  const models2 = local.defaultModel && MODELS4.options.some((model) => model.id === local.defaultModel) ? { ...MODELS4, default: local.defaultModel } : MODELS4;
51588
52104
  return createOpenAIChatRuntime({
51589
52105
  input,
@@ -51640,6 +52156,7 @@ var init_builtIn = __esm({
51640
52156
  init_openai_compat();
51641
52157
  init_pi();
51642
52158
  init_mistral();
52159
+ init_cerebras();
51643
52160
  init_minimax();
51644
52161
  BUILT_IN_DRIVERS = [
51645
52162
  GrokDriver,
@@ -51659,7 +52176,8 @@ var init_builtIn = __esm({
51659
52176
  AntigravityDriver,
51660
52177
  BoatAgentDriver,
51661
52178
  MinimaxDriver,
51662
- MistralDriver
52179
+ MistralDriver,
52180
+ CerebrasDriver
51663
52181
  ];
51664
52182
  }
51665
52183
  });
@@ -52280,7 +52798,7 @@ async function closeBrowserSession(binaryPath, env, timeoutMs = 15e3) {
52280
52798
  const closeEnv = Object.fromEntries(Object.entries(env).filter(([key]) => !key.startsWith("AGENT_BROWSER_") || ["AGENT_BROWSER_SESSION", "AGENT_BROWSER_SOCKET_DIR", "AGENT_BROWSER_NAMESPACE", "AGENT_BROWSER_ENCRYPTION_KEY", "AGENT_BROWSER_RESTORE", "AGENT_BROWSER_RESTORE_SAVE"].includes(key)));
52281
52799
  closeEnv.AGENT_BROWSER_CONFIG = configPath;
52282
52800
  const deadline = Date.now() + timeoutMs;
52283
- const run2 = (args, capture = false) => new Promise((done) => {
52801
+ const run3 = (args, capture = false) => new Promise((done) => {
52284
52802
  let settled = false;
52285
52803
  let output = "";
52286
52804
  const finish = (ok) => {
@@ -52327,9 +52845,9 @@ async function closeBrowserSession(binaryPath, env, timeoutMs = 15e3) {
52327
52845
  finish(code === 0);
52328
52846
  });
52329
52847
  });
52330
- if (!(await run2(["close"])).ok) return false;
52848
+ if (!(await run3(["close"])).ok) return false;
52331
52849
  while (Date.now() < deadline) {
52332
- const status = await run2(["session", "list", "--json"], true);
52850
+ const status = await run3(["session", "list", "--json"], true);
52333
52851
  if (!status.ok) return false;
52334
52852
  try {
52335
52853
  const result = JSON.parse(status.output);
@@ -52720,7 +53238,7 @@ var init_browser_engine = __esm({
52720
53238
  var import_qrcode_terminal = __toESM(require_main(), 1);
52721
53239
  import { spawn as spawn9 } from "node:child_process";
52722
53240
  import { randomBytes as randomBytes5 } from "node:crypto";
52723
- import { closeSync as closeSync7, existsSync as existsSync19, mkdirSync as mkdirSync24, openSync as openSync7, readFileSync as readFileSync28 } from "node:fs";
53241
+ import { closeSync as closeSync8, existsSync as existsSync19, mkdirSync as mkdirSync24, openSync as openSync8, readFileSync as readFileSync28 } from "node:fs";
52724
53242
  import { homedir as homedir21 } from "node:os";
52725
53243
  import { dirname as dirname16, join as join47, resolve as resolve13 } from "node:path";
52726
53244
  import { createInterface as createInterface2 } from "node:readline/promises";
@@ -52763,15 +53281,30 @@ var validClientInstance = (value) => {
52763
53281
  return input !== null && CLIENT_INSTANCE.test(input);
52764
53282
  };
52765
53283
  var boundedSecret = (value, maximum = 8192) => typeof value === "string" && value.length >= 20 && value.length <= maximum && /^\S+$/.test(value) ? value : null;
53284
+ var MAX_RETRY_AFTER_MS = 24 * 60 * 60 * 1e3;
52766
53285
  var ControlPlaneError = class extends Error {
52767
- constructor(code, status = 0, requestId = "") {
53286
+ constructor(code, status = 0, requestId = "", retryAfterMs = 0) {
52768
53287
  super(code);
52769
53288
  this.name = "ControlPlaneError";
52770
53289
  this.code = code;
52771
53290
  this.status = status;
52772
53291
  this.requestId = REQUEST_ID.test(requestId) ? requestId : "";
53292
+ this.retryAfterMs = Number.isSafeInteger(retryAfterMs) && retryAfterMs > 0 ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS) : 0;
52773
53293
  }
52774
53294
  };
53295
+ function retryAfterMilliseconds(value) {
53296
+ const input = stringValue(value)?.trim() ?? "";
53297
+ if (!/^[0-9]{1,6}$/.test(input)) return 0;
53298
+ return Math.min(Number(input) * 1e3, MAX_RETRY_AFTER_MS);
53299
+ }
53300
+ var ENDPOINT_STATUSES = /* @__PURE__ */ new Set([
53301
+ "pending",
53302
+ "provisioning",
53303
+ "ready",
53304
+ "deleting",
53305
+ "deleted",
53306
+ "error"
53307
+ ]);
52775
53308
  function statusErrorCode(status) {
52776
53309
  if (status === 400 || status === 422) return "invalid_request";
52777
53310
  if (status === 401) return "unauthorized";
@@ -52883,7 +53416,8 @@ function createControlPlaneClient({
52883
53416
  throw new ControlPlaneError(
52884
53417
  code ?? statusErrorCode(response.status),
52885
53418
  response.status,
52886
- response.headers.get("x-request-id") ?? ""
53419
+ response.headers.get("x-request-id") ?? "",
53420
+ retryAfterMilliseconds(response.headers.get("retry-after"))
52887
53421
  );
52888
53422
  }
52889
53423
  if (!allowEmpty && !plainObject(payload)) {
@@ -53004,6 +53538,25 @@ function createControlPlaneClient({
53004
53538
  if (!endpoint || !connectorToken) throw new ControlPlaneError("invalid_response");
53005
53539
  return { endpoint, connectorToken };
53006
53540
  },
53541
+ /** The server's view of this installation's endpoint, or null when it
53542
+ * has none (never allocated, or removed by owner cleanup or idle
53543
+ * reclaim). Never returns or requests a connector token. */
53544
+ async getEndpoint(installationCredential) {
53545
+ if (typeof installationCredential !== "string" || !INSTALLATION_CREDENTIAL.test(installationCredential)) {
53546
+ throw new ControlPlaneError("signed_out", 401);
53547
+ }
53548
+ const { payload } = await request("/v1/installations/self/endpoint", {
53549
+ token: installationCredential
53550
+ });
53551
+ if (payload.endpoint === null) return null;
53552
+ const endpoint = validatedEndpoint(payload.endpoint);
53553
+ if (!endpoint) throw new ControlPlaneError("invalid_response");
53554
+ const status = stringValue(plainObject(payload.endpoint)?.status);
53555
+ return {
53556
+ url: endpoint.url,
53557
+ status: status !== null && ENDPOINT_STATUSES.has(status) ? status : "unknown"
53558
+ };
53559
+ },
53007
53560
  async deleteEndpoint(installationCredential) {
53008
53561
  if (typeof installationCredential !== "string" || !INSTALLATION_CREDENTIAL.test(installationCredential)) {
53009
53562
  throw new ControlPlaneError("signed_out", 401);
@@ -53651,6 +54204,26 @@ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]
53651
54204
  var INSTALLATION_ID2 = UUID;
53652
54205
  var INSTALLATION_CREDENTIAL2 = /^omb_install_[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}$/;
53653
54206
  var DEFAULT_HEALTH_CACHE_MS = 3e4;
54207
+ var DEFAULT_AUTO_RETRY_BASE_MS = 10 * 6e4;
54208
+ var DEFAULT_AUTO_RETRY_MAX_MS = 6 * 60 * 6e4;
54209
+ var DEFAULT_ENDPOINT_CHECK_INTERVAL_MS = 15 * 6e4;
54210
+ var DEFAULT_FIRST_ENDPOINT_CHECK_MS = 2 * 6e4;
54211
+ var AUTO_RETRY_CODES = /* @__PURE__ */ new Set([
54212
+ "endpoint_capacity",
54213
+ "endpoint_unavailable",
54214
+ "endpoint_busy",
54215
+ "network_unavailable",
54216
+ "control_plane_unavailable",
54217
+ "internal_error",
54218
+ "rate_limited",
54219
+ "credential_rotation_rate_limited"
54220
+ ]);
54221
+ var ENDPOINT_NEEDS_RECONCILE = /* @__PURE__ */ new Set(["deleting", "deleted", "error"]);
54222
+ var defaultSetTimer = (callback, milliseconds) => {
54223
+ const timer = setTimeout(callback, milliseconds);
54224
+ timer.unref?.();
54225
+ return timer;
54226
+ };
53654
54227
  var ownString = (document, field) => typeof document?.[field] === "string" ? document[field] : "";
53655
54228
  function resolveCompanionControlPlaneURL({
53656
54229
  isPackaged,
@@ -53747,6 +54320,7 @@ var FRIENDLY_MESSAGES = Object.freeze({
53747
54320
  installation_limit_reached: "This account has reached its computer limit. Remove an old computer and try again.",
53748
54321
  installation_exists: "This computer is already connected. Try again to recover it.",
53749
54322
  endpoint_busy: "The secure connection is still being prepared. Try again in a moment.",
54323
+ endpoint_capacity: "Secure HTTPS links are temporarily full. Pair on this Wi-Fi or with Tailscale for now; we'll retry automatically.",
53750
54324
  endpoint_unavailable: "The secure connection service could not finish setup. Local Wi-Fi and Tailscale pairing still work. If this keeps happening, contact support with the error reference.",
53751
54325
  endpoint_cleanup_pending: "The secure connection is still being removed. Try signing out again shortly.",
53752
54326
  control_plane_unavailable: "Secure access is not available right now. Local pairing still works.",
@@ -53790,7 +54364,19 @@ function createCompanionAccountService({
53790
54364
  managedConnectionState = () => ({ status: "stopped", ready: false }),
53791
54365
  companionIsOn = () => false,
53792
54366
  now = Date.now,
53793
- healthCacheMs = DEFAULT_HEALTH_CACHE_MS
54367
+ healthCacheMs = DEFAULT_HEALTH_CACHE_MS,
54368
+ // Background recovery is opt-in: the desktop app enables it, one-shot CLI
54369
+ // commands do not. When on, retryable setup failures are retried with
54370
+ // backoff, and a saved address whose server endpoint was removed (idle
54371
+ // reclaim) is re-provisioned without a new sign-in.
54372
+ autoRecover = false,
54373
+ setTimer = defaultSetTimer,
54374
+ clearTimer = clearTimeout,
54375
+ random = Math.random,
54376
+ autoRetryBaseMs = DEFAULT_AUTO_RETRY_BASE_MS,
54377
+ autoRetryMaxMs = DEFAULT_AUTO_RETRY_MAX_MS,
54378
+ endpointCheckIntervalMs = DEFAULT_ENDPOINT_CHECK_INTERVAL_MS,
54379
+ firstEndpointCheckMs = DEFAULT_FIRST_ENDPOINT_CHECK_MS
53794
54380
  } = {}) {
53795
54381
  const configured = Boolean(client);
53796
54382
  let healthy = false;
@@ -53798,6 +54384,11 @@ function createCompanionAccountService({
53798
54384
  let healthProbe = null;
53799
54385
  let phase = null;
53800
54386
  let transition = Promise.resolve();
54387
+ let autoRetryTimer = null;
54388
+ let autoRetryAttempt = 0;
54389
+ let watchdogTimer = null;
54390
+ let lastEndpointCheck = null;
54391
+ let disposed = false;
53801
54392
  const serialize = (work) => {
53802
54393
  const next = transition.then(work, work);
53803
54394
  transition = next.then(
@@ -53809,6 +54400,34 @@ function createCompanionAccountService({
53809
54400
  return next;
53810
54401
  };
53811
54402
  const credentials = () => readCredentials?.() ?? {};
54403
+ const cancelAutoRetry = () => {
54404
+ if (autoRetryTimer !== null) clearTimer(autoRetryTimer);
54405
+ autoRetryTimer = null;
54406
+ };
54407
+ const resetAutoRetry = () => {
54408
+ cancelAutoRetry();
54409
+ autoRetryAttempt = 0;
54410
+ };
54411
+ let retryFromTimer = () => Promise.resolve();
54412
+ const scheduleAutoRetry = (error51) => {
54413
+ if (!autoRecover || disposed || !configured) return;
54414
+ if (!(error51 instanceof ControlPlaneError) || !AUTO_RETRY_CODES.has(error51.code)) return;
54415
+ const document = credentials();
54416
+ if (!storedAccount(document) || companionAccountCleanupPending(document)) return;
54417
+ cancelAutoRetry();
54418
+ const backoff = Math.min(
54419
+ autoRetryMaxMs,
54420
+ autoRetryBaseMs * 2 ** Math.min(autoRetryAttempt, 20)
54421
+ );
54422
+ const jittered = Math.round(backoff * (0.8 + 0.4 * random()));
54423
+ const delay2 = Math.min(autoRetryMaxMs, Math.max(jittered, error51.retryAfterMs ?? 0));
54424
+ autoRetryAttempt += 1;
54425
+ autoRetryTimer = setTimer(() => {
54426
+ autoRetryTimer = null;
54427
+ void retryFromTimer().catch(() => {
54428
+ });
54429
+ }, delay2);
54430
+ };
53812
54431
  const probeControlPlane = async ({ force = false } = {}) => {
53813
54432
  if (!configured) return false;
53814
54433
  const checkedAt = now();
@@ -54002,6 +54621,7 @@ function createCompanionAccountService({
54002
54621
  });
54003
54622
  throw error51;
54004
54623
  }
54624
+ resetAutoRetry();
54005
54625
  const connection = await activatePersistedEndpoint();
54006
54626
  phase = null;
54007
54627
  if (companionIsOn() && connection && ["unavailable", "error"].includes(connection.status)) {
@@ -54086,6 +54706,7 @@ function createCompanionAccountService({
54086
54706
  email: authenticatedPersisted ? verified.user.email : previous?.email ?? email3,
54087
54707
  expiredSessionIsSignedOut: authenticatedPersisted
54088
54708
  });
54709
+ if (authenticatedPersisted) scheduleAutoRetry(error51);
54089
54710
  return settledState();
54090
54711
  }
54091
54712
  });
@@ -54115,11 +54736,48 @@ function createCompanionAccountService({
54115
54736
  });
54116
54737
  } catch (error51) {
54117
54738
  failAction(error51, { email: account.email, expiredSessionIsSignedOut: true });
54739
+ scheduleAutoRetry(error51);
54118
54740
  return settledState();
54119
54741
  }
54120
54742
  };
54121
54743
  const retry = () => serialize(retryWork);
54744
+ retryFromTimer = retry;
54745
+ const reconcileIfEndpointGone = async () => {
54746
+ const document = credentials();
54747
+ const account = storedAccount(document);
54748
+ const access2 = managedCompanionTunnelAccess(document);
54749
+ if (!account || !access2 || !account.installationCredential || companionAccountCleanupPending(document) || typeof client.getEndpoint !== "function") {
54750
+ return null;
54751
+ }
54752
+ lastEndpointCheck = now();
54753
+ let endpoint;
54754
+ try {
54755
+ endpoint = await client.getEndpoint(account.installationCredential);
54756
+ } catch {
54757
+ return null;
54758
+ }
54759
+ if (endpoint && endpoint.url === access2.endpoint && !ENDPOINT_NEEDS_RECONCILE.has(endpoint.status)) {
54760
+ return null;
54761
+ }
54762
+ return retryWork();
54763
+ };
54764
+ const watchdogTick = () => {
54765
+ watchdogTimer = null;
54766
+ if (disposed) return;
54767
+ watchdogTimer = setTimer(watchdogTick, endpointCheckIntervalMs);
54768
+ if (!companionIsOn() || autoRetryTimer !== null) return;
54769
+ const connection = managedConnectionState?.() ?? {};
54770
+ if (!["retrying", "error"].includes(connection.status)) return;
54771
+ if (lastEndpointCheck !== null && now() - lastEndpointCheck < endpointCheckIntervalMs) return;
54772
+ void serialize(reconcileIfEndpointGone).catch(() => {
54773
+ });
54774
+ };
54775
+ const startWatchdog = () => {
54776
+ if (!autoRecover || disposed || !configured || watchdogTimer !== null) return;
54777
+ watchdogTimer = setTimer(watchdogTick, firstEndpointCheckMs);
54778
+ };
54122
54779
  const signOut = () => serialize(async () => {
54780
+ resetAutoRetry();
54123
54781
  if (!storedAccount(credentials())) {
54124
54782
  await clearAfterCleanup();
54125
54783
  return settledState();
@@ -54138,14 +54796,25 @@ function createCompanionAccountService({
54138
54796
  });
54139
54797
  const restore = () => serialize(async () => {
54140
54798
  if (!configured) return settledState();
54799
+ startWatchdog();
54141
54800
  if (!await probeControlPlane({ force: true })) return settledState();
54142
54801
  if (companionAccountCleanupPending(credentials())) return retryWork();
54143
54802
  await ensureClientIdentity();
54144
54803
  const account = storedAccount(credentials());
54145
54804
  if (account && !managedCompanionTunnelAccess(credentials())) return retryWork();
54805
+ if (autoRecover && account && companionIsOn()) {
54806
+ const reconciled = await reconcileIfEndpointGone();
54807
+ if (reconciled) return reconciled;
54808
+ }
54146
54809
  phase = null;
54147
54810
  return settledState();
54148
54811
  });
54812
+ const dispose = () => {
54813
+ disposed = true;
54814
+ cancelAutoRetry();
54815
+ if (watchdogTimer !== null) clearTimer(watchdogTimer);
54816
+ watchdogTimer = null;
54817
+ };
54149
54818
  return Object.freeze({
54150
54819
  state: async () => {
54151
54820
  await probeControlPlane();
@@ -54155,7 +54824,8 @@ function createCompanionAccountService({
54155
54824
  verifyCode,
54156
54825
  retry,
54157
54826
  signOut,
54158
- restore
54827
+ restore,
54828
+ dispose
54159
54829
  });
54160
54830
  }
54161
54831
 
@@ -54273,7 +54943,7 @@ function isSecretParameter(name) {
54273
54943
  return isSecretName2(bare) || /^(?:auth|sig|signature|pass|pwd|code)$/i.test(bare);
54274
54944
  }
54275
54945
  function keyLikeSegment(segment) {
54276
- return (segment.match(/[A-Za-z0-9]{16,}/g) ?? []).some((run2) => /\d/.test(run2) && /[A-Za-z]/.test(run2));
54946
+ return (segment.match(/[A-Za-z0-9]{16,}/g) ?? []).some((run3) => /\d/.test(run3) && /[A-Za-z]/.test(run3));
54277
54947
  }
54278
54948
  function maskUrlSecrets(text4) {
54279
54949
  return text4.replace(/\b([a-z][\w+.-]*:\/\/)([^\s/?#]*)([^\s?#]*)([^\s]*)/gi, (_all, scheme, authority, path3, rest) => {
@@ -56932,7 +57602,7 @@ async function runServe(options, log = console.log) {
56932
57602
  const logsDir = join47(options.dataDir, "logs");
56933
57603
  mkdirSync24(logsDir, { recursive: true, mode: 448 });
56934
57604
  logPath = join47(logsDir, `server-${Date.now()}-${process.pid}.log`);
56935
- logFd = openSync7(logPath, "wx", 384);
57605
+ logFd = openSync8(logPath, "wx", 384);
56936
57606
  log("\nStarting your workspace\u2026");
56937
57607
  }
56938
57608
  if (tailscale) {
@@ -56963,7 +57633,7 @@ async function runServe(options, log = console.log) {
56963
57633
  }
56964
57634
  throw error51;
56965
57635
  } finally {
56966
- if (logFd !== void 0) closeSync7(logFd);
57636
+ if (logFd !== void 0) closeSync8(logFd);
56967
57637
  process.removeListener("SIGINT", cancelStartup);
56968
57638
  process.removeListener("SIGTERM", cancelStartup);
56969
57639
  }