openmausbot 0.1.74 → 0.1.76

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 (51) hide show
  1. package/dist/assets/index-BtT047dk.css +1 -0
  2. package/dist/assets/{index-CwoQdM0z.js → index-C6blGSq5.js} +1 -1
  3. package/dist/assets/index-fKXFpB_J.js +305 -0
  4. package/dist/index.html +2 -2
  5. package/dist-server/companion/src/routes.js +6 -2
  6. package/dist-server/drivers/agents-proxy.js +133 -11
  7. package/dist-server/index.js +3379 -1431
  8. package/dist-server/mcp-gate.js +28 -18
  9. package/dist-server/openmausbot.js +557 -325
  10. package/dist-server/pair-cli.js +557 -325
  11. package/dist-server/permission-proxy.js +119 -14
  12. package/dist-server/server/agent-tool-policy.js +1 -0
  13. package/dist-server/server/atomic.js +37 -1
  14. package/dist-server/server/auto-approve.js +15 -0
  15. package/dist-server/server/bot-overview.js +5 -1
  16. package/dist-server/server/chief-of-staff.js +17 -12
  17. package/dist-server/server/delegations.js +177 -63
  18. package/dist-server/server/drivers/acp/core.js +2 -1
  19. package/dist-server/server/drivers/acp/grok.js +12 -1
  20. package/dist-server/server/drivers/acp/hermes.js +21 -3
  21. package/dist-server/server/drivers/agents-proxy.js +113 -10
  22. package/dist-server/server/drivers/claude.js +91 -17
  23. package/dist-server/server/drivers/codex.js +15 -4
  24. package/dist-server/server/drivers/openai-chat.js +90 -67
  25. package/dist-server/server/drivers/openai-compat.js +9 -1
  26. package/dist-server/server/drivers/pi.js +18 -3
  27. package/dist-server/server/drivers/retry.js +4 -0
  28. package/dist-server/server/env-path.js +18 -17
  29. package/dist-server/server/index.js +1172 -302
  30. package/dist-server/server/message-db.js +21 -0
  31. package/dist-server/server/peer-roster.js +48 -9
  32. package/dist-server/server/permission-proxy.js +90 -15
  33. package/dist-server/server/procs.js +19 -3
  34. package/dist-server/server/room-handoffs.js +237 -0
  35. package/dist-server/server/section-context.js +64 -14
  36. package/dist-server/server/setup-mode.js +6 -7
  37. package/dist-server/server/store.js +266 -8
  38. package/dist-server/server/surface.js +16 -5
  39. package/dist-server/server/team-backup.js +17 -1
  40. package/dist-server/server/team-setup-requests.js +252 -0
  41. package/dist-server/server/thread-events.js +10 -1
  42. package/dist-server/server/turn-dispatch-guard.js +31 -3
  43. package/dist-server/server/workspace.js +13 -0
  44. package/dist-server/shared/approval-mode.js +7 -0
  45. package/dist-server/shared/ask-question.js +172 -0
  46. package/dist-server/shared/provider-safety.js +6 -0
  47. package/dist-server/shared/team-backup.js +1 -0
  48. package/dist-server/shared/team-setup.js +1 -0
  49. package/package.json +1 -1
  50. package/dist/assets/index-BRLHsQ5g.js +0 -298
  51. package/dist/assets/index-ByLQ7a3F.css +0 -1
@@ -1113,6 +1113,21 @@ var require_main = __commonJS({
1113
1113
  // server/atomic.ts
1114
1114
  import { randomUUID as randomUUID3 } from "node:crypto";
1115
1115
  import { closeSync, fsyncSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
1116
+ function sleepSync(ms) {
1117
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
1118
+ }
1119
+ function renameWithRetry(tmp, path3, rename2 = renameSync) {
1120
+ for (let attempt = 0; ; attempt += 1) {
1121
+ try {
1122
+ rename2(tmp, path3);
1123
+ return;
1124
+ } catch (e) {
1125
+ const code = e.code;
1126
+ if (!code || !RETRYABLE_RENAME_CODES.has(code) || attempt >= RENAME_RETRY_DELAYS_MS.length) throw e;
1127
+ sleepSync(RENAME_RETRY_DELAYS_MS[attempt]);
1128
+ }
1129
+ }
1130
+ }
1116
1131
  function writeFileAtomic(path3, data, options = {}) {
1117
1132
  const tmp = `${path3}.${process.pid}.${randomUUID3()}.tmp`;
1118
1133
  let fd = null;
@@ -1122,7 +1137,7 @@ function writeFileAtomic(path3, data, options = {}) {
1122
1137
  fsyncSync(fd);
1123
1138
  closeSync(fd);
1124
1139
  fd = null;
1125
- renameSync(tmp, path3);
1140
+ renameWithRetry(tmp, path3);
1126
1141
  } catch (e) {
1127
1142
  if (fd !== null) {
1128
1143
  try {
@@ -1137,9 +1152,12 @@ function writeFileAtomic(path3, data, options = {}) {
1137
1152
  throw e;
1138
1153
  }
1139
1154
  }
1155
+ var RENAME_RETRY_DELAYS_MS, RETRYABLE_RENAME_CODES;
1140
1156
  var init_atomic = __esm({
1141
1157
  "server/atomic.ts"() {
1142
1158
  "use strict";
1159
+ RENAME_RETRY_DELAYS_MS = [5, 10, 20, 40, 80];
1160
+ RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
1143
1161
  }
1144
1162
  });
1145
1163
 
@@ -9752,8 +9770,8 @@ var capitalizeFirstCharacter, error28;
9752
9770
  var init_lt = __esm({
9753
9771
  "node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/locales/lt.js"() {
9754
9772
  init_util();
9755
- capitalizeFirstCharacter = (text3) => {
9756
- return text3.charAt(0).toUpperCase() + text3.slice(1);
9773
+ capitalizeFirstCharacter = (text4) => {
9774
+ return text4.charAt(0).toUpperCase() + text4.slice(1);
9757
9775
  };
9758
9776
  error28 = () => {
9759
9777
  const Sizable = {
@@ -17585,8 +17603,8 @@ var init_mcp_registry = __esm({
17585
17603
  });
17586
17604
 
17587
17605
  // server/schema.ts
17588
- function parseJson(text3) {
17589
- return JSON.parse(text3);
17606
+ function parseJson(text4) {
17607
+ return JSON.parse(text4);
17590
17608
  }
17591
17609
  function schemaIssue(error51, fallback) {
17592
17610
  const issue2 = error51.issues[0];
@@ -18609,45 +18627,46 @@ function isFile(p) {
18609
18627
  return false;
18610
18628
  }
18611
18629
  }
18612
- function whichWin(cli) {
18613
- const exts = (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
18630
+ function whichWin(cli, env) {
18631
+ const exts = ((env ?? process.env).PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
18614
18632
  const probe = (base2) => {
18615
18633
  const order = extname(base2) ? [base2, ...exts.map((e) => base2 + e)] : [...exts.map((e) => base2 + e), base2];
18616
18634
  return order.find(isFile) ?? null;
18617
18635
  };
18618
18636
  if (/[\\/]/.test(cli) || /^[a-zA-Z]:/.test(cli)) return probe(cli);
18619
- for (const dir of augmentedPath().split(delimiter3)) {
18637
+ const path3 = env ? env.PATH ?? env.Path ?? "" : augmentedPath();
18638
+ for (const dir of path3.split(delimiter3)) {
18620
18639
  if (!dir) continue;
18621
18640
  const hit = probe(join9(dir, cli));
18622
18641
  if (hit) return hit;
18623
18642
  }
18624
18643
  return null;
18625
18644
  }
18626
- function nodeExe(near) {
18645
+ function nodeExe(near, env) {
18627
18646
  const local = join9(near, "node.exe");
18628
18647
  if (isFile(local)) return local;
18629
- const onPath2 = whichWin("node.exe");
18648
+ const onPath2 = whichWin("node.exe", env);
18630
18649
  if (onPath2 && extname(onPath2).toLowerCase() === ".exe") return onPath2;
18631
18650
  return process.versions.electron ? null : process.execPath;
18632
18651
  }
18633
- function parseCmdShim(shim) {
18634
- let text3;
18652
+ function parseCmdShim(shim, env) {
18653
+ let text4;
18635
18654
  try {
18636
- text3 = readFileSync6(shim, "utf8");
18655
+ text4 = readFileSync6(shim, "utf8");
18637
18656
  } catch {
18638
18657
  return null;
18639
18658
  }
18640
18659
  const dir = dirname4(shim);
18641
- const targets = [...text3.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) => join9(dir, m[1])).filter((p) => isFile(p) && basename2(p).toLowerCase() !== "node.exe");
18660
+ const targets = [...text4.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) => join9(dir, m[1])).filter((p) => isFile(p) && basename2(p).toLowerCase() !== "node.exe");
18642
18661
  const script = targets.find((p) => /\.[cm]?js$/i.test(p));
18643
18662
  if (script) {
18644
- const node = nodeExe(dir);
18663
+ const node = nodeExe(dir, env);
18645
18664
  if (node) return { command: node, args: [script] };
18646
18665
  }
18647
18666
  const exe = targets.find((p) => extname(p).toLowerCase() === ".exe");
18648
18667
  return exe ? { command: exe, args: [] } : null;
18649
18668
  }
18650
- function parseNodeShebang(file2) {
18669
+ function parseNodeShebang(file2, env) {
18651
18670
  let head = "";
18652
18671
  let fd = null;
18653
18672
  try {
@@ -18666,32 +18685,32 @@ function parseNodeShebang(file2) {
18666
18685
  }
18667
18686
  }
18668
18687
  if (!/^#!.*\bnode(\.exe)?\b/.test(head)) return null;
18669
- const node = nodeExe(dirname4(file2));
18688
+ const node = nodeExe(dirname4(file2), env);
18670
18689
  return node ? { command: node, args: [file2] } : null;
18671
18690
  }
18672
- function resolveWord(cli, args) {
18691
+ function resolveWord(cli, args, env) {
18673
18692
  if (process.platform !== "win32") return { command: cli, args };
18674
- const file2 = whichWin(cli);
18693
+ const file2 = whichWin(cli, env);
18675
18694
  if (!file2) return { command: cli, args };
18676
18695
  const ext = extname(file2).toLowerCase();
18677
18696
  if (ext === ".cmd" || ext === ".bat") {
18678
- const direct = parseCmdShim(file2);
18697
+ const direct = parseCmdShim(file2, env);
18679
18698
  return direct ? { command: direct.command, args: [...direct.args, ...args] } : { command: file2, args };
18680
18699
  }
18681
18700
  if (ext === ".exe" || ext === ".com") return { command: file2, args };
18682
- const viaNode = parseNodeShebang(file2);
18701
+ const viaNode = parseNodeShebang(file2, env);
18683
18702
  return viaNode ? { command: viaNode.command, args: [...viaNode.args, ...args] } : { command: file2, args };
18684
18703
  }
18685
- function resolveCliSpawn(cli, args) {
18704
+ function resolveCliSpawn(cli, args, env) {
18686
18705
  const trimmed = cli.trim();
18687
- if (trimmed.includes(" ") && existsSync7(trimmed)) return resolveWord(trimmed, args);
18706
+ if (trimmed.includes(" ") && existsSync7(trimmed)) return resolveWord(trimmed, args, env);
18688
18707
  const split = splitCliString(cli);
18689
18708
  if (split.length > 1 || split[0] !== trimmed) {
18690
18709
  const [head, ...fixed] = split;
18691
18710
  if (!head) return { command: cli, args };
18692
- return resolveWord(head, [...fixed, ...args]);
18711
+ return resolveWord(head, [...fixed, ...args], env);
18693
18712
  }
18694
- return resolveWord(cli, args);
18713
+ return resolveWord(cli, args, env);
18695
18714
  }
18696
18715
  var cached2, probed, loginShellPath, registeredDirs;
18697
18716
  var init_env_path = __esm({
@@ -18710,8 +18729,8 @@ import {
18710
18729
  execFile as execFile3
18711
18730
  } from "node:child_process";
18712
18731
  import { join as join10 } from "node:path";
18713
- function resolveCli(cli, args = []) {
18714
- return resolveCliSpawn(cli, args);
18732
+ function resolveCli(cli, args = [], env) {
18733
+ return resolveCliSpawn(cli, args, env);
18715
18734
  }
18716
18735
  function estimatedWindowsCommandLineChars(resolved) {
18717
18736
  return [resolved.command, ...resolved.args].reduce(
@@ -18825,9 +18844,13 @@ async function stopCliTree(child, pid, timeoutMs) {
18825
18844
  if (exited()) return true;
18826
18845
  return new Promise((resolve13) => {
18827
18846
  let timer;
18847
+ let finished = false;
18828
18848
  const done = (stopped) => {
18849
+ if (finished) return;
18850
+ finished = true;
18829
18851
  clearTimeout(timer);
18830
18852
  child.off("close", closed);
18853
+ child.off("exit", closed);
18831
18854
  resolve13(stopped);
18832
18855
  };
18833
18856
  const closed = () => done(true);
@@ -18835,7 +18858,12 @@ async function stopCliTree(child, pid, timeoutMs) {
18835
18858
  timer = setTimeout(() => done(false), timeoutMs);
18836
18859
  timer.unref?.();
18837
18860
  execFile3("taskkill", ["/PID", String(pid), "/T", "/F"], { windowsHide: true }, (err) => {
18838
- if (!err) return;
18861
+ if (finished) return;
18862
+ if (!err) {
18863
+ if (exited()) done(true);
18864
+ else child.once("exit", closed);
18865
+ return;
18866
+ }
18839
18867
  try {
18840
18868
  child.kill();
18841
18869
  } catch {
@@ -19583,9 +19611,9 @@ function isSecretName(name) {
19583
19611
  if (SECRET_KEY_PARTS.some((part) => lower.includes(part))) return true;
19584
19612
  return /(^|[_.-])keys?$/.test(lower);
19585
19613
  }
19586
- function redactSecretsInText(text3) {
19587
- if (!text3 || text3.length < 8) return text3;
19588
- let out = text3;
19614
+ function redactSecretsInText(text4) {
19615
+ if (!text4 || text4.length < 8) return text4;
19616
+ let out = text4;
19589
19617
  out = out.replace(PEM_BLOCK, (_m, open2, body, close) => `${open2}
19590
19618
  ${mask(body.trim())}
19591
19619
  ${close}`);
@@ -19696,8 +19724,8 @@ function askInputSummary(input) {
19696
19724
  if (typeof fields.question === "string") return cut(fields.question, QUESTION_LIMIT);
19697
19725
  if (typeof fields.command === "string") return cut(fields.command, LIMIT);
19698
19726
  if (typeof fields.url === "string") return cut(fields.url, LIMIT);
19699
- const text3 = JSON.stringify(fields);
19700
- return text3 === "{}" ? void 0 : cut(text3, LIMIT);
19727
+ const text4 = JSON.stringify(fields);
19728
+ return text4 === "{}" ? void 0 : cut(text4, LIMIT);
19701
19729
  }
19702
19730
  var QUESTION_LIMIT, LIMIT, cut;
19703
19731
  var init_tool_summary = __esm({
@@ -19706,7 +19734,7 @@ var init_tool_summary = __esm({
19706
19734
  init_redact();
19707
19735
  QUESTION_LIMIT = 300;
19708
19736
  LIMIT = 200;
19709
- cut = (text3, limit) => redactSecretsInText(text3).trim().slice(0, limit);
19737
+ cut = (text4, limit) => redactSecretsInText(text4).trim().slice(0, limit);
19710
19738
  }
19711
19739
  });
19712
19740
 
@@ -19729,11 +19757,11 @@ function sanitizeToolLogValue(value, budget, depth = 0) {
19729
19757
  if (typeof value === "string") {
19730
19758
  if (/^data:image\//iu.test(value) || budget.text <= 0) return void 0;
19731
19759
  const limit = Math.min(TOOL_LOG_TEXT_LIMIT, budget.text);
19732
- const text3 = value.length <= limit ? value : `[Earlier output truncated]
19760
+ const text4 = value.length <= limit ? value : `[Earlier output truncated]
19733
19761
 
19734
19762
  ${value.slice(-limit)}`;
19735
- budget.text -= text3.length;
19736
- return text3;
19763
+ budget.text -= text4.length;
19764
+ return text4;
19737
19765
  }
19738
19766
  if (Array.isArray(value)) {
19739
19767
  return value.flatMap((entry) => {
@@ -20040,10 +20068,10 @@ function createAcpDriver(support11) {
20040
20068
  }
20041
20069
  };
20042
20070
  const flushAssistantText = () => {
20043
- const text3 = state.text;
20071
+ const text4 = state.text;
20044
20072
  state.text = "";
20045
- if (!text3.trim()) return;
20046
- emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text3 });
20073
+ if (!text4.trim()) return;
20074
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
20047
20075
  };
20048
20076
  const settle = (ok, stopReason) => {
20049
20077
  if (state.settled) return;
@@ -20299,7 +20327,7 @@ function createAcpDriver(support11) {
20299
20327
  }
20300
20328
  }
20301
20329
  const images = turn.images ?? [];
20302
- const runtimeAcceptsImages = init?.agentCapabilities?.promptCapabilities?.image === true;
20330
+ const runtimeAcceptsImages = init?.agentCapabilities?.promptCapabilities?.image === true || support11.acceptsUnadvertisedImages?.(init) === true;
20303
20331
  if (images.length && support11.images === true && !runtimeAcceptsImages) {
20304
20332
  throw new Error(
20305
20333
  `${support11.displayName} is configured for image attachments, but this installed runtime does not advertise ACP image input. Update the ${support11.displayName} CLI or send the message without an image.`
@@ -20372,13 +20400,13 @@ function createAcpDriver(support11) {
20372
20400
  }
20373
20401
  emitSessionStarted();
20374
20402
  state.promptSent = true;
20375
- const text3 = support11.buildPromptText ? support11.buildPromptText(turn) : turn.system ? `${turn.system}
20403
+ const text4 = support11.buildPromptText ? support11.buildPromptText(turn) : turn.system ? `${turn.system}
20376
20404
 
20377
20405
  ${turn.text}` : turn.text;
20378
20406
  const imageBlocks = support11.images === true && runtimeAcceptsImages ? await readAcpImageBlocks(images) : [];
20379
20407
  const result = await request("session/prompt", {
20380
20408
  sessionId,
20381
- prompt: [{ type: "text", text: text3 }, ...imageBlocks]
20409
+ prompt: [{ type: "text", text: text4 }, ...imageBlocks]
20382
20410
  });
20383
20411
  const usage = result?.usage ?? result?._meta ?? {};
20384
20412
  if (typeof usage.inputTokens === "number" || typeof usage.outputTokens === "number") {
@@ -21706,14 +21734,14 @@ var init_boxagent = __esm({
21706
21734
  let lastText = "";
21707
21735
  let pendingText = "";
21708
21736
  const flushAssistantText = () => {
21709
- const text3 = pendingText;
21737
+ const text4 = pendingText;
21710
21738
  pendingText = "";
21711
- if (!text3.trim()) return;
21712
- emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text3 });
21739
+ if (!text4.trim()) return;
21740
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
21713
21741
  };
21714
- const ingest = (text3) => {
21715
- const delta = text3.startsWith(lastText) ? text3.slice(lastText.length) : text3;
21716
- lastText = text3;
21742
+ const ingest = (text4) => {
21743
+ const delta = text4.startsWith(lastText) ? text4.slice(lastText.length) : text4;
21744
+ lastText = text4;
21717
21745
  if (!delta) return;
21718
21746
  pendingText += delta;
21719
21747
  emit({ ...base2(threadId, turnId), type: "content.delta", streamKind: "assistant_text", delta });
@@ -21730,9 +21758,9 @@ var init_boxagent = __esm({
21730
21758
  seen.add(id);
21731
21759
  appendNative(threadId, { dir: "in", source: "box.events", msg: ev });
21732
21760
  const kind = String(ev.type ?? ev.kind ?? "");
21733
- const text3 = ev.text ?? ev.message ?? ev.data?.text ?? ev.data?.content ?? null;
21734
- if (/assistant|message|output|response/i.test(kind) && typeof text3 === "string" && text3.trim()) {
21735
- ingest(text3);
21761
+ const text4 = ev.text ?? ev.message ?? ev.data?.text ?? ev.data?.content ?? null;
21762
+ if (/assistant|message|output|response/i.test(kind) && typeof text4 === "string" && text4.trim()) {
21763
+ ingest(text4);
21736
21764
  } else if (/tool|command|exec|browse/i.test(kind)) {
21737
21765
  flushAssistantText();
21738
21766
  emit({
@@ -22272,25 +22300,36 @@ var init_mcp_gate_config = __esm({
22272
22300
  }
22273
22301
  });
22274
22302
 
22303
+ // shared/provider-safety.ts
22304
+ function isProviderSafetyBlock(message2) {
22305
+ 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);
22306
+ }
22307
+ var init_provider_safety = __esm({
22308
+ "shared/provider-safety.ts"() {
22309
+ "use strict";
22310
+ }
22311
+ });
22312
+
22275
22313
  // server/drivers/retry.ts
22276
22314
  function classifyError(err) {
22277
- const text3 = messageOf(err);
22315
+ const text4 = messageOf(err);
22316
+ if (isProviderSafetyBlock(text4)) return { transient: false, reason: "provider_safety" };
22278
22317
  if (err && "exitCode" in err) {
22279
22318
  const { exitCode: code } = err;
22280
22319
  if (code !== null && code < 0) return { transient: false, reason: "interrupted" };
22281
22320
  for (const { pattern, reason } of TRANSIENT_PATTERNS) {
22282
- if (pattern.test(text3)) return { transient: true, reason };
22321
+ if (pattern.test(text4)) return { transient: true, reason };
22283
22322
  }
22284
22323
  for (const { pattern, reason } of TERMINAL_PATTERNS) {
22285
- if (pattern.test(text3)) return { transient: false, reason };
22324
+ if (pattern.test(text4)) return { transient: false, reason };
22286
22325
  }
22287
22326
  return { transient: false, reason: "terminal_exit" };
22288
22327
  }
22289
22328
  for (const { pattern, reason } of TERMINAL_PATTERNS) {
22290
- if (pattern.test(text3)) return { transient: false, reason };
22329
+ if (pattern.test(text4)) return { transient: false, reason };
22291
22330
  }
22292
22331
  for (const { pattern, reason } of TRANSIENT_PATTERNS) {
22293
- if (pattern.test(text3)) return { transient: true, reason };
22332
+ if (pattern.test(text4)) return { transient: true, reason };
22294
22333
  }
22295
22334
  return { transient: false, reason: "unknown" };
22296
22335
  }
@@ -22321,6 +22360,7 @@ var RETRY_MAX_ATTEMPTS, BACKOFF_BASE_MS, TRANSIENT_PATTERNS, TERMINAL_PATTERNS,
22321
22360
  var init_retry = __esm({
22322
22361
  "server/drivers/retry.ts"() {
22323
22362
  "use strict";
22363
+ init_provider_safety();
22324
22364
  RETRY_MAX_ATTEMPTS = 3;
22325
22365
  BACKOFF_BASE_MS = [1e3, 3e3, 8e3];
22326
22366
  TRANSIENT_PATTERNS = [
@@ -22355,6 +22395,82 @@ var init_retry = __esm({
22355
22395
  }
22356
22396
  });
22357
22397
 
22398
+ // shared/ask-question.ts
22399
+ function text3(value, limit) {
22400
+ if (typeof value !== "string") return void 0;
22401
+ const trimmed = value.trim();
22402
+ return trimmed ? trimmed.slice(0, limit) : void 0;
22403
+ }
22404
+ function isRecord(value) {
22405
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22406
+ }
22407
+ function parseOption(value) {
22408
+ if (typeof value === "string") {
22409
+ const label2 = text3(value, MAX_LABEL);
22410
+ return label2 ? { label: label2 } : null;
22411
+ }
22412
+ if (!isRecord(value)) return null;
22413
+ const label = text3(value.label, MAX_LABEL);
22414
+ if (!label) return null;
22415
+ const description = text3(value.description, MAX_DESCRIPTION);
22416
+ return description ? { label, description } : { label };
22417
+ }
22418
+ function parseQuestion(value) {
22419
+ if (!isRecord(value)) return null;
22420
+ const question = text3(value.question, MAX_QUESTION_TEXT);
22421
+ if (!question) return null;
22422
+ const options = [];
22423
+ const seen = /* @__PURE__ */ new Set();
22424
+ for (const raw of Array.isArray(value.options) ? value.options : []) {
22425
+ const option = parseOption(raw);
22426
+ if (!option || seen.has(option.label)) continue;
22427
+ seen.add(option.label);
22428
+ options.push(option);
22429
+ if (options.length === MAX_OPTIONS) break;
22430
+ }
22431
+ const header = text3(value.header, MAX_LABEL);
22432
+ return {
22433
+ question,
22434
+ ...header ? { header } : {},
22435
+ ...value.multiSelect === true ? { multiSelect: true } : {},
22436
+ options
22437
+ };
22438
+ }
22439
+ function parseAskQuestions(input) {
22440
+ if (!isRecord(input) || !Array.isArray(input.questions)) return null;
22441
+ const questions = [];
22442
+ for (const raw of input.questions) {
22443
+ const question = parseQuestion(raw);
22444
+ if (!question) continue;
22445
+ questions.push(question);
22446
+ if (questions.length === MAX_QUESTIONS) break;
22447
+ }
22448
+ return questions.length ? questions : null;
22449
+ }
22450
+ function askQuestionSummary(questions) {
22451
+ const first = questions[0]?.question ?? "";
22452
+ const rest = questions.length - 1;
22453
+ return rest > 0 ? `${first} (+${rest} more question${rest > 1 ? "s" : ""})` : first;
22454
+ }
22455
+ function questionChoices(questions) {
22456
+ if (questions.length !== 1) return void 0;
22457
+ const only = questions[0];
22458
+ if (only.multiSelect || only.options.length < 2) return void 0;
22459
+ return only.options.map((option) => option.label);
22460
+ }
22461
+ var ASK_USER_QUESTION_TOOL, MAX_QUESTIONS, MAX_OPTIONS, MAX_QUESTION_TEXT, MAX_LABEL, MAX_DESCRIPTION;
22462
+ var init_ask_question = __esm({
22463
+ "shared/ask-question.ts"() {
22464
+ "use strict";
22465
+ ASK_USER_QUESTION_TOOL = "AskUserQuestion";
22466
+ MAX_QUESTIONS = 6;
22467
+ MAX_OPTIONS = 12;
22468
+ MAX_QUESTION_TEXT = 400;
22469
+ MAX_LABEL = 120;
22470
+ MAX_DESCRIPTION = 400;
22471
+ }
22472
+ });
22473
+
22358
22474
  // server/drivers/claude.ts
22359
22475
  import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
22360
22476
  import { chmodSync as chmodSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
@@ -22395,9 +22511,9 @@ function resolveClaudeConfigDir(configDir, env = process.env) {
22395
22511
  }
22396
22512
  return normalize(expanded);
22397
22513
  }
22398
- function claudeAuthFailure(frame, text3) {
22514
+ function claudeAuthFailure(frame, text4) {
22399
22515
  if (frame.is_api_error_message !== true && typeof frame.error !== "string") return false;
22400
- return frame.error === "authentication_failed" || classifyError({ text: text3 }).reason === "auth";
22516
+ return frame.error === "authentication_failed" || classifyError({ text: text4 }).reason === "auth";
22401
22517
  }
22402
22518
  function claudeEnvironment(model, source = process.env, configDir, instanceEnvironment = {}) {
22403
22519
  const env = { ...source, PATH: augmentedPath(), NPM_CONFIG_LOGLEVEL: "error" };
@@ -22421,6 +22537,24 @@ function claudeEnvironment(model, source = process.env, configDir, instanceEnvir
22421
22537
  function inheritsUserConfig(env) {
22422
22538
  return env.OMB_CLAUDE_INHERIT_USER_CONFIG === "1";
22423
22539
  }
22540
+ function readClaudeAuthSettings(env, instanceEnvironment = {}) {
22541
+ if (CLAUDE_ACCOUNT_ENV_KEYS.some((key) => instanceEnvironment[key])) return {};
22542
+ try {
22543
+ const settings2 = JSON.parse(readFileSync8(join20(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
22544
+ const authEnv = {};
22545
+ for (const key of CLAUDE_ACCOUNT_ENV_KEYS) {
22546
+ if (!key.endsWith("_FILE_DESCRIPTOR") && typeof settings2?.env?.[key] === "string") {
22547
+ authEnv[key] = settings2.env[key];
22548
+ }
22549
+ }
22550
+ return {
22551
+ ...Object.keys(authEnv).length ? { env: authEnv } : {},
22552
+ ...typeof settings2?.apiKeyHelper === "string" && settings2.apiKeyHelper.trim() ? { apiKeyHelper: settings2.apiKeyHelper } : {}
22553
+ };
22554
+ } catch {
22555
+ return {};
22556
+ }
22557
+ }
22424
22558
  function projectMcpServers(cwd) {
22425
22559
  let parsed;
22426
22560
  try {
@@ -22525,18 +22659,24 @@ function removePrivateTempDir(filePath) {
22525
22659
  function systemEndedReply(kind) {
22526
22660
  return kind === "question" ? { behavior: "answer", message: "OpenMausBot: the turn is ending \u2014 wrap up." } : { behavior: "deny", message: "OpenMausBot: the turn ended" };
22527
22661
  }
22662
+ function askQuestions(ask) {
22663
+ return ask.tool === ASK_USER_QUESTION_TOOL ? parseAskQuestions(ask.input) : null;
22664
+ }
22528
22665
  function askSummary(ask) {
22666
+ const questions = askQuestions(ask);
22667
+ if (questions) return askQuestionSummary(questions).slice(0, 300);
22529
22668
  return askInputSummary(ask.input) ?? ask.tool ?? "tool";
22530
22669
  }
22531
- function permissionSocketPath(threadId) {
22670
+ function permissionSocketPath(threadId, botId) {
22671
+ const key = botId ? `${botId}\0${threadId}` : threadId;
22532
22672
  const prefix = threadId.replace(/[^\w-]/g, "").slice(0, 4);
22533
- const digest2 = createHash5("sha256").update(threadId).digest("hex").slice(0, 4);
22673
+ const digest2 = createHash5("sha256").update(key).digest("hex").slice(0, 4);
22534
22674
  return brokerSocketPath(DATA_DIR, `${prefix}${digest2}`);
22535
22675
  }
22536
- function brokerSocketCandidates(threadId) {
22537
- const base2 = permissionSocketPath(threadId);
22676
+ function brokerSocketCandidates(threadId, botId) {
22677
+ const base2 = permissionSocketPath(threadId, botId);
22538
22678
  if (process.platform !== "win32") {
22539
- const scope = createHash5("sha256").update(`${DATA_DIR}\0${process.pid}\0${threadId}`).digest("hex").slice(0, 16);
22679
+ const scope = createHash5("sha256").update(`${DATA_DIR}\0${process.pid}\0${botId ?? ""}\0${threadId}`).digest("hex").slice(0, 16);
22540
22680
  return [base2, join20(tmpdir3(), `omb-perm-${scope}.sock`)];
22541
22681
  }
22542
22682
  return [
@@ -22596,7 +22736,9 @@ async function createPermissionBroker(opts) {
22596
22736
  if (!pending.delete(askId)) return;
22597
22737
  clearTimeout(timer);
22598
22738
  try {
22599
- conn.write(JSON.stringify({ t: "answer", id: askId, behavior, message: message2, ...always ? { always: true } : {} }) + "\n");
22739
+ conn.write(
22740
+ JSON.stringify({ t: "answer", id: askId, behavior, message: message2, source, ...always ? { always: true } : {} }) + "\n"
22741
+ );
22600
22742
  } catch {
22601
22743
  }
22602
22744
  opts.onResolve({ ...ask, behavior, source });
@@ -22735,19 +22877,19 @@ function firstText(content) {
22735
22877
  }
22736
22878
  return "";
22737
22879
  }
22738
- function withVolatileNote(text3, volatile) {
22880
+ function withVolatileNote(text4, volatile) {
22739
22881
  const body = volatile.trim() ? `This part of your instructions changed since this session started. It replaces the earlier copy:
22740
22882
 
22741
22883
  ${volatile.trim()}` : "The notes that were in your instructions when this session started have been cleared.";
22742
22884
  const note = `<system-reminder>
22743
22885
  ${body}
22744
22886
  </system-reminder>`;
22745
- return text3 ? `${note}
22887
+ return text4 ? `${note}
22746
22888
 
22747
- ${text3}` : note;
22889
+ ${text4}` : note;
22748
22890
  }
22749
- function claudeUserMessage(text3, images) {
22750
- if (!images?.length) return { type: "user", message: { role: "user", content: text3 } };
22891
+ function claudeUserMessage(text4, images) {
22892
+ if (!images?.length) return { type: "user", message: { role: "user", content: text4 } };
22751
22893
  const content = images.map((image) => ({
22752
22894
  type: "image",
22753
22895
  source: {
@@ -22756,7 +22898,7 @@ function claudeUserMessage(text3, images) {
22756
22898
  data: readFileSync8(image.path).toString("base64")
22757
22899
  }
22758
22900
  }));
22759
- if (text3) content.push({ type: "text", text: text3 });
22901
+ if (text4) content.push({ type: "text", text: text4 });
22760
22902
  return { type: "user", message: { role: "user", content } };
22761
22903
  }
22762
22904
  function diagnosticClaudeUserMessage(message2) {
@@ -22794,6 +22936,7 @@ var init_claude = __esm({
22794
22936
  init_local_inject();
22795
22937
  init_native();
22796
22938
  init_proxy_paths();
22939
+ init_ask_question();
22797
22940
  CLAUDE_ACCOUNT_ENV_KEYS = [
22798
22941
  "CLAUDE_CODE_OAUTH_TOKEN",
22799
22942
  "CLAUDE_CODE_OAUTH_REFRESH_TOKEN",
@@ -22931,8 +23074,8 @@ var init_claude = __esm({
22931
23074
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
22932
23075
  });
22933
23076
  const retryState = /* @__PURE__ */ new Map();
22934
- const sendTurn = async (turn) => {
22935
- const { threadId } = turn;
23077
+ const sendTurn = async (turn, logicalTurnId) => {
23078
+ const { threadId, botId } = turn;
22936
23079
  if (active.has(threadId)) throw new Error("a turn is already running on this thread");
22937
23080
  const permissionMode = turn.approvalMode === void 0 ? config2.permissionMode : turn.approvalMode === "full" ? "bypassPermissions" : turn.approvalMode === "auto" ? "auto" : turn.approvalMode === "edits" ? "acceptEdits" : "default";
22938
23081
  const controlsHost = turn.integrations?.localComputer?.scope === "local-computer";
@@ -22940,7 +23083,7 @@ var init_claude = __esm({
22940
23083
  throw new Error("local computer control requires the interactive approval broker");
22941
23084
  }
22942
23085
  const promptMsg = claudeUserMessage(turn.text, turn.images);
22943
- const turnId = newId();
23086
+ const turnId = logicalTurnId ?? newId();
22944
23087
  const retryAbort = new AbortController();
22945
23088
  const retry = retryState.get(threadId) ?? { attempt: 0, cancelled: false };
22946
23089
  retry.cancelled = false;
@@ -23045,7 +23188,7 @@ var init_claude = __esm({
23045
23188
  if (gated) mcpServers[name] = gated;
23046
23189
  }
23047
23190
  let broker;
23048
- const socketPath = permissionSocketPath(threadId);
23191
+ const socketPath = permissionSocketPath(threadId, botId);
23049
23192
  if (permissionMode !== "bypassPermissions") {
23050
23193
  args.push("--permission-prompt-tool", "mcp__ogb__approve");
23051
23194
  }
@@ -23058,9 +23201,12 @@ var init_claude = __esm({
23058
23201
  args.push("--allowedTools", allowed.join(","));
23059
23202
  }
23060
23203
  const env = environment(turnModel);
23204
+ const authSettings = isolated && !injected.injected ? readClaudeAuthSettings(env, input.environment) : {};
23205
+ const authSettingsPath = mcpConfigPath && Object.keys(authSettings).length ? join20(dirname9(mcpConfigPath), "auth-settings.json") : null;
23206
+ if (authSettingsPath) args.push("--settings", authSettingsPath);
23061
23207
  env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS = "1";
23062
23208
  const cwd = turn.cwd ?? homedir9();
23063
- const privateFileFlags = /* @__PURE__ */ new Set(["--mcp-config"]);
23209
+ const privateFileFlags = /* @__PURE__ */ new Set(["--mcp-config", "--settings"]);
23064
23210
  const keyArgs = args.filter((a2, i2) => !privateFileFlags.has(a2) && !privateFileFlags.has(args[i2 - 1] ?? ""));
23065
23211
  const argsKey = JSON.stringify({
23066
23212
  args: keyArgs,
@@ -23071,14 +23217,21 @@ var init_claude = __esm({
23071
23217
  cwd,
23072
23218
  model: injected.model ?? null,
23073
23219
  base: env.ANTHROPIC_BASE_URL ?? null,
23074
- configDir: env.CLAUDE_CONFIG_DIR ?? null
23220
+ configDir: env.CLAUDE_CONFIG_DIR ?? null,
23221
+ // Rotating an account's key/helper must not reuse the old process.
23222
+ auth: createHash5("sha256").update(JSON.stringify({
23223
+ settings: authSettings,
23224
+ env: Object.fromEntries(CLAUDE_ACCOUNT_ENV_KEYS.map((key) => [key, env[key]]))
23225
+ })).digest("hex")
23075
23226
  });
23076
23227
  const live = sessions.get(threadId);
23077
23228
  if (live && !live.turn && !live.closing && live.child.exitCode === null && live.argsKey === argsKey && (!sessionId || sessionId === live.sessionId)) {
23078
23229
  if (live.idleTimer) clearTimeout(live.idleTimer);
23079
- live.turn = { turnId, settled: false, sawStreamDelta: false };
23230
+ live.turn = { turnId, input: turn, retryAbort, settled: false, sawStreamDelta: false };
23080
23231
  active.set(threadId, { stop: () => {
23081
23232
  closeSession(threadId, "interrupted");
23233
+ retry.cancelled = true;
23234
+ retryAbort.abort();
23082
23235
  stopSession(live);
23083
23236
  }, turnId, broker: live.broker });
23084
23237
  emit({ ...base2(threadId, turnId), type: "turn.started" });
@@ -23133,13 +23286,14 @@ var init_claude = __esm({
23133
23286
  if (socketPath) {
23134
23287
  const askTools = /* @__PURE__ */ new Map();
23135
23288
  broker = await createPermissionBroker({
23136
- socketPaths: brokerSocketCandidates(threadId),
23289
+ socketPaths: brokerSocketCandidates(threadId, botId),
23137
23290
  isActive: () => Boolean(sessions.get(threadId)?.turn),
23138
23291
  onAsk: (ask) => {
23139
23292
  const eventTurnId = sessions.get(threadId)?.turn?.turnId ?? turnId;
23140
23293
  askTools.set(ask.id, typeof ask.tool === "string" ? ask.tool : void 0);
23141
23294
  const nativeMode = sessions.get(threadId)?.nativePermissionMode ?? null;
23142
23295
  const nativeReview = permissionMode === "auto" && nativeMode !== null ? nativeMode === "auto" ? "active" : "inactive" : void 0;
23296
+ const questions = askQuestions(ask);
23143
23297
  emit({
23144
23298
  ...base2(threadId, eventTurnId),
23145
23299
  type: "request.opened",
@@ -23152,7 +23306,10 @@ var init_claude = __esm({
23152
23306
  // host control stays one action at a time
23153
23307
  allowSession: ask.kind === "permission" && !(controlsHost && typeof ask.tool === "string" && ask.tool.startsWith("mcp__computer")) ? true : void 0,
23154
23308
  approvalScope: typeof ask.tool === "string" && controlsHost && ask.tool.startsWith("mcp__computer") ? "local-computer" : void 0,
23155
- choices: Array.isArray(ask.input?.choices) ? ask.input.choices.slice(0, 5) : void 0
23309
+ questions: questions ?? void 0,
23310
+ // A structured ask still offers flat labels, for the phone
23311
+ // companions and any client that predates the question card.
23312
+ choices: questions ? questionChoices(questions) : Array.isArray(ask.input?.choices) ? ask.input.choices.slice(0, 5) : void 0
23156
23313
  });
23157
23314
  },
23158
23315
  onResolve: (resolved) => {
@@ -23175,6 +23332,9 @@ var init_claude = __esm({
23175
23332
  if (mcpConfigPath) {
23176
23333
  writeFileSync6(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
23177
23334
  }
23335
+ if (authSettingsPath) {
23336
+ writeFileSync6(authSettingsPath, JSON.stringify(authSettings), { mode: 384 });
23337
+ }
23178
23338
  if (sessionId) args.push("--resume", sessionId);
23179
23339
  else args.push("--session-id", newSessionId);
23180
23340
  } catch (error51) {
@@ -23202,7 +23362,7 @@ var init_claude = __esm({
23202
23362
  sessionId: sessionId ?? newSessionId,
23203
23363
  sawInit: false,
23204
23364
  nativePermissionMode: null,
23205
- turn: { turnId, settled: false, sawStreamDelta: false },
23365
+ turn: { turnId, input: turn, retryAbort, settled: false, sawStreamDelta: false },
23206
23366
  idleTimer: null,
23207
23367
  closing: false,
23208
23368
  stderr: ""
@@ -23265,18 +23425,18 @@ var init_claude = __esm({
23265
23425
  }
23266
23426
  case "assistant": {
23267
23427
  const msg = o.message ?? {};
23268
- const text3 = firstText(msg.content);
23269
- if (claudeAuthFailure(o, text3)) {
23428
+ const text4 = firstText(msg.content);
23429
+ if (claudeAuthFailure(o, text4)) {
23270
23430
  if (session.turn) session.turn.authFailed = true;
23271
- emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: text3, setup: true });
23431
+ emit({ ...base2(threadId, currentTurnId()), type: "runtime.error", message: text4, setup: true });
23272
23432
  break;
23273
23433
  }
23274
- if (text3.trim()) {
23434
+ if (text4.trim()) {
23275
23435
  if (!session.turn?.sawStreamDelta) {
23276
- emit({ ...base2(threadId, currentTurnId()), type: "content.delta", streamKind: "assistant_text", delta: text3 });
23436
+ emit({ ...base2(threadId, currentTurnId()), type: "content.delta", streamKind: "assistant_text", delta: text4 });
23277
23437
  }
23278
23438
  if (session.turn) session.turn.sawStreamDelta = false;
23279
- emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "assistant_text", text: text3 });
23439
+ emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "assistant_text", text: text4 });
23280
23440
  }
23281
23441
  for (const b of Array.isArray(msg.content) ? msg.content : []) {
23282
23442
  if (b.type === "tool_use") {
@@ -23354,9 +23514,11 @@ var init_claude = __esm({
23354
23514
  if (closeFinalized) return;
23355
23515
  closeFinalized = true;
23356
23516
  if (session.turn && !session.turn.settled) {
23517
+ const { turnId: turnId2, input: turn2, retryAbort: retryAbort2 } = session.turn;
23518
+ const retry2 = retryState.get(threadId) ?? { attempt: 0, cancelled: false };
23357
23519
  const message2 = `claude exited ${code} before result${session.stderr ? `: ${session.stderr.trim().slice(-300)}` : ""}`;
23358
23520
  const verdict = classifyError({ exitCode: code, stderr: message2 });
23359
- if (!retry.cancelled && code !== 0 && verdict.transient && !session.turn.sawStreamDelta && retry.attempt < RETRY_MAX_ATTEMPTS - 1) {
23521
+ if (!retry2.cancelled && code !== 0 && verdict.transient && !session.turn.sawStreamDelta && retry2.attempt < RETRY_MAX_ATTEMPTS - 1) {
23360
23522
  const failedBroker = session.broker;
23361
23523
  session.broker = void 0;
23362
23524
  failedBroker?.pause();
@@ -23374,23 +23536,23 @@ var init_claude = __esm({
23374
23536
  }
23375
23537
  sessions.delete(threadId);
23376
23538
  session.turn = null;
23377
- retry.attempt++;
23378
- const delayMs = computeBackoff(retry.attempt - 1);
23539
+ retry2.attempt++;
23540
+ const delayMs = computeBackoff(retry2.attempt - 1);
23379
23541
  emit({
23380
- ...base2(threadId, turnId),
23542
+ ...base2(threadId, turnId2),
23381
23543
  type: "turn.retrying",
23382
- attempt: retry.attempt,
23544
+ attempt: retry2.attempt,
23383
23545
  delayMs,
23384
23546
  reason: verdict.reason
23385
23547
  });
23386
23548
  void (async () => {
23387
- const wait2 = interruptibleDelay(delayMs * retryScale, retryAbort.signal);
23549
+ const wait2 = interruptibleDelay(delayMs * retryScale, retryAbort2.signal);
23388
23550
  await wait2.promise;
23389
- if (retry.cancelled) {
23551
+ if (retry2.cancelled) {
23390
23552
  active.delete(threadId);
23391
23553
  retryState.delete(threadId);
23392
23554
  emit({
23393
- ...base2(threadId, turnId),
23555
+ ...base2(threadId, turnId2),
23394
23556
  type: "turn.completed",
23395
23557
  ok: false,
23396
23558
  stopReason: "interrupted",
@@ -23401,16 +23563,16 @@ var init_claude = __esm({
23401
23563
  active.delete(threadId);
23402
23564
  try {
23403
23565
  const cursor3 = session.sessionId ?? sessionId ?? void 0;
23404
- await sendTurn({ ...turn, resumeCursor: cursor3 });
23566
+ await sendTurn({ ...turn2, resumeCursor: cursor3 }, turnId2);
23405
23567
  } catch (e) {
23406
23568
  retryState.delete(threadId);
23407
23569
  emit({
23408
- ...base2(threadId, turnId),
23570
+ ...base2(threadId, turnId2),
23409
23571
  type: "runtime.error",
23410
23572
  message: e instanceof Error ? e.message : String(e)
23411
23573
  });
23412
23574
  emit({
23413
- ...base2(threadId, turnId),
23575
+ ...base2(threadId, turnId2),
23414
23576
  type: "turn.completed",
23415
23577
  ok: false,
23416
23578
  stopReason: "exit_before_result",
@@ -23426,10 +23588,10 @@ var init_claude = __esm({
23426
23588
  promptSubmitted: session.sawInit,
23427
23589
  producedOutput: session.turn.sawStreamDelta
23428
23590
  });
23429
- if (mayReplay(resumeFailure) && !retry.cancelled) {
23591
+ if (mayReplay(resumeFailure) && !retry2.cancelled) {
23430
23592
  const recovery = recoveryPromptFor({
23431
- recoveryText: turn.recoveryText,
23432
- currentText: turn.text,
23593
+ recoveryText: turn2.recoveryText,
23594
+ currentText: turn2.text,
23433
23595
  failure: resumeFailure
23434
23596
  });
23435
23597
  session.broker?.close();
@@ -23449,23 +23611,23 @@ var init_claude = __esm({
23449
23611
  session.turn = null;
23450
23612
  active.delete(threadId);
23451
23613
  emit({
23452
- ...base2(threadId, turnId),
23614
+ ...base2(threadId, turnId2),
23453
23615
  type: "turn.retrying",
23454
- attempt: retry.attempt + 1,
23616
+ attempt: retry2.attempt + 1,
23455
23617
  delayMs: 0,
23456
23618
  reason: "resume_rejected"
23457
23619
  });
23458
23620
  void (async () => {
23459
23621
  try {
23460
- await sendTurn({ ...turn, resumeCursor: void 0, recoveryText: void 0, text: recovery.text });
23622
+ await sendTurn({ ...turn2, resumeCursor: void 0, recoveryText: void 0, text: recovery.text }, turnId2);
23461
23623
  } catch (e) {
23462
23624
  retryState.delete(threadId);
23463
23625
  emit({
23464
- ...base2(threadId, turnId),
23626
+ ...base2(threadId, turnId2),
23465
23627
  type: "runtime.error",
23466
23628
  message: e instanceof Error ? e.message : String(e)
23467
23629
  });
23468
- emit({ ...base2(threadId, turnId), type: "turn.completed", ok: false, stopReason: "exit_before_result", cost: null });
23630
+ emit({ ...base2(threadId, turnId2), type: "turn.completed", ok: false, stopReason: "exit_before_result", cost: null });
23469
23631
  }
23470
23632
  })();
23471
23633
  return;
@@ -23507,10 +23669,10 @@ var init_claude = __esm({
23507
23669
  }
23508
23670
  return { turnId };
23509
23671
  };
23510
- const steer = async (threadId, text3) => {
23672
+ const steer = async (threadId, text4) => {
23511
23673
  const s = sessions.get(threadId);
23512
23674
  if (!s || !s.turn || s.turn.settled || s.closing || s.child.exitCode !== null) return false;
23513
- return writeUser(s, threadId, claudeUserMessage(text3, void 0));
23675
+ return writeUser(s, threadId, claudeUserMessage(text4, void 0));
23514
23676
  };
23515
23677
  const login = new ClaudeLoginController({ cli: config2.cli, environment, onAuthenticated: async () => {
23516
23678
  await refreshModels();
@@ -23788,7 +23950,7 @@ function unquote(raw) {
23788
23950
  }
23789
23951
  return value;
23790
23952
  }
23791
- function parseCodexToml(text3) {
23953
+ function parseCodexToml(text4) {
23792
23954
  const result = { providers: [] };
23793
23955
  const byId = /* @__PURE__ */ new Map();
23794
23956
  let section = "root";
@@ -23801,7 +23963,7 @@ function parseCodexToml(text3) {
23801
23963
  }
23802
23964
  return provider;
23803
23965
  };
23804
- for (const line of text3.split(/\r?\n/)) {
23966
+ for (const line of text4.split(/\r?\n/)) {
23805
23967
  const stripped = line.trim();
23806
23968
  if (!stripped || stripped.startsWith("#")) continue;
23807
23969
  if (stripped.startsWith("[") && stripped.endsWith("]")) {
@@ -24883,6 +25045,7 @@ var init_codex = __esm({
24883
25045
  const earlyNotifications = [];
24884
25046
  const state = {
24885
25047
  settled: false,
25048
+ lastError: "",
24886
25049
  lastText: "",
24887
25050
  sawStreamDelta: false,
24888
25051
  // codex reports token usage as a running THREAD total; the harness
@@ -25144,13 +25307,26 @@ var init_codex = __esm({
25144
25307
  }
25145
25308
  case "turn/completed": {
25146
25309
  const t2 = p.turn ?? {};
25147
- void settle(t2.status === "completed", t2.status === "completed" ? null : t2.error?.message ?? t2.status ?? "failed");
25310
+ const message2 = typeof t2.error?.message === "string" ? t2.error.message.slice(0, 400) : "";
25311
+ if (t2.status !== "completed" && message2 && message2 !== state.lastError) {
25312
+ state.lastError = message2;
25313
+ emit({
25314
+ ...base2(threadId, turnId),
25315
+ type: "runtime.error",
25316
+ message: message2,
25317
+ ...classifyError({ text: message2 }).reason === "auth" ? { setup: true } : {}
25318
+ });
25319
+ }
25320
+ void settle(t2.status === "completed", t2.status === "completed" ? null : classifyError({ text: message2 || state.lastError }).reason === "provider_safety" ? "provider_safety" : message2 || t2.status || "failed");
25148
25321
  break;
25149
25322
  }
25150
25323
  case "error":
25151
25324
  {
25152
25325
  const message2 = p.message ?? p.error?.message;
25153
- if (message2) emit({ ...base2(threadId, turnId), type: "runtime.error", message: String(message2).slice(0, 400) });
25326
+ if (message2) {
25327
+ state.lastError = String(message2).slice(0, 400);
25328
+ emit({ ...base2(threadId, turnId), type: "runtime.error", message: state.lastError });
25329
+ }
25154
25330
  }
25155
25331
  break;
25156
25332
  }
@@ -25358,7 +25534,7 @@ var init_codex = __esm({
25358
25534
  message: message2,
25359
25535
  ...needsAuth ? { setup: true } : {}
25360
25536
  });
25361
- await settle(false, needsAuth ? "auth_required" : "rpc_error");
25537
+ await settle(false, needsAuth ? "auth_required" : verdict.reason === "provider_safety" ? "provider_safety" : "rpc_error");
25362
25538
  }
25363
25539
  }
25364
25540
  };
@@ -25472,72 +25648,89 @@ function createOpenAIChatRuntime(options) {
25472
25648
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
25473
25649
  });
25474
25650
  const complete = async (messages, model, stream, signal, onDelta) => {
25475
- const timeout = AbortSignal.timeout(options.timeoutMs);
25476
- const response = await fetch(`${options.apiUrl}/chat/completions`, {
25477
- method: "POST",
25478
- headers: { authorization: `Bearer ${options.apiKey}`, "content-type": "application/json" },
25479
- body: JSON.stringify(options.requestBody(model, messages, stream)),
25480
- signal: signal ? AbortSignal.any([signal, timeout]) : timeout
25481
- });
25482
- if (!response.ok) {
25483
- const body = await response.text().catch(() => "");
25484
- throw new Error(`${options.httpErrorLabel} HTTP ${response.status}${body ? `: ${body.slice(0, 200)}` : ""}`);
25485
- }
25486
- if (!stream) {
25487
- const json2 = await response.json();
25488
- const message2 = json2.choices?.[0]?.message;
25489
- return {
25490
- text: typeof message2?.content === "string" ? message2.content : "",
25491
- reasoning: options.reasoning && typeof message2?.reasoning_content === "string" ? message2.reasoning_content : "",
25492
- usage: usageFrom(json2.usage)
25493
- };
25494
- }
25495
- if (!response.body) {
25496
- throw new Error(options.noBodyError ?? `${options.httpErrorLabel} returned no response body`);
25497
- }
25498
- let text3 = "";
25499
- let reasoning = "";
25500
- let usage = null;
25501
- const reader = response.body.getReader();
25502
- const decoder = new TextDecoder();
25503
- let buffer = "";
25651
+ const timeoutController = new AbortController();
25652
+ let idleTimer = null;
25653
+ const resetIdleTimer = () => {
25654
+ if (idleTimer) clearTimeout(idleTimer);
25655
+ idleTimer = setTimeout(() => {
25656
+ timeoutController.abort(new DOMException("Streaming idle timeout elapsed", "AbortError"));
25657
+ }, options.timeoutMs);
25658
+ };
25659
+ resetIdleTimer();
25504
25660
  try {
25505
- readLoop: for (; ; ) {
25506
- const { done, value } = await reader.read();
25507
- if (done) break;
25508
- buffer += decoder.decode(value, { stream: true });
25509
- let newline;
25510
- while ((newline = buffer.indexOf("\n")) !== -1) {
25511
- const line = buffer.slice(0, newline).trim();
25512
- buffer = buffer.slice(newline + 1);
25513
- if (!line.startsWith("data:")) continue;
25514
- const data = line.slice(5).trim();
25515
- if (data === "[DONE]") break readLoop;
25516
- let chunk;
25517
- try {
25518
- chunk = JSON.parse(data);
25519
- } catch {
25520
- continue;
25521
- }
25522
- const delta = chunk.choices?.[0]?.delta;
25523
- const reasoningDelta = options.reasoning && typeof delta?.reasoning_content === "string" ? delta.reasoning_content : "";
25524
- const contentDelta = typeof delta?.content === "string" ? delta.content : "";
25525
- if (reasoningDelta) {
25526
- reasoning += reasoningDelta;
25527
- onDelta?.(reasoningDelta, "reasoning_text");
25528
- }
25529
- if (contentDelta) {
25530
- text3 += contentDelta;
25531
- onDelta?.(contentDelta, "assistant_text");
25661
+ const activeSignal = signal ? AbortSignal.any([signal, timeoutController.signal]) : timeoutController.signal;
25662
+ const response = await fetch(`${options.apiUrl}/chat/completions`, {
25663
+ method: "POST",
25664
+ headers: { authorization: `Bearer ${options.apiKey}`, "content-type": "application/json" },
25665
+ body: JSON.stringify(options.requestBody(model, messages, stream)),
25666
+ signal: activeSignal
25667
+ });
25668
+ if (!response.ok) {
25669
+ const body = await response.text().catch(() => "");
25670
+ throw new Error(`${options.httpErrorLabel} HTTP ${response.status}${body ? `: ${body.slice(0, 200)}` : ""}`);
25671
+ }
25672
+ if (!stream) {
25673
+ const json2 = await response.json();
25674
+ const message2 = json2.choices?.[0]?.message;
25675
+ return {
25676
+ text: typeof message2?.content === "string" ? message2.content : "",
25677
+ reasoning: options.reasoning && typeof message2?.reasoning_content === "string" ? message2.reasoning_content : "",
25678
+ usage: usageFrom(json2.usage)
25679
+ };
25680
+ }
25681
+ if (!response.body) {
25682
+ throw new Error(options.noBodyError ?? `${options.httpErrorLabel} returned no response body`);
25683
+ }
25684
+ let text4 = "";
25685
+ let reasoning = "";
25686
+ let usage = null;
25687
+ const reader = response.body.getReader();
25688
+ const decoder = new TextDecoder();
25689
+ let buffer = "";
25690
+ try {
25691
+ readLoop: for (; ; ) {
25692
+ const { done, value } = await reader.read();
25693
+ if (done) {
25694
+ if (buffer.trim() === "data: [DONE]") break;
25695
+ throw new DOMException("Stream ended before completion", "AbortError");
25696
+ }
25697
+ resetIdleTimer();
25698
+ buffer += decoder.decode(value, { stream: true });
25699
+ let newline;
25700
+ while ((newline = buffer.indexOf("\n")) !== -1) {
25701
+ const line = buffer.slice(0, newline).trim();
25702
+ buffer = buffer.slice(newline + 1);
25703
+ if (!line.startsWith("data:")) continue;
25704
+ const data = line.slice(5).trim();
25705
+ if (data === "[DONE]") break readLoop;
25706
+ let chunk;
25707
+ try {
25708
+ chunk = JSON.parse(data);
25709
+ } catch {
25710
+ continue;
25711
+ }
25712
+ const delta = chunk.choices?.[0]?.delta;
25713
+ const reasoningDelta = options.reasoning && typeof delta?.reasoning_content === "string" ? delta.reasoning_content : "";
25714
+ const contentDelta = typeof delta?.content === "string" ? delta.content : "";
25715
+ if (reasoningDelta) {
25716
+ reasoning += reasoningDelta;
25717
+ onDelta?.(reasoningDelta, "reasoning_text");
25718
+ }
25719
+ if (contentDelta) {
25720
+ text4 += contentDelta;
25721
+ onDelta?.(contentDelta, "assistant_text");
25722
+ }
25723
+ if (chunk.usage) usage = usageFrom(chunk.usage);
25532
25724
  }
25533
- if (chunk.usage) usage = usageFrom(chunk.usage);
25534
25725
  }
25726
+ } finally {
25727
+ await reader.cancel().catch(() => {
25728
+ });
25535
25729
  }
25730
+ return { text: text4, reasoning, usage };
25536
25731
  } finally {
25537
- await reader.cancel().catch(() => {
25538
- });
25732
+ if (idleTimer) clearTimeout(idleTimer);
25539
25733
  }
25540
- return { text: text3, reasoning, usage };
25541
25734
  };
25542
25735
  const messagesFor = (turn) => [
25543
25736
  ...turn.system ? [{ role: "system", content: turn.system }] : [],
@@ -25654,8 +25847,8 @@ function createOpenAIChatRuntime(options) {
25654
25847
  },
25655
25848
  generateText: async (prompt) => {
25656
25849
  const model = options.generateModel?.() ?? options.models().default;
25657
- const { text: text3, reasoning } = await complete([{ role: "user", content: prompt }], model, false);
25658
- return text3.trim() ? text3 : reasoning;
25850
+ const { text: text4, reasoning } = await complete([{ role: "user", content: prompt }], model, false);
25851
+ return text4.trim() ? text4 : reasoning;
25659
25852
  },
25660
25853
  dispose: async () => {
25661
25854
  for (const abort of active.values()) abort.abort();
@@ -25723,7 +25916,7 @@ var init_grok = __esm({
25723
25916
  nativeLog: {
25724
25917
  source: "xai.chat.completions",
25725
25918
  outgoing: (_turn, messages, model) => ({ model, messages }),
25726
- incoming: ({ text: text3, usage }) => ({ text: text3, usage })
25919
+ incoming: ({ text: text4, usage }) => ({ text: text4, usage })
25727
25920
  }
25728
25921
  });
25729
25922
  }
@@ -25748,9 +25941,9 @@ function unquote2(raw) {
25748
25941
  }
25749
25942
  function readGrokModelCatalog(env = process.env) {
25750
25943
  const path3 = join25(grokHome(env), "config.toml");
25751
- let text3 = "";
25944
+ let text4 = "";
25752
25945
  try {
25753
- text3 = readFileSync11(path3, "utf8");
25946
+ text4 = readFileSync11(path3, "utf8");
25754
25947
  } catch {
25755
25948
  return STATIC_GROK_MODELS;
25756
25949
  }
@@ -25768,7 +25961,7 @@ function readGrokModelCatalog(env = process.env) {
25768
25961
  options.push({ id: current.slug, label: current.name || current.slug, custom: true });
25769
25962
  current = null;
25770
25963
  };
25771
- for (const line of text3.split(/\r?\n/)) {
25964
+ for (const line of text4.split(/\r?\n/)) {
25772
25965
  const stripped = line.trim();
25773
25966
  if (stripped === "[models]") {
25774
25967
  flush();
@@ -25821,11 +26014,11 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
25821
26014
  const host = localHost(inject.host);
25822
26015
  if (!host) return modelId;
25823
26016
  const path3 = join25(grokHome(env), "config.toml");
25824
- let text3 = "";
26017
+ let text4 = "";
25825
26018
  try {
25826
- text3 = readFileSync11(path3, "utf8");
26019
+ text4 = readFileSync11(path3, "utf8");
25827
26020
  } catch {
25828
- text3 = "";
26021
+ text4 = "";
25829
26022
  }
25830
26023
  const taken = new Set(STATIC_GROK_MODELS.options.map((option) => option.id));
25831
26024
  let current = null;
@@ -25838,7 +26031,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
25838
26031
  current = null;
25839
26032
  };
25840
26033
  let found = null;
25841
- for (const line of text3.split(/\r?\n/)) {
26034
+ for (const line of text4.split(/\r?\n/)) {
25842
26035
  const stripped = line.trim();
25843
26036
  if (stripped.startsWith("[model.") && stripped.endsWith("]")) {
25844
26037
  flush();
@@ -25871,12 +26064,18 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
25871
26064
  `api_key = ${quoteToml(hostApiKey(host, env))}`,
25872
26065
  ""
25873
26066
  ].join("\n");
25874
- const next = text3 && !text3.endsWith("\n") ? `${text3}
26067
+ const next = text4 && !text4.endsWith("\n") ? `${text4}
25875
26068
 
25876
- ${block2}` : `${text3}${text3 ? "\n" : ""}${block2}`;
26069
+ ${block2}` : `${text4}${text4 ? "\n" : ""}${block2}`;
25877
26070
  writeFileSync7(path3, next);
25878
26071
  return slug;
25879
26072
  }
26073
+ function grokAcceptsUnadvertisedImages(init) {
26074
+ const meta3 = init?._meta;
26075
+ if (meta3?.grokShell !== true || typeof meta3.agentVersion !== "string") return false;
26076
+ const version2 = /^1\.0\.(\d+)$/.exec(meta3.agentVersion);
26077
+ return Boolean(version2 && Number(version2[1]) >= 25);
26078
+ }
25880
26079
  var STATIC_GROK_MODELS, SLUG, support2, GrokAgentDriver;
25881
26080
  var init_grok2 = __esm({
25882
26081
  "server/drivers/acp/grok.ts"() {
@@ -25894,7 +26093,8 @@ var init_grok2 = __esm({
25894
26093
  support2 = {
25895
26094
  driverKind: "grokAgent",
25896
26095
  displayName: "Grok",
25897
- images: false,
26096
+ images: true,
26097
+ acceptsUnadvertisedImages: grokAcceptsUnadvertisedImages,
25898
26098
  models: STATIC_GROK_MODELS,
25899
26099
  resolveModels: (env) => mergeLocalInject(readGrokModelCatalog(env), env),
25900
26100
  // Grok's accepted levels vary by model and the CLI validates lazily — a
@@ -26068,18 +26268,18 @@ function stripTomlLineComment(line) {
26068
26268
  }
26069
26269
  return line;
26070
26270
  }
26071
- function takeTomlBasicEscape(text3, i2) {
26072
- const code = text3[i2 + 1];
26271
+ function takeTomlBasicEscape(text4, i2) {
26272
+ const code = text4[i2 + 1];
26073
26273
  if (code === void 0) return { ok: false };
26074
26274
  if (code === "u") {
26075
- const hex3 = text3.slice(i2 + 2, i2 + 6);
26275
+ const hex3 = text4.slice(i2 + 2, i2 + 6);
26076
26276
  if (!/^[0-9a-fA-F]{4}$/.test(hex3)) return { ok: false };
26077
26277
  const point = parseInt(hex3, 16);
26078
26278
  if (point >= 55296 && point <= 57343) return { ok: false };
26079
26279
  return { ok: true, value: String.fromCharCode(point), next: i2 + 6 };
26080
26280
  }
26081
26281
  if (code === "U") {
26082
- const hex3 = text3.slice(i2 + 2, i2 + 10);
26282
+ const hex3 = text4.slice(i2 + 2, i2 + 10);
26083
26283
  if (!/^[0-9a-fA-F]{8}$/.test(hex3)) return { ok: false };
26084
26284
  const point = parseInt(hex3, 16);
26085
26285
  if (point > 1114111 || point >= 55296 && point <= 57343) return { ok: false };
@@ -26151,14 +26351,14 @@ function tomlRowKey(row) {
26151
26351
  const eq = row.indexOf("=");
26152
26352
  return unquoteTomlKey(eq < 0 ? row : row.slice(0, eq));
26153
26353
  }
26154
- function tomlTables(text3) {
26354
+ function tomlTables(text4) {
26155
26355
  const headings = [];
26156
26356
  let mode = "out";
26157
26357
  let i2 = 0;
26158
- const atLineStart = (idx) => idx === 0 || text3[idx - 1] === "\n";
26159
- while (i2 < text3.length) {
26358
+ const atLineStart = (idx) => idx === 0 || text4[idx - 1] === "\n";
26359
+ while (i2 < text4.length) {
26160
26360
  if (mode === "mlbasic") {
26161
- if (text3.startsWith('"""', i2)) {
26361
+ if (text4.startsWith('"""', i2)) {
26162
26362
  mode = "out";
26163
26363
  i2 += 3;
26164
26364
  continue;
@@ -26167,7 +26367,7 @@ function tomlTables(text3) {
26167
26367
  continue;
26168
26368
  }
26169
26369
  if (mode === "mllit") {
26170
- if (text3.startsWith("'''", i2)) {
26370
+ if (text4.startsWith("'''", i2)) {
26171
26371
  mode = "out";
26172
26372
  i2 += 3;
26173
26373
  continue;
@@ -26176,61 +26376,61 @@ function tomlTables(text3) {
26176
26376
  continue;
26177
26377
  }
26178
26378
  if (mode === "basic") {
26179
- if (text3[i2] === "\n") {
26379
+ if (text4[i2] === "\n") {
26180
26380
  mode = "out";
26181
26381
  i2 += 1;
26182
26382
  continue;
26183
26383
  }
26184
- if (text3[i2] === "\\") {
26384
+ if (text4[i2] === "\\") {
26185
26385
  i2 += 2;
26186
26386
  continue;
26187
26387
  }
26188
- if (text3[i2] === '"') mode = "out";
26388
+ if (text4[i2] === '"') mode = "out";
26189
26389
  i2 += 1;
26190
26390
  continue;
26191
26391
  }
26192
26392
  if (mode === "literal") {
26193
- if (text3[i2] === "\n") {
26393
+ if (text4[i2] === "\n") {
26194
26394
  mode = "out";
26195
26395
  i2 += 1;
26196
26396
  continue;
26197
26397
  }
26198
- if (text3[i2] === "'") mode = "out";
26398
+ if (text4[i2] === "'") mode = "out";
26199
26399
  i2 += 1;
26200
26400
  continue;
26201
26401
  }
26202
- if (text3.startsWith('"""', i2)) {
26402
+ if (text4.startsWith('"""', i2)) {
26203
26403
  mode = "mlbasic";
26204
26404
  i2 += 3;
26205
26405
  continue;
26206
26406
  }
26207
- if (text3.startsWith("'''", i2)) {
26407
+ if (text4.startsWith("'''", i2)) {
26208
26408
  mode = "mllit";
26209
26409
  i2 += 3;
26210
26410
  continue;
26211
26411
  }
26212
- if (text3[i2] === '"') {
26412
+ if (text4[i2] === '"') {
26213
26413
  mode = "basic";
26214
26414
  i2 += 1;
26215
26415
  continue;
26216
26416
  }
26217
- if (text3[i2] === "'") {
26417
+ if (text4[i2] === "'") {
26218
26418
  mode = "literal";
26219
26419
  i2 += 1;
26220
26420
  continue;
26221
26421
  }
26222
- if (text3[i2] === "#") {
26223
- const nl = text3.indexOf("\n", i2);
26224
- i2 = nl < 0 ? text3.length : nl + 1;
26422
+ if (text4[i2] === "#") {
26423
+ const nl = text4.indexOf("\n", i2);
26424
+ i2 = nl < 0 ? text4.length : nl + 1;
26225
26425
  continue;
26226
26426
  }
26227
26427
  if (atLineStart(i2)) {
26228
26428
  let j = i2;
26229
- while (j < text3.length && (text3[j] === " " || text3[j] === " ")) j += 1;
26230
- if (text3[j] === "[") {
26231
- const nl = text3.indexOf("\n", j);
26232
- const lineEnd = nl < 0 ? text3.length : nl;
26233
- const raw = text3.slice(j, lineEnd).replace(/\r$/, "");
26429
+ while (j < text4.length && (text4[j] === " " || text4[j] === " ")) j += 1;
26430
+ if (text4[j] === "[") {
26431
+ const nl = text4.indexOf("\n", j);
26432
+ const lineEnd = nl < 0 ? text4.length : nl;
26433
+ const raw = text4.slice(j, lineEnd).replace(/\r$/, "");
26234
26434
  const stripped = stripTomlLineComment(raw).trim();
26235
26435
  const array2 = stripped.startsWith("[[");
26236
26436
  const name = array2 ? canonicalizeTomlHeading(`[${stripped.replace(/^\s*\[\[/, "").replace(/\]\]\s*$/, "")}]`) : canonicalizeTomlHeading(raw);
@@ -26243,11 +26443,11 @@ function tomlTables(text3) {
26243
26443
  }
26244
26444
  return headings.map((heading, index) => ({
26245
26445
  heading,
26246
- end: index + 1 < headings.length ? headings[index + 1].lineStart : text3.length
26446
+ end: index + 1 < headings.length ? headings[index + 1].lineStart : text4.length
26247
26447
  })).filter((entry) => entry.heading.patchable && entry.heading.name).map((entry) => ({
26248
26448
  name: entry.heading.name,
26249
26449
  headingStart: entry.heading.lineStart,
26250
- bodyStart: entry.heading.lineEnd + (text3[entry.heading.lineEnd] === "\n" ? 1 : 0),
26450
+ bodyStart: entry.heading.lineEnd + (text4[entry.heading.lineEnd] === "\n" ? 1 : 0),
26251
26451
  end: entry.end
26252
26452
  }));
26253
26453
  }
@@ -26303,22 +26503,22 @@ function tomlKeys(block2) {
26303
26503
  take(block2.length);
26304
26504
  return keys;
26305
26505
  }
26306
- function hasTomlTable(text3, heading) {
26506
+ function hasTomlTable(text4, heading) {
26307
26507
  const name = canonicalizeTomlHeading(heading);
26308
- return name !== null && tomlTables(text3).some((table) => table.name === name);
26508
+ return name !== null && tomlTables(text4).some((table) => table.name === name);
26309
26509
  }
26310
- function patchTomlTable(text3, heading, rows) {
26510
+ function patchTomlTable(text4, heading, rows) {
26311
26511
  const name = canonicalizeTomlHeading(heading);
26312
- if (!name) return text3;
26313
- const table = tomlTables(text3).find((entry) => entry.name === name);
26314
- if (!table) return text3;
26315
- const keys = tomlKeys(text3.slice(table.bodyStart, table.end));
26512
+ if (!name) return text4;
26513
+ const table = tomlTables(text4).find((entry) => entry.name === name);
26514
+ if (!table) return text4;
26515
+ const keys = tomlKeys(text4.slice(table.bodyStart, table.end));
26316
26516
  const missing = rows.filter((row) => !keys.has(tomlRowKey(row)));
26317
- if (!missing.length) return text3;
26517
+ if (!missing.length) return text4;
26318
26518
  let insertAt = table.end;
26319
- while (insertAt > table.bodyStart && (text3[insertAt - 1] === "\n" || text3[insertAt - 1] === "\r")) insertAt -= 1;
26320
- const before = text3.slice(0, insertAt);
26321
- const after = text3.slice(insertAt);
26519
+ while (insertAt > table.bodyStart && (text4[insertAt - 1] === "\n" || text4[insertAt - 1] === "\r")) insertAt -= 1;
26520
+ const before = text4.slice(0, insertAt);
26521
+ const after = text4.slice(insertAt);
26322
26522
  const pad = before.endsWith("\n") || before.length === 0 ? "" : "\n";
26323
26523
  return `${before}${pad}${missing.join("\n")}${after.startsWith("\n") ? "" : "\n"}${after}`;
26324
26524
  }
@@ -26331,17 +26531,17 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
26331
26531
  const dataRoot = kimiDataRoot(env);
26332
26532
  mkdirSync10(dataRoot, { recursive: true });
26333
26533
  const path3 = join27(dataRoot, "config.toml");
26334
- let text3 = "";
26534
+ let text4 = "";
26335
26535
  try {
26336
- text3 = readFileSync13(path3, "utf8");
26536
+ text4 = readFileSync13(path3, "utf8");
26337
26537
  } catch {
26338
- text3 = "";
26538
+ text4 = "";
26339
26539
  }
26340
- const original = text3;
26540
+ const original = text4;
26341
26541
  const providerHeading = `[providers.${inject.host}]`;
26342
26542
  const modelHeading = `[models.${quoteTomlKey(alias)}]`;
26343
26543
  const blocks = [];
26344
- if (!hasTomlTable(text3, providerHeading)) {
26544
+ if (!hasTomlTable(text4, providerHeading)) {
26345
26545
  blocks.push(
26346
26546
  [
26347
26547
  providerHeading,
@@ -26352,8 +26552,8 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
26352
26552
  ].join("\n")
26353
26553
  );
26354
26554
  }
26355
- if (hasTomlTable(text3, modelHeading)) {
26356
- text3 = patchTomlTable(text3, modelHeading, [`protocol = "openai"`, `max_context_size = 262144`]);
26555
+ if (hasTomlTable(text4, modelHeading)) {
26556
+ text4 = patchTomlTable(text4, modelHeading, [`protocol = "openai"`, `max_context_size = 262144`]);
26357
26557
  } else {
26358
26558
  blocks.push(
26359
26559
  [
@@ -26367,13 +26567,13 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
26367
26567
  );
26368
26568
  }
26369
26569
  if (blocks.length) {
26370
- const prefix = text3 && !text3.endsWith("\n") ? `${text3}
26570
+ const prefix = text4 && !text4.endsWith("\n") ? `${text4}
26371
26571
 
26372
- ` : text3 ? `${text3}
26572
+ ` : text4 ? `${text4}
26373
26573
  ` : "";
26374
26574
  writeFileSync8(path3, `${prefix}${blocks.join("\n")}`);
26375
- } else if (text3 !== original) {
26376
- writeFileSync8(path3, text3);
26575
+ } else if (text4 !== original) {
26576
+ writeFileSync8(path3, text4);
26377
26577
  }
26378
26578
  return alias;
26379
26579
  }
@@ -26393,9 +26593,9 @@ function stripKimiModelEnv(env) {
26393
26593
  }
26394
26594
  function readKimiModelCatalog(env) {
26395
26595
  const dataRoot = kimiDataRoot(env);
26396
- let text3 = "";
26596
+ let text4 = "";
26397
26597
  try {
26398
- text3 = readFileSync13(join27(dataRoot, "config.toml"), "utf8");
26598
+ text4 = readFileSync13(join27(dataRoot, "config.toml"), "utf8");
26399
26599
  } catch {
26400
26600
  return STATIC_KIMI_MODELS;
26401
26601
  }
@@ -26411,7 +26611,7 @@ function readKimiModelCatalog(env) {
26411
26611
  options.push({ id: current.slug, label: current.name || current.slug, custom: true });
26412
26612
  current = null;
26413
26613
  };
26414
- for (const line of text3.split(/\r?\n/)) {
26614
+ for (const line of text4.split(/\r?\n/)) {
26415
26615
  const stripped = line.trim();
26416
26616
  if ((stripped.startsWith("[model.") || stripped.startsWith("[models.")) && stripped.endsWith("]")) {
26417
26617
  flush();
@@ -26704,8 +26904,8 @@ function resolveCursorAcpModelId(available, wanted) {
26704
26904
  }
26705
26905
  return null;
26706
26906
  }
26707
- function firstJsonValue(text3) {
26708
- const trimmed = text3.trim();
26907
+ function firstJsonValue(text4) {
26908
+ const trimmed = text4.trim();
26709
26909
  if (!trimmed) return null;
26710
26910
  try {
26711
26911
  return JSON.parse(trimmed);
@@ -26779,12 +26979,12 @@ function stripModelMarkers(label) {
26779
26979
  }
26780
26980
  return { label: cleaned, isDefault, isCurrent };
26781
26981
  }
26782
- function decodeCursorModelText(text3) {
26982
+ function decodeCursorModelText(text4) {
26783
26983
  const options = [];
26784
26984
  const seen = /* @__PURE__ */ new Set();
26785
26985
  let markedDefault;
26786
26986
  let markedCurrent;
26787
- for (const raw of text3.split(/\r?\n/)) {
26987
+ for (const raw of text4.split(/\r?\n/)) {
26788
26988
  const line = raw.trim();
26789
26989
  if (!line || line.startsWith("#") || /^available\s+models?\b/i.test(line) || /^models?\b/i.test(line)) continue;
26790
26990
  const stripped = line.replace(/^[\s*•-]+\s*/, "");
@@ -26837,8 +27037,8 @@ function decodeCursorAuthStatus(payload) {
26837
27037
  }
26838
27038
  return null;
26839
27039
  }
26840
- function decodeCursorAuthText(text3) {
26841
- const normalized = text3.trim().toLowerCase();
27040
+ function decodeCursorAuthText(text4) {
27041
+ const normalized = text4.trim().toLowerCase();
26842
27042
  if (!normalized) return null;
26843
27043
  if (/not (?:logged|signed) in|not authenticated|unauthenticated|logged out/.test(normalized)) return false;
26844
27044
  if (/login successful|logged in|signed in|authenticated/.test(normalized)) return true;
@@ -27258,7 +27458,7 @@ function qwenHome(env) {
27258
27458
  const home = process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir19() : env.HOME || env.USERPROFILE || homedir19();
27259
27459
  return join30(home, ".qwen");
27260
27460
  }
27261
- function isRecord(value) {
27461
+ function isRecord2(value) {
27262
27462
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
27263
27463
  }
27264
27464
  function qwenModelLabel(id, name) {
@@ -27272,15 +27472,15 @@ function readQwenRoutes(env) {
27272
27472
  } catch {
27273
27473
  return [];
27274
27474
  }
27275
- if (!isRecord(settings2) || !isRecord(settings2.modelProviders)) return [];
27475
+ if (!isRecord2(settings2) || !isRecord2(settings2.modelProviders)) return [];
27276
27476
  const routes = [];
27277
27477
  const seen = /* @__PURE__ */ new Set();
27278
- const mappings = isRecord(settings2.providerProtocol) ? settings2.providerProtocol : {};
27478
+ const mappings = isRecord2(settings2.providerProtocol) ? settings2.providerProtocol : {};
27279
27479
  for (const [provider, rows] of Object.entries(settings2.modelProviders)) {
27280
27480
  const protocol = Object.hasOwn(mappings, provider) ? mappings[provider] : provider;
27281
27481
  if (typeof protocol !== "string" || !PROTOCOLS.has(protocol) || !Array.isArray(rows)) continue;
27282
27482
  for (const row of rows) {
27283
- if (!isRecord(row) || typeof row.id !== "string") continue;
27483
+ if (!isRecord2(row) || typeof row.id !== "string") continue;
27284
27484
  const model = row.id;
27285
27485
  if (!model.trim()) continue;
27286
27486
  if (row.baseUrl !== void 0 && typeof row.baseUrl !== "string") continue;
@@ -28426,14 +28626,14 @@ var init_stringifyComment = __esm({
28426
28626
  });
28427
28627
 
28428
28628
  // node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/stringify/foldFlowLines.js
28429
- function foldFlowLines(text3, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
28629
+ function foldFlowLines(text4, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
28430
28630
  if (!lineWidth || lineWidth < 0)
28431
- return text3;
28631
+ return text4;
28432
28632
  if (lineWidth < minContentWidth)
28433
28633
  minContentWidth = 0;
28434
28634
  const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);
28435
- if (text3.length <= endStep)
28436
- return text3;
28635
+ if (text4.length <= endStep)
28636
+ return text4;
28437
28637
  const folds = [];
28438
28638
  const escapedFolds = {};
28439
28639
  let end = lineWidth - indent.length;
@@ -28450,14 +28650,14 @@ function foldFlowLines(text3, indent, mode = "flow", { indentAtStart, lineWidth
28450
28650
  let escStart = -1;
28451
28651
  let escEnd = -1;
28452
28652
  if (mode === FOLD_BLOCK) {
28453
- i2 = consumeMoreIndentedLines(text3, i2, indent.length);
28653
+ i2 = consumeMoreIndentedLines(text4, i2, indent.length);
28454
28654
  if (i2 !== -1)
28455
28655
  end = i2 + endStep;
28456
28656
  }
28457
- for (let ch; ch = text3[i2 += 1]; ) {
28657
+ for (let ch; ch = text4[i2 += 1]; ) {
28458
28658
  if (mode === FOLD_QUOTED && ch === "\\") {
28459
28659
  escStart = i2;
28460
- switch (text3[i2 + 1]) {
28660
+ switch (text4[i2 + 1]) {
28461
28661
  case "x":
28462
28662
  i2 += 3;
28463
28663
  break;
@@ -28474,12 +28674,12 @@ function foldFlowLines(text3, indent, mode = "flow", { indentAtStart, lineWidth
28474
28674
  }
28475
28675
  if (ch === "\n") {
28476
28676
  if (mode === FOLD_BLOCK)
28477
- i2 = consumeMoreIndentedLines(text3, i2, indent.length);
28677
+ i2 = consumeMoreIndentedLines(text4, i2, indent.length);
28478
28678
  end = i2 + indent.length + endStep;
28479
28679
  split = void 0;
28480
28680
  } else {
28481
28681
  if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") {
28482
- const next = text3[i2 + 1];
28682
+ const next = text4[i2 + 1];
28483
28683
  if (next && next !== " " && next !== "\n" && next !== " ")
28484
28684
  split = i2;
28485
28685
  }
@@ -28491,12 +28691,12 @@ function foldFlowLines(text3, indent, mode = "flow", { indentAtStart, lineWidth
28491
28691
  } else if (mode === FOLD_QUOTED) {
28492
28692
  while (prev === " " || prev === " ") {
28493
28693
  prev = ch;
28494
- ch = text3[i2 += 1];
28694
+ ch = text4[i2 += 1];
28495
28695
  overflow = true;
28496
28696
  }
28497
28697
  const j = i2 > escEnd + 1 ? i2 - 2 : escStart - 1;
28498
28698
  if (escapedFolds[j])
28499
- return text3;
28699
+ return text4;
28500
28700
  folds.push(j);
28501
28701
  escapedFolds[j] = true;
28502
28702
  end = j + endStep;
@@ -28511,39 +28711,39 @@ function foldFlowLines(text3, indent, mode = "flow", { indentAtStart, lineWidth
28511
28711
  if (overflow && onOverflow)
28512
28712
  onOverflow();
28513
28713
  if (folds.length === 0)
28514
- return text3;
28714
+ return text4;
28515
28715
  if (onFold)
28516
28716
  onFold();
28517
- let res = text3.slice(0, folds[0]);
28717
+ let res = text4.slice(0, folds[0]);
28518
28718
  for (let i3 = 0; i3 < folds.length; ++i3) {
28519
28719
  const fold = folds[i3];
28520
- const end2 = folds[i3 + 1] || text3.length;
28720
+ const end2 = folds[i3 + 1] || text4.length;
28521
28721
  if (fold === 0)
28522
28722
  res = `
28523
- ${indent}${text3.slice(0, end2)}`;
28723
+ ${indent}${text4.slice(0, end2)}`;
28524
28724
  else {
28525
28725
  if (mode === FOLD_QUOTED && escapedFolds[fold])
28526
- res += `${text3[fold]}\\`;
28726
+ res += `${text4[fold]}\\`;
28527
28727
  res += `
28528
- ${indent}${text3.slice(fold + 1, end2)}`;
28728
+ ${indent}${text4.slice(fold + 1, end2)}`;
28529
28729
  }
28530
28730
  }
28531
28731
  return res;
28532
28732
  }
28533
- function consumeMoreIndentedLines(text3, i2, indent) {
28733
+ function consumeMoreIndentedLines(text4, i2, indent) {
28534
28734
  let end = i2;
28535
28735
  let start = i2 + 1;
28536
- let ch = text3[start];
28736
+ let ch = text4[start];
28537
28737
  while (ch === " " || ch === " ") {
28538
28738
  if (i2 < start + indent) {
28539
- ch = text3[++i2];
28739
+ ch = text4[++i2];
28540
28740
  } else {
28541
28741
  do {
28542
- ch = text3[++i2];
28742
+ ch = text4[++i2];
28543
28743
  } while (ch && ch !== "\n");
28544
28744
  end = i2;
28545
28745
  start = i2 + 1;
28546
- ch = text3[start];
28746
+ ch = text4[start];
28547
28747
  }
28548
28748
  }
28549
28749
  return end;
@@ -34369,24 +34569,24 @@ function quoteYaml(value) {
34369
34569
  if (/^[\w./:+-]+$/.test(value)) return value;
34370
34570
  return JSON.stringify(value);
34371
34571
  }
34372
- function upsertHermesProvider(text3, hostId, baseUrl, apiKey) {
34572
+ function upsertHermesProvider(text4, hostId, baseUrl, apiKey) {
34373
34573
  const block2 = [` ${hostId}:`, ` base_url: ${quoteYaml(baseUrl)}`, ` api_key: ${quoteYaml(apiKey)}`, ""].join(
34374
34574
  "\n"
34375
34575
  );
34376
- if (/^providers:\s*$/m.test(text3)) {
34377
- const replaced = replaceHermesHostBlock(text3, hostId, block2);
34576
+ if (/^providers:\s*$/m.test(text4)) {
34577
+ const replaced = replaceHermesHostBlock(text4, hostId, block2);
34378
34578
  if (replaced !== null) return replaced;
34379
- return text3.replace(/^providers:\s*$/m, `providers:
34579
+ return text4.replace(/^providers:\s*$/m, `providers:
34380
34580
  ${block2.trimEnd()}`);
34381
34581
  }
34382
- const prefix = text3 && !text3.endsWith("\n") ? `${text3}
34383
- ` : text3;
34582
+ const prefix = text4 && !text4.endsWith("\n") ? `${text4}
34583
+ ` : text4;
34384
34584
  return `${prefix}
34385
34585
  providers:
34386
34586
  ${block2}`;
34387
34587
  }
34388
- function replaceHermesHostBlock(text3, hostId, block2) {
34389
- const lines = text3.split("\n");
34588
+ function replaceHermesHostBlock(text4, hostId, block2) {
34589
+ const lines = text4.split("\n");
34390
34590
  const start = lines.findIndex((line) => line === ` ${hostId}:`);
34391
34591
  if (start < 0) return null;
34392
34592
  let end = start + 1;
@@ -34406,14 +34606,14 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
34406
34606
  const dir = hermesHome(env);
34407
34607
  mkdirSync14(dir, { recursive: true });
34408
34608
  const path3 = join31(dir, "config.yaml");
34409
- let text3 = "";
34609
+ let text4 = "";
34410
34610
  try {
34411
- text3 = readFileSync17(path3, "utf8");
34611
+ text4 = readFileSync17(path3, "utf8");
34412
34612
  } catch {
34413
- text3 = "";
34613
+ text4 = "";
34414
34614
  }
34415
- const next = upsertHermesProvider(text3, inject.host, host.baseUrl, hostApiKey(host, env));
34416
- if (next !== text3) writeFileSync12(path3, next);
34615
+ const next = upsertHermesProvider(text4, inject.host, host.baseUrl, hostApiKey(host, env));
34616
+ if (next !== text4) writeFileSync12(path3, next);
34417
34617
  return hermesAcpModelId(modelId) ?? modelId;
34418
34618
  }
34419
34619
  function hermesAcpModelId(modelId) {
@@ -34423,8 +34623,8 @@ function hermesAcpModelId(modelId) {
34423
34623
  if (native && !native.includes(INJECT_SEP) && /^[a-z0-9_-]+:[\w./:-]+$/i.test(native)) return native;
34424
34624
  return null;
34425
34625
  }
34426
- function nonEmptyDotenvValue(text3, name) {
34427
- const match = new RegExp(`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=[ \\t]*([^\\r\\n]*)$`, "m").exec(text3);
34626
+ function nonEmptyDotenvValue(text4, name) {
34627
+ const match = new RegExp(`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=[ \\t]*([^\\r\\n]*)$`, "m").exec(text4);
34428
34628
  if (!match) return null;
34429
34629
  const raw = match[1].trim();
34430
34630
  if (!raw || raw.startsWith("#")) return null;
@@ -34441,10 +34641,10 @@ function nonEmptyDotenvValue(text3, name) {
34441
34641
  function yamlString(value) {
34442
34642
  return typeof value === "string" ? value.trim() : "";
34443
34643
  }
34444
- function hermesConfigDefault(text3) {
34644
+ function hermesConfigDefault(text4) {
34445
34645
  let raw;
34446
34646
  try {
34447
- raw = parse3(text3);
34647
+ raw = parse3(text4);
34448
34648
  } catch {
34449
34649
  return null;
34450
34650
  }
@@ -34497,11 +34697,18 @@ function hermesConfiguredModel(env = process.env) {
34497
34697
  custom: true
34498
34698
  };
34499
34699
  }
34700
+ function hermesAcpModelsTimeoutMs(env) {
34701
+ const raw = env[HERMES_ACP_MODELS_TIMEOUT_ENV];
34702
+ if (raw === void 0) return HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS;
34703
+ const parsed = Number(raw);
34704
+ return Number.isInteger(parsed) && parsed > 0 && parsed <= 2147483647 ? parsed : HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS;
34705
+ }
34500
34706
  async function fetchHermesAcpModels(cli, env) {
34501
34707
  return await new Promise((resolve13) => {
34502
34708
  let child;
34503
34709
  try {
34504
- child = spawn6(cli, ["acp"], { stdio: ["pipe", "pipe", "ignore"], env });
34710
+ const resolved = resolveCli(cli, ["acp"], env);
34711
+ child = spawn6(resolved.command, resolved.args, { stdio: ["pipe", "pipe", "ignore"], env, windowsHide: true });
34505
34712
  } catch {
34506
34713
  return resolve13([]);
34507
34714
  }
@@ -34526,7 +34733,7 @@ async function fetchHermesAcpModels(cli, env) {
34526
34733
  }
34527
34734
  resolve13(out);
34528
34735
  };
34529
- timer = setTimeout(() => done([]), 5e3);
34736
+ timer = setTimeout(() => done([]), hermesAcpModelsTimeoutMs(env));
34530
34737
  child.once("error", () => done([]));
34531
34738
  child.once("close", () => {
34532
34739
  if (hardKillTimer) clearTimeout(hardKillTimer);
@@ -34606,11 +34813,12 @@ async function applySetting2(request, method, params, what) {
34606
34813
  throw new Error(`Hermes rejected ${what} via ${method}: ${e.message}`);
34607
34814
  }
34608
34815
  }
34609
- var EMPTY2, HERMES_OPENMAUS_SCREENSHOT_COMPAT, HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL, HERMES_CONFIG_MODEL_ID, HERMES_HOSTED_PROVIDER_KEYS, HERMES_LOCAL_CONFIG_PROVIDERS, support10, HermesAgentDriver;
34816
+ var EMPTY2, HERMES_OPENMAUS_SCREENSHOT_COMPAT, HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL, HERMES_CONFIG_MODEL_ID, HERMES_HOSTED_PROVIDER_KEYS, HERMES_LOCAL_CONFIG_PROVIDERS, HERMES_ACP_MODELS_TIMEOUT_ENV, HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS, support10, HermesAgentDriver;
34610
34817
  var init_hermes = __esm({
34611
34818
  "server/drivers/acp/hermes.ts"() {
34612
34819
  "use strict";
34613
34820
  init_browser();
34821
+ init_procs();
34614
34822
  init_local_inject();
34615
34823
  init_core3();
34616
34824
  EMPTY2 = { default: "", options: [] };
@@ -34624,6 +34832,8 @@ var init_hermes = __esm({
34624
34832
  "Z_AI_API_KEY"
34625
34833
  ];
34626
34834
  HERMES_LOCAL_CONFIG_PROVIDERS = /* @__PURE__ */ new Set(["custom", "lmstudio", "ollama", "vllm", "llamacpp"]);
34835
+ HERMES_ACP_MODELS_TIMEOUT_ENV = "HERMES_ACP_MODELS_TIMEOUT_MS";
34836
+ HERMES_ACP_MODELS_DEFAULT_TIMEOUT_MS = 15e3;
34627
34837
  support10 = {
34628
34838
  driverKind: "hermesAgent",
34629
34839
  displayName: "Hermes",
@@ -34696,12 +34906,19 @@ function decodeConfig5(raw) {
34696
34906
  provider: typeof config2.provider === "string" ? config2.provider || void 0 : process.env.OPENAI_COMPAT_PROVIDER || void 0
34697
34907
  };
34698
34908
  }
34699
- var DRIVER_KIND5, DEFAULT_MODELS, OpenAICompatDriver;
34909
+ var DRIVER_KIND5, DEFAULT_IDLE_TIMEOUT_MS, idleTimeoutMs, DEFAULT_MODELS, OpenAICompatDriver;
34700
34910
  var init_openai_compat = __esm({
34701
34911
  "server/drivers/openai-compat.ts"() {
34702
34912
  "use strict";
34703
34913
  init_openai_chat();
34704
34914
  DRIVER_KIND5 = "openai-compat";
34915
+ DEFAULT_IDLE_TIMEOUT_MS = 18e4;
34916
+ idleTimeoutMs = () => {
34917
+ const raw = process.env.OPENMAUS_OPENAI_COMPAT_IDLE_TIMEOUT_MS;
34918
+ if (!raw) return DEFAULT_IDLE_TIMEOUT_MS;
34919
+ const value = Number(raw);
34920
+ return Number.isSafeInteger(value) && value >= 1e3 && value <= 2147483647 ? value : DEFAULT_IDLE_TIMEOUT_MS;
34921
+ };
34705
34922
  DEFAULT_MODELS = {
34706
34923
  default: "meta-llama/llama-3.3-70b-instruct",
34707
34924
  options: [
@@ -34782,15 +34999,15 @@ var init_openai_compat = __esm({
34782
34999
  httpErrorLabel: "upstream",
34783
35000
  missingKeyError: `no API key \u2014 set ${config2.apiKeyEnv} or add it to the instance config`,
34784
35001
  unavailableReason: `no API key \u2014 set ${config2.apiKeyEnv} or add it to the instance config`,
34785
- timeoutMs: 12e4,
35002
+ timeoutMs: idleTimeoutMs(),
34786
35003
  reasoning: true,
34787
35004
  billing: "metered",
34788
35005
  includeUsageInCompleted: true,
34789
35006
  nativeLog: {
34790
35007
  source: "openai-compat.chat.completions",
34791
35008
  outgoing: (_turn, messages, model) => ({ model, messageCount: messages.length }),
34792
- incoming: ({ text: text3, reasoning, usage }) => ({
34793
- textLength: text3.length,
35009
+ incoming: ({ text: text4, reasoning, usage }) => ({
35010
+ textLength: text4.length,
34794
35011
  reasoningLength: reasoning.length,
34795
35012
  usage
34796
35013
  })
@@ -35026,6 +35243,16 @@ async function fetchPiModels(cli, env) {
35026
35243
  }
35027
35244
  });
35028
35245
  }
35246
+ async function updatePiModelCatalog(cli, env) {
35247
+ return new Promise((resolve13) => {
35248
+ execCli(
35249
+ cli,
35250
+ PI_MODEL_UPDATE_ARGS,
35251
+ { env, timeout: 6e4, maxBuffer: 1024 * 1024 },
35252
+ (error51) => resolve13(!error51)
35253
+ );
35254
+ });
35255
+ }
35029
35256
  function decodeConfig6(raw) {
35030
35257
  if (raw === null || raw === void 0) return { cli: "pi", fullAuto: false };
35031
35258
  if (typeof raw !== "object") throw new Error("pi config must be an object");
@@ -35043,7 +35270,7 @@ function piEnvironment(source) {
35043
35270
  for (const key of PROVIDER_CREDENTIAL_ENV) delete env[key];
35044
35271
  return env;
35045
35272
  }
35046
- var DRIVER_KIND6, PI_ARGS, NODE_ENV_FLAG2, EMPTY3, PiDriver;
35273
+ var DRIVER_KIND6, PI_ARGS, PI_MODEL_UPDATE_ARGS, NODE_ENV_FLAG2, EMPTY3, PiDriver;
35047
35274
  var init_pi = __esm({
35048
35275
  "server/drivers/pi.ts"() {
35049
35276
  "use strict";
@@ -35057,6 +35284,7 @@ var init_pi = __esm({
35057
35284
  init_native();
35058
35285
  DRIVER_KIND6 = "piAgent";
35059
35286
  PI_ARGS = ["--mode", "rpc", "--no-session"];
35287
+ PI_MODEL_UPDATE_ARGS = ["update", "--models", "--no-approve"];
35060
35288
  NODE_ENV_FLAG2 = { ELECTRON_RUN_AS_NODE: "1" };
35061
35289
  EMPTY3 = { default: "", options: [] };
35062
35290
  PiDriver = {
@@ -35078,7 +35306,7 @@ var init_pi = __esm({
35078
35306
  const { instanceId, config: config2 } = input;
35079
35307
  const catalogEnv = piEnvironment({ ...process.env, ...input.environment });
35080
35308
  let models = EMPTY3;
35081
- const refreshModels = async () => {
35309
+ const readModels = async () => {
35082
35310
  let base3 = models;
35083
35311
  try {
35084
35312
  const resolved = await fetchPiModels(config2.cli, catalogEnv);
@@ -35092,7 +35320,11 @@ var init_pi = __esm({
35092
35320
  if (base3.options.length) models = base3;
35093
35321
  }
35094
35322
  };
35095
- await refreshModels();
35323
+ const refreshModels = async () => {
35324
+ await updatePiModelCatalog(config2.cli, catalogEnv);
35325
+ await readModels();
35326
+ };
35327
+ await readModels();
35096
35328
  const listeners = /* @__PURE__ */ new Set();
35097
35329
  const active = /* @__PURE__ */ new Map();
35098
35330
  const emit = (event) => {
@@ -35181,10 +35413,10 @@ var init_pi = __esm({
35181
35413
  child.stdin.write(JSON.stringify(obj) + "\n");
35182
35414
  };
35183
35415
  const flushAssistantText = () => {
35184
- const text3 = assistantText;
35416
+ const text4 = assistantText;
35185
35417
  assistantText = "";
35186
- if (!text3.trim()) return;
35187
- emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text3 });
35418
+ if (!text4.trim()) return;
35419
+ emit({ ...base2(threadId, turnId), type: "item.completed", itemType: "assistant_text", text: text4 });
35188
35420
  };
35189
35421
  const settle = (ok, stopReason, usage) => {
35190
35422
  if (settled) return;
@@ -35577,7 +35809,7 @@ var init_minimax = __esm({
35577
35809
  nativeLog: {
35578
35810
  source: "minimax.chat.completions",
35579
35811
  outgoing: (_turn, messages, model) => ({ model, messageCount: messages.length }),
35580
- incoming: ({ text: text3, usage }) => ({ textLength: text3.length, usage })
35812
+ incoming: ({ text: text4, usage }) => ({ textLength: text4.length, usage })
35581
35813
  }
35582
35814
  });
35583
35815
  }
@@ -37244,10 +37476,10 @@ async function verifyHostedEndpoint(endpoint, { fetchImpl, timeoutSignal, timeou
37244
37476
  signal: cancellationSignal ? AbortSignal.any([requestSignal, cancellationSignal]) : requestSignal
37245
37477
  });
37246
37478
  if (!response.ok) return false;
37247
- const text3 = await response.text();
37248
- if (Buffer.byteLength(text3) > 4096) return false;
37479
+ const text4 = await response.text();
37480
+ if (Buffer.byteLength(text4) > 4096) return false;
37249
37481
  try {
37250
- return JSON.parse(text3)?.app === "openmausbot";
37482
+ return JSON.parse(text4)?.app === "openmausbot";
37251
37483
  } catch {
37252
37484
  return false;
37253
37485
  }
@@ -38168,9 +38400,9 @@ async function startCaddy(options) {
38168
38400
  });
38169
38401
  let output = "";
38170
38402
  const receive = (chunk) => {
38171
- const text3 = chunk.toString("utf8");
38172
- if (output.length < 16384) output += text3;
38173
- for (const line of text3.split(/\r?\n/)) {
38403
+ const text4 = chunk.toString("utf8");
38404
+ if (output.length < 16384) output += text4;
38405
+ for (const line of text4.split(/\r?\n/)) {
38174
38406
  if (!line.trim()) continue;
38175
38407
  if (/"level":"(warn|error)"/.test(line) || /^\S+\s+(WARN|ERROR)/.test(line) || !line.startsWith("{")) options.log?.(`caddy: ${line.slice(0, 400)}`);
38176
38408
  }
@@ -38437,8 +38669,8 @@ function agentUnit(spec) {
38437
38669
  function emptyRegistry(domain2) {
38438
38670
  return { version: 1, domain: domain2, nextPort: FLEET_FIRST_PORT, workspaces: {} };
38439
38671
  }
38440
- function parseRegistry(text3) {
38441
- const value = JSON.parse(text3);
38672
+ function parseRegistry(text4) {
38673
+ const value = JSON.parse(text4);
38442
38674
  const record3 = value && typeof value === "object" ? value : null;
38443
38675
  if (!record3 || record3.version !== 1 || typeof record3.domain !== "string" || typeof record3.nextPort !== "number" || !record3.workspaces || typeof record3.workspaces !== "object") {
38444
38676
  throw new Error("fleet.json is not a fleet registry this version understands");
@@ -38862,9 +39094,9 @@ function failed(io, what, output) {
38862
39094
  return 1;
38863
39095
  }
38864
39096
  function loadRegistry(layout, deps) {
38865
- const text3 = deps.readText(layout.registryFile);
38866
- if (text3 === null) throw new Error(`no fleet on this server yet: run \`openmausbot fleet init --domain your.domain\` first`);
38867
- return parseRegistry(text3);
39097
+ const text4 = deps.readText(layout.registryFile);
39098
+ if (text4 === null) throw new Error(`no fleet on this server yet: run \`openmausbot fleet init --domain your.domain\` first`);
39099
+ return parseRegistry(text4);
38868
39100
  }
38869
39101
  function planFleetAction(input, deps) {
38870
39102
  const layout = fleetLayout(input.root ?? "/");
@@ -39011,13 +39243,13 @@ function readUsage(dataDir, range) {
39011
39243
  const from = range.from.getTime();
39012
39244
  const to = range.to.getTime();
39013
39245
  for (const key of monthsBetween(range.from, range.to)) {
39014
- let text3;
39246
+ let text4;
39015
39247
  try {
39016
- text3 = readFileSync2(join4(dataDir, DIR, `${key}.jsonl`), "utf8");
39248
+ text4 = readFileSync2(join4(dataDir, DIR, `${key}.jsonl`), "utf8");
39017
39249
  } catch {
39018
39250
  continue;
39019
39251
  }
39020
- for (const line of text3.split("\n")) {
39252
+ for (const line of text4.split("\n")) {
39021
39253
  if (!line) continue;
39022
39254
  try {
39023
39255
  const value = JSON.parse(line);
@@ -39111,15 +39343,15 @@ function summarizeUsage(rows, groupBy, prices = null) {
39111
39343
  var MAX_BODY = 256 * 1024;
39112
39344
  function readBody(req) {
39113
39345
  return new Promise((resolve13, reject) => {
39114
- let text3 = "";
39346
+ let text4 = "";
39115
39347
  req.on("data", (chunk) => {
39116
- text3 += chunk.toString("utf8");
39117
- if (text3.length > MAX_BODY) reject(Object.assign(new Error("request too large"), { status: 413 }));
39348
+ text4 += chunk.toString("utf8");
39349
+ if (text4.length > MAX_BODY) reject(Object.assign(new Error("request too large"), { status: 413 }));
39118
39350
  });
39119
39351
  req.on("end", () => {
39120
- if (!text3.trim()) return resolve13({});
39352
+ if (!text4.trim()) return resolve13({});
39121
39353
  try {
39122
- const value = JSON.parse(text3);
39354
+ const value = JSON.parse(text4);
39123
39355
  resolve13(value && typeof value === "object" && !Array.isArray(value) ? value : {});
39124
39356
  } catch {
39125
39357
  reject(Object.assign(new Error("body must be JSON"), { status: 400 }));
@@ -39290,8 +39522,8 @@ var tailscaleEnvironment = () => ({
39290
39522
  });
39291
39523
 
39292
39524
  // server/tailscale.ts
39293
- function classifyTailscaleStderr(text3) {
39294
- const t2 = text3.toLowerCase();
39525
+ function classifyTailscaleStderr(text4) {
39526
+ const t2 = text4.toLowerCase();
39295
39527
  if (/not logged in|logged out|needs login|please log in/.test(t2)) return "not-logged-in";
39296
39528
  if (/is tailscale running|tailscaled is not running|connection refused|failed to connect to local tailscaled/.test(t2)) return "not-running";
39297
39529
  if (/access denied|permission denied|operator|must be root|not permitted/.test(t2)) return "needs-operator";
@@ -40022,9 +40254,9 @@ function pairingBlock(input) {
40022
40254
  }
40023
40255
  return lines.join("\n");
40024
40256
  }
40025
- function qrToString(text3) {
40257
+ function qrToString(text4) {
40026
40258
  let out = "";
40027
- import_qrcode_terminal.default.generate(text3, { small: true }, (rendered) => {
40259
+ import_qrcode_terminal.default.generate(text4, { small: true }, (rendered) => {
40028
40260
  out = rendered;
40029
40261
  });
40030
40262
  return out;