frizz-server 0.15.12 → 0.15.13

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 (100) hide show
  1. package/dist/dev-child.js +244 -121
  2. package/package.json +1 -1
  3. package/runtime/cc-worker/bin/frizz-mcp.mjs +16 -11
  4. package/runtime/cc-worker/hooks/session-seed.mjs +1 -1
  5. package/web-dist/assets/{abnfDiagram-VRR7QNED-CnXrrU0W.js → abnfDiagram-VRR7QNED-BBUrh3y4.js} +1 -1
  6. package/web-dist/assets/{arc-CmRXAX4k.js → arc-CzSC2w85.js} +1 -1
  7. package/web-dist/assets/architecture-TIHT7OUA-Bkytur05.js +1 -0
  8. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-Pdv71Zuq.js → architectureDiagram-ZJ3FMSHR-BZdFvCPS.js} +1 -1
  9. package/web-dist/assets/{blockDiagram-677ZJIJ3-B0b2_ZpN.js → blockDiagram-677ZJIJ3-CFw4HlAZ.js} +1 -1
  10. package/web-dist/assets/{c4Diagram-LMCZKHZV-GSc1L18t.js → c4Diagram-LMCZKHZV-BeWg4GGT.js} +1 -1
  11. package/web-dist/assets/channel-CRbCFAU9.js +1 -0
  12. package/web-dist/assets/{chunk-32BRIVSS-C0vobooW.js → chunk-32BRIVSS-Q_OGIKCm.js} +1 -1
  13. package/web-dist/assets/{chunk-52WLFC77-15f3pZkP.js → chunk-52WLFC77-C2ghSBWi.js} +1 -1
  14. package/web-dist/assets/{chunk-C7G6YPKG-m8AKg2cJ.js → chunk-C7G6YPKG-E7JHlQhC.js} +1 -1
  15. package/web-dist/assets/{chunk-EX3LRPZG-BW0qg3im.js → chunk-EX3LRPZG-c7FlR3e8.js} +1 -1
  16. package/web-dist/assets/{chunk-FWX5IMBZ-Ds3wze9N.js → chunk-FWX5IMBZ-1mU_aS7O.js} +2 -2
  17. package/web-dist/assets/{chunk-HOUHSVGY-B01xY05R.js → chunk-HOUHSVGY-DLJJRwLZ.js} +1 -1
  18. package/web-dist/assets/{chunk-ICXQ74PX-DLoF7WQO.js → chunk-ICXQ74PX-D7jDcuWv.js} +1 -1
  19. package/web-dist/assets/{chunk-MOJQB5TN-q3Li6yTJ.js → chunk-MOJQB5TN-C-KYsjX1.js} +1 -1
  20. package/web-dist/assets/{chunk-OGEWGWER-CgMb4Plp.js → chunk-OGEWGWER-Dnfz8Ffe.js} +1 -1
  21. package/web-dist/assets/{chunk-PUDLZKDR-DjLXqXCy.js → chunk-PUDLZKDR-BdzmoKkv.js} +1 -1
  22. package/web-dist/assets/{chunk-Q4XR5HBZ-DgVAx69q.js → chunk-Q4XR5HBZ-HNAZNAAv.js} +1 -1
  23. package/web-dist/assets/{chunk-RYQCIY6F-jOR4GLsC.js → chunk-RYQCIY6F-e5BApeF1.js} +1 -1
  24. package/web-dist/assets/{chunk-V7JOEXUC-N7ybBPSc.js → chunk-V7JOEXUC-cK1Hu1b4.js} +1 -1
  25. package/web-dist/assets/{chunk-VAUOI2AC-nUxrqZNY.js → chunk-VAUOI2AC-Df9HZp6Z.js} +1 -1
  26. package/web-dist/assets/{chunk-VR4S4FIN-6hmO23ny.js → chunk-VR4S4FIN-D6CAtMMq.js} +1 -1
  27. package/web-dist/assets/{chunk-WYO6CB5R--VNr0O3X.js → chunk-WYO6CB5R-JyE0Wb29.js} +1 -1
  28. package/web-dist/assets/{chunk-XXDRQBXY-nI5LprjB.js → chunk-XXDRQBXY-D_O0RmZ-.js} +1 -1
  29. package/web-dist/assets/{chunk-ZGVPDNZ5-BqBpuih6.js → chunk-ZGVPDNZ5-TKNm5tbx.js} +1 -1
  30. package/web-dist/assets/classDiagram-OUVF2IWQ-DV4x88Gs.js +1 -0
  31. package/web-dist/assets/classDiagram-v2-EOCWNBFH-DV4x88Gs.js +1 -0
  32. package/web-dist/assets/{cose-bilkent-JH36ORCC-DDZerLcw.js → cose-bilkent-JH36ORCC-aJ2vJo3h.js} +1 -1
  33. package/web-dist/assets/{cynefin-VYW2F7L2-e1KR3fxM.js → cynefin-VYW2F7L2-BzC5SCFm.js} +1 -1
  34. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-C8BKMn0G.js → cynefinDiagram-TSTJHNR4-BUBneCyZ.js} +1 -1
  35. package/web-dist/assets/{dagre-VKFMJZFB-BmMWANIX.js → dagre-VKFMJZFB-CyqWV8--.js} +1 -1
  36. package/web-dist/assets/{diagram-FQU43EPY-dy3NpwvL.js → diagram-FQU43EPY-4Mmv21C-.js} +1 -1
  37. package/web-dist/assets/{diagram-G47NLZAW-Cpu90AXQ.js → diagram-G47NLZAW-C2RFiyED.js} +1 -1
  38. package/web-dist/assets/{diagram-NH7WQ7WH-5SHLGdgc.js → diagram-NH7WQ7WH-hZnAY_wV.js} +1 -1
  39. package/web-dist/assets/{diagram-OA4YK3LP-CLPs2sBw.js → diagram-OA4YK3LP-K1Zd1JAb.js} +1 -1
  40. package/web-dist/assets/{diagram-WEI45ONY-CMEkLLze.js → diagram-WEI45ONY-CHKaRrZp.js} +1 -1
  41. package/web-dist/assets/{dist-rUBtQxB1.js → dist-ImoGVzkQ.js} +1 -1
  42. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-ClagaphN.js → ebnfDiagram-CCIWWBDH-BMR3sXl5.js} +1 -1
  43. package/web-dist/assets/{erDiagram-Q63AITRT-BEXXtSN9.js → erDiagram-Q63AITRT-DZmU3YbK.js} +1 -1
  44. package/web-dist/assets/eventmodeling-45OFAUF4-DGvQ-Cb8.js +1 -0
  45. package/web-dist/assets/flowDiagram-23GEKE2U-COGNeA6-.js +1 -0
  46. package/web-dist/assets/{ganttDiagram-NO4QXBWP-BoI4Xvme.js → ganttDiagram-NO4QXBWP-0cAynm96.js} +1 -1
  47. package/web-dist/assets/{gitGraph-TEB2WS4Q-D5Xdj6HL.js → gitGraph-TEB2WS4Q-DQgaAaBP.js} +1 -1
  48. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-o_hWIe-e.js → gitGraphDiagram-IHSO6WYX-BnULFM88.js} +1 -1
  49. package/web-dist/assets/index-Cs3gPFNZ.js +330 -0
  50. package/web-dist/assets/index-DA4tWPyA.css +2 -0
  51. package/web-dist/assets/{info-DKCQHKI2-BwhA8Kcv.js → info-DKCQHKI2-DlvSk8u3.js} +1 -1
  52. package/web-dist/assets/{infoDiagram-FWYZ7A6U-D1bT6ACJ.js → infoDiagram-FWYZ7A6U-CV-UmITw.js} +1 -1
  53. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-P7IDrOwM.js → ishikawaDiagram-FXEZZL3T-xQWAaJiF.js} +1 -1
  54. package/web-dist/assets/{journeyDiagram-5HDEW3XC-6XJBNIRb.js → journeyDiagram-5HDEW3XC-DJK8kVzY.js} +1 -1
  55. package/web-dist/assets/{kanban-definition-HUTT4EX6-La7dnlhq.js → kanban-definition-HUTT4EX6-moIoFsSZ.js} +1 -1
  56. package/web-dist/assets/{line-Dv2tv-OY.js → line-B9t5KW3M.js} +1 -1
  57. package/web-dist/assets/{linear-BO4R75Jg.js → linear-p05acNA3.js} +1 -1
  58. package/web-dist/assets/{mermaid-parser.core-DfuFuqcx.js → mermaid-parser.core-Db7dttgI.js} +3 -3
  59. package/web-dist/assets/{mermaid.core-BP_gsV2E.js → mermaid.core-BFbkt8RT.js} +3 -3
  60. package/web-dist/assets/{mindmap-definition-LN4V7U3C-BPDWuBXV.js → mindmap-definition-LN4V7U3C-dOYDtsm4.js} +1 -1
  61. package/web-dist/assets/{packet-7NZHBO7P-DvGyCLho.js → packet-7NZHBO7P-Y34L2B_H.js} +1 -1
  62. package/web-dist/assets/{pegDiagram-2B236MQR-B3XyjF78.js → pegDiagram-2B236MQR-BlI8nWnE.js} +1 -1
  63. package/web-dist/assets/{pie-RZYD4A2V-BcmTzATg.js → pie-RZYD4A2V-n-Qg1sTG.js} +1 -1
  64. package/web-dist/assets/{pieDiagram-ENE6RG2P-B13x3h-1.js → pieDiagram-ENE6RG2P-DoHW9Gyq.js} +1 -1
  65. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-B_qjpmSD.js → quadrantDiagram-ABIIQ3AL-BMUuqVdW.js} +1 -1
  66. package/web-dist/assets/{radar-I7S5WNFK-zvSO8c-R.js → radar-I7S5WNFK-skxmXNaM.js} +1 -1
  67. package/web-dist/assets/{railroad-3IZDKUUU-CJtUTUYW.js → railroad-3IZDKUUU-LfjLO5Ny.js} +1 -1
  68. package/web-dist/assets/railroad-abnf-AHOZXSZD-D4N57HJe.js +1 -0
  69. package/web-dist/assets/railroad-ebnf-EBAXGLYW-kukg91Jv.js +1 -0
  70. package/web-dist/assets/railroad-peg-LSFZ7HO6-4UH_xlt7.js +1 -0
  71. package/web-dist/assets/{railroadDiagram-RFXS5EU6-BP7Scsjp.js → railroadDiagram-RFXS5EU6-Zdj5BMQG.js} +1 -1
  72. package/web-dist/assets/{requirementDiagram-TGXJPOKE-BLn9ee9E.js → requirementDiagram-TGXJPOKE-DcFm1DAf.js} +1 -1
  73. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-DhjUURDl.js → sankeyDiagram-HTMAVEWB-BcRmQRQe.js} +1 -1
  74. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-alvtgf-F.js → sequenceDiagram-DBY2YBRQ-bET8bpzv.js} +1 -1
  75. package/web-dist/assets/{src-C6GQP2x2.js → src-CUSFAnq6.js} +1 -1
  76. package/web-dist/assets/{stateDiagram-2N3HPSRC-sga-NxRF.js → stateDiagram-2N3HPSRC-AGBIK4Rz.js} +1 -1
  77. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-CGZk-pxB.js +1 -0
  78. package/web-dist/assets/{swimlanes-5IMT3BWC-tVkOu3TT.js → swimlanes-5IMT3BWC-BrxNGoSO.js} +1 -1
  79. package/web-dist/assets/swimlanesDiagram-G3AALYLV-tBUJkYsP.js +8 -0
  80. package/web-dist/assets/{timeline-definition-FHXFAJF6-DEAZRgmn.js → timeline-definition-FHXFAJF6-Dqoh4MQ-.js} +1 -1
  81. package/web-dist/assets/{treeView-QDETBFTQ-Dnvq1Ma7.js → treeView-QDETBFTQ-C_vYdzCy.js} +1 -1
  82. package/web-dist/assets/{treemap-6X3UGDF4-Ce3cuN8q.js → treemap-6X3UGDF4-B_o40Eep.js} +1 -1
  83. package/web-dist/assets/{vennDiagram-L72KCM5P-DMyU7yi9.js → vennDiagram-L72KCM5P-AHb21nUH.js} +1 -1
  84. package/web-dist/assets/{wardley-OPB4EBWU-Dyao3Zne.js → wardley-OPB4EBWU-CDvRbffW.js} +1 -1
  85. package/web-dist/assets/{wardleyDiagram-EHGQE667-B6C8_1LE.js → wardleyDiagram-EHGQE667-DmLMQrCO.js} +1 -1
  86. package/web-dist/assets/{xychartDiagram-FW5EYKEG-_P-iqki1.js → xychartDiagram-FW5EYKEG-CBq8Fuud.js} +1 -1
  87. package/web-dist/index.html +2 -2
  88. package/web-dist/assets/architecture-TIHT7OUA-C-U11PKN.js +0 -1
  89. package/web-dist/assets/channel-jQPajw2y.js +0 -1
  90. package/web-dist/assets/classDiagram-OUVF2IWQ-BVGlW0VN.js +0 -1
  91. package/web-dist/assets/classDiagram-v2-EOCWNBFH-BVGlW0VN.js +0 -1
  92. package/web-dist/assets/eventmodeling-45OFAUF4-BPmoLPav.js +0 -1
  93. package/web-dist/assets/flowDiagram-23GEKE2U-CC63wNIt.js +0 -1
  94. package/web-dist/assets/index-DTxhrDcZ.js +0 -330
  95. package/web-dist/assets/index-W88ZnKOr.css +0 -2
  96. package/web-dist/assets/railroad-abnf-AHOZXSZD-CM-7EymF.js +0 -1
  97. package/web-dist/assets/railroad-ebnf-EBAXGLYW--3-yYzZ6.js +0 -1
  98. package/web-dist/assets/railroad-peg-LSFZ7HO6-CgVkrtHL.js +0 -1
  99. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-BburKdyU.js +0 -1
  100. package/web-dist/assets/swimlanesDiagram-G3AALYLV-eANJELfk.js +0 -8
package/dist/dev-child.js CHANGED
@@ -13349,7 +13349,8 @@ function splitAwaitingFrontmatter(raw2) {
13349
13349
  if (title) parsed.hints.push({ kind: "title", value: title });
13350
13350
  const stepHints = steps.map((step) => step.trim()).filter(Boolean).slice(0, AWAITING_STEPS_MAX).map((step) => ({ kind: "step", value: step.slice(0, AWAITING_STEP_VALUE_MAX) }));
13351
13351
  const questionHints = parsed.hints.filter((h10) => h10.kind === "question").slice(0, AWAITING_QUESTIONS_MAX);
13352
- const capped = parsed.hints.filter((h10) => h10.kind !== "question").slice(0, AWAITING_HINT_MAX);
13352
+ let items = 0;
13353
+ const capped = parsed.hints.filter((h10) => h10.kind !== "question" && (AWAITING_SCALAR_KINDS.has(h10.kind) || items++ < AWAITING_HINT_MAX));
13353
13354
  return { body: rest.join("\n").trim(), hints: [...capped, ...questionHints, ...stepHints] };
13354
13355
  }
13355
13356
  function inlineSteps(value) {
@@ -13397,21 +13398,44 @@ function parseAwaitingYaml(text2) {
13397
13398
  for (const entry of Array.isArray(raw2) ? raw2 : [raw2]) push("question", entry);
13398
13399
  } else if (key === "for") {
13399
13400
  push("for", raw2);
13401
+ } else if (key === "status") {
13402
+ push("status", raw2);
13400
13403
  } else if (key === "needs_input" || key === "needs-input") {
13401
13404
  push("needs_input", raw2);
13402
13405
  }
13403
13406
  }
13404
13407
  return { ok: true, hints };
13405
13408
  }
13406
- function awaitingNeedsInput(hints) {
13407
- if (awaitingSteps(hints).length > 0 || awaitingQuestions(hints).length > 0) return true;
13408
- let answer = null;
13409
+ function statusValueOf(value) {
13410
+ const v = value.trim().toLowerCase().replace(/-/g, "_");
13411
+ return AWAITING_STATUSES.includes(v) ? v : null;
13412
+ }
13413
+ function restAnswerLines(hints) {
13414
+ let status = null;
13415
+ let needsInput = null;
13409
13416
  for (const h10 of hints ?? []) {
13410
- if (h10.kind !== "needs_input") continue;
13411
- const value = h10.value.trim().toLowerCase();
13412
- answer = value === "true" ? true : value === "false" ? false : null;
13417
+ if (h10.kind === "status") status = statusValueOf(h10.value);
13418
+ else if (h10.kind === "needs_input") {
13419
+ const value = h10.value.trim().toLowerCase();
13420
+ needsInput = value === "true" ? true : value === "false" ? false : null;
13421
+ }
13413
13422
  }
13414
- return answer;
13423
+ return { status, needsInput };
13424
+ }
13425
+ function fenceWaitsOnHuman(hints) {
13426
+ return awaitingSteps(hints).length > 0 || awaitingQuestions(hints).length > 0;
13427
+ }
13428
+ function awaitingStatus(hints) {
13429
+ if (fenceWaitsOnHuman(hints)) return "needs_input";
13430
+ const { status, needsInput } = restAnswerLines(hints);
13431
+ if (status) return status;
13432
+ return needsInput === true ? "needs_input" : null;
13433
+ }
13434
+ function awaitingNeedsInput(hints) {
13435
+ if (fenceWaitsOnHuman(hints)) return true;
13436
+ const { status, needsInput } = restAnswerLines(hints);
13437
+ if (status) return status === "needs_input";
13438
+ return needsInput;
13415
13439
  }
13416
13440
  function isAwaitingItemKind(kind) {
13417
13441
  return AWAITING_ITEM_KINDS.includes(kind);
@@ -13546,9 +13570,9 @@ function signoffNudgeMessage(ops, needsInput = false) {
13546
13570
  if (lines.length) {
13547
13571
  lines.push("", "An ```awaiting fence names only what you are ACTUALLY waiting on, one such list per kind, plus");
13548
13572
  if (needsInput) {
13549
- lines.push("a required `for:` duration (`30s`/`15m`/`2h`/`3d`) and a required `needs_input: true|false`, then");
13550
- lines.push("a `---` line and whatever prose the human needs. Frizz checks every id: name something that is");
13551
- lines.push("not running and you are bumped rather than parked.");
13573
+ lines.push("a required `for:` duration (`30s`/`15m`/`2h`/`3d`) and a required `status:` (`working`, `watching`");
13574
+ lines.push("or `needs_input`), then a `---` line and whatever prose the human needs. Frizz checks every id:");
13575
+ lines.push("name something that is not running and you are bumped rather than parked.");
13552
13576
  } else {
13553
13577
  lines.push("a required `for:` duration (`30s`/`15m`/`2h`/`3d`), then a `---` line and whatever prose you want");
13554
13578
  lines.push("(optional). Frizz checks every id: name something that is not running and you are bumped");
@@ -13577,22 +13601,27 @@ function signoffNudgeAwaitingLines(needsInput) {
13577
13601
  return [
13578
13602
  "- `` ```awaiting `` \u2014 you are WAITING on work that is actually running: a background shell, a",
13579
13603
  " sub-agent, a timer or a registered PR. FRONTMATTER, THEN MARKDOWN: one YAML list per kind of thing",
13580
- " you are waiting on, a REQUIRED `for:` duration, a REQUIRED `needs_input:` answer, then optionally a",
13581
- " `---` line and prose.",
13604
+ " you are waiting on, a REQUIRED `for:` duration, a REQUIRED `status:`, then optionally a `---` line",
13605
+ " and prose.",
13582
13606
  "",
13583
13607
  " ```awaiting",
13584
13608
  " agents: [<the id your runtime gave you>]",
13585
- " needs_input: false",
13609
+ " status: working",
13586
13610
  " for: 2h",
13587
13611
  " ```",
13588
13612
  "",
13589
- " `needs_input: false` \u2014 nothing for the human yet. The thread stays out of their queue until the work",
13590
- " wakes you, and you owe NO write-up: the fence alone is the whole message. `needs_input: true` \u2014 the",
13591
- " human can read, try or act on something NOW while the work runs (a partial result, a file you wrote,",
13592
- " a server to try), even if you need nothing back from them; the thread goes into their queue, and the",
13593
- " prose under `---` says what to look at. If you wrote ANY words for the human at this rest, it is",
13594
- " `true` \u2014 a `false` rest is never put in front of them. A rest on running work with NO fence is a",
13595
- " bare rest, and it lands in the human's queue."
13613
+ " `status:` says where the thread sits while you wait, in one of three words:",
13614
+ " - `working` \u2014 what you named finishes by itself: a test, a build, a benchmark, CI, a sub-agent doing",
13615
+ " a task. The thread shows as running until the work wakes you.",
13616
+ " - `watching` \u2014 something has to HAPPEN outside the thread: a release, a review, another agent's",
13617
+ " merge, the next daily cycle. The thread is snoozed, and nothing on its row moves, until a named",
13618
+ " item reports or `for:` runs out.",
13619
+ " - `needs_input` \u2014 the human can read, try or act on something NOW while the work runs (a partial",
13620
+ " result, a file you wrote, a server to try), even if you need nothing back from them. The thread",
13621
+ " goes into their queue, and the prose under `---` says what to look at.",
13622
+ " `working` and `watching` owe NO write-up: the fence alone is the whole message. If you wrote ANY",
13623
+ " words for the human at this rest, it is `needs_input` \u2014 nothing else is put in front of them. A rest",
13624
+ " on running work with NO fence is a bare rest, and it lands in the human's queue."
13596
13625
  ];
13597
13626
  }
13598
13627
  function signoffNudgeText(needsInput) {
@@ -13805,9 +13834,13 @@ function hasLiveSubAgents(t) {
13805
13834
  return (t.subAgents ?? []).some((s) => isDirectSubAgent(s) && s.state === "running");
13806
13835
  }
13807
13836
  function hasLiveOps(t) {
13837
+ if (watchingRest(t)) return false;
13808
13838
  if (hasLiveSubAgents(t)) return true;
13809
13839
  return t.awaitingBackground === true && !parkedOnArmedTimerAlone(t);
13810
13840
  }
13841
+ function watchingRest(t) {
13842
+ return t.waitStatus === "watching" && t.runtime === "turn-idle";
13843
+ }
13811
13844
  function parkedOnArmedTimerAlone(t) {
13812
13845
  if (t.awaitingBackground !== true) return false;
13813
13846
  const watches = t.watches ?? [];
@@ -13823,12 +13856,14 @@ function isSnoozed(t, nowMs = Date.now()) {
13823
13856
  const userSnooze = futureSnoozedUntil(t, nowMs) !== void 0;
13824
13857
  if (t.state === "archived") return false;
13825
13858
  const eventSnooze = t.bgSnoozed === true && t.runtime === "turn-idle";
13826
- if (hasLiveSubAgents(t) || hasLiveOps(t) && !eventSnooze) return false;
13859
+ const watching = watchingRest(t);
13860
+ if (!watching && (hasLiveSubAgents(t) || hasLiveOps(t) && !eventSnooze)) return false;
13827
13861
  if (userSnooze) return t.runtime !== "running" && t.runtime !== "spawning";
13828
13862
  if (t.limitPause && t.foreign !== true) return false;
13829
13863
  if (t.needsYou || t.pendingAsk || t.runtime === "perm-prompt") return false;
13830
13864
  if (!atRest(t)) return false;
13831
13865
  if (eventSnooze) return true;
13866
+ if (watching) return true;
13832
13867
  const declaredWait = t.lastFence?.kind === "awaiting";
13833
13868
  return userSnooze || declaredWait;
13834
13869
  }
@@ -14021,7 +14056,7 @@ function parseGithubWakeSteer(text2) {
14021
14056
  function frizzRoute(path3) {
14022
14057
  return `${FRIZZ_ROUTE_PREFIX}${path3.startsWith("/") ? path3 : `/${path3}`}`;
14023
14058
  }
14024
- var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ClaudeModel, AcpAgent, ACP_MODEL_PREFIX, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_STEPS_MAX, AWAITING_STEP_VALUE_MAX, AWAITING_QUESTIONS_MAX, AWAITING_TITLE_MAX, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, ISSUE_WATCH_SPENT_TRAILER, ISSUE_WATCH_ARMED_TRAILER, INTERRUPT_ENDED_LEAD, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, PARK_CORRECTION_NEEDS_INPUT_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, QUESTION_FENCE_RETIRED_AT, NEEDS_INPUT_REQUIRED_AT, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, SettledQuestionView, ThreadSettledQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, ANSWER_CONTINUATION_INDENT, indentAnswerContinuation, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, UpgradeThreadModelInput, UpgradeThreadModelResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, ProjectRailCounts, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
14059
+ var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ClaudeModel, CODEX_MODELS_FALLBACK, AcpAgent, ACP_MODEL_PREFIX, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_SCALAR_KINDS, AWAITING_STEPS_MAX, AWAITING_STEP_VALUE_MAX, AWAITING_QUESTIONS_MAX, AWAITING_TITLE_MAX, AWAITING_STATUSES, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, ISSUE_WATCH_SPENT_TRAILER, ISSUE_WATCH_ARMED_TRAILER, INTERRUPT_ENDED_LEAD, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, PARK_CORRECTION_NEEDS_INPUT_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, QUESTION_FENCE_RETIRED_AT, NEEDS_INPUT_REQUIRED_AT, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, SettledQuestionView, ThreadSettledQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, ANSWER_CONTINUATION_INDENT, indentAnswerContinuation, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, UpgradeThreadModelInput, UpgradeThreadModelResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, ProjectRailCounts, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
14025
14060
  var init_src = __esm({
14026
14061
  "packages/shared/src/index.ts"() {
14027
14062
  "use strict";
@@ -14163,6 +14198,15 @@ var init_src = __esm({
14163
14198
  // Absent exactly when `resolvedModel` is.
14164
14199
  edition: external_exports.string().optional()
14165
14200
  });
14201
+ CODEX_MODELS_FALLBACK = [
14202
+ { slug: "gpt-6.1-sol", displayName: "GPT-6.1 Sol", defaultEffort: "low", efforts: ["low", "medium", "high", "xhigh", "max", "ultra"] },
14203
+ { slug: "gpt-6-astra", displayName: "GPT-6 Astra", defaultEffort: "medium", efforts: ["low", "medium", "high", "xhigh", "max", "ultra"] },
14204
+ { slug: "gpt-6-sol", displayName: "GPT-6 Sol", defaultEffort: "medium", efforts: ["low", "medium", "high", "xhigh", "max", "ultra"] },
14205
+ { slug: "gpt-6-luna", displayName: "GPT-6 Luna", defaultEffort: "medium", efforts: ["low", "medium", "high", "xhigh", "max"] },
14206
+ { slug: "gpt-5.6-sol", displayName: "GPT-5.6 Sol", defaultEffort: "low", efforts: ["low", "medium", "high", "xhigh", "max", "ultra"] },
14207
+ { slug: "gpt-5.6-terra", displayName: "GPT-5.6 Terra", defaultEffort: "medium", efforts: ["low", "medium", "high", "xhigh", "max", "ultra"] },
14208
+ { slug: "gpt-5.6-luna", displayName: "GPT-5.6 Luna", defaultEffort: "medium", efforts: ["low", "medium", "high", "xhigh", "max"] }
14209
+ ];
14166
14210
  AcpAgent = external_exports.object({
14167
14211
  id: external_exports.string(),
14168
14212
  label: external_exports.string(),
@@ -14360,7 +14404,7 @@ var init_src = __esm({
14360
14404
  questions: external_exports.array(AskQuestion)
14361
14405
  });
14362
14406
  AwaitingHint = external_exports.object({
14363
- kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue", "for", "title", "needs_input", "step", "question"]),
14407
+ kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue", "for", "title", "status", "needs_input", "step", "question"]),
14364
14408
  value: external_exports.string()
14365
14409
  });
14366
14410
  RETIRED_AWAITING_KINDS = ["watch", "pr-watch", "human", "ci", "session", "shell", "agent", "timer", "pr", "reason"];
@@ -14382,7 +14426,7 @@ var init_src = __esm({
14382
14426
  "reason": "put it below the `---` as ordinary Markdown \u2014 the frontmatter is YAML now and takes no prose"
14383
14427
  };
14384
14428
  AWAITING_KEY_RE = /^([a-z][a-z_-]*):\s*(\S.*)?$/i;
14385
- AWAITING_YAML_KEYS = /* @__PURE__ */ new Set(["shells", "agents", "timers", "prs", "issues", "questions", "for", "title", "steps", "needs_input", "needs-input"]);
14429
+ AWAITING_YAML_KEYS = /* @__PURE__ */ new Set(["shells", "agents", "timers", "prs", "issues", "questions", "for", "title", "steps", "status", "needs_input", "needs-input"]);
14386
14430
  AWAITING_SEQUENCE_KEYS = {
14387
14431
  shells: "shell",
14388
14432
  agents: "agent",
@@ -14395,10 +14439,12 @@ var init_src = __esm({
14395
14439
  };
14396
14440
  AWAITING_HINT_MAX = 8;
14397
14441
  AWAITING_HINT_VALUE_MAX = 200;
14442
+ AWAITING_SCALAR_KINDS = /* @__PURE__ */ new Set(["for", "title", "status", "needs_input"]);
14398
14443
  AWAITING_STEPS_MAX = 12;
14399
14444
  AWAITING_STEP_VALUE_MAX = 500;
14400
14445
  AWAITING_QUESTIONS_MAX = 24;
14401
14446
  AWAITING_TITLE_MAX = 120;
14447
+ AWAITING_STATUSES = ["needs_input", "working", "watching"];
14402
14448
  AWAITING_ITEM_KINDS = ["shell", "agent", "timer", "pr", "issue"];
14403
14449
  AWAITING_DURATION_RE = /^(\d{1,5})(s|m|h|d)$/;
14404
14450
  DURATION_UNIT_MS = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
@@ -15024,6 +15070,13 @@ ${ANSWER_CONTINUATION_INDENT}`);
15024
15070
  // Optional like needsYou/crashed: absent ⇒ a pre-restart server or a non-session row; the client
15025
15071
  // treats absence as false.
15026
15072
  awaitingBackground: external_exports.boolean().optional(),
15073
+ // SERVER-DERIVED: which band a rest OUT OF THE QUEUE sits in (board.deriveWaitStatus, 2026-10-05).
15074
+ // `working` — the work it waits on finishes by itself, so it is Running and spins; `watching` —
15075
+ // something must happen outside the thread, so it is Snoozed and still. Present only for a thread at
15076
+ // rest behind a wait the server honoured with no queue card; absent everywhere else, including every
15077
+ // queued rest, which never spins. Absent from a pre-2026-10-05 server, which the client reads as the
15078
+ // old Running placement.
15079
+ waitStatus: external_exports.enum(["working", "watching"]).optional(),
15027
15080
  // Exact typed-interaction presence for this CURRENT registered session. The board already derives
15028
15081
  // this from the scoped durable journal to compute needsYou; exposing the reason lets React avoid a
15029
15082
  // pendingInteractions RPC for every unrelated question/completion card. Optional preserves rolling
@@ -22025,42 +22078,51 @@ function toCodexModel(raw2) {
22025
22078
  priority
22026
22079
  };
22027
22080
  }
22028
- function parseCodexModelsCache(raw2) {
22081
+ function parseCodexModelsCache(raw2, expectedClientVersion, previousModels = CODEX_MODELS_FALLBACK) {
22029
22082
  let doc;
22030
22083
  try {
22031
22084
  doc = JSON.parse(raw2);
22032
22085
  } catch {
22033
- return CODEX_MODELS_FALLBACK;
22086
+ return [...previousModels];
22034
22087
  }
22035
- const models = doc && typeof doc === "object" ? doc.models : void 0;
22036
- if (!Array.isArray(models)) return CODEX_MODELS_FALLBACK;
22088
+ const record = doc && typeof doc === "object" ? doc : void 0;
22089
+ if (expectedClientVersion && record?.client_version !== expectedClientVersion) return [...previousModels];
22090
+ const models = record?.models;
22091
+ if (!Array.isArray(models)) return [...previousModels];
22037
22092
  const parsed = models.map(toCodexModel).filter((x) => x !== void 0).sort((a, b) => a.priority - b.priority).map((x) => x.model);
22038
- return parsed.length ? parsed : CODEX_MODELS_FALLBACK;
22093
+ return parsed.length ? parsed : [...previousModels];
22039
22094
  }
22040
- function readCodexModels(codexHome = defaultCodexHome()) {
22095
+ function readCodexModels(codexHome = defaultCodexHome(), expectedClientVersion, nowMs = Date.now()) {
22041
22096
  const path3 = cachePath(codexHome);
22042
- const hit = memo.get(path3);
22043
- const now = Date.now();
22044
- if (hit && now - hit.at < TTL_MS) return hit.models;
22097
+ const key = `${path3}\0${expectedClientVersion}`;
22098
+ const hit = memo.get(key);
22099
+ if (hit && nowMs - hit.at < TTL_MS) return hit.models;
22045
22100
  let models;
22046
22101
  try {
22047
- models = parseCodexModelsCache(readFileSync8(path3, "utf8"));
22102
+ const raw2 = readFileSync8(path3, "utf8");
22103
+ models = parseCodexModelsCache(raw2, expectedClientVersion, trusted.get(key));
22104
+ let writer;
22105
+ try {
22106
+ writer = JSON.parse(raw2).client_version;
22107
+ } catch {
22108
+ }
22109
+ if (writer === expectedClientVersion) trusted.set(key, models);
22048
22110
  } catch {
22049
- models = CODEX_MODELS_FALLBACK;
22111
+ models = trusted.get(key) ?? CODEX_MODELS_FALLBACK;
22050
22112
  }
22051
- memo.set(path3, { at: now, models });
22113
+ memo.set(key, { at: nowMs, models });
22052
22114
  return models;
22053
22115
  }
22054
- var CODEX_MODELS_FALLBACK, TTL_MS, memo;
22116
+ var TTL_MS, memo, trusted;
22055
22117
  var init_codex_models = __esm({
22056
22118
  "packages/server/src/backend/codex-models.ts"() {
22057
22119
  "use strict";
22120
+ init_src();
22058
22121
  init_codex();
22059
- CODEX_MODELS_FALLBACK = [
22060
- { slug: "gpt-5.5", displayName: "GPT-5.5", defaultEffort: "medium", efforts: ["low", "medium", "high", "xhigh"] }
22061
- ];
22122
+ init_src();
22062
22123
  TTL_MS = 5e3;
22063
22124
  memo = /* @__PURE__ */ new Map();
22125
+ trusted = /* @__PURE__ */ new Map();
22064
22126
  }
22065
22127
  });
22066
22128
 
@@ -22105,7 +22167,7 @@ function claudeModelFromLimitName(name) {
22105
22167
  if (!slug) return void 0;
22106
22168
  return CLAUDE_THREAD_PROFILES.find(({ model }) => slug === model || slug.startsWith(`${model}-`) || model.startsWith(`${slug}-`))?.model;
22107
22169
  }
22108
- function threadProfileOptions(backend, claudeModels) {
22170
+ function threadProfileOptions(backend, claudeModels, codexModels) {
22109
22171
  if (backend === "claude") {
22110
22172
  return {
22111
22173
  backend,
@@ -22123,7 +22185,7 @@ function threadProfileOptions(backend, claudeModels) {
22123
22185
  if (backend === "codex") {
22124
22186
  return {
22125
22187
  backend,
22126
- options: readCodexModels().map((model) => ({
22188
+ options: (codexModels ?? readCodexModels()).map((model) => ({
22127
22189
  model: model.slug,
22128
22190
  label: model.displayName,
22129
22191
  defaultEffort: model.defaultEffort,
@@ -22134,16 +22196,18 @@ function threadProfileOptions(backend, claudeModels) {
22134
22196
  if (backend === "acp") return { backend, options: [] };
22135
22197
  throw new Error("This thread has an unknown backend; its runtime profile cannot be changed");
22136
22198
  }
22137
- function validateThreadProfile(backend, model, effort) {
22138
- const catalogue = threadProfileOptions(backend);
22199
+ function validateThreadProfile(backend, model, effort, codexModels) {
22200
+ const catalogue = threadProfileOptions(backend, void 0, codexModels);
22139
22201
  const option = catalogue.options.find((candidate) => candidate.model === model);
22140
22202
  if (!option || !option.efforts.includes(effort)) {
22141
22203
  throw new Error(`Unsupported ${catalogue.backend} model/effort pair: ${model} / ${effort}`);
22142
22204
  }
22143
22205
  }
22144
- function normalizeObservedThreadModel(backend, model) {
22206
+ function normalizeObservedThreadModel(backend, model, codexModels) {
22145
22207
  const value = model.trim();
22146
- if (backend === "codex") return threadProfileOptions(backend).options.some((option) => option.model === value) ? value : void 0;
22208
+ if (backend === "codex") {
22209
+ return threadProfileOptions(backend, void 0, codexModels).options.some((option) => option.model === value) ? value : void 0;
22210
+ }
22147
22211
  if (backend === "claude") {
22148
22212
  const exact = CLAUDE_THREAD_PROFILES.find((option) => option.model === value);
22149
22213
  if (exact) return exact.model;
@@ -26357,11 +26421,13 @@ ${detail2}
26357
26421
  if (profileRecordLanded && state.model && state.profileAt) {
26358
26422
  const observedAt = Date.parse(state.profileAt);
26359
26423
  const spawnedAt = Date.parse(row.spawned_at);
26360
- const model = normalizeObservedThreadModel(row.backend ?? "claude", state.model);
26424
+ const backendKind = row.backend ?? "claude";
26425
+ const codexModels = backendKind === "codex" ? deps.codexModels?.() : void 0;
26426
+ const model = normalizeObservedThreadModel(backendKind, state.model, codexModels);
26361
26427
  const effort = state.effort?.trim() || row.effort?.trim();
26362
26428
  if (model && effort && Number.isFinite(observedAt) && Number.isFinite(spawnedAt) && observedAt >= spawnedAt) {
26363
26429
  try {
26364
- validateThreadProfile(row.backend ?? "claude", model, effort);
26430
+ validateThreadProfile(backendKind, model, effort, codexModels);
26365
26431
  deps.storage.setObservedProfileIfCurrent(
26366
26432
  row.slug,
26367
26433
  { sessionId: row.session_id, generation: runtimeGeneration },
@@ -36147,7 +36213,7 @@ async function readQuota(opts = {}) {
36147
36213
  }
36148
36214
  const [claude, codex] = await Promise.all([
36149
36215
  readClaudeQuota(opts.claudeBin, {}, { force: opts.force }),
36150
- readCodexQuota()
36216
+ readCodexQuota(void 0, opts.codexBin)
36151
36217
  ]);
36152
36218
  return { claude, codex };
36153
36219
  }
@@ -36186,11 +36252,11 @@ function isUnder(real, root) {
36186
36252
  return real === rootReal || real.startsWith(rootReal.endsWith(sep2) ? rootReal : rootReal + sep2);
36187
36253
  }
36188
36254
  try {
36189
- const trusted = statSync12(rootReal, { bigint: true });
36190
- if (!trusted.isDirectory() || trusted.ino === 0n) return false;
36255
+ const trusted2 = statSync12(rootReal, { bigint: true });
36256
+ if (!trusted2.isDirectory() || trusted2.ino === 0n) return false;
36191
36257
  for (let ancestor = real; ; ) {
36192
36258
  const actual = statSync12(ancestor, { bigint: true });
36193
- if (actual.dev === trusted.dev && actual.ino === trusted.ino) return true;
36259
+ if (actual.dev === trusted2.dev && actual.ino === trusted2.ino) return true;
36194
36260
  const parent = dirname9(ancestor);
36195
36261
  if (parent === ancestor) return false;
36196
36262
  ancestor = parent;
@@ -36983,12 +37049,12 @@ found, changed, and would do next. Be concise but do not omit; use whatever mark
36983
37049
 
36984
37050
  **A QUIET PARK NEEDS NO WRITE-UP.** When you rest on work that is still running and the human has
36985
37051
  nothing to read, try or act on yet \u2014 a sub-agent mid-task, a build, CI \u2014 end with an \` \`\`\`awaiting \`
36986
- fence carrying \`needs_input: false\` and NOTHING else: no summary, no progress report, no prose. The
36987
- thread stays out of the human's queue and nobody is meant to read that message, so writing one costs you
36988
- a turn of tokens for no reader. The moment there IS something for them \u2014 a partial result, a file, a
36989
- server, a question \u2014 the rest is \`needs_input: true\` instead. The write-up is owed at the rest that
36990
- DOES need them \u2014 \`needs_input: true\`, a question, or \`done\` \u2014 and it covers everything since their
36991
- last message, the quiet stretches included.
37052
+ fence carrying \`status: working\` or \`status: watching\` and NOTHING else: no summary, no progress
37053
+ report, no prose. The thread stays out of the human's queue and nobody is meant to read that message, so
37054
+ writing one costs you a turn of tokens for no reader. The moment there IS something for them \u2014 a partial
37055
+ result, a file, a server, a question \u2014 the rest is \`status: needs_input\` instead. The write-up is owed
37056
+ at the rest that DOES need them \u2014 \`status: needs_input\`, a question, or \`done\` \u2014 and it covers
37057
+ everything since their last message, the quiet stretches included.
36992
37058
 
36993
37059
  **ALWAYS SIGN OFF.** A bare rest \u2014 nothing said about where you stand \u2014 is not a handoff, it is an item
36994
37060
  nobody can triage: it says neither "answer me" nor "this is finished", so it sits in the queue meaning
@@ -37001,8 +37067,8 @@ has to be rewritten at every rest and is wrong the moment anything changes; a ro
37001
37067
  ending, a compaction and a frizz restart. **The \`done\` fence below still works**, and it is still the
37002
37068
  right shape for the prose the human reads \u2014 but where a verb exists for what you are saying, call it.
37003
37069
  **A WAIT IS THE ONE EXCEPTION, and it always takes the fence.** \`mcp__frizz__watch\` registers WHEN you
37004
- wake; whether the human is needed meanwhile is an answer about THIS rest, and only the
37005
- \` \`\`\`awaiting \` fence carries it (\`needs_input:\`, below). A rest on running work with no fence is a
37070
+ wake; where the thread sits meanwhile is an answer about THIS rest, and only the
37071
+ \` \`\`\`awaiting \` fence carries it (\`status:\`, below). A rest on running work with no fence is a
37006
37072
  bare rest, and it lands in the human's queue. **A QUESTION HAS NO FENCE ANY MORE** (retired
37007
37073
  2026-09-11): the only way to ask is \`mcp__frizz__ask\`, and a question written into a fence's body is
37008
37074
  plain prose \u2014 no card, no answer, and no sign-off.
@@ -37099,23 +37165,31 @@ exactly ONE of them.
37099
37165
  - \` \`\`\`awaiting \` \u2014 you have STOPPED, and you are waiting on work that is actually running, or on the
37100
37166
  human to PERFORM steps you cannot (\`steps:\`, below). A DECISION you need from them is a question, never
37101
37167
  a fence. The fence is PURE STRUCTURE: YAML frontmatter naming what frizz can
37102
- look up and saying whether the human is needed, then \u2014 only when they are \u2014 \`---\` and Markdown prose.
37103
- The everyday shape is a QUIET PARK, the fence alone:
37168
+ look up and saying where the thread sits meanwhile, then \u2014 only when the human is needed \u2014 \`---\` and
37169
+ Markdown prose. The everyday shape is a QUIET PARK, the fence alone \u2014 here, on a test run and a
37170
+ sub-agent that finish by themselves:
37104
37171
 
37105
37172
  \`\`\`awaiting
37106
37173
  shells: [bzvtnt3ig]
37107
37174
  agents: [<the runtime agent id>]
37108
- timers: [tmr_a1b2c3d4e5f6]
37109
- prs: [acme/app#391]
37110
- needs_input: false
37175
+ status: working
37111
37176
  for: 2h
37112
37177
  \`\`\`
37113
37178
 
37179
+ And on a wait whose end is someone else's \u2014 a review, a release, a reply:
37180
+
37181
+ \`\`\`awaiting
37182
+ prs: [acme/app#391]
37183
+ timers: [tmr_a1b2c3d4e5f6]
37184
+ status: watching
37185
+ for: 1d
37186
+ \`\`\`
37187
+
37114
37188
  And when the human can already act on something while the work runs:
37115
37189
 
37116
37190
  \`\`\`awaiting
37117
37191
  agents: [<the runtime agent id>]
37118
- needs_input: true
37192
+ status: needs_input
37119
37193
  for: 1h
37120
37194
  title: Three of five audits ready
37121
37195
  ---
@@ -37135,20 +37209,31 @@ exactly ONE of them.
37135
37209
  The publish step runs as the maintainer, and no token for that account is available to this thread.
37136
37210
  \`\`\`
37137
37211
 
37138
- - **\`needs_input:\` \u2014 REQUIRED, \`true\` or \`false\`: does the human need to look NOW?** \`false\` keeps
37139
- the thread out of their queue until the work wakes you, and the fence is the whole message.
37140
- \`true\` puts it in their queue while the work keeps running \u2014 the live work is listed on the card \u2014
37141
- and the prose under \`---\` says what to look at. **\`true\` IS NOT ONLY FOR A QUESTION.** Anything the
37142
- human could read, try or act on right now \u2014 a partial result, a file you wrote, a server to click
37143
- through \u2014 is \`true\`, even when you need nothing back from them. And if you wrote ANY words for the
37144
- human at this rest, the answer is \`true\`: a \`false\` rest is never put in front of them, so those
37145
- words would go unread. It is YOUR call, and frizz does not second-guess it:
37146
- no rule about which kinds of wait need the human overrides what you say. A fence without the line \u2014
37147
- or with anything but \`true\`/\`false\` \u2014 is not a park: the thread queues and frizz tells you which
37148
- line is missing. And a \`false\` holds only while every name is live and \`for:\` has not run out, so a
37149
- wrong \`false\` cannot hide the thread.
37212
+ - **\`status:\` \u2014 REQUIRED: where does the thread sit while the work runs?** One of three values, one
37213
+ per place on the board:
37214
+ - \`working\` \u2014 the work finishes BY ITSELF: a test run, a build, a benchmark, CI, a sub-agent doing
37215
+ a job. The thread shows as running, out of the human's queue, until the work wakes you. It shows so
37216
+ only while something you named is actually MOVING \u2014 a live sub-agent, a shell you name here or
37217
+ registered with \`mcp__frizz__watch\`, CI that is still running \u2014 so \`working\` on a timer or on a
37218
+ PR whose checks have settled shows as the watch it is.
37219
+ - \`watching\` \u2014 the wait is on something OUTSIDE the thread: a review, a release, a reporter's
37220
+ reply, another agent's merge, a poller you left running. The thread is snoozed \u2014 out of the queue
37221
+ and out of the running rows \u2014 until the watch wakes you. Pick it beside a sub-agent too when that
37222
+ child does nothing but watch the world for you.
37223
+ - \`needs_input\` \u2014 the human can read, try or act on something NOW while the work runs. The thread
37224
+ goes into their queue \u2014 the live work is listed on the card \u2014 and the prose under \`---\` says
37225
+ what to look at. **IT IS NOT ONLY FOR A QUESTION.** A partial result, a file you wrote, a server
37226
+ to click through is \`needs_input\`, even when you need nothing back from them. And if you wrote
37227
+ ANY words for the human at this rest, the answer is \`needs_input\`: a \`working\` or \`watching\`
37228
+ rest is never put in front of them, so those words would go unread.
37229
+ It is YOUR call, and frizz does not second-guess it: no rule about which kinds of wait need the
37230
+ human overrides what you say. A fence without the line \u2014 or with any other value \u2014 is not a park:
37231
+ the thread queues and frizz tells you which line is missing. And \`working\` or \`watching\` holds
37232
+ only while every name is live and \`for:\` has not run out, so a wrong answer cannot hide the
37233
+ thread. (\`needs_input: true\` and \`needs_input: false\`, the answer before 2026-10-05, are still
37234
+ read. Write \`status:\`.)
37150
37235
  - **THE FRONTMATTER IS YAML. THE BODY IS MARKDOWN.** The PLURAL keys take LISTS (either list form;
37151
- a single item may be written bare), plus the scalars \`for:\`, \`needs_input:\` and \`title:\`. **NO PROSE ABOVE THE
37236
+ a single item may be written bare), plus the scalars \`for:\`, \`status:\` and \`title:\`. **NO PROSE ABOVE THE
37152
37237
  \`---\`, EVER** \u2014 a colon or a \` #\` inside a sentence breaks the parse. (The \`title:\` and \`steps:\`
37153
37238
  values are the exceptions: frizz reads them verbatim, so code, \`#391\` and a colon are safe there.) There is no \`reason:\` key any more;
37154
37239
  the reason goes in the body, which is the handoff the human reads. **THE SINGULAR KEYS ARE GONE** \u2014 \`shell:\`,
@@ -37166,7 +37251,7 @@ exactly ONE of them.
37166
37251
  and a frizz restart, and \`activity\` marks anything already covered. It refuses a handle nothing
37167
37252
  live answers to, and a \`kind\` that disagrees with what frizz can see, so you cannot register a
37168
37253
  wait that will not fire. It does NOT replace the fence: each rest still ends with one, naming the
37169
- work and answering \`needs_input:\`. Withdraw a registration with \`mcp__frizz__unwatch\`. A sub-agent
37254
+ work and answering \`status:\`. Withdraw a registration with \`mcp__frizz__unwatch\`. A sub-agent
37170
37255
  needs no registration \u2014 its return re-invokes you \u2014 but resting on one still takes the fence,
37171
37256
  \`agents: [<id>]\`.
37172
37257
  - \`timers:\` \u2014 timer ids from \`mcp__frizz__timer\`. NOT instants; the tool creates the timer.
@@ -37199,7 +37284,7 @@ exactly ONE of them.
37199
37284
  may not, press a button you are not allowed to press. One \`- \` item per step, each written to be
37200
37285
  followed COLD by someone who has read nothing else; frizz reads them verbatim. Steps NAME THE HUMAN
37201
37286
  as the wait, so the fence needs no other name and no \`for:\` (add one only if you want to be woken
37202
- to re-check), and it always puts the thread in their queue \u2014 \`needs_input:\` may be left out. Their
37287
+ to re-check), and it always puts the thread in their queue \u2014 \`status:\` may be left out. Their
37203
37288
  card shows the steps over one **Done** button, and its click comes back to you as their reply,
37204
37289
  \`Done\`, the same as anything they type; anything else they need to tell you \u2014 a step that failed,
37205
37290
  the account they used \u2014 comes as a message of their own. An act is not a decision: never ask
@@ -37213,8 +37298,8 @@ exactly ONE of them.
37213
37298
  - **REGISTERING IS NOT PARKING, AND PARKING IS NOT REGISTERING.** Your shells, sub-agents, timers
37214
37299
  and PR watchers are watched AUTOMATICALLY, fence or no fence \u2014 frizz wakes you when one finishes,
37215
37300
  every time. The fence only declares that you have STOPPED, names which of them you stopped for,
37216
- and says whether the human is needed meanwhile. **FRIZZ CHECKS EVERY NAME THE MOMENT YOU REST:** all
37217
- of them live \u21D2 your thread rests where \`needs_input:\` put it; any one dead, unknown or not yours \u21D2
37301
+ and says where the thread sits meanwhile. **FRIZZ CHECKS EVERY NAME THE MOMENT YOU REST:** all
37302
+ of them live \u21D2 your thread rests where \`status:\` put it; any one dead, unknown or not yours \u21D2
37218
37303
  you are BUMPED immediately and told which, and the thread sits in the queue until you fix it.
37219
37304
  **A FENCE THAT NAMES NOTHING IS NOT A PARK** \u2014 \`for:\` describes a wait, it is not one \u2014 so
37220
37305
  "waiting on the checks" plus a duration just sits in the queue: REGISTER the PR and name it, and
@@ -37603,15 +37688,16 @@ the handoff.
37603
37688
  ## Automated waits in Claude Code
37604
37689
 
37605
37690
  **The mechanism is decided by whether you will REST while it runs.** Only an \` \`\`\`awaiting \` fence
37606
- answering \`needs_input: false\` keeps a rested thread out of the queue \u2014 a live sub-agent alone does not.
37691
+ answering \`status: working\` or \`status: watching\` keeps a rested thread out of the queue \u2014 a live
37692
+ sub-agent alone does not.
37607
37693
 
37608
37694
  - **Resting until a condition is met** (the usual CI / PR / release wait) \u2192 for a pull request,
37609
37695
  \`mcp__frizz__watch_pr\`; for a GitHub issue, \`mcp__frizz__watch_issue\`; for anything else dispatch a
37610
37696
  SUB-AGENT to own the wait. It runs the watcher
37611
- to completion in its own foreground and returns the verdict; you stay Active and its return
37612
- re-invokes you. A Bash call that names no \`timeout\` moves to the background after a minute, so the
37613
- watcher names one sized to the wait (up to 24h) and loops until its terminal condition. A helper must
37614
- not hand back while its own watcher is still live.
37697
+ to completion in its own foreground and returns the verdict, and its return re-invokes you; rest on
37698
+ it with \`status: watching\`. A Bash call that names no \`timeout\` moves to the background after a
37699
+ minute, so the watcher names one sized to the wait (up to 24h) and loops until its terminal
37700
+ condition. A helper must not hand back while its own watcher is still live.
37615
37701
  - **Working alongside a process you launched** (dev server, log tail) \u2192 \`Bash\` with
37616
37702
  \`run_in_background: true\`. Never put shell job control (\`&\`, \`nohup \u2026 &\`, \`disown\`) inside the
37617
37703
  command to imitate the native flag: frizz's hook rejects an escaping job, because the process could
@@ -37634,7 +37720,7 @@ its outbox and is unaffected:
37634
37720
  on a CLOCK, whatever you are doing, delivered MID-TURN at your next tool boundary and never aborting
37635
37721
  what you are running; \`post_compaction\` \u2014 into the emptied window after a compaction. Disarm with
37636
37722
  \`action: "stop"\` when the work it drives is finished \u2014 one left armed on a finished thread wakes it
37637
- forever, and the human can also switch it off in the thread footer. Replying \`ALLDONE\` on its own
37723
+ forever, and the human can also switch it off from the thread's Goal control. Replying \`ALLDONE\` on its own
37638
37724
  line stops it too, but that permanently stalls the run: a last resort, only when nothing is left.
37639
37725
  - \`mcp__frizz__timer\` is your own alarm clock: \`action: "set"\` with \`prompt\` plus \`in_seconds\` or an
37640
37726
  ISO \`at\`, delivered exactly once, mid-turn, and then gone. You may hold MANY at once; \`action: "list"\`
@@ -39974,11 +40060,11 @@ function hasDeclaredWait(tele, nowMs, armedTimerIds = /* @__PURE__ */ new Set(),
39974
40060
  }
39975
40061
  function heldByRunningChecks(github, registered) {
39976
40062
  if (registered.size === 0) return false;
39977
- return [...registered].every((key) => {
39978
- const status = github[key];
39979
- if (!status || status.checks !== "running" || status.state !== "open") return false;
39980
- return !(status.running === 0 && (status.gated ?? 0) > 0);
39981
- });
40063
+ return [...registered].every((key) => ciInMotion(github[key]));
40064
+ }
40065
+ function ciInMotion(status) {
40066
+ if (!status || status.checks !== "running" || status.state !== "open") return false;
40067
+ return !(status.running === 0 && (status.gated ?? 0) > 0);
39982
40068
  }
39983
40069
  function hasLiveOwnWork(tele, registeredPrWatches) {
39984
40070
  return Boolean(
@@ -40117,6 +40203,20 @@ function deriveAwaitingBackground(row, tele, runtime, hasActionableInteraction =
40117
40203
  !hasHumanSteps(tele)) return false;
40118
40204
  return deriveNeedsYou({ ...row, bg_snooze_rested_at: null }, tele, runtime, hasActionableInteraction, nowMs, limitPause, false, deliveryProcessGone, {}, /* @__PURE__ */ new Set(), armedTimerIds, armedWatches, openQuestions);
40119
40205
  }
40206
+ function deriveWaitStatus(tele, runtime, needsYou, awaitingBackground, messageInFlight, nowMs, github = {}, registeredPrWatches = /* @__PURE__ */ new Set(), armedWatches = []) {
40207
+ if (runtime !== "turn-idle" || needsYou || messageInFlight) return void 0;
40208
+ const liveChild = hasLiveBackgroundWork(tele);
40209
+ if (!awaitingBackground && !liveChild) return void 0;
40210
+ const fence = tele?.lastFence?.kind === "awaiting" ? tele.lastFence : void 0;
40211
+ const declared = fence ? awaitingStatus(fence.hints) : null;
40212
+ if (declared === "needs_input") return void 0;
40213
+ if (declared === "watching") return "watching";
40214
+ if (liveChild || [...registeredPrWatches].some((key) => ciInMotion(github[key]))) return "working";
40215
+ if (declared !== "working") return "watching";
40216
+ const namedShell = readAwaitingPark(fence?.hints ?? []).items.some((i) => i.kind === "shell" && resolveLiveWatchTarget(tele, i.value)?.kind === "shell");
40217
+ const registeredShell = armedWatches.some((w2) => w2.kind === "shell" && Date.parse(w2.expiresAt) > nowMs && resolveLiveWatchTarget(tele, w2.target)?.kind === "shell");
40218
+ return namedShell || registeredShell ? "working" : "watching";
40219
+ }
40120
40220
  function resolveSessionProfile(row, tele) {
40121
40221
  const persistedModel = row.model?.trim() || void 0;
40122
40222
  const persistedEffort = row.effort?.trim() || void 0;
@@ -40288,6 +40388,7 @@ function sessionThreadView(projectDir, storage, row, rawTele, registeredLegacyTe
40288
40388
  const needsYou = archived ? false : deriveNeedsYou(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, true, deliveryProcessGone, github, registeredPrWatches, armedTimerIds, armedWatches, questions.length, answerInFlight);
40289
40389
  const deliveryInFlight = !archived && deriveDeliveryInFlight(row, runtime, needsYou, deliveryProcessGone, answerInFlight, nowMs);
40290
40390
  const awaitingBackground = archived ? false : deriveAwaitingBackground(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, deliveryProcessGone, github, registeredPrWatches, armedTimerIds, armedWatches, questions.length);
40391
+ const waitStatus = archived ? void 0 : deriveWaitStatus(tele, runtime, needsYou, awaitingBackground, answerInFlight || hasFreshDelivery(row, deliveryProcessGone), nowMs, github, registeredPrWatches, armedWatches);
40291
40392
  const crashed = runtime === "exited" && (tele?.turn === "in-flight" || hasLiveBackgroundWork(tele) || headlessLostWork);
40292
40393
  const snoozedUntil = futureSnooze(row, nowMs);
40293
40394
  const profile = resolveSessionProfile(row, tele);
@@ -40361,6 +40462,7 @@ function sessionThreadView(projectDir, storage, row, rawTele, registeredLegacyTe
40361
40462
  recurringPrompt: resolveRecurringPrompt(row),
40362
40463
  needsYou,
40363
40464
  awaitingBackground,
40465
+ waitStatus,
40364
40466
  crashed,
40365
40467
  pendingInteraction: interactionPresence.pending,
40366
40468
  actionableInteraction: interactionPresence.needsUser,
@@ -42504,16 +42606,20 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
42504
42606
  const fenceId2 = parkFenceId("needs-input", spokeAt);
42505
42607
  const deliveryId2 = wakeDeliveryId(row.slug, row.session_id, fenceId2);
42506
42608
  if (outbox.get(deliveryId2)) continue;
42507
- const written = tele.lastFence.hints.find((h10) => h10.kind === "needs_input")?.value;
42609
+ const written = [...tele.lastFence.hints].reverse().find((h10) => h10.kind === "status") ?? [...tele.lastFence.hints].reverse().find((h10) => h10.kind === "needs_input");
42508
42610
  const message2 = [
42509
- `${PARK_CORRECTION_NEEDS_INPUT_LEAD}\`needs_input: true\` or \`needs_input: false\`${written ? ` (it says \`needs_input: ${written}\`)` : ""}, so frizz did not park it \u2014 your thread is in the human's queue.`,
42611
+ `${PARK_CORRECTION_NEEDS_INPUT_LEAD}\`status:\` that frizz can read \u2014 \`working\`, \`watching\` or \`needs_input\`${written ? ` (it says \`${written.kind}: ${written.value}\`)` : ""} \u2014 so frizz did not park it, and your thread is in the human's queue.`,
42612
+ "",
42613
+ "Every ```awaiting fence answers one question: where does this rest sit while the work runs?",
42510
42614
  "",
42511
- "Every ```awaiting fence answers one question: does the human need to look NOW?",
42615
+ "- `status: working` \u2014 the work finishes by itself: a test run, a build, CI, a sub-agent doing a job.",
42616
+ " The thread shows as running until the work wakes you.",
42617
+ "- `status: watching` \u2014 the wait is on something outside the thread: a review, a release, a",
42618
+ " reporter, another agent's merge. The thread is snoozed until the watch wakes you.",
42619
+ "- `status: needs_input` \u2014 the human can act on something now while the work runs. The thread goes",
42620
+ " into their queue, and the prose under `---` says what to look at.",
42512
42621
  "",
42513
- "- `needs_input: false` \u2014 nothing for them yet. The thread stays out of their queue until the work",
42514
- " wakes you, and the fence alone is the whole message: no write-up.",
42515
- "- `needs_input: true` \u2014 they can act on something now while the work runs. The thread goes into",
42516
- " their queue, and the prose under `---` says what to look at.",
42622
+ "With `working` or `watching` the fence alone is the whole message: no write-up.",
42517
42623
  "",
42518
42624
  "Re-send the same fence with that line added."
42519
42625
  ].join("\n");
@@ -42526,7 +42632,7 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
42526
42632
  message: `${message2}
42527
42633
 
42528
42634
  ${wakeTimeHeader(nowMs, spokeAt)}`,
42529
- reason: "awaiting fence gives no needs_input answer"
42635
+ reason: "awaiting fence gives no status answer"
42530
42636
  }, nowMs).delivery;
42531
42637
  deps.storage.countParkBump(row.slug, fenceId2);
42532
42638
  log3(`waker: queued ${row.slug} \u2014 ${item2.reason}`);
@@ -72350,6 +72456,7 @@ function createContextUnchecked(opts, resources) {
72350
72456
  storage,
72351
72457
  bus,
72352
72458
  backendFor,
72459
+ codexModels: () => readCodexModels(void 0, opts.codexVersion),
72353
72460
  onChange: () => board.refresh(),
72354
72461
  onTranscriptChange: (slugs) => transcriptChange.emit(slugs),
72355
72462
  // The SDK's own reading of a headless broker session: its turn (so the fold's 5s unknown-stop_reason
@@ -72407,8 +72514,9 @@ function createContextUnchecked(opts, resources) {
72407
72514
  // Second wake source: every thread a subscription window cut off mid-turn gets its own "continue"
72408
72515
  // once that window rolls, over this same delivery path. The quota reader supplies the fallback
72409
72516
  // instant for a weekly limit, whose message text carries a clock but no date; readQuota memoizes,
72410
- // so consulting it per tick costs a live request only every few minutes.
72411
- readQuota,
72517
+ // so consulting it per tick costs a live request only every few minutes. Read through the resolved
72518
+ // runtimes, as the quota RPC does: a bare call would start whatever `codex` is first on PATH.
72519
+ readQuota: () => readQuota({ claudeBin: opts.claudeBin, codexBin: opts.codexBin }),
72412
72520
  // The only runtime that can answer is the broker: its daemon record is on disk while the daemon
72413
72521
  // lives and is unlinked when it dies (liveBrokerRecords checks the pid), so "did the process that
72414
72522
  // took this wake survive" is one directory read. Codex and any row whose session moved on answer
@@ -72534,7 +72642,8 @@ ${wakeDeliveryToken(deliveryId)}`;
72534
72642
  teardownProject: opts.teardownProject,
72535
72643
  launchProjectId: opts.launchProjectId,
72536
72644
  claudeBin: opts.claudeBin,
72537
- codexBin: opts.codexBin
72645
+ codexBin: opts.codexBin,
72646
+ codexVersion: opts.codexVersion
72538
72647
  };
72539
72648
  }
72540
72649
  var CONTEXT_STARTUP_CLEANUP_TIMEOUT_MS, QUOTA_REFRESH_INTERVAL_MS, ContextStartupError;
@@ -72556,6 +72665,7 @@ var init_context = __esm({
72556
72665
  init_resume();
72557
72666
  init_claude();
72558
72667
  init_codex();
72668
+ init_codex_models();
72559
72669
  init_acp_transcript();
72560
72670
  init_acp_bridge();
72561
72671
  init_auth_status();
@@ -79158,10 +79268,10 @@ import { randomUUID as randomUUID17 } from "node:crypto";
79158
79268
  import { basename as basename9, dirname as dirname18 } from "node:path";
79159
79269
  import { existsSync as existsSync18, mkdirSync as mkdirSync20, rmSync as rmSync11, writeFileSync as writeFileSync14 } from "node:fs";
79160
79270
  import { homedir as homedir17 } from "node:os";
79161
- function validateGithubDispatchProfile(input) {
79271
+ function validateGithubDispatchProfile(input, codexModels) {
79162
79272
  if (input.backend === "acp") return;
79163
79273
  if (input.effort === void 0) throw new Error(`Unsupported ${input.backend} model/effort pair: ${input.model} / (no effort)`);
79164
- validateThreadProfile(input.backend, input.model, input.effort);
79274
+ validateThreadProfile(input.backend, input.model, input.effort, codexModels);
79165
79275
  }
79166
79276
  function githubDispatcherRequest(input, item) {
79167
79277
  return {
@@ -80418,7 +80528,8 @@ ${gapNote}` : input.message;
80418
80528
  handler: async ({ input }) => {
80419
80529
  const row = ctx.storage.getSession(input.slug);
80420
80530
  if (!row) throw new Error(`thread ${input.slug} is not editable`);
80421
- return threadProfileOptions(row.backend, row.backend === "claude" ? await readClaudeModels({ claudeBin: ctx.claudeBin, cwd: ctx.project.dir }) : void 0);
80531
+ const claudeModels = row.backend === "claude" ? await readClaudeModels({ claudeBin: ctx.claudeBin, cwd: ctx.project.dir }) : void 0;
80532
+ return threadProfileOptions(row.backend, claudeModels, readCodexModels(void 0, ctx.codexVersion));
80422
80533
  }
80423
80534
  }),
80424
80535
  // The composer's `/` typeahead asks the thread's HARNESS for its skills — the broker daemon's
@@ -81400,7 +81511,7 @@ Say which way you went and why in your write-up, so the human can course-correct
81400
81511
  // hand-maintained list. Degrades to a minimal fallback (never throws) when the cache is absent.
81401
81512
  codexModels: query({
81402
81513
  output: external_exports.array(CodexModel),
81403
- handler: async () => readCodexModels()
81514
+ handler: async () => readCodexModels(void 0, ctx.codexVersion)
81404
81515
  }),
81405
81516
  // The Claude aliases with the EDITION the pinned runtime resolves each to ("Opus 5.5"), asked of the
81406
81517
  // runtime itself once per server life; the bare family words while it answers (claude-models.ts).
@@ -81429,7 +81540,7 @@ Say which way you went and why in your write-up, so the human can course-correct
81429
81540
  quota: query({
81430
81541
  input: external_exports.object({ force: external_exports.boolean().optional() }).strict().optional(),
81431
81542
  output: QuotaSnapshot,
81432
- handler: async ({ input }) => readQuota({ claudeBin: ctx.claudeBin, force: input?.force })
81543
+ handler: async ({ input }) => readQuota({ claudeBin: ctx.claudeBin, codexBin: ctx.codexBin, force: input?.force })
81433
81544
  }),
81434
81545
  // Per-provider LOCAL credential presence for the new-thread dispatch gate. Distinct from `quota`
81435
81546
  // (whose "unavailable" is overloaded with transient endpoint failures): this reports only whether a
@@ -81760,12 +81871,12 @@ Say which way you went and why in your write-up, so the human can course-correct
81760
81871
  }),
81761
81872
  dispatchPreferencesGet: query({
81762
81873
  output: DispatchPreferences,
81763
- handler: async () => ctx.getDispatchPreferences(readCodexModels())
81874
+ handler: async () => ctx.getDispatchPreferences(readCodexModels(void 0, ctx.codexVersion))
81764
81875
  }),
81765
81876
  dispatchPreferenceSet: mutation({
81766
81877
  input: SetDispatchPreferenceInput,
81767
81878
  output: DispatchPreferences,
81768
- handler: async ({ input }) => ctx.setDispatchPreference(input, readCodexModels())
81879
+ handler: async ({ input }) => ctx.setDispatchPreference(input, readCodexModels(void 0, ctx.codexVersion))
81769
81880
  }),
81770
81881
  // The shipped GitHub batch-dispatch prompt template (single source of truth: server/github.ts).
81771
81882
  // The Settings UI reads it to prefill the editor and to power "reset to default"; an empty/unset
@@ -81824,7 +81935,7 @@ Say which way you went and why in your write-up, so the human can course-correct
81824
81935
  input: GithubBatchInput,
81825
81936
  output: GithubBatchResult,
81826
81937
  handler: async ({ input }) => {
81827
- validateGithubDispatchProfile(input);
81938
+ validateGithubDispatchProfile(input, readCodexModels(void 0, ctx.codexVersion));
81828
81939
  const repo = await resolveRepo();
81829
81940
  if (!repo) throw new Error("not a GitHub repo");
81830
81941
  const template = effectiveTemplate(ctx.getSettings().githubPrompt);
@@ -85295,13 +85406,24 @@ async function startServer(opts = {}) {
85295
85406
  },
85296
85407
  // serverLockPath is the LAUNCHING project's: it is the only `server.lock` this process publishes
85297
85408
  // (see "status publication"), so it is the only file a tenant's worker can read the port out of.
85298
- contextOptions: { get claudeBin() {
85299
- return runtimes?.claude.bin ?? opts.claudeBin;
85300
- }, get codexBin() {
85301
- return runtimes?.codex.bin ?? opts.codexBin;
85302
- }, serverLockPath: serverLockPathFor(project), activeTenants, teardownProject, launchProjectId: project.id, get database() {
85303
- return frizzDb?.db;
85304
- } },
85409
+ contextOptions: {
85410
+ get claudeBin() {
85411
+ return runtimes?.claude.bin ?? opts.claudeBin;
85412
+ },
85413
+ get codexBin() {
85414
+ return runtimes?.codex.bin ?? opts.codexBin;
85415
+ },
85416
+ get codexVersion() {
85417
+ return runtimes?.codex.version === "unknown" ? void 0 : runtimes?.codex.version;
85418
+ },
85419
+ serverLockPath: serverLockPathFor(project),
85420
+ activeTenants,
85421
+ teardownProject,
85422
+ launchProjectId: project.id,
85423
+ get database() {
85424
+ return frizzDb?.db;
85425
+ }
85426
+ },
85305
85427
  // Each project's app carries ITS OWN owner proof, so /health stays honest per project rather than
85306
85428
  // answering for whichever one happened to launch the server. The socket is per project for a
85307
85429
  // blunter reason: it is a live feed of ONE board, so sharing the launcher's would push its
@@ -85563,6 +85685,7 @@ async function startServer(opts = {}) {
85563
85685
  () => runtime.createContext({
85564
85686
  claudeBin: runtimes.claude.bin,
85565
85687
  codexBin: runtimes.codex.bin,
85688
+ codexVersion: runtimes.codex.version === "unknown" ? void 0 : runtimes.codex.version,
85566
85689
  project,
85567
85690
  database: frizzDb.db,
85568
85691
  serverLockPath: serverLockPathFor(project),