frizz-server 0.13.4 → 0.13.6
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/dist/claude-agent-broker.js +10152 -10057
- package/dist/dev-child.js +381 -67
- package/package.json +1 -1
- package/runtime/cc-worker/bin/frizz-mcp.mjs +131 -5
- package/runtime/cc-worker/skills/gh/SKILL.md +4 -2
- package/web-dist/assets/{TerminalPane-BA0mIiPN.js → TerminalPane-P7iAI_rU.js} +1 -1
- package/web-dist/assets/{abnfDiagram-VRR7QNED-C0d_GOgf.js → abnfDiagram-VRR7QNED-B0JxaKjB.js} +1 -1
- package/web-dist/assets/architecture-TIHT7OUA-ZNmKXuTL.js +1 -0
- package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-CLDHAnqy.js → architectureDiagram-ZJ3FMSHR-E8bWaWhF.js} +1 -1
- package/web-dist/assets/{blockDiagram-677ZJIJ3-Byj82lPn.js → blockDiagram-677ZJIJ3-BNg7FcxS.js} +1 -1
- package/web-dist/assets/{c4Diagram-LMCZKHZV-BSiBmLxJ.js → c4Diagram-LMCZKHZV-BHkry-JG.js} +1 -1
- package/web-dist/assets/channel-CTEEc7oT.js +1 -0
- package/web-dist/assets/{chunk-32BRIVSS-DjJuumwL.js → chunk-32BRIVSS-BJLx0sIU.js} +1 -1
- package/web-dist/assets/{chunk-52WLFC77-7w-u-XrO.js → chunk-52WLFC77-DmMATfuh.js} +1 -1
- package/web-dist/assets/{chunk-C7G6YPKG-CrQOxr9R.js → chunk-C7G6YPKG-Ceg42lXA.js} +1 -1
- package/web-dist/assets/{chunk-EX3LRPZG-CyElaUyz.js → chunk-EX3LRPZG-CAx16zp2.js} +1 -1
- package/web-dist/assets/{chunk-FWX5IMBZ-uBs1pWz3.js → chunk-FWX5IMBZ-DWb22U8j.js} +2 -2
- package/web-dist/assets/{chunk-HOUHSVGY-D-0S_vPu.js → chunk-HOUHSVGY-bKZVUQyT.js} +1 -1
- package/web-dist/assets/{chunk-ICXQ74PX-BPZjHh6l.js → chunk-ICXQ74PX-C3i_eW5g.js} +1 -1
- package/web-dist/assets/{chunk-MOJQB5TN-DQ8X1g7u.js → chunk-MOJQB5TN-Bt6x6J8D.js} +1 -1
- package/web-dist/assets/{chunk-OGEWGWER-DwEl4tV6.js → chunk-OGEWGWER-BwJ73hmt.js} +1 -1
- package/web-dist/assets/{chunk-PUDLZKDR-w7FH7FgF.js → chunk-PUDLZKDR-DfcEHpjL.js} +1 -1
- package/web-dist/assets/{chunk-Q4XR5HBZ-DUF1qPxz.js → chunk-Q4XR5HBZ-VYeS43tP.js} +1 -1
- package/web-dist/assets/{chunk-V7JOEXUC-DQDZupnO.js → chunk-V7JOEXUC-Cs1_YKkF.js} +1 -1
- package/web-dist/assets/{chunk-VAUOI2AC-DcBMBNly.js → chunk-VAUOI2AC-CCOn0B9D.js} +1 -1
- package/web-dist/assets/{chunk-VR4S4FIN-DSR3Pq54.js → chunk-VR4S4FIN-CL_PLrUe.js} +1 -1
- package/web-dist/assets/{chunk-WYO6CB5R-DqmEM55p.js → chunk-WYO6CB5R-Dupt_rk_.js} +1 -1
- package/web-dist/assets/{chunk-ZGVPDNZ5-DiAOqAtq.js → chunk-ZGVPDNZ5-UwgE4khW.js} +1 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-D5na7mob.js +1 -0
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-D5na7mob.js +1 -0
- package/web-dist/assets/{cynefin-VYW2F7L2-BOgXXJux.js → cynefin-VYW2F7L2-DGZJYJNa.js} +1 -1
- package/web-dist/assets/{cynefinDiagram-TSTJHNR4-D_cDNjb5.js → cynefinDiagram-TSTJHNR4-B34DgCmC.js} +1 -1
- package/web-dist/assets/{dagre-VKFMJZFB-COoeIysT.js → dagre-VKFMJZFB-C7HODJG3.js} +1 -1
- package/web-dist/assets/{diagram-FQU43EPY-DvObHRy5.js → diagram-FQU43EPY-CunP-lqf.js} +1 -1
- package/web-dist/assets/{diagram-G47NLZAW-CVsApdT5.js → diagram-G47NLZAW-C3O84bOk.js} +1 -1
- package/web-dist/assets/{diagram-NH7WQ7WH-B1R3H3FB.js → diagram-NH7WQ7WH-Dv0TR4Ic.js} +1 -1
- package/web-dist/assets/{diagram-OA4YK3LP-CLoRC7it.js → diagram-OA4YK3LP-CUvcZIxp.js} +1 -1
- package/web-dist/assets/{diagram-WEI45ONY-BNtWH_b0.js → diagram-WEI45ONY-CwPr97NN.js} +1 -1
- package/web-dist/assets/{ebnfDiagram-CCIWWBDH-BIHit-oa.js → ebnfDiagram-CCIWWBDH-Dq2O1mOC.js} +1 -1
- package/web-dist/assets/{erDiagram-Q63AITRT-CX3wj9wZ.js → erDiagram-Q63AITRT-C6dJ8Dpx.js} +1 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-B8cYCwrU.js +1 -0
- package/web-dist/assets/flowDiagram-23GEKE2U-C-EWCSnf.js +1 -0
- package/web-dist/assets/{ganttDiagram-NO4QXBWP-ovukTiTD.js → ganttDiagram-NO4QXBWP-sa-TSsB5.js} +1 -1
- package/web-dist/assets/{gitGraph-TEB2WS4Q-C9E-9V1E.js → gitGraph-TEB2WS4Q-BdZIuH-o.js} +1 -1
- package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-BtJWd26X.js → gitGraphDiagram-IHSO6WYX-C__OynR9.js} +1 -1
- package/web-dist/assets/{index-rU3F6wBN.js → index-CGrszN1x.js} +96 -96
- package/web-dist/assets/index-d_g5kIKJ.css +1 -0
- package/web-dist/assets/{info-DKCQHKI2-DGGMsp6D.js → info-DKCQHKI2-BEaDQqRu.js} +1 -1
- package/web-dist/assets/{infoDiagram-FWYZ7A6U-yVcULGK0.js → infoDiagram-FWYZ7A6U-RJlw-Bzq.js} +1 -1
- package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-BXGbRvgE.js → ishikawaDiagram-FXEZZL3T-xop_QrWs.js} +1 -1
- package/web-dist/assets/{journeyDiagram-5HDEW3XC-KyPIkV6n.js → journeyDiagram-5HDEW3XC-O6nmpoje.js} +1 -1
- package/web-dist/assets/{kanban-definition-HUTT4EX6-7IRwylrK.js → kanban-definition-HUTT4EX6-IXwPs_L0.js} +1 -1
- package/web-dist/assets/{line-DHhaY6hP.js → line-BMRguCzE.js} +1 -1
- package/web-dist/assets/{mermaid-parser.core-D_zIUq4c.js → mermaid-parser.core-BCXFwFN1.js} +3 -3
- package/web-dist/assets/{mermaid.core-Cer3Sm6Y.js → mermaid.core-Cqq_9ya3.js} +3 -3
- package/web-dist/assets/{mindmap-definition-LN4V7U3C-Ds5w6J2p.js → mindmap-definition-LN4V7U3C-DEfWfnOg.js} +1 -1
- package/web-dist/assets/{packet-7NZHBO7P-BxQmF_wj.js → packet-7NZHBO7P-CQGAnngT.js} +1 -1
- package/web-dist/assets/{pegDiagram-2B236MQR-BgcChc4K.js → pegDiagram-2B236MQR-tOrSsHlA.js} +1 -1
- package/web-dist/assets/{pie-RZYD4A2V-7ckW3b8f.js → pie-RZYD4A2V-C0KP3XF8.js} +1 -1
- package/web-dist/assets/{pieDiagram-ENE6RG2P-dAKPXidr.js → pieDiagram-ENE6RG2P-FzmIWC1K.js} +1 -1
- package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-BhIIvMD3.js → quadrantDiagram-ABIIQ3AL-N6zuxKqV.js} +1 -1
- package/web-dist/assets/{radar-I7S5WNFK-BjHZZeUT.js → radar-I7S5WNFK-CB3WQRMp.js} +1 -1
- package/web-dist/assets/{railroad-3IZDKUUU-DGLm30_k.js → railroad-3IZDKUUU-Ck8x0o2o.js} +1 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-QzrMOCpt.js +1 -0
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-CwEnUu4A.js +1 -0
- package/web-dist/assets/railroad-peg-LSFZ7HO6-BoDEbNAA.js +1 -0
- package/web-dist/assets/{railroadDiagram-RFXS5EU6-NDZ1o78x.js → railroadDiagram-RFXS5EU6-6TyF6de4.js} +1 -1
- package/web-dist/assets/{requirementDiagram-TGXJPOKE-ClwNJzK_.js → requirementDiagram-TGXJPOKE-D6n7xpc4.js} +1 -1
- package/web-dist/assets/{sankeyDiagram-HTMAVEWB-DUe4YqBe.js → sankeyDiagram-HTMAVEWB-B7R6Natf.js} +1 -1
- package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-Co6LVrCi.js → sequenceDiagram-DBY2YBRQ-CFbbuWF6.js} +1 -1
- package/web-dist/assets/{stateDiagram-2N3HPSRC-SsEEX3n0.js → stateDiagram-2N3HPSRC-DwXu0kos.js} +1 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-CQctakxF.js +1 -0
- package/web-dist/assets/{swimlanes-5IMT3BWC-CoVdeWtQ.js → swimlanes-5IMT3BWC-Clie2Qk3.js} +1 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-CGOfs7yz.js +8 -0
- package/web-dist/assets/{timeline-definition-FHXFAJF6-FWjCOjFe.js → timeline-definition-FHXFAJF6-DWDsPzuQ.js} +1 -1
- package/web-dist/assets/{treeView-QDETBFTQ-C5XuetRx.js → treeView-QDETBFTQ-Ddd-Wxpb.js} +1 -1
- package/web-dist/assets/{treemap-6X3UGDF4-DSYA_yZU.js → treemap-6X3UGDF4-CuZ3jD4K.js} +1 -1
- package/web-dist/assets/{vennDiagram-L72KCM5P-CBlj6q2E.js → vennDiagram-L72KCM5P-BoE4aTMb.js} +1 -1
- package/web-dist/assets/{wardley-OPB4EBWU-xRxS5w8C.js → wardley-OPB4EBWU-CfaztQL0.js} +1 -1
- package/web-dist/assets/{wardleyDiagram-EHGQE667-BOJNbJTn.js → wardleyDiagram-EHGQE667-pxcV16qp.js} +1 -1
- package/web-dist/assets/{xychartDiagram-FW5EYKEG-BH7s_eja.js → xychartDiagram-FW5EYKEG--d5N7oDL.js} +1 -1
- package/web-dist/index.html +2 -2
- package/web-dist/assets/architecture-TIHT7OUA-Dabq5ynP.js +0 -1
- package/web-dist/assets/channel-569Fbotp.js +0 -1
- package/web-dist/assets/classDiagram-OUVF2IWQ-DdJ4fu0T.js +0 -1
- package/web-dist/assets/classDiagram-v2-EOCWNBFH-DdJ4fu0T.js +0 -1
- package/web-dist/assets/eventmodeling-45OFAUF4-Bxh_9VAJ.js +0 -1
- package/web-dist/assets/flowDiagram-23GEKE2U-BVT2CoPd.js +0 -1
- package/web-dist/assets/index-PAxGNU-o.css +0 -1
- package/web-dist/assets/railroad-abnf-AHOZXSZD-ez6c0JVR.js +0 -1
- package/web-dist/assets/railroad-ebnf-EBAXGLYW-G_O_IjaE.js +0 -1
- package/web-dist/assets/railroad-peg-LSFZ7HO6-CDAvQbft.js +0 -1
- package/web-dist/assets/stateDiagram-v2-6OUMAXLB-BCCL236E.js +0 -1
- package/web-dist/assets/swimlanesDiagram-G3AALYLV-BXTLDQC2.js +0 -8
package/dist/dev-child.js
CHANGED
|
@@ -13402,6 +13402,22 @@ function prWatchWakeMessage(input) {
|
|
|
13402
13402
|
lines.push("", PR_WATCH_ARMED_TRAILER);
|
|
13403
13403
|
return lines.join("\n");
|
|
13404
13404
|
}
|
|
13405
|
+
function issueWatchWakeMessage(input) {
|
|
13406
|
+
const lines = [];
|
|
13407
|
+
if (input.closed) {
|
|
13408
|
+
lines.push(`\u23F0 ${input.target} was CLOSED.`, "");
|
|
13409
|
+
if (input.closed.reason) lines.push(`GitHub's reason: ${input.closed.reason}.`, "");
|
|
13410
|
+
lines.push(ISSUE_WATCH_SPENT_TRAILER);
|
|
13411
|
+
return lines.join("\n");
|
|
13412
|
+
}
|
|
13413
|
+
if (input.changes?.length) lines.push(`\u{1F514} ${input.target}: ${input.changes.join("; ")}.`);
|
|
13414
|
+
if (input.review) {
|
|
13415
|
+
if (lines.length) lines.push("");
|
|
13416
|
+
lines.push(input.review);
|
|
13417
|
+
}
|
|
13418
|
+
lines.push("", ISSUE_WATCH_ARMED_TRAILER);
|
|
13419
|
+
return lines.join("\n");
|
|
13420
|
+
}
|
|
13405
13421
|
function shellDoneMessage(shell) {
|
|
13406
13422
|
const what = shell.taskId ? `\`${shell.taskId}\` \u2014 ${shell.label}` : shell.label;
|
|
13407
13423
|
const verb = shell.status === "failed" ? "FAILED" : shell.status === "killed" ? "was STOPPED" : "finished";
|
|
@@ -13420,6 +13436,7 @@ function liveOpsLines(ops) {
|
|
|
13420
13436
|
section("Sub-agents still running (they re-invoke you on their own, so parking on one is optional):", "agent", ops?.subAgents ?? []);
|
|
13421
13437
|
section("Timers you have armed:", "timer", ops?.timers ?? []);
|
|
13422
13438
|
section("Pull requests you registered:", "pr", ops?.prs ?? []);
|
|
13439
|
+
section("Issues you registered:", "issue", ops?.issues ?? []);
|
|
13423
13440
|
return lines;
|
|
13424
13441
|
}
|
|
13425
13442
|
function signoffNudgeMessage(ops) {
|
|
@@ -13472,10 +13489,12 @@ function parkFinishedWakeMessage(status, several) {
|
|
|
13472
13489
|
"Then park on whatever comes next, or end in ```done or a registered question (`mcp__frizz__ask`) if nothing is left."
|
|
13473
13490
|
].join("\n");
|
|
13474
13491
|
}
|
|
13475
|
-
function prWatchExpiredWakeMessage(ref) {
|
|
13476
|
-
|
|
13492
|
+
function prWatchExpiredWakeMessage(ref, kind = "pull") {
|
|
13493
|
+
const noun = kind === "issue" ? "issue" : "PR";
|
|
13494
|
+
const tool = kind === "issue" ? "mcp__frizz__watch_issue" : "mcp__frizz__watch_pr";
|
|
13495
|
+
return `\u23F0 Your watcher on ${ref} has expired and is no longer armed \u2014 nothing on that ${noun} will wake you now.
|
|
13477
13496
|
|
|
13478
|
-
If you still care about it, register it again with \`
|
|
13497
|
+
If you still care about it, register it again with \`${tool}\` and a fresh \`for:\`. If you do not, and it was the only thing you were waiting on, end in a proper terminal state instead of parking on it again.`;
|
|
13479
13498
|
}
|
|
13480
13499
|
function ownWatchExpiredWakeMessage(kind, target) {
|
|
13481
13500
|
const what = kind === "agent" ? "sub-agent" : "background shell";
|
|
@@ -13685,7 +13704,7 @@ function parseGithubWakeSteer(text2) {
|
|
|
13685
13704
|
function frizzRoute(path3) {
|
|
13686
13705
|
return `${FRIZZ_ROUTE_PREFIX}${path3.startsWith("/") ? path3 : `/${path3}`}`;
|
|
13687
13706
|
}
|
|
13688
|
-
var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_TITLE_MAX, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, QUESTION_FENCE_RETIRED_AT, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
|
|
13707
|
+
var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_TITLE_MAX, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, ISSUE_WATCH_SPENT_TRAILER, ISSUE_WATCH_ARMED_TRAILER, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, QUESTION_FENCE_RETIRED_AT, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, DISMISSED_ANSWER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
|
|
13689
13708
|
var init_src = __esm({
|
|
13690
13709
|
"packages/shared/src/index.ts"() {
|
|
13691
13710
|
"use strict";
|
|
@@ -13995,7 +14014,7 @@ var init_src = __esm({
|
|
|
13995
14014
|
questions: external_exports.array(AskQuestion)
|
|
13996
14015
|
});
|
|
13997
14016
|
AwaitingHint = external_exports.object({
|
|
13998
|
-
kind: external_exports.enum(["shell", "agent", "timer", "pr", "for", "title"]),
|
|
14017
|
+
kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue", "for", "title"]),
|
|
13999
14018
|
value: external_exports.string()
|
|
14000
14019
|
});
|
|
14001
14020
|
RETIRED_AWAITING_KINDS = ["watch", "pr-watch", "human", "ci", "session", "shell", "agent", "timer", "pr", "reason"];
|
|
@@ -14017,17 +14036,21 @@ var init_src = __esm({
|
|
|
14017
14036
|
"reason": "put it below the `---` as ordinary Markdown \u2014 the frontmatter is YAML now and takes no prose"
|
|
14018
14037
|
};
|
|
14019
14038
|
AWAITING_KEY_RE = /^([a-z][a-z-]*):\s*(\S.*)?$/i;
|
|
14020
|
-
AWAITING_YAML_KEYS = /* @__PURE__ */ new Set(["shells", "agents", "timers", "prs", "for", "title"]);
|
|
14039
|
+
AWAITING_YAML_KEYS = /* @__PURE__ */ new Set(["shells", "agents", "timers", "prs", "issues", "for", "title"]);
|
|
14021
14040
|
AWAITING_SEQUENCE_KEYS = {
|
|
14022
14041
|
shells: "shell",
|
|
14023
14042
|
agents: "agent",
|
|
14024
14043
|
timers: "timer",
|
|
14025
|
-
prs: "pr"
|
|
14044
|
+
prs: "pr",
|
|
14045
|
+
// `issues:` (2026-09-14) names a GitHub ISSUE registered with `mcp__frizz__watch_issue`. Its own key
|
|
14046
|
+
// rather than a second spelling under `prs:`, because the two are different registrations: an issue
|
|
14047
|
+
// has no CI and no merge, and the correction for an unregistered one names a different tool.
|
|
14048
|
+
issues: "issue"
|
|
14026
14049
|
};
|
|
14027
14050
|
AWAITING_HINT_MAX = 8;
|
|
14028
14051
|
AWAITING_HINT_VALUE_MAX = 200;
|
|
14029
14052
|
AWAITING_TITLE_MAX = 40;
|
|
14030
|
-
AWAITING_ITEM_KINDS = ["shell", "agent", "timer", "pr"];
|
|
14053
|
+
AWAITING_ITEM_KINDS = ["shell", "agent", "timer", "pr", "issue"];
|
|
14031
14054
|
AWAITING_DURATION_RE = /^(\d{1,5})(s|m|h|d)$/;
|
|
14032
14055
|
DURATION_UNIT_MS = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
14033
14056
|
AWAITING_FOR_MAX_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -14071,6 +14094,8 @@ var init_src = __esm({
|
|
|
14071
14094
|
PR_WATCH_SPENT_TRAILER = "(This watcher is spent \u2014 there is nothing further to report on a finished PR.)";
|
|
14072
14095
|
PR_WATCH_ARMED_TRAILER = "(Registered PR watcher \u2014 STILL ARMED. It reports again on the next CI change, review, comment, label, conflict or review request. Drop it with `mcp__frizz__watch_pr` when it stops mattering.)";
|
|
14073
14096
|
SHELL_DONE_TRAILER = "(Frizz sends this because it finished after you came to rest, where your runtime's own completion notification does not reach you. Read its output if you still need it.)";
|
|
14097
|
+
ISSUE_WATCH_SPENT_TRAILER = "(This watcher is spent \u2014 there is nothing further to report on a closed issue.)";
|
|
14098
|
+
ISSUE_WATCH_ARMED_TRAILER = "(Registered issue watcher \u2014 STILL ARMED. It reports again on the next comment, label or assignee change, and once more when the issue closes. Drop it with `mcp__frizz__watch_issue` when it stops mattering.)";
|
|
14074
14099
|
SIGNOFF_NUDGE_MARKER = "**This message is from frizz, not from the human.**";
|
|
14075
14100
|
SIGNOFF_NUDGE_MESSAGE = [
|
|
14076
14101
|
`${SIGNOFF_NUDGE_MARKER} Nothing about your task has changed, and no new work is being asked of you.`,
|
|
@@ -14186,6 +14211,20 @@ var init_src = __esm({
|
|
|
14186
14211
|
* must hear about. Empty when nothing is failing. */
|
|
14187
14212
|
failureSig: external_exports.string().optional()
|
|
14188
14213
|
}).strict();
|
|
14214
|
+
GithubIssueStatus = external_exports.object({
|
|
14215
|
+
/** OPEN | CLOSED, lowercased. A closed issue ends the wait outright. */
|
|
14216
|
+
state: external_exports.enum(["open", "closed"]),
|
|
14217
|
+
/** GitHub's own reason for a close — `completed`, `not_planned`, `duplicate` — lowercased, when it
|
|
14218
|
+
* gave one. Absent while open. */
|
|
14219
|
+
stateReason: external_exports.string().optional(),
|
|
14220
|
+
/** The issue's title, as of the last poll — the one fact a human needs to place a bare `owner/repo#N`
|
|
14221
|
+
* on the card. Capped, because a title is a headline and the card is not the place to read one. */
|
|
14222
|
+
title: external_exports.string().max(200).optional(),
|
|
14223
|
+
/** The conversation's size, so the card can say "14 comments" the way GitHub's list does. */
|
|
14224
|
+
comments: external_exports.number().int().nonnegative(),
|
|
14225
|
+
/** When frizz last heard from GitHub, for the same reason `GithubWatchStatus.polledAt` carries it. */
|
|
14226
|
+
polledAt: external_exports.string()
|
|
14227
|
+
}).strict();
|
|
14189
14228
|
ThreadWatchView = external_exports.object({
|
|
14190
14229
|
id: external_exports.string(),
|
|
14191
14230
|
kind: ThreadWatchKind,
|
|
@@ -14194,6 +14233,15 @@ var init_src = __esm({
|
|
|
14194
14233
|
createdAt: external_exports.string(),
|
|
14195
14234
|
/** `github` rows only, and absent until the first successful poll. */
|
|
14196
14235
|
github: GithubWatchStatus.optional(),
|
|
14236
|
+
/** `github` rows only: WHAT the row watches. A pull request and an issue share the kind — they are one
|
|
14237
|
+
* registry, one ref grammar (`owner/repo#N`) and one place on every surface that lists waits — and
|
|
14238
|
+
* differ in what the poll can read off them, which is what this field lets a card branch on.
|
|
14239
|
+
* Optional, and absent means a pull request — a row from a server that predates issues reads as the
|
|
14240
|
+
* PR it always was, and the non-github kinds never carry it. */
|
|
14241
|
+
subject: external_exports.enum(["pull", "issue"]).optional(),
|
|
14242
|
+
/** `github` rows watching an ISSUE only, and absent until the first successful poll — the twin of the
|
|
14243
|
+
* `github` half above, for the rows whose subject has no checks to report. */
|
|
14244
|
+
issue: GithubIssueStatus.optional(),
|
|
14197
14245
|
/** `timer` rows only: the armed timer's own registration, which is everything the row renders — the
|
|
14198
14246
|
* worker's prompt is the row's NAME (a `tmr_…` id names nothing to a human) and the fire instant is
|
|
14199
14247
|
* its status. Unlike `github` there is no polled half to be absent: a timer that exists is fully
|
|
@@ -14207,10 +14255,15 @@ var init_src = __esm({
|
|
|
14207
14255
|
id: external_exports.string(),
|
|
14208
14256
|
/** `owner/repo#N`, normalized — the same string the board's row and the status book are keyed by. */
|
|
14209
14257
|
target: external_exports.string(),
|
|
14258
|
+
/** A pull request or an issue. Both live in one registry (`pr_watch`) and answer to one drop verb;
|
|
14259
|
+
* `watch_pr` and `watch_issue` each list only their own. Defaulted for a row an older server sends. */
|
|
14260
|
+
kind: external_exports.enum(["pull", "issue"]).default("pull"),
|
|
14210
14261
|
state: external_exports.enum(["armed", "dropped", "settled"]),
|
|
14211
14262
|
createdAt: external_exports.string(),
|
|
14212
14263
|
/** The PR's checks/mergeability as the poller last saw it. Absent until the first successful poll. */
|
|
14213
|
-
github: GithubWatchStatus.optional()
|
|
14264
|
+
github: GithubWatchStatus.optional(),
|
|
14265
|
+
/** An ISSUE's state as the poller last saw it — `kind: "issue"` rows only, absent until polled. */
|
|
14266
|
+
issue: GithubIssueStatus.optional()
|
|
14214
14267
|
}).strict();
|
|
14215
14268
|
AddOwnPrWatchInput = external_exports.object({
|
|
14216
14269
|
slug: ThreadSlug,
|
|
@@ -14235,7 +14288,13 @@ var init_src = __esm({
|
|
|
14235
14288
|
* already running, with no recourse from inside those threads. Absent ⇒ PR_WATCH_DEFAULT_FOR, which
|
|
14236
14289
|
* still BOUNDS the poll; the point of the field is that a worker chooses, and one that cannot choose
|
|
14237
14290
|
* is better bounded than broken. */
|
|
14238
|
-
for: external_exports.string().trim().min(1).max(16).optional()
|
|
14291
|
+
for: external_exports.string().trim().min(1).max(16).optional(),
|
|
14292
|
+
/** What `target` names. `issue` (from `mcp__frizz__watch_issue`, 2026-09-14) registers a watcher on a
|
|
14293
|
+
* GitHub issue — same registry, same poll cadence, same drop verb, but a different probe (`gh issue
|
|
14294
|
+
* view`), a different GraphQL fragment and a report with no CI in it. ABSENT MEANS A PULL REQUEST,
|
|
14295
|
+
* for the same reason `for` is optional on the wire: every `watch_pr` binary dispatched before this
|
|
14296
|
+
* field existed sends nothing here and must go on meaning what it always did. */
|
|
14297
|
+
kind: external_exports.enum(["pull", "issue"]).optional()
|
|
14239
14298
|
}).strict();
|
|
14240
14299
|
AddOwnPrWatchResult = external_exports.object({
|
|
14241
14300
|
id: external_exports.string(),
|
|
@@ -15070,7 +15129,7 @@ var init_src = __esm({
|
|
|
15070
15129
|
timers: external_exports.array(ThreadTimerView)
|
|
15071
15130
|
}).strict();
|
|
15072
15131
|
ThreadActivityItem = external_exports.object({
|
|
15073
|
-
kind: external_exports.enum(["shell", "agent", "timer", "pr"]),
|
|
15132
|
+
kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue"]),
|
|
15074
15133
|
/** The string a `<kind>:` fence line must carry. For a shell that is the runtime task id the worker
|
|
15075
15134
|
* was shown; for a PR, `owner/repo#N`; for a timer, its `tmr_…` row id. */
|
|
15076
15135
|
id: external_exports.string(),
|
|
@@ -15190,7 +15249,7 @@ ${DISPATCH_TASK_BANNER}
|
|
|
15190
15249
|
`;
|
|
15191
15250
|
GITHUB_DISPATCH_UI_BOUNDARY = "<!-- frizz:github-dispatch-ui-boundary:v1 -->";
|
|
15192
15251
|
WAKE_TIME_HEADER_TAIL = /\n+⏱ \d{4}-\d{2}-\d{2} \d{2}:\d{2}(?: — you last spoke [^\n]*? ago)?\.[ \t]*$/;
|
|
15193
|
-
WAKE_TRAILERS = [PR_WATCH_ARMED_TRAILER, PR_WATCH_SPENT_TRAILER, SHELL_DONE_TRAILER];
|
|
15252
|
+
WAKE_TRAILERS = [PR_WATCH_ARMED_TRAILER, PR_WATCH_SPENT_TRAILER, ISSUE_WATCH_ARMED_TRAILER, ISSUE_WATCH_SPENT_TRAILER, SHELL_DONE_TRAILER];
|
|
15194
15253
|
HUMAN_GAP_FLOOR_MS = 20 * 6e4;
|
|
15195
15254
|
HUMAN_GAP_NOTE_TAIL = /\n+⏱ Frizz: the message above arrived [^\n]* after your last one\. It is now \d{4}-\d{2}-\d{2} \d{2}:\d{2}\.[ \t]*$/;
|
|
15196
15255
|
WAKE_DELIVERY_TOKEN_TAIL = /\n*<!-- frizz-wake:[A-Za-z0-9_-]+ -->\s*$/;
|
|
@@ -19025,6 +19084,12 @@ function ensureStorageSchema(db) {
|
|
|
19025
19084
|
} catch {
|
|
19026
19085
|
}
|
|
19027
19086
|
}
|
|
19087
|
+
for (const [table, column] of [["pr_watch", "kind TEXT NOT NULL DEFAULT 'pull'"]]) {
|
|
19088
|
+
try {
|
|
19089
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column}`);
|
|
19090
|
+
} catch {
|
|
19091
|
+
}
|
|
19092
|
+
}
|
|
19028
19093
|
}
|
|
19029
19094
|
function createStorage(source, projectId) {
|
|
19030
19095
|
const owned = typeof source === "string";
|
|
@@ -19401,8 +19466,8 @@ function createStorage(source, projectId) {
|
|
|
19401
19466
|
WHERE project_id = @project_id AND slug = ? AND park_bumps > 0
|
|
19402
19467
|
`);
|
|
19403
19468
|
const armPrWatchStmt = scope.prepare(`
|
|
19404
|
-
INSERT INTO pr_watch (project_id, id, thread_slug, owner, repo, number, state, created_at, settled_at, cursor, expires_at)
|
|
19405
|
-
VALUES (@project_id, @id, @slug, @owner, @repo, @number, 'armed', @createdAtMs, NULL, NULL, @expiresAtMs)
|
|
19469
|
+
INSERT INTO pr_watch (project_id, id, thread_slug, kind, owner, repo, number, state, created_at, settled_at, cursor, expires_at)
|
|
19470
|
+
VALUES (@project_id, @id, @slug, @kind, @owner, @repo, @number, 'armed', @createdAtMs, NULL, NULL, @expiresAtMs)
|
|
19406
19471
|
`);
|
|
19407
19472
|
const expiredPrWatchesStmt = scope.prepare(
|
|
19408
19473
|
"SELECT * FROM pr_watch WHERE project_id = @project_id AND state = 'armed' AND expires_at IS NOT NULL AND expires_at <= ? ORDER BY expires_at, id"
|
|
@@ -20021,7 +20086,7 @@ function createStorage(source, projectId) {
|
|
|
20021
20086
|
resetSignoffNudges: (slug) => void resetNudgesStmt.run(slug),
|
|
20022
20087
|
countParkBump: (slug, anchor) => void countParkBumpStmt.run(anchor, slug),
|
|
20023
20088
|
resetParkBumps: (slug) => void resetParkBumpsStmt.run(slug),
|
|
20024
|
-
armPrWatch: (watch2) => void armPrWatchStmt.run(watch2),
|
|
20089
|
+
armPrWatch: (watch2) => void armPrWatchStmt.run({ ...watch2, kind: watch2.kind ?? "pull" }),
|
|
20025
20090
|
listPrWatches: (slug, opts) => (opts?.armedOnly ? armedPrWatchesBySlugStmt : prWatchesBySlugStmt).all(slug),
|
|
20026
20091
|
// Grouped off `armedPrWatchesStmt` — the scheduler's own whole-project read, which already carries
|
|
20027
20092
|
// the identical `state = 'armed'` predicate and `created_at, id` order the per-slug statement uses.
|
|
@@ -20487,6 +20552,9 @@ var init_storage = __esm({
|
|
|
20487
20552
|
created_at INTEGER NOT NULL,
|
|
20488
20553
|
settled_at INTEGER,
|
|
20489
20554
|
cursor TEXT,
|
|
20555
|
+
-- 'pull' or 'issue' (2026-09-14). Defaulted so every row written before issues existed reads as
|
|
20556
|
+
-- the pull request it was; added to a live file by the ALTER in ensureStorageSchema.
|
|
20557
|
+
kind TEXT NOT NULL DEFAULT 'pull',
|
|
20490
20558
|
-- When this watcher stops polling by itself. REQUIRED at registration (2026-08-15): a PR nobody
|
|
20491
20559
|
-- ever touches would otherwise be polled forever, and the thread parked on it would wait forever
|
|
20492
20560
|
-- with it. Nullable in the column only so an imported older row reads; the tool refuses to arm
|
|
@@ -35223,6 +35291,13 @@ function parsePrRef(value) {
|
|
|
35223
35291
|
if (!Number.isFinite(number) || number <= 0) return void 0;
|
|
35224
35292
|
return { owner: m[1], repo: m[2].replace(/\.git$/, ""), number };
|
|
35225
35293
|
}
|
|
35294
|
+
function parseIssueRef(value) {
|
|
35295
|
+
const m = value.trim().match(ISSUE_REF_RE);
|
|
35296
|
+
if (!m) return void 0;
|
|
35297
|
+
const number = Number.parseInt(m[3], 10);
|
|
35298
|
+
if (!Number.isFinite(number) || number <= 0) return void 0;
|
|
35299
|
+
return { owner: m[1], repo: m[2].replace(/\.git$/, ""), number };
|
|
35300
|
+
}
|
|
35226
35301
|
function readAwaitingPark(hints) {
|
|
35227
35302
|
const items = [];
|
|
35228
35303
|
let forMs = null;
|
|
@@ -35237,12 +35312,18 @@ function readAwaitingPark(hints) {
|
|
|
35237
35312
|
return { items, forMs };
|
|
35238
35313
|
}
|
|
35239
35314
|
function unaccountedItems(items, live) {
|
|
35240
|
-
return items.filter((i) => !live[LIVE_SET[i.kind]]
|
|
35315
|
+
return items.filter((i) => !live[LIVE_SET[i.kind]]?.has(liveKey(i)));
|
|
35241
35316
|
}
|
|
35242
35317
|
function liveKey(i) {
|
|
35243
|
-
if (i.kind
|
|
35244
|
-
|
|
35245
|
-
|
|
35318
|
+
if (i.kind === "pr") {
|
|
35319
|
+
const ref = parsePrRef(i.value);
|
|
35320
|
+
return ref ? githubStatusKey(ref) : i.value;
|
|
35321
|
+
}
|
|
35322
|
+
if (i.kind === "issue") {
|
|
35323
|
+
const ref = parseIssueRef(i.value);
|
|
35324
|
+
return ref ? githubStatusKey(ref) : i.value;
|
|
35325
|
+
}
|
|
35326
|
+
return i.value;
|
|
35246
35327
|
}
|
|
35247
35328
|
function parkIsHonoured(park, live) {
|
|
35248
35329
|
if (park.items.length === 0) return false;
|
|
@@ -35251,7 +35332,7 @@ function parkIsHonoured(park, live) {
|
|
|
35251
35332
|
}
|
|
35252
35333
|
function parkForMaxMs(park) {
|
|
35253
35334
|
if (park.items.length === 0) return AWAITING_FOR_MAX_MS;
|
|
35254
|
-
return park.items.every((i) => i.kind === "pr") ? PR_WATCH_FOR_MAX_MS : AWAITING_FOR_MAX_MS;
|
|
35335
|
+
return park.items.every((i) => i.kind === "pr" || i.kind === "issue") ? PR_WATCH_FOR_MAX_MS : AWAITING_FOR_MAX_MS;
|
|
35255
35336
|
}
|
|
35256
35337
|
function parkExpiresAt(park, fenceAtMs) {
|
|
35257
35338
|
if (park.forMs === null || !Number.isFinite(fenceAtMs)) return null;
|
|
@@ -35269,19 +35350,31 @@ function readGithubStatusBook(raw2) {
|
|
|
35269
35350
|
function githubStatusKey(ref) {
|
|
35270
35351
|
return `${ref.owner}/${ref.repo}#${ref.number}`;
|
|
35271
35352
|
}
|
|
35272
|
-
|
|
35353
|
+
function readGithubIssueStatusBook(raw2) {
|
|
35354
|
+
if (!raw2 || typeof raw2 !== "object" || Array.isArray(raw2)) return {};
|
|
35355
|
+
const out = {};
|
|
35356
|
+
for (const [key, value] of Object.entries(raw2)) {
|
|
35357
|
+
const parsed = GithubIssueStatus.safeParse(value);
|
|
35358
|
+
if (parsed.success) out[key] = parsed.data;
|
|
35359
|
+
}
|
|
35360
|
+
return out;
|
|
35361
|
+
}
|
|
35362
|
+
var PR_REF_RE, ISSUE_REF_RE, LIVE_SET, GITHUB_STATUS_SETTING, GITHUB_ISSUE_STATUS_SETTING;
|
|
35273
35363
|
var init_awaiting = __esm({
|
|
35274
35364
|
"packages/server/src/awaiting.ts"() {
|
|
35275
35365
|
"use strict";
|
|
35276
35366
|
init_src();
|
|
35277
35367
|
PR_REF_RE = /(?:https?:\/\/github\.com\/)?([A-Za-z0-9][\w.-]*)\/([A-Za-z0-9][\w.-]*?)(?:\/pull\/|\/pulls\/|#)(\d+)/;
|
|
35368
|
+
ISSUE_REF_RE = /(?:https?:\/\/github\.com\/)?([A-Za-z0-9][\w.-]*)\/([A-Za-z0-9][\w.-]*?)(?:\/issues\/|#)(\d+)/;
|
|
35278
35369
|
LIVE_SET = {
|
|
35279
35370
|
shell: "shells",
|
|
35280
35371
|
agent: "agents",
|
|
35281
35372
|
timer: "timers",
|
|
35282
|
-
pr: "prs"
|
|
35373
|
+
pr: "prs",
|
|
35374
|
+
issue: "issues"
|
|
35283
35375
|
};
|
|
35284
35376
|
GITHUB_STATUS_SETTING = "waker.github.status.v1";
|
|
35377
|
+
GITHUB_ISSUE_STATUS_SETTING = "waker.github.issue.status.v1";
|
|
35285
35378
|
}
|
|
35286
35379
|
});
|
|
35287
35380
|
|
|
@@ -35682,9 +35775,13 @@ exactly ONE of them.
|
|
|
35682
35775
|
It also tells you when CI is HELD FOR AN APPROVAL, which is the one reading that never resolves on
|
|
35683
35776
|
its own: a fork or first-time contributor's workflows wait for a maintainer to press the button, so
|
|
35684
35777
|
that wake is your cue to go and ask rather than to keep parking.
|
|
35778
|
+
- \`issues:\` \u2014 GitHub issues you REGISTERED with \`mcp__frizz__watch_issue\`, written \`owner/repo#N\`.
|
|
35779
|
+
Same shape as \`prs:\`, same register-first rule. That tool wakes you on every later comment, when a
|
|
35780
|
+
label moves or someone is assigned, and once more when the issue closes \u2014 the wait for a reporter's
|
|
35781
|
+
reproduction or a maintainer's triage. An issue has no CI and no merge, so it never says either.
|
|
35685
35782
|
- \`for:\` \u2014 **REQUIRED**, and a DURATION: \`30s\`, \`15m\`, \`2h\`, \`3d\`. Never an instant. When it runs
|
|
35686
35783
|
out frizz brings you back to re-check everything; re-parking is fine and uncapped. Capped at a day,
|
|
35687
|
-
except on a park naming ONLY \`prs:\`, where it runs to a year.
|
|
35784
|
+
except on a park naming ONLY \`prs:\` and \`issues:\`, where it runs to a year.
|
|
35688
35785
|
- **A PULL REQUEST IN SOMEONE ELSE'S REPO TAKES MONTHS, SO ASK FOR MONTHS** \u2014 \`for: 180d\`, and give
|
|
35689
35786
|
\`mcp__frizz__watch_pr\` the same. It moves on its maintainers' clock, not yours, so a short \`for:\`
|
|
35690
35787
|
expires against a PR nothing has touched: a wake carrying no news, and a re-arm, once per expiry
|
|
@@ -35705,8 +35802,9 @@ exactly ONE of them.
|
|
|
35705
35802
|
- **WAITING ON A PERSON IS A REGISTERED QUESTION** \u2014 \`mcp__frizz__ask\`. There is no human gate, no
|
|
35706
35803
|
prose park, and no question fence.
|
|
35707
35804
|
- **CI, RELEASES, DEPLOYS AND MERGE PROGRESSION ARE AUTOMATABLE \u2014 never \` \`\`\`awaiting \` them
|
|
35708
|
-
BLINDLY.** For a pull request, \`mcp__frizz__watch_pr
|
|
35709
|
-
sub-agent to own the wait (its return re-invokes you), or
|
|
35805
|
+
BLINDLY.** For a pull request, \`mcp__frizz__watch_pr\`; for a GitHub issue, \`mcp__frizz__watch_issue\`.
|
|
35806
|
+
For anything else stay ACTIVE: dispatch a sub-agent to own the wait (its return re-invokes you), or
|
|
35807
|
+
set a timer and name it here.
|
|
35710
35808
|
- **THE PARK SURVIVES A RESTART.** Registration and fence are both durable, so frizz brings you back
|
|
35711
35809
|
(\`{{FRIZZ_RESUME_CMD}}\`) even if its own server or your daemon is replaced while you wait.
|
|
35712
35810
|
- **A follow-up clears the previous fence.** If the human says "back to awaiting", never answer that
|
|
@@ -36054,7 +36152,8 @@ the handoff.
|
|
|
36054
36152
|
rested thread out of the queue.
|
|
36055
36153
|
|
|
36056
36154
|
- **Resting until a condition is met** (the usual CI / PR / release wait) \u2192 for a pull request,
|
|
36057
|
-
\`mcp__frizz__watch_pr\`; for
|
|
36155
|
+
\`mcp__frizz__watch_pr\`; for a GitHub issue, \`mcp__frizz__watch_issue\`; for anything else dispatch a
|
|
36156
|
+
SUB-AGENT to own the wait. It runs the watcher
|
|
36058
36157
|
to completion in its own foreground and returns the verdict; you stay Active and its return
|
|
36059
36158
|
re-invokes you. Foreground Bash caps at ~10 min, so a longer wait loops until its terminal
|
|
36060
36159
|
condition. A helper must not hand back while its own watcher is still live.
|
|
@@ -38211,8 +38310,8 @@ function hasLiveOwnWork(tele, registeredPrWatches) {
|
|
|
38211
38310
|
function hasParkedPrWatch(tele, registered) {
|
|
38212
38311
|
if (tele?.lastFence?.kind !== "awaiting") return registered.size > 0;
|
|
38213
38312
|
return tele.lastFence.hints.some((hint) => {
|
|
38214
|
-
if (hint.kind !== "pr") return false;
|
|
38215
|
-
const ref = parsePrRef(hint.value);
|
|
38313
|
+
if (hint.kind !== "pr" && hint.kind !== "issue") return false;
|
|
38314
|
+
const ref = hint.kind === "pr" ? parsePrRef(hint.value) : parseIssueRef(hint.value);
|
|
38216
38315
|
return ref !== void 0 && registered.has(githubStatusKey(ref));
|
|
38217
38316
|
});
|
|
38218
38317
|
}
|
|
@@ -38222,7 +38321,7 @@ function bgSnoozeArmed(row) {
|
|
|
38222
38321
|
function hasParkedExternalWait(tele, _nowMs) {
|
|
38223
38322
|
return false;
|
|
38224
38323
|
}
|
|
38225
|
-
function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], armedTimers = [], armedWatches = []) {
|
|
38324
|
+
function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], armedTimers = [], armedWatches = [], issueBook = {}) {
|
|
38226
38325
|
const seen = /* @__PURE__ */ new Set();
|
|
38227
38326
|
const out = [];
|
|
38228
38327
|
for (const t of armedTimers) {
|
|
@@ -38240,17 +38339,21 @@ function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], arme
|
|
|
38240
38339
|
});
|
|
38241
38340
|
}
|
|
38242
38341
|
for (const w of registered) {
|
|
38243
|
-
|
|
38244
|
-
|
|
38342
|
+
const subject = w.kind === "issue" ? "issue" : "pull";
|
|
38343
|
+
const dedupe = `github:${subject}:${w.target}`;
|
|
38344
|
+
if (seen.has(dedupe)) continue;
|
|
38345
|
+
seen.add(dedupe);
|
|
38245
38346
|
out.push({
|
|
38246
|
-
id: `github:${slug}:${w.target}`,
|
|
38347
|
+
id: `github:${slug}:${subject === "issue" ? "issue:" : ""}${w.target}`,
|
|
38247
38348
|
kind: "github",
|
|
38248
38349
|
target: w.target,
|
|
38249
38350
|
state: "armed",
|
|
38250
38351
|
createdAt: w.createdAt,
|
|
38352
|
+
subject,
|
|
38251
38353
|
// Absent until the poller's first successful read. The card says "checking…" then, rather than
|
|
38252
38354
|
// inventing a verdict — an unpolled PR and a PR with no CI are different facts.
|
|
38253
|
-
...github[w.target] ? { github: github[w.target] } : {}
|
|
38355
|
+
...subject === "pull" && github[w.target] ? { github: github[w.target] } : {},
|
|
38356
|
+
...subject === "issue" && issueBook[w.target] ? { issue: issueBook[w.target] } : {}
|
|
38254
38357
|
});
|
|
38255
38358
|
}
|
|
38256
38359
|
for (const w of armedWatches) {
|
|
@@ -38405,9 +38508,10 @@ function readThreadRegistries(storage) {
|
|
|
38405
38508
|
done: storage.threadDoneBySlug()
|
|
38406
38509
|
};
|
|
38407
38510
|
}
|
|
38408
|
-
function sessionThreadView(projectDir, storage, row, rawTele, registeredLegacyTerminal, interactionPresence, nowMs, codexTurnLiveness, claudeBrokerDaemonAlive, registries, github = {}) {
|
|
38511
|
+
function sessionThreadView(projectDir, storage, row, rawTele, registeredLegacyTerminal, interactionPresence, nowMs, codexTurnLiveness, claudeBrokerDaemonAlive, registries, github = {}, issueBook = {}) {
|
|
38409
38512
|
const armedPrWatches = (registries.prWatches.get(row.slug) ?? []).map((w) => ({
|
|
38410
38513
|
target: `${w.owner}/${w.repo}#${w.number}`,
|
|
38514
|
+
kind: w.kind,
|
|
38411
38515
|
createdAt: new Date(w.created_at).toISOString()
|
|
38412
38516
|
}));
|
|
38413
38517
|
const registeredPrWatches = new Set(armedPrWatches.map((w) => w.target));
|
|
@@ -38510,7 +38614,7 @@ function sessionThreadView(projectDir, storage, row, rawTele, registeredLegacyTe
|
|
|
38510
38614
|
// become the github rows, `watch:` lines the shell rows — so this strip lists exactly what will
|
|
38511
38615
|
// actually wake the thread, and the two cannot drift into claiming different things. There is no
|
|
38512
38616
|
// registry behind either any more (`thread_watch`, retired 2026-08-14).
|
|
38513
|
-
watches: fenceWatchViews(row.slug, tele, tele?.lastAssistantAt, github, armedPrWatches, armedTimers, armedWatches),
|
|
38617
|
+
watches: fenceWatchViews(row.slug, tele, tele?.lastAssistantAt, github, armedPrWatches, armedTimers, armedWatches, issueBook),
|
|
38514
38618
|
pendingAsk: tele?.pendingAsk ? { questions: tele.pendingAsk.questions } : void 0,
|
|
38515
38619
|
pendingQuestion: tele?.pendingQuestion ?? false,
|
|
38516
38620
|
questions,
|
|
@@ -38687,6 +38791,7 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
|
|
|
38687
38791
|
function buildSessionThreads(nowMs) {
|
|
38688
38792
|
const rows = storage.allSessions().filter((row) => ThreadSlug.safeParse(row.slug).success);
|
|
38689
38793
|
const github = readGithubStatusBook(storage.getSetting(GITHUB_STATUS_SETTING));
|
|
38794
|
+
const issueBook = readGithubIssueStatusBook(storage.getSetting(GITHUB_ISSUE_STATUS_SETTING));
|
|
38690
38795
|
const registries = readThreadRegistries(storage);
|
|
38691
38796
|
const currentInteractionKeys = /* @__PURE__ */ new Set();
|
|
38692
38797
|
const out = [];
|
|
@@ -38725,7 +38830,8 @@ function createBoard(project, storage, bus, tailer, bootId, deps = {}) {
|
|
|
38725
38830
|
codexTurnLiveness,
|
|
38726
38831
|
claudeBrokerDaemonAlive,
|
|
38727
38832
|
registries,
|
|
38728
|
-
github
|
|
38833
|
+
github,
|
|
38834
|
+
issueBook
|
|
38729
38835
|
));
|
|
38730
38836
|
}
|
|
38731
38837
|
for (const key of pendingInteractionCache.keys()) {
|
|
@@ -39190,7 +39296,7 @@ var init_wake_store = __esm({
|
|
|
39190
39296
|
import { execFile as execFile6 } from "node:child_process";
|
|
39191
39297
|
import { promisify as promisify6 } from "node:util";
|
|
39192
39298
|
function refKey(ref) {
|
|
39193
|
-
return `${ref.owner}/${ref.repo}#${ref.number}`;
|
|
39299
|
+
return `${ref.kind === "issue" ? "issue:" : ""}${ref.owner}/${ref.repo}#${ref.number}`;
|
|
39194
39300
|
}
|
|
39195
39301
|
function conciseError(error) {
|
|
39196
39302
|
const raw2 = error instanceof Error ? error.message : String(error);
|
|
@@ -39289,6 +39395,25 @@ function parseGithubPrSnapshot(pr) {
|
|
|
39289
39395
|
})
|
|
39290
39396
|
};
|
|
39291
39397
|
}
|
|
39398
|
+
function parseGithubIssueActivities(raw2) {
|
|
39399
|
+
const issue = raw2?.data?.repository?.issue;
|
|
39400
|
+
if (!issue || typeof issue !== "object") return [];
|
|
39401
|
+
return parseGithubReviewActivities({ data: { repository: { pullRequest: { comments: issue.comments } } } });
|
|
39402
|
+
}
|
|
39403
|
+
function parseGithubIssueSnapshot(issue) {
|
|
39404
|
+
if (!issue || typeof issue !== "object") return void 0;
|
|
39405
|
+
const i = issue;
|
|
39406
|
+
if (typeof i.state !== "string" || !i.state) return void 0;
|
|
39407
|
+
const comments = Number(i.comments?.totalCount);
|
|
39408
|
+
return {
|
|
39409
|
+
state: i.state,
|
|
39410
|
+
...typeof i.stateReason === "string" && i.stateReason ? { stateReason: i.stateReason } : {},
|
|
39411
|
+
...typeof i.title === "string" && i.title ? { title: i.title.slice(0, 200) } : {},
|
|
39412
|
+
labels: strings(i.labels?.nodes, (n) => n.name),
|
|
39413
|
+
assignees: strings(i.assignees?.nodes, (n) => n.login),
|
|
39414
|
+
comments: Number.isFinite(comments) && comments >= 0 ? comments : 0
|
|
39415
|
+
};
|
|
39416
|
+
}
|
|
39292
39417
|
function isBotGithubActor(a) {
|
|
39293
39418
|
return a.actorType?.toLowerCase() === "bot" || a.actor.toLowerCase().endsWith("[bot]");
|
|
39294
39419
|
}
|
|
@@ -39301,6 +39426,18 @@ function buildQuery(refs) {
|
|
|
39301
39426
|
variables[`owner${index}`] = ref.owner;
|
|
39302
39427
|
variables[`repo${index}`] = ref.repo;
|
|
39303
39428
|
variables[`number${index}`] = ref.number;
|
|
39429
|
+
if (ref.kind === "issue") {
|
|
39430
|
+
fields.push(`
|
|
39431
|
+
ref${index}: repository(owner: $owner${index}, name: $repo${index}) {
|
|
39432
|
+
issue(number: $number${index}) {
|
|
39433
|
+
state stateReason title
|
|
39434
|
+
comments(last: 50) { totalCount nodes { id url createdAt body author { login __typename } } }
|
|
39435
|
+
labels(first: ${LABEL_CAP}) { nodes { name } }
|
|
39436
|
+
assignees(first: ${ASSIGNEE_CAP}) { nodes { login } }
|
|
39437
|
+
}
|
|
39438
|
+
}`);
|
|
39439
|
+
return;
|
|
39440
|
+
}
|
|
39304
39441
|
fields.push(`
|
|
39305
39442
|
ref${index}: repository(owner: $owner${index}, name: $repo${index}) {
|
|
39306
39443
|
pullRequest(number: $number${index}) {
|
|
@@ -39485,6 +39622,24 @@ function createGithubReviewFetcher(deps = {}) {
|
|
|
39485
39622
|
}
|
|
39486
39623
|
refs.forEach((ref, index) => {
|
|
39487
39624
|
const repository = body?.data?.[`ref${index}`];
|
|
39625
|
+
if (ref.kind === "issue") {
|
|
39626
|
+
const issue = repository?.issue;
|
|
39627
|
+
if (!issue || typeof issue !== "object") {
|
|
39628
|
+
results.set(refKey(ref), {
|
|
39629
|
+
status: "error",
|
|
39630
|
+
failure: {
|
|
39631
|
+
kind: graphRateLimited ? "rate-limit" : genericGraphError ? "graphql" : "shape",
|
|
39632
|
+
message: graphRateLimited ? `GitHub API rate limit exhausted${graphResetAt ? `; resets at ${graphResetAt}` : ""}` : genericGraphError ? `GitHub GraphQL error: ${genericGraphError}` : `GitHub returned no accessible issue for ${refKey(ref)}`,
|
|
39633
|
+
...graphRateLimited && graphResetAt ? { retryAt: graphResetAt } : {}
|
|
39634
|
+
}
|
|
39635
|
+
});
|
|
39636
|
+
return;
|
|
39637
|
+
}
|
|
39638
|
+
const activity2 = parseGithubIssueActivities({ data: { repository: { issue } } });
|
|
39639
|
+
const snapshot2 = parseGithubIssueSnapshot(issue);
|
|
39640
|
+
results.set(refKey(ref), { status: "ok", activity: activity2, ...snapshot2 ? { issue: snapshot2 } : {} });
|
|
39641
|
+
return;
|
|
39642
|
+
}
|
|
39488
39643
|
const pr = repository?.pullRequest;
|
|
39489
39644
|
if (!pr || typeof pr !== "object") {
|
|
39490
39645
|
results.set(refKey(ref), {
|
|
@@ -39553,7 +39708,7 @@ function createGithubReviewFetcher(deps = {}) {
|
|
|
39553
39708
|
}
|
|
39554
39709
|
});
|
|
39555
39710
|
}
|
|
39556
|
-
var execFileAsync4, GH_TIMEOUT_MS, GH_MAX_BUFFER, REQUEST_TIMEOUT_MS, MAX_REFS_PER_REQUEST, RATE_LIMIT_RESERVE, ROLLUP_CAP, CHECK_SUITE_CAP, LABEL_CAP, REVIEW_REQUEST_CAP, strings;
|
|
39711
|
+
var execFileAsync4, GH_TIMEOUT_MS, GH_MAX_BUFFER, REQUEST_TIMEOUT_MS, MAX_REFS_PER_REQUEST, RATE_LIMIT_RESERVE, ROLLUP_CAP, CHECK_SUITE_CAP, LABEL_CAP, REVIEW_REQUEST_CAP, ASSIGNEE_CAP, strings;
|
|
39557
39712
|
var init_github_review = __esm({
|
|
39558
39713
|
"packages/server/src/github-review.ts"() {
|
|
39559
39714
|
"use strict";
|
|
@@ -39567,6 +39722,7 @@ var init_github_review = __esm({
|
|
|
39567
39722
|
CHECK_SUITE_CAP = 50;
|
|
39568
39723
|
LABEL_CAP = 30;
|
|
39569
39724
|
REVIEW_REQUEST_CAP = 20;
|
|
39725
|
+
ASSIGNEE_CAP = 20;
|
|
39570
39726
|
strings = (nodes, pick) => (Array.isArray(nodes) ? nodes : []).flatMap((node) => {
|
|
39571
39727
|
if (!node || typeof node !== "object") return [];
|
|
39572
39728
|
const value = pick(node);
|
|
@@ -39859,7 +40015,7 @@ function restMessageIsSignedOff(storage, slug, tele, _registeredPrWatches = /* @
|
|
|
39859
40015
|
if (tele.lastFence?.kind === "awaiting") return true;
|
|
39860
40016
|
return threadSaidDone(storage, slug, tele, armedAt);
|
|
39861
40017
|
}
|
|
39862
|
-
function liveActivityOf(tele, registeredPrWatches, armedTimerIds) {
|
|
40018
|
+
function liveActivityOf(tele, registeredPrWatches, armedTimerIds, registeredIssueWatches = /* @__PURE__ */ new Set()) {
|
|
39863
40019
|
const shells = /* @__PURE__ */ new Set();
|
|
39864
40020
|
for (const sh of tele.bgShells ?? []) {
|
|
39865
40021
|
if (sh.state !== "running") continue;
|
|
@@ -39870,10 +40026,13 @@ function liveActivityOf(tele, registeredPrWatches, armedTimerIds) {
|
|
|
39870
40026
|
if (a.state !== "running") continue;
|
|
39871
40027
|
for (const h of [a.taskId, a.id, a.label]) if (h) agents.add(h);
|
|
39872
40028
|
}
|
|
39873
|
-
return { shells, agents, timers: armedTimerIds, prs: registeredPrWatches };
|
|
40029
|
+
return { shells, agents, timers: armedTimerIds, prs: registeredPrWatches, issues: registeredIssueWatches };
|
|
39874
40030
|
}
|
|
39875
40031
|
function registeredPrWatchesOf(storage, slug) {
|
|
39876
|
-
return new Set(storage.listPrWatches(slug, { armedOnly: true }).map((w) => `${w.owner}/${w.repo}#${w.number}`));
|
|
40032
|
+
return new Set(storage.listPrWatches(slug, { armedOnly: true }).filter((w) => w.kind !== "issue").map((w) => `${w.owner}/${w.repo}#${w.number}`));
|
|
40033
|
+
}
|
|
40034
|
+
function registeredIssueWatchesOf(storage, slug) {
|
|
40035
|
+
return new Set(storage.listPrWatches(slug, { armedOnly: true }).filter((w) => w.kind === "issue").map((w) => `${w.owner}/${w.repo}#${w.number}`));
|
|
39877
40036
|
}
|
|
39878
40037
|
function armedSchedule(row) {
|
|
39879
40038
|
const prompt = row.recurring_prompt?.trim();
|
|
@@ -40008,6 +40167,22 @@ async function probePrReadable(ref) {
|
|
|
40008
40167
|
return { ok: false, reason: conciseGhError(err) };
|
|
40009
40168
|
}
|
|
40010
40169
|
}
|
|
40170
|
+
async function probeIssueReadable(ref) {
|
|
40171
|
+
try {
|
|
40172
|
+
const { stdout } = await execFileAsync5(
|
|
40173
|
+
"gh",
|
|
40174
|
+
["issue", "view", String(ref.number), "--repo", `${ref.owner}/${ref.repo}`, "--json", "state,url"],
|
|
40175
|
+
{ timeout: 15e3, maxBuffer: 1e6, env: { ...process.env, GH_PAGER: "cat", GH_PROMPT_DISABLED: "1" } }
|
|
40176
|
+
);
|
|
40177
|
+
const url = JSON.parse(stdout)?.url;
|
|
40178
|
+
if (typeof url === "string" && /\/pull\/\d+$/.test(url)) {
|
|
40179
|
+
return { ok: false, reason: `#${ref.number} is a pull request, not an issue \u2014 register it with \`mcp__frizz__watch_pr\`` };
|
|
40180
|
+
}
|
|
40181
|
+
return { ok: true };
|
|
40182
|
+
} catch (err) {
|
|
40183
|
+
return { ok: false, reason: conciseGhError(err) };
|
|
40184
|
+
}
|
|
40185
|
+
}
|
|
40011
40186
|
async function defaultFetchPr(ref) {
|
|
40012
40187
|
try {
|
|
40013
40188
|
const { stdout } = await execFileAsync5(
|
|
@@ -40064,6 +40239,21 @@ function createScheduler(deps) {
|
|
|
40064
40239
|
for (const stale of keys.slice(0, Math.max(0, keys.length - GITHUB_STATUS_CAP))) delete book[stale];
|
|
40065
40240
|
deps.storage.setSetting(GITHUB_STATUS_SETTING, book);
|
|
40066
40241
|
}
|
|
40242
|
+
function publishGithubIssueStatus(key, issue, nowMs) {
|
|
40243
|
+
const raw2 = deps.storage.getSetting(GITHUB_ISSUE_STATUS_SETTING);
|
|
40244
|
+
const book = raw2 && typeof raw2 === "object" && !Array.isArray(raw2) ? { ...raw2 } : {};
|
|
40245
|
+
const reading = {
|
|
40246
|
+
state: issue.state.toUpperCase() === "CLOSED" ? "closed" : "open",
|
|
40247
|
+
...issue.stateReason ? { stateReason: issue.stateReason.toLowerCase() } : {},
|
|
40248
|
+
...issue.title ? { title: issue.title } : {},
|
|
40249
|
+
comments: issue.comments,
|
|
40250
|
+
polledAt: new Date(nowMs).toISOString()
|
|
40251
|
+
};
|
|
40252
|
+
book[key] = reading;
|
|
40253
|
+
const keys = Object.keys(book);
|
|
40254
|
+
for (const stale of keys.slice(0, Math.max(0, keys.length - GITHUB_STATUS_CAP))) delete book[stale];
|
|
40255
|
+
deps.storage.setSetting(GITHUB_ISSUE_STATUS_SETTING, book);
|
|
40256
|
+
}
|
|
40067
40257
|
class InjectedSchedulerCrash extends Error {
|
|
40068
40258
|
constructor(cause) {
|
|
40069
40259
|
super("simulated scheduler hard crash", { cause });
|
|
@@ -40387,7 +40577,8 @@ function createScheduler(deps) {
|
|
|
40387
40577
|
// than the two the fold happens to know about — a worker told about half its work writes half
|
|
40388
40578
|
// a fence, and the half it left out is not what gets it bumped.
|
|
40389
40579
|
timers: deps.storage.listThreadTimers(row.slug, { armedOnly: true }).map((t) => ({ id: t.id, label: t.prompt.trim().replace(/\s+/g, " ").slice(0, 80) })),
|
|
40390
|
-
prs: deps.storage.listPrWatches(row.slug, { armedOnly: true }).map((w) => ({ id: `${w.owner}/${w.repo}#${w.number}`, label: `${w.owner}/${w.repo}#${w.number}` }))
|
|
40580
|
+
prs: deps.storage.listPrWatches(row.slug, { armedOnly: true }).filter((w) => w.kind !== "issue").map((w) => ({ id: `${w.owner}/${w.repo}#${w.number}`, label: `${w.owner}/${w.repo}#${w.number}` })),
|
|
40581
|
+
issues: deps.storage.listPrWatches(row.slug, { armedOnly: true }).filter((w) => w.kind === "issue").map((w) => ({ id: `${w.owner}/${w.repo}#${w.number}`, label: `${w.owner}/${w.repo}#${w.number}` }))
|
|
40391
40582
|
}), spokeAt),
|
|
40392
40583
|
reason: "rested without signing off"
|
|
40393
40584
|
}, nowMs).delivery;
|
|
@@ -40452,7 +40643,8 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
|
|
|
40452
40643
|
const live = liveActivityOf(
|
|
40453
40644
|
tele,
|
|
40454
40645
|
registeredPrWatchesOf(deps.storage, row.slug),
|
|
40455
|
-
armedTimerIdsOf(deps.storage, row.slug)
|
|
40646
|
+
armedTimerIdsOf(deps.storage, row.slug),
|
|
40647
|
+
registeredIssueWatchesOf(deps.storage, row.slug)
|
|
40456
40648
|
);
|
|
40457
40649
|
const dead = unaccountedItems(park.items, live);
|
|
40458
40650
|
const expiresAt = parkExpiresAt(park, Date.parse(spokeAt));
|
|
@@ -40475,7 +40667,7 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
|
|
|
40475
40667
|
const finishedItem = (i) => finishedHandles.has(i.value) || i.kind === "timer" && firedTimers.has(i.value);
|
|
40476
40668
|
const status = park.items.map((i) => {
|
|
40477
40669
|
const gone = dead.some((d) => d.kind === i.kind && d.value === i.value);
|
|
40478
|
-
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)";
|
|
40670
|
+
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" : i.kind === "issue" ? "NOT REGISTERED \u2014 register it with `mcp__frizz__watch_issue` first, then name it here" : "NOT RUNNING (nothing by that name)";
|
|
40479
40671
|
return `- \`${AWAITING_KEY_OF[i.kind]}: [${i.value}]\` \u2014 ${note}`;
|
|
40480
40672
|
});
|
|
40481
40673
|
const allFinished = dead.length > 0 && dead.every(finishedItem);
|
|
@@ -40495,6 +40687,7 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
|
|
|
40495
40687
|
"agents: [<runtime agent id>] sub-agents you dispatched",
|
|
40496
40688
|
"timers: [tmr_\u2026] timers from `mcp__frizz__timer`",
|
|
40497
40689
|
"prs: [owner/repo#123] PRs registered with `mcp__frizz__watch_pr`",
|
|
40690
|
+
"issues: [owner/repo#45] GitHub issues registered with `mcp__frizz__watch_issue`",
|
|
40498
40691
|
"for: 2h REQUIRED \u2014 a DURATION, never an instant",
|
|
40499
40692
|
"---",
|
|
40500
40693
|
"your handoff prose, as much as you want",
|
|
@@ -40514,6 +40707,8 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
|
|
|
40514
40707
|
"",
|
|
40515
40708
|
"- a pull request \u2192 `mcp__frizz__watch_pr` (it wakes you on CI going green or red, and on every",
|
|
40516
40709
|
" later review and comment) \u2192 `prs: [owner/repo#123]`",
|
|
40710
|
+
"- a GitHub issue \u2192 `mcp__frizz__watch_issue` (every later comment, a label or assignee moving,",
|
|
40711
|
+
" and the close) \u2192 `issues: [owner/repo#45]`",
|
|
40517
40712
|
"- a wall-clock check \u2192 `mcp__frizz__timer` \u2192 `timers: [tmr_\u2026]`",
|
|
40518
40713
|
"- work you already launched \u2192 `shells: [<the id your runtime gave you>]` or `agents: [<id>]`",
|
|
40519
40714
|
"",
|
|
@@ -40536,7 +40731,8 @@ ${wakeTimeHeader(nowMs, spokeAt)}`,
|
|
|
40536
40731
|
shells: (tele.bgShells ?? []).filter((sh) => sh.state === "running").map((sh) => ({ id: sh.taskId ?? sh.id, label: sh.label })),
|
|
40537
40732
|
subAgents: (tele.subAgents ?? []).filter((a) => a.state === "running").map((a) => ({ id: a.taskId ?? a.id, label: a.label })),
|
|
40538
40733
|
timers: deps.storage.listThreadTimers(row.slug, { armedOnly: true }).map((t) => ({ id: t.id, label: t.prompt.trim().replace(/\s+/g, " ").slice(0, 80) })),
|
|
40539
|
-
prs: deps.storage.listPrWatches(row.slug, { armedOnly: true }).map((w) => ({ id: `${w.owner}/${w.repo}#${w.number}`, label: `${w.owner}/${w.repo}#${w.number}` }))
|
|
40734
|
+
prs: deps.storage.listPrWatches(row.slug, { armedOnly: true }).filter((w) => w.kind !== "issue").map((w) => ({ id: `${w.owner}/${w.repo}#${w.number}`, label: `${w.owner}/${w.repo}#${w.number}` })),
|
|
40735
|
+
issues: deps.storage.listPrWatches(row.slug, { armedOnly: true }).filter((w) => w.kind === "issue").map((w) => ({ id: `${w.owner}/${w.repo}#${w.number}`, label: `${w.owner}/${w.repo}#${w.number}` }))
|
|
40540
40736
|
});
|
|
40541
40737
|
const clock = wakeTimeHeader(nowMs, spokeAt);
|
|
40542
40738
|
const message = ops.length > 0 ? `${head}
|
|
@@ -40567,6 +40763,7 @@ ${clock}`;
|
|
|
40567
40763
|
}
|
|
40568
40764
|
}
|
|
40569
40765
|
const prWatchPolledAt = /* @__PURE__ */ new Map();
|
|
40766
|
+
const watchKey = (w) => `${w.kind === "issue" ? "issue:" : ""}${w.owner}/${w.repo}#${w.number}`;
|
|
40570
40767
|
const prStatusFallback = concurrencyGate(PR_STATUS_FALLBACK_LIMIT);
|
|
40571
40768
|
function readPrWatchHeld(raw2) {
|
|
40572
40769
|
if (!raw2 || typeof raw2 !== "object") return void 0;
|
|
@@ -40614,6 +40811,7 @@ ${clock}`;
|
|
|
40614
40811
|
const held = readPrWatchHeld(parsed.held);
|
|
40615
40812
|
const labels = readCursorList(parsed.labels);
|
|
40616
40813
|
const reviewers = readCursorList(parsed.reviewers);
|
|
40814
|
+
const assignees = readCursorList(parsed.assignees);
|
|
40617
40815
|
return {
|
|
40618
40816
|
seen,
|
|
40619
40817
|
checks: typeof parsed.checks === "string" ? parsed.checks : void 0,
|
|
@@ -40621,7 +40819,8 @@ ${clock}`;
|
|
|
40621
40819
|
...held ? { held } : {},
|
|
40622
40820
|
...typeof parsed.merge === "string" ? { merge: parsed.merge } : {},
|
|
40623
40821
|
...labels ? { labels } : {},
|
|
40624
|
-
...reviewers ? { reviewers } : {}
|
|
40822
|
+
...reviewers ? { reviewers } : {},
|
|
40823
|
+
...assignees ? { assignees } : {}
|
|
40625
40824
|
};
|
|
40626
40825
|
} catch {
|
|
40627
40826
|
return { seen: [] };
|
|
@@ -40637,7 +40836,7 @@ ${clock}`;
|
|
|
40637
40836
|
const row = deps.storage.getSession(w.thread_slug);
|
|
40638
40837
|
deps.storage.settlePrWatch(w.id, nowMs);
|
|
40639
40838
|
const ref = `${w.owner}/${w.repo}#${w.number}`;
|
|
40640
|
-
log3(`waker: settled ${w.thread_slug} \u2014 pr watcher ${w.id} expired (${ref})`);
|
|
40839
|
+
log3(`waker: settled ${w.thread_slug} \u2014 ${w.kind === "issue" ? "issue" : "pr"} watcher ${w.id} expired (${ref})`);
|
|
40641
40840
|
if (!row || row.state === "archived" || row.archived === 1) continue;
|
|
40642
40841
|
const fenceId = prWatchExpiredFenceId(w.id);
|
|
40643
40842
|
const deliveryId = wakeDeliveryId(row.slug, row.session_id, fenceId);
|
|
@@ -40649,8 +40848,8 @@ ${clock}`;
|
|
|
40649
40848
|
fenceId,
|
|
40650
40849
|
hintKey: fenceId,
|
|
40651
40850
|
// In @frizz/shared beside `parsePrWatchExpiredWake`, for the reason above.
|
|
40652
|
-
message: prWatchExpiredWakeMessage(ref),
|
|
40653
|
-
reason:
|
|
40851
|
+
message: prWatchExpiredWakeMessage(ref, w.kind === "issue" ? "issue" : "pull"),
|
|
40852
|
+
reason: `${w.kind === "issue" ? "issue" : "pr"} watcher ${w.id} expired (${ref})`
|
|
40654
40853
|
}, nowMs).delivery;
|
|
40655
40854
|
log3(`waker: queued ${row.slug} \u2014 ${item.reason}`);
|
|
40656
40855
|
checkpoint("after-enqueue", item);
|
|
@@ -40669,16 +40868,36 @@ ${clock}`;
|
|
|
40669
40868
|
const refs = /* @__PURE__ */ new Map();
|
|
40670
40869
|
for (const w of armed) {
|
|
40671
40870
|
if (!isThreadLive(w.thread_slug)) continue;
|
|
40672
|
-
const key =
|
|
40871
|
+
const key = watchKey(w);
|
|
40673
40872
|
const last = prWatchPolledAt.get(key) ?? 0;
|
|
40674
40873
|
if (nowMs - last < PR_WATCH_POLL_MS) continue;
|
|
40675
|
-
refs.set(key, { owner: w.owner, repo: w.repo, number: w.number });
|
|
40874
|
+
refs.set(key, { owner: w.owner, repo: w.repo, number: w.number, kind: w.kind === "issue" ? "issue" : "pull" });
|
|
40676
40875
|
}
|
|
40677
40876
|
const status = /* @__PURE__ */ new Map();
|
|
40678
40877
|
const activity = /* @__PURE__ */ new Map();
|
|
40878
|
+
const issues = /* @__PURE__ */ new Map();
|
|
40679
40879
|
const meta = /* @__PURE__ */ new Map();
|
|
40680
40880
|
await Promise.all([...refs].map(async ([key, ref]) => {
|
|
40681
40881
|
prWatchPolledAt.set(key, nowMs);
|
|
40882
|
+
if (ref.kind === "issue") {
|
|
40883
|
+
try {
|
|
40884
|
+
const result = normalizeReviewResult(await fetchGithubReview(ref));
|
|
40885
|
+
if (result.status === "ok") {
|
|
40886
|
+
activity.set(key, result.activity);
|
|
40887
|
+
if (result.issue) {
|
|
40888
|
+
issues.set(key, result.issue);
|
|
40889
|
+
publishGithubIssueStatus(`${ref.owner}/${ref.repo}#${ref.number}`, result.issue, nowMs);
|
|
40890
|
+
}
|
|
40891
|
+
recordReviewSuccess(key, "issue-watch registry");
|
|
40892
|
+
} else if (result.status === "error") recordReviewFailure(key, "issue-watch registry", result, nowMs);
|
|
40893
|
+
} catch (err) {
|
|
40894
|
+
recordReviewFailure(key, "issue-watch registry", {
|
|
40895
|
+
status: "error",
|
|
40896
|
+
failure: { kind: "network", message: err instanceof Error ? err.message : String(err) }
|
|
40897
|
+
}, nowMs);
|
|
40898
|
+
}
|
|
40899
|
+
return;
|
|
40900
|
+
}
|
|
40682
40901
|
let snapshot;
|
|
40683
40902
|
let deferred = false;
|
|
40684
40903
|
try {
|
|
@@ -40710,7 +40929,11 @@ ${clock}`;
|
|
|
40710
40929
|
for (const w of armed) {
|
|
40711
40930
|
const row = deps.storage.getSession(w.thread_slug);
|
|
40712
40931
|
if (!row || row.state === "archived" || row.archived === 1) continue;
|
|
40713
|
-
const key =
|
|
40932
|
+
const key = watchKey(w);
|
|
40933
|
+
if (w.kind === "issue") {
|
|
40934
|
+
evalIssueWatch(row, w, issues.get(key), activity.get(key), nowMs);
|
|
40935
|
+
continue;
|
|
40936
|
+
}
|
|
40714
40937
|
const st = status.get(key);
|
|
40715
40938
|
const acts = activity.get(key);
|
|
40716
40939
|
if (!st && !acts) continue;
|
|
@@ -40808,6 +41031,88 @@ ${clock}`;
|
|
|
40808
41031
|
deps.storage.setPrWatchCursor(w.id, JSON.stringify({ ...nextCursor, report, held: carried }));
|
|
40809
41032
|
}
|
|
40810
41033
|
}
|
|
41034
|
+
function issueStateChanges(cursor, labels, assignees) {
|
|
41035
|
+
const out = [];
|
|
41036
|
+
if (labels && cursor.labels) {
|
|
41037
|
+
const added = labels.filter((l) => !cursor.labels.includes(l));
|
|
41038
|
+
const removed = cursor.labels.filter((l) => !labels.includes(l));
|
|
41039
|
+
const parts = [...added.map((l) => `+${l}`), ...removed.map((l) => `\u2212${l}`)];
|
|
41040
|
+
if (parts.length) out.push(`labels ${parts.join(", ")}`);
|
|
41041
|
+
}
|
|
41042
|
+
if (assignees && cursor.assignees) {
|
|
41043
|
+
const added = assignees.filter((a) => !cursor.assignees.includes(a));
|
|
41044
|
+
if (added.length) out.push(`assigned to ${added.map((a) => `@${a}`).join(", ")}`);
|
|
41045
|
+
}
|
|
41046
|
+
return out;
|
|
41047
|
+
}
|
|
41048
|
+
function evalIssueWatch(row, w, snap, acts, nowMs) {
|
|
41049
|
+
if (!snap && !acts) return;
|
|
41050
|
+
const target = `${w.owner}/${w.repo}#${w.number}`;
|
|
41051
|
+
const cursor = readPrWatchCursor(w.cursor);
|
|
41052
|
+
const waiting = undeliveredPrWatchReport(row.slug, w.id);
|
|
41053
|
+
if (snap && snap.state.toUpperCase() === "CLOSED") {
|
|
41054
|
+
deps.storage.settlePrWatch(w.id, nowMs);
|
|
41055
|
+
if (waiting) outbox.supersede(waiting.id, nowMs, "replaced by the watcher's closed report");
|
|
41056
|
+
const reason = snap.stateReason ? snap.stateReason.toLowerCase().replace(/_/g, " ") : void 0;
|
|
41057
|
+
enqueuePrWatchWake(row, w.id, nextReport(cursor), issueWatchWakeMessage({
|
|
41058
|
+
target,
|
|
41059
|
+
closed: reason ? { reason } : {}
|
|
41060
|
+
}), `issue-watch ${target} closed`, nowMs);
|
|
41061
|
+
return;
|
|
41062
|
+
}
|
|
41063
|
+
if (waiting && !cursor.held) return;
|
|
41064
|
+
const seen = new Set(cursor.seen);
|
|
41065
|
+
const firstPoll = w.cursor === null;
|
|
41066
|
+
const newestFirst = [...acts ?? []].sort((a, b) => {
|
|
41067
|
+
const at = Date.parse(b.at ?? "") - Date.parse(a.at ?? "");
|
|
41068
|
+
return Number.isFinite(at) && at !== 0 ? at : b.id.localeCompare(a.id);
|
|
41069
|
+
});
|
|
41070
|
+
const fresh = newestFirst.filter((a) => {
|
|
41071
|
+
if (seen.has(a.id) || isNoisePrActivity(a)) return false;
|
|
41072
|
+
if (!firstPoll) return true;
|
|
41073
|
+
const landed = Date.parse(a.at ?? "");
|
|
41074
|
+
return Number.isFinite(landed) && landed > w.created_at;
|
|
41075
|
+
});
|
|
41076
|
+
const named = fresh.slice(0, REVIEW_STEER_CAP);
|
|
41077
|
+
const freshItems = [...named].reverse().map((a) => ({
|
|
41078
|
+
label: activityLabel(a),
|
|
41079
|
+
actor: a.actor,
|
|
41080
|
+
bot: isBotGithubActor(a),
|
|
41081
|
+
...a.at ? { at: a.at } : {},
|
|
41082
|
+
...a.url ? { url: a.url } : {}
|
|
41083
|
+
}));
|
|
41084
|
+
const changes = firstPoll ? [] : issueStateChanges(cursor, snap?.labels, snap?.assignees);
|
|
41085
|
+
const nextCursor = {
|
|
41086
|
+
seen: acts ? [.../* @__PURE__ */ new Set([...cursor.seen, ...acts.map((a) => a.id)])].slice(-REVIEW_SEEN_CAP) : cursor.seen,
|
|
41087
|
+
report: cursor.report ?? 0,
|
|
41088
|
+
...snap?.labels ? { labels: snap.labels } : cursor.labels ? { labels: cursor.labels } : {},
|
|
41089
|
+
...snap?.assignees ? { assignees: snap.assignees } : cursor.assignees ? { assignees: cursor.assignees } : {}
|
|
41090
|
+
};
|
|
41091
|
+
if (fresh.length === 0 && changes.length === 0) {
|
|
41092
|
+
if (JSON.stringify(nextCursor) !== JSON.stringify(cursor)) {
|
|
41093
|
+
deps.storage.setPrWatchCursor(w.id, JSON.stringify(nextCursor));
|
|
41094
|
+
}
|
|
41095
|
+
return;
|
|
41096
|
+
}
|
|
41097
|
+
deps.storage.setSnoozedUntil(row.slug, null);
|
|
41098
|
+
const held = waiting ? cursor.held : void 0;
|
|
41099
|
+
const items = [...held?.items ?? [], ...freshItems];
|
|
41100
|
+
const mergedChanges = [.../* @__PURE__ */ new Set([...held?.changes ?? [], ...changes])];
|
|
41101
|
+
const carried = {
|
|
41102
|
+
items: items.slice(-REVIEW_STEER_CAP),
|
|
41103
|
+
omitted: (held?.omitted ?? 0) + (fresh.length - named.length) + Math.max(0, items.length - REVIEW_STEER_CAP),
|
|
41104
|
+
...mergedChanges.length ? { changes: mergedChanges } : {}
|
|
41105
|
+
};
|
|
41106
|
+
const review = carried.items.length > 0 ? formatGithubWakeSteer({ ref: target, items: carried.items, omitted: carried.omitted }) : void 0;
|
|
41107
|
+
const report = nextReport(cursor);
|
|
41108
|
+
if (waiting) outbox.supersede(waiting.id, nowMs, `folded into report ${report}, which says this and everything since`);
|
|
41109
|
+
enqueuePrWatchWake(row, w.id, report, issueWatchWakeMessage({
|
|
41110
|
+
target,
|
|
41111
|
+
...carried.changes?.length ? { changes: carried.changes } : {},
|
|
41112
|
+
...review ? { review } : {}
|
|
41113
|
+
}), `issue-watch ${target}${carried.changes?.length ? " state" : ""}${review ? " comment" : ""}`, nowMs);
|
|
41114
|
+
deps.storage.setPrWatchCursor(w.id, JSON.stringify({ ...nextCursor, report, held: carried }));
|
|
41115
|
+
}
|
|
40811
41116
|
function nextReport(cursor) {
|
|
40812
41117
|
return (cursor.report ?? 0) + 1;
|
|
40813
41118
|
}
|
|
@@ -41473,7 +41778,8 @@ var init_scheduler = __esm({
|
|
|
41473
41778
|
shell: "shells",
|
|
41474
41779
|
agent: "agents",
|
|
41475
41780
|
timer: "timers",
|
|
41476
|
-
pr: "prs"
|
|
41781
|
+
pr: "prs",
|
|
41782
|
+
issue: "issues"
|
|
41477
41783
|
};
|
|
41478
41784
|
MID_TURN_HOLD_MAX_MS = 10 * 6e4;
|
|
41479
41785
|
WAKE_HOLD_DEFERRAL = "delivery deferred until exact awaiting telemetry is idle and available";
|
|
@@ -42878,6 +43184,7 @@ ${wakeDeliveryToken(deliveryId)}`;
|
|
|
42878
43184
|
dispatcher,
|
|
42879
43185
|
scheduler,
|
|
42880
43186
|
probePr: probePrReadable,
|
|
43187
|
+
probeIssue: probeIssueReadable,
|
|
42881
43188
|
stopSubscriptions,
|
|
42882
43189
|
backendFor,
|
|
42883
43190
|
getSettings: () => getSettings(storage, home),
|
|
@@ -49595,14 +49902,18 @@ function createRouter(ctx) {
|
|
|
49595
49902
|
}
|
|
49596
49903
|
function armedPrWatchViews(slug) {
|
|
49597
49904
|
const github = readGithubStatusBook(ctx.storage.getSetting(GITHUB_STATUS_SETTING));
|
|
49905
|
+
const issues = readGithubIssueStatusBook(ctx.storage.getSetting(GITHUB_ISSUE_STATUS_SETTING));
|
|
49598
49906
|
return ctx.storage.listPrWatches(slug, { armedOnly: true }).map((w) => {
|
|
49599
49907
|
const target = `${w.owner}/${w.repo}#${w.number}`;
|
|
49908
|
+
const kind = w.kind === "issue" ? "issue" : "pull";
|
|
49600
49909
|
return {
|
|
49601
49910
|
id: w.id,
|
|
49602
49911
|
target,
|
|
49912
|
+
kind,
|
|
49603
49913
|
state: w.state,
|
|
49604
49914
|
createdAt: new Date(w.created_at).toISOString(),
|
|
49605
|
-
...github[target] ? { github: github[target] } : {}
|
|
49915
|
+
...kind === "pull" && github[target] ? { github: github[target] } : {},
|
|
49916
|
+
...kind === "issue" && issues[target] ? { issue: issues[target] } : {}
|
|
49606
49917
|
};
|
|
49607
49918
|
});
|
|
49608
49919
|
}
|
|
@@ -50737,10 +51048,11 @@ ${gapNote}` : input.message;
|
|
|
50737
51048
|
}
|
|
50738
51049
|
for (const w of ctx.storage.listPrWatches(input.slug, { armedOnly: true })) {
|
|
50739
51050
|
activity.push({
|
|
50740
|
-
kind: "pr",
|
|
51051
|
+
kind: w.kind === "issue" ? "issue" : "pr",
|
|
50741
51052
|
id: `${w.owner}/${w.repo}#${w.number}`,
|
|
50742
51053
|
label: `${w.owner}/${w.repo}#${w.number}`,
|
|
50743
|
-
since: new Date(w.created_at).toISOString()
|
|
51054
|
+
since: new Date(w.created_at).toISOString(),
|
|
51055
|
+
...w.expires_at ? { until: new Date(w.expires_at).toISOString() } : {}
|
|
50744
51056
|
});
|
|
50745
51057
|
}
|
|
50746
51058
|
return { activity, questions: openQuestionViews(input.slug), links: ctx.storage.listThreadLinks(input.slug).map(threadLinkView) };
|
|
@@ -50798,19 +51110,21 @@ ${gapNote}` : input.message;
|
|
|
50798
51110
|
if (row.state === "archived" || row.archived === 1) {
|
|
50799
51111
|
throw new Error("Reopen this thread before registering a watcher on it");
|
|
50800
51112
|
}
|
|
50801
|
-
const
|
|
51113
|
+
const kind = input.kind ?? "pull";
|
|
51114
|
+
const noun = kind === "issue" ? "issue" : "pull request";
|
|
51115
|
+
const ref = kind === "issue" ? parseIssueRef(input.target) : parsePrRef(input.target);
|
|
50802
51116
|
if (!ref) {
|
|
50803
|
-
throw new Error(`\`${input.target}\` is not a pull request I can watch \u2014 give owner/repo#123 or a PR URL`);
|
|
51117
|
+
throw new Error(`\`${input.target}\` is not ${kind === "issue" ? "an issue" : "a pull request"} I can watch \u2014 give owner/repo#123 or ${kind === "issue" ? "an issue" : "a PR"} URL`);
|
|
50804
51118
|
}
|
|
50805
51119
|
const armed = ctx.storage.listPrWatches(input.slug, { armedOnly: true });
|
|
50806
|
-
const existing = armed.find((w) => w.owner === ref.owner && w.repo === ref.repo && w.number === ref.number);
|
|
51120
|
+
const existing = armed.find((w) => w.kind === kind && w.owner === ref.owner && w.repo === ref.repo && w.number === ref.number);
|
|
50807
51121
|
const target = `${ref.owner}/${ref.repo}#${ref.number}`;
|
|
50808
51122
|
if (existing) {
|
|
50809
51123
|
const expiresAt = new Date(existing.expires_at ?? Date.now()).toISOString();
|
|
50810
51124
|
return { id: existing.id, target, alreadyArmed: true, expiresAt, watches: armedPrWatchViews(input.slug) };
|
|
50811
51125
|
}
|
|
50812
51126
|
if (armed.length >= PR_WATCH_MAX_ARMED) {
|
|
50813
|
-
throw new Error(`this thread already watches ${armed.length} pull requests (the limit is ${PR_WATCH_MAX_ARMED}) \u2014 drop one first`);
|
|
51127
|
+
throw new Error(`this thread already watches ${armed.length} pull requests and issues (the limit is ${PR_WATCH_MAX_ARMED}) \u2014 drop one first`);
|
|
50814
51128
|
}
|
|
50815
51129
|
const asked = input.for === void 0 ? PR_WATCH_DEFAULT_FOR_MS : parseAwaitingDurationRaw(input.for);
|
|
50816
51130
|
if (asked === null) {
|
|
@@ -50818,16 +51132,16 @@ ${gapNote}` : input.message;
|
|
|
50818
51132
|
}
|
|
50819
51133
|
const forMs = Math.min(asked, PR_WATCH_FOR_MAX_MS);
|
|
50820
51134
|
const clampedFrom = input.for !== void 0 && asked > PR_WATCH_FOR_MAX_MS ? { clampedFrom: input.for } : {};
|
|
50821
|
-
const probe = await ctx.probePr(ref);
|
|
51135
|
+
const probe = kind === "issue" ? await ctx.probeIssue(ref) : await ctx.probePr(ref);
|
|
50822
51136
|
if (!probe.ok) {
|
|
50823
51137
|
throw new Error(
|
|
50824
|
-
`\`${target}\` cannot be watched \u2014 the server's \`gh\` could not read it: ${probe.reason}. Frizz polls with the \`gh\` of the process it runs as, not yours: check \`gh auth status\` there and that the repo is reachable, then register again. If GitHub itself was briefly down, registering again in a minute is enough.`
|
|
51138
|
+
`\`${target}\` cannot be watched as ${kind === "issue" ? "an issue" : "a pull request"} \u2014 the server's \`gh\` could not read it: ${probe.reason}. Frizz polls with the \`gh\` of the process it runs as, not yours: check \`gh auth status\` there and that the repo is reachable, then register again. If GitHub itself was briefly down, registering again in a minute is enough.`
|
|
50825
51139
|
);
|
|
50826
51140
|
}
|
|
50827
51141
|
const now = Date.now();
|
|
50828
|
-
const id =
|
|
51142
|
+
const id = `${kind === "issue" ? "isw" : "prw"}_${randomUUID16().replace(/-/g, "").slice(0, 12)}`;
|
|
50829
51143
|
ctx.storage.clearThreadDone(input.slug);
|
|
50830
|
-
ctx.storage.armPrWatch({ id, slug: input.slug, owner: ref.owner, repo: ref.repo, number: ref.number, createdAtMs: now, expiresAtMs: now + forMs });
|
|
51144
|
+
ctx.storage.armPrWatch({ id, slug: input.slug, kind, owner: ref.owner, repo: ref.repo, number: ref.number, createdAtMs: now, expiresAtMs: now + forMs });
|
|
50831
51145
|
ctx.board.refresh();
|
|
50832
51146
|
return { id, target, alreadyArmed: false, expiresAt: new Date(now + forMs).toISOString(), ...clampedFrom, watches: armedPrWatchViews(input.slug) };
|
|
50833
51147
|
}
|
|
@@ -50999,7 +51313,7 @@ Say which way you went and why in your write-up, so the human can course-correct
|
|
|
50999
51313
|
id: w.id,
|
|
51000
51314
|
what: `${w.kind === "agent" ? "sub-agent" : "shell"}: ${w.label ? `${w.label} (${w.target})` : w.target}`
|
|
51001
51315
|
})),
|
|
51002
|
-
...armedPrWatchViews(input.slug).map((w) => ({ id: w.id, what:
|
|
51316
|
+
...armedPrWatchViews(input.slug).map((w) => ({ id: w.id, what: `${w.kind === "issue" ? "issue" : "pull request"}: ${w.target}` })),
|
|
51003
51317
|
...ctx.storage.listThreadTimers(input.slug, { armedOnly: true }).map((t) => ({ id: t.id, what: `timer, fires ${new Date(t.fire_at).toISOString()}` }))
|
|
51004
51318
|
];
|
|
51005
51319
|
if (blockingQuestions.length > 0 || blockingWatches.length > 0) {
|
|
@@ -54303,8 +54617,8 @@ var init_runtimes = __esm({
|
|
|
54303
54617
|
init_codex_app_server();
|
|
54304
54618
|
init_frizz_paths();
|
|
54305
54619
|
init_runtime_lease();
|
|
54306
|
-
CLAUDE_AGENT_SDK_VERSION = "0.3.
|
|
54307
|
-
CLAUDE_CODE_VERSION = "2.1.
|
|
54620
|
+
CLAUDE_AGENT_SDK_VERSION = "0.3.271";
|
|
54621
|
+
CLAUDE_CODE_VERSION = "2.1.271";
|
|
54308
54622
|
CODEX_VERSION = CODEX_APP_SERVER_SUPPORTED_VERSION;
|
|
54309
54623
|
RUNTIMES_MODE_ENV = "FRIZZ_RUNTIMES";
|
|
54310
54624
|
RUNTIMES_DIR_ENV = "FRIZZ_RUNTIMES_DIR";
|