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