openmausbot 0.1.87 → 0.1.89

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 (150) hide show
  1. package/README.md +2 -1
  2. package/dist/assets/{abnfDiagram-O67JEVCF-BTiuL79F.js → abnfDiagram-O67JEVCF-BwaMaZEm.js} +1 -1
  3. package/dist/assets/{arc-C88Blc2v.js → arc-Be-8GKxd.js} +1 -1
  4. package/dist/assets/{architectureDiagram-NJMV4G6O-BZsJIZrb.js → architectureDiagram-NJMV4G6O-Dp_U2Kv1.js} +1 -1
  5. package/dist/assets/{blockDiagram-BEXU5L5S-CVeMe9Ah.js → blockDiagram-BEXU5L5S-DQjUjYUk.js} +1 -1
  6. package/dist/assets/{c4Diagram-YGBWAQC7-Cedk3Bhx.js → c4Diagram-YGBWAQC7-Bd3aE4Kc.js} +1 -1
  7. package/dist/assets/channel-DqY_gMLg.js +1 -0
  8. package/dist/assets/{chunk-2Q5K7J3B-D-VRqzTB.js → chunk-2Q5K7J3B-BijjTBeJ.js} +1 -1
  9. package/dist/assets/{chunk-3FUC2YCW-BChov01Y.js → chunk-3FUC2YCW-CMGdwvzW.js} +2 -2
  10. package/dist/assets/{chunk-5VM5RSS4-C-TfwvHy.js → chunk-5VM5RSS4-0G09BfsZ.js} +1 -1
  11. package/dist/assets/{chunk-GTNCS2PH-Db7CGj5Y.js → chunk-GTNCS2PH-FOp3vSHe.js} +1 -1
  12. package/dist/assets/{chunk-GWA4HPMP-CNvIfqKT.js → chunk-GWA4HPMP-Dv-20VIn.js} +1 -1
  13. package/dist/assets/{chunk-JWPE2WC7-C_bEp5tq.js → chunk-JWPE2WC7-CRgrdFcz.js} +1 -1
  14. package/dist/assets/{chunk-NETBCI7D-Coi4BOls.js → chunk-NETBCI7D-3hP4f7QB.js} +1 -1
  15. package/dist/assets/{chunk-WEXAMYUT-CzylEuL3.js → chunk-WEXAMYUT-CG98IjlG.js} +1 -1
  16. package/dist/assets/{chunk-XXDRQBXY-m7p7ztaD.js → chunk-XXDRQBXY-BrSTC9xG.js} +1 -1
  17. package/dist/assets/{classDiagram-v2-NBCMYWYE-BMy6o9x2.js → classDiagram-v2-NBCMYWYE-rpQQQDFX.js} +1 -1
  18. package/dist/assets/{cose-bilkent-JH36ORCC-BDQ__0_J.js → cose-bilkent-JH36ORCC-G6RSafHA.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-Djv4ghUb.js → cynefin-OW5HDTMX-CXjv-IM2.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-VND7K2PF-BULrlGYH.js → cynefinDiagram-VND7K2PF-Dw07MR9f.js} +1 -1
  21. package/dist/assets/{dagre-6A5THRUB-D-jrT_hU.js → dagre-6A5THRUB-BwPnlTg2.js} +1 -1
  22. package/dist/assets/{diagram-22UHCM2B-p2UhQmfL.js → diagram-22UHCM2B-Cm6xcMiz.js} +1 -1
  23. package/dist/assets/{diagram-3UASUU5V-DHvGWZ7q.js → diagram-3UASUU5V-CDpogPbE.js} +1 -1
  24. package/dist/assets/{diagram-ATOU4E4O-ChhKDot-.js → diagram-ATOU4E4O-BV8z3Pnc.js} +1 -1
  25. package/dist/assets/{diagram-CDSNMT55-B-T7A43R.js → diagram-CDSNMT55-CKCQhOOC.js} +1 -1
  26. package/dist/assets/{diagram-MLGK6HIB-CJm8ENNY.js → diagram-MLGK6HIB-FGvJg69C.js} +1 -1
  27. package/dist/assets/{diagram-MPIPVDR6-510tlza8.js → diagram-MPIPVDR6-CwATvgnS.js} +1 -1
  28. package/dist/assets/{ebnfDiagram-ZINNZB2B-PkvRPHel.js → ebnfDiagram-ZINNZB2B-CH_c3SSC.js} +1 -1
  29. package/dist/assets/{elk-276RUBZZ-BVBVPoX3.js → elk-276RUBZZ-3guZi71R.js} +1 -1
  30. package/dist/assets/{erDiagram-OPXOYQCR-BgYMVm7L.js → erDiagram-OPXOYQCR-BMHphR2n.js} +1 -1
  31. package/dist/assets/{flowDiagram-KWPJA3E3-BGqsnc_3.js → flowDiagram-KWPJA3E3-Vm5DkW5x.js} +1 -1
  32. package/dist/assets/{ganttDiagram-FUAMR5RP-D_-UeReH.js → ganttDiagram-FUAMR5RP-DO-1ct07.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-X574FWY7-D0uooONx.js → gitGraphDiagram-X574FWY7-nxeksEIN.js} +1 -1
  34. package/dist/assets/index-BIMdIbaw.css +1 -0
  35. package/dist/assets/index-BVPdbq2r.js +583 -0
  36. package/dist/assets/{index-zRsJERGJ.js → index-Oxk9x45i.js} +1 -1
  37. package/dist/assets/{infoDiagram-VRGFBTTK-B9e1z77f.js → infoDiagram-VRGFBTTK-Dkcvr0p7.js} +1 -1
  38. package/dist/assets/{ishikawaDiagram-OU5B5YK6-BB4vNwvD.js → ishikawaDiagram-OU5B5YK6-BwVibkoO.js} +1 -1
  39. package/dist/assets/{journeyDiagram-ZHPQQLJL-DpkosOTB.js → journeyDiagram-ZHPQQLJL-ByiBZqgG.js} +1 -1
  40. package/dist/assets/{kanban-definition-PNTS6WVX-DbDP4kUu.js → kanban-definition-PNTS6WVX-qqjgXBF3.js} +1 -1
  41. package/dist/assets/{linear-DBJGPmjT.js → linear-CtJnKe6A.js} +1 -1
  42. package/dist/assets/{mermaid.core-DWPRi-ue.js → mermaid.core-BEiDklrY.js} +5 -5
  43. package/dist/assets/{mindmap-definition-NLK3R4M7-nszBbJmx.js → mindmap-definition-NLK3R4M7-DfIYHia9.js} +1 -1
  44. package/dist/assets/{pegDiagram-GJSIUBJH-CWykJWu_.js → pegDiagram-GJSIUBJH-CYyRqa1a.js} +1 -1
  45. package/dist/assets/{pieDiagram-5QR66LMP-g3Da-Vnv.js → pieDiagram-5QR66LMP-hOToy_zH.js} +1 -1
  46. package/dist/assets/{quadrantDiagram-O4NWA36T-Wzrlpzgh.js → quadrantDiagram-O4NWA36T-DKAsNN9I.js} +1 -1
  47. package/dist/assets/{railroadDiagram-XR7U4H2S-moHTFMC0.js → railroadDiagram-XR7U4H2S-BSA-d8JU.js} +1 -1
  48. package/dist/assets/{requirementDiagram-PLB6GJNP-CwfXdFH8.js → requirementDiagram-PLB6GJNP-CSmlvb-p.js} +1 -1
  49. package/dist/assets/{sankeyDiagram-IPEJSGJF-3ZROYvnq.js → sankeyDiagram-IPEJSGJF-CD5F6fnG.js} +1 -1
  50. package/dist/assets/{sequenceDiagram-PO4LG4MO-E1TBebP8.js → sequenceDiagram-PO4LG4MO-JUq5sx6d.js} +1 -1
  51. package/dist/assets/{sizeCapture-INFHLROL-DHKEU5pu.js → sizeCapture-INFHLROL-BjIS-Stk.js} +1 -1
  52. package/dist/assets/{stateDiagram-v2-GCMORJYK-BguEj09D.js → stateDiagram-v2-GCMORJYK-DklkUxOg.js} +1 -1
  53. package/dist/assets/{swimlanes-2SLR337P-C7IDYnhd.js → swimlanes-2SLR337P-CsjIU6be.js} +1 -1
  54. package/dist/assets/swimlanesDiagram-TC7HE7FX-Bk14M0_B.js +8 -0
  55. package/dist/assets/{timeline-definition-EJHVYXUP-DCW-F5PW.js → timeline-definition-EJHVYXUP-BkFqK7AB.js} +1 -1
  56. package/dist/assets/{usecaseDiagram-POWQR4AR-xBMFtWYV.js → usecaseDiagram-POWQR4AR-70gJB1EU.js} +1 -1
  57. package/dist/assets/{vennDiagram-UO4OBE2U-B5nIkuH8.js → vennDiagram-UO4OBE2U-83Tm62dN.js} +1 -1
  58. package/dist/assets/{wardleyDiagram-VNRHLVJA-DYFAhdbr.js → wardleyDiagram-VNRHLVJA-tTmseoX9.js} +1 -1
  59. package/dist/assets/{xychartDiagram-PMCCYNJV-DeXwX6Cz.js → xychartDiagram-PMCCYNJV-R1LBA55a.js} +1 -1
  60. package/dist/index.html +2 -2
  61. package/dist-server/companion/src/routes.js +8 -1
  62. package/dist-server/connector-proxy.js +105 -1
  63. package/dist-server/container-mcp.js +11 -1
  64. package/dist-server/drivers/agents-proxy.js +277 -22
  65. package/dist-server/index.js +23722 -20272
  66. package/dist-server/local-computer-proxy.js +2 -0
  67. package/dist-server/mcp-gate.js +2 -0
  68. package/dist-server/mcp-server.js +5 -3
  69. package/dist-server/message-search.worker.js +52 -0
  70. package/dist-server/openmausbot.js +755 -404
  71. package/dist-server/pair-cli.js +755 -404
  72. package/dist-server/server/admission.js +81 -0
  73. package/dist-server/server/aside-queue.js +294 -0
  74. package/dist-server/server/attachments.js +43 -1
  75. package/dist-server/server/automatic-recovery.js +15 -0
  76. package/dist-server/server/bot-attachment.js +107 -0
  77. package/dist-server/server/bot-overview.js +19 -0
  78. package/dist-server/server/composio.js +297 -1
  79. package/dist-server/server/config.js +34 -3
  80. package/dist-server/server/connector-advertisement.js +165 -0
  81. package/dist-server/server/connector-proxy.js +14 -2
  82. package/dist-server/server/connector-verdict.js +31 -4
  83. package/dist-server/server/container-computer.js +70 -0
  84. package/dist-server/server/contracts.js +32 -0
  85. package/dist-server/server/drivers/acp/core.js +148 -23
  86. package/dist-server/server/drivers/acp/cursor.js +3 -7
  87. package/dist-server/server/drivers/acp/droid.js +4 -7
  88. package/dist-server/server/drivers/acp/gemini.js +4 -4
  89. package/dist-server/server/drivers/acp/grok.js +4 -9
  90. package/dist-server/server/drivers/acp/hermes.js +3 -5
  91. package/dist-server/server/drivers/acp/opencode-go.js +2 -5
  92. package/dist-server/server/drivers/acp/qwen.js +12 -15
  93. package/dist-server/server/drivers/agents-call.js +137 -10
  94. package/dist-server/server/drivers/agents-catalog.js +100 -6
  95. package/dist-server/server/drivers/boxagent.js +3 -17
  96. package/dist-server/server/drivers/claude.js +31 -17
  97. package/dist-server/server/drivers/codex-catalog.js +14 -6
  98. package/dist-server/server/drivers/codex-identity.js +2 -1
  99. package/dist-server/server/drivers/codex-release.js +48 -0
  100. package/dist-server/server/drivers/codex.js +7 -25
  101. package/dist-server/server/drivers/openai-chat.js +31 -3
  102. package/dist-server/server/drivers/pi.js +237 -30
  103. package/dist-server/server/drivers/prompt-split.js +32 -9
  104. package/dist-server/server/drivers/session-idle.js +29 -0
  105. package/dist-server/server/env-path.js +15 -0
  106. package/dist-server/server/guarded-requests.js +2 -2
  107. package/dist-server/server/harness-capabilities.js +61 -0
  108. package/dist-server/server/index.js +1173 -262
  109. package/dist-server/server/message-db.js +22 -42
  110. package/dist-server/server/message-file.js +9 -1
  111. package/dist-server/server/message-search-query.js +43 -0
  112. package/dist-server/server/message-search-worker.js +80 -0
  113. package/dist-server/server/message-search.worker.js +21 -0
  114. package/dist-server/server/model-requests.js +248 -0
  115. package/dist-server/server/options-card.js +21 -0
  116. package/dist-server/server/org-library.js +15 -1
  117. package/dist-server/server/peer-delivery.js +60 -0
  118. package/dist-server/server/peer-roster.js +57 -0
  119. package/dist-server/server/presets.js +32 -32
  120. package/dist-server/server/profile-requests.js +66 -39
  121. package/dist-server/server/profile-revision.js +9 -1
  122. package/dist-server/server/profile-versions.js +6 -3
  123. package/dist-server/server/routine-requests.js +29 -7
  124. package/dist-server/server/skill-fetch.js +72 -14
  125. package/dist-server/server/skills.js +1 -0
  126. package/dist-server/server/store.js +32 -2
  127. package/dist-server/server/system-prompt.js +37 -2
  128. package/dist-server/server/team-setup-requests.js +124 -23
  129. package/dist-server/server/thread-events.js +3 -1
  130. package/dist-server/server/tightening-requests.js +405 -0
  131. package/dist-server/server/tts/chatterbox.js +27 -5
  132. package/dist-server/server/usage-ledger.js +7 -1
  133. package/dist-server/server/webhooks.js +21 -3
  134. package/dist-server/server/workspace-backup.js +40 -3
  135. package/dist-server/server/workspace-backup.worker.js +10 -0
  136. package/dist-server/shared/ask-question.js +19 -0
  137. package/dist-server/shared/credential-request.js +5 -1
  138. package/dist-server/shared/line-diff.js +23 -0
  139. package/dist-server/shared/model-request.js +1 -0
  140. package/dist-server/shared/options-card.js +51 -0
  141. package/dist-server/shared/profile-request.js +5 -4
  142. package/dist-server/shared/tightening-request.js +87 -0
  143. package/dist-server/src/lib/composer-attachments.js +42 -3
  144. package/dist-server/vps-container-mcp.js +11 -1
  145. package/dist-server/workspace-backup.worker.js +19482 -0
  146. package/package.json +1 -1
  147. package/dist/assets/channel-taCNRlUU.js +0 -1
  148. package/dist/assets/index-BwdMAQ6p.css +0 -1
  149. package/dist/assets/index-ByJennr7.js +0 -581
  150. package/dist/assets/swimlanesDiagram-TC7HE7FX-BpEdoB2e.js +0 -8
@@ -17791,10 +17791,26 @@ var init_wire = __esm({
17791
17791
  function isModelVariant(value) {
17792
17792
  return typeof value === "string" && value.length > 0 && value.length <= 256 && value.trim() === value && !new RegExp("\\p{Cc}", "u").test(value);
17793
17793
  }
17794
- var eventCounter, newEventId, newId;
17794
+ function titleCaseModelId(id, wordBreaks) {
17795
+ return id.split(wordBreaks).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
17796
+ }
17797
+ function qualifiedModelLabel(id, qualifier, options = {}) {
17798
+ const { redundantWhen = () => false, decorate = (facet) => ` \u2014 ${facet}` } = options;
17799
+ if (!qualifier || redundantWhen(id, qualifier)) return id;
17800
+ return `${id}${decorate(qualifier)}`;
17801
+ }
17802
+ var TurnNotStartedError, eventCounter, newEventId, newId;
17795
17803
  var init_contracts = __esm({
17796
17804
  "server/contracts.ts"() {
17797
17805
  "use strict";
17806
+ TurnNotStartedError = class extends Error {
17807
+ turnId;
17808
+ constructor(turnId, message2) {
17809
+ super(message2);
17810
+ this.name = "TurnNotStartedError";
17811
+ this.turnId = turnId;
17812
+ }
17813
+ };
17798
17814
  eventCounter = 0;
17799
17815
  newEventId = () => `ev-${Date.now().toString(36)}-${(eventCounter++).toString(36)}`;
17800
17816
  newId = () => crypto.randomUUID();
@@ -18923,7 +18939,16 @@ function loadConfig() {
18923
18939
  let cfg = {};
18924
18940
  try {
18925
18941
  cfg = parseStoredConfig(parseJson(readFileSync3(join9(DATA_DIR, "config.json"), "utf8")));
18926
- } catch {
18942
+ lastIgnoredConfigWarning = "";
18943
+ } catch (error51) {
18944
+ if (error51?.code !== "ENOENT") {
18945
+ const reason = error51 instanceof SyntaxError ? "invalid JSON" : error51 instanceof Error ? error51.message : "unable to read configuration";
18946
+ const warning = `config: ignoring ${join9(DATA_DIR, "config.json")} and using defaults: ${reason}`;
18947
+ if (warning !== lastIgnoredConfigWarning) console.warn(warning);
18948
+ lastIgnoredConfigWarning = warning;
18949
+ } else {
18950
+ lastIgnoredConfigWarning = "";
18951
+ }
18927
18952
  }
18928
18953
  cfg.mistral = { ...cfg.mistral };
18929
18954
  if (process.env.MISTRAL_API_KEY !== void 0) cfg.mistral.key = process.env.MISTRAL_API_KEY;
@@ -18995,6 +19020,7 @@ function saveConfig(patch, options = {}) {
18995
19020
  if (checkedPatch.language !== void 0) disk.language = checkedPatch.language;
18996
19021
  if (checkedPatch.customDomain !== void 0) disk.customDomain = checkedPatch.customDomain;
18997
19022
  if (checkedPatch.signIn !== void 0) disk.signIn = checkedPatch.signIn;
19023
+ if (checkedPatch.automaticRecovery !== void 0) disk.automaticRecovery = checkedPatch.automaticRecovery;
18998
19024
  if (checkedPatch.defaultModelSelection !== void 0) {
18999
19025
  disk.defaultModelSelection = checkedPatch.defaultModelSelection;
19000
19026
  if (disk.newBotDefaults) {
@@ -19150,7 +19176,7 @@ function instanceConfigs(cfg) {
19150
19176
  }
19151
19177
  return map3;
19152
19178
  }
19153
- var optionalText, SSH_ALIAS, LEGACY_BROWSER_PROFILE_ID, BROWSER_PROFILE_ID, MIN_ROOM_TURN_TIMEOUT_MINUTES, MAX_ROOM_TURN_TIMEOUT_MINUTES, DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES, DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES, DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES, DEFAULT_MAX_CONCURRENT_BOT_THREADS, MAX_CONCURRENT_BOT_THREADS, MIN_THREAD_EVENT_LOG_BYTES, MAX_THREAD_EVENT_LOG_BYTES, MIN_LOCAL_VM_MAX_INSTANCES, MAX_LOCAL_VM_MAX_INSTANCES, CDP_PORT, CDP_URL, vpsConfigSchema, browserEngineConfigSchema, roomConfigSchema, localVmConfigSchema, browserProfileSchema, legacyBrowserProfileSchema, legacyBrowserProfilesSchema, storedBrowserProfilesSchema, browserProfilesSchema, featureConfigSchema, onboardingConfigSchema, instanceConfigSchema, instanceConfigMapSchema, defaultModelSelectionSchema, threadsConfigSchema, newBotsConfigSchema, newBotsPatchSchema, threadsPatchSchema, appConfigSchema, storedAppConfigSchema, appConfigPatchSchema, jsonObjectSchema, DATA_DIR, LEGACY_DATA_DIR, EVENTS_DIR, NATIVE_DIR, WORKSPACE_CREDENTIAL_ENV, CONTROL_PLANE_ENV, CONTROL_PLANE_ENV_PREFIX, PROVIDER_CREDENTIAL_ENV, configSaveListeners;
19179
+ var optionalText, SSH_ALIAS, LEGACY_BROWSER_PROFILE_ID, BROWSER_PROFILE_ID, MIN_ROOM_TURN_TIMEOUT_MINUTES, MAX_ROOM_TURN_TIMEOUT_MINUTES, DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES, DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES, DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES, DEFAULT_MAX_CONCURRENT_BOT_THREADS, MAX_CONCURRENT_BOT_THREADS, MIN_THREAD_EVENT_LOG_BYTES, MAX_THREAD_EVENT_LOG_BYTES, MIN_LOCAL_VM_MAX_INSTANCES, MAX_LOCAL_VM_MAX_INSTANCES, CDP_PORT, CDP_URL, vpsConfigSchema, browserEngineConfigSchema, roomConfigSchema, localVmConfigSchema, browserProfileSchema, legacyBrowserProfileSchema, legacyBrowserProfilesSchema, storedBrowserProfilesSchema, browserProfilesSchema, featureConfigSchema, onboardingConfigSchema, instanceConfigSchema, instanceConfigMapSchema, defaultModelSelectionSchema, automaticRecoverySchema, threadsConfigSchema, newBotsConfigSchema, newBotsPatchSchema, threadsPatchSchema, appConfigSchema, storedAppConfigSchema, appConfigPatchSchema, jsonObjectSchema, DATA_DIR, LEGACY_DATA_DIR, EVENTS_DIR, NATIVE_DIR, lastIgnoredConfigWarning, WORKSPACE_CREDENTIAL_ENV, CONTROL_PLANE_ENV, CONTROL_PLANE_ENV_PREFIX, PROVIDER_CREDENTIAL_ENV, configSaveListeners;
19154
19180
  var init_config = __esm({
19155
19181
  "server/config.ts"() {
19156
19182
  "use strict";
@@ -19177,7 +19203,7 @@ var init_config = __esm({
19177
19203
  MIN_THREAD_EVENT_LOG_BYTES = 256 * 1024;
19178
19204
  MAX_THREAD_EVENT_LOG_BYTES = 4 * 1024 * 1024 * 1024;
19179
19205
  MIN_LOCAL_VM_MAX_INSTANCES = 1;
19180
- MAX_LOCAL_VM_MAX_INSTANCES = 4;
19206
+ MAX_LOCAL_VM_MAX_INSTANCES = 8;
19181
19207
  CDP_PORT = /^[0-9]{1,5}$/;
19182
19208
  CDP_URL = /^(https?|wss?):\/\/\S+$/i;
19183
19209
  vpsConfigSchema = external_exports.object({
@@ -19290,6 +19316,13 @@ var init_config = __esm({
19290
19316
  (selection) => selection.variant === void 0 || selection.effort === void 0,
19291
19317
  "choose either a model variant or an effort level"
19292
19318
  );
19319
+ automaticRecoverySchema = external_exports.object({
19320
+ enabled: external_exports.boolean(),
19321
+ backup: defaultModelSelectionSchema.optional()
19322
+ }).strict().refine(
19323
+ (recovery) => !recovery.enabled || recovery.backup !== void 0,
19324
+ { message: "Choose a backup model before enabling automatic recovery", path: ["backup"] }
19325
+ );
19293
19326
  threadsConfigSchema = external_exports.object({
19294
19327
  maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS),
19295
19328
  /** Cap each per-thread events/ and native/ NDJSON log at this many
@@ -19318,6 +19351,7 @@ var init_config = __esm({
19318
19351
  * addresses or `@domain` entries; admins get every scope, members chat only. */
19319
19352
  signIn: external_exports.object({ admins: external_exports.array(external_exports.string().max(320)).max(500).optional(), members: external_exports.array(external_exports.string().max(320)).max(5e3).optional() }).optional(),
19320
19353
  defaultModelSelection: defaultModelSelectionSchema.optional(),
19354
+ automaticRecovery: automaticRecoverySchema.optional(),
19321
19355
  newBotDefaults: newBotDefaultsSchema.optional(),
19322
19356
  newBots: newBotsConfigSchema.optional(),
19323
19357
  /** CLI-only launch preferences. Never enable remote access implicitly. */
@@ -19432,6 +19466,7 @@ var init_config = __esm({
19432
19466
  LEGACY_DATA_DIR = join9(homedir4(), ".opengrokbot");
19433
19467
  EVENTS_DIR = join9(DATA_DIR, "events");
19434
19468
  NATIVE_DIR = join9(DATA_DIR, "native");
19469
+ lastIgnoredConfigWarning = "";
19435
19470
  WORKSPACE_CREDENTIAL_ENV = [
19436
19471
  "XAI_API_KEY",
19437
19472
  "MISTRAL_API_KEY",
@@ -19877,6 +19912,8 @@ function knownDirs() {
19877
19912
  join11(home, ".volta", "bin"),
19878
19913
  join11(home, ".bun", "bin"),
19879
19914
  join11(home, ".asdf", "shims"),
19915
+ join11(home, ".local", "share", "mise", "shims"),
19916
+ // mise installer
19880
19917
  join11(home, ".deno", "bin"),
19881
19918
  join11(home, "bin"),
19882
19919
  ...nvmBinDirs()
@@ -19952,6 +19989,12 @@ function probeLoginShellPath() {
19952
19989
  }
19953
19990
  );
19954
19991
  }
19992
+ function userHome(env = process.env) {
19993
+ return process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir5() : env.HOME || env.USERPROFILE || homedir5();
19994
+ }
19995
+ function harnessHome(name, env = process.env) {
19996
+ return join11(userHome(env), `.${name}`);
19997
+ }
19955
19998
  function findCliCandidates(name) {
19956
19999
  if (!name || /[\n\r]/.test(name)) return [];
19957
20000
  if (/[/\\]/.test(name) || /^[a-zA-Z]:/.test(name)) return [name];
@@ -20900,7 +20943,7 @@ var init_local_inject = __esm({
20900
20943
 
20901
20944
  // server/drivers/prompt-split.ts
20902
20945
  import { createHash as createHash3 } from "node:crypto";
20903
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync7 } from "node:fs";
20946
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync7, rmSync as rmSync4 } from "node:fs";
20904
20947
  import { join as join15 } from "node:path";
20905
20948
  function promptHalves(turn) {
20906
20949
  const { systemStable, systemVolatile } = turn;
@@ -20927,7 +20970,11 @@ function readPromptSplitReceipt(scope, key) {
20927
20970
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
20928
20971
  const record3 = raw;
20929
20972
  if (typeof record3.stable === "string" && typeof record3.volatile === "string") {
20930
- return { stable: record3.stable, volatile: record3.volatile };
20973
+ return {
20974
+ stable: record3.stable,
20975
+ volatile: record3.volatile,
20976
+ ...typeof record3.turnsSinceFull === "number" ? { turnsSinceFull: record3.turnsSinceFull } : {}
20977
+ };
20931
20978
  }
20932
20979
  }
20933
20980
  } catch {
@@ -20938,13 +20985,21 @@ function writePromptSplitReceipt(scope, key, receipt) {
20938
20985
  mkdirSync9(join15(DATA_DIR, "prompt-split"), { recursive: true });
20939
20986
  writeFileAtomic(receiptPath(scope, key), JSON.stringify(receipt), { mode: 384 });
20940
20987
  }
20941
- function splitSessionPrompt(stable, volatile, previous, fullSystem, text4, perTurnVolatile = false) {
20942
- const receipt = promptSplitFingerprints(stable, volatile);
20943
- if (previous === null || previous.stable !== receipt.stable) {
20944
- return { text: fullSystem ? fullSystem + "\n\n" + text4 : text4, receipt };
20988
+ function deletePromptSplitReceipt(scope, key) {
20989
+ try {
20990
+ rmSync4(receiptPath(scope, key));
20991
+ } catch {
20992
+ }
20993
+ }
20994
+ function splitSessionPrompt(stable, volatile, previous, fullSystem, text4, perTurnVolatile = false, reAnchorAfterTurns = 0) {
20995
+ const fingerprints = promptSplitFingerprints(stable, volatile);
20996
+ const turnsSinceFull = typeof previous?.turnsSinceFull === "number" ? previous.turnsSinceFull : 0;
20997
+ const reAnchor = reAnchorAfterTurns > 0 && previous !== null && turnsSinceFull >= reAnchorAfterTurns;
20998
+ if (previous === null || previous.stable !== fingerprints.stable || reAnchor) {
20999
+ return { text: fullSystem ? fullSystem + "\n\n" + text4 : text4, receipt: { ...fingerprints, turnsSinceFull: 0 } };
20945
21000
  }
20946
- const note = previous.volatile === receipt.volatile && !perTurnVolatile ? "" : volatileContextNote(volatile, previous.volatile !== EMPTY_FINGERPRINT);
20947
- return { text: withContextNote(note, text4), receipt };
21001
+ const note = previous.volatile === fingerprints.volatile && !perTurnVolatile ? "" : volatileContextNote(volatile, previous.volatile !== EMPTY_FINGERPRINT);
21002
+ return { text: withContextNote(note, text4), receipt: { ...fingerprints, turnsSinceFull: turnsSinceFull + 1 } };
20948
21003
  }
20949
21004
  var VOLATILE_CONTEXT_NOTE_PREFIX, VOLATILE_CONTEXT_CLEARED_NOTE, digest, EMPTY_FINGERPRINT, receiptPath;
20950
21005
  var init_prompt_split = __esm({
@@ -21051,6 +21106,19 @@ function stripOmbAskBlock(output) {
21051
21106
  if (!OMB_ASK_FENCE.test(output)) return output;
21052
21107
  return output.replace(OMB_ASK_FENCE_GLOBAL, "").replace(/\n{3,}/g, "\n\n").trim();
21053
21108
  }
21109
+ function ombAskProtocolPrompt() {
21110
+ return [
21111
+ "",
21112
+ "## Asking the person a question",
21113
+ "When a decision belongs to the person, end your reply with a fenced block exactly like this:",
21114
+ "",
21115
+ "```omb-ask",
21116
+ '{"questions":[{"question":"Ship the release now?","header":"Release","options":[{"label":"Ship now"},{"label":"Wait for the QA signoff"}]}]}',
21117
+ "```",
21118
+ "",
21119
+ `The block must be the last thing in your reply. You may ask up to ${MAX_QUESTIONS} questions at once, each with up to ${MAX_OPTIONS} options; the person can always answer in their own words. Their answers arrive on your next prompt as \`Q:\`/\`A:\` lines \u2014 never invent them.`
21120
+ ].join("\n");
21121
+ }
21054
21122
  function askQuestionSummary(questions) {
21055
21123
  const first = questions[0]?.question ?? "";
21056
21124
  const rest = questions.length - 1;
@@ -21199,7 +21267,7 @@ var init_ask_question = __esm({
21199
21267
  });
21200
21268
 
21201
21269
  // server/thread-log-rotation.ts
21202
- import { closeSync as closeSync4, fstatSync, openSync as openSync4, readSync as readSync2, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync3, writeSync } from "node:fs";
21270
+ import { closeSync as closeSync4, fstatSync, openSync as openSync4, readSync as readSync2, renameSync as renameSync5, rmSync as rmSync5, statSync as statSync3, writeSync } from "node:fs";
21203
21271
  function currentThreadLogCap() {
21204
21272
  try {
21205
21273
  return capProvider();
@@ -21251,7 +21319,7 @@ function trimToTail(file2, keepBytes) {
21251
21319
  renameSync5(staging, file2);
21252
21320
  } catch {
21253
21321
  try {
21254
- rmSync4(staging, { force: true });
21322
+ rmSync5(staging, { force: true });
21255
21323
  } catch {
21256
21324
  }
21257
21325
  }
@@ -21423,6 +21491,128 @@ var init_resume_recovery = __esm({
21423
21491
  }
21424
21492
  });
21425
21493
 
21494
+ // server/drivers/session-idle.ts
21495
+ function usableMilliseconds(raw) {
21496
+ const value = Number(raw);
21497
+ return Number.isFinite(value) && value > 0 && value <= MAX_TIMER_MS ? value : null;
21498
+ }
21499
+ function sessionIdlePolicy(driverPrefix, env = process.env) {
21500
+ const minimumMs = Math.max(
21501
+ MINIMUM_IDLE_MS,
21502
+ usableMilliseconds(
21503
+ env[`OMB_${driverPrefix}_SESSION_IDLE_MIN_MS`] ?? env.OMB_SESSION_IDLE_MIN_MS
21504
+ ) ?? 0
21505
+ );
21506
+ const configured = usableMilliseconds(
21507
+ env[`OMB_${driverPrefix}_SESSION_IDLE_MS`] ?? env.OMB_SESSION_IDLE_MS
21508
+ ) ?? DEFAULT_IDLE_MS;
21509
+ return { idleMs: Math.max(minimumMs, configured), minimumMs };
21510
+ }
21511
+ var DEFAULT_IDLE_MS, MINIMUM_IDLE_MS, MAX_TIMER_MS;
21512
+ var init_session_idle = __esm({
21513
+ "server/drivers/session-idle.ts"() {
21514
+ "use strict";
21515
+ DEFAULT_IDLE_MS = 10 * 6e4;
21516
+ MINIMUM_IDLE_MS = 1e4;
21517
+ MAX_TIMER_MS = 2147483647;
21518
+ }
21519
+ });
21520
+
21521
+ // shared/provider-safety.ts
21522
+ function isProviderSafetyBlock(message2) {
21523
+ return /\bblocked by (?:our|the provider['’]s) safety systems\b|\bsafety monitoring\b.{0,100}\b(?:paused|ended|blocked)\b|\b(?:safety_check_failed|safety_policy_violation)\b/i.test(message2);
21524
+ }
21525
+ var init_provider_safety = __esm({
21526
+ "shared/provider-safety.ts"() {
21527
+ "use strict";
21528
+ }
21529
+ });
21530
+
21531
+ // server/drivers/retry.ts
21532
+ function classifyError(err) {
21533
+ const text4 = messageOf(err);
21534
+ if (isProviderSafetyBlock(text4)) return { transient: false, reason: "provider_safety" };
21535
+ if (err && "exitCode" in err) {
21536
+ const { exitCode: code } = err;
21537
+ if (code !== null && code < 0) return { transient: false, reason: "interrupted" };
21538
+ for (const { pattern, reason } of TRANSIENT_PATTERNS) {
21539
+ if (pattern.test(text4)) return { transient: true, reason };
21540
+ }
21541
+ for (const { pattern, reason } of TERMINAL_PATTERNS) {
21542
+ if (pattern.test(text4)) return { transient: false, reason };
21543
+ }
21544
+ return { transient: false, reason: "terminal_exit" };
21545
+ }
21546
+ for (const { pattern, reason } of TERMINAL_PATTERNS) {
21547
+ if (pattern.test(text4)) return { transient: false, reason };
21548
+ }
21549
+ for (const { pattern, reason } of TRANSIENT_PATTERNS) {
21550
+ if (pattern.test(text4)) return { transient: true, reason };
21551
+ }
21552
+ return { transient: false, reason: "unknown" };
21553
+ }
21554
+ function computeBackoff(attempt, random = Math.random) {
21555
+ const base2 = BACKOFF_BASE_MS[Math.min(Math.max(attempt, 0), BACKOFF_BASE_MS.length - 1)];
21556
+ const jitter = base2 * 0.25;
21557
+ return Math.round(base2 - jitter + random() * jitter * 2);
21558
+ }
21559
+ function interruptibleDelay(ms, signal) {
21560
+ let timer = null;
21561
+ let onCancel = null;
21562
+ const promise2 = new Promise((resolve12) => {
21563
+ timer = setTimeout(() => resolve12("elapsed"), Math.max(1, ms));
21564
+ timer.unref?.();
21565
+ if (signal?.aborted) return resolve12("cancelled");
21566
+ onCancel = () => {
21567
+ clearTimeout(timer);
21568
+ resolve12("cancelled");
21569
+ };
21570
+ signal?.addEventListener("abort", onCancel, { once: true });
21571
+ });
21572
+ return {
21573
+ promise: promise2,
21574
+ cancel: () => onCancel?.()
21575
+ };
21576
+ }
21577
+ var RETRY_MAX_ATTEMPTS, BACKOFF_BASE_MS, TRANSIENT_PATTERNS, TERMINAL_PATTERNS, messageOf;
21578
+ var init_retry = __esm({
21579
+ "server/drivers/retry.ts"() {
21580
+ "use strict";
21581
+ init_provider_safety();
21582
+ RETRY_MAX_ATTEMPTS = 3;
21583
+ BACKOFF_BASE_MS = [1e3, 3e3, 8e3];
21584
+ TRANSIENT_PATTERNS = [
21585
+ { pattern: /\b(?:429|rate.?limit|too many requests)\b/i, reason: "rate_limited" },
21586
+ { pattern: /\boverloaded\b|\bcapacity\b/i, reason: "overloaded" },
21587
+ { pattern: /\b5\d{2}\b|\binternal server error\b|\bbad gateway\b|\bservice unavailable\b/i, reason: "server_error" },
21588
+ {
21589
+ pattern: /\b(?:econnreset|econnrefused|epipe|etimedout|eai_again|connection reset|connection refused|socket hang up|network error|fetch failed)\b/i,
21590
+ reason: "connection_reset"
21591
+ },
21592
+ { pattern: /\btimeout(ed)?\b|\btimed? out\b/i, reason: "timeout" }
21593
+ ];
21594
+ TERMINAL_PATTERNS = [
21595
+ {
21596
+ pattern: /\b(?:40[13]|unauthorized|forbidden|invalid api key|missing bearer|authentication required|not logged in|logged out)\b/i,
21597
+ reason: "auth"
21598
+ },
21599
+ { pattern: /\b402\b|\bquota\b|\bbilling\b|\bsubscription\b/i, reason: "quota" },
21600
+ { pattern: /\bmodel not found\b|\bunknown model\b|\bdoes not exist for model\b|\bunsupported model\b/i, reason: "unknown_model" },
21601
+ { pattern: /\b400\b|\b422\b|\binvalid request\b|\bmalformed\b|\bunexpected status\b/i, reason: "invalid_request" },
21602
+ { pattern: /\b404\b|\bno such thread\b|\bthread gone\b/i, reason: "not_found" },
21603
+ // interrupt/cancel vocabulary from the drivers' own stop paths — a turn the
21604
+ // user stopped must never come back as an auto-retry
21605
+ { pattern: /\b(?:interrupted|cancelled by user)\b/i, reason: "interrupted" }
21606
+ ];
21607
+ messageOf = (err) => {
21608
+ if (!err) return "";
21609
+ if (err instanceof Error) return `${err.message}${err.cause ? ` ${String(err.cause)}` : ""}`;
21610
+ if ("text" in err) return err.text;
21611
+ return [err.stderr ?? "", ""].join(" ").trim();
21612
+ };
21613
+ }
21614
+ });
21615
+
21426
21616
  // server/drivers/acp/core.ts
21427
21617
  import { homedir as homedir7 } from "node:os";
21428
21618
  import { lstat, mkdir as mkdir3, readFile, realpath, stat as stat2, writeFile } from "node:fs/promises";
@@ -21451,6 +21641,12 @@ function acpRpcError(value, method) {
21451
21641
  acpSessionFailure: value?.code === -32603
21452
21642
  });
21453
21643
  }
21644
+ function trackRunningTool(current, update, defaultStatus) {
21645
+ if (typeof update.toolCallId !== "string" || !update.toolCallId) return;
21646
+ const status = update.status ?? defaultStatus;
21647
+ if (status === "completed" || status === "failed") current.runningTools.delete(update.toolCallId);
21648
+ else if (status === "pending" || status === "in_progress") current.runningTools.add(update.toolCallId);
21649
+ }
21454
21650
  function acpVariantOption(result) {
21455
21651
  const option = (Array.isArray(result?.configOptions) ? result.configOptions : []).find(
21456
21652
  (entry) => entry?.type === "select" && typeof entry.id === "string" && (entry.id === "effort" || entry.category === "thought_level")
@@ -21542,6 +21738,17 @@ function versionFromProbe(stdout2, stderr) {
21542
21738
  const err = (stderr ?? "").trim().split(/\r\n|\n|\r/, 1)[0]?.trim() ?? "";
21543
21739
  return err || null;
21544
21740
  }
21741
+ function parseVersionTriple(value) {
21742
+ const match = /(\d+)\.(\d+)\.(\d+)/.exec(value);
21743
+ if (!match) return null;
21744
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
21745
+ }
21746
+ function versionAtLeast(installed, floor) {
21747
+ for (let i2 = 0; i2 < 3; i2 += 1) {
21748
+ if (installed[i2] !== floor[i2]) return installed[i2] > floor[i2];
21749
+ }
21750
+ return true;
21751
+ }
21545
21752
  function createAcpDriver(support11) {
21546
21753
  const DRIVER_KIND8 = support11.driverKind;
21547
21754
  const SOURCE = support11.nativeSource;
@@ -21587,9 +21794,19 @@ function createAcpDriver(support11) {
21587
21794
  const listeners = /* @__PURE__ */ new Set();
21588
21795
  const active = /* @__PURE__ */ new Map();
21589
21796
  const sessions = /* @__PURE__ */ new Map();
21590
- const configuredIdleMinimum = Number(process.env.OMB_ACP_SESSION_IDLE_MIN_MS);
21591
- const sessionIdleMinimum = Number.isFinite(configuredIdleMinimum) && configuredIdleMinimum > 0 ? configuredIdleMinimum : 1e4;
21592
- const SESSION_IDLE_MS = Math.max(sessionIdleMinimum, Number(process.env.OMB_ACP_SESSION_IDLE_MS) || 10 * 6e4);
21797
+ const retiring = /* @__PURE__ */ new Map();
21798
+ const retireChild = async (threadId, child) => {
21799
+ let children = retiring.get(threadId);
21800
+ if (!children) retiring.set(threadId, children = /* @__PURE__ */ new Set());
21801
+ children.add(child);
21802
+ const stopped = await killCliTree(child);
21803
+ if (stopped) {
21804
+ children.delete(child);
21805
+ if (!children.size && retiring.get(threadId) === children) retiring.delete(threadId);
21806
+ }
21807
+ return stopped;
21808
+ };
21809
+ const { idleMs: SESSION_IDLE_MS } = sessionIdlePolicy("ACP");
21593
21810
  const closeSession = (threadId, why) => {
21594
21811
  const session = sessions.get(threadId);
21595
21812
  if (!session || session.closing) return;
@@ -21602,10 +21819,13 @@ function createAcpDriver(support11) {
21602
21819
  session.child.stdin.end();
21603
21820
  } catch {
21604
21821
  }
21605
- const kill = setTimeout(() => {
21606
- void killCliTree(session.child);
21607
- }, 5e3);
21608
- kill.unref?.();
21822
+ if (support11.restartOnMcpChange) void retireChild(threadId, session.child);
21823
+ else {
21824
+ const kill = setTimeout(() => {
21825
+ void killCliTree(session.child);
21826
+ }, 5e3);
21827
+ kill.unref?.();
21828
+ }
21609
21829
  };
21610
21830
  const armIdle = (threadId) => {
21611
21831
  const session = sessions.get(threadId);
@@ -21696,6 +21916,7 @@ function createAcpDriver(support11) {
21696
21916
  const current = session.current;
21697
21917
  if (!current || current.state.settled) return;
21698
21918
  current.state.settled = true;
21919
+ current.acknowledge();
21699
21920
  if (current.interruptTimer) clearTimeout(current.interruptTimer);
21700
21921
  for (const finish of current.asks.values()) finish("cancel", "system");
21701
21922
  session.acp.failAll(new Error("turn settled"));
@@ -21755,7 +21976,7 @@ function createAcpDriver(support11) {
21755
21976
  if (!(idleMs && idleMs > 0)) return;
21756
21977
  if (idleTimer) clearTimeout(idleTimer);
21757
21978
  idleTimer = setTimeout(() => {
21758
- if (session.current?.asks.size) {
21979
+ if (session.current?.asks.size || session.current?.runningTools.size) {
21759
21980
  armIdle2();
21760
21981
  return;
21761
21982
  }
@@ -21869,6 +22090,7 @@ function createAcpDriver(support11) {
21869
22090
  }
21870
22091
  };
21871
22092
  const handleServerRequest = (msg, current) => {
22093
+ current.state.startupActivity = true;
21872
22094
  if (msg.method === "fs/read_text_file" || msg.method === "fs/write_text_file") {
21873
22095
  void handleClientFileRequest(msg);
21874
22096
  return;
@@ -21972,6 +22194,9 @@ function createAcpDriver(support11) {
21972
22194
  if (current && !current.state.settled && session.sessionId && p2.sessionId === session.sessionId) current.receiveModelVariants(p2.update);
21973
22195
  return;
21974
22196
  }
22197
+ if (current && ["agent_message_chunk", "agent_thought_chunk", "tool_call", "tool_call_update"].includes(p2.update?.sessionUpdate)) {
22198
+ current.state.startupActivity = true;
22199
+ }
21975
22200
  if (!current || !current.state.promptSent) return;
21976
22201
  const u4 = p2.update ?? {};
21977
22202
  switch (u4.sessionUpdate) {
@@ -22003,6 +22228,7 @@ function createAcpDriver(support11) {
22003
22228
  }
22004
22229
  case "tool_call": {
22005
22230
  current.flushAssistantText();
22231
+ trackRunningTool(current, u4, "pending");
22006
22232
  emit({
22007
22233
  ...base2(threadId, current.turnId),
22008
22234
  type: "item.started",
@@ -22015,6 +22241,7 @@ function createAcpDriver(support11) {
22015
22241
  break;
22016
22242
  }
22017
22243
  case "tool_call_update": {
22244
+ trackRunningTool(current, u4);
22018
22245
  if (u4.status === "completed" || u4.status === "failed") {
22019
22246
  current.state.producedItem = true;
22020
22247
  emit({
@@ -22164,7 +22391,7 @@ function createAcpDriver(support11) {
22164
22391
  }
22165
22392
  const pooled = sessions.get(threadId);
22166
22393
  let session;
22167
- if (pooled && !pooled.dead && !pooled.closing && pooled.contractKey === contractKey) {
22394
+ if (pooled && !pooled.dead && !pooled.closing && pooled.contractKey === contractKey && (!support11.restartOnMcpChange || pooled.sessionKey === sessionKey)) {
22168
22395
  if (pooled.idleTimer) clearTimeout(pooled.idleTimer);
22169
22396
  pooled.idleTimer = null;
22170
22397
  session = pooled;
@@ -22177,7 +22404,15 @@ function createAcpDriver(support11) {
22177
22404
  sessions.set(threadId, session);
22178
22405
  }
22179
22406
  const request = (method, params, timeoutMs, receive, idleMs, idleMessage) => session.acp.request(method, params, timeoutMs, receive, idleMs, idleMessage);
22180
- const state = { settled: false, promptSent: false, text: "", producedItem: false };
22407
+ const state = { settled: false, promptSent: false, text: "", producedItem: false, startupActivity: false, stopped: false };
22408
+ let acknowledge = () => {
22409
+ };
22410
+ let rejectStartup = (_error) => {
22411
+ };
22412
+ const startupAck = turn.startupRecovery ? new Promise((resolve12, reject) => {
22413
+ acknowledge = () => resolve12({ turnId });
22414
+ rejectStartup = reject;
22415
+ }) : null;
22181
22416
  const asks = /* @__PURE__ */ new Map();
22182
22417
  const modelOf = (result) => {
22183
22418
  const option = (Array.isArray(result?.configOptions) ? result.configOptions : []).find(
@@ -22222,12 +22457,15 @@ function createAcpDriver(support11) {
22222
22457
  turnConfig,
22223
22458
  controlsHost,
22224
22459
  state,
22460
+ acknowledge,
22225
22461
  asks,
22462
+ runningTools: /* @__PURE__ */ new Set(),
22226
22463
  interruptTimer: null,
22227
22464
  flushAssistantText,
22228
22465
  receiveModelVariants
22229
22466
  };
22230
22467
  const interrupt = () => {
22468
+ state.stopped = true;
22231
22469
  if (session.sessionId) {
22232
22470
  session.acp.send({ jsonrpc: "2.0", method: "session/cancel", params: { sessionId: session.sessionId } });
22233
22471
  if (current.interruptTimer) clearTimeout(current.interruptTimer);
@@ -22240,12 +22478,24 @@ function createAcpDriver(support11) {
22240
22478
  closeSession(threadId, "stop");
22241
22479
  }
22242
22480
  };
22243
- active.set(threadId, { stop: () => closeSession(threadId, "stop"), interrupt, turnId, asks });
22481
+ active.set(threadId, { stop: () => {
22482
+ state.stopped = true;
22483
+ closeSession(threadId, "stop");
22484
+ }, interrupt, turnId, asks });
22244
22485
  emit({ ...base2(threadId, turnId), type: "turn.started" });
22245
22486
  session.current = current;
22246
22487
  (async () => {
22247
22488
  try {
22248
22489
  const handshake = async () => {
22490
+ for (const child of retiring.get(threadId) ?? []) {
22491
+ const stopped = await retireChild(threadId, child);
22492
+ if (state.settled || session.closing) throw new Error("session closed");
22493
+ if (!stopped) {
22494
+ closeSession(threadId, "replace-failed");
22495
+ throw new Error(`${support11.displayName} could not close its previous tool session. Try again.`);
22496
+ }
22497
+ }
22498
+ if (state.settled || session.closing) throw new Error("session closed");
22249
22499
  if (!session.initResult) {
22250
22500
  session.initResult = await request(
22251
22501
  "initialize",
@@ -22421,7 +22671,8 @@ function createAcpDriver(support11) {
22421
22671
  readPromptSplitReceipt(DRIVER_KIND8, receiptKey),
22422
22672
  promptTurn.system,
22423
22673
  promptTurn.text,
22424
- Boolean(turn.mentionTurn)
22674
+ Boolean(turn.mentionTurn),
22675
+ ACP_PROMPT_RE_ANCHOR_TURNS
22425
22676
  );
22426
22677
  promptInput = { ...promptTurn, system: "", text: composed.text };
22427
22678
  pendingSplitReceipt = { key: receiptKey, receipt: composed.receipt };
@@ -22436,7 +22687,9 @@ ${promptInput.text}` : promptInput.text;
22436
22687
  if (turn.variant !== void 0 && requestedVariantOption().currentValue !== turn.variant) {
22437
22688
  throw new Error(`${support11.displayName} changed variant before the prompt`);
22438
22689
  }
22690
+ if (state.settled || state.stopped || session.closing) throw new Error("turn stopped");
22439
22691
  state.promptSent = true;
22692
+ acknowledge();
22440
22693
  const promptIdleMs = promptIdleTimeoutMs();
22441
22694
  const result = await request(
22442
22695
  "session/prompt",
@@ -22444,7 +22697,7 @@ ${promptInput.text}` : promptInput.text;
22444
22697
  void 0,
22445
22698
  void 0,
22446
22699
  promptIdleMs,
22447
- `${DRIVER_KIND8} went fully silent ${Math.round(promptIdleMs / 1e3)} s after the message and the turn was stopped. Raise OPENMAUS_ACP_PROMPT_IDLE_TIMEOUT_MS if this model legitimately takes longer to answer.`
22700
+ `${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).`
22448
22701
  );
22449
22702
  if (pendingSplitReceipt) {
22450
22703
  writePromptSplitReceipt(DRIVER_KIND8, pendingSplitReceipt.key, pendingSplitReceipt.receipt);
@@ -22475,6 +22728,32 @@ ${promptInput.text}` : promptInput.text;
22475
22728
  const message2 = e instanceof Error ? e.message : String(e);
22476
22729
  const code = support11.classifyError?.(e);
22477
22730
  const needsAuth = code === "invalid_credentials" || code === "inactive_subscription" || message2 === support11.loginNote;
22731
+ const failure = classifyError({ text: message2 });
22732
+ const transientStartup = failure.transient || failure.reason === "unknown" && code === "upstream_outage";
22733
+ const denied = /\b(?:(?:permission|access) denied|(?:approval|permission) (?:required|denied|rejected)|requires? (?:approval|permission)|policy (?:restriction|violation)|(?:blocked|denied|restricted) by (?:the )?policy)\b/i.test(message2);
22734
+ if (turn.startupRecovery && transientStartup && !needsAuth && !denied && (!code || code === "upstream_outage") && ![-32700, -32600, -32601, -32602].includes(e?.code) && !state.promptSent && !state.startupActivity && !state.producedItem && !state.text && !state.stopped && !session.closing && !asks.size && !current.runningTools.size) {
22735
+ state.settled = true;
22736
+ session.current = null;
22737
+ if (current.interruptTimer) clearTimeout(current.interruptTimer);
22738
+ closeSession(threadId, "startup-recovery");
22739
+ const stopped = await killCliTree(session.child).catch(() => false);
22740
+ if (current.interruptTimer) clearTimeout(current.interruptTimer);
22741
+ active.delete(threadId);
22742
+ if (stopped && !state.stopped) {
22743
+ rejectStartup(new TurnNotStartedError(turnId, message2));
22744
+ return;
22745
+ }
22746
+ acknowledge();
22747
+ if (!state.stopped) emit({ ...base2(threadId, turnId), type: "runtime.error", message: message2 });
22748
+ emit({
22749
+ ...base2(threadId, turnId),
22750
+ type: "turn.completed",
22751
+ ok: state.stopped,
22752
+ stopReason: state.stopped ? "cancelled" : "rpc_error",
22753
+ cost: null
22754
+ });
22755
+ return;
22756
+ }
22478
22757
  emit({
22479
22758
  ...base2(threadId, turnId),
22480
22759
  type: "runtime.error",
@@ -22488,7 +22767,7 @@ ${promptInput.text}` : promptInput.text;
22488
22767
  }
22489
22768
  }
22490
22769
  })();
22491
- return { turnId };
22770
+ return startupAck ?? { turnId };
22492
22771
  };
22493
22772
  const snapshot = async () => {
22494
22773
  const env = childEnv();
@@ -22544,6 +22823,9 @@ ${promptInput.text}` : promptInput.text;
22544
22823
  stopAll: async () => {
22545
22824
  for (const { stop } of active.values()) stop();
22546
22825
  for (const threadId of Array.from(sessions.keys())) closeSession(threadId, "stopAll");
22826
+ for (const [threadId, children] of retiring) {
22827
+ for (const child of children) void retireChild(threadId, child);
22828
+ }
22547
22829
  },
22548
22830
  onEvent: (listener) => {
22549
22831
  listeners.add(listener);
@@ -22553,13 +22835,16 @@ ${promptInput.text}` : promptInput.text;
22553
22835
  dispose: async () => {
22554
22836
  for (const { stop } of active.values()) stop();
22555
22837
  for (const threadId of Array.from(sessions.keys())) closeSession(threadId, "dispose");
22838
+ for (const [threadId, children] of retiring) {
22839
+ for (const child of children) void retireChild(threadId, child);
22840
+ }
22556
22841
  listeners.clear();
22557
22842
  }
22558
22843
  };
22559
22844
  }
22560
22845
  };
22561
22846
  }
22562
- var envOr, INIT_TIMEOUT, SESSION_CONFIG_TIMEOUT, NEW_SESSION_TIMEOUT, LOAD_SESSION_TIMEOUT, promptIdleTimeoutMs, CLIENT_FILE_MAX_BYTES, TOOL_LOG_TEXT_LIMIT;
22847
+ var 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;
22563
22848
  var init_core3 = __esm({
22564
22849
  "server/drivers/acp/core.ts"() {
22565
22850
  "use strict";
@@ -22577,11 +22862,14 @@ var init_core3 = __esm({
22577
22862
  init_mcp_tool_images();
22578
22863
  init_redact2();
22579
22864
  init_resume_recovery();
22865
+ init_session_idle();
22866
+ init_retry();
22580
22867
  envOr = (key, fallback) => Number(process.env[key] ?? fallback);
22581
22868
  INIT_TIMEOUT = envOr("OPENMAUS_ACP_INIT_TIMEOUT_MS", 3e5);
22582
22869
  SESSION_CONFIG_TIMEOUT = envOr("OPENMAUS_ACP_SESSION_CONFIG_TIMEOUT_MS", 3e5);
22583
22870
  NEW_SESSION_TIMEOUT = envOr("OPENMAUS_ACP_NEW_SESSION_TIMEOUT_MS", 3e5);
22584
22871
  LOAD_SESSION_TIMEOUT = envOr("OPENMAUS_ACP_LOAD_SESSION_TIMEOUT_MS", 12e4);
22872
+ ACP_PROMPT_RE_ANCHOR_TURNS = 8;
22585
22873
  promptIdleTimeoutMs = () => {
22586
22874
  const raw = process.env.OPENMAUS_ACP_PROMPT_IDLE_TIMEOUT_MS;
22587
22875
  if (raw === void 0) return 18e4;
@@ -23705,7 +23993,7 @@ function decodeConfig(raw) {
23705
23993
  ...typeof o.askTimeoutMs === "number" ? { askTimeoutMs: o.askTimeoutMs } : {}
23706
23994
  };
23707
23995
  }
23708
- var DRIVER_KIND, BOX_API, MODELS, ASK_PROTOCOL, ASK_FENCE_ANY, catalog, providerFor, BoxAgentDriver;
23996
+ var DRIVER_KIND, BOX_API, MODELS, ASK_FENCE_ANY, catalog, providerFor, BoxAgentDriver;
23709
23997
  var init_boxagent = __esm({
23710
23998
  "server/drivers/boxagent.ts"() {
23711
23999
  "use strict";
@@ -23722,17 +24010,6 @@ var init_boxagent = __esm({
23722
24010
  { id: "gpt-5.4", label: "GPT-5.4 (Codex) \xB7 on the box" }
23723
24011
  ]
23724
24012
  };
23725
- ASK_PROTOCOL = [
23726
- "",
23727
- "## Asking the person a question",
23728
- "When a decision belongs to the person, end your reply with a fenced block exactly like this:",
23729
- "",
23730
- "```omb-ask",
23731
- '{"questions":[{"question":"Ship the release now?","header":"Release","options":[{"label":"Ship now"},{"label":"Wait for the QA signoff"}]}]}',
23732
- "```",
23733
- "",
23734
- "The block must be the last thing in your reply. You may ask up to 6 questions at once, each with up to 12 options; the person can always answer in their own words. Their answers arrive on your next prompt as `Q:`/`A:` lines \u2014 never invent them."
23735
- ].join("\n");
23736
24013
  ASK_FENCE_ANY = /(^|\n)[ \t]{0,3}(`{3,}|~{3,})[ \t]*omb-ask\b/;
23737
24014
  catalog = null;
23738
24015
  providerFor = (model) => {
@@ -23793,7 +24070,7 @@ var init_boxagent = __esm({
23793
24070
  const prompt = [
23794
24071
  turn.system,
23795
24072
  "You are working on the assigned cloud computer \u2014 use its desktop, Chrome, and shell within the access described above.",
23796
- ASK_PROTOCOL,
24073
+ ombAskProtocolPrompt(),
23797
24074
  ...correction ? [correction] : [],
23798
24075
  "",
23799
24076
  turn.text
@@ -23992,7 +24269,7 @@ var init_boxagent = __esm({
23992
24269
  snapshot,
23993
24270
  adapter: {
23994
24271
  provider: DRIVER_KIND,
23995
- capabilities: { sessionModelSwitch: "in-session" },
24272
+ capabilities: { sessionModelSwitch: "in-session", remoteAgent: true, usesCloudComputer: true },
23996
24273
  sendTurn,
23997
24274
  interruptTurn: async (threadId) => active.get(threadId)?.cancel(),
23998
24275
  respondToRequest: async (threadId, requestId, decision) => {
@@ -24445,104 +24722,9 @@ var init_mcp_gate_config = __esm({
24445
24722
  }
24446
24723
  });
24447
24724
 
24448
- // shared/provider-safety.ts
24449
- function isProviderSafetyBlock(message2) {
24450
- return /\bblocked by (?:our|the provider['’]s) safety systems\b|\bsafety monitoring\b.{0,100}\b(?:paused|ended|blocked)\b|\b(?:safety_check_failed|safety_policy_violation)\b/i.test(message2);
24451
- }
24452
- var init_provider_safety = __esm({
24453
- "shared/provider-safety.ts"() {
24454
- "use strict";
24455
- }
24456
- });
24457
-
24458
- // server/drivers/retry.ts
24459
- function classifyError(err) {
24460
- const text4 = messageOf(err);
24461
- if (isProviderSafetyBlock(text4)) return { transient: false, reason: "provider_safety" };
24462
- if (err && "exitCode" in err) {
24463
- const { exitCode: code } = err;
24464
- if (code !== null && code < 0) return { transient: false, reason: "interrupted" };
24465
- for (const { pattern, reason } of TRANSIENT_PATTERNS) {
24466
- if (pattern.test(text4)) return { transient: true, reason };
24467
- }
24468
- for (const { pattern, reason } of TERMINAL_PATTERNS) {
24469
- if (pattern.test(text4)) return { transient: false, reason };
24470
- }
24471
- return { transient: false, reason: "terminal_exit" };
24472
- }
24473
- for (const { pattern, reason } of TERMINAL_PATTERNS) {
24474
- if (pattern.test(text4)) return { transient: false, reason };
24475
- }
24476
- for (const { pattern, reason } of TRANSIENT_PATTERNS) {
24477
- if (pattern.test(text4)) return { transient: true, reason };
24478
- }
24479
- return { transient: false, reason: "unknown" };
24480
- }
24481
- function computeBackoff(attempt, random = Math.random) {
24482
- const base2 = BACKOFF_BASE_MS[Math.min(Math.max(attempt, 0), BACKOFF_BASE_MS.length - 1)];
24483
- const jitter = base2 * 0.25;
24484
- return Math.round(base2 - jitter + random() * jitter * 2);
24485
- }
24486
- function interruptibleDelay(ms, signal) {
24487
- let timer = null;
24488
- let onCancel = null;
24489
- const promise2 = new Promise((resolve12) => {
24490
- timer = setTimeout(() => resolve12("elapsed"), Math.max(1, ms));
24491
- timer.unref?.();
24492
- if (signal?.aborted) return resolve12("cancelled");
24493
- onCancel = () => {
24494
- clearTimeout(timer);
24495
- resolve12("cancelled");
24496
- };
24497
- signal?.addEventListener("abort", onCancel, { once: true });
24498
- });
24499
- return {
24500
- promise: promise2,
24501
- cancel: () => onCancel?.()
24502
- };
24503
- }
24504
- var RETRY_MAX_ATTEMPTS, BACKOFF_BASE_MS, TRANSIENT_PATTERNS, TERMINAL_PATTERNS, messageOf;
24505
- var init_retry = __esm({
24506
- "server/drivers/retry.ts"() {
24507
- "use strict";
24508
- init_provider_safety();
24509
- RETRY_MAX_ATTEMPTS = 3;
24510
- BACKOFF_BASE_MS = [1e3, 3e3, 8e3];
24511
- TRANSIENT_PATTERNS = [
24512
- { pattern: /\b(?:429|rate.?limit|too many requests)\b/i, reason: "rate_limited" },
24513
- { pattern: /\boverloaded\b|\bcapacity\b/i, reason: "overloaded" },
24514
- { pattern: /\b5\d{2}\b|\binternal server error\b|\bbad gateway\b|\bservice unavailable\b/i, reason: "server_error" },
24515
- {
24516
- pattern: /\b(?:econnreset|econnrefused|epipe|etimedout|eai_again|connection reset|connection refused|socket hang up|network error|fetch failed)\b/i,
24517
- reason: "connection_reset"
24518
- },
24519
- { pattern: /\btimeout(ed)?\b|\btimed? out\b/i, reason: "timeout" }
24520
- ];
24521
- TERMINAL_PATTERNS = [
24522
- {
24523
- pattern: /\b(?:40[13]|unauthorized|forbidden|invalid api key|missing bearer|authentication required|not logged in|logged out)\b/i,
24524
- reason: "auth"
24525
- },
24526
- { pattern: /\b402\b|\bquota\b|\bbilling\b|\bsubscription\b/i, reason: "quota" },
24527
- { pattern: /\bmodel not found\b|\bunknown model\b|\bdoes not exist for model\b|\bunsupported model\b/i, reason: "unknown_model" },
24528
- { pattern: /\b400\b|\b422\b|\binvalid request\b|\bmalformed\b|\bunexpected status\b/i, reason: "invalid_request" },
24529
- { pattern: /\b404\b|\bno such thread\b|\bthread gone\b/i, reason: "not_found" },
24530
- // interrupt/cancel vocabulary from the drivers' own stop paths — a turn the
24531
- // user stopped must never come back as an auto-retry
24532
- { pattern: /\b(?:interrupted|cancelled by user)\b/i, reason: "interrupted" }
24533
- ];
24534
- messageOf = (err) => {
24535
- if (!err) return "";
24536
- if (err instanceof Error) return `${err.message}${err.cause ? ` ${String(err.cause)}` : ""}`;
24537
- if ("text" in err) return err.text;
24538
- return [err.stderr ?? "", ""].join(" ").trim();
24539
- };
24540
- }
24541
- });
24542
-
24543
24725
  // server/drivers/claude.ts
24544
24726
  import { createHash as createHash7, randomBytes as randomBytes2 } from "node:crypto";
24545
- import { chmodSync as chmodSync4, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
24727
+ import { chmodSync as chmodSync4, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
24546
24728
  import { createServer as createNetServer } from "node:net";
24547
24729
  import { homedir as homedir9, tmpdir as tmpdir3 } from "node:os";
24548
24730
  import { join as join21, dirname as dirname9, isAbsolute as isAbsolute5, normalize } from "node:path";
@@ -24584,6 +24766,10 @@ function claudeAuthFailure(frame, text4) {
24584
24766
  if (frame.is_api_error_message !== true && typeof frame.error !== "string") return false;
24585
24767
  return frame.error === "authentication_failed" || classifyError({ text: text4 }).reason === "auth";
24586
24768
  }
24769
+ function claudeVersionTooOld(frame, text4) {
24770
+ if (frame.is_api_error_message !== true && typeof frame.error !== "string") return false;
24771
+ return /\bClaude Code v?\d+(?:\.\d+)+ does not support this model\b/i.test(text4);
24772
+ }
24587
24773
  function claudeEnvironment(model, source = process.env, configDir, instanceEnvironment = {}) {
24588
24774
  const env = { ...source, PATH: augmentedPath(), NPM_CONFIG_LOGLEVEL: "error" };
24589
24775
  if (configDir?.trim()) {
@@ -24655,15 +24841,7 @@ function autoCompactWindow(env) {
24655
24841
  return String(Math.min(1e6, Math.max(1e5, Math.floor(parsed))));
24656
24842
  }
24657
24843
  function parseClaudeCliVersion(stdout2) {
24658
- const match = /(\d+)\.(\d+)\.(\d+)/.exec(stdout2 ?? "");
24659
- if (!match) return null;
24660
- return [Number(match[1]), Number(match[2]), Number(match[3])];
24661
- }
24662
- function versionAtLeast(installed, floor) {
24663
- for (let i2 = 0; i2 < 3; i2 += 1) {
24664
- if (installed[i2] !== floor[i2]) return installed[i2] > floor[i2];
24665
- }
24666
- return true;
24844
+ return parseVersionTriple(stdout2 ?? "");
24667
24845
  }
24668
24846
  function claudeCliSupports(version2, flag) {
24669
24847
  return version2 === null || versionAtLeast(version2, CLAUDE_FLAG_FLOORS[flag]);
@@ -24731,7 +24909,7 @@ function readClaudeModelCatalog(env = process.env) {
24731
24909
  function removePrivateTempDir(filePath) {
24732
24910
  if (!filePath) return true;
24733
24911
  try {
24734
- rmSync5(dirname9(filePath), { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
24912
+ rmSync6(dirname9(filePath), { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
24735
24913
  return true;
24736
24914
  } catch {
24737
24915
  return false;
@@ -25026,6 +25204,8 @@ var init_claude = __esm({
25026
25204
  init_contracts();
25027
25205
  init_tool_summary();
25028
25206
  init_retry();
25207
+ init_session_idle();
25208
+ init_core3();
25029
25209
  init_local_inject();
25030
25210
  init_native();
25031
25211
  init_permission_command();
@@ -25124,9 +25304,7 @@ var init_claude = __esm({
25124
25304
  const listeners = /* @__PURE__ */ new Set();
25125
25305
  const active = /* @__PURE__ */ new Map();
25126
25306
  const sessions = /* @__PURE__ */ new Map();
25127
- const configuredIdleMinimum = Number(process.env.OMB_CLAUDE_SESSION_IDLE_MIN_MS);
25128
- const sessionIdleMinimum = Number.isFinite(configuredIdleMinimum) && configuredIdleMinimum > 0 ? configuredIdleMinimum : 1e4;
25129
- const SESSION_IDLE_MS = Math.max(sessionIdleMinimum, Number(process.env.OMB_CLAUDE_SESSION_IDLE_MS) || 10 * 6e4);
25307
+ const { idleMs: SESSION_IDLE_MS } = sessionIdlePolicy("CLAUDE");
25130
25308
  const stopSession = (session) => {
25131
25309
  void killCliTree(session.child).then((stopped) => {
25132
25310
  if (stopped) void session.finishClose?.();
@@ -25387,7 +25565,7 @@ var init_claude = __esm({
25387
25565
  retryState.delete(threadId);
25388
25566
  if (mcpConfigPath) {
25389
25567
  try {
25390
- rmSync5(dirname9(mcpConfigPath), { recursive: true, force: true });
25568
+ rmSync6(dirname9(mcpConfigPath), { recursive: true, force: true });
25391
25569
  } catch {
25392
25570
  }
25393
25571
  }
@@ -25395,7 +25573,7 @@ var init_claude = __esm({
25395
25573
  }
25396
25574
  if (mcpConfigPath) {
25397
25575
  try {
25398
- rmSync5(dirname9(mcpConfigPath), { recursive: true, force: true });
25576
+ rmSync6(dirname9(mcpConfigPath), { recursive: true, force: true });
25399
25577
  } catch {
25400
25578
  }
25401
25579
  }
@@ -25407,7 +25585,7 @@ var init_claude = __esm({
25407
25585
  broker = void 0;
25408
25586
  if (mcpConfigPath) {
25409
25587
  try {
25410
- rmSync5(dirname9(mcpConfigPath), { recursive: true, force: true });
25588
+ rmSync6(dirname9(mcpConfigPath), { recursive: true, force: true });
25411
25589
  } catch {
25412
25590
  }
25413
25591
  mcpConfigPath = null;
@@ -25524,7 +25702,7 @@ var init_claude = __esm({
25524
25702
  session.broker?.pause();
25525
25703
  if (session.mcpConfigPath) {
25526
25704
  try {
25527
- rmSync5(dirname9(session.mcpConfigPath), { recursive: true, force: true });
25705
+ rmSync6(dirname9(session.mcpConfigPath), { recursive: true, force: true });
25528
25706
  } catch {
25529
25707
  }
25530
25708
  session.mcpConfigPath = null;
@@ -25535,6 +25713,7 @@ var init_claude = __esm({
25535
25713
  active.delete(threadId);
25536
25714
  session.turn = null;
25537
25715
  retryState.delete(threadId);
25716
+ if (stopReason === "update_required") closeSession(threadId, "update required");
25538
25717
  emit({ ...base2(threadId, t2.turnId), type: "turn.completed", ok, stopReason, cost, ...usage ? { usage } : {} });
25539
25718
  if (session.child.exitCode === null && !session.closing) armIdle(threadId);
25540
25719
  };
@@ -25580,6 +25759,11 @@ var init_claude = __esm({
25580
25759
  emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: text4, setup: true });
25581
25760
  break;
25582
25761
  }
25762
+ if (claudeVersionTooOld(o, text4)) {
25763
+ if (session.turn) session.turn.updateRequired = true;
25764
+ emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: text4, setup: true, claudeUpdate: true });
25765
+ break;
25766
+ }
25583
25767
  if (text4.trim()) {
25584
25768
  const synthetic = o.is_api_error_message === true || typeof o.error === "string" ? { synthetic: true } : {};
25585
25769
  if (!session.turn?.sawStreamDelta) {
@@ -25629,7 +25813,7 @@ var init_claude = __esm({
25629
25813
  if (o.origin?.kind === "task-notification") break;
25630
25814
  settle(
25631
25815
  o.is_error !== true,
25632
- session.turn?.authFailed ? "auth_required" : o.stop_reason ?? o.terminal_reason ?? null,
25816
+ session.turn?.authFailed ? "auth_required" : session.turn?.updateRequired ? "update_required" : o.stop_reason ?? o.terminal_reason ?? null,
25633
25817
  o.total_cost_usd ?? null,
25634
25818
  o.usage ? {
25635
25819
  input: (o.usage.input_tokens || 0) + (o.usage.cache_read_input_tokens || 0) + (o.usage.cache_creation_input_tokens || 0),
@@ -25684,7 +25868,7 @@ var init_claude = __esm({
25684
25868
  failedBroker?.close();
25685
25869
  if (session.mcpConfigPath) {
25686
25870
  try {
25687
- rmSync5(dirname9(session.mcpConfigPath), { recursive: true, force: true });
25871
+ rmSync6(dirname9(session.mcpConfigPath), { recursive: true, force: true });
25688
25872
  } catch {
25689
25873
  }
25690
25874
  session.mcpConfigPath = null;
@@ -25762,7 +25946,7 @@ var init_claude = __esm({
25762
25946
  session.broker = void 0;
25763
25947
  if (session.mcpConfigPath) {
25764
25948
  try {
25765
- rmSync5(dirname9(session.mcpConfigPath), { recursive: true, force: true });
25949
+ rmSync6(dirname9(session.mcpConfigPath), { recursive: true, force: true });
25766
25950
  } catch {
25767
25951
  }
25768
25952
  session.mcpConfigPath = null;
@@ -25814,7 +25998,7 @@ var init_claude = __esm({
25814
25998
  session.broker?.close();
25815
25999
  if (session.mcpConfigPath) {
25816
26000
  try {
25817
- rmSync5(dirname9(session.mcpConfigPath), { recursive: true, force: true });
26001
+ rmSync6(dirname9(session.mcpConfigPath), { recursive: true, force: true });
25818
26002
  } catch {
25819
26003
  }
25820
26004
  }
@@ -26028,9 +26212,9 @@ function mcpServerNamesInToml(toml) {
26028
26212
  return names;
26029
26213
  }
26030
26214
  function codexConfigMcpServerNames(env) {
26031
- const codexHome3 = env.CODEX_HOME || join22(env.HOME || env.USERPROFILE || homedir10(), ".codex");
26215
+ const codexHome2 = env.CODEX_HOME || join22(env.HOME || env.USERPROFILE || homedir10(), ".codex");
26032
26216
  try {
26033
- return mcpServerNamesInToml(readFileSync9(join22(codexHome3, "config.toml"), "utf8"));
26217
+ return mcpServerNamesInToml(readFileSync9(join22(codexHome2, "config.toml"), "utf8"));
26034
26218
  } catch {
26035
26219
  return /* @__PURE__ */ new Set();
26036
26220
  }
@@ -26049,10 +26233,124 @@ var init_codex_mcp_names = __esm({
26049
26233
  }
26050
26234
  });
26051
26235
 
26236
+ // server/drivers/codex-identity.ts
26237
+ import { homedir as homedir11 } from "node:os";
26238
+ import { isAbsolute as isAbsolute6, join as join23, resolve as resolve7 } from "node:path";
26239
+ function codexHome(env) {
26240
+ if (env.CODEX_HOME) return isAbsolute6(env.CODEX_HOME) ? resolve7(env.CODEX_HOME) : null;
26241
+ const home = userHome(env);
26242
+ return isAbsolute6(home) ? join23(home, ".codex") : null;
26243
+ }
26244
+ async function codexAccountEmail(cli, env, timeoutMs = 3e3) {
26245
+ const cwd = env.HOME || env.USERPROFILE || homedir11();
26246
+ if (!isAbsolute6(cwd) || !codexHome(env)) return null;
26247
+ return new Promise((resolveEmail) => {
26248
+ let child;
26249
+ try {
26250
+ child = spawnCli(cli, ["app-server"], { cwd, env, stdio: ["pipe", "pipe", "pipe"] });
26251
+ } catch {
26252
+ resolveEmail(null);
26253
+ return;
26254
+ }
26255
+ let finishing = false;
26256
+ let buffer = "";
26257
+ let outputBytes = 0;
26258
+ let initialized = false;
26259
+ const finish = async (email3) => {
26260
+ if (finishing) return;
26261
+ finishing = true;
26262
+ clearTimeout(timer);
26263
+ let stopped = await killCliTree(child, 1e3);
26264
+ if (!stopped && child.pid) {
26265
+ try {
26266
+ if (process.platform === "win32") child.kill("SIGKILL");
26267
+ else process.kill(-child.pid, "SIGKILL");
26268
+ } catch {
26269
+ try {
26270
+ child.kill("SIGKILL");
26271
+ } catch {
26272
+ }
26273
+ }
26274
+ stopped = await killCliTree(child, 1e3);
26275
+ }
26276
+ resolveEmail(stopped ? email3 : null);
26277
+ };
26278
+ const timer = setTimeout(() => {
26279
+ void finish(null);
26280
+ }, timeoutMs);
26281
+ timer.unref();
26282
+ const send = (message2) => {
26283
+ try {
26284
+ child.stdin.write(`${JSON.stringify(message2)}
26285
+ `);
26286
+ } catch {
26287
+ void finish(null);
26288
+ }
26289
+ };
26290
+ child.stdin.on("error", () => {
26291
+ void finish(null);
26292
+ });
26293
+ child.stderr.resume();
26294
+ child.on("error", () => {
26295
+ void finish(null);
26296
+ });
26297
+ child.on("close", () => {
26298
+ void finish(null);
26299
+ });
26300
+ child.stdout.setEncoding("utf8");
26301
+ child.stdout.on("data", (chunk) => {
26302
+ if (finishing) return;
26303
+ outputBytes += Buffer.byteLength(chunk);
26304
+ if (outputBytes > MAX_OUTPUT3) {
26305
+ void finish(null);
26306
+ return;
26307
+ }
26308
+ buffer += chunk;
26309
+ let newline;
26310
+ while (!finishing && (newline = buffer.indexOf("\n")) !== -1) {
26311
+ const line = buffer.slice(0, newline);
26312
+ buffer = buffer.slice(newline + 1);
26313
+ if (!line.trim()) continue;
26314
+ let message2;
26315
+ try {
26316
+ message2 = JSON.parse(line);
26317
+ } catch {
26318
+ void finish(null);
26319
+ return;
26320
+ }
26321
+ if (!initialized && message2?.id === 1) {
26322
+ if (message2.error || !message2.result) {
26323
+ void finish(null);
26324
+ return;
26325
+ }
26326
+ initialized = true;
26327
+ send({ method: "initialized", params: {} });
26328
+ send({ id: 2, method: "account/read", params: { refreshToken: false } });
26329
+ } else if (initialized && message2?.id === 2) {
26330
+ const account = message2.error ? null : message2.result?.account;
26331
+ const email3 = account?.type === "chatgpt" ? account.email : null;
26332
+ void finish(typeof email3 === "string" && email3.length <= 254 && EMAIL.test(email3) && !/[\p{Cc}\p{Cf}]/u.test(email3) ? email3 : null);
26333
+ }
26334
+ }
26335
+ });
26336
+ send({ id: 1, method: "initialize", params: { clientInfo: { name: "openmausbot", version: "1" } } });
26337
+ });
26338
+ }
26339
+ var MAX_OUTPUT3, EMAIL;
26340
+ var init_codex_identity = __esm({
26341
+ "server/drivers/codex-identity.ts"() {
26342
+ "use strict";
26343
+ init_env_path();
26344
+ init_procs();
26345
+ MAX_OUTPUT3 = 16384;
26346
+ EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
26347
+ }
26348
+ });
26349
+
26052
26350
  // server/drivers/codex-catalog.ts
26053
26351
  import { readdirSync as readdirSync2, readFileSync as readFileSync10 } from "node:fs";
26054
- import { homedir as homedir11 } from "node:os";
26055
- import { basename as basename7, join as join23 } from "node:path";
26352
+ import { homedir as homedir12 } from "node:os";
26353
+ import { basename as basename7, join as join24 } from "node:path";
26056
26354
  function encodeCodexSelection(provider, model) {
26057
26355
  return `${provider}${SEP}${model}`;
26058
26356
  }
@@ -26067,7 +26365,7 @@ function decodeCodexSelection(id) {
26067
26365
  function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
26068
26366
  return new Promise((resolve12) => {
26069
26367
  const child = spawnCli(cli, ["app-server"], {
26070
- cwd: homedir11(),
26368
+ cwd: homedir12(),
26071
26369
  env,
26072
26370
  stdio: ["pipe", "pipe", "pipe"]
26073
26371
  });
@@ -26166,10 +26464,6 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
26166
26464
  request("initialize", { clientInfo: { name: "openmausbot", version: "1" } }, "initialize");
26167
26465
  });
26168
26466
  }
26169
- function codexHome(env) {
26170
- if (env.CODEX_HOME) return env.CODEX_HOME;
26171
- return join23(env.HOME || env.USERPROFILE || homedir11(), ".codex");
26172
- }
26173
26467
  function unquote(raw) {
26174
26468
  let value = raw.trim();
26175
26469
  const hash2 = value.indexOf(" #");
@@ -26250,15 +26544,18 @@ function niceLabel(model, provider, known, named) {
26250
26544
  const encoded = encodeCodexSelection(provider, model);
26251
26545
  if (named.has(encoded)) return named.get(encoded);
26252
26546
  const host = providerName(provider, known);
26253
- return host === provider ? model : `${model} (${host})`;
26547
+ return qualifiedModelLabel(model, host, {
26548
+ redundantWhen: () => host === provider,
26549
+ decorate: (facet) => ` (${facet})`
26550
+ });
26254
26551
  }
26255
26552
  function collectCatalogNames(home) {
26256
26553
  const named = /* @__PURE__ */ new Map();
26257
- for (const file2 of listDir(join23(home, "model-catalogs"))) {
26554
+ for (const file2 of listDir(join24(home, "model-catalogs"))) {
26258
26555
  if (!file2.endsWith(".json")) continue;
26259
26556
  const provider = basename7(file2, ".json").replace(/-models$/, "");
26260
26557
  if (!PROVIDER_ID.test(provider)) continue;
26261
- const raw = readText(join23(home, "model-catalogs", file2));
26558
+ const raw = readText(join24(home, "model-catalogs", file2));
26262
26559
  if (!raw) continue;
26263
26560
  let parsed;
26264
26561
  try {
@@ -26310,7 +26607,8 @@ async function probeProviderModels(provider, env, fetchImpl) {
26310
26607
  async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli) {
26311
26608
  const official = (cli ? await readCodexAppServerModelCatalog(cli, env) : null) ?? STATIC_CODEX_MODELS;
26312
26609
  const home = codexHome(env);
26313
- const mainText = readText(join23(home, "config.toml"));
26610
+ if (!home) return mergeLocalInject(official, env, fetchImpl);
26611
+ const mainText = readText(join24(home, "config.toml"));
26314
26612
  if (!mainText) return mergeLocalInject(official, env, fetchImpl);
26315
26613
  const main2 = parseCodexToml(mainText);
26316
26614
  const known = new Map(main2.providers.map((provider) => [provider.id, provider]));
@@ -26325,7 +26623,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
26325
26623
  remember(main2.modelProvider, main2.model);
26326
26624
  for (const file2 of listDir(home)) {
26327
26625
  if (!file2.endsWith(".config.toml")) continue;
26328
- const profile = parseCodexToml(readText(join23(home, file2)) ?? "");
26626
+ const profile = parseCodexToml(readText(join24(home, file2)) ?? "");
26329
26627
  for (const provider of profile.providers) {
26330
26628
  if (!known.has(provider.id)) known.set(provider.id, provider);
26331
26629
  }
@@ -26368,11 +26666,17 @@ var STATIC_CODEX_MODELS, OFFICIAL_CODEX_PROVIDER, SEP, MODEL_ID2, PROVIDER_ID;
26368
26666
  var init_codex_catalog = __esm({
26369
26667
  "server/drivers/codex-catalog.ts"() {
26370
26668
  "use strict";
26669
+ init_contracts();
26371
26670
  init_procs();
26671
+ init_codex_identity();
26372
26672
  init_local_inject();
26373
26673
  STATIC_CODEX_MODELS = {
26374
26674
  default: "gpt-5.6-sol",
26375
26675
  options: [
26676
+ // Only used when live discovery fails; these rows do not establish account access.
26677
+ { id: "gpt-6-astra", label: "GPT-6 Astra (availability unverified)" },
26678
+ { id: "gpt-6-sol", label: "GPT-6 Sol (availability unverified)" },
26679
+ { id: "gpt-6-luna", label: "GPT-6 Luna (availability unverified)" },
26376
26680
  { id: "gpt-5.6-sol", label: "GPT-5.6 Sol" },
26377
26681
  { id: "gpt-5.6-terra", label: "GPT-5.6 Terra" },
26378
26682
  { id: "gpt-5.6-luna", label: "GPT-5.6 Luna" },
@@ -26392,7 +26696,7 @@ var init_codex_catalog = __esm({
26392
26696
  // server/drivers/codex-instructions.ts
26393
26697
  import { createHash as createHash8 } from "node:crypto";
26394
26698
  import { mkdirSync as mkdirSync11, readFileSync as readFileSync11 } from "node:fs";
26395
- import { join as join24 } from "node:path";
26699
+ import { join as join25 } from "node:path";
26396
26700
  function parseReceipt(raw) {
26397
26701
  if (!raw) return null;
26398
26702
  const trimmed = raw.trim();
@@ -26425,8 +26729,8 @@ function codexDeveloperInstructions(config2, botInstructions) {
26425
26729
  ${configured}` : botInstructions;
26426
26730
  }
26427
26731
  async function syncCodexInstructions(key, nativeThreadId, instructions, volatile, resumed, request, mentionTurn = false) {
26428
- const directory = join24(DATA_DIR, "codex-instructions");
26429
- const path3 = join24(directory, `${digest2(JSON.stringify([key, nativeThreadId]))}.sha256`);
26732
+ const directory = join25(DATA_DIR, "codex-instructions");
26733
+ const path3 = join25(directory, `${digest2(JSON.stringify([key, nativeThreadId]))}.sha256`);
26430
26734
  const fingerprint = digest2(instructions);
26431
26735
  const volatileFingerprint = digest2(volatile);
26432
26736
  let previous;
@@ -26482,15 +26786,15 @@ var init_codex_instructions = __esm({
26482
26786
  // server/drivers/codex-device-auth.ts
26483
26787
  import { randomUUID as randomUUID9 } from "node:crypto";
26484
26788
  import { realpathSync as realpathSync3 } from "node:fs";
26485
- import { homedir as homedir12 } from "node:os";
26486
- import { basename as basename8, dirname as dirname10, isAbsolute as isAbsolute6, join as join25, resolve as resolve7 } from "node:path";
26789
+ import { homedir as homedir13 } from "node:os";
26790
+ import { basename as basename8, dirname as dirname10, isAbsolute as isAbsolute7, join as join26, resolve as resolve8 } from "node:path";
26487
26791
  import { stripVTControlCharacters as stripVTControlCharacters5 } from "node:util";
26488
26792
  function canonicalPath2(path3) {
26489
26793
  try {
26490
26794
  return realpathSync3(path3);
26491
26795
  } catch {
26492
26796
  const parent = dirname10(path3);
26493
- return parent === path3 ? path3 : join25(canonicalPath2(parent), basename8(path3));
26797
+ return parent === path3 ? path3 : join26(canonicalPath2(parent), basename8(path3));
26494
26798
  }
26495
26799
  }
26496
26800
  function codexDevicePrompt(output) {
@@ -26512,13 +26816,13 @@ function loginFailure(output) {
26512
26816
  }
26513
26817
  return "ChatGPT sign-in did not finish. Check the server's connection and that device-code login is enabled in ChatGPT, then try again.";
26514
26818
  }
26515
- var DEVICE_URL, MAX_OUTPUT3, authenticatingHomes2, CodexDeviceAuthController;
26819
+ var DEVICE_URL, MAX_OUTPUT4, authenticatingHomes2, CodexDeviceAuthController;
26516
26820
  var init_codex_device_auth = __esm({
26517
26821
  "server/drivers/codex-device-auth.ts"() {
26518
26822
  "use strict";
26519
26823
  init_procs();
26520
26824
  DEVICE_URL = "https://auth.openai.com/codex/device";
26521
- MAX_OUTPUT3 = 16384;
26825
+ MAX_OUTPUT4 = 16384;
26522
26826
  authenticatingHomes2 = /* @__PURE__ */ new Set();
26523
26827
  CodexDeviceAuthController = class {
26524
26828
  flow = null;
@@ -26537,11 +26841,11 @@ var init_codex_device_auth = __esm({
26537
26841
  await this.flow?.stopping;
26538
26842
  if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing in.");
26539
26843
  const env = this.options.environment();
26540
- const home = env.HOME || env.USERPROFILE || homedir12();
26541
- if (!isAbsolute6(home) || env.CODEX_HOME && !isAbsolute6(env.CODEX_HOME)) {
26844
+ const home = env.HOME || env.USERPROFILE || homedir13();
26845
+ if (!isAbsolute7(home) || env.CODEX_HOME && !isAbsolute7(env.CODEX_HOME)) {
26542
26846
  throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing in.");
26543
26847
  }
26544
- const homeKey = canonicalPath2(resolve7(env.CODEX_HOME || join25(home, ".codex")));
26848
+ const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join26(home, ".codex")));
26545
26849
  if (authenticatingHomes2.has(homeKey)) {
26546
26850
  throw new Error("A ChatGPT sign-in is already running for this server account. Finish or cancel it before starting another.");
26547
26851
  }
@@ -26616,11 +26920,11 @@ var init_codex_device_auth = __esm({
26616
26920
  await this.flow?.stopping;
26617
26921
  if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing out.");
26618
26922
  const env = this.options.environment();
26619
- const home = env.HOME || env.USERPROFILE || homedir12();
26620
- if (!isAbsolute6(home) || env.CODEX_HOME && !isAbsolute6(env.CODEX_HOME)) {
26923
+ const home = env.HOME || env.USERPROFILE || homedir13();
26924
+ if (!isAbsolute7(home) || env.CODEX_HOME && !isAbsolute7(env.CODEX_HOME)) {
26621
26925
  throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing out.");
26622
26926
  }
26623
- const homeKey = canonicalPath2(resolve7(env.CODEX_HOME || join25(home, ".codex")));
26927
+ const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join26(home, ".codex")));
26624
26928
  if (authenticatingHomes2.has(homeKey)) {
26625
26929
  throw new Error("A ChatGPT sign-in is running for this server account. Finish or cancel it before signing out.");
26626
26930
  }
@@ -26663,7 +26967,7 @@ var init_codex_device_auth = __esm({
26663
26967
  child.stdin.end();
26664
26968
  let output = "";
26665
26969
  const receive = (chunk) => {
26666
- if (output.length < MAX_OUTPUT3) output += chunk.toString("utf8").slice(0, MAX_OUTPUT3 - output.length);
26970
+ if (output.length < MAX_OUTPUT4) output += chunk.toString("utf8").slice(0, MAX_OUTPUT4 - output.length);
26667
26971
  };
26668
26972
  child.stdout.on("data", receive);
26669
26973
  child.stderr.on("data", receive);
@@ -26698,7 +27002,7 @@ var init_codex_device_auth = __esm({
26698
27002
  let output = "";
26699
27003
  const receive = (chunk) => {
26700
27004
  if (flow.status.phase !== "waiting") return;
26701
- if (output.length + chunk.length > MAX_OUTPUT3) {
27005
+ if (output.length + chunk.length > MAX_OUTPUT4) {
26702
27006
  this.finish(flow, "failed", "Codex returned an unexpected sign-in response. Update the server's Codex CLI and try again.");
26703
27007
  return;
26704
27008
  }
@@ -26777,116 +27081,51 @@ var init_codex_device_auth = __esm({
26777
27081
  }
26778
27082
  });
26779
27083
 
26780
- // server/drivers/codex-identity.ts
26781
- import { homedir as homedir13 } from "node:os";
26782
- import { isAbsolute as isAbsolute7, join as join26, resolve as resolve8 } from "node:path";
26783
- function codexHome2(env) {
26784
- if (env.CODEX_HOME) return isAbsolute7(env.CODEX_HOME) ? resolve8(env.CODEX_HOME) : null;
26785
- const home = env.HOME || env.USERPROFILE || homedir13();
26786
- return isAbsolute7(home) ? join26(home, ".codex") : null;
27084
+ // server/drivers/codex-release.ts
27085
+ function parseCodexReleaseVersion(value) {
27086
+ const match = /^(?:.*\bcodex-cli\s+)?v?(\d+)\.(\d+)\.(\d+)(-[0-9a-z.-]+)?(?:\+[0-9a-z.-]+)?$/i.exec(value.trim());
27087
+ return match ? { parts: match.slice(1, 4).map(Number), prerelease: Boolean(match[4]) } : null;
26787
27088
  }
26788
- async function codexAccountEmail(cli, env, timeoutMs = 3e3) {
26789
- const cwd = env.HOME || env.USERPROFILE || homedir13();
26790
- if (!isAbsolute7(cwd) || !codexHome2(env)) return null;
26791
- return new Promise((resolveEmail) => {
26792
- let child;
26793
- try {
26794
- child = spawnCli(cli, ["app-server"], { cwd, env, stdio: ["pipe", "pipe", "pipe"] });
26795
- } catch {
26796
- resolveEmail(null);
26797
- return;
26798
- }
26799
- let finishing = false;
26800
- let buffer = "";
26801
- let outputBytes = 0;
26802
- let initialized = false;
26803
- const finish = async (email3) => {
26804
- if (finishing) return;
26805
- finishing = true;
26806
- clearTimeout(timer);
26807
- let stopped = await killCliTree(child, 1e3);
26808
- if (!stopped && child.pid) {
26809
- try {
26810
- if (process.platform === "win32") child.kill("SIGKILL");
26811
- else process.kill(-child.pid, "SIGKILL");
26812
- } catch {
26813
- try {
26814
- child.kill("SIGKILL");
26815
- } catch {
26816
- }
26817
- }
26818
- stopped = await killCliTree(child, 1e3);
26819
- }
26820
- resolveEmail(stopped ? email3 : null);
26821
- };
26822
- const timer = setTimeout(() => {
26823
- void finish(null);
26824
- }, timeoutMs);
26825
- timer.unref();
26826
- const send = (message2) => {
27089
+ function codexVersionBehind(installed, latest) {
27090
+ const a2 = parseCodexReleaseVersion(installed);
27091
+ const b2 = parseCodexReleaseVersion(latest);
27092
+ if (!a2 || !b2 || b2.prerelease) return false;
27093
+ for (let i2 = 0; i2 < 3; i2++) {
27094
+ if (a2.parts[i2] !== b2.parts[i2]) return a2.parts[i2] < b2.parts[i2];
27095
+ }
27096
+ return a2.prerelease;
27097
+ }
27098
+ function createCodexReleaseReader(fetchImpl = fetch, now = Date.now) {
27099
+ let cached3;
27100
+ let expires = 0;
27101
+ return () => {
27102
+ if (cached3 && now() < expires) return cached3;
27103
+ expires = now() + 60 * 60 * 1e3;
27104
+ cached3 = (async () => {
26827
27105
  try {
26828
- child.stdin.write(`${JSON.stringify(message2)}
26829
- `);
27106
+ const response = await fetchImpl("https://registry.npmjs.org/@openai/codex/latest", {
27107
+ signal: AbortSignal.timeout(3e3),
27108
+ headers: { Accept: "application/json" }
27109
+ });
27110
+ if (!response.ok) throw new Error("Release lookup failed");
27111
+ const payload = await response.json();
27112
+ const version2 = typeof payload.version === "string" ? payload.version : "";
27113
+ const parsed = parseCodexReleaseVersion(version2);
27114
+ if (!parsed || parsed.prerelease) throw new Error("Invalid stable release");
27115
+ return version2;
26830
27116
  } catch {
26831
- void finish(null);
26832
- }
26833
- };
26834
- child.stdin.on("error", () => {
26835
- void finish(null);
26836
- });
26837
- child.stderr.resume();
26838
- child.on("error", () => {
26839
- void finish(null);
26840
- });
26841
- child.on("close", () => {
26842
- void finish(null);
26843
- });
26844
- child.stdout.setEncoding("utf8");
26845
- child.stdout.on("data", (chunk) => {
26846
- if (finishing) return;
26847
- outputBytes += Buffer.byteLength(chunk);
26848
- if (outputBytes > MAX_OUTPUT4) {
26849
- void finish(null);
26850
- return;
26851
- }
26852
- buffer += chunk;
26853
- let newline;
26854
- while (!finishing && (newline = buffer.indexOf("\n")) !== -1) {
26855
- const line = buffer.slice(0, newline);
26856
- buffer = buffer.slice(newline + 1);
26857
- if (!line.trim()) continue;
26858
- let message2;
26859
- try {
26860
- message2 = JSON.parse(line);
26861
- } catch {
26862
- void finish(null);
26863
- return;
26864
- }
26865
- if (!initialized && message2?.id === 1) {
26866
- if (message2.error || !message2.result) {
26867
- void finish(null);
26868
- return;
26869
- }
26870
- initialized = true;
26871
- send({ method: "initialized", params: {} });
26872
- send({ id: 2, method: "account/read", params: { refreshToken: false } });
26873
- } else if (initialized && message2?.id === 2) {
26874
- const account = message2.error ? null : message2.result?.account;
26875
- const email3 = account?.type === "chatgpt" ? account.email : null;
26876
- void finish(typeof email3 === "string" && email3.length <= 254 && EMAIL.test(email3) && !/[\p{Cc}\p{Cf}]/u.test(email3) ? email3 : null);
26877
- }
27117
+ expires = now() + 5 * 60 * 1e3;
27118
+ return null;
26878
27119
  }
26879
- });
26880
- send({ id: 1, method: "initialize", params: { clientInfo: { name: "openmausbot", version: "1" } } });
26881
- });
27120
+ })();
27121
+ return cached3;
27122
+ };
26882
27123
  }
26883
- var MAX_OUTPUT4, EMAIL;
26884
- var init_codex_identity = __esm({
26885
- "server/drivers/codex-identity.ts"() {
27124
+ var readLatestCodexRelease;
27125
+ var init_codex_release = __esm({
27126
+ "server/drivers/codex-release.ts"() {
26886
27127
  "use strict";
26887
- init_procs();
26888
- MAX_OUTPUT4 = 16384;
26889
- EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
27128
+ readLatestCodexRelease = createCodexReleaseReader();
26890
27129
  }
26891
27130
  });
26892
27131
 
@@ -26896,18 +27135,6 @@ import { homedir as homedir14 } from "node:os";
26896
27135
  function missingNativeCodexThread(error51, cursor3) {
26897
27136
  return error51 instanceof CodexRpcError && error51.code === -32600 && error51.message === `no rollout found for thread id ${cursor3}`;
26898
27137
  }
26899
- function codexPredatesAstra(version2) {
26900
- const value = version2.trim();
26901
- const match = /\bcodex-cli\s+v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?(?![\d.])\b/i.exec(value) ?? /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?$/i.exec(value);
26902
- if (!match) return false;
26903
- const installed = match.slice(1, 4).map(Number);
26904
- for (let i2 = 0; i2 < ASTRA_MIN_CODEX_VERSION.length; i2 += 1) {
26905
- if (installed[i2] !== ASTRA_MIN_CODEX_VERSION[i2]) {
26906
- return installed[i2] < ASTRA_MIN_CODEX_VERSION[i2];
26907
- }
26908
- }
26909
- return false;
26910
- }
26911
27138
  function codexUpdateCommand(cli, platform = process.platform) {
26912
27139
  if (cli === "codex") return "codex update";
26913
27140
  const trimmed = cli.trim();
@@ -26916,13 +27143,12 @@ function codexUpdateCommand(cli, platform = process.platform) {
26916
27143
  const command = (tokens.length > 0 ? tokens : [trimmed]).map(quote2).join(" ");
26917
27144
  return platform === "win32" ? `& ${command} update` : `${command} update`;
26918
27145
  }
26919
- function codexAstraUpdate(version2, models2, cli) {
26920
- if (models2.options.some((model) => model.id === ASTRA_MODEL_ID) || !codexPredatesAstra(version2)) {
26921
- return void 0;
26922
- }
27146
+ async function codexReleaseUpdate(version2, cli) {
27147
+ const latest = await readLatestCodexRelease();
27148
+ if (!latest || !codexVersionBehind(version2, latest)) return void 0;
26923
27149
  return {
26924
- title: "Update Codex for GPT-6 Astra",
26925
- message: "This Codex version predates Astra support. Update it, then refresh models. Astra must also be available to your signed-in ChatGPT account.",
27150
+ title: `Update Codex to ${latest}`,
27151
+ message: `A newer stable Codex CLI is available (installed: ${version2}). Update it, then refresh models. Model availability also depends on your signed-in account.`,
26926
27152
  command: codexUpdateCommand(cli)
26927
27153
  };
26928
27154
  }
@@ -27224,7 +27450,7 @@ function mountMcpServer(appServerArgs, env, name, server, preApproved = true) {
27224
27450
  appServerArgs.push("-c", `${prefix}.default_tools_approval_mode="auto"`);
27225
27451
  }
27226
27452
  }
27227
- var DRIVER_KIND3, ASTRA_MODEL_ID, ASTRA_MIN_CODEX_VERSION, CodexRpcError, DENY_TIMEOUT_NOTE2, skippedSseServers, renamedMcpServers, plainRecord, containsControlCharacter, CodexDriver;
27453
+ var DRIVER_KIND3, CodexRpcError, DENY_TIMEOUT_NOTE2, skippedSseServers, renamedMcpServers, plainRecord, containsControlCharacter, CodexDriver;
27228
27454
  var init_codex = __esm({
27229
27455
  "server/drivers/codex.ts"() {
27230
27456
  "use strict";
@@ -27248,10 +27474,9 @@ var init_codex = __esm({
27248
27474
  init_resume_recovery();
27249
27475
  init_mcp_tool_images();
27250
27476
  init_ask_question();
27477
+ init_codex_release();
27251
27478
  init_codex_catalog();
27252
27479
  DRIVER_KIND3 = "codex";
27253
- ASTRA_MODEL_ID = "gpt-6-astra";
27254
- ASTRA_MIN_CODEX_VERSION = [0, 153, 1];
27255
27480
  CodexRpcError = class extends Error {
27256
27481
  code;
27257
27482
  constructor(error51) {
@@ -28090,7 +28315,7 @@ ${stderr ?? ""}`))
28090
28315
  version: version2,
28091
28316
  authenticated,
28092
28317
  ...email3 ? { account: { email: email3 } } : {},
28093
- update: codexAstraUpdate(version2, models2, config2.cli),
28318
+ update: await codexReleaseUpdate(version2, config2.cli),
28094
28319
  billing: "subscription"
28095
28320
  };
28096
28321
  };
@@ -37738,7 +37963,8 @@ function createOpenAIChatRuntime(options) {
37738
37963
  const done = new Promise((resolve12) => {
37739
37964
  resolveDone = resolve12;
37740
37965
  });
37741
- active.set(turn.threadId, { abort, turnId, done, approval });
37966
+ const turnEntry = { abort, turnId, done, approval, asides: [] };
37967
+ active.set(turn.threadId, turnEntry);
37742
37968
  emit({ ...base2(turn.threadId, turnId), type: "turn.started" });
37743
37969
  emit({ ...base2(turn.threadId, turnId), type: "session.started", sessionId: null, model });
37744
37970
  void (async () => {
@@ -37759,6 +37985,8 @@ function createOpenAIChatRuntime(options) {
37759
37985
  }
37760
37986
  for (let round = 0; round < 16; round++) {
37761
37987
  abort.signal.throwIfAborted();
37988
+ const parked = turnEntry.asides.splice(0);
37989
+ if (parked.length) messages.push({ role: "user", content: parked.join("\n\n") });
37762
37990
  native("out", options.nativeLog.outgoing(turn, messages, model));
37763
37991
  let attempt = 0;
37764
37992
  let completion;
@@ -37975,6 +38203,10 @@ function createOpenAIChatRuntime(options) {
37975
38203
  capabilities: {
37976
38204
  ...options.computerUse ? {
37977
38205
  computerMcp: options.tools !== false,
38206
+ // Same gate as cloudComputerMcp: with tools off the runtime cannot
38207
+ // mount the leased Box descriptor either. The fleet invariant test
38208
+ // pins usesCloudComputer === (remoteAgent || cloudComputerMcp).
38209
+ usesCloudComputer: options.tools !== false,
37978
38210
  cloudComputerMcp: options.tools !== false,
37979
38211
  localComputerMcp: options.tools !== false,
37980
38212
  browserMcp: options.tools !== false,
@@ -37984,7 +38216,10 @@ function createOpenAIChatRuntime(options) {
37984
38216
  sessionModelSwitch: "in-session",
37985
38217
  customMcp: options.tools !== false,
37986
38218
  agentsMcp: options.tools !== false,
37987
- composioMcp: options.tools !== false
38219
+ composioMcp: options.tools !== false,
38220
+ // The runtime owns the whole tool loop, so it can always take a
38221
+ // user message mid-turn: park it, deliver before the next completion.
38222
+ queueing: true
37988
38223
  },
37989
38224
  sendTurn,
37990
38225
  interruptTurn: async (threadId, turnId) => {
@@ -37994,6 +38229,12 @@ function createOpenAIChatRuntime(options) {
37994
38229
  await turn.done;
37995
38230
  },
37996
38231
  respondToRequest: async (threadId, requestId, decision) => active.get(threadId)?.approval.answer(requestId, decision.behavior, decision.message) ?? "unavailable",
38232
+ steer: async (threadId, text4) => {
38233
+ const running2 = active.get(threadId);
38234
+ if (!running2) return "refused";
38235
+ running2.asides.push(text4);
38236
+ return "steered";
38237
+ },
37997
38238
  hasSession: (threadId) => active.has(threadId),
37998
38239
  stopAll: async () => {
37999
38240
  const turns = [...active.values()];
@@ -38103,12 +38344,7 @@ var init_grok = __esm({
38103
38344
 
38104
38345
  // server/drivers/acp/grok.ts
38105
38346
  import { existsSync as existsSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
38106
- import { homedir as homedir15 } from "node:os";
38107
38347
  import { join as join30 } from "node:path";
38108
- function grokHome(env) {
38109
- if (env.GROK_HOME) return env.GROK_HOME;
38110
- return join30(env.HOME || env.USERPROFILE || homedir15(), ".grok");
38111
- }
38112
38348
  function unquote2(raw) {
38113
38349
  const value = raw.trim();
38114
38350
  if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
@@ -38117,7 +38353,7 @@ function unquote2(raw) {
38117
38353
  return value;
38118
38354
  }
38119
38355
  function readGrokModelCatalog(env = process.env) {
38120
- const path3 = join30(grokHome(env), "config.toml");
38356
+ const path3 = join30(env.GROK_HOME || harnessHome("grok", env), "config.toml");
38121
38357
  let text4 = "";
38122
38358
  try {
38123
38359
  text4 = readFileSync16(path3, "utf8");
@@ -38190,7 +38426,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
38190
38426
  if (!inject) return modelId;
38191
38427
  const host = localHost(inject.host);
38192
38428
  if (!host) return modelId;
38193
- const path3 = join30(grokHome(env), "config.toml");
38429
+ const path3 = join30(env.GROK_HOME || harnessHome("grok", env), "config.toml");
38194
38430
  let text4 = "";
38195
38431
  try {
38196
38432
  text4 = readFileSync16(path3, "utf8");
@@ -38257,6 +38493,7 @@ var STATIC_GROK_MODELS, SLUG, support2, GrokAgentDriver;
38257
38493
  var init_grok2 = __esm({
38258
38494
  "server/drivers/acp/grok.ts"() {
38259
38495
  "use strict";
38496
+ init_env_path();
38260
38497
  init_local_inject();
38261
38498
  init_core3();
38262
38499
  STATIC_GROK_MODELS = {
@@ -38337,7 +38574,7 @@ var init_grok2 = __esm({
38337
38574
  // an unauthenticated CLI is a user action, not something to paper over.
38338
38575
  pickAuthMethod: (methods) => methods.some((m2) => m2.id === "cached_token") ? "cached_token" : null,
38339
38576
  authFailure: "fail",
38340
- isAuthenticated: () => existsSync11(join30(homedir15(), ".grok", "auth.json")),
38577
+ isAuthenticated: (env) => existsSync11(join30(harnessHome("grok", env), "auth.json")),
38341
38578
  // `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
38342
38579
  // reach the agent-stdio system prompt (verified against 1.0.0), so the
38343
38580
  // persona is prepended codex-style.
@@ -38351,11 +38588,10 @@ ${turn.text}` : turn.text
38351
38588
 
38352
38589
  // server/drivers/acp/gemini.ts
38353
38590
  import { existsSync as existsSync12, readFileSync as readFileSync17 } from "node:fs";
38354
- import { homedir as homedir16 } from "node:os";
38355
38591
  import { join as join31 } from "node:path";
38356
- function liveOauthCredential() {
38592
+ function liveOauthCredential(env) {
38357
38593
  try {
38358
- const creds = JSON.parse(readFileSync17(join31(homedir16(), ".gemini", "oauth_creds.json"), "utf8"));
38594
+ const creds = JSON.parse(readFileSync17(join31(userHome(env), ".gemini", "oauth_creds.json"), "utf8"));
38359
38595
  if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
38360
38596
  const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
38361
38597
  if (creds.expiry_date !== void 0) {
@@ -38368,7 +38604,7 @@ function liveOauthCredential() {
38368
38604
  }
38369
38605
  }
38370
38606
  function geminiIsAuthenticated(env) {
38371
- return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync12(join31(homedir16(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
38607
+ return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync12(join31(userHome(env), ".gemini", "oauth_creds.json")) && liveOauthCredential(env);
38372
38608
  }
38373
38609
  function geminiApprovalArgs(fullAuto, approvalMode) {
38374
38610
  if (fullAuto) return ["--yolo"];
@@ -38379,6 +38615,7 @@ var AUTH_PREFERENCE, nonBlank, support3, GeminiAgentDriver;
38379
38615
  var init_gemini = __esm({
38380
38616
  "server/drivers/acp/gemini.ts"() {
38381
38617
  "use strict";
38618
+ init_env_path();
38382
38619
  init_core3();
38383
38620
  AUTH_PREFERENCE = ["gemini-api-key", "oauth-personal", "vertex-ai"];
38384
38621
  nonBlank = (value) => Boolean(value?.trim());
@@ -38416,13 +38653,13 @@ ${turn.text}` : turn.text
38416
38653
 
38417
38654
  // server/drivers/acp/kimi.ts
38418
38655
  import { existsSync as existsSync13, mkdirSync as mkdirSync15, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "node:fs";
38419
- import { homedir as homedir17 } from "node:os";
38656
+ import { homedir as homedir15 } from "node:os";
38420
38657
  import { join as join32 } from "node:path";
38421
38658
  function isLocalInjectAlias(slug) {
38422
38659
  return LOCAL_PROVIDER_PREFIXES.some((prefix) => slug.startsWith(prefix));
38423
38660
  }
38424
38661
  function kimiDataRoot(env) {
38425
- return env.KIMI_CODE_HOME || join32(env.HOME || env.USERPROFILE || homedir17(), ".kimi-code");
38662
+ return env.KIMI_CODE_HOME || join32(env.HOME || env.USERPROFILE || homedir15(), ".kimi-code");
38426
38663
  }
38427
38664
  function credentialsPath(env) {
38428
38665
  return join32(kimiDataRoot(env), "credentials", "kimi-code.json");
@@ -38898,26 +39135,22 @@ ${turn.text}` : turn.text
38898
39135
 
38899
39136
  // server/drivers/acp/droid.ts
38900
39137
  import { existsSync as existsSync14, mkdirSync as mkdirSync16, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "node:fs";
38901
- import { homedir as homedir18 } from "node:os";
38902
39138
  import { join as join33 } from "node:path";
38903
39139
  function authFilePaths(env) {
38904
- const home = env.FACTORY_HOME_OVERRIDE || env.HOME || homedir18();
39140
+ const home = env.FACTORY_HOME_OVERRIDE || userHome(env);
38905
39141
  return AUTH_FILES.map((name) => join33(home, ".factory", name));
38906
39142
  }
38907
39143
  function droidInjectId(host, model) {
38908
39144
  const safe = `${host}-${model}`.replace(/[^a-zA-Z0-9._+-]+/g, "-").replace(/-+/g, "-");
38909
39145
  return `${INJECT_ID_PREFIX}${safe}`;
38910
39146
  }
38911
- function factoryHome(env) {
38912
- return env.FACTORY_HOME_OVERRIDE || env.HOME || env.USERPROFILE || homedir18();
38913
- }
38914
39147
  function ensureDroidInjectModel(modelId, env = process.env) {
38915
39148
  const inject = decodeInjectId(modelId);
38916
39149
  if (!inject) return modelId;
38917
39150
  const host = localHost(inject.host);
38918
39151
  if (!host) return modelId;
38919
39152
  const id = droidInjectId(inject.host, inject.model);
38920
- const dir = join33(factoryHome(env), ".factory");
39153
+ const dir = join33(env.FACTORY_HOME_OVERRIDE || userHome(env), ".factory");
38921
39154
  mkdirSync16(dir, { recursive: true });
38922
39155
  const path3 = join33(dir, "settings.json");
38923
39156
  let settings2 = {};
@@ -38959,7 +39192,7 @@ function applyDroidLocalAuthEnv(env, modelId) {
38959
39192
  env.FACTORY_API_KEY = "openmausbot-local";
38960
39193
  }
38961
39194
  function readSettings(env) {
38962
- return JSON.parse(readFileSync19(join33(factoryHome(env), ".factory", "settings.json"), "utf8"));
39195
+ return JSON.parse(readFileSync19(join33(env.FACTORY_HOME_OVERRIDE || userHome(env), ".factory", "settings.json"), "utf8"));
38963
39196
  }
38964
39197
  async function resolveModels(env) {
38965
39198
  let catalog2 = MODELS3;
@@ -38997,6 +39230,7 @@ var AUTH_FILES, INJECT_ID_PREFIX, MODE_DEFAULT, MODE_FULL_AUTO, MODELS3, support
38997
39230
  var init_droid = __esm({
38998
39231
  "server/drivers/acp/droid.ts"() {
38999
39232
  "use strict";
39233
+ init_env_path();
39000
39234
  init_local_inject();
39001
39235
  init_core3();
39002
39236
  AUTH_FILES = ["auth.v2.file", "auth.v2.loginkeychain", "auth.v2.keyring"];
@@ -39110,8 +39344,8 @@ function asRecord(value) {
39110
39344
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
39111
39345
  }
39112
39346
  function labelFor(id, explicit) {
39113
- if (explicit?.trim()) return explicit.trim();
39114
- return id.split(/[-_./]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
39347
+ const trimmed = explicit?.trim();
39348
+ return trimmed || titleCaseModelId(id, /[-_./]+/);
39115
39349
  }
39116
39350
  function pushModel(options, seen, id, label, custom2 = false) {
39117
39351
  if (!SLUG3.test(id) || seen.has(id)) return;
@@ -39277,6 +39511,7 @@ var STATIC_CURSOR_MODELS, SLUG3, EXEC_TIMEOUT_MS, nonBlank2, support6, CursorAge
39277
39511
  var init_cursor = __esm({
39278
39512
  "server/drivers/acp/cursor.ts"() {
39279
39513
  "use strict";
39514
+ init_contracts();
39280
39515
  init_procs();
39281
39516
  init_core3();
39282
39517
  STATIC_CURSOR_MODELS = {
@@ -39350,10 +39585,10 @@ ${turn.text}` : turn.text
39350
39585
 
39351
39586
  // server/drivers/acp/opencode-go.ts
39352
39587
  import { existsSync as existsSync15, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "node:fs";
39353
- import { homedir as homedir19 } from "node:os";
39588
+ import { homedir as homedir16 } from "node:os";
39354
39589
  import { join as join34 } from "node:path";
39355
39590
  function labelForModel(id) {
39356
- return id.split(/[-_.]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
39591
+ return titleCaseModelId(id, /[-_.]+/);
39357
39592
  }
39358
39593
  function providerLabel(id) {
39359
39594
  if (id === "opencode") return "Zen";
@@ -39453,7 +39688,7 @@ async function discoverOpenCodeModels(environment, cli = "opencode") {
39453
39688
  }
39454
39689
  }
39455
39690
  function opencodeConfigDir(env) {
39456
- const home = env.HOME || env.USERPROFILE || homedir19();
39691
+ const home = env.HOME || env.USERPROFILE || homedir16();
39457
39692
  return join34(env.XDG_CONFIG_HOME || join34(home, ".config"), "opencode");
39458
39693
  }
39459
39694
  function ensureOpenCodeInjectModel(modelId, env = process.env) {
@@ -39500,7 +39735,7 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
39500
39735
  return native;
39501
39736
  }
39502
39737
  function storedAuthPaths(env) {
39503
- const home = env.HOME || env.USERPROFILE || homedir19();
39738
+ const home = env.HOME || env.USERPROFILE || homedir16();
39504
39739
  const roots = [
39505
39740
  env.XDG_DATA_HOME || join34(home, ".local", "share"),
39506
39741
  process.platform === "darwin" ? join34(home, "Library", "Application Support") : process.platform === "win32" ? env.LOCALAPPDATA || join34(home, "AppData", "Local") : ""
@@ -39568,6 +39803,7 @@ var init_opencode_go = __esm({
39568
39803
  "use strict";
39569
39804
  init_local_inject();
39570
39805
  init_core3();
39806
+ init_contracts();
39571
39807
  init_procs();
39572
39808
  STATIC_MODELS = {
39573
39809
  default: "opencode/x-preview-f-free",
@@ -39660,23 +39896,14 @@ ${turn.text}` : turn.text
39660
39896
  // server/drivers/acp/qwen.ts
39661
39897
  import { chmodSync as chmodSync5, existsSync as existsSync16, mkdirSync as mkdirSync18, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "node:fs";
39662
39898
  import { createHash as createHash12 } from "node:crypto";
39663
- import { homedir as homedir20 } from "node:os";
39664
39899
  import { join as join35 } from "node:path";
39665
- function qwenHome(env) {
39666
- const home = process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir20() : env.HOME || env.USERPROFILE || homedir20();
39667
- return join35(home, ".qwen");
39668
- }
39669
39900
  function isRecord3(value) {
39670
39901
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
39671
39902
  }
39672
- function qwenModelLabel(id, name) {
39673
- if (!name || name.toLocaleLowerCase().endsWith(id.toLocaleLowerCase())) return id;
39674
- return `${id} \u2014 ${name}`;
39675
- }
39676
39903
  function readQwenRoutes(env) {
39677
39904
  let settings2;
39678
39905
  try {
39679
- settings2 = JSON.parse(readFileSync21(join35(qwenHome(env), "settings.json"), "utf8"));
39906
+ settings2 = JSON.parse(readFileSync21(join35(harnessHome("qwen", env), "settings.json"), "utf8"));
39680
39907
  } catch {
39681
39908
  return [];
39682
39909
  }
@@ -39744,7 +39971,9 @@ function readQwenRoutes(env) {
39744
39971
  function readQwenModelCatalog(env = process.env) {
39745
39972
  const options = readQwenRoutes(env).map(({ id, model, label, provider }) => ({
39746
39973
  id,
39747
- label: qwenModelLabel(model, label),
39974
+ label: qualifiedModelLabel(model, label, {
39975
+ redundantWhen: (id2, name) => name.toLocaleLowerCase().endsWith(id2.toLocaleLowerCase())
39976
+ }),
39748
39977
  custom: true,
39749
39978
  provider
39750
39979
  }));
@@ -39758,7 +39987,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
39758
39987
  if (!inject) return modelId;
39759
39988
  const host = localHost(inject.host);
39760
39989
  if (!host) return modelId;
39761
- const dir = qwenHome(env);
39990
+ const dir = harnessHome("qwen", env);
39762
39991
  mkdirSync18(dir, { recursive: true });
39763
39992
  const path3 = join35(dir, "settings.json");
39764
39993
  let settings2 = {};
@@ -39840,6 +40069,8 @@ var EMPTY, PROTOCOLS, support8, QwenAgentDriver;
39840
40069
  var init_qwen = __esm({
39841
40070
  "server/drivers/acp/qwen.ts"() {
39842
40071
  "use strict";
40072
+ init_contracts();
40073
+ init_env_path();
39843
40074
  init_local_inject();
39844
40075
  init_core3();
39845
40076
  EMPTY = { default: "", options: [] };
@@ -39865,6 +40096,9 @@ var init_qwen = __esm({
39865
40096
  // A raw -m only changes the model within the saved provider. ACP switches
39866
40097
  // the complete route and confirms it before any prompt leaves OMB.
39867
40098
  spawnArgs: (config2, turn) => ["--acp", ...qwenApprovalArgs(config2.fullAuto, turn.approvalMode)],
40099
+ // Qwen's live session/load returns its cached MCP clients, ignoring fresh
40100
+ // credentials. Cold-load the same conversation when those inputs change.
40101
+ restartOnMcpChange: true,
39868
40102
  selectModel: { configId: "model" },
39869
40103
  pickAuthMethod: () => null,
39870
40104
  authFailure: "continue",
@@ -46766,7 +47000,7 @@ var init_browser = __esm({
46766
47000
  // server/drivers/acp/hermes.ts
46767
47001
  import { spawn as spawn6 } from "node:child_process";
46768
47002
  import { mkdirSync as mkdirSync19, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "node:fs";
46769
- import { homedir as homedir21 } from "node:os";
47003
+ import { homedir as homedir17 } from "node:os";
46770
47004
  import { join as join36 } from "node:path";
46771
47005
  function bindHermesScreenshotCompat(env, modelId) {
46772
47006
  delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT];
@@ -46776,9 +47010,6 @@ function bindHermesScreenshotCompat(env, modelId) {
46776
47010
  env[HERMES_OPENMAUS_SCREENSHOT_COMPAT] = "1";
46777
47011
  env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL] = inject.model;
46778
47012
  }
46779
- function hermesHome(env) {
46780
- return env.HERMES_HOME || join36(env.HOME || env.USERPROFILE || homedir21(), ".hermes");
46781
- }
46782
47013
  function quoteYaml(value) {
46783
47014
  if (/^[\w./:+-]+$/.test(value)) return value;
46784
47015
  return JSON.stringify(value);
@@ -46817,7 +47048,7 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
46817
47048
  if (!inject) return modelId;
46818
47049
  const host = localHost(inject.host);
46819
47050
  if (!host) return modelId;
46820
- const dir = hermesHome(env);
47051
+ const dir = env.HERMES_HOME || harnessHome("hermes", env);
46821
47052
  mkdirSync19(dir, { recursive: true });
46822
47053
  const path3 = join36(dir, "config.yaml");
46823
47054
  let text4 = "";
@@ -46887,7 +47118,7 @@ function hermesConfigDefault(text4) {
46887
47118
  return null;
46888
47119
  }
46889
47120
  function hermesConfiguredModel(env = process.env) {
46890
- const dir = hermesHome(env);
47121
+ const dir = env.HERMES_HOME || harnessHome("hermes", env);
46891
47122
  let secrets = "";
46892
47123
  try {
46893
47124
  secrets = readFileSync22(join36(dir, ".env"), "utf8");
@@ -46988,7 +47219,7 @@ async function fetchHermesAcpModels(cli, env) {
46988
47219
  }
46989
47220
  if (msg?.id === initId) {
46990
47221
  if (!msg.result) return done([]);
46991
- sessionId = send("session/new", { cwd: env.HOME || env.USERPROFILE || homedir21(), mcpServers: [] });
47222
+ sessionId = send("session/new", { cwd: env.HOME || env.USERPROFILE || homedir17(), mcpServers: [] });
46992
47223
  } else if (sessionId && msg?.id === sessionId) {
46993
47224
  const list = Array.isArray(msg.result?.models?.availableModels) ? msg.result.models.availableModels : [];
46994
47225
  done(
@@ -47032,6 +47263,7 @@ var init_hermes = __esm({
47032
47263
  "server/drivers/acp/hermes.ts"() {
47033
47264
  "use strict";
47034
47265
  init_browser();
47266
+ init_env_path();
47035
47267
  init_procs();
47036
47268
  init_local_inject();
47037
47269
  init_core3();
@@ -47241,8 +47473,8 @@ var init_openai_compat = __esm({
47241
47473
  });
47242
47474
 
47243
47475
  // server/drivers/pi.ts
47244
- import { chmodSync as chmodSync6, existsSync as existsSync17, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync3, readFileSync as readFileSync23, rmSync as rmSync6, writeFileSync as writeFileSync15 } from "node:fs";
47245
- import { homedir as homedir22, tmpdir as tmpdir4 } from "node:os";
47476
+ import { chmodSync as chmodSync6, existsSync as existsSync17, mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync3, readFileSync as readFileSync23, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "node:fs";
47477
+ import { homedir as homedir18, tmpdir as tmpdir4 } from "node:os";
47246
47478
  import { join as join37 } from "node:path";
47247
47479
  function readPiPromptImages(turn) {
47248
47480
  return (turn.images ?? []).map((image) => ({
@@ -47344,7 +47576,7 @@ async function applyPiLocalCatalog(catalog2, env = process.env, fetchImpl = fetc
47344
47576
  return preferPiInjectRows(await mergeLocalInject(catalog2, env, fetchImpl));
47345
47577
  }
47346
47578
  function piAgentDir(env) {
47347
- return join37(env.HOME || env.USERPROFILE || homedir22(), ".pi", "agent");
47579
+ return join37(env.HOME || env.USERPROFILE || homedir18(), ".pi", "agent");
47348
47580
  }
47349
47581
  function ensurePiInjectModel(modelId, env = process.env) {
47350
47582
  const split = splitPiModel(modelId);
@@ -47409,7 +47641,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
47409
47641
  return split;
47410
47642
  }
47411
47643
  function readPiDefaultModel(env) {
47412
- const home = env.HOME || env.USERPROFILE || homedir22();
47644
+ const home = env.HOME || env.USERPROFILE || homedir18();
47413
47645
  try {
47414
47646
  const s2 = JSON.parse(readFileSync23(`${home}/.pi/agent/settings.json`, "utf8"));
47415
47647
  return s2.defaultProvider && s2.defaultModel ? `${s2.defaultProvider}/${s2.defaultModel}` : "";
@@ -47486,13 +47718,14 @@ function piEnvironment(source) {
47486
47718
  for (const key of PROVIDER_CREDENTIAL_ENV) delete env[key];
47487
47719
  return env;
47488
47720
  }
47489
- var DRIVER_KIND6, PI_ARGS, PI_MODEL_UPDATE_ARGS, NODE_ENV_FLAG2, EMPTY3, PiDriver;
47721
+ var DRIVER_KIND6, PI_ARGS, PI_MODEL_UPDATE_ARGS, NODE_ENV_FLAG2, PI_PROMPT_RE_ANCHOR_TURNS, PI_AGENT_END_GRACE_MS, PiRpcRefusalError, EMPTY3, PiDriver;
47490
47722
  var init_pi = __esm({
47491
47723
  "server/drivers/pi.ts"() {
47492
47724
  "use strict";
47493
47725
  init_config();
47494
47726
  init_env_path();
47495
47727
  init_procs();
47728
+ init_prompt_split();
47496
47729
  init_proxy_paths();
47497
47730
  init_tool_summary();
47498
47731
  init_wire();
@@ -47504,6 +47737,10 @@ var init_pi = __esm({
47504
47737
  PI_ARGS = ["--mode", "rpc", "--no-session"];
47505
47738
  PI_MODEL_UPDATE_ARGS = ["update", "--models", "--no-approve"];
47506
47739
  NODE_ENV_FLAG2 = { ELECTRON_RUN_AS_NODE: "1" };
47740
+ PI_PROMPT_RE_ANCHOR_TURNS = 8;
47741
+ PI_AGENT_END_GRACE_MS = 6e4;
47742
+ PiRpcRefusalError = class extends Error {
47743
+ };
47507
47744
  EMPTY3 = { default: "", options: [] };
47508
47745
  PiDriver = {
47509
47746
  driverKind: DRIVER_KIND6,
@@ -47568,6 +47805,10 @@ var init_pi = __esm({
47568
47805
  const pending = /* @__PURE__ */ new Map();
47569
47806
  const askTimers = /* @__PURE__ */ new Set();
47570
47807
  let settled = false;
47808
+ let endStopReason;
47809
+ let endUsage;
47810
+ let endErrorMessage;
47811
+ let agentEndTimer = null;
47571
47812
  const images = readPiPromptImages(turn);
47572
47813
  if (typeof turn.model === "string" && turn.model) {
47573
47814
  ensurePiInjectModel(turn.model, { ...process.env, ...input.environment });
@@ -47580,7 +47821,7 @@ var init_pi = __esm({
47580
47821
  writeFileSync15(join37(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
47581
47822
  } catch (err) {
47582
47823
  try {
47583
- rmSync6(mcpTempDir, { recursive: true, force: true });
47824
+ rmSync7(mcpTempDir, { recursive: true, force: true });
47584
47825
  } catch {
47585
47826
  }
47586
47827
  throw err;
@@ -47601,7 +47842,7 @@ var init_pi = __esm({
47601
47842
  } catch (err) {
47602
47843
  if (mcpTempDir) {
47603
47844
  try {
47604
- rmSync6(mcpTempDir, { recursive: true, force: true });
47845
+ rmSync7(mcpTempDir, { recursive: true, force: true });
47605
47846
  } catch {
47606
47847
  }
47607
47848
  }
@@ -47610,7 +47851,9 @@ var init_pi = __esm({
47610
47851
  })();
47611
47852
  let buf = "";
47612
47853
  let assistantText = "";
47854
+ let compactionObserved = false;
47613
47855
  const responseWaiters = /* @__PURE__ */ new Map();
47856
+ let steerSeq = 0;
47614
47857
  const rejectWaiters = (err) => {
47615
47858
  for (const waiter of responseWaiters.values()) {
47616
47859
  clearTimeout(waiter.timer);
@@ -47618,13 +47861,13 @@ var init_pi = __esm({
47618
47861
  }
47619
47862
  responseWaiters.clear();
47620
47863
  };
47621
- const awaitResponse = (command, timeoutMs = 2e4) => new Promise((resolve12, reject) => {
47864
+ const awaitResponse = (command, timeoutMs = 2e4, key = command) => new Promise((resolve12, reject) => {
47622
47865
  const timer = setTimeout(() => {
47623
- responseWaiters.delete(command);
47866
+ responseWaiters.delete(key);
47624
47867
  reject(new Error(`pi ${command} timed out`));
47625
47868
  }, timeoutMs);
47626
47869
  timer.unref?.();
47627
- responseWaiters.set(command, { resolve: resolve12, reject, timer });
47870
+ responseWaiters.set(key, { resolve: resolve12, reject, timer });
47628
47871
  });
47629
47872
  child.stdin.on("error", () => rejectWaiters(new Error("pi stdin closed")));
47630
47873
  const send = (obj) => {
@@ -47640,6 +47883,10 @@ var init_pi = __esm({
47640
47883
  const settle = (ok, stopReason, usage) => {
47641
47884
  if (settled) return;
47642
47885
  settled = true;
47886
+ if (agentEndTimer) {
47887
+ clearTimeout(agentEndTimer);
47888
+ agentEndTimer = null;
47889
+ }
47643
47890
  for (const timer of askTimers) clearTimeout(timer);
47644
47891
  askTimers.clear();
47645
47892
  pending.clear();
@@ -47661,12 +47908,25 @@ var init_pi = __esm({
47661
47908
  }
47662
47909
  if (mcpTempDir) {
47663
47910
  try {
47664
- rmSync6(mcpTempDir, { recursive: true, force: true });
47911
+ rmSync7(mcpTempDir, { recursive: true, force: true });
47665
47912
  } catch {
47666
47913
  }
47667
47914
  }
47668
47915
  active.delete(threadId);
47669
47916
  };
47917
+ const finishRun = () => {
47918
+ const sr = endStopReason;
47919
+ if (sr === "error" || sr === "failed") {
47920
+ emit({
47921
+ ...base2(threadId, turnId),
47922
+ type: "runtime.error",
47923
+ message: String(endErrorMessage ?? "pi turn failed").slice(0, 2e3)
47924
+ });
47925
+ settle(false, "failed", endUsage);
47926
+ return;
47927
+ }
47928
+ settle(true, sr === "cancelled" || sr === "aborted" ? "cancelled" : "end_turn", endUsage);
47929
+ };
47670
47930
  const stop = () => {
47671
47931
  try {
47672
47932
  send({ type: "abort" });
@@ -47678,17 +47938,35 @@ var init_pi = __esm({
47678
47938
  }
47679
47939
  settle(true, "cancelled");
47680
47940
  };
47681
- active.set(threadId, { stop, turnId, pending, child });
47941
+ const steerActiveTurn = async (text4) => {
47942
+ if (settled || child.exitCode !== null || child.signalCode !== null) return "refused";
47943
+ let ack;
47944
+ try {
47945
+ const id = `steer:${++steerSeq}`;
47946
+ ack = awaitResponse("steer", 2e4, id);
47947
+ send({ type: "steer", id, message: text4 });
47948
+ } catch {
47949
+ return "refused";
47950
+ }
47951
+ try {
47952
+ await ack;
47953
+ return "steered";
47954
+ } catch (error51) {
47955
+ return error51 instanceof PiRpcRefusalError ? "refused" : "indeterminate";
47956
+ }
47957
+ };
47958
+ active.set(threadId, { stop, turnId, pending, child, steer: steerActiveTurn });
47682
47959
  const onEvent = (evt) => {
47683
47960
  appendNative(threadId, { dir: "in", source: "pi.rpc", msg: evt });
47684
47961
  switch (evt.type) {
47685
47962
  case "response": {
47686
- if (evt.command && responseWaiters.has(evt.command)) {
47687
- const waiter = responseWaiters.get(evt.command);
47688
- responseWaiters.delete(evt.command);
47963
+ const key = typeof evt.id === "string" && responseWaiters.has(evt.id) ? evt.id : evt.command;
47964
+ if (key && responseWaiters.has(key)) {
47965
+ const waiter = responseWaiters.get(key);
47966
+ responseWaiters.delete(key);
47689
47967
  clearTimeout(waiter.timer);
47690
47968
  if (evt.success) waiter.resolve(evt.data);
47691
- else waiter.reject(new Error(`pi ${evt.command} failed`));
47969
+ else waiter.reject(new PiRpcRefusalError(`pi ${evt.command} failed`));
47692
47970
  }
47693
47971
  return;
47694
47972
  }
@@ -47779,21 +48057,43 @@ var init_pi = __esm({
47779
48057
  }
47780
48058
  return;
47781
48059
  }
47782
- case "turn_end":
47783
- case "agent_end": {
48060
+ case "compaction_start":
48061
+ case "compaction_end":
48062
+ case "auto_compaction_start":
48063
+ case "auto_compaction_end": {
48064
+ if (sessionReady && sessionFile) {
48065
+ compactionObserved = true;
48066
+ deletePromptSplitReceipt("pi", JSON.stringify([threadId, sessionFile]));
48067
+ }
48068
+ return;
48069
+ }
48070
+ case "turn_end": {
48071
+ if (settled) return;
47784
48072
  const sr = evt.message?.stopReason;
47785
48073
  if (sr === "toolUse" || sr === "tool_use" || sr === "tool_calls") return;
47786
- const usage = evt.usage ?? evt.message?.usage;
47787
- if (sr === "error" || sr === "failed") {
47788
- emit({
47789
- ...base2(threadId, turnId),
47790
- type: "runtime.error",
47791
- message: String(evt.message?.errorMessage ?? "pi turn failed").slice(0, 2e3)
47792
- });
47793
- settle(false, "failed", usage);
47794
- return;
48074
+ endStopReason = sr;
48075
+ endUsage = evt.usage ?? evt.message?.usage;
48076
+ endErrorMessage = evt.message?.errorMessage;
48077
+ if (agentEndTimer) clearTimeout(agentEndTimer);
48078
+ agentEndTimer = setTimeout(finishRun, PI_AGENT_END_GRACE_MS);
48079
+ agentEndTimer.unref?.();
48080
+ return;
48081
+ }
48082
+ case "agent_end": {
48083
+ if (evt.isTerminal === false || evt.willRetry === true) return;
48084
+ if (agentEndTimer) {
48085
+ clearTimeout(agentEndTimer);
48086
+ agentEndTimer = null;
47795
48087
  }
47796
- settle(true, sr === "cancelled" || sr === "aborted" ? "cancelled" : "end_turn", usage);
48088
+ finishRun();
48089
+ return;
48090
+ }
48091
+ case "agent_settled": {
48092
+ if (agentEndTimer) {
48093
+ clearTimeout(agentEndTimer);
48094
+ agentEndTimer = null;
48095
+ }
48096
+ finishRun();
47797
48097
  return;
47798
48098
  }
47799
48099
  default:
@@ -47827,12 +48127,14 @@ var init_pi = __esm({
47827
48127
  emit({ ...base2(threadId, turnId), type: "turn.started" });
47828
48128
  const sessionPath = typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
47829
48129
  let sessionFile = sessionPath;
48130
+ let sessionReady = false;
47830
48131
  try {
47831
48132
  const command = sessionPath ? "switch_session" : "new_session";
47832
48133
  const hsPromise = awaitResponse(command);
47833
48134
  send(sessionPath ? { type: "switch_session", sessionPath } : { type: "new_session" });
47834
48135
  const hs = await hsPromise;
47835
48136
  if (hs?.sessionFile) sessionFile = hs.sessionFile;
48137
+ sessionReady = true;
47836
48138
  emit({
47837
48139
  ...base2(threadId, turnId),
47838
48140
  type: "session.started",
@@ -47858,12 +48160,54 @@ var init_pi = __esm({
47858
48160
  } catch {
47859
48161
  }
47860
48162
  }
47861
- const message2 = turn.system ? `${turn.system}
48163
+ const halves = promptHalves(turn);
48164
+ let message2;
48165
+ let pendingReceipt = null;
48166
+ if (halves.stable !== null && sessionReady && sessionFile) {
48167
+ const receiptKey = JSON.stringify([threadId, sessionFile]);
48168
+ const composed = splitSessionPrompt(
48169
+ halves.stable,
48170
+ halves.volatile,
48171
+ readPromptSplitReceipt("pi", receiptKey),
48172
+ turn.system,
48173
+ turn.text,
48174
+ Boolean(turn.mentionTurn),
48175
+ PI_PROMPT_RE_ANCHOR_TURNS
48176
+ );
48177
+ message2 = composed.text;
48178
+ pendingReceipt = { key: receiptKey, receipt: composed.receipt };
48179
+ } else {
48180
+ message2 = turn.system ? `${turn.system}
47862
48181
 
47863
48182
  ${turn.text}` : turn.text;
48183
+ }
47864
48184
  try {
48185
+ const accepted = awaitResponse("prompt", 12e4);
47865
48186
  send({ type: "prompt", message: message2, ...images.length ? { images } : {} });
47866
- } catch {
48187
+ void accepted.then(
48188
+ () => {
48189
+ if (!pendingReceipt || compactionObserved) return;
48190
+ try {
48191
+ writePromptSplitReceipt("pi", pendingReceipt.key, pendingReceipt.receipt);
48192
+ } catch {
48193
+ }
48194
+ },
48195
+ (err) => {
48196
+ if (settled) return;
48197
+ emit({
48198
+ ...base2(threadId, turnId),
48199
+ type: "runtime.error",
48200
+ message: String(err?.message ?? err).slice(0, 2e3)
48201
+ });
48202
+ settle(false);
48203
+ }
48204
+ );
48205
+ } catch (err) {
48206
+ emit({
48207
+ ...base2(threadId, turnId),
48208
+ type: "runtime.error",
48209
+ message: String(err?.message ?? err).slice(0, 2e3)
48210
+ });
47867
48211
  settle(false);
47868
48212
  }
47869
48213
  return { turnId };
@@ -47930,7 +48274,10 @@ ${turn.text}` : turn.text;
47930
48274
  // Reasoning effort pins pi's thinking level per turn (none → off).
47931
48275
  // xhigh/max only land on models that expose them; pi rejects an
47932
48276
  // unsupported level and the turn keeps the engine default.
47933
- effortLevels: EFFORT_LEVELS
48277
+ effortLevels: EFFORT_LEVELS,
48278
+ // pi's RPC mode takes a mid-turn steer frame the runtime queues
48279
+ // into the running agent without aborting the current step.
48280
+ queueing: true
47934
48281
  },
47935
48282
  sendTurn,
47936
48283
  interruptTurn: async (threadId) => active.get(threadId)?.stop(),
@@ -47949,6 +48296,10 @@ ${turn.text}` : turn.text;
47949
48296
  });
47950
48297
  return decision.behavior === "allow" ? "allowed-once" : decision.behavior === "answer" ? "answered" : "rejected";
47951
48298
  },
48299
+ steer: async (threadId, text4) => {
48300
+ const entry = active.get(threadId);
48301
+ return entry?.steer ? await entry.steer(text4) : "refused";
48302
+ },
47952
48303
  hasSession: (threadId) => active.has(threadId),
47953
48304
  stopAll: async () => {
47954
48305
  for (const { stop } of active.values()) stop();
@@ -48092,13 +48443,13 @@ var init_mistral = __esm({
48092
48443
 
48093
48444
  // server/drivers/minimax.ts
48094
48445
  import { readFileSync as readFileSync24 } from "node:fs";
48095
- import { homedir as homedir23 } from "node:os";
48446
+ import { homedir as homedir19 } from "node:os";
48096
48447
  import { join as join38 } from "node:path";
48097
48448
  function normalizedApiUrl(value) {
48098
48449
  const root = value.trim().replace(/\/+$/, "");
48099
48450
  return root.endsWith("/v1") ? root : `${root}/v1`;
48100
48451
  }
48101
- function loadLocalMiniMaxConfig(home = homedir23()) {
48452
+ function loadLocalMiniMaxConfig(home = homedir19()) {
48102
48453
  try {
48103
48454
  const raw = localConfigSchema.parse(JSON.parse(readFileSync24(join38(home, ".mmx", "config.json"), "utf8")));
48104
48455
  const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL3;
@@ -48823,8 +49174,8 @@ __export(browser_engine_exports, {
48823
49174
  });
48824
49175
  import { spawn as spawn8 } from "node:child_process";
48825
49176
  import { createHash as createHash13, randomBytes as randomBytes3, randomUUID as randomUUID14 } from "node:crypto";
48826
- import { chmodSync as chmodSync7, constants as constants2, copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync21, readFileSync as readFileSync26, readdirSync as readdirSync3, renameSync as renameSync6, rmSync as rmSync7, statSync as statSync5, unlinkSync as unlinkSync7, writeFileSync as writeFileSync16 } from "node:fs";
48827
- import { homedir as homedir24, tmpdir as tmpdir5 } from "node:os";
49177
+ import { chmodSync as chmodSync7, constants as constants2, copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync21, readFileSync as readFileSync26, readdirSync as readdirSync3, renameSync as renameSync6, rmSync as rmSync8, statSync as statSync5, unlinkSync as unlinkSync7, writeFileSync as writeFileSync16 } from "node:fs";
49178
+ import { homedir as homedir20, tmpdir as tmpdir5 } from "node:os";
48828
49179
  import { delimiter as delimiter6, dirname as dirname12, join as join42, resolve as resolve10 } from "node:path";
48829
49180
  function browserRestoreKey(session) {
48830
49181
  if (!/^[A-Za-z0-9_.-]{1,96}$/.test(session)) throw new Error("Invalid browser session.");
@@ -48833,7 +49184,7 @@ function browserRestoreKey(session) {
48833
49184
  function browserSessionsDirectory(env) {
48834
49185
  const namespace = env.AGENT_BROWSER_NAMESPACE;
48835
49186
  if (namespace && !/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(namespace)) throw new Error("Invalid browser namespace.");
48836
- const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir24();
49187
+ const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir20();
48837
49188
  const stateRoot = namespace ? join42(home, ".agent-browser", "namespaces", namespace, "state") : join42(home, ".agent-browser");
48838
49189
  return join42(stateRoot, "sessions");
48839
49190
  }
@@ -49215,7 +49566,7 @@ function agentBrowserFrame(input) {
49215
49566
  } catch {
49216
49567
  fail(new Error("the browser reported a picture it did not write"));
49217
49568
  } finally {
49218
- rmSync7(file2, { force: true });
49569
+ rmSync8(file2, { force: true });
49219
49570
  }
49220
49571
  };
49221
49572
  child.on("error", (error51) => done(error51 instanceof Error ? error51 : new Error(String(error51))));
@@ -49299,7 +49650,7 @@ var import_qrcode_terminal = __toESM(require_main(), 1);
49299
49650
  import { spawn as spawn9 } from "node:child_process";
49300
49651
  import { randomBytes as randomBytes4 } from "node:crypto";
49301
49652
  import { closeSync as closeSync6, existsSync as existsSync19, mkdirSync as mkdirSync22, openSync as openSync6, readFileSync as readFileSync27 } from "node:fs";
49302
- import { homedir as homedir25 } from "node:os";
49653
+ import { homedir as homedir21 } from "node:os";
49303
49654
  import { dirname as dirname13, join as join43, resolve as resolve11 } from "node:path";
49304
49655
  import { createInterface as createInterface2 } from "node:readline/promises";
49305
49656
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -52793,7 +53144,7 @@ function parseArgs(argv, env = process.env) {
52793
53144
  const options = {
52794
53145
  command: command === "--help" || command === "-h" ? "help" : command,
52795
53146
  port: Number(env.OMB_PORT || 8799),
52796
- dataDir: env.OMB_DATA_DIR || join43(homedir25(), ".openmausbot"),
53147
+ dataDir: env.OMB_DATA_DIR || join43(homedir21(), ".openmausbot"),
52797
53148
  tailscale: false,
52798
53149
  tunnel: false,
52799
53150
  client: false,
@@ -53721,7 +54072,7 @@ async function runOnboardingCommand(options, io = defaultIo(), startServer = run
53721
54072
  }
53722
54073
  if (options.command === "setup") {
53723
54074
  io.log("\nAll set. Start with: openmausbot (or npx openmausbot without a global install).");
53724
- if (options.dataDir !== join43(homedir25(), ".openmausbot") || options.port !== 8799) {
54075
+ if (options.dataDir !== join43(homedir21(), ".openmausbot") || options.port !== 8799) {
53725
54076
  io.log(`Use the same --data-dir (${options.dataDir}) and --port (${options.port}) options when starting.`);
53726
54077
  }
53727
54078
  return 0;