frizz-server 0.15.23 → 0.15.25

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 (98) hide show
  1. package/dist/dev-child.js +355 -222
  2. package/package.json +1 -1
  3. package/web-dist/assets/{abnfDiagram-VRR7QNED-BW_CIF9J.js → abnfDiagram-VRR7QNED-BUGGhGcL.js} +1 -1
  4. package/web-dist/assets/{arc-B475yv46.js → arc-DlmiB5Kw.js} +1 -1
  5. package/web-dist/assets/architecture-TIHT7OUA-DvYaN-Hu.js +1 -0
  6. package/web-dist/assets/{architectureDiagram-ZJ3FMSHR-DkPvFjFp.js → architectureDiagram-ZJ3FMSHR-m4afIjbT.js} +1 -1
  7. package/web-dist/assets/{blockDiagram-677ZJIJ3-Rls7Nttu.js → blockDiagram-677ZJIJ3-Kz2humn5.js} +1 -1
  8. package/web-dist/assets/{c4Diagram-LMCZKHZV-maV0q2bv.js → c4Diagram-LMCZKHZV-SeHCCIRv.js} +1 -1
  9. package/web-dist/assets/channel-Og7O24Xl.js +1 -0
  10. package/web-dist/assets/{chunk-32BRIVSS-Coi7L0de.js → chunk-32BRIVSS-Cs4BaciL.js} +1 -1
  11. package/web-dist/assets/{chunk-52WLFC77-DZaDe-48.js → chunk-52WLFC77-Hrl54hxN.js} +1 -1
  12. package/web-dist/assets/{chunk-C7G6YPKG-DNQ-JhD9.js → chunk-C7G6YPKG-C_LwXwMT.js} +1 -1
  13. package/web-dist/assets/{chunk-EX3LRPZG-BQCebwoA.js → chunk-EX3LRPZG-s0-pq_ja.js} +1 -1
  14. package/web-dist/assets/{chunk-FWX5IMBZ-CqiHtpEB.js → chunk-FWX5IMBZ-Ci_L6bRR.js} +2 -2
  15. package/web-dist/assets/{chunk-HOUHSVGY-Bop69PFg.js → chunk-HOUHSVGY-Cn43tXaT.js} +1 -1
  16. package/web-dist/assets/{chunk-ICXQ74PX-D3PkeZ6B.js → chunk-ICXQ74PX-DaehSUDb.js} +1 -1
  17. package/web-dist/assets/{chunk-MOJQB5TN-lidXw8-C.js → chunk-MOJQB5TN-BOLvHrOY.js} +1 -1
  18. package/web-dist/assets/{chunk-OGEWGWER-CzD91FAh.js → chunk-OGEWGWER-B7N0H72R.js} +1 -1
  19. package/web-dist/assets/{chunk-PUDLZKDR-Cilkzvck.js → chunk-PUDLZKDR-D9Tpv5xu.js} +1 -1
  20. package/web-dist/assets/{chunk-Q4XR5HBZ-C37UQCfb.js → chunk-Q4XR5HBZ-CZxzEfvh.js} +1 -1
  21. package/web-dist/assets/{chunk-RYQCIY6F-C0qnX-Ux.js → chunk-RYQCIY6F-CH35JHMA.js} +1 -1
  22. package/web-dist/assets/{chunk-V7JOEXUC-Da5vOGHX.js → chunk-V7JOEXUC-28qGatq3.js} +1 -1
  23. package/web-dist/assets/{chunk-VAUOI2AC-FrJadrJY.js → chunk-VAUOI2AC-CkO9u5CM.js} +1 -1
  24. package/web-dist/assets/{chunk-VR4S4FIN-DYxK2Z50.js → chunk-VR4S4FIN-BFszczwO.js} +1 -1
  25. package/web-dist/assets/{chunk-WYO6CB5R-C164KTfT.js → chunk-WYO6CB5R-CuBoterc.js} +1 -1
  26. package/web-dist/assets/{chunk-XXDRQBXY-D4wHtxet.js → chunk-XXDRQBXY-8nlDi-YO.js} +1 -1
  27. package/web-dist/assets/{chunk-ZGVPDNZ5-BXKkVljT.js → chunk-ZGVPDNZ5-B5WfIiLa.js} +1 -1
  28. package/web-dist/assets/classDiagram-OUVF2IWQ-DBua7nWT.js +1 -0
  29. package/web-dist/assets/classDiagram-v2-EOCWNBFH-DBua7nWT.js +1 -0
  30. package/web-dist/assets/{cose-bilkent-JH36ORCC-BFSgVzNT.js → cose-bilkent-JH36ORCC-Csxg0M3P.js} +1 -1
  31. package/web-dist/assets/{cynefin-VYW2F7L2-BSoEbT-s.js → cynefin-VYW2F7L2-DY-oaly-.js} +1 -1
  32. package/web-dist/assets/{cynefinDiagram-TSTJHNR4-BBZgLbdX.js → cynefinDiagram-TSTJHNR4-Baev5OIz.js} +1 -1
  33. package/web-dist/assets/{dagre-VKFMJZFB-Dx6giO7u.js → dagre-VKFMJZFB-VV2KSHL4.js} +1 -1
  34. package/web-dist/assets/{diagram-FQU43EPY-BMI3qRca.js → diagram-FQU43EPY-rLuhqzIC.js} +1 -1
  35. package/web-dist/assets/{diagram-G47NLZAW-CJY-MA3L.js → diagram-G47NLZAW-BBRVU5Kr.js} +1 -1
  36. package/web-dist/assets/{diagram-NH7WQ7WH-CbQVFJ3r.js → diagram-NH7WQ7WH-BMSjTTHf.js} +1 -1
  37. package/web-dist/assets/{diagram-OA4YK3LP-CPnrakdY.js → diagram-OA4YK3LP-7NO_QsSx.js} +1 -1
  38. package/web-dist/assets/{diagram-WEI45ONY-D9DNwzlo.js → diagram-WEI45ONY-evQWfr1h.js} +1 -1
  39. package/web-dist/assets/{dist-CO93_xVa.js → dist-BoiR_HZa.js} +1 -1
  40. package/web-dist/assets/{ebnfDiagram-CCIWWBDH-DNHudB8r.js → ebnfDiagram-CCIWWBDH-BGCXILB-.js} +1 -1
  41. package/web-dist/assets/{erDiagram-Q63AITRT-Cwt63Xz8.js → erDiagram-Q63AITRT-CJSi0YB2.js} +1 -1
  42. package/web-dist/assets/eventmodeling-45OFAUF4-CMWWvyUk.js +1 -0
  43. package/web-dist/assets/flowDiagram-23GEKE2U-CFOr0Vr_.js +1 -0
  44. package/web-dist/assets/{ganttDiagram-NO4QXBWP-DiFpSJc2.js → ganttDiagram-NO4QXBWP-DfKGoGJe.js} +1 -1
  45. package/web-dist/assets/{gitGraph-TEB2WS4Q-2-hTvDOu.js → gitGraph-TEB2WS4Q-DDOEbCVa.js} +1 -1
  46. package/web-dist/assets/{gitGraphDiagram-IHSO6WYX-mbc4Il71.js → gitGraphDiagram-IHSO6WYX-tKMl1T5T.js} +1 -1
  47. package/web-dist/assets/index-DYhjAs_a.js +354 -0
  48. package/web-dist/assets/index-htGvcLfn.css +2 -0
  49. package/web-dist/assets/{info-DKCQHKI2-BO0ZUEnn.js → info-DKCQHKI2-BqMXJJEF.js} +1 -1
  50. package/web-dist/assets/{infoDiagram-FWYZ7A6U-Cn3DmnjS.js → infoDiagram-FWYZ7A6U-DXpmORxt.js} +1 -1
  51. package/web-dist/assets/{ishikawaDiagram-FXEZZL3T-Ckwh42aC.js → ishikawaDiagram-FXEZZL3T-D9yCb_iG.js} +1 -1
  52. package/web-dist/assets/{journeyDiagram-5HDEW3XC-D26ySLLD.js → journeyDiagram-5HDEW3XC-DwyXsO1X.js} +1 -1
  53. package/web-dist/assets/{kanban-definition-HUTT4EX6-CXKwXGxe.js → kanban-definition-HUTT4EX6-C-BupP2L.js} +1 -1
  54. package/web-dist/assets/{line-BHqhqZTw.js → line-CVohBkYV.js} +1 -1
  55. package/web-dist/assets/{linear-Xq8ICqCe.js → linear-CUS0dsDf.js} +1 -1
  56. package/web-dist/assets/{mermaid-parser.core-ByagDKoG.js → mermaid-parser.core-BaUI30hf.js} +3 -3
  57. package/web-dist/assets/{mermaid.core-CEwbkqrb.js → mermaid.core-DRwFVBM9.js} +3 -3
  58. package/web-dist/assets/{mindmap-definition-LN4V7U3C-C3S3nEo5.js → mindmap-definition-LN4V7U3C-Dwz7wXjl.js} +1 -1
  59. package/web-dist/assets/{packet-7NZHBO7P-BjcgqWf7.js → packet-7NZHBO7P-Cc8S-JIW.js} +1 -1
  60. package/web-dist/assets/{pegDiagram-2B236MQR-Due7wPzY.js → pegDiagram-2B236MQR-DEvuejFX.js} +1 -1
  61. package/web-dist/assets/{pie-RZYD4A2V-C1X-Fm2K.js → pie-RZYD4A2V-lDSko9Ox.js} +1 -1
  62. package/web-dist/assets/{pieDiagram-ENE6RG2P-CbKS5jFv.js → pieDiagram-ENE6RG2P-DoIHahh_.js} +1 -1
  63. package/web-dist/assets/{quadrantDiagram-ABIIQ3AL-CVF1CNNO.js → quadrantDiagram-ABIIQ3AL-ttYUswX_.js} +1 -1
  64. package/web-dist/assets/{radar-I7S5WNFK-Co7Otr5p.js → radar-I7S5WNFK-Dlai2VWL.js} +1 -1
  65. package/web-dist/assets/{railroad-3IZDKUUU-BTwZqHtN.js → railroad-3IZDKUUU-CfOvvQCL.js} +1 -1
  66. package/web-dist/assets/railroad-abnf-AHOZXSZD-Br3ohVd5.js +1 -0
  67. package/web-dist/assets/railroad-ebnf-EBAXGLYW-DFaY0AnY.js +1 -0
  68. package/web-dist/assets/railroad-peg-LSFZ7HO6-Cy2FBxXb.js +1 -0
  69. package/web-dist/assets/{railroadDiagram-RFXS5EU6-DCymlvVU.js → railroadDiagram-RFXS5EU6-BRpMN8zD.js} +1 -1
  70. package/web-dist/assets/{requirementDiagram-TGXJPOKE-CEFbAu30.js → requirementDiagram-TGXJPOKE-C1f5KNrx.js} +1 -1
  71. package/web-dist/assets/{sankeyDiagram-HTMAVEWB-fnX7WrAj.js → sankeyDiagram-HTMAVEWB-CjXPLZ2A.js} +1 -1
  72. package/web-dist/assets/{sequenceDiagram-DBY2YBRQ-BUvsAYkQ.js → sequenceDiagram-DBY2YBRQ-BxVxwb4e.js} +1 -1
  73. package/web-dist/assets/{src-V5VjKILh.js → src-DjcREoQq.js} +1 -1
  74. package/web-dist/assets/{stateDiagram-2N3HPSRC-C9LJKbHM.js → stateDiagram-2N3HPSRC-CCx9rSmu.js} +1 -1
  75. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-bSyE7VSt.js +1 -0
  76. package/web-dist/assets/{swimlanes-5IMT3BWC-aq9B5aBs.js → swimlanes-5IMT3BWC-L3Legvfa.js} +1 -1
  77. package/web-dist/assets/swimlanesDiagram-G3AALYLV-n0v1Z0Xu.js +8 -0
  78. package/web-dist/assets/{timeline-definition-FHXFAJF6-DhJ9Jf4f.js → timeline-definition-FHXFAJF6-0p22547Z.js} +1 -1
  79. package/web-dist/assets/{treeView-QDETBFTQ-D42FrEH4.js → treeView-QDETBFTQ-Xv4-kZ7I.js} +1 -1
  80. package/web-dist/assets/{treemap-6X3UGDF4-BV_9JyG6.js → treemap-6X3UGDF4-MhycNmte.js} +1 -1
  81. package/web-dist/assets/{vennDiagram-L72KCM5P-BkwHzxsN.js → vennDiagram-L72KCM5P-P33JCPMu.js} +1 -1
  82. package/web-dist/assets/{wardley-OPB4EBWU-fwm52ei3.js → wardley-OPB4EBWU-CS41M7I-.js} +1 -1
  83. package/web-dist/assets/{wardleyDiagram-EHGQE667-D_UqvgcE.js → wardleyDiagram-EHGQE667-CbQaI_DG.js} +1 -1
  84. package/web-dist/assets/{xychartDiagram-FW5EYKEG-tKdmcTfS.js → xychartDiagram-FW5EYKEG-tg1lQQ6H.js} +1 -1
  85. package/web-dist/index.html +2 -2
  86. package/web-dist/assets/architecture-TIHT7OUA-wAP7tOoN.js +0 -1
  87. package/web-dist/assets/channel-Clg0HJPA.js +0 -1
  88. package/web-dist/assets/classDiagram-OUVF2IWQ-23bCXfvg.js +0 -1
  89. package/web-dist/assets/classDiagram-v2-EOCWNBFH-23bCXfvg.js +0 -1
  90. package/web-dist/assets/eventmodeling-45OFAUF4-Q3Nq1ecc.js +0 -1
  91. package/web-dist/assets/flowDiagram-23GEKE2U-B2snV3ot.js +0 -1
  92. package/web-dist/assets/index-C6AXnNpg.css +0 -2
  93. package/web-dist/assets/index-DewBdeY0.js +0 -352
  94. package/web-dist/assets/railroad-abnf-AHOZXSZD-CEkmgARz.js +0 -1
  95. package/web-dist/assets/railroad-ebnf-EBAXGLYW-D2dXzrFd.js +0 -1
  96. package/web-dist/assets/railroad-peg-LSFZ7HO6-yA2XVaxN.js +0 -1
  97. package/web-dist/assets/stateDiagram-v2-6OUMAXLB-Bm1cPGFT.js +0 -1
  98. package/web-dist/assets/swimlanesDiagram-G3AALYLV-DR6Yx_Ic.js +0 -8
package/dist/dev-child.js CHANGED
@@ -14227,7 +14227,7 @@ function parseGithubWakeSteer(text2) {
14227
14227
  function frizzRoute(path3) {
14228
14228
  return `${FRIZZ_ROUTE_PREFIX}${path3.startsWith("/") ? path3 : `/${path3}`}`;
14229
14229
  }
14230
- 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, ClaudeModel, CODEX_MODELS_FALLBACK, AcpAgent, ACP_MODEL_PREFIX, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_SCALAR_KINDS, AWAITING_STEPS_MAX, AWAITING_STEP_VALUE_MAX, AWAITING_QUESTIONS_MAX, AWAITING_TITLE_MAX, AWAITING_STATUSES, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, ISSUE_WATCH_SPENT_TRAILER, ISSUE_WATCH_ARMED_TRAILER, LEGACY_PR_WATCH_ARMED_TRAILER, LEGACY_ISSUE_WATCH_ARMED_TRAILER, INTERRUPT_ENDED_LEAD, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, PARK_CORRECTION_NEEDS_INPUT_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, COMMAND_WATCH_EVERY_MIN_MS, COMMAND_WATCH_EVERY_MAX_MS, COMMAND_WATCH_FOR_MAX_MS, COMMAND_WATCH_MAX_ARMED, COMMAND_WATCH_COMMAND_MAX, COMMAND_WATCH_OUTPUT_MAX_BYTES, COMMAND_WATCH_RUN_TIMEOUT_MS, CommandWatchRun, CommandWatchState, CommandWatchView, AddOwnCommandWatchInput, AddOwnCommandWatchResult, DropOwnCommandWatchInput, DropOwnCommandWatchResult, ListOwnCommandWatchesInput, OwnCommandWatchesResult, QUESTION_FENCE_RETIRED_AT, NEEDS_INPUT_REQUIRED_AT, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, SECRET_ANSWER_PREFIX, SECRET_TTL_MS, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, SettledQuestionView, ThreadSettledQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, ANSWER_CONTINUATION_INDENT, indentAnswerContinuation, ANSWER_PICK_PREFIX, DISMISSED_ANSWER, ANSWERS_NOTE_HEADER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, SteerThreadInput, SteerThreadResult, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, UpgradeThreadModelInput, UpgradeThreadModelResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, MERGED_WAKE_LEAD, MERGED_WAKE_HEADING, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, CROSS_SESSION_PREAMBLE, CROSS_SESSION_WRAPPER, CROSS_SESSION_GUIDANCE, XML_ATTRIBUTE, THREAD_MESSAGE_OPEN, THREAD_MESSAGE_CLOSE, THREAD_MESSAGE_GUIDANCE, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, ProjectRailCounts, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
14230
+ 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, ClaudeModel, CODEX_MODELS_FALLBACK, AcpAgent, ACP_MODEL_PREFIX, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, RETIRED_AWAITING_REPLACEMENT, AWAITING_KEY_RE, AWAITING_YAML_KEYS, AWAITING_SEQUENCE_KEYS, AWAITING_HINT_MAX, AWAITING_HINT_VALUE_MAX, AWAITING_SCALAR_KINDS, AWAITING_STEPS_MAX, AWAITING_STEP_VALUE_MAX, AWAITING_QUESTIONS_MAX, AWAITING_TITLE_MAX, AWAITING_STATUSES, AWAITING_ITEM_KINDS, AWAITING_DURATION_RE, DURATION_UNIT_MS, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, INTERRUPT_MARKERS, ALLDONE_SENTINEL, OPT_OUT_NOTE, OVER_QUESTION_NOTE, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TIMER_MAX_ARMED, TimerPromptText, PR_WATCH_SPENT_TRAILER, PR_WATCH_ARMED_TRAILER, SHELL_DONE_TRAILER, ISSUE_WATCH_SPENT_TRAILER, ISSUE_WATCH_ARMED_TRAILER, LEGACY_PR_WATCH_ARMED_TRAILER, LEGACY_ISSUE_WATCH_ARMED_TRAILER, INTERRUPT_ENDED_LEAD, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT, PARK_CORRECTION_NAMES_LEAD, PARK_CORRECTION_RETIRED_LEAD, PARK_CORRECTION_QUESTION_LEAD, PARK_CORRECTION_NEEDS_INPUT_LEAD, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, CommandWatchRun, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PR_WATCH_MAX_ARMED, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, OWN_WATCH_MAX_ARMED, COMMAND_WATCH_EVERY_MIN_MS, COMMAND_WATCH_EVERY_MAX_MS, COMMAND_WATCH_FOR_MAX_MS, COMMAND_WATCH_MAX_ARMED, COMMAND_WATCH_COMMAND_MAX, COMMAND_WATCH_OUTPUT_MAX_BYTES, COMMAND_WATCH_RUN_TIMEOUT_MS, CommandWatchState, CommandWatchView, AddOwnCommandWatchInput, AddOwnCommandWatchResult, DropOwnCommandWatchInput, DropOwnCommandWatchResult, ListOwnCommandWatchesInput, OwnCommandWatchesResult, QUESTION_FENCE_RETIRED_AT, NEEDS_INPUT_REQUIRED_AT, AskQuestionKind, ASK_MAX_DEPTH, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, SECRET_ANSWER_PREFIX, SECRET_TTL_MS, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, SettledQuestionView, ThreadSettledQuestionsResult, BURIED_ANSWERS_HEADER, ANSWER_FOLLOW_UP_MARKER, ANSWER_CONTINUATION_INDENT, indentAnswerContinuation, ANSWER_PICK_PREFIX, DISMISSED_ANSWER, ANSWERS_NOTE_HEADER, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, SteerThreadInput, SteerThreadResult, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, UpgradeThreadModelInput, UpgradeThreadModelResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, GITHUB_DISPATCH_UI_BOUNDARY, WAKE_TIME_HEADER_TAIL, WAKE_TRAILERS, HUMAN_GAP_FLOOR_MS, HUMAN_GAP_NOTE_TAIL, WAKE_DELIVERY_TOKEN_TAIL, WAKE_DELIVERY_TOKEN_LINE, MERGED_WAKE_LEAD, MERGED_WAKE_HEADING, NOISE_PREFIXES, AGENT_MESSAGE_WRAPPER, CROSS_SESSION_PREAMBLE, CROSS_SESSION_WRAPPER, CROSS_SESSION_GUIDANCE, XML_ATTRIBUTE, THREAD_MESSAGE_OPEN, THREAD_MESSAGE_CLOSE, THREAD_MESSAGE_GUIDANCE, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REVIEW_LEAD, WAKE_BACKLOG_TAIL, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, WAKE_ITEM, WAKE_MORE, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, ProjectRailCounts, PROJECT_ICON_EXTENSIONS, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT, threadIdentityName;
14231
14231
  var init_src = __esm({
14232
14232
  "packages/shared/src/index.ts"() {
14233
14233
  "use strict";
@@ -14675,7 +14675,7 @@ var init_src = __esm({
14675
14675
  PARK_CORRECTION_RETIRED_LEAD = "\u26D4 Your ```awaiting fence uses ";
14676
14676
  PARK_CORRECTION_QUESTION_LEAD = "\u26A0\uFE0F Your ```awaiting fence landed while ";
14677
14677
  PARK_CORRECTION_NEEDS_INPUT_LEAD = "\u26A0\uFE0F Your ```awaiting fence gives no ";
14678
- ThreadWatchKind = external_exports.enum(["shell", "github", "timer"]);
14678
+ ThreadWatchKind = external_exports.enum(["shell", "github", "timer", "command"]);
14679
14679
  GithubChecksState = external_exports.enum(["none", "running", "passing", "failing"]);
14680
14680
  GithubMergeState = external_exports.enum(["mergeable", "blocked", "conflicting", "unknown"]);
14681
14681
  GithubWatchStatus = external_exports.object({
@@ -14729,6 +14729,12 @@ var init_src = __esm({
14729
14729
  /** When frizz last heard from GitHub, for the same reason `GithubWatchStatus.polledAt` carries it. */
14730
14730
  polledAt: external_exports.string()
14731
14731
  }).strict();
14732
+ CommandWatchRun = external_exports.object({
14733
+ at: external_exports.string(),
14734
+ outcome: external_exports.enum(["exit", "timeout", "signal", "error"]),
14735
+ exitCode: external_exports.number().int().nullable(),
14736
+ output: external_exports.string()
14737
+ }).strict();
14732
14738
  ThreadWatchView = external_exports.object({
14733
14739
  id: external_exports.string(),
14734
14740
  kind: ThreadWatchKind,
@@ -14750,7 +14756,19 @@ var init_src = __esm({
14750
14756
  * worker's prompt is the row's NAME (a `tmr_…` id names nothing to a human) and the fire instant is
14751
14757
  * its status. Unlike `github` there is no polled half to be absent: a timer that exists is fully
14752
14758
  * known, so a timer row always carries this. */
14753
- timer: external_exports.object({ fireAt: external_exports.string(), prompt: external_exports.string() }).strict().optional()
14759
+ timer: external_exports.object({ fireAt: external_exports.string(), prompt: external_exports.string() }).strict().optional(),
14760
+ /** `command` rows only: the armed command watch's registration and its last check — what the worker's
14761
+ * own read-back carries (CommandWatchView), minus the id and state the row already has. The command is
14762
+ * the row's NAME and the cadence its status. Until 2026-10-13 a command watch had no row at all, so a
14763
+ * thread parked on nothing else drew a card that said only "Waiting for an external update." */
14764
+ command: external_exports.object({
14765
+ command: external_exports.string(),
14766
+ /** The cadence in the house duration grammar (`2m`, `1h 30m`). */
14767
+ every: external_exports.string(),
14768
+ expiresAt: external_exports.string(),
14769
+ runs: external_exports.number().int().nonnegative(),
14770
+ lastRun: CommandWatchRun.optional()
14771
+ }).strict().optional()
14754
14772
  }).strict();
14755
14773
  PR_WATCH_FOR_MAX_MS = 365 * 24 * 60 * 60 * 1e3;
14756
14774
  PR_WATCH_DEFAULT_FOR_MS = 30 * 24 * 60 * 60 * 1e3;
@@ -14889,12 +14907,6 @@ var init_src = __esm({
14889
14907
  COMMAND_WATCH_COMMAND_MAX = 4e3;
14890
14908
  COMMAND_WATCH_OUTPUT_MAX_BYTES = 4096;
14891
14909
  COMMAND_WATCH_RUN_TIMEOUT_MS = 2 * 6e4;
14892
- CommandWatchRun = external_exports.object({
14893
- at: external_exports.string(),
14894
- outcome: external_exports.enum(["exit", "timeout", "signal", "error"]),
14895
- exitCode: external_exports.number().int().nullable(),
14896
- output: external_exports.string()
14897
- }).strict();
14898
14910
  CommandWatchState = external_exports.enum(["armed", "dropped", "passed", "expired", "cancelled"]);
14899
14911
  CommandWatchView = external_exports.object({
14900
14912
  id: external_exports.string(),
@@ -23434,7 +23446,8 @@ function createClaudeRuntimeIngest(deps) {
23434
23446
  live2.set(item.sessionId, {
23435
23447
  turn: signal ?? prior?.turn,
23436
23448
  at: now,
23437
- events: (prior?.events ?? 0) + 1
23449
+ events: (prior?.events ?? 0) + 1,
23450
+ messageId: signal === void 0 ? prior?.messageId : signal === "running" && item.event.kind === "assistant" ? item.event.messageId : void 0
23438
23451
  });
23439
23452
  deps.receipts?.publish({ type: "claude.runtime.event", slug: item.slug, sessionId: item.sessionId, kind: item.event.kind });
23440
23453
  if (item.event.kind === "task") {
@@ -23499,9 +23512,10 @@ function createClaudeRuntimeIngest(deps) {
23499
23512
  }
23500
23513
  };
23501
23514
  }
23502
- function resolveRuntimeTurn(folded, backstopped, runtime, ageMs = 0) {
23515
+ function resolveRuntimeTurn(folded, backstopped, runtime, ageMs = 0, caughtUp = false) {
23503
23516
  if (!runtime) return folded;
23504
23517
  if (runtime === "running" && folded === "idle") {
23518
+ if (caughtUp) return "idle";
23505
23519
  return ageMs > RUNNING_OVERRIDE_MAX_AGE_MS ? "idle" : "in-flight";
23506
23520
  }
23507
23521
  if (runtime === "settled" && backstopped) return "idle";
@@ -26158,6 +26172,7 @@ function applyRecord(state, rec) {
26158
26172
  state.interrupted = void 0;
26159
26173
  state.localCommandDone = void 0;
26160
26174
  if (typeof rec.timestamp === "string") state.lastAssistantAt = rec.timestamp;
26175
+ state.lastAssistantUuid = typeof rec.uuid === "string" ? rec.uuid : void 0;
26161
26176
  state.lastStopReason = typeof rec.message?.stop_reason === "string" ? rec.message.stop_reason : void 0;
26162
26177
  const observedModel = typeof rec.message?.model === "string" ? rec.message.model.trim() : "";
26163
26178
  if (observedModel && observedModel !== "<synthetic>") {
@@ -26435,7 +26450,8 @@ function createTailer(deps) {
26435
26450
  if (!deps.runtimeLiveness || !isBrokerClaudeRow(row)) return detailed.turn;
26436
26451
  const live2 = deps.runtimeLiveness(row.session_id);
26437
26452
  const ageMs = live2 ? Math.max(0, nowMs - live2.at) : 0;
26438
- return resolveRuntimeTurn(detailed.turn, detailed.backstopped, live2?.turn, ageMs);
26453
+ const caughtUp = live2?.messageId !== void 0 && live2.messageId === state.lastAssistantUuid;
26454
+ return resolveRuntimeTurn(detailed.turn, detailed.backstopped, live2?.turn, ageMs, caughtUp);
26439
26455
  }
26440
26456
  function chaseRuntime(row, state, foldAdvanced) {
26441
26457
  if (!deps.runtimeLiveness || !isBrokerClaudeRow(row)) return false;
@@ -35554,7 +35570,7 @@ var init_codex_app_server = __esm({
35554
35570
  approvalsReviewer: "user",
35555
35571
  ...this.resumeSandboxOverride(binding),
35556
35572
  ...cwdOverride,
35557
- ...holdsExec ? {} : this.threadConfig(threadSlug, sessionId)
35573
+ ...holdsExec ? {} : this.resumeRebuild(threadSlug, sessionId)
35558
35574
  });
35559
35575
  const response = ThreadResponse.parse(rawResponse);
35560
35576
  if (response.thread.id !== binding.codex_thread_id || response.thread.ephemeral) {
@@ -35595,7 +35611,7 @@ var init_codex_app_server = __esm({
35595
35611
  * with it, no thread is released to be rebuilt, and each keeps whatever its last load gave it.
35596
35612
  */
35597
35613
  syncsThreadConfig() {
35598
- return !this.unsubscribeRefused && process.env.FRIZZ_PLUGIN_SYNC_OFF !== "1" && (this.options.frizzMcp !== void 0 || this.options.workerConfig !== void 0);
35614
+ return !this.unsubscribeRefused && process.env.FRIZZ_PLUGIN_SYNC_OFF !== "1" && (this.options.frizzMcp !== void 0 || this.options.workerConfig !== void 0 || this.options.workerBaseInstructions !== void 0);
35599
35615
  }
35600
35616
  // Adopt a rollout this bridge did NOT create — a legacy Codex thread from before the app-server
35601
35617
  // cutover, dispatched as its own `codex` process (its `agent_session_id` is the on-disk rollout id).
@@ -35630,7 +35646,9 @@ var init_codex_app_server = __esm({
35630
35646
  // Adoption is the first time frizz owns this rollout, so it is also the first chance to give
35631
35647
  // the thread a frizz MCP mount that knows its slug, and its hooks. The rollout belonged to a
35632
35648
  // process of its own, so this app-server is loading it cold and the bag is what it is built from.
35633
- ...this.threadConfig(input.threadSlug, input.sessionId)
35649
+ // And its contract: a pre-app-server thread carried it only in its FIRST PROMPT, so its rollout's
35650
+ // saved base instructions are Codex's default, and a compaction took the contract with it.
35651
+ ...this.resumeRebuild(input.threadSlug, input.sessionId)
35634
35652
  });
35635
35653
  const response = ThreadResponse.parse(rawResponse);
35636
35654
  if (response.thread.id !== input.codexThreadId || response.thread.ephemeral) {
@@ -36485,7 +36503,7 @@ var init_codex_app_server = __esm({
36485
36503
  // commands and ended every one of them instead (measured 2026-10-11 on codex 0.161.0: a
36486
36504
  // `sleep` alive after the old server left was gone after the new server's warm-up). Without
36487
36505
  // the bag the same resume only rejoins.
36488
- ...execThreads.includes(row.codex_thread_id) ? {} : this.threadConfig(row.thread_slug, row.frizz_session_id)
36506
+ ...execThreads.includes(row.codex_thread_id) ? {} : this.resumeRebuild(row.thread_slug, row.frizz_session_id)
36489
36507
  });
36490
36508
  const response = ThreadResponse.parse(rawResponse);
36491
36509
  if (response.thread.id !== row.codex_thread_id || response.thread.ephemeral) throw new Error("resume ownership mismatch");
@@ -36682,6 +36700,31 @@ var init_codex_app_server = __esm({
36682
36700
  };
36683
36701
  return Object.keys(config).length ? { config } : {};
36684
36702
  }
36703
+ /**
36704
+ * Everything a `thread/resume` that may rebuild the thread sends of this server's own: the bag, and
36705
+ * the worker contract as `baseInstructions`. Both or neither, because they obey the same rule.
36706
+ *
36707
+ * The contract has to ride every such resume. Codex picks a resumed thread's base instructions from
36708
+ * the request first, then from `model_instructions_file` / `instructions` in any config.toml layer,
36709
+ * and only then from the rollout's saved `session_meta` (codex 0.161.0 `config/mod.rs:4003-4011`,
36710
+ * `session/mod.rs:722-756`). Sending none handed a worker whose operator set either key that file in
36711
+ * place of the Frizz contract, after every restart and for the rest of its life — measured against a
36712
+ * fake model (`instructions` read the file's text on the first turn after the resume and after a
36713
+ * compaction), and fixed by this (the same run reads the contract).
36714
+ *
36715
+ * And it has to stay off the resumes the bag stays off. Codex counts ANY `baseInstructions` on a
36716
+ * resume of a loaded thread as an override (`thread_processor.rs:218`, no comparison with what the
36717
+ * thread already has), and an override on a loaded, idle thread nobody subscribes to tears it down
36718
+ * and rebuilds it (`resume_running_thread`, `:4350-4380`) — the very rebuild that ends a background
36719
+ * command. Measured on 0.161.0 with `_live_codex_config_sync.mts`: a restart-reconcile rejoin that
36720
+ * carried `baseInstructions` and no bag ended the thread's `sleep`, which survives the same rejoin
36721
+ * carrying neither. So a rejoin of a thread holding one carries neither; it is loaded already, with
36722
+ * its instructions, and the rebuild before its next turn (startTurn) brings the contract in.
36723
+ */
36724
+ resumeRebuild(threadSlug, sessionId) {
36725
+ const baseInstructions = this.options.workerBaseInstructions?.(threadSlug, sessionId);
36726
+ return { ...this.threadConfig(threadSlug, sessionId), ...baseInstructions ? { baseInstructions } : {} };
36727
+ }
36685
36728
  /** An idle thread on this connection that still runs on a previous server's config and can be
36686
36729
  * rebuilt now — see threadConfig. */
36687
36730
  threadConfigIsBehind(binding) {
@@ -39060,9 +39103,10 @@ exactly ONE of them.
39060
39103
  followed COLD by someone who has read nothing else; frizz reads them verbatim. Steps NAME THE HUMAN
39061
39104
  as the wait, so the fence needs no other name and no \`for:\` (add one only if you want to be woken
39062
39105
  to re-check), and it always puts the thread in their queue \u2014 \`status:\` may be left out. Their
39063
- card, headed **Awaiting human**, shows the steps over one **Done** button, and its click comes
39064
- back to you as their reply, \`Done\`, the same as anything they type; anything else they need to
39065
- tell you \u2014 a step that failed, the account they used \u2014 comes as a message of their own. An act is not a decision: never ask
39106
+ card, headed **Awaiting human**, shows the steps over one checkbox, **I have completed this**;
39107
+ ticked and sent, it comes back to you as their reply, \`Done\`, the same as anything they type,
39108
+ with any note they typed under it; anything else they need to tell you \u2014 a step that failed, the
39109
+ account they used \u2014 comes as that note or a message of their own. An act is not a decision: never ask
39066
39110
  WHETHER they will do it, and never sign off \`done\` while it is still owed.
39067
39111
  **THE FIRST TEST OF EVERY STEP: COULD YOU DO IT YOURSELF? THEN IT IS NOT A STEP \u2014 DO IT.** This
39068
39112
  outranks every rule below. Steps are what is LEFT after you have done all you can: a command you
@@ -41141,6 +41185,15 @@ function composePrompt(sessionId, prompt, kind = "claude") {
41141
41185
 
41142
41186
  ${DISPATCH_TASK_BANNER_MARKER}${prompt}`;
41143
41187
  }
41188
+ function claudeWorkerSystemPrompt(sessionId, projectDir) {
41189
+ return [loadWorkerPrompt("claude"), scratchpadOrientation(sessionId, "claude"), frizzConfigBlock(projectDir)].filter(Boolean).join("\n\n");
41190
+ }
41191
+ function codexWorkerBaseInstructions(sessionId, projectDir) {
41192
+ return [loadWorkerPrompt("codex"), scratchpadOrientation(sessionId, "codex"), frizzConfigBlock(projectDir)].filter(Boolean).join("\n\n");
41193
+ }
41194
+ function acpWorkerContract(sessionId, projectDir) {
41195
+ return [loadWorkerPrompt("acp"), scratchpadOrientation(sessionId, "acp"), frizzConfigBlock(projectDir)].filter(Boolean).join("\n\n");
41196
+ }
41144
41197
  function scratchpadOrientation(sessionId, kind = "claude") {
41145
41198
  const children2 = kind === "codex" ? "native sub-agents share it, so give each its own file" : "name it in a sub-agent's prompt when you want its notes back, and give each child its own file";
41146
41199
  return `SCRATCH DIRECTORY: .frizz/threads/${sessionId}/ \u2014 yours, free-form, as many files as you like, and nothing is expected in it. A single direct task usually needs none; writing notes is never a substitute for doing the work (${children2}). Nothing in this directory is read automatically; if you want a note back after a compaction, \`frizzctl goal start --post-compaction\` re-sends a prompt of your choosing.`;
@@ -41305,6 +41358,7 @@ function createDispatcher(deps) {
41305
41358
  const readBoardSource = deps.readBoard ?? readBoard;
41306
41359
  const frizzDir = join36(deps.project.dir, ".frizz");
41307
41360
  const adoptionRuntime = deps.adoptionRuntime ?? productionRuntime;
41361
+ const inFlightSlugs = /* @__PURE__ */ new Set();
41308
41362
  function cleanupPrewrites(built) {
41309
41363
  for (const path3 of new Set(built.prewrite.map((file) => file.path))) {
41310
41364
  try {
@@ -41334,181 +41388,181 @@ function createDispatcher(deps) {
41334
41388
  }
41335
41389
  const title = input.title?.trim() || fallbackTitle(input.prompt);
41336
41390
  const base = input.slug ?? slugify(title);
41337
- const slug = resolveSlug(frizzDir, base, (s) => deps.storage.getSession(s) !== void 0);
41338
- const registryTitle = title;
41339
- const sessionId = randomUUID13();
41340
- const permissionMode = workerDispatchPermission(kind, settings);
41341
- const model = input.model ?? settings.model;
41342
- const effort = input.effort ?? settings.effort;
41343
- const scratchRel = writeScratchDir(deps.project.dir, sessionId);
41344
- const prompt = composePrompt(sessionId, input.prompt, kind);
41345
- if (kind === "codex") {
41346
- const bridge = deps.codexAppServer;
41347
- if (!bridge) {
41348
- cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41349
- throw new Error("Codex app-server is unavailable; cannot start this thread. Check that `codex` is installed and its app-server protocol matches the pinned revision (re-pin if you upgraded codex).");
41350
- }
41351
- const extraSystemPrompt = [scratchpadOrientation(sessionId, kind), frizzConfigBlock(deps.project.dir)].filter(Boolean).join("\n\n");
41352
- try {
41353
- const spawned = await bridge.spawnDispatch({
41354
- threadSlug: slug,
41355
- sessionId,
41356
- cwd: deps.project.dir,
41357
- prompt,
41358
- model,
41359
- effort,
41360
- sandbox: codexSandbox(permissionMode),
41361
- baseInstructions: [loadWorkerPrompt("codex"), extraSystemPrompt].filter(Boolean).join("\n\n"),
41362
- developerInstructions: CODEX_FIRST_OUTPUT_TITLE_DEVELOPER_INSTRUCTIONS
41363
- // No `config` here: the hooks and the reasoning summary are the bridge's to send
41364
- // (codexWorkerThreadConfig), with this start and with every resume after it.
41365
- });
41366
- deps.storage.upsertSession({
41367
- slug,
41368
- session_id: sessionId,
41369
- thread_name: threadIdentityName(slug),
41370
- spawned_at: (/* @__PURE__ */ new Date()).toISOString(),
41371
- last_read_at: null,
41372
- unread: 0,
41373
- exited: 0,
41374
- archived: 0,
41375
- rested_at: null,
41376
- title_auto: input.title?.trim() ? 0 : 1,
41377
- title_locked: 0,
41378
- // a caller's hard-coded title is not a human's — the worker may rename it
41379
- title: registryTitle,
41380
- state: "open",
41381
- meta: null,
41382
- seen_at: null,
41383
- transcript_id: null,
41384
- model: model ?? null,
41385
- effort: effort ?? null,
41386
- permission_mode: permissionMode
41387
- });
41388
- deps.storage.setBackend(slug, "codex");
41389
- deps.storage.setAgentSession(slug, spawned.binding.codexSessionId);
41390
- deps.storage.setCodexRuntime(slug, "app-server");
41391
- deps.storage.setWorkerSurface(slug, "cli");
41392
- void deps.board.rebuild().catch(() => {
41393
- });
41394
- return { slug, sessionId };
41395
- } catch (err) {
41391
+ const slug = resolveSlug(frizzDir, base, (s) => inFlightSlugs.has(s) || deps.storage.getSession(s) !== void 0);
41392
+ inFlightSlugs.add(slug);
41393
+ try {
41394
+ const registryTitle = title;
41395
+ const sessionId = randomUUID13();
41396
+ const permissionMode = workerDispatchPermission(kind, settings);
41397
+ const model = input.model ?? settings.model;
41398
+ const effort = input.effort ?? settings.effort;
41399
+ const scratchRel = writeScratchDir(deps.project.dir, sessionId);
41400
+ const prompt = composePrompt(sessionId, input.prompt, kind);
41401
+ if (kind === "codex") {
41402
+ const bridge = deps.codexAppServer;
41403
+ if (!bridge) {
41404
+ cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41405
+ throw new Error("Codex app-server is unavailable; cannot start this thread. Check that `codex` is installed and its app-server protocol matches the pinned revision (re-pin if you upgraded codex).");
41406
+ }
41396
41407
  try {
41397
- bridge.releaseSession(slug, sessionId, "session-deleted");
41398
- } catch {
41408
+ const spawned = await bridge.spawnDispatch({
41409
+ threadSlug: slug,
41410
+ sessionId,
41411
+ cwd: deps.project.dir,
41412
+ prompt,
41413
+ model,
41414
+ effort,
41415
+ sandbox: codexSandbox(permissionMode),
41416
+ baseInstructions: codexWorkerBaseInstructions(sessionId, deps.project.dir),
41417
+ developerInstructions: CODEX_FIRST_OUTPUT_TITLE_DEVELOPER_INSTRUCTIONS
41418
+ // No `config` here: the hooks and the reasoning summary are the bridge's to send
41419
+ // (codexWorkerThreadConfig), with this start and with every resume after it.
41420
+ });
41421
+ deps.storage.upsertSession({
41422
+ slug,
41423
+ session_id: sessionId,
41424
+ thread_name: threadIdentityName(slug),
41425
+ spawned_at: (/* @__PURE__ */ new Date()).toISOString(),
41426
+ last_read_at: null,
41427
+ unread: 0,
41428
+ exited: 0,
41429
+ archived: 0,
41430
+ rested_at: null,
41431
+ title_auto: input.title?.trim() ? 0 : 1,
41432
+ title_locked: 0,
41433
+ // a caller's hard-coded title is not a human's — the worker may rename it
41434
+ title: registryTitle,
41435
+ state: "open",
41436
+ meta: null,
41437
+ seen_at: null,
41438
+ transcript_id: null,
41439
+ model: model ?? null,
41440
+ effort: effort ?? null,
41441
+ permission_mode: permissionMode
41442
+ });
41443
+ deps.storage.setBackend(slug, "codex");
41444
+ deps.storage.setAgentSession(slug, spawned.binding.codexSessionId);
41445
+ deps.storage.setCodexRuntime(slug, "app-server");
41446
+ deps.storage.setWorkerSurface(slug, "cli");
41447
+ void deps.board.rebuild().catch(() => {
41448
+ });
41449
+ return { slug, sessionId };
41450
+ } catch (err) {
41451
+ try {
41452
+ bridge.releaseSession(slug, sessionId, "session-deleted");
41453
+ } catch {
41454
+ }
41455
+ cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41456
+ throw new Error(`Codex app-server could not start this thread: ${err.message}. Check that \`codex\` is installed and its app-server protocol matches the pinned revision (re-pin if you upgraded codex).`);
41399
41457
  }
41400
- cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41401
- throw new Error(`Codex app-server could not start this thread: ${err.message}. Check that \`codex\` is installed and its app-server protocol matches the pinned revision (re-pin if you upgraded codex).`);
41402
- }
41403
- }
41404
- if (kind === "acp") {
41405
- const bridge = deps.acpBridge;
41406
- const agentId = acpAgentIdFromModel(model);
41407
- if (!bridge || !agentId) {
41408
- cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41409
- throw new Error(!bridge ? "The ACP bridge is unavailable; cannot start this thread." : `An ACP dispatch needs an agent: pick one in the composer (model \`acp:<agent>\`), got ${JSON.stringify(model ?? null)}.`);
41410
41458
  }
41411
- const firstPrompt = [loadWorkerPrompt("acp"), scratchpadOrientation(sessionId, kind), frizzConfigBlock(deps.project.dir), prompt].filter(Boolean).join("\n\n");
41412
- try {
41413
- const spawned = await bridge.spawnDispatch({ threadSlug: slug, sessionId, cwd: deps.project.dir, agentId, modelId: acpModelIdFromModel(model), prompt: firstPrompt, userText: input.prompt });
41414
- deps.storage.upsertSession({
41415
- slug,
41416
- session_id: sessionId,
41417
- thread_name: threadIdentityName(slug),
41418
- spawned_at: (/* @__PURE__ */ new Date()).toISOString(),
41419
- last_read_at: null,
41420
- unread: 0,
41421
- exited: 0,
41422
- archived: 0,
41423
- rested_at: null,
41424
- title_auto: input.title?.trim() ? 0 : 1,
41425
- title_locked: 0,
41426
- title: registryTitle,
41427
- state: "open",
41428
- meta: null,
41429
- seen_at: null,
41430
- transcript_id: null,
41431
- model: model ?? null,
41432
- effort: null,
41433
- permission_mode: permissionMode
41434
- });
41435
- deps.storage.setBackend(slug, "acp");
41436
- deps.storage.setAgentSession(slug, spawned.acpSessionId);
41437
- deps.storage.setAcpAgent(slug, agentId);
41438
- deps.storage.setWorkerSurface(slug, "cli");
41439
- void deps.board.rebuild().catch(() => {
41440
- });
41441
- return { slug, sessionId };
41442
- } catch (err) {
41459
+ if (kind === "acp") {
41460
+ const bridge = deps.acpBridge;
41461
+ const agentId = acpAgentIdFromModel(model);
41462
+ if (!bridge || !agentId) {
41463
+ cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41464
+ throw new Error(!bridge ? "The ACP bridge is unavailable; cannot start this thread." : `An ACP dispatch needs an agent: pick one in the composer (model \`acp:<agent>\`), got ${JSON.stringify(model ?? null)}.`);
41465
+ }
41466
+ const firstPrompt = [acpWorkerContract(sessionId, deps.project.dir), prompt].filter(Boolean).join("\n\n");
41443
41467
  try {
41444
- bridge.releaseSession(slug, sessionId, "session-deleted");
41445
- } catch {
41468
+ const spawned = await bridge.spawnDispatch({ threadSlug: slug, sessionId, cwd: deps.project.dir, agentId, modelId: acpModelIdFromModel(model), prompt: firstPrompt, userText: input.prompt });
41469
+ deps.storage.upsertSession({
41470
+ slug,
41471
+ session_id: sessionId,
41472
+ thread_name: threadIdentityName(slug),
41473
+ spawned_at: (/* @__PURE__ */ new Date()).toISOString(),
41474
+ last_read_at: null,
41475
+ unread: 0,
41476
+ exited: 0,
41477
+ archived: 0,
41478
+ rested_at: null,
41479
+ title_auto: input.title?.trim() ? 0 : 1,
41480
+ title_locked: 0,
41481
+ title: registryTitle,
41482
+ state: "open",
41483
+ meta: null,
41484
+ seen_at: null,
41485
+ transcript_id: null,
41486
+ model: model ?? null,
41487
+ effort: null,
41488
+ permission_mode: permissionMode
41489
+ });
41490
+ deps.storage.setBackend(slug, "acp");
41491
+ deps.storage.setAgentSession(slug, spawned.acpSessionId);
41492
+ deps.storage.setAcpAgent(slug, agentId);
41493
+ deps.storage.setWorkerSurface(slug, "cli");
41494
+ void deps.board.rebuild().catch(() => {
41495
+ });
41496
+ return { slug, sessionId };
41497
+ } catch (err) {
41498
+ try {
41499
+ bridge.releaseSession(slug, sessionId, "session-deleted");
41500
+ } catch {
41501
+ }
41502
+ cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41503
+ throw new Error(`The ACP agent could not start this thread: ${err.message}`);
41446
41504
  }
41447
- cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41448
- throw new Error(`The ACP agent could not start this thread: ${err.message}`);
41449
41505
  }
41450
- }
41451
- if (kind === "claude") {
41452
- const bridge = deps.claudeBroker;
41453
- if (!bridge) {
41454
- cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41455
- throw new Error("Claude session broker is unavailable; cannot start this thread.");
41456
- }
41457
- const appendSystemPrompt = [
41458
- loadWorkerPrompt("claude"),
41459
- scratchpadOrientation(sessionId, kind),
41460
- frizzConfigBlock(deps.project.dir)
41461
- ].filter(Boolean).join("\n\n");
41462
- try {
41463
- await bridge.spawnDispatch({
41464
- threadSlug: slug,
41465
- sessionId,
41466
- cwd: deps.project.dir,
41467
- prompt,
41468
- permissionMode,
41469
- appendSystemPrompt,
41470
- model,
41471
- effort
41472
- });
41473
- deps.storage.upsertSession({
41474
- slug,
41475
- session_id: sessionId,
41476
- thread_name: threadIdentityName(slug),
41477
- spawned_at: (/* @__PURE__ */ new Date()).toISOString(),
41478
- last_read_at: null,
41479
- unread: 0,
41480
- exited: 0,
41481
- archived: 0,
41482
- rested_at: null,
41483
- title_auto: input.title?.trim() ? 0 : 1,
41484
- title_locked: 0,
41485
- // a caller's hard-coded title is not a human's — the worker may rename it
41486
- title: registryTitle,
41487
- state: "open",
41488
- meta: null,
41489
- seen_at: null,
41490
- transcript_id: null,
41491
- model: model ?? null,
41492
- effort: effort ?? null,
41493
- permission_mode: permissionMode
41494
- });
41495
- deps.storage.setBackend(slug, "claude");
41496
- deps.storage.setClaudeRuntime(slug, "broker");
41497
- deps.storage.setWorkerSurface(slug, "cli");
41498
- void deps.board.rebuild().catch(() => {
41499
- });
41500
- return { slug, sessionId };
41501
- } catch (err) {
41506
+ if (kind === "claude") {
41507
+ const bridge = deps.claudeBroker;
41508
+ if (!bridge) {
41509
+ cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41510
+ throw new Error("Claude session broker is unavailable; cannot start this thread.");
41511
+ }
41512
+ const appendSystemPrompt = claudeWorkerSystemPrompt(sessionId, deps.project.dir);
41502
41513
  try {
41503
- bridge.releaseSession(slug, sessionId, "session-deleted");
41504
- } catch {
41514
+ await bridge.spawnDispatch({
41515
+ threadSlug: slug,
41516
+ sessionId,
41517
+ cwd: deps.project.dir,
41518
+ prompt,
41519
+ permissionMode,
41520
+ appendSystemPrompt,
41521
+ model,
41522
+ effort
41523
+ });
41524
+ deps.storage.upsertSession({
41525
+ slug,
41526
+ session_id: sessionId,
41527
+ thread_name: threadIdentityName(slug),
41528
+ spawned_at: (/* @__PURE__ */ new Date()).toISOString(),
41529
+ last_read_at: null,
41530
+ unread: 0,
41531
+ exited: 0,
41532
+ archived: 0,
41533
+ rested_at: null,
41534
+ title_auto: input.title?.trim() ? 0 : 1,
41535
+ title_locked: 0,
41536
+ // a caller's hard-coded title is not a human's — the worker may rename it
41537
+ title: registryTitle,
41538
+ state: "open",
41539
+ meta: null,
41540
+ seen_at: null,
41541
+ transcript_id: null,
41542
+ model: model ?? null,
41543
+ effort: effort ?? null,
41544
+ permission_mode: permissionMode
41545
+ });
41546
+ deps.storage.setBackend(slug, "claude");
41547
+ deps.storage.setClaudeRuntime(slug, "broker");
41548
+ deps.storage.setWorkerSurface(slug, "cli");
41549
+ void deps.board.rebuild().catch(() => {
41550
+ });
41551
+ return { slug, sessionId };
41552
+ } catch (err) {
41553
+ try {
41554
+ bridge.releaseSession(slug, sessionId, "session-deleted");
41555
+ } catch {
41556
+ }
41557
+ cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41558
+ throw new Error(`Claude session broker could not start this thread: ${err.message}.`);
41505
41559
  }
41506
- cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41507
- throw new Error(`Claude session broker could not start this thread: ${err.message}.`);
41508
41560
  }
41561
+ cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41562
+ throw new Error(`unsupported backend for dispatch: ${String(kind)}`);
41563
+ } finally {
41564
+ inFlightSlugs.delete(slug);
41509
41565
  }
41510
- cleanupDispatchFiles(scratchRel, { argv: [], env: {}, prewrite: [] }, sessionId);
41511
- throw new Error(`unsupported backend for dispatch: ${String(kind)}`);
41512
41566
  },
41513
41567
  // ---- PROMOTION: the human steered one of their own terminals, so it becomes a frizz thread ----
41514
41568
  //
@@ -41678,12 +41732,7 @@ function createDispatcher(deps) {
41678
41732
  cwd: deps.project.dir,
41679
41733
  prompt,
41680
41734
  permissionMode,
41681
- appendSystemPrompt: [
41682
- loadWorkerPrompt("claude"),
41683
- scratchpadOrientation(sessionId),
41684
- frizzConfigBlock(deps.project.dir),
41685
- adoption
41686
- ].filter(Boolean).join("\n\n"),
41735
+ appendSystemPrompt: [claudeWorkerSystemPrompt(sessionId, deps.project.dir), adoption].filter(Boolean).join("\n\n"),
41687
41736
  model: settings.model,
41688
41737
  effort: settings.effort
41689
41738
  });
@@ -42040,7 +42089,7 @@ function bgSnoozeArmed(row) {
42040
42089
  function hasParkedExternalWait(tele, _nowMs) {
42041
42090
  return false;
42042
42091
  }
42043
- function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], armedTimers = [], armedWatches = [], issueBook = {}) {
42092
+ function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], armedTimers = [], armedWatches = [], issueBook = {}, armedCommands = []) {
42044
42093
  const seen = /* @__PURE__ */ new Set();
42045
42094
  const out = [];
42046
42095
  for (const t of armedTimers) {
@@ -42057,6 +42106,26 @@ function fenceWatchViews(slug, tele, fenceAt, github = {}, registered = [], arme
42057
42106
  timer: { fireAt: t.fireAt, prompt: t.prompt }
42058
42107
  });
42059
42108
  }
42109
+ for (const w of armedCommands) {
42110
+ if (w.state !== "armed" || seen.has(`command:${w.id}`)) continue;
42111
+ seen.add(`command:${w.id}`);
42112
+ out.push({
42113
+ id: `command:${slug}:${w.id}`,
42114
+ kind: "command",
42115
+ target: w.id,
42116
+ state: "armed",
42117
+ createdAt: new Date(w.created_at).toISOString(),
42118
+ // The same projection as the worker's read-back (router commandWatchView), so the row and
42119
+ // `frizzctl watch-command` name one cadence and one last check.
42120
+ command: {
42121
+ command: w.command,
42122
+ every: formatElapsed(w.every_ms),
42123
+ expiresAt: new Date(w.expires_at).toISOString(),
42124
+ runs: w.runs,
42125
+ ...w.last_run_at !== null && w.last_outcome ? { lastRun: { at: new Date(w.last_run_at).toISOString(), outcome: w.last_outcome, exitCode: w.last_exit, output: w.last_output ?? "" } } : {}
42126
+ }
42127
+ });
42128
+ }
42060
42129
  for (const w of registered) {
42061
42130
  const subject = w.kind === "issue" ? "issue" : "pull";
42062
42131
  const dedupe = `github:${subject}:${w.target}`;
@@ -42392,7 +42461,7 @@ function sessionThreadView(projectDir, storage, row, rawTele, registeredLegacyTe
42392
42461
  // become the github rows, `watch:` lines the shell rows — so this strip lists exactly what will
42393
42462
  // actually wake the thread, and the two cannot drift into claiming different things. There is no
42394
42463
  // registry behind either any more (`thread_watch`, retired 2026-08-14).
42395
- watches: fenceWatchViews(row.slug, tele, tele?.lastAssistantAt, github, armedPrWatches, armedTimers, armedWatches, issueBook),
42464
+ watches: fenceWatchViews(row.slug, tele, tele?.lastAssistantAt, github, armedPrWatches, armedTimers, armedWatches, issueBook, armedCommandWatches),
42396
42465
  pendingAsk: tele?.pendingAsk ? { questions: tele.pendingAsk.questions } : void 0,
42397
42466
  pendingQuestion: tele?.pendingQuestion ?? false,
42398
42467
  questions,
@@ -46241,15 +46310,31 @@ function parseAcpRecord(line) {
46241
46310
  }
46242
46311
  if (!raw2 || typeof raw2 !== "object" || typeof raw2.kind !== "string") return void 0;
46243
46312
  const kind = raw2.kind;
46244
- if (kind === "acp-session" || kind === "acp-note" || EVENT_KINDS.has(kind)) return raw2;
46313
+ if (kind === "acp-session" || kind === "acp-note" || kind === "acp-contract" || EVENT_KINDS.has(kind)) return raw2;
46245
46314
  return void 0;
46246
46315
  }
46247
46316
  function parseAcpLine(line) {
46248
46317
  const rec = parseAcpRecord(line);
46249
- if (!rec || rec.kind === "acp-session" || rec.kind === "acp-note") return [];
46318
+ if (!rec || rec.kind === "acp-session" || rec.kind === "acp-note" || rec.kind === "acp-contract") return [];
46250
46319
  const { messageId: _m2, acp: _a2, ...ev } = rec;
46251
46320
  return [ev];
46252
46321
  }
46322
+ function acpContractOwed(path3) {
46323
+ let text2;
46324
+ try {
46325
+ text2 = readFileSync25(path3, "utf8");
46326
+ } catch {
46327
+ return false;
46328
+ }
46329
+ let owed = false;
46330
+ for (const line of text2.split("\n")) {
46331
+ if (!line.includes('"compaction"') && !line.includes('"acp-contract"')) continue;
46332
+ const rec = parseAcpRecord(line);
46333
+ if (rec?.kind === "compaction") owed = true;
46334
+ else if (rec?.kind === "acp-contract") owed = false;
46335
+ }
46336
+ return owed;
46337
+ }
46253
46338
  function createAcpBackend(opts) {
46254
46339
  return {
46255
46340
  kind: "acp",
@@ -46341,6 +46426,7 @@ function projectAcpTranscript(raw2, identityPrefix = "acp") {
46341
46426
  switch (rec.kind) {
46342
46427
  case "acp-session":
46343
46428
  case "acp-note":
46429
+ case "acp-contract":
46344
46430
  case "title":
46345
46431
  case "context-usage":
46346
46432
  case "compaction":
@@ -46565,7 +46651,10 @@ var init_acp_types = __esm({
46565
46651
  external_exports.object({ sessionUpdate: external_exports.literal("usage_update"), used: external_exports.number(), size: external_exports.number(), cost: external_exports.unknown().optional() }).passthrough(),
46566
46652
  external_exports.object({ sessionUpdate: external_exports.literal("session_info_update"), title: external_exports.string().nullish(), updatedAt: external_exports.string().nullish() }).passthrough(),
46567
46653
  external_exports.object({ sessionUpdate: external_exports.literal("config_option_update"), configOptions: external_exports.array(AcpConfigOption) }).passthrough(),
46568
- external_exports.object({ sessionUpdate: external_exports.literal("compaction_update"), compactionId: external_exports.string().optional(), status: external_exports.string().optional() }).passthrough()
46654
+ external_exports.object({ sessionUpdate: external_exports.literal("compaction_update"), compactionId: external_exports.string().optional(), status: external_exports.string().optional() }).passthrough(),
46655
+ // The summary streaming in while a compaction runs — sent only to a client that advertised
46656
+ // `session.compaction`, which the bridge does. The `completed` update is the one that matters.
46657
+ external_exports.object({ sessionUpdate: external_exports.literal("compaction_summary_chunk"), compactionId: external_exports.string().optional() }).passthrough()
46569
46658
  ]);
46570
46659
  AcpSessionNotification = external_exports.object({ sessionId: external_exports.string(), update: external_exports.unknown() }).passthrough();
46571
46660
  AcpStopReason = external_exports.enum(["end_turn", "max_tokens", "max_turn_requests", "refusal", "cancelled"]);
@@ -47107,6 +47196,14 @@ var init_acp_host = __esm({
47107
47196
  function clip3(text2, max) {
47108
47197
  return text2.length > max ? `${text2.slice(0, max - 1)}\u2026` : text2;
47109
47198
  }
47199
+ function isSlashCommand(text2) {
47200
+ return /^\/[\w-]+(?:\s|$)/.test(text2.trimStart());
47201
+ }
47202
+ function opencodeCompactionStatus(update) {
47203
+ const meta = update?._meta?.["opencode/compaction"];
47204
+ const status = meta?.status;
47205
+ return typeof status === "string" ? status : void 0;
47206
+ }
47110
47207
  function frizzMcpServer(mcp, slug) {
47111
47208
  if (!mcp) return [];
47112
47209
  return [{
@@ -47235,6 +47332,7 @@ var init_acp_bridge = __esm({
47235
47332
  host() {
47236
47333
  return this.options.host ?? (this.options.spawn ? directAcpHost(this.options.spawn) : daemonAcpHost);
47237
47334
  }
47335
+ /** `dispatch` marks the open that starts a brand-new thread, whose first prompt already IS the contract. */
47238
47336
  async open(input) {
47239
47337
  const { threadSlug: slug, sessionId, cwd } = input;
47240
47338
  const agent = this.resolveAgent(input.agentId);
@@ -47244,6 +47342,7 @@ var init_acp_bridge = __esm({
47244
47342
  const attachment = await this.host()({ stateDir: this.options.stateDir, threadSlug: slug, sessionId, command: agent.bin, args: agent.args, cwd, env });
47245
47343
  const proc = attachment.process;
47246
47344
  const writer = new AcpTranscriptWriter(acpTranscriptPath(this.options.stateDir, sessionId));
47345
+ const owedFromTranscript = acpContractOwed(writer.path);
47247
47346
  let live2;
47248
47347
  let isReady = false;
47249
47348
  const held = [];
@@ -47280,14 +47379,16 @@ var init_acp_bridge = __esm({
47280
47379
  try {
47281
47380
  init = AcpInitializeResult.parse(await conn.request("initialize", {
47282
47381
  protocolVersion: ACP_PROTOCOL_VERSION,
47283
- clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
47382
+ // `session.compaction` asks for the standard `compaction_update`; without it opencode reports a
47383
+ // compaction only through `_meta` on a `session_info_update` (both are handled below).
47384
+ clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false, session: { compaction: {} } },
47284
47385
  clientInfo: { name: "frizz", version: "0" }
47285
47386
  }));
47286
47387
  } catch (err) {
47287
47388
  return fail(err instanceof Error ? new Error(`${agent.label} did not complete the ACP handshake: ${err.message}`) : err);
47288
47389
  }
47289
47390
  const mcpServers = this.options.mountsMcp?.(slug) === false ? [] : frizzMcpServer(this.options.frizzMcp, slug);
47290
- live2 = { slug, sessionId, cwd, agent, conn, init, acpSessionId: "", writer, loading: false, queue: [], pendingPerms: /* @__PURE__ */ new Map(), tools: /* @__PURE__ */ new Map(), exited: false, detaching: false };
47391
+ live2 = { slug, sessionId, cwd, agent, conn, init, acpSessionId: "", writer, loading: false, queue: [], pendingPerms: /* @__PURE__ */ new Map(), tools: /* @__PURE__ */ new Map(), exited: false, detaching: false, ...owedFromTranscript ? { contractOwed: "compaction" } : {} };
47291
47392
  void conn.exited.then(() => {
47292
47393
  if (live2) this.onExit(live2);
47293
47394
  });
@@ -47343,6 +47444,7 @@ var init_acp_bridge = __esm({
47343
47444
  if (resumed === "fresh") {
47344
47445
  writer.append({ kind: "acp-session", at: this.now(), agent: { id: agent.id, ...init.agentInfo?.name ? { name: init.agentInfo.name } : {}, ...init.agentInfo?.version ? { version: init.agentInfo.version } : {} }, acpSessionId: live2.acpSessionId, cwd, ...live2.model ? { model: live2.model } : {} });
47345
47446
  if (input.acpSessionId) writer.append({ kind: "acp-note", at: this.now(), text: `Resumed with a fresh ${agent.label} session: the previous one (${input.acpSessionId}) could not be loaded, so the agent no longer has the earlier conversation.` });
47447
+ if (!input.dispatch) live2.contractOwed = "fresh-session";
47346
47448
  }
47347
47449
  if (modelProblem) writer.append({ kind: "acp-note", at: this.now(), text: modelProblem });
47348
47450
  this.sessions.set(sessionId, live2);
@@ -47474,7 +47576,7 @@ var init_acp_bridge = __esm({
47474
47576
  // ---- public API --------------------------------------------------------------------------------
47475
47577
  async spawnDispatch(input) {
47476
47578
  if (this.closed) throw new Error("ACP bridge is closed");
47477
- const { live: live2 } = await this.openOnce(input);
47579
+ const { live: live2 } = await this.openOnce({ ...input, dispatch: true });
47478
47580
  void this.runTurn(live2, input.prompt, input.userText, void 0);
47479
47581
  return this.info(live2);
47480
47582
  }
@@ -47615,6 +47717,15 @@ var init_acp_bridge = __esm({
47615
47717
  async runTurn(live2, sendText, recordText, deliveryId) {
47616
47718
  const turn = this.newTurn(deliveryId);
47617
47719
  live2.turn = turn;
47720
+ const owed = live2.contractOwed;
47721
+ const contract = owed && !isSlashCommand(sendText) ? this.options.contractFor?.(live2.slug, live2.sessionId) : void 0;
47722
+ if (owed && contract) {
47723
+ sendText = `${contract}
47724
+
47725
+ ${sendText}`;
47726
+ live2.contractOwed = void 0;
47727
+ live2.writer.append({ kind: "acp-contract", at: turn.startedAt, reason: owed });
47728
+ }
47618
47729
  live2.writer.append({ kind: "user-message", at: turn.startedAt, text: recordText, synthetic: false });
47619
47730
  live2.writer.append({ kind: "turn-start", at: turn.startedAt });
47620
47731
  this.options.onStatusChange?.();
@@ -47768,19 +47879,32 @@ var init_acp_bridge = __esm({
47768
47879
  return;
47769
47880
  case "session_info_update":
47770
47881
  if (update.title) live2.writer.append({ kind: "title", title: clip3(update.title, 200) });
47882
+ if (opencodeCompactionStatus(update) === "completed") this.compacted(live2);
47771
47883
  return;
47772
47884
  case "config_option_update":
47773
47885
  this.adoptModel(live2, update.configOptions);
47774
47886
  return;
47887
+ case "compaction_update":
47888
+ if (update.status === "completed") this.compacted(live2);
47889
+ return;
47775
47890
  case "plan":
47776
47891
  case "user_message_chunk":
47777
- case "compaction_update":
47892
+ case "compaction_summary_chunk":
47778
47893
  return;
47779
47894
  case "unknown":
47780
47895
  this.diagnostic(live2.slug, "update", `unhandled session/update ${String(update.raw?.sessionUpdate)}`);
47781
47896
  return;
47782
47897
  }
47783
47898
  }
47899
+ /** The agent rewrote its history. The `compaction` record is the fold's (it sets `lastCompactionAt`,
47900
+ * which the post-compaction Goal trigger reads, and the activity clock); the debt is this bridge's.
47901
+ * A `session/load` replays old compactions too, but `handleNotification` drops replays (`loading`). */
47902
+ compacted(live2) {
47903
+ this.flushThought(live2);
47904
+ this.flushText(live2, false);
47905
+ live2.writer.append({ kind: "compaction", at: this.now() });
47906
+ live2.contractOwed = "compaction";
47907
+ }
47784
47908
  recordToolResult(live2, tc) {
47785
47909
  const meta = live2.tools.get(tc.toolCallId);
47786
47910
  if (meta?.status === "completed" || meta?.status === "failed") return;
@@ -74756,11 +74880,7 @@ function deliverCodexWake(deps) {
74756
74880
  }
74757
74881
  function deliverClaudeBrokerWake(deps) {
74758
74882
  const { bridge, slug, cwd, row, settings, deliveryMessage, freshProcess } = deps;
74759
- const appendSystemPrompt = [
74760
- loadWorkerPrompt("claude"),
74761
- scratchpadOrientation(row.session_id, "claude"),
74762
- frizzConfigBlock(cwd)
74763
- ].filter(Boolean).join("\n\n");
74883
+ const appendSystemPrompt = claudeWorkerSystemPrompt(row.session_id, cwd);
74764
74884
  return bridge.followUp({
74765
74885
  threadSlug: slug,
74766
74886
  sessionId: row.session_id,
@@ -74926,13 +75046,18 @@ function createContextUnchecked(opts, resources) {
74926
75046
  // Codex's MCP servers mount PROCESS-wide on the app-server, not per thread, so the descriptor
74927
75047
  // is resolved once here — the codex twin of the per-dispatch resolveFrizzMcp on the claude side.
74928
75048
  frizzMcp: resolveFrizzMcp(frizzMcpTarget),
74929
- // …and only for a thread whose contract taught it the MCP tools: a codex thread keeps the
74930
- // contract it was started with, so one that predates `frizzctl` keeps its mount.
75049
+ // …and only for a thread whose contract taught it the MCP tools. One that predates `frizzctl`
75050
+ // keeps its mount even though its next resume hands it the current contract: its history still
75051
+ // calls the tools, and a thread only rejoined (mid-turn, or holding a background command) runs
75052
+ // on the contract it was loaded with until it is rebuilt.
74931
75053
  mountsMcp: (slug) => workerTaughtMcp(storage.getSession(slug)),
74932
75054
  frizzCli: resolveFrizzCli(frizzMcpTarget),
74933
75055
  // The rest of a worker thread's config — hooks and reasoning summaries — sent with the start
74934
75056
  // and every resume, so a thread that comes back under this server runs on this server's.
74935
75057
  workerConfig: (_slug, sessionId) => codexWorkerThreadConfig(sessionId),
75058
+ // The worker contract, rebuilt for every resume exactly as dispatch builds it for the start —
75059
+ // see codexWorkerBaseInstructions for why a resume that sends none can lose it.
75060
+ workerBaseInstructions: (_slug, sessionId) => codexWorkerBaseInstructions(sessionId, project.dir),
74936
75061
  db: storage.db,
74937
75062
  interactions: storage.interactions,
74938
75063
  codexBin: opts.codexBin,
@@ -74984,6 +75109,11 @@ function createContextUnchecked(opts, resources) {
74984
75109
  interactions: storage.interactions,
74985
75110
  env: process.env,
74986
75111
  customAgents: () => getSettings(storage, home).acpAgents,
75112
+ // Re-sent after the agent compacts and into a fresh session that replaced a lost one (acp-bridge.ts).
75113
+ // Today's contract, not the one the thread was born with: that one is gone from the session. A thread
75114
+ // born under the MCP contract keeps its mount (mountsMcp above), and its agent is forked with
75115
+ // `frizzctl` on PATH like any other (frizzCli above), so the `frizzctl` contract still holds for it.
75116
+ contractFor: (_slug, sessionId) => acpWorkerContract(sessionId, project.dir),
74987
75117
  onStatusChange: () => board?.refresh()
74988
75118
  });
74989
75119
  resources.acpBridge = acpBridge;
@@ -78692,6 +78822,14 @@ function createTranscriptFold(identityPrefix = "claude") {
78692
78822
  const childDispatchIds = /* @__PURE__ */ new Map();
78693
78823
  const dispatchLabels = /* @__PURE__ */ new Map();
78694
78824
  const queuedPending = [];
78825
+ const placed = /* @__PURE__ */ new Set();
78826
+ function place(m) {
78827
+ m.queued = false;
78828
+ if (placed.has(m)) return;
78829
+ placed.add(m);
78830
+ out.push(m);
78831
+ lastAssistantId = null;
78832
+ }
78695
78833
  let deliveredDedupe = null;
78696
78834
  let compactCommandOpen = false;
78697
78835
  let pendingRest = null;
@@ -78728,7 +78866,7 @@ function createTranscriptFold(identityPrefix = "claude") {
78728
78866
  function resolveQueuedThrough(entry) {
78729
78867
  for (const e13 of queuedPending) {
78730
78868
  if (e13 === entry) break;
78731
- e13.message.queued = false;
78869
+ place(e13.message);
78732
78870
  }
78733
78871
  }
78734
78872
  function resolveQueued(key) {
@@ -78736,7 +78874,7 @@ function createTranscriptFold(identityPrefix = "claude") {
78736
78874
  if (!entry) return false;
78737
78875
  if (entry.message.queued) resolveQueuedThrough(entry);
78738
78876
  dropQueued(entry);
78739
- entry.message.queued = false;
78877
+ place(entry.message);
78740
78878
  return true;
78741
78879
  }
78742
78880
  let offset = 0;
@@ -78788,13 +78926,12 @@ function createTranscriptFold(identityPrefix = "claude") {
78788
78926
  if (op2 === "enqueue" && content.trim() && !isInjectedNoise(content) && !dropCorrection(content)) {
78789
78927
  const queuedProjection = userProjection(content, out.length === 0);
78790
78928
  const m = { sourceId, role: "user", text: content, ...queuedProjection, tools: [], parts: [], at: thisTs, queued: true };
78791
- out.push(m);
78792
78929
  queuedPending.push({ key: content, message: m });
78793
78930
  } else if ((op2 === "remove" || op2 === "dequeue" || op2 === "popAll") && content.trim()) {
78794
78931
  const entry = findQueued(content);
78795
78932
  if (entry) {
78796
78933
  resolveQueuedThrough(entry);
78797
- entry.message.queued = false;
78934
+ place(entry.message);
78798
78935
  }
78799
78936
  }
78800
78937
  return;
@@ -79043,7 +79180,9 @@ ${block.text}` : block.text;
79043
79180
  buffer = "";
79044
79181
  }
79045
79182
  function projected() {
79046
- return pendingRest ? [...out, restMessage(pendingRest.sourceId, pendingRest.at)] : out;
79183
+ const waiting = queuedPending.filter((e13) => e13.message.queued && !placed.has(e13.message)).map((e13) => e13.message);
79184
+ if (!pendingRest && waiting.length === 0) return out;
79185
+ return [...out, ...pendingRest ? [restMessage(pendingRest.sourceId, pendingRest.at)] : [], ...waiting];
79047
79186
  }
79048
79187
  return {
79049
79188
  ingest,
@@ -82666,11 +82805,7 @@ ${gapNote}` : input.message;
82666
82805
  if (row?.backend === "claude" && row.claude_runtime === "broker") {
82667
82806
  const bridge = ctx.claudeBroker;
82668
82807
  if (!bridge) throw new Error("Claude session broker is unavailable; cannot deliver this follow-up");
82669
- const appendSystemPrompt = [
82670
- loadWorkerPrompt("claude"),
82671
- scratchpadOrientation(row.session_id, "claude"),
82672
- frizzConfigBlock(ctx.project.dir)
82673
- ].filter(Boolean).join("\n\n");
82808
+ const appendSystemPrompt = claudeWorkerSystemPrompt(row.session_id, ctx.project.dir);
82674
82809
  const midTurn = (ctx.tailer.get(input.slug)?.turn ?? "in-flight") === "in-flight";
82675
82810
  const upgradeCandidate = claudeUpgradeCandidate({ stateDir: ctx.project.stateDir, projectId: ctx.project.id, storage: ctx.storage, telemetry: ctx.tailer.get(input.slug) }, row);
82676
82811
  const upgrade = upgradeCandidate ? claudeModelUpgradeDue(upgradeCandidate, { catalogue: peekClaudeModels(), nowMs: Date.now(), serverStartedAtMs: SERVER_STARTED_AT_MS }) : void 0;
@@ -83103,8 +83238,10 @@ ${gapNote}` : input.message;
83103
83238
  // `isDaemonAlive` above is a check, not a hold: the daemon can exit before this frame lands,
83104
83239
  // and followUp then COLD-RESUMES rather than failing. Take the same floor every other fork
83105
83240
  // takes, so a notice that loses the race cannot be the thing that rebuilds the worker at
83106
- // Claude's `default` — see coldResumePermission.
83241
+ // Claude's `default` — see coldResumePermission — or rebuilds it with no worker contract,
83242
+ // which the daemon it forks would then keep for every later message.
83107
83243
  permissionMode: coldResumePermission(row, ctx.getSettings()),
83244
+ appendSystemPrompt: claudeWorkerSystemPrompt(row.session_id, ctx.project.dir),
83108
83245
  model: row.model ?? void 0,
83109
83246
  effort: row.effort ?? void 0,
83110
83247
  // Frizz's own notice, not the operator's reply to anything: an open card stays open.
@@ -83742,11 +83879,7 @@ ${gapNote}` : input.message;
83742
83879
  }
83743
83880
  const bridge = ctx.claudeBroker;
83744
83881
  if (!bridge) throw new Error("Claude session broker is unavailable; cannot compact this thread");
83745
- const appendSystemPrompt = [
83746
- loadWorkerPrompt("claude"),
83747
- scratchpadOrientation(row.session_id, "claude"),
83748
- frizzConfigBlock(ctx.project.dir)
83749
- ].filter(Boolean).join("\n\n");
83882
+ const appendSystemPrompt = claudeWorkerSystemPrompt(row.session_id, ctx.project.dir);
83750
83883
  await bridge.followUp({
83751
83884
  threadSlug: input.slug,
83752
83885
  sessionId: row.session_id,