frizz 0.6.0 → 0.7.1

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 (98) hide show
  1. package/README.md +9 -6
  2. package/dist/codex-app-server-daemon.js +12 -3
  3. package/dist/dev-child.js +196 -66
  4. package/dist/frizz.js +50 -18
  5. package/package.json +2 -2
  6. package/runtime/cc-worker/bin/frizz-mcp.mjs +2 -2
  7. package/runtime/cc-worker/hooks/session-seed.mjs +2 -2
  8. package/runtime/cc-worker/skills/gh/SKILL.md +8 -7
  9. package/runtime/cc-worker/skills/waits/SKILL.md +4 -3
  10. package/web-dist/assets/{TerminalPane-GfnCbZQ8.js → TerminalPane-DBmtdVkV.js} +1 -1
  11. package/web-dist/assets/{abnfDiagram-VRR7QNED-By12QfQW.js → abnfDiagram-VRR7QNED-s9x3PTMx.js} +1 -1
  12. package/web-dist/assets/architecture-TIHT7OUA-Eb73yePg.js +1 -0
  13. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-CcXU45QO.js → architectureDiagram-ZJ3FMSHR-CIFfgliT.js} +1 -1
  14. package/web-dist/assets/{blockDiagram-677ZJIJ3-CmzyO2OF.js → blockDiagram-677ZJIJ3-BC-TeFao.js} +1 -1
  15. package/web-dist/assets/{c4Diagram-LMCZKHZV-DFGSW_ZS.js → c4Diagram-LMCZKHZV-DoJ_Bulk.js} +1 -1
  16. package/web-dist/assets/channel-CgSC9ikg.js +1 -0
  17. package/web-dist/assets/{chunk-32BRIVSS-whgaFxPY.js → chunk-32BRIVSS-BgchlScG.js} +1 -1
  18. package/web-dist/assets/{chunk-52WLFC77-DCT0MLgl.js → chunk-52WLFC77-D94nMfY5.js} +1 -1
  19. package/web-dist/assets/{chunk-C7G6YPKG-Br0OaxIX.js → chunk-C7G6YPKG-BNU4g2Rm.js} +1 -1
  20. package/web-dist/assets/{chunk-EX3LRPZG-CUnQkdEF.js → chunk-EX3LRPZG-DooNeZ0v.js} +1 -1
  21. package/web-dist/assets/{chunk-FWX5IMBZ-D1NqKRFr.js → chunk-FWX5IMBZ-DuXYfpte.js} +2 -2
  22. package/web-dist/assets/{chunk-HOUHSVGY-7ZL78OrO.js → chunk-HOUHSVGY-DDtc0Lod.js} +1 -1
  23. package/web-dist/assets/{chunk-ICXQ74PX-QSgBAuYw.js → chunk-ICXQ74PX-D1soMHlW.js} +1 -1
  24. package/web-dist/assets/{chunk-MOJQB5TN-DOI-0kaG.js → chunk-MOJQB5TN-BYZZoPrO.js} +1 -1
  25. package/web-dist/assets/{chunk-OGEWGWER-CZ3ED-QB.js → chunk-OGEWGWER-CSI1op21.js} +1 -1
  26. package/web-dist/assets/{chunk-PUDLZKDR-BQJElUFX.js → chunk-PUDLZKDR-B2_qeHID.js} +1 -1
  27. package/web-dist/assets/{chunk-Q4XR5HBZ-5B6Qs7rQ.js → chunk-Q4XR5HBZ-BNiafbsU.js} +1 -1
  28. package/web-dist/assets/{chunk-V7JOEXUC-DkMaAMFK.js → chunk-V7JOEXUC-D9aOhGjB.js} +1 -1
  29. package/web-dist/assets/{chunk-VAUOI2AC-Bvq-8GYO.js → chunk-VAUOI2AC-mt04_MVl.js} +1 -1
  30. package/web-dist/assets/{chunk-VR4S4FIN-DRVTU-HZ.js → chunk-VR4S4FIN-CTbhzN9l.js} +1 -1
  31. package/web-dist/assets/{chunk-WYO6CB5R-CRxI5d1E.js → chunk-WYO6CB5R-t-QJiLir.js} +1 -1
  32. package/web-dist/assets/{chunk-ZGVPDNZ5-D_3ow6xU.js → chunk-ZGVPDNZ5-BNBiwqoz.js} +1 -1
  33. package/web-dist/assets/classDiagram-OUVF2IWQ-DrMJp1ua.js +1 -0
  34. package/web-dist/assets/classDiagram-v2-EOCWNBFH-DrMJp1ua.js +1 -0
  35. package/web-dist/assets/{cynefin-VYW2F7L2-Bexf41pD.js → cynefin-VYW2F7L2-CJzDWQw9.js} +1 -1
  36. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-9gbbl1EH.js → cynefinDiagram-TSTJHNR4-C_wWdWpO.js} +1 -1
  37. package/web-dist/assets/{dagre-VKFMJZFB-B04shbL0.js → dagre-VKFMJZFB-JcoSMott.js} +1 -1
  38. package/web-dist/assets/{diagram-FQU43EPY-CGXlhEsB.js → diagram-FQU43EPY-DwPIrxvI.js} +1 -1
  39. package/web-dist/assets/{diagram-G47NLZAW-DmPCeTOa.js → diagram-G47NLZAW-Bs1Iyjn4.js} +1 -1
  40. package/web-dist/assets/{diagram-NH7WQ7WH-D8Hthvi7.js → diagram-NH7WQ7WH-DFXMkDQZ.js} +1 -1
  41. package/web-dist/assets/{diagram-OA4YK3LP-DbLR6tfL.js → diagram-OA4YK3LP-BdeEougN.js} +1 -1
  42. package/web-dist/assets/{diagram-WEI45ONY-B6YWev_0.js → diagram-WEI45ONY-DOeCVBYO.js} +1 -1
  43. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-8G1uf683.js → ebnfDiagram-CCIWWBDH-Dm5CrqN3.js} +1 -1
  44. package/web-dist/assets/{erDiagram-Q63AITRT-U_VRdQHz.js → erDiagram-Q63AITRT-B0ud7ncc.js} +1 -1
  45. package/web-dist/assets/eventmodeling-45OFAUF4-B52bjxMe.js +1 -0
  46. package/web-dist/assets/flowDiagram-23GEKE2U-DbFccp_w.js +1 -0
  47. package/web-dist/assets/{ganttDiagram-NO4QXBWP-Cnsi9uVf.js → ganttDiagram-NO4QXBWP-BdowNjAm.js} +1 -1
  48. package/web-dist/assets/{gitGraph-TEB2WS4Q-DYf7ka-G.js → gitGraph-TEB2WS4Q-BSDXrBNB.js} +1 -1
  49. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-Bvmr7dEH.js → gitGraphDiagram-IHSO6WYX-B3i3egZt.js} +1 -1
  50. package/web-dist/assets/{index-Czv3R-v0.js → index-BUX0IseR.js} +118 -118
  51. package/web-dist/assets/index-CjEngYN7.css +1 -0
  52. package/web-dist/assets/{info-DKCQHKI2-CdP9Q9r0.js → info-DKCQHKI2-B7CT3rc8.js} +1 -1
  53. package/web-dist/assets/{infoDiagram-FWYZ7A6U-RBcOH3Pi.js → infoDiagram-FWYZ7A6U-Bqmpgcu2.js} +1 -1
  54. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-zS0gy3fS.js → ishikawaDiagram-FXEZZL3T-Bd5LBQOn.js} +1 -1
  55. package/web-dist/assets/{journeyDiagram-5HDEW3XC-cOciJlFY.js → journeyDiagram-5HDEW3XC-lJOF3I7X.js} +1 -1
  56. package/web-dist/assets/{kanban-definition-HUTT4EX6-B-v0XClo.js → kanban-definition-HUTT4EX6-D4E5PRAv.js} +1 -1
  57. package/web-dist/assets/{line-z44YXdci.js → line-C9ZcrRyz.js} +1 -1
  58. package/web-dist/assets/{mermaid-parser.core-0Bg1x4p0.js → mermaid-parser.core-DkaL73Gv.js} +3 -3
  59. package/web-dist/assets/{mermaid.core-ByFqgcIE.js → mermaid.core-BQ89ySVU.js} +3 -3
  60. package/web-dist/assets/{mindmap-definition-LN4V7U3C-DiGaAQFh.js → mindmap-definition-LN4V7U3C-CB7JnCHh.js} +1 -1
  61. package/web-dist/assets/{packet-7NZHBO7P-Bv8ILo_S.js → packet-7NZHBO7P-BvyvpOhm.js} +1 -1
  62. package/web-dist/assets/{pegDiagram-2B236MQR-CtOUW7n0.js → pegDiagram-2B236MQR-BoygVhix.js} +1 -1
  63. package/web-dist/assets/{pie-RZYD4A2V-Dz4Bm-Vx.js → pie-RZYD4A2V-ChTjZLeC.js} +1 -1
  64. package/web-dist/assets/{pieDiagram-ENE6RG2P-VMY_CK3i.js → pieDiagram-ENE6RG2P-B3UNPTpF.js} +1 -1
  65. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-V7HZxM7j.js → quadrantDiagram-ABIIQ3AL-DJdWJ4GS.js} +1 -1
  66. package/web-dist/assets/{radar-I7S5WNFK-CWk6RruH.js → radar-I7S5WNFK-DHi5ReAY.js} +1 -1
  67. package/web-dist/assets/{railroad-3IZDKUUU-m_8st_Ez.js → railroad-3IZDKUUU-DcLJuGRW.js} +1 -1
  68. package/web-dist/assets/railroad-abnf-AHOZXSZD-BJcLzkwL.js +1 -0
  69. package/web-dist/assets/railroad-ebnf-EBAXGLYW-B4jrlWYB.js +1 -0
  70. package/web-dist/assets/railroad-peg-LSFZ7HO6-BpIzbo9k.js +1 -0
  71. package/web-dist/assets/{railroadDiagram-RFXS5EU6-Dz80V-7-.js → railroadDiagram-RFXS5EU6-W4vnMFZ2.js} +1 -1
  72. package/web-dist/assets/{requirementDiagram-TGXJPOKE-DrDDgTwm.js → requirementDiagram-TGXJPOKE-CV_V-9DW.js} +1 -1
  73. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-D4SeC-Bi.js → sankeyDiagram-HTMAVEWB-DuIIsNBv.js} +1 -1
  74. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-CA-ZO_qp.js → sequenceDiagram-DBY2YBRQ-DTT8ETnv.js} +1 -1
  75. package/web-dist/assets/{stateDiagram-2N3HPSRC-C_ZYvV2s.js → stateDiagram-2N3HPSRC-HVZaDROf.js} +1 -1
  76. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-DF9q3s7U.js +1 -0
  77. package/web-dist/assets/{swimlanes-5IMT3BWC-DGMDseYx.js → swimlanes-5IMT3BWC-DpGdPqRB.js} +1 -1
  78. package/web-dist/assets/swimlanesDiagram-G3AALYLV-BNeW4PCd.js +8 -0
  79. package/web-dist/assets/{timeline-definition-FHXFAJF6-CEiMp3_j.js → timeline-definition-FHXFAJF6-D9u3mgpd.js} +1 -1
  80. package/web-dist/assets/{treeView-QDETBFTQ-BSspY0US.js → treeView-QDETBFTQ-Dga6MhEJ.js} +1 -1
  81. package/web-dist/assets/{treemap-6X3UGDF4-DXNQxmQ7.js → treemap-6X3UGDF4-BwEXmcD6.js} +1 -1
  82. package/web-dist/assets/{vennDiagram-L72KCM5P-C-khqkdm.js → vennDiagram-L72KCM5P-BMvodYUW.js} +1 -1
  83. package/web-dist/assets/{wardley-OPB4EBWU-B9QCQ_Tm.js → wardley-OPB4EBWU-BzB6QK8X.js} +1 -1
  84. package/web-dist/assets/{wardleyDiagram-EHGQE667-vhVpAUzh.js → wardleyDiagram-EHGQE667-4ID7QRJi.js} +1 -1
  85. package/web-dist/assets/{xychartDiagram-FW5EYKEG-B8ev9zqs.js → xychartDiagram-FW5EYKEG-Ckob9hhW.js} +1 -1
  86. package/web-dist/index.html +2 -2
  87. package/web-dist/assets/architecture-TIHT7OUA-XOYod28l.js +0 -1
  88. package/web-dist/assets/channel-Bqn3pFKp.js +0 -1
  89. package/web-dist/assets/classDiagram-OUVF2IWQ-DEMCehy_.js +0 -1
  90. package/web-dist/assets/classDiagram-v2-EOCWNBFH-DEMCehy_.js +0 -1
  91. package/web-dist/assets/eventmodeling-45OFAUF4-DIpO2jQx.js +0 -1
  92. package/web-dist/assets/flowDiagram-23GEKE2U-aERDdceE.js +0 -1
  93. package/web-dist/assets/index-D8iyWUZ0.css +0 -1
  94. package/web-dist/assets/railroad-abnf-AHOZXSZD-DIaRscxr.js +0 -1
  95. package/web-dist/assets/railroad-ebnf-EBAXGLYW-CeHpbK-W.js +0 -1
  96. package/web-dist/assets/railroad-peg-LSFZ7HO6-CbhVIbOj.js +0 -1
  97. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-BLevFa3H.js +0 -1
  98. package/web-dist/assets/swimlanesDiagram-G3AALYLV-00mQRq57.js +0 -8
package/dist/dev-child.js CHANGED
@@ -475,7 +475,7 @@ function readGitWorktreeProjectId(worktree) {
475
475
  );
476
476
  }
477
477
  function syncGitConfigPath(path) {
478
- const fd = openSync(path, "r");
478
+ const fd = openSync(path, process.platform === "win32" ? "r+" : "r");
479
479
  try {
480
480
  fsyncSync(fd);
481
481
  } finally {
@@ -12702,6 +12702,16 @@ var init_interactions = __esm({
12702
12702
  }
12703
12703
  });
12704
12704
 
12705
+ // packages/shared/src/claim.ts
12706
+ var CLAIM_MAX_AGE_MS, CLAIM_LEASE_MS;
12707
+ var init_claim = __esm({
12708
+ "packages/shared/src/claim.ts"() {
12709
+ "use strict";
12710
+ CLAIM_MAX_AGE_MS = 5 * 6e4;
12711
+ CLAIM_LEASE_MS = 30 * 24 * 60 * 6e4;
12712
+ }
12713
+ });
12714
+
12705
12715
  // packages/shared/src/code-fences.ts
12706
12716
  function fencedInteriors(text) {
12707
12717
  const out = [];
@@ -13208,6 +13218,7 @@ var init_src = __esm({
13208
13218
  init_zod();
13209
13219
  init_interactions();
13210
13220
  init_thread_slug();
13221
+ init_claim();
13211
13222
  init_code_fences();
13212
13223
  init_delta();
13213
13224
  init_drainable_worker();
@@ -13598,7 +13609,7 @@ var init_src = __esm({
13598
13609
  ].join("\n");
13599
13610
  PARK_CORRECTION_NAMES_LEAD = "\u26A0\uFE0F Your ```awaiting fence names ";
13600
13611
  PARK_CORRECTION_RETIRED_LEAD = "\u26D4 Your ```awaiting fence uses ";
13601
- ThreadWatchKind = external_exports.enum(["shell", "github"]);
13612
+ ThreadWatchKind = external_exports.enum(["shell", "github", "timer"]);
13602
13613
  GithubChecksState = external_exports.enum(["none", "running", "passing", "failing"]);
13603
13614
  GithubMergeState = external_exports.enum(["mergeable", "blocked", "conflicting", "unknown"]);
13604
13615
  GithubWatchStatus = external_exports.object({
@@ -13632,7 +13643,12 @@ var init_src = __esm({
13632
13643
  state: external_exports.enum(["armed", "fired", "dropped"]),
13633
13644
  createdAt: external_exports.string(),
13634
13645
  /** `github` rows only, and absent until the first successful poll. */
13635
- github: GithubWatchStatus.optional()
13646
+ github: GithubWatchStatus.optional(),
13647
+ /** `timer` rows only: the armed timer's own registration, which is everything the row renders — the
13648
+ * worker's prompt is the row's NAME (a `tmr_…` id names nothing to a human) and the fire instant is
13649
+ * its status. Unlike `github` there is no polled half to be absent: a timer that exists is fully
13650
+ * known, so a timer row always carries this. */
13651
+ timer: external_exports.object({ fireAt: external_exports.string(), prompt: external_exports.string() }).strict().optional()
13636
13652
  }).strict();
13637
13653
  PR_WATCH_DEFAULT_FOR_MS = 6 * 60 * 60 * 1e3;
13638
13654
  PR_WATCH_MAX_ARMED = 32;
@@ -20911,6 +20927,21 @@ function retireOutstandingDeliveries(storage, slug) {
20911
20927
  storage.setDeliveryLedger(slug, serializeDeliveryLedger(next));
20912
20928
  return items.length - next.length;
20913
20929
  }
20930
+ function deliverOutstandingDeliveries(storage, slug) {
20931
+ const row = storage.getSession(slug);
20932
+ if (!row) return 0;
20933
+ const items = parseDeliveryLedger(row.delivery_ledger);
20934
+ const at = (/* @__PURE__ */ new Date()).toISOString();
20935
+ let moved = 0;
20936
+ const next = items.map((item) => {
20937
+ if (item.state !== "pending" && item.state !== "enqueued") return item;
20938
+ moved++;
20939
+ return { ...item, state: "delivered", updatedAt: at };
20940
+ });
20941
+ if (!moved) return 0;
20942
+ storage.setDeliveryLedger(slug, serializeDeliveryLedger(next));
20943
+ return moved;
20944
+ }
20914
20945
  function deliveryItem(storage, slug, id) {
20915
20946
  const row = storage.getSession(slug);
20916
20947
  if (!row) return null;
@@ -21112,13 +21143,13 @@ function dropCancelled(messages, items) {
21112
21143
  const dropped = /* @__PURE__ */ new Set();
21113
21144
  for (const item of items) {
21114
21145
  if (item.state !== "cancelled") continue;
21115
- const text = canon(item.text);
21146
+ const text = renderMatchKey(item.text);
21116
21147
  const from = Date.parse(item.at) - 5e3;
21117
21148
  const to = Date.parse(item.updatedAt) + CANCEL_MATCH_SLACK_MS;
21118
21149
  for (let i = messages.length - 1; i >= 0; i--) {
21119
21150
  const m = messages[i];
21120
21151
  if (m.role !== "user" || dropped.has(i)) continue;
21121
- if (canon(stripDeliveryMarkers(m.text)) !== text) continue;
21152
+ if (renderMatchKey(m.text) !== text) continue;
21122
21153
  const at = m.at ? Date.parse(m.at) : NaN;
21123
21154
  if (Number.isFinite(at) && Number.isFinite(from) && Number.isFinite(to) && (at < from || at > to)) continue;
21124
21155
  dropped.add(i);
@@ -21134,13 +21165,13 @@ function projectDeliveryLedger(messages, items) {
21134
21165
  if (!cancelled.live.length) return messages;
21135
21166
  const claimed = /* @__PURE__ */ new Set();
21136
21167
  for (const item of cancelled.live) {
21137
- const text = canon(item.text);
21168
+ const text = renderMatchKey(item.text);
21138
21169
  const tag = deliveryTag(item.id);
21139
21170
  let handled = false;
21140
21171
  for (let i = messages.length - 1; i >= 0; i--) {
21141
21172
  const m = messages[i];
21142
21173
  if (m.role !== "user" || claimed.has(i)) continue;
21143
- if (!decodeDeliveryMarkers(m.text).includes(tag) && canon(stripDeliveryMarkers(m.text)) !== text) continue;
21174
+ if (!decodeDeliveryMarkers(m.text).includes(tag) && renderMatchKey(m.text) !== text) continue;
21144
21175
  if (m.queued) {
21145
21176
  if (item.state === "delivered") {
21146
21177
  messages[i] = { ...m, queued: false, deliveryId: item.id, deliveryState: item.state };
@@ -21171,10 +21202,11 @@ function projectDeliveryLedger(messages, items) {
21171
21202
  }
21172
21203
  return messages;
21173
21204
  }
21174
- var PENDING_TIMEOUT_MS, UNCONFIRMED_DROP_MS, MAX_LEDGER_ITEMS, MAX_CANCELLED_ITEMS, canon, COMPOSED_ANCHOR_MIN, CANCEL_MATCH_SLACK_MS;
21205
+ var PENDING_TIMEOUT_MS, UNCONFIRMED_DROP_MS, MAX_LEDGER_ITEMS, MAX_CANCELLED_ITEMS, canon, COMPOSED_ANCHOR_MIN, renderMatchKey, CANCEL_MATCH_SLACK_MS;
21175
21206
  var init_delivery_ledger = __esm({
21176
21207
  "packages/server/src/delivery-ledger.ts"() {
21177
21208
  "use strict";
21209
+ init_src();
21178
21210
  init_delivery_marker();
21179
21211
  PENDING_TIMEOUT_MS = 6e4;
21180
21212
  UNCONFIRMED_DROP_MS = 60 * 6e4;
@@ -21182,6 +21214,7 @@ var init_delivery_ledger = __esm({
21182
21214
  MAX_CANCELLED_ITEMS = 12;
21183
21215
  canon = (s) => s.replace(/\s+/g, " ").trim();
21184
21216
  COMPOSED_ANCHOR_MIN = 24;
21217
+ renderMatchKey = (s) => canon(stripHumanGapNote(stripWakeDeliveryToken(stripDeliveryMarkers(s))));
21185
21218
  CANCEL_MATCH_SLACK_MS = 5e3;
21186
21219
  }
21187
21220
  });
@@ -35651,9 +35684,14 @@ function hasDeclaredBackgroundPark(tele, nowMs) {
35651
35684
  if (Number.isFinite(restedMs) && nowMs - restedMs > DECLARED_PARK_MAX_MS) return false;
35652
35685
  return true;
35653
35686
  }
35654
- function hasDeclaredWait(tele, nowMs) {
35687
+ function hasParkedTimerWatch(tele, armedTimerIds) {
35688
+ if (tele?.lastFence?.kind !== "awaiting") return false;
35689
+ return tele.lastFence.hints.some((hint) => hint.kind === "timer" && armedTimerIds.has(hint.value.trim()));
35690
+ }
35691
+ function hasDeclaredWait(tele, nowMs, armedTimerIds = /* @__PURE__ */ new Set()) {
35655
35692
  if (hasDeclaredBackgroundPark(tele, nowMs)) return true;
35656
35693
  if (tele?.lastFence?.kind === "awaiting" && hasParkedPrWatch(tele)) return true;
35694
+ if (hasParkedTimerWatch(tele, armedTimerIds)) return true;
35657
35695
  return (tele?.subAgents ?? []).some((a) => isDirectSubAgent(a) && a.state === "running");
35658
35696
  }
35659
35697
  function heldByRunningChecks(tele, github, registered) {
@@ -35691,9 +35729,23 @@ function bgSnoozeArmed(row) {
35691
35729
  function hasParkedExternalWait(tele, _nowMs) {
35692
35730
  return false;
35693
35731
  }
35694
- function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = []) {
35732
+ function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], armedTimers = []) {
35695
35733
  const seen = /* @__PURE__ */ new Set();
35696
35734
  const out = [];
35735
+ for (const t of armedTimers) {
35736
+ if (seen.has(`timer:${t.id}`)) continue;
35737
+ seen.add(`timer:${t.id}`);
35738
+ out.push({
35739
+ id: `timer:${slug}:${t.id}`,
35740
+ kind: "timer",
35741
+ target: t.id,
35742
+ state: "armed",
35743
+ createdAt: t.createdAt,
35744
+ // Always present, unlike the github half above: a timer that exists is fully known — there is no
35745
+ // poller to wait for. The prompt is the row's NAME client-side (a `tmr_…` id names nothing).
35746
+ timer: { fireAt: t.fireAt, prompt: t.prompt }
35747
+ });
35748
+ }
35697
35749
  for (const w of registered) {
35698
35750
  if (seen.has(`github:${w.target}`)) continue;
35699
35751
  seen.add(`github:${w.target}`);
@@ -35742,11 +35794,13 @@ function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, no
35742
35794
  if (tele?.lastFence?.kind === "done") return true;
35743
35795
  return true;
35744
35796
  }
35745
- function deriveAwaitingBackground(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set()) {
35746
- if (runtime !== "turn-idle" || !hasDeclaredWait(tele, nowMs)) return false;
35797
+ function deriveAwaitingBackground(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set(), armedTimerIds = /* @__PURE__ */ new Set()) {
35798
+ if (runtime !== "turn-idle" || !hasDeclaredWait(tele, nowMs, armedTimerIds)) return false;
35747
35799
  if (hasActionableInteraction || tele?.pendingAsk || tele?.pendingQuestion) return false;
35748
35800
  if (tele?.lastFence?.kind === "done") return false;
35749
- if (tele?.lastFence?.kind === "awaiting" && !hasParkedPrWatch(tele) && !hasDeclaredBackgroundPark(tele, nowMs)) return false;
35801
+ 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
35802
+ // suppressing this card left the wait stated nowhere but the fence's own machinery footer.
35803
+ !hasParkedTimerWatch(tele, armedTimerIds)) return false;
35750
35804
  return deriveNeedsYou({ ...row, bg_snooze_rested_at: null }, tele, runtime, hasActionableInteraction, nowMs, limitPause, false, deliveryProcessGone);
35751
35805
  }
35752
35806
  function resolveSessionProfile(row, tele) {
@@ -35830,6 +35884,13 @@ function sessionThreadView(projectDir, storage, row, tele, registeredLegacyTermi
35830
35884
  createdAt: new Date(w.created_at).toISOString()
35831
35885
  }));
35832
35886
  const registeredPrWatches = new Set(armedPrWatches.map((w) => w.target));
35887
+ const armedTimers = storage.listThreadTimers(row.slug, { armedOnly: true }).map((t) => ({
35888
+ id: t.id,
35889
+ prompt: t.prompt,
35890
+ fireAt: new Date(t.fire_at).toISOString(),
35891
+ createdAt: new Date(t.created_at).toISOString()
35892
+ }));
35893
+ const armedTimerIds = new Set(armedTimers.map((t) => t.id));
35833
35894
  const headlessStalled = row.codex_runtime === "app-server" ? appServerTurnStalled(codexTurnLiveness(row.slug, row.session_id), tele?.lastActivityAt, nowMs) : isBrokerClaudeRow(row) ? !claudeBrokerDaemonAlive(row.session_id) : false;
35834
35895
  const headlessLostWork = isBrokerClaudeRow(row) && headlessStalled && hasUnretiredOwnAgents(tele);
35835
35896
  const deliveryProcessGone = isBrokerClaudeRow(row) && headlessStalled;
@@ -35847,7 +35908,7 @@ function sessionThreadView(projectDir, storage, row, tele, registeredLegacyTermi
35847
35908
  const archived = state === "archived";
35848
35909
  const limitPause = resolveLimitPause(row, tele, nowMs);
35849
35910
  const needsYou = archived ? false : deriveNeedsYou(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, true, deliveryProcessGone, github, registeredPrWatches);
35850
- const awaitingBackground = archived ? false : deriveAwaitingBackground(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, deliveryProcessGone, github, registeredPrWatches);
35911
+ const awaitingBackground = archived ? false : deriveAwaitingBackground(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, deliveryProcessGone, github, registeredPrWatches, armedTimerIds);
35851
35912
  const crashed = runtime === "exited" && (tele?.turn === "in-flight" || hasLiveBackgroundWork(tele) || headlessLostWork);
35852
35913
  const snoozedUntil = futureSnooze(row, nowMs);
35853
35914
  const profile = resolveSessionProfile(row, tele);
@@ -35883,7 +35944,7 @@ function sessionThreadView(projectDir, storage, row, tele, registeredLegacyTermi
35883
35944
  // become the github rows, `watch:` lines the shell rows — so this strip lists exactly what will
35884
35945
  // actually wake the thread, and the two cannot drift into claiming different things. There is no
35885
35946
  // registry behind either any more (`thread_watch`, retired 2026-08-14).
35886
- watches: fenceWatchViews(row.slug, tele, tele?.lastAssistantAt, github, armedPrWatches),
35947
+ watches: fenceWatchViews(row.slug, tele, tele?.lastAssistantAt, github, armedPrWatches, armedTimers),
35887
35948
  pendingAsk: tele?.pendingAsk ? { questions: tele.pendingAsk.questions } : void 0,
35888
35949
  pendingQuestion: tele?.pendingQuestion ?? false,
35889
35950
  lastUserAt: tele?.lastUserAt,
@@ -38445,6 +38506,17 @@ import { execFile as execFile7 } from "node:child_process";
38445
38506
  import { readdirSync as readdirSync8, unlinkSync as unlinkSync4 } from "node:fs";
38446
38507
  import { connect as connectSocket } from "node:net";
38447
38508
  import { join as join29 } from "node:path";
38509
+ function parseLsofSocketNames(stdout) {
38510
+ const referenced = /* @__PURE__ */ new Set();
38511
+ for (const line of stdout.split("\n")) {
38512
+ if (!line.startsWith("n/")) continue;
38513
+ const raw2 = line.slice(1);
38514
+ referenced.add(raw2);
38515
+ const meta = raw2.indexOf(" type=");
38516
+ if (meta > 0) referenced.add(raw2.slice(0, meta));
38517
+ }
38518
+ return referenced;
38519
+ }
38448
38520
  function sweepStaleSockets(options, deps = {}) {
38449
38521
  if (process.platform === "win32") return;
38450
38522
  const keep = new Set(options.keep ?? []);
@@ -38480,9 +38552,7 @@ var init_stale_socket_sweep = __esm({
38480
38552
  execFile7("lsof", ["-U", "-F", "n"], { timeout: 3e4, maxBuffer: 16 * 1024 * 1024 }, (error, stdout) => {
38481
38553
  if (!stdout) return done(null);
38482
38554
  if (error && !stdout.includes(prefix)) return done(null);
38483
- const referenced = /* @__PURE__ */ new Set();
38484
- for (const line of stdout.split("\n")) if (line.startsWith("n/")) referenced.add(line.slice(1));
38485
- done(referenced);
38555
+ done(parseLsofSocketNames(stdout));
38486
38556
  }).unref?.();
38487
38557
  };
38488
38558
  defaultProbe = (path, verdict) => {
@@ -38502,6 +38572,7 @@ var init_stale_socket_sweep = __esm({
38502
38572
 
38503
38573
  // packages/server/src/orphan-reaper.ts
38504
38574
  import { execFileSync as execFileSync5 } from "node:child_process";
38575
+ import { readFileSync as readFileSync17 } from "node:fs";
38505
38576
  import { dirname as dirname12 } from "node:path";
38506
38577
  function firstTokenBasename(command) {
38507
38578
  const first = command.trimStart().split(/\s+/, 1)[0] ?? "";
@@ -38601,8 +38672,8 @@ function parseEtimeMs(etime) {
38601
38672
  else return 0;
38602
38673
  return (((days * 24 + h) * 60 + m) * 60 + s) * 1e3;
38603
38674
  }
38604
- function enumerateProcs(exec = defaultExec) {
38605
- const base = exec("ps", ["-axww", "-o", "pid=,ppid=,etime=,command="]);
38675
+ function enumerateProcs(exec = defaultExec, opts = {}) {
38676
+ const base = exec("ps", ["-Aww", "-o", "pid=,ppid=,etime=,command="]);
38606
38677
  const rows = [];
38607
38678
  const byPid = /* @__PURE__ */ new Map();
38608
38679
  const pids = [];
@@ -38618,6 +38689,21 @@ function enumerateProcs(exec = defaultExec) {
38618
38689
  pids.push(pid);
38619
38690
  }
38620
38691
  if (!pids.length) return rows;
38692
+ if ((opts.platform ?? process.platform) === "linux") {
38693
+ const readEnv = opts.readEnv ?? defaultReadEnv;
38694
+ for (const row of rows) {
38695
+ const environ = readEnv(row.pid);
38696
+ if (environ === null) continue;
38697
+ for (const entry of environ.split("\0")) {
38698
+ const m = entry.match(/^FRIZZ_THREAD=([A-Za-z0-9._-]+)$/);
38699
+ if (m) {
38700
+ row.slug = m[1];
38701
+ break;
38702
+ }
38703
+ }
38704
+ }
38705
+ return rows;
38706
+ }
38621
38707
  let text;
38622
38708
  try {
38623
38709
  text = exec("ps", ["-Eww", "-o", "pid=,command=", "-p", pids.join(",")]);
@@ -38693,7 +38779,7 @@ function sweepOrphansOnce(deps = {}) {
38693
38779
  const minAgeMs = deps.minAgeMs ?? ORPHAN_MIN_AGE_MS;
38694
38780
  let rows;
38695
38781
  try {
38696
- rows = enumerateProcs(exec);
38782
+ rows = enumerateProcs(exec, { platform: deps.platform, readEnv: deps.readEnv });
38697
38783
  } catch {
38698
38784
  return { reaped: 0, deadSlugs: [], liveSlugs: [] };
38699
38785
  }
@@ -38741,7 +38827,7 @@ function startOrphanReaper(deps = {}) {
38741
38827
  clearInterval(socketTimer);
38742
38828
  };
38743
38829
  }
38744
- var ORPHAN_REAP_INTERVAL_MS, ORPHAN_MIN_AGE_MS, SLUG_RE, defaultExec, defaultKill, STALE_SOCKET_SWEEP_INTERVAL_MS, STALE_SOCKET_SWEEP_DELAY_MS, CLAUDE_BROKER_SOCKET_PREFIX;
38830
+ var ORPHAN_REAP_INTERVAL_MS, ORPHAN_MIN_AGE_MS, SLUG_RE, defaultExec, defaultReadEnv, defaultKill, STALE_SOCKET_SWEEP_INTERVAL_MS, STALE_SOCKET_SWEEP_DELAY_MS, CLAUDE_BROKER_SOCKET_PREFIX;
38745
38831
  var init_orphan_reaper = __esm({
38746
38832
  "packages/server/src/orphan-reaper.ts"() {
38747
38833
  "use strict";
@@ -38751,6 +38837,13 @@ var init_orphan_reaper = __esm({
38751
38837
  ORPHAN_MIN_AGE_MS = 12e4;
38752
38838
  SLUG_RE = /FRIZZ_THREAD=([A-Za-z0-9._-]+)/;
38753
38839
  defaultExec = (file, args) => execFileSync5(file, args, { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 1e4 });
38840
+ defaultReadEnv = (pid) => {
38841
+ try {
38842
+ return readFileSync17(`/proc/${pid}/environ`, "utf8");
38843
+ } catch {
38844
+ return null;
38845
+ }
38846
+ };
38754
38847
  defaultKill = (pid) => {
38755
38848
  try {
38756
38849
  process.kill(pid, "SIGKILL");
@@ -42078,7 +42171,7 @@ var init_server = __esm({
42078
42171
  });
42079
42172
 
42080
42173
  // packages/server/src/repair.ts
42081
- import { existsSync as existsSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "node:fs";
42174
+ import { existsSync as existsSync12, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
42082
42175
  import { basename as basename7, dirname as dirname13, resolve as resolve6 } from "node:path";
42083
42176
  function quoteValue(v) {
42084
42177
  if (/^[\w./#:+-]+$/.test(v)) return v;
@@ -42094,7 +42187,7 @@ function repairThreadFile(frizzDir, file) {
42094
42187
  if (dirname13(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
42095
42188
  if (!abs.endsWith(".md")) throw new RepairError(`refusing to repair "${file}": not a .md thread file`);
42096
42189
  if (!existsSync12(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
42097
- const content = readFileSync17(abs, "utf8");
42190
+ const content = readFileSync18(abs, "utf8");
42098
42191
  const firstLine2 = content.replace(/^/, "").split(/\r?\n/).find((l) => l.trim() !== "");
42099
42192
  if (firstLine2?.trim() === "---") {
42100
42193
  throw new RepairError(`${basename7(abs)} already opens with a "---" block \u2014 repair only heals a MISSING frontmatter block`);
@@ -42127,7 +42220,7 @@ var init_repair = __esm({
42127
42220
  });
42128
42221
 
42129
42222
  // cc-worker/hooks/bash-background.mjs
42130
- import { readFileSync as readFileSync18 } from "node:fs";
42223
+ import { readFileSync as readFileSync19 } from "node:fs";
42131
42224
  import { basename as basename8 } from "node:path";
42132
42225
  import { pathToFileURL } from "node:url";
42133
42226
  function emit(obj) {
@@ -42276,7 +42369,7 @@ var init_bash_background = __esm({
42276
42369
  if (isDirectHookExecution(process.argv[1], import.meta.url)) {
42277
42370
  try {
42278
42371
  const env = process.argv.includes("--frizz-thread") ? { ...process.env, FRIZZ_THREAD: process.env.FRIZZ_THREAD || "codex-worker" } : process.env;
42279
- emit(evaluateBashBackgroundHook(JSON.parse(readFileSync18(0, "utf8")), env));
42372
+ emit(evaluateBashBackgroundHook(JSON.parse(readFileSync19(0, "utf8")), env));
42280
42373
  } catch {
42281
42374
  emit({});
42282
42375
  }
@@ -42285,7 +42378,7 @@ var init_bash_background = __esm({
42285
42378
  });
42286
42379
 
42287
42380
  // packages/server/src/transcript.ts
42288
- import { closeSync as closeSync13, existsSync as existsSync13, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync19, readSync as readSync7, renameSync as renameSync9, statSync as statSync14, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
42381
+ 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";
42289
42382
  import { createHash as createHash13 } from "node:crypto";
42290
42383
  import { StringDecoder as StringDecoder4 } from "node:string_decoder";
42291
42384
  import { join as join31 } from "node:path";
@@ -42393,17 +42486,28 @@ function pushToolPart(m, call) {
42393
42486
  else m.parts.push({ kind: "tools", tools: [call] });
42394
42487
  }
42395
42488
  function coalescedQueuedKeys(deliveredText, pendingKeys) {
42396
- const consumed = [];
42397
- let cursor = 0;
42398
- for (const key of pendingKeys) {
42399
- if (!deliveredText.startsWith(key, cursor)) break;
42400
- cursor += key.length;
42401
- consumed.push(key);
42402
- if (cursor === deliveredText.length) return consumed;
42403
- if (!deliveredText.startsWith("\n", cursor)) break;
42404
- cursor += 1;
42405
- }
42406
- return cursor === deliveredText.length && consumed.length > 0 ? consumed : [];
42489
+ if (!deliveredText) return [];
42490
+ const keys = [...pendingKeys];
42491
+ const dead = /* @__PURE__ */ new Set();
42492
+ const walk = (cursor, index) => {
42493
+ if (index >= keys.length) return null;
42494
+ const state = cursor * (keys.length + 1) + index;
42495
+ if (dead.has(state)) return null;
42496
+ const key = keys[index];
42497
+ if (key && deliveredText.startsWith(key, cursor)) {
42498
+ const next = cursor + key.length;
42499
+ if (next === deliveredText.length) return [key];
42500
+ if (deliveredText.startsWith("\n", next)) {
42501
+ const rest = walk(next + 1, index + 1);
42502
+ if (rest) return [key, ...rest];
42503
+ }
42504
+ }
42505
+ const skipped = walk(cursor, index + 1);
42506
+ if (skipped) return skipped;
42507
+ dead.add(state);
42508
+ return null;
42509
+ };
42510
+ return walk(0, 0) ?? [];
42407
42511
  }
42408
42512
  function commandEnvelopeQueuedKey(deliveredText) {
42409
42513
  const name = COMMAND_NAME_RE.exec(deliveredText)?.[1]?.trim();
@@ -42890,7 +42994,7 @@ function persistSentFile(srcPath, idKey) {
42890
42994
  if (existsSync13(dest)) return dest;
42891
42995
  const size = statSync14(srcPath).size;
42892
42996
  if (size === 0 || size > SENT_IMAGE_MAX_BYTES) return void 0;
42893
- const buf = readFileSync19(srcPath);
42997
+ const buf = readFileSync20(srcPath);
42894
42998
  if (!looksLikeImage(buf, outExt)) return void 0;
42895
42999
  mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
42896
43000
  const tmp = join31(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
@@ -43310,7 +43414,7 @@ function readTranscript(project, sessionId) {
43310
43414
  }
43311
43415
  function verifyIncrementalParse(path, identityPrefix, incremental) {
43312
43416
  try {
43313
- const fresh = parseTranscript(readFileSync19(path, "utf8"), identityPrefix);
43417
+ const fresh = parseTranscript(readFileSync20(path, "utf8"), identityPrefix);
43314
43418
  const a = JSON.stringify(incremental);
43315
43419
  const b = JSON.stringify(fresh);
43316
43420
  if (a !== b) {
@@ -44542,7 +44646,7 @@ function readEarlierThreadTranscriptPage(project, storage, slug, cursor, backend
44542
44646
  }
44543
44647
  function readCodexTranscriptFile(absPath, nativeId = absPath) {
44544
44648
  try {
44545
- return parseCodexTranscript(readFileSync19(absPath, "utf8"), `codex:${nativeId}`);
44649
+ return parseCodexTranscript(readFileSync20(absPath, "utf8"), `codex:${nativeId}`);
44546
44650
  } catch {
44547
44651
  return [];
44548
44652
  }
@@ -44636,7 +44740,7 @@ function projectTranscriptPageAgentLifecycles(page, lookup, peerLookup) {
44636
44740
  function readTranscriptFile(absPath) {
44637
44741
  try {
44638
44742
  const pathKey = createHash13("sha256").update(absPath).digest("base64url").slice(0, 16);
44639
- return parseTranscript(readFileSync19(absPath, "utf8"), `claude-file:${pathKey}`);
44743
+ return parseTranscript(readFileSync20(absPath, "utf8"), `claude-file:${pathKey}`);
44640
44744
  } catch {
44641
44745
  return [];
44642
44746
  }
@@ -44733,7 +44837,7 @@ var init_open_external = __esm({
44733
44837
 
44734
44838
  // packages/server/src/local-file.ts
44735
44839
  import { spawn as spawn6 } from "node:child_process";
44736
- import { readFileSync as readFileSync20, realpathSync as realpathSync8, statSync as statSync15 } from "node:fs";
44840
+ import { readFileSync as readFileSync21, realpathSync as realpathSync8, statSync as statSync15 } from "node:fs";
44737
44841
  import { homedir as homedir15 } from "node:os";
44738
44842
  import { isAbsolute as isAbsolute3, join as join32, resolve as resolve7, sep } from "node:path";
44739
44843
  function isUnder(real, root) {
@@ -44776,7 +44880,7 @@ function readLocalMarkdown(rawPath, roots) {
44776
44880
  if (!MARKDOWN_FILE_EXT.test(rawPath.trim())) throw new Error("Local file is not a Markdown file");
44777
44881
  const path = resolveLocalFile(rawPath, roots);
44778
44882
  if (!MARKDOWN_FILE_EXT.test(path)) throw new Error("Local file is not a Markdown file");
44779
- const bytes = readFileSync20(path);
44883
+ const bytes = readFileSync21(path);
44780
44884
  if (bytes.length <= MARKDOWN_READ_LIMIT) return { path, markdown: bytes.toString("utf8"), truncated: false };
44781
44885
  const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
44782
44886
  const lastBreak = head.lastIndexOf(10);
@@ -45222,6 +45326,17 @@ function readBackgroundShellOutput(path, maxBytes = OUTPUT_TAIL_BYTES) {
45222
45326
  if (fd !== void 0) closeSync14(fd);
45223
45327
  }
45224
45328
  }
45329
+ function readHead2(fd, length) {
45330
+ if (length <= 0) return "";
45331
+ const buffer = Buffer.alloc(length);
45332
+ let read = 0;
45333
+ while (read < length) {
45334
+ const count = readSync8(fd, buffer, read, length - read, read);
45335
+ if (count === 0) break;
45336
+ read += count;
45337
+ }
45338
+ return buffer.subarray(0, read).toString("latin1");
45339
+ }
45225
45340
  function backgroundShellLineCount(path) {
45226
45341
  let fd;
45227
45342
  try {
@@ -45229,9 +45344,21 @@ function backgroundShellLineCount(path) {
45229
45344
  const stat2 = fstatSync4(fd);
45230
45345
  const size = stat2.size;
45231
45346
  let state = scans.get(path);
45232
- if (state && (state.bytes > size || state.ino !== stat2.ino)) state = void 0;
45347
+ if (state && (state.bytes > size || state.ino !== stat2.ino || state.birthtimeMs !== stat2.birthtimeMs)) state = void 0;
45348
+ if (state && state.fingerprint !== readHead2(fd, state.fingerprint.length)) state = void 0;
45233
45349
  if (state?.overflowed) return void 0;
45234
- if (!state) state = { ino: stat2.ino, bytes: 0, breaks: 0, pendingCR: false, trailing: false, overflowed: false };
45350
+ if (!state) {
45351
+ state = {
45352
+ ino: stat2.ino,
45353
+ birthtimeMs: stat2.birthtimeMs,
45354
+ fingerprint: readHead2(fd, Math.min(FINGERPRINT_BYTES, size)),
45355
+ bytes: 0,
45356
+ breaks: 0,
45357
+ pendingCR: false,
45358
+ trailing: false,
45359
+ overflowed: false
45360
+ };
45361
+ }
45235
45362
  if (size - state.bytes > SCAN_CEILING_BYTES) {
45236
45363
  scans.set(path, { ...state, overflowed: true });
45237
45364
  return void 0;
@@ -45267,7 +45394,7 @@ function backgroundShellLineCount(path) {
45267
45394
  if (fd !== void 0) closeSync14(fd);
45268
45395
  }
45269
45396
  }
45270
- var OUTPUT_TAIL_BYTES, SCAN_CHUNK_BYTES, SCAN_CEILING_BYTES, SCAN_CACHE_LIMIT, ANSI_ESCAPE_RE, scans;
45397
+ var OUTPUT_TAIL_BYTES, SCAN_CHUNK_BYTES, SCAN_CEILING_BYTES, SCAN_CACHE_LIMIT, FINGERPRINT_BYTES, ANSI_ESCAPE_RE, scans;
45271
45398
  var init_background_shell_output = __esm({
45272
45399
  "packages/server/src/background-shell-output.ts"() {
45273
45400
  "use strict";
@@ -45275,6 +45402,7 @@ var init_background_shell_output = __esm({
45275
45402
  SCAN_CHUNK_BYTES = 256 * 1024;
45276
45403
  SCAN_CEILING_BYTES = 128 * 1024 * 1024;
45277
45404
  SCAN_CACHE_LIMIT = 512;
45405
+ FINGERPRINT_BYTES = 64;
45278
45406
  ANSI_ESCAPE_RE = /\x1b(?:\][^\x07]*(?:\x07|\x1b\\)|\[[0-?]*[ -/]*[@-~])/g;
45279
45407
  scans = /* @__PURE__ */ new Map();
45280
45408
  }
@@ -45283,7 +45411,7 @@ var init_background_shell_output = __esm({
45283
45411
  // packages/server/src/image-header.ts
45284
45412
  import { closeSync as closeSync15, openSync as openSync16, readSync as readSync9, statSync as statSync16 } from "node:fs";
45285
45413
  import { extname } from "node:path";
45286
- function readHead2(path, budget) {
45414
+ function readHead3(path, budget) {
45287
45415
  let fd;
45288
45416
  try {
45289
45417
  const size = statSync16(path).size;
@@ -45391,7 +45519,7 @@ function svg(head) {
45391
45519
  }
45392
45520
  function svgDeclaresColour(path) {
45393
45521
  if (extname(path).toLowerCase() !== ".svg") return void 0;
45394
- const head = readHead2(path, 512 * 1024);
45522
+ const head = readHead3(path, 512 * 1024);
45395
45523
  if (!head) return void 0;
45396
45524
  const source = head.toString("utf8");
45397
45525
  if (!/<svg\b/iu.test(source)) return void 0;
@@ -45404,7 +45532,7 @@ function svgDeclaresColour(path) {
45404
45532
  function imageDimensions(path) {
45405
45533
  const extension2 = extname(path).toLowerCase();
45406
45534
  const budget = extension2 === ".jpg" || extension2 === ".jpeg" ? JPEG_SCAN_BYTES : HEAD_BYTES2;
45407
- const head = readHead2(path, budget);
45535
+ const head = readHead3(path, budget);
45408
45536
  if (!head) return void 0;
45409
45537
  return png(head) ?? gif(head) ?? webp(head) ?? ico(head) ?? jpeg(head) ?? svg(head);
45410
45538
  }
@@ -45512,7 +45640,7 @@ var init_directory_picker = __esm({
45512
45640
  });
45513
45641
 
45514
45642
  // packages/server/src/router.ts
45515
- import { readFileSync as readFileSync21, statSync as statSync17 } from "node:fs";
45643
+ import { readFileSync as readFileSync22, statSync as statSync17 } from "node:fs";
45516
45644
  import { join as join33, resolve as resolve8 } from "node:path";
45517
45645
  import { randomUUID as randomUUID16 } from "node:crypto";
45518
45646
  import { basename as basename9, dirname as dirname14 } from "node:path";
@@ -45721,7 +45849,7 @@ function storeProjectIcon(id, name, bytes) {
45721
45849
  function setProjectIconFromFile(id, file) {
45722
45850
  let bytes;
45723
45851
  try {
45724
- bytes = readFileSync21(file);
45852
+ bytes = readFileSync22(file);
45725
45853
  } catch {
45726
45854
  throw new Error("That file could not be read.");
45727
45855
  }
@@ -46361,15 +46489,16 @@ ${gapNote}` : input.message;
46361
46489
  )
46362
46490
  });
46363
46491
  if (input.freshProcess) retireOutstandingDeliveries(ctx.storage, input.slug);
46364
- if (input.interrupt) bridge.interruptTurn({ threadSlug: input.slug, sessionId: row.session_id });
46492
+ const preempted = input.interrupt === true && bridge.interruptTurn({ threadSlug: input.slug, sessionId: row.session_id });
46365
46493
  if (input.deliveryId) {
46366
46494
  appendDelivery(ctx.storage, input.slug, {
46367
46495
  id: input.deliveryId,
46368
46496
  text: input.message,
46369
46497
  state: midTurn && input.freshProcess !== true ? "enqueued" : "delivered"
46370
46498
  });
46371
- ctx.transcriptChange.emit([input.slug]);
46372
46499
  }
46500
+ if (preempted) deliverOutstandingDeliveries(ctx.storage, input.slug);
46501
+ if (input.deliveryId || preempted) ctx.transcriptChange.emit([input.slug]);
46373
46502
  ctx.board.refresh();
46374
46503
  return;
46375
46504
  }
@@ -46455,6 +46584,7 @@ ${gapNote}` : input.message;
46455
46584
  if (!bridge.interruptTurn({ threadSlug: input.slug, sessionId: row.session_id })) {
46456
46585
  return { interrupted: false, reason: "Nothing to interrupt \u2014 this thread has no turn running" };
46457
46586
  }
46587
+ if (deliverOutstandingDeliveries(ctx.storage, input.slug)) ctx.transcriptChange.emit([input.slug]);
46458
46588
  return { interrupted: true };
46459
46589
  }
46460
46590
  }),
@@ -47710,7 +47840,7 @@ var init_compression = __esm({
47710
47840
  });
47711
47841
 
47712
47842
  // packages/server/src/local-image.ts
47713
- import { readFileSync as readFileSync22, realpathSync as realpathSync9, statSync as statSync18 } from "node:fs";
47843
+ import { readFileSync as readFileSync23, realpathSync as realpathSync9, statSync as statSync18 } from "node:fs";
47714
47844
  import { extname as extname2, isAbsolute as isAbsolute4 } from "node:path";
47715
47845
  function resolveLocalImage(rawPath) {
47716
47846
  if (!rawPath || !isAbsolute4(rawPath)) return { status: 400 };
@@ -47724,7 +47854,7 @@ function resolveLocalImage(rawPath) {
47724
47854
  }
47725
47855
  try {
47726
47856
  if (!statSync18(real).isFile()) return { status: 404 };
47727
- return { status: 200, contentType, body: readFileSync22(real) };
47857
+ return { status: 200, contentType, body: readFileSync23(real) };
47728
47858
  } catch {
47729
47859
  return { status: 404 };
47730
47860
  }
@@ -47744,7 +47874,7 @@ var init_local_image = __esm({
47744
47874
  });
47745
47875
 
47746
47876
  // packages/server/src/project-icon.ts
47747
- import { readFileSync as readFileSync23, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync19 } from "node:fs";
47877
+ import { readFileSync as readFileSync24, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync19 } from "node:fs";
47748
47878
  import { homedir as homedir17 } from "node:os";
47749
47879
  import { basename as basename10, dirname as dirname15, extname as extname3, join as join34, relative, sep as sep2 } from "node:path";
47750
47880
  function normalizeStem(stem) {
@@ -47806,7 +47936,7 @@ function iconDirectories(root) {
47806
47936
  function manifestIcons(manifestPath) {
47807
47937
  let parsed;
47808
47938
  try {
47809
- parsed = JSON.parse(readFileSync23(manifestPath, "utf8"));
47939
+ parsed = JSON.parse(readFileSync24(manifestPath, "utf8"));
47810
47940
  } catch {
47811
47941
  return [];
47812
47942
  }
@@ -47907,7 +48037,7 @@ function resolveProjectIconResponse(id, options = {}) {
47907
48037
  });
47908
48038
  if (!roots.some((root) => real === root || real.startsWith(root + sep2))) return { status: 404 };
47909
48039
  try {
47910
- return { status: 200, contentType, body: readFileSync23(real) };
48040
+ return { status: 200, contentType, body: readFileSync24(real) };
47911
48041
  } catch {
47912
48042
  return { status: 404 };
47913
48043
  }
@@ -48019,7 +48149,7 @@ var init_project_icon = __esm({
48019
48149
  });
48020
48150
 
48021
48151
  // packages/server/src/local-visualization.ts
48022
- import { readdirSync as readdirSync11, readFileSync as readFileSync24, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
48152
+ import { readdirSync as readdirSync11, readFileSync as readFileSync25, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
48023
48153
  import { join as join35, sep as sep3 } from "node:path";
48024
48154
  function children(path, pattern) {
48025
48155
  try {
@@ -48134,7 +48264,7 @@ function resolveLocalVisualization(projectDir, sessionId, file) {
48134
48264
  try {
48135
48265
  const size = statSync20(path).size;
48136
48266
  if (size > MAX_FRAGMENT_BYTES) return { status: 413 };
48137
- fragment = readFileSync24(path, "utf8");
48267
+ fragment = readFileSync25(path, "utf8");
48138
48268
  } catch {
48139
48269
  return { status: 404 };
48140
48270
  }
@@ -49523,7 +49653,7 @@ var init_app_socket = __esm({
49523
49653
  });
49524
49654
 
49525
49655
  // packages/server/src/boot-progress.ts
49526
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync25, renameSync as renameSync10, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "node:fs";
49656
+ import { mkdirSync as mkdirSync18, readFileSync as readFileSync26, renameSync as renameSync10, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "node:fs";
49527
49657
  import { basename as basename11, dirname as dirname16, join as join37 } from "node:path";
49528
49658
  function bootProgressPath(stateDir) {
49529
49659
  return join37(stateDir, BOOT_PROGRESS_NAME);
@@ -49664,11 +49794,11 @@ __export(index_exports, {
49664
49794
  unknownProjectPage: () => unknownProjectPage
49665
49795
  });
49666
49796
  import { createServer } from "node:http";
49667
- import { readFileSync as readFileSync26, existsSync as existsSync15 } from "node:fs";
49797
+ import { readFileSync as readFileSync27, existsSync as existsSync15 } from "node:fs";
49668
49798
  import { dirname as dirname17, join as join38, resolve as resolve9, extname as extname4, normalize } from "node:path";
49669
49799
  function readServerAddressHolder(path) {
49670
49800
  try {
49671
- const value = JSON.parse(readFileSync26(path, "utf8"));
49801
+ const value = JSON.parse(readFileSync27(path, "utf8"));
49672
49802
  if (typeof value?.pid !== "number") return void 0;
49673
49803
  return { pid: value.pid, port: typeof value.port === "number" ? value.port : -1 };
49674
49804
  } catch {
@@ -49772,7 +49902,7 @@ function serveStatic(distDir, req, res) {
49772
49902
  if (!file.startsWith(distDir)) file = join38(distDir, "index.html");
49773
49903
  if (!existsSync15(file)) file = join38(distDir, "index.html");
49774
49904
  try {
49775
- const body = readFileSync26(file);
49905
+ const body = readFileSync27(file);
49776
49906
  res.writeHead(200, { "content-type": MIME[extname4(file)] ?? "application/octet-stream" });
49777
49907
  res.end(body);
49778
49908
  } catch {
@@ -50195,7 +50325,7 @@ async function startServer(opts = {}) {
50195
50325
  if (vite) {
50196
50326
  vite.middlewares(req, res, () => {
50197
50327
  try {
50198
- const html = readFileSync26(join38(webRoot, "index.html"), "utf8");
50328
+ const html = readFileSync27(join38(webRoot, "index.html"), "utf8");
50199
50329
  void vite.transformIndexHtml(url, html).then((out) => {
50200
50330
  res.writeHead(200, { "content-type": "text/html" });
50201
50331
  res.end(out);