frizz 0.7.5 → 0.7.8

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 (95) hide show
  1. package/README.md +31 -53
  2. package/dist/dev-child.js +476 -303
  3. package/dist/frizz.js +13476 -6556
  4. package/package.json +5 -3
  5. package/runtime/cc-worker/DECISIONS.md +12 -0
  6. package/runtime/cc-worker/bin/frizz-mcp.mjs +3 -2
  7. package/web-dist/assets/{TerminalPane-Cyao7kTi.js → TerminalPane-ubcEP7X3.js} +1 -1
  8. package/web-dist/assets/{abnfDiagram-VRR7QNED-D1U0Q_zY.js → abnfDiagram-VRR7QNED-0brfnieb.js} +1 -1
  9. package/web-dist/assets/architecture-TIHT7OUA-DksosNDW.js +1 -0
  10. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-CtUBwBcU.js → architectureDiagram-ZJ3FMSHR-Dji-XBdF.js} +1 -1
  11. package/web-dist/assets/{blockDiagram-677ZJIJ3-OzqrN18g.js → blockDiagram-677ZJIJ3-BQMyz3Q9.js} +1 -1
  12. package/web-dist/assets/{c4Diagram-LMCZKHZV-DgYAtD7L.js → c4Diagram-LMCZKHZV-BMNws0Oi.js} +1 -1
  13. package/web-dist/assets/channel-DqoPvfQz.js +1 -0
  14. package/web-dist/assets/{chunk-32BRIVSS-CKwBruPa.js → chunk-32BRIVSS-CVv1ae7n.js} +1 -1
  15. package/web-dist/assets/{chunk-52WLFC77-gvoNW_ar.js → chunk-52WLFC77-BYhPlKCT.js} +1 -1
  16. package/web-dist/assets/{chunk-C7G6YPKG-EJ5BdmRQ.js → chunk-C7G6YPKG-Cn4HhNwb.js} +1 -1
  17. package/web-dist/assets/{chunk-EX3LRPZG-DJQF-VwN.js → chunk-EX3LRPZG-P3ZYFC1W.js} +1 -1
  18. package/web-dist/assets/{chunk-FWX5IMBZ-CXBLvjvR.js → chunk-FWX5IMBZ-Ba3NpEQa.js} +2 -2
  19. package/web-dist/assets/{chunk-HOUHSVGY-De3ULdBq.js → chunk-HOUHSVGY-BCWyMhwJ.js} +1 -1
  20. package/web-dist/assets/{chunk-ICXQ74PX-aUbMdGsl.js → chunk-ICXQ74PX-DWAAqHL7.js} +1 -1
  21. package/web-dist/assets/{chunk-MOJQB5TN-BLK44SUW.js → chunk-MOJQB5TN-pc5VJ4Nq.js} +1 -1
  22. package/web-dist/assets/{chunk-OGEWGWER-CQZ3zQ2q.js → chunk-OGEWGWER-DUxmbiuX.js} +1 -1
  23. package/web-dist/assets/{chunk-PUDLZKDR-DcdN44K2.js → chunk-PUDLZKDR-DRdZWzxX.js} +1 -1
  24. package/web-dist/assets/{chunk-Q4XR5HBZ-Db2ge7fW.js → chunk-Q4XR5HBZ-Bafzu7s-.js} +1 -1
  25. package/web-dist/assets/{chunk-V7JOEXUC-D80jxcmB.js → chunk-V7JOEXUC-Bj0A8ftW.js} +1 -1
  26. package/web-dist/assets/{chunk-VAUOI2AC-nDGsXZZE.js → chunk-VAUOI2AC-DBgVQKIO.js} +1 -1
  27. package/web-dist/assets/{chunk-VR4S4FIN-BGXgLAsD.js → chunk-VR4S4FIN-DU-1KtuM.js} +1 -1
  28. package/web-dist/assets/{chunk-WYO6CB5R-BbGEK50o.js → chunk-WYO6CB5R-DzVb3-Rj.js} +1 -1
  29. package/web-dist/assets/{chunk-ZGVPDNZ5-CjEI23Vu.js → chunk-ZGVPDNZ5-BPKQvduj.js} +1 -1
  30. package/web-dist/assets/classDiagram-OUVF2IWQ-DwUs74RB.js +1 -0
  31. package/web-dist/assets/classDiagram-v2-EOCWNBFH-DwUs74RB.js +1 -0
  32. package/web-dist/assets/{cynefin-VYW2F7L2-CR69iDgw.js → cynefin-VYW2F7L2-lo8PqVCk.js} +1 -1
  33. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-Le_FSC65.js → cynefinDiagram-TSTJHNR4-BtQLaDV0.js} +1 -1
  34. package/web-dist/assets/{dagre-VKFMJZFB-CxAlFDI9.js → dagre-VKFMJZFB-BJ2-f-A0.js} +1 -1
  35. package/web-dist/assets/{diagram-FQU43EPY-BFMwv6Pj.js → diagram-FQU43EPY-CLM-8r74.js} +1 -1
  36. package/web-dist/assets/{diagram-G47NLZAW-mWAc5hJ0.js → diagram-G47NLZAW-CfuoJGpN.js} +1 -1
  37. package/web-dist/assets/{diagram-NH7WQ7WH-CC-QXuir.js → diagram-NH7WQ7WH-DfxxRwH8.js} +1 -1
  38. package/web-dist/assets/{diagram-OA4YK3LP-Cqs5o39q.js → diagram-OA4YK3LP-D7knxZee.js} +1 -1
  39. package/web-dist/assets/{diagram-WEI45ONY-B9PF6IpG.js → diagram-WEI45ONY-C2WfMXWl.js} +1 -1
  40. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-C1DkTGUE.js → ebnfDiagram-CCIWWBDH-DRSZ24tF.js} +1 -1
  41. package/web-dist/assets/{erDiagram-Q63AITRT-CbCG5w3K.js → erDiagram-Q63AITRT-DXe0OrbY.js} +1 -1
  42. package/web-dist/assets/eventmodeling-45OFAUF4-CUIjQ7kE.js +1 -0
  43. package/web-dist/assets/flowDiagram-23GEKE2U-Dka-3F3n.js +1 -0
  44. package/web-dist/assets/{ganttDiagram-NO4QXBWP-CaywKz56.js → ganttDiagram-NO4QXBWP-wGqOgjvs.js} +1 -1
  45. package/web-dist/assets/{gitGraph-TEB2WS4Q-DhbijOBs.js → gitGraph-TEB2WS4Q-BygokG6y.js} +1 -1
  46. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-DHngf1QZ.js → gitGraphDiagram-IHSO6WYX-CV1933_M.js} +1 -1
  47. package/web-dist/assets/index-CmrDpsD8.css +1 -0
  48. package/web-dist/assets/{index-CxwWndgM.js → index-DB1d3eMJ.js} +107 -102
  49. package/web-dist/assets/{info-DKCQHKI2-D2uo8Yq_.js → info-DKCQHKI2-DLavbzui.js} +1 -1
  50. package/web-dist/assets/{infoDiagram-FWYZ7A6U-Bixn1mvJ.js → infoDiagram-FWYZ7A6U-Biouxd9G.js} +1 -1
  51. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-DG48ASOp.js → ishikawaDiagram-FXEZZL3T-DJxwwS3R.js} +1 -1
  52. package/web-dist/assets/{journeyDiagram-5HDEW3XC-DpQOwYAS.js → journeyDiagram-5HDEW3XC-Clx_WkdW.js} +1 -1
  53. package/web-dist/assets/{kanban-definition-HUTT4EX6-Dph0AMZo.js → kanban-definition-HUTT4EX6-Bf7bX_96.js} +1 -1
  54. package/web-dist/assets/{line-hnYRfUMr.js → line-CV2v49IN.js} +1 -1
  55. package/web-dist/assets/{mermaid-parser.core-DdD3ClpS.js → mermaid-parser.core-BPsFhEi6.js} +3 -3
  56. package/web-dist/assets/{mermaid.core-C2DMq9ss.js → mermaid.core-0EWDNL_R.js} +3 -3
  57. package/web-dist/assets/{mindmap-definition-LN4V7U3C-CirJBG-x.js → mindmap-definition-LN4V7U3C-C1y4yy3Z.js} +1 -1
  58. package/web-dist/assets/{packet-7NZHBO7P-kD-OtFAF.js → packet-7NZHBO7P-CpcpxruB.js} +1 -1
  59. package/web-dist/assets/{pegDiagram-2B236MQR-DGmi8fl-.js → pegDiagram-2B236MQR-CVlVP4cH.js} +1 -1
  60. package/web-dist/assets/{pie-RZYD4A2V-DpKplj-N.js → pie-RZYD4A2V-BcVPOhTZ.js} +1 -1
  61. package/web-dist/assets/{pieDiagram-ENE6RG2P-CW-zVLyR.js → pieDiagram-ENE6RG2P-DZv4Tfae.js} +1 -1
  62. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-fxSaIvo2.js → quadrantDiagram-ABIIQ3AL-DOzS_U_a.js} +1 -1
  63. package/web-dist/assets/{radar-I7S5WNFK-BPu7ClHP.js → radar-I7S5WNFK-8YADxkdT.js} +1 -1
  64. package/web-dist/assets/{railroad-3IZDKUUU-CoQXFYm6.js → railroad-3IZDKUUU-DzndPrRy.js} +1 -1
  65. package/web-dist/assets/railroad-abnf-AHOZXSZD-CCnWa6K9.js +1 -0
  66. package/web-dist/assets/railroad-ebnf-EBAXGLYW-jtVdzv7e.js +1 -0
  67. package/web-dist/assets/railroad-peg-LSFZ7HO6-B8D6TyEe.js +1 -0
  68. package/web-dist/assets/{railroadDiagram-RFXS5EU6-Bqnz_3Bc.js → railroadDiagram-RFXS5EU6-CPJAH-r7.js} +1 -1
  69. package/web-dist/assets/{requirementDiagram-TGXJPOKE-DJHMdBhh.js → requirementDiagram-TGXJPOKE-BVFJZfwl.js} +1 -1
  70. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-BlxzmT-W.js → sankeyDiagram-HTMAVEWB-CUhvFPmE.js} +1 -1
  71. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-BT7Rn1Uo.js → sequenceDiagram-DBY2YBRQ-Cy9CGhNq.js} +1 -1
  72. package/web-dist/assets/{stateDiagram-2N3HPSRC-CDoiFcke.js → stateDiagram-2N3HPSRC-CqOnmPLp.js} +1 -1
  73. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-DWKW-4qu.js +1 -0
  74. package/web-dist/assets/{swimlanes-5IMT3BWC-DtzTgSSP.js → swimlanes-5IMT3BWC-c_zBqkb9.js} +1 -1
  75. package/web-dist/assets/swimlanesDiagram-G3AALYLV-DZKoB7ae.js +8 -0
  76. package/web-dist/assets/{timeline-definition-FHXFAJF6-Dn63VmSO.js → timeline-definition-FHXFAJF6-y_QM62SK.js} +1 -1
  77. package/web-dist/assets/{treeView-QDETBFTQ-DIfJIc9R.js → treeView-QDETBFTQ-T3y_1KXV.js} +1 -1
  78. package/web-dist/assets/{treemap-6X3UGDF4-n9Fwqngl.js → treemap-6X3UGDF4-CzvqucqY.js} +1 -1
  79. package/web-dist/assets/{vennDiagram-L72KCM5P-C1thEMxE.js → vennDiagram-L72KCM5P-6-HNOpN0.js} +1 -1
  80. package/web-dist/assets/{wardley-OPB4EBWU-Dl6RiScD.js → wardley-OPB4EBWU-BrsaqsLL.js} +1 -1
  81. package/web-dist/assets/{wardleyDiagram-EHGQE667-DBelgTwB.js → wardleyDiagram-EHGQE667-Cr_rCzwd.js} +1 -1
  82. package/web-dist/assets/{xychartDiagram-FW5EYKEG-DFGVnPLj.js → xychartDiagram-FW5EYKEG-BnX6ccx4.js} +1 -1
  83. package/web-dist/index.html +2 -2
  84. package/web-dist/assets/architecture-TIHT7OUA-BS8x9TQa.js +0 -1
  85. package/web-dist/assets/channel-DHZV3ADN.js +0 -1
  86. package/web-dist/assets/classDiagram-OUVF2IWQ-Dcx3TDdU.js +0 -1
  87. package/web-dist/assets/classDiagram-v2-EOCWNBFH-Dcx3TDdU.js +0 -1
  88. package/web-dist/assets/eventmodeling-45OFAUF4-DAhP7KCX.js +0 -1
  89. package/web-dist/assets/flowDiagram-23GEKE2U-Be2iuqwq.js +0 -1
  90. package/web-dist/assets/index-C-3lsl4q.css +0 -1
  91. package/web-dist/assets/railroad-abnf-AHOZXSZD-BiqkuXNr.js +0 -1
  92. package/web-dist/assets/railroad-ebnf-EBAXGLYW-CDaukY4Z.js +0 -1
  93. package/web-dist/assets/railroad-peg-LSFZ7HO6-CsUPdvVL.js +0 -1
  94. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-DiLnjG8k.js +0 -1
  95. package/web-dist/assets/swimlanesDiagram-G3AALYLV-BqvWOcR2.js +0 -8
package/dist/dev-child.js CHANGED
@@ -12905,6 +12905,16 @@ var init_receipt_bus = __esm({
12905
12905
  }
12906
12906
  });
12907
12907
 
12908
+ // packages/shared/src/relay-protocol.ts
12909
+ var RELAY_MAX_FRAME_BODY, RELAY_MAX_WS_CHUNK;
12910
+ var init_relay_protocol = __esm({
12911
+ "packages/shared/src/relay-protocol.ts"() {
12912
+ "use strict";
12913
+ RELAY_MAX_FRAME_BODY = 512 * 1024;
12914
+ RELAY_MAX_WS_CHUNK = 64 * 1024;
12915
+ }
12916
+ });
12917
+
12908
12918
  // packages/shared/src/index.ts
12909
12919
  function attachmentExtension(name) {
12910
12920
  const m = /\.([a-zA-Z0-9]+)$/.exec(name.trim());
@@ -13267,6 +13277,7 @@ var init_src = __esm({
13267
13277
  init_drainable_worker();
13268
13278
  init_interactions();
13269
13279
  init_receipt_bus();
13280
+ init_relay_protocol();
13270
13281
  init_thread_slug();
13271
13282
  ATTACHMENT_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"];
13272
13283
  ATTACHMENT_DOC_EXTENSIONS = [
@@ -19507,40 +19518,29 @@ var init_storage = __esm({
19507
19518
  }
19508
19519
  });
19509
19520
 
19510
- // packages/server/src/settings.ts
19521
+ // packages/server/src/machine-config.ts
19511
19522
  import { closeSync as closeSync5, fsyncSync as fsyncSync5, mkdirSync as mkdirSync7, openSync as openSync6, readFileSync as readFileSync6, renameSync as renameSync5, rmSync as rmSync6, writeFileSync as writeFileSync6 } from "node:fs";
19512
19523
  import { dirname as dirname6, join as join9 } from "node:path";
19513
- function machineSettingsPath(home) {
19514
- return join9(frizzPaths({ home }).data, "settings.json");
19515
- }
19516
- function pickMachine(settings) {
19517
- return Object.fromEntries(MACHINE_KEYS.map((key) => [key, settings[key]]));
19524
+ function machineConfigPath(home) {
19525
+ return join9(frizzPaths({ home }).data, "config.json");
19518
19526
  }
19519
- function readMachineSettings(home) {
19527
+ function readAll(home) {
19520
19528
  let raw2;
19521
19529
  try {
19522
- raw2 = JSON.parse(readFileSync6(machineSettingsPath(home), "utf8"));
19530
+ raw2 = JSON.parse(readFileSync6(machineConfigPath(home), "utf8"));
19523
19531
  } catch {
19524
19532
  return {};
19525
19533
  }
19526
- if (!raw2 || typeof raw2 !== "object") return {};
19527
- const parsed = Settings.partial().safeParse(raw2);
19528
- if (!parsed.success) return {};
19529
- return pickPresent(parsed.data);
19530
- }
19531
- function pickPresent(partial) {
19532
- const out = {};
19533
- for (const key of MACHINE_KEYS) if (partial[key] !== void 0) Object.assign(out, { [key]: partial[key] });
19534
- return out;
19534
+ return raw2 && typeof raw2 === "object" && !Array.isArray(raw2) ? raw2 : {};
19535
19535
  }
19536
- function writeMachineSettings(next, home) {
19537
- const path = machineSettingsPath(home);
19536
+ function writeAll(home, all) {
19537
+ const path = machineConfigPath(home);
19538
19538
  mkdirSync7(dirname6(path), { recursive: true });
19539
19539
  const temp = `${path}.${process.pid}.tmp`;
19540
19540
  let fd;
19541
19541
  try {
19542
19542
  fd = openSync6(temp, "w", 384);
19543
- writeFileSync6(fd, `${JSON.stringify(next, null, 2)}
19543
+ writeFileSync6(fd, `${JSON.stringify(all, null, 2)}
19544
19544
  `, "utf8");
19545
19545
  fsyncSync5(fd);
19546
19546
  closeSync5(fd);
@@ -19557,6 +19557,58 @@ function writeMachineSettings(next, home) {
19557
19557
  }
19558
19558
  }
19559
19559
  }
19560
+ function readMachineConfig(home, key, schema) {
19561
+ const value = readAll(home)[key];
19562
+ if (value === void 0) return void 0;
19563
+ const parsed = schema.safeParse(value);
19564
+ return parsed.success ? parsed.data : void 0;
19565
+ }
19566
+ function writeMachineConfig(home, key, value) {
19567
+ writeAll(home, { ...readAll(home), [key]: value });
19568
+ }
19569
+ function deleteMachineConfig(home, key) {
19570
+ const all = readAll(home);
19571
+ if (!(key in all)) return;
19572
+ delete all[key];
19573
+ writeAll(home, all);
19574
+ }
19575
+ var init_machine_config = __esm({
19576
+ "packages/server/src/machine-config.ts"() {
19577
+ "use strict";
19578
+ init_frizz_paths();
19579
+ }
19580
+ });
19581
+
19582
+ // packages/server/src/settings.ts
19583
+ import { readFileSync as readFileSync7, rmSync as rmSync7 } from "node:fs";
19584
+ import { join as join10 } from "node:path";
19585
+ function legacyMachineSettingsPath(home) {
19586
+ return join10(frizzPaths({ home }).data, "settings.json");
19587
+ }
19588
+ function pickMachine(settings) {
19589
+ return Object.fromEntries(MACHINE_KEYS.map((key) => [key, settings[key]]));
19590
+ }
19591
+ function readMachineSettings(home) {
19592
+ const stored = readMachineConfig(home, SETTINGS_KEY, MachineSettingsRecord);
19593
+ if (stored) return pickPresent(stored);
19594
+ let raw2;
19595
+ try {
19596
+ raw2 = JSON.parse(readFileSync7(legacyMachineSettingsPath(home), "utf8"));
19597
+ } catch {
19598
+ return {};
19599
+ }
19600
+ if (!raw2 || typeof raw2 !== "object") return {};
19601
+ const parsed = MachineSettingsRecord.safeParse(raw2);
19602
+ return parsed.success ? pickPresent(parsed.data) : {};
19603
+ }
19604
+ function pickPresent(partial) {
19605
+ const out = {};
19606
+ for (const key of MACHINE_KEYS) if (partial[key] !== void 0) Object.assign(out, { [key]: partial[key] });
19607
+ return out;
19608
+ }
19609
+ function writeMachineSettings(next, home) {
19610
+ writeMachineConfig(home, SETTINGS_KEY, next);
19611
+ }
19560
19612
  function getSettings(storage, home) {
19561
19613
  const raw2 = storage.getSetting(SETTINGS_KEY);
19562
19614
  const project = raw2 === void 0 ? defaultSettings() : Settings.safeParse({ ...defaultSettings(), ...raw2 }).data ?? defaultSettings();
@@ -19570,20 +19622,23 @@ function setSettings(storage, next, home) {
19570
19622
  }
19571
19623
  function resetSettings(storage, home) {
19572
19624
  storage.deleteSetting(SETTINGS_KEY);
19625
+ deleteMachineConfig(home, SETTINGS_KEY);
19573
19626
  try {
19574
- rmSync6(machineSettingsPath(home), { force: true });
19627
+ rmSync7(legacyMachineSettingsPath(home), { force: true });
19575
19628
  } catch {
19576
19629
  }
19577
19630
  return defaultSettings();
19578
19631
  }
19579
- var SETTINGS_KEY, MACHINE_KEYS, defaultSettings;
19632
+ var SETTINGS_KEY, MACHINE_KEYS, MachineSettingsRecord, defaultSettings;
19580
19633
  var init_settings = __esm({
19581
19634
  "packages/server/src/settings.ts"() {
19582
19635
  "use strict";
19583
19636
  init_src();
19584
19637
  init_frizz_paths();
19638
+ init_machine_config();
19585
19639
  SETTINGS_KEY = "settings";
19586
19640
  MACHINE_KEYS = ["font", "notifications", "localFileOpener", "projectRail"];
19641
+ MachineSettingsRecord = Settings.partial();
19587
19642
  defaultSettings = () => ({
19588
19643
  // `bypassPermissions` = claude's --dangerously-skip-permissions: a headless worker never stops on an
19589
19644
  // approval card. Shipped default since 0.7.2 (maintainer 2026-08-24); the Settings "Permissions"
@@ -19603,9 +19658,78 @@ var init_settings = __esm({
19603
19658
  }
19604
19659
  });
19605
19660
 
19661
+ // packages/server/src/dispatch-preferences.ts
19662
+ function readMachineDispatchPreferences(home) {
19663
+ return readMachineConfig(home, MACHINE_KEY, DispatchPreferences);
19664
+ }
19665
+ function permissionFor(backend, mode) {
19666
+ if (backend === "codex") {
19667
+ if (mode === "plan" || mode === "bypassPermissions") return mode;
19668
+ return "default";
19669
+ }
19670
+ return mode === "plan" ? "auto" : mode;
19671
+ }
19672
+ function defaultDispatchPreferences(settings, codexModels = []) {
19673
+ const backend = settings.backend ?? (codexModels.some((model) => model.slug === settings.model) ? "codex" : "claude");
19674
+ const selected = {
19675
+ ...settings.model ? { model: settings.model } : {},
19676
+ ...settings.effort ? { effort: settings.effort } : {},
19677
+ permissionMode: permissionFor(backend, settings.permissionMode)
19678
+ };
19679
+ return {
19680
+ backend,
19681
+ claude: backend === "claude" ? selected : { permissionMode: "auto" },
19682
+ codex: backend === "codex" ? selected : { permissionMode: "default" }
19683
+ };
19684
+ }
19685
+ function getDispatchPreferences(storage, settings, home, codexModels = []) {
19686
+ const machine = readMachineDispatchPreferences(home);
19687
+ if (machine) return machine;
19688
+ const parsed = DispatchPreferences.safeParse(storage.getSetting(KEY));
19689
+ return parsed.success ? parsed.data : defaultDispatchPreferences(settings, codexModels);
19690
+ }
19691
+ function setDispatchPreference(storage, settings, home, update, codexModels = []) {
19692
+ const current = getDispatchPreferences(storage, settings, home, codexModels);
19693
+ let next;
19694
+ if (update.field === "backend") {
19695
+ next = { ...current, backend: update.value };
19696
+ } else if (update.field === "profile") {
19697
+ next = {
19698
+ ...current,
19699
+ backend: update.backend,
19700
+ [update.backend]: {
19701
+ ...current[update.backend],
19702
+ model: update.model,
19703
+ effort: update.effort
19704
+ }
19705
+ };
19706
+ } else {
19707
+ const profile = current[update.backend];
19708
+ next = {
19709
+ ...current,
19710
+ ...update.field === "model" ? { backend: update.backend } : {},
19711
+ [update.backend]: { ...profile, [update.field]: update.value }
19712
+ };
19713
+ }
19714
+ const validated = DispatchPreferences.parse(next);
19715
+ writeMachineConfig(home, MACHINE_KEY, validated);
19716
+ storage.setSetting(KEY, validated);
19717
+ return validated;
19718
+ }
19719
+ var KEY, MACHINE_KEY;
19720
+ var init_dispatch_preferences = __esm({
19721
+ "packages/server/src/dispatch-preferences.ts"() {
19722
+ "use strict";
19723
+ init_src();
19724
+ init_machine_config();
19725
+ KEY = "dispatch-preferences.v1";
19726
+ MACHINE_KEY = "dispatchPreferences";
19727
+ }
19728
+ });
19729
+
19606
19730
  // packages/server/src/discover.ts
19607
19731
  import { readdirSync as readdirSync3, statSync as statSync4, openSync as openSync7, readSync, closeSync as closeSync6 } from "node:fs";
19608
- import { dirname as dirname7, join as join10 } from "node:path";
19732
+ import { dirname as dirname7, join as join11 } from "node:path";
19609
19733
  function sentinelFor(sessionId) {
19610
19734
  return `threads/${sessionId}/`;
19611
19735
  }
@@ -19642,7 +19766,7 @@ function discoverTranscriptId(logDir, sessionId, opts = {}) {
19642
19766
  if (name.startsWith(".") || !name.endsWith(".jsonl")) continue;
19643
19767
  const id = name.slice(0, -".jsonl".length);
19644
19768
  if (!id || id === sessionId || exclude?.has(id)) continue;
19645
- const path = join10(logDir, name);
19769
+ const path = join11(logDir, name);
19646
19770
  let mtime;
19647
19771
  try {
19648
19772
  mtime = statSync4(path).mtimeMs;
@@ -19670,7 +19794,7 @@ function discoverTranscriptDir(logDir, sessionId, memo3 = strandedLogDirs) {
19670
19794
  const name = `${sessionId}.jsonl`;
19671
19795
  const better = (best2, dir) => {
19672
19796
  if (dir === logDir) return best2;
19673
- const at = mtimeOfNonEmpty(join10(dir, name));
19797
+ const at = mtimeOfNonEmpty(join11(dir, name));
19674
19798
  if (at === void 0) return best2;
19675
19799
  return !best2 || at > best2.mtimeMs ? { dir, mtimeMs: at } : best2;
19676
19800
  };
@@ -19683,7 +19807,7 @@ function discoverTranscriptDir(logDir, sessionId, memo3 = strandedLogDirs) {
19683
19807
  } catch {
19684
19808
  return void 0;
19685
19809
  }
19686
- for (const entry of entries) best = better(best, join10(dirname7(logDir), entry));
19810
+ for (const entry of entries) best = better(best, join11(dirname7(logDir), entry));
19687
19811
  if (!best) return void 0;
19688
19812
  memo3.add(best.dir);
19689
19813
  return best.dir;
@@ -19701,11 +19825,11 @@ var init_discover = __esm({
19701
19825
  });
19702
19826
 
19703
19827
  // packages/server/src/session-files.ts
19704
- import { lstatSync, rmSync as rmSync7 } from "node:fs";
19705
- import { join as join11 } from "node:path";
19828
+ import { lstatSync, rmSync as rmSync8 } from "node:fs";
19829
+ import { join as join12 } from "node:path";
19706
19830
  function systemPromptPath(sessionId) {
19707
19831
  if (!SESSION_ID_RE.test(sessionId)) throw new Error("invalid session id");
19708
- return join11(SYSTEM_PROMPT_DIR, `${sessionId}.md`);
19832
+ return join12(SYSTEM_PROMPT_DIR, `${sessionId}.md`);
19709
19833
  }
19710
19834
  function isDirectDirectory(path) {
19711
19835
  try {
@@ -19725,9 +19849,9 @@ function pathAbsent(path) {
19725
19849
  }
19726
19850
  function unlinkDirectChild(parent, filename) {
19727
19851
  if (!isDirectDirectory(parent)) return pathAbsent(parent);
19728
- const child = join11(parent, filename);
19852
+ const child = join12(parent, filename);
19729
19853
  try {
19730
- rmSync7(child, { force: true });
19854
+ rmSync8(child, { force: true });
19731
19855
  } catch {
19732
19856
  return false;
19733
19857
  }
@@ -19735,14 +19859,14 @@ function unlinkDirectChild(parent, filename) {
19735
19859
  }
19736
19860
  function cleanupAdoptionSessionFiles(projectDir, sessionId) {
19737
19861
  if (!SESSION_ID_RE.test(sessionId)) return false;
19738
- const frizzDir = join11(projectDir, ".frizz");
19862
+ const frizzDir = join12(projectDir, ".frizz");
19739
19863
  let clean = true;
19740
19864
  if (isDirectDirectory(frizzDir)) {
19741
- const threads = join11(frizzDir, "threads");
19865
+ const threads = join12(frizzDir, "threads");
19742
19866
  if (isDirectDirectory(threads)) {
19743
- const child = join11(threads, sessionId);
19867
+ const child = join12(threads, sessionId);
19744
19868
  try {
19745
- rmSync7(child, { recursive: true, force: true });
19869
+ rmSync8(child, { recursive: true, force: true });
19746
19870
  } catch {
19747
19871
  clean = false;
19748
19872
  }
@@ -19992,10 +20116,10 @@ var init_adoption_recovery = __esm({
19992
20116
  });
19993
20117
 
19994
20118
  // packages/server/src/backend/codex-models.ts
19995
- import { join as join12 } from "node:path";
19996
- import { readFileSync as readFileSync7 } from "node:fs";
20119
+ import { join as join13 } from "node:path";
20120
+ import { readFileSync as readFileSync8 } from "node:fs";
19997
20121
  function cachePath(codexHome) {
19998
- return join12(codexHome, "models_cache.json");
20122
+ return join13(codexHome, "models_cache.json");
19999
20123
  }
20000
20124
  function toCodexModel(raw2) {
20001
20125
  if (!raw2 || typeof raw2 !== "object") return void 0;
@@ -20031,7 +20155,7 @@ function readCodexModels(codexHome = defaultCodexHome()) {
20031
20155
  if (hit && now - hit.at < TTL_MS) return hit.models;
20032
20156
  let models;
20033
20157
  try {
20034
- models = parseCodexModelsCache(readFileSync7(path, "utf8"));
20158
+ models = parseCodexModelsCache(readFileSync8(path, "utf8"));
20035
20159
  } catch {
20036
20160
  models = CODEX_MODELS_FALLBACK;
20037
20161
  }
@@ -20573,10 +20697,10 @@ var init_claude_agent_sdk_protocol = __esm({
20573
20697
  });
20574
20698
 
20575
20699
  // packages/server/src/backend/claude-broker-diagnostics.ts
20576
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync8, renameSync as renameSync6, statSync as statSync5 } from "node:fs";
20577
- import { join as join13 } from "node:path";
20700
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync6, statSync as statSync5 } from "node:fs";
20701
+ import { join as join14 } from "node:path";
20578
20702
  function claudeBrokerDiagnosticLogPath(stateDir, sessionId) {
20579
- return join13(stateDir, "claude-broker", `${sessionId}.diagnostics.log`);
20703
+ return join14(stateDir, "claude-broker", `${sessionId}.diagnostics.log`);
20580
20704
  }
20581
20705
  function readClaudeBrokerExit(stateDir, sessionId, generation) {
20582
20706
  if (!generation) return null;
@@ -20584,7 +20708,7 @@ function readClaudeBrokerExit(stateDir, sessionId, generation) {
20584
20708
  for (const path of [`${claudeBrokerDiagnosticLogPath(stateDir, sessionId)}.1`, claudeBrokerDiagnosticLogPath(stateDir, sessionId)]) {
20585
20709
  let text;
20586
20710
  try {
20587
- text = readFileSync8(path, "utf8");
20711
+ text = readFileSync9(path, "utf8");
20588
20712
  } catch {
20589
20713
  continue;
20590
20714
  }
@@ -20648,10 +20772,10 @@ var init_detached_daemons = __esm({
20648
20772
  });
20649
20773
 
20650
20774
  // packages/server/src/backend/ipc-path.ts
20651
- import { join as join14 } from "node:path";
20775
+ import { join as join15 } from "node:path";
20652
20776
  function frizzIpcPath(name) {
20653
20777
  if (process.platform === "win32") return `\\\\.\\pipe\\${name}`;
20654
- return join14(process.env.TMPDIR ?? "/tmp", `${name}.sock`);
20778
+ return join15(process.env.TMPDIR ?? "/tmp", `${name}.sock`);
20655
20779
  }
20656
20780
  var init_ipc_path = __esm({
20657
20781
  "packages/server/src/backend/ipc-path.ts"() {
@@ -20662,18 +20786,18 @@ var init_ipc_path = __esm({
20662
20786
  // packages/server/src/backend/claude-broker-host.ts
20663
20787
  import { spawn } from "node:child_process";
20664
20788
  import { createHash as createHash5, randomUUID as randomUUID8 } from "node:crypto";
20665
- import { accessSync, constants as fsConstants, mkdirSync as mkdirSync9, readFileSync as readFileSync9, readdirSync as readdirSync4, unlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
20666
- import { delimiter, dirname as dirname8, isAbsolute as isAbsolute2, join as join15 } from "node:path";
20789
+ import { accessSync, constants as fsConstants, mkdirSync as mkdirSync9, readFileSync as readFileSync10, readdirSync as readdirSync4, unlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
20790
+ import { delimiter, dirname as dirname8, isAbsolute as isAbsolute2, join as join16 } from "node:path";
20667
20791
  function windowsShimTarget(shimPath) {
20668
20792
  let body;
20669
20793
  try {
20670
- body = readFileSync9(shimPath, "utf8");
20794
+ body = readFileSync10(shimPath, "utf8");
20671
20795
  } catch {
20672
20796
  return void 0;
20673
20797
  }
20674
20798
  const target = WINDOWS_SHIM_TARGET.exec(body)?.[1];
20675
20799
  if (!target) return void 0;
20676
- const full = join15(dirname8(shimPath), target);
20800
+ const full = join16(dirname8(shimPath), target);
20677
20801
  try {
20678
20802
  accessSync(full, fsConstants.F_OK);
20679
20803
  return full;
@@ -20694,17 +20818,17 @@ function resolveClaudeExecutableAbsolute(bin, env = process.env) {
20694
20818
  for (const dir of searchPath(env).split(delimiter)) {
20695
20819
  if (!dir) continue;
20696
20820
  if (windows) {
20697
- const exe = join15(dir, `${candidate}.exe`);
20821
+ const exe = join16(dir, `${candidate}.exe`);
20698
20822
  try {
20699
20823
  accessSync(exe, fsConstants.F_OK);
20700
20824
  return exe;
20701
20825
  } catch {
20702
20826
  }
20703
- const viaShim = windowsShimTarget(join15(dir, `${candidate}.cmd`));
20827
+ const viaShim = windowsShimTarget(join16(dir, `${candidate}.cmd`));
20704
20828
  if (viaShim) return viaShim;
20705
20829
  continue;
20706
20830
  }
20707
- const full = join15(dir, candidate);
20831
+ const full = join16(dir, candidate);
20708
20832
  try {
20709
20833
  accessSync(full, fsConstants.X_OK);
20710
20834
  return full;
@@ -20719,11 +20843,11 @@ function claudeBrokerSocketPath(stateDir, sessionId) {
20719
20843
  }
20720
20844
  function claudeBrokerRecordPath(stateDir, sessionId) {
20721
20845
  const key = createHash5("sha256").update(sessionId).digest("hex").slice(0, 16);
20722
- return join15(stateDir, "claude-broker", `${key}.json`);
20846
+ return join16(stateDir, "claude-broker", `${key}.json`);
20723
20847
  }
20724
20848
  function claudeBrokerRetirementPath(stateDir, sessionId) {
20725
20849
  const key = createHash5("sha256").update(sessionId).digest("hex").slice(0, 16);
20726
- return join15(stateDir, "claude-broker", `${key}.retired`);
20850
+ return join16(stateDir, "claude-broker", `${key}.retired`);
20727
20851
  }
20728
20852
  function markBrokerRetired(stateDir, sessionId, reason, generation) {
20729
20853
  const mark = { at: (/* @__PURE__ */ new Date()).toISOString(), generation, reason };
@@ -20738,7 +20862,7 @@ function takeBrokerRetirement(stateDir, sessionId) {
20738
20862
  const path = claudeBrokerRetirementPath(stateDir, sessionId);
20739
20863
  let mark = null;
20740
20864
  try {
20741
- const parsed = JSON.parse(readFileSync9(path, "utf8"));
20865
+ const parsed = JSON.parse(readFileSync10(path, "utf8"));
20742
20866
  if (typeof parsed?.at === "string" && typeof parsed.generation === "string" && typeof parsed.reason === "string") {
20743
20867
  mark = { at: parsed.at, generation: parsed.generation, reason: parsed.reason };
20744
20868
  }
@@ -20760,7 +20884,7 @@ function pidAlive(pid) {
20760
20884
  }
20761
20885
  function readBrokerRecord(recordPath2) {
20762
20886
  try {
20763
- return JSON.parse(readFileSync9(recordPath2, "utf8"));
20887
+ return JSON.parse(readFileSync10(recordPath2, "utf8"));
20764
20888
  } catch {
20765
20889
  return null;
20766
20890
  }
@@ -20790,7 +20914,7 @@ function liveBrokerRecord(recordPath2) {
20790
20914
  return null;
20791
20915
  }
20792
20916
  function liveBrokerRecords(stateDir) {
20793
- const dir = join15(stateDir, "claude-broker");
20917
+ const dir = join16(stateDir, "claude-broker");
20794
20918
  let names;
20795
20919
  try {
20796
20920
  names = readdirSync4(dir);
@@ -20800,7 +20924,7 @@ function liveBrokerRecords(stateDir) {
20800
20924
  const out = [];
20801
20925
  for (const name of names) {
20802
20926
  if (!name.endsWith(".json")) continue;
20803
- const record = liveBrokerRecord(join15(dir, name));
20927
+ const record = liveBrokerRecord(join16(dir, name));
20804
20928
  if (record) out.push(record);
20805
20929
  }
20806
20930
  return out;
@@ -21608,15 +21732,15 @@ var init_completion_relay = __esm({
21608
21732
 
21609
21733
  // packages/server/src/tail-cache.ts
21610
21734
  import { createHash as createHash6 } from "node:crypto";
21611
- import { closeSync as closeSync8, openSync as openSync9, readFileSync as readFileSync10, readSync as readSync3, statSync as statSync7 } from "node:fs";
21612
- import { join as join16 } from "node:path";
21735
+ import { closeSync as closeSync8, openSync as openSync9, readFileSync as readFileSync11, readSync as readSync3, statSync as statSync7 } from "node:fs";
21736
+ import { join as join17 } from "node:path";
21613
21737
  function foldSchemaDigest(dir = import.meta.dirname) {
21614
21738
  if (foldSchemaMemo) return foldSchemaMemo;
21615
21739
  const hash = createHash6("sha256").update("frizz-tail-state-v1\0");
21616
21740
  let read = 0;
21617
21741
  for (const name of FOLD_SOURCES) {
21618
21742
  try {
21619
- hash.update(name).update("\0").update(readFileSync10(join16(dir, name)));
21743
+ hash.update(name).update("\0").update(readFileSync11(join17(dir, name)));
21620
21744
  read++;
21621
21745
  } catch {
21622
21746
  hash.update(name).update("\0missing\0");
@@ -21812,9 +21936,9 @@ var init_tail_cache = __esm({
21812
21936
  });
21813
21937
 
21814
21938
  // packages/server/src/tailer.ts
21815
- import { statSync as statSync8, openSync as openSync10, readSync as readSync4, closeSync as closeSync9, readdirSync as readdirSync5, realpathSync as realpathSync5, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, readFileSync as readFileSync11, existsSync as existsSync5 } from "node:fs";
21939
+ import { statSync as statSync8, openSync as openSync10, readSync as readSync4, closeSync as closeSync9, readdirSync as readdirSync5, realpathSync as realpathSync5, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, readFileSync as readFileSync12, existsSync as existsSync5 } from "node:fs";
21816
21940
  import { execFileSync as execFileSync4 } from "node:child_process";
21817
- import { basename as basename4, join as join17 } from "node:path";
21941
+ import { basename as basename4, join as join18 } from "node:path";
21818
21942
  import { homedir as homedir7 } from "node:os";
21819
21943
  function probeShellAlive(outputFile) {
21820
21944
  if (!existsSync5(outputFile)) return void 0;
@@ -22076,7 +22200,7 @@ function launchTaskId(text) {
22076
22200
  function readDescendantSidecars(sessionDir, mtimeMs) {
22077
22201
  let names;
22078
22202
  try {
22079
- names = readdirSync5(join17(sessionDir, "subagents"));
22203
+ names = readdirSync5(join18(sessionDir, "subagents"));
22080
22204
  } catch {
22081
22205
  return [];
22082
22206
  }
@@ -22087,7 +22211,7 @@ function readDescendantSidecars(sessionDir, mtimeMs) {
22087
22211
  if (!agentId) continue;
22088
22212
  let parsed;
22089
22213
  try {
22090
- parsed = JSON.parse(readFileSync11(join17(sessionDir, "subagents", name), "utf8"));
22214
+ parsed = JSON.parse(readFileSync12(join18(sessionDir, "subagents", name), "utf8"));
22091
22215
  } catch {
22092
22216
  continue;
22093
22217
  }
@@ -22101,7 +22225,7 @@ function readDescendantSidecars(sessionDir, mtimeMs) {
22101
22225
  agentType: text(meta.agentType),
22102
22226
  parentAgentId: text(meta.parentAgentId),
22103
22227
  spawnDepth: typeof meta.spawnDepth === "number" && Number.isFinite(meta.spawnDepth) ? meta.spawnDepth : void 0,
22104
- spawnedAtMs: mtimeMs(join17(sessionDir, "subagents", name))
22228
+ spawnedAtMs: mtimeMs(join18(sessionDir, "subagents", name))
22105
22229
  });
22106
22230
  }
22107
22231
  return out;
@@ -22581,13 +22705,13 @@ function defaultMtimeMs(path) {
22581
22705
  }
22582
22706
  }
22583
22707
  function defaultLogDir(project) {
22584
- return join17(homedir7(), ".claude", "projects", project.cwdSlug);
22708
+ return join18(homedir7(), ".claude", "projects", project.cwdSlug);
22585
22709
  }
22586
22710
  function defaultReadPermMarker(project) {
22587
22711
  if (!project.stateDir) return () => void 0;
22588
22712
  return (slug) => {
22589
22713
  try {
22590
- const parsed = JSON.parse(readFileSync11(permMarkerPath(project, slug), "utf8"));
22714
+ const parsed = JSON.parse(readFileSync12(permMarkerPath(project, slug), "utf8"));
22591
22715
  return isPermMarker(parsed) ? parsed : void 0;
22592
22716
  } catch {
22593
22717
  return void 0;
@@ -22713,7 +22837,7 @@ function createTailer(deps) {
22713
22837
  return paneDead(row.slug);
22714
22838
  }
22715
22839
  const defaultBackend = {
22716
- transcriptPath: (sessionId) => join17(logDir, `${sessionId}.jsonl`),
22840
+ transcriptPath: (sessionId) => join18(logDir, `${sessionId}.jsonl`),
22717
22841
  foldLine: (state, line) => {
22718
22842
  const rec = parseLine(line);
22719
22843
  if (rec) applyRecord(state, rec);
@@ -22838,7 +22962,7 @@ ${ask}`;
22838
22962
  return state.path.replace(/\.jsonl$/, "");
22839
22963
  }
22840
22964
  function descendantSidecars(state) {
22841
- const at = mtimeMs(join17(sessionDirOf(state), "subagents"));
22965
+ const at = mtimeMs(join18(sessionDirOf(state), "subagents"));
22842
22966
  const cached2 = descendantIndex.get(state.slug);
22843
22967
  if (cached2 && cached2.at === at) return cached2.all;
22844
22968
  const all = readDescendantSidecars(sessionDirOf(state), mtimeMs);
@@ -22852,7 +22976,7 @@ ${ask}`;
22852
22976
  return descendantIndex.get(state.slug)?.byToolUse.get(id);
22853
22977
  }
22854
22978
  function descendantTranscript(state, meta) {
22855
- return join17(sessionDirOf(state), "subagents", `agent-${meta.agentId}.jsonl`);
22979
+ return join18(sessionDirOf(state), "subagents", `agent-${meta.agentId}.jsonl`);
22856
22980
  }
22857
22981
  function subAgentDescendantTasks(slug, id) {
22858
22982
  const state = states.get(slug);
@@ -23166,7 +23290,7 @@ ${ask}`;
23166
23290
  if (name.startsWith(".") || !name.endsWith(".jsonl")) continue;
23167
23291
  const id = name.slice(0, -".jsonl".length);
23168
23292
  if (!id || registered.has(id)) continue;
23169
- const path = join17(logDir, name);
23293
+ const path = join18(logDir, name);
23170
23294
  let mtime;
23171
23295
  try {
23172
23296
  mtime = statSync8(path).mtimeMs;
@@ -23438,7 +23562,7 @@ ${ask}`;
23438
23562
  );
23439
23563
  try {
23440
23564
  mkdirSync10(stallLogDir, { recursive: true });
23441
- writeFileSync8(join17(stallLogDir, `${row.slug}.stall.log`), `session_id: ${row.session_id}
23565
+ writeFileSync8(join18(stallLogDir, `${row.slug}.stall.log`), `session_id: ${row.session_id}
23442
23566
  captured_at: ${new Date(now()).toISOString()}
23443
23567
 
23444
23568
  ${detail}
@@ -23466,7 +23590,7 @@ ${detail}
23466
23590
  state.nextDiscoverMs = nowMs + DISCOVER_RETRY_MS;
23467
23591
  const strandedDir = discoverTranscriptDir(logDir, row.session_id);
23468
23592
  if (strandedDir) {
23469
- state.path = join17(strandedDir, `${row.session_id}.jsonl`);
23593
+ state.path = join18(strandedDir, `${row.session_id}.jsonl`);
23470
23594
  state.offset = 0;
23471
23595
  state.partial = "";
23472
23596
  state.primed = false;
@@ -23489,7 +23613,7 @@ ${detail}
23489
23613
  committed = false;
23490
23614
  }
23491
23615
  if (!committed) return false;
23492
- state.path = join17(logDir, `${found}.jsonl`);
23616
+ state.path = join18(logDir, `${found}.jsonl`);
23493
23617
  state.offset = 0;
23494
23618
  state.partial = "";
23495
23619
  state.primed = false;
@@ -23535,7 +23659,7 @@ ${detail}
23535
23659
  let state = states.get(row.slug);
23536
23660
  const runtimeGeneration = row.runtime_generation ?? 0;
23537
23661
  if (!state || state.sessionId !== row.session_id || state.nativeSessionId !== nativeId || state.runtimeGeneration !== runtimeGeneration) {
23538
- const path = backend.transcriptPath(nativeId) ?? join17(logDir, `${nativeId}.jsonl`);
23662
+ const path = backend.transcriptPath(nativeId) ?? join18(logDir, `${nativeId}.jsonl`);
23539
23663
  state = newTailState(row.slug, row.session_id, path, false, nativeId, runtimeGeneration);
23540
23664
  state.dismissedOps = deps.storage.retiredOps(row.slug, row.session_id);
23541
23665
  hydrateFromCache(state, row, nativeId);
@@ -23984,14 +24108,14 @@ var init_tailer = __esm({
23984
24108
  });
23985
24109
 
23986
24110
  // packages/server/src/backend/codex.ts
23987
- import { join as join18 } from "node:path";
24111
+ import { join as join19 } from "node:path";
23988
24112
  import { homedir as homedir8 } from "node:os";
23989
- import { readdirSync as readdirSync6, statSync as statSync9, readFileSync as readFileSync12, openSync as openSync11, readSync as readSync5, closeSync as closeSync10 } from "node:fs";
24113
+ import { readdirSync as readdirSync6, statSync as statSync9, readFileSync as readFileSync13, openSync as openSync11, readSync as readSync5, closeSync as closeSync10 } from "node:fs";
23990
24114
  function defaultCodexHome() {
23991
- return process.env.CODEX_HOME && process.env.CODEX_HOME.trim() ? process.env.CODEX_HOME : join18(homedir8(), ".codex");
24115
+ return process.env.CODEX_HOME && process.env.CODEX_HOME.trim() ? process.env.CODEX_HOME : join19(homedir8(), ".codex");
23992
24116
  }
23993
24117
  function sessionsDir(codexHome) {
23994
- return join18(codexHome, "sessions");
24118
+ return join19(codexHome, "sessions");
23995
24119
  }
23996
24120
  function codexSandbox(mode) {
23997
24121
  switch (mode) {
@@ -24372,17 +24496,17 @@ function collectRollouts(dir, out, budget) {
24372
24496
  const files = entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).sort(descByName);
24373
24497
  for (const d of dirs) {
24374
24498
  if (budget.n <= 0) return;
24375
- collectRollouts(join18(dir, d.name), out, budget);
24499
+ collectRollouts(join19(dir, d.name), out, budget);
24376
24500
  }
24377
24501
  for (const f of files) {
24378
24502
  if (budget.n <= 0) return;
24379
24503
  let mtimeMs;
24380
24504
  try {
24381
- mtimeMs = statSync9(join18(dir, f.name)).mtimeMs;
24505
+ mtimeMs = statSync9(join19(dir, f.name)).mtimeMs;
24382
24506
  } catch {
24383
24507
  continue;
24384
24508
  }
24385
- out.push({ path: join18(dir, f.name), mtimeMs });
24509
+ out.push({ path: join19(dir, f.name), mtimeMs });
24386
24510
  budget.n--;
24387
24511
  }
24388
24512
  }
@@ -24426,9 +24550,9 @@ function readCodexThreadNames(codexHome = defaultCodexHome()) {
24426
24550
  const out = /* @__PURE__ */ new Map();
24427
24551
  let raw2;
24428
24552
  try {
24429
- const path = join18(codexHome, "session_index.jsonl");
24553
+ const path = join19(codexHome, "session_index.jsonl");
24430
24554
  if (statSync9(path).size > SESSION_INDEX_MAX_BYTES) return out;
24431
- raw2 = readFileSync12(path, "utf8");
24555
+ raw2 = readFileSync13(path, "utf8");
24432
24556
  } catch {
24433
24557
  return out;
24434
24558
  }
@@ -28464,16 +28588,16 @@ var init_wrapper = __esm({
28464
28588
 
28465
28589
  // packages/server/src/worker-plugin-dir.ts
28466
28590
  import { existsSync as existsSync6 } from "node:fs";
28467
- import { dirname as dirname9, join as join19 } from "node:path";
28591
+ import { dirname as dirname9, join as join20 } from "node:path";
28468
28592
  import { fileURLToPath as fileURLToPath2 } from "node:url";
28469
28593
  function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
28470
28594
  const override = env.FRIZZ_WORKER_PLUGIN_DIR;
28471
- if (override && existsSync6(join19(override, ".claude-plugin", "plugin.json")))
28595
+ if (override && existsSync6(join20(override, ".claude-plugin", "plugin.json")))
28472
28596
  return override;
28473
28597
  let current = dirname9(fileURLToPath2(moduleUrl));
28474
28598
  for (; ; ) {
28475
- const candidate = join19(current, "cc-worker");
28476
- if (existsSync6(join19(candidate, ".claude-plugin", "plugin.json"))) return candidate;
28599
+ const candidate = join20(current, "cc-worker");
28600
+ if (existsSync6(join20(candidate, ".claude-plugin", "plugin.json"))) return candidate;
28477
28601
  const parent = dirname9(current);
28478
28602
  if (parent === current) return void 0;
28479
28603
  current = parent;
@@ -28487,7 +28611,7 @@ var init_worker_plugin_dir = __esm({
28487
28611
 
28488
28612
  // packages/server/src/backend/types.ts
28489
28613
  import { existsSync as existsSync7 } from "node:fs";
28490
- import { join as join20 } from "node:path";
28614
+ import { join as join21 } from "node:path";
28491
28615
  function frizzMcpEnv(mcp) {
28492
28616
  return {
28493
28617
  FRIZZ_STATE_DIR: mcp.stateDir,
@@ -28511,7 +28635,7 @@ function chromeDevtoolsMcpMount() {
28511
28635
  function resolveBrowserMcpScript(moduleUrl = import.meta.url, env = process.env) {
28512
28636
  const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
28513
28637
  if (!pluginDir) return void 0;
28514
- const scriptPath = join20(pluginDir, "bin", CHROME_DEVTOOLS_MCP.script);
28638
+ const scriptPath = join21(pluginDir, "bin", CHROME_DEVTOOLS_MCP.script);
28515
28639
  return existsSync7(scriptPath) ? scriptPath : void 0;
28516
28640
  }
28517
28641
  function workerCap(name, lifted, env) {
@@ -28609,15 +28733,15 @@ import { spawn as spawn2 } from "node:child_process";
28609
28733
  import { connect } from "node:net";
28610
28734
  import { Agent as HttpAgent } from "node:http";
28611
28735
  import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
28612
- import { existsSync as existsSync8, mkdirSync as mkdirSync11, readFileSync as readFileSync13, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
28613
- import { join as join21 } from "node:path";
28736
+ import { existsSync as existsSync8, mkdirSync as mkdirSync11, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
28737
+ import { join as join22 } from "node:path";
28614
28738
  import { PassThrough, Writable } from "node:stream";
28615
28739
  import { StringDecoder } from "node:string_decoder";
28616
28740
  function nativeDir(stateDir) {
28617
- return join21(stateDir, "codex-app-server-native");
28741
+ return join22(stateDir, "codex-app-server-native");
28618
28742
  }
28619
28743
  function nativeRecordPath(stateDir, projectId) {
28620
- return join21(nativeDir(stateDir), `${projectId}.json`);
28744
+ return join22(nativeDir(stateDir), `${projectId}.json`);
28621
28745
  }
28622
28746
  function nativeListenSocketPath(stateDir, projectId) {
28623
28747
  const key = createHash7("sha256").update(stateDir).update("\0").update(projectId).digest("hex").slice(0, 16);
@@ -28633,7 +28757,7 @@ function pidAlive2(pid) {
28633
28757
  }
28634
28758
  function readNativeRecord(stateDir, projectId) {
28635
28759
  try {
28636
- const value = JSON.parse(readFileSync13(nativeRecordPath(stateDir, projectId), "utf8"));
28760
+ const value = JSON.parse(readFileSync14(nativeRecordPath(stateDir, projectId), "utf8"));
28637
28761
  if (typeof value.listenerPid !== "number" || typeof value.socketPath !== "string" || typeof value.generation !== "string") return null;
28638
28762
  return {
28639
28763
  projectId,
@@ -28881,15 +29005,15 @@ var init_codex_app_server_native = __esm({
28881
29005
  import { spawn as spawn3, spawnSync } from "node:child_process";
28882
29006
  import { createConnection } from "node:net";
28883
29007
  import { createHash as createHash8, randomUUID as randomUUID10 } from "node:crypto";
28884
- import { existsSync as existsSync9, mkdirSync as mkdirSync12, readFileSync as readFileSync14, unlinkSync as unlinkSync3 } from "node:fs";
28885
- import { join as join22 } from "node:path";
29008
+ import { existsSync as existsSync9, mkdirSync as mkdirSync12, readFileSync as readFileSync15, unlinkSync as unlinkSync3 } from "node:fs";
29009
+ import { join as join23 } from "node:path";
28886
29010
  import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream";
28887
29011
  import { StringDecoder as StringDecoder2 } from "node:string_decoder";
28888
29012
  function daemonDir(stateDir) {
28889
- return join22(stateDir, "codex-app-server");
29013
+ return join23(stateDir, "codex-app-server");
28890
29014
  }
28891
29015
  function recordPath(stateDir, projectId) {
28892
- return join22(daemonDir(stateDir), `${projectId}.json`);
29016
+ return join23(daemonDir(stateDir), `${projectId}.json`);
28893
29017
  }
28894
29018
  function codexAppServerSocketPath(stateDir, projectId) {
28895
29019
  const key = createHash8("sha256").update(stateDir).update("\0").update(projectId).digest("hex").slice(0, 16);
@@ -28905,7 +29029,7 @@ function pidAlive3(pid) {
28905
29029
  }
28906
29030
  function readDaemonRecord(stateDir, projectId) {
28907
29031
  try {
28908
- const value = JSON.parse(readFileSync14(recordPath(stateDir, projectId), "utf8"));
29032
+ const value = JSON.parse(readFileSync15(recordPath(stateDir, projectId), "utf8"));
28909
29033
  if (typeof value.daemonPid !== "number" || typeof value.socketPath !== "string" || typeof value.generation !== "string") return null;
28910
29034
  return {
28911
29035
  projectId,
@@ -28921,7 +29045,7 @@ function readDaemonRecord(stateDir, projectId) {
28921
29045
  }
28922
29046
  function readDaemonExitBreadcrumb(stateDir, projectId) {
28923
29047
  try {
28924
- const value = JSON.parse(readFileSync14(`${recordPath(stateDir, projectId)}.exit`, "utf8"));
29048
+ const value = JSON.parse(readFileSync15(`${recordPath(stateDir, projectId)}.exit`, "utf8"));
28925
29049
  if (typeof value.generation !== "string" || typeof value.reason !== "string") return null;
28926
29050
  return {
28927
29051
  generation: value.generation,
@@ -32117,11 +32241,11 @@ var init_codex_app_server = __esm({
32117
32241
  });
32118
32242
 
32119
32243
  // packages/server/src/backend/codex-quota.ts
32120
- import { join as join23 } from "node:path";
32244
+ import { join as join24 } from "node:path";
32121
32245
  import { readdirSync as readdirSync7, statSync as statSync10, openSync as openSync12, readSync as readSync6, fstatSync, closeSync as closeSync11 } from "node:fs";
32122
32246
  import { spawn as spawn4 } from "node:child_process";
32123
32247
  function sessionsDir2(codexHome) {
32124
- return join23(codexHome, "sessions");
32248
+ return join24(codexHome, "sessions");
32125
32249
  }
32126
32250
  function newestRollouts(dir, out, budget) {
32127
32251
  if (budget.n <= 0) return;
@@ -32135,11 +32259,11 @@ function newestRollouts(dir, out, budget) {
32135
32259
  const files = entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => e.name).sort(descByName2);
32136
32260
  for (const d of dirs) {
32137
32261
  if (budget.n <= 0) return;
32138
- newestRollouts(join23(dir, d), out, budget);
32262
+ newestRollouts(join24(dir, d), out, budget);
32139
32263
  }
32140
32264
  for (const f of files) {
32141
32265
  if (budget.n <= 0) return;
32142
- out.push(join23(dir, f));
32266
+ out.push(join24(dir, f));
32143
32267
  budget.n--;
32144
32268
  }
32145
32269
  }
@@ -32339,7 +32463,7 @@ var init_codex_quota = __esm({
32339
32463
  });
32340
32464
 
32341
32465
  // packages/server/src/backend/claude-quota.ts
32342
- import { join as join24 } from "node:path";
32466
+ import { join as join25 } from "node:path";
32343
32467
  import { homedir as homedir9, platform } from "node:os";
32344
32468
  import { createHash as createHash10 } from "node:crypto";
32345
32469
  import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -32347,7 +32471,7 @@ import { execFile as execFile2 } from "node:child_process";
32347
32471
  import { promisify as promisify2 } from "node:util";
32348
32472
  function claudeConfigDir() {
32349
32473
  const override = process.env.CLAUDE_CONFIG_DIR;
32350
- return override && override.trim() ? override : join24(homedir9(), ".claude");
32474
+ return override && override.trim() ? override : join25(homedir9(), ".claude");
32351
32475
  }
32352
32476
  function tokenFromCredentialsJson(raw2) {
32353
32477
  let doc;
@@ -32376,7 +32500,7 @@ async function readKeychainToken() {
32376
32500
  }
32377
32501
  async function readAccessToken(configDir) {
32378
32502
  try {
32379
- const fromFile = tokenFromCredentialsJson(await readFile(join24(configDir, ".credentials.json"), "utf8"));
32503
+ const fromFile = tokenFromCredentialsJson(await readFile(join25(configDir, ".credentials.json"), "utf8"));
32380
32504
  if (fromFile) return fromFile;
32381
32505
  } catch {
32382
32506
  }
@@ -32445,8 +32569,8 @@ function parseClaudeUsage(body, planType) {
32445
32569
  function cachePaths(cacheDir, configDir) {
32446
32570
  const profile = createHash10("sha256").update(configDir).digest("hex").slice(0, 12);
32447
32571
  return {
32448
- data: join24(cacheDir, `claude-${profile}.json`),
32449
- lock: join24(cacheDir, `claude-${profile}.lock`)
32572
+ data: join25(cacheDir, `claude-${profile}.json`),
32573
+ lock: join25(cacheDir, `claude-${profile}.lock`)
32450
32574
  };
32451
32575
  }
32452
32576
  async function readShared(path) {
@@ -32642,7 +32766,7 @@ function refreshSharedInBackground(paths, claudeBin, deps, now) {
32642
32766
  async function refreshClaudeQuotaInBackground(claudeBin = "claude", deps = {}) {
32643
32767
  const now = (deps.now ?? Date.now)();
32644
32768
  const configDir = claudeConfigDir();
32645
- const cacheDir = deps.cacheDir ?? join24(frizzRoots().cache, "quota-cache");
32769
+ const cacheDir = deps.cacheDir ?? join25(frizzRoots().cache, "quota-cache");
32646
32770
  try {
32647
32771
  await mkdir(cacheDir, { recursive: true, mode: 448 });
32648
32772
  } catch {
@@ -32653,7 +32777,7 @@ async function refreshClaudeQuotaInBackground(claudeBin = "claude", deps = {}) {
32653
32777
  async function readClaudeQuota(claudeBin = "claude", deps = {}, options = {}) {
32654
32778
  const now = (deps.now ?? Date.now)();
32655
32779
  const configDir = claudeConfigDir();
32656
- const cacheDir = deps.cacheDir ?? join24(frizzRoots().cache, "quota-cache");
32780
+ const cacheDir = deps.cacheDir ?? join25(frizzRoots().cache, "quota-cache");
32657
32781
  const paths = cachePaths(cacheDir, configDir);
32658
32782
  try {
32659
32783
  await mkdir(cacheDir, { recursive: true, mode: 448 });
@@ -32813,7 +32937,7 @@ var init_quota = __esm({
32813
32937
  // packages/server/src/frizz.ts
32814
32938
  import { execFile as execFile3 } from "node:child_process";
32815
32939
  import { lstatSync as lstatSync2, realpathSync as realpathSync6 } from "node:fs";
32816
- import { basename as basename5, dirname as dirname10, join as join25, resolve as resolve4 } from "node:path";
32940
+ import { basename as basename5, dirname as dirname10, join as join26, resolve as resolve4 } from "node:path";
32817
32941
  import { promisify as promisify3 } from "node:util";
32818
32942
  function frizzScriptsDir() {
32819
32943
  if (process.env.FRIZZ_SCRIPTS_DIR) return process.env.FRIZZ_SCRIPTS_DIR;
@@ -32822,7 +32946,7 @@ function frizzScriptsDir() {
32822
32946
  function directFrizzRoot(projectDir) {
32823
32947
  try {
32824
32948
  const projectRoot = realpathSync6(projectDir);
32825
- const path = join25(projectRoot, ".frizz");
32949
+ const path = join26(projectRoot, ".frizz");
32826
32950
  const stat2 = lstatSync2(path);
32827
32951
  if (!stat2.isDirectory() || stat2.isSymbolicLink()) return null;
32828
32952
  const real = realpathSync6(path);
@@ -32836,7 +32960,7 @@ function frizzDirExists(projectDir) {
32836
32960
  }
32837
32961
  async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
32838
32962
  if (!directFrizzRoot(projectDir)) throw new Error("unsafe or missing .frizz directory");
32839
- const { stdout } = await execFileP("node", [join25(scriptsDir, "index.mjs"), "--json"], {
32963
+ const { stdout } = await execFileP("node", [join26(scriptsDir, "index.mjs"), "--json"], {
32840
32964
  cwd: projectDir,
32841
32965
  env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir },
32842
32966
  maxBuffer: 32 * 1024 * 1024
@@ -32861,7 +32985,7 @@ async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
32861
32985
  };
32862
32986
  }
32863
32987
  async function runThreadUpdate(projectDir, slug, args, scriptsDir = frizzScriptsDir()) {
32864
- await execFileP("node", [join25(scriptsDir, "thread-update.mjs"), slug, ...args], {
32988
+ await execFileP("node", [join26(scriptsDir, "thread-update.mjs"), slug, ...args], {
32865
32989
  cwd: projectDir,
32866
32990
  env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir }
32867
32991
  });
@@ -34746,19 +34870,19 @@ var init_claude_agent_broker_bridge = __esm({
34746
34870
  });
34747
34871
 
34748
34872
  // packages/server/src/backend/auth-status.ts
34749
- import { join as join26 } from "node:path";
34873
+ import { join as join27 } from "node:path";
34750
34874
  import { homedir as homedir11, platform as platform2 } from "node:os";
34751
- import { readFileSync as readFileSync15, statSync as statSync11 } from "node:fs";
34875
+ import { readFileSync as readFileSync16, statSync as statSync11 } from "node:fs";
34752
34876
  import { execFile as execFile4 } from "node:child_process";
34753
34877
  import { promisify as promisify4 } from "node:util";
34754
34878
  function claudeConfigDir2() {
34755
34879
  const override = process.env.CLAUDE_CONFIG_DIR;
34756
- return override && override.trim() ? override : join26(homedir11(), ".claude");
34880
+ return override && override.trim() ? override : join27(homedir11(), ".claude");
34757
34881
  }
34758
34882
  function claudeFileState(configDir) {
34759
34883
  let raw2;
34760
34884
  try {
34761
- raw2 = readFileSync15(join26(configDir, ".credentials.json"), "utf8");
34885
+ raw2 = readFileSync16(join27(configDir, ".credentials.json"), "utf8");
34762
34886
  } catch (err) {
34763
34887
  return err.code === "ENOENT" ? "absent" : "error";
34764
34888
  }
@@ -34791,7 +34915,7 @@ function readCodexAuthState(codexHome = defaultCodexHome()) {
34791
34915
  if (process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY || process.env.CODEX_ACCESS_TOKEN) return "authed";
34792
34916
  let raw2;
34793
34917
  try {
34794
- raw2 = readFileSync15(join26(codexHome, "auth.json"), "utf8");
34918
+ raw2 = readFileSync16(join27(codexHome, "auth.json"), "utf8");
34795
34919
  } catch (err) {
34796
34920
  return err.code === "ENOENT" ? "signed-out" : "unknown";
34797
34921
  }
@@ -34856,7 +34980,7 @@ function asEmail(value) {
34856
34980
  }
34857
34981
  function claudeAccountFile() {
34858
34982
  const override = process.env.CLAUDE_CONFIG_DIR;
34859
- return override && override.trim() ? join26(override, ".claude.json") : join26(homedir11(), ".claude.json");
34983
+ return override && override.trim() ? join27(override, ".claude.json") : join27(homedir11(), ".claude.json");
34860
34984
  }
34861
34985
  function readClaudeAccountEmail(path = claudeAccountFile()) {
34862
34986
  let stat2;
@@ -34869,7 +34993,7 @@ function readClaudeAccountEmail(path = claudeAccountFile()) {
34869
34993
  if (memo3 && memo3.path === path && memo3.mtimeMs === stat2.mtimeMs && memo3.size === stat2.size) return memo3.email;
34870
34994
  let email;
34871
34995
  try {
34872
- const doc = JSON.parse(readFileSync15(path, "utf8"));
34996
+ const doc = JSON.parse(readFileSync16(path, "utf8"));
34873
34997
  email = asEmail(doc?.oauthAccount?.emailAddress);
34874
34998
  } catch {
34875
34999
  email = void 0;
@@ -34880,7 +35004,7 @@ function readClaudeAccountEmail(path = claudeAccountFile()) {
34880
35004
  function readCodexAccountEmail(codexHome = defaultCodexHome()) {
34881
35005
  let doc;
34882
35006
  try {
34883
- doc = JSON.parse(readFileSync15(join26(codexHome, "auth.json"), "utf8"));
35007
+ doc = JSON.parse(readFileSync16(join27(codexHome, "auth.json"), "utf8"));
34884
35008
  } catch {
34885
35009
  return void 0;
34886
35010
  }
@@ -34948,8 +35072,8 @@ var init_auth_status = __esm({
34948
35072
  });
34949
35073
 
34950
35074
  // packages/server/src/dispatch.ts
34951
- import { closeSync as closeSync12, constants, existsSync as existsSync10, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync16, realpathSync as realpathSync7, statSync as statSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync8 } from "node:fs";
34952
- import { basename as basename6, join as join27, dirname as dirname11 } from "node:path";
35075
+ import { closeSync as closeSync12, constants, existsSync as existsSync10, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync17, realpathSync as realpathSync7, statSync as statSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync9 } from "node:fs";
35076
+ import { basename as basename6, join as join28, dirname as dirname11 } from "node:path";
34953
35077
  import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
34954
35078
  function fallbackTitle(prompt) {
34955
35079
  const firstLine2 = prompt.trim().split("\n", 1)[0].trim();
@@ -34968,7 +35092,7 @@ function fallbackTitle(prompt) {
34968
35092
  }
34969
35093
  function resolveSlug(frizzDir, base, taken) {
34970
35094
  base = ThreadSlug.parse(base);
34971
- const isTaken = (slug) => existsSync10(join27(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
35095
+ const isTaken = (slug) => existsSync10(join28(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
34972
35096
  if (!isTaken(base)) return base;
34973
35097
  for (let n = 2; ; n++) {
34974
35098
  const suffix = `-${n}`;
@@ -34985,12 +35109,12 @@ function resolveLegacyThreadFile(projectDir, value) {
34985
35109
  if (!parsed.success) return null;
34986
35110
  try {
34987
35111
  const projectRoot = realpathSync7(projectDir);
34988
- const frizzPath = join27(projectRoot, ".frizz");
35112
+ const frizzPath = join28(projectRoot, ".frizz");
34989
35113
  const frizzStat = lstatSync3(frizzPath);
34990
35114
  if (!frizzStat.isDirectory() || frizzStat.isSymbolicLink()) return null;
34991
35115
  const realFrizz = realpathSync7(frizzPath);
34992
35116
  if (dirname11(realFrizz) !== projectRoot || basename6(realFrizz) !== ".frizz") return null;
34993
- const path = join27(realFrizz, `${parsed.data}.md`);
35117
+ const path = join28(realFrizz, `${parsed.data}.md`);
34994
35118
  const before = lstatSync3(path);
34995
35119
  if (!before.isFile() || before.isSymbolicLink()) return null;
34996
35120
  const realPath = realpathSync7(path);
@@ -35001,7 +35125,7 @@ function resolveLegacyThreadFile(projectDir, value) {
35001
35125
  const fd = openSync13(path, constants.O_RDONLY | constants.O_NOFOLLOW);
35002
35126
  try {
35003
35127
  openedBefore = fstatSync2(fd);
35004
- contents = readFileSync16(fd);
35128
+ contents = readFileSync17(fd);
35005
35129
  openedAfter = fstatSync2(fd);
35006
35130
  } finally {
35007
35131
  closeSync12(fd);
@@ -35034,7 +35158,7 @@ function boardAuthorizesAdoption(board, slug) {
35034
35158
  return !board.errorItems.some((item) => item.file === `${slug}.md`);
35035
35159
  }
35036
35160
  function ensureSafeDirectDirectory(parent, name) {
35037
- const path = join27(parent, name);
35161
+ const path = join28(parent, name);
35038
35162
  try {
35039
35163
  mkdirSync13(path);
35040
35164
  } catch (error) {
@@ -35064,8 +35188,8 @@ function loadWorkerPrompt(kind = "claude") {
35064
35188
  function monitorScriptsDir() {
35065
35189
  const plugin = workerPluginDir();
35066
35190
  if (!plugin) return void 0;
35067
- const dir = join27(plugin, "skills", "gh", "scripts");
35068
- return existsSync10(join27(dir, "ci-watch.mjs")) ? dir : void 0;
35191
+ const dir = join28(plugin, "skills", "gh", "scripts");
35192
+ return existsSync10(join28(dir, "ci-watch.mjs")) ? dir : void 0;
35069
35193
  }
35070
35194
  function codexScratchpadHookConfig(hookScript, sessionId) {
35071
35195
  if (!hookScript || !sessionId) return {};
@@ -35077,7 +35201,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
35077
35201
  }
35078
35202
  ]
35079
35203
  });
35080
- const bashBackgroundHook = join27(dirname11(hookScript), "bash-background.mjs");
35204
+ const bashBackgroundHook = join28(dirname11(hookScript), "bash-background.mjs");
35081
35205
  return {
35082
35206
  bypass_hook_trust: true,
35083
35207
  hooks: {
@@ -35103,7 +35227,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
35103
35227
  }
35104
35228
  function scratchpadHookScript() {
35105
35229
  const plugin = workerPluginDir();
35106
- return plugin ? join27(plugin, "hooks", "scratchpad.mjs") : void 0;
35230
+ return plugin ? join28(plugin, "hooks", "scratchpad.mjs") : void 0;
35107
35231
  }
35108
35232
  function composePrompt(sessionId, prompt, kind = "claude") {
35109
35233
  const children2 = kind === "codex" ? "Native sub-agents share it \u2014 have each write its OWN file rather than all editing one." : "Name it in a sub-agent's prompt when you want its notes to land somewhere you can read; give each child its OWN file rather than having them all edit one.";
@@ -35119,12 +35243,12 @@ function scratchpadOrientation(sessionId, kind = "claude") {
35119
35243
  return `SCRATCH DIRECTORY: .frizz/threads/${sessionId}/ \u2014 yours, free-form, as many files as you like, and nothing is expected in it. A single direct task usually needs none; writing notes is never a substitute for doing the work. On a long effort write the doc you would want if you lost your context (${children2}), then arm mcp__frizz__recurring_prompt with post_compaction: true and a prompt LINKING that file \u2014 frizz hands the link back when your context is compacted. Nothing in this directory is read automatically.`;
35120
35244
  }
35121
35245
  function frizzConfigBlock(projectDir) {
35122
- const path = join27(projectDir, "FRIZZ.md");
35246
+ const path = join28(projectDir, "FRIZZ.md");
35123
35247
  let body;
35124
35248
  try {
35125
35249
  const st = statSync12(path);
35126
35250
  if (!st.isFile() || st.size > FRIZZ_MD_MAX_BYTES) return "";
35127
- body = readFileSync16(path, "utf8").trim();
35251
+ body = readFileSync17(path, "utf8").trim();
35128
35252
  } catch {
35129
35253
  return "";
35130
35254
  }
@@ -35159,7 +35283,7 @@ function resolveFrizzMcp(target, moduleUrl = import.meta.url, env = process.env,
35159
35283
  const { stateDir, serverLock, projectId } = typeof target === "string" ? { stateDir: target } : target;
35160
35284
  const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
35161
35285
  if (!pluginDir) return void 0;
35162
- const scriptPath = join27(pluginDir, "bin", FRIZZ_MCP.script);
35286
+ const scriptPath = join28(pluginDir, "bin", FRIZZ_MCP.script);
35163
35287
  if (!existsSync10(scriptPath)) return void 0;
35164
35288
  return {
35165
35289
  scriptPath,
@@ -35242,12 +35366,12 @@ function buildClaudeResumeCommand(opts) {
35242
35366
  }
35243
35367
  function createDispatcher(deps) {
35244
35368
  const readBoardSource = deps.readBoard ?? readBoard;
35245
- const frizzDir = join27(deps.project.dir, ".frizz");
35369
+ const frizzDir = join28(deps.project.dir, ".frizz");
35246
35370
  const adoptionRuntime = deps.adoptionRuntime ?? productionRuntime;
35247
35371
  function cleanupPrewrites(built) {
35248
35372
  for (const path of new Set(built.prewrite.map((file) => file.path))) {
35249
35373
  try {
35250
- rmSync8(path, { force: true });
35374
+ rmSync9(path, { force: true });
35251
35375
  } catch {
35252
35376
  }
35253
35377
  }
@@ -35255,7 +35379,7 @@ function createDispatcher(deps) {
35255
35379
  function cleanupDispatchFiles(scratchRel, built, sessionId) {
35256
35380
  cleanupPrewrites(built);
35257
35381
  try {
35258
- rmSync8(join27(deps.project.dir, scratchRel), { force: true, recursive: true });
35382
+ rmSync9(join28(deps.project.dir, scratchRel), { force: true, recursive: true });
35259
35383
  } catch {
35260
35384
  }
35261
35385
  cleanupAdoptionSessionFiles(deps.project.dir, sessionId);
@@ -35688,7 +35812,7 @@ import {
35688
35812
  watch as fsWatch
35689
35813
  } from "node:fs";
35690
35814
  import { homedir as homedir12 } from "node:os";
35691
- import { join as join28 } from "node:path";
35815
+ import { join as join29 } from "node:path";
35692
35816
  import watcher from "@parcel/watcher";
35693
35817
  function appServerTurnStalled(liveness, lastActivityAt, nowMs) {
35694
35818
  if (!liveness) return false;
@@ -35873,7 +35997,7 @@ function hasFreshDelivery(row, processGone) {
35873
35997
  if (processGone) return false;
35874
35998
  return parseDeliveryLedger(row.delivery_ledger).some((d) => d.state === "pending" || d.state === "enqueued" || d.state === "delivered");
35875
35999
  }
35876
- function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, excuseLiveOwnWork = true, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set()) {
36000
+ function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, excuseLiveOwnWork = true, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set(), armedTimerIds = /* @__PURE__ */ new Set()) {
35877
36001
  if (futureSnooze(row, nowMs)) return false;
35878
36002
  if (hasActionableInteraction) return true;
35879
36003
  if (runtime === "perm-prompt") return true;
@@ -35888,7 +36012,7 @@ function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, no
35888
36012
  if (excuseLiveOwnWork && runtime !== "exited" && hasLiveBackgroundWork(tele) && !tele?.lastFence) return false;
35889
36013
  if (excuseLiveOwnWork && runtime !== "exited" && hasDeclaredBackgroundPark(tele, nowMs)) return false;
35890
36014
  if (excuseLiveOwnWork && runtime !== "exited" && heldByRunningChecks(tele, github, registeredPrWatches)) return false;
35891
- if (runtime !== "exited" && hasLiveOwnWork(tele) && tele?.lastFence?.kind !== "done") return !bgSnoozeArmed(row);
36015
+ if (runtime !== "exited" && (hasLiveOwnWork(tele) || hasParkedTimerWatch(tele, armedTimerIds)) && tele?.lastFence?.kind !== "done") return !bgSnoozeArmed(row);
35892
36016
  if (tele?.lastFence?.kind === "done") return true;
35893
36017
  return true;
35894
36018
  }
@@ -35899,7 +36023,7 @@ function deriveAwaitingBackground(row, tele, runtime, hasActionableInteraction =
35899
36023
  if (tele?.lastFence?.kind === "awaiting" && !hasParkedPrWatch(tele) && !hasDeclaredBackgroundPark(tele, nowMs) && // A TIMER PARK IS THE SAME EXCEPTION AGAIN (2026-08-24): its fence has no park action either, so
35900
36024
  // suppressing this card left the wait stated nowhere but the fence's own machinery footer.
35901
36025
  !hasParkedTimerWatch(tele, armedTimerIds)) return false;
35902
- return deriveNeedsYou({ ...row, bg_snooze_rested_at: null }, tele, runtime, hasActionableInteraction, nowMs, limitPause, false, deliveryProcessGone);
36026
+ return deriveNeedsYou({ ...row, bg_snooze_rested_at: null }, tele, runtime, hasActionableInteraction, nowMs, limitPause, false, deliveryProcessGone, {}, /* @__PURE__ */ new Set(), armedTimerIds);
35903
36027
  }
35904
36028
  function resolveSessionProfile(row, tele) {
35905
36029
  const persistedModel = row.model?.trim() || void 0;
@@ -36005,7 +36129,7 @@ function sessionThreadView(projectDir, storage, row, tele, registeredLegacyTermi
36005
36129
  const state = effectiveSessionState(row, registeredLegacyTerminal);
36006
36130
  const archived = state === "archived";
36007
36131
  const limitPause = resolveLimitPause(row, tele, nowMs);
36008
- const needsYou = archived ? false : deriveNeedsYou(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, true, deliveryProcessGone, github, registeredPrWatches);
36132
+ const needsYou = archived ? false : deriveNeedsYou(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, true, deliveryProcessGone, github, registeredPrWatches, armedTimerIds);
36009
36133
  const awaitingBackground = archived ? false : deriveAwaitingBackground(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, deliveryProcessGone, github, registeredPrWatches, armedTimerIds);
36010
36134
  const crashed = runtime === "exited" && (tele?.turn === "in-flight" || hasLiveBackgroundWork(tele) || headlessLostWork);
36011
36135
  const snoozedUntil = futureSnooze(row, nowMs);
@@ -36373,7 +36497,7 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
36373
36497
  if (parcelSub || stopped) return Promise.resolve();
36374
36498
  if (watchSetup) return watchSetup;
36375
36499
  const setup = (async () => {
36376
- const next = await subscribe(join28(project.dir, ".frizz"), () => scheduleRebuild());
36500
+ const next = await subscribe(join29(project.dir, ".frizz"), () => scheduleRebuild());
36377
36501
  if (stopped) {
36378
36502
  await next.unsubscribe();
36379
36503
  return;
@@ -36491,7 +36615,8 @@ function delivery(row) {
36491
36615
  createdAt: row.created_at,
36492
36616
  updatedAt: row.updated_at,
36493
36617
  deliveredAt: row.delivered_at,
36494
- terminalAt: row.terminal_at
36618
+ terminalAt: row.terminal_at,
36619
+ sentAt: row.sent_at ?? null
36495
36620
  };
36496
36621
  }
36497
36622
  function createWakeDeliveryStore(db) {
@@ -36514,11 +36639,14 @@ function createWakeDeliveryStore(db) {
36514
36639
  updated_at INTEGER NOT NULL,
36515
36640
  delivered_at INTEGER,
36516
36641
  terminal_at INTEGER,
36642
+ sent_at INTEGER,
36517
36643
  UNIQUE(thread_slug, session_id, fence_id)
36518
36644
  );
36519
36645
  CREATE INDEX IF NOT EXISTS wake_delivery_due
36520
36646
  ON wake_delivery(state, next_attempt_at, created_at);
36521
36647
  `);
36648
+ const columns = db.prepare("PRAGMA table_info(wake_delivery)").all();
36649
+ if (!columns.some((c) => c.name === "sent_at")) db.exec("ALTER TABLE wake_delivery ADD COLUMN sent_at INTEGER");
36522
36650
  const byId = db.prepare("SELECT * FROM wake_delivery WHERE id = ?");
36523
36651
  const all = db.prepare("SELECT * FROM wake_delivery ORDER BY created_at, id");
36524
36652
  const open = db.prepare(
@@ -36555,7 +36683,7 @@ function createWakeDeliveryStore(db) {
36555
36683
  const claimStmt = db.prepare(`
36556
36684
  UPDATE wake_delivery SET
36557
36685
  state = 'leased', attempts = attempts + 1, lease_owner = @owner, lease_until = @leaseUntil,
36558
- last_error = NULL, updated_at = @now
36686
+ last_error = NULL, sent_at = NULL, updated_at = @now
36559
36687
  WHERE id = @id AND state = 'pending' AND next_attempt_at <= @now AND attempts < @maxAttempts
36560
36688
  `);
36561
36689
  const deferFailureStmt = db.prepare(`
@@ -36563,6 +36691,11 @@ function createWakeDeliveryStore(db) {
36563
36691
  lease_until = @retryAt, last_error = @error, updated_at = @now
36564
36692
  WHERE id = @id AND state = 'leased' AND lease_owner = @owner
36565
36693
  `);
36694
+ const markSentStmt = db.prepare(`
36695
+ UPDATE wake_delivery SET
36696
+ sent_at = @now, lease_until = @confirmUntil, last_error = NULL, updated_at = @now
36697
+ WHERE id = @id AND state = 'leased' AND lease_owner = @owner
36698
+ `);
36566
36699
  const recoverExpiredStmt = db.prepare(`
36567
36700
  UPDATE wake_delivery SET
36568
36701
  state = CASE WHEN attempts >= @maxAttempts THEN 'exhausted' ELSE 'pending' END,
@@ -36631,6 +36764,7 @@ function createWakeDeliveryStore(db) {
36631
36764
  return row ? delivery(row) : void 0;
36632
36765
  },
36633
36766
  acknowledge: (id, owner, now) => acknowledgeStmt.run({ id, owner, now }).changes === 1,
36767
+ markSent: (id, owner, now, confirmUntil) => markSentStmt.run({ id, owner, now, confirmUntil }).changes === 1,
36634
36768
  confirm: (id, now) => confirmStmt.run({ id, now }).changes === 1,
36635
36769
  supersede: (id, now, reason) => supersedeStmt.run({ id, now, reason: reason.slice(0, 500) }).changes === 1
36636
36770
  };
@@ -37183,6 +37317,28 @@ function isTimerFenceId(fenceId) {
37183
37317
  function timerIdOf(fenceId) {
37184
37318
  return fenceId.slice(TIMER_FENCE_PREFIX.length + 1);
37185
37319
  }
37320
+ function conciseGhError(err) {
37321
+ const code = err?.code;
37322
+ if (code === "ENOENT") return "the `gh` CLI is not installed on the PATH of the process Frizz runs as";
37323
+ if (err?.killed || code === "ETIMEDOUT") return "`gh` did not answer within 15s";
37324
+ const stderr = typeof err?.stderr === "string" ? err.stderr : "";
37325
+ const line = stderr.trim().split(/\r?\n/).map((l) => l.trim()).find((l) => l.length > 0);
37326
+ if (line) return line.slice(0, 300);
37327
+ const message = err instanceof Error ? err.message : String(err);
37328
+ return message.trim().split(/\r?\n/, 1)[0]?.slice(0, 300) || "unknown error";
37329
+ }
37330
+ async function probePrReadable(ref) {
37331
+ try {
37332
+ await execFileAsync4(
37333
+ "gh",
37334
+ ["pr", "view", String(ref.number), "--repo", `${ref.owner}/${ref.repo}`, "--json", "state"],
37335
+ { timeout: 15e3, maxBuffer: 1e6, env: { ...process.env, GH_PAGER: "cat", GH_PROMPT_DISABLED: "1" } }
37336
+ );
37337
+ return { ok: true };
37338
+ } catch (err) {
37339
+ return { ok: false, reason: conciseGhError(err) };
37340
+ }
37341
+ }
37186
37342
  async function defaultFetchPr(ref) {
37187
37343
  try {
37188
37344
  const { stdout } = await execFileAsync4(
@@ -37209,8 +37365,8 @@ async function defaultFetchPr(ref) {
37209
37365
  reviewDecision: typeof j.reviewDecision === "string" ? j.reviewDecision : void 0,
37210
37366
  workflowRuns
37211
37367
  };
37212
- } catch {
37213
- return void 0;
37368
+ } catch (err) {
37369
+ throw new Error(conciseGhError(err));
37214
37370
  }
37215
37371
  }
37216
37372
  function createScheduler(deps) {
@@ -37223,6 +37379,7 @@ function createScheduler(deps) {
37223
37379
  const retryBaseMs = Math.max(1, deps.retryBaseMs ?? 5e3);
37224
37380
  const retryMaxMs = Math.max(retryBaseMs, deps.retryMaxMs ?? 5 * 6e4);
37225
37381
  const maxDeliveryAttempts = Math.max(1, deps.maxDeliveryAttempts ?? 6);
37382
+ const confirmGraceMs = Math.max(1, deps.confirmGraceMs ?? 6e4);
37226
37383
  const deliveryBatchSize = Math.max(0, deps.deliveryBatchSize ?? 50);
37227
37384
  const deliveryOwner = randomUUID14();
37228
37385
  const outbox = createWakeDeliveryStore(deps.storage.db);
@@ -37352,6 +37509,23 @@ function createScheduler(deps) {
37352
37509
  log3(`waker: GitHub review check failed for ${key} (${slug}) [${result.failure.kind}] \u2014 ${result.failure.message}${suppressed}`);
37353
37510
  reviewFailures.set(key, { signature, loggedAt: at, suppressed: 0 });
37354
37511
  }
37512
+ const statusFailures = /* @__PURE__ */ new Map();
37513
+ function recordStatusFailure(key, message, at) {
37514
+ const prior = statusFailures.get(key);
37515
+ if (prior?.signature === message && at - prior.loggedAt < 15 * 6e4) {
37516
+ prior.suppressed++;
37517
+ return;
37518
+ }
37519
+ const suppressed = prior?.suppressed ? ` (${prior.suppressed} identical repeats suppressed)` : "";
37520
+ log3(`waker: PR status check failed for ${key} \u2014 ${message}${suppressed}; CI and merge wakes cannot fire until \`gh\` can read it`);
37521
+ statusFailures.set(key, { signature: message, loggedAt: at, suppressed: 0 });
37522
+ }
37523
+ function recordStatusSuccess(key) {
37524
+ const prior = statusFailures.get(key);
37525
+ if (!prior) return;
37526
+ statusFailures.delete(key);
37527
+ log3(`waker: PR status check recovered for ${key}${prior.suppressed ? `; ${prior.suppressed} identical repeats were suppressed` : ""}`);
37528
+ }
37355
37529
  function recordReviewSuccess(key, slug) {
37356
37530
  const prior = reviewFailures.get(key);
37357
37531
  if (!prior) return;
@@ -37696,9 +37870,10 @@ ${clock}`;
37696
37870
  if (pr) {
37697
37871
  status.set(key, githubWatchStatus(pr, new Date(nowMs).toISOString()));
37698
37872
  publishGithubStatus(key, pr, nowMs);
37873
+ recordStatusSuccess(key);
37699
37874
  }
37700
37875
  } catch (err) {
37701
- log3(`waker: pr-watch status fetch failed for ${key}: ${err instanceof Error ? err.message : String(err)}`);
37876
+ recordStatusFailure(key, err instanceof Error ? err.message : String(err), nowMs);
37702
37877
  }
37703
37878
  try {
37704
37879
  const result = normalizeReviewResult(await fetchGithubReview(ref));
@@ -37992,6 +38167,38 @@ ${clock}`;
37992
38167
  continue;
37993
38168
  }
37994
38169
  if (item.state !== "leased" || item.leaseUntil === null || item.leaseUntil > nowMs) continue;
38170
+ if (item.sentAt !== null) {
38171
+ const runtime = deps.wakeRuntimeState?.(item.slug, item.sessionId) ?? "unknown";
38172
+ if (runtime !== "dead") {
38173
+ if (outbox.confirm(item.id, nowMs)) {
38174
+ settleSnooze(item);
38175
+ settleSchedulePrompt(item);
38176
+ settleRestPrompt(item);
38177
+ settleCompactPrompt(item);
38178
+ settleTimer(item);
38179
+ settleSignoffNudge(item);
38180
+ log3(`waker: delivered ${item.slug} \u2014 ${item.reason}${item.attempts > 1 ? ` (on attempt ${item.attempts})` : ""}`);
38181
+ }
38182
+ continue;
38183
+ }
38184
+ if (context !== "current-idle") continue;
38185
+ const recovered2 = outbox.recoverExpired(
38186
+ item.id,
38187
+ nowMs,
38188
+ nowMs + retryDelay(item.attempts),
38189
+ maxDeliveryAttempts,
38190
+ "the worker's process died before it read this wake"
38191
+ );
38192
+ if (!recovered2) continue;
38193
+ if (recovered2.state === "exhausted") {
38194
+ settleSnooze(item);
38195
+ settleTimer(item);
38196
+ log3(`waker: delivery EXHAUSTED for ${item.slug} after ${recovered2.attempts} attempts \u2014 ${recovered2.lastError ?? "unknown error"}`);
38197
+ } else {
38198
+ log3(`waker: wake LOST for ${item.slug} \u2014 ${item.reason}: the worker's process died before it read it; sending again (attempt ${item.attempts} of ${maxDeliveryAttempts} spent)`);
38199
+ }
38200
+ continue;
38201
+ }
37995
38202
  if (context !== "current-idle") continue;
37996
38203
  const recovered = outbox.recoverExpired(
37997
38204
  item.id,
@@ -38061,6 +38268,15 @@ ${clock}`;
38061
38268
  log3(`waker: delivery FAILED for ${item.slug} (attempt ${item.attempts} of ${maxDeliveryAttempts}): ${message}`);
38062
38269
  continue;
38063
38270
  }
38271
+ const runtime = deps.wakeRuntimeState?.(item.slug, item.sessionId) ?? "unknown";
38272
+ if (runtime !== "unknown") {
38273
+ const sentAt = now();
38274
+ if (outbox.markSent(item.id, deliveryOwner, sentAt, sentAt + confirmGraceMs)) {
38275
+ log3(`waker: sent ${item.slug} \u2014 ${item.reason}; confirming within ${Math.round(confirmGraceMs / 1e3)}s`);
38276
+ checkpoint("after-delivery", item);
38277
+ continue;
38278
+ }
38279
+ }
38064
38280
  const retried = item.attempts > 1 ? ` (on attempt ${item.attempts})` : "";
38065
38281
  log3(`waker: delivered ${item.slug} \u2014 ${item.reason}${retried}`);
38066
38282
  checkpoint("after-delivery", item);
@@ -38307,7 +38523,7 @@ var init_resume = __esm({
38307
38523
  });
38308
38524
 
38309
38525
  // packages/server/src/backend/claude.ts
38310
- import { join as join29 } from "node:path";
38526
+ import { join as join30 } from "node:path";
38311
38527
  function toolResultText2(content) {
38312
38528
  if (typeof content === "string") return content;
38313
38529
  if (!Array.isArray(content)) return "";
@@ -38396,7 +38612,7 @@ function createClaudeBackend(opts) {
38396
38612
  return { argv, env: claudeWorkerEnvironment(), prewrite: [] };
38397
38613
  },
38398
38614
  transcriptPath(sessionId) {
38399
- return join29(opts.logDir, `${sessionId}.jsonl`);
38615
+ return join30(opts.logDir, `${sessionId}.jsonl`);
38400
38616
  },
38401
38617
  parseLine(line) {
38402
38618
  return parseClaudeLine(line);
@@ -38566,12 +38782,12 @@ var init_login_utility = __esm({
38566
38782
 
38567
38783
  // packages/server/src/backend/codex-app-server-diagnostics.ts
38568
38784
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, renameSync as renameSync8, statSync as statSync13 } from "node:fs";
38569
- import { join as join30 } from "node:path";
38785
+ import { join as join31 } from "node:path";
38570
38786
  function codexDiagnosticLogPath(stateDir, projectId) {
38571
- return join30(stateDir, "codex-app-server", `${projectId}.diagnostics.log`);
38787
+ return join31(stateDir, "codex-app-server", `${projectId}.diagnostics.log`);
38572
38788
  }
38573
38789
  function createCodexDiagnosticSink(stateDir, projectId, now = () => /* @__PURE__ */ new Date()) {
38574
- const dir = join30(stateDir, "codex-app-server");
38790
+ const dir = join31(stateDir, "codex-app-server");
38575
38791
  const path = codexDiagnosticLogPath(stateDir, projectId);
38576
38792
  let ensured = false;
38577
38793
  return (event) => {
@@ -38603,7 +38819,7 @@ var init_codex_app_server_diagnostics = __esm({
38603
38819
  import { execFile as execFile7 } from "node:child_process";
38604
38820
  import { readdirSync as readdirSync8, unlinkSync as unlinkSync4 } from "node:fs";
38605
38821
  import { connect as connectSocket } from "node:net";
38606
- import { join as join31 } from "node:path";
38822
+ import { join as join32 } from "node:path";
38607
38823
  function parseLsofSocketNames(stdout) {
38608
38824
  const referenced = /* @__PURE__ */ new Set();
38609
38825
  for (const line of stdout.split("\n")) {
@@ -38620,7 +38836,7 @@ function sweepStaleSockets(options, deps = {}) {
38620
38836
  const keep = new Set(options.keep ?? []);
38621
38837
  let candidates;
38622
38838
  try {
38623
- candidates = (deps.readdir ?? readdirSync8)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) => join31(options.dir, name)).filter((path) => !keep.has(path));
38839
+ candidates = (deps.readdir ?? readdirSync8)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) => join32(options.dir, name)).filter((path) => !keep.has(path));
38624
38840
  } catch {
38625
38841
  return;
38626
38842
  }
@@ -38670,7 +38886,7 @@ var init_stale_socket_sweep = __esm({
38670
38886
 
38671
38887
  // packages/server/src/orphan-reaper.ts
38672
38888
  import { execFileSync as execFileSync5 } from "node:child_process";
38673
- import { readFileSync as readFileSync17 } from "node:fs";
38889
+ import { readFileSync as readFileSync18 } from "node:fs";
38674
38890
  import { dirname as dirname12 } from "node:path";
38675
38891
  function firstTokenBasename(command) {
38676
38892
  const first = command.trimStart().split(/\s+/, 1)[0] ?? "";
@@ -38937,7 +39153,7 @@ var init_orphan_reaper = __esm({
38937
39153
  defaultExec = (file, args) => execFileSync5(file, args, { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 1e4 });
38938
39154
  defaultReadEnv = (pid) => {
38939
39155
  try {
38940
- return readFileSync17(`/proc/${pid}/environ`, "utf8");
39156
+ return readFileSync18(`/proc/${pid}/environ`, "utf8");
38941
39157
  } catch {
38942
39158
  return null;
38943
39159
  }
@@ -39084,7 +39300,7 @@ var init_thread_hibernation = __esm({
39084
39300
  });
39085
39301
 
39086
39302
  // packages/server/src/context.ts
39087
- import { join as join32 } from "node:path";
39303
+ import { join as join33 } from "node:path";
39088
39304
  import { randomUUID as randomUUID15 } from "node:crypto";
39089
39305
  import { homedir as homedir13 } from "node:os";
39090
39306
  function reconcileSessions(storage) {
@@ -39242,7 +39458,7 @@ async function createContext(opts = {}) {
39242
39458
  function createContextUnchecked(opts, resources) {
39243
39459
  const home = opts.home ?? homedir13();
39244
39460
  const project = opts.project ?? resolveProject();
39245
- const dbPath = join32(project.stateDir, "ui.db");
39461
+ const dbPath = join33(project.stateDir, "ui.db");
39246
39462
  const storage = createStorage(dbPath);
39247
39463
  resources.storage = storage;
39248
39464
  const bus = new Bus();
@@ -39484,6 +39700,15 @@ function createContextUnchecked(opts, resources) {
39484
39700
  // instant for a weekly limit, whose message text carries a clock but no date; readQuota memoizes,
39485
39701
  // so consulting it per tick costs a live request only every few minutes.
39486
39702
  readQuota,
39703
+ // The only runtime that can answer is the broker: its daemon record is on disk while the daemon
39704
+ // lives and is unlinked when it dies (liveBrokerRecords checks the pid), so "did the process that
39705
+ // took this wake survive" is one directory read. Codex and any row whose session moved on answer
39706
+ // "unknown", which keeps their wakes on the delivered-on-return path they always had.
39707
+ wakeRuntimeState: (slug, sessionId) => {
39708
+ const row = storage.getSession(slug);
39709
+ if (!row || row.session_id !== sessionId || row.backend !== "claude" || row.claude_runtime !== "broker" || !claudeBroker) return "unknown";
39710
+ return liveBrokerRecords(project.stateDir).some((r) => r.sessionId === sessionId) ? "alive" : "dead";
39711
+ },
39487
39712
  resume: (slug, message, deliveryId) => {
39488
39713
  const deliveryMessage = `${message}
39489
39714
 
@@ -39565,11 +39790,14 @@ ${wakeDeliveryToken(deliveryId)}`;
39565
39790
  tailer,
39566
39791
  dispatcher,
39567
39792
  scheduler,
39793
+ probePr: probePrReadable,
39568
39794
  stopSubscriptions,
39569
39795
  backendFor,
39570
39796
  getSettings: () => getSettings(storage, home),
39571
39797
  setSettings: (s) => setSettings(storage, s, home),
39572
39798
  resetSettings: () => resetSettings(storage, home),
39799
+ getDispatchPreferences: (codexModels) => getDispatchPreferences(storage, getSettings(storage, home), home, codexModels),
39800
+ setDispatchPreference: (update, codexModels) => setDispatchPreference(storage, getSettings(storage, home), home, update, codexModels),
39573
39801
  activeTenants: opts.activeTenants,
39574
39802
  claudeBin: opts.claudeBin,
39575
39803
  codexBin: opts.codexBin,
@@ -39585,6 +39813,7 @@ var init_context = __esm({
39585
39813
  init_project();
39586
39814
  init_storage();
39587
39815
  init_settings();
39816
+ init_dispatch_preferences();
39588
39817
  init_quota();
39589
39818
  init_claude_quota();
39590
39819
  init_board();
@@ -42270,7 +42499,7 @@ var init_server = __esm({
42270
42499
  });
42271
42500
 
42272
42501
  // packages/server/src/repair.ts
42273
- import { existsSync as existsSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
42502
+ import { existsSync as existsSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "node:fs";
42274
42503
  import { basename as basename7, dirname as dirname13, resolve as resolve6 } from "node:path";
42275
42504
  function quoteValue(v) {
42276
42505
  if (/^[\w./#:+-]+$/.test(v)) return v;
@@ -42286,7 +42515,7 @@ function repairThreadFile(frizzDir, file) {
42286
42515
  if (dirname13(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
42287
42516
  if (!abs.endsWith(".md")) throw new RepairError(`refusing to repair "${file}": not a .md thread file`);
42288
42517
  if (!existsSync12(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
42289
- const content = readFileSync18(abs, "utf8");
42518
+ const content = readFileSync19(abs, "utf8");
42290
42519
  const firstLine2 = content.replace(/^/, "").split(/\r?\n/).find((l) => l.trim() !== "");
42291
42520
  if (firstLine2?.trim() === "---") {
42292
42521
  throw new RepairError(`${basename7(abs)} already opens with a "---" block \u2014 repair only heals a MISSING frontmatter block`);
@@ -42319,7 +42548,7 @@ var init_repair = __esm({
42319
42548
  });
42320
42549
 
42321
42550
  // cc-worker/hooks/bash-background.mjs
42322
- import { readFileSync as readFileSync19 } from "node:fs";
42551
+ import { readFileSync as readFileSync20 } from "node:fs";
42323
42552
  import { basename as basename8 } from "node:path";
42324
42553
  import { pathToFileURL } from "node:url";
42325
42554
  function emit(obj) {
@@ -42468,7 +42697,7 @@ var init_bash_background = __esm({
42468
42697
  if (isDirectHookExecution(process.argv[1], import.meta.url)) {
42469
42698
  try {
42470
42699
  const env = process.argv.includes("--frizz-thread") ? { ...process.env, FRIZZ_THREAD: process.env.FRIZZ_THREAD || "codex-worker" } : process.env;
42471
- emit(evaluateBashBackgroundHook(JSON.parse(readFileSync19(0, "utf8")), env));
42700
+ emit(evaluateBashBackgroundHook(JSON.parse(readFileSync20(0, "utf8")), env));
42472
42701
  } catch {
42473
42702
  emit({});
42474
42703
  }
@@ -42477,10 +42706,10 @@ var init_bash_background = __esm({
42477
42706
  });
42478
42707
 
42479
42708
  // packages/server/src/transcript.ts
42480
- import { closeSync as closeSync13, existsSync as existsSync13, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync20, readSync as readSync7, renameSync as renameSync9, statSync as statSync14, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
42709
+ import { closeSync as closeSync13, existsSync as existsSync13, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync21, readSync as readSync7, renameSync as renameSync9, statSync as statSync14, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
42481
42710
  import { createHash as createHash13 } from "node:crypto";
42482
42711
  import { StringDecoder as StringDecoder4 } from "node:string_decoder";
42483
- import { join as join33 } from "node:path";
42712
+ import { join as join34 } from "node:path";
42484
42713
  import { homedir as homedir14 } from "node:os";
42485
42714
  function formatTokens(n) {
42486
42715
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
@@ -43030,14 +43259,14 @@ function pruneScreenshotCache() {
43030
43259
  if (entries.length <= SCREENSHOT_CACHE_MAX) return;
43031
43260
  const byMtime = entries.map((n) => {
43032
43261
  try {
43033
- return { n, m: statSync14(join33(SCREENSHOT_CACHE_DIR, n)).mtimeMs };
43262
+ return { n, m: statSync14(join34(SCREENSHOT_CACHE_DIR, n)).mtimeMs };
43034
43263
  } catch {
43035
43264
  return { n, m: 0 };
43036
43265
  }
43037
43266
  }).sort((a, b) => b.m - a.m);
43038
43267
  for (const { n } of byMtime.slice(SCREENSHOT_CACHE_MAX)) {
43039
43268
  try {
43040
- unlinkSync5(join33(SCREENSHOT_CACHE_DIR, n));
43269
+ unlinkSync5(join34(SCREENSHOT_CACHE_DIR, n));
43041
43270
  } catch {
43042
43271
  }
43043
43272
  }
@@ -43067,14 +43296,14 @@ function persistBase64Image(mediaType, data, idKey) {
43067
43296
  const ext = IMAGE_MEDIA_EXT[typeof mediaType === "string" ? mediaType.toLowerCase() : ""];
43068
43297
  if (!ext || !data) return void 0;
43069
43298
  const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
43070
- const path = join33(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
43299
+ const path = join34(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
43071
43300
  try {
43072
43301
  if (existsSync13(path)) return path;
43073
43302
  if (data.length > SCREENSHOT_MAX_BASE64) return void 0;
43074
43303
  const buf = Buffer.from(data, "base64");
43075
43304
  if (buf.length === 0 || !looksLikeImage(buf, ext)) return void 0;
43076
43305
  mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
43077
- const tmp = join33(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43306
+ const tmp = join34(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43078
43307
  writeFileSync12(tmp, buf);
43079
43308
  renameSync9(tmp, path);
43080
43309
  pruneScreenshotCache();
@@ -43089,14 +43318,14 @@ function persistSentFile(srcPath, idKey) {
43089
43318
  const outExt = ext === "jpeg" ? "jpg" : ext;
43090
43319
  try {
43091
43320
  const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
43092
- const dest = join33(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
43321
+ const dest = join34(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
43093
43322
  if (existsSync13(dest)) return dest;
43094
43323
  const size = statSync14(srcPath).size;
43095
43324
  if (size === 0 || size > SENT_IMAGE_MAX_BYTES) return void 0;
43096
- const buf = readFileSync20(srcPath);
43325
+ const buf = readFileSync21(srcPath);
43097
43326
  if (!looksLikeImage(buf, outExt)) return void 0;
43098
43327
  mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
43099
- const tmp = join33(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43328
+ const tmp = join34(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43100
43329
  writeFileSync12(tmp, buf);
43101
43330
  renameSync9(tmp, dest);
43102
43331
  pruneScreenshotCache();
@@ -43513,7 +43742,7 @@ function readTranscript(project, sessionId) {
43513
43742
  }
43514
43743
  function verifyIncrementalParse(path, identityPrefix, incremental) {
43515
43744
  try {
43516
- const fresh = parseTranscript(readFileSync20(path, "utf8"), identityPrefix);
43745
+ const fresh = parseTranscript(readFileSync21(path, "utf8"), identityPrefix);
43517
43746
  const a = JSON.stringify(incremental);
43518
43747
  const b = JSON.stringify(fresh);
43519
43748
  if (a !== b) {
@@ -43533,16 +43762,16 @@ function verifyIncrementalParse(path, identityPrefix, incremental) {
43533
43762
  }
43534
43763
  }
43535
43764
  function logDirOf(project) {
43536
- return join33(homedir14(), ".claude", "projects", project.cwdSlug);
43765
+ return join34(homedir14(), ".claude", "projects", project.cwdSlug);
43537
43766
  }
43538
43767
  function resolveTranscriptPath(project, sessionId) {
43539
- const path = join33(logDirOf(project), `${sessionId}.jsonl`);
43768
+ const path = join34(logDirOf(project), `${sessionId}.jsonl`);
43540
43769
  try {
43541
43770
  if (statSync14(path).size > 0) return path;
43542
43771
  } catch {
43543
43772
  }
43544
43773
  const stranded = discoverTranscriptDir(logDirOf(project), sessionId);
43545
- return stranded ? join33(stranded, `${sessionId}.jsonl`) : path;
43774
+ return stranded ? join34(stranded, `${sessionId}.jsonl`) : path;
43546
43775
  }
43547
43776
  function projectCodexTranscript(raw2, identityPrefix = "codex") {
43548
43777
  const out = [];
@@ -44745,7 +44974,7 @@ function readEarlierThreadTranscriptPage(project, storage, slug, cursor, backend
44745
44974
  }
44746
44975
  function readCodexTranscriptFile(absPath, nativeId = absPath) {
44747
44976
  try {
44748
- return parseCodexTranscript(readFileSync20(absPath, "utf8"), `codex:${nativeId}`);
44977
+ return parseCodexTranscript(readFileSync21(absPath, "utf8"), `codex:${nativeId}`);
44749
44978
  } catch {
44750
44979
  return [];
44751
44980
  }
@@ -44843,7 +45072,7 @@ function projectTranscriptPageAgentLifecycles(page, lookup, peerLookup) {
44843
45072
  function readTranscriptFile(absPath) {
44844
45073
  try {
44845
45074
  const pathKey = createHash13("sha256").update(absPath).digest("base64url").slice(0, 16);
44846
- return parseTranscript(readFileSync20(absPath, "utf8"), `claude-file:${pathKey}`);
45075
+ return parseTranscript(readFileSync21(absPath, "utf8"), `claude-file:${pathKey}`);
44847
45076
  } catch {
44848
45077
  return [];
44849
45078
  }
@@ -44940,9 +45169,9 @@ var init_open_external = __esm({
44940
45169
 
44941
45170
  // packages/server/src/local-file.ts
44942
45171
  import { spawn as spawn6 } from "node:child_process";
44943
- import { readFileSync as readFileSync21, realpathSync as realpathSync8, statSync as statSync15 } from "node:fs";
45172
+ import { readFileSync as readFileSync22, realpathSync as realpathSync8, statSync as statSync15 } from "node:fs";
44944
45173
  import { homedir as homedir15 } from "node:os";
44945
- import { isAbsolute as isAbsolute3, join as join34, resolve as resolve7, sep } from "node:path";
45174
+ import { isAbsolute as isAbsolute3, join as join35, resolve as resolve7, sep } from "node:path";
44946
45175
  function isUnder(real, root) {
44947
45176
  let rootReal;
44948
45177
  try {
@@ -44972,7 +45201,7 @@ function resolveLocalFile(rawPath, roots) {
44972
45201
  function resolveOpenableFile(raw2, projectDir, roots, home = homedir15()) {
44973
45202
  const trimmed = raw2.trim().replace(/:\d+(?::\d+)?$/, "");
44974
45203
  if (!trimmed) return null;
44975
- const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ? join34(home, trimmed.slice(2)) : isAbsolute3(trimmed) ? trimmed : resolve7(projectDir, trimmed);
45204
+ const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ? join35(home, trimmed.slice(2)) : isAbsolute3(trimmed) ? trimmed : resolve7(projectDir, trimmed);
44976
45205
  try {
44977
45206
  return resolveLocalFile(abs, roots);
44978
45207
  } catch {
@@ -44983,7 +45212,7 @@ function readLocalMarkdown(rawPath, roots) {
44983
45212
  if (!MARKDOWN_FILE_EXT.test(rawPath.trim())) throw new Error("Local file is not a Markdown file");
44984
45213
  const path = resolveLocalFile(rawPath, roots);
44985
45214
  if (!MARKDOWN_FILE_EXT.test(path)) throw new Error("Local file is not a Markdown file");
44986
- const bytes = readFileSync21(path);
45215
+ const bytes = readFileSync22(path);
44987
45216
  if (bytes.length <= MARKDOWN_READ_LIMIT) return { path, markdown: bytes.toString("utf8"), truncated: false };
44988
45217
  const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
44989
45218
  const lastBreak = head.lastIndexOf(10);
@@ -45004,7 +45233,7 @@ var MARKDOWN_FILE_EXT, MARKDOWN_READ_LIMIT;
45004
45233
  var init_local_file = __esm({
45005
45234
  "packages/server/src/local-file.ts"() {
45006
45235
  "use strict";
45007
- MARKDOWN_FILE_EXT = /\.(?:md|markdown)$/i;
45236
+ MARKDOWN_FILE_EXT = /\.(?:md|mdx|markdown)$/i;
45008
45237
  MARKDOWN_READ_LIMIT = 1024 * 1024;
45009
45238
  }
45010
45239
  });
@@ -45330,67 +45559,6 @@ var init_account_actions = __esm({
45330
45559
  }
45331
45560
  });
45332
45561
 
45333
- // packages/server/src/dispatch-preferences.ts
45334
- function permissionFor(backend, mode) {
45335
- if (backend === "codex") {
45336
- if (mode === "plan" || mode === "bypassPermissions") return mode;
45337
- return "default";
45338
- }
45339
- return mode === "plan" ? "auto" : mode;
45340
- }
45341
- function defaultDispatchPreferences(settings, codexModels = []) {
45342
- const backend = settings.backend ?? (codexModels.some((model) => model.slug === settings.model) ? "codex" : "claude");
45343
- const selected = {
45344
- ...settings.model ? { model: settings.model } : {},
45345
- ...settings.effort ? { effort: settings.effort } : {},
45346
- permissionMode: permissionFor(backend, settings.permissionMode)
45347
- };
45348
- return {
45349
- backend,
45350
- claude: backend === "claude" ? selected : { permissionMode: "auto" },
45351
- codex: backend === "codex" ? selected : { permissionMode: "default" }
45352
- };
45353
- }
45354
- function getDispatchPreferences(storage, settings, codexModels = []) {
45355
- const parsed = DispatchPreferences.safeParse(storage.getSetting(KEY));
45356
- return parsed.success ? parsed.data : defaultDispatchPreferences(settings, codexModels);
45357
- }
45358
- function setDispatchPreference(storage, settings, update, codexModels = []) {
45359
- const current = getDispatchPreferences(storage, settings, codexModels);
45360
- let next;
45361
- if (update.field === "backend") {
45362
- next = { ...current, backend: update.value };
45363
- } else if (update.field === "profile") {
45364
- next = {
45365
- ...current,
45366
- backend: update.backend,
45367
- [update.backend]: {
45368
- ...current[update.backend],
45369
- model: update.model,
45370
- effort: update.effort
45371
- }
45372
- };
45373
- } else {
45374
- const profile = current[update.backend];
45375
- next = {
45376
- ...current,
45377
- ...update.field === "model" ? { backend: update.backend } : {},
45378
- [update.backend]: { ...profile, [update.field]: update.value }
45379
- };
45380
- }
45381
- const validated = DispatchPreferences.parse(next);
45382
- storage.setSetting(KEY, validated);
45383
- return validated;
45384
- }
45385
- var KEY;
45386
- var init_dispatch_preferences = __esm({
45387
- "packages/server/src/dispatch-preferences.ts"() {
45388
- "use strict";
45389
- init_src();
45390
- KEY = "dispatch-preferences.v1";
45391
- }
45392
- });
45393
-
45394
45562
  // packages/server/src/external-terminal.ts
45395
45563
  function shellQuote(value) {
45396
45564
  return `'${value.replace(/'/g, `'"'"'`)}'`;
@@ -45743,11 +45911,11 @@ var init_directory_picker = __esm({
45743
45911
  });
45744
45912
 
45745
45913
  // packages/server/src/router.ts
45746
- import { readFileSync as readFileSync22, statSync as statSync17 } from "node:fs";
45747
- import { join as join35, resolve as resolve8 } from "node:path";
45914
+ import { readFileSync as readFileSync23, statSync as statSync17 } from "node:fs";
45915
+ import { join as join36, resolve as resolve8 } from "node:path";
45748
45916
  import { randomUUID as randomUUID16 } from "node:crypto";
45749
45917
  import { basename as basename9, dirname as dirname14 } from "node:path";
45750
- import { existsSync as existsSync14, mkdirSync as mkdirSync16, rmSync as rmSync9, writeFileSync as writeFileSync13 } from "node:fs";
45918
+ import { existsSync as existsSync14, mkdirSync as mkdirSync16, rmSync as rmSync10, writeFileSync as writeFileSync13 } from "node:fs";
45751
45919
  import { homedir as homedir16 } from "node:os";
45752
45920
  function validateGithubDispatchProfile(input) {
45753
45921
  validateThreadProfile(input.backend, input.model, input.effort);
@@ -45907,7 +46075,7 @@ function projectCard(entry, stale) {
45907
46075
  function addProjectAtPath(input) {
45908
46076
  const typed = input.trim();
45909
46077
  if (!typed) throw new Error("Enter a folder path.");
45910
- const expanded = typed === "~" || typed.startsWith("~/") ? join35(homedir16(), typed.slice(1)) : typed;
46078
+ const expanded = typed === "~" || typed.startsWith("~/") ? join36(homedir16(), typed.slice(1)) : typed;
45911
46079
  const absolute = resolve8(expanded);
45912
46080
  let stats;
45913
46081
  try {
@@ -45938,12 +46106,12 @@ function storeProjectIcon(id, name, bytes) {
45938
46106
  mkdirSync16(dirname14(path), { recursive: true });
45939
46107
  writeFileSync13(path, bytes);
45940
46108
  if (!imageDimensions(path)) {
45941
- rmSync9(path, { force: true });
46109
+ rmSync10(path, { force: true });
45942
46110
  throw new Error("That file does not look like an image Frizz can draw.");
45943
46111
  }
45944
46112
  for (const stale of PROJECT_ICON_EXTENSIONS) {
45945
46113
  const other = customIconPath(id, `.${stale}`);
45946
- if (other !== path) rmSync9(other, { force: true });
46114
+ if (other !== path) rmSync10(other, { force: true });
45947
46115
  }
45948
46116
  const updated = setProjectIcon(id, path);
45949
46117
  if (!updated) throw new Error("No such project.");
@@ -45952,7 +46120,7 @@ function storeProjectIcon(id, name, bytes) {
45952
46120
  function setProjectIconFromFile(id, file) {
45953
46121
  let bytes;
45954
46122
  try {
45955
- bytes = readFileSync22(file);
46123
+ bytes = readFileSync23(file);
45956
46124
  } catch {
45957
46125
  throw new Error("That file could not be read.");
45958
46126
  }
@@ -45960,7 +46128,7 @@ function setProjectIconFromFile(id, file) {
45960
46128
  return storeProjectIcon(id, file, bytes);
45961
46129
  }
45962
46130
  function createRouter(ctx) {
45963
- const frizzDir = join35(ctx.project.dir, ".frizz");
46131
+ const frizzDir = join36(ctx.project.dir, ".frizz");
45964
46132
  const openRoots = openableFileRoots(ctx.project);
45965
46133
  function isAutoTitledSession(slug) {
45966
46134
  return ctx.storage.getSession(slug)?.title_auto === 1;
@@ -47094,6 +47262,12 @@ ${gapNote}` : input.message;
47094
47262
  if (forMs === null) {
47095
47263
  throw new Error(`\`for: ${input.for}\` is not a duration \u2014 give one like \`30m\`, \`2h\` or \`3d\` (max 24h)`);
47096
47264
  }
47265
+ const probe = await ctx.probePr(ref);
47266
+ if (!probe.ok) {
47267
+ throw new Error(
47268
+ `\`${target}\` cannot be watched \u2014 the server's \`gh\` could not read it: ${probe.reason}. Frizz polls with the \`gh\` of the process it runs as, not yours: check \`gh auth status\` there and that the repo is reachable, then register again. If GitHub itself was briefly down, registering again in a minute is enough.`
47269
+ );
47270
+ }
47097
47271
  const now = Date.now();
47098
47272
  const id = `prw_${randomUUID16().replace(/-/g, "").slice(0, 12)}`;
47099
47273
  ctx.storage.armPrWatch({ id, slug: input.slug, owner: ref.owner, repo: ref.repo, number: ref.number, createdAtMs: now, expiresAtMs: now + forMs });
@@ -47561,7 +47735,7 @@ ${gapNote}` : input.message;
47561
47735
  output: ProjectCard,
47562
47736
  handler: async ({ input }) => {
47563
47737
  for (const extension2 of PROJECT_ICON_EXTENSIONS) {
47564
- rmSync9(customIconPath(input.id, `.${extension2}`), { force: true });
47738
+ rmSync10(customIconPath(input.id, `.${extension2}`), { force: true });
47565
47739
  }
47566
47740
  const updated = clearProjectIcon(input.id);
47567
47741
  if (!updated) throw new Error("No such project.");
@@ -47615,7 +47789,7 @@ ${gapNote}` : input.message;
47615
47789
  for (const entry of listProjects()) {
47616
47790
  if (entry.stale) continue;
47617
47791
  try {
47618
- const db = new sqlite_default(join35(projectStateDir(entry.id), "ui.db"), { readonly: true });
47792
+ const db = new sqlite_default(join36(projectStateDir(entry.id), "ui.db"), { readonly: true });
47619
47793
  try {
47620
47794
  const hit = db.prepare("SELECT 1 FROM session WHERE slug = ? LIMIT 1").get(input.slug);
47621
47795
  if (hit) found.push({ projectSlug: entry.slug, projectName: entry.name ?? entry.slug });
@@ -47645,12 +47819,12 @@ ${gapNote}` : input.message;
47645
47819
  }),
47646
47820
  dispatchPreferencesGet: query({
47647
47821
  output: DispatchPreferences,
47648
- handler: async () => getDispatchPreferences(ctx.storage, ctx.getSettings(), readCodexModels())
47822
+ handler: async () => ctx.getDispatchPreferences(readCodexModels())
47649
47823
  }),
47650
47824
  dispatchPreferenceSet: mutation({
47651
47825
  input: SetDispatchPreferenceInput,
47652
47826
  output: DispatchPreferences,
47653
- handler: async ({ input }) => setDispatchPreference(ctx.storage, ctx.getSettings(), input, readCodexModels())
47827
+ handler: async ({ input }) => ctx.setDispatchPreference(input, readCodexModels())
47654
47828
  }),
47655
47829
  // The shipped GitHub batch-dispatch prompt template (single source of truth: server/github.ts).
47656
47830
  // The Settings UI reads it to prefill the editor and to power "reset to default"; an empty/unset
@@ -47761,7 +47935,6 @@ var init_router5 = __esm({
47761
47935
  init_thread_profiles();
47762
47936
  init_adoption_recovery();
47763
47937
  init_awaiting();
47764
- init_dispatch_preferences();
47765
47938
  init_storage();
47766
47939
  init_external_terminal();
47767
47940
  init_background_shell_output();
@@ -47972,7 +48145,7 @@ var init_compression = __esm({
47972
48145
  });
47973
48146
 
47974
48147
  // packages/server/src/local-image.ts
47975
- import { readFileSync as readFileSync23, realpathSync as realpathSync9, statSync as statSync18 } from "node:fs";
48148
+ import { readFileSync as readFileSync24, realpathSync as realpathSync9, statSync as statSync18 } from "node:fs";
47976
48149
  import { extname as extname2, isAbsolute as isAbsolute4 } from "node:path";
47977
48150
  function resolveLocalImage(rawPath) {
47978
48151
  if (!rawPath || !isAbsolute4(rawPath)) return { status: 400 };
@@ -47986,7 +48159,7 @@ function resolveLocalImage(rawPath) {
47986
48159
  }
47987
48160
  try {
47988
48161
  if (!statSync18(real).isFile()) return { status: 404 };
47989
- return { status: 200, contentType, body: readFileSync23(real) };
48162
+ return { status: 200, contentType, body: readFileSync24(real) };
47990
48163
  } catch {
47991
48164
  return { status: 404 };
47992
48165
  }
@@ -48006,9 +48179,9 @@ var init_local_image = __esm({
48006
48179
  });
48007
48180
 
48008
48181
  // packages/server/src/project-icon.ts
48009
- import { readFileSync as readFileSync24, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync19 } from "node:fs";
48182
+ import { readFileSync as readFileSync25, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync19 } from "node:fs";
48010
48183
  import { homedir as homedir17 } from "node:os";
48011
- import { basename as basename10, dirname as dirname15, extname as extname3, join as join36, relative, sep as sep2 } from "node:path";
48184
+ import { basename as basename10, dirname as dirname15, extname as extname3, join as join37, relative, sep as sep2 } from "node:path";
48012
48185
  function normalizeStem(stem) {
48013
48186
  const sized = stem.toLowerCase().replace(/[@-]\d+(\.\d+)?x(\d+)?$/u, "").replace(/[-_]\d{2,4}$/u, "");
48014
48187
  const normalized = sized.replace(VARIANT_SUFFIX, "");
@@ -48026,7 +48199,7 @@ function sizeScore(dimensions) {
48026
48199
  return Math.max(0, Math.min(40, Math.round(Math.log2(edge / 24) * 8)));
48027
48200
  }
48028
48201
  function under(root, relativeDirectory) {
48029
- return relativeDirectory ? join36(root, relativeDirectory) : root;
48202
+ return relativeDirectory ? join37(root, relativeDirectory) : root;
48030
48203
  }
48031
48204
  function listFiles(directory) {
48032
48205
  try {
@@ -48048,19 +48221,19 @@ function iconDirectories(root) {
48048
48221
  for (const asset of ASSET_DIRECTORIES) directories.push(under(root, asset));
48049
48222
  for (const host of HOST_DIRECTORIES) {
48050
48223
  if (!children2.has(host)) continue;
48051
- for (const asset of ASSET_DIRECTORIES) directories.push(under(join36(root, host), asset));
48224
+ for (const asset of ASSET_DIRECTORIES) directories.push(under(join37(root, host), asset));
48052
48225
  }
48053
48226
  for (const extra of EXTRA_DIRECTORIES) directories.push(under(root, extra));
48054
48227
  for (const container of WORKSPACE_CONTAINERS) {
48055
48228
  if (!children2.has(container)) continue;
48056
48229
  let members;
48057
48230
  try {
48058
- members = readdirSync10(join36(root, container), { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
48231
+ members = readdirSync10(join37(root, container), { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
48059
48232
  } catch {
48060
48233
  continue;
48061
48234
  }
48062
48235
  for (const member of members) {
48063
- for (const asset of ASSET_DIRECTORIES) directories.push(under(join36(root, container, member), asset));
48236
+ for (const asset of ASSET_DIRECTORIES) directories.push(under(join37(root, container, member), asset));
48064
48237
  }
48065
48238
  }
48066
48239
  return [...new Set(directories)];
@@ -48068,13 +48241,13 @@ function iconDirectories(root) {
48068
48241
  function manifestIcons(manifestPath) {
48069
48242
  let parsed;
48070
48243
  try {
48071
- parsed = JSON.parse(readFileSync24(manifestPath, "utf8"));
48244
+ parsed = JSON.parse(readFileSync25(manifestPath, "utf8"));
48072
48245
  } catch {
48073
48246
  return [];
48074
48247
  }
48075
48248
  if (!Array.isArray(parsed?.icons)) return [];
48076
48249
  const directory = dirname15(manifestPath);
48077
- return parsed.icons.map((icon) => typeof icon?.src === "string" ? icon.src : void 0).filter((src) => !!src && !/^(https?:)?\/\//u.test(src) && !src.startsWith("data:")).map((src) => join36(directory, src.replace(/[?#].*$/u, "").replace(/^\//u, "")));
48250
+ return parsed.icons.map((icon) => typeof icon?.src === "string" ? icon.src : void 0).filter((src) => !!src && !/^(https?:)?\/\//u.test(src) && !src.startsWith("data:")).map((src) => join37(directory, src.replace(/[?#].*$/u, "").replace(/^\//u, "")));
48078
48251
  }
48079
48252
  function projectIconCandidates(root) {
48080
48253
  const projectStem = normalizeStem(basename10(root)).normalized;
@@ -48093,7 +48266,7 @@ function projectIconCandidates(root) {
48093
48266
  for (const name of listFiles(directory)) {
48094
48267
  const extension2 = extname3(name).toLowerCase();
48095
48268
  if (MANIFEST_NAMES.includes(name.toLowerCase())) {
48096
- for (const icon of manifestIcons(join36(directory, name))) {
48269
+ for (const icon of manifestIcons(join37(directory, name))) {
48097
48270
  if (MEASURABLE_IMAGE_EXTENSIONS.has(extname3(icon).toLowerCase())) {
48098
48271
  fromManifest.add(icon);
48099
48272
  files.push({ path: icon, directory: dirname15(icon) });
@@ -48104,7 +48277,7 @@ function projectIconCandidates(root) {
48104
48277
  if (!MEASURABLE_IMAGE_EXTENSIONS.has(extension2)) continue;
48105
48278
  const stem = name.slice(0, name.length - extension2.length);
48106
48279
  if (normalizeStem(stem).normalized === "favicon") iconHomes.add(directory);
48107
- files.push({ path: join36(directory, name), directory });
48280
+ files.push({ path: join37(directory, name), directory });
48108
48281
  if (files.length >= MAX_CANDIDATES) break;
48109
48282
  }
48110
48283
  }
@@ -48169,7 +48342,7 @@ function resolveProjectIconResponse(id, options = {}) {
48169
48342
  });
48170
48343
  if (!roots.some((root) => real === root || real.startsWith(root + sep2))) return { status: 404 };
48171
48344
  try {
48172
- return { status: 200, contentType, body: readFileSync24(real) };
48345
+ return { status: 200, contentType, body: readFileSync25(real) };
48173
48346
  } catch {
48174
48347
  return { status: 404 };
48175
48348
  }
@@ -48281,8 +48454,8 @@ var init_project_icon = __esm({
48281
48454
  });
48282
48455
 
48283
48456
  // packages/server/src/local-visualization.ts
48284
- import { readdirSync as readdirSync11, readFileSync as readFileSync25, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
48285
- import { join as join37, sep as sep3 } from "node:path";
48457
+ import { readdirSync as readdirSync11, readFileSync as readFileSync26, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
48458
+ import { join as join38, sep as sep3 } from "node:path";
48286
48459
  function children(path, pattern) {
48287
48460
  try {
48288
48461
  return readdirSync11(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && pattern.test(entry.name)).map((entry) => entry.name).sort().reverse();
@@ -48294,7 +48467,7 @@ function isUnder2(path, root) {
48294
48467
  return path === root || path.startsWith(root.endsWith(sep3) ? root : root + sep3);
48295
48468
  }
48296
48469
  function resolveFragment(projectDir, sessionId, file) {
48297
- const base = join37(projectDir, ".codex", "visualizations");
48470
+ const base = join38(projectDir, ".codex", "visualizations");
48298
48471
  let projectReal;
48299
48472
  let baseReal;
48300
48473
  try {
@@ -48305,12 +48478,12 @@ function resolveFragment(projectDir, sessionId, file) {
48305
48478
  }
48306
48479
  if (!isUnder2(baseReal, projectReal)) return null;
48307
48480
  for (const year of children(base, YEAR_PART)) {
48308
- const yearDir = join37(base, year);
48481
+ const yearDir = join38(base, year);
48309
48482
  for (const month of children(yearDir, DATE_PART)) {
48310
- const monthDir = join37(yearDir, month);
48483
+ const monthDir = join38(yearDir, month);
48311
48484
  for (const day of children(monthDir, DATE_PART)) {
48312
- const sessionRoot = join37(baseReal, year, month, day, sessionId);
48313
- const candidate = join37(monthDir, day, sessionId, file);
48485
+ const sessionRoot = join38(baseReal, year, month, day, sessionId);
48486
+ const candidate = join38(monthDir, day, sessionId, file);
48314
48487
  try {
48315
48488
  const real = realpathSync11(candidate);
48316
48489
  if (isUnder2(real, sessionRoot) && statSync20(real).isFile()) return real;
@@ -48396,7 +48569,7 @@ function resolveLocalVisualization(projectDir, sessionId, file) {
48396
48569
  try {
48397
48570
  const size = statSync20(path).size;
48398
48571
  if (size > MAX_FRAGMENT_BYTES) return { status: 413 };
48399
- fragment = readFileSync25(path, "utf8");
48572
+ fragment = readFileSync26(path, "utf8");
48400
48573
  } catch {
48401
48574
  return { status: 404 };
48402
48575
  }
@@ -48439,7 +48612,7 @@ var init_local_visualization = __esm({
48439
48612
 
48440
48613
  // packages/server/src/app.ts
48441
48614
  import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync14 } from "node:fs";
48442
- import { join as join38 } from "node:path";
48615
+ import { join as join39 } from "node:path";
48443
48616
  import { randomUUID as randomUUID17 } from "node:crypto";
48444
48617
  function createApp(ctx, options = {}) {
48445
48618
  const app = new Hono2();
@@ -48543,10 +48716,10 @@ function createApp(ctx, options = {}) {
48543
48716
  if (typeof body.data !== "string" || body.data.length > ATTACHMENT_MAX_BASE64_CHARS) return c.json({ error: "bad payload" }, 400);
48544
48717
  const ext = `.${attachmentExtension(name)}`;
48545
48718
  const buf = Buffer.from(body.data, "base64");
48546
- const dir = join38(ctx.project.stateDir, "attachments");
48719
+ const dir = join39(ctx.project.stateDir, "attachments");
48547
48720
  mkdirSync17(dir, { recursive: true });
48548
48721
  const base = name.replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9_-]+/g, "-").slice(0, 40) || "file";
48549
- const path = join38(dir, `${Date.now()}-${randomUUID17().slice(0, 8)}-${base}${ext}`);
48722
+ const path = join39(dir, `${Date.now()}-${randomUUID17().slice(0, 8)}-${base}${ext}`);
48550
48723
  writeFileSync14(path, buf);
48551
48724
  return c.json({ path });
48552
48725
  });
@@ -49785,10 +49958,10 @@ var init_app_socket = __esm({
49785
49958
  });
49786
49959
 
49787
49960
  // packages/server/src/boot-progress.ts
49788
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync26, renameSync as renameSync10, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "node:fs";
49789
- import { basename as basename11, dirname as dirname16, join as join39 } from "node:path";
49961
+ import { mkdirSync as mkdirSync18, readFileSync as readFileSync27, renameSync as renameSync10, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
49962
+ import { basename as basename11, dirname as dirname16, join as join40 } from "node:path";
49790
49963
  function bootProgressPath(stateDir) {
49791
- return join39(stateDir, BOOT_PROGRESS_NAME);
49964
+ return join40(stateDir, BOOT_PROGRESS_NAME);
49792
49965
  }
49793
49966
  function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
49794
49967
  if (!stateDir) {
@@ -49809,7 +49982,7 @@ function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
49809
49982
  step++;
49810
49983
  try {
49811
49984
  mkdirSync18(dirname16(path), { recursive: true, mode: 448 });
49812
- const temp = join39(dirname16(path), `.${basename11(path)}.${process.pid}.tmp`);
49985
+ const temp = join40(dirname16(path), `.${basename11(path)}.${process.pid}.tmp`);
49813
49986
  writeFileSync15(temp, `${JSON.stringify({ pid: process.pid, step, phase, at: new Date(now).toISOString() })}
49814
49987
  `, "utf8");
49815
49988
  renameSync10(temp, path);
@@ -49819,7 +49992,7 @@ function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
49819
49992
  return Object.assign(write, {
49820
49993
  done: () => {
49821
49994
  try {
49822
- rmSync10(path, { force: true });
49995
+ rmSync11(path, { force: true });
49823
49996
  } catch {
49824
49997
  }
49825
49998
  }
@@ -49926,11 +50099,11 @@ __export(index_exports, {
49926
50099
  unknownProjectPage: () => unknownProjectPage
49927
50100
  });
49928
50101
  import { createServer } from "node:http";
49929
- import { readFileSync as readFileSync27, existsSync as existsSync15 } from "node:fs";
49930
- import { dirname as dirname17, join as join40, resolve as resolve9, extname as extname4, normalize } from "node:path";
50102
+ import { readFileSync as readFileSync28, existsSync as existsSync15 } from "node:fs";
50103
+ import { dirname as dirname17, join as join41, resolve as resolve9, extname as extname4, normalize } from "node:path";
49931
50104
  function readServerAddressHolder(path) {
49932
50105
  try {
49933
- const value = JSON.parse(readFileSync27(path, "utf8"));
50106
+ const value = JSON.parse(readFileSync28(path, "utf8"));
49934
50107
  if (typeof value?.pid !== "number") return void 0;
49935
50108
  return { pid: value.pid, port: typeof value.port === "number" ? value.port : -1 };
49936
50109
  } catch {
@@ -50030,11 +50203,11 @@ function createShutdownSignalHandler(options) {
50030
50203
  }
50031
50204
  function serveStatic(distDir, req, res) {
50032
50205
  const rel = normalize((req.url ?? "/").split("?")[0]).replace(/^(\.\.[/\\])+/, "");
50033
- let file = join40(distDir, rel === "/" ? "index.html" : rel);
50034
- if (!file.startsWith(distDir)) file = join40(distDir, "index.html");
50035
- if (!existsSync15(file)) file = join40(distDir, "index.html");
50206
+ let file = join41(distDir, rel === "/" ? "index.html" : rel);
50207
+ if (!file.startsWith(distDir)) file = join41(distDir, "index.html");
50208
+ if (!existsSync15(file)) file = join41(distDir, "index.html");
50036
50209
  try {
50037
- const body = readFileSync27(file);
50210
+ const body = readFileSync28(file);
50038
50211
  res.writeHead(200, { "content-type": MIME[extname4(file)] ?? "application/octet-stream" });
50039
50212
  res.end(body);
50040
50213
  } catch {
@@ -50383,7 +50556,7 @@ async function startServer(opts = {}) {
50383
50556
  }
50384
50557
  statusPath = serverLockPathFor(ctx.project);
50385
50558
  const webRoot = resolve9(import.meta.dirname, "..", "..", "web");
50386
- const distDir = opts.webDistDir ? resolve9(opts.webDistDir) : join40(webRoot, "dist");
50559
+ const distDir = opts.webDistDir ? resolve9(opts.webDistDir) : join41(webRoot, "dist");
50387
50560
  startupPhase = "Vite";
50388
50561
  if (opts.dev) {
50389
50562
  try {
@@ -50459,7 +50632,7 @@ async function startServer(opts = {}) {
50459
50632
  if (vite) {
50460
50633
  vite.middlewares(req, res, () => {
50461
50634
  try {
50462
- const html = readFileSync27(join40(webRoot, "index.html"), "utf8");
50635
+ const html = readFileSync28(join41(webRoot, "index.html"), "utf8");
50463
50636
  void vite.transformIndexHtml(url, html).then((out) => {
50464
50637
  res.writeHead(200, { "content-type": "text/html" });
50465
50638
  res.end(out);
@@ -50651,7 +50824,7 @@ var init_index = __esm({
50651
50824
  }
50652
50825
  };
50653
50826
  isApiUrl = (url) => url === FRIZZ_ROUTE_PREFIX || url.startsWith(`${FRIZZ_ROUTE_PREFIX}/`);
50654
- serverLockPathFor = (project) => join40(project.stateDir, "server.lock");
50827
+ serverLockPathFor = (project) => join41(project.stateDir, "server.lock");
50655
50828
  MIME = {
50656
50829
  ".html": "text/html",
50657
50830
  ".js": "text/javascript",
@@ -50670,7 +50843,7 @@ init_project_launch();
50670
50843
  init_shutdown();
50671
50844
  init_logging();
50672
50845
  import { existsSync as existsSync16 } from "node:fs";
50673
- import { join as join41 } from "node:path";
50846
+ import { join as join42 } from "node:path";
50674
50847
  process.on("uncaughtException", (error) => {
50675
50848
  log.error("dev-child", `uncaught exception: ${error instanceof Error ? error.stack ?? error.message : error}`);
50676
50849
  process.exit(1);
@@ -50710,7 +50883,7 @@ try {
50710
50883
  ];
50711
50884
  if (!existsSync16(stableWebDist)) throw new Error("stable artifact launch is missing its verified web directory");
50712
50885
  for (const [name, directory, requiredFile] of required) {
50713
- if (!directory || !existsSync16(join41(directory, requiredFile)))
50886
+ if (!directory || !existsSync16(join42(directory, requiredFile)))
50714
50887
  throw new Error(`stable artifact launch is missing verified ${name}`);
50715
50888
  }
50716
50889
  }