frizz 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/claude-agent-broker.js +141 -12
  2. package/dist/dev-child.js +869 -333
  3. package/dist/frizz.js +81 -19
  4. package/package.json +1 -1
  5. package/runtime/cc-worker/DECISIONS.md +11 -0
  6. package/runtime/cc-worker/bin/frizz-mcp.mjs +58 -24
  7. package/web-dist/assets/{TerminalPane-CNFymHXK.js → TerminalPane-XNji7D_c.js} +1 -1
  8. package/web-dist/assets/{abnfDiagram-VRR7QNED-DNBrcJfV.js → abnfDiagram-VRR7QNED-gPeb-B7g.js} +1 -1
  9. package/web-dist/assets/architecture-TIHT7OUA-BsgSgexh.js +1 -0
  10. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-SXVydqly.js → architectureDiagram-ZJ3FMSHR-DV69ktbl.js} +1 -1
  11. package/web-dist/assets/{blockDiagram-677ZJIJ3-CgI1GSdx.js → blockDiagram-677ZJIJ3-D8vw5eim.js} +1 -1
  12. package/web-dist/assets/{c4Diagram-LMCZKHZV-KDmJm2tG.js → c4Diagram-LMCZKHZV--9PLGn-L.js} +1 -1
  13. package/web-dist/assets/channel-ClLnncxh.js +1 -0
  14. package/web-dist/assets/{chunk-32BRIVSS-j3eOjq4Z.js → chunk-32BRIVSS-D4BSk0FZ.js} +1 -1
  15. package/web-dist/assets/{chunk-52WLFC77-DZHfa78H.js → chunk-52WLFC77-WNcmTkcR.js} +1 -1
  16. package/web-dist/assets/{chunk-C7G6YPKG-CiB5uo24.js → chunk-C7G6YPKG-BDlcLa5W.js} +1 -1
  17. package/web-dist/assets/{chunk-EX3LRPZG-B35Inyjz.js → chunk-EX3LRPZG-CFnqFt7s.js} +1 -1
  18. package/web-dist/assets/{chunk-FWX5IMBZ-D2sRvM5U.js → chunk-FWX5IMBZ-Ci9LgOOg.js} +2 -2
  19. package/web-dist/assets/{chunk-HOUHSVGY-D247PDv6.js → chunk-HOUHSVGY-BTD_X5OP.js} +1 -1
  20. package/web-dist/assets/{chunk-ICXQ74PX-Bu3Tb6Ec.js → chunk-ICXQ74PX-D2Y3Ys5K.js} +1 -1
  21. package/web-dist/assets/{chunk-MOJQB5TN-C_lQy4on.js → chunk-MOJQB5TN-C-GDt7vC.js} +1 -1
  22. package/web-dist/assets/{chunk-OGEWGWER-DU5ZBedR.js → chunk-OGEWGWER-DvnPoUnz.js} +1 -1
  23. package/web-dist/assets/{chunk-PUDLZKDR-DWRT7ztS.js → chunk-PUDLZKDR-C3AymM-j.js} +1 -1
  24. package/web-dist/assets/{chunk-Q4XR5HBZ-CCX4AK_n.js → chunk-Q4XR5HBZ-DmyBNAl6.js} +1 -1
  25. package/web-dist/assets/{chunk-V7JOEXUC-VbixbkDG.js → chunk-V7JOEXUC-DTuPUeRa.js} +1 -1
  26. package/web-dist/assets/{chunk-VAUOI2AC-PJvChxMA.js → chunk-VAUOI2AC-CcW8PviB.js} +1 -1
  27. package/web-dist/assets/{chunk-VR4S4FIN-BjsMfxj8.js → chunk-VR4S4FIN-0tm6n83G.js} +1 -1
  28. package/web-dist/assets/{chunk-WYO6CB5R-Bcb97r3w.js → chunk-WYO6CB5R-CH3ADqeI.js} +1 -1
  29. package/web-dist/assets/{chunk-ZGVPDNZ5-CT2sZlhT.js → chunk-ZGVPDNZ5-BHFnlpXH.js} +1 -1
  30. package/web-dist/assets/classDiagram-OUVF2IWQ-CwLnPgud.js +1 -0
  31. package/web-dist/assets/classDiagram-v2-EOCWNBFH-CwLnPgud.js +1 -0
  32. package/web-dist/assets/{cynefin-VYW2F7L2-dUpO4u0w.js → cynefin-VYW2F7L2-DtFCLOIN.js} +1 -1
  33. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-BQZ3UIsx.js → cynefinDiagram-TSTJHNR4-llFTHp2q.js} +1 -1
  34. package/web-dist/assets/{dagre-VKFMJZFB-K-oO8w51.js → dagre-VKFMJZFB-CvBRVl_k.js} +1 -1
  35. package/web-dist/assets/{diagram-FQU43EPY-Cr47GJ_S.js → diagram-FQU43EPY-D1QXo6ri.js} +1 -1
  36. package/web-dist/assets/{diagram-G47NLZAW-BbWzSkoZ.js → diagram-G47NLZAW-2pZew-Bk.js} +1 -1
  37. package/web-dist/assets/{diagram-NH7WQ7WH-BGJJBgGM.js → diagram-NH7WQ7WH-Ch9JRwGN.js} +1 -1
  38. package/web-dist/assets/{diagram-OA4YK3LP-2W7aunEN.js → diagram-OA4YK3LP-BIbrLf02.js} +1 -1
  39. package/web-dist/assets/{diagram-WEI45ONY-D-4ITFI6.js → diagram-WEI45ONY-D3KtL2s-.js} +1 -1
  40. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-CxUcx103.js → ebnfDiagram-CCIWWBDH-D829sKG_.js} +1 -1
  41. package/web-dist/assets/{erDiagram-Q63AITRT-Cf_5SWAE.js → erDiagram-Q63AITRT-Cj3Grovg.js} +1 -1
  42. package/web-dist/assets/eventmodeling-45OFAUF4-CzZyv3HA.js +1 -0
  43. package/web-dist/assets/flowDiagram-23GEKE2U-rQ7PRoGL.js +1 -0
  44. package/web-dist/assets/{ganttDiagram-NO4QXBWP-CVZI8Ywa.js → ganttDiagram-NO4QXBWP-CGHaXF-g.js} +1 -1
  45. package/web-dist/assets/{gitGraph-TEB2WS4Q-BsiGpR9l.js → gitGraph-TEB2WS4Q-BbxeaotX.js} +1 -1
  46. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-DVym_4l1.js → gitGraphDiagram-IHSO6WYX-C4WHE0cI.js} +1 -1
  47. package/web-dist/assets/index-BpCFdr7Z.css +1 -0
  48. package/web-dist/assets/index-CiaKj-0V.js +506 -0
  49. package/web-dist/assets/{info-DKCQHKI2-DYcsKFv-.js → info-DKCQHKI2-3fKpn7Qr.js} +1 -1
  50. package/web-dist/assets/{infoDiagram-FWYZ7A6U-hjVxlrTT.js → infoDiagram-FWYZ7A6U-CDQl4JMv.js} +1 -1
  51. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-B0DKOPSS.js → ishikawaDiagram-FXEZZL3T-CDKjMW-u.js} +1 -1
  52. package/web-dist/assets/{journeyDiagram-5HDEW3XC-Cohibc5O.js → journeyDiagram-5HDEW3XC-BOsabu1Z.js} +1 -1
  53. package/web-dist/assets/{kanban-definition-HUTT4EX6-_6Zr31vM.js → kanban-definition-HUTT4EX6-KTuh-6eN.js} +1 -1
  54. package/web-dist/assets/{line-B14EZI6M.js → line-KaxbYSN3.js} +1 -1
  55. package/web-dist/assets/{mermaid-parser.core-DHkc6cqw.js → mermaid-parser.core-B53huyyd.js} +3 -3
  56. package/web-dist/assets/{mermaid.core-DuTvdsKf.js → mermaid.core-Bz6B5ouC.js} +3 -3
  57. package/web-dist/assets/{mindmap-definition-LN4V7U3C-DPRF9pW2.js → mindmap-definition-LN4V7U3C-Bb7T7B3a.js} +1 -1
  58. package/web-dist/assets/{packet-7NZHBO7P-Ci8oCcGM.js → packet-7NZHBO7P-w3lynC8r.js} +1 -1
  59. package/web-dist/assets/{pegDiagram-2B236MQR-Cr3ctC6e.js → pegDiagram-2B236MQR-nQ7c2Z03.js} +1 -1
  60. package/web-dist/assets/{pie-RZYD4A2V-EHgA307y.js → pie-RZYD4A2V-B3Yko5Wb.js} +1 -1
  61. package/web-dist/assets/{pieDiagram-ENE6RG2P-UW6Bmu6F.js → pieDiagram-ENE6RG2P-CN4Lp5HG.js} +1 -1
  62. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-D6tAsRj9.js → quadrantDiagram-ABIIQ3AL-3QTB92dH.js} +1 -1
  63. package/web-dist/assets/{radar-I7S5WNFK-CAQqojcd.js → radar-I7S5WNFK-8ZsXt0FI.js} +1 -1
  64. package/web-dist/assets/{railroad-3IZDKUUU-BZsAOTrm.js → railroad-3IZDKUUU-C4IKxzEZ.js} +1 -1
  65. package/web-dist/assets/railroad-abnf-AHOZXSZD-g2si5iUv.js +1 -0
  66. package/web-dist/assets/railroad-ebnf-EBAXGLYW-BikkLG6y.js +1 -0
  67. package/web-dist/assets/railroad-peg-LSFZ7HO6-Dv4rDehz.js +1 -0
  68. package/web-dist/assets/{railroadDiagram-RFXS5EU6-bSpff55H.js → railroadDiagram-RFXS5EU6-DrGnmjpD.js} +1 -1
  69. package/web-dist/assets/{requirementDiagram-TGXJPOKE-DIAYvXoB.js → requirementDiagram-TGXJPOKE-Bh9XNuxa.js} +1 -1
  70. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-DEnsSz3F.js → sankeyDiagram-HTMAVEWB-CXy3ra5-.js} +1 -1
  71. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-DsG7eb9R.js → sequenceDiagram-DBY2YBRQ-B7ESCT52.js} +1 -1
  72. package/web-dist/assets/{stateDiagram-2N3HPSRC-DT3XNwjo.js → stateDiagram-2N3HPSRC-DFr9k5Xv.js} +1 -1
  73. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-BQUd0MEa.js +1 -0
  74. package/web-dist/assets/{swimlanes-5IMT3BWC-B5hZwgRo.js → swimlanes-5IMT3BWC-B2RKoz1q.js} +1 -1
  75. package/web-dist/assets/swimlanesDiagram-G3AALYLV-GWn4zXxj.js +8 -0
  76. package/web-dist/assets/{timeline-definition-FHXFAJF6-BVTsjkfu.js → timeline-definition-FHXFAJF6-KpTqaG5g.js} +1 -1
  77. package/web-dist/assets/{treeView-QDETBFTQ-M6HmP4bB.js → treeView-QDETBFTQ-oB1a6ikv.js} +1 -1
  78. package/web-dist/assets/{treemap-6X3UGDF4-C60TppfC.js → treemap-6X3UGDF4-DO8OvvZg.js} +1 -1
  79. package/web-dist/assets/{vennDiagram-L72KCM5P-Dv1y3g4S.js → vennDiagram-L72KCM5P-BgTjK4_q.js} +1 -1
  80. package/web-dist/assets/{wardley-OPB4EBWU-D0lMJMWt.js → wardley-OPB4EBWU-CGyjn939.js} +1 -1
  81. package/web-dist/assets/{wardleyDiagram-EHGQE667-B1E5NtZY.js → wardleyDiagram-EHGQE667-CM_HxruS.js} +1 -1
  82. package/web-dist/assets/{xychartDiagram-FW5EYKEG-oA91YOvh.js → xychartDiagram-FW5EYKEG-vamHu0jN.js} +1 -1
  83. package/web-dist/index.html +2 -2
  84. package/web-dist/assets/architecture-TIHT7OUA-BSUDKbro.js +0 -1
  85. package/web-dist/assets/channel-DaCOnTxr.js +0 -1
  86. package/web-dist/assets/classDiagram-OUVF2IWQ-BlylbZXV.js +0 -1
  87. package/web-dist/assets/classDiagram-v2-EOCWNBFH-BlylbZXV.js +0 -1
  88. package/web-dist/assets/eventmodeling-45OFAUF4-DxKQnZNV.js +0 -1
  89. package/web-dist/assets/flowDiagram-23GEKE2U-Cdz9YM2Z.js +0 -1
  90. package/web-dist/assets/index-CYzJK7u0.js +0 -497
  91. package/web-dist/assets/index-CovJDCWc.css +0 -1
  92. package/web-dist/assets/railroad-abnf-AHOZXSZD-_2rSHfPz.js +0 -1
  93. package/web-dist/assets/railroad-ebnf-EBAXGLYW-xM3eAMFz.js +0 -1
  94. package/web-dist/assets/railroad-peg-LSFZ7HO6-cZlBUsED.js +0 -1
  95. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-C06v0Ltu.js +0 -1
  96. package/web-dist/assets/swimlanesDiagram-G3AALYLV-CXyN7ZyO.js +0 -8
package/dist/frizz.js CHANGED
@@ -12462,7 +12462,7 @@ var init_relay_protocol = __esm({
12462
12462
  function fallbackPort(base) {
12463
12463
  return base + 1e4;
12464
12464
  }
12465
- 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, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, ASK_MAX_PER_CALL, ASK_MAX_OPEN, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketClientMsg, ProjectCard, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12465
+ 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, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, 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, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12466
12466
  var init_src = __esm({
12467
12467
  "packages/shared/src/index.ts"() {
12468
12468
  "use strict";
@@ -12742,8 +12742,13 @@ var init_src = __esm({
12742
12742
  subAgentCount: external_exports.number().default(0),
12743
12743
  // total live sub-agents (≥ subAgents.length)
12744
12744
  bgShells: external_exports.array(CompletionHoldOp).default([]),
12745
- bgShellCount: external_exports.number().default(0)
12745
+ bgShellCount: external_exports.number().default(0),
12746
12746
  // total live background shells (≥ bgShells.length)
12747
+ // The worker is DEAD and its recorded turn never ended — cut off by a reboot, a signal or a crash
12748
+ // mid-tool-call (router.cutOffHold). Nothing is running, so nothing will be killed; the hold exists
12749
+ // because the thread is not finished and Done would say it was. Optional rather than defaulted so it
12750
+ // is absent (⇒ false) on every hold that is not this one, and a pre-change client reads them unchanged.
12751
+ cutOff: external_exports.boolean().optional()
12747
12752
  });
12748
12753
  AskOption = external_exports.object({
12749
12754
  label: external_exports.string(),
@@ -12908,7 +12913,8 @@ var init_src = __esm({
12908
12913
  * known, so a timer row always carries this. */
12909
12914
  timer: external_exports.object({ fireAt: external_exports.string(), prompt: external_exports.string() }).strict().optional()
12910
12915
  }).strict();
12911
- PR_WATCH_DEFAULT_FOR_MS = 6 * 60 * 60 * 1e3;
12916
+ PR_WATCH_FOR_MAX_MS = 365 * 24 * 60 * 60 * 1e3;
12917
+ PR_WATCH_DEFAULT_FOR_MS = 30 * 24 * 60 * 60 * 1e3;
12912
12918
  PrWatchView = external_exports.object({
12913
12919
  id: external_exports.string(),
12914
12920
  /** `owner/repo#N`, normalized — the same string the board's row and the status book are keyed by. */
@@ -12923,13 +12929,18 @@ var init_src = __esm({
12923
12929
  /** `owner/repo#123` or a PR URL. Parsed server-side; an unparseable ref is refused rather than stored,
12924
12930
  * because a watcher that can never fire is worse than none — the worker rests believing it is covered. */
12925
12931
  target: external_exports.string().trim().min(1).max(200),
12926
- /** How long to watch, as a DURATION (`30m`, `2h`, `3d` — parseAwaitingDuration).
12932
+ /** How long to watch, as a DURATION (`2h`, `3d`, `180d` — parseAwaitingDurationRaw, capped at
12933
+ * PR_WATCH_FOR_MAX_MS).
12927
12934
  *
12928
12935
  * A PR nobody ever reviews would otherwise be polled forever, and a thread parked on it would wait
12929
12936
  * forever with it — the same unbounded wait the awaiting fence's `for:` closes, one level down. A
12930
12937
  * duration rather than an instant for the same reason it is one there: it cannot be written in the
12931
12938
  * past (maintainer 2026-08-15, asking for it explicitly).
12932
12939
  *
12940
+ * BOUNDED IS NOT THE SAME AS SHORT, and conflating them is what made this field a noise source. The
12941
+ * bound exists so a forgotten watcher stops polling eventually, which a year satisfies exactly as
12942
+ * well as a day — and a day spent it against every external PR, which does not move on that clock.
12943
+ *
12933
12944
  * REQUIRED BY THE TOOL, OPTIONAL ON THE WIRE, and the asymmetry is deliberate. A worker's MCP server
12934
12945
  * outlives every frizz restart, so a session dispatched before this existed still holds a binary that
12935
12946
  * cannot send it — and making the RPC reject that would break `watch_pr` outright for every thread
@@ -12944,6 +12955,12 @@ var init_src = __esm({
12944
12955
  /** True when this exact PR was ALREADY watched by this thread, so the call registered nothing new.
12945
12956
  * Re-registering after a compaction is the common case, and a duplicate would double every wake. */
12946
12957
  alreadyArmed: external_exports.boolean(),
12958
+ /** When this watcher runs out. Read back so the worker sees the duration it ACTUALLY got rather than
12959
+ * the one it asked for — on a re-registration that is the original expiry, which the call left alone. */
12960
+ expiresAt: external_exports.string(),
12961
+ /** The `for:` the worker wrote, present ONLY when it exceeded the ceiling and was capped. A clamp
12962
+ * nobody is told about is a worker resting on a year of coverage it does not have. */
12963
+ clampedFrom: external_exports.string().optional(),
12947
12964
  watches: external_exports.array(PrWatchView)
12948
12965
  }).strict();
12949
12966
  DropOwnPrWatchInput = external_exports.object({
@@ -12970,7 +12987,11 @@ var init_src = __esm({
12970
12987
  * that is both the stronger check and the one that catches the real mistake: naming a sub-agent under
12971
12988
  * `kind: "shell"`, which is what put two sub-agents under a "Background shells" heading on 2026-08-26. */
12972
12989
  target: external_exports.string().trim().min(1).max(200),
12973
- /** REQUIRED, and a DURATION (`30m`, `2h`, `3d` — parseAwaitingDuration, capped at 24h).
12990
+ /** REQUIRED, and a DURATION (`30m`, `2h`, `3d` — parseAwaitingDurationRaw, capped at 24h).
12991
+ *
12992
+ * STILL A DAY, where a PR watcher now gets a year: this names a shell or a sub-agent, which lives
12993
+ * inside the session that launched it, so a wait standing longer than a day is one whose target is
12994
+ * almost certainly already gone.
12974
12995
  *
12975
12996
  * No default and no wire-level optionality, unlike `AddOwnPrWatchInput.for`: that field is optional
12976
12997
  * only to keep working for sessions dispatched before it existed, and this RPC has no such sessions.
@@ -12995,6 +13016,10 @@ var init_src = __esm({
12995
13016
  /** True when this exact (kind, target) was already watched, so the call registered nothing new and the
12996
13017
  * existing expiry stands. Re-registering after a wake or a compaction is the common, correct case. */
12997
13018
  alreadyArmed: external_exports.boolean(),
13019
+ /** The `for:` the worker wrote, present ONLY when it exceeded the ceiling and was capped — the twin of
13020
+ * `AddOwnPrWatchResult.clampedFrom`, for the same reason: a silent clamp is a worker resting on
13021
+ * coverage it does not have. */
13022
+ clampedFrom: external_exports.string().optional(),
12998
13023
  watches: external_exports.array(OwnWatchView)
12999
13024
  }).strict();
13000
13025
  DropOwnWatchInput = external_exports.object({
@@ -13006,25 +13031,38 @@ var init_src = __esm({
13006
13031
  watches: external_exports.array(OwnWatchView)
13007
13032
  }).strict();
13008
13033
  AskQuestionKind = external_exports.enum(["question", "multi"]);
13009
- ASK_MAX_PER_CALL = 4;
13010
- ASK_MAX_OPEN = 12;
13011
13034
  AskedOptionSchema = external_exports.lazy(() => external_exports.object({
13012
- label: external_exports.string().trim().min(1).max(120),
13013
- description: external_exports.string().trim().max(400).optional(),
13035
+ // 400, not the 120 it launched with (2026-09-03): a label is a chip line, but "Post the drafted
13036
+ // comment as written" plus its qualifier runs long, and the fence has no cap at all. The answer's
13037
+ // `chosen` carries labels, so its per-item cap matches this one.
13038
+ label: external_exports.string().trim().min(1).max(400),
13039
+ // 20000, not the 4000 the retired `preview` had (2026-09-03): a description is allowed to BE the rich
13040
+ // body — a diff, a drafted comment, a table — and a real diff exceeds 4000.
13041
+ description: external_exports.string().trim().max(2e4).optional(),
13014
13042
  recommended: external_exports.boolean().optional(),
13015
13043
  preview: external_exports.string().max(4e3).optional(),
13016
- followUps: external_exports.array(AskedQuestionSchema).max(ASK_MAX_PER_CALL).optional()
13044
+ // No count cap, like `options` (2026-09-03); the tree is bounded by ASK_MAX_DEPTH instead.
13045
+ followUps: external_exports.array(AskedQuestionSchema).optional()
13017
13046
  }).strict());
13018
13047
  AskedQuestionSchema = external_exports.lazy(() => external_exports.object({
13019
- question: external_exports.string().trim().min(1).max(600),
13048
+ // 4000, not the 600 it launched with (2026-09-03): this field is the whole card context — everything
13049
+ // the human needs to answer cold — and a question that carries its own evidence needs the room. The
13050
+ // answer restates it, so QuestionAnswerSchema's cap matches.
13051
+ question: external_exports.string().trim().min(1).max(4e3),
13020
13052
  header: external_exports.string().trim().max(24).optional(),
13021
13053
  kind: AskQuestionKind,
13022
13054
  danger: external_exports.boolean().optional(),
13023
- options: external_exports.array(AskedOptionSchema).max(8).optional()
13055
+ // UNBOUNDED, deliberately. This carried `.max(8)` from launch until 2026-09-03, when the maintainer
13056
+ // asked for the cap to go ("allow arbitrary numbers of options"): a `multi` over a long list — which
13057
+ // gates to run, which of twenty findings to act on — is a real shape, and the card letters past 26
13058
+ // (`AA.`) already. The count is the worker's to choose; the answer's `chosen` is unbounded to match.
13059
+ options: external_exports.array(AskedOptionSchema).optional()
13024
13060
  }).strict());
13025
13061
  AskInput = external_exports.object({
13026
13062
  slug: ThreadSlug,
13027
- questions: external_exports.array(AskedQuestionSchema).min(1).max(ASK_MAX_PER_CALL)
13063
+ // No cap on the count (four until 2026-09-03): the tool's own text says "several at once is one
13064
+ // call — register them together", and a cap here told a worker with six to batch and then refused it.
13065
+ questions: external_exports.array(AskedQuestionSchema).min(1)
13028
13066
  }).strict();
13029
13067
  RegisteredQuestionView = external_exports.object({
13030
13068
  /** Minted by frizz. The worker never chose it, which is why an answer RESTATES the question text —
@@ -13048,17 +13086,19 @@ var init_src = __esm({
13048
13086
  }).strict();
13049
13087
  QuestionAnswerSchema = external_exports.lazy(() => external_exports.object({
13050
13088
  questionId: external_exports.string().min(1).max(64),
13051
- question: external_exports.string().min(1).max(600),
13052
- chosen: external_exports.array(external_exports.string().max(200)).max(8),
13089
+ question: external_exports.string().min(1).max(4e3),
13090
+ // No count cap: a `multi` may carry any number of options (see AskedQuestionSchema), and the human
13091
+ // may pick every one of them. The per-item cap is the option label's.
13092
+ chosen: external_exports.array(external_exports.string().max(400)),
13053
13093
  text: external_exports.string().max(8e3).optional(),
13054
- followUps: external_exports.array(QuestionAnswerSchema).max(ASK_MAX_PER_CALL).optional()
13094
+ followUps: external_exports.array(QuestionAnswerSchema).optional()
13055
13095
  }).strict());
13056
13096
  AnswerQuestionsInput = external_exports.object({
13057
13097
  slug: ThreadSlug,
13058
13098
  /** SUBMITTED AS A UNIT. The card sends whatever was answered in one call, because a per-question send
13059
13099
  * would half-wake a turn: the worker would come back to a payload it cannot act on and would have to
13060
13100
  * ask again for the rest. */
13061
- answers: external_exports.array(QuestionAnswerSchema).min(1).max(ASK_MAX_OPEN)
13101
+ answers: external_exports.array(QuestionAnswerSchema).min(1)
13062
13102
  }).strict();
13063
13103
  AnswerQuestionsResult = external_exports.object({
13064
13104
  /** The ids that were open and are now answered. An id that was already settled is silently absent
@@ -13068,7 +13108,7 @@ var init_src = __esm({
13068
13108
  }).strict();
13069
13109
  DismissQuestionsInput = external_exports.object({
13070
13110
  slug: ThreadSlug,
13071
- ids: external_exports.array(external_exports.string().min(1).max(64)).min(1).max(ASK_MAX_OPEN)
13111
+ ids: external_exports.array(external_exports.string().min(1).max(64)).min(1)
13072
13112
  }).strict();
13073
13113
  DismissQuestionsResult = external_exports.object({
13074
13114
  dismissed: external_exports.array(external_exports.string()),
@@ -13248,6 +13288,11 @@ var init_src = __esm({
13248
13288
  // AUTO-bump (the scheduler resumes the agent with exactly this text) rather than a reminder, which is
13249
13289
  // the distinction the held row's tooltip renders. Absent ⇒ the card merely re-surfaces.
13250
13290
  snoozePrompt: external_exports.string().optional(),
13291
+ // The instant the human PINNED this thread (absent = not pinned). A pinned thread leaves the rail's
13292
+ // band system entirely — Rested/Active/Snoozed/Done — and holds the pinned band at the very top, in
13293
+ // this instant's order. Lifecycle metadata like the snooze: written only by the pin/unpin verb, and
13294
+ // it outranks every derived state (a pinned thread that finishes stays pinned).
13295
+ pinnedAt: external_exports.string().optional(),
13251
13296
  /** The EVENT snooze on the resting card is armed for this exact rest — the human has said "hide this
13252
13297
  * until something reports". Distinct from `snoozedUntil`, which is a wall-clock park on the whole
13253
13298
  * thread: this one has no deadline and clears itself when the thread comes to a NEW rest.
@@ -13493,6 +13538,12 @@ var init_src = __esm({
13493
13538
  // Code caps to the model's real window. Optional so an old blob parses; defaultSettings pins 500_000
13494
13539
  // (maintainer 2026-08-26: "a default compaction window of 500k by default").
13495
13540
  autoCompactWindow: external_exports.number().int().positive().optional(),
13541
+ // The prompt-cache tier a NEW Claude worker writes to. "auto" leaves the CLI's own choice (1h on a
13542
+ // subscription); "5m" and "1h" reach the worker as CLAUDE_CODE_PROMPT_CACHE_TTL (and the sub-agent
13543
+ // twin). The 1h tier bills a cache write at 2x input against 1.25x for 5m; on 2026-09-03 cache
13544
+ // writes were 51% of a day's spend while the entries were invalidated every 15–30 minutes anyway
13545
+ // (see claudePromptCacheEnv). Optional so an old blob parses; defaultSettings pins "auto".
13546
+ promptCacheTtl: external_exports.enum(["auto", "5m", "1h"]).optional(),
13496
13547
  // The GitHub batch-dispatch prompt template (the picker's per-item worker prompt). Optional: when
13497
13548
  // unset OR blank the server falls back to its exported DEFAULT_GITHUB_PROMPT. Substitution tokens
13498
13549
  // the server fills: {repo} {n} {title} {url} {labels} {body}. The leading `THREAD: <slug>` tag is
@@ -13631,6 +13682,11 @@ var init_src = __esm({
13631
13682
  // can never leave an armed prompt behind.
13632
13683
  prompt: SnoozePrompt.nullable().optional()
13633
13684
  }).strict();
13685
+ SetThreadPinnedInput = external_exports.object({
13686
+ slug: ThreadSlug,
13687
+ sessionId: external_exports.string().min(1),
13688
+ pinned: external_exports.boolean()
13689
+ }).strict();
13634
13690
  SetThreadRecurringPromptInput = external_exports.object({
13635
13691
  slug: ThreadSlug,
13636
13692
  sessionId: external_exports.string().min(1),
@@ -14311,10 +14367,16 @@ ${DISPATCH_TASK_BANNER}
14311
14367
  cursor: TranscriptPageCursor
14312
14368
  }).strict();
14313
14369
  SocketTranscriptSlug = ThreadSlug;
14314
- SocketClientMsg = external_exports.discriminatedUnion("t", [
14370
+ SocketFilePath = external_exports.string().min(1).max(4096);
14371
+ SocketTranscriptClientMsg = external_exports.discriminatedUnion("t", [
14315
14372
  external_exports.object({ t: external_exports.literal("sub"), topic: external_exports.literal("transcript"), slug: SocketTranscriptSlug }).strict(),
14316
14373
  external_exports.object({ t: external_exports.literal("unsub"), topic: external_exports.literal("transcript"), slug: SocketTranscriptSlug }).strict()
14317
14374
  ]);
14375
+ SocketFileClientMsg = external_exports.discriminatedUnion("t", [
14376
+ external_exports.object({ t: external_exports.literal("sub"), topic: external_exports.literal("file"), path: SocketFilePath }).strict(),
14377
+ external_exports.object({ t: external_exports.literal("unsub"), topic: external_exports.literal("file"), path: SocketFilePath }).strict()
14378
+ ]);
14379
+ SocketClientMsg = external_exports.union([SocketTranscriptClientMsg, SocketFileClientMsg]);
14318
14380
  ProjectCard = external_exports.object({
14319
14381
  id: external_exports.string(),
14320
14382
  slug: external_exports.string(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frizz",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "description": "A local web UI for running many coding agents at once",
6
6
  "keywords": [
@@ -1147,3 +1147,14 @@ Two corrections from one maintainer thread, both about Frizz telling the worker
1147
1147
 
1148
1148
  - **`recurring_prompt` is renamed `goal`.** The board panel, the footer mark and the delivered trailer have all said "Goal" since 2026-08-11; only the MCP tool still carried the mechanism's name. Maintainer: *"I don't think the tool is even called Recurring Prompt anymore, is it?"* — then *"Rename it."* Only the TOOL name and the worker-facing wording changed: the RPC procedures (`setOwnThreadRecurringPrompt`, `getOwnThreadRecurringPrompt`) and the `session.recurring_*` columns keep their names, because renaming the procedure is exactly what stranded every in-flight worker the last time (see the legacy aliases in `@frizz/shared`). No alias tool is listed for the old name — Claude Code rejects a tool name it has not listed, so an alias would have to be a visible second tool forever — and the new tool's description names its former name once, for a resumed worker whose summary still says `recurring_prompt`.
1149
1149
  - **Every surface that mentions the scratch directory stopped prescribing the notes-plus-arming arrangement.** The 2026-08-06 entry above set the rule "every surface that mentions the directory also mentions `post_compaction: true`", and it produced the effect the maintainer noticed: workers armed the post-compaction goal constantly, 49 of the 65 armed prompts in the live db copying the contract's template sentence verbatim. Maintainer: *"this is a very opinionated thing, and I don't think we should be pushing it as the thing to do. We should just be saying that the scratch directory is available to it if it wants it. We can also be saying that it can set these goal hooks."* The contract's scratch sections, the dispatch preamble, `scratchpadOrientation`, the five `scratchpad.mjs` hook texts and the tool's own `post_compaction` wording now say the directory and the goal hooks EXIST and leave the choice to the worker. The literal `post_compaction: true` stays on each surface so the capability is still discoverable; the nudge hook keeps its ~60k-token cadence with the informational wording.
1150
+
1151
+ ## 2026-09-03: a worker mounts ONLY what its project declares — `--strict-mcp-config`, with the project's `.mcp.json` carried inline
1152
+
1153
+ Maintainer, the evening of two memory crashes (a jetsam spiral into a watchdog panic at 19:50, a forced power-off at 20:52): *"Very weird that Frizz eagerly spawns Chrome DevTools."* It was not Frizz. It was the CLI's own discovery doing exactly what the 2026-08-26 entry above asked of it — in every worker at once.
1154
+
1155
+ Measured on the maintainer's machine 2026-09-02, nine live workers: the user-scope `chrome-real` (`claude mcp add --scope user`, an npx chrome-devtools-mcp) and each project's `chrome-devtools` (`.mcp.json`) both booted in EVERY worker — 18 browser servers, 36 processes, 4.2 GB, and 61 thirty-second MCP connection timeouts inside the crash window. The 2026-08-26 line *"Frizz's `--mcp-config` ADDS to whatever the CLI discovered rather than replacing it (frizz never passes `--strict-mcp-config`)"* was written so a project and an operator could each bring a browser. It also meant a fleet multiplied the operator's personal servers by N.
1156
+
1157
+ - **Every worker now launches under `--strict-mcp-config`** (`strictMcpConfig: true` on the SDK path, the flag on the argv path) and is handed its whole MCP surface inline, lowest precedence first: the operator's REMOTE user-scope servers (`type: http`/`sse` in `~/.claude.json` — a URL costs no process), the project's `.mcp.json` filtered by the same approval the CLI applies (`enableAllProjectMcpServers` / `enabledMcpjsonServers` / `disabledMcpjsonServers`, every layer unioned), then the `frizz` server, which wins a name collision. `packages/server/src/backend/project-mcp-servers.ts` is the rule and the reasons; the daemon reads it at every fork, so a `.mcp.json` edit reaches the next dispatch as it reaches the next plain `claude`.
1158
+ - **User-scope STDIO servers are the one thing a worker no longer inherits.** `claude mcp add --scope user` still reaches the operator's own sessions. A project that wants one in its fleet declares it in its `.mcp.json` — the scope that means "this repo's sessions".
1159
+ - **Nothing about settings changed.** `settingSources` stays `user,project,local` (the 2026-08-16 entry): auth, permissions, hooks, plugins and `CLAUDE.md` all still reach a worker. Only MCP discovery is replaced, and only with what discovery would have produced minus the operator's local processes.
1160
+ - **What this does not fix.** Twelve threads restarted at once are still ~9 processes and ~1 GB each before any of them runs a build; the crash was the aggregate, and this removes its largest avoidable term. A dispatch-time back-pressure check is the follow-up.
@@ -271,7 +271,13 @@ const WATCH_PR = {
271
271
  "wait at all. This tool is the wait.\n\n" +
272
272
  "THE ```awaiting FENCE IS STILL WORTH WRITING, and it is a different job: it is how you come to REST " +
273
273
  "without frizz asking you for a handoff, and how the human sees what you are waiting for. Register " +
274
- "the watcher with this tool, then name the same PR in your fence's `prs:` list.\n\n" +
274
+ "the watcher with this tool, then name the same PR in your fence's `prs:` list — and give the fence " +
275
+ "the same long `for:` you gave the watcher, or the fence expires first and bumps you anyway.\n\n" +
276
+ "GIVE AN EXTERNAL PR A LONG `for` — MONTHS, up to a year. A pull request into a repo nobody here " +
277
+ "controls moves on its maintainers' clock, not yours, and a short watcher on one expires against a " +
278
+ "PR that has not changed: a wake with nothing in it, and a re-arm. Long costs nothing — real " +
279
+ "activity still wakes you the instant it lands, and the human snoozes or archives the thread if " +
280
+ "they want it off the board.\n\n" +
275
281
  "REGISTERING IS IDEMPOTENT per pull request: asking twice returns the SAME id and tells you it was " +
276
282
  "already armed, so re-registering after a compaction is safe and is the right instinct. Use `list` " +
277
283
  "when you want to know what you are holding without changing anything — it answers with each PR's " +
@@ -299,10 +305,18 @@ const WATCH_PR = {
299
305
  for: {
300
306
  type: "string",
301
307
  description:
302
- "REQUIRED for `add`. How long to watch, as a DURATION — `30m`, `2h`, `3d` (max 24h). Never an " +
303
- "instant, and there is no default. A PR nobody ever reviews would otherwise be polled forever " +
304
- "and hold your thread with it; the watcher settles itself when this runs out and tells you, " +
305
- "and you re-register if you still care.",
308
+ "REQUIRED for `add`. How long to watch, as a DURATION — `2h`, `3d`, `180d` (max 365d). Never " +
309
+ "an instant, and there is no default. The watcher settles itself when this runs out and tells " +
310
+ "you, and you re-register if you still care.\n\n" +
311
+ "MATCH IT TO WHOSE PR IT IS, and the two cases are far apart. Your own PR, waiting on CI or on " +
312
+ "a review you expect today: hours. A PULL REQUEST IN A REPO NOBODY HERE CONTROLS — an upstream " +
313
+ "project, someone else's maintainers — takes as long as it takes, so give it MONTHS (`90d`, " +
314
+ "`180d`, `365d`). A short `for` on one of those does not make it get reviewed sooner; it just " +
315
+ "expires against a PR nothing has touched, wakes you for nothing, and costs a re-arm. That is " +
316
+ "not hypothetical — a watcher on an external PR re-armed at the old 24h ceiling four days " +
317
+ "running, with zero maintainer activity in between. Long is FREE here: real activity wakes you " +
318
+ "the moment it happens either way, and the human can snooze or archive the thread whenever " +
319
+ "they want it gone.",
306
320
  },
307
321
  id: {
308
322
  type: "string",
@@ -368,7 +382,9 @@ const WATCH = {
368
382
  "REQUIRED. How long to hold the wait, as a DURATION — `30m`, `2h`, `3d` (max 24h). Never an " +
369
383
  "instant, and there is no default: choose it for THIS wait. When it elapses the row is " +
370
384
  "cancelled and you are woken to re-decide, so an over-long guess costs a wait that outlives " +
371
- "its reason and a too-short one costs one extra turn.",
385
+ "its reason and a too-short one costs one extra turn. The ceiling is a DAY here, unlike " +
386
+ "`watch_pr`'s year: a shell or a sub-agent dies with this session, so a wait on one that has " +
387
+ "stood for a day is a wait on something already gone.",
372
388
  },
373
389
  },
374
390
  required: ["kind", "target", "for"],
@@ -414,8 +430,12 @@ function questionSchema(depth) {
414
430
  description: {
415
431
  type: "string",
416
432
  description:
417
- "ONE LINE of trade-off. What this option costs, or why it is the one to take. An option list " +
418
- "with no trade-offs asks the human to reconstruct your reasoning before they can choose.",
433
+ "The trade-off, or the evidence — and it renders INSIDE the option, always visible, so this " +
434
+ "is where the human learns what they are choosing BEFORE they pick anything. One line for a " +
435
+ "simple fork. FULL MARKDOWN, over several lines, for one that deserves it: a list of " +
436
+ "consequences, a code block, the diff the option would produce, the exact message that would " +
437
+ "be posted. An option list with no trade-offs asks the human to reconstruct your reasoning " +
438
+ "before they can choose.",
419
439
  },
420
440
  recommended: {
421
441
  type: "boolean",
@@ -425,18 +445,16 @@ function questionSchema(depth) {
425
445
  "YOU ARE ASKING: you already know the answer, so implement it and say which way you went. " +
426
446
  "This is for the fork you genuinely cannot take yourself.",
427
447
  },
428
- preview: {
429
- type: "string",
430
- description:
431
- "Markdown revealed under this option when the human picks it — the diff it produces, the " +
432
- "message that would be posted, the mockup. Use it when SEEING the outcome is what decides the " +
433
- "question; skip it when the one-line trade-off already says everything.",
434
- },
448
+ // `preview` (markdown revealed under the option once picked) is RETIRED from this schema
449
+ // (2026-09-01): detail that decides a choice must be visible before the choice, so it belongs in
450
+ // a rich `description` now. The server still ACCEPTS the field — an in-flight worker dispatched
451
+ // against the old schema keeps working, and the card folds it into the same always-visible body.
435
452
  ...(depth < ASK_MAX_DEPTH
436
453
  ? {
454
+ // No `maxItems` here either (four until 2026-09-03): the tree is bounded by its DEPTH, not
455
+ // by how many branches hang off one option.
437
456
  followUps: {
438
457
  type: "array",
439
- maxItems: 4,
440
458
  description:
441
459
  "Questions that become live ONLY if the human picks this option — the conditional " +
442
460
  "branch. A branch nobody takes is never asked and never answered, so this is how you " +
@@ -479,10 +497,13 @@ function questionSchema(depth) {
479
497
  "x cannot dismiss it, because a generic close icon is not consent for something irreversible. " +
480
498
  "Declining must therefore be one of your own options.",
481
499
  },
500
+ // No `maxItems`: the count is the worker's to choose (maintainer 2026-09-03 — "allow arbitrary
501
+ // numbers of options"). A `multi` over a long list is a real shape, and the card letters past 26.
482
502
  options: {
483
503
  type: "array",
484
- maxItems: 8,
485
- description: "Two to four is almost always right. Omit entirely for a free-text question.",
504
+ description:
505
+ "As many as the choice actually has — a fork of two, or a `multi` over twenty findings. " +
506
+ "Omit entirely for a free-text question.",
486
507
  items: option,
487
508
  },
488
509
  },
@@ -530,10 +551,11 @@ const ASK = {
530
551
  inputSchema: {
531
552
  type: "object",
532
553
  properties: {
554
+ // No `maxItems` (four until 2026-09-03): "several at once is one call" above, and a cap here told
555
+ // a worker with six to batch them and then refused the batch.
533
556
  questions: {
534
557
  type: "array",
535
558
  minItems: 1,
536
- maxItems: 4,
537
559
  description: "The questions to register, together. Each becomes its own card and its own row.",
538
560
  items: questionSchema(1),
539
561
  },
@@ -1299,8 +1321,13 @@ async function watch(args) {
1299
1321
  ? `Already watching \`${target}\` as ${id} — nothing new was registered, and its original expiry stands.`
1300
1322
  : `Watching \`${target}\` as ${id}. Your thread is held out of the queue until it finishes, and the ` +
1301
1323
  "registration survives your turn ending, a compaction and a frizz restart."
1324
+ // A clamp is news here for the same reason it is on `watch_pr` — see that handler.
1325
+ const clamped = result?.clampedFrom
1326
+ ? `\n\nYOUR \`for: ${result.clampedFrom}\` WAS CAPPED at the 24h ceiling for a shell or a sub-agent — ` +
1327
+ "the expiry listed below is what you actually hold."
1328
+ : ""
1302
1329
  return (
1303
- `${head}\n\nWHEN \`for\` RUNS OUT the row is CANCELLED and you are woken to re-decide — register ` +
1330
+ `${head}${clamped}\n\nWHEN \`for\` RUNS OUT the row is CANCELLED and you are woken to re-decide — register ` +
1304
1331
  `again if you still mean it.\n\nDROP IT the moment it stops mattering (\`unwatch\`, id \`${id}\`); ` +
1305
1332
  `you do NOT need to when the work simply finishes.\n\n${armedWatchList(result)}`
1306
1333
  )
@@ -1436,12 +1463,19 @@ async function watchPr(args) {
1436
1463
  const result = (await callRpc("addOwnPrWatch", { slug, target, for: typeof args.for === "string" ? args.for.trim() : "" }))?.result
1437
1464
  const id = result?.id ?? "(unknown)"
1438
1465
  const ref = result?.target ?? target
1466
+ const until = result?.expiresAt ? ` until ${result.expiresAt}` : ""
1439
1467
  const head = result?.alreadyArmed
1440
- ? `Already watching ${ref} as ${id} — nothing new was registered, and you will be woken once per event.`
1441
- : `Watching ${ref} as ${id}. Frizz wakes you when CI passes or fails and on every later review or ` +
1442
- "comment, and the registration survives your turn ending, a compaction and a frizz restart."
1468
+ ? `Already watching ${ref} as ${id}${until} — nothing new was registered, its original expiry stands, and you will be woken once per event.`
1469
+ : `Watching ${ref} as ${id}${until}. Frizz wakes you when CI passes or fails and on every later ` +
1470
+ "review or comment, and the registration survives your turn ending, a compaction and a frizz restart."
1471
+ // A CLAMP IS NEWS. Silently handing back less coverage than was asked for is how a worker comes to
1472
+ // rest believing it is watched for a year when it is watched for one day.
1473
+ const clamped = result?.clampedFrom
1474
+ ? `\n\nYOUR \`for: ${result.clampedFrom}\` WAS CAPPED at the ceiling — the expiry above is what you ` +
1475
+ "actually hold. Nothing else about the watcher changed."
1476
+ : ""
1443
1477
  return (
1444
- `${head}\n\nNAME IT IN YOUR \`\`\`awaiting FENCE TOO (\`prs: [${ref}]\`) — the watcher does the ` +
1478
+ `${head}${clamped}\n\nNAME IT IN YOUR \`\`\`awaiting FENCE TOO (\`prs: [${ref}]\`) — the watcher does the ` +
1445
1479
  `waking, the fence is what lets you come to rest and shows the human what you are waiting for.\n\n` +
1446
1480
  `DROP IT when it stops mattering (\`action: "drop", id: "${id}"\`).\n\n${armedPrWatchList(result)}`
1447
1481
  )