frizz 0.12.10 → 0.12.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/claude-agent-broker.js +56 -17
  2. package/dist/codex-app-server-daemon.js +49 -10
  3. package/dist/dev-child.js +630 -286
  4. package/dist/frizz.js +118 -21
  5. package/package.json +1 -1
  6. package/runtime/cc-worker/bin/frizz-mcp.mjs +58 -5
  7. package/web-dist/assets/{TerminalPane-BLyRh8S1.js → TerminalPane-DHvHCziB.js} +1 -1
  8. package/web-dist/assets/{abnfDiagram-VRR7QNED-B0n1m57h.js → abnfDiagram-VRR7QNED-DIzM_IN8.js} +1 -1
  9. package/web-dist/assets/architecture-TIHT7OUA-DBvKOf0j.js +1 -0
  10. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-CW78Jo4Q.js → architectureDiagram-ZJ3FMSHR-Bm_8vd5v.js} +1 -1
  11. package/web-dist/assets/{blockDiagram-677ZJIJ3-DasDnytT.js → blockDiagram-677ZJIJ3-BXUW_N2o.js} +1 -1
  12. package/web-dist/assets/{c4Diagram-LMCZKHZV-B7cCJxqg.js → c4Diagram-LMCZKHZV-C7rfqxUO.js} +1 -1
  13. package/web-dist/assets/channel-CbdXYswc.js +1 -0
  14. package/web-dist/assets/{chunk-32BRIVSS-BBBSn1aq.js → chunk-32BRIVSS-DbfUERLa.js} +1 -1
  15. package/web-dist/assets/{chunk-52WLFC77-B5lATDKz.js → chunk-52WLFC77-BT_3wKSP.js} +1 -1
  16. package/web-dist/assets/{chunk-C7G6YPKG-B86e9JiF.js → chunk-C7G6YPKG-JqApnOVt.js} +1 -1
  17. package/web-dist/assets/{chunk-EX3LRPZG-CpKL4dGX.js → chunk-EX3LRPZG-Cj6hlA4t.js} +1 -1
  18. package/web-dist/assets/{chunk-FWX5IMBZ-Dp3AvQlT.js → chunk-FWX5IMBZ-BGthhuW_.js} +2 -2
  19. package/web-dist/assets/{chunk-HOUHSVGY-i9yVPrLo.js → chunk-HOUHSVGY-uyLpm_YS.js} +1 -1
  20. package/web-dist/assets/{chunk-ICXQ74PX--Y7uMkOf.js → chunk-ICXQ74PX-BlwUMZAk.js} +1 -1
  21. package/web-dist/assets/{chunk-MOJQB5TN-DyMHpsmW.js → chunk-MOJQB5TN-MXbNNk-P.js} +1 -1
  22. package/web-dist/assets/{chunk-OGEWGWER-1mtDKLBA.js → chunk-OGEWGWER-Da4My1Rf.js} +1 -1
  23. package/web-dist/assets/{chunk-PUDLZKDR-CFgSrqyc.js → chunk-PUDLZKDR-C-PgvO__.js} +1 -1
  24. package/web-dist/assets/{chunk-Q4XR5HBZ-BPhNENQ8.js → chunk-Q4XR5HBZ-BxZFe7gr.js} +1 -1
  25. package/web-dist/assets/{chunk-V7JOEXUC-CMrXD-em.js → chunk-V7JOEXUC-DgERuXm_.js} +1 -1
  26. package/web-dist/assets/{chunk-VAUOI2AC-B6lbggln.js → chunk-VAUOI2AC-T5OoQE-_.js} +1 -1
  27. package/web-dist/assets/{chunk-VR4S4FIN-B9qypAjb.js → chunk-VR4S4FIN--HFvJUG1.js} +1 -1
  28. package/web-dist/assets/{chunk-WYO6CB5R-CI9tkIhS.js → chunk-WYO6CB5R-DlAvy41i.js} +1 -1
  29. package/web-dist/assets/{chunk-ZGVPDNZ5-DFJPi6M6.js → chunk-ZGVPDNZ5-BdDg7X_H.js} +1 -1
  30. package/web-dist/assets/classDiagram-OUVF2IWQ-BVXwECjS.js +1 -0
  31. package/web-dist/assets/classDiagram-v2-EOCWNBFH-BVXwECjS.js +1 -0
  32. package/web-dist/assets/{cynefin-VYW2F7L2-g28vb2Hf.js → cynefin-VYW2F7L2-BhjNO28R.js} +1 -1
  33. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-bBtOt0pp.js → cynefinDiagram-TSTJHNR4-B6VA5gCd.js} +1 -1
  34. package/web-dist/assets/{dagre-VKFMJZFB-DuIcY2-t.js → dagre-VKFMJZFB-6MXoEc6K.js} +1 -1
  35. package/web-dist/assets/{diagram-FQU43EPY-CsYoD3ve.js → diagram-FQU43EPY-D2RSPoK4.js} +1 -1
  36. package/web-dist/assets/{diagram-G47NLZAW-H3NxrIKS.js → diagram-G47NLZAW-BOSbtzOf.js} +1 -1
  37. package/web-dist/assets/{diagram-NH7WQ7WH-DIojGMfZ.js → diagram-NH7WQ7WH-Dt_u21nP.js} +1 -1
  38. package/web-dist/assets/{diagram-OA4YK3LP-D0L444Xe.js → diagram-OA4YK3LP-CjuWW-7r.js} +1 -1
  39. package/web-dist/assets/{diagram-WEI45ONY-C3PEfMxp.js → diagram-WEI45ONY-rANIKJ8N.js} +1 -1
  40. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-Dknugppg.js → ebnfDiagram-CCIWWBDH-Bf3kfaKZ.js} +1 -1
  41. package/web-dist/assets/{erDiagram-Q63AITRT-Ds5u_SEw.js → erDiagram-Q63AITRT-6vBp0ITn.js} +1 -1
  42. package/web-dist/assets/eventmodeling-45OFAUF4-STTT-geQ.js +1 -0
  43. package/web-dist/assets/flowDiagram-23GEKE2U-CC7DF1Fz.js +1 -0
  44. package/web-dist/assets/{ganttDiagram-NO4QXBWP-KFFdgLKc.js → ganttDiagram-NO4QXBWP-C2qN2PMS.js} +1 -1
  45. package/web-dist/assets/{gitGraph-TEB2WS4Q-B62DXBoO.js → gitGraph-TEB2WS4Q-CRASqc9n.js} +1 -1
  46. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-BPtTY3Ld.js → gitGraphDiagram-IHSO6WYX-gsXSPrH2.js} +1 -1
  47. package/web-dist/assets/{index-B-zUKqGI.js → index-DimQCLe8.js} +93 -93
  48. package/web-dist/assets/index-XhfdAXcw.css +1 -0
  49. package/web-dist/assets/{info-DKCQHKI2-Ct-TkGHr.js → info-DKCQHKI2-rDkFlFOd.js} +1 -1
  50. package/web-dist/assets/{infoDiagram-FWYZ7A6U-j0jFbDdJ.js → infoDiagram-FWYZ7A6U-03ql8Tqr.js} +1 -1
  51. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-DgfNQn9k.js → ishikawaDiagram-FXEZZL3T-CfDSAVNp.js} +1 -1
  52. package/web-dist/assets/{journeyDiagram-5HDEW3XC-BfykOv3I.js → journeyDiagram-5HDEW3XC-RWaA6AvH.js} +1 -1
  53. package/web-dist/assets/{kanban-definition-HUTT4EX6-BvhHHrMj.js → kanban-definition-HUTT4EX6-C89qresM.js} +1 -1
  54. package/web-dist/assets/{line-DZ-APW6S.js → line-CZtSsvHu.js} +1 -1
  55. package/web-dist/assets/{mermaid-parser.core-BWisef3-.js → mermaid-parser.core-DKNnvxt0.js} +3 -3
  56. package/web-dist/assets/{mermaid.core-DnlDGPhM.js → mermaid.core-DX8cFeBF.js} +3 -3
  57. package/web-dist/assets/{mindmap-definition-LN4V7U3C-BoTGWSEy.js → mindmap-definition-LN4V7U3C-DW8ClyAX.js} +1 -1
  58. package/web-dist/assets/{packet-7NZHBO7P-DaifVkPE.js → packet-7NZHBO7P-CUOv3apI.js} +1 -1
  59. package/web-dist/assets/{pegDiagram-2B236MQR-DpLfBJwm.js → pegDiagram-2B236MQR-CFyxKEBL.js} +1 -1
  60. package/web-dist/assets/{pie-RZYD4A2V-BvB0Jxcm.js → pie-RZYD4A2V-DyXXRie2.js} +1 -1
  61. package/web-dist/assets/{pieDiagram-ENE6RG2P-CSdBcMSK.js → pieDiagram-ENE6RG2P-yUw7HA8l.js} +1 -1
  62. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-i7k-hoMS.js → quadrantDiagram-ABIIQ3AL-Mc7aea_i.js} +1 -1
  63. package/web-dist/assets/{radar-I7S5WNFK-DpnvOCzG.js → radar-I7S5WNFK-BKj0BsQy.js} +1 -1
  64. package/web-dist/assets/{railroad-3IZDKUUU-Bj4Lhzob.js → railroad-3IZDKUUU-DUK_kJIY.js} +1 -1
  65. package/web-dist/assets/railroad-abnf-AHOZXSZD-BDImJcFw.js +1 -0
  66. package/web-dist/assets/railroad-ebnf-EBAXGLYW-BR2XIYqq.js +1 -0
  67. package/web-dist/assets/railroad-peg-LSFZ7HO6-DRCwBbJY.js +1 -0
  68. package/web-dist/assets/{railroadDiagram-RFXS5EU6-3HJeC9YI.js → railroadDiagram-RFXS5EU6-By0Xgb_U.js} +1 -1
  69. package/web-dist/assets/{requirementDiagram-TGXJPOKE-CeBCsZVw.js → requirementDiagram-TGXJPOKE-CgTycsRY.js} +1 -1
  70. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-BPhonqDa.js → sankeyDiagram-HTMAVEWB-mJ_-Grkn.js} +1 -1
  71. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-C5nocNwQ.js → sequenceDiagram-DBY2YBRQ-Dn6YyeNv.js} +1 -1
  72. package/web-dist/assets/{stateDiagram-2N3HPSRC-CzAZsCPb.js → stateDiagram-2N3HPSRC-BBws1E-e.js} +1 -1
  73. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-B2jOFVGG.js +1 -0
  74. package/web-dist/assets/{swimlanes-5IMT3BWC-DRn8Vs4e.js → swimlanes-5IMT3BWC-Jab-07o9.js} +1 -1
  75. package/web-dist/assets/swimlanesDiagram-G3AALYLV-DhYK_6fH.js +8 -0
  76. package/web-dist/assets/{timeline-definition-FHXFAJF6-Bc99eShR.js → timeline-definition-FHXFAJF6-Cq6RznHz.js} +1 -1
  77. package/web-dist/assets/{treeView-QDETBFTQ--Hn_52w1.js → treeView-QDETBFTQ-KSelJnyL.js} +1 -1
  78. package/web-dist/assets/{treemap-6X3UGDF4-BCI_8eXP.js → treemap-6X3UGDF4-B-iiUurZ.js} +1 -1
  79. package/web-dist/assets/{vennDiagram-L72KCM5P-BR-5GJF6.js → vennDiagram-L72KCM5P-B8t4NeQy.js} +1 -1
  80. package/web-dist/assets/{wardley-OPB4EBWU-4HuUekM-.js → wardley-OPB4EBWU-CtkAPZLQ.js} +1 -1
  81. package/web-dist/assets/{wardleyDiagram-EHGQE667-BI9w8zpX.js → wardleyDiagram-EHGQE667-DC-ms_sD.js} +1 -1
  82. package/web-dist/assets/{xychartDiagram-FW5EYKEG-BZ72575j.js → xychartDiagram-FW5EYKEG-DW6fcgjF.js} +1 -1
  83. package/web-dist/index.html +2 -2
  84. package/web-dist/assets/architecture-TIHT7OUA-BOgxi8qb.js +0 -1
  85. package/web-dist/assets/channel-cwFXt8Vr.js +0 -1
  86. package/web-dist/assets/classDiagram-OUVF2IWQ-BLP_ClYo.js +0 -1
  87. package/web-dist/assets/classDiagram-v2-EOCWNBFH-BLP_ClYo.js +0 -1
  88. package/web-dist/assets/eventmodeling-45OFAUF4-DgF-cIkx.js +0 -1
  89. package/web-dist/assets/flowDiagram-23GEKE2U-CB3C8FCr.js +0 -1
  90. package/web-dist/assets/index-BIGkwpMf.css +0 -1
  91. package/web-dist/assets/railroad-abnf-AHOZXSZD-scsBBZv_.js +0 -1
  92. package/web-dist/assets/railroad-ebnf-EBAXGLYW-z2-UIe31.js +0 -1
  93. package/web-dist/assets/railroad-peg-LSFZ7HO6-Dm1rBPEF.js +0 -1
  94. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-DEGF_nZS.js +0 -1
  95. package/web-dist/assets/swimlanesDiagram-G3AALYLV-CBLKdauI.js +0 -8
package/dist/dev-child.js CHANGED
@@ -13679,7 +13679,7 @@ function parseGithubWakeSteer(text2) {
13679
13679
  function frizzRoute(path3) {
13680
13680
  return `${FRIZZ_ROUTE_PREFIX}${path3.startsWith("/") ? path3 : `/${path3}`}`;
13681
13681
  }
13682
- var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, 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, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, 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, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
13682
+ var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, 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, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, 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, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
13683
13683
  var init_src = __esm({
13684
13684
  "packages/shared/src/index.ts"() {
13685
13685
  "use strict";
@@ -13803,7 +13803,14 @@ var init_src = __esm({
13803
13803
  slug: external_exports.string(),
13804
13804
  displayName: external_exports.string(),
13805
13805
  defaultEffort: external_exports.string(),
13806
- efforts: external_exports.array(external_exports.string())
13806
+ efforts: external_exports.array(external_exports.string()),
13807
+ // The model's stock context window and the largest one it accepts, in tokens, straight from the
13808
+ // cache's `context_window` / `max_context_window` (272K / 872K on GPT-5.6, 128K / 128K on Spark).
13809
+ // The Settings "Context window" presets are built from these so the drawer names the numbers codex
13810
+ // will actually run at (maintainer 2026-09-11: "reflect the actual numbers"). Optional: a cache from
13811
+ // before the fields existed, or the degraded fallback, simply carries neither.
13812
+ contextWindow: external_exports.number().int().positive().optional(),
13813
+ maxContextWindow: external_exports.number().int().positive().optional()
13807
13814
  });
13808
13815
  ThreadProfileOption = external_exports.object({
13809
13816
  model: external_exports.string().min(1),
@@ -14444,6 +14451,20 @@ var init_src = __esm({
14444
14451
  retrying: external_exports.boolean().optional(),
14445
14452
  at: external_exports.string().optional()
14446
14453
  });
14454
+ ThreadLinkView = external_exports.object({
14455
+ id: external_exports.string(),
14456
+ kind: external_exports.enum(["link", "file"]),
14457
+ label: external_exports.string(),
14458
+ target: external_exports.string()
14459
+ }).strict();
14460
+ UpsertOwnLinkInput = external_exports.object({
14461
+ slug: ThreadSlug,
14462
+ label: external_exports.string().trim().min(1).max(240).regex(/^[^\x00-\x1f\x7f]+$/),
14463
+ target: external_exports.string().trim().min(1).max(8192).regex(/^[^\x00-\x1f\x7f]+$/)
14464
+ }).strict();
14465
+ UpsertOwnLinkResult = external_exports.object({ link: ThreadLinkView }).strict();
14466
+ DropOwnLinkInput = external_exports.object({ slug: ThreadSlug, id: external_exports.string().min(1) }).strict();
14467
+ DropOwnLinkResult = external_exports.object({ dropped: external_exports.boolean() }).strict();
14447
14468
  ThreadView = external_exports.object({
14448
14469
  id: ThreadSlug,
14449
14470
  // slug; filename is <slug>.md
@@ -14512,6 +14533,8 @@ var init_src = __esm({
14512
14533
  // Live background SHELLS the worker launched (tailer-derived). Same default-[] discipline. Rendered
14513
14534
  // in the anchored background-ops strip alongside sub-agents; ids make current rows drillable.
14514
14535
  bgShells: external_exports.array(BgShellView).default([]),
14536
+ // Optional for snapshots from a server that predates link registration.
14537
+ links: external_exports.array(ThreadLinkView).optional(),
14515
14538
  // The thread's ARMED WATCHERS — registry-derived, not folded from the transcript, which is what makes
14516
14539
  // them survive the worker saying one more sentence. Same default-[] discipline as the two above.
14517
14540
  //
@@ -14830,6 +14853,15 @@ var init_src = __esm({
14830
14853
  // writes were 51% of a day's spend while the entries were invalidated every 15–30 minutes anyway
14831
14854
  // (see claudePromptCacheEnv). Optional so an old blob parses; defaultSettings pins "auto".
14832
14855
  promptCacheTtl: external_exports.enum(["auto", "5m", "1h"]).optional(),
14856
+ // The context window a NEW Codex thread runs with, in tokens. Codex's own default is the model's
14857
+ // stock window (272K on GPT-5.6), but the catalogue also carries a larger `max_context_window` (872K
14858
+ // on GPT-5.6-sol/terra/luna and GPT-6-astra, measured 2026-09-11 in ~/.codex/models_cache.json) that
14859
+ // a thread only reaches when asked. Reaches codex as the `model_context_window` config override on
14860
+ // `thread/start` and every cold `thread/resume` (see codexContextWindowConfig); codex clamps it to
14861
+ // the model's maximum, so a value above it is harmless, and it moves the auto-compact threshold with
14862
+ // it (codex compacts at 90% of the resolved window). Unset ⇒ nothing is sent and the model's stock
14863
+ // window applies. Optional so an old blob parses; defaultSettings leaves it unset.
14864
+ codexContextWindow: external_exports.number().int().positive().optional(),
14833
14865
  // The GitHub batch-dispatch prompt template (the picker's per-item worker prompt). Optional: when
14834
14866
  // unset OR blank the server falls back to its exported DEFAULT_GITHUB_PROMPT. Substitution tokens
14835
14867
  // the server fills: {repo} {n} {title} {url} {labels} {body}. The leading `THREAD: <slug>` tag is
@@ -15049,6 +15081,7 @@ var init_src = __esm({
15049
15081
  }).strict();
15050
15082
  OwnThreadActivityResult = external_exports.object({
15051
15083
  activity: external_exports.array(ThreadActivityItem),
15084
+ links: external_exports.array(ThreadLinkView).optional(),
15052
15085
  /** Every question still owed an answer. NOT a `ThreadActivityItem` and deliberately its own list: a
15053
15086
  * question is not running work, it waits on a PERSON, and there is no `questions:` key in the awaiting
15054
15087
  * fence for it to be written into. Until 2026-08-28 a worker could read its open questions back only
@@ -19385,6 +19418,20 @@ function createStorage(source, projectId) {
19385
19418
  `);
19386
19419
  const prWatchCursorStmt = scope.prepare("UPDATE pr_watch SET cursor = ? WHERE project_id = @project_id AND id = ? AND state = 'armed'");
19387
19420
  const delPrWatches = scope.prepare("DELETE FROM pr_watch WHERE project_id = @project_id AND thread_slug = ?");
19421
+ const upsertThreadLinkStmt = scope.prepare(`
19422
+ INSERT INTO thread_link (project_id, id, thread_slug, kind, label, target, created_at)
19423
+ VALUES (@project_id, @id, @slug, @kind, @label, @target, @createdAtMs)
19424
+ ON CONFLICT (project_id, thread_slug, label) DO UPDATE SET kind = excluded.kind, target = excluded.target
19425
+ RETURNING *
19426
+ `);
19427
+ const threadLinksStmt = scope.prepare(
19428
+ "SELECT * FROM thread_link WHERE project_id = @project_id ORDER BY created_at, rowid"
19429
+ );
19430
+ const threadLinksBySlugStmt = scope.prepare(
19431
+ "SELECT * FROM thread_link WHERE project_id = @project_id AND thread_slug = ? ORDER BY created_at, rowid"
19432
+ );
19433
+ const dropThreadLinkStmt = scope.prepare("DELETE FROM thread_link WHERE project_id = @project_id AND thread_slug = ? AND id = ?");
19434
+ const delThreadLinks = scope.prepare("DELETE FROM thread_link WHERE project_id = @project_id AND thread_slug = ?");
19388
19435
  const armThreadWatchStmt = scope.prepare(`
19389
19436
  INSERT INTO thread_watch (project_id, id, thread_slug, kind, target, state, created_at, expires_at, settled_at)
19390
19437
  VALUES (@project_id, @id, @slug, @kind, @target, 'armed', @createdAtMs, @expiresAtMs, NULL)
@@ -19524,6 +19571,7 @@ function createStorage(source, projectId) {
19524
19571
  delThreadTimers.run(existing.slug);
19525
19572
  delPrWatches.run(existing.slug);
19526
19573
  delThreadWatches.run(existing.slug);
19574
+ delThreadLinks.run(existing.slug);
19527
19575
  delThreadQuestions.run(existing.slug);
19528
19576
  delThreadDone.run(existing.slug);
19529
19577
  delSubAgentSteers.run(existing.slug);
@@ -19975,6 +20023,10 @@ function createStorage(source, projectId) {
19975
20023
  dropPrWatch: (slug, id, settledAtMs) => dropPrWatchStmt.run(settledAtMs, id, slug).changes === 1,
19976
20024
  settlePrWatch: (id, settledAtMs) => settlePrWatchStmt.run(settledAtMs, id).changes === 1,
19977
20025
  setPrWatchCursor: (id, cursor) => prWatchCursorStmt.run(cursor, id).changes === 1,
20026
+ upsertThreadLink: (link) => upsertThreadLinkStmt.get(link),
20027
+ listThreadLinks: (slug) => threadLinksBySlugStmt.all(slug),
20028
+ threadLinksBySlug: () => groupBySlug(threadLinksStmt.all()),
20029
+ dropThreadLink: (slug, id) => dropThreadLinkStmt.run(slug, id).changes === 1,
19978
20030
  // IDEMPOTENT BY (thread, kind, target), which is what the partial unique index enforces. A worker
19979
20031
  // woken by an expiry re-registers the same wait, and a worker that simply calls twice must not end
19980
20032
  // up with two rows to drop — so an existing armed row is RETURNED rather than replaced. Replacing
@@ -20436,6 +20488,17 @@ var init_storage = __esm({
20436
20488
  ON pr_watch(project_id, state);
20437
20489
  CREATE INDEX IF NOT EXISTS pr_watch_slug
20438
20490
  ON pr_watch(project_id, thread_slug, state, created_at);
20491
+ -- Saved destinations have no liveness or expiry. The label is a stable slot within one thread.
20492
+ CREATE TABLE IF NOT EXISTS thread_link (
20493
+ id TEXT PRIMARY KEY,
20494
+ project_id TEXT NOT NULL,
20495
+ thread_slug TEXT NOT NULL,
20496
+ kind TEXT NOT NULL CHECK (kind IN ('link', 'file')),
20497
+ label TEXT NOT NULL,
20498
+ target TEXT NOT NULL,
20499
+ created_at INTEGER NOT NULL,
20500
+ UNIQUE (project_id, thread_slug, label)
20501
+ );
20439
20502
  -- A worker's registered WATCHES on its own running work (2026-08-26). See
20440
20503
  -- plans/rest-by-registration.md: a wait stops being a line the worker re-writes at every rest and
20441
20504
  -- becomes a row it creates once, which the human sees in the queue and which wakes the thread itself.
@@ -20549,7 +20612,8 @@ var init_storage = __esm({
20549
20612
  "thread_watch",
20550
20613
  "thread_question",
20551
20614
  "thread_done",
20552
- "subagent_steer"
20615
+ "subagent_steer",
20616
+ "thread_link"
20553
20617
  ];
20554
20618
  }
20555
20619
  });
@@ -20694,6 +20758,9 @@ var init_settings = __esm({
20694
20758
  // "auto" = the CLI's own tier (1h on a subscription). See the schema and claudePromptCacheEnv for
20695
20759
  // when "5m" is the cheaper choice.
20696
20760
  promptCacheTtl: "auto",
20761
+ // Unset = codex's own window for the model (272K on GPT-5.6). The schema and codexContextWindowConfig
20762
+ // explain the 872K ceiling a larger value is clamped to.
20763
+ codexContextWindow: void 0,
20697
20764
  // Hidden until asked for — see the schema for why.
20698
20765
  projectRail: false
20699
20766
  });
@@ -21350,7 +21417,23 @@ function toCodexModel(raw2) {
21350
21417
  const def = typeof m.default_reasoning_level === "string" ? m.default_reasoning_level : "";
21351
21418
  const defaultEffort = def && efforts.includes(def) ? def : efforts[0];
21352
21419
  const priority = typeof m.priority === "number" && Number.isFinite(m.priority) ? m.priority : Number.MAX_SAFE_INTEGER;
21353
- return { model: { slug, displayName, defaultEffort, efforts }, priority };
21420
+ const window = (key) => {
21421
+ const v = m[key];
21422
+ return typeof v === "number" && Number.isInteger(v) && v > 0 ? v : void 0;
21423
+ };
21424
+ const contextWindow = window("context_window");
21425
+ const maxContextWindow = window("max_context_window");
21426
+ return {
21427
+ model: {
21428
+ slug,
21429
+ displayName,
21430
+ defaultEffort,
21431
+ efforts,
21432
+ ...contextWindow === void 0 ? {} : { contextWindow },
21433
+ ...maxContextWindow === void 0 ? {} : { maxContextWindow }
21434
+ },
21435
+ priority
21436
+ };
21354
21437
  }
21355
21438
  function parseCodexModelsCache(raw2) {
21356
21439
  let doc;
@@ -22260,6 +22343,7 @@ function forkBroker(options) {
22260
22343
  const socketPath = claudeBrokerSocketPath(options.stateDir, options.sessionId);
22261
22344
  const recordPath2 = claudeBrokerRecordPath(options.stateDir, options.sessionId);
22262
22345
  mkdirSync9(dirname8(recordPath2), { recursive: true });
22346
+ const generation = randomUUID8();
22263
22347
  const config = {
22264
22348
  socketPath,
22265
22349
  cwd: options.cwd,
@@ -22268,7 +22352,7 @@ function forkBroker(options) {
22268
22352
  permissionMode: options.permissionMode,
22269
22353
  env: options.env,
22270
22354
  recordPath: recordPath2,
22271
- generation: randomUUID8(),
22355
+ generation,
22272
22356
  // The daemon writes its own death forensics here. Same dir as the record, so a session's socket,
22273
22357
  // record and diagnostics stay together and a project teardown removes all three.
22274
22358
  diagnosticLogPath: claudeBrokerDiagnosticLogPath(options.stateDir, options.sessionId),
@@ -22290,14 +22374,45 @@ function forkBroker(options) {
22290
22374
  });
22291
22375
  child.unref();
22292
22376
  const deadline = Date.now() + (options.timeoutMs ?? 3e4);
22377
+ const recordedCause = () => readClaudeBrokerExit(options.stateDir, options.sessionId, generation);
22293
22378
  return new Promise((resolve12, reject) => {
22379
+ let settled = false;
22380
+ let timer;
22381
+ const settle = (outcome) => {
22382
+ if (settled) return;
22383
+ settled = true;
22384
+ if (timer) clearTimeout(timer);
22385
+ outcome();
22386
+ };
22294
22387
  const poll = () => {
22295
22388
  const record = readBrokerRecord(recordPath2);
22296
- if (record && pidAlive(record.daemonPid)) return resolve12(record);
22297
- if (Date.now() > deadline) return reject(new Error(`Claude broker for session ${options.sessionId} did not become ready`));
22298
- setTimeout(poll, 50);
22389
+ if (record && pidAlive(record.daemonPid)) return settle(() => resolve12(record));
22390
+ if (Date.now() > deadline) {
22391
+ const exit = recordedCause();
22392
+ return settle(() => reject(new Error(
22393
+ `Claude broker for session ${options.sessionId} did not become ready${exit ? `: ${describeClaudeBrokerExit(exit)}` : ""}`
22394
+ )));
22395
+ }
22396
+ timer = setTimeout(poll, 50);
22299
22397
  };
22300
- child.once("error", reject);
22398
+ child.once("error", (error) => settle(() => reject(error)));
22399
+ child.once("exit", (code, signal) => {
22400
+ if (settled) return;
22401
+ const how = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
22402
+ const until = Date.now() + 500;
22403
+ const read = () => {
22404
+ if (settled) return;
22405
+ const exit = recordedCause();
22406
+ if (!exit && Date.now() < until) {
22407
+ setTimeout(read, 25);
22408
+ return;
22409
+ }
22410
+ settle(() => reject(new Error(
22411
+ `Claude broker for session ${options.sessionId} exited before it became ready (${how}): ${describeClaudeBrokerExit(exit)}`
22412
+ )));
22413
+ };
22414
+ read();
22415
+ });
22301
22416
  poll();
22302
22417
  });
22303
22418
  }
@@ -25979,14 +26094,15 @@ function parseToolArguments(args) {
25979
26094
  }
25980
26095
  function imageField(output) {
25981
26096
  if (!Array.isArray(output)) return {};
26097
+ const images = [];
25982
26098
  for (const part of output) {
25983
26099
  if (!part || typeof part !== "object") continue;
25984
26100
  const p = part;
25985
26101
  if (p.type !== "input_image" && p.type !== "output_image" && p.type !== "image") continue;
25986
26102
  const url = typeof p.image_url === "string" ? p.image_url : typeof p.url === "string" ? p.url : void 0;
25987
- if (url?.startsWith("data:image/")) return { image: url };
26103
+ if (url?.startsWith("data:image/")) images.push(url);
25988
26104
  }
25989
- return {};
26105
+ return images.length ? { image: images[0], images } : {};
25990
26106
  }
25991
26107
  function codexItemText(content) {
25992
26108
  if (!Array.isArray(content)) return "";
@@ -29996,6 +30112,11 @@ function claudeCompactionEnv(settings) {
29996
30112
  if (window === void 0 || !Number.isInteger(window) || window <= 0) return {};
29997
30113
  return { [CLAUDE_AUTO_COMPACT_WINDOW_ENV]: String(window) };
29998
30114
  }
30115
+ function codexContextWindowConfig(settings) {
30116
+ const window = settings?.codexContextWindow;
30117
+ if (window === void 0 || !Number.isInteger(window) || window <= 0) return {};
30118
+ return { [CODEX_CONTEXT_WINDOW_KEY]: window };
30119
+ }
29999
30120
  function claudePromptCacheEnv(settings) {
30000
30121
  const ttl = settings?.promptCacheTtl;
30001
30122
  if (ttl !== "5m" && ttl !== "1h") return {};
@@ -30007,7 +30128,7 @@ function claudeCompactionWindowOf(env) {
30007
30128
  const window = Number(raw2);
30008
30129
  return Number.isInteger(window) && window > 0 ? window : void 0;
30009
30130
  }
30010
- var FRIZZ_MCP, WORKER_MAX_WEB_SEARCHES, WORKER_MAX_SUBAGENTS, WORKER_MAX_CONCURRENT_SUBAGENTS, CLAUDE_WORKER_ENV, CLAUDE_AUTO_COMPACT_WINDOW_ENV, CLAUDE_PROMPT_CACHE_TTL_ENV, CLAUDE_SUBAGENT_PROMPT_CACHE_TTL_ENV, WORKER_DISALLOWED_TOOLS;
30131
+ var FRIZZ_MCP, WORKER_MAX_WEB_SEARCHES, WORKER_MAX_SUBAGENTS, WORKER_MAX_CONCURRENT_SUBAGENTS, CLAUDE_WORKER_ENV, CLAUDE_AUTO_COMPACT_WINDOW_ENV, CODEX_CONTEXT_WINDOW_KEY, CLAUDE_PROMPT_CACHE_TTL_ENV, CLAUDE_SUBAGENT_PROMPT_CACHE_TTL_ENV, WORKER_DISALLOWED_TOOLS;
30011
30132
  var init_types2 = __esm({
30012
30133
  "packages/server/src/backend/types.ts"() {
30013
30134
  "use strict";
@@ -30025,6 +30146,7 @@ var init_types2 = __esm({
30025
30146
  BASH_MAX_TIMEOUT_MS: String(24 * 60 * 60 * 1e3)
30026
30147
  };
30027
30148
  CLAUDE_AUTO_COMPACT_WINDOW_ENV = "CLAUDE_CODE_AUTO_COMPACT_WINDOW";
30149
+ CODEX_CONTEXT_WINDOW_KEY = "model_context_window";
30028
30150
  CLAUDE_PROMPT_CACHE_TTL_ENV = "CLAUDE_CODE_PROMPT_CACHE_TTL";
30029
30151
  CLAUDE_SUBAGENT_PROMPT_CACHE_TTL_ENV = "CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL";
30030
30152
  WORKER_DISALLOWED_TOOLS = ["AskUserQuestion"];
@@ -31957,6 +32079,7 @@ var init_codex_app_server = __esm({
31957
32079
  init_auth_status();
31958
32080
  init_codex_mcp();
31959
32081
  init_codex_error();
32082
+ init_types2();
31960
32083
  init_logging();
31961
32084
  CODEX_APP_SERVER_PROVIDER = "codex-app-server";
31962
32085
  CODEX_NATIVE_LISTEN_FLAG = "FRIZZ_CODEX_NATIVE_LISTEN";
@@ -33624,19 +33747,29 @@ var init_codex_app_server = __esm({
33624
33747
  */
33625
33748
  /**
33626
33749
  * The `config` bag for one thread's `thread/start` / `thread/resume`: frizz's MCP server mounted
33627
- * PER THREAD so it knows who is calling, plus whatever the caller passed.
33750
+ * PER THREAD so it knows who is calling, the operator's Codex context window from Settings, plus
33751
+ * whatever the caller passed.
33628
33752
  *
33629
33753
  * This is the only channel that can carry a caller identity on codex. The argv mount on the
33630
33754
  * app-server is process-wide and serves every thread in the project, so `FRIZZ_THREAD_SLUG` was
33631
33755
  * simply absent and every tool that acts on the caller's own thread failed at the moment of use —
33632
33756
  * see `codexThreadMcpConfig` for the list and the measurement. Returns `{}` when there is nothing
33633
- * to send, so a caller with no config and no resolved MCP descriptor sends no `config` key at all,
33634
- * exactly as before.
33757
+ * to send, so a caller with no config, no resolved MCP descriptor and no context-window setting
33758
+ * sends no `config` key at all, exactly as before.
33759
+ *
33760
+ * The context window rides the same bag rather than the process-wide argv because the app-server is
33761
+ * a long-lived daemon: an argv value would be frozen at its first spawn, while a per-thread override
33762
+ * reads Settings at every start and every cold resume — the same "next thread, and a thread that
33763
+ * comes back" semantics the Claude compaction window has (see codexContextWindowConfig).
33635
33764
  *
33636
33765
  * The caller's own config wins on a key collision: this is a default, not an override.
33637
33766
  */
33638
33767
  threadConfig(threadSlug, callerConfig) {
33639
- const config = { ...codexThreadMcpConfig(this.options.frizzMcp, threadSlug), ...callerConfig };
33768
+ const config = {
33769
+ ...codexThreadMcpConfig(this.options.frizzMcp, threadSlug),
33770
+ ...codexContextWindowConfig(this.options.getSettings?.()),
33771
+ ...callerConfig
33772
+ };
33640
33773
  return Object.keys(config).length ? { config } : {};
33641
33774
  }
33642
33775
  resumeSandboxOverride(row) {
@@ -34681,22 +34814,140 @@ var init_quota = __esm({
34681
34814
  }
34682
34815
  });
34683
34816
 
34817
+ // packages/server/src/local-file.ts
34818
+ import { spawn as spawn5 } from "node:child_process";
34819
+ import { readFileSync as readFileSync17, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
34820
+ import { homedir as homedir11 } from "node:os";
34821
+ import { isAbsolute as isAbsolute4, join as join25, resolve as resolve5, sep as sep2 } from "node:path";
34822
+ function isUnder(real, root) {
34823
+ let rootReal;
34824
+ try {
34825
+ rootReal = realpathSync6(root);
34826
+ } catch {
34827
+ return false;
34828
+ }
34829
+ return real === rootReal || real.startsWith(rootReal.endsWith(sep2) ? rootReal : rootReal + sep2);
34830
+ }
34831
+ function resolveLocalFile(rawPath, roots) {
34832
+ if (!isAbsolute4(rawPath)) throw new Error("Local path must be absolute");
34833
+ let real;
34834
+ try {
34835
+ real = realpathSync6(rawPath);
34836
+ } catch {
34837
+ throw new Error("Local file was not found");
34838
+ }
34839
+ if (!roots.some((root) => isUnder(real, root))) throw new Error("Local file is outside Frizz's trusted roots");
34840
+ try {
34841
+ if (!statSync12(real).isFile()) throw new Error("Local path is not a regular file");
34842
+ } catch (error) {
34843
+ if (error instanceof Error && error.message === "Local path is not a regular file") throw error;
34844
+ throw new Error("Local file was not found");
34845
+ }
34846
+ return real;
34847
+ }
34848
+ function resolveOpenableFile(raw2, projectDir, roots, home = homedir11()) {
34849
+ const trimmed = raw2.trim().replace(/:\d+(?::\d+)?$/, "");
34850
+ if (!trimmed) return null;
34851
+ const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ? join25(home, trimmed.slice(2)) : isAbsolute4(trimmed) ? trimmed : resolve5(projectDir, trimmed);
34852
+ try {
34853
+ return resolveLocalFile(abs, roots);
34854
+ } catch {
34855
+ return null;
34856
+ }
34857
+ }
34858
+ function readLocalTextFile(rawPath, roots) {
34859
+ const path3 = resolveLocalFile(rawPath, roots);
34860
+ const bytes = readFileSync17(path3);
34861
+ if (bytes.subarray(0, 8192).includes(0)) throw new Error("Local file is not a text file");
34862
+ if (bytes.length <= MARKDOWN_READ_LIMIT) return { path: path3, text: bytes.toString("utf8"), truncated: false };
34863
+ const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
34864
+ const lastBreak = head.lastIndexOf(10);
34865
+ return { path: path3, text: head.subarray(0, lastBreak > 0 ? lastBreak : head.length).toString("utf8"), truncated: true };
34866
+ }
34867
+ function readLocalMarkdown(rawPath, roots) {
34868
+ if (!MARKDOWN_FILE_EXT.test(rawPath.trim())) throw new Error("Local file is not a Markdown file");
34869
+ const path3 = resolveLocalFile(rawPath, roots);
34870
+ if (!MARKDOWN_FILE_EXT.test(path3)) throw new Error("Local file is not a Markdown file");
34871
+ const bytes = readFileSync17(path3);
34872
+ if (bytes.length <= MARKDOWN_READ_LIMIT) return { path: path3, markdown: bytes.toString("utf8"), truncated: false };
34873
+ const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
34874
+ const lastBreak = head.lastIndexOf(10);
34875
+ return { path: path3, markdown: head.subarray(0, lastBreak > 0 ? lastBreak : head.length).toString("utf8"), truncated: true };
34876
+ }
34877
+ function resolveWatchableLocalFile(rawPath, roots) {
34878
+ const path3 = resolveLocalFile(rawPath, roots);
34879
+ if (MARKDOWN_FILE_EXT.test(rawPath.trim()) && !MARKDOWN_FILE_EXT.test(path3)) {
34880
+ throw new Error("Local file is not a Markdown file");
34881
+ }
34882
+ return path3;
34883
+ }
34884
+ function defaultSpawn(command, args, options) {
34885
+ return spawn5(command, [...args], options);
34886
+ }
34887
+ function openLocalFile(rawPath, opener, roots, options = {}) {
34888
+ const path3 = resolveLocalFile(rawPath, roots);
34889
+ const selected = options.forceSystem ? "system" : opener;
34890
+ if (selected === "copy") return { action: "copy", path: path3 };
34891
+ const spec = process.platform === "darwin" ? selected === "cursor" ? ["open", ["-a", "Cursor", path3]] : selected === "vscode" ? ["open", ["-a", "Visual Studio Code", path3]] : selected === "finder" ? ["open", ["-R", path3]] : ["open", [path3]] : selected === "finder" ? ["xdg-open", [path3]] : selected === "cursor" ? ["cursor", [path3]] : selected === "vscode" ? ["code", [path3]] : ["xdg-open", [path3]];
34892
+ (options.spawn ?? defaultSpawn)(spec[0], spec[1], { detached: true, stdio: "ignore", shell: false }).unref();
34893
+ return { action: "opened", path: path3 };
34894
+ }
34895
+ var MARKDOWN_FILE_EXT, MARKDOWN_READ_LIMIT;
34896
+ var init_local_file = __esm({
34897
+ "packages/server/src/local-file.ts"() {
34898
+ "use strict";
34899
+ MARKDOWN_FILE_EXT = /\.(?:md|mdx|markdown)$/i;
34900
+ MARKDOWN_READ_LIMIT = 1024 * 1024;
34901
+ }
34902
+ });
34903
+
34904
+ // packages/server/src/thread-links.ts
34905
+ import { isAbsolute as isAbsolute5 } from "node:path";
34906
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
34907
+ function threadLinkView(row) {
34908
+ return { id: row.id, kind: row.kind, label: row.label, target: row.target };
34909
+ }
34910
+ function resolveThreadLink(target, projectDir, roots) {
34911
+ if (/^https?:\/\//i.test(target)) {
34912
+ const url = new URL(target);
34913
+ if (url.username || url.password) throw new Error("Link URLs must not contain credentials");
34914
+ return { kind: "link", target: url.href };
34915
+ }
34916
+ let path3 = target;
34917
+ if (/^file:/i.test(target)) {
34918
+ const url = new URL(target);
34919
+ if (url.search || url.hash) throw new Error("File links must not contain a query or fragment");
34920
+ path3 = fileURLToPath2(url);
34921
+ } else if (!isAbsolute5(target) && /^[a-z][a-z\d+.-]*:/i.test(target)) {
34922
+ throw new Error("Register an HTTP(S) URL or a local file");
34923
+ }
34924
+ const resolved = resolveOpenableFile(path3, projectDir, roots);
34925
+ if (!resolved) throw new Error("Local file was not found or is outside Frizz's trusted roots");
34926
+ return { kind: "file", target: resolved };
34927
+ }
34928
+ var init_thread_links = __esm({
34929
+ "packages/server/src/thread-links.ts"() {
34930
+ "use strict";
34931
+ init_local_file();
34932
+ }
34933
+ });
34934
+
34684
34935
  // packages/server/src/frizz.ts
34685
34936
  import { execFile as execFile5 } from "node:child_process";
34686
- import { lstatSync as lstatSync2, realpathSync as realpathSync6 } from "node:fs";
34687
- import { basename as basename5, dirname as dirname9, join as join25, resolve as resolve5 } from "node:path";
34937
+ import { lstatSync as lstatSync2, realpathSync as realpathSync7 } from "node:fs";
34938
+ import { basename as basename5, dirname as dirname9, join as join26, resolve as resolve6 } from "node:path";
34688
34939
  import { promisify as promisify5 } from "node:util";
34689
34940
  function frizzScriptsDir() {
34690
34941
  if (process.env.FRIZZ_SCRIPTS_DIR) return process.env.FRIZZ_SCRIPTS_DIR;
34691
- return resolve5(import.meta.dirname, "..", "..", "..", "board");
34942
+ return resolve6(import.meta.dirname, "..", "..", "..", "board");
34692
34943
  }
34693
34944
  function directFrizzRoot(projectDir) {
34694
34945
  try {
34695
- const projectRoot = realpathSync6(projectDir);
34696
- const path3 = join25(projectRoot, ".frizz");
34946
+ const projectRoot = realpathSync7(projectDir);
34947
+ const path3 = join26(projectRoot, ".frizz");
34697
34948
  const stat2 = lstatSync2(path3);
34698
34949
  if (!stat2.isDirectory() || stat2.isSymbolicLink()) return null;
34699
- const real = realpathSync6(path3);
34950
+ const real = realpathSync7(path3);
34700
34951
  return dirname9(real) === projectRoot && basename5(real) === ".frizz" ? real : null;
34701
34952
  } catch {
34702
34953
  return null;
@@ -34707,7 +34958,7 @@ function frizzDirExists(projectDir) {
34707
34958
  }
34708
34959
  async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
34709
34960
  if (!directFrizzRoot(projectDir)) throw new Error("unsafe or missing .frizz directory");
34710
- const { stdout } = await execFileP("node", [join25(scriptsDir, "index.mjs"), "--json"], {
34961
+ const { stdout } = await execFileP("node", [join26(scriptsDir, "index.mjs"), "--json"], {
34711
34962
  cwd: projectDir,
34712
34963
  env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir },
34713
34964
  maxBuffer: 32 * 1024 * 1024
@@ -34732,7 +34983,7 @@ async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
34732
34983
  };
34733
34984
  }
34734
34985
  async function runThreadUpdate(projectDir, slug, args, scriptsDir = frizzScriptsDir()) {
34735
- await execFileP("node", [join25(scriptsDir, "thread-update.mjs"), slug, ...args], {
34986
+ await execFileP("node", [join26(scriptsDir, "thread-update.mjs"), slug, ...args], {
34736
34987
  cwd: projectDir,
34737
34988
  env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir }
34738
34989
  });
@@ -34817,12 +35068,12 @@ var init_awaiting = __esm({
34817
35068
  });
34818
35069
 
34819
35070
  // packages/server/src/backend/project-mcp-servers.ts
34820
- import { readFileSync as readFileSync17 } from "node:fs";
34821
- import { homedir as homedir11 } from "node:os";
34822
- import { join as join26 } from "node:path";
35071
+ import { readFileSync as readFileSync18 } from "node:fs";
35072
+ import { homedir as homedir12 } from "node:os";
35073
+ import { join as join27 } from "node:path";
34823
35074
  function readJsonObject(path3) {
34824
35075
  try {
34825
- const parsed = JSON.parse(readFileSync17(path3, "utf8"));
35076
+ const parsed = JSON.parse(readFileSync18(path3, "utf8"));
34826
35077
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
34827
35078
  } catch {
34828
35079
  return void 0;
@@ -34913,17 +35164,17 @@ function userScopeRemoteServers(account, cwd, env) {
34913
35164
  }
34914
35165
  function projectMcpServers(cwd, options = {}) {
34915
35166
  const env = options.env ?? process.env;
34916
- const home = options.home ?? homedir11();
35167
+ const home = options.home ?? homedir12();
34917
35168
  const configOverride = options.claudeConfigDir ?? (env.CLAUDE_CONFIG_DIR?.trim() ? env.CLAUDE_CONFIG_DIR.trim() : void 0);
34918
- const accountFile = configOverride ? join26(configOverride, ".claude.json") : join26(home, ".claude.json");
34919
- const userSettingsFile = join26(configOverride ?? join26(home, ".claude"), "settings.json");
35169
+ const accountFile = configOverride ? join27(configOverride, ".claude.json") : join27(home, ".claude.json");
35170
+ const userSettingsFile = join27(configOverride ?? join27(home, ".claude"), "settings.json");
34920
35171
  const account = readJsonObject(accountFile);
34921
35172
  const out = userScopeRemoteServers(account, cwd, env);
34922
- const declared = objectField(readJsonObject(join26(cwd, ".mcp.json")), "mcpServers");
35173
+ const declared = objectField(readJsonObject(join27(cwd, ".mcp.json")), "mcpServers");
34923
35174
  if (!declared) return out;
34924
35175
  const approval = collectApproval([
34925
- readJsonObject(join26(cwd, ".claude", "settings.json")),
34926
- readJsonObject(join26(cwd, ".claude", "settings.local.json")),
35176
+ readJsonObject(join27(cwd, ".claude", "settings.json")),
35177
+ readJsonObject(join27(cwd, ".claude", "settings.local.json")),
34927
35178
  readJsonObject(userSettingsFile),
34928
35179
  objectField(objectField(account, "projects"), cwd)
34929
35180
  ]);
@@ -34948,16 +35199,16 @@ var init_project_mcp_servers = __esm({
34948
35199
 
34949
35200
  // packages/server/src/worker-plugin-dir.ts
34950
35201
  import { existsSync as existsSync8 } from "node:fs";
34951
- import { dirname as dirname10, join as join27 } from "node:path";
34952
- import { fileURLToPath as fileURLToPath2 } from "node:url";
35202
+ import { dirname as dirname10, join as join28 } from "node:path";
35203
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
34953
35204
  function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
34954
35205
  const override = env.FRIZZ_WORKER_PLUGIN_DIR;
34955
- if (override && existsSync8(join27(override, ".claude-plugin", "plugin.json")))
35206
+ if (override && existsSync8(join28(override, ".claude-plugin", "plugin.json")))
34956
35207
  return override;
34957
- let current = dirname10(fileURLToPath2(moduleUrl));
35208
+ let current = dirname10(fileURLToPath3(moduleUrl));
34958
35209
  for (; ; ) {
34959
- const candidate = join27(current, "cc-worker");
34960
- if (existsSync8(join27(candidate, ".claude-plugin", "plugin.json"))) return candidate;
35210
+ const candidate = join28(current, "cc-worker");
35211
+ if (existsSync8(join28(candidate, ".claude-plugin", "plugin.json"))) return candidate;
34961
35212
  const parent = dirname10(current);
34962
35213
  if (parent === current) return void 0;
34963
35214
  current = parent;
@@ -34985,6 +35236,7 @@ function buildWorkerPrompt(kind = "claude", opts = {}) {
34985
35236
  lean ? null : THREAD_EXECUTION[kind],
34986
35237
  AGENT_COMPLETION,
34987
35238
  VISUAL_EVIDENCE,
35239
+ REGISTERED_LINKS,
34988
35240
  QUALITY_BAR,
34989
35241
  TOKEN_ECONOMY,
34990
35242
  QUESTIONS,
@@ -34996,7 +35248,7 @@ function buildWorkerPrompt(kind = "claude", opts = {}) {
34996
35248
  out = out.replaceAll("{{FRIZZ_MONITORS_DIR}}", opts.monitorsDir?.trim() || MONITORS_DIR_FALLBACK);
34997
35249
  return out;
34998
35250
  }
34999
- var INLINE, INTRO, DEFER, OPENING, ACTIVITY_CAPTIONS, SIGNALS, AGENT_COMPLETION, VISUAL_EVIDENCE, QUALITY_BAR, TOKEN_ECONOMY, QUESTIONS, STOP_CRITERION, TRIVIAL_PROMPTS, THREAD_NAME, SCRATCHPAD, BACKEND, SPAWN_THREAD, THREAD_EXECUTION, MONITORS_DIR_FALLBACK;
35251
+ var INLINE, INTRO, DEFER, OPENING, ACTIVITY_CAPTIONS, SIGNALS, AGENT_COMPLETION, VISUAL_EVIDENCE, REGISTERED_LINKS, QUALITY_BAR, TOKEN_ECONOMY, QUESTIONS, STOP_CRITERION, TRIVIAL_PROMPTS, THREAD_NAME, SCRATCHPAD, BACKEND, SPAWN_THREAD, THREAD_EXECUTION, MONITORS_DIR_FALLBACK;
35000
35252
  var init_workerPrompt = __esm({
35001
35253
  "packages/server/src/workerPrompt.ts"() {
35002
35254
  "use strict";
@@ -35288,6 +35540,12 @@ image paths through its guarded local-image proxy; only eligible workspace or ex
35288
35540
  image files can embed, and a path outside that safe boundary stays non-navigable. Do not bulk-embed
35289
35541
  irrelevant screenshots. Always keep a concise textual finding alongside them, so the handoff still
35290
35542
  reads when images are unavailable.`;
35543
+ REGISTERED_LINKS = `## Saved links and files
35544
+
35545
+ Use \`mcp__frizz__link\` with \`label\` and \`target\` for a dev server, working document, report or download
35546
+ the human should keep at hand. It stays underneath the prompt. Reuse the label to update its destination;
35547
+ read registrations with \`activity\` and remove one with \`unlink\`. Register existing files, using an
35548
+ absolute path outside the project root. A saved reference is not a running process or a completion gate.`;
35291
35549
  QUALITY_BAR = `## Quality bar
35292
35550
 
35293
35551
  - Verify behavior end-to-end before calling anything done. A green suite over a stubbed implementation
@@ -36112,12 +36370,12 @@ var init_claude_broker_client = __esm({
36112
36370
  });
36113
36371
 
36114
36372
  // packages/server/src/backend/claude-permission-interactions.ts
36115
- import { homedir as homedir12 } from "node:os";
36373
+ import { homedir as homedir13 } from "node:os";
36116
36374
  function clip(text2, max) {
36117
36375
  return text2.length > max ? `${text2.slice(0, max - 1)}\u2026` : text2;
36118
36376
  }
36119
36377
  function tildePath(path3) {
36120
- const home = homedir12();
36378
+ const home = homedir13();
36121
36379
  if (!path3.startsWith(home)) return path3;
36122
36380
  const rest = path3.slice(home.length);
36123
36381
  return rest === "" ? "~" : rest.startsWith("/") ? `~${rest}` : path3;
@@ -36767,8 +37025,8 @@ var init_claude_agent_broker_bridge = __esm({
36767
37025
  });
36768
37026
 
36769
37027
  // packages/server/src/dispatch.ts
36770
- import { closeSync as closeSync12, constants, existsSync as existsSync9, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync18, realpathSync as realpathSync7, statSync as statSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync9 } from "node:fs";
36771
- import { basename as basename6, join as join28, dirname as dirname11 } from "node:path";
37028
+ import { closeSync as closeSync12, constants, existsSync as existsSync9, fstatSync as fstatSync2, lstatSync as lstatSync3, openSync as openSync13, readFileSync as readFileSync19, realpathSync as realpathSync8, statSync as statSync13, writeFileSync as writeFileSync10, mkdirSync as mkdirSync13, rmSync as rmSync9 } from "node:fs";
37029
+ import { basename as basename6, join as join29, dirname as dirname11 } from "node:path";
36772
37030
  import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
36773
37031
  function fallbackTitle(prompt) {
36774
37032
  const firstLine2 = prompt.trim().split("\n", 1)[0].trim();
@@ -36787,7 +37045,7 @@ function fallbackTitle(prompt) {
36787
37045
  }
36788
37046
  function resolveSlug(frizzDir, base, taken) {
36789
37047
  base = ThreadSlug.parse(base);
36790
- const isTaken = (slug) => existsSync9(join28(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
37048
+ const isTaken = (slug) => existsSync9(join29(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
36791
37049
  if (!isTaken(base)) return base;
36792
37050
  for (let n = 2; ; n++) {
36793
37051
  const suffix = `-${n}`;
@@ -36803,16 +37061,16 @@ function resolveLegacyThreadFile(projectDir, value) {
36803
37061
  const parsed = ThreadSlug.safeParse(value);
36804
37062
  if (!parsed.success) return null;
36805
37063
  try {
36806
- const projectRoot = realpathSync7(projectDir);
36807
- const frizzPath = join28(projectRoot, ".frizz");
37064
+ const projectRoot = realpathSync8(projectDir);
37065
+ const frizzPath = join29(projectRoot, ".frizz");
36808
37066
  const frizzStat = lstatSync3(frizzPath);
36809
37067
  if (!frizzStat.isDirectory() || frizzStat.isSymbolicLink()) return null;
36810
- const realFrizz = realpathSync7(frizzPath);
37068
+ const realFrizz = realpathSync8(frizzPath);
36811
37069
  if (dirname11(realFrizz) !== projectRoot || basename6(realFrizz) !== ".frizz") return null;
36812
- const path3 = join28(realFrizz, `${parsed.data}.md`);
37070
+ const path3 = join29(realFrizz, `${parsed.data}.md`);
36813
37071
  const before = lstatSync3(path3);
36814
37072
  if (!before.isFile() || before.isSymbolicLink()) return null;
36815
- const realPath = realpathSync7(path3);
37073
+ const realPath = realpathSync8(path3);
36816
37074
  if (dirname11(realPath) !== realFrizz || basename6(realPath) !== `${parsed.data}.md`) return null;
36817
37075
  let contents;
36818
37076
  let openedBefore;
@@ -36820,7 +37078,7 @@ function resolveLegacyThreadFile(projectDir, value) {
36820
37078
  const fd = openSync13(path3, constants.O_RDONLY | constants.O_NOFOLLOW);
36821
37079
  try {
36822
37080
  openedBefore = fstatSync2(fd);
36823
- contents = readFileSync18(fd);
37081
+ contents = readFileSync19(fd);
36824
37082
  openedAfter = fstatSync2(fd);
36825
37083
  } finally {
36826
37084
  closeSync12(fd);
@@ -36853,7 +37111,7 @@ function boardAuthorizesAdoption(board, slug) {
36853
37111
  return !board.errorItems.some((item) => item.file === `${slug}.md`);
36854
37112
  }
36855
37113
  function ensureSafeDirectDirectory(parent, name) {
36856
- const path3 = join28(parent, name);
37114
+ const path3 = join29(parent, name);
36857
37115
  try {
36858
37116
  mkdirSync13(path3);
36859
37117
  } catch (error) {
@@ -36862,7 +37120,7 @@ function ensureSafeDirectDirectory(parent, name) {
36862
37120
  }
36863
37121
  const stat2 = lstatSync3(path3);
36864
37122
  if (!stat2.isDirectory() || stat2.isSymbolicLink()) throw new Error("unsafe project directory");
36865
- const real = realpathSync7(path3);
37123
+ const real = realpathSync8(path3);
36866
37124
  if (dirname11(real) !== parent || basename6(real) !== name) throw new Error("unsafe project directory");
36867
37125
  return real;
36868
37126
  }
@@ -36871,7 +37129,7 @@ function scratchDirRelPath(sessionId) {
36871
37129
  }
36872
37130
  function writeScratchDir(projectDir, sessionId) {
36873
37131
  if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,199}$/.test(sessionId)) throw new Error("invalid session id");
36874
- const projectRoot = realpathSync7(projectDir);
37132
+ const projectRoot = realpathSync8(projectDir);
36875
37133
  const frizzDir = ensureSafeDirectDirectory(projectRoot, ".frizz");
36876
37134
  const threadsDir = ensureSafeDirectDirectory(frizzDir, "threads");
36877
37135
  ensureSafeDirectDirectory(threadsDir, sessionId);
@@ -36883,8 +37141,8 @@ function loadWorkerPrompt(kind = "claude") {
36883
37141
  function monitorScriptsDir() {
36884
37142
  const plugin = workerPluginDir();
36885
37143
  if (!plugin) return void 0;
36886
- const dir = join28(plugin, "skills", "gh", "scripts");
36887
- return existsSync9(join28(dir, "ci-watch.mjs")) ? dir : void 0;
37144
+ const dir = join29(plugin, "skills", "gh", "scripts");
37145
+ return existsSync9(join29(dir, "ci-watch.mjs")) ? dir : void 0;
36888
37146
  }
36889
37147
  function codexScratchpadHookConfig(hookScript, sessionId) {
36890
37148
  if (!hookScript || !sessionId) return {};
@@ -36896,7 +37154,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
36896
37154
  }
36897
37155
  ]
36898
37156
  });
36899
- const bashBackgroundHook = join28(dirname11(hookScript), "bash-background.mjs");
37157
+ const bashBackgroundHook = join29(dirname11(hookScript), "bash-background.mjs");
36900
37158
  return {
36901
37159
  bypass_hook_trust: true,
36902
37160
  hooks: {
@@ -36922,7 +37180,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
36922
37180
  }
36923
37181
  function scratchpadHookScript() {
36924
37182
  const plugin = workerPluginDir();
36925
- return plugin ? join28(plugin, "hooks", "scratchpad.mjs") : void 0;
37183
+ return plugin ? join29(plugin, "hooks", "scratchpad.mjs") : void 0;
36926
37184
  }
36927
37185
  function composePrompt(sessionId, prompt, kind = "claude") {
36928
37186
  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.";
@@ -36938,12 +37196,12 @@ function scratchpadOrientation(sessionId, kind = "claude") {
36938
37196
  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 (${children2}). Nothing in this directory is read automatically; if you want a note back after a compaction, mcp__frizz__goal with post_compaction: true re-sends a prompt of your choosing.`;
36939
37197
  }
36940
37198
  function frizzConfigBlock(projectDir) {
36941
- const path3 = join28(projectDir, "FRIZZ.md");
37199
+ const path3 = join29(projectDir, "FRIZZ.md");
36942
37200
  let body;
36943
37201
  try {
36944
- const st = statSync12(path3);
37202
+ const st = statSync13(path3);
36945
37203
  if (!st.isFile() || st.size > FRIZZ_MD_MAX_BYTES) return "";
36946
- body = readFileSync18(path3, "utf8").trim();
37204
+ body = readFileSync19(path3, "utf8").trim();
36947
37205
  } catch {
36948
37206
  return "";
36949
37207
  }
@@ -36982,7 +37240,7 @@ function resolveFrizzMcp(target, moduleUrl = import.meta.url, env = process.env,
36982
37240
  const { stateDir, serverLock, projectId } = typeof target === "string" ? { stateDir: target } : target;
36983
37241
  const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
36984
37242
  if (!pluginDir) return void 0;
36985
- const scriptPath = join28(pluginDir, "bin", FRIZZ_MCP.script);
37243
+ const scriptPath = join29(pluginDir, "bin", FRIZZ_MCP.script);
36986
37244
  if (!existsSync9(scriptPath)) return void 0;
36987
37245
  return {
36988
37246
  scriptPath,
@@ -37065,7 +37323,7 @@ function buildClaudeResumeCommand(opts) {
37065
37323
  }
37066
37324
  function createDispatcher(deps) {
37067
37325
  const readBoardSource = deps.readBoard ?? readBoard;
37068
- const frizzDir = join28(deps.project.dir, ".frizz");
37326
+ const frizzDir = join29(deps.project.dir, ".frizz");
37069
37327
  const adoptionRuntime = deps.adoptionRuntime ?? productionRuntime;
37070
37328
  function cleanupPrewrites(built) {
37071
37329
  for (const path3 of new Set(built.prewrite.map((file) => file.path))) {
@@ -37078,7 +37336,7 @@ function createDispatcher(deps) {
37078
37336
  function cleanupDispatchFiles(scratchRel, built, sessionId) {
37079
37337
  cleanupPrewrites(built);
37080
37338
  try {
37081
- rmSync9(join28(deps.project.dir, scratchRel), { force: true, recursive: true });
37339
+ rmSync9(join29(deps.project.dir, scratchRel), { force: true, recursive: true });
37082
37340
  } catch {
37083
37341
  }
37084
37342
  cleanupAdoptionSessionFiles(deps.project.dir, sessionId);
@@ -37516,11 +37774,11 @@ var init_dispatch = __esm({
37516
37774
 
37517
37775
  // packages/server/src/board.ts
37518
37776
  import {
37519
- realpathSync as realpathSync8,
37777
+ realpathSync as realpathSync9,
37520
37778
  watch as fsWatch
37521
37779
  } from "node:fs";
37522
- import { homedir as homedir13 } from "node:os";
37523
- import { dirname as dirname12, join as join29 } from "node:path";
37780
+ import { homedir as homedir14 } from "node:os";
37781
+ import { dirname as dirname12, join as join30 } from "node:path";
37524
37782
  import watcher from "@parcel/watcher";
37525
37783
  function appServerTurnStalled(liveness, lastActivityAt, nowMs) {
37526
37784
  if (!liveness) return false;
@@ -37903,6 +38161,7 @@ function registeredDoneFence(done, lastUserAt) {
37903
38161
  }
37904
38162
  function readThreadRegistries(storage) {
37905
38163
  return {
38164
+ links: storage.threadLinksBySlug(),
37906
38165
  prWatches: storage.armedPrWatchesBySlug(),
37907
38166
  timers: storage.armedThreadTimersBySlug(),
37908
38167
  questions: storage.threadQuestionsBySlug(),
@@ -38010,6 +38269,7 @@ function sessionThreadView(projectDir, storage, row, rawTele, registeredLegacyTe
38010
38269
  lastAssistantAt: tele?.lastAssistantAt,
38011
38270
  subAgents: stampStoppable(tele?.subAgents ?? [], row),
38012
38271
  bgShells: stampStoppableShells(tele?.bgShells ?? [], row),
38272
+ links: (registries.links.get(row.slug) ?? []).map(threadLinkView),
38013
38273
  // ONE SOURCE: the FENCE. Both kinds are derived from what the worker wrote — `prs:` entries
38014
38274
  // become the github rows, `watch:` lines the shell rows — so this strip lists exactly what will
38015
38275
  // actually wake the thread, and the two cannot drift into claiming different things. There is no
@@ -38257,7 +38517,7 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
38257
38517
  ...project.githubRepo ? { githubRepo: project.githubRepo } : {},
38258
38518
  projectSlug: findByPath(project.dir)?.slug,
38259
38519
  // So the client can expand a `~` a worker wrote in prose (see BoardSnapshot.homeDir).
38260
- homeDir: homedir13()
38520
+ homeDir: homedir14()
38261
38521
  };
38262
38522
  const sessionThreads = buildSessionThreads(assembledAtMs);
38263
38523
  armSnoozeWake(sessionThreads, assembledAtMs);
@@ -38358,10 +38618,10 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
38358
38618
  if (parcelSub || stopped) return Promise.resolve();
38359
38619
  if (watchSetup) return watchSetup;
38360
38620
  const setup = (async () => {
38361
- const dir = join29(project.dir, ".frizz");
38621
+ const dir = join30(project.dir, ".frizz");
38362
38622
  const roots = /* @__PURE__ */ new Set([dir]);
38363
38623
  try {
38364
- roots.add(realpathSync8(dir));
38624
+ roots.add(realpathSync9(dir));
38365
38625
  } catch {
38366
38626
  }
38367
38627
  const next = await subscribe(
@@ -38373,7 +38633,7 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
38373
38633
  }
38374
38634
  if (events.some((event) => isBoardRelevantFrizzPath(roots, event.path))) scheduleRebuild();
38375
38635
  },
38376
- { ignore: FRIZZ_WATCH_IGNORED_DIRS.map((name) => join29(dir, name)) }
38636
+ { ignore: FRIZZ_WATCH_IGNORED_DIRS.map((name) => join30(dir, name)) }
38377
38637
  );
38378
38638
  if (stopped) {
38379
38639
  await next.unsubscribe();
@@ -38455,6 +38715,7 @@ var init_board = __esm({
38455
38715
  "use strict";
38456
38716
  init_src();
38457
38717
  init_storage();
38718
+ init_thread_links();
38458
38719
  init_thread_profiles();
38459
38720
  init_frizz();
38460
38721
  init_awaiting();
@@ -41050,7 +41311,7 @@ var init_resume = __esm({
41050
41311
  });
41051
41312
 
41052
41313
  // packages/server/src/backend/claude.ts
41053
- import { join as join30 } from "node:path";
41314
+ import { join as join31 } from "node:path";
41054
41315
  function toolResultText2(content) {
41055
41316
  if (typeof content === "string") return content;
41056
41317
  if (!Array.isArray(content)) return "";
@@ -41141,7 +41402,7 @@ function createClaudeBackend(opts) {
41141
41402
  return { argv, env: claudeWorkerEnvironment(), prewrite: [] };
41142
41403
  },
41143
41404
  transcriptPath(sessionId) {
41144
- return join30(opts.logDir, `${sessionId}.jsonl`);
41405
+ return join31(opts.logDir, `${sessionId}.jsonl`);
41145
41406
  },
41146
41407
  parseLine(line) {
41147
41408
  return parseClaudeLine(line);
@@ -41311,13 +41572,13 @@ var init_login_utility = __esm({
41311
41572
  });
41312
41573
 
41313
41574
  // packages/server/src/backend/codex-app-server-diagnostics.ts
41314
- import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, renameSync as renameSync8, statSync as statSync13 } from "node:fs";
41315
- import { join as join31 } from "node:path";
41575
+ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, renameSync as renameSync8, statSync as statSync14 } from "node:fs";
41576
+ import { join as join32 } from "node:path";
41316
41577
  function codexDiagnosticLogPath(stateDir, projectId) {
41317
- return join31(stateDir, "codex-app-server", `${projectId}.diagnostics.log`);
41578
+ return join32(stateDir, "codex-app-server", `${projectId}.diagnostics.log`);
41318
41579
  }
41319
41580
  function createCodexDiagnosticSink(stateDir, projectId, now = () => /* @__PURE__ */ new Date()) {
41320
- const dir = join31(stateDir, "codex-app-server");
41581
+ const dir = join32(stateDir, "codex-app-server");
41321
41582
  const path3 = codexDiagnosticLogPath(stateDir, projectId);
41322
41583
  let ensured = false;
41323
41584
  return (event) => {
@@ -41328,7 +41589,7 @@ function createCodexDiagnosticSink(stateDir, projectId, now = () => /* @__PURE__
41328
41589
  ensured = true;
41329
41590
  }
41330
41591
  try {
41331
- if (statSync13(path3).size > MAX_BYTES2) renameSync8(path3, `${path3}.1`);
41592
+ if (statSync14(path3).size > MAX_BYTES2) renameSync8(path3, `${path3}.1`);
41332
41593
  } catch {
41333
41594
  }
41334
41595
  appendFileSync3(path3, `${JSON.stringify({ at: now().toISOString(), ...event })}
@@ -41349,7 +41610,7 @@ var init_codex_app_server_diagnostics = __esm({
41349
41610
  import { execFile as execFile8 } from "node:child_process";
41350
41611
  import { readdirSync as readdirSync8, unlinkSync as unlinkSync4 } from "node:fs";
41351
41612
  import { connect as connectSocket } from "node:net";
41352
- import { join as join32 } from "node:path";
41613
+ import { join as join33 } from "node:path";
41353
41614
  function parseLsofSocketNames(stdout) {
41354
41615
  const referenced = /* @__PURE__ */ new Set();
41355
41616
  for (const line of stdout.split("\n")) {
@@ -41366,7 +41627,7 @@ function sweepStaleSockets(options, deps = {}) {
41366
41627
  const keep = new Set(options.keep ?? []);
41367
41628
  let candidates;
41368
41629
  try {
41369
- candidates = (deps.readdir ?? readdirSync8)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) => join32(options.dir, name)).filter((path3) => !keep.has(path3));
41630
+ candidates = (deps.readdir ?? readdirSync8)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) => join33(options.dir, name)).filter((path3) => !keep.has(path3));
41370
41631
  } catch {
41371
41632
  return;
41372
41633
  }
@@ -41416,7 +41677,7 @@ var init_stale_socket_sweep = __esm({
41416
41677
 
41417
41678
  // packages/server/src/orphan-reaper.ts
41418
41679
  import { execFileSync as execFileSync4 } from "node:child_process";
41419
- import { readFileSync as readFileSync19 } from "node:fs";
41680
+ import { readFileSync as readFileSync20 } from "node:fs";
41420
41681
  import { dirname as dirname13 } from "node:path";
41421
41682
  function firstTokenBasename(command) {
41422
41683
  const first = command.trimStart().split(/\s+/, 1)[0] ?? "";
@@ -41683,7 +41944,7 @@ var init_orphan_reaper = __esm({
41683
41944
  defaultExec = (file, args) => execFileSync4(file, args, { encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 1e4 });
41684
41945
  defaultReadEnv = (pid) => {
41685
41946
  try {
41686
- return readFileSync19(`/proc/${pid}/environ`, "utf8");
41947
+ return readFileSync20(`/proc/${pid}/environ`, "utf8");
41687
41948
  } catch {
41688
41949
  return null;
41689
41950
  }
@@ -41830,9 +42091,9 @@ var init_thread_hibernation = __esm({
41830
42091
  });
41831
42092
 
41832
42093
  // packages/server/src/context.ts
41833
- import { join as join33 } from "node:path";
42094
+ import { join as join34 } from "node:path";
41834
42095
  import { randomUUID as randomUUID15 } from "node:crypto";
41835
- import { homedir as homedir14 } from "node:os";
42096
+ import { homedir as homedir15 } from "node:os";
41836
42097
  function reconcileSessions(storage) {
41837
42098
  for (const row of storage.allSessions()) {
41838
42099
  if (isHeadlessRow(row)) continue;
@@ -41978,9 +42239,9 @@ async function createContext(opts = {}) {
41978
42239
  }
41979
42240
  }
41980
42241
  function createContextUnchecked(opts, resources) {
41981
- const home = opts.home ?? homedir14();
42242
+ const home = opts.home ?? homedir15();
41982
42243
  const project = opts.project ?? resolveProject();
41983
- const storage = createStorage(opts.database ?? join33(project.stateDir, "ui.db"), project.id);
42244
+ const storage = createStorage(opts.database ?? join34(project.stateDir, "ui.db"), project.id);
41984
42245
  resources.storage = storage;
41985
42246
  const bus = new Bus();
41986
42247
  const transcriptChange = new Emitter();
@@ -42090,7 +42351,10 @@ function createContextUnchecked(opts, resources) {
42090
42351
  const mode = PermissionMode.safeParse(row.permission_mode);
42091
42352
  if (!mode.success) return void 0;
42092
42353
  return codexSandbox(mode.data);
42093
- }
42354
+ },
42355
+ // Read at every thread/start and cold resume, so the Codex context window the operator picks in
42356
+ // Settings reaches the next thread without an app-server restart.
42357
+ getSettings: () => getSettings(storage, home)
42094
42358
  }) : void 0;
42095
42359
  resources.codexAppServer = codexAppServer;
42096
42360
  if (codexAppServer) {
@@ -45035,8 +45299,8 @@ var init_server = __esm({
45035
45299
  });
45036
45300
 
45037
45301
  // packages/server/src/repair.ts
45038
- import { existsSync as existsSync11, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "node:fs";
45039
- import { basename as basename7, dirname as dirname14, resolve as resolve7 } from "node:path";
45302
+ import { existsSync as existsSync11, readFileSync as readFileSync21, writeFileSync as writeFileSync11 } from "node:fs";
45303
+ import { basename as basename7, dirname as dirname14, resolve as resolve8 } from "node:path";
45040
45304
  function quoteValue(v) {
45041
45305
  if (/^[\w./#:+-]+$/.test(v)) return v;
45042
45306
  return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
@@ -45046,12 +45310,12 @@ function deriveTitle(body, slug) {
45046
45310
  return m ? m[1].trim() : slug;
45047
45311
  }
45048
45312
  function repairThreadFile(frizzDir, file) {
45049
- const root = resolve7(frizzDir);
45050
- const abs = resolve7(root, file);
45313
+ const root = resolve8(frizzDir);
45314
+ const abs = resolve8(root, file);
45051
45315
  if (dirname14(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
45052
45316
  if (!abs.endsWith(".md")) throw new RepairError(`refusing to repair "${file}": not a .md thread file`);
45053
45317
  if (!existsSync11(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
45054
- const content = readFileSync20(abs, "utf8");
45318
+ const content = readFileSync21(abs, "utf8");
45055
45319
  const firstLine2 = content.replace(/^/, "").split(/\r?\n/).find((l) => l.trim() !== "");
45056
45320
  if (firstLine2?.trim() === "---") {
45057
45321
  throw new RepairError(`${basename7(abs)} already opens with a "---" block \u2014 repair only heals a MISSING frontmatter block`);
@@ -45242,7 +45506,7 @@ var init_repo_files = __esm({
45242
45506
  });
45243
45507
 
45244
45508
  // cc-worker/hooks/bash-background.mjs
45245
- import { readFileSync as readFileSync21 } from "node:fs";
45509
+ import { readFileSync as readFileSync22 } from "node:fs";
45246
45510
  import { basename as basename8 } from "node:path";
45247
45511
  import { pathToFileURL } from "node:url";
45248
45512
  function emit(obj) {
@@ -45391,7 +45655,7 @@ var init_bash_background = __esm({
45391
45655
  if (isDirectHookExecution(process.argv[1], import.meta.url)) {
45392
45656
  try {
45393
45657
  const env = process.argv.includes("--frizz-thread") ? { ...process.env, FRIZZ_THREAD: process.env.FRIZZ_THREAD || "codex-worker" } : process.env;
45394
- emit(evaluateBashBackgroundHook(JSON.parse(readFileSync21(0, "utf8")), env));
45658
+ emit(evaluateBashBackgroundHook(JSON.parse(readFileSync22(0, "utf8")), env));
45395
45659
  } catch {
45396
45660
  emit({});
45397
45661
  }
@@ -45400,11 +45664,11 @@ var init_bash_background = __esm({
45400
45664
  });
45401
45665
 
45402
45666
  // packages/server/src/transcript.ts
45403
- import { closeSync as closeSync13, existsSync as existsSync12, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync22, readSync as readSync7, renameSync as renameSync9, statSync as statSync14, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
45667
+ import { closeSync as closeSync13, existsSync as existsSync12, fstatSync as fstatSync3, mkdirSync as mkdirSync15, openSync as openSync14, readdirSync as readdirSync9, readFileSync as readFileSync23, readSync as readSync7, renameSync as renameSync9, statSync as statSync15, unlinkSync as unlinkSync5, writeFileSync as writeFileSync12 } from "node:fs";
45404
45668
  import { createHash as createHash13 } from "node:crypto";
45405
45669
  import { StringDecoder as StringDecoder4 } from "node:string_decoder";
45406
- import { join as join34 } from "node:path";
45407
- import { homedir as homedir15 } from "node:os";
45670
+ import { join as join35 } from "node:path";
45671
+ import { homedir as homedir16 } from "node:os";
45408
45672
  function formatTokens(n) {
45409
45673
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
45410
45674
  if (n >= 1e3) return `${Math.round(n / 1e3)}k`;
@@ -45983,14 +46247,14 @@ function pruneScreenshotCache() {
45983
46247
  if (entries2.length <= SCREENSHOT_CACHE_MAX) return;
45984
46248
  const byMtime = entries2.map((n) => {
45985
46249
  try {
45986
- return { n, m: statSync14(join34(SCREENSHOT_CACHE_DIR, n)).mtimeMs };
46250
+ return { n, m: statSync15(join35(SCREENSHOT_CACHE_DIR, n)).mtimeMs };
45987
46251
  } catch {
45988
46252
  return { n, m: 0 };
45989
46253
  }
45990
46254
  }).sort((a, b) => b.m - a.m);
45991
46255
  for (const { n } of byMtime.slice(SCREENSHOT_CACHE_MAX)) {
45992
46256
  try {
45993
- unlinkSync5(join34(SCREENSHOT_CACHE_DIR, n));
46257
+ unlinkSync5(join35(SCREENSHOT_CACHE_DIR, n));
45994
46258
  } catch {
45995
46259
  }
45996
46260
  }
@@ -46020,14 +46284,14 @@ function persistBase64Image(mediaType, data, idKey) {
46020
46284
  const ext = IMAGE_MEDIA_EXT[typeof mediaType === "string" ? mediaType.toLowerCase() : ""];
46021
46285
  if (!ext || !data) return void 0;
46022
46286
  const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
46023
- const path3 = join34(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
46287
+ const path3 = join35(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
46024
46288
  try {
46025
46289
  if (existsSync12(path3)) return path3;
46026
46290
  if (data.length > SCREENSHOT_MAX_BASE64) return void 0;
46027
46291
  const buf = Buffer.from(data, "base64");
46028
46292
  if (buf.length === 0 || !looksLikeImage(buf, ext)) return void 0;
46029
46293
  mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
46030
- const tmp = join34(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
46294
+ const tmp = join35(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
46031
46295
  writeFileSync12(tmp, buf);
46032
46296
  renameSync9(tmp, path3);
46033
46297
  pruneScreenshotCache();
@@ -46042,14 +46306,14 @@ function persistSentFile(srcPath, idKey) {
46042
46306
  const outExt = ext === "jpeg" ? "jpg" : ext;
46043
46307
  try {
46044
46308
  const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
46045
- const dest = join34(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
46309
+ const dest = join35(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
46046
46310
  if (existsSync12(dest)) return dest;
46047
- const size = statSync14(srcPath).size;
46311
+ const size = statSync15(srcPath).size;
46048
46312
  if (size === 0 || size > SENT_IMAGE_MAX_BYTES) return void 0;
46049
- const buf = readFileSync22(srcPath);
46313
+ const buf = readFileSync23(srcPath);
46050
46314
  if (!looksLikeImage(buf, outExt)) return void 0;
46051
46315
  mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
46052
- const tmp = join34(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
46316
+ const tmp = join35(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
46053
46317
  writeFileSync12(tmp, buf);
46054
46318
  renameSync9(tmp, dest);
46055
46319
  pruneScreenshotCache();
@@ -46487,7 +46751,7 @@ function readTranscript(project, sessionId) {
46487
46751
  }
46488
46752
  function verifyIncrementalParse(path3, identityPrefix, incremental) {
46489
46753
  try {
46490
- const fresh = parseTranscript(readFileSync22(path3, "utf8"), identityPrefix);
46754
+ const fresh = parseTranscript(readFileSync23(path3, "utf8"), identityPrefix);
46491
46755
  const a = JSON.stringify(incremental);
46492
46756
  const b = JSON.stringify(fresh);
46493
46757
  if (a !== b) {
@@ -46507,16 +46771,16 @@ function verifyIncrementalParse(path3, identityPrefix, incremental) {
46507
46771
  }
46508
46772
  }
46509
46773
  function logDirOf(project) {
46510
- return join34(homedir15(), ".claude", "projects", project.cwdSlug);
46774
+ return join35(homedir16(), ".claude", "projects", project.cwdSlug);
46511
46775
  }
46512
46776
  function resolveTranscriptPath(project, sessionId) {
46513
- const path3 = join34(logDirOf(project), `${sessionId}.jsonl`);
46777
+ const path3 = join35(logDirOf(project), `${sessionId}.jsonl`);
46514
46778
  try {
46515
- if (statSync14(path3).size > 0) return path3;
46779
+ if (statSync15(path3).size > 0) return path3;
46516
46780
  } catch {
46517
46781
  }
46518
46782
  const stranded = discoverTranscriptDir(logDirOf(project), sessionId);
46519
- return stranded ? join34(stranded, `${sessionId}.jsonl`) : path3;
46783
+ return stranded ? join35(stranded, `${sessionId}.jsonl`) : path3;
46520
46784
  }
46521
46785
  function projectCodexTranscript(raw2, identityPrefix = "codex") {
46522
46786
  const out = [];
@@ -46581,8 +46845,9 @@ ${text2}` : text2;
46581
46845
  }
46582
46846
  case "tool-call": {
46583
46847
  const m = openAssistant(ev.at, sourceId);
46584
- const call = codexToolCall(ev.name, ev.input, ev.id);
46585
- call.status = "pending";
46848
+ const split = codexToolCards(ev.name, ev.input, ev.id);
46849
+ const call = split.owner;
46850
+ for (const card of split.cards) card.status = "pending";
46586
46851
  if (pendingCaption) {
46587
46852
  if (!call.desc) call.desc = pendingCaption;
46588
46853
  pendingCaption = void 0;
@@ -46592,8 +46857,10 @@ ${text2}` : text2;
46592
46857
  const interruptedOwner = call.name === "Interrupt process" && call.sessionId !== void 0 ? shellSessions.get(runningKey("Poll process", call.sessionId)) : void 0;
46593
46858
  if (!owner) {
46594
46859
  if (isPoll) call.backgroundState = "unknown";
46595
- pushToolPart(m, call);
46596
- m.tools.push(call);
46860
+ for (const card of split.cards) {
46861
+ pushToolPart(m, card);
46862
+ m.tools.push(card);
46863
+ }
46597
46864
  }
46598
46865
  if (call.name === "Spawn agent" && call.detail) agentDispatches.set(call.detail, { call, at: ev.at });
46599
46866
  if (ev.id) pendingCalls.set(ev.id, {
@@ -46602,7 +46869,8 @@ ${text2}` : text2;
46602
46869
  owner,
46603
46870
  interruptedOwner,
46604
46871
  orphanPoll: isPoll && !owner,
46605
- explicitBackground: codexExplicitBackground(ev.name, ev.input)
46872
+ explicitBackground: codexExplicitBackground(ev.name, ev.input),
46873
+ ...split.cards.length > 1 ? { companions: split.cards.filter((card) => card !== call), views: split.views } : {}
46606
46874
  });
46607
46875
  break;
46608
46876
  }
@@ -46612,7 +46880,17 @@ ${text2}` : text2;
46612
46880
  pendingCalls.delete(ev.id);
46613
46881
  const result = codexToolResult(ev.text);
46614
46882
  if (result.durationMs === void 0) result.durationMs = elapsedBetween(pending.at, ev.at);
46615
- if (ev.image && result.status !== "failed" && result.status !== "cancelled") {
46883
+ if (pending.companions) {
46884
+ for (const card of pending.companions) card.status = result.status === "cancelled" ? "cancelled" : "completed";
46885
+ if (result.status !== "cancelled") {
46886
+ const inline = ev.images ?? (ev.image ? [ev.image] : []);
46887
+ pending.views?.forEach((view, i) => {
46888
+ if (view.outputImage || !inline[i]) return;
46889
+ const shot = persistDataUrlImage(inline[i], `${ev.id}:view:${i}`);
46890
+ if (shot) view.outputImage = shot;
46891
+ });
46892
+ }
46893
+ } else if (ev.image && result.status !== "failed" && result.status !== "cancelled") {
46616
46894
  const shot = persistDataUrlImage(ev.image, ev.id);
46617
46895
  if (shot) (pending.owner ?? pending).call.outputImage = shot;
46618
46896
  }
@@ -46841,6 +47119,11 @@ function latestTranscriptWindow(messages) {
46841
47119
  }
46842
47120
  return [...earlierInstructions, ...messages.slice(start), ...pinned.slice(-MAX_PINNED_BACKGROUND_OPERATIONS)];
46843
47121
  }
47122
+ function codexToolCards(name, input, callId) {
47123
+ if (name === "exec" && typeof input === "string") return codexExecWrapperCards(input, callId);
47124
+ const owner = codexToolCall(name, input, callId);
47125
+ return { owner, cards: [owner] };
47126
+ }
46844
47127
  function codexToolCall(name, input, callId) {
46845
47128
  if (name === "exec" && typeof input === "string") return codexExecWrapperCall(input, callId);
46846
47129
  const obj = input && typeof input === "object" && !Array.isArray(input) ? input : {};
@@ -46999,8 +47282,32 @@ function formatCompactDuration(ms) {
46999
47282
  if (minutes < 60) return `${minutes}m`;
47000
47283
  return minutes % 60 ? `${Math.floor(minutes / 60)}h ${minutes % 60}m` : `${Math.floor(minutes / 60)}h`;
47001
47284
  }
47002
- function codexExecWrapperCall(source, callId) {
47285
+ function codexExecWrapperCards(source, callId) {
47003
47286
  const calls = wrappedInvocations(source);
47287
+ const rest = [];
47288
+ const views = [];
47289
+ const order = [];
47290
+ if (calls.length > 1) {
47291
+ for (const call of calls) {
47292
+ if (call.name !== "view_image") {
47293
+ if (!rest.length) order.push("rest");
47294
+ rest.push(call);
47295
+ continue;
47296
+ }
47297
+ const view = viewImageCall(jsStringProperty(call.args, "path"), `${callId ?? source}:view:${views.length}`);
47298
+ views.push(view);
47299
+ order.push(view);
47300
+ }
47301
+ }
47302
+ if (!views.length) {
47303
+ const owner = codexExecWrapperCall(source, callId, calls);
47304
+ return { owner, cards: [owner] };
47305
+ }
47306
+ const remainder = rest.length ? codexExecWrapperCall(source, callId, rest) : void 0;
47307
+ const cards = order.map((entry) => entry === "rest" ? remainder : entry);
47308
+ return { owner: remainder ?? views[0], cards, views };
47309
+ }
47310
+ function codexExecWrapperCall(source, callId, calls = wrappedInvocations(source)) {
47004
47311
  if (calls.length !== 1) {
47005
47312
  return {
47006
47313
  name: "Exec",
@@ -47008,7 +47315,9 @@ function codexExecWrapperCall(source, callId) {
47008
47315
  input: capToolInput(source.trim())
47009
47316
  };
47010
47317
  }
47011
- const call = calls[0];
47318
+ return wrappedSingleCall(calls[0], source, callId);
47319
+ }
47320
+ function wrappedSingleCall(call, source, callId) {
47012
47321
  if (call.name === "exec_command") {
47013
47322
  const cmd = jsStringProperty(call.args, "cmd") ?? jsStringProperty(call.args, "command");
47014
47323
  const cwd = jsStringProperty(call.args, "workdir") ?? jsStringProperty(call.args, "cwd");
@@ -47384,7 +47693,7 @@ function unifiedToolResult(text2) {
47384
47693
  if (!header) return void 0;
47385
47694
  const wrapperStatus = header[1] === "failed" ? "failed" : header[1] === "terminated" ? "cancelled" : "completed";
47386
47695
  const wrapperDurationMs = Number(header[2]) * 1e3;
47387
- const body = raw2.slice(header[0].length).trim();
47696
+ const body = raw2.slice(header[0].length).split("[image output]").join("").trim();
47388
47697
  if (!body || body === "{}") return { status: wrapperStatus, durationMs: wrapperDurationMs, terminal: true };
47389
47698
  try {
47390
47699
  const parsed = JSON.parse(body);
@@ -47764,7 +48073,7 @@ function readEarlierThreadTranscriptPage(project, storage, slug, cursor, backend
47764
48073
  }
47765
48074
  function readCodexTranscriptFile(absPath, nativeId = absPath) {
47766
48075
  try {
47767
- return parseCodexTranscript(readFileSync22(absPath, "utf8"), `codex:${nativeId}`);
48076
+ return parseCodexTranscript(readFileSync23(absPath, "utf8"), `codex:${nativeId}`);
47768
48077
  } catch {
47769
48078
  return [];
47770
48079
  }
@@ -47862,7 +48171,7 @@ function projectTranscriptPageAgentLifecycles(page, lookup, peerLookup) {
47862
48171
  function readTranscriptFile(absPath) {
47863
48172
  try {
47864
48173
  const pathKey = createHash13("sha256").update(absPath).digest("base64url").slice(0, 16);
47865
- return parseTranscript(readFileSync22(absPath, "utf8"), `claude-file:${pathKey}`);
48174
+ return parseTranscript(readFileSync23(absPath, "utf8"), `claude-file:${pathKey}`);
47866
48175
  } catch {
47867
48176
  return [];
47868
48177
  }
@@ -47938,7 +48247,7 @@ var init_transcript = __esm({
47938
48247
  });
47939
48248
 
47940
48249
  // packages/server/src/open-external.ts
47941
- import { spawn as spawn5 } from "node:child_process";
48250
+ import { spawn as spawn6 } from "node:child_process";
47942
48251
  function validateExternalUrl(raw2) {
47943
48252
  let parsed;
47944
48253
  try {
@@ -47955,7 +48264,7 @@ function openExternalUrl(raw2) {
47955
48264
  const v = validateExternalUrl(raw2);
47956
48265
  if (!v.ok) throw new Error(v.reason);
47957
48266
  const [cmd, args] = process.platform === "darwin" ? ["open", [v.url]] : ["xdg-open", [v.url]];
47958
- spawn5(cmd, args, { detached: true, stdio: "ignore" }).unref();
48267
+ spawn6(cmd, args, { detached: true, stdio: "ignore" }).unref();
47959
48268
  }
47960
48269
  var init_open_external = __esm({
47961
48270
  "packages/server/src/open-external.ts"() {
@@ -47963,93 +48272,6 @@ var init_open_external = __esm({
47963
48272
  }
47964
48273
  });
47965
48274
 
47966
- // packages/server/src/local-file.ts
47967
- import { spawn as spawn6 } from "node:child_process";
47968
- import { readFileSync as readFileSync23, realpathSync as realpathSync9, statSync as statSync15 } from "node:fs";
47969
- import { homedir as homedir16 } from "node:os";
47970
- import { isAbsolute as isAbsolute4, join as join35, resolve as resolve8, sep as sep2 } from "node:path";
47971
- function isUnder(real, root) {
47972
- let rootReal;
47973
- try {
47974
- rootReal = realpathSync9(root);
47975
- } catch {
47976
- return false;
47977
- }
47978
- return real === rootReal || real.startsWith(rootReal.endsWith(sep2) ? rootReal : rootReal + sep2);
47979
- }
47980
- function resolveLocalFile(rawPath, roots) {
47981
- if (!isAbsolute4(rawPath)) throw new Error("Local path must be absolute");
47982
- let real;
47983
- try {
47984
- real = realpathSync9(rawPath);
47985
- } catch {
47986
- throw new Error("Local file was not found");
47987
- }
47988
- if (!roots.some((root) => isUnder(real, root))) throw new Error("Local file is outside Frizz's trusted roots");
47989
- try {
47990
- if (!statSync15(real).isFile()) throw new Error("Local path is not a regular file");
47991
- } catch (error) {
47992
- if (error instanceof Error && error.message === "Local path is not a regular file") throw error;
47993
- throw new Error("Local file was not found");
47994
- }
47995
- return real;
47996
- }
47997
- function resolveOpenableFile(raw2, projectDir, roots, home = homedir16()) {
47998
- const trimmed = raw2.trim().replace(/:\d+(?::\d+)?$/, "");
47999
- if (!trimmed) return null;
48000
- const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ? join35(home, trimmed.slice(2)) : isAbsolute4(trimmed) ? trimmed : resolve8(projectDir, trimmed);
48001
- try {
48002
- return resolveLocalFile(abs, roots);
48003
- } catch {
48004
- return null;
48005
- }
48006
- }
48007
- function readLocalTextFile(rawPath, roots) {
48008
- const path3 = resolveLocalFile(rawPath, roots);
48009
- const bytes = readFileSync23(path3);
48010
- if (bytes.subarray(0, 8192).includes(0)) throw new Error("Local file is not a text file");
48011
- if (bytes.length <= MARKDOWN_READ_LIMIT) return { path: path3, text: bytes.toString("utf8"), truncated: false };
48012
- const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
48013
- const lastBreak = head.lastIndexOf(10);
48014
- return { path: path3, text: head.subarray(0, lastBreak > 0 ? lastBreak : head.length).toString("utf8"), truncated: true };
48015
- }
48016
- function readLocalMarkdown(rawPath, roots) {
48017
- if (!MARKDOWN_FILE_EXT.test(rawPath.trim())) throw new Error("Local file is not a Markdown file");
48018
- const path3 = resolveLocalFile(rawPath, roots);
48019
- if (!MARKDOWN_FILE_EXT.test(path3)) throw new Error("Local file is not a Markdown file");
48020
- const bytes = readFileSync23(path3);
48021
- if (bytes.length <= MARKDOWN_READ_LIMIT) return { path: path3, markdown: bytes.toString("utf8"), truncated: false };
48022
- const head = bytes.subarray(0, MARKDOWN_READ_LIMIT);
48023
- const lastBreak = head.lastIndexOf(10);
48024
- return { path: path3, markdown: head.subarray(0, lastBreak > 0 ? lastBreak : head.length).toString("utf8"), truncated: true };
48025
- }
48026
- function resolveWatchableLocalFile(rawPath, roots) {
48027
- const path3 = resolveLocalFile(rawPath, roots);
48028
- if (MARKDOWN_FILE_EXT.test(rawPath.trim()) && !MARKDOWN_FILE_EXT.test(path3)) {
48029
- throw new Error("Local file is not a Markdown file");
48030
- }
48031
- return path3;
48032
- }
48033
- function defaultSpawn(command, args, options) {
48034
- return spawn6(command, [...args], options);
48035
- }
48036
- function openLocalFile(rawPath, opener, roots, options = {}) {
48037
- const path3 = resolveLocalFile(rawPath, roots);
48038
- const selected = options.forceSystem ? "system" : opener;
48039
- if (selected === "copy") return { action: "copy", path: path3 };
48040
- const spec = process.platform === "darwin" ? selected === "cursor" ? ["open", ["-a", "Cursor", path3]] : selected === "vscode" ? ["open", ["-a", "Visual Studio Code", path3]] : selected === "finder" ? ["open", ["-R", path3]] : ["open", [path3]] : selected === "finder" ? ["xdg-open", [path3]] : selected === "cursor" ? ["cursor", [path3]] : selected === "vscode" ? ["code", [path3]] : ["xdg-open", [path3]];
48041
- (options.spawn ?? defaultSpawn)(spec[0], spec[1], { detached: true, stdio: "ignore", shell: false }).unref();
48042
- return { action: "opened", path: path3 };
48043
- }
48044
- var MARKDOWN_FILE_EXT, MARKDOWN_READ_LIMIT;
48045
- var init_local_file = __esm({
48046
- "packages/server/src/local-file.ts"() {
48047
- "use strict";
48048
- MARKDOWN_FILE_EXT = /\.(?:md|mdx|markdown)$/i;
48049
- MARKDOWN_READ_LIMIT = 1024 * 1024;
48050
- }
48051
- });
48052
-
48053
48275
  // packages/server/src/github-hovercard.ts
48054
48276
  function parseRef(ref) {
48055
48277
  const match2 = REF_PATTERN.exec(ref);
@@ -50164,7 +50386,7 @@ ${gapNote}` : input.message;
50164
50386
  since: new Date(w.created_at).toISOString()
50165
50387
  });
50166
50388
  }
50167
- return { activity, questions: openQuestionViews(input.slug) };
50389
+ return { activity, questions: openQuestionViews(input.slug), links: ctx.storage.listThreadLinks(input.slug).map(threadLinkView) };
50168
50390
  }
50169
50391
  }),
50170
50392
  // THE WATCHER REGISTRY WAS DELETED ON 2026-08-14 AND CAME BACK ON 2026-08-26, under two narrow verbs
@@ -50178,6 +50400,34 @@ ${gapNote}` : input.message;
50178
50400
  // binary naming them and gets a 404, which is the honest answer: its arguments do not fit this
50179
50401
  // registry (there is no `for:` in them at all), so an alias would have to invent the one field that
50180
50402
  // must not be guessed at.
50403
+ upsertOwnLink: mutation({
50404
+ input: UpsertOwnLinkInput,
50405
+ output: UpsertOwnLinkResult,
50406
+ handler: async ({ input }) => {
50407
+ const row = ctx.storage.getSession(input.slug);
50408
+ if (!row) throw new Error(`thread ${input.slug} is not registered`);
50409
+ if (row.state === "archived" || row.archived === 1) throw new Error("Reopen this thread before registering a link");
50410
+ const destination = resolveThreadLink(input.target, ctx.project.dir, openRoots);
50411
+ const link = ctx.storage.upsertThreadLink({
50412
+ id: `lnk_${randomUUID16().replace(/-/g, "").slice(0, 12)}`,
50413
+ slug: input.slug,
50414
+ label: input.label,
50415
+ ...destination,
50416
+ createdAtMs: Date.now()
50417
+ });
50418
+ ctx.board.refresh();
50419
+ return { link: threadLinkView(link) };
50420
+ }
50421
+ }),
50422
+ dropOwnLink: mutation({
50423
+ input: DropOwnLinkInput,
50424
+ output: DropOwnLinkResult,
50425
+ handler: async ({ input }) => {
50426
+ const dropped = ctx.storage.dropThreadLink(input.slug, input.id);
50427
+ if (dropped) ctx.board.refresh();
50428
+ return { dropped };
50429
+ }
50430
+ }),
50181
50431
  // ---- REGISTERED PR WATCHERS (add / drop / list) ---------------------------------------------
50182
50432
  // The worker's own PR watchers, from `mcp__frizz__watch_pr`. Same caller and therefore the same rules
50183
50433
  // as the timers above: slug-only (the MCP server outlives the session ids underneath it), and no
@@ -51121,6 +51371,7 @@ var init_router5 = __esm({
51121
51371
  init_open_external();
51122
51372
  init_local_file();
51123
51373
  init_project();
51374
+ init_thread_links();
51124
51375
  init_github();
51125
51376
  init_github_hovercard();
51126
51377
  init_dispatch();
@@ -51343,9 +51594,9 @@ var init_compression = __esm({
51343
51594
 
51344
51595
  // packages/server/src/local-image.ts
51345
51596
  import { readFileSync as readFileSync25, realpathSync as realpathSync10, statSync as statSync18 } from "node:fs";
51346
- import { extname as extname2, isAbsolute as isAbsolute5 } from "node:path";
51597
+ import { extname as extname2, isAbsolute as isAbsolute6 } from "node:path";
51347
51598
  function resolveLocalImage(rawPath) {
51348
- if (!rawPath || !isAbsolute5(rawPath)) return { status: 400 };
51599
+ if (!rawPath || !isAbsolute6(rawPath)) return { status: 400 };
51349
51600
  const contentType = IMAGE_CONTENT_TYPE[extname2(rawPath).toLowerCase()];
51350
51601
  if (!contentType) return { status: 400 };
51351
51602
  let real;
@@ -53336,30 +53587,111 @@ var init_boot_progress = __esm({
53336
53587
  }
53337
53588
  });
53338
53589
 
53590
+ // packages/server/src/runtime-lease.ts
53591
+ import { existsSync as existsSync14, mkdirSync as mkdirSync19, readdirSync as readdirSync12, readFileSync as readFileSync29, unlinkSync as unlinkSync6, writeFileSync as writeFileSync16 } from "node:fs";
53592
+ import { dirname as dirname19, join as join41 } from "node:path";
53593
+ function runtimeVersionDir(bin) {
53594
+ let dir = dirname19(bin);
53595
+ for (let step = 0; step < MAX_ASCENT; step++) {
53596
+ if (existsSync14(join41(dir, PROVISIONED_MARKER))) return dir;
53597
+ const parent = dirname19(dir);
53598
+ if (parent === dir) return void 0;
53599
+ dir = parent;
53600
+ }
53601
+ return void 0;
53602
+ }
53603
+ function pidAlive4(pid) {
53604
+ try {
53605
+ process.kill(pid, 0);
53606
+ return true;
53607
+ } catch (error) {
53608
+ return error.code === "EPERM";
53609
+ }
53610
+ }
53611
+ function leaseRuntime(bin, role) {
53612
+ const versionDir = runtimeVersionDir(bin);
53613
+ if (!versionDir) return () => {
53614
+ };
53615
+ const path3 = join41(versionDir, LEASES_DIR, `${process.pid}.json`);
53616
+ try {
53617
+ mkdirSync19(dirname19(path3), { recursive: true });
53618
+ const lease = { pid: process.pid, role, bin, at: (/* @__PURE__ */ new Date()).toISOString() };
53619
+ writeFileSync16(path3, JSON.stringify(lease));
53620
+ } catch {
53621
+ return () => {
53622
+ };
53623
+ }
53624
+ return () => {
53625
+ try {
53626
+ unlinkSync6(path3);
53627
+ } catch {
53628
+ }
53629
+ };
53630
+ }
53631
+ function liveRuntimeLeases(versionDir) {
53632
+ const dir = join41(versionDir, LEASES_DIR);
53633
+ let names;
53634
+ try {
53635
+ names = readdirSync12(dir);
53636
+ } catch {
53637
+ return [];
53638
+ }
53639
+ const live = [];
53640
+ for (const name of names) {
53641
+ if (!name.endsWith(".json")) continue;
53642
+ const path3 = join41(dir, name);
53643
+ let lease;
53644
+ try {
53645
+ lease = JSON.parse(readFileSync29(path3, "utf8"));
53646
+ } catch {
53647
+ continue;
53648
+ }
53649
+ if (typeof lease.pid !== "number" || !pidAlive4(lease.pid)) {
53650
+ try {
53651
+ unlinkSync6(path3);
53652
+ } catch {
53653
+ }
53654
+ ;
53655
+ continue;
53656
+ }
53657
+ live.push({ pid: lease.pid, role: typeof lease.role === "string" ? lease.role : "unknown", bin: typeof lease.bin === "string" ? lease.bin : "", at: typeof lease.at === "string" ? lease.at : "" });
53658
+ }
53659
+ return live;
53660
+ }
53661
+ var PROVISIONED_MARKER, LEASES_DIR, MAX_ASCENT;
53662
+ var init_runtime_lease = __esm({
53663
+ "packages/server/src/runtime-lease.ts"() {
53664
+ "use strict";
53665
+ PROVISIONED_MARKER = "provisioned.json";
53666
+ LEASES_DIR = ".leases";
53667
+ MAX_ASCENT = 6;
53668
+ }
53669
+ });
53670
+
53339
53671
  // packages/server/src/runtimes.ts
53340
53672
  import { createHash as createHash15 } from "node:crypto";
53341
53673
  import {
53342
53674
  chmodSync,
53343
53675
  closeSync as closeSync16,
53344
- existsSync as existsSync14,
53345
- mkdirSync as mkdirSync19,
53676
+ existsSync as existsSync15,
53677
+ mkdirSync as mkdirSync20,
53346
53678
  openSync as openSync17,
53347
- readFileSync as readFileSync29,
53348
- readdirSync as readdirSync12,
53679
+ readFileSync as readFileSync30,
53680
+ readdirSync as readdirSync13,
53349
53681
  renameSync as renameSync11,
53350
53682
  rmSync as rmSync12,
53351
53683
  statSync as statSync22,
53352
- writeFileSync as writeFileSync16,
53684
+ writeFileSync as writeFileSync17,
53353
53685
  writeSync as writeSync2
53354
53686
  } from "node:fs";
53355
- import { dirname as dirname19, isAbsolute as isAbsolute6, join as join41, normalize, relative as relative3, sep as sep5 } from "node:path";
53687
+ import { dirname as dirname20, isAbsolute as isAbsolute7, join as join42, normalize, relative as relative3, sep as sep5 } from "node:path";
53356
53688
  import { Readable, Transform, Writable as Writable3 } from "node:stream";
53357
53689
  import { pipeline } from "node:stream/promises";
53358
53690
  import { createGunzip } from "node:zlib";
53359
53691
  function runtimesRoot(env = process.env) {
53360
53692
  const override = env[RUNTIMES_DIR_ENV];
53361
- if (override && isAbsolute6(override)) return override;
53362
- return join41(frizzRoots().cache, "runtimes");
53693
+ if (override && isAbsolute7(override)) return override;
53694
+ return join42(frizzRoots().cache, "runtimes");
53363
53695
  }
53364
53696
  function isMusl() {
53365
53697
  if (process.platform !== "linux") return false;
@@ -53376,16 +53708,16 @@ function runtimeCoordinates(backend, platform3 = process.platform, arch = proces
53376
53708
  return { pkg: "@openai/codex", packageVersion: `${CODEX_VERSION}-${platform3}-${arch}`, label: CODEX_VERSION, binary: `codex${exe}` };
53377
53709
  }
53378
53710
  function provisionedBinary(backend, root, label) {
53379
- const dir = join41(root, backend, label);
53711
+ const dir = join42(root, backend, label);
53380
53712
  let marker;
53381
53713
  try {
53382
- marker = JSON.parse(readFileSync29(join41(dir, MARKER), "utf8"));
53714
+ marker = JSON.parse(readFileSync30(join42(dir, MARKER), "utf8"));
53383
53715
  } catch {
53384
53716
  return void 0;
53385
53717
  }
53386
53718
  if (typeof marker.binary !== "string" || marker.label !== label) return void 0;
53387
- const bin = join41(dir, marker.binary);
53388
- return existsSync14(bin) ? bin : void 0;
53719
+ const bin = join42(dir, marker.binary);
53720
+ return existsSync15(bin) ? bin : void 0;
53389
53721
  }
53390
53722
  function octal(buffer, offset, length) {
53391
53723
  const text2 = buffer.toString("latin1", offset, offset + length).replace(/\0.*$/su, "").trim();
@@ -53398,27 +53730,27 @@ function safeEntryPath(dest, name) {
53398
53730
  const parts = name.split("/").filter((part) => part.length > 0);
53399
53731
  if (parts.length < 2) return void 0;
53400
53732
  const rel = normalize(parts.slice(1).join(sep5));
53401
- if (isAbsolute6(rel) || rel === ".." || rel.startsWith(`..${sep5}`)) return void 0;
53402
- const full = join41(dest, rel);
53733
+ if (isAbsolute7(rel) || rel === ".." || rel.startsWith(`..${sep5}`)) return void 0;
53734
+ const full = join42(dest, rel);
53403
53735
  const back = relative3(dest, full);
53404
- if (back.startsWith("..") || isAbsolute6(back)) return void 0;
53736
+ if (back.startsWith("..") || isAbsolute7(back)) return void 0;
53405
53737
  return full;
53406
53738
  }
53407
53739
  function locateBinary(dir, backend, binary) {
53408
53740
  if (backend === "claude") {
53409
- const direct = join41(dir, binary);
53410
- return existsSync14(direct) ? direct : void 0;
53741
+ const direct = join42(dir, binary);
53742
+ return existsSync15(direct) ? direct : void 0;
53411
53743
  }
53412
- const vendor = join41(dir, "vendor");
53744
+ const vendor = join42(dir, "vendor");
53413
53745
  let triples;
53414
53746
  try {
53415
- triples = readdirSync12(vendor);
53747
+ triples = readdirSync13(vendor);
53416
53748
  } catch {
53417
53749
  return void 0;
53418
53750
  }
53419
53751
  for (const triple of triples) {
53420
- const candidate = join41(vendor, triple, "bin", binary);
53421
- if (existsSync14(candidate)) return candidate;
53752
+ const candidate = join42(vendor, triple, "bin", binary);
53753
+ if (existsSync15(candidate)) return candidate;
53422
53754
  }
53423
53755
  return void 0;
53424
53756
  }
@@ -53433,12 +53765,12 @@ async function provisionRuntime(backend, options) {
53433
53765
  const registry = options.registry ?? REGISTRY;
53434
53766
  const progress = options.onProgress ?? (() => {
53435
53767
  });
53436
- const backendDir = join41(root, backend);
53437
- const finalDir = join41(backendDir, coordinates.label);
53438
- const partial = join41(backendDir, `.partial-${coordinates.label}-${process.pid}`);
53439
- mkdirSync19(backendDir, { recursive: true });
53768
+ const backendDir = join42(root, backend);
53769
+ const finalDir = join42(backendDir, coordinates.label);
53770
+ const partial = join42(backendDir, `.partial-${coordinates.label}-${process.pid}`);
53771
+ mkdirSync20(backendDir, { recursive: true });
53440
53772
  rmSync12(partial, { recursive: true, force: true });
53441
- mkdirSync19(partial);
53773
+ mkdirSync20(partial);
53442
53774
  try {
53443
53775
  const manifestUrl = `${registry}/${coordinates.pkg.replace("/", "%2F")}/${coordinates.packageVersion}`;
53444
53776
  progress(`resolving ${coordinates.pkg}@${coordinates.packageVersion}`);
@@ -53482,7 +53814,7 @@ async function provisionRuntime(backend, options) {
53482
53814
  binary: relative3(partial, bin),
53483
53815
  at: (/* @__PURE__ */ new Date()).toISOString()
53484
53816
  };
53485
- writeFileSync16(join41(partial, MARKER), `${JSON.stringify(marker, null, 2)}
53817
+ writeFileSync17(join42(partial, MARKER), `${JSON.stringify(marker, null, 2)}
53486
53818
  `, "utf8");
53487
53819
  try {
53488
53820
  renameSync11(partial, finalDir);
@@ -53493,35 +53825,41 @@ async function provisionRuntime(backend, options) {
53493
53825
  return { bin: theirs, label: coordinates.label, fetched: false };
53494
53826
  }
53495
53827
  progress("ready");
53496
- return { bin: join41(finalDir, marker.binary), label: coordinates.label, fetched: true };
53828
+ return { bin: join42(finalDir, marker.binary), label: coordinates.label, fetched: true };
53497
53829
  } catch (err) {
53498
53830
  rmSync12(partial, { recursive: true, force: true });
53499
53831
  throw err;
53500
53832
  }
53501
53833
  }
53502
53834
  function sweepRuntimes(backend, root, keepLabel, now = Date.now()) {
53503
- const backendDir = join41(root, backend);
53835
+ const backendDir = join42(root, backend);
53504
53836
  let entries2;
53505
53837
  try {
53506
- entries2 = readdirSync12(backendDir);
53838
+ entries2 = readdirSync13(backendDir);
53507
53839
  } catch {
53508
- return [];
53840
+ return { removed: [], kept: [] };
53509
53841
  }
53510
- const removed = [];
53842
+ const sweep = { removed: [], kept: [] };
53511
53843
  for (const entry of entries2) {
53512
53844
  if (entry === keepLabel) continue;
53513
- const full = join41(backendDir, entry);
53845
+ const full = join42(backendDir, entry);
53514
53846
  if (entry.startsWith(".partial-")) {
53515
53847
  try {
53516
53848
  if (now - statSync22(full).mtimeMs < 24 * 60 * 60 * 1e3) continue;
53517
53849
  } catch {
53518
53850
  continue;
53519
53851
  }
53852
+ } else {
53853
+ const leases = liveRuntimeLeases(full);
53854
+ if (leases.length > 0) {
53855
+ sweep.kept.push({ dir: full, leases: leases.map(({ pid, role }) => ({ pid, role })) });
53856
+ continue;
53857
+ }
53520
53858
  }
53521
53859
  rmSync12(full, { recursive: true, force: true });
53522
- removed.push(full);
53860
+ sweep.removed.push(full);
53523
53861
  }
53524
- return removed;
53862
+ return sweep;
53525
53863
  }
53526
53864
  async function resolveOne(backend, explicit, options) {
53527
53865
  const env = options.env ?? process.env;
@@ -53547,7 +53885,12 @@ async function resolveOne(backend, explicit, options) {
53547
53885
  onProgress: (message) => options.onProgress?.(backend, message)
53548
53886
  });
53549
53887
  if (provisioned.fetched) log3("info", `runtimes: provisioned ${backend} ${provisioned.label} at ${provisioned.bin}`);
53550
- for (const retired of sweepRuntimes(backend, root, coordinates.label)) log3("info", `runtimes: retired ${retired}`);
53888
+ leaseRuntime(provisioned.bin, "server");
53889
+ const sweep = sweepRuntimes(backend, root, coordinates.label);
53890
+ for (const retired of sweep.removed) log3("info", `runtimes: retired ${retired}`);
53891
+ for (const { dir, leases } of sweep.kept) {
53892
+ log3("info", `runtimes: kept ${dir} \u2014 still used by ${leases.map((lease) => `${lease.role} pid ${lease.pid}`).join(", ")}`);
53893
+ }
53551
53894
  return { bin: provisioned.bin, source: "provisioned", version: provisioned.label };
53552
53895
  } catch (err) {
53553
53896
  const note = err instanceof Error ? err.message : String(err);
@@ -53578,6 +53921,7 @@ var init_runtimes = __esm({
53578
53921
  "use strict";
53579
53922
  init_codex_app_server();
53580
53923
  init_frizz_paths();
53924
+ init_runtime_lease();
53581
53925
  CLAUDE_AGENT_SDK_VERSION = "0.3.268";
53582
53926
  CLAUDE_CODE_VERSION = "2.1.268";
53583
53927
  CODEX_VERSION = CODEX_APP_SERVER_SUPPORTED_VERSION;
@@ -53668,7 +54012,7 @@ var init_runtimes = __esm({
53668
54012
  return;
53669
54013
  case "5": {
53670
54014
  const dir = safeEntryPath(this.dest, name);
53671
- if (dir) mkdirSync19(dir, { recursive: true });
54015
+ if (dir) mkdirSync20(dir, { recursive: true });
53672
54016
  return;
53673
54017
  }
53674
54018
  case "0":
@@ -53676,7 +54020,7 @@ var init_runtimes = __esm({
53676
54020
  case "7": {
53677
54021
  const file = safeEntryPath(this.dest, name);
53678
54022
  if (!file) return;
53679
- mkdirSync19(dirname19(file), { recursive: true });
54023
+ mkdirSync20(dirname20(file), { recursive: true });
53680
54024
  this.fd = openSync17(file, "w", octal(header, 100, 8) & 511 || 420);
53681
54025
  this.written.push(file);
53682
54026
  return;
@@ -54248,18 +54592,18 @@ var init_legacy_project_db = __esm({
54248
54592
  });
54249
54593
 
54250
54594
  // packages/server/src/frizz-db.ts
54251
- import { existsSync as existsSync15, mkdirSync as mkdirSync20, readdirSync as readdirSync13, renameSync as renameSync12, statSync as statSync23 } from "node:fs";
54252
- import { basename as basename13, dirname as dirname20, join as join42 } from "node:path";
54595
+ import { existsSync as existsSync16, mkdirSync as mkdirSync21, readdirSync as readdirSync14, renameSync as renameSync12, statSync as statSync23 } from "node:fs";
54596
+ import { basename as basename13, dirname as dirname21, join as join43 } from "node:path";
54253
54597
  function frizzDatabaseLocation(options) {
54254
54598
  if (options.stateDir !== void 0) {
54255
- const projectsDir = dirname20(options.stateDir);
54599
+ const projectsDir = dirname21(options.stateDir);
54256
54600
  if (basename13(projectsDir) === "projects") {
54257
- return { path: options.path ?? join42(dirname20(projectsDir), "ui.db"), projectsDir };
54601
+ return { path: options.path ?? join43(dirname21(projectsDir), "ui.db"), projectsDir };
54258
54602
  }
54259
- return { path: options.path ?? join42(options.stateDir, "ui.db"), projectsDir: void 0 };
54603
+ return { path: options.path ?? join43(options.stateDir, "ui.db"), projectsDir: void 0 };
54260
54604
  }
54261
54605
  const data = options.home ? frizzPaths({ home: options.home }).data : frizzRoots().data;
54262
- return { path: options.path ?? join42(data, "ui.db"), projectsDir: join42(data, "projects") };
54606
+ return { path: options.path ?? join43(data, "ui.db"), projectsDir: join43(data, "projects") };
54263
54607
  }
54264
54608
  function ensureFrizzSchema(db) {
54265
54609
  db.exec(`
@@ -54278,7 +54622,7 @@ function ensureFrizzSchema(db) {
54278
54622
  }
54279
54623
  function openFrizzDatabase(options = {}) {
54280
54624
  const { path: path3, projectsDir } = frizzDatabaseLocation(options);
54281
- mkdirSync20(dirname20(path3), { recursive: true });
54625
+ mkdirSync21(dirname21(path3), { recursive: true });
54282
54626
  const db = new sqlite_default(path3);
54283
54627
  db.pragma("busy_timeout = 5000");
54284
54628
  db.pragma("journal_mode = WAL");
@@ -54290,10 +54634,10 @@ function openFrizzDatabase(options = {}) {
54290
54634
  );
54291
54635
  for (const projectId of retiredLegacyProjectIds(projectsDir)) {
54292
54636
  if (alreadyImported.has(projectId)) continue;
54293
- const dir = join42(projectsDir, projectId);
54637
+ const dir = join43(projectsDir, projectId);
54294
54638
  try {
54295
- renameSync12(join42(dir, "ui.db.imported"), join42(dir, "ui.db"));
54296
- db.prepare("INSERT INTO imported_project (project_id, source, rows, imported_at) VALUES (?, ?, 0, ?)").run(projectId, join42(dir, "ui.db"), (/* @__PURE__ */ new Date()).toISOString());
54639
+ renameSync12(join43(dir, "ui.db.imported"), join43(dir, "ui.db"));
54640
+ db.prepare("INSERT INTO imported_project (project_id, source, rows, imported_at) VALUES (?, ?, 0, ?)").run(projectId, join43(dir, "ui.db"), (/* @__PURE__ */ new Date()).toISOString());
54297
54641
  alreadyImported.add(projectId);
54298
54642
  log.info("frizz-db", `${projectId}: restored ui.db.imported to ui.db and recorded it as imported`);
54299
54643
  } catch (error) {
@@ -54303,7 +54647,7 @@ function openFrizzDatabase(options = {}) {
54303
54647
  }
54304
54648
  for (const projectId of legacyProjectIds(projectsDir)) {
54305
54649
  if (alreadyImported.has(projectId)) continue;
54306
- const legacyPath = join42(projectsDir, projectId, "ui.db");
54650
+ const legacyPath = join43(projectsDir, projectId, "ui.db");
54307
54651
  try {
54308
54652
  const rows = importLegacyProject(db, projectId, legacyPath);
54309
54653
  imported.push({ projectId, rows });
@@ -54333,10 +54677,10 @@ function openFrizzDatabase(options = {}) {
54333
54677
  };
54334
54678
  }
54335
54679
  function projectIdsWithFile(projectsDir, file) {
54336
- if (!existsSync15(projectsDir)) return [];
54337
- return readdirSync13(projectsDir).filter((name) => {
54680
+ if (!existsSync16(projectsDir)) return [];
54681
+ return readdirSync14(projectsDir).filter((name) => {
54338
54682
  try {
54339
- return statSync23(join42(projectsDir, name, file)).isFile();
54683
+ return statSync23(join43(projectsDir, name, file)).isFile();
54340
54684
  } catch {
54341
54685
  return false;
54342
54686
  }
@@ -54346,7 +54690,7 @@ function legacyProjectIds(projectsDir) {
54346
54690
  return projectIdsWithFile(projectsDir, "ui.db");
54347
54691
  }
54348
54692
  function retiredLegacyProjectIds(projectsDir) {
54349
- return projectIdsWithFile(projectsDir, "ui.db.imported").filter((id) => !existsSync15(join42(projectsDir, id, "ui.db")));
54693
+ return projectIdsWithFile(projectsDir, "ui.db.imported").filter((id) => !existsSync16(join43(projectsDir, id, "ui.db")));
54350
54694
  }
54351
54695
  function tableColumns(db, schema, table) {
54352
54696
  return db.prepare(`PRAGMA ${schema}.table_info(${table})`).all().map((c) => c.name);
@@ -54502,7 +54846,7 @@ var init_tenant_prime = __esm({
54502
54846
  // packages/server/src/project-teardown.ts
54503
54847
  import { rmSync as rmSync13 } from "node:fs";
54504
54848
  import { homedir as homedir19 } from "node:os";
54505
- import { dirname as dirname21, resolve as resolve10 } from "node:path";
54849
+ import { dirname as dirname22, resolve as resolve10 } from "node:path";
54506
54850
  async function stopProjectWorkers(ctx) {
54507
54851
  let stopped = 0;
54508
54852
  for (const row of ctx.storage.allSessions()) {
@@ -54522,7 +54866,7 @@ function deleteProjectState(projectId, home = homedir19()) {
54522
54866
  if (!/^[\w.-]+$/u.test(projectId) || projectId === "." || projectId === "..") {
54523
54867
  throw new Error(`Refusing to delete state for a malformed project id: ${projectId}`);
54524
54868
  }
54525
- if (dirname21(resolve10(target)) !== resolve10(dirname21(projectStateDir("x", home)))) {
54869
+ if (dirname22(resolve10(target)) !== resolve10(dirname22(projectStateDir("x", home)))) {
54526
54870
  throw new Error(`Refusing to delete ${target} \u2014 it is not a project state directory`);
54527
54871
  }
54528
54872
  rmSync13(target, { recursive: true, force: true });
@@ -54551,11 +54895,11 @@ __export(index_exports, {
54551
54895
  unknownProjectPage: () => unknownProjectPage
54552
54896
  });
54553
54897
  import { createServer } from "node:http";
54554
- import { readFileSync as readFileSync30, existsSync as existsSync16, statSync as statSync24 } from "node:fs";
54555
- import { dirname as dirname22, join as join43, resolve as resolve11, extname as extname4, normalize as normalize2, sep as sep6 } from "node:path";
54898
+ import { readFileSync as readFileSync31, existsSync as existsSync17, statSync as statSync24 } from "node:fs";
54899
+ import { dirname as dirname23, join as join44, resolve as resolve11, extname as extname4, normalize as normalize2, sep as sep6 } from "node:path";
54556
54900
  function readServerAddressHolder(path3) {
54557
54901
  try {
54558
- const value = JSON.parse(readFileSync30(path3, "utf8"));
54902
+ const value = JSON.parse(readFileSync31(path3, "utf8"));
54559
54903
  if (typeof value?.pid !== "number") return void 0;
54560
54904
  return { pid: value.pid, port: typeof value.port === "number" ? value.port : -1 };
54561
54905
  } catch {
@@ -54680,12 +55024,12 @@ function staticIsFresh(req, etag, mtimeMs) {
54680
55024
  }
54681
55025
  function serveStatic(distDir, req, res) {
54682
55026
  const rel = normalize2((req.url ?? "/").split("?")[0]).replace(/^(\.\.[/\\])+/, "");
54683
- let file = join43(distDir, rel === "/" ? "index.html" : rel);
54684
- if (!file.startsWith(distDir)) file = join43(distDir, "index.html");
54685
- if (!existsSync16(file)) file = join43(distDir, "index.html");
55027
+ let file = join44(distDir, rel === "/" ? "index.html" : rel);
55028
+ if (!file.startsWith(distDir)) file = join44(distDir, "index.html");
55029
+ if (!existsSync17(file)) file = join44(distDir, "index.html");
54686
55030
  try {
54687
55031
  const stats = statSync24(file);
54688
- const cacheControl = file.startsWith(join43(distDir, "assets") + sep6) ? STATIC_CACHE_IMMUTABLE : STATIC_CACHE_REVALIDATE;
55032
+ const cacheControl = file.startsWith(join44(distDir, "assets") + sep6) ? STATIC_CACHE_IMMUTABLE : STATIC_CACHE_REVALIDATE;
54689
55033
  const etag = `"${stats.size.toString(16)}-${Math.trunc(stats.mtimeMs).toString(16)}"`;
54690
55034
  const headers = {
54691
55035
  "content-type": MIME[extname4(file)] ?? "application/octet-stream",
@@ -54701,7 +55045,7 @@ function serveStatic(distDir, req, res) {
54701
55045
  res.end();
54702
55046
  return;
54703
55047
  }
54704
- let body = readFileSync30(file);
55048
+ let body = readFileSync31(file);
54705
55049
  const accept = req.headers["accept-encoding"];
54706
55050
  const encoding = negotiateEncoding(Array.isArray(accept) ? accept.join(",") : accept);
54707
55051
  if (encoding && shouldCompress(new Headers({ "content-type": headers["content-type"] }), body.byteLength)) {
@@ -54963,7 +55307,7 @@ async function startServer(opts = {}) {
54963
55307
  if (statusPath && statusIdentity) runtime.removeStatus(statusPath, statusIdentity);
54964
55308
  if (statusPath && statusIdentity) {
54965
55309
  try {
54966
- runtime.removeStatus(serverAddressPathForStateDir(dirname22(statusPath)), statusIdentity);
55310
+ runtime.removeStatus(serverAddressPathForStateDir(dirname23(statusPath)), statusIdentity);
54967
55311
  } catch {
54968
55312
  }
54969
55313
  }
@@ -55101,7 +55445,7 @@ async function startServer(opts = {}) {
55101
55445
  }
55102
55446
  statusPath = serverLockPathFor(ctx.project);
55103
55447
  const webRoot = resolve11(import.meta.dirname, "..", "..", "web");
55104
- const distDir = opts.webDistDir ? resolve11(opts.webDistDir) : join43(webRoot, "dist");
55448
+ const distDir = opts.webDistDir ? resolve11(opts.webDistDir) : join44(webRoot, "dist");
55105
55449
  startupPhase = "Vite";
55106
55450
  if (opts.dev) {
55107
55451
  try {
@@ -55177,7 +55521,7 @@ async function startServer(opts = {}) {
55177
55521
  if (vite) {
55178
55522
  vite.middlewares(req, res, () => {
55179
55523
  try {
55180
- const html = readFileSync30(join43(webRoot, "index.html"), "utf8");
55524
+ const html = readFileSync31(join44(webRoot, "index.html"), "utf8");
55181
55525
  void vite.transformIndexHtml(url, html).then((out) => {
55182
55526
  res.writeHead(200, { "content-type": "text/html" });
55183
55527
  res.end(out);
@@ -55189,7 +55533,7 @@ async function startServer(opts = {}) {
55189
55533
  });
55190
55534
  return;
55191
55535
  }
55192
- if (existsSync16(distDir)) {
55536
+ if (existsSync17(distDir)) {
55193
55537
  serveStatic(distDir, req, res);
55194
55538
  return;
55195
55539
  }
@@ -55387,7 +55731,7 @@ var init_index = __esm({
55387
55731
  }
55388
55732
  };
55389
55733
  isApiUrl = (url) => url === FRIZZ_ROUTE_PREFIX || url.startsWith(`${FRIZZ_ROUTE_PREFIX}/`);
55390
- serverLockPathFor = (project) => join43(project.stateDir, "server.lock");
55734
+ serverLockPathFor = (project) => join44(project.stateDir, "server.lock");
55391
55735
  MIME = {
55392
55736
  ".html": "text/html",
55393
55737
  ".js": "text/javascript",
@@ -55410,8 +55754,8 @@ init_project();
55410
55754
  init_project_launch();
55411
55755
  init_shutdown();
55412
55756
  init_logging();
55413
- import { existsSync as existsSync17 } from "node:fs";
55414
- import { join as join44 } from "node:path";
55757
+ import { existsSync as existsSync18 } from "node:fs";
55758
+ import { join as join45 } from "node:path";
55415
55759
  process.on("uncaughtException", (error) => {
55416
55760
  log.error("dev-child", `uncaught exception: ${error instanceof Error ? error.stack ?? error.message : error}`);
55417
55761
  process.exit(1);
@@ -55449,9 +55793,9 @@ try {
55449
55793
  ["FRIZZ_SCRIPTS_DIR", process.env.FRIZZ_SCRIPTS_DIR, "index.mjs"],
55450
55794
  ["FRIZZ_WORKER_PLUGIN_DIR", process.env.FRIZZ_WORKER_PLUGIN_DIR, ".claude-plugin/plugin.json"]
55451
55795
  ];
55452
- if (!existsSync17(stableWebDist)) throw new Error("stable artifact launch is missing its verified web directory");
55796
+ if (!existsSync18(stableWebDist)) throw new Error("stable artifact launch is missing its verified web directory");
55453
55797
  for (const [name, directory, requiredFile] of required) {
55454
- if (!directory || !existsSync17(join44(directory, requiredFile)))
55798
+ if (!directory || !existsSync18(join45(directory, requiredFile)))
55455
55799
  throw new Error(`stable artifact launch is missing verified ${name}`);
55456
55800
  }
55457
55801
  }