frizz 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +17 -0
  2. package/dist/claude-agent-broker.js +19 -1
  3. package/dist/dev-child.js +111 -28
  4. package/dist/frizz.js +107 -3
  5. package/package.json +1 -1
  6. package/runtime/cc-worker/bin/frizz-mcp.mjs +8 -0
  7. package/runtime/cc-worker/hooks/session-seed.mjs +1 -1
  8. package/web-dist/assets/{TerminalPane-sd3XXjx5.js → TerminalPane-WcpgmCA-.js} +1 -1
  9. package/web-dist/assets/{abnfDiagram-VRR7QNED-Bry_FIqg.js → abnfDiagram-VRR7QNED-Caecklbz.js} +1 -1
  10. package/web-dist/assets/architecture-TIHT7OUA-BTy2nvYL.js +1 -0
  11. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-Baok4OeI.js → architectureDiagram-ZJ3FMSHR-BEk0fg8C.js} +1 -1
  12. package/web-dist/assets/{blockDiagram-677ZJIJ3-BgHIO9lI.js → blockDiagram-677ZJIJ3-B4yq3xiy.js} +1 -1
  13. package/web-dist/assets/{c4Diagram-LMCZKHZV-rfj9Z17Z.js → c4Diagram-LMCZKHZV-DaA5NBkz.js} +1 -1
  14. package/web-dist/assets/channel-WghlT4VS.js +1 -0
  15. package/web-dist/assets/{chunk-32BRIVSS-BwaHfK1Y.js → chunk-32BRIVSS-2XsSs8F_.js} +1 -1
  16. package/web-dist/assets/{chunk-52WLFC77-BmdQDZOA.js → chunk-52WLFC77-DWJfECP3.js} +1 -1
  17. package/web-dist/assets/{chunk-C7G6YPKG-ByQFjoZe.js → chunk-C7G6YPKG-Bz07eaGt.js} +1 -1
  18. package/web-dist/assets/{chunk-EX3LRPZG-DQFudBKr.js → chunk-EX3LRPZG-D78kZEMM.js} +1 -1
  19. package/web-dist/assets/{chunk-FWX5IMBZ-OW9qEPiq.js → chunk-FWX5IMBZ-CeCa17CW.js} +2 -2
  20. package/web-dist/assets/{chunk-HOUHSVGY-BdKLF8nO.js → chunk-HOUHSVGY-9H4GFuBa.js} +1 -1
  21. package/web-dist/assets/{chunk-ICXQ74PX-C1roqKR1.js → chunk-ICXQ74PX--Aa17r4X.js} +1 -1
  22. package/web-dist/assets/{chunk-MOJQB5TN-Cx0u1ZDK.js → chunk-MOJQB5TN-C3vXhsPV.js} +1 -1
  23. package/web-dist/assets/{chunk-OGEWGWER-rxv9xtNR.js → chunk-OGEWGWER-G4kZRBkX.js} +1 -1
  24. package/web-dist/assets/{chunk-PUDLZKDR-BXWPPPPi.js → chunk-PUDLZKDR-DWd5kvyM.js} +1 -1
  25. package/web-dist/assets/{chunk-Q4XR5HBZ-CpqbD101.js → chunk-Q4XR5HBZ-u55p39Up.js} +1 -1
  26. package/web-dist/assets/{chunk-V7JOEXUC-CP8_3qVt.js → chunk-V7JOEXUC-BU4YBmV4.js} +1 -1
  27. package/web-dist/assets/{chunk-VAUOI2AC-Dl7xahpk.js → chunk-VAUOI2AC-VXcmT20q.js} +1 -1
  28. package/web-dist/assets/{chunk-VR4S4FIN-BKtMX64l.js → chunk-VR4S4FIN-CSjEXLOC.js} +1 -1
  29. package/web-dist/assets/{chunk-WYO6CB5R-DmLOdJcu.js → chunk-WYO6CB5R-4WGk2dpL.js} +1 -1
  30. package/web-dist/assets/{chunk-ZGVPDNZ5-5wN7eXvS.js → chunk-ZGVPDNZ5-D5Xl8ZV6.js} +1 -1
  31. package/web-dist/assets/classDiagram-OUVF2IWQ-D21-Dy4r.js +1 -0
  32. package/web-dist/assets/classDiagram-v2-EOCWNBFH-D21-Dy4r.js +1 -0
  33. package/web-dist/assets/{cynefin-VYW2F7L2-DUYahBXb.js → cynefin-VYW2F7L2-O6wB49wK.js} +1 -1
  34. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-Rm0i30nE.js → cynefinDiagram-TSTJHNR4-pd6ZogNC.js} +1 -1
  35. package/web-dist/assets/{dagre-VKFMJZFB-3uhsB-W3.js → dagre-VKFMJZFB-D8W1S6fw.js} +1 -1
  36. package/web-dist/assets/{diagram-FQU43EPY-BUvflNRQ.js → diagram-FQU43EPY-Bm1NOkHd.js} +1 -1
  37. package/web-dist/assets/{diagram-G47NLZAW-DUluIZel.js → diagram-G47NLZAW-CjwnXTIc.js} +1 -1
  38. package/web-dist/assets/{diagram-NH7WQ7WH-BBrwsbed.js → diagram-NH7WQ7WH-DH1jZ_mu.js} +1 -1
  39. package/web-dist/assets/{diagram-OA4YK3LP-DWnTQBw4.js → diagram-OA4YK3LP-B-s969Q_.js} +1 -1
  40. package/web-dist/assets/{diagram-WEI45ONY-BjEavoC0.js → diagram-WEI45ONY-BU3lv6cL.js} +1 -1
  41. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-CW4xZAZH.js → ebnfDiagram-CCIWWBDH-FsAeyqcC.js} +1 -1
  42. package/web-dist/assets/{erDiagram-Q63AITRT-BemQ1R4B.js → erDiagram-Q63AITRT-BRvg5Y9d.js} +1 -1
  43. package/web-dist/assets/eventmodeling-45OFAUF4-RhX2WA7g.js +1 -0
  44. package/web-dist/assets/flowDiagram-23GEKE2U-C7N14tdV.js +1 -0
  45. package/web-dist/assets/{ganttDiagram-NO4QXBWP-lDdG5I_z.js → ganttDiagram-NO4QXBWP-eATKNh9U.js} +1 -1
  46. package/web-dist/assets/{gitGraph-TEB2WS4Q-Bw87a_Se.js → gitGraph-TEB2WS4Q-CNYqsua8.js} +1 -1
  47. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-B762r0yi.js → gitGraphDiagram-IHSO6WYX-BUxgluf8.js} +1 -1
  48. package/web-dist/assets/{index-CAapfhMv.js → index-BFuReKr0.js} +75 -72
  49. package/web-dist/assets/index-BH9U3Zlg.css +1 -0
  50. package/web-dist/assets/{info-DKCQHKI2-DGOv6Gx0.js → info-DKCQHKI2-HsVLwo1c.js} +1 -1
  51. package/web-dist/assets/{infoDiagram-FWYZ7A6U-DL92kAzf.js → infoDiagram-FWYZ7A6U-D7U65xDp.js} +1 -1
  52. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-CFYd4OtU.js → ishikawaDiagram-FXEZZL3T-BsfIYXjX.js} +1 -1
  53. package/web-dist/assets/{journeyDiagram-5HDEW3XC-QHuYL5zq.js → journeyDiagram-5HDEW3XC-CpkREKol.js} +1 -1
  54. package/web-dist/assets/{kanban-definition-HUTT4EX6-DwmMluQ8.js → kanban-definition-HUTT4EX6-2M3KW6Gw.js} +1 -1
  55. package/web-dist/assets/{line-Je_PFFHm.js → line-Cs7zWs24.js} +1 -1
  56. package/web-dist/assets/{mermaid-parser.core-BeiGWsn3.js → mermaid-parser.core-Dw-IECV5.js} +3 -3
  57. package/web-dist/assets/{mermaid.core-BP8JrW48.js → mermaid.core-C1lrddjT.js} +3 -3
  58. package/web-dist/assets/{mindmap-definition-LN4V7U3C-BsmjCIrM.js → mindmap-definition-LN4V7U3C-gVS5On1g.js} +1 -1
  59. package/web-dist/assets/{packet-7NZHBO7P-D6ekYYGJ.js → packet-7NZHBO7P-Tti1-UWA.js} +1 -1
  60. package/web-dist/assets/{pegDiagram-2B236MQR-CQc2Kt0u.js → pegDiagram-2B236MQR-DE5in93G.js} +1 -1
  61. package/web-dist/assets/{pie-RZYD4A2V-C_45fO8J.js → pie-RZYD4A2V-DfLNkvHl.js} +1 -1
  62. package/web-dist/assets/{pieDiagram-ENE6RG2P-Dg5z6iWt.js → pieDiagram-ENE6RG2P-DcqSqJqb.js} +1 -1
  63. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-c7gZxuCX.js → quadrantDiagram-ABIIQ3AL-DR-nRPgN.js} +1 -1
  64. package/web-dist/assets/{radar-I7S5WNFK-D80XNJQq.js → radar-I7S5WNFK-DGLDBfPd.js} +1 -1
  65. package/web-dist/assets/{railroad-3IZDKUUU-Brz7eZQ0.js → railroad-3IZDKUUU-BaiGEHlV.js} +1 -1
  66. package/web-dist/assets/railroad-abnf-AHOZXSZD-BCeoIOWA.js +1 -0
  67. package/web-dist/assets/railroad-ebnf-EBAXGLYW-lZpXJLD8.js +1 -0
  68. package/web-dist/assets/railroad-peg-LSFZ7HO6-CaUBrHTL.js +1 -0
  69. package/web-dist/assets/{railroadDiagram-RFXS5EU6-Db9K7bty.js → railroadDiagram-RFXS5EU6-82aEev5_.js} +1 -1
  70. package/web-dist/assets/{requirementDiagram-TGXJPOKE-m1ipMrDe.js → requirementDiagram-TGXJPOKE-Bs8opBRj.js} +1 -1
  71. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-4Zz9ry0k.js → sankeyDiagram-HTMAVEWB-gOb3FIr8.js} +1 -1
  72. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-CZeGHw4X.js → sequenceDiagram-DBY2YBRQ-6DvjtRHR.js} +1 -1
  73. package/web-dist/assets/{stateDiagram-2N3HPSRC-rLUVs1Pg.js → stateDiagram-2N3HPSRC-Cg2LWdUf.js} +1 -1
  74. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-Digf9CWZ.js +1 -0
  75. package/web-dist/assets/{swimlanes-5IMT3BWC-BAeBdscz.js → swimlanes-5IMT3BWC-BDpPdYEl.js} +1 -1
  76. package/web-dist/assets/swimlanesDiagram-G3AALYLV-DEZRDWHA.js +8 -0
  77. package/web-dist/assets/{timeline-definition-FHXFAJF6-DL0eWMF-.js → timeline-definition-FHXFAJF6-BSjfeutv.js} +1 -1
  78. package/web-dist/assets/{treeView-QDETBFTQ-gEtzLMA8.js → treeView-QDETBFTQ-DwgPy6V3.js} +1 -1
  79. package/web-dist/assets/{treemap-6X3UGDF4-CCym1Gz6.js → treemap-6X3UGDF4-7eiifTKW.js} +1 -1
  80. package/web-dist/assets/{vennDiagram-L72KCM5P-cF97-nb5.js → vennDiagram-L72KCM5P-9u4AhcnV.js} +1 -1
  81. package/web-dist/assets/{wardley-OPB4EBWU-DQJHCGbM.js → wardley-OPB4EBWU-DqNb5x9U.js} +1 -1
  82. package/web-dist/assets/{wardleyDiagram-EHGQE667-0CnHzXLX.js → wardleyDiagram-EHGQE667-DlJ7h3vg.js} +1 -1
  83. package/web-dist/assets/{xychartDiagram-FW5EYKEG-9YDOp4m5.js → xychartDiagram-FW5EYKEG-B_9ukg2S.js} +1 -1
  84. package/web-dist/index.html +2 -2
  85. package/web-dist/assets/architecture-TIHT7OUA-CoMJQ5Ya.js +0 -1
  86. package/web-dist/assets/channel-D6uS1y7I.js +0 -1
  87. package/web-dist/assets/classDiagram-OUVF2IWQ-BW5YiBf4.js +0 -1
  88. package/web-dist/assets/classDiagram-v2-EOCWNBFH-BW5YiBf4.js +0 -1
  89. package/web-dist/assets/eventmodeling-45OFAUF4-BF5KB7ja.js +0 -1
  90. package/web-dist/assets/flowDiagram-23GEKE2U-BYo4Eytx.js +0 -1
  91. package/web-dist/assets/index-B3QArXu2.css +0 -1
  92. package/web-dist/assets/railroad-abnf-AHOZXSZD-D44pcGhJ.js +0 -1
  93. package/web-dist/assets/railroad-ebnf-EBAXGLYW-CiWVH5SP.js +0 -1
  94. package/web-dist/assets/railroad-peg-LSFZ7HO6-Cy1B6-pN.js +0 -1
  95. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-w7QDFEvx.js +0 -1
  96. package/web-dist/assets/swimlanesDiagram-G3AALYLV-UhdgWpNZ.js +0 -8
package/README.md CHANGED
@@ -122,6 +122,7 @@ $ npx frizz --help
122
122
  Frizz production launcher
123
123
 
124
124
  Usage: npx frizz [options] [repository]
125
+ npx frizz up serve this repo publicly, board + tunnel together
125
126
 
126
127
  Runs the npm-resolved immutable Frizz package, then opens it in your default browser. Use frizz-dev
127
128
  only for a source checkout.
@@ -276,6 +277,22 @@ Frizz has its own small vocabulary. Most of it names a feature, so this doubles
276
277
 
277
278
  <br/>
278
279
 
280
+ <h2 align="center">Contributing</h2>
281
+
282
+ Issues and pull requests are welcome. Fork the repo, branch off `main`, and open the PR against `main` — CI runs on every pull request.
283
+
284
+ Three checks run in CI, and they need no install:
285
+
286
+ ```sh
287
+ $ node --test board/*.test.mjs
288
+ $ node scripts/sync-portable-monitors.mjs --check
289
+ $ node --test monitors/*.test.mjs
290
+ ```
291
+
292
+ Everything else runs locally. Install with `pnpm install`, typecheck with `pnpm typecheck`, and run the full suite with `pnpm test` — that suite drives real agent CLIs and a real browser, which is why CI does not gate on it. Say in the PR what you ran.
293
+
294
+ <br/>
295
+
279
296
  <h2 align="center">License</h2>
280
297
 
281
298
  MIT
@@ -19832,6 +19832,18 @@ function claudeUltracodeSettings() {
19832
19832
  return { ultracode: true };
19833
19833
  }
19834
19834
 
19835
+ // packages/server/src/backend/claude-context-window.ts
19836
+ var WINDOW_SUFFIX = "[1m]";
19837
+ var CLAUDE_1M_MODELS = ["fable", "opus", "sonnet"];
19838
+ function resolveClaudeLaunchModel(model) {
19839
+ if (!model) return void 0;
19840
+ if (model.endsWith(WINDOW_SUFFIX)) {
19841
+ return { model, fallbackModel: model.slice(0, -WINDOW_SUFFIX.length) };
19842
+ }
19843
+ if (!CLAUDE_1M_MODELS.includes(model)) return { model };
19844
+ return { model: `${model}${WINDOW_SUFFIX}`, fallbackModel: model };
19845
+ }
19846
+
19835
19847
  // packages/server/src/backend/claude-agent-sdk-protocol.ts
19836
19848
  var CLAUDE_AGENT_SDK_PROTOCOL_VERSION = 1;
19837
19849
  var CLAUDE_AGENT_SDK_MAX_INPUT_BYTES = 64 * 1024;
@@ -20612,6 +20624,7 @@ function startClaudeQuery(executablePath, options) {
20612
20624
  }
20613
20625
  } : void 0;
20614
20626
  const claudeEffort = resolveClaudeEffort(options.effort);
20627
+ const launchModel = resolveClaudeLaunchModel(options.model);
20615
20628
  const raw = uGe({
20616
20629
  prompt: input,
20617
20630
  options: {
@@ -20646,7 +20659,12 @@ function startClaudeQuery(executablePath, options) {
20646
20659
  // Keep Claude's default (preset) system prompt and APPEND the frizz worker contract, the SDK
20647
20660
  // equivalent of the tmux path's --append-system-prompt-file.
20648
20661
  ...options.appendSystemPrompt ? { systemPrompt: { type: "preset", preset: "claude_code", append: options.appendSystemPrompt } } : {},
20649
- ...options.model ? { model: options.model } : {},
20662
+ // The 1M context window rides the MODEL value as a `[1m]` suffix, always paired with the bare
20663
+ // alias as `fallbackModel` — an unavailable long-context beta is a hard 400 that kills the
20664
+ // session, and the fallback is what makes asking for it safe on every subscription. See
20665
+ // claude-context-window.ts for the measurements.
20666
+ ...launchModel ? { model: launchModel.model } : {},
20667
+ ...launchModel?.fallbackModel ? { fallbackModel: launchModel.fallbackModel } : {},
20650
20668
  // "ultracode" is not an `effort` value — it resolves to xhigh plus a session setting, and the two
20651
20669
  // must travel TOGETHER or the setting is silently ignored (see resolveClaudeEffort). `settings` is
20652
20670
  // an additional highest-precedence settings source, layered over `settingSources` above.
package/dist/dev-child.js CHANGED
@@ -5751,7 +5751,7 @@ function parseGithubWakeSteer(text) {
5751
5751
  function frizzRoute(path) {
5752
5752
  return `${FRIZZ_ROUTE_PREFIX}${path.startsWith("/") ? path : `/${path}`}`;
5753
5753
  }
5754
- var ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, NativeInputRequired, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, 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, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, PlanView, 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, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, ConfirmAwaitingInput, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
5754
+ var ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, NativeInputRequired, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, 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, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, PlanView, 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, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, ConfirmAwaitingInput, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, AGENT_MESSAGE_WRAPPER, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketClientMsg, ProjectCard, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
5755
5755
  var init_src = __esm({
5756
5756
  "packages/shared/src/index.ts"() {
5757
5757
  "use strict";
@@ -6113,7 +6113,9 @@ var init_src = __esm({
6113
6113
  "**STILL OWED counts things you are not going to do yourself.** A decision you are RECOMMENDING, a",
6114
6114
  "draft you wrote but did not send, follow-up work you discovered \u2014 all of it dies with the card, even",
6115
6115
  "the part that is someone else's to do. Each ends on a question with your recommendation as option A,",
6116
- "or gets its own card first via `mcp__frizz__spawn_thread`; none of them is a `done`. And what is not",
6116
+ "or you DO it first \u2014 a sub-agent's result comes BACK to you, so it lands on your card; a new card via",
6117
+ "`mcp__frizz__spawn_thread` is the LAST resort, since nothing it learns returns to you or its siblings.",
6118
+ "None of them is a `done`. And what is not",
6117
6119
  'worth a card is not worth a SENTENCE: delete the dangling "one thing to carry forward", never park it',
6118
6120
  "in the handoff.",
6119
6121
  "",
@@ -6928,6 +6930,9 @@ ${DISPATCH_TASK_BANNER}
6928
6930
  );
6929
6931
  WAKE_ITEM = /^- (👤|🤖) (.+?) from @(\S+?)(?: at (\S+?))?(?:: (\S+))?$/;
6930
6932
  WAKE_MORE = /^- …and (\d+) more not listed — check .+ for the rest$/;
6933
+ PR_WATCH_FINISHED = new RegExp(String.raw`^⏰ (${WAKE_REF}) was (MERGED|CLOSED)\.$`);
6934
+ PR_WATCH_CI_PASSED = new RegExp(String.raw`^✅ CI PASSED on (${WAKE_REF}) — (\d+) checks? green\.$`);
6935
+ PR_WATCH_CI_FAILED = new RegExp(String.raw`^❌ CI FAILED on (${WAKE_REF})(?:: (.+?))?\.$`);
6931
6936
  GithubStatus = external_exports.object({
6932
6937
  installed: external_exports.boolean(),
6933
6938
  inRepo: external_exports.boolean(),
@@ -15240,7 +15245,9 @@ function createTailer(deps) {
15240
15245
  for (const e of state.subAgents.values()) {
15241
15246
  if (e.kind !== "shell") continue;
15242
15247
  const lastActivityAt = entryLastActivity(e);
15243
- out.push({ label: e.label, startedAt: e.startedAt, state: "running", id: e.toolUseId, ...e.taskId ? { stoppable: true, taskId: e.taskId } : {}, ...lastActivityAt ? { lastActivityAt } : {} });
15248
+ const since = Date.parse(lastActivityAt ?? e.startedAt);
15249
+ const shellState = Number.isFinite(since) && now() - since > BGSHELL_STALE_MS ? "stale" : "running";
15250
+ out.push({ label: e.label, startedAt: e.startedAt, state: shellState, id: e.toolUseId, ...e.taskId ? { stoppable: true, taskId: e.taskId } : {}, ...lastActivityAt ? { lastActivityAt } : {} });
15244
15251
  }
15245
15252
  return out;
15246
15253
  }
@@ -16294,7 +16301,7 @@ function landsAfterRead(eventAt, lastReadAt) {
16294
16301
  if (!Number.isFinite(e) || !Number.isFinite(r)) return true;
16295
16302
  return e > r;
16296
16303
  }
16297
- var IDLE_BACKSTOP_MS, RUNTIME_CHASE_MAX, POLL_MS2, MAX_PRIME_ROWS_PER_TICK, PRIME_BUDGET_MS, TRANSCRIPT_READ_WINDOW, EMPTY_BUFFER, MAX_POLL_MS, SUBAGENT_STALE_MS, FOREIGN_FRESH_MS, FOREIGN_MAX, FOREIGN_SCAN_EVERY, CACHE_FLUSH_MS, PRIME_PROGRESS_EVERY, DISCOVER_RETRY_MS, DISCOVER_RETRY_MAX_MS, PERM_YES_OPTION, PERM_QUESTION, PERM_FOOTER, PERM_COMPOSER_FOOTER, PERM_MODAL_TAIL_ROWS, PERM_COMPOSER_TAIL_ROWS, BOOT_APIKEY_HEADLINE, BOOT_APIKEY_QUESTION, BOOT_TRUST_QUESTION, BOOT_TRUST_OPTION, BOOT_MODAL_TAIL_ROWS, RETAINED_SUBAGENTS_MAX, RETAINED_SHELLS_MAX, DESCENDANT_TERMINALS_MAX, PENDING_RESUMES_MAX, PENDING_SHELLS_MAX, RESUME_REPLAY_SLACK_MS, QUESTION_BLOCK_RE, SIGNAL_FENCE_RE, AWAITING_HINT_RE, FENCE_BODY_MAX, HINT_MAX, HINT_VALUE_MAX, LAUNCH_ACK_RE, AUTO_BACKGROUND_ACK_RE, DESCENDANT_SIDECAR_MAX, DESCENDANT_NOTIFY_GRACE_MS, MAX_OWNED_TOOL_USE_IDS, BROKER_LIVENESS_TTL_MS, UNRESTORED_TAIL_FIELDS;
16304
+ var IDLE_BACKSTOP_MS, RUNTIME_CHASE_MAX, POLL_MS2, MAX_PRIME_ROWS_PER_TICK, PRIME_BUDGET_MS, TRANSCRIPT_READ_WINDOW, EMPTY_BUFFER, MAX_POLL_MS, SUBAGENT_STALE_MS, BGSHELL_STALE_MS, FOREIGN_FRESH_MS, FOREIGN_MAX, FOREIGN_SCAN_EVERY, CACHE_FLUSH_MS, PRIME_PROGRESS_EVERY, DISCOVER_RETRY_MS, DISCOVER_RETRY_MAX_MS, PERM_YES_OPTION, PERM_QUESTION, PERM_FOOTER, PERM_COMPOSER_FOOTER, PERM_MODAL_TAIL_ROWS, PERM_COMPOSER_TAIL_ROWS, BOOT_APIKEY_HEADLINE, BOOT_APIKEY_QUESTION, BOOT_TRUST_QUESTION, BOOT_TRUST_OPTION, BOOT_MODAL_TAIL_ROWS, RETAINED_SUBAGENTS_MAX, RETAINED_SHELLS_MAX, DESCENDANT_TERMINALS_MAX, PENDING_RESUMES_MAX, PENDING_SHELLS_MAX, RESUME_REPLAY_SLACK_MS, QUESTION_BLOCK_RE, SIGNAL_FENCE_RE, AWAITING_HINT_RE, FENCE_BODY_MAX, HINT_MAX, HINT_VALUE_MAX, LAUNCH_ACK_RE, AUTO_BACKGROUND_ACK_RE, DESCENDANT_SIDECAR_MAX, DESCENDANT_NOTIFY_GRACE_MS, MAX_OWNED_TOOL_USE_IDS, BROKER_LIVENESS_TTL_MS, UNRESTORED_TAIL_FIELDS;
16298
16305
  var init_tailer = __esm({
16299
16306
  "packages/server/src/tailer.ts"() {
16300
16307
  "use strict";
@@ -16323,6 +16330,7 @@ var init_tailer = __esm({
16323
16330
  EMPTY_BUFFER = Buffer.alloc(0);
16324
16331
  MAX_POLL_MS = 1e4;
16325
16332
  SUBAGENT_STALE_MS = 15 * 6e4;
16333
+ BGSHELL_STALE_MS = 12 * 60 * 6e4;
16326
16334
  FOREIGN_FRESH_MS = 24 * 60 * 6e4;
16327
16335
  FOREIGN_MAX = 20;
16328
16336
  FOREIGN_SCAN_EVERY = 5;
@@ -25485,15 +25493,26 @@ landed, a follow-up the change implies. \`done\` is barred \u2014 the finding di
25485
25493
  is barred \u2014 nothing is running. \`question\` looks barred \u2014 the human asked nothing. **That deadlock is
25486
25494
  not real, and stretching \`done\` is not how you break it.** Take the first exit that fits:
25487
25495
 
25488
- 1. **DO IT** \u2014 small, in scope, yours to make. The default from **The stop criterion**. Then \`done\` is
25489
- honest with nothing left over.
25490
- 2. **HAND IT OFF TO ITS OWN CARD** \u2014 \`mcp__frizz__spawn_thread\` puts a separable effort on the board as
25491
- its own thread. Once the follow-up has a card, THIS thread owes nothing and \`done\` is accurate; put
25492
- the returned \`[title](/thread/<slug>)\` link in the body. Spawn what deserves an effort, not every
25493
- stray observation \u2014 but an observation that does not deserve a card does not deserve a trailing
25494
- sentence either. DROP it.
25495
- 3. **ASK** \u2014 the human should choose whether or how it happens. An ordinary \` \`\`\`question \`; "nothing
25496
- was pending" is not an objection. Make "spawn it as its own thread" an option when it is a candidate.
25496
+ 1. **DO IT \u2014 the default, and it covers far more than "small".** Anything inside your change's own blast
25497
+ radius is exit 1: the fix your change implies, the test it needs, the breakage it revealed, the caller
25498
+ it left stale. **Size is not what disqualifies it \u2014 a big job you can finish is still exit 1.** If it
25499
+ is more than one sitting, dispatch an in-session SUB-AGENT: its result comes BACK to you, so the
25500
+ follow-up lands on YOUR card, in your diff, under one review. One thread that finished two things
25501
+ beats two threads that each finished one and never learned of the other. Then \`done\` is honest with
25502
+ nothing left over.
25503
+ 2. **ASK** \u2014 the human should choose whether or how it happens. An ordinary \` \`\`\`question \`; "nothing
25504
+ was pending" is not an objection. This is the exit for work you cannot bound, cannot verify, or that
25505
+ is genuinely the human's call \u2014 and it is where a separable effort goes, as an OPTION they pick,
25506
+ rather than a card you create for them.
25507
+ 3. **HAND IT OFF TO ITS OWN CARD \u2014 the LAST resort, not the tidy one.** \`mcp__frizz__spawn_thread\` puts
25508
+ a separable effort on the board as its own thread; put the returned \`[title](/thread/<slug>)\` link in
25509
+ the body. **A spawned thread is FIRE-AND-FORGET: it reports to the HUMAN, never to you, so nothing it
25510
+ learns can ever reach this effort or its siblings.** That is the whole cost, and it is not
25511
+ theoretical \u2014 one thread here spawned four, three of those spawned more, and three separate
25512
+ descendants independently rediscovered the SAME root-cause commit over twenty hours because none of
25513
+ them could tell the others. Reach for it only when the work genuinely cannot ride on your card: a
25514
+ different repo, a different long-lived runtime, an effort that must outlive yours. If a sub-agent
25515
+ could do it, exit 1. If the human should choose, exit 2. Never spawn merely to clear your own \`done\`.
25497
25516
  4. **DROP IT** \u2014 it is not worth an effort, a card, or a question. Then it is not worth a SENTENCE
25498
25517
  either: delete it. Do not park it in the handoff as "one thing to carry forward".
25499
25518
 
@@ -26000,10 +26019,12 @@ Give it a self-contained \`prompt\` and choose \`model\` + \`effort\` by the new
26000
26019
  required). It returns a \`[title](/thread/<slug>)\` link \u2014 put that in your handoff so the human can open
26001
26020
  it.
26002
26021
 
26003
- **Its most valuable use is the one that unblocks \` \`\`\`done \`.** When your own mandate is complete but
26004
- you turned up separable follow-up work, that finding is exactly what a \`done\` card would bury. Spawn it,
26005
- and it lives on its own card instead of inside a thread nobody will reopen \u2014 which is what makes \`done\`
26006
- on THIS thread honest rather than a stretch. See **When the work is finished but the thread found
26022
+ **It is the LAST resort among the exits, never the tidy one.** A finding you turned up is not by itself a
26023
+ reason to spawn: the ordered exits put DOING it (with a sub-agent, whose result comes back to you) first
26024
+ and ASKING second, precisely because both keep the work reachable. A spawned thread does not \u2014 it reports
26025
+ to the HUMAN and nothing it learns returns to you or to its siblings, so a chain of them re-derives the
26026
+ same facts in parallel and nobody notices. Spawn only when the work genuinely cannot ride on your card,
26027
+ and never merely to clear your own \` \`\`\`done \`. See **When the work is finished but the thread found
26007
26028
  more**.`;
26008
26029
  THREAD_EXECUTION = {
26009
26030
  claude: `## Thread types
@@ -29378,8 +29399,11 @@ function isShellFenceId(fenceId) {
29378
29399
  function prWatchFenceId(watchId, report) {
29379
29400
  return `${PR_WATCH_FENCE_PREFIX}:${watchId}:${report}`;
29380
29401
  }
29402
+ function prWatchExpiredFenceId(watchId) {
29403
+ return `${PR_WATCH_EXPIRED_FENCE_PREFIX}:${watchId}`;
29404
+ }
29381
29405
  function isPrWatchFenceId(fenceId) {
29382
- return fenceId.startsWith(`${PR_WATCH_FENCE_PREFIX}:`);
29406
+ return fenceId.startsWith(`${PR_WATCH_FENCE_PREFIX}:`) || fenceId.startsWith(`${PR_WATCH_EXPIRED_FENCE_PREFIX}:`);
29383
29407
  }
29384
29408
  function signoffFenceId(restedAt) {
29385
29409
  return `${SIGNOFF_FENCE_PREFIX}:${restedAt}`;
@@ -29387,6 +29411,16 @@ function signoffFenceId(restedAt) {
29387
29411
  function isSignoffFenceId(fenceId) {
29388
29412
  return fenceId.startsWith(`${SIGNOFF_FENCE_PREFIX}:`);
29389
29413
  }
29414
+ function parkFenceId(cause, spokeAt) {
29415
+ return `${PARK_FENCE_PREFIX}:${cause}:${spokeAt}`;
29416
+ }
29417
+ function isParkFenceId(fenceId) {
29418
+ return fenceId.startsWith(`${PARK_FENCE_PREFIX}:`);
29419
+ }
29420
+ function parkFenceRestOf(fenceId) {
29421
+ const cut = fenceId.indexOf(":", PARK_FENCE_PREFIX.length + 1);
29422
+ return cut < 0 ? "" : fenceId.slice(cut + 1);
29423
+ }
29390
29424
  function timerFenceId(timerId) {
29391
29425
  return `${TIMER_FENCE_PREFIX}:${timerId}`;
29392
29426
  }
@@ -29670,6 +29704,14 @@ function createScheduler(deps) {
29670
29704
  if (!stillMissing) return "superseded";
29671
29705
  return tele.turn === "idle" ? "current-idle" : "current-busy";
29672
29706
  }
29707
+ if (isParkFenceId(item.fenceId)) {
29708
+ if (parkFenceRestOf(item.fenceId) !== (tele.lastAssistantAt ?? "")) return "superseded";
29709
+ return tele.turn === "idle" ? "current-idle" : "current-busy";
29710
+ }
29711
+ if (!item.fenceId.includes("")) {
29712
+ log3(`waker: UNROUTED wake prefix ${item.fenceId.split(":")[0]} for ${item.slug} \u2014 no deliveryContext branch, superseding`);
29713
+ return "superseded";
29714
+ }
29673
29715
  const fence = tele.lastFence;
29674
29716
  if (!fence || fence.kind !== "awaiting" || !fence.hints.some(isActionable)) return "superseded";
29675
29717
  if (fenceIdentity(fence.hints, tele.lastActivityAt) !== item.fenceId) return "superseded";
@@ -30135,7 +30177,7 @@ You have NOTHING running right now \u2014 no shell, no sub-agent, no timer, no r
30135
30177
  pull request. There is nothing that could wake you, so this thread is not awaiting: finish in
30136
30178
  \`\`\`done, or ask a \`\`\`question.`;
30137
30179
  if (cause !== "expired" && (row.park_bumps ?? 0) >= PARK_BUMP_MAX) continue;
30138
- const fenceId = `park:${cause}:${spokeAt}`;
30180
+ const fenceId = parkFenceId(cause, spokeAt);
30139
30181
  const deliveryId = wakeDeliveryId(row.slug, row.session_id, fenceId);
30140
30182
  if (outbox.get(deliveryId)) continue;
30141
30183
  const item = outbox.enqueue({
@@ -30170,7 +30212,7 @@ pull request. There is nothing that could wake you, so this thread is not awaiti
30170
30212
  const ref = `${w.owner}/${w.repo}#${w.number}`;
30171
30213
  log3(`waker: settled ${w.thread_slug} \u2014 pr watcher ${w.id} expired (${ref})`);
30172
30214
  if (!row || row.state === "archived" || row.archived === 1) continue;
30173
- const fenceId = `prwatch-expired:${w.id}`;
30215
+ const fenceId = prWatchExpiredFenceId(w.id);
30174
30216
  const deliveryId = wakeDeliveryId(row.slug, row.session_id, fenceId);
30175
30217
  if (outbox.get(deliveryId)) continue;
30176
30218
  const item = outbox.enqueue({
@@ -30178,7 +30220,7 @@ pull request. There is nothing that could wake you, so this thread is not awaiti
30178
30220
  slug: row.slug,
30179
30221
  sessionId: row.session_id,
30180
30222
  fenceId,
30181
- hintKey: `prwatch-expired:${w.id}`,
30223
+ hintKey: fenceId,
30182
30224
  message: `\u23F0 Your watcher on ${ref} has expired and is no longer armed \u2014 nothing on that PR will wake you now.
30183
30225
 
30184
30226
  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.`,
@@ -30715,7 +30757,7 @@ If you still care about it, register it again with \`mcp__frizz__watch_pr\` and
30715
30757
  tick
30716
30758
  };
30717
30759
  }
30718
- var execFileAsync4, PR_REF_RE2, LIMIT_FENCE_PREFIX, LIMIT_HINT_PREFIX, LIMIT_RESUME_GRACE_MS, LIMIT_RESUME_HEADROOM_PERCENT, LIMIT_HEADROOM_MIN_FAULT_AGE_MS, REPORT_FENCE_PREFIX, REPORT_HINT_PREFIX, SNOOZE_FENCE_PREFIX, SNOOZE_HINT_PREFIX, HEARTBEAT_FENCE_PREFIX, HEARTBEAT_HINT_PREFIX, STOP_HOOK_FENCE_PREFIX, STOP_HOOK_HINT_KEY, COMPACT_FENCE_PREFIX, COMPACT_HINT_KEY, TIMER_FENCE_PREFIX, TIMER_HINT_PREFIX, SHELL_FENCE_PREFIX, PR_WATCH_FENCE_PREFIX, PR_WATCH_POLL_MS, SIGNOFF_FENCE_PREFIX, SIGNOFF_HINT_KEY, SIGNOFF_NUDGE_MAX, PARK_BUMP_MAX, SIGNOFF_NUDGE_SETTING, FIRED_CAP, GITHUB_STATUS_CAP, REGISTRATION_CAP, REVIEW_SEEN_CAP, INTRODUCED_CAP, REVIEW_STEER_CAP;
30760
+ var execFileAsync4, PR_REF_RE2, LIMIT_FENCE_PREFIX, LIMIT_HINT_PREFIX, LIMIT_RESUME_GRACE_MS, LIMIT_RESUME_HEADROOM_PERCENT, LIMIT_HEADROOM_MIN_FAULT_AGE_MS, REPORT_FENCE_PREFIX, REPORT_HINT_PREFIX, SNOOZE_FENCE_PREFIX, SNOOZE_HINT_PREFIX, HEARTBEAT_FENCE_PREFIX, HEARTBEAT_HINT_PREFIX, STOP_HOOK_FENCE_PREFIX, STOP_HOOK_HINT_KEY, COMPACT_FENCE_PREFIX, COMPACT_HINT_KEY, TIMER_FENCE_PREFIX, TIMER_HINT_PREFIX, SHELL_FENCE_PREFIX, PR_WATCH_FENCE_PREFIX, PR_WATCH_EXPIRED_FENCE_PREFIX, PR_WATCH_POLL_MS, SIGNOFF_FENCE_PREFIX, SIGNOFF_HINT_KEY, SIGNOFF_NUDGE_MAX, PARK_BUMP_MAX, SIGNOFF_NUDGE_SETTING, PARK_FENCE_PREFIX, FIRED_CAP, GITHUB_STATUS_CAP, REGISTRATION_CAP, REVIEW_SEEN_CAP, INTRODUCED_CAP, REVIEW_STEER_CAP;
30719
30761
  var init_scheduler = __esm({
30720
30762
  "packages/server/src/scheduler.ts"() {
30721
30763
  "use strict";
@@ -30749,12 +30791,14 @@ var init_scheduler = __esm({
30749
30791
  TIMER_HINT_PREFIX = "timer:";
30750
30792
  SHELL_FENCE_PREFIX = "shell";
30751
30793
  PR_WATCH_FENCE_PREFIX = "prwatch";
30794
+ PR_WATCH_EXPIRED_FENCE_PREFIX = "prwatch-expired";
30752
30795
  PR_WATCH_POLL_MS = 6e4;
30753
30796
  SIGNOFF_FENCE_PREFIX = "signoff";
30754
30797
  SIGNOFF_HINT_KEY = "signoff:rest";
30755
30798
  SIGNOFF_NUDGE_MAX = 2;
30756
30799
  PARK_BUMP_MAX = 3;
30757
30800
  SIGNOFF_NUDGE_SETTING = "signoffNudge";
30801
+ PARK_FENCE_PREFIX = "park";
30758
30802
  FIRED_CAP = 500;
30759
30803
  GITHUB_STATUS_CAP = 200;
30760
30804
  REGISTRATION_CAP = 500;
@@ -34981,6 +35025,7 @@ function createTranscriptFold(identityPrefix = "claude") {
34981
35025
  const pendingTools = /* @__PURE__ */ new Map();
34982
35026
  const agentDispatches = /* @__PURE__ */ new Map();
34983
35027
  const backgroundShells = /* @__PURE__ */ new Map();
35028
+ const consumedShells = /* @__PURE__ */ new Map();
34984
35029
  const backgroundTaskIds = /* @__PURE__ */ new Map();
34985
35030
  const relayedWakesDrawn = /* @__PURE__ */ new Set();
34986
35031
  const childDispatchIds = /* @__PURE__ */ new Map();
@@ -34988,9 +35033,11 @@ function createTranscriptFold(identityPrefix = "claude") {
34988
35033
  const queuedPending = [];
34989
35034
  let deliveredDedupe = null;
34990
35035
  let pendingRest = null;
35036
+ const restEpoch = { n: 0 };
34991
35037
  function flushRest() {
34992
35038
  if (!pendingRest) return;
34993
35039
  out.push(restMessage(pendingRest.sourceId, pendingRest.at));
35040
+ restEpoch.n++;
34994
35041
  pendingRest = null;
34995
35042
  lastAssistantId = null;
34996
35043
  }
@@ -35034,7 +35081,7 @@ function createTranscriptFold(identityPrefix = "claude") {
35034
35081
  if (pendingRest && !(rec.type === "assistant" && typeof rec.message?.id === "string" && rec.message.id === pendingRest.msgId)) {
35035
35082
  flushRest();
35036
35083
  }
35037
- const evs = completionEvents(rec, agentDispatches, backgroundShells, backgroundTaskIds, relayedWakesDrawn);
35084
+ const evs = completionEvents(rec, agentDispatches, backgroundShells, backgroundTaskIds, relayedWakesDrawn, consumedShells, restEpoch);
35038
35085
  let drewEvents = false;
35039
35086
  if (evs.length > 0) {
35040
35087
  attachToolResults(rec, pendingTools, backgroundShells, backgroundTaskIds, childDispatchIds);
@@ -35283,9 +35330,14 @@ ${block.text}` : block.text;
35283
35330
  return {
35284
35331
  ingest,
35285
35332
  finalize,
35333
+ // The SAME window the paged reader returns (latestWindowStart), because this is the OTHER producer:
35334
+ // the /ws push renders from here while the RPC page renders from there, and a push whose head sat
35335
+ // further forward than the page's would splice the human's ask straight back out of a card that had
35336
+ // just anchored on it (reconcileLiveMessages replaces the window wholesale).
35286
35337
  messages: () => {
35287
35338
  const all = projected();
35288
- return all.length > MAX_MESSAGES ? all.slice(-MAX_MESSAGES) : all;
35339
+ const start = latestWindowStart(all);
35340
+ return start === 0 ? all : all.slice(start);
35289
35341
  },
35290
35342
  allMessages: projected,
35291
35343
  consumedBytes: () => offset + Buffer.byteLength(buffer)
@@ -35715,7 +35767,7 @@ function notificationCarrierText(rec) {
35715
35767
  }
35716
35768
  return void 0;
35717
35769
  }
35718
- function completionEvents(rec, dispatches, backgroundShells, backgroundTaskIds, relayedWakesDrawn) {
35770
+ function completionEvents(rec, dispatches, backgroundShells, backgroundTaskIds, relayedWakesDrawn, consumedShells = /* @__PURE__ */ new Map(), restEpoch = { n: 0 }) {
35719
35771
  const carrier = notificationCarrierText(rec);
35720
35772
  const raw2 = carrier === void 0 ? void 0 : splitWakeDeliveries(carrier).map((s) => relayNotificationBlock(s) ?? s).join("\n");
35721
35773
  if (!raw2 || !raw2.includes("<task-notification>")) return [];
@@ -35749,10 +35801,17 @@ function completionEvents(rec, dispatches, backgroundShells, backgroundTaskIds,
35749
35801
  if (!d) {
35750
35802
  const shell = backgroundShells.get(id);
35751
35803
  if (!shell) {
35804
+ const drawn = consumedShells.get(id);
35805
+ if (drawn && restEpoch.n > drawn.epoch) {
35806
+ consumedShells.set(id, { epoch: restEpoch.n, call: drawn.call });
35807
+ out.push({ role: "assistant", kind: "event", boundary: "wake", text: backgroundWakeLabel(drawn.call, status, block), tools: [], parts: [], at });
35808
+ continue;
35809
+ }
35752
35810
  relayedFallback();
35753
35811
  continue;
35754
35812
  }
35755
35813
  backgroundShells.delete(id);
35814
+ consumedShells.set(id, { epoch: restEpoch.n, call: shell.call });
35756
35815
  const elapsedMs2 = elapsedBetween(shell.at, rec.timestamp);
35757
35816
  shell.call.status = status === "completed" ? "completed" : status === "killed" ? "cancelled" : "failed";
35758
35817
  if (elapsedMs2 !== void 0) shell.call.durationMs = elapsedMs2;
@@ -36108,8 +36167,30 @@ function parseCodexTranscript(raw2, identityPrefix = "codex") {
36108
36167
  const out = projectCodexTranscript(raw2, identityPrefix);
36109
36168
  return latestTranscriptWindow(out);
36110
36169
  }
36170
+ function latestWindowStart(messages) {
36171
+ const tail = Math.max(0, messages.length - MAX_MESSAGES);
36172
+ if (tail === 0) return 0;
36173
+ let boundary = -1;
36174
+ for (let i = tail - 1; i >= 0; i--) {
36175
+ const m = messages[i];
36176
+ if (m.role === "user" && !m.wake && !m.queued) {
36177
+ boundary = i;
36178
+ break;
36179
+ }
36180
+ }
36181
+ if (boundary < 0) return tail;
36182
+ let start = tail;
36183
+ let bytes = 0;
36184
+ while (start > boundary && tail - start < LATEST_WINDOW_ASK_REACH_ITEMS) {
36185
+ const nextBytes = messageBytes(messages[start - 1]);
36186
+ if (bytes + nextBytes > LATEST_WINDOW_ASK_REACH_BYTES) break;
36187
+ start--;
36188
+ bytes += nextBytes;
36189
+ }
36190
+ return start === boundary ? start : tail;
36191
+ }
36111
36192
  function latestTranscriptWindow(messages) {
36112
- const start = Math.max(0, messages.length - MAX_MESSAGES);
36193
+ const start = latestWindowStart(messages);
36113
36194
  if (start === 0) return [...messages];
36114
36195
  const pinned = [];
36115
36196
  for (let i = 0; i < start; i++) {
@@ -37002,7 +37083,7 @@ function readLatestThreadTranscriptPage(project, storage, slug, backendFor) {
37002
37083
  }
37003
37084
  }
37004
37085
  const latest = latestTranscriptWindow(projected);
37005
- const canonicalStart = Math.max(0, projected.length - MAX_MESSAGES);
37086
+ const canonicalStart = latestWindowStart(projected);
37006
37087
  const row = storage.getSession(slug);
37007
37088
  const pageMessages = row ? projectDeliveryLedger(latest, parseDeliveryLedger(row.delivery_ledger)) : latest;
37008
37089
  return {
@@ -37143,7 +37224,7 @@ function readTranscriptFile(absPath) {
37143
37224
  return [];
37144
37225
  }
37145
37226
  }
37146
- var MAX_MESSAGES, NOISE_PREFIXES, INTERRUPT_MARKERS, REST_LABEL, LEGACY_TASK_MARKER, LEGACY_BANNER_PREAMBLE, COMMAND_NAME_RE, COMMAND_ARGS_RE, PEER_SESSION_PREAMBLE, QUEUED_STALE_MS, EDIT_CAP, TRUNC_MARKER, ENCRYPTED_PAYLOAD, AGENT_PROMPT_CAP, SEND_BODY_CAP, COMMAND_CAP, TOOL_INPUT_CAP, READ_LINE_CAP, READ_BYTE_CAP, IMAGE_MEDIA_EXT, SCREENSHOT_CACHE_DIR, SCREENSHOT_CACHE_MAX, SCREENSHOT_MAX_BASE64, screenshotTmpSeq, SENT_IMAGE_MAX_BYTES, SENT_FILES_MAX, TODO_ROWS_MAX, TODO_TEXT_CAP, TRANSCRIPT_CACHE_CAP, transcriptCache, PARSE_VERIFY_SAMPLE, TRANSCRIPT_DECODE_WINDOW, FOREIGN_SESSION_ID_RE, MAX_PINNED_BACKGROUND_OPERATIONS, CAPTION_MAX, _defaultCodexBackend, TRANSCRIPT_EARLIER_MAX_ITEMS, TRANSCRIPT_EARLIER_MAX_BYTES, fullDigestCache, FULL_DIGEST_CACHE_CAP, EMPTY_RETIRED_OPS;
37227
+ var MAX_MESSAGES, LATEST_WINDOW_ASK_REACH_ITEMS, LATEST_WINDOW_ASK_REACH_BYTES, NOISE_PREFIXES, INTERRUPT_MARKERS, REST_LABEL, LEGACY_TASK_MARKER, LEGACY_BANNER_PREAMBLE, COMMAND_NAME_RE, COMMAND_ARGS_RE, PEER_SESSION_PREAMBLE, QUEUED_STALE_MS, EDIT_CAP, TRUNC_MARKER, ENCRYPTED_PAYLOAD, AGENT_PROMPT_CAP, SEND_BODY_CAP, COMMAND_CAP, TOOL_INPUT_CAP, READ_LINE_CAP, READ_BYTE_CAP, IMAGE_MEDIA_EXT, SCREENSHOT_CACHE_DIR, SCREENSHOT_CACHE_MAX, SCREENSHOT_MAX_BASE64, screenshotTmpSeq, SENT_IMAGE_MAX_BYTES, SENT_FILES_MAX, TODO_ROWS_MAX, TODO_TEXT_CAP, TRANSCRIPT_CACHE_CAP, transcriptCache, PARSE_VERIFY_SAMPLE, TRANSCRIPT_DECODE_WINDOW, FOREIGN_SESSION_ID_RE, MAX_PINNED_BACKGROUND_OPERATIONS, CAPTION_MAX, _defaultCodexBackend, TRANSCRIPT_EARLIER_MAX_ITEMS, TRANSCRIPT_EARLIER_MAX_BYTES, fullDigestCache, FULL_DIGEST_CACHE_CAP, EMPTY_RETIRED_OPS;
37147
37228
  var init_transcript = __esm({
37148
37229
  "packages/server/src/transcript.ts"() {
37149
37230
  "use strict";
@@ -37158,6 +37239,8 @@ var init_transcript = __esm({
37158
37239
  init_bash_background();
37159
37240
  init_frizz_paths();
37160
37241
  MAX_MESSAGES = 300;
37242
+ LATEST_WINDOW_ASK_REACH_ITEMS = 100;
37243
+ LATEST_WINDOW_ASK_REACH_BYTES = 512 * 1024;
37161
37244
  NOISE_PREFIXES = ["<task-notification>", "[SYSTEM NOTIFICATION", "<system-reminder>", "<frizz-", "[frizz]"];
37162
37245
  INTERRUPT_MARKERS = ["[Request interrupted by user]", "[Request interrupted by user for tool use]"];
37163
37246
  REST_LABEL = "Agent rested";