frizz 0.7.7 → 0.8.0

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 (127) hide show
  1. package/README.md +15 -45
  2. package/dist/claude-agent-broker.js +7 -4
  3. package/dist/dev-child.js +422 -273
  4. package/dist/frizz.js +1003 -398
  5. package/package.json +1 -1
  6. package/runtime/cc-worker/.claude-plugin/plugin.json +1 -1
  7. package/runtime/cc-worker/DECISIONS.md +31 -2
  8. package/runtime/cc-worker/agents/high.md +7 -0
  9. package/runtime/cc-worker/agents/low.md +7 -0
  10. package/runtime/cc-worker/agents/max.md +7 -0
  11. package/runtime/cc-worker/agents/medium.md +7 -0
  12. package/runtime/cc-worker/agents/xhigh.md +7 -0
  13. package/runtime/cc-worker/bin/frizz-mcp.mjs +3 -2
  14. package/runtime/cc-worker/hooks/agent-dispatch.mjs +7 -6
  15. package/runtime/cc-worker/hooks/hooks.json +0 -2
  16. package/runtime/cc-worker/hooks/scratchpad.mjs +5 -3
  17. package/runtime/cc-worker/hooks/session-seed.mjs +17 -17
  18. package/runtime/cc-worker/skills/gh/SKILL.md +5 -19
  19. package/web-dist/assets/{TerminalPane-Cb8jOFP9.js → TerminalPane-BIBHC61j.js} +1 -1
  20. package/web-dist/assets/{abnfDiagram-VRR7QNED-Dtbg2eZ0.js → abnfDiagram-VRR7QNED-CcFQGFPf.js} +1 -1
  21. package/web-dist/assets/architecture-TIHT7OUA-BZEZt7jZ.js +1 -0
  22. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-DzQjWW7u.js → architectureDiagram-ZJ3FMSHR-DErIQOIs.js} +1 -1
  23. package/web-dist/assets/{blockDiagram-677ZJIJ3-BJnpkLMt.js → blockDiagram-677ZJIJ3-CpsUqur1.js} +1 -1
  24. package/web-dist/assets/{c4Diagram-LMCZKHZV-COdatsYu.js → c4Diagram-LMCZKHZV-D2dHmCEJ.js} +1 -1
  25. package/web-dist/assets/channel-DdFRPviT.js +1 -0
  26. package/web-dist/assets/{chunk-32BRIVSS-CORMw4Vm.js → chunk-32BRIVSS-aOCEfxRW.js} +1 -1
  27. package/web-dist/assets/{chunk-52WLFC77-C7lEadTg.js → chunk-52WLFC77-2iw4nJPC.js} +1 -1
  28. package/web-dist/assets/{chunk-C7G6YPKG-DZy4F97l.js → chunk-C7G6YPKG-BPD3iHVE.js} +1 -1
  29. package/web-dist/assets/{chunk-EX3LRPZG-HynlfV9p.js → chunk-EX3LRPZG-DrefHift.js} +1 -1
  30. package/web-dist/assets/{chunk-FWX5IMBZ-BbwKNhKm.js → chunk-FWX5IMBZ-By940EHT.js} +2 -2
  31. package/web-dist/assets/{chunk-HOUHSVGY-lIhOZSmL.js → chunk-HOUHSVGY-B1Cqvz7e.js} +1 -1
  32. package/web-dist/assets/{chunk-ICXQ74PX-WGxaMO8c.js → chunk-ICXQ74PX-1LkVpWuI.js} +1 -1
  33. package/web-dist/assets/{chunk-MOJQB5TN-B-cx0f3u.js → chunk-MOJQB5TN-Ip1cy64r.js} +1 -1
  34. package/web-dist/assets/{chunk-OGEWGWER-DJceVwZH.js → chunk-OGEWGWER-CNjVyu8F.js} +1 -1
  35. package/web-dist/assets/{chunk-PUDLZKDR-CGwHZns8.js → chunk-PUDLZKDR-9HZR6DAU.js} +1 -1
  36. package/web-dist/assets/{chunk-Q4XR5HBZ-PTSWtmk1.js → chunk-Q4XR5HBZ-vtWwOCuQ.js} +1 -1
  37. package/web-dist/assets/{chunk-V7JOEXUC-VNxHZPcf.js → chunk-V7JOEXUC-DC6YaHG0.js} +1 -1
  38. package/web-dist/assets/{chunk-VAUOI2AC-ZXYCz_Aw.js → chunk-VAUOI2AC-CP-xe7SD.js} +1 -1
  39. package/web-dist/assets/{chunk-VR4S4FIN-9WCtmFJp.js → chunk-VR4S4FIN-wbxDFptH.js} +1 -1
  40. package/web-dist/assets/{chunk-WYO6CB5R-V4EBjpmt.js → chunk-WYO6CB5R-dibXmwpK.js} +1 -1
  41. package/web-dist/assets/{chunk-ZGVPDNZ5-CPW0Z0ae.js → chunk-ZGVPDNZ5-_y_itJHG.js} +1 -1
  42. package/web-dist/assets/classDiagram-OUVF2IWQ-Bf3duLb8.js +1 -0
  43. package/web-dist/assets/classDiagram-v2-EOCWNBFH-Bf3duLb8.js +1 -0
  44. package/web-dist/assets/{cynefin-VYW2F7L2-E7PklfJD.js → cynefin-VYW2F7L2-D1RYokZP.js} +1 -1
  45. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-JIXAPjDf.js → cynefinDiagram-TSTJHNR4-qofqoy3T.js} +1 -1
  46. package/web-dist/assets/{dagre-VKFMJZFB-DjXrdcNx.js → dagre-VKFMJZFB-CgHLoGFk.js} +1 -1
  47. package/web-dist/assets/{diagram-FQU43EPY--i9i0qhm.js → diagram-FQU43EPY-JEAs4LOb.js} +1 -1
  48. package/web-dist/assets/{diagram-G47NLZAW-DxgcENZ_.js → diagram-G47NLZAW--tLd4Lf7.js} +1 -1
  49. package/web-dist/assets/{diagram-NH7WQ7WH-CUy9Nr90.js → diagram-NH7WQ7WH-DPbw9CJU.js} +1 -1
  50. package/web-dist/assets/{diagram-OA4YK3LP-DbTbUaCb.js → diagram-OA4YK3LP-CDrb7nWs.js} +1 -1
  51. package/web-dist/assets/{diagram-WEI45ONY-2gk6xhOq.js → diagram-WEI45ONY-DJswZe3f.js} +1 -1
  52. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-uE2iDETg.js → ebnfDiagram-CCIWWBDH-BP8-PZMF.js} +1 -1
  53. package/web-dist/assets/{erDiagram-Q63AITRT-dztd4MUw.js → erDiagram-Q63AITRT-BQXuebf5.js} +1 -1
  54. package/web-dist/assets/eventmodeling-45OFAUF4-DOLxRQQ5.js +1 -0
  55. package/web-dist/assets/flowDiagram-23GEKE2U-DoK1yb0H.js +1 -0
  56. package/web-dist/assets/{ganttDiagram-NO4QXBWP-DAds6Aht.js → ganttDiagram-NO4QXBWP-DotMb935.js} +1 -1
  57. package/web-dist/assets/{gitGraph-TEB2WS4Q-Ci0XVvIa.js → gitGraph-TEB2WS4Q-B1vbYyKQ.js} +1 -1
  58. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-C6FULyt8.js → gitGraphDiagram-IHSO6WYX-BjDXKk4m.js} +1 -1
  59. package/web-dist/assets/index-CmrDpsD8.css +1 -0
  60. package/web-dist/assets/{index-CNy5uOpI.js → index-Cs0TDGMu.js} +124 -124
  61. package/web-dist/assets/{info-DKCQHKI2-9N77dOiw.js → info-DKCQHKI2-xLTLqQju.js} +1 -1
  62. package/web-dist/assets/{infoDiagram-FWYZ7A6U-BymP3PgG.js → infoDiagram-FWYZ7A6U-BFfYUsrC.js} +1 -1
  63. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-CU_kQBz1.js → ishikawaDiagram-FXEZZL3T-45a7VMwm.js} +1 -1
  64. package/web-dist/assets/{journeyDiagram-5HDEW3XC-DZN15HSd.js → journeyDiagram-5HDEW3XC-CEBUpu59.js} +1 -1
  65. package/web-dist/assets/{kanban-definition-HUTT4EX6-J4wR5g_3.js → kanban-definition-HUTT4EX6-RwKtDFzG.js} +1 -1
  66. package/web-dist/assets/{line-SRzGpiDr.js → line-DStcMIry.js} +1 -1
  67. package/web-dist/assets/{mermaid-parser.core-CGBscNGo.js → mermaid-parser.core-B0ekgQMA.js} +3 -3
  68. package/web-dist/assets/{mermaid.core-xNdwXb1r.js → mermaid.core-w1hcQaPy.js} +3 -3
  69. package/web-dist/assets/{mindmap-definition-LN4V7U3C-gR2dbzrj.js → mindmap-definition-LN4V7U3C-D_n38Imb.js} +1 -1
  70. package/web-dist/assets/{packet-7NZHBO7P-DrR4W6I3.js → packet-7NZHBO7P-CstGeBOX.js} +1 -1
  71. package/web-dist/assets/{pegDiagram-2B236MQR-DN7R-_t4.js → pegDiagram-2B236MQR-DvowVyTO.js} +1 -1
  72. package/web-dist/assets/{pie-RZYD4A2V-C8VjQAFZ.js → pie-RZYD4A2V-BOP2j_HH.js} +1 -1
  73. package/web-dist/assets/{pieDiagram-ENE6RG2P-5w64zdD1.js → pieDiagram-ENE6RG2P-CKRqu_2f.js} +1 -1
  74. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-B4rbezq_.js → quadrantDiagram-ABIIQ3AL-zSArSS8R.js} +1 -1
  75. package/web-dist/assets/{radar-I7S5WNFK-CTbbPB8m.js → radar-I7S5WNFK-BqRdNIBw.js} +1 -1
  76. package/web-dist/assets/{railroad-3IZDKUUU-CY1er15b.js → railroad-3IZDKUUU-BaKcl_LU.js} +1 -1
  77. package/web-dist/assets/railroad-abnf-AHOZXSZD-itqDLg1p.js +1 -0
  78. package/web-dist/assets/railroad-ebnf-EBAXGLYW-ze7rLVCi.js +1 -0
  79. package/web-dist/assets/railroad-peg-LSFZ7HO6-DKsYkEuG.js +1 -0
  80. package/web-dist/assets/{railroadDiagram-RFXS5EU6-Bx2HJ3yT.js → railroadDiagram-RFXS5EU6-BrXTQge9.js} +1 -1
  81. package/web-dist/assets/{requirementDiagram-TGXJPOKE-BLK-Y2rx.js → requirementDiagram-TGXJPOKE-DBBNi_SS.js} +1 -1
  82. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-Dk-8Miv-.js → sankeyDiagram-HTMAVEWB-BwFi7xFy.js} +1 -1
  83. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-DL2alfR-.js → sequenceDiagram-DBY2YBRQ-CeivqMeT.js} +1 -1
  84. package/web-dist/assets/{stateDiagram-2N3HPSRC-Bka44wf1.js → stateDiagram-2N3HPSRC-C6NZsnFm.js} +1 -1
  85. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-B3YRnI4W.js +1 -0
  86. package/web-dist/assets/{swimlanes-5IMT3BWC-e0GbMwIr.js → swimlanes-5IMT3BWC-C0MubXTG.js} +1 -1
  87. package/web-dist/assets/swimlanesDiagram-G3AALYLV-CXva4Ic9.js +8 -0
  88. package/web-dist/assets/{timeline-definition-FHXFAJF6-CT3GFq89.js → timeline-definition-FHXFAJF6-CtxwSvFu.js} +1 -1
  89. package/web-dist/assets/{treeView-QDETBFTQ-BJauJ0bm.js → treeView-QDETBFTQ-CXcwSjey.js} +1 -1
  90. package/web-dist/assets/{treemap-6X3UGDF4-DCX7A-nx.js → treemap-6X3UGDF4-Uspgr-d1.js} +1 -1
  91. package/web-dist/assets/{vennDiagram-L72KCM5P-D4dm2iTR.js → vennDiagram-L72KCM5P-tZamQQAQ.js} +1 -1
  92. package/web-dist/assets/{wardley-OPB4EBWU-DklTDSId.js → wardley-OPB4EBWU-0_79c19P.js} +1 -1
  93. package/web-dist/assets/{wardleyDiagram-EHGQE667-CikP5hBd.js → wardleyDiagram-EHGQE667-uYmUsP66.js} +1 -1
  94. package/web-dist/assets/{xychartDiagram-FW5EYKEG-FXuOAFpz.js → xychartDiagram-FW5EYKEG-BsOU7Wf7.js} +1 -1
  95. package/web-dist/index.html +2 -2
  96. package/runtime/cc-worker/agents/fable-high.md +0 -8
  97. package/runtime/cc-worker/agents/fable-low.md +0 -8
  98. package/runtime/cc-worker/agents/fable-max.md +0 -8
  99. package/runtime/cc-worker/agents/fable-medium.md +0 -8
  100. package/runtime/cc-worker/agents/fable-xhigh.md +0 -8
  101. package/runtime/cc-worker/agents/haiku.md +0 -7
  102. package/runtime/cc-worker/agents/opus-high.md +0 -8
  103. package/runtime/cc-worker/agents/opus-low.md +0 -8
  104. package/runtime/cc-worker/agents/opus-max.md +0 -8
  105. package/runtime/cc-worker/agents/opus-medium.md +0 -8
  106. package/runtime/cc-worker/agents/opus-xhigh.md +0 -8
  107. package/runtime/cc-worker/agents/sonnet-high.md +0 -8
  108. package/runtime/cc-worker/agents/sonnet-low.md +0 -8
  109. package/runtime/cc-worker/agents/sonnet-max.md +0 -8
  110. package/runtime/cc-worker/agents/sonnet-medium.md +0 -8
  111. package/runtime/cc-worker/agents/sonnet-xhigh.md +0 -8
  112. package/runtime/cc-worker/bin/browser-mcp-tools.json +0 -1192
  113. package/runtime/cc-worker/bin/browser-mcp.mjs +0 -730
  114. package/runtime/cc-worker/hooks/precompact-instructions.mjs +0 -133
  115. package/runtime/cc-worker/skills/waits/SKILL.md +0 -84
  116. package/web-dist/assets/architecture-TIHT7OUA-CTxVpl9_.js +0 -1
  117. package/web-dist/assets/channel-COS0Ygbp.js +0 -1
  118. package/web-dist/assets/classDiagram-OUVF2IWQ-Df5TLnrN.js +0 -1
  119. package/web-dist/assets/classDiagram-v2-EOCWNBFH-Df5TLnrN.js +0 -1
  120. package/web-dist/assets/eventmodeling-45OFAUF4-DPVPOEaJ.js +0 -1
  121. package/web-dist/assets/flowDiagram-23GEKE2U-BMIMgVYd.js +0 -1
  122. package/web-dist/assets/index-D6LsViUY.css +0 -1
  123. package/web-dist/assets/railroad-abnf-AHOZXSZD-DfEpkrtJ.js +0 -1
  124. package/web-dist/assets/railroad-ebnf-EBAXGLYW-Csunejgu.js +0 -1
  125. package/web-dist/assets/railroad-peg-LSFZ7HO6-BgRslbM_.js +0 -1
  126. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-6KcScVPX.js +0 -1
  127. package/web-dist/assets/swimlanesDiagram-G3AALYLV-jaJhlDbE.js +0 -8
package/dist/dev-child.js CHANGED
@@ -12938,6 +12938,13 @@ function retiredAwaitingKindsIn(body) {
12938
12938
  }
12939
12939
  return found;
12940
12940
  }
12941
+ function trimAwaitingTitle(raw2) {
12942
+ const flat = raw2.replace(/\s+/g, " ").trim();
12943
+ if (flat.length <= AWAITING_TITLE_MAX) return flat;
12944
+ const cut = flat.slice(0, AWAITING_TITLE_MAX);
12945
+ const space = cut.lastIndexOf(" ");
12946
+ return `${(space > AWAITING_TITLE_MAX / 2 ? cut.slice(0, space) : cut).replace(/[.,;:–—-]$/, "").trimEnd()}\u2026`;
12947
+ }
12941
12948
  function splitAwaitingFrontmatter(raw2) {
12942
12949
  const lines = raw2.split("\n").map((l) => l.replace(/\r$/, ""));
12943
12950
  const delimiter2 = lines.findIndex((l) => /^\s*---+\s*$/.test(l));
@@ -12981,6 +12988,8 @@ function parseAwaitingYaml(text) {
12981
12988
  for (const entry of Array.isArray(raw2) ? raw2 : [raw2]) push(itemKind, entry);
12982
12989
  } else if (key === "for") {
12983
12990
  push("for", raw2);
12991
+ } else if (key === "title") {
12992
+ if (typeof raw2 === "string" || typeof raw2 === "number") push("title", trimAwaitingTitle(String(raw2)));
12984
12993
  }
12985
12994
  }
12986
12995
  return { ok: true, hints };
@@ -13263,7 +13272,7 @@ function parseGithubWakeSteer(text) {
13263
13272
  function frizzRoute(path) {
13264
13273
  return `${FRIZZ_ROUTE_PREFIX}${path.startsWith("/") ? path : `/${path}`}`;
13265
13274
  }
13266
- var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, 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_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, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, 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_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
13275
+ var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, 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_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, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, 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_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
13267
13276
  var init_src = __esm({
13268
13277
  "packages/shared/src/index.ts"() {
13269
13278
  "use strict";
@@ -13519,7 +13528,7 @@ var init_src = __esm({
13519
13528
  questions: external_exports.array(AskQuestion)
13520
13529
  });
13521
13530
  AwaitingHint = external_exports.object({
13522
- kind: external_exports.enum(["shell", "agent", "timer", "pr", "for"]),
13531
+ kind: external_exports.enum(["shell", "agent", "timer", "pr", "for", "title"]),
13523
13532
  value: external_exports.string()
13524
13533
  });
13525
13534
  RETIRED_AWAITING_KINDS = ["watch", "pr-watch", "human", "ci", "session", "shell", "agent", "timer", "pr", "reason"];
@@ -13541,7 +13550,7 @@ var init_src = __esm({
13541
13550
  "reason": "put it below the `---` as ordinary Markdown \u2014 the frontmatter is YAML now and takes no prose"
13542
13551
  };
13543
13552
  AWAITING_KEY_RE = /^([a-z][a-z-]*):\s*(\S.*)?$/i;
13544
- AWAITING_YAML_KEYS = /* @__PURE__ */ new Set(["shells", "agents", "timers", "prs", "for"]);
13553
+ AWAITING_YAML_KEYS = /* @__PURE__ */ new Set(["shells", "agents", "timers", "prs", "for", "title"]);
13545
13554
  AWAITING_SEQUENCE_KEYS = {
13546
13555
  shells: "shell",
13547
13556
  agents: "agent",
@@ -13550,6 +13559,7 @@ var init_src = __esm({
13550
13559
  };
13551
13560
  AWAITING_HINT_MAX = 8;
13552
13561
  AWAITING_HINT_VALUE_MAX = 200;
13562
+ AWAITING_TITLE_MAX = 40;
13553
13563
  AWAITING_ITEM_KINDS = ["shell", "agent", "timer", "pr"];
13554
13564
  AWAITING_DURATION_RE = /^(\d{1,5})(s|m|h|d)$/;
13555
13565
  DURATION_UNIT_MS = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
@@ -14133,6 +14143,14 @@ var init_src = __esm({
14133
14143
  // Default action for a vetted non-image local path in agent markdown. Image clicks always use the
14134
14144
  // OS default viewer so screenshots retain their expected behavior.
14135
14145
  localFileOpener: LocalFileOpener.optional(),
14146
+ // The token count at which a NEW Claude worker auto-compacts its conversation. Frizz requests the
14147
+ // 1M context window on every dispatch (resolveClaudeLaunchModel), so without this a worker grows
14148
+ // toward 1M before it ever compacts — and every turn past 200K re-sends up to 5x the conversation a
14149
+ // 200K TUI session would, which is the single largest reason a Frizz thread spends quota faster than
14150
+ // the TUI (measured 2026-08-26). Reaches the worker as CLAUDE_CODE_AUTO_COMPACT_WINDOW, which Claude
14151
+ // Code caps to the model's real window. Optional so an old blob parses; defaultSettings pins 500_000
14152
+ // (maintainer 2026-08-26: "a default compaction window of 500k by default").
14153
+ autoCompactWindow: external_exports.number().int().positive().optional(),
14136
14154
  // The GitHub batch-dispatch prompt template (the picker's per-item worker prompt). Optional: when
14137
14155
  // unset OR blank the server falls back to its exported DEFAULT_GITHUB_PROMPT. Substitution tokens
14138
14156
  // the server fills: {repo} {n} {title} {url} {labels} {body}. The leading `THREAD: <slug>` tag is
@@ -19518,42 +19536,29 @@ var init_storage = __esm({
19518
19536
  }
19519
19537
  });
19520
19538
 
19521
- // packages/server/src/settings.ts
19539
+ // packages/server/src/machine-config.ts
19522
19540
  import { closeSync as closeSync5, fsyncSync as fsyncSync5, mkdirSync as mkdirSync7, openSync as openSync6, readFileSync as readFileSync6, renameSync as renameSync5, rmSync as rmSync6, writeFileSync as writeFileSync6 } from "node:fs";
19523
19541
  import { dirname as dirname6, join as join9 } from "node:path";
19524
- function machineSettingsPath(home) {
19525
- return join9(frizzPaths({ home }).data, "settings.json");
19526
- }
19527
- function pickMachine(settings) {
19528
- return Object.fromEntries(MACHINE_KEYS.map((key) => [key, settings[key]]));
19542
+ function machineConfigPath(home) {
19543
+ return join9(frizzPaths({ home }).data, "config.json");
19529
19544
  }
19530
- function readMachineSettings(home) {
19545
+ function readAll(home) {
19531
19546
  let raw2;
19532
19547
  try {
19533
- raw2 = JSON.parse(readFileSync6(machineSettingsPath(home), "utf8"));
19548
+ raw2 = JSON.parse(readFileSync6(machineConfigPath(home), "utf8"));
19534
19549
  } catch {
19535
19550
  return {};
19536
19551
  }
19537
- if (!raw2 || typeof raw2 !== "object") return {};
19538
- const parsed = Settings.partial().safeParse(raw2);
19539
- if (!parsed.success) return {};
19540
- return pickPresent(parsed.data);
19541
- }
19542
- function pickPresent(partial) {
19543
- const out = {};
19544
- for (const key of MACHINE_KEYS) if (partial[key] !== void 0) Object.assign(out, { [key]: partial[key] });
19545
- return out;
19552
+ return raw2 && typeof raw2 === "object" && !Array.isArray(raw2) ? raw2 : {};
19546
19553
  }
19547
- function writeMachineSettings(next, home) {
19548
- writeMachineFile(machineSettingsPath(home), next);
19549
- }
19550
- function writeMachineFile(path, value) {
19554
+ function writeAll(home, all) {
19555
+ const path = machineConfigPath(home);
19551
19556
  mkdirSync7(dirname6(path), { recursive: true });
19552
19557
  const temp = `${path}.${process.pid}.tmp`;
19553
19558
  let fd;
19554
19559
  try {
19555
19560
  fd = openSync6(temp, "w", 384);
19556
- writeFileSync6(fd, `${JSON.stringify(value, null, 2)}
19561
+ writeFileSync6(fd, `${JSON.stringify(all, null, 2)}
19557
19562
  `, "utf8");
19558
19563
  fsyncSync5(fd);
19559
19564
  closeSync5(fd);
@@ -19570,6 +19575,58 @@ function writeMachineFile(path, value) {
19570
19575
  }
19571
19576
  }
19572
19577
  }
19578
+ function readMachineConfig(home, key, schema) {
19579
+ const value = readAll(home)[key];
19580
+ if (value === void 0) return void 0;
19581
+ const parsed = schema.safeParse(value);
19582
+ return parsed.success ? parsed.data : void 0;
19583
+ }
19584
+ function writeMachineConfig(home, key, value) {
19585
+ writeAll(home, { ...readAll(home), [key]: value });
19586
+ }
19587
+ function deleteMachineConfig(home, key) {
19588
+ const all = readAll(home);
19589
+ if (!(key in all)) return;
19590
+ delete all[key];
19591
+ writeAll(home, all);
19592
+ }
19593
+ var init_machine_config = __esm({
19594
+ "packages/server/src/machine-config.ts"() {
19595
+ "use strict";
19596
+ init_frizz_paths();
19597
+ }
19598
+ });
19599
+
19600
+ // packages/server/src/settings.ts
19601
+ import { readFileSync as readFileSync7, rmSync as rmSync7 } from "node:fs";
19602
+ import { join as join10 } from "node:path";
19603
+ function legacyMachineSettingsPath(home) {
19604
+ return join10(frizzPaths({ home }).data, "settings.json");
19605
+ }
19606
+ function pickMachine(settings) {
19607
+ return Object.fromEntries(MACHINE_KEYS.map((key) => [key, settings[key]]));
19608
+ }
19609
+ function readMachineSettings(home) {
19610
+ const stored = readMachineConfig(home, SETTINGS_KEY, MachineSettingsRecord);
19611
+ if (stored) return pickPresent(stored);
19612
+ let raw2;
19613
+ try {
19614
+ raw2 = JSON.parse(readFileSync7(legacyMachineSettingsPath(home), "utf8"));
19615
+ } catch {
19616
+ return {};
19617
+ }
19618
+ if (!raw2 || typeof raw2 !== "object") return {};
19619
+ const parsed = MachineSettingsRecord.safeParse(raw2);
19620
+ return parsed.success ? pickPresent(parsed.data) : {};
19621
+ }
19622
+ function pickPresent(partial) {
19623
+ const out = {};
19624
+ for (const key of MACHINE_KEYS) if (partial[key] !== void 0) Object.assign(out, { [key]: partial[key] });
19625
+ return out;
19626
+ }
19627
+ function writeMachineSettings(next, home) {
19628
+ writeMachineConfig(home, SETTINGS_KEY, next);
19629
+ }
19573
19630
  function getSettings(storage, home) {
19574
19631
  const raw2 = storage.getSetting(SETTINGS_KEY);
19575
19632
  const project = raw2 === void 0 ? defaultSettings() : Settings.safeParse({ ...defaultSettings(), ...raw2 }).data ?? defaultSettings();
@@ -19583,20 +19640,23 @@ function setSettings(storage, next, home) {
19583
19640
  }
19584
19641
  function resetSettings(storage, home) {
19585
19642
  storage.deleteSetting(SETTINGS_KEY);
19643
+ deleteMachineConfig(home, SETTINGS_KEY);
19586
19644
  try {
19587
- rmSync6(machineSettingsPath(home), { force: true });
19645
+ rmSync7(legacyMachineSettingsPath(home), { force: true });
19588
19646
  } catch {
19589
19647
  }
19590
19648
  return defaultSettings();
19591
19649
  }
19592
- var SETTINGS_KEY, MACHINE_KEYS, defaultSettings;
19650
+ var SETTINGS_KEY, MACHINE_KEYS, MachineSettingsRecord, defaultSettings;
19593
19651
  var init_settings = __esm({
19594
19652
  "packages/server/src/settings.ts"() {
19595
19653
  "use strict";
19596
19654
  init_src();
19597
19655
  init_frizz_paths();
19656
+ init_machine_config();
19598
19657
  SETTINGS_KEY = "settings";
19599
19658
  MACHINE_KEYS = ["font", "notifications", "localFileOpener", "projectRail"];
19659
+ MachineSettingsRecord = Settings.partial();
19600
19660
  defaultSettings = () => ({
19601
19661
  // `bypassPermissions` = claude's --dangerously-skip-permissions: a headless worker never stops on an
19602
19662
  // approval card. Shipped default since 0.7.2 (maintainer 2026-08-24); the Settings "Permissions"
@@ -19610,6 +19670,9 @@ var init_settings = __esm({
19610
19670
  notifications: true,
19611
19671
  font: "sans",
19612
19672
  localFileOpener: "system",
19673
+ // 500K tokens: half the 1M window every Claude worker is dispatched with. See the schema for why a
19674
+ // worker left to compact at 1M is the biggest single reason a Frizz thread out-spends the TUI.
19675
+ autoCompactWindow: 5e5,
19613
19676
  // Hidden until asked for — see the schema for why.
19614
19677
  projectRail: false
19615
19678
  });
@@ -19617,20 +19680,8 @@ var init_settings = __esm({
19617
19680
  });
19618
19681
 
19619
19682
  // packages/server/src/dispatch-preferences.ts
19620
- import { readFileSync as readFileSync7 } from "node:fs";
19621
- import { join as join10 } from "node:path";
19622
- function machineDispatchPreferencesPath(home) {
19623
- return join10(frizzPaths({ home }).data, "dispatch-preferences.json");
19624
- }
19625
19683
  function readMachineDispatchPreferences(home) {
19626
- let raw2;
19627
- try {
19628
- raw2 = JSON.parse(readFileSync7(machineDispatchPreferencesPath(home), "utf8"));
19629
- } catch {
19630
- return void 0;
19631
- }
19632
- const parsed = DispatchPreferences.safeParse(raw2);
19633
- return parsed.success ? parsed.data : void 0;
19684
+ return readMachineConfig(home, MACHINE_KEY, DispatchPreferences);
19634
19685
  }
19635
19686
  function permissionFor(backend, mode) {
19636
19687
  if (backend === "codex") {
@@ -19682,18 +19733,18 @@ function setDispatchPreference(storage, settings, home, update, codexModels = []
19682
19733
  };
19683
19734
  }
19684
19735
  const validated = DispatchPreferences.parse(next);
19685
- writeMachineFile(machineDispatchPreferencesPath(home), validated);
19736
+ writeMachineConfig(home, MACHINE_KEY, validated);
19686
19737
  storage.setSetting(KEY, validated);
19687
19738
  return validated;
19688
19739
  }
19689
- var KEY;
19740
+ var KEY, MACHINE_KEY;
19690
19741
  var init_dispatch_preferences = __esm({
19691
19742
  "packages/server/src/dispatch-preferences.ts"() {
19692
19743
  "use strict";
19693
19744
  init_src();
19694
- init_frizz_paths();
19695
- init_settings();
19745
+ init_machine_config();
19696
19746
  KEY = "dispatch-preferences.v1";
19747
+ MACHINE_KEY = "dispatchPreferences";
19697
19748
  }
19698
19749
  });
19699
19750
 
@@ -19795,7 +19846,7 @@ var init_discover = __esm({
19795
19846
  });
19796
19847
 
19797
19848
  // packages/server/src/session-files.ts
19798
- import { lstatSync, rmSync as rmSync7 } from "node:fs";
19849
+ import { lstatSync, rmSync as rmSync8 } from "node:fs";
19799
19850
  import { join as join12 } from "node:path";
19800
19851
  function systemPromptPath(sessionId) {
19801
19852
  if (!SESSION_ID_RE.test(sessionId)) throw new Error("invalid session id");
@@ -19821,7 +19872,7 @@ function unlinkDirectChild(parent, filename) {
19821
19872
  if (!isDirectDirectory(parent)) return pathAbsent(parent);
19822
19873
  const child = join12(parent, filename);
19823
19874
  try {
19824
- rmSync7(child, { force: true });
19875
+ rmSync8(child, { force: true });
19825
19876
  } catch {
19826
19877
  return false;
19827
19878
  }
@@ -19836,7 +19887,7 @@ function cleanupAdoptionSessionFiles(projectDir, sessionId) {
19836
19887
  if (isDirectDirectory(threads)) {
19837
19888
  const child = join12(threads, sessionId);
19838
19889
  try {
19839
- rmSync7(child, { recursive: true, force: true });
19890
+ rmSync8(child, { recursive: true, force: true });
19840
19891
  } catch {
19841
19892
  clean = false;
19842
19893
  }
@@ -28556,32 +28607,7 @@ var init_wrapper = __esm({
28556
28607
  }
28557
28608
  });
28558
28609
 
28559
- // packages/server/src/worker-plugin-dir.ts
28560
- import { existsSync as existsSync6 } from "node:fs";
28561
- import { dirname as dirname9, join as join20 } from "node:path";
28562
- import { fileURLToPath as fileURLToPath2 } from "node:url";
28563
- function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
28564
- const override = env.FRIZZ_WORKER_PLUGIN_DIR;
28565
- if (override && existsSync6(join20(override, ".claude-plugin", "plugin.json")))
28566
- return override;
28567
- let current = dirname9(fileURLToPath2(moduleUrl));
28568
- for (; ; ) {
28569
- const candidate = join20(current, "cc-worker");
28570
- if (existsSync6(join20(candidate, ".claude-plugin", "plugin.json"))) return candidate;
28571
- const parent = dirname9(current);
28572
- if (parent === current) return void 0;
28573
- current = parent;
28574
- }
28575
- }
28576
- var init_worker_plugin_dir = __esm({
28577
- "packages/server/src/worker-plugin-dir.ts"() {
28578
- "use strict";
28579
- }
28580
- });
28581
-
28582
28610
  // packages/server/src/backend/types.ts
28583
- import { existsSync as existsSync7 } from "node:fs";
28584
- import { join as join21 } from "node:path";
28585
28611
  function frizzMcpEnv(mcp) {
28586
28612
  return {
28587
28613
  FRIZZ_STATE_DIR: mcp.stateDir,
@@ -28590,24 +28616,6 @@ function frizzMcpEnv(mcp) {
28590
28616
  ...mcp.slug ? { FRIZZ_THREAD_SLUG: mcp.slug } : {}
28591
28617
  };
28592
28618
  }
28593
- function chromeDevtoolsMcpSpec(scriptPath, nodeBin) {
28594
- const args = [...CHROME_DEVTOOLS_MCP.args];
28595
- if (!scriptPath) return { command: "npx", args: ["-y", `${CHROME_DEVTOOLS_MCP.package}@${CHROME_DEVTOOLS_MCP.version}`, ...args] };
28596
- return {
28597
- command: nodeBin,
28598
- args: [scriptPath, ...args],
28599
- env: { FRIZZ_BROWSER_MCP_PACKAGE: `${CHROME_DEVTOOLS_MCP.package}@${CHROME_DEVTOOLS_MCP.version}` }
28600
- };
28601
- }
28602
- function chromeDevtoolsMcpMount() {
28603
- return chromeDevtoolsMcpSpec(resolveBrowserMcpScript(), process.execPath);
28604
- }
28605
- function resolveBrowserMcpScript(moduleUrl = import.meta.url, env = process.env) {
28606
- const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
28607
- if (!pluginDir) return void 0;
28608
- const scriptPath = join21(pluginDir, "bin", CHROME_DEVTOOLS_MCP.script);
28609
- return existsSync7(scriptPath) ? scriptPath : void 0;
28610
- }
28611
28619
  function workerCap(name, lifted, env) {
28612
28620
  const override = env[name];
28613
28621
  if (override !== void 0 && /^[1-9][0-9]*$/.test(override)) return override;
@@ -28621,29 +28629,20 @@ function claudeWorkerEnv(env = process.env) {
28621
28629
  CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS: workerCap("CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS", WORKER_MAX_CONCURRENT_SUBAGENTS, env)
28622
28630
  };
28623
28631
  }
28624
- var FRIZZ_MCP, CHROME_DEVTOOLS_MCP, WORKER_MAX_WEB_SEARCHES, WORKER_MAX_SUBAGENTS, WORKER_MAX_CONCURRENT_SUBAGENTS, CLAUDE_WORKER_ENV, WORKER_DISALLOWED_TOOLS;
28632
+ function claudeCompactionEnv(settings) {
28633
+ const window = settings?.autoCompactWindow;
28634
+ if (window === void 0 || !Number.isInteger(window) || window <= 0) return {};
28635
+ return { CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(window) };
28636
+ }
28637
+ var FRIZZ_MCP, WORKER_MAX_WEB_SEARCHES, WORKER_MAX_SUBAGENTS, WORKER_MAX_CONCURRENT_SUBAGENTS, CLAUDE_WORKER_ENV, WORKER_DISALLOWED_TOOLS;
28625
28638
  var init_types2 = __esm({
28626
28639
  "packages/server/src/backend/types.ts"() {
28627
28640
  "use strict";
28628
- init_worker_plugin_dir();
28629
28641
  FRIZZ_MCP = {
28630
28642
  name: "frizz",
28631
28643
  script: "frizz-mcp.mjs"
28632
28644
  // resolved under <worker plugin dir>/bin/
28633
28645
  };
28634
- CHROME_DEVTOOLS_MCP = {
28635
- name: "chrome-devtools",
28636
- // Resolved under <worker plugin dir>/bin/, exactly like FRIZZ_MCP.script.
28637
- script: "browser-mcp.mjs",
28638
- package: "chrome-devtools-mcp",
28639
- version: "1.7.0",
28640
- args: [
28641
- "--experimentalPageIdRouting",
28642
- "--headless",
28643
- "--isolated",
28644
- "--no-usage-statistics"
28645
- ]
28646
- };
28647
28646
  WORKER_MAX_WEB_SEARCHES = 1e4;
28648
28647
  WORKER_MAX_SUBAGENTS = 1e4;
28649
28648
  WORKER_MAX_CONCURRENT_SUBAGENTS = 100;
@@ -28673,12 +28672,8 @@ function serverTable(command, args, env) {
28673
28672
  }
28674
28673
  return inlineTable(entries);
28675
28674
  }
28676
- function codexMcpConfigArgs(frizzMcp, nodeBin = process.execPath, browserMcpScript) {
28677
- const chrome = chromeDevtoolsMcpSpec(browserMcpScript ?? resolveBrowserMcpScript(), nodeBin);
28678
- const args = [
28679
- "-c",
28680
- `mcp_servers.${CHROME_DEVTOOLS_MCP.name}=${serverTable(chrome.command, chrome.args, chrome.env)}`
28681
- ];
28675
+ function codexMcpConfigArgs(frizzMcp, nodeBin = process.execPath) {
28676
+ const args = [];
28682
28677
  if (frizzMcp) {
28683
28678
  args.push(
28684
28679
  "-c",
@@ -28703,15 +28698,15 @@ import { spawn as spawn2 } from "node:child_process";
28703
28698
  import { connect } from "node:net";
28704
28699
  import { Agent as HttpAgent } from "node:http";
28705
28700
  import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
28706
- import { existsSync as existsSync8, mkdirSync as mkdirSync11, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
28707
- import { join as join22 } from "node:path";
28701
+ import { existsSync as existsSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
28702
+ import { join as join20 } from "node:path";
28708
28703
  import { PassThrough, Writable } from "node:stream";
28709
28704
  import { StringDecoder } from "node:string_decoder";
28710
28705
  function nativeDir(stateDir) {
28711
- return join22(stateDir, "codex-app-server-native");
28706
+ return join20(stateDir, "codex-app-server-native");
28712
28707
  }
28713
28708
  function nativeRecordPath(stateDir, projectId) {
28714
- return join22(nativeDir(stateDir), `${projectId}.json`);
28709
+ return join20(nativeDir(stateDir), `${projectId}.json`);
28715
28710
  }
28716
28711
  function nativeListenSocketPath(stateDir, projectId) {
28717
28712
  const key = createHash7("sha256").update(stateDir).update("\0").update(projectId).digest("hex").slice(0, 16);
@@ -28743,7 +28738,7 @@ function readNativeRecord(stateDir, projectId) {
28743
28738
  function liveNativeRecord(stateDir, projectId) {
28744
28739
  const record = readNativeRecord(stateDir, projectId);
28745
28740
  if (!record) return null;
28746
- if (pidAlive2(record.listenerPid) && existsSync8(record.socketPath)) return record;
28741
+ if (pidAlive2(record.listenerPid) && existsSync6(record.socketPath)) return record;
28747
28742
  try {
28748
28743
  unlinkSync2(nativeRecordPath(stateDir, projectId));
28749
28744
  } catch {
@@ -28885,7 +28880,7 @@ async function startListener(options) {
28885
28880
  const { stateDir, projectId } = options;
28886
28881
  mkdirSync11(nativeDir(stateDir), { recursive: true });
28887
28882
  const socketPath = nativeListenSocketPath(stateDir, projectId);
28888
- if (existsSync8(socketPath) && !await socketAccepting(socketPath)) {
28883
+ if (existsSync6(socketPath) && !await socketAccepting(socketPath)) {
28889
28884
  try {
28890
28885
  unlinkSync2(socketPath);
28891
28886
  } catch {
@@ -28909,7 +28904,7 @@ async function startListener(options) {
28909
28904
  writeFileSync9(nativeRecordPath(stateDir, projectId), JSON.stringify(record));
28910
28905
  const deadline = Date.now() + (options.timeoutMs ?? 3e4);
28911
28906
  for (; ; ) {
28912
- if (existsSync8(socketPath) && await socketAccepting(socketPath)) return record;
28907
+ if (existsSync6(socketPath) && await socketAccepting(socketPath)) return record;
28913
28908
  if (!pidAlive2(record.listenerPid)) throw new Error("codex app-server listener exited before it became ready");
28914
28909
  if (Date.now() > deadline) throw new Error("codex app-server listener did not become ready");
28915
28910
  await new Promise((resolve10) => setTimeout(resolve10, 50));
@@ -28975,15 +28970,15 @@ var init_codex_app_server_native = __esm({
28975
28970
  import { spawn as spawn3, spawnSync } from "node:child_process";
28976
28971
  import { createConnection } from "node:net";
28977
28972
  import { createHash as createHash8, randomUUID as randomUUID10 } from "node:crypto";
28978
- import { existsSync as existsSync9, mkdirSync as mkdirSync12, readFileSync as readFileSync15, unlinkSync as unlinkSync3 } from "node:fs";
28979
- import { join as join23 } from "node:path";
28973
+ import { existsSync as existsSync7, mkdirSync as mkdirSync12, readFileSync as readFileSync15, unlinkSync as unlinkSync3 } from "node:fs";
28974
+ import { join as join21 } from "node:path";
28980
28975
  import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream";
28981
28976
  import { StringDecoder as StringDecoder2 } from "node:string_decoder";
28982
28977
  function daemonDir(stateDir) {
28983
- return join23(stateDir, "codex-app-server");
28978
+ return join21(stateDir, "codex-app-server");
28984
28979
  }
28985
28980
  function recordPath(stateDir, projectId) {
28986
- return join23(daemonDir(stateDir), `${projectId}.json`);
28981
+ return join21(daemonDir(stateDir), `${projectId}.json`);
28987
28982
  }
28988
28983
  function codexAppServerSocketPath(stateDir, projectId) {
28989
28984
  const key = createHash8("sha256").update(stateDir).update("\0").update(projectId).digest("hex").slice(0, 16);
@@ -29265,7 +29260,7 @@ var init_codex_app_server_host = __esm({
29265
29260
  }
29266
29261
  if (process.platform !== "win32") {
29267
29262
  const stale = codexAppServerSocketPath(options.stateDir, options.projectId);
29268
- if (existsSync9(stale) && !liveDaemonRecord(options.stateDir, options.projectId)) {
29263
+ if (existsSync7(stale) && !liveDaemonRecord(options.stateDir, options.projectId)) {
29269
29264
  try {
29270
29265
  unlinkSync3(stale);
29271
29266
  } catch {
@@ -32211,11 +32206,11 @@ var init_codex_app_server = __esm({
32211
32206
  });
32212
32207
 
32213
32208
  // packages/server/src/backend/codex-quota.ts
32214
- import { join as join24 } from "node:path";
32209
+ import { join as join22 } from "node:path";
32215
32210
  import { readdirSync as readdirSync7, statSync as statSync10, openSync as openSync12, readSync as readSync6, fstatSync, closeSync as closeSync11 } from "node:fs";
32216
32211
  import { spawn as spawn4 } from "node:child_process";
32217
32212
  function sessionsDir2(codexHome) {
32218
- return join24(codexHome, "sessions");
32213
+ return join22(codexHome, "sessions");
32219
32214
  }
32220
32215
  function newestRollouts(dir, out, budget) {
32221
32216
  if (budget.n <= 0) return;
@@ -32229,11 +32224,11 @@ function newestRollouts(dir, out, budget) {
32229
32224
  const files = entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => e.name).sort(descByName2);
32230
32225
  for (const d of dirs) {
32231
32226
  if (budget.n <= 0) return;
32232
- newestRollouts(join24(dir, d), out, budget);
32227
+ newestRollouts(join22(dir, d), out, budget);
32233
32228
  }
32234
32229
  for (const f of files) {
32235
32230
  if (budget.n <= 0) return;
32236
- out.push(join24(dir, f));
32231
+ out.push(join22(dir, f));
32237
32232
  budget.n--;
32238
32233
  }
32239
32234
  }
@@ -32433,7 +32428,7 @@ var init_codex_quota = __esm({
32433
32428
  });
32434
32429
 
32435
32430
  // packages/server/src/backend/claude-quota.ts
32436
- import { join as join25 } from "node:path";
32431
+ import { join as join23 } from "node:path";
32437
32432
  import { homedir as homedir9, platform } from "node:os";
32438
32433
  import { createHash as createHash10 } from "node:crypto";
32439
32434
  import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -32441,7 +32436,7 @@ import { execFile as execFile2 } from "node:child_process";
32441
32436
  import { promisify as promisify2 } from "node:util";
32442
32437
  function claudeConfigDir() {
32443
32438
  const override = process.env.CLAUDE_CONFIG_DIR;
32444
- return override && override.trim() ? override : join25(homedir9(), ".claude");
32439
+ return override && override.trim() ? override : join23(homedir9(), ".claude");
32445
32440
  }
32446
32441
  function tokenFromCredentialsJson(raw2) {
32447
32442
  let doc;
@@ -32470,7 +32465,7 @@ async function readKeychainToken() {
32470
32465
  }
32471
32466
  async function readAccessToken(configDir) {
32472
32467
  try {
32473
- const fromFile = tokenFromCredentialsJson(await readFile(join25(configDir, ".credentials.json"), "utf8"));
32468
+ const fromFile = tokenFromCredentialsJson(await readFile(join23(configDir, ".credentials.json"), "utf8"));
32474
32469
  if (fromFile) return fromFile;
32475
32470
  } catch {
32476
32471
  }
@@ -32539,8 +32534,8 @@ function parseClaudeUsage(body, planType) {
32539
32534
  function cachePaths(cacheDir, configDir) {
32540
32535
  const profile = createHash10("sha256").update(configDir).digest("hex").slice(0, 12);
32541
32536
  return {
32542
- data: join25(cacheDir, `claude-${profile}.json`),
32543
- lock: join25(cacheDir, `claude-${profile}.lock`)
32537
+ data: join23(cacheDir, `claude-${profile}.json`),
32538
+ lock: join23(cacheDir, `claude-${profile}.lock`)
32544
32539
  };
32545
32540
  }
32546
32541
  async function readShared(path) {
@@ -32736,7 +32731,7 @@ function refreshSharedInBackground(paths, claudeBin, deps, now) {
32736
32731
  async function refreshClaudeQuotaInBackground(claudeBin = "claude", deps = {}) {
32737
32732
  const now = (deps.now ?? Date.now)();
32738
32733
  const configDir = claudeConfigDir();
32739
- const cacheDir = deps.cacheDir ?? join25(frizzRoots().cache, "quota-cache");
32734
+ const cacheDir = deps.cacheDir ?? join23(frizzRoots().cache, "quota-cache");
32740
32735
  try {
32741
32736
  await mkdir(cacheDir, { recursive: true, mode: 448 });
32742
32737
  } catch {
@@ -32747,7 +32742,7 @@ async function refreshClaudeQuotaInBackground(claudeBin = "claude", deps = {}) {
32747
32742
  async function readClaudeQuota(claudeBin = "claude", deps = {}, options = {}) {
32748
32743
  const now = (deps.now ?? Date.now)();
32749
32744
  const configDir = claudeConfigDir();
32750
- const cacheDir = deps.cacheDir ?? join25(frizzRoots().cache, "quota-cache");
32745
+ const cacheDir = deps.cacheDir ?? join23(frizzRoots().cache, "quota-cache");
32751
32746
  const paths = cachePaths(cacheDir, configDir);
32752
32747
  try {
32753
32748
  await mkdir(cacheDir, { recursive: true, mode: 448 });
@@ -32907,7 +32902,7 @@ var init_quota = __esm({
32907
32902
  // packages/server/src/frizz.ts
32908
32903
  import { execFile as execFile3 } from "node:child_process";
32909
32904
  import { lstatSync as lstatSync2, realpathSync as realpathSync6 } from "node:fs";
32910
- import { basename as basename5, dirname as dirname10, join as join26, resolve as resolve4 } from "node:path";
32905
+ import { basename as basename5, dirname as dirname9, join as join24, resolve as resolve4 } from "node:path";
32911
32906
  import { promisify as promisify3 } from "node:util";
32912
32907
  function frizzScriptsDir() {
32913
32908
  if (process.env.FRIZZ_SCRIPTS_DIR) return process.env.FRIZZ_SCRIPTS_DIR;
@@ -32916,11 +32911,11 @@ function frizzScriptsDir() {
32916
32911
  function directFrizzRoot(projectDir) {
32917
32912
  try {
32918
32913
  const projectRoot = realpathSync6(projectDir);
32919
- const path = join26(projectRoot, ".frizz");
32914
+ const path = join24(projectRoot, ".frizz");
32920
32915
  const stat2 = lstatSync2(path);
32921
32916
  if (!stat2.isDirectory() || stat2.isSymbolicLink()) return null;
32922
32917
  const real = realpathSync6(path);
32923
- return dirname10(real) === projectRoot && basename5(real) === ".frizz" ? real : null;
32918
+ return dirname9(real) === projectRoot && basename5(real) === ".frizz" ? real : null;
32924
32919
  } catch {
32925
32920
  return null;
32926
32921
  }
@@ -32930,7 +32925,7 @@ function frizzDirExists(projectDir) {
32930
32925
  }
32931
32926
  async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
32932
32927
  if (!directFrizzRoot(projectDir)) throw new Error("unsafe or missing .frizz directory");
32933
- const { stdout } = await execFileP("node", [join26(scriptsDir, "index.mjs"), "--json"], {
32928
+ const { stdout } = await execFileP("node", [join24(scriptsDir, "index.mjs"), "--json"], {
32934
32929
  cwd: projectDir,
32935
32930
  env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir },
32936
32931
  maxBuffer: 32 * 1024 * 1024
@@ -32955,7 +32950,7 @@ async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
32955
32950
  };
32956
32951
  }
32957
32952
  async function runThreadUpdate(projectDir, slug, args, scriptsDir = frizzScriptsDir()) {
32958
- await execFileP("node", [join26(scriptsDir, "thread-update.mjs"), slug, ...args], {
32953
+ await execFileP("node", [join24(scriptsDir, "thread-update.mjs"), slug, ...args], {
32959
32954
  cwd: projectDir,
32960
32955
  env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir }
32961
32956
  });
@@ -32991,7 +32986,12 @@ function readAwaitingPark(hints) {
32991
32986
  return { items, forMs };
32992
32987
  }
32993
32988
  function unaccountedItems(items, live) {
32994
- return items.filter((i) => !live[LIVE_SET[i.kind]].has(i.value));
32989
+ return items.filter((i) => !live[LIVE_SET[i.kind]].has(liveKey(i)));
32990
+ }
32991
+ function liveKey(i) {
32992
+ if (i.kind !== "pr") return i.value;
32993
+ const ref = parsePrRef(i.value);
32994
+ return ref ? githubStatusKey(ref) : i.value;
32995
32995
  }
32996
32996
  function parkIsHonoured(park, live) {
32997
32997
  if (park.items.length === 0) return false;
@@ -33030,6 +33030,29 @@ var init_awaiting = __esm({
33030
33030
  }
33031
33031
  });
33032
33032
 
33033
+ // packages/server/src/worker-plugin-dir.ts
33034
+ import { existsSync as existsSync8 } from "node:fs";
33035
+ import { dirname as dirname10, join as join25 } from "node:path";
33036
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
33037
+ function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
33038
+ const override = env.FRIZZ_WORKER_PLUGIN_DIR;
33039
+ if (override && existsSync8(join25(override, ".claude-plugin", "plugin.json")))
33040
+ return override;
33041
+ let current = dirname10(fileURLToPath2(moduleUrl));
33042
+ for (; ; ) {
33043
+ const candidate = join25(current, "cc-worker");
33044
+ if (existsSync8(join25(candidate, ".claude-plugin", "plugin.json"))) return candidate;
33045
+ const parent = dirname10(current);
33046
+ if (parent === current) return void 0;
33047
+ current = parent;
33048
+ }
33049
+ }
33050
+ var init_worker_plugin_dir = __esm({
33051
+ "packages/server/src/worker-plugin-dir.ts"() {
33052
+ "use strict";
33053
+ }
33054
+ });
33055
+
33033
33056
  // packages/server/src/workerPrompt.ts
33034
33057
  function buildWorkerPrompt(kind = "claude", opts = {}) {
33035
33058
  const lean = kind === "claude";
@@ -33234,6 +33257,7 @@ a second read for nothing, and it is the commonest thing wrong with an otherwise
33234
33257
  timers: [tmr_a1b2c3d4e5f6]
33235
33258
  prs: [acme/app#391]
33236
33259
  for: 2h
33260
+ title: Three-platform CI run
33237
33261
  ---
33238
33262
  Waiting on the three-platform run before porting the v2 drivers.
33239
33263
 
@@ -33242,9 +33266,9 @@ a second read for nothing, and it is the commonest thing wrong with an otherwise
33242
33266
  \`\`\`
33243
33267
 
33244
33268
  **THE FRONTMATTER IS YAML. THE BODY IS MARKDOWN.** Everything above the \`---\` is a real YAML mapping \u2014
33245
- four PLURAL keys taking LISTS, plus \`for:\`. Everything after the \`---\` is ordinary Markdown, as much as
33246
- you want, with paragraphs, lists and links. That prose is what the human reads on your resting card, so
33247
- write it as a handoff, not as a label.
33269
+ four PLURAL keys taking LISTS, plus the scalars \`for:\` and \`title:\`. Everything after the \`---\` is
33270
+ ordinary Markdown, as much as you want, with paragraphs, lists and links. That prose is what the human
33271
+ reads on your resting card, so write it as a handoff, not as a label.
33248
33272
 
33249
33273
  **NO PROSE ABOVE THE \`---\`, EVER.** A sentence in the frontmatter is what makes it stop being YAML: a
33250
33274
  colon inside it, or a \` #\` before an issue ref, and the parse either fails or silently eats half the
@@ -33273,6 +33297,11 @@ a second read for nothing, and it is the commonest thing wrong with an otherwise
33273
33297
  and on every later review, approval or comment, human or bot, for as long as the PR lives.
33274
33298
  - \`for:\` \u2014 **REQUIRED**, and a DURATION: \`30s\`, \`15m\`, \`2h\`, \`3d\`. Never an instant. When it runs
33275
33299
  out frizz brings you back to re-check everything; re-parking is fine and uncapped.
33300
+ - \`title:\` \u2014 OPTIONAL, and the one place your own words reach the top of the resting card. Without it
33301
+ the card is headed "Awaiting", which is true of every park on the board and specific to none. Write a
33302
+ HEADING, not a sentence: **40 characters or fewer**, sentence case, naming THIS wait \u2014 "Three-platform
33303
+ CI run", "Fork-CI approval", "Nightly bench, arm 3 of 3". Anything longer is trimmed on a word
33304
+ boundary, and the detail belongs below the \`---\` where there is room for it.
33276
33305
 
33277
33306
  **THE SINGULAR KEYS ARE GONE** \u2014 \`shell:\`, \`agent:\`, \`timer:\`, \`pr:\` and \`reason:\`, one per line, were
33278
33307
  the grammar until 2026-08-24. YAML cannot express a repeated key, so they became the lists above. Write
@@ -33600,13 +33629,10 @@ see your conversation, so handing over context is always your job. The absence o
33600
33629
  a blocker to report \u2014 write what the child needs into the prompt or a scratch file you name, or do
33601
33630
  the work inline.
33602
33631
 
33603
- Pass \`subagent_type\` as the namespaced string \`frizz:<model>-<effort>\` (a bare \`opus-high\` will not
33604
- resolve); the profile descriptions carry the routing guidance. Tier by JUDGMENT required, not task
33605
- type: \`frizz:haiku\` for fully-scripted mechanical harvest only; \`frizz:sonnet-medium\` for
33606
- observable-fact probes, scaffolding, and doc/CI work; \`frizz:opus-high\`/\`-xhigh\` for the fix that
33607
- lands, diagnosis, architecture, and any probe whose deliverable is a load-bearing verdict. Effort runs
33608
- low\u2192medium\u2192high\u2192xhigh\u2192max. Bias toward Opus and buy extra rigor with EFFORT; re-verify any cheap-tier
33609
- load-bearing claim yourself.
33632
+ Two knobs, one each: the model rides the Agent tool's own \`model\` parameter (omit it and the child
33633
+ inherits yours), and the effort rides \`subagent_type\` as the namespaced string \`frizz:<effort>\` \u2014
33634
+ \`frizz:low\`, \`frizz:medium\`, \`frizz:high\`, \`frizz:xhigh\` or \`frizz:max\` (a bare \`high\` will not
33635
+ resolve). Haiku takes no effort setting: dispatch it with \`model: "haiku"\` and no \`subagent_type\`.
33610
33636
 
33611
33637
  Fan out one sub-agent per prong when work genuinely decomposes and the scale warrants it \u2014 authorized,
33612
33638
  never required, and never a substitute for running the thing yourself.
@@ -34565,6 +34591,9 @@ function createClaudeAgentBrokerBridge(deps) {
34565
34591
  // claudeWorkerEnv). Spread here rather than inherited, which is what gives these per-thread
34566
34592
  // values priority over anything in the environment frizz itself was launched with.
34567
34593
  ...claudeWorkerEnv(),
34594
+ // The auto-compact ceiling, from Settings (500K by default). Without it a `[1m]` worker compacts
34595
+ // only near 1M, re-sending up to 5x a TUI session's conversation on every turn past 200K.
34596
+ ...claudeCompactionEnv(deps.getSettings?.()),
34568
34597
  ...we?.permDir ? { FRIZZ_PERM_DIR: we.permDir } : {},
34569
34598
  // The cc-worker plugin's PreToolUse hook DENIES AskUserQuestion, because without frizz in the
34570
34599
  // loop a blocking question freezes a headless worker where nobody can answer it. On the broker path
@@ -34840,19 +34869,19 @@ var init_claude_agent_broker_bridge = __esm({
34840
34869
  });
34841
34870
 
34842
34871
  // packages/server/src/backend/auth-status.ts
34843
- import { join as join27 } from "node:path";
34872
+ import { join as join26 } from "node:path";
34844
34873
  import { homedir as homedir11, platform as platform2 } from "node:os";
34845
34874
  import { readFileSync as readFileSync16, statSync as statSync11 } from "node:fs";
34846
34875
  import { execFile as execFile4 } from "node:child_process";
34847
34876
  import { promisify as promisify4 } from "node:util";
34848
34877
  function claudeConfigDir2() {
34849
34878
  const override = process.env.CLAUDE_CONFIG_DIR;
34850
- return override && override.trim() ? override : join27(homedir11(), ".claude");
34879
+ return override && override.trim() ? override : join26(homedir11(), ".claude");
34851
34880
  }
34852
34881
  function claudeFileState(configDir) {
34853
34882
  let raw2;
34854
34883
  try {
34855
- raw2 = readFileSync16(join27(configDir, ".credentials.json"), "utf8");
34884
+ raw2 = readFileSync16(join26(configDir, ".credentials.json"), "utf8");
34856
34885
  } catch (err) {
34857
34886
  return err.code === "ENOENT" ? "absent" : "error";
34858
34887
  }
@@ -34885,7 +34914,7 @@ function readCodexAuthState(codexHome = defaultCodexHome()) {
34885
34914
  if (process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY || process.env.CODEX_ACCESS_TOKEN) return "authed";
34886
34915
  let raw2;
34887
34916
  try {
34888
- raw2 = readFileSync16(join27(codexHome, "auth.json"), "utf8");
34917
+ raw2 = readFileSync16(join26(codexHome, "auth.json"), "utf8");
34889
34918
  } catch (err) {
34890
34919
  return err.code === "ENOENT" ? "signed-out" : "unknown";
34891
34920
  }
@@ -34950,7 +34979,7 @@ function asEmail(value) {
34950
34979
  }
34951
34980
  function claudeAccountFile() {
34952
34981
  const override = process.env.CLAUDE_CONFIG_DIR;
34953
- return override && override.trim() ? join27(override, ".claude.json") : join27(homedir11(), ".claude.json");
34982
+ return override && override.trim() ? join26(override, ".claude.json") : join26(homedir11(), ".claude.json");
34954
34983
  }
34955
34984
  function readClaudeAccountEmail(path = claudeAccountFile()) {
34956
34985
  let stat2;
@@ -34974,7 +35003,7 @@ function readClaudeAccountEmail(path = claudeAccountFile()) {
34974
35003
  function readCodexAccountEmail(codexHome = defaultCodexHome()) {
34975
35004
  let doc;
34976
35005
  try {
34977
- doc = JSON.parse(readFileSync16(join27(codexHome, "auth.json"), "utf8"));
35006
+ doc = JSON.parse(readFileSync16(join26(codexHome, "auth.json"), "utf8"));
34978
35007
  } catch {
34979
35008
  return void 0;
34980
35009
  }
@@ -35042,8 +35071,8 @@ var init_auth_status = __esm({
35042
35071
  });
35043
35072
 
35044
35073
  // packages/server/src/dispatch.ts
35045
- import { closeSync as closeSync12, constants, existsSync as existsSync10, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync17, realpathSync as realpathSync7, statSync as statSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync8 } from "node:fs";
35046
- import { basename as basename6, join as join28, dirname as dirname11 } from "node:path";
35074
+ import { closeSync as closeSync12, constants, existsSync as existsSync9, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync17, realpathSync as realpathSync7, statSync as statSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync9 } from "node:fs";
35075
+ import { basename as basename6, join as join27, dirname as dirname11 } from "node:path";
35047
35076
  import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
35048
35077
  function fallbackTitle(prompt) {
35049
35078
  const firstLine2 = prompt.trim().split("\n", 1)[0].trim();
@@ -35062,7 +35091,7 @@ function fallbackTitle(prompt) {
35062
35091
  }
35063
35092
  function resolveSlug(frizzDir, base, taken) {
35064
35093
  base = ThreadSlug.parse(base);
35065
- const isTaken = (slug) => existsSync10(join28(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
35094
+ const isTaken = (slug) => existsSync9(join27(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
35066
35095
  if (!isTaken(base)) return base;
35067
35096
  for (let n = 2; ; n++) {
35068
35097
  const suffix = `-${n}`;
@@ -35079,12 +35108,12 @@ function resolveLegacyThreadFile(projectDir, value) {
35079
35108
  if (!parsed.success) return null;
35080
35109
  try {
35081
35110
  const projectRoot = realpathSync7(projectDir);
35082
- const frizzPath = join28(projectRoot, ".frizz");
35111
+ const frizzPath = join27(projectRoot, ".frizz");
35083
35112
  const frizzStat = lstatSync3(frizzPath);
35084
35113
  if (!frizzStat.isDirectory() || frizzStat.isSymbolicLink()) return null;
35085
35114
  const realFrizz = realpathSync7(frizzPath);
35086
35115
  if (dirname11(realFrizz) !== projectRoot || basename6(realFrizz) !== ".frizz") return null;
35087
- const path = join28(realFrizz, `${parsed.data}.md`);
35116
+ const path = join27(realFrizz, `${parsed.data}.md`);
35088
35117
  const before = lstatSync3(path);
35089
35118
  if (!before.isFile() || before.isSymbolicLink()) return null;
35090
35119
  const realPath = realpathSync7(path);
@@ -35128,7 +35157,7 @@ function boardAuthorizesAdoption(board, slug) {
35128
35157
  return !board.errorItems.some((item) => item.file === `${slug}.md`);
35129
35158
  }
35130
35159
  function ensureSafeDirectDirectory(parent, name) {
35131
- const path = join28(parent, name);
35160
+ const path = join27(parent, name);
35132
35161
  try {
35133
35162
  mkdirSync13(path);
35134
35163
  } catch (error) {
@@ -35158,8 +35187,8 @@ function loadWorkerPrompt(kind = "claude") {
35158
35187
  function monitorScriptsDir() {
35159
35188
  const plugin = workerPluginDir();
35160
35189
  if (!plugin) return void 0;
35161
- const dir = join28(plugin, "skills", "gh", "scripts");
35162
- return existsSync10(join28(dir, "ci-watch.mjs")) ? dir : void 0;
35190
+ const dir = join27(plugin, "skills", "gh", "scripts");
35191
+ return existsSync9(join27(dir, "ci-watch.mjs")) ? dir : void 0;
35163
35192
  }
35164
35193
  function codexScratchpadHookConfig(hookScript, sessionId) {
35165
35194
  if (!hookScript || !sessionId) return {};
@@ -35171,7 +35200,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
35171
35200
  }
35172
35201
  ]
35173
35202
  });
35174
- const bashBackgroundHook = join28(dirname11(hookScript), "bash-background.mjs");
35203
+ const bashBackgroundHook = join27(dirname11(hookScript), "bash-background.mjs");
35175
35204
  return {
35176
35205
  bypass_hook_trust: true,
35177
35206
  hooks: {
@@ -35197,7 +35226,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
35197
35226
  }
35198
35227
  function scratchpadHookScript() {
35199
35228
  const plugin = workerPluginDir();
35200
- return plugin ? join28(plugin, "hooks", "scratchpad.mjs") : void 0;
35229
+ return plugin ? join27(plugin, "hooks", "scratchpad.mjs") : void 0;
35201
35230
  }
35202
35231
  function composePrompt(sessionId, prompt, kind = "claude") {
35203
35232
  const children2 = kind === "codex" ? "Native sub-agents share it \u2014 have each write its OWN file rather than all editing one." : "Name it in a sub-agent's prompt when you want its notes to land somewhere you can read; give each child its OWN file rather than having them all edit one.";
@@ -35213,7 +35242,7 @@ function scratchpadOrientation(sessionId, kind = "claude") {
35213
35242
  return `SCRATCH DIRECTORY: .frizz/threads/${sessionId}/ \u2014 yours, free-form, as many files as you like, and nothing is expected in it. A single direct task usually needs none; writing notes is never a substitute for doing the work. On a long effort write the doc you would want if you lost your context (${children2}), then arm mcp__frizz__recurring_prompt with post_compaction: true and a prompt LINKING that file \u2014 frizz hands the link back when your context is compacted. Nothing in this directory is read automatically.`;
35214
35243
  }
35215
35244
  function frizzConfigBlock(projectDir) {
35216
- const path = join28(projectDir, "FRIZZ.md");
35245
+ const path = join27(projectDir, "FRIZZ.md");
35217
35246
  let body;
35218
35247
  try {
35219
35248
  const st = statSync12(path);
@@ -35253,8 +35282,8 @@ function resolveFrizzMcp(target, moduleUrl = import.meta.url, env = process.env,
35253
35282
  const { stateDir, serverLock, projectId } = typeof target === "string" ? { stateDir: target } : target;
35254
35283
  const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
35255
35284
  if (!pluginDir) return void 0;
35256
- const scriptPath = join28(pluginDir, "bin", FRIZZ_MCP.script);
35257
- if (!existsSync10(scriptPath)) return void 0;
35285
+ const scriptPath = join27(pluginDir, "bin", FRIZZ_MCP.script);
35286
+ if (!existsSync9(scriptPath)) return void 0;
35258
35287
  return {
35259
35288
  scriptPath,
35260
35289
  stateDir,
@@ -35264,12 +35293,8 @@ function resolveFrizzMcp(target, moduleUrl = import.meta.url, env = process.env,
35264
35293
  };
35265
35294
  }
35266
35295
  function claudeMcpConfig(mcp) {
35267
- const mcpServers = {
35268
- // chromeDevtoolsMcpMount(), never the fields: it is the ONE builder both backends render, and it
35269
- // is what mounts frizz's lazy proxy instead of the real server (backend/types.ts).
35270
- [CHROME_DEVTOOLS_MCP.name]: chromeDevtoolsMcpMount()
35271
- };
35272
- const allowedTools = [`mcp__${CHROME_DEVTOOLS_MCP.name}`];
35296
+ const mcpServers = {};
35297
+ const allowedTools = [];
35273
35298
  if (mcp) {
35274
35299
  const env = frizzMcpEnv(mcp);
35275
35300
  mcpServers[FRIZZ_MCP.name] = { command: process.execPath, args: [mcp.scriptPath], env };
@@ -35279,6 +35304,7 @@ function claudeMcpConfig(mcp) {
35279
35304
  }
35280
35305
  function claudeMcpFlags(mcp) {
35281
35306
  const { mcpServers, allowedTools } = claudeMcpConfig(mcp);
35307
+ if (Object.keys(mcpServers).length === 0) return [];
35282
35308
  const config = JSON.stringify({ mcpServers });
35283
35309
  return ["--mcp-config", config, `--allowedTools=${allowedTools.join(",")}`];
35284
35310
  }
@@ -35336,12 +35362,12 @@ function buildClaudeResumeCommand(opts) {
35336
35362
  }
35337
35363
  function createDispatcher(deps) {
35338
35364
  const readBoardSource = deps.readBoard ?? readBoard;
35339
- const frizzDir = join28(deps.project.dir, ".frizz");
35365
+ const frizzDir = join27(deps.project.dir, ".frizz");
35340
35366
  const adoptionRuntime = deps.adoptionRuntime ?? productionRuntime;
35341
35367
  function cleanupPrewrites(built) {
35342
35368
  for (const path of new Set(built.prewrite.map((file) => file.path))) {
35343
35369
  try {
35344
- rmSync8(path, { force: true });
35370
+ rmSync9(path, { force: true });
35345
35371
  } catch {
35346
35372
  }
35347
35373
  }
@@ -35349,7 +35375,7 @@ function createDispatcher(deps) {
35349
35375
  function cleanupDispatchFiles(scratchRel, built, sessionId) {
35350
35376
  cleanupPrewrites(built);
35351
35377
  try {
35352
- rmSync8(join28(deps.project.dir, scratchRel), { force: true, recursive: true });
35378
+ rmSync9(join27(deps.project.dir, scratchRel), { force: true, recursive: true });
35353
35379
  } catch {
35354
35380
  }
35355
35381
  cleanupAdoptionSessionFiles(deps.project.dir, sessionId);
@@ -35782,7 +35808,7 @@ import {
35782
35808
  watch as fsWatch
35783
35809
  } from "node:fs";
35784
35810
  import { homedir as homedir12 } from "node:os";
35785
- import { join as join29 } from "node:path";
35811
+ import { join as join28 } from "node:path";
35786
35812
  import watcher from "@parcel/watcher";
35787
35813
  function appServerTurnStalled(liveness, lastActivityAt, nowMs) {
35788
35814
  if (!liveness) return false;
@@ -35880,9 +35906,9 @@ function hasParkedTimerWatch(tele, armedTimerIds) {
35880
35906
  if (tele?.lastFence?.kind !== "awaiting") return false;
35881
35907
  return tele.lastFence.hints.some((hint) => hint.kind === "timer" && armedTimerIds.has(hint.value.trim()));
35882
35908
  }
35883
- function hasDeclaredWait(tele, nowMs, armedTimerIds = /* @__PURE__ */ new Set()) {
35909
+ function hasDeclaredWait(tele, nowMs, armedTimerIds = /* @__PURE__ */ new Set(), registeredPrWatches = /* @__PURE__ */ new Set()) {
35884
35910
  if (hasDeclaredBackgroundPark(tele, nowMs)) return true;
35885
- if (tele?.lastFence?.kind === "awaiting" && hasParkedPrWatch(tele)) return true;
35911
+ if (tele?.lastFence?.kind === "awaiting" && hasParkedPrWatch(tele, registeredPrWatches)) return true;
35886
35912
  if (hasParkedTimerWatch(tele, armedTimerIds)) return true;
35887
35913
  return (tele?.subAgents ?? []).some((a) => isDirectSubAgent(a) && a.state === "running");
35888
35914
  }
@@ -35900,7 +35926,7 @@ function heldByRunningChecks(tele, github, registered) {
35900
35926
  return status?.checks === "running" && status.state === "open";
35901
35927
  });
35902
35928
  }
35903
- function hasLiveOwnWork(tele) {
35929
+ function hasLiveOwnWork(tele, registeredPrWatches) {
35904
35930
  return Boolean(
35905
35931
  tele?.subAgents?.some((agent) => isDirectSubAgent(agent) && agent.state === "running") || tele?.bgShells?.some((shell) => shell.state === "running") || // A PARKED PR WATCHER COUNTS (2026-08-13). It is the same shape of thing as a detached shell: the
35906
35932
  // worker left it running, it will re-invoke the thread on its own, and until it does there is
@@ -35908,12 +35934,16 @@ function hasLiveOwnWork(tele) {
35908
35934
  // counting it — for that card's event-snooze, which is now the ONE control for hiding a parked
35909
35935
  // watcher (maintainer: "the user can just use the generic snooze card… now GitHub watchers can be
35910
35936
  // included in the ranks of those").
35911
- hasParkedPrWatch(tele)
35937
+ hasParkedPrWatch(tele, registeredPrWatches)
35912
35938
  );
35913
35939
  }
35914
- function hasParkedPrWatch(tele) {
35940
+ function hasParkedPrWatch(tele, registered) {
35915
35941
  if (tele?.lastFence?.kind !== "awaiting") return false;
35916
- return tele.lastFence.hints.some((hint) => hint.kind === "pr" && parsePrRef(hint.value) !== void 0);
35942
+ return tele.lastFence.hints.some((hint) => {
35943
+ if (hint.kind !== "pr") return false;
35944
+ const ref = parsePrRef(hint.value);
35945
+ return ref !== void 0 && registered.has(githubStatusKey(ref));
35946
+ });
35917
35947
  }
35918
35948
  function bgSnoozeArmed(row) {
35919
35949
  return row.bg_snooze_rested_at != null && row.rested_at != null && row.bg_snooze_rested_at === row.rested_at;
@@ -35955,7 +35985,7 @@ function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], arme
35955
35985
  if (tele?.lastFence?.kind !== "awaiting") return out;
35956
35986
  const createdAt = fenceAt ?? tele.lastAssistantAt ?? (/* @__PURE__ */ new Date()).toISOString();
35957
35987
  for (const hint of tele.lastFence.hints) {
35958
- if (hint.kind !== "shell" && hint.kind !== "agent") continue;
35988
+ if (hint.kind !== "shell") continue;
35959
35989
  const target = hint.value.trim();
35960
35990
  if (!target || seen.has(`shell:${target}`) || !liveWaitHandles(tele).has(target)) continue;
35961
35991
  seen.add(`shell:${target}`);
@@ -35967,7 +35997,7 @@ function hasFreshDelivery(row, processGone) {
35967
35997
  if (processGone) return false;
35968
35998
  return parseDeliveryLedger(row.delivery_ledger).some((d) => d.state === "pending" || d.state === "enqueued" || d.state === "delivered");
35969
35999
  }
35970
- function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, excuseLiveOwnWork = true, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set()) {
36000
+ function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, excuseLiveOwnWork = true, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set(), armedTimerIds = /* @__PURE__ */ new Set()) {
35971
36001
  if (futureSnooze(row, nowMs)) return false;
35972
36002
  if (hasActionableInteraction) return true;
35973
36003
  if (runtime === "perm-prompt") return true;
@@ -35982,18 +36012,18 @@ function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, no
35982
36012
  if (excuseLiveOwnWork && runtime !== "exited" && hasLiveBackgroundWork(tele) && !tele?.lastFence) return false;
35983
36013
  if (excuseLiveOwnWork && runtime !== "exited" && hasDeclaredBackgroundPark(tele, nowMs)) return false;
35984
36014
  if (excuseLiveOwnWork && runtime !== "exited" && heldByRunningChecks(tele, github, registeredPrWatches)) return false;
35985
- if (runtime !== "exited" && hasLiveOwnWork(tele) && tele?.lastFence?.kind !== "done") return !bgSnoozeArmed(row);
36015
+ if (runtime !== "exited" && (hasLiveOwnWork(tele, registeredPrWatches) || hasParkedTimerWatch(tele, armedTimerIds)) && tele?.lastFence?.kind !== "done") return !bgSnoozeArmed(row);
35986
36016
  if (tele?.lastFence?.kind === "done") return true;
35987
36017
  return true;
35988
36018
  }
35989
36019
  function deriveAwaitingBackground(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set(), armedTimerIds = /* @__PURE__ */ new Set()) {
35990
- if (runtime !== "turn-idle" || !hasDeclaredWait(tele, nowMs, armedTimerIds)) return false;
36020
+ if (runtime !== "turn-idle" || !hasDeclaredWait(tele, nowMs, armedTimerIds, registeredPrWatches)) return false;
35991
36021
  if (hasActionableInteraction || tele?.pendingAsk || tele?.pendingQuestion) return false;
35992
36022
  if (tele?.lastFence?.kind === "done") return false;
35993
- 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
36023
+ if (tele?.lastFence?.kind === "awaiting" && !hasParkedPrWatch(tele, registeredPrWatches) && !hasDeclaredBackgroundPark(tele, nowMs) && // A TIMER PARK IS THE SAME EXCEPTION AGAIN (2026-08-24): its fence has no park action either, so
35994
36024
  // suppressing this card left the wait stated nowhere but the fence's own machinery footer.
35995
36025
  !hasParkedTimerWatch(tele, armedTimerIds)) return false;
35996
- return deriveNeedsYou({ ...row, bg_snooze_rested_at: null }, tele, runtime, hasActionableInteraction, nowMs, limitPause, false, deliveryProcessGone);
36026
+ return deriveNeedsYou({ ...row, bg_snooze_rested_at: null }, tele, runtime, hasActionableInteraction, nowMs, limitPause, false, deliveryProcessGone, {}, /* @__PURE__ */ new Set(), armedTimerIds);
35997
36027
  }
35998
36028
  function resolveSessionProfile(row, tele) {
35999
36029
  const persistedModel = row.model?.trim() || void 0;
@@ -36099,7 +36129,7 @@ function sessionThreadView(projectDir, storage, row, tele, registeredLegacyTermi
36099
36129
  const state = effectiveSessionState(row, registeredLegacyTerminal);
36100
36130
  const archived = state === "archived";
36101
36131
  const limitPause = resolveLimitPause(row, tele, nowMs);
36102
- const needsYou = archived ? false : deriveNeedsYou(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, true, deliveryProcessGone, github, registeredPrWatches);
36132
+ const needsYou = archived ? false : deriveNeedsYou(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, true, deliveryProcessGone, github, registeredPrWatches, armedTimerIds);
36103
36133
  const awaitingBackground = archived ? false : deriveAwaitingBackground(row, tele, runtime, interactionPresence.needsUser, nowMs, limitPause, deliveryProcessGone, github, registeredPrWatches, armedTimerIds);
36104
36134
  const crashed = runtime === "exited" && (tele?.turn === "in-flight" || hasLiveBackgroundWork(tele) || headlessLostWork);
36105
36135
  const snoozedUntil = futureSnooze(row, nowMs);
@@ -36467,7 +36497,7 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
36467
36497
  if (parcelSub || stopped) return Promise.resolve();
36468
36498
  if (watchSetup) return watchSetup;
36469
36499
  const setup = (async () => {
36470
- const next = await subscribe(join29(project.dir, ".frizz"), () => scheduleRebuild());
36500
+ const next = await subscribe(join28(project.dir, ".frizz"), () => scheduleRebuild());
36471
36501
  if (stopped) {
36472
36502
  await next.unsubscribe();
36473
36503
  return;
@@ -36585,7 +36615,8 @@ function delivery(row) {
36585
36615
  createdAt: row.created_at,
36586
36616
  updatedAt: row.updated_at,
36587
36617
  deliveredAt: row.delivered_at,
36588
- terminalAt: row.terminal_at
36618
+ terminalAt: row.terminal_at,
36619
+ sentAt: row.sent_at ?? null
36589
36620
  };
36590
36621
  }
36591
36622
  function createWakeDeliveryStore(db) {
@@ -36608,11 +36639,14 @@ function createWakeDeliveryStore(db) {
36608
36639
  updated_at INTEGER NOT NULL,
36609
36640
  delivered_at INTEGER,
36610
36641
  terminal_at INTEGER,
36642
+ sent_at INTEGER,
36611
36643
  UNIQUE(thread_slug, session_id, fence_id)
36612
36644
  );
36613
36645
  CREATE INDEX IF NOT EXISTS wake_delivery_due
36614
36646
  ON wake_delivery(state, next_attempt_at, created_at);
36615
36647
  `);
36648
+ const columns = db.prepare("PRAGMA table_info(wake_delivery)").all();
36649
+ if (!columns.some((c) => c.name === "sent_at")) db.exec("ALTER TABLE wake_delivery ADD COLUMN sent_at INTEGER");
36616
36650
  const byId = db.prepare("SELECT * FROM wake_delivery WHERE id = ?");
36617
36651
  const all = db.prepare("SELECT * FROM wake_delivery ORDER BY created_at, id");
36618
36652
  const open = db.prepare(
@@ -36649,7 +36683,7 @@ function createWakeDeliveryStore(db) {
36649
36683
  const claimStmt = db.prepare(`
36650
36684
  UPDATE wake_delivery SET
36651
36685
  state = 'leased', attempts = attempts + 1, lease_owner = @owner, lease_until = @leaseUntil,
36652
- last_error = NULL, updated_at = @now
36686
+ last_error = NULL, sent_at = NULL, updated_at = @now
36653
36687
  WHERE id = @id AND state = 'pending' AND next_attempt_at <= @now AND attempts < @maxAttempts
36654
36688
  `);
36655
36689
  const deferFailureStmt = db.prepare(`
@@ -36657,6 +36691,11 @@ function createWakeDeliveryStore(db) {
36657
36691
  lease_until = @retryAt, last_error = @error, updated_at = @now
36658
36692
  WHERE id = @id AND state = 'leased' AND lease_owner = @owner
36659
36693
  `);
36694
+ const markSentStmt = db.prepare(`
36695
+ UPDATE wake_delivery SET
36696
+ sent_at = @now, lease_until = @confirmUntil, last_error = NULL, updated_at = @now
36697
+ WHERE id = @id AND state = 'leased' AND lease_owner = @owner
36698
+ `);
36660
36699
  const recoverExpiredStmt = db.prepare(`
36661
36700
  UPDATE wake_delivery SET
36662
36701
  state = CASE WHEN attempts >= @maxAttempts THEN 'exhausted' ELSE 'pending' END,
@@ -36725,6 +36764,7 @@ function createWakeDeliveryStore(db) {
36725
36764
  return row ? delivery(row) : void 0;
36726
36765
  },
36727
36766
  acknowledge: (id, owner, now) => acknowledgeStmt.run({ id, owner, now }).changes === 1,
36767
+ markSent: (id, owner, now, confirmUntil) => markSentStmt.run({ id, owner, now, confirmUntil }).changes === 1,
36728
36768
  confirm: (id, now) => confirmStmt.run({ id, now }).changes === 1,
36729
36769
  supersede: (id, now, reason) => supersedeStmt.run({ id, now, reason: reason.slice(0, 500) }).changes === 1
36730
36770
  };
@@ -37277,6 +37317,28 @@ function isTimerFenceId(fenceId) {
37277
37317
  function timerIdOf(fenceId) {
37278
37318
  return fenceId.slice(TIMER_FENCE_PREFIX.length + 1);
37279
37319
  }
37320
+ function conciseGhError(err) {
37321
+ const code = err?.code;
37322
+ if (code === "ENOENT") return "the `gh` CLI is not installed on the PATH of the process Frizz runs as";
37323
+ if (err?.killed || code === "ETIMEDOUT") return "`gh` did not answer within 15s";
37324
+ const stderr = typeof err?.stderr === "string" ? err.stderr : "";
37325
+ const line = stderr.trim().split(/\r?\n/).map((l) => l.trim()).find((l) => l.length > 0);
37326
+ if (line) return line.slice(0, 300);
37327
+ const message = err instanceof Error ? err.message : String(err);
37328
+ return message.trim().split(/\r?\n/, 1)[0]?.slice(0, 300) || "unknown error";
37329
+ }
37330
+ async function probePrReadable(ref) {
37331
+ try {
37332
+ await execFileAsync4(
37333
+ "gh",
37334
+ ["pr", "view", String(ref.number), "--repo", `${ref.owner}/${ref.repo}`, "--json", "state"],
37335
+ { timeout: 15e3, maxBuffer: 1e6, env: { ...process.env, GH_PAGER: "cat", GH_PROMPT_DISABLED: "1" } }
37336
+ );
37337
+ return { ok: true };
37338
+ } catch (err) {
37339
+ return { ok: false, reason: conciseGhError(err) };
37340
+ }
37341
+ }
37280
37342
  async function defaultFetchPr(ref) {
37281
37343
  try {
37282
37344
  const { stdout } = await execFileAsync4(
@@ -37303,8 +37365,8 @@ async function defaultFetchPr(ref) {
37303
37365
  reviewDecision: typeof j.reviewDecision === "string" ? j.reviewDecision : void 0,
37304
37366
  workflowRuns
37305
37367
  };
37306
- } catch {
37307
- return void 0;
37368
+ } catch (err) {
37369
+ throw new Error(conciseGhError(err));
37308
37370
  }
37309
37371
  }
37310
37372
  function createScheduler(deps) {
@@ -37317,6 +37379,7 @@ function createScheduler(deps) {
37317
37379
  const retryBaseMs = Math.max(1, deps.retryBaseMs ?? 5e3);
37318
37380
  const retryMaxMs = Math.max(retryBaseMs, deps.retryMaxMs ?? 5 * 6e4);
37319
37381
  const maxDeliveryAttempts = Math.max(1, deps.maxDeliveryAttempts ?? 6);
37382
+ const confirmGraceMs = Math.max(1, deps.confirmGraceMs ?? 6e4);
37320
37383
  const deliveryBatchSize = Math.max(0, deps.deliveryBatchSize ?? 50);
37321
37384
  const deliveryOwner = randomUUID14();
37322
37385
  const outbox = createWakeDeliveryStore(deps.storage.db);
@@ -37446,6 +37509,23 @@ function createScheduler(deps) {
37446
37509
  log3(`waker: GitHub review check failed for ${key} (${slug}) [${result.failure.kind}] \u2014 ${result.failure.message}${suppressed}`);
37447
37510
  reviewFailures.set(key, { signature, loggedAt: at, suppressed: 0 });
37448
37511
  }
37512
+ const statusFailures = /* @__PURE__ */ new Map();
37513
+ function recordStatusFailure(key, message, at) {
37514
+ const prior = statusFailures.get(key);
37515
+ if (prior?.signature === message && at - prior.loggedAt < 15 * 6e4) {
37516
+ prior.suppressed++;
37517
+ return;
37518
+ }
37519
+ const suppressed = prior?.suppressed ? ` (${prior.suppressed} identical repeats suppressed)` : "";
37520
+ log3(`waker: PR status check failed for ${key} \u2014 ${message}${suppressed}; CI and merge wakes cannot fire until \`gh\` can read it`);
37521
+ statusFailures.set(key, { signature: message, loggedAt: at, suppressed: 0 });
37522
+ }
37523
+ function recordStatusSuccess(key) {
37524
+ const prior = statusFailures.get(key);
37525
+ if (!prior) return;
37526
+ statusFailures.delete(key);
37527
+ log3(`waker: PR status check recovered for ${key}${prior.suppressed ? `; ${prior.suppressed} identical repeats were suppressed` : ""}`);
37528
+ }
37449
37529
  function recordReviewSuccess(key, slug) {
37450
37530
  const prior = reviewFailures.get(key);
37451
37531
  if (!prior) return;
@@ -37644,10 +37724,17 @@ ${wakeTimeHeader(now(), spokeAt)}`;
37644
37724
  for (const sh of tele.retiredShells ?? []) {
37645
37725
  for (const h of [sh.taskId, sh.id, sh.label]) if (h) finishedHandles.add(h);
37646
37726
  }
37647
- const finishedItem = (i) => finishedHandles.has(i.value);
37727
+ for (const a of tele.subAgents ?? []) {
37728
+ if (a.state !== "rested") continue;
37729
+ for (const h of [a.id, a.label]) if (h) finishedHandles.add(h);
37730
+ }
37731
+ const firedTimers = new Set(
37732
+ deps.storage.listThreadTimers(row.slug).filter((t) => t.state === "fired").map((t) => t.id)
37733
+ );
37734
+ const finishedItem = (i) => finishedHandles.has(i.value) || i.kind === "timer" && firedTimers.has(i.value);
37648
37735
  const status = park.items.map((i) => {
37649
37736
  const gone = dead.some((d) => d.kind === i.kind && d.value === i.value);
37650
- const note = !gone ? "still running" : finishedItem(i) ? "FINISHED \u2014 its result is waiting for you" : i.kind === "pr" ? "NOT REGISTERED \u2014 register it with `mcp__frizz__watch_pr` first, then name it here" : "NOT RUNNING (nothing by that name)";
37737
+ const note = !gone ? "still running" : i.kind === "timer" && firedTimers.has(i.value) ? "already FIRED \u2014 its wake was delivered; there is nothing left to wait on" : finishedItem(i) ? "FINISHED \u2014 its result is waiting for you" : i.kind === "pr" ? "NOT REGISTERED \u2014 register it with `mcp__frizz__watch_pr` first, then name it here" : "NOT RUNNING (nothing by that name)";
37651
37738
  return `- \`${AWAITING_KEY_OF[i.kind]}: [${i.value}]\` \u2014 ${note}`;
37652
37739
  });
37653
37740
  const allFinished = dead.length > 0 && dead.every(finishedItem);
@@ -37790,9 +37877,10 @@ ${clock}`;
37790
37877
  if (pr) {
37791
37878
  status.set(key, githubWatchStatus(pr, new Date(nowMs).toISOString()));
37792
37879
  publishGithubStatus(key, pr, nowMs);
37880
+ recordStatusSuccess(key);
37793
37881
  }
37794
37882
  } catch (err) {
37795
- log3(`waker: pr-watch status fetch failed for ${key}: ${err instanceof Error ? err.message : String(err)}`);
37883
+ recordStatusFailure(key, err instanceof Error ? err.message : String(err), nowMs);
37796
37884
  }
37797
37885
  try {
37798
37886
  const result = normalizeReviewResult(await fetchGithubReview(ref));
@@ -38086,6 +38174,38 @@ ${clock}`;
38086
38174
  continue;
38087
38175
  }
38088
38176
  if (item.state !== "leased" || item.leaseUntil === null || item.leaseUntil > nowMs) continue;
38177
+ if (item.sentAt !== null) {
38178
+ const runtime = deps.wakeRuntimeState?.(item.slug, item.sessionId) ?? "unknown";
38179
+ if (runtime !== "dead") {
38180
+ if (outbox.confirm(item.id, nowMs)) {
38181
+ settleSnooze(item);
38182
+ settleSchedulePrompt(item);
38183
+ settleRestPrompt(item);
38184
+ settleCompactPrompt(item);
38185
+ settleTimer(item);
38186
+ settleSignoffNudge(item);
38187
+ log3(`waker: delivered ${item.slug} \u2014 ${item.reason}${item.attempts > 1 ? ` (on attempt ${item.attempts})` : ""}`);
38188
+ }
38189
+ continue;
38190
+ }
38191
+ if (context !== "current-idle") continue;
38192
+ const recovered2 = outbox.recoverExpired(
38193
+ item.id,
38194
+ nowMs,
38195
+ nowMs + retryDelay(item.attempts),
38196
+ maxDeliveryAttempts,
38197
+ "the worker's process died before it read this wake"
38198
+ );
38199
+ if (!recovered2) continue;
38200
+ if (recovered2.state === "exhausted") {
38201
+ settleSnooze(item);
38202
+ settleTimer(item);
38203
+ log3(`waker: delivery EXHAUSTED for ${item.slug} after ${recovered2.attempts} attempts \u2014 ${recovered2.lastError ?? "unknown error"}`);
38204
+ } else {
38205
+ log3(`waker: wake LOST for ${item.slug} \u2014 ${item.reason}: the worker's process died before it read it; sending again (attempt ${item.attempts} of ${maxDeliveryAttempts} spent)`);
38206
+ }
38207
+ continue;
38208
+ }
38089
38209
  if (context !== "current-idle") continue;
38090
38210
  const recovered = outbox.recoverExpired(
38091
38211
  item.id,
@@ -38155,6 +38275,15 @@ ${clock}`;
38155
38275
  log3(`waker: delivery FAILED for ${item.slug} (attempt ${item.attempts} of ${maxDeliveryAttempts}): ${message}`);
38156
38276
  continue;
38157
38277
  }
38278
+ const runtime = deps.wakeRuntimeState?.(item.slug, item.sessionId) ?? "unknown";
38279
+ if (runtime !== "unknown") {
38280
+ const sentAt = now();
38281
+ if (outbox.markSent(item.id, deliveryOwner, sentAt, sentAt + confirmGraceMs)) {
38282
+ log3(`waker: sent ${item.slug} \u2014 ${item.reason}; confirming within ${Math.round(confirmGraceMs / 1e3)}s`);
38283
+ checkpoint("after-delivery", item);
38284
+ continue;
38285
+ }
38286
+ }
38158
38287
  const retried = item.attempts > 1 ? ` (on attempt ${item.attempts})` : "";
38159
38288
  log3(`waker: delivered ${item.slug} \u2014 ${item.reason}${retried}`);
38160
38289
  checkpoint("after-delivery", item);
@@ -38401,7 +38530,7 @@ var init_resume = __esm({
38401
38530
  });
38402
38531
 
38403
38532
  // packages/server/src/backend/claude.ts
38404
- import { join as join30 } from "node:path";
38533
+ import { join as join29 } from "node:path";
38405
38534
  function toolResultText2(content) {
38406
38535
  if (typeof content === "string") return content;
38407
38536
  if (!Array.isArray(content)) return "";
@@ -38490,7 +38619,7 @@ function createClaudeBackend(opts) {
38490
38619
  return { argv, env: claudeWorkerEnvironment(), prewrite: [] };
38491
38620
  },
38492
38621
  transcriptPath(sessionId) {
38493
- return join30(opts.logDir, `${sessionId}.jsonl`);
38622
+ return join29(opts.logDir, `${sessionId}.jsonl`);
38494
38623
  },
38495
38624
  parseLine(line) {
38496
38625
  return parseClaudeLine(line);
@@ -38660,12 +38789,12 @@ var init_login_utility = __esm({
38660
38789
 
38661
38790
  // packages/server/src/backend/codex-app-server-diagnostics.ts
38662
38791
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, renameSync as renameSync8, statSync as statSync13 } from "node:fs";
38663
- import { join as join31 } from "node:path";
38792
+ import { join as join30 } from "node:path";
38664
38793
  function codexDiagnosticLogPath(stateDir, projectId) {
38665
- return join31(stateDir, "codex-app-server", `${projectId}.diagnostics.log`);
38794
+ return join30(stateDir, "codex-app-server", `${projectId}.diagnostics.log`);
38666
38795
  }
38667
38796
  function createCodexDiagnosticSink(stateDir, projectId, now = () => /* @__PURE__ */ new Date()) {
38668
- const dir = join31(stateDir, "codex-app-server");
38797
+ const dir = join30(stateDir, "codex-app-server");
38669
38798
  const path = codexDiagnosticLogPath(stateDir, projectId);
38670
38799
  let ensured = false;
38671
38800
  return (event) => {
@@ -38697,7 +38826,7 @@ var init_codex_app_server_diagnostics = __esm({
38697
38826
  import { execFile as execFile7 } from "node:child_process";
38698
38827
  import { readdirSync as readdirSync8, unlinkSync as unlinkSync4 } from "node:fs";
38699
38828
  import { connect as connectSocket } from "node:net";
38700
- import { join as join32 } from "node:path";
38829
+ import { join as join31 } from "node:path";
38701
38830
  function parseLsofSocketNames(stdout) {
38702
38831
  const referenced = /* @__PURE__ */ new Set();
38703
38832
  for (const line of stdout.split("\n")) {
@@ -38714,7 +38843,7 @@ function sweepStaleSockets(options, deps = {}) {
38714
38843
  const keep = new Set(options.keep ?? []);
38715
38844
  let candidates;
38716
38845
  try {
38717
- candidates = (deps.readdir ?? readdirSync8)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) => join32(options.dir, name)).filter((path) => !keep.has(path));
38846
+ candidates = (deps.readdir ?? readdirSync8)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) => join31(options.dir, name)).filter((path) => !keep.has(path));
38718
38847
  } catch {
38719
38848
  return;
38720
38849
  }
@@ -39178,7 +39307,7 @@ var init_thread_hibernation = __esm({
39178
39307
  });
39179
39308
 
39180
39309
  // packages/server/src/context.ts
39181
- import { join as join33 } from "node:path";
39310
+ import { join as join32 } from "node:path";
39182
39311
  import { randomUUID as randomUUID15 } from "node:crypto";
39183
39312
  import { homedir as homedir13 } from "node:os";
39184
39313
  function reconcileSessions(storage) {
@@ -39336,7 +39465,7 @@ async function createContext(opts = {}) {
39336
39465
  function createContextUnchecked(opts, resources) {
39337
39466
  const home = opts.home ?? homedir13();
39338
39467
  const project = opts.project ?? resolveProject();
39339
- const dbPath = join33(project.stateDir, "ui.db");
39468
+ const dbPath = join32(project.stateDir, "ui.db");
39340
39469
  const storage = createStorage(dbPath);
39341
39470
  resources.storage = storage;
39342
39471
  const bus = new Bus();
@@ -39481,12 +39610,16 @@ function createContextUnchecked(opts, resources) {
39481
39610
  projectId: project.id,
39482
39611
  // The frizz worker environment — the SDK equivalent of the CLI argv's --plugin-dir / --mcp-config.
39483
39612
  // Computed ONCE here (constant per project) and applied on every broker fork so a broker worker
39484
- // gets the frizz sub-agent profiles, the frizz + chrome-devtools MCP, and the cc-worker hooks.
39613
+ // gets the frizz sub-agent profiles, the frizz MCP server (the only one frizz mounts), and the
39614
+ // cc-worker hooks. A project's own `.mcp.json` servers load on top of these, not through them.
39485
39615
  workerEnv: {
39486
39616
  pluginDir: workerPluginDir(),
39487
39617
  ...claudeMcpConfig(resolveFrizzMcp(frizzMcpTarget)),
39488
39618
  permDir: permRequestDir(project)
39489
39619
  },
39620
+ // Read at every fork, not captured once: the compaction window is a Settings value the drawer
39621
+ // can change while the server runs, and the next dispatch or cold resume should carry it.
39622
+ getSettings: () => getSettings(storage, home),
39490
39623
  // Which broker threads warmUp() may reattach at boot. Same predicate codex's shouldAutoResume
39491
39624
  // applies: never wake a thread the human has already put away — a boot reattach is only for a
39492
39625
  // thread that is still open and still theirs to come back to. Broker-backed rows only; a
@@ -39578,6 +39711,15 @@ function createContextUnchecked(opts, resources) {
39578
39711
  // instant for a weekly limit, whose message text carries a clock but no date; readQuota memoizes,
39579
39712
  // so consulting it per tick costs a live request only every few minutes.
39580
39713
  readQuota,
39714
+ // The only runtime that can answer is the broker: its daemon record is on disk while the daemon
39715
+ // lives and is unlinked when it dies (liveBrokerRecords checks the pid), so "did the process that
39716
+ // took this wake survive" is one directory read. Codex and any row whose session moved on answer
39717
+ // "unknown", which keeps their wakes on the delivered-on-return path they always had.
39718
+ wakeRuntimeState: (slug, sessionId) => {
39719
+ const row = storage.getSession(slug);
39720
+ if (!row || row.session_id !== sessionId || row.backend !== "claude" || row.claude_runtime !== "broker" || !claudeBroker) return "unknown";
39721
+ return liveBrokerRecords(project.stateDir).some((r) => r.sessionId === sessionId) ? "alive" : "dead";
39722
+ },
39581
39723
  resume: (slug, message, deliveryId) => {
39582
39724
  const deliveryMessage = `${message}
39583
39725
 
@@ -39659,6 +39801,7 @@ ${wakeDeliveryToken(deliveryId)}`;
39659
39801
  tailer,
39660
39802
  dispatcher,
39661
39803
  scheduler,
39804
+ probePr: probePrReadable,
39662
39805
  stopSubscriptions,
39663
39806
  backendFor,
39664
39807
  getSettings: () => getSettings(storage, home),
@@ -42367,7 +42510,7 @@ var init_server = __esm({
42367
42510
  });
42368
42511
 
42369
42512
  // packages/server/src/repair.ts
42370
- import { existsSync as existsSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "node:fs";
42513
+ import { existsSync as existsSync11, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "node:fs";
42371
42514
  import { basename as basename7, dirname as dirname13, resolve as resolve6 } from "node:path";
42372
42515
  function quoteValue(v) {
42373
42516
  if (/^[\w./#:+-]+$/.test(v)) return v;
@@ -42382,7 +42525,7 @@ function repairThreadFile(frizzDir, file) {
42382
42525
  const abs = resolve6(root, file);
42383
42526
  if (dirname13(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
42384
42527
  if (!abs.endsWith(".md")) throw new RepairError(`refusing to repair "${file}": not a .md thread file`);
42385
- if (!existsSync12(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
42528
+ if (!existsSync11(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
42386
42529
  const content = readFileSync19(abs, "utf8");
42387
42530
  const firstLine2 = content.replace(/^/, "").split(/\r?\n/).find((l) => l.trim() !== "");
42388
42531
  if (firstLine2?.trim() === "---") {
@@ -42574,10 +42717,10 @@ var init_bash_background = __esm({
42574
42717
  });
42575
42718
 
42576
42719
  // packages/server/src/transcript.ts
42577
- import { closeSync as closeSync13, existsSync as existsSync13, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync21, readSync as readSync7, renameSync as renameSync9, statSync as statSync14, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
42720
+ import { closeSync as closeSync13, existsSync as existsSync12, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync21, readSync as readSync7, renameSync as renameSync9, statSync as statSync14, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
42578
42721
  import { createHash as createHash13 } from "node:crypto";
42579
42722
  import { StringDecoder as StringDecoder4 } from "node:string_decoder";
42580
- import { join as join34 } from "node:path";
42723
+ import { join as join33 } from "node:path";
42581
42724
  import { homedir as homedir14 } from "node:os";
42582
42725
  function formatTokens(n) {
42583
42726
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
@@ -43127,14 +43270,14 @@ function pruneScreenshotCache() {
43127
43270
  if (entries.length <= SCREENSHOT_CACHE_MAX) return;
43128
43271
  const byMtime = entries.map((n) => {
43129
43272
  try {
43130
- return { n, m: statSync14(join34(SCREENSHOT_CACHE_DIR, n)).mtimeMs };
43273
+ return { n, m: statSync14(join33(SCREENSHOT_CACHE_DIR, n)).mtimeMs };
43131
43274
  } catch {
43132
43275
  return { n, m: 0 };
43133
43276
  }
43134
43277
  }).sort((a, b) => b.m - a.m);
43135
43278
  for (const { n } of byMtime.slice(SCREENSHOT_CACHE_MAX)) {
43136
43279
  try {
43137
- unlinkSync5(join34(SCREENSHOT_CACHE_DIR, n));
43280
+ unlinkSync5(join33(SCREENSHOT_CACHE_DIR, n));
43138
43281
  } catch {
43139
43282
  }
43140
43283
  }
@@ -43164,14 +43307,14 @@ function persistBase64Image(mediaType, data, idKey) {
43164
43307
  const ext = IMAGE_MEDIA_EXT[typeof mediaType === "string" ? mediaType.toLowerCase() : ""];
43165
43308
  if (!ext || !data) return void 0;
43166
43309
  const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
43167
- const path = join34(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
43310
+ const path = join33(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
43168
43311
  try {
43169
- if (existsSync13(path)) return path;
43312
+ if (existsSync12(path)) return path;
43170
43313
  if (data.length > SCREENSHOT_MAX_BASE64) return void 0;
43171
43314
  const buf = Buffer.from(data, "base64");
43172
43315
  if (buf.length === 0 || !looksLikeImage(buf, ext)) return void 0;
43173
43316
  mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
43174
- const tmp = join34(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43317
+ const tmp = join33(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43175
43318
  writeFileSync12(tmp, buf);
43176
43319
  renameSync9(tmp, path);
43177
43320
  pruneScreenshotCache();
@@ -43186,14 +43329,14 @@ function persistSentFile(srcPath, idKey) {
43186
43329
  const outExt = ext === "jpeg" ? "jpg" : ext;
43187
43330
  try {
43188
43331
  const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
43189
- const dest = join34(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
43190
- if (existsSync13(dest)) return dest;
43332
+ const dest = join33(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
43333
+ if (existsSync12(dest)) return dest;
43191
43334
  const size = statSync14(srcPath).size;
43192
43335
  if (size === 0 || size > SENT_IMAGE_MAX_BYTES) return void 0;
43193
43336
  const buf = readFileSync21(srcPath);
43194
43337
  if (!looksLikeImage(buf, outExt)) return void 0;
43195
43338
  mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
43196
- const tmp = join34(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43339
+ const tmp = join33(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
43197
43340
  writeFileSync12(tmp, buf);
43198
43341
  renameSync9(tmp, dest);
43199
43342
  pruneScreenshotCache();
@@ -43630,16 +43773,16 @@ function verifyIncrementalParse(path, identityPrefix, incremental) {
43630
43773
  }
43631
43774
  }
43632
43775
  function logDirOf(project) {
43633
- return join34(homedir14(), ".claude", "projects", project.cwdSlug);
43776
+ return join33(homedir14(), ".claude", "projects", project.cwdSlug);
43634
43777
  }
43635
43778
  function resolveTranscriptPath(project, sessionId) {
43636
- const path = join34(logDirOf(project), `${sessionId}.jsonl`);
43779
+ const path = join33(logDirOf(project), `${sessionId}.jsonl`);
43637
43780
  try {
43638
43781
  if (statSync14(path).size > 0) return path;
43639
43782
  } catch {
43640
43783
  }
43641
43784
  const stranded = discoverTranscriptDir(logDirOf(project), sessionId);
43642
- return stranded ? join34(stranded, `${sessionId}.jsonl`) : path;
43785
+ return stranded ? join33(stranded, `${sessionId}.jsonl`) : path;
43643
43786
  }
43644
43787
  function projectCodexTranscript(raw2, identityPrefix = "codex") {
43645
43788
  const out = [];
@@ -44583,7 +44726,7 @@ function sourceForThread(project, storage, slug, backendFor) {
44583
44726
  }
44584
44727
  if (!FOREIGN_SESSION_ID_RE.test(slug)) return void 0;
44585
44728
  const claudePath = resolveTranscriptPath(project, slug);
44586
- if (existsSync13(claudePath)) {
44729
+ if (existsSync12(claudePath)) {
44587
44730
  return { slug, sessionId: slug, nativeId: slug, backend: "claude", runtimeGeneration: 0, path: claudePath };
44588
44731
  }
44589
44732
  const rollout = (backendFor?.("codex") ?? defaultCodexBackend()).transcriptPath(slug);
@@ -45039,7 +45182,7 @@ var init_open_external = __esm({
45039
45182
  import { spawn as spawn6 } from "node:child_process";
45040
45183
  import { readFileSync as readFileSync22, realpathSync as realpathSync8, statSync as statSync15 } from "node:fs";
45041
45184
  import { homedir as homedir15 } from "node:os";
45042
- import { isAbsolute as isAbsolute3, join as join35, resolve as resolve7, sep } from "node:path";
45185
+ import { isAbsolute as isAbsolute3, join as join34, resolve as resolve7, sep } from "node:path";
45043
45186
  function isUnder(real, root) {
45044
45187
  let rootReal;
45045
45188
  try {
@@ -45069,7 +45212,7 @@ function resolveLocalFile(rawPath, roots) {
45069
45212
  function resolveOpenableFile(raw2, projectDir, roots, home = homedir15()) {
45070
45213
  const trimmed = raw2.trim().replace(/:\d+(?::\d+)?$/, "");
45071
45214
  if (!trimmed) return null;
45072
- const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ? join35(home, trimmed.slice(2)) : isAbsolute3(trimmed) ? trimmed : resolve7(projectDir, trimmed);
45215
+ const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ? join34(home, trimmed.slice(2)) : isAbsolute3(trimmed) ? trimmed : resolve7(projectDir, trimmed);
45073
45216
  try {
45074
45217
  return resolveLocalFile(abs, roots);
45075
45218
  } catch {
@@ -45101,7 +45244,7 @@ var MARKDOWN_FILE_EXT, MARKDOWN_READ_LIMIT;
45101
45244
  var init_local_file = __esm({
45102
45245
  "packages/server/src/local-file.ts"() {
45103
45246
  "use strict";
45104
- MARKDOWN_FILE_EXT = /\.(?:md|markdown)$/i;
45247
+ MARKDOWN_FILE_EXT = /\.(?:md|mdx|markdown)$/i;
45105
45248
  MARKDOWN_READ_LIMIT = 1024 * 1024;
45106
45249
  }
45107
45250
  });
@@ -45780,10 +45923,10 @@ var init_directory_picker = __esm({
45780
45923
 
45781
45924
  // packages/server/src/router.ts
45782
45925
  import { readFileSync as readFileSync23, statSync as statSync17 } from "node:fs";
45783
- import { join as join36, resolve as resolve8 } from "node:path";
45926
+ import { join as join35, resolve as resolve8 } from "node:path";
45784
45927
  import { randomUUID as randomUUID16 } from "node:crypto";
45785
45928
  import { basename as basename9, dirname as dirname14 } from "node:path";
45786
- import { existsSync as existsSync14, mkdirSync as mkdirSync16, rmSync as rmSync9, writeFileSync as writeFileSync13 } from "node:fs";
45929
+ import { existsSync as existsSync13, mkdirSync as mkdirSync16, rmSync as rmSync10, writeFileSync as writeFileSync13 } from "node:fs";
45787
45930
  import { homedir as homedir16 } from "node:os";
45788
45931
  function validateGithubDispatchProfile(input) {
45789
45932
  validateThreadProfile(input.backend, input.model, input.effort);
@@ -45943,7 +46086,7 @@ function projectCard(entry, stale) {
45943
46086
  function addProjectAtPath(input) {
45944
46087
  const typed = input.trim();
45945
46088
  if (!typed) throw new Error("Enter a folder path.");
45946
- const expanded = typed === "~" || typed.startsWith("~/") ? join36(homedir16(), typed.slice(1)) : typed;
46089
+ const expanded = typed === "~" || typed.startsWith("~/") ? join35(homedir16(), typed.slice(1)) : typed;
45947
46090
  const absolute = resolve8(expanded);
45948
46091
  let stats;
45949
46092
  try {
@@ -45974,12 +46117,12 @@ function storeProjectIcon(id, name, bytes) {
45974
46117
  mkdirSync16(dirname14(path), { recursive: true });
45975
46118
  writeFileSync13(path, bytes);
45976
46119
  if (!imageDimensions(path)) {
45977
- rmSync9(path, { force: true });
46120
+ rmSync10(path, { force: true });
45978
46121
  throw new Error("That file does not look like an image Frizz can draw.");
45979
46122
  }
45980
46123
  for (const stale of PROJECT_ICON_EXTENSIONS) {
45981
46124
  const other = customIconPath(id, `.${stale}`);
45982
- if (other !== path) rmSync9(other, { force: true });
46125
+ if (other !== path) rmSync10(other, { force: true });
45983
46126
  }
45984
46127
  const updated = setProjectIcon(id, path);
45985
46128
  if (!updated) throw new Error("No such project.");
@@ -45996,7 +46139,7 @@ function setProjectIconFromFile(id, file) {
45996
46139
  return storeProjectIcon(id, file, bytes);
45997
46140
  }
45998
46141
  function createRouter(ctx) {
45999
- const frizzDir = join36(ctx.project.dir, ".frizz");
46142
+ const frizzDir = join35(ctx.project.dir, ".frizz");
46000
46143
  const openRoots = openableFileRoots(ctx.project);
46001
46144
  function isAutoTitledSession(slug) {
46002
46145
  return ctx.storage.getSession(slug)?.title_auto === 1;
@@ -47130,6 +47273,12 @@ ${gapNote}` : input.message;
47130
47273
  if (forMs === null) {
47131
47274
  throw new Error(`\`for: ${input.for}\` is not a duration \u2014 give one like \`30m\`, \`2h\` or \`3d\` (max 24h)`);
47132
47275
  }
47276
+ const probe = await ctx.probePr(ref);
47277
+ if (!probe.ok) {
47278
+ throw new Error(
47279
+ `\`${target}\` cannot be watched \u2014 the server's \`gh\` could not read it: ${probe.reason}. Frizz polls with the \`gh\` of the process it runs as, not yours: check \`gh auth status\` there and that the repo is reachable, then register again. If GitHub itself was briefly down, registering again in a minute is enough.`
47280
+ );
47281
+ }
47133
47282
  const now = Date.now();
47134
47283
  const id = `prw_${randomUUID16().replace(/-/g, "").slice(0, 12)}`;
47135
47284
  ctx.storage.armPrWatch({ id, slug: input.slug, owner: ref.owner, repo: ref.repo, number: ref.number, createdAtMs: now, expiresAtMs: now + forMs });
@@ -47597,11 +47746,11 @@ ${gapNote}` : input.message;
47597
47746
  output: ProjectCard,
47598
47747
  handler: async ({ input }) => {
47599
47748
  for (const extension2 of PROJECT_ICON_EXTENSIONS) {
47600
- rmSync9(customIconPath(input.id, `.${extension2}`), { force: true });
47749
+ rmSync10(customIconPath(input.id, `.${extension2}`), { force: true });
47601
47750
  }
47602
47751
  const updated = clearProjectIcon(input.id);
47603
47752
  if (!updated) throw new Error("No such project.");
47604
- return projectCard(updated, !existsSync14(updated.path));
47753
+ return projectCard(updated, !existsSync13(updated.path));
47605
47754
  }
47606
47755
  }),
47607
47756
  /**
@@ -47651,7 +47800,7 @@ ${gapNote}` : input.message;
47651
47800
  for (const entry of listProjects()) {
47652
47801
  if (entry.stale) continue;
47653
47802
  try {
47654
- const db = new sqlite_default(join36(projectStateDir(entry.id), "ui.db"), { readonly: true });
47803
+ const db = new sqlite_default(join35(projectStateDir(entry.id), "ui.db"), { readonly: true });
47655
47804
  try {
47656
47805
  const hit = db.prepare("SELECT 1 FROM session WHERE slug = ? LIMIT 1").get(input.slug);
47657
47806
  if (hit) found.push({ projectSlug: entry.slug, projectName: entry.name ?? entry.slug });
@@ -48043,7 +48192,7 @@ var init_local_image = __esm({
48043
48192
  // packages/server/src/project-icon.ts
48044
48193
  import { readFileSync as readFileSync25, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync19 } from "node:fs";
48045
48194
  import { homedir as homedir17 } from "node:os";
48046
- import { basename as basename10, dirname as dirname15, extname as extname3, join as join37, relative, sep as sep2 } from "node:path";
48195
+ import { basename as basename10, dirname as dirname15, extname as extname3, join as join36, relative, sep as sep2 } from "node:path";
48047
48196
  function normalizeStem(stem) {
48048
48197
  const sized = stem.toLowerCase().replace(/[@-]\d+(\.\d+)?x(\d+)?$/u, "").replace(/[-_]\d{2,4}$/u, "");
48049
48198
  const normalized = sized.replace(VARIANT_SUFFIX, "");
@@ -48061,7 +48210,7 @@ function sizeScore(dimensions) {
48061
48210
  return Math.max(0, Math.min(40, Math.round(Math.log2(edge / 24) * 8)));
48062
48211
  }
48063
48212
  function under(root, relativeDirectory) {
48064
- return relativeDirectory ? join37(root, relativeDirectory) : root;
48213
+ return relativeDirectory ? join36(root, relativeDirectory) : root;
48065
48214
  }
48066
48215
  function listFiles(directory) {
48067
48216
  try {
@@ -48083,19 +48232,19 @@ function iconDirectories(root) {
48083
48232
  for (const asset of ASSET_DIRECTORIES) directories.push(under(root, asset));
48084
48233
  for (const host of HOST_DIRECTORIES) {
48085
48234
  if (!children2.has(host)) continue;
48086
- for (const asset of ASSET_DIRECTORIES) directories.push(under(join37(root, host), asset));
48235
+ for (const asset of ASSET_DIRECTORIES) directories.push(under(join36(root, host), asset));
48087
48236
  }
48088
48237
  for (const extra of EXTRA_DIRECTORIES) directories.push(under(root, extra));
48089
48238
  for (const container of WORKSPACE_CONTAINERS) {
48090
48239
  if (!children2.has(container)) continue;
48091
48240
  let members;
48092
48241
  try {
48093
- members = readdirSync10(join37(root, container), { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
48242
+ members = readdirSync10(join36(root, container), { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
48094
48243
  } catch {
48095
48244
  continue;
48096
48245
  }
48097
48246
  for (const member of members) {
48098
- for (const asset of ASSET_DIRECTORIES) directories.push(under(join37(root, container, member), asset));
48247
+ for (const asset of ASSET_DIRECTORIES) directories.push(under(join36(root, container, member), asset));
48099
48248
  }
48100
48249
  }
48101
48250
  return [...new Set(directories)];
@@ -48109,7 +48258,7 @@ function manifestIcons(manifestPath) {
48109
48258
  }
48110
48259
  if (!Array.isArray(parsed?.icons)) return [];
48111
48260
  const directory = dirname15(manifestPath);
48112
- return parsed.icons.map((icon) => typeof icon?.src === "string" ? icon.src : void 0).filter((src) => !!src && !/^(https?:)?\/\//u.test(src) && !src.startsWith("data:")).map((src) => join37(directory, src.replace(/[?#].*$/u, "").replace(/^\//u, "")));
48261
+ return parsed.icons.map((icon) => typeof icon?.src === "string" ? icon.src : void 0).filter((src) => !!src && !/^(https?:)?\/\//u.test(src) && !src.startsWith("data:")).map((src) => join36(directory, src.replace(/[?#].*$/u, "").replace(/^\//u, "")));
48113
48262
  }
48114
48263
  function projectIconCandidates(root) {
48115
48264
  const projectStem = normalizeStem(basename10(root)).normalized;
@@ -48128,7 +48277,7 @@ function projectIconCandidates(root) {
48128
48277
  for (const name of listFiles(directory)) {
48129
48278
  const extension2 = extname3(name).toLowerCase();
48130
48279
  if (MANIFEST_NAMES.includes(name.toLowerCase())) {
48131
- for (const icon of manifestIcons(join37(directory, name))) {
48280
+ for (const icon of manifestIcons(join36(directory, name))) {
48132
48281
  if (MEASURABLE_IMAGE_EXTENSIONS.has(extname3(icon).toLowerCase())) {
48133
48282
  fromManifest.add(icon);
48134
48283
  files.push({ path: icon, directory: dirname15(icon) });
@@ -48139,7 +48288,7 @@ function projectIconCandidates(root) {
48139
48288
  if (!MEASURABLE_IMAGE_EXTENSIONS.has(extension2)) continue;
48140
48289
  const stem = name.slice(0, name.length - extension2.length);
48141
48290
  if (normalizeStem(stem).normalized === "favicon") iconHomes.add(directory);
48142
- files.push({ path: join37(directory, name), directory });
48291
+ files.push({ path: join36(directory, name), directory });
48143
48292
  if (files.length >= MAX_CANDIDATES) break;
48144
48293
  }
48145
48294
  }
@@ -48317,7 +48466,7 @@ var init_project_icon = __esm({
48317
48466
 
48318
48467
  // packages/server/src/local-visualization.ts
48319
48468
  import { readdirSync as readdirSync11, readFileSync as readFileSync26, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
48320
- import { join as join38, sep as sep3 } from "node:path";
48469
+ import { join as join37, sep as sep3 } from "node:path";
48321
48470
  function children(path, pattern) {
48322
48471
  try {
48323
48472
  return readdirSync11(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && pattern.test(entry.name)).map((entry) => entry.name).sort().reverse();
@@ -48329,7 +48478,7 @@ function isUnder2(path, root) {
48329
48478
  return path === root || path.startsWith(root.endsWith(sep3) ? root : root + sep3);
48330
48479
  }
48331
48480
  function resolveFragment(projectDir, sessionId, file) {
48332
- const base = join38(projectDir, ".codex", "visualizations");
48481
+ const base = join37(projectDir, ".codex", "visualizations");
48333
48482
  let projectReal;
48334
48483
  let baseReal;
48335
48484
  try {
@@ -48340,12 +48489,12 @@ function resolveFragment(projectDir, sessionId, file) {
48340
48489
  }
48341
48490
  if (!isUnder2(baseReal, projectReal)) return null;
48342
48491
  for (const year of children(base, YEAR_PART)) {
48343
- const yearDir = join38(base, year);
48492
+ const yearDir = join37(base, year);
48344
48493
  for (const month of children(yearDir, DATE_PART)) {
48345
- const monthDir = join38(yearDir, month);
48494
+ const monthDir = join37(yearDir, month);
48346
48495
  for (const day of children(monthDir, DATE_PART)) {
48347
- const sessionRoot = join38(baseReal, year, month, day, sessionId);
48348
- const candidate = join38(monthDir, day, sessionId, file);
48496
+ const sessionRoot = join37(baseReal, year, month, day, sessionId);
48497
+ const candidate = join37(monthDir, day, sessionId, file);
48349
48498
  try {
48350
48499
  const real = realpathSync11(candidate);
48351
48500
  if (isUnder2(real, sessionRoot) && statSync20(real).isFile()) return real;
@@ -48474,7 +48623,7 @@ var init_local_visualization = __esm({
48474
48623
 
48475
48624
  // packages/server/src/app.ts
48476
48625
  import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync14 } from "node:fs";
48477
- import { join as join39 } from "node:path";
48626
+ import { join as join38 } from "node:path";
48478
48627
  import { randomUUID as randomUUID17 } from "node:crypto";
48479
48628
  function createApp(ctx, options = {}) {
48480
48629
  const app = new Hono2();
@@ -48578,10 +48727,10 @@ function createApp(ctx, options = {}) {
48578
48727
  if (typeof body.data !== "string" || body.data.length > ATTACHMENT_MAX_BASE64_CHARS) return c.json({ error: "bad payload" }, 400);
48579
48728
  const ext = `.${attachmentExtension(name)}`;
48580
48729
  const buf = Buffer.from(body.data, "base64");
48581
- const dir = join39(ctx.project.stateDir, "attachments");
48730
+ const dir = join38(ctx.project.stateDir, "attachments");
48582
48731
  mkdirSync17(dir, { recursive: true });
48583
48732
  const base = name.replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9_-]+/g, "-").slice(0, 40) || "file";
48584
- const path = join39(dir, `${Date.now()}-${randomUUID17().slice(0, 8)}-${base}${ext}`);
48733
+ const path = join38(dir, `${Date.now()}-${randomUUID17().slice(0, 8)}-${base}${ext}`);
48585
48734
  writeFileSync14(path, buf);
48586
48735
  return c.json({ path });
48587
48736
  });
@@ -49820,10 +49969,10 @@ var init_app_socket = __esm({
49820
49969
  });
49821
49970
 
49822
49971
  // packages/server/src/boot-progress.ts
49823
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync27, renameSync as renameSync10, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "node:fs";
49824
- import { basename as basename11, dirname as dirname16, join as join40 } from "node:path";
49972
+ import { mkdirSync as mkdirSync18, readFileSync as readFileSync27, renameSync as renameSync10, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
49973
+ import { basename as basename11, dirname as dirname16, join as join39 } from "node:path";
49825
49974
  function bootProgressPath(stateDir) {
49826
- return join40(stateDir, BOOT_PROGRESS_NAME);
49975
+ return join39(stateDir, BOOT_PROGRESS_NAME);
49827
49976
  }
49828
49977
  function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
49829
49978
  if (!stateDir) {
@@ -49844,7 +49993,7 @@ function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
49844
49993
  step++;
49845
49994
  try {
49846
49995
  mkdirSync18(dirname16(path), { recursive: true, mode: 448 });
49847
- const temp = join40(dirname16(path), `.${basename11(path)}.${process.pid}.tmp`);
49996
+ const temp = join39(dirname16(path), `.${basename11(path)}.${process.pid}.tmp`);
49848
49997
  writeFileSync15(temp, `${JSON.stringify({ pid: process.pid, step, phase, at: new Date(now).toISOString() })}
49849
49998
  `, "utf8");
49850
49999
  renameSync10(temp, path);
@@ -49854,7 +50003,7 @@ function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
49854
50003
  return Object.assign(write, {
49855
50004
  done: () => {
49856
50005
  try {
49857
- rmSync10(path, { force: true });
50006
+ rmSync11(path, { force: true });
49858
50007
  } catch {
49859
50008
  }
49860
50009
  }
@@ -49961,8 +50110,8 @@ __export(index_exports, {
49961
50110
  unknownProjectPage: () => unknownProjectPage
49962
50111
  });
49963
50112
  import { createServer } from "node:http";
49964
- import { readFileSync as readFileSync28, existsSync as existsSync15 } from "node:fs";
49965
- import { dirname as dirname17, join as join41, resolve as resolve9, extname as extname4, normalize } from "node:path";
50113
+ import { readFileSync as readFileSync28, existsSync as existsSync14 } from "node:fs";
50114
+ import { dirname as dirname17, join as join40, resolve as resolve9, extname as extname4, normalize } from "node:path";
49966
50115
  function readServerAddressHolder(path) {
49967
50116
  try {
49968
50117
  const value = JSON.parse(readFileSync28(path, "utf8"));
@@ -50065,9 +50214,9 @@ function createShutdownSignalHandler(options) {
50065
50214
  }
50066
50215
  function serveStatic(distDir, req, res) {
50067
50216
  const rel = normalize((req.url ?? "/").split("?")[0]).replace(/^(\.\.[/\\])+/, "");
50068
- let file = join41(distDir, rel === "/" ? "index.html" : rel);
50069
- if (!file.startsWith(distDir)) file = join41(distDir, "index.html");
50070
- if (!existsSync15(file)) file = join41(distDir, "index.html");
50217
+ let file = join40(distDir, rel === "/" ? "index.html" : rel);
50218
+ if (!file.startsWith(distDir)) file = join40(distDir, "index.html");
50219
+ if (!existsSync14(file)) file = join40(distDir, "index.html");
50071
50220
  try {
50072
50221
  const body = readFileSync28(file);
50073
50222
  res.writeHead(200, { "content-type": MIME[extname4(file)] ?? "application/octet-stream" });
@@ -50418,7 +50567,7 @@ async function startServer(opts = {}) {
50418
50567
  }
50419
50568
  statusPath = serverLockPathFor(ctx.project);
50420
50569
  const webRoot = resolve9(import.meta.dirname, "..", "..", "web");
50421
- const distDir = opts.webDistDir ? resolve9(opts.webDistDir) : join41(webRoot, "dist");
50570
+ const distDir = opts.webDistDir ? resolve9(opts.webDistDir) : join40(webRoot, "dist");
50422
50571
  startupPhase = "Vite";
50423
50572
  if (opts.dev) {
50424
50573
  try {
@@ -50494,7 +50643,7 @@ async function startServer(opts = {}) {
50494
50643
  if (vite) {
50495
50644
  vite.middlewares(req, res, () => {
50496
50645
  try {
50497
- const html = readFileSync28(join41(webRoot, "index.html"), "utf8");
50646
+ const html = readFileSync28(join40(webRoot, "index.html"), "utf8");
50498
50647
  void vite.transformIndexHtml(url, html).then((out) => {
50499
50648
  res.writeHead(200, { "content-type": "text/html" });
50500
50649
  res.end(out);
@@ -50506,7 +50655,7 @@ async function startServer(opts = {}) {
50506
50655
  });
50507
50656
  return;
50508
50657
  }
50509
- if (existsSync15(distDir)) {
50658
+ if (existsSync14(distDir)) {
50510
50659
  serveStatic(distDir, req, res);
50511
50660
  return;
50512
50661
  }
@@ -50686,7 +50835,7 @@ var init_index = __esm({
50686
50835
  }
50687
50836
  };
50688
50837
  isApiUrl = (url) => url === FRIZZ_ROUTE_PREFIX || url.startsWith(`${FRIZZ_ROUTE_PREFIX}/`);
50689
- serverLockPathFor = (project) => join41(project.stateDir, "server.lock");
50838
+ serverLockPathFor = (project) => join40(project.stateDir, "server.lock");
50690
50839
  MIME = {
50691
50840
  ".html": "text/html",
50692
50841
  ".js": "text/javascript",
@@ -50704,8 +50853,8 @@ init_project();
50704
50853
  init_project_launch();
50705
50854
  init_shutdown();
50706
50855
  init_logging();
50707
- import { existsSync as existsSync16 } from "node:fs";
50708
- import { join as join42 } from "node:path";
50856
+ import { existsSync as existsSync15 } from "node:fs";
50857
+ import { join as join41 } from "node:path";
50709
50858
  process.on("uncaughtException", (error) => {
50710
50859
  log.error("dev-child", `uncaught exception: ${error instanceof Error ? error.stack ?? error.message : error}`);
50711
50860
  process.exit(1);
@@ -50743,9 +50892,9 @@ try {
50743
50892
  ["FRIZZ_SCRIPTS_DIR", process.env.FRIZZ_SCRIPTS_DIR, "index.mjs"],
50744
50893
  ["FRIZZ_WORKER_PLUGIN_DIR", process.env.FRIZZ_WORKER_PLUGIN_DIR, ".claude-plugin/plugin.json"]
50745
50894
  ];
50746
- if (!existsSync16(stableWebDist)) throw new Error("stable artifact launch is missing its verified web directory");
50895
+ if (!existsSync15(stableWebDist)) throw new Error("stable artifact launch is missing its verified web directory");
50747
50896
  for (const [name, directory, requiredFile] of required) {
50748
- if (!directory || !existsSync16(join42(directory, requiredFile)))
50897
+ if (!directory || !existsSync15(join41(directory, requiredFile)))
50749
50898
  throw new Error(`stable artifact launch is missing verified ${name}`);
50750
50899
  }
50751
50900
  }