frizz-server 0.13.5 → 0.13.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/claude-agent-broker.js +11324 -11170
  2. package/dist/dev-child.js +381 -67
  3. package/package.json +1 -1
  4. package/runtime/cc-worker/bin/frizz-mcp.mjs +131 -5
  5. package/runtime/cc-worker/skills/gh/SKILL.md +4 -2
  6. package/web-dist/assets/{TerminalPane-vtrJrhRI.js → TerminalPane-P7iAI_rU.js} +1 -1
  7. package/web-dist/assets/{abnfDiagram-VRR7QNED-BXqgguHD.js → abnfDiagram-VRR7QNED-B0JxaKjB.js} +1 -1
  8. package/web-dist/assets/architecture-TIHT7OUA-ZNmKXuTL.js +1 -0
  9. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-C3G8MDqx.js → architectureDiagram-ZJ3FMSHR-E8bWaWhF.js} +1 -1
  10. package/web-dist/assets/{blockDiagram-677ZJIJ3-CKFcYZqr.js → blockDiagram-677ZJIJ3-BNg7FcxS.js} +1 -1
  11. package/web-dist/assets/{c4Diagram-LMCZKHZV-CmiqmtLP.js → c4Diagram-LMCZKHZV-BHkry-JG.js} +1 -1
  12. package/web-dist/assets/channel-CTEEc7oT.js +1 -0
  13. package/web-dist/assets/{chunk-32BRIVSS-Db5i6LWw.js → chunk-32BRIVSS-BJLx0sIU.js} +1 -1
  14. package/web-dist/assets/{chunk-52WLFC77-aW1ilNGv.js → chunk-52WLFC77-DmMATfuh.js} +1 -1
  15. package/web-dist/assets/{chunk-C7G6YPKG-GqqNbHig.js → chunk-C7G6YPKG-Ceg42lXA.js} +1 -1
  16. package/web-dist/assets/{chunk-EX3LRPZG--P4v8X90.js → chunk-EX3LRPZG-CAx16zp2.js} +1 -1
  17. package/web-dist/assets/{chunk-FWX5IMBZ-CixF1fU4.js → chunk-FWX5IMBZ-DWb22U8j.js} +2 -2
  18. package/web-dist/assets/{chunk-HOUHSVGY-B2HhGZvF.js → chunk-HOUHSVGY-bKZVUQyT.js} +1 -1
  19. package/web-dist/assets/{chunk-ICXQ74PX-BBE-x4C3.js → chunk-ICXQ74PX-C3i_eW5g.js} +1 -1
  20. package/web-dist/assets/{chunk-MOJQB5TN-BEPO2Wkl.js → chunk-MOJQB5TN-Bt6x6J8D.js} +1 -1
  21. package/web-dist/assets/{chunk-OGEWGWER-D9JtkMIm.js → chunk-OGEWGWER-BwJ73hmt.js} +1 -1
  22. package/web-dist/assets/{chunk-PUDLZKDR-DUS_PVgd.js → chunk-PUDLZKDR-DfcEHpjL.js} +1 -1
  23. package/web-dist/assets/{chunk-Q4XR5HBZ-E-hz3UXp.js → chunk-Q4XR5HBZ-VYeS43tP.js} +1 -1
  24. package/web-dist/assets/{chunk-V7JOEXUC-Bx_zIZQA.js → chunk-V7JOEXUC-Cs1_YKkF.js} +1 -1
  25. package/web-dist/assets/{chunk-VAUOI2AC-B2fl33V0.js → chunk-VAUOI2AC-CCOn0B9D.js} +1 -1
  26. package/web-dist/assets/{chunk-VR4S4FIN-_v2TMpT3.js → chunk-VR4S4FIN-CL_PLrUe.js} +1 -1
  27. package/web-dist/assets/{chunk-WYO6CB5R-DGCmK4zM.js → chunk-WYO6CB5R-Dupt_rk_.js} +1 -1
  28. package/web-dist/assets/{chunk-ZGVPDNZ5-eEh-P7hx.js → chunk-ZGVPDNZ5-UwgE4khW.js} +1 -1
  29. package/web-dist/assets/classDiagram-OUVF2IWQ-D5na7mob.js +1 -0
  30. package/web-dist/assets/classDiagram-v2-EOCWNBFH-D5na7mob.js +1 -0
  31. package/web-dist/assets/{cynefin-VYW2F7L2-BEP3QFtJ.js → cynefin-VYW2F7L2-DGZJYJNa.js} +1 -1
  32. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-i_a9SM7I.js → cynefinDiagram-TSTJHNR4-B34DgCmC.js} +1 -1
  33. package/web-dist/assets/{dagre-VKFMJZFB-DOjX7leJ.js → dagre-VKFMJZFB-C7HODJG3.js} +1 -1
  34. package/web-dist/assets/{diagram-FQU43EPY-u22O3K0y.js → diagram-FQU43EPY-CunP-lqf.js} +1 -1
  35. package/web-dist/assets/{diagram-G47NLZAW-DQCs9ymG.js → diagram-G47NLZAW-C3O84bOk.js} +1 -1
  36. package/web-dist/assets/{diagram-NH7WQ7WH-CM0xr4Ew.js → diagram-NH7WQ7WH-Dv0TR4Ic.js} +1 -1
  37. package/web-dist/assets/{diagram-OA4YK3LP-DSwe8_-_.js → diagram-OA4YK3LP-CUvcZIxp.js} +1 -1
  38. package/web-dist/assets/{diagram-WEI45ONY-R4P40Ge5.js → diagram-WEI45ONY-CwPr97NN.js} +1 -1
  39. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-BLeGHrIO.js → ebnfDiagram-CCIWWBDH-Dq2O1mOC.js} +1 -1
  40. package/web-dist/assets/{erDiagram-Q63AITRT-DDqG5wBf.js → erDiagram-Q63AITRT-C6dJ8Dpx.js} +1 -1
  41. package/web-dist/assets/eventmodeling-45OFAUF4-B8cYCwrU.js +1 -0
  42. package/web-dist/assets/flowDiagram-23GEKE2U-C-EWCSnf.js +1 -0
  43. package/web-dist/assets/{ganttDiagram-NO4QXBWP-nZB1wUEf.js → ganttDiagram-NO4QXBWP-sa-TSsB5.js} +1 -1
  44. package/web-dist/assets/{gitGraph-TEB2WS4Q-k1DZH2Ze.js → gitGraph-TEB2WS4Q-BdZIuH-o.js} +1 -1
  45. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-scE9lsZp.js → gitGraphDiagram-IHSO6WYX-C__OynR9.js} +1 -1
  46. package/web-dist/assets/{index-DccJ_Mii.js → index-CGrszN1x.js} +96 -96
  47. package/web-dist/assets/{info-DKCQHKI2-BdKVaTmg.js → info-DKCQHKI2-BEaDQqRu.js} +1 -1
  48. package/web-dist/assets/{infoDiagram-FWYZ7A6U-C5VyMItr.js → infoDiagram-FWYZ7A6U-RJlw-Bzq.js} +1 -1
  49. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-1j1BQVqM.js → ishikawaDiagram-FXEZZL3T-xop_QrWs.js} +1 -1
  50. package/web-dist/assets/{journeyDiagram-5HDEW3XC-CvssEnRC.js → journeyDiagram-5HDEW3XC-O6nmpoje.js} +1 -1
  51. package/web-dist/assets/{kanban-definition-HUTT4EX6-Ca_rI42D.js → kanban-definition-HUTT4EX6-IXwPs_L0.js} +1 -1
  52. package/web-dist/assets/{line-DQC7_SA6.js → line-BMRguCzE.js} +1 -1
  53. package/web-dist/assets/{mermaid-parser.core-CH_XDp0n.js → mermaid-parser.core-BCXFwFN1.js} +3 -3
  54. package/web-dist/assets/{mermaid.core-CvmBari2.js → mermaid.core-Cqq_9ya3.js} +3 -3
  55. package/web-dist/assets/{mindmap-definition-LN4V7U3C-BrJ260AG.js → mindmap-definition-LN4V7U3C-DEfWfnOg.js} +1 -1
  56. package/web-dist/assets/{packet-7NZHBO7P-C2gQDScs.js → packet-7NZHBO7P-CQGAnngT.js} +1 -1
  57. package/web-dist/assets/{pegDiagram-2B236MQR-bPOaNoIP.js → pegDiagram-2B236MQR-tOrSsHlA.js} +1 -1
  58. package/web-dist/assets/{pie-RZYD4A2V-Zx8C_Xgj.js → pie-RZYD4A2V-C0KP3XF8.js} +1 -1
  59. package/web-dist/assets/{pieDiagram-ENE6RG2P-DMYMm0LG.js → pieDiagram-ENE6RG2P-FzmIWC1K.js} +1 -1
  60. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-ByfVxmA2.js → quadrantDiagram-ABIIQ3AL-N6zuxKqV.js} +1 -1
  61. package/web-dist/assets/{radar-I7S5WNFK-DlT-36Nu.js → radar-I7S5WNFK-CB3WQRMp.js} +1 -1
  62. package/web-dist/assets/{railroad-3IZDKUUU-BV6ldow9.js → railroad-3IZDKUUU-Ck8x0o2o.js} +1 -1
  63. package/web-dist/assets/railroad-abnf-AHOZXSZD-QzrMOCpt.js +1 -0
  64. package/web-dist/assets/railroad-ebnf-EBAXGLYW-CwEnUu4A.js +1 -0
  65. package/web-dist/assets/railroad-peg-LSFZ7HO6-BoDEbNAA.js +1 -0
  66. package/web-dist/assets/{railroadDiagram-RFXS5EU6-369iyQ0K.js → railroadDiagram-RFXS5EU6-6TyF6de4.js} +1 -1
  67. package/web-dist/assets/{requirementDiagram-TGXJPOKE-DPgR3B5t.js → requirementDiagram-TGXJPOKE-D6n7xpc4.js} +1 -1
  68. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-Cp9KGmau.js → sankeyDiagram-HTMAVEWB-B7R6Natf.js} +1 -1
  69. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-CYwpuO_c.js → sequenceDiagram-DBY2YBRQ-CFbbuWF6.js} +1 -1
  70. package/web-dist/assets/{stateDiagram-2N3HPSRC-_FymJQW-.js → stateDiagram-2N3HPSRC-DwXu0kos.js} +1 -1
  71. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-CQctakxF.js +1 -0
  72. package/web-dist/assets/{swimlanes-5IMT3BWC-hjeRmVWf.js → swimlanes-5IMT3BWC-Clie2Qk3.js} +1 -1
  73. package/web-dist/assets/swimlanesDiagram-G3AALYLV-CGOfs7yz.js +8 -0
  74. package/web-dist/assets/{timeline-definition-FHXFAJF6-6mAlMLED.js → timeline-definition-FHXFAJF6-DWDsPzuQ.js} +1 -1
  75. package/web-dist/assets/{treeView-QDETBFTQ-CQiOrDsk.js → treeView-QDETBFTQ-Ddd-Wxpb.js} +1 -1
  76. package/web-dist/assets/{treemap-6X3UGDF4-C6QbRu-J.js → treemap-6X3UGDF4-CuZ3jD4K.js} +1 -1
  77. package/web-dist/assets/{vennDiagram-L72KCM5P-DG9sLom6.js → vennDiagram-L72KCM5P-BoE4aTMb.js} +1 -1
  78. package/web-dist/assets/{wardley-OPB4EBWU-cmwUvJ8F.js → wardley-OPB4EBWU-CfaztQL0.js} +1 -1
  79. package/web-dist/assets/{wardleyDiagram-EHGQE667-DJFJy55B.js → wardleyDiagram-EHGQE667-pxcV16qp.js} +1 -1
  80. package/web-dist/assets/{xychartDiagram-FW5EYKEG-CZad7etX.js → xychartDiagram-FW5EYKEG--d5N7oDL.js} +1 -1
  81. package/web-dist/index.html +1 -1
  82. package/web-dist/assets/architecture-TIHT7OUA-Cs0QGGqy.js +0 -1
  83. package/web-dist/assets/channel-DfJjgFlV.js +0 -1
  84. package/web-dist/assets/classDiagram-OUVF2IWQ-DuB6VVfg.js +0 -1
  85. package/web-dist/assets/classDiagram-v2-EOCWNBFH-DuB6VVfg.js +0 -1
  86. package/web-dist/assets/eventmodeling-45OFAUF4-D0PpSf66.js +0 -1
  87. package/web-dist/assets/flowDiagram-23GEKE2U-7s3GPshr.js +0 -1
  88. package/web-dist/assets/railroad-abnf-AHOZXSZD-DL4tTpQ2.js +0 -1
  89. package/web-dist/assets/railroad-ebnf-EBAXGLYW-DZXU1erj.js +0 -1
  90. package/web-dist/assets/railroad-peg-LSFZ7HO6-CJoMV3fw.js +0 -1
  91. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-2QmL1Mxm.js +0 -1
  92. package/web-dist/assets/swimlanesDiagram-G3AALYLV-BA9G-axI.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
- return `\u23F0 Your watcher on ${ref} has expired and is no longer armed \u2014 nothing on that PR will wake you now.
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 \`mcp__frizz__watch_pr\` 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.`;
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]].has(liveKey(i)));
35315
+ return items.filter((i) => !live[LIVE_SET[i.kind]]?.has(liveKey(i)));
35241
35316
  }
35242
35317
  function liveKey(i) {
35243
- if (i.kind !== "pr") return i.value;
35244
- const ref = parsePrRef(i.value);
35245
- return ref ? githubStatusKey(ref) : i.value;
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
- var PR_REF_RE, LIVE_SET, GITHUB_STATUS_SETTING;
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\`. For anything else stay ACTIVE: dispatch a
35709
- sub-agent to own the wait (its return re-invokes you), or set a timer and name it here.
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 anything else dispatch a SUB-AGENT to own the wait. It runs the watcher
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
- if (seen.has(`github:${w.target}`)) continue;
38244
- seen.add(`github:${w.target}`);
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: `pr watcher ${w.id} expired (${ref})`
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 = `${w.owner}/${w.repo}#${w.number}`;
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 = `${w.owner}/${w.repo}#${w.number}`;
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 ref = parsePrRef(input.target);
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 = `prw_${randomUUID16().replace(/-/g, "").slice(0, 12)}`;
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: `pull request: ${w.target}` })),
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.270";
54307
- CLAUDE_CODE_VERSION = "2.1.270";
54620
+ CLAUDE_AGENT_SDK_VERSION = "0.3.273";
54621
+ CLAUDE_CODE_VERSION = "2.1.273";
54308
54622
  CODEX_VERSION = CODEX_APP_SERVER_SUPPORTED_VERSION;
54309
54623
  RUNTIMES_MODE_ENV = "FRIZZ_RUNTIMES";
54310
54624
  RUNTIMES_DIR_ENV = "FRIZZ_RUNTIMES_DIR";