frizz 0.7.8 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/claude-agent-broker.js +65 -5
- package/dist/dev-child.js +457 -553
- package/dist/frizz.js +96 -9
- package/package.json +1 -1
- package/runtime/cc-worker/.claude-plugin/plugin.json +1 -1
- package/runtime/cc-worker/DECISIONS.md +29 -1
- 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/hooks/agent-dispatch.mjs +10 -9
- 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-ubcEP7X3.js → TerminalPane-BM_laA_I.js} +1 -1
- package/web-dist/assets/{abnfDiagram-VRR7QNED-0brfnieb.js → abnfDiagram-VRR7QNED-C5FCLi1n.js} +1 -1
- package/web-dist/assets/architecture-TIHT7OUA-ngY1F0r2.js +1 -0
- package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-Dji-XBdF.js → architectureDiagram-ZJ3FMSHR-Dm7tUnwt.js} +1 -1
- package/web-dist/assets/{blockDiagram-677ZJIJ3-BQMyz3Q9.js → blockDiagram-677ZJIJ3-3EhpmwKM.js} +1 -1
- package/web-dist/assets/{c4Diagram-LMCZKHZV-BMNws0Oi.js → c4Diagram-LMCZKHZV-Ca-BjDxw.js} +1 -1
- package/web-dist/assets/channel-BpyNaJoi.js +1 -0
- package/web-dist/assets/{chunk-32BRIVSS-CVv1ae7n.js → chunk-32BRIVSS-xGbaUlTN.js} +1 -1
- package/web-dist/assets/{chunk-52WLFC77-BYhPlKCT.js → chunk-52WLFC77-YYhE3F1a.js} +1 -1
- package/web-dist/assets/{chunk-C7G6YPKG-Cn4HhNwb.js → chunk-C7G6YPKG-SYKXFtUg.js} +1 -1
- package/web-dist/assets/{chunk-EX3LRPZG-P3ZYFC1W.js → chunk-EX3LRPZG-cqjtdM_c.js} +1 -1
- package/web-dist/assets/{chunk-FWX5IMBZ-Ba3NpEQa.js → chunk-FWX5IMBZ-qyaTngKT.js} +2 -2
- package/web-dist/assets/{chunk-HOUHSVGY-BCWyMhwJ.js → chunk-HOUHSVGY-DBFt1HnE.js} +1 -1
- package/web-dist/assets/{chunk-ICXQ74PX-DWAAqHL7.js → chunk-ICXQ74PX-Bme_2_gm.js} +1 -1
- package/web-dist/assets/{chunk-MOJQB5TN-pc5VJ4Nq.js → chunk-MOJQB5TN-DupTmWhN.js} +1 -1
- package/web-dist/assets/{chunk-OGEWGWER-DUxmbiuX.js → chunk-OGEWGWER-DSqmbKCq.js} +1 -1
- package/web-dist/assets/{chunk-PUDLZKDR-DRdZWzxX.js → chunk-PUDLZKDR-DVnHUoFN.js} +1 -1
- package/web-dist/assets/{chunk-Q4XR5HBZ-Bafzu7s-.js → chunk-Q4XR5HBZ-BIZh_Yuh.js} +1 -1
- package/web-dist/assets/{chunk-V7JOEXUC-Bj0A8ftW.js → chunk-V7JOEXUC-e2G7UgOg.js} +1 -1
- package/web-dist/assets/{chunk-VAUOI2AC-DBgVQKIO.js → chunk-VAUOI2AC-uYgyYWPh.js} +1 -1
- package/web-dist/assets/{chunk-VR4S4FIN-DU-1KtuM.js → chunk-VR4S4FIN-BjGjcexQ.js} +1 -1
- package/web-dist/assets/{chunk-WYO6CB5R-DzVb3-Rj.js → chunk-WYO6CB5R-BKKd_W3B.js} +1 -1
- package/web-dist/assets/{chunk-ZGVPDNZ5-BPKQvduj.js → chunk-ZGVPDNZ5-D5_hsjnF.js} +1 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-DTEWwxt7.js +1 -0
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-DTEWwxt7.js +1 -0
- package/web-dist/assets/{cynefin-VYW2F7L2-lo8PqVCk.js → cynefin-VYW2F7L2-bkFv7EpV.js} +1 -1
- package/web-dist/assets/{cynefinDiagram-TSTJHNR4-BtQLaDV0.js → cynefinDiagram-TSTJHNR4-Du2-dyuZ.js} +1 -1
- package/web-dist/assets/{dagre-VKFMJZFB-BJ2-f-A0.js → dagre-VKFMJZFB-CU7w00Ul.js} +1 -1
- package/web-dist/assets/{diagram-FQU43EPY-CLM-8r74.js → diagram-FQU43EPY-BxvVgxWb.js} +1 -1
- package/web-dist/assets/{diagram-G47NLZAW-CfuoJGpN.js → diagram-G47NLZAW-D8VvetwQ.js} +1 -1
- package/web-dist/assets/{diagram-NH7WQ7WH-DfxxRwH8.js → diagram-NH7WQ7WH-CwsChkED.js} +1 -1
- package/web-dist/assets/{diagram-OA4YK3LP-D7knxZee.js → diagram-OA4YK3LP-BIPYbe6L.js} +1 -1
- package/web-dist/assets/{diagram-WEI45ONY-C2WfMXWl.js → diagram-WEI45ONY-BoyH4dKl.js} +1 -1
- package/web-dist/assets/{ebnfDiagram-CCIWWBDH-DRSZ24tF.js → ebnfDiagram-CCIWWBDH-9A3WRDfU.js} +1 -1
- package/web-dist/assets/{erDiagram-Q63AITRT-DXe0OrbY.js → erDiagram-Q63AITRT-D3Y9WA4H.js} +1 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-D7g9kDF0.js +1 -0
- package/web-dist/assets/flowDiagram-23GEKE2U-BqKnxVr3.js +1 -0
- package/web-dist/assets/{ganttDiagram-NO4QXBWP-wGqOgjvs.js → ganttDiagram-NO4QXBWP-Bk2-LClm.js} +1 -1
- package/web-dist/assets/{gitGraph-TEB2WS4Q-BygokG6y.js → gitGraph-TEB2WS4Q-CXvPrsio.js} +1 -1
- package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-CV1933_M.js → gitGraphDiagram-IHSO6WYX-BC9D72kT.js} +1 -1
- package/web-dist/assets/index-CCtNIGi6.js +491 -0
- package/web-dist/assets/index-Cq79m8AR.css +1 -0
- package/web-dist/assets/{info-DKCQHKI2-DLavbzui.js → info-DKCQHKI2-Beag76kE.js} +1 -1
- package/web-dist/assets/{infoDiagram-FWYZ7A6U-Biouxd9G.js → infoDiagram-FWYZ7A6U-DaqYXHYB.js} +1 -1
- package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-DJxwwS3R.js → ishikawaDiagram-FXEZZL3T-D-d4tmLU.js} +1 -1
- package/web-dist/assets/{journeyDiagram-5HDEW3XC-Clx_WkdW.js → journeyDiagram-5HDEW3XC-kkFYzNg1.js} +1 -1
- package/web-dist/assets/{kanban-definition-HUTT4EX6-Bf7bX_96.js → kanban-definition-HUTT4EX6-BYRPjbfv.js} +1 -1
- package/web-dist/assets/{line-CV2v49IN.js → line-C8l7y6rG.js} +1 -1
- package/web-dist/assets/{mermaid-parser.core-BPsFhEi6.js → mermaid-parser.core-BG3fgy77.js} +3 -3
- package/web-dist/assets/{mermaid.core-0EWDNL_R.js → mermaid.core-BCDzcbhr.js} +3 -3
- package/web-dist/assets/{mindmap-definition-LN4V7U3C-C1y4yy3Z.js → mindmap-definition-LN4V7U3C-CP_bgQm8.js} +1 -1
- package/web-dist/assets/{packet-7NZHBO7P-CpcpxruB.js → packet-7NZHBO7P-Dgd69pFS.js} +1 -1
- package/web-dist/assets/{pegDiagram-2B236MQR-CVlVP4cH.js → pegDiagram-2B236MQR-BJUb_DOD.js} +1 -1
- package/web-dist/assets/{pie-RZYD4A2V-BcVPOhTZ.js → pie-RZYD4A2V-DPbVRAuL.js} +1 -1
- package/web-dist/assets/{pieDiagram-ENE6RG2P-DZv4Tfae.js → pieDiagram-ENE6RG2P-DavUeOqM.js} +1 -1
- package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-DOzS_U_a.js → quadrantDiagram-ABIIQ3AL-BdvoSedu.js} +1 -1
- package/web-dist/assets/{radar-I7S5WNFK-8YADxkdT.js → radar-I7S5WNFK-C2TAta9K.js} +1 -1
- package/web-dist/assets/{railroad-3IZDKUUU-DzndPrRy.js → railroad-3IZDKUUU-DEMjzYqC.js} +1 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-_4FByoRI.js +1 -0
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-DKAvMIMu.js +1 -0
- package/web-dist/assets/railroad-peg-LSFZ7HO6-CkJl3c69.js +1 -0
- package/web-dist/assets/{railroadDiagram-RFXS5EU6-CPJAH-r7.js → railroadDiagram-RFXS5EU6-C4rERo6H.js} +1 -1
- package/web-dist/assets/{requirementDiagram-TGXJPOKE-BVFJZfwl.js → requirementDiagram-TGXJPOKE-DlIG9D0n.js} +1 -1
- package/web-dist/assets/{sankeyDiagram-HTMAVEWB-CUhvFPmE.js → sankeyDiagram-HTMAVEWB-DVMrG-qY.js} +1 -1
- package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-Cy9CGhNq.js → sequenceDiagram-DBY2YBRQ-DDcDM_-e.js} +1 -1
- package/web-dist/assets/{stateDiagram-2N3HPSRC-CqOnmPLp.js → stateDiagram-2N3HPSRC-DuCRKU5t.js} +1 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-90dqEQum.js +1 -0
- package/web-dist/assets/{swimlanes-5IMT3BWC-c_zBqkb9.js → swimlanes-5IMT3BWC-CY0fdvUm.js} +1 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-BETWueIt.js +8 -0
- package/web-dist/assets/{timeline-definition-FHXFAJF6-y_QM62SK.js → timeline-definition-FHXFAJF6-De0fDNdU.js} +1 -1
- package/web-dist/assets/{treeView-QDETBFTQ-T3y_1KXV.js → treeView-QDETBFTQ-AIoMhcen.js} +1 -1
- package/web-dist/assets/{treemap-6X3UGDF4-CzvqucqY.js → treemap-6X3UGDF4-CPZ1Ln05.js} +1 -1
- package/web-dist/assets/{vennDiagram-L72KCM5P-6-HNOpN0.js → vennDiagram-L72KCM5P-FrSO6pmh.js} +1 -1
- package/web-dist/assets/{wardley-OPB4EBWU-BrsaqsLL.js → wardley-OPB4EBWU-BbET6cjs.js} +1 -1
- package/web-dist/assets/{wardleyDiagram-EHGQE667-Cr_rCzwd.js → wardleyDiagram-EHGQE667-DqvcCBjr.js} +1 -1
- package/web-dist/assets/{xychartDiagram-FW5EYKEG-BnX6ccx4.js → xychartDiagram-FW5EYKEG-91xKEO07.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-DksosNDW.js +0 -1
- package/web-dist/assets/channel-DqoPvfQz.js +0 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-DwUs74RB.js +0 -1
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-DwUs74RB.js +0 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-CUIjQ7kE.js +0 -1
- package/web-dist/assets/flowDiagram-23GEKE2U-Dka-3F3n.js +0 -1
- package/web-dist/assets/index-CmrDpsD8.css +0 -1
- package/web-dist/assets/index-DB1d3eMJ.js +0 -491
- package/web-dist/assets/railroad-abnf-AHOZXSZD-CCnWa6K9.js +0 -1
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-jtVdzv7e.js +0 -1
- package/web-dist/assets/railroad-peg-LSFZ7HO6-B8D6TyEe.js +0 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-DWKW-4qu.js +0 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-DZKoB7ae.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, ThreadSkillSource, 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
|
|
@@ -14398,9 +14416,11 @@ var init_src = __esm({
|
|
|
14398
14416
|
backend: Backend,
|
|
14399
14417
|
options: external_exports.array(ThreadProfileOption)
|
|
14400
14418
|
});
|
|
14419
|
+
ThreadSkillSource = external_exports.enum(["project", "user", "builtin", "plugin"]);
|
|
14401
14420
|
ThreadSkill = external_exports.object({
|
|
14402
14421
|
name: external_exports.string().min(1).max(512),
|
|
14403
|
-
description: external_exports.string().max(1024)
|
|
14422
|
+
description: external_exports.string().max(1024),
|
|
14423
|
+
source: ThreadSkillSource.optional()
|
|
14404
14424
|
}).strict();
|
|
14405
14425
|
ThreadSkillsInput = external_exports.object({ slug: ThreadSlug }).strict();
|
|
14406
14426
|
ThreadSkillsResult = external_exports.object({ skills: external_exports.array(ThreadSkill).max(1024) }).strict();
|
|
@@ -19652,6 +19672,9 @@ var init_settings = __esm({
|
|
|
19652
19672
|
notifications: true,
|
|
19653
19673
|
font: "sans",
|
|
19654
19674
|
localFileOpener: "system",
|
|
19675
|
+
// 500K tokens: half the 1M window every Claude worker is dispatched with. See the schema for why a
|
|
19676
|
+
// worker left to compact at 1M is the biggest single reason a Frizz thread out-spends the TUI.
|
|
19677
|
+
autoCompactWindow: 5e5,
|
|
19655
19678
|
// Hidden until asked for — see the schema for why.
|
|
19656
19679
|
projectRail: false
|
|
19657
19680
|
});
|
|
@@ -28586,32 +28609,7 @@ var init_wrapper = __esm({
|
|
|
28586
28609
|
}
|
|
28587
28610
|
});
|
|
28588
28611
|
|
|
28589
|
-
// packages/server/src/worker-plugin-dir.ts
|
|
28590
|
-
import { existsSync as existsSync6 } from "node:fs";
|
|
28591
|
-
import { dirname as dirname9, join as join20 } from "node:path";
|
|
28592
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28593
|
-
function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
|
|
28594
|
-
const override = env.FRIZZ_WORKER_PLUGIN_DIR;
|
|
28595
|
-
if (override && existsSync6(join20(override, ".claude-plugin", "plugin.json")))
|
|
28596
|
-
return override;
|
|
28597
|
-
let current = dirname9(fileURLToPath2(moduleUrl));
|
|
28598
|
-
for (; ; ) {
|
|
28599
|
-
const candidate = join20(current, "cc-worker");
|
|
28600
|
-
if (existsSync6(join20(candidate, ".claude-plugin", "plugin.json"))) return candidate;
|
|
28601
|
-
const parent = dirname9(current);
|
|
28602
|
-
if (parent === current) return void 0;
|
|
28603
|
-
current = parent;
|
|
28604
|
-
}
|
|
28605
|
-
}
|
|
28606
|
-
var init_worker_plugin_dir = __esm({
|
|
28607
|
-
"packages/server/src/worker-plugin-dir.ts"() {
|
|
28608
|
-
"use strict";
|
|
28609
|
-
}
|
|
28610
|
-
});
|
|
28611
|
-
|
|
28612
28612
|
// packages/server/src/backend/types.ts
|
|
28613
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
28614
|
-
import { join as join21 } from "node:path";
|
|
28615
28613
|
function frizzMcpEnv(mcp) {
|
|
28616
28614
|
return {
|
|
28617
28615
|
FRIZZ_STATE_DIR: mcp.stateDir,
|
|
@@ -28620,24 +28618,6 @@ function frizzMcpEnv(mcp) {
|
|
|
28620
28618
|
...mcp.slug ? { FRIZZ_THREAD_SLUG: mcp.slug } : {}
|
|
28621
28619
|
};
|
|
28622
28620
|
}
|
|
28623
|
-
function chromeDevtoolsMcpSpec(scriptPath, nodeBin) {
|
|
28624
|
-
const args = [...CHROME_DEVTOOLS_MCP.args];
|
|
28625
|
-
if (!scriptPath) return { command: "npx", args: ["-y", `${CHROME_DEVTOOLS_MCP.package}@${CHROME_DEVTOOLS_MCP.version}`, ...args] };
|
|
28626
|
-
return {
|
|
28627
|
-
command: nodeBin,
|
|
28628
|
-
args: [scriptPath, ...args],
|
|
28629
|
-
env: { FRIZZ_BROWSER_MCP_PACKAGE: `${CHROME_DEVTOOLS_MCP.package}@${CHROME_DEVTOOLS_MCP.version}` }
|
|
28630
|
-
};
|
|
28631
|
-
}
|
|
28632
|
-
function chromeDevtoolsMcpMount() {
|
|
28633
|
-
return chromeDevtoolsMcpSpec(resolveBrowserMcpScript(), process.execPath);
|
|
28634
|
-
}
|
|
28635
|
-
function resolveBrowserMcpScript(moduleUrl = import.meta.url, env = process.env) {
|
|
28636
|
-
const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
|
|
28637
|
-
if (!pluginDir) return void 0;
|
|
28638
|
-
const scriptPath = join21(pluginDir, "bin", CHROME_DEVTOOLS_MCP.script);
|
|
28639
|
-
return existsSync7(scriptPath) ? scriptPath : void 0;
|
|
28640
|
-
}
|
|
28641
28621
|
function workerCap(name, lifted, env) {
|
|
28642
28622
|
const override = env[name];
|
|
28643
28623
|
if (override !== void 0 && /^[1-9][0-9]*$/.test(override)) return override;
|
|
@@ -28651,29 +28631,20 @@ function claudeWorkerEnv(env = process.env) {
|
|
|
28651
28631
|
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS: workerCap("CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS", WORKER_MAX_CONCURRENT_SUBAGENTS, env)
|
|
28652
28632
|
};
|
|
28653
28633
|
}
|
|
28654
|
-
|
|
28634
|
+
function claudeCompactionEnv(settings) {
|
|
28635
|
+
const window = settings?.autoCompactWindow;
|
|
28636
|
+
if (window === void 0 || !Number.isInteger(window) || window <= 0) return {};
|
|
28637
|
+
return { CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(window) };
|
|
28638
|
+
}
|
|
28639
|
+
var FRIZZ_MCP, WORKER_MAX_WEB_SEARCHES, WORKER_MAX_SUBAGENTS, WORKER_MAX_CONCURRENT_SUBAGENTS, CLAUDE_WORKER_ENV, WORKER_DISALLOWED_TOOLS;
|
|
28655
28640
|
var init_types2 = __esm({
|
|
28656
28641
|
"packages/server/src/backend/types.ts"() {
|
|
28657
28642
|
"use strict";
|
|
28658
|
-
init_worker_plugin_dir();
|
|
28659
28643
|
FRIZZ_MCP = {
|
|
28660
28644
|
name: "frizz",
|
|
28661
28645
|
script: "frizz-mcp.mjs"
|
|
28662
28646
|
// resolved under <worker plugin dir>/bin/
|
|
28663
28647
|
};
|
|
28664
|
-
CHROME_DEVTOOLS_MCP = {
|
|
28665
|
-
name: "chrome-devtools",
|
|
28666
|
-
// Resolved under <worker plugin dir>/bin/, exactly like FRIZZ_MCP.script.
|
|
28667
|
-
script: "browser-mcp.mjs",
|
|
28668
|
-
package: "chrome-devtools-mcp",
|
|
28669
|
-
version: "1.7.0",
|
|
28670
|
-
args: [
|
|
28671
|
-
"--experimentalPageIdRouting",
|
|
28672
|
-
"--headless",
|
|
28673
|
-
"--isolated",
|
|
28674
|
-
"--no-usage-statistics"
|
|
28675
|
-
]
|
|
28676
|
-
};
|
|
28677
28648
|
WORKER_MAX_WEB_SEARCHES = 1e4;
|
|
28678
28649
|
WORKER_MAX_SUBAGENTS = 1e4;
|
|
28679
28650
|
WORKER_MAX_CONCURRENT_SUBAGENTS = 100;
|
|
@@ -28703,12 +28674,8 @@ function serverTable(command, args, env) {
|
|
|
28703
28674
|
}
|
|
28704
28675
|
return inlineTable(entries);
|
|
28705
28676
|
}
|
|
28706
|
-
function codexMcpConfigArgs(frizzMcp, nodeBin = process.execPath
|
|
28707
|
-
const
|
|
28708
|
-
const args = [
|
|
28709
|
-
"-c",
|
|
28710
|
-
`mcp_servers.${CHROME_DEVTOOLS_MCP.name}=${serverTable(chrome.command, chrome.args, chrome.env)}`
|
|
28711
|
-
];
|
|
28677
|
+
function codexMcpConfigArgs(frizzMcp, nodeBin = process.execPath) {
|
|
28678
|
+
const args = [];
|
|
28712
28679
|
if (frizzMcp) {
|
|
28713
28680
|
args.push(
|
|
28714
28681
|
"-c",
|
|
@@ -28733,15 +28700,15 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
28733
28700
|
import { connect } from "node:net";
|
|
28734
28701
|
import { Agent as HttpAgent } from "node:http";
|
|
28735
28702
|
import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
|
|
28736
|
-
import { existsSync as
|
|
28737
|
-
import { join as
|
|
28703
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
|
|
28704
|
+
import { join as join20 } from "node:path";
|
|
28738
28705
|
import { PassThrough, Writable } from "node:stream";
|
|
28739
28706
|
import { StringDecoder } from "node:string_decoder";
|
|
28740
28707
|
function nativeDir(stateDir) {
|
|
28741
|
-
return
|
|
28708
|
+
return join20(stateDir, "codex-app-server-native");
|
|
28742
28709
|
}
|
|
28743
28710
|
function nativeRecordPath(stateDir, projectId) {
|
|
28744
|
-
return
|
|
28711
|
+
return join20(nativeDir(stateDir), `${projectId}.json`);
|
|
28745
28712
|
}
|
|
28746
28713
|
function nativeListenSocketPath(stateDir, projectId) {
|
|
28747
28714
|
const key = createHash7("sha256").update(stateDir).update("\0").update(projectId).digest("hex").slice(0, 16);
|
|
@@ -28773,7 +28740,7 @@ function readNativeRecord(stateDir, projectId) {
|
|
|
28773
28740
|
function liveNativeRecord(stateDir, projectId) {
|
|
28774
28741
|
const record = readNativeRecord(stateDir, projectId);
|
|
28775
28742
|
if (!record) return null;
|
|
28776
|
-
if (pidAlive2(record.listenerPid) &&
|
|
28743
|
+
if (pidAlive2(record.listenerPid) && existsSync6(record.socketPath)) return record;
|
|
28777
28744
|
try {
|
|
28778
28745
|
unlinkSync2(nativeRecordPath(stateDir, projectId));
|
|
28779
28746
|
} catch {
|
|
@@ -28915,7 +28882,7 @@ async function startListener(options) {
|
|
|
28915
28882
|
const { stateDir, projectId } = options;
|
|
28916
28883
|
mkdirSync11(nativeDir(stateDir), { recursive: true });
|
|
28917
28884
|
const socketPath = nativeListenSocketPath(stateDir, projectId);
|
|
28918
|
-
if (
|
|
28885
|
+
if (existsSync6(socketPath) && !await socketAccepting(socketPath)) {
|
|
28919
28886
|
try {
|
|
28920
28887
|
unlinkSync2(socketPath);
|
|
28921
28888
|
} catch {
|
|
@@ -28939,7 +28906,7 @@ async function startListener(options) {
|
|
|
28939
28906
|
writeFileSync9(nativeRecordPath(stateDir, projectId), JSON.stringify(record));
|
|
28940
28907
|
const deadline = Date.now() + (options.timeoutMs ?? 3e4);
|
|
28941
28908
|
for (; ; ) {
|
|
28942
|
-
if (
|
|
28909
|
+
if (existsSync6(socketPath) && await socketAccepting(socketPath)) return record;
|
|
28943
28910
|
if (!pidAlive2(record.listenerPid)) throw new Error("codex app-server listener exited before it became ready");
|
|
28944
28911
|
if (Date.now() > deadline) throw new Error("codex app-server listener did not become ready");
|
|
28945
28912
|
await new Promise((resolve10) => setTimeout(resolve10, 50));
|
|
@@ -29005,15 +28972,15 @@ var init_codex_app_server_native = __esm({
|
|
|
29005
28972
|
import { spawn as spawn3, spawnSync } from "node:child_process";
|
|
29006
28973
|
import { createConnection } from "node:net";
|
|
29007
28974
|
import { createHash as createHash8, randomUUID as randomUUID10 } from "node:crypto";
|
|
29008
|
-
import { existsSync as
|
|
29009
|
-
import { join as
|
|
28975
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync12, readFileSync as readFileSync15, unlinkSync as unlinkSync3 } from "node:fs";
|
|
28976
|
+
import { join as join21 } from "node:path";
|
|
29010
28977
|
import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream";
|
|
29011
28978
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
29012
28979
|
function daemonDir(stateDir) {
|
|
29013
|
-
return
|
|
28980
|
+
return join21(stateDir, "codex-app-server");
|
|
29014
28981
|
}
|
|
29015
28982
|
function recordPath(stateDir, projectId) {
|
|
29016
|
-
return
|
|
28983
|
+
return join21(daemonDir(stateDir), `${projectId}.json`);
|
|
29017
28984
|
}
|
|
29018
28985
|
function codexAppServerSocketPath(stateDir, projectId) {
|
|
29019
28986
|
const key = createHash8("sha256").update(stateDir).update("\0").update(projectId).digest("hex").slice(0, 16);
|
|
@@ -29295,7 +29262,7 @@ var init_codex_app_server_host = __esm({
|
|
|
29295
29262
|
}
|
|
29296
29263
|
if (process.platform !== "win32") {
|
|
29297
29264
|
const stale = codexAppServerSocketPath(options.stateDir, options.projectId);
|
|
29298
|
-
if (
|
|
29265
|
+
if (existsSync7(stale) && !liveDaemonRecord(options.stateDir, options.projectId)) {
|
|
29299
29266
|
try {
|
|
29300
29267
|
unlinkSync3(stale);
|
|
29301
29268
|
} catch {
|
|
@@ -29842,6 +29809,12 @@ Suggested answers: ${question.options.map((option) => option.label).join(", ")}`
|
|
|
29842
29809
|
return { ...base, input: "multiline", maxLength: 4e3 };
|
|
29843
29810
|
});
|
|
29844
29811
|
}
|
|
29812
|
+
function codexSkillSource(scope) {
|
|
29813
|
+
if (scope === "repo") return "project";
|
|
29814
|
+
if (scope === "user") return "user";
|
|
29815
|
+
if (scope === "system") return "builtin";
|
|
29816
|
+
return void 0;
|
|
29817
|
+
}
|
|
29845
29818
|
function resumedThreadHasLiveTurn(rawResponse) {
|
|
29846
29819
|
const parsed = ThreadResumeStatus.safeParse(rawResponse);
|
|
29847
29820
|
return parsed.success && parsed.data.thread.status.type === "active";
|
|
@@ -30380,7 +30353,12 @@ var init_codex_app_server = __esm({
|
|
|
30380
30353
|
skills: external_exports.array(external_exports.object({
|
|
30381
30354
|
name: external_exports.string(),
|
|
30382
30355
|
description: external_exports.string().optional(),
|
|
30383
|
-
enabled: external_exports.boolean().optional()
|
|
30356
|
+
enabled: external_exports.boolean().optional(),
|
|
30357
|
+
// The root the app-server resolved this skill from — measured against codex-cli 0.146.0 as
|
|
30358
|
+
// `repo` (`<cwd>/.agents/skills/…`), `user` (`~/.agents/skills/…`) and `system`
|
|
30359
|
+
// (`~/.codex/skills/.system/…`). Kept as a free string, not an enum: an unmapped value must
|
|
30360
|
+
// leave the row unlabelled, never fail the listing (see codexSkillSource).
|
|
30361
|
+
scope: external_exports.string().optional()
|
|
30384
30362
|
}).passthrough()).optional()
|
|
30385
30363
|
}).passthrough()).optional()
|
|
30386
30364
|
}).passthrough();
|
|
@@ -30875,7 +30853,11 @@ var init_codex_app_server = __esm({
|
|
|
30875
30853
|
if (skills.length >= 1024) return skills;
|
|
30876
30854
|
if (seen.has(skill.name)) continue;
|
|
30877
30855
|
seen.add(skill.name);
|
|
30878
|
-
skills.push({
|
|
30856
|
+
skills.push({
|
|
30857
|
+
name: skill.name.slice(0, 512),
|
|
30858
|
+
description: (skill.description ?? "").slice(0, 1024),
|
|
30859
|
+
source: codexSkillSource(skill.scope)
|
|
30860
|
+
});
|
|
30879
30861
|
}
|
|
30880
30862
|
}
|
|
30881
30863
|
return skills;
|
|
@@ -32241,11 +32223,11 @@ var init_codex_app_server = __esm({
|
|
|
32241
32223
|
});
|
|
32242
32224
|
|
|
32243
32225
|
// packages/server/src/backend/codex-quota.ts
|
|
32244
|
-
import { join as
|
|
32226
|
+
import { join as join22 } from "node:path";
|
|
32245
32227
|
import { readdirSync as readdirSync7, statSync as statSync10, openSync as openSync12, readSync as readSync6, fstatSync, closeSync as closeSync11 } from "node:fs";
|
|
32246
32228
|
import { spawn as spawn4 } from "node:child_process";
|
|
32247
32229
|
function sessionsDir2(codexHome) {
|
|
32248
|
-
return
|
|
32230
|
+
return join22(codexHome, "sessions");
|
|
32249
32231
|
}
|
|
32250
32232
|
function newestRollouts(dir, out, budget) {
|
|
32251
32233
|
if (budget.n <= 0) return;
|
|
@@ -32259,11 +32241,11 @@ function newestRollouts(dir, out, budget) {
|
|
|
32259
32241
|
const files = entries.filter((e) => e.isFile() && e.name.startsWith("rollout-") && e.name.endsWith(".jsonl")).map((e) => e.name).sort(descByName2);
|
|
32260
32242
|
for (const d of dirs) {
|
|
32261
32243
|
if (budget.n <= 0) return;
|
|
32262
|
-
newestRollouts(
|
|
32244
|
+
newestRollouts(join22(dir, d), out, budget);
|
|
32263
32245
|
}
|
|
32264
32246
|
for (const f of files) {
|
|
32265
32247
|
if (budget.n <= 0) return;
|
|
32266
|
-
out.push(
|
|
32248
|
+
out.push(join22(dir, f));
|
|
32267
32249
|
budget.n--;
|
|
32268
32250
|
}
|
|
32269
32251
|
}
|
|
@@ -32463,7 +32445,7 @@ var init_codex_quota = __esm({
|
|
|
32463
32445
|
});
|
|
32464
32446
|
|
|
32465
32447
|
// packages/server/src/backend/claude-quota.ts
|
|
32466
|
-
import { join as
|
|
32448
|
+
import { join as join23 } from "node:path";
|
|
32467
32449
|
import { homedir as homedir9, platform } from "node:os";
|
|
32468
32450
|
import { createHash as createHash10 } from "node:crypto";
|
|
32469
32451
|
import { mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
@@ -32471,7 +32453,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
32471
32453
|
import { promisify as promisify2 } from "node:util";
|
|
32472
32454
|
function claudeConfigDir() {
|
|
32473
32455
|
const override = process.env.CLAUDE_CONFIG_DIR;
|
|
32474
|
-
return override && override.trim() ? override :
|
|
32456
|
+
return override && override.trim() ? override : join23(homedir9(), ".claude");
|
|
32475
32457
|
}
|
|
32476
32458
|
function tokenFromCredentialsJson(raw2) {
|
|
32477
32459
|
let doc;
|
|
@@ -32500,7 +32482,7 @@ async function readKeychainToken() {
|
|
|
32500
32482
|
}
|
|
32501
32483
|
async function readAccessToken(configDir) {
|
|
32502
32484
|
try {
|
|
32503
|
-
const fromFile = tokenFromCredentialsJson(await readFile(
|
|
32485
|
+
const fromFile = tokenFromCredentialsJson(await readFile(join23(configDir, ".credentials.json"), "utf8"));
|
|
32504
32486
|
if (fromFile) return fromFile;
|
|
32505
32487
|
} catch {
|
|
32506
32488
|
}
|
|
@@ -32569,8 +32551,8 @@ function parseClaudeUsage(body, planType) {
|
|
|
32569
32551
|
function cachePaths(cacheDir, configDir) {
|
|
32570
32552
|
const profile = createHash10("sha256").update(configDir).digest("hex").slice(0, 12);
|
|
32571
32553
|
return {
|
|
32572
|
-
data:
|
|
32573
|
-
lock:
|
|
32554
|
+
data: join23(cacheDir, `claude-${profile}.json`),
|
|
32555
|
+
lock: join23(cacheDir, `claude-${profile}.lock`)
|
|
32574
32556
|
};
|
|
32575
32557
|
}
|
|
32576
32558
|
async function readShared(path) {
|
|
@@ -32766,7 +32748,7 @@ function refreshSharedInBackground(paths, claudeBin, deps, now) {
|
|
|
32766
32748
|
async function refreshClaudeQuotaInBackground(claudeBin = "claude", deps = {}) {
|
|
32767
32749
|
const now = (deps.now ?? Date.now)();
|
|
32768
32750
|
const configDir = claudeConfigDir();
|
|
32769
|
-
const cacheDir = deps.cacheDir ??
|
|
32751
|
+
const cacheDir = deps.cacheDir ?? join23(frizzRoots().cache, "quota-cache");
|
|
32770
32752
|
try {
|
|
32771
32753
|
await mkdir(cacheDir, { recursive: true, mode: 448 });
|
|
32772
32754
|
} catch {
|
|
@@ -32777,7 +32759,7 @@ async function refreshClaudeQuotaInBackground(claudeBin = "claude", deps = {}) {
|
|
|
32777
32759
|
async function readClaudeQuota(claudeBin = "claude", deps = {}, options = {}) {
|
|
32778
32760
|
const now = (deps.now ?? Date.now)();
|
|
32779
32761
|
const configDir = claudeConfigDir();
|
|
32780
|
-
const cacheDir = deps.cacheDir ??
|
|
32762
|
+
const cacheDir = deps.cacheDir ?? join23(frizzRoots().cache, "quota-cache");
|
|
32781
32763
|
const paths = cachePaths(cacheDir, configDir);
|
|
32782
32764
|
try {
|
|
32783
32765
|
await mkdir(cacheDir, { recursive: true, mode: 448 });
|
|
@@ -32937,7 +32919,7 @@ var init_quota = __esm({
|
|
|
32937
32919
|
// packages/server/src/frizz.ts
|
|
32938
32920
|
import { execFile as execFile3 } from "node:child_process";
|
|
32939
32921
|
import { lstatSync as lstatSync2, realpathSync as realpathSync6 } from "node:fs";
|
|
32940
|
-
import { basename as basename5, dirname as
|
|
32922
|
+
import { basename as basename5, dirname as dirname9, join as join24, resolve as resolve4 } from "node:path";
|
|
32941
32923
|
import { promisify as promisify3 } from "node:util";
|
|
32942
32924
|
function frizzScriptsDir() {
|
|
32943
32925
|
if (process.env.FRIZZ_SCRIPTS_DIR) return process.env.FRIZZ_SCRIPTS_DIR;
|
|
@@ -32946,11 +32928,11 @@ function frizzScriptsDir() {
|
|
|
32946
32928
|
function directFrizzRoot(projectDir) {
|
|
32947
32929
|
try {
|
|
32948
32930
|
const projectRoot = realpathSync6(projectDir);
|
|
32949
|
-
const path =
|
|
32931
|
+
const path = join24(projectRoot, ".frizz");
|
|
32950
32932
|
const stat2 = lstatSync2(path);
|
|
32951
32933
|
if (!stat2.isDirectory() || stat2.isSymbolicLink()) return null;
|
|
32952
32934
|
const real = realpathSync6(path);
|
|
32953
|
-
return
|
|
32935
|
+
return dirname9(real) === projectRoot && basename5(real) === ".frizz" ? real : null;
|
|
32954
32936
|
} catch {
|
|
32955
32937
|
return null;
|
|
32956
32938
|
}
|
|
@@ -32960,7 +32942,7 @@ function frizzDirExists(projectDir) {
|
|
|
32960
32942
|
}
|
|
32961
32943
|
async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
|
|
32962
32944
|
if (!directFrizzRoot(projectDir)) throw new Error("unsafe or missing .frizz directory");
|
|
32963
|
-
const { stdout } = await execFileP("node", [
|
|
32945
|
+
const { stdout } = await execFileP("node", [join24(scriptsDir, "index.mjs"), "--json"], {
|
|
32964
32946
|
cwd: projectDir,
|
|
32965
32947
|
env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir },
|
|
32966
32948
|
maxBuffer: 32 * 1024 * 1024
|
|
@@ -32985,7 +32967,7 @@ async function readBoard(projectDir, scriptsDir = frizzScriptsDir()) {
|
|
|
32985
32967
|
};
|
|
32986
32968
|
}
|
|
32987
32969
|
async function runThreadUpdate(projectDir, slug, args, scriptsDir = frizzScriptsDir()) {
|
|
32988
|
-
await execFileP("node", [
|
|
32970
|
+
await execFileP("node", [join24(scriptsDir, "thread-update.mjs"), slug, ...args], {
|
|
32989
32971
|
cwd: projectDir,
|
|
32990
32972
|
env: { ...process.env, CLAUDE_PROJECT_DIR: projectDir }
|
|
32991
32973
|
});
|
|
@@ -33021,7 +33003,12 @@ function readAwaitingPark(hints) {
|
|
|
33021
33003
|
return { items, forMs };
|
|
33022
33004
|
}
|
|
33023
33005
|
function unaccountedItems(items, live) {
|
|
33024
|
-
return items.filter((i) => !live[LIVE_SET[i.kind]].has(i
|
|
33006
|
+
return items.filter((i) => !live[LIVE_SET[i.kind]].has(liveKey(i)));
|
|
33007
|
+
}
|
|
33008
|
+
function liveKey(i) {
|
|
33009
|
+
if (i.kind !== "pr") return i.value;
|
|
33010
|
+
const ref = parsePrRef(i.value);
|
|
33011
|
+
return ref ? githubStatusKey(ref) : i.value;
|
|
33025
33012
|
}
|
|
33026
33013
|
function parkIsHonoured(park, live) {
|
|
33027
33014
|
if (park.items.length === 0) return false;
|
|
@@ -33060,6 +33047,29 @@ var init_awaiting = __esm({
|
|
|
33060
33047
|
}
|
|
33061
33048
|
});
|
|
33062
33049
|
|
|
33050
|
+
// packages/server/src/worker-plugin-dir.ts
|
|
33051
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
33052
|
+
import { dirname as dirname10, join as join25 } from "node:path";
|
|
33053
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
33054
|
+
function resolveWorkerPluginDir(moduleUrl = import.meta.url, env = process.env) {
|
|
33055
|
+
const override = env.FRIZZ_WORKER_PLUGIN_DIR;
|
|
33056
|
+
if (override && existsSync8(join25(override, ".claude-plugin", "plugin.json")))
|
|
33057
|
+
return override;
|
|
33058
|
+
let current = dirname10(fileURLToPath2(moduleUrl));
|
|
33059
|
+
for (; ; ) {
|
|
33060
|
+
const candidate = join25(current, "cc-worker");
|
|
33061
|
+
if (existsSync8(join25(candidate, ".claude-plugin", "plugin.json"))) return candidate;
|
|
33062
|
+
const parent = dirname10(current);
|
|
33063
|
+
if (parent === current) return void 0;
|
|
33064
|
+
current = parent;
|
|
33065
|
+
}
|
|
33066
|
+
}
|
|
33067
|
+
var init_worker_plugin_dir = __esm({
|
|
33068
|
+
"packages/server/src/worker-plugin-dir.ts"() {
|
|
33069
|
+
"use strict";
|
|
33070
|
+
}
|
|
33071
|
+
});
|
|
33072
|
+
|
|
33063
33073
|
// packages/server/src/workerPrompt.ts
|
|
33064
33074
|
function buildWorkerPrompt(kind = "claude", opts = {}) {
|
|
33065
33075
|
const lean = kind === "claude";
|
|
@@ -33075,7 +33085,6 @@ function buildWorkerPrompt(kind = "claude", opts = {}) {
|
|
|
33075
33085
|
lean ? null : THREAD_EXECUTION[kind],
|
|
33076
33086
|
AGENT_COMPLETION,
|
|
33077
33087
|
VISUAL_EVIDENCE,
|
|
33078
|
-
GIT_DISCIPLINE,
|
|
33079
33088
|
QUALITY_BAR,
|
|
33080
33089
|
QUESTIONS,
|
|
33081
33090
|
STOP_CRITERION,
|
|
@@ -33086,7 +33095,7 @@ function buildWorkerPrompt(kind = "claude", opts = {}) {
|
|
|
33086
33095
|
out = out.replaceAll("{{FRIZZ_MONITORS_DIR}}", opts.monitorsDir?.trim() || MONITORS_DIR_FALLBACK);
|
|
33087
33096
|
return out;
|
|
33088
33097
|
}
|
|
33089
|
-
var INLINE, INTRO, DEFER, OPENING, ACTIVITY_CAPTIONS, SIGNALS, AGENT_COMPLETION, VISUAL_EVIDENCE,
|
|
33098
|
+
var INLINE, INTRO, DEFER, OPENING, ACTIVITY_CAPTIONS, SIGNALS, AGENT_COMPLETION, VISUAL_EVIDENCE, QUALITY_BAR, QUESTIONS, STOP_CRITERION, TRIVIAL_PROMPTS, SCRATCHPAD, BACKEND, SPAWN_THREAD, THREAD_EXECUTION, MONITORS_DIR_FALLBACK;
|
|
33090
33099
|
var init_workerPrompt = __esm({
|
|
33091
33100
|
"packages/server/src/workerPrompt.ts"() {
|
|
33092
33101
|
"use strict";
|
|
@@ -33149,114 +33158,81 @@ command pasted as prose. Never prefix a description with \`Running\` to force it
|
|
|
33149
33158
|
verb itself, and use \`Running\` only when the thing you are doing is literally running something.`;
|
|
33150
33159
|
SIGNALS = `## End-of-turn signals \u2014 your final message IS the interface
|
|
33151
33160
|
|
|
33152
|
-
When you come to rest, your last message is the
|
|
33153
|
-
|
|
33161
|
+
When you come to rest, your last message is the whole interface: the human reads it in a queue, hours
|
|
33162
|
+
later, with none of your context \u2014 and they have seen NOTHING since their own last message; every
|
|
33163
|
+
prompt in between (the Goal, a sign-off reminder, a watcher wake) came from frizz. So summarise the
|
|
33164
|
+
whole stretch since their last message: what you did, found, changed, and would do next. Be concise
|
|
33165
|
+
but do not omit; use whatever markup reads well.
|
|
33154
33166
|
|
|
33155
33167
|
**ALWAYS SIGN OFF WITH A FENCE.** A bare rest \u2014 no fence \u2014 is not a handoff, it is an item nobody can
|
|
33156
33168
|
triage: it says neither "answer me" nor "this is finished", so it sits in the queue meaning nothing.
|
|
33157
|
-
Frizz
|
|
33158
|
-
|
|
33159
|
-
|
|
33160
|
-
|
|
33161
|
-
|
|
33162
|
-
|
|
33163
|
-
|
|
33164
|
-
|
|
33165
|
-
|
|
33166
|
-
|
|
33167
|
-
|
|
33168
|
-
your context \u2014 and they have NOT seen anything since their own last message. Every prompt you received
|
|
33169
|
-
in between came from FRIZZ, not from them: the Goal, the sign-off reminder, a watcher waking you. So
|
|
33170
|
-
summarise the whole stretch since their last message \u2014 what you did, what you found, what changed, what
|
|
33171
|
-
you would do next \u2014 rather than only the last thing you touched. Do not assume they watched.
|
|
33172
|
-
|
|
33173
|
-
Be concise, but do not omit. Use whatever markup makes it readable: headings, bullets, tables, code.
|
|
33174
|
-
The one place brevity is a RULE is a \`done\` body \u2014 see its entry below.
|
|
33175
|
-
|
|
33176
|
-
**NAME THE PULL REQUEST, EVERY TIME YOU REST.** If your effort has a PR \u2014 one you opened, one you are
|
|
33177
|
-
reviewing, one you are fixing \u2014 write its number in EVERY resting message, not just the one where it
|
|
33178
|
-
first appeared. The human reads these as a queue of cards from a dozen threads at once, and a card that
|
|
33179
|
-
says "pushed the fix, CI is green" without a number is a card they cannot place: they have to open the
|
|
33180
|
-
thread to learn which PR it was about (maintainer 2026-08-16: "I keep being unclear what PR is being
|
|
33181
|
-
implemented in a given chat"). One \`#123\` in the first line or two costs you nothing and answers it.
|
|
33182
|
-
|
|
33183
|
-
Several PRs in play \u21D2 name them all. No PR yet \u21D2 say what the work is against instead (a branch, an
|
|
33184
|
-
issue, a file) \u2014 the point is that the card identifies its own subject without being opened.
|
|
33185
|
-
|
|
33186
|
-
Frizz turns \`#123\`, \`owner/repo#123\` and bare commit hashes into github.com links itself, in every
|
|
33187
|
-
surface it draws your prose, for any project whose origin remote is github.com. Write the bare form;
|
|
33188
|
-
wrapping one in a \`[link](url)\` yourself is redundant. Everything else you want clickable \u2014 a file, a
|
|
33189
|
-
thread, any other URL \u2014 is still yours to write as a real link.
|
|
33190
|
-
|
|
33191
|
-
Use at most ONE fenced signal block, at the very END of the final message. The fence language is the
|
|
33192
|
-
state; the body is the card the human reads. A \` \`\`\`question \` block and real permission prompts are
|
|
33193
|
-
higher-priority asks than either fence below.
|
|
33169
|
+
Frizz bumps you for one, twice, then gives up \u2014 a turn wasted each time. Use at most ONE fenced signal
|
|
33170
|
+
block, at the very END. The fence language is the state; the body is the card the human reads. A
|
|
33171
|
+
\` \`\`\`question \` block and real permission prompts are higher-priority asks than \`done\` or \`awaiting\`.
|
|
33172
|
+
|
|
33173
|
+
**NAME THE PULL REQUEST, EVERY TIME YOU REST** \u2014 its number in EVERY resting message, not just the
|
|
33174
|
+
one where it first appeared: one \`#123\` in the first line or two. Several PRs in play \u21D2 name them
|
|
33175
|
+
all. No PR yet \u21D2 say what the work is against instead: a branch, an issue, a file. Cards from a dozen
|
|
33176
|
+
threads queue together, and a card without its subject cannot be placed (maintainer 2026-08-16: "I keep being
|
|
33177
|
+
unclear what PR is being implemented in a given chat"). Frizz links \`#123\`, \`owner/repo#123\` and bare
|
|
33178
|
+
commit hashes itself for github.com remotes; write the bare form. Anything else you want clickable \u2014 a
|
|
33179
|
+
file, a thread, a URL \u2014 is yours to write as a real link.
|
|
33194
33180
|
|
|
33195
33181
|
**THE FENCE AND THE PROSE ABOVE IT ARE TWO SURFACES, NOT ONE MESSAGE WRITTEN TWICE.** The card is the
|
|
33196
|
-
LEDGER: one bullet per deliverable, what shipped and where. The prose
|
|
33197
|
-
|
|
33198
|
-
|
|
33199
|
-
|
|
33200
|
-
|
|
33201
|
-
|
|
33202
|
-
|
|
33203
|
-
|
|
33204
|
-
|
|
33205
|
-
|
|
33206
|
-
identifier and command, and make FILE references real \`[links](url)\` \u2014 issue numbers and commit
|
|
33207
|
-
hashes link themselves, per above. It renders as a
|
|
33208
|
-
checked success card in the queue and stays there until the human archives it. The fence MUTATES
|
|
33209
|
-
NOTHING.
|
|
33182
|
+
LEDGER: one bullet per deliverable, what shipped and where. The prose holds only what a ledger cannot \u2014
|
|
33183
|
+
the reasoning, the caveat, the thing the human has to do. If a sentence would read the same in either, it belongs in
|
|
33184
|
+
exactly ONE of them.
|
|
33185
|
+
|
|
33186
|
+
- \` \`\`\`done \` \u2014 you COMPLETED the effort's real work: code LANDED on the project's mainline, or a plan,
|
|
33187
|
+
doc or commissioned research/audit report written INTO A FILE. Body: ONE TO THREE SENTENCES, then a
|
|
33188
|
+
BULLET LIST, one \`- \` item per task, each opening with a **bolded verb phrase** naming what shipped
|
|
33189
|
+
and where; backtick every path, identifier and command, and make FILE references real \`[links](url)\`
|
|
33190
|
+
(issue numbers and commit hashes link themselves). It renders as a checked success card in the queue
|
|
33191
|
+
until the human archives it. The fence MUTATES NOTHING.
|
|
33210
33192
|
|
|
33211
33193
|
\`\`\`done
|
|
33212
33194
|
- Fixed the cache collision in [\`src/resolver.ts\`](https://github.com/acme/app/pull/391) \u2014 the lookup now keys on the normalized id.
|
|
33213
33195
|
- Added a regression test; \`npm test\` green.
|
|
33214
33196
|
\`\`\`
|
|
33215
33197
|
|
|
33216
|
-
**\`done\` is a DISMISSAL, not a summary
|
|
33217
|
-
and anything living only in the conversation goes with it.
|
|
33218
|
-
working"
|
|
33219
|
-
done
|
|
33220
|
-
|
|
33221
|
-
|
|
33222
|
-
|
|
33223
|
-
|
|
33224
|
-
|
|
33225
|
-
|
|
33226
|
-
|
|
33227
|
-
|
|
33228
|
-
|
|
33229
|
-
|
|
33230
|
-
|
|
33231
|
-
|
|
33232
|
-
|
|
33233
|
-
|
|
33234
|
-
|
|
33235
|
-
|
|
33236
|
-
|
|
33237
|
-
|
|
33238
|
-
|
|
33239
|
-
|
|
33240
|
-
|
|
33241
|
-
|
|
33242
|
-
|
|
33243
|
-
|
|
33244
|
-
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33249
|
-
|
|
33250
|
-
|
|
33251
|
-
|
|
33252
|
-
|
|
33253
|
-
that
|
|
33254
|
-
exception stretches to a report that ENDS IN A DECISION the human has yet to make: write the file AND
|
|
33255
|
-
ask the question.
|
|
33256
|
-
|
|
33257
|
-
- \` \`\`\`awaiting \` \u2014 you have STOPPED, and you are waiting on work that is actually running. The
|
|
33258
|
-
fence is PURE STRUCTURE: a list of things frizz can look up, how long the park may stand, and one line
|
|
33259
|
-
of prose. Nothing else belongs in it.
|
|
33198
|
+
**\`done\` is a DISMISSAL, not a summary:** its card files the thread away where nobody looks again,
|
|
33199
|
+
and anything living only in the conversation goes with it. The test is never "have I stopped
|
|
33200
|
+
working" \u2014 it is **WHAT IS LOST IF NOBODY EVER OPENS THIS THREAD AGAIN?** Name one thing and it is
|
|
33201
|
+
not done; uncertain is not done. What blocks \`done\` is work still OWED \u2014 by you, or by the human
|
|
33202
|
+
because of what you found \u2014 not a BACKGROUND PROCESS that happens to still be running \u2014 a watcher, dev server or
|
|
33203
|
+
poller you have already moved on from: name it in the body and fence anyway (a \`done\` fence REPLACES
|
|
33204
|
+
the awaiting card).
|
|
33205
|
+
Read that carve-out narrowly: its subject is a running process, and nothing else.
|
|
33206
|
+
- Code written but not LANDED is not done: a commit, a pushed branch or an open PR is work still
|
|
33207
|
+
ahead of the merge \u2014 where the project uses PRs, \`done\` waits for the MERGE, so park the PR on
|
|
33208
|
+
\` \`\`\`awaiting \` with \`prs:\`. Opening the PR does NOT finish the thread \u2014 the MERGE does. An
|
|
33209
|
+
investigation headed for a fix is not done; the fix is still owed.
|
|
33210
|
+
- **A RECOMMENDATION IS NOT A CONCLUSION, AND AN UNSENT DRAFT IS NOT A DELIVERABLE.** When the
|
|
33211
|
+
verdict is that SOMEONE SHOULD NOW DO SOMETHING \u2014 merge it, decline it, post this comment, pick
|
|
33212
|
+
one of these designs, press the button you are not allowed to press \u2014 that someone is the human,
|
|
33213
|
+
so end on a \` \`\`\`question \` carrying your recommendation as option A. Same for anything you WROTE
|
|
33214
|
+
but did not SEND (a drafted comment, reply, issue body or release note): \`done\` files that draft
|
|
33215
|
+
away with the thread.
|
|
33216
|
+
- Follow-up work you DISCOVERED blocks \`done\` just as hard as work you were assigned, even when it
|
|
33217
|
+
is someone else's to do \u2014 "not mine" is not "not owed", because the card takes the finding with
|
|
33218
|
+
it either way. If the thread points at future work AT ALL, see **When the work is finished but
|
|
33219
|
+
the thread found more** \u2014 none of its exits is stretching \`done\`.
|
|
33220
|
+
- **A \`done\` MESSAGE SAYS WHAT HAPPENED. DELETE EVERY DANGLING "WORTH DOING LATER".** "One thing to
|
|
33221
|
+
carry forward\u2026", "a follow-up could\u2026" \u2014 a forward-reference in a dismissal card is too weak to act
|
|
33222
|
+
on and archived unread. Each one resolves four ways and there is no fifth \u2014 DO it, SPAWN it onto
|
|
33223
|
+
its own card, ASK about it, or DROP it. Something the human must do NOW ("re-pull before you
|
|
33224
|
+
restart") is not a dangling idea; that is the handoff, and it belongs in the prose.
|
|
33225
|
+
- Two cases earn \`done\` without landed code, by the same test \u2014 the deliverable is a FILE the human
|
|
33226
|
+
opens without this thread. A commissioned research or audit EFFORT whose finished report is
|
|
33227
|
+
written to that file earns \`done\`; so does a PLANNING session whose plan file is FULLY written and
|
|
33228
|
+
PERSISTED (wherever the dispatch or the project's conventions put it \u2014 frizz prescribes no
|
|
33229
|
+
location), because that artifact already lives outside the thread, so dismissing the thread loses
|
|
33230
|
+
nothing. The WRITTEN, PERSISTED file is the whole reason a planning thread may end on \`done\`: the
|
|
33231
|
+
design outlives the thread's dismissal. Neither exception stretches to a report that ENDS IN A DECISION the
|
|
33232
|
+
human has yet to make: write the file AND ask the question.
|
|
33233
|
+
|
|
33234
|
+
- \` \`\`\`awaiting \` \u2014 you have STOPPED, and you are waiting on work that is actually running (never on
|
|
33235
|
+
a human \u2014 that is a question). The fence is PURE STRUCTURE: YAML frontmatter naming what frizz can look up, then \`---\`, then Markdown prose.
|
|
33260
33236
|
|
|
33261
33237
|
\`\`\`awaiting
|
|
33262
33238
|
shells: [bzvtnt3ig]
|
|
@@ -33264,6 +33240,7 @@ a second read for nothing, and it is the commonest thing wrong with an otherwise
|
|
|
33264
33240
|
timers: [tmr_a1b2c3d4e5f6]
|
|
33265
33241
|
prs: [acme/app#391]
|
|
33266
33242
|
for: 2h
|
|
33243
|
+
title: Three-platform CI run
|
|
33267
33244
|
---
|
|
33268
33245
|
Waiting on the three-platform run before porting the v2 drivers.
|
|
33269
33246
|
|
|
@@ -33271,111 +33248,78 @@ a second read for nothing, and it is the commonest thing wrong with an otherwise
|
|
|
33271
33248
|
- if it goes red I will bisect rather than re-run
|
|
33272
33249
|
\`\`\`
|
|
33273
33250
|
|
|
33274
|
-
**THE FRONTMATTER IS YAML. THE BODY IS MARKDOWN.**
|
|
33275
|
-
|
|
33276
|
-
|
|
33277
|
-
|
|
33278
|
-
|
|
33279
|
-
|
|
33280
|
-
|
|
33281
|
-
|
|
33282
|
-
|
|
33283
|
-
|
|
33284
|
-
|
|
33285
|
-
\`\`\`awaiting
|
|
33286
|
-
prs:
|
|
33287
|
-
- acme/app#391
|
|
33288
|
-
- acme/app#402
|
|
33289
|
-
for: 6h
|
|
33290
|
-
---
|
|
33291
|
-
Both are green; waiting on your merge.
|
|
33292
|
-
\`\`\`
|
|
33293
|
-
|
|
33294
|
-
- \`shells:\` / \`agents:\` \u2014 the runtime ids you were handed when you launched them ("Command running in
|
|
33295
|
-
background with ID: bzvtnt3ig").
|
|
33296
|
-
|
|
33297
|
-
**LOST AN ID? \`mcp__frizz__activity\` PRINTS THEM ALL** \u2014 every shell, sub-agent, timer and PR you have
|
|
33298
|
-
out, each with the exact id your fence needs. Call it rather than guessing: a guessed id names
|
|
33299
|
-
nothing, so frizz refuses the park and bumps you.
|
|
33251
|
+
- **THE FRONTMATTER IS YAML. THE BODY IS MARKDOWN.** Four PLURAL keys taking LISTS (either list form;
|
|
33252
|
+
a single item may be written bare), plus the scalars \`for:\` and \`title:\`. **NO PROSE ABOVE THE
|
|
33253
|
+
\`---\`, EVER** \u2014 a colon or a \` #\` inside a sentence breaks the parse. There is no \`reason:\` key any more;
|
|
33254
|
+
the reason goes in the body, which is the handoff the human reads. **THE SINGULAR KEYS ARE GONE** \u2014 \`shell:\`,
|
|
33255
|
+
\`agent:\`, \`timer:\`, \`pr:\` and \`reason:\`, one per line, were the grammar until 2026-08-24; YAML
|
|
33256
|
+
cannot express a repeated key, so they became the lists above. Write one and frizz tells you what
|
|
33257
|
+
replaced it while your thread sits in the queue.
|
|
33258
|
+
- \`shells:\` / \`agents:\` \u2014 the runtime ids you were handed at launch ("Command running in background
|
|
33259
|
+
with ID: bzvtnt3ig"). **LOST AN ID? \`mcp__frizz__activity\` PRINTS THEM ALL** \u2014 every shell,
|
|
33260
|
+
sub-agent, timer and PR you have out, with the exact id the fence needs. A guessed id names
|
|
33261
|
+
nothing, so frizz refuses the park and bumps you.
|
|
33300
33262
|
- \`timers:\` \u2014 timer ids from \`mcp__frizz__timer\`. NOT instants; the tool creates the timer.
|
|
33301
|
-
- \`prs:\` \u2014 pull requests you REGISTERED with \`mcp__frizz__watch_pr\`, written \`owner/repo#N\`.
|
|
33302
|
-
FIRST: this line states the wait, it does not create one. That tool wakes you on CI
|
|
33303
|
-
and on every later review, approval or comment,
|
|
33263
|
+
- \`prs:\` \u2014 pull requests you REGISTERED with \`mcp__frizz__watch_pr\`, written \`owner/repo#N\`.
|
|
33264
|
+
Register FIRST: this line states the wait, it does not create one. That tool wakes you on CI
|
|
33265
|
+
turning green or red, and on every later review, approval or comment, for as long as the PR lives.
|
|
33304
33266
|
- \`for:\` \u2014 **REQUIRED**, and a DURATION: \`30s\`, \`15m\`, \`2h\`, \`3d\`. Never an instant. When it runs
|
|
33305
33267
|
out frizz brings you back to re-check everything; re-parking is fine and uncapped.
|
|
33306
|
-
|
|
33307
|
-
|
|
33308
|
-
|
|
33309
|
-
|
|
33310
|
-
|
|
33311
|
-
|
|
33312
|
-
|
|
33313
|
-
|
|
33314
|
-
|
|
33315
|
-
|
|
33316
|
-
|
|
33317
|
-
|
|
33318
|
-
|
|
33319
|
-
|
|
33320
|
-
|
|
33321
|
-
**
|
|
33322
|
-
|
|
33323
|
-
A
|
|
33324
|
-
|
|
33325
|
-
|
|
33326
|
-
|
|
33327
|
-
If you need a human, ask them something answerable.
|
|
33328
|
-
|
|
33329
|
-
**CI, RELEASES, DEPLOYS AND MERGE PROGRESSION ARE AUTOMATABLE \u2014 never \` \`\`\`awaiting \` them
|
|
33330
|
-
BLINDLY.** For a PULL REQUEST that is what \`mcp__frizz__watch_pr\` is for: register it and you are told
|
|
33331
|
-
when CI settles, so the wait is real rather than a hope. For anything else, stay ACTIVE \u2014 dispatch a
|
|
33332
|
-
sub-agent to own the wait (its return re-invokes you), or set a timer and name it here.
|
|
33333
|
-
|
|
33334
|
-
**THE PARK SURVIVES A RESTART.** The registration and the fence are both durable, so frizz brings you
|
|
33335
|
-
back (\`{{FRIZZ_RESUME_CMD}}\`) even if its own server or your daemon is replaced while you wait.
|
|
33336
|
-
|
|
33337
|
-
**A follow-up clears the previous fence.** If the human says "back to awaiting", never answer that it
|
|
33338
|
-
is already parked and never rely on the old fence: re-check what you are waiting on, confirm it is
|
|
33339
|
-
still running, and emit a FRESH fence naming it. If it turns out to be automatable, arm the active
|
|
33340
|
-
wait instead and do not fence.
|
|
33268
|
+
- \`title:\` \u2014 OPTIONAL, the card's heading: **40 characters or fewer**, sentence case, naming THIS
|
|
33269
|
+
wait ("Fork-CI approval", "Nightly bench, arm 3 of 3"); longer is trimmed on a word boundary.
|
|
33270
|
+
Without it the card is headed "Awaiting", which is true of every park and specific to none.
|
|
33271
|
+
- **REGISTERING IS NOT PARKING, AND PARKING IS NOT REGISTERING.** Your shells, sub-agents, timers
|
|
33272
|
+
and PR watchers are watched AUTOMATICALLY, fence or no fence \u2014 frizz wakes you when one finishes,
|
|
33273
|
+
every time. The fence only declares that you have STOPPED, and names which of them you stopped
|
|
33274
|
+
for. **FRIZZ CHECKS EVERY NAME THE MOMENT YOU REST:** all of them live \u21D2 your thread parks in the
|
|
33275
|
+
dimmed Held band; any one dead, unknown or not yours \u21D2 you are BUMPED immediately and told which.
|
|
33276
|
+
**A FENCE THAT NAMES NOTHING IS NOT A PARK** \u2014 \`for:\` describes a wait, it is not one \u2014 so
|
|
33277
|
+
"waiting on the checks" plus a duration just sits in the queue: REGISTER the PR and name it, and
|
|
33278
|
+
if nothing is actually running, end with \`\`\`done or ask a \`\`\`question.
|
|
33279
|
+
- **WAITING ON A PERSON IS A \` \`\`\`question \`.** There is no human gate and no prose park.
|
|
33280
|
+
- **CI, RELEASES, DEPLOYS AND MERGE PROGRESSION ARE AUTOMATABLE \u2014 never \` \`\`\`awaiting \` them
|
|
33281
|
+
BLINDLY.** For a pull request, \`mcp__frizz__watch_pr\`. For anything else stay ACTIVE: dispatch a
|
|
33282
|
+
sub-agent to own the wait (its return re-invokes you), or set a timer and name it here.
|
|
33283
|
+
- **THE PARK SURVIVES A RESTART.** Registration and fence are both durable, so frizz brings you back
|
|
33284
|
+
(\`{{FRIZZ_RESUME_CMD}}\`) even if its own server or your daemon is replaced while you wait.
|
|
33285
|
+
- **A follow-up clears the previous fence.** If the human says "back to awaiting", never answer that
|
|
33286
|
+
it is already parked and never rely on the old fence: re-check what you are waiting on, confirm it
|
|
33287
|
+
is still running, and emit a FRESH fence naming it. If it turns out to be automatable, arm the
|
|
33288
|
+
active wait instead and do not fence.
|
|
33341
33289
|
|
|
33342
33290
|
- \` \`\`\`question \` \u2014 you need the human's input; see **Questions for the human**.
|
|
33343
33291
|
|
|
33344
33292
|
### When the work is finished but the thread found more
|
|
33345
33293
|
|
|
33346
|
-
Your mandate is complete and you turned up something else
|
|
33347
|
-
|
|
33348
|
-
is
|
|
33349
|
-
|
|
33350
|
-
|
|
33351
|
-
1. **DO IT \u2014 the default, and it covers far more than "small".** Anything inside your change's own
|
|
33352
|
-
radius
|
|
33353
|
-
|
|
33354
|
-
is more than one sitting, dispatch an in-session
|
|
33355
|
-
follow-up lands on YOUR card, in your diff, under one
|
|
33356
|
-
|
|
33357
|
-
|
|
33358
|
-
|
|
33359
|
-
|
|
33360
|
-
|
|
33361
|
-
rather than a card you create for them.
|
|
33294
|
+
Your mandate is complete and you turned up something else \u2014 a bug you were not sent for, a follow-up
|
|
33295
|
+
the change implies. \`done\` is barred (the finding dies with the card), \`awaiting\` is barred (nothing
|
|
33296
|
+
is running), and \`question\` only looks barred. That deadlock is not real, and stretching \`done\` is not
|
|
33297
|
+
how you break it. Take the first exit that fits:
|
|
33298
|
+
|
|
33299
|
+
1. **DO IT \u2014 the default, and it covers far more than "small".** Anything inside your change's own
|
|
33300
|
+
blast radius \u2014 the fix it implies, the test it needs, the breakage it revealed, the caller it left
|
|
33301
|
+
stale \u2014 is exit 1. **Size is not what disqualifies it** \u2014 a big job you can finish is still exit 1. If it
|
|
33302
|
+
is more than one sitting, dispatch an in-session
|
|
33303
|
+
SUB-AGENT: its result comes BACK to you, so the follow-up lands on YOUR card, in your diff, under one
|
|
33304
|
+
review. Then \`done\` is honest with nothing left over.
|
|
33305
|
+
2. **ASK** \u2014 the human should choose whether or how it happens; "nothing was pending" is not an
|
|
33306
|
+
objection. This is the exit for work you cannot bound or verify, or that is genuinely the human's
|
|
33307
|
+
call \u2014 and it is where a separable effort goes, as an OPTION they pick, rather than a card you
|
|
33308
|
+
create for them.
|
|
33362
33309
|
3. **HAND IT OFF TO ITS OWN CARD \u2014 the LAST resort, not the tidy one.** \`mcp__frizz__spawn_thread\` puts
|
|
33363
|
-
a separable effort on the board as its own thread; put the returned \`[title](/thread/<slug>)\` link
|
|
33364
|
-
the body. **A spawned thread is FIRE-AND-FORGET: it reports to the HUMAN, never to you, so
|
|
33365
|
-
learns can ever reach this effort or its siblings
|
|
33366
|
-
|
|
33367
|
-
|
|
33368
|
-
|
|
33369
|
-
|
|
33370
|
-
|
|
33371
|
-
|
|
33372
|
-
either: delete it. Do not park it in the handoff as "one thing to carry forward".
|
|
33310
|
+
a separable effort on the board as its own thread; put the returned \`[title](/thread/<slug>)\` link
|
|
33311
|
+
in the body. **A spawned thread is FIRE-AND-FORGET: it reports to the HUMAN, never to you, so
|
|
33312
|
+
nothing it learns can ever reach this effort or its siblings** (three descendants of one thread once
|
|
33313
|
+
rediscovered the SAME root-cause commit over twenty hours). Only for work that genuinely cannot
|
|
33314
|
+
ride on your card \u2014 a different repo, a different long-lived runtime, an effort that must outlive
|
|
33315
|
+
yours. If a sub-agent could do it, exit 1; if the human should choose, exit 2. Never spawn merely
|
|
33316
|
+
to clear your own \`done\`.
|
|
33317
|
+
4. **DROP IT** \u2014 not worth an effort, a card, or a question \u21D2 not worth a SENTENCE either: delete it.
|
|
33318
|
+
Do not park it in the handoff as "one thing to carry forward".
|
|
33373
33319
|
|
|
33374
33320
|
A bare rest is the residual, not a plan \u2014 legitimate only when nothing above fits, and frizz will ask
|
|
33375
|
-
you twice for a fence before it gives up.
|
|
33376
|
-
|
|
33377
|
-
A mid-conversation turn carries NO fence. Nor is a turn on a thread that still points at future work \u2014
|
|
33378
|
-
a live code-change discussion above all \u2014 ever \`done\`.`;
|
|
33321
|
+
you twice for a fence before it gives up. A mid-conversation turn carries NO fence. Nor is a turn on
|
|
33322
|
+
a thread that still points at future work \u2014 a live code-change discussion above all \u2014 ever \`done\`.`;
|
|
33379
33323
|
AGENT_COMPLETION = `## Agent completion invariant
|
|
33380
33324
|
|
|
33381
33325
|
Once you spawn a sub-agent, let it run to its terminal return. Never interrupt one to reclaim capacity,
|
|
@@ -33392,16 +33336,6 @@ image paths through its guarded local-image proxy; only eligible workspace or ex
|
|
|
33392
33336
|
image files can embed, and a path outside that safe boundary stays non-navigable. Do not bulk-embed
|
|
33393
33337
|
irrelevant screenshots. Always keep a concise textual finding alongside them, so the handoff still
|
|
33394
33338
|
reads when images are unavailable.`;
|
|
33395
|
-
GIT_DISCIPLINE = `## Git discipline
|
|
33396
|
-
|
|
33397
|
-
Follow the project's own git/branch/PR conventions \u2014 \`FRIZZ.md\` first, then \`AGENTS.md\`/\`CLAUDE.md\`.
|
|
33398
|
-
This contract states NO default about pull requests: read the repo and do what it says.
|
|
33399
|
-
|
|
33400
|
-
Where the project is silent: if others (humans or agents) may share the working tree, work from an
|
|
33401
|
-
isolated worktree on a fresh branch (\`git worktree add <dir> -b <slug>\`) and never branch, reset, or
|
|
33402
|
-
stash the shared tree. Commit small and often \u2014 committed work cannot be clobbered \u2014 and commit before
|
|
33403
|
-
you rest. Where the project does use PRs, push as soon as a commit exists. Opening the PR does NOT
|
|
33404
|
-
finish the thread \u2014 the MERGE does.`;
|
|
33405
33339
|
QUALITY_BAR = `## Quality bar
|
|
33406
33340
|
|
|
33407
33341
|
- Verify behavior end-to-end before calling anything done. A green suite over a stubbed implementation
|
|
@@ -33464,84 +33398,53 @@ A \` \`\`\`question \` block IS the handback \u2014 do not also emit a \`done\`/
|
|
|
33464
33398
|
as your next user message, possibly as terse as "1: A, 2: B".`;
|
|
33465
33399
|
STOP_CRITERION = `## The stop criterion
|
|
33466
33400
|
|
|
33467
|
-
**COMING TO REST IS A STOP, and it needs the same justification as a question.**
|
|
33468
|
-
|
|
33469
|
-
|
|
33470
|
-
|
|
33471
|
-
|
|
33472
|
-
|
|
33473
|
-
|
|
33474
|
-
|
|
33475
|
-
|
|
33476
|
-
|
|
33477
|
-
|
|
33478
|
-
|
|
33479
|
-
|
|
33480
|
-
|
|
33481
|
-
|
|
33482
|
-
|
|
33483
|
-
|
|
33484
|
-
|
|
33485
|
-
|
|
33486
|
-
|
|
33487
|
-
|
|
33488
|
-
|
|
33489
|
-
**
|
|
33490
|
-
|
|
33491
|
-
|
|
33492
|
-
|
|
33493
|
-
|
|
33494
|
-
|
|
33495
|
-
|
|
33496
|
-
|
|
33497
|
-
|
|
33498
|
-
|
|
33499
|
-
|
|
33500
|
-
|
|
33501
|
-
|
|
33502
|
-
|
|
33503
|
-
|
|
33504
|
-
|
|
33505
|
-
|
|
33506
|
-
**Your default is to DECIDE.** A reversible call costs minutes to redo; a round-trip to the human costs
|
|
33507
|
-
hours \u2014 they may not read your question for hours, and the whole effort sits idle until they do. If it
|
|
33508
|
-
is derivable from the code, the conventions, or ordinary engineering judgment, it is yours: make it,
|
|
33509
|
-
say which way you went, and keep moving.
|
|
33510
|
-
|
|
33511
|
-
**The test that catches almost every bad question: if you are about to mark one option
|
|
33512
|
-
\`(recommended)\`, you already know the answer \u2014 so implement it instead of asking.** Writing a
|
|
33513
|
-
confident rationale for your own recommendation is proof the decision was yours to make. The same goes
|
|
33514
|
-
for "want me to fix it?" on a bug you were dispatched to fix, "should I also
|
|
33515
|
-
handle X?" where X is obviously in scope, and any question about a name, a default value, a file
|
|
33516
|
-
location, an error message, a flag spelling, or which of two equivalent designs to use. Those are
|
|
33517
|
-
granular implementation calls. (A bug you merely NOTICED is not a question either \u2014 by the ceiling
|
|
33518
|
-
above it is a line in your handoff, not work to adopt and not permission to seek.) Make them, note the
|
|
33519
|
-
notable ones in your handoff, and move on \u2014 a
|
|
33520
|
-
decision the human can see and reverse in a line of code is not worth an hours-long stall.
|
|
33521
|
-
|
|
33522
|
-
**"Run it now, or leave it for the human?" is never a question.** Running commands is the job. A
|
|
33523
|
-
safe, reversible command that fixes what you found \u2014 an install, a rebuild, a cache clear, a restart
|
|
33524
|
-
of a process you own \u2014 is run and reported, not offered; a shared working tree changes WHEN you run
|
|
33525
|
-
it, not WHO. Only the ACT behind a command can earn a card (destructive, irreversible, outside your
|
|
33526
|
-
boundary) \u2014 and then the question is whether the thing happens, never who types it.
|
|
33527
|
-
|
|
33528
|
-
**That test inverts when knowing the answer and being ABLE TO ACT ON IT come apart.** Where the act is
|
|
33529
|
-
one you are not permitted or not able to perform \u2014 a read-only boundary, a comment that goes out under
|
|
33530
|
-
the human's name, a merge, a close, a publish, a spend \u2014 your recommendation is not a decision you take
|
|
33531
|
-
silently. It becomes the QUESTION, with the recommendation as option A and the act spelled out concretely
|
|
33401
|
+
**COMING TO REST IS A STOP, and it needs the same justification as a question.** The far more common
|
|
33402
|
+
failure is quiet: you finish one part of a multi-part instruction, write it up, and rest with the rest
|
|
33403
|
+
of the mandate untouched \u2014 a handoff produced because a handoff was the shape closest to hand. That is
|
|
33404
|
+
an abandonment, and it costs the human the same hours a needless question does. **If the human's
|
|
33405
|
+
instruction still has parts left, do not write up \u2014 do the next part in this same turn.** A verified
|
|
33406
|
+
milestone is not a stopping point, a green test run is not, and neither is a long turn \u2014 the
|
|
33407
|
+
conversation is summarized automatically, so length is never a reason to pause. Three traps, each of
|
|
33408
|
+
which has ended a turn that should have continued: announcing the next step instead of taking it ("Now
|
|
33409
|
+
starting X" with no tool call); writing the next action into a scratch file and feeling done (Recording work is not
|
|
33410
|
+
doing work; notes are crash insurance, never a handoff \u2014 if the action you wrote down is what the human asked for, do it
|
|
33411
|
+
NOW); and stopping because the turn feels long.
|
|
33412
|
+
|
|
33413
|
+
**AND THE INSTRUCTION IS ALSO THE CEILING.** "Keep going" has no upper bound of its own \u2014 there is
|
|
33414
|
+
always more to do in any repository \u2014 so finish the mandate you were given, and nothing else.
|
|
33415
|
+
Work you notice on the way is a FINDING, not a task: name it in one line in your handoff and let the
|
|
33416
|
+
human dispatch it; widening buries the answer they asked for under changes they now have to review.
|
|
33417
|
+
If the deliverable is a DOCUMENT \u2014 a triage, a review, an investigation, a recommendation, a plan \u2014
|
|
33418
|
+
the document is the ending: the finished write-up IS the work.
|
|
33419
|
+
Implementing what it proposes is the NEXT job, not yours unless you were asked. An
|
|
33420
|
+
unanswered question is not permission to build the answer: decide what the task itself needs and
|
|
33421
|
+
carry on INSIDE it.
|
|
33422
|
+
|
|
33423
|
+
**Your default is to DECIDE.** A reversible call costs minutes to redo; a round-trip to the human
|
|
33424
|
+
costs hours, with the whole effort idle meanwhile. Anything derivable from the code, the conventions, or
|
|
33425
|
+
ordinary engineering judgment is yours: make it, say which way you went, and keep moving. **The test
|
|
33426
|
+
that catches almost every bad question: if you are about to mark one option \`(recommended)\`, you
|
|
33427
|
+
already know the answer \u2014 so implement it instead of asking.** The same goes for "want me to fix it?"
|
|
33428
|
+
on a bug you were dispatched to fix, "should I also handle X?" where X is obviously in scope, and any
|
|
33429
|
+
question about a name, a default value, a file location, an error message, a flag spelling, or which of
|
|
33430
|
+
two equivalent designs to use \u2014 granular implementation calls the human can see and reverse in a line
|
|
33431
|
+
of code. (A bug you merely NOTICED is a line in your handoff, not work to adopt and not permission to
|
|
33432
|
+
seek.) **"Run it now, or leave it for the human?" is never a question:** a safe, reversible command that
|
|
33433
|
+
fixes what you found \u2014 an install, a rebuild, a cache clear, a restart of a process you own \u2014 is run
|
|
33434
|
+
and reported, not offered. Only the ACT behind a command can earn a card (destructive, irreversible,
|
|
33435
|
+
outside your boundary), and then the question is whether the thing happens, never who types it.
|
|
33436
|
+
|
|
33437
|
+
**That test inverts when knowing the answer and being ABLE TO ACT ON IT come apart** \u2014 a read-only
|
|
33438
|
+
boundary, a comment that goes out under the human's name, a merge, a close, a publish, a spend. It becomes the QUESTION, with the recommendation as option A and the act spelled out concretely
|
|
33532
33439
|
enough to approve in one word. Never resolve that fork by fencing \` \`\`\`done \` on the investigation.
|
|
33533
33440
|
|
|
33534
|
-
Stop only when a wrong guess would be BOTH costly AND hard to undo:
|
|
33535
|
-
|
|
33536
|
-
|
|
33537
|
-
|
|
33538
|
-
|
|
33539
|
-
|
|
33540
|
-
|
|
33541
|
-
|
|
33542
|
-
Everything else \u2014 mechanical work, clear bugs, refactors, defaults you can justify, architecture you
|
|
33543
|
-
can reverse \u2014 you finish. When you do stop, surface it as a \` \`\`\`question \` with your recommendation
|
|
33544
|
-
marked, and do all the work that does NOT depend on the answer first.`;
|
|
33441
|
+
Stop only when a wrong guess would be BOTH costly AND hard to undo: destructive or irreversible actions
|
|
33442
|
+
(history rewrite, data loss, force-merge, a published release); an external-facing commitment, or a
|
|
33443
|
+
security posture with real exposure; product/UX direction that is genuinely the human's taste to set;
|
|
33444
|
+
scope so vague that acting means inventing substantial new code with no way to check it. Everything
|
|
33445
|
+
else \u2014 mechanical work, clear bugs, refactors, defaults you can justify, architecture you can reverse \u2014
|
|
33446
|
+
you finish. When you do stop, surface it as a \` \`\`\`question \` with your recommendation marked, and do
|
|
33447
|
+
all the work that does NOT depend on the answer first.`;
|
|
33545
33448
|
TRIVIAL_PROMPTS = `## Trivial and conversational prompts
|
|
33546
33449
|
|
|
33547
33450
|
Some dispatches never deserved a work effort \u2014 a greeting, a one-line question, a joke, a test ping.
|
|
@@ -33630,13 +33533,10 @@ see your conversation, so handing over context is always your job. The absence o
|
|
|
33630
33533
|
a blocker to report \u2014 write what the child needs into the prompt or a scratch file you name, or do
|
|
33631
33534
|
the work inline.
|
|
33632
33535
|
|
|
33633
|
-
|
|
33634
|
-
|
|
33635
|
-
|
|
33636
|
-
|
|
33637
|
-
lands, diagnosis, architecture, and any probe whose deliverable is a load-bearing verdict. Effort runs
|
|
33638
|
-
low\u2192medium\u2192high\u2192xhigh\u2192max. Bias toward Opus and buy extra rigor with EFFORT; re-verify any cheap-tier
|
|
33639
|
-
load-bearing claim yourself.
|
|
33536
|
+
Two knobs, one each: the model rides the Agent tool's own \`model\` parameter (omit it and the child
|
|
33537
|
+
inherits yours), and the effort rides \`subagent_type\` as the namespaced string \`frizz:<effort>\` \u2014
|
|
33538
|
+
\`frizz:low\`, \`frizz:medium\`, \`frizz:high\`, \`frizz:xhigh\` or \`frizz:max\` (a bare \`high\` will not
|
|
33539
|
+
resolve). Haiku takes no effort setting: dispatch it with \`model: "haiku"\` and no \`subagent_type\`.
|
|
33640
33540
|
|
|
33641
33541
|
Fan out one sub-agent per prong when work genuinely decomposes and the scale warrants it \u2014 authorized,
|
|
33642
33542
|
never required, and never a substitute for running the thing yourself.
|
|
@@ -33656,66 +33556,42 @@ the handoff.
|
|
|
33656
33556
|
|
|
33657
33557
|
## Automated waits in Claude Code
|
|
33658
33558
|
|
|
33659
|
-
Prefer a project-declared monitor when one exists (inspect project-local \`AGENTS.md\`, skills, docs,
|
|
33660
|
-
package scripts, and declared monitor tooling) only after validating its absolute command and
|
|
33661
|
-
terminal event/exit semantics; invalid declared tooling is a visible configuration error and must never be
|
|
33662
|
-
silently shadowed. Otherwise frizz's portable \`monitors/*.mjs\` are the fallback and native \`Monitor\` is the
|
|
33663
|
-
Claude adapter for a changing condition.
|
|
33664
|
-
|
|
33665
33559
|
**The mechanism is decided by whether you will REST while it runs.** Only a live sub-agent keeps a
|
|
33666
33560
|
rested thread out of the queue.
|
|
33667
33561
|
|
|
33668
|
-
- **Resting until
|
|
33669
|
-
|
|
33670
|
-
|
|
33671
|
-
|
|
33672
|
-
|
|
33673
|
-
|
|
33674
|
-
job control (\`&\`, \`nohup \u2026 &\`,
|
|
33675
|
-
|
|
33562
|
+
- **Resting until a condition is met** (the usual CI / PR / release wait) \u2192 for a pull request,
|
|
33563
|
+
\`mcp__frizz__watch_pr\`; for anything else dispatch a SUB-AGENT to own the wait. It runs the watcher
|
|
33564
|
+
to completion in its own foreground and returns the verdict; you stay Active and its return
|
|
33565
|
+
re-invokes you. Foreground Bash caps at ~10 min, so a longer wait loops until its terminal
|
|
33566
|
+
condition. A helper must not hand back while its own watcher is still live.
|
|
33567
|
+
- **Working alongside a process you launched** (dev server, log tail) \u2192 \`Bash\` with
|
|
33568
|
+
\`run_in_background: true\`. Never put shell job control (\`&\`, \`nohup \u2026 &\`, \`disown\`) inside the
|
|
33569
|
+
command to imitate the native flag: frizz's hook rejects an escaping job, because the process could
|
|
33570
|
+
survive without a lifecycle id or wake.
|
|
33676
33571
|
- \`Monitor\` streams events INTO an active turn (\`persistent: true\` runs until \`TaskStop\` or session
|
|
33677
33572
|
end); it is not something to park a rest on. \`TaskOutput\` is deprecated \u2014 use \`Read\` on that output
|
|
33678
33573
|
path for diagnostics. \`TaskStop\` is only for your own monitor after its terminal handoff, never to
|
|
33679
33574
|
cut off a sub-agent.
|
|
33680
33575
|
|
|
33681
33576
|
These live tasks do not survive the session ending. Never fake a wait with \`echo waiting\` or repeated
|
|
33682
|
-
foreground sleeps.
|
|
33683
|
-
|
|
33684
|
-
|
|
33685
|
-
|
|
33686
|
-
|
|
33687
|
-
|
|
33688
|
-
|
|
33689
|
-
|
|
33690
|
-
|
|
33691
|
-
|
|
33692
|
-
|
|
33693
|
-
|
|
33694
|
-
|
|
33695
|
-
|
|
33696
|
-
|
|
33697
|
-
|
|
33698
|
-
|
|
33699
|
-
|
|
33700
|
-
drives is finished \u2014 one left armed on a finished thread wakes it forever, and the human can also
|
|
33701
|
-
switch it off in the thread footer. Replying \`ALLDONE\` on its own line stops BOTH triggers too, but
|
|
33702
|
-
treat that as a last resort: it permanently stalls the run, and a stalled run nobody is watching does
|
|
33703
|
-
not restart itself. Be certain there is no further work before you use it.
|
|
33704
|
-
|
|
33705
|
-
**And for a single moment in the future, \`mcp__frizz__timer\` \u2014 your own alarm clock.** It arms ONE
|
|
33706
|
-
piece of text for ONE instant (\`action: "set"\` with \`prompt\` plus either \`in_seconds\` or an ISO
|
|
33707
|
-
\`at\`), delivered exactly once and then gone. It is the heartbeat without the repetition, and it shares
|
|
33708
|
-
the property that matters: it reaches you MID-TURN, so it lands when you asked for it whether or not
|
|
33709
|
-
you have stopped, and it never aborts what you are running.
|
|
33710
|
-
|
|
33711
|
-
Unlike the recurring prompt \u2014 of which a thread has at most one \u2014 you may hold MANY timers at once,
|
|
33712
|
-
each with its own instant and its own text. \`action: "list"\` shows what is armed and \`action:
|
|
33713
|
-
"cancel"\` withdraws one by id; a fired timer needs no cleanup.
|
|
33714
|
-
|
|
33715
|
-
Reach for it when you want to come back to something at a specific time: re-check a deploy in ten
|
|
33716
|
-
minutes, re-read a slow log at the top of the hour. Do NOT use it to poll something you could be woken
|
|
33717
|
-
by \u2014 if a background shell, a sub-agent or a monitor can tell you the moment a thing happens, that is
|
|
33718
|
-
strictly better than an alarm that asks "is it done yet".
|
|
33577
|
+
foreground sleeps.
|
|
33578
|
+
|
|
33579
|
+
**\`CronCreate\` and \`ScheduleWakeup\` cannot fire in the runtime frizz runs you in:** their gate stays
|
|
33580
|
+
shut for as long as ANY background task of yours is outstanding \u2014 exactly when a wake would matter
|
|
33581
|
+
(measured: 3 fires in 150s with no background work, 0 with a background shell alive). Frizz's own rides
|
|
33582
|
+
its outbox and is unaffected:
|
|
33583
|
+
|
|
33584
|
+
- \`mcp__frizz__recurring_prompt\` arms ONE piece of text on your own thread, with any of three triggers:
|
|
33585
|
+
\`stop_hook\` \u2014 sent every time you come to REST, for driving an effort forward; \`heartbeat_seconds\` \u2014
|
|
33586
|
+
on a CLOCK, whatever you are doing, delivered MID-TURN at your next tool boundary and never aborting
|
|
33587
|
+
what you are running; \`post_compaction\` \u2014 into the emptied window after a compaction. Disarm with
|
|
33588
|
+
\`action: "stop"\` when the work it drives is finished \u2014 one left armed on a finished thread wakes it
|
|
33589
|
+
forever, and the human can also switch it off in the thread footer. Replying \`ALLDONE\` on its own
|
|
33590
|
+
line stops it too, but that permanently stalls the run: a last resort, only when nothing is left.
|
|
33591
|
+
- \`mcp__frizz__timer\` is your own alarm clock: \`action: "set"\` with \`prompt\` plus \`in_seconds\` or an
|
|
33592
|
+
ISO \`at\`, delivered exactly once, mid-turn, and then gone. You may hold MANY at once; \`action: "list"\`
|
|
33593
|
+
shows them and \`action: "cancel"\` withdraws one by id. Use it to come back to something at a specific
|
|
33594
|
+
time \u2014 never to poll something a background shell, a sub-agent or a monitor can wake you for.
|
|
33719
33595
|
|
|
33720
33596
|
## Showing the human files and images
|
|
33721
33597
|
|
|
@@ -33725,6 +33601,15 @@ your scratch directory. Pass an ARRAY to render several in one captioned block:
|
|
|
33725
33601
|
\u2014 \`"proactive"\` when the human is away and should get a push, else \`"normal"\`. Reach for it eagerly
|
|
33726
33602
|
whenever you have screenshots worth showing: it renders the whole decisive set inline, which a terminal
|
|
33727
33603
|
agent cannot do.`,
|
|
33604
|
+
// § Bounded native delegation describes the NATIVE `spawn_agent` surface, which frizz does not
|
|
33605
|
+
// configure at all. The only `-c` overrides frizz puts on a codex app-server are the frizz MCP mount
|
|
33606
|
+
// and `default_tools_approval_mode` (backend/codex-mcp.ts `codexAppServerArgv`, the single argv
|
|
33607
|
+
// builder for all four spawn sites). Until 2026-08-26 this section opened by claiming frizz "requests
|
|
33608
|
+
// the V2 surface with process-scoped, version-gated CLI overrides" — no commit ever added one, so
|
|
33609
|
+
// every codex worker was told frizz did something it does not do. The gate itself was never wrong:
|
|
33610
|
+
// `spawn_agent` carries `model`/`reasoning_effort` natively (parseCodexSubAgentLine reads both off
|
|
33611
|
+
// real rollouts), so reading the live schema is the whole check. Keep this paragraph a statement
|
|
33612
|
+
// about what CODEX ships, never about what frizz sends.
|
|
33728
33613
|
codex: `## Own one task
|
|
33729
33614
|
|
|
33730
33615
|
You are one top-level Frizz worker, not the dashboard's portfolio orchestrator. Own only the TASK
|
|
@@ -33780,11 +33665,12 @@ for the title signal: H1 parsing exists only for compatibility with old transcri
|
|
|
33780
33665
|
|
|
33781
33666
|
When delegation is explicitly authorized:
|
|
33782
33667
|
|
|
33783
|
-
1. Frizz
|
|
33784
|
-
|
|
33668
|
+
1. Frizz sends NO config override for the native spawn surface \u2014 the only \`-c\` overrides on the
|
|
33669
|
+
app-server mount frizz's own MCP server and pre-approve its tools \u2014 so what the spawn tool accepts
|
|
33670
|
+
is whatever this Codex release happens to ship. Use the active native spawn tool only when its
|
|
33785
33671
|
runtime schema exposes both \`model\` and
|
|
33786
|
-
\`reasoning_effort\`.
|
|
33787
|
-
|
|
33672
|
+
\`reasoning_effort\`. Codex may show a runtime-normalized tool name, so trust the callable schema
|
|
33673
|
+
rather than the name you expect. Pass both fields on every dispatch; omit \`agent_type\` for
|
|
33788
33674
|
ordinary compute routing. Choose the child's CONTEXT deliberately \u2014 the schema's context-fork
|
|
33789
33675
|
control goes BOTH ways, under whatever name the live schema exposes it (current Codex: \`fork_turns\`;
|
|
33790
33676
|
older builds: \`fork_context\`). Pass NO parent history (\`fork_turns: "none"\`) for an INDEPENDENT
|
|
@@ -33794,10 +33680,10 @@ When delegation is explicitly authorized:
|
|
|
33794
33680
|
conversation so far is load-bearing. Fresh is the default for frizz work; a fork is heavier and
|
|
33795
33681
|
carries your assumptions with it. The schema default is a FULL fork, so an unset control silently
|
|
33796
33682
|
hands the child everything \u2014 set it explicitly either way, and when you do fork, verify the child's
|
|
33797
|
-
effective model/effort from native metadata rather than assuming
|
|
33798
|
-
invent a field the schema lacks, and a missing or unfamiliar context-fork control is NOT by
|
|
33799
|
-
a routing failure (keep such a child self-contained and note it). Only
|
|
33800
|
-
being
|
|
33683
|
+
effective model/effort from native metadata rather than assuming the fields you passed survived.
|
|
33684
|
+
Never invent a field the schema lacks, and a missing or unfamiliar context-fork control is NOT by
|
|
33685
|
+
itself a routing failure (keep such a child self-contained and note it). Only
|
|
33686
|
+
\`model\`/\`reasoning_effort\` being absent from the live schema makes the session degraded/no-routing:
|
|
33801
33687
|
do not silently fall back to inherited compute. Finish inline when independence is not required,
|
|
33802
33688
|
or report the unmet gate.
|
|
33803
33689
|
2. Give each child one self-contained, non-overlapping outcome with its paths, authority, evidence or
|
|
@@ -34065,7 +33951,7 @@ function connectClaudeBroker(socketPath, handlers, options = {}) {
|
|
|
34065
33951
|
pendingSkills.delete(frame.requestId);
|
|
34066
33952
|
clearTimeout(entry.timer);
|
|
34067
33953
|
if (typeof frame.error === "string" && frame.error) entry.fail(new Error(frame.error));
|
|
34068
|
-
else entry.settle((Array.isArray(frame.skills) ? frame.skills : []).filter((s) => typeof s?.name === "string" && typeof s?.description === "string"));
|
|
33954
|
+
else entry.settle((Array.isArray(frame.skills) ? frame.skills : []).filter((s) => typeof s?.name === "string" && typeof s?.description === "string").map((s) => ({ name: s.name, description: s.description, source: SKILL_SOURCES.has(s.source) ? s.source : void 0 })));
|
|
34069
33955
|
break;
|
|
34070
33956
|
}
|
|
34071
33957
|
}
|
|
@@ -34213,9 +34099,12 @@ function connectClaudeBroker(socketPath, handlers, options = {}) {
|
|
|
34213
34099
|
}
|
|
34214
34100
|
};
|
|
34215
34101
|
}
|
|
34102
|
+
var SKILL_SOURCES;
|
|
34216
34103
|
var init_claude_broker_client = __esm({
|
|
34217
34104
|
"packages/server/src/backend/claude-broker-client.ts"() {
|
|
34218
34105
|
"use strict";
|
|
34106
|
+
init_src();
|
|
34107
|
+
SKILL_SOURCES = new Set(ThreadSkillSource.options);
|
|
34219
34108
|
}
|
|
34220
34109
|
});
|
|
34221
34110
|
|
|
@@ -34595,6 +34484,9 @@ function createClaudeAgentBrokerBridge(deps) {
|
|
|
34595
34484
|
// claudeWorkerEnv). Spread here rather than inherited, which is what gives these per-thread
|
|
34596
34485
|
// values priority over anything in the environment frizz itself was launched with.
|
|
34597
34486
|
...claudeWorkerEnv(),
|
|
34487
|
+
// The auto-compact ceiling, from Settings (500K by default). Without it a `[1m]` worker compacts
|
|
34488
|
+
// only near 1M, re-sending up to 5x a TUI session's conversation on every turn past 200K.
|
|
34489
|
+
...claudeCompactionEnv(deps.getSettings?.()),
|
|
34598
34490
|
...we?.permDir ? { FRIZZ_PERM_DIR: we.permDir } : {},
|
|
34599
34491
|
// The cc-worker plugin's PreToolUse hook DENIES AskUserQuestion, because without frizz in the
|
|
34600
34492
|
// loop a blocking question freezes a headless worker where nobody can answer it. On the broker path
|
|
@@ -34870,19 +34762,19 @@ var init_claude_agent_broker_bridge = __esm({
|
|
|
34870
34762
|
});
|
|
34871
34763
|
|
|
34872
34764
|
// packages/server/src/backend/auth-status.ts
|
|
34873
|
-
import { join as
|
|
34765
|
+
import { join as join26 } from "node:path";
|
|
34874
34766
|
import { homedir as homedir11, platform as platform2 } from "node:os";
|
|
34875
34767
|
import { readFileSync as readFileSync16, statSync as statSync11 } from "node:fs";
|
|
34876
34768
|
import { execFile as execFile4 } from "node:child_process";
|
|
34877
34769
|
import { promisify as promisify4 } from "node:util";
|
|
34878
34770
|
function claudeConfigDir2() {
|
|
34879
34771
|
const override = process.env.CLAUDE_CONFIG_DIR;
|
|
34880
|
-
return override && override.trim() ? override :
|
|
34772
|
+
return override && override.trim() ? override : join26(homedir11(), ".claude");
|
|
34881
34773
|
}
|
|
34882
34774
|
function claudeFileState(configDir) {
|
|
34883
34775
|
let raw2;
|
|
34884
34776
|
try {
|
|
34885
|
-
raw2 = readFileSync16(
|
|
34777
|
+
raw2 = readFileSync16(join26(configDir, ".credentials.json"), "utf8");
|
|
34886
34778
|
} catch (err) {
|
|
34887
34779
|
return err.code === "ENOENT" ? "absent" : "error";
|
|
34888
34780
|
}
|
|
@@ -34915,7 +34807,7 @@ function readCodexAuthState(codexHome = defaultCodexHome()) {
|
|
|
34915
34807
|
if (process.env.OPENAI_API_KEY || process.env.CODEX_API_KEY || process.env.CODEX_ACCESS_TOKEN) return "authed";
|
|
34916
34808
|
let raw2;
|
|
34917
34809
|
try {
|
|
34918
|
-
raw2 = readFileSync16(
|
|
34810
|
+
raw2 = readFileSync16(join26(codexHome, "auth.json"), "utf8");
|
|
34919
34811
|
} catch (err) {
|
|
34920
34812
|
return err.code === "ENOENT" ? "signed-out" : "unknown";
|
|
34921
34813
|
}
|
|
@@ -34980,7 +34872,7 @@ function asEmail(value) {
|
|
|
34980
34872
|
}
|
|
34981
34873
|
function claudeAccountFile() {
|
|
34982
34874
|
const override = process.env.CLAUDE_CONFIG_DIR;
|
|
34983
|
-
return override && override.trim() ?
|
|
34875
|
+
return override && override.trim() ? join26(override, ".claude.json") : join26(homedir11(), ".claude.json");
|
|
34984
34876
|
}
|
|
34985
34877
|
function readClaudeAccountEmail(path = claudeAccountFile()) {
|
|
34986
34878
|
let stat2;
|
|
@@ -35004,7 +34896,7 @@ function readClaudeAccountEmail(path = claudeAccountFile()) {
|
|
|
35004
34896
|
function readCodexAccountEmail(codexHome = defaultCodexHome()) {
|
|
35005
34897
|
let doc;
|
|
35006
34898
|
try {
|
|
35007
|
-
doc = JSON.parse(readFileSync16(
|
|
34899
|
+
doc = JSON.parse(readFileSync16(join26(codexHome, "auth.json"), "utf8"));
|
|
35008
34900
|
} catch {
|
|
35009
34901
|
return void 0;
|
|
35010
34902
|
}
|
|
@@ -35072,8 +34964,8 @@ var init_auth_status = __esm({
|
|
|
35072
34964
|
});
|
|
35073
34965
|
|
|
35074
34966
|
// packages/server/src/dispatch.ts
|
|
35075
|
-
import { closeSync as closeSync12, constants, existsSync as
|
|
35076
|
-
import { basename as basename6, join as
|
|
34967
|
+
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";
|
|
34968
|
+
import { basename as basename6, join as join27, dirname as dirname11 } from "node:path";
|
|
35077
34969
|
import { createHash as createHash11, randomUUID as randomUUID13 } from "node:crypto";
|
|
35078
34970
|
function fallbackTitle(prompt) {
|
|
35079
34971
|
const firstLine2 = prompt.trim().split("\n", 1)[0].trim();
|
|
@@ -35092,7 +34984,7 @@ function fallbackTitle(prompt) {
|
|
|
35092
34984
|
}
|
|
35093
34985
|
function resolveSlug(frizzDir, base, taken) {
|
|
35094
34986
|
base = ThreadSlug.parse(base);
|
|
35095
|
-
const isTaken = (slug) =>
|
|
34987
|
+
const isTaken = (slug) => existsSync9(join27(frizzDir, `${slug}.md`)) || (taken?.(slug) ?? false);
|
|
35096
34988
|
if (!isTaken(base)) return base;
|
|
35097
34989
|
for (let n = 2; ; n++) {
|
|
35098
34990
|
const suffix = `-${n}`;
|
|
@@ -35109,12 +35001,12 @@ function resolveLegacyThreadFile(projectDir, value) {
|
|
|
35109
35001
|
if (!parsed.success) return null;
|
|
35110
35002
|
try {
|
|
35111
35003
|
const projectRoot = realpathSync7(projectDir);
|
|
35112
|
-
const frizzPath =
|
|
35004
|
+
const frizzPath = join27(projectRoot, ".frizz");
|
|
35113
35005
|
const frizzStat = lstatSync3(frizzPath);
|
|
35114
35006
|
if (!frizzStat.isDirectory() || frizzStat.isSymbolicLink()) return null;
|
|
35115
35007
|
const realFrizz = realpathSync7(frizzPath);
|
|
35116
35008
|
if (dirname11(realFrizz) !== projectRoot || basename6(realFrizz) !== ".frizz") return null;
|
|
35117
|
-
const path =
|
|
35009
|
+
const path = join27(realFrizz, `${parsed.data}.md`);
|
|
35118
35010
|
const before = lstatSync3(path);
|
|
35119
35011
|
if (!before.isFile() || before.isSymbolicLink()) return null;
|
|
35120
35012
|
const realPath = realpathSync7(path);
|
|
@@ -35158,7 +35050,7 @@ function boardAuthorizesAdoption(board, slug) {
|
|
|
35158
35050
|
return !board.errorItems.some((item) => item.file === `${slug}.md`);
|
|
35159
35051
|
}
|
|
35160
35052
|
function ensureSafeDirectDirectory(parent, name) {
|
|
35161
|
-
const path =
|
|
35053
|
+
const path = join27(parent, name);
|
|
35162
35054
|
try {
|
|
35163
35055
|
mkdirSync13(path);
|
|
35164
35056
|
} catch (error) {
|
|
@@ -35188,8 +35080,8 @@ function loadWorkerPrompt(kind = "claude") {
|
|
|
35188
35080
|
function monitorScriptsDir() {
|
|
35189
35081
|
const plugin = workerPluginDir();
|
|
35190
35082
|
if (!plugin) return void 0;
|
|
35191
|
-
const dir =
|
|
35192
|
-
return
|
|
35083
|
+
const dir = join27(plugin, "skills", "gh", "scripts");
|
|
35084
|
+
return existsSync9(join27(dir, "ci-watch.mjs")) ? dir : void 0;
|
|
35193
35085
|
}
|
|
35194
35086
|
function codexScratchpadHookConfig(hookScript, sessionId) {
|
|
35195
35087
|
if (!hookScript || !sessionId) return {};
|
|
@@ -35201,7 +35093,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
|
|
|
35201
35093
|
}
|
|
35202
35094
|
]
|
|
35203
35095
|
});
|
|
35204
|
-
const bashBackgroundHook =
|
|
35096
|
+
const bashBackgroundHook = join27(dirname11(hookScript), "bash-background.mjs");
|
|
35205
35097
|
return {
|
|
35206
35098
|
bypass_hook_trust: true,
|
|
35207
35099
|
hooks: {
|
|
@@ -35227,7 +35119,7 @@ function codexScratchpadHookConfig(hookScript, sessionId) {
|
|
|
35227
35119
|
}
|
|
35228
35120
|
function scratchpadHookScript() {
|
|
35229
35121
|
const plugin = workerPluginDir();
|
|
35230
|
-
return plugin ?
|
|
35122
|
+
return plugin ? join27(plugin, "hooks", "scratchpad.mjs") : void 0;
|
|
35231
35123
|
}
|
|
35232
35124
|
function composePrompt(sessionId, prompt, kind = "claude") {
|
|
35233
35125
|
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.";
|
|
@@ -35243,7 +35135,7 @@ function scratchpadOrientation(sessionId, kind = "claude") {
|
|
|
35243
35135
|
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.`;
|
|
35244
35136
|
}
|
|
35245
35137
|
function frizzConfigBlock(projectDir) {
|
|
35246
|
-
const path =
|
|
35138
|
+
const path = join27(projectDir, "FRIZZ.md");
|
|
35247
35139
|
let body;
|
|
35248
35140
|
try {
|
|
35249
35141
|
const st = statSync12(path);
|
|
@@ -35283,8 +35175,8 @@ function resolveFrizzMcp(target, moduleUrl = import.meta.url, env = process.env,
|
|
|
35283
35175
|
const { stateDir, serverLock, projectId } = typeof target === "string" ? { stateDir: target } : target;
|
|
35284
35176
|
const pluginDir = resolveWorkerPluginDir(moduleUrl, env);
|
|
35285
35177
|
if (!pluginDir) return void 0;
|
|
35286
|
-
const scriptPath =
|
|
35287
|
-
if (!
|
|
35178
|
+
const scriptPath = join27(pluginDir, "bin", FRIZZ_MCP.script);
|
|
35179
|
+
if (!existsSync9(scriptPath)) return void 0;
|
|
35288
35180
|
return {
|
|
35289
35181
|
scriptPath,
|
|
35290
35182
|
stateDir,
|
|
@@ -35294,12 +35186,8 @@ function resolveFrizzMcp(target, moduleUrl = import.meta.url, env = process.env,
|
|
|
35294
35186
|
};
|
|
35295
35187
|
}
|
|
35296
35188
|
function claudeMcpConfig(mcp) {
|
|
35297
|
-
const mcpServers = {
|
|
35298
|
-
|
|
35299
|
-
// is what mounts frizz's lazy proxy instead of the real server (backend/types.ts).
|
|
35300
|
-
[CHROME_DEVTOOLS_MCP.name]: chromeDevtoolsMcpMount()
|
|
35301
|
-
};
|
|
35302
|
-
const allowedTools = [`mcp__${CHROME_DEVTOOLS_MCP.name}`];
|
|
35189
|
+
const mcpServers = {};
|
|
35190
|
+
const allowedTools = [];
|
|
35303
35191
|
if (mcp) {
|
|
35304
35192
|
const env = frizzMcpEnv(mcp);
|
|
35305
35193
|
mcpServers[FRIZZ_MCP.name] = { command: process.execPath, args: [mcp.scriptPath], env };
|
|
@@ -35309,6 +35197,7 @@ function claudeMcpConfig(mcp) {
|
|
|
35309
35197
|
}
|
|
35310
35198
|
function claudeMcpFlags(mcp) {
|
|
35311
35199
|
const { mcpServers, allowedTools } = claudeMcpConfig(mcp);
|
|
35200
|
+
if (Object.keys(mcpServers).length === 0) return [];
|
|
35312
35201
|
const config = JSON.stringify({ mcpServers });
|
|
35313
35202
|
return ["--mcp-config", config, `--allowedTools=${allowedTools.join(",")}`];
|
|
35314
35203
|
}
|
|
@@ -35366,7 +35255,7 @@ function buildClaudeResumeCommand(opts) {
|
|
|
35366
35255
|
}
|
|
35367
35256
|
function createDispatcher(deps) {
|
|
35368
35257
|
const readBoardSource = deps.readBoard ?? readBoard;
|
|
35369
|
-
const frizzDir =
|
|
35258
|
+
const frizzDir = join27(deps.project.dir, ".frizz");
|
|
35370
35259
|
const adoptionRuntime = deps.adoptionRuntime ?? productionRuntime;
|
|
35371
35260
|
function cleanupPrewrites(built) {
|
|
35372
35261
|
for (const path of new Set(built.prewrite.map((file) => file.path))) {
|
|
@@ -35379,7 +35268,7 @@ function createDispatcher(deps) {
|
|
|
35379
35268
|
function cleanupDispatchFiles(scratchRel, built, sessionId) {
|
|
35380
35269
|
cleanupPrewrites(built);
|
|
35381
35270
|
try {
|
|
35382
|
-
rmSync9(
|
|
35271
|
+
rmSync9(join27(deps.project.dir, scratchRel), { force: true, recursive: true });
|
|
35383
35272
|
} catch {
|
|
35384
35273
|
}
|
|
35385
35274
|
cleanupAdoptionSessionFiles(deps.project.dir, sessionId);
|
|
@@ -35812,7 +35701,7 @@ import {
|
|
|
35812
35701
|
watch as fsWatch
|
|
35813
35702
|
} from "node:fs";
|
|
35814
35703
|
import { homedir as homedir12 } from "node:os";
|
|
35815
|
-
import { join as
|
|
35704
|
+
import { join as join28 } from "node:path";
|
|
35816
35705
|
import watcher from "@parcel/watcher";
|
|
35817
35706
|
function appServerTurnStalled(liveness, lastActivityAt, nowMs) {
|
|
35818
35707
|
if (!liveness) return false;
|
|
@@ -35910,9 +35799,9 @@ function hasParkedTimerWatch(tele, armedTimerIds) {
|
|
|
35910
35799
|
if (tele?.lastFence?.kind !== "awaiting") return false;
|
|
35911
35800
|
return tele.lastFence.hints.some((hint) => hint.kind === "timer" && armedTimerIds.has(hint.value.trim()));
|
|
35912
35801
|
}
|
|
35913
|
-
function hasDeclaredWait(tele, nowMs, armedTimerIds = /* @__PURE__ */ new Set()) {
|
|
35802
|
+
function hasDeclaredWait(tele, nowMs, armedTimerIds = /* @__PURE__ */ new Set(), registeredPrWatches = /* @__PURE__ */ new Set()) {
|
|
35914
35803
|
if (hasDeclaredBackgroundPark(tele, nowMs)) return true;
|
|
35915
|
-
if (tele?.lastFence?.kind === "awaiting" && hasParkedPrWatch(tele)) return true;
|
|
35804
|
+
if (tele?.lastFence?.kind === "awaiting" && hasParkedPrWatch(tele, registeredPrWatches)) return true;
|
|
35916
35805
|
if (hasParkedTimerWatch(tele, armedTimerIds)) return true;
|
|
35917
35806
|
return (tele?.subAgents ?? []).some((a) => isDirectSubAgent(a) && a.state === "running");
|
|
35918
35807
|
}
|
|
@@ -35930,7 +35819,7 @@ function heldByRunningChecks(tele, github, registered) {
|
|
|
35930
35819
|
return status?.checks === "running" && status.state === "open";
|
|
35931
35820
|
});
|
|
35932
35821
|
}
|
|
35933
|
-
function hasLiveOwnWork(tele) {
|
|
35822
|
+
function hasLiveOwnWork(tele, registeredPrWatches) {
|
|
35934
35823
|
return Boolean(
|
|
35935
35824
|
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
|
|
35936
35825
|
// worker left it running, it will re-invoke the thread on its own, and until it does there is
|
|
@@ -35938,12 +35827,16 @@ function hasLiveOwnWork(tele) {
|
|
|
35938
35827
|
// counting it — for that card's event-snooze, which is now the ONE control for hiding a parked
|
|
35939
35828
|
// watcher (maintainer: "the user can just use the generic snooze card… now GitHub watchers can be
|
|
35940
35829
|
// included in the ranks of those").
|
|
35941
|
-
hasParkedPrWatch(tele)
|
|
35830
|
+
hasParkedPrWatch(tele, registeredPrWatches)
|
|
35942
35831
|
);
|
|
35943
35832
|
}
|
|
35944
|
-
function hasParkedPrWatch(tele) {
|
|
35833
|
+
function hasParkedPrWatch(tele, registered) {
|
|
35945
35834
|
if (tele?.lastFence?.kind !== "awaiting") return false;
|
|
35946
|
-
return tele.lastFence.hints.some((hint) =>
|
|
35835
|
+
return tele.lastFence.hints.some((hint) => {
|
|
35836
|
+
if (hint.kind !== "pr") return false;
|
|
35837
|
+
const ref = parsePrRef(hint.value);
|
|
35838
|
+
return ref !== void 0 && registered.has(githubStatusKey(ref));
|
|
35839
|
+
});
|
|
35947
35840
|
}
|
|
35948
35841
|
function bgSnoozeArmed(row) {
|
|
35949
35842
|
return row.bg_snooze_rested_at != null && row.rested_at != null && row.bg_snooze_rested_at === row.rested_at;
|
|
@@ -35985,7 +35878,7 @@ function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], arme
|
|
|
35985
35878
|
if (tele?.lastFence?.kind !== "awaiting") return out;
|
|
35986
35879
|
const createdAt = fenceAt ?? tele.lastAssistantAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
35987
35880
|
for (const hint of tele.lastFence.hints) {
|
|
35988
|
-
if (hint.kind !== "shell"
|
|
35881
|
+
if (hint.kind !== "shell") continue;
|
|
35989
35882
|
const target = hint.value.trim();
|
|
35990
35883
|
if (!target || seen.has(`shell:${target}`) || !liveWaitHandles(tele).has(target)) continue;
|
|
35991
35884
|
seen.add(`shell:${target}`);
|
|
@@ -36012,15 +35905,15 @@ function deriveNeedsYou(row, tele, runtime, hasActionableInteraction = false, no
|
|
|
36012
35905
|
if (excuseLiveOwnWork && runtime !== "exited" && hasLiveBackgroundWork(tele) && !tele?.lastFence) return false;
|
|
36013
35906
|
if (excuseLiveOwnWork && runtime !== "exited" && hasDeclaredBackgroundPark(tele, nowMs)) return false;
|
|
36014
35907
|
if (excuseLiveOwnWork && runtime !== "exited" && heldByRunningChecks(tele, github, registeredPrWatches)) return false;
|
|
36015
|
-
if (runtime !== "exited" && (hasLiveOwnWork(tele) || hasParkedTimerWatch(tele, armedTimerIds)) && tele?.lastFence?.kind !== "done") return !bgSnoozeArmed(row);
|
|
35908
|
+
if (runtime !== "exited" && (hasLiveOwnWork(tele, registeredPrWatches) || hasParkedTimerWatch(tele, armedTimerIds)) && tele?.lastFence?.kind !== "done") return !bgSnoozeArmed(row);
|
|
36016
35909
|
if (tele?.lastFence?.kind === "done") return true;
|
|
36017
35910
|
return true;
|
|
36018
35911
|
}
|
|
36019
35912
|
function deriveAwaitingBackground(row, tele, runtime, hasActionableInteraction = false, nowMs = Date.now(), limitPause = void 0, deliveryProcessGone = false, github = {}, registeredPrWatches = /* @__PURE__ */ new Set(), armedTimerIds = /* @__PURE__ */ new Set()) {
|
|
36020
|
-
if (runtime !== "turn-idle" || !hasDeclaredWait(tele, nowMs, armedTimerIds)) return false;
|
|
35913
|
+
if (runtime !== "turn-idle" || !hasDeclaredWait(tele, nowMs, armedTimerIds, registeredPrWatches)) return false;
|
|
36021
35914
|
if (hasActionableInteraction || tele?.pendingAsk || tele?.pendingQuestion) return false;
|
|
36022
35915
|
if (tele?.lastFence?.kind === "done") return false;
|
|
36023
|
-
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
|
|
35916
|
+
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
|
|
36024
35917
|
// suppressing this card left the wait stated nowhere but the fence's own machinery footer.
|
|
36025
35918
|
!hasParkedTimerWatch(tele, armedTimerIds)) return false;
|
|
36026
35919
|
return deriveNeedsYou({ ...row, bg_snooze_rested_at: null }, tele, runtime, hasActionableInteraction, nowMs, limitPause, false, deliveryProcessGone, {}, /* @__PURE__ */ new Set(), armedTimerIds);
|
|
@@ -36497,7 +36390,7 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
|
|
|
36497
36390
|
if (parcelSub || stopped) return Promise.resolve();
|
|
36498
36391
|
if (watchSetup) return watchSetup;
|
|
36499
36392
|
const setup = (async () => {
|
|
36500
|
-
const next = await subscribe(
|
|
36393
|
+
const next = await subscribe(join28(project.dir, ".frizz"), () => scheduleRebuild());
|
|
36501
36394
|
if (stopped) {
|
|
36502
36395
|
await next.unsubscribe();
|
|
36503
36396
|
return;
|
|
@@ -37724,10 +37617,17 @@ ${wakeTimeHeader(now(), spokeAt)}`;
|
|
|
37724
37617
|
for (const sh of tele.retiredShells ?? []) {
|
|
37725
37618
|
for (const h of [sh.taskId, sh.id, sh.label]) if (h) finishedHandles.add(h);
|
|
37726
37619
|
}
|
|
37727
|
-
const
|
|
37620
|
+
for (const a of tele.subAgents ?? []) {
|
|
37621
|
+
if (a.state !== "rested") continue;
|
|
37622
|
+
for (const h of [a.id, a.label]) if (h) finishedHandles.add(h);
|
|
37623
|
+
}
|
|
37624
|
+
const firedTimers = new Set(
|
|
37625
|
+
deps.storage.listThreadTimers(row.slug).filter((t) => t.state === "fired").map((t) => t.id)
|
|
37626
|
+
);
|
|
37627
|
+
const finishedItem = (i) => finishedHandles.has(i.value) || i.kind === "timer" && firedTimers.has(i.value);
|
|
37728
37628
|
const status = park.items.map((i) => {
|
|
37729
37629
|
const gone = dead.some((d) => d.kind === i.kind && d.value === i.value);
|
|
37730
|
-
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)";
|
|
37630
|
+
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)";
|
|
37731
37631
|
return `- \`${AWAITING_KEY_OF[i.kind]}: [${i.value}]\` \u2014 ${note}`;
|
|
37732
37632
|
});
|
|
37733
37633
|
const allFinished = dead.length > 0 && dead.every(finishedItem);
|
|
@@ -38523,7 +38423,7 @@ var init_resume = __esm({
|
|
|
38523
38423
|
});
|
|
38524
38424
|
|
|
38525
38425
|
// packages/server/src/backend/claude.ts
|
|
38526
|
-
import { join as
|
|
38426
|
+
import { join as join29 } from "node:path";
|
|
38527
38427
|
function toolResultText2(content) {
|
|
38528
38428
|
if (typeof content === "string") return content;
|
|
38529
38429
|
if (!Array.isArray(content)) return "";
|
|
@@ -38612,7 +38512,7 @@ function createClaudeBackend(opts) {
|
|
|
38612
38512
|
return { argv, env: claudeWorkerEnvironment(), prewrite: [] };
|
|
38613
38513
|
},
|
|
38614
38514
|
transcriptPath(sessionId) {
|
|
38615
|
-
return
|
|
38515
|
+
return join29(opts.logDir, `${sessionId}.jsonl`);
|
|
38616
38516
|
},
|
|
38617
38517
|
parseLine(line) {
|
|
38618
38518
|
return parseClaudeLine(line);
|
|
@@ -38782,12 +38682,12 @@ var init_login_utility = __esm({
|
|
|
38782
38682
|
|
|
38783
38683
|
// packages/server/src/backend/codex-app-server-diagnostics.ts
|
|
38784
38684
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, renameSync as renameSync8, statSync as statSync13 } from "node:fs";
|
|
38785
|
-
import { join as
|
|
38685
|
+
import { join as join30 } from "node:path";
|
|
38786
38686
|
function codexDiagnosticLogPath(stateDir, projectId) {
|
|
38787
|
-
return
|
|
38687
|
+
return join30(stateDir, "codex-app-server", `${projectId}.diagnostics.log`);
|
|
38788
38688
|
}
|
|
38789
38689
|
function createCodexDiagnosticSink(stateDir, projectId, now = () => /* @__PURE__ */ new Date()) {
|
|
38790
|
-
const dir =
|
|
38690
|
+
const dir = join30(stateDir, "codex-app-server");
|
|
38791
38691
|
const path = codexDiagnosticLogPath(stateDir, projectId);
|
|
38792
38692
|
let ensured = false;
|
|
38793
38693
|
return (event) => {
|
|
@@ -38819,7 +38719,7 @@ var init_codex_app_server_diagnostics = __esm({
|
|
|
38819
38719
|
import { execFile as execFile7 } from "node:child_process";
|
|
38820
38720
|
import { readdirSync as readdirSync8, unlinkSync as unlinkSync4 } from "node:fs";
|
|
38821
38721
|
import { connect as connectSocket } from "node:net";
|
|
38822
|
-
import { join as
|
|
38722
|
+
import { join as join31 } from "node:path";
|
|
38823
38723
|
function parseLsofSocketNames(stdout) {
|
|
38824
38724
|
const referenced = /* @__PURE__ */ new Set();
|
|
38825
38725
|
for (const line of stdout.split("\n")) {
|
|
@@ -38836,7 +38736,7 @@ function sweepStaleSockets(options, deps = {}) {
|
|
|
38836
38736
|
const keep = new Set(options.keep ?? []);
|
|
38837
38737
|
let candidates;
|
|
38838
38738
|
try {
|
|
38839
|
-
candidates = (deps.readdir ?? readdirSync8)(options.dir).filter((name) => name.startsWith(options.prefix) && name.endsWith(".sock")).map((name) =>
|
|
38739
|
+
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));
|
|
38840
38740
|
} catch {
|
|
38841
38741
|
return;
|
|
38842
38742
|
}
|
|
@@ -39300,7 +39200,7 @@ var init_thread_hibernation = __esm({
|
|
|
39300
39200
|
});
|
|
39301
39201
|
|
|
39302
39202
|
// packages/server/src/context.ts
|
|
39303
|
-
import { join as
|
|
39203
|
+
import { join as join32 } from "node:path";
|
|
39304
39204
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
39305
39205
|
import { homedir as homedir13 } from "node:os";
|
|
39306
39206
|
function reconcileSessions(storage) {
|
|
@@ -39458,7 +39358,7 @@ async function createContext(opts = {}) {
|
|
|
39458
39358
|
function createContextUnchecked(opts, resources) {
|
|
39459
39359
|
const home = opts.home ?? homedir13();
|
|
39460
39360
|
const project = opts.project ?? resolveProject();
|
|
39461
|
-
const dbPath =
|
|
39361
|
+
const dbPath = join32(project.stateDir, "ui.db");
|
|
39462
39362
|
const storage = createStorage(dbPath);
|
|
39463
39363
|
resources.storage = storage;
|
|
39464
39364
|
const bus = new Bus();
|
|
@@ -39603,12 +39503,16 @@ function createContextUnchecked(opts, resources) {
|
|
|
39603
39503
|
projectId: project.id,
|
|
39604
39504
|
// The frizz worker environment — the SDK equivalent of the CLI argv's --plugin-dir / --mcp-config.
|
|
39605
39505
|
// Computed ONCE here (constant per project) and applied on every broker fork so a broker worker
|
|
39606
|
-
// gets the frizz sub-agent profiles, the frizz
|
|
39506
|
+
// gets the frizz sub-agent profiles, the frizz MCP server (the only one frizz mounts), and the
|
|
39507
|
+
// cc-worker hooks. A project's own `.mcp.json` servers load on top of these, not through them.
|
|
39607
39508
|
workerEnv: {
|
|
39608
39509
|
pluginDir: workerPluginDir(),
|
|
39609
39510
|
...claudeMcpConfig(resolveFrizzMcp(frizzMcpTarget)),
|
|
39610
39511
|
permDir: permRequestDir(project)
|
|
39611
39512
|
},
|
|
39513
|
+
// Read at every fork, not captured once: the compaction window is a Settings value the drawer
|
|
39514
|
+
// can change while the server runs, and the next dispatch or cold resume should carry it.
|
|
39515
|
+
getSettings: () => getSettings(storage, home),
|
|
39612
39516
|
// Which broker threads warmUp() may reattach at boot. Same predicate codex's shouldAutoResume
|
|
39613
39517
|
// applies: never wake a thread the human has already put away — a boot reattach is only for a
|
|
39614
39518
|
// thread that is still open and still theirs to come back to. Broker-backed rows only; a
|
|
@@ -42499,7 +42403,7 @@ var init_server = __esm({
|
|
|
42499
42403
|
});
|
|
42500
42404
|
|
|
42501
42405
|
// packages/server/src/repair.ts
|
|
42502
|
-
import { existsSync as
|
|
42406
|
+
import { existsSync as existsSync11, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "node:fs";
|
|
42503
42407
|
import { basename as basename7, dirname as dirname13, resolve as resolve6 } from "node:path";
|
|
42504
42408
|
function quoteValue(v) {
|
|
42505
42409
|
if (/^[\w./#:+-]+$/.test(v)) return v;
|
|
@@ -42514,7 +42418,7 @@ function repairThreadFile(frizzDir, file) {
|
|
|
42514
42418
|
const abs = resolve6(root, file);
|
|
42515
42419
|
if (dirname13(abs) !== root) throw new RepairError(`refusing to repair "${file}": not directly under .frizz/`);
|
|
42516
42420
|
if (!abs.endsWith(".md")) throw new RepairError(`refusing to repair "${file}": not a .md thread file`);
|
|
42517
|
-
if (!
|
|
42421
|
+
if (!existsSync11(abs)) throw new RepairError(`no thread file to repair: ${basename7(abs)}`);
|
|
42518
42422
|
const content = readFileSync19(abs, "utf8");
|
|
42519
42423
|
const firstLine2 = content.replace(/^/, "").split(/\r?\n/).find((l) => l.trim() !== "");
|
|
42520
42424
|
if (firstLine2?.trim() === "---") {
|
|
@@ -42706,10 +42610,10 @@ var init_bash_background = __esm({
|
|
|
42706
42610
|
});
|
|
42707
42611
|
|
|
42708
42612
|
// packages/server/src/transcript.ts
|
|
42709
|
-
import { closeSync as closeSync13, existsSync as
|
|
42613
|
+
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";
|
|
42710
42614
|
import { createHash as createHash13 } from "node:crypto";
|
|
42711
42615
|
import { StringDecoder as StringDecoder4 } from "node:string_decoder";
|
|
42712
|
-
import { join as
|
|
42616
|
+
import { join as join33 } from "node:path";
|
|
42713
42617
|
import { homedir as homedir14 } from "node:os";
|
|
42714
42618
|
function formatTokens(n) {
|
|
42715
42619
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
@@ -43259,14 +43163,14 @@ function pruneScreenshotCache() {
|
|
|
43259
43163
|
if (entries.length <= SCREENSHOT_CACHE_MAX) return;
|
|
43260
43164
|
const byMtime = entries.map((n) => {
|
|
43261
43165
|
try {
|
|
43262
|
-
return { n, m: statSync14(
|
|
43166
|
+
return { n, m: statSync14(join33(SCREENSHOT_CACHE_DIR, n)).mtimeMs };
|
|
43263
43167
|
} catch {
|
|
43264
43168
|
return { n, m: 0 };
|
|
43265
43169
|
}
|
|
43266
43170
|
}).sort((a, b) => b.m - a.m);
|
|
43267
43171
|
for (const { n } of byMtime.slice(SCREENSHOT_CACHE_MAX)) {
|
|
43268
43172
|
try {
|
|
43269
|
-
unlinkSync5(
|
|
43173
|
+
unlinkSync5(join33(SCREENSHOT_CACHE_DIR, n));
|
|
43270
43174
|
} catch {
|
|
43271
43175
|
}
|
|
43272
43176
|
}
|
|
@@ -43296,14 +43200,14 @@ function persistBase64Image(mediaType, data, idKey) {
|
|
|
43296
43200
|
const ext = IMAGE_MEDIA_EXT[typeof mediaType === "string" ? mediaType.toLowerCase() : ""];
|
|
43297
43201
|
if (!ext || !data) return void 0;
|
|
43298
43202
|
const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
|
|
43299
|
-
const path =
|
|
43203
|
+
const path = join33(SCREENSHOT_CACHE_DIR, `${name}.${ext}`);
|
|
43300
43204
|
try {
|
|
43301
|
-
if (
|
|
43205
|
+
if (existsSync12(path)) return path;
|
|
43302
43206
|
if (data.length > SCREENSHOT_MAX_BASE64) return void 0;
|
|
43303
43207
|
const buf = Buffer.from(data, "base64");
|
|
43304
43208
|
if (buf.length === 0 || !looksLikeImage(buf, ext)) return void 0;
|
|
43305
43209
|
mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
|
|
43306
|
-
const tmp =
|
|
43210
|
+
const tmp = join33(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
|
|
43307
43211
|
writeFileSync12(tmp, buf);
|
|
43308
43212
|
renameSync9(tmp, path);
|
|
43309
43213
|
pruneScreenshotCache();
|
|
@@ -43318,14 +43222,14 @@ function persistSentFile(srcPath, idKey) {
|
|
|
43318
43222
|
const outExt = ext === "jpeg" ? "jpg" : ext;
|
|
43319
43223
|
try {
|
|
43320
43224
|
const name = createHash13("sha256").update(idKey).digest("hex").slice(0, 32);
|
|
43321
|
-
const dest =
|
|
43322
|
-
if (
|
|
43225
|
+
const dest = join33(SCREENSHOT_CACHE_DIR, `${name}.${outExt}`);
|
|
43226
|
+
if (existsSync12(dest)) return dest;
|
|
43323
43227
|
const size = statSync14(srcPath).size;
|
|
43324
43228
|
if (size === 0 || size > SENT_IMAGE_MAX_BYTES) return void 0;
|
|
43325
43229
|
const buf = readFileSync21(srcPath);
|
|
43326
43230
|
if (!looksLikeImage(buf, outExt)) return void 0;
|
|
43327
43231
|
mkdirSync15(SCREENSHOT_CACHE_DIR, { recursive: true });
|
|
43328
|
-
const tmp =
|
|
43232
|
+
const tmp = join33(SCREENSHOT_CACHE_DIR, `.${name}.${process.pid}.${screenshotTmpSeq++}.tmp`);
|
|
43329
43233
|
writeFileSync12(tmp, buf);
|
|
43330
43234
|
renameSync9(tmp, dest);
|
|
43331
43235
|
pruneScreenshotCache();
|
|
@@ -43762,16 +43666,16 @@ function verifyIncrementalParse(path, identityPrefix, incremental) {
|
|
|
43762
43666
|
}
|
|
43763
43667
|
}
|
|
43764
43668
|
function logDirOf(project) {
|
|
43765
|
-
return
|
|
43669
|
+
return join33(homedir14(), ".claude", "projects", project.cwdSlug);
|
|
43766
43670
|
}
|
|
43767
43671
|
function resolveTranscriptPath(project, sessionId) {
|
|
43768
|
-
const path =
|
|
43672
|
+
const path = join33(logDirOf(project), `${sessionId}.jsonl`);
|
|
43769
43673
|
try {
|
|
43770
43674
|
if (statSync14(path).size > 0) return path;
|
|
43771
43675
|
} catch {
|
|
43772
43676
|
}
|
|
43773
43677
|
const stranded = discoverTranscriptDir(logDirOf(project), sessionId);
|
|
43774
|
-
return stranded ?
|
|
43678
|
+
return stranded ? join33(stranded, `${sessionId}.jsonl`) : path;
|
|
43775
43679
|
}
|
|
43776
43680
|
function projectCodexTranscript(raw2, identityPrefix = "codex") {
|
|
43777
43681
|
const out = [];
|
|
@@ -44715,7 +44619,7 @@ function sourceForThread(project, storage, slug, backendFor) {
|
|
|
44715
44619
|
}
|
|
44716
44620
|
if (!FOREIGN_SESSION_ID_RE.test(slug)) return void 0;
|
|
44717
44621
|
const claudePath = resolveTranscriptPath(project, slug);
|
|
44718
|
-
if (
|
|
44622
|
+
if (existsSync12(claudePath)) {
|
|
44719
44623
|
return { slug, sessionId: slug, nativeId: slug, backend: "claude", runtimeGeneration: 0, path: claudePath };
|
|
44720
44624
|
}
|
|
44721
44625
|
const rollout = (backendFor?.("codex") ?? defaultCodexBackend()).transcriptPath(slug);
|
|
@@ -45171,7 +45075,7 @@ var init_open_external = __esm({
|
|
|
45171
45075
|
import { spawn as spawn6 } from "node:child_process";
|
|
45172
45076
|
import { readFileSync as readFileSync22, realpathSync as realpathSync8, statSync as statSync15 } from "node:fs";
|
|
45173
45077
|
import { homedir as homedir15 } from "node:os";
|
|
45174
|
-
import { isAbsolute as isAbsolute3, join as
|
|
45078
|
+
import { isAbsolute as isAbsolute3, join as join34, resolve as resolve7, sep } from "node:path";
|
|
45175
45079
|
function isUnder(real, root) {
|
|
45176
45080
|
let rootReal;
|
|
45177
45081
|
try {
|
|
@@ -45201,7 +45105,7 @@ function resolveLocalFile(rawPath, roots) {
|
|
|
45201
45105
|
function resolveOpenableFile(raw2, projectDir, roots, home = homedir15()) {
|
|
45202
45106
|
const trimmed = raw2.trim().replace(/:\d+(?::\d+)?$/, "");
|
|
45203
45107
|
if (!trimmed) return null;
|
|
45204
|
-
const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ?
|
|
45108
|
+
const abs = trimmed === "~" ? home : trimmed.startsWith("~/") ? join34(home, trimmed.slice(2)) : isAbsolute3(trimmed) ? trimmed : resolve7(projectDir, trimmed);
|
|
45205
45109
|
try {
|
|
45206
45110
|
return resolveLocalFile(abs, roots);
|
|
45207
45111
|
} catch {
|
|
@@ -45912,10 +45816,10 @@ var init_directory_picker = __esm({
|
|
|
45912
45816
|
|
|
45913
45817
|
// packages/server/src/router.ts
|
|
45914
45818
|
import { readFileSync as readFileSync23, statSync as statSync17 } from "node:fs";
|
|
45915
|
-
import { join as
|
|
45819
|
+
import { join as join35, resolve as resolve8 } from "node:path";
|
|
45916
45820
|
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
45917
45821
|
import { basename as basename9, dirname as dirname14 } from "node:path";
|
|
45918
|
-
import { existsSync as
|
|
45822
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync16, rmSync as rmSync10, writeFileSync as writeFileSync13 } from "node:fs";
|
|
45919
45823
|
import { homedir as homedir16 } from "node:os";
|
|
45920
45824
|
function validateGithubDispatchProfile(input) {
|
|
45921
45825
|
validateThreadProfile(input.backend, input.model, input.effort);
|
|
@@ -46075,7 +45979,7 @@ function projectCard(entry, stale) {
|
|
|
46075
45979
|
function addProjectAtPath(input) {
|
|
46076
45980
|
const typed = input.trim();
|
|
46077
45981
|
if (!typed) throw new Error("Enter a folder path.");
|
|
46078
|
-
const expanded = typed === "~" || typed.startsWith("~/") ?
|
|
45982
|
+
const expanded = typed === "~" || typed.startsWith("~/") ? join35(homedir16(), typed.slice(1)) : typed;
|
|
46079
45983
|
const absolute = resolve8(expanded);
|
|
46080
45984
|
let stats;
|
|
46081
45985
|
try {
|
|
@@ -46128,7 +46032,7 @@ function setProjectIconFromFile(id, file) {
|
|
|
46128
46032
|
return storeProjectIcon(id, file, bytes);
|
|
46129
46033
|
}
|
|
46130
46034
|
function createRouter(ctx) {
|
|
46131
|
-
const frizzDir =
|
|
46035
|
+
const frizzDir = join35(ctx.project.dir, ".frizz");
|
|
46132
46036
|
const openRoots = openableFileRoots(ctx.project);
|
|
46133
46037
|
function isAutoTitledSession(slug) {
|
|
46134
46038
|
return ctx.storage.getSession(slug)?.title_auto === 1;
|
|
@@ -47739,7 +47643,7 @@ ${gapNote}` : input.message;
|
|
|
47739
47643
|
}
|
|
47740
47644
|
const updated = clearProjectIcon(input.id);
|
|
47741
47645
|
if (!updated) throw new Error("No such project.");
|
|
47742
|
-
return projectCard(updated, !
|
|
47646
|
+
return projectCard(updated, !existsSync13(updated.path));
|
|
47743
47647
|
}
|
|
47744
47648
|
}),
|
|
47745
47649
|
/**
|
|
@@ -47789,7 +47693,7 @@ ${gapNote}` : input.message;
|
|
|
47789
47693
|
for (const entry of listProjects()) {
|
|
47790
47694
|
if (entry.stale) continue;
|
|
47791
47695
|
try {
|
|
47792
|
-
const db = new sqlite_default(
|
|
47696
|
+
const db = new sqlite_default(join35(projectStateDir(entry.id), "ui.db"), { readonly: true });
|
|
47793
47697
|
try {
|
|
47794
47698
|
const hit = db.prepare("SELECT 1 FROM session WHERE slug = ? LIMIT 1").get(input.slug);
|
|
47795
47699
|
if (hit) found.push({ projectSlug: entry.slug, projectName: entry.name ?? entry.slug });
|
|
@@ -48181,7 +48085,7 @@ var init_local_image = __esm({
|
|
|
48181
48085
|
// packages/server/src/project-icon.ts
|
|
48182
48086
|
import { readFileSync as readFileSync25, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync19 } from "node:fs";
|
|
48183
48087
|
import { homedir as homedir17 } from "node:os";
|
|
48184
|
-
import { basename as basename10, dirname as dirname15, extname as extname3, join as
|
|
48088
|
+
import { basename as basename10, dirname as dirname15, extname as extname3, join as join36, relative, sep as sep2 } from "node:path";
|
|
48185
48089
|
function normalizeStem(stem) {
|
|
48186
48090
|
const sized = stem.toLowerCase().replace(/[@-]\d+(\.\d+)?x(\d+)?$/u, "").replace(/[-_]\d{2,4}$/u, "");
|
|
48187
48091
|
const normalized = sized.replace(VARIANT_SUFFIX, "");
|
|
@@ -48199,7 +48103,7 @@ function sizeScore(dimensions) {
|
|
|
48199
48103
|
return Math.max(0, Math.min(40, Math.round(Math.log2(edge / 24) * 8)));
|
|
48200
48104
|
}
|
|
48201
48105
|
function under(root, relativeDirectory) {
|
|
48202
|
-
return relativeDirectory ?
|
|
48106
|
+
return relativeDirectory ? join36(root, relativeDirectory) : root;
|
|
48203
48107
|
}
|
|
48204
48108
|
function listFiles(directory) {
|
|
48205
48109
|
try {
|
|
@@ -48221,19 +48125,19 @@ function iconDirectories(root) {
|
|
|
48221
48125
|
for (const asset of ASSET_DIRECTORIES) directories.push(under(root, asset));
|
|
48222
48126
|
for (const host of HOST_DIRECTORIES) {
|
|
48223
48127
|
if (!children2.has(host)) continue;
|
|
48224
|
-
for (const asset of ASSET_DIRECTORIES) directories.push(under(
|
|
48128
|
+
for (const asset of ASSET_DIRECTORIES) directories.push(under(join36(root, host), asset));
|
|
48225
48129
|
}
|
|
48226
48130
|
for (const extra of EXTRA_DIRECTORIES) directories.push(under(root, extra));
|
|
48227
48131
|
for (const container of WORKSPACE_CONTAINERS) {
|
|
48228
48132
|
if (!children2.has(container)) continue;
|
|
48229
48133
|
let members;
|
|
48230
48134
|
try {
|
|
48231
|
-
members = readdirSync10(
|
|
48135
|
+
members = readdirSync10(join36(root, container), { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
48232
48136
|
} catch {
|
|
48233
48137
|
continue;
|
|
48234
48138
|
}
|
|
48235
48139
|
for (const member of members) {
|
|
48236
|
-
for (const asset of ASSET_DIRECTORIES) directories.push(under(
|
|
48140
|
+
for (const asset of ASSET_DIRECTORIES) directories.push(under(join36(root, container, member), asset));
|
|
48237
48141
|
}
|
|
48238
48142
|
}
|
|
48239
48143
|
return [...new Set(directories)];
|
|
@@ -48247,7 +48151,7 @@ function manifestIcons(manifestPath) {
|
|
|
48247
48151
|
}
|
|
48248
48152
|
if (!Array.isArray(parsed?.icons)) return [];
|
|
48249
48153
|
const directory = dirname15(manifestPath);
|
|
48250
|
-
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) =>
|
|
48154
|
+
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, "")));
|
|
48251
48155
|
}
|
|
48252
48156
|
function projectIconCandidates(root) {
|
|
48253
48157
|
const projectStem = normalizeStem(basename10(root)).normalized;
|
|
@@ -48266,7 +48170,7 @@ function projectIconCandidates(root) {
|
|
|
48266
48170
|
for (const name of listFiles(directory)) {
|
|
48267
48171
|
const extension2 = extname3(name).toLowerCase();
|
|
48268
48172
|
if (MANIFEST_NAMES.includes(name.toLowerCase())) {
|
|
48269
|
-
for (const icon of manifestIcons(
|
|
48173
|
+
for (const icon of manifestIcons(join36(directory, name))) {
|
|
48270
48174
|
if (MEASURABLE_IMAGE_EXTENSIONS.has(extname3(icon).toLowerCase())) {
|
|
48271
48175
|
fromManifest.add(icon);
|
|
48272
48176
|
files.push({ path: icon, directory: dirname15(icon) });
|
|
@@ -48277,7 +48181,7 @@ function projectIconCandidates(root) {
|
|
|
48277
48181
|
if (!MEASURABLE_IMAGE_EXTENSIONS.has(extension2)) continue;
|
|
48278
48182
|
const stem = name.slice(0, name.length - extension2.length);
|
|
48279
48183
|
if (normalizeStem(stem).normalized === "favicon") iconHomes.add(directory);
|
|
48280
|
-
files.push({ path:
|
|
48184
|
+
files.push({ path: join36(directory, name), directory });
|
|
48281
48185
|
if (files.length >= MAX_CANDIDATES) break;
|
|
48282
48186
|
}
|
|
48283
48187
|
}
|
|
@@ -48455,7 +48359,7 @@ var init_project_icon = __esm({
|
|
|
48455
48359
|
|
|
48456
48360
|
// packages/server/src/local-visualization.ts
|
|
48457
48361
|
import { readdirSync as readdirSync11, readFileSync as readFileSync26, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
|
|
48458
|
-
import { join as
|
|
48362
|
+
import { join as join37, sep as sep3 } from "node:path";
|
|
48459
48363
|
function children(path, pattern) {
|
|
48460
48364
|
try {
|
|
48461
48365
|
return readdirSync11(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && pattern.test(entry.name)).map((entry) => entry.name).sort().reverse();
|
|
@@ -48467,7 +48371,7 @@ function isUnder2(path, root) {
|
|
|
48467
48371
|
return path === root || path.startsWith(root.endsWith(sep3) ? root : root + sep3);
|
|
48468
48372
|
}
|
|
48469
48373
|
function resolveFragment(projectDir, sessionId, file) {
|
|
48470
|
-
const base =
|
|
48374
|
+
const base = join37(projectDir, ".codex", "visualizations");
|
|
48471
48375
|
let projectReal;
|
|
48472
48376
|
let baseReal;
|
|
48473
48377
|
try {
|
|
@@ -48478,12 +48382,12 @@ function resolveFragment(projectDir, sessionId, file) {
|
|
|
48478
48382
|
}
|
|
48479
48383
|
if (!isUnder2(baseReal, projectReal)) return null;
|
|
48480
48384
|
for (const year of children(base, YEAR_PART)) {
|
|
48481
|
-
const yearDir =
|
|
48385
|
+
const yearDir = join37(base, year);
|
|
48482
48386
|
for (const month of children(yearDir, DATE_PART)) {
|
|
48483
|
-
const monthDir =
|
|
48387
|
+
const monthDir = join37(yearDir, month);
|
|
48484
48388
|
for (const day of children(monthDir, DATE_PART)) {
|
|
48485
|
-
const sessionRoot =
|
|
48486
|
-
const candidate =
|
|
48389
|
+
const sessionRoot = join37(baseReal, year, month, day, sessionId);
|
|
48390
|
+
const candidate = join37(monthDir, day, sessionId, file);
|
|
48487
48391
|
try {
|
|
48488
48392
|
const real = realpathSync11(candidate);
|
|
48489
48393
|
if (isUnder2(real, sessionRoot) && statSync20(real).isFile()) return real;
|
|
@@ -48612,7 +48516,7 @@ var init_local_visualization = __esm({
|
|
|
48612
48516
|
|
|
48613
48517
|
// packages/server/src/app.ts
|
|
48614
48518
|
import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync14 } from "node:fs";
|
|
48615
|
-
import { join as
|
|
48519
|
+
import { join as join38 } from "node:path";
|
|
48616
48520
|
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
48617
48521
|
function createApp(ctx, options = {}) {
|
|
48618
48522
|
const app = new Hono2();
|
|
@@ -48716,10 +48620,10 @@ function createApp(ctx, options = {}) {
|
|
|
48716
48620
|
if (typeof body.data !== "string" || body.data.length > ATTACHMENT_MAX_BASE64_CHARS) return c.json({ error: "bad payload" }, 400);
|
|
48717
48621
|
const ext = `.${attachmentExtension(name)}`;
|
|
48718
48622
|
const buf = Buffer.from(body.data, "base64");
|
|
48719
|
-
const dir =
|
|
48623
|
+
const dir = join38(ctx.project.stateDir, "attachments");
|
|
48720
48624
|
mkdirSync17(dir, { recursive: true });
|
|
48721
48625
|
const base = name.replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9_-]+/g, "-").slice(0, 40) || "file";
|
|
48722
|
-
const path =
|
|
48626
|
+
const path = join38(dir, `${Date.now()}-${randomUUID17().slice(0, 8)}-${base}${ext}`);
|
|
48723
48627
|
writeFileSync14(path, buf);
|
|
48724
48628
|
return c.json({ path });
|
|
48725
48629
|
});
|
|
@@ -49959,9 +49863,9 @@ var init_app_socket = __esm({
|
|
|
49959
49863
|
|
|
49960
49864
|
// packages/server/src/boot-progress.ts
|
|
49961
49865
|
import { mkdirSync as mkdirSync18, readFileSync as readFileSync27, renameSync as renameSync10, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
|
|
49962
|
-
import { basename as basename11, dirname as dirname16, join as
|
|
49866
|
+
import { basename as basename11, dirname as dirname16, join as join39 } from "node:path";
|
|
49963
49867
|
function bootProgressPath(stateDir) {
|
|
49964
|
-
return
|
|
49868
|
+
return join39(stateDir, BOOT_PROGRESS_NAME);
|
|
49965
49869
|
}
|
|
49966
49870
|
function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
|
|
49967
49871
|
if (!stateDir) {
|
|
@@ -49982,7 +49886,7 @@ function createBootProgressPublisher(stateDir, minIntervalMs = 200) {
|
|
|
49982
49886
|
step++;
|
|
49983
49887
|
try {
|
|
49984
49888
|
mkdirSync18(dirname16(path), { recursive: true, mode: 448 });
|
|
49985
|
-
const temp =
|
|
49889
|
+
const temp = join39(dirname16(path), `.${basename11(path)}.${process.pid}.tmp`);
|
|
49986
49890
|
writeFileSync15(temp, `${JSON.stringify({ pid: process.pid, step, phase, at: new Date(now).toISOString() })}
|
|
49987
49891
|
`, "utf8");
|
|
49988
49892
|
renameSync10(temp, path);
|
|
@@ -50099,8 +50003,8 @@ __export(index_exports, {
|
|
|
50099
50003
|
unknownProjectPage: () => unknownProjectPage
|
|
50100
50004
|
});
|
|
50101
50005
|
import { createServer } from "node:http";
|
|
50102
|
-
import { readFileSync as readFileSync28, existsSync as
|
|
50103
|
-
import { dirname as dirname17, join as
|
|
50006
|
+
import { readFileSync as readFileSync28, existsSync as existsSync14 } from "node:fs";
|
|
50007
|
+
import { dirname as dirname17, join as join40, resolve as resolve9, extname as extname4, normalize } from "node:path";
|
|
50104
50008
|
function readServerAddressHolder(path) {
|
|
50105
50009
|
try {
|
|
50106
50010
|
const value = JSON.parse(readFileSync28(path, "utf8"));
|
|
@@ -50203,9 +50107,9 @@ function createShutdownSignalHandler(options) {
|
|
|
50203
50107
|
}
|
|
50204
50108
|
function serveStatic(distDir, req, res) {
|
|
50205
50109
|
const rel = normalize((req.url ?? "/").split("?")[0]).replace(/^(\.\.[/\\])+/, "");
|
|
50206
|
-
let file =
|
|
50207
|
-
if (!file.startsWith(distDir)) file =
|
|
50208
|
-
if (!
|
|
50110
|
+
let file = join40(distDir, rel === "/" ? "index.html" : rel);
|
|
50111
|
+
if (!file.startsWith(distDir)) file = join40(distDir, "index.html");
|
|
50112
|
+
if (!existsSync14(file)) file = join40(distDir, "index.html");
|
|
50209
50113
|
try {
|
|
50210
50114
|
const body = readFileSync28(file);
|
|
50211
50115
|
res.writeHead(200, { "content-type": MIME[extname4(file)] ?? "application/octet-stream" });
|
|
@@ -50556,7 +50460,7 @@ async function startServer(opts = {}) {
|
|
|
50556
50460
|
}
|
|
50557
50461
|
statusPath = serverLockPathFor(ctx.project);
|
|
50558
50462
|
const webRoot = resolve9(import.meta.dirname, "..", "..", "web");
|
|
50559
|
-
const distDir = opts.webDistDir ? resolve9(opts.webDistDir) :
|
|
50463
|
+
const distDir = opts.webDistDir ? resolve9(opts.webDistDir) : join40(webRoot, "dist");
|
|
50560
50464
|
startupPhase = "Vite";
|
|
50561
50465
|
if (opts.dev) {
|
|
50562
50466
|
try {
|
|
@@ -50632,7 +50536,7 @@ async function startServer(opts = {}) {
|
|
|
50632
50536
|
if (vite) {
|
|
50633
50537
|
vite.middlewares(req, res, () => {
|
|
50634
50538
|
try {
|
|
50635
|
-
const html = readFileSync28(
|
|
50539
|
+
const html = readFileSync28(join40(webRoot, "index.html"), "utf8");
|
|
50636
50540
|
void vite.transformIndexHtml(url, html).then((out) => {
|
|
50637
50541
|
res.writeHead(200, { "content-type": "text/html" });
|
|
50638
50542
|
res.end(out);
|
|
@@ -50644,7 +50548,7 @@ async function startServer(opts = {}) {
|
|
|
50644
50548
|
});
|
|
50645
50549
|
return;
|
|
50646
50550
|
}
|
|
50647
|
-
if (
|
|
50551
|
+
if (existsSync14(distDir)) {
|
|
50648
50552
|
serveStatic(distDir, req, res);
|
|
50649
50553
|
return;
|
|
50650
50554
|
}
|
|
@@ -50824,7 +50728,7 @@ var init_index = __esm({
|
|
|
50824
50728
|
}
|
|
50825
50729
|
};
|
|
50826
50730
|
isApiUrl = (url) => url === FRIZZ_ROUTE_PREFIX || url.startsWith(`${FRIZZ_ROUTE_PREFIX}/`);
|
|
50827
|
-
serverLockPathFor = (project) =>
|
|
50731
|
+
serverLockPathFor = (project) => join40(project.stateDir, "server.lock");
|
|
50828
50732
|
MIME = {
|
|
50829
50733
|
".html": "text/html",
|
|
50830
50734
|
".js": "text/javascript",
|
|
@@ -50842,8 +50746,8 @@ init_project();
|
|
|
50842
50746
|
init_project_launch();
|
|
50843
50747
|
init_shutdown();
|
|
50844
50748
|
init_logging();
|
|
50845
|
-
import { existsSync as
|
|
50846
|
-
import { join as
|
|
50749
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
50750
|
+
import { join as join41 } from "node:path";
|
|
50847
50751
|
process.on("uncaughtException", (error) => {
|
|
50848
50752
|
log.error("dev-child", `uncaught exception: ${error instanceof Error ? error.stack ?? error.message : error}`);
|
|
50849
50753
|
process.exit(1);
|
|
@@ -50881,9 +50785,9 @@ try {
|
|
|
50881
50785
|
["FRIZZ_SCRIPTS_DIR", process.env.FRIZZ_SCRIPTS_DIR, "index.mjs"],
|
|
50882
50786
|
["FRIZZ_WORKER_PLUGIN_DIR", process.env.FRIZZ_WORKER_PLUGIN_DIR, ".claude-plugin/plugin.json"]
|
|
50883
50787
|
];
|
|
50884
|
-
if (!
|
|
50788
|
+
if (!existsSync15(stableWebDist)) throw new Error("stable artifact launch is missing its verified web directory");
|
|
50885
50789
|
for (const [name, directory, requiredFile] of required) {
|
|
50886
|
-
if (!directory || !
|
|
50790
|
+
if (!directory || !existsSync15(join41(directory, requiredFile)))
|
|
50887
50791
|
throw new Error(`stable artifact launch is missing verified ${name}`);
|
|
50888
50792
|
}
|
|
50889
50793
|
}
|