@bastani/atomic 0.9.16-alpha.7 → 0.9.16-alpha.9

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 (226) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +4 -4
  4. package/dist/builtin/intercom/broker/broker.ts +617 -22
  5. package/dist/builtin/intercom/broker/client-message-validation.ts +9 -0
  6. package/dist/builtin/intercom/broker/client.ts +200 -10
  7. package/dist/builtin/intercom/broker/pending-send-registry.ts +4 -0
  8. package/dist/builtin/intercom/broker/send-handler.ts +90 -36
  9. package/dist/builtin/intercom/broker/send-signature.ts +20 -8
  10. package/dist/builtin/intercom/group.ts +85 -0
  11. package/dist/builtin/intercom/index.bundle.mjs +746 -55
  12. package/dist/builtin/intercom/package.json +1 -1
  13. package/dist/builtin/intercom/session-target.ts +54 -0
  14. package/dist/builtin/intercom/skills/intercom/SKILL.md +18 -3
  15. package/dist/builtin/intercom/source-ownership.ts +31 -0
  16. package/dist/builtin/intercom/types.ts +115 -0
  17. package/dist/builtin/mcp/index.bundle.mjs +175 -37
  18. package/dist/builtin/mcp/package.json +1 -1
  19. package/dist/builtin/subagents/package.json +1 -1
  20. package/dist/builtin/subagents/src/extension/index.bundle.mjs +239 -53
  21. package/dist/builtin/web-access/index.bundle.mjs +175 -37
  22. package/dist/builtin/web-access/package.json +1 -1
  23. package/dist/builtin/workflows/CHANGELOG.md +15 -0
  24. package/dist/builtin/workflows/README.md +40 -37
  25. package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
  26. package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +133 -154
  27. package/dist/builtin/workflows/builtin/chunk-17zk6ffd.js +69794 -0
  28. package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
  29. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  30. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  31. package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
  32. package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
  33. package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
  34. package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
  35. package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
  36. package/dist/builtin/workflows/builtin/chunk-8v8wmb7z.js +529 -0
  37. package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
  38. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  39. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  40. package/dist/builtin/workflows/builtin/chunk-h5e65g7g.js +1080 -0
  41. package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
  42. package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
  43. package/dist/builtin/workflows/builtin/chunk-qe6bfpbd.js +1207 -0
  44. package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
  45. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  46. package/dist/builtin/workflows/builtin/chunk-rbnj9621.js +2447 -0
  47. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  48. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  49. package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
  50. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
  51. package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
  52. package/dist/builtin/workflows/builtin/goal.js +16 -0
  53. package/dist/builtin/workflows/builtin/index.js +49 -0
  54. package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
  55. package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
  56. package/dist/builtin/workflows/builtin/ralph.js +16 -0
  57. package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
  58. package/dist/builtin/workflows/builtin/steering-context.js +7 -0
  59. package/dist/builtin/workflows/builtin/tournament.js +13 -0
  60. package/dist/builtin/workflows/package.json +10 -7
  61. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  62. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1294 -430
  63. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +1022 -429
  64. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
  65. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
  66. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  67. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
  68. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  69. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  70. package/dist/core/agent-session-custom-message-commit.js +12 -5
  71. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  72. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  73. package/dist/core/agent-session-persistent-custom-messages.js +2 -0
  74. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  75. package/dist/core/agent-session.d.ts.map +1 -1
  76. package/dist/core/agent-session.js +2 -1
  77. package/dist/core/agent-session.js.map +1 -1
  78. package/dist/core/auth-storage.d.ts +2 -0
  79. package/dist/core/auth-storage.d.ts.map +1 -1
  80. package/dist/core/auth-storage.js +4 -0
  81. package/dist/core/auth-storage.js.map +1 -1
  82. package/dist/core/builtin-install-layout.d.ts +1 -1
  83. package/dist/core/builtin-install-layout.d.ts.map +1 -1
  84. package/dist/core/builtin-install-layout.js +1 -1
  85. package/dist/core/builtin-install-layout.js.map +1 -1
  86. package/dist/core/codex-fast-mode.d.ts +10 -7
  87. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  88. package/dist/core/codex-fast-mode.js +31 -13
  89. package/dist/core/codex-fast-mode.js.map +1 -1
  90. package/dist/core/extensions/agent-events.d.ts +15 -0
  91. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  92. package/dist/core/extensions/agent-events.js.map +1 -1
  93. package/dist/core/extensions/api-types.d.ts +3 -1
  94. package/dist/core/extensions/api-types.d.ts.map +1 -1
  95. package/dist/core/extensions/api-types.js.map +1 -1
  96. package/dist/core/extensions/context-types.d.ts +35 -0
  97. package/dist/core/extensions/context-types.d.ts.map +1 -1
  98. package/dist/core/extensions/context-types.js.map +1 -1
  99. package/dist/core/extensions/event-types.d.ts +2 -2
  100. package/dist/core/extensions/event-types.d.ts.map +1 -1
  101. package/dist/core/extensions/event-types.js.map +1 -1
  102. package/dist/core/extensions/index.d.ts +1 -1
  103. package/dist/core/extensions/index.d.ts.map +1 -1
  104. package/dist/core/extensions/index.js.map +1 -1
  105. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  106. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  107. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  108. package/dist/core/extensions/runner.d.ts +6 -0
  109. package/dist/core/extensions/runner.d.ts.map +1 -1
  110. package/dist/core/extensions/runner.js +65 -1
  111. package/dist/core/extensions/runner.js.map +1 -1
  112. package/dist/core/model-runtime.d.ts +2 -0
  113. package/dist/core/model-runtime.d.ts.map +1 -1
  114. package/dist/core/model-runtime.js +4 -0
  115. package/dist/core/model-runtime.js.map +1 -1
  116. package/dist/core/runtime-credentials.d.ts +1 -0
  117. package/dist/core/runtime-credentials.d.ts.map +1 -1
  118. package/dist/core/runtime-credentials.js +8 -0
  119. package/dist/core/runtime-credentials.js.map +1 -1
  120. package/dist/core/sdk.d.ts.map +1 -1
  121. package/dist/core/sdk.js +5 -5
  122. package/dist/core/sdk.js.map +1 -1
  123. package/dist/core/session-manager-storage.d.ts +2 -0
  124. package/dist/core/session-manager-storage.d.ts.map +1 -1
  125. package/dist/core/session-manager-storage.js +4 -2
  126. package/dist/core/session-manager-storage.js.map +1 -1
  127. package/dist/core/slash-commands.js +1 -1
  128. package/dist/core/slash-commands.js.map +1 -1
  129. package/dist/core/workflow-stage-admission.d.ts +6 -1
  130. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  131. package/dist/core/workflow-stage-admission.js +8 -9
  132. package/dist/core/workflow-stage-admission.js.map +1 -1
  133. package/dist/index-extensions.d.ts +1 -1
  134. package/dist/index-extensions.d.ts.map +1 -1
  135. package/dist/index-extensions.js.map +1 -1
  136. package/dist/index.d.ts +0 -1
  137. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  138. package/dist/modes/interactive/components/footer.js +1 -1
  139. package/dist/modes/interactive/components/footer.js.map +1 -1
  140. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  141. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  142. package/dist/modes/interactive/interactive-extension-runtime.js +1 -1
  143. package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
  144. package/dist/modes/interactive/interactive-startup.js +1 -1
  145. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  146. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  147. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  148. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  149. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  150. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  151. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  152. package/dist/modes/rpc/rpc-client-api.js +33 -5
  153. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  154. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  155. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  156. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  157. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  158. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  159. package/dist/modes/rpc/rpc-types.js.map +1 -1
  160. package/docs/extensions.md +19 -0
  161. package/docs/intercom.md +15 -6
  162. package/docs/packages.md +2 -2
  163. package/docs/providers.md +8 -3
  164. package/docs/quickstart.md +2 -2
  165. package/docs/rpc.md +21 -4
  166. package/docs/settings.md +6 -4
  167. package/docs/subagents.md +1 -1
  168. package/docs/usage.md +1 -1
  169. package/docs/workflows.md +108 -239
  170. package/npm-shrinkwrap.json +32 -32
  171. package/package.json +9 -13
  172. package/dist/builtin/workflows/ambient.d.ts +0 -61
  173. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  174. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  175. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  176. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  177. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  178. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  179. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  180. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  181. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  182. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  183. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  184. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  185. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  186. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  187. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  188. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  189. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  190. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -217
  191. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  192. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  193. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  194. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  195. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  196. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  197. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  198. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  199. package/dist/builtin/workflows/builtin/index.ts +0 -16
  200. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  201. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  202. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  203. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  204. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  205. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  206. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  207. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  208. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  209. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  210. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  211. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  212. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  213. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  214. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  215. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -104
  216. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -459
  217. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  218. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  219. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  220. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  221. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  222. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  223. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  224. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  225. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  226. package/dist/builtin/workflows/builtin/verification-usage.ts +0 -44
@@ -4589,10 +4589,10 @@ function hasAssistantMessage(entries) {
4589
4589
  return entries.some((entry) => entry.type === "message" && entry.message.role === "assistant");
4590
4590
  }
4591
4591
  function persistAppendedEntry(filePath, entries, entry, flushed) {
4592
- if (!hasAssistantMessage(entries))
4593
- return false;
4594
4592
  if (flushed)
4595
4593
  appendSessionEntry(filePath, entry);
4594
+ else if (!hasAssistantMessage(entries))
4595
+ return false;
4596
4596
  else
4597
4597
  appendSessionEntries(filePath, entries);
4598
4598
  return true;
@@ -10723,6 +10723,8 @@ function appendDurableDisplayCard(session, message, delivery) {
10723
10723
  const card = { ...message, excludeFromContext: true };
10724
10724
  const admitted = message;
10725
10725
  const intentEntryId = session.sessionManager.appendCustomMessageEntry(card.customType, card.content, card.display, card.details, true, { delivery }, admitted.stageAdmissionKey);
10726
+ if (admitted.stageAdmissionKey !== undefined)
10727
+ session.sessionManager.flush();
10726
10728
  session.agent.state.messages.push(card);
10727
10729
  return { card, intentEntryId };
10728
10730
  }
@@ -10948,12 +10950,17 @@ var init_agent_session_persistent_custom_messages = __esm(() => {
10948
10950
  });
10949
10951
 
10950
10952
  // src/core/agent-session-custom-message-commit.ts
10953
+ function appendDurableStageAdmission(session, message) {
10954
+ session._appendCustomMessage(message);
10955
+ if (message.stageAdmissionKey !== undefined)
10956
+ session.sessionManager?.flush();
10957
+ }
10951
10958
  async function commitAdmittedCustomMessage(session, appMessage, options) {
10952
10959
  const owner = resolveWorkflowStageDeliveryTarget(session);
10953
10960
  if (owner !== session)
10954
10961
  return commitAdmittedCustomMessage(owner, appMessage, options);
10955
10962
  const self = session;
10956
- const useProtectedReconciliation = options?.persistWhenStreaming === true && options.triggerTurn === true && options.excludeFromContext !== true;
10963
+ const useProtectedReconciliation = options?.persistWhenStreaming === true && options.excludeFromContext !== true && (options.triggerTurn === true || options.stageAdmissionKey !== undefined);
10957
10964
  if (options?.deliverAs === "nextTurn") {
10958
10965
  self._pendingNextTurnMessages.push(appMessage);
10959
10966
  } else if (self._queuedMessagesPaused) {
@@ -10967,20 +10974,20 @@ async function commitAdmittedCustomMessage(session, appMessage, options) {
10967
10974
  } else if (self.isStreaming) {
10968
10975
  self._pendingCustomMessages.push(appMessage);
10969
10976
  } else {
10970
- self._appendCustomMessage(appMessage);
10977
+ appendDurableStageAdmission(self, appMessage);
10971
10978
  }
10972
10979
  } else if (options?.deliverAs === "interrupt" && options.triggerTurn) {
10973
10980
  const interrupt = self._enqueueInterruptCustomMessage(appMessage, options);
10974
10981
  self._workflowStageAdmission?.trackAdmittedWork(interrupt);
10975
10982
  interrupt.catch(() => {});
10976
10983
  } else if (self.isStreaming && options?.excludeFromContext === true && options.triggerTurn !== true && options.deliverAs === undefined) {
10977
- self._appendCustomMessage(appMessage);
10984
+ appendDurableStageAdmission(self, appMessage);
10978
10985
  } else if (self.isStreaming && options?.triggerTurn === false) {
10979
10986
  self._pendingCustomMessages.push(appMessage);
10980
10987
  } else if (self.isStreaming && useProtectedReconciliation) {
10981
10988
  await queueProtectedStreamingCustomMessage(self, appMessage, options?.deliverAs === "followUp" ? "followUp" : "steer");
10982
10989
  } else if (self.isStreaming && options?.persistWhenStreaming === true) {
10983
- self._appendCustomMessage(appMessage);
10990
+ appendDurableStageAdmission(self, appMessage);
10984
10991
  } else if (self.isStreaming && options?.triggerTurn !== false) {
10985
10992
  self._queueAgentMessage(appMessage, options?.deliverAs === "followUp" ? "followUp" : "steer");
10986
10993
  } else if (options?.triggerTurn) {
@@ -11003,7 +11010,7 @@ async function commitAdmittedCustomMessage(session, appMessage, options) {
11003
11010
  self._continueQueuedAgentMessages().catch(() => {});
11004
11011
  }
11005
11012
  } else {
11006
- self._appendCustomMessage(appMessage);
11013
+ appendDurableStageAdmission(self, appMessage);
11007
11014
  }
11008
11015
  }
11009
11016
  function _commitAdmittedCustomMessage(appMessage, options) {
@@ -13310,6 +13317,8 @@ class ExtensionRunner {
13310
13317
  shortcutDiagnostics = [];
13311
13318
  commandDiagnostics = [];
13312
13319
  staleMessage;
13320
+ uiPromptBinding = 0;
13321
+ activeUIPrompt;
13313
13322
  constructor(extensions, runtime, cwd, sessionManager, modelRegistry, orchestrationContext, subagentPolicy) {
13314
13323
  this.extensions = extensions;
13315
13324
  this.runtime = runtime;
@@ -13404,9 +13413,71 @@ class ExtensionRunner {
13404
13413
  this.reloadHandler = async () => {};
13405
13414
  }
13406
13415
  setUIContext(uiContext, mode = "print") {
13407
- this.uiContext = uiContext ?? noOpUIContext;
13416
+ this.endActiveUIPrompt();
13417
+ const binding = ++this.uiPromptBinding;
13418
+ this.uiContext = uiContext ? this.wrapUIPromptContext(uiContext, binding) : noOpUIContext;
13408
13419
  this.mode = mode;
13409
13420
  }
13421
+ wrapUIPromptContext(ui, binding) {
13422
+ return {
13423
+ ...ui,
13424
+ select: (title, options, opts) => this.withUIPrompt(binding, "select", title, () => ui.select(title, options, opts)),
13425
+ confirm: (title, message, opts) => this.withUIPrompt(binding, "confirm", title, () => ui.confirm(title, message, opts)),
13426
+ input: (title, placeholder, opts) => this.withUIPrompt(binding, "input", title, () => ui.input(title, placeholder, opts)),
13427
+ editor: (title, prefill, opts) => this.withUIPrompt(binding, "editor", title, () => ui.editor(title, prefill, opts)),
13428
+ custom: (factory, options) => this.withUIPrompt(binding, "custom", undefined, () => ui.custom(factory, options))
13429
+ };
13430
+ }
13431
+ withUIPrompt(binding, kind, title, run) {
13432
+ if (binding !== this.uiPromptBinding)
13433
+ return run();
13434
+ let prompt = this.activeUIPrompt;
13435
+ if (!prompt) {
13436
+ prompt = { depth: 0, kind, title };
13437
+ this.activeUIPrompt = prompt;
13438
+ this.emitUIPromptEvent({
13439
+ type: "ui_prompt_start",
13440
+ reason: "ui_prompt",
13441
+ kind,
13442
+ ...title === undefined ? {} : { title }
13443
+ });
13444
+ }
13445
+ prompt.depth += 1;
13446
+ let finished = false;
13447
+ const finish = () => {
13448
+ if (finished)
13449
+ return;
13450
+ finished = true;
13451
+ if (this.activeUIPrompt !== prompt)
13452
+ return;
13453
+ prompt.depth -= 1;
13454
+ if (prompt.depth === 0)
13455
+ this.endActiveUIPrompt(prompt);
13456
+ };
13457
+ try {
13458
+ return run().finally(finish);
13459
+ } catch (error) {
13460
+ finish();
13461
+ throw error;
13462
+ }
13463
+ }
13464
+ endActiveUIPrompt(prompt = this.activeUIPrompt) {
13465
+ if (!prompt || this.activeUIPrompt !== prompt)
13466
+ return;
13467
+ this.activeUIPrompt = undefined;
13468
+ prompt.depth = 0;
13469
+ this.emitUIPromptEvent({
13470
+ type: "ui_prompt_end",
13471
+ reason: "ui_prompt",
13472
+ kind: prompt.kind,
13473
+ ...prompt.title === undefined ? {} : { title: prompt.title }
13474
+ });
13475
+ }
13476
+ emitUIPromptEvent(event) {
13477
+ queueMicrotask(() => {
13478
+ this.emit(event);
13479
+ });
13480
+ }
13410
13481
  getUIContext() {
13411
13482
  return this.uiContext;
13412
13483
  }
@@ -19160,6 +19231,7 @@ async function loadVirtualModules() {
19160
19231
  piAi,
19161
19232
  piAiOauth,
19162
19233
  piAiCloudflareGatewayBinding,
19234
+ properLockfile,
19163
19235
  piCodingAgent
19164
19236
  ] = await Promise.all([
19165
19237
  import("typebox"),
@@ -19171,6 +19243,7 @@ async function loadVirtualModules() {
19171
19243
  import("@bastani/pi-ai/compat"),
19172
19244
  import("@bastani/pi-ai/oauth"),
19173
19245
  import("@bastani/pi-ai/api/cloudflare-gateway-binding"),
19246
+ import("proper-lockfile"),
19174
19247
  Promise.resolve().then(() => (init_src(), exports_src))
19175
19248
  ]);
19176
19249
  return {
@@ -19191,6 +19264,7 @@ async function loadVirtualModules() {
19191
19264
  "@earendil-works/pi-ai/compat": piAi,
19192
19265
  "@earendil-works/pi-ai/oauth": piAiOauth,
19193
19266
  "@earendil-works/pi-ai/api/cloudflare-gateway-binding": piAiCloudflareGatewayBinding,
19267
+ "proper-lockfile": properLockfile,
19194
19268
  "@bastani/atomic": piCodingAgent,
19195
19269
  "@mariozechner/pi-agent-core": piAgentCore,
19196
19270
  "@mariozechner/pi-tui": piTui,
@@ -26320,11 +26394,20 @@ function isCodexFastModeCandidateModelId(modelId) {
26320
26394
  const provider = modelId?.split("/", 1)[0];
26321
26395
  return provider !== undefined && isCodexFastModeSupportedProvider(provider);
26322
26396
  }
26323
- function isCodexFastModeSupportedModel(model) {
26324
- return isCodexFastModeSupportedProvider(model.provider) || model.api === "openai-codex-responses";
26397
+ function isGitHubCopilotModel(model) {
26398
+ return model.provider === "github-copilot";
26325
26399
  }
26326
- function hasSupportedCodexFastModeModel(models) {
26327
- return models.some(isCodexFastModeSupportedModel);
26400
+ function isGitHubCopilotFastModeSupportedModel(model, credential) {
26401
+ if (!isGitHubCopilotModel(model) || credential?.type !== "oauth")
26402
+ return false;
26403
+ const fastModelIds = credential.fastModelIds;
26404
+ return Array.isArray(fastModelIds) && fastModelIds.every((modelId) => typeof modelId === "string") && fastModelIds.includes(`${model.id}-fast`);
26405
+ }
26406
+ function isCodexFastModeSupportedModel(model, copilotCredential) {
26407
+ return isCodexFastModeSupportedProvider(model.provider) || model.api === "openai-codex-responses" || model.id !== undefined && isGitHubCopilotFastModeSupportedModel({ provider: model.provider, id: model.id }, copilotCredential);
26408
+ }
26409
+ function hasSupportedCodexFastModeModel(models, copilotCredential) {
26410
+ return models.some((model) => isCodexFastModeSupportedModel(model, copilotCredential));
26328
26411
  }
26329
26412
  function isWorkflowStageOrchestrationContext(context) {
26330
26413
  return context?.kind === "workflow-stage";
@@ -26335,11 +26418,11 @@ function getCodexFastModeScope(context) {
26335
26418
  function isCodexFastModeEnabledForScope(settings, scope) {
26336
26419
  return settings[scope];
26337
26420
  }
26338
- function shouldApplyCodexFastModeForScope(model, settings, scope) {
26339
- return isCodexFastModeSupportedModel(model) && isCodexFastModeEnabledForScope(settings, scope);
26421
+ function shouldApplyCodexFastModeForScope(model, settings, scope, copilotCredential) {
26422
+ return isCodexFastModeSupportedModel(model, copilotCredential) && isCodexFastModeEnabledForScope(settings, scope);
26340
26423
  }
26341
- function shouldApplyCodexFastMode(model, settings, context) {
26342
- return shouldApplyCodexFastModeForScope(model, settings, getCodexFastModeScope(context));
26424
+ function shouldApplyCodexFastMode(model, settings, context, copilotCredential) {
26425
+ return shouldApplyCodexFastModeForScope(model, settings, getCodexFastModeScope(context), copilotCredential);
26343
26426
  }
26344
26427
  function usesFirstPartyCodexRouting(model) {
26345
26428
  return model.provider === "openai-codex" && isFirstPartyCodexBaseUrl(model.baseUrl);
@@ -26365,8 +26448,8 @@ function withCodexFastModeHeaders(model, headers, enabled) {
26365
26448
  setHeader(fastHeaders, CODEX_FAST_MODE_ROUTING_HEADER, `model=${model.id};tier=${CODEX_FAST_MODE_SERVICE_TIER}`);
26366
26449
  return fastHeaders;
26367
26450
  }
26368
- function withCodexFastModeStreamOptions(_model, options, enabled) {
26369
- if (!enabled) {
26451
+ function withCodexFastModeStreamOptions(model, options, enabled) {
26452
+ if (!enabled || model.provider === "github-copilot") {
26370
26453
  return options;
26371
26454
  }
26372
26455
  return {
@@ -26500,10 +26583,14 @@ function streamWithCodexFastMode(model, context, options, streamers = DEFAULT_CO
26500
26583
  function isObjectPayload(payload) {
26501
26584
  return typeof payload === "object" && payload !== null && !Array.isArray(payload);
26502
26585
  }
26503
- function withCodexFastModePayload(payload, enabled) {
26504
- if (!enabled || !isObjectPayload(payload) || payload.service_tier !== undefined) {
26586
+ function withCodexFastModePayload(payload, enabled, model) {
26587
+ if (!enabled || !isObjectPayload(payload))
26505
26588
  return payload;
26589
+ if (model?.provider === "github-copilot") {
26590
+ return payload.model === model.id ? { ...payload, model: `${model.id}-fast` } : payload;
26506
26591
  }
26592
+ if (payload.service_tier !== undefined)
26593
+ return payload;
26507
26594
  return {
26508
26595
  ...payload,
26509
26596
  service_tier: CODEX_FAST_MODE_SERVICE_TIER
@@ -26653,7 +26740,7 @@ class FooterComponent {
26653
26740
  pwd += ` • ${sessionName}`;
26654
26741
  const modelName = state.model?.id || "no-model";
26655
26742
  const fastModeSettings = this.session.settingsManager.getCodexFastModeSettings();
26656
- const fastModeEnabled = state.model ? shouldApplyCodexFastMode(state.model, fastModeSettings, this.session.orchestrationContext) : false;
26743
+ const fastModeEnabled = state.model ? shouldApplyCodexFastMode(state.model, fastModeSettings, this.session.orchestrationContext, this.session.modelRuntime.getCredentialSnapshot?.("github-copilot")) : false;
26657
26744
  let modelLabel2 = modelName;
26658
26745
  if (state.model?.reasoning) {
26659
26746
  const thinkingLevel = state.thinkingLevel || "off";
@@ -45421,14 +45508,18 @@ import { AsyncLocalStorage } from "node:async_hooks";
45421
45508
 
45422
45509
  class WorkflowStageAdmissionBoundary {
45423
45510
  open = true;
45424
- completed = new Set;
45511
+ completed;
45425
45512
  inFlight = new Map;
45426
45513
  pending = new Set;
45427
45514
  invocationContext = new AsyncLocalStorage;
45428
45515
  closePromise;
45429
45516
  drainAdmittedWork;
45430
- constructor(drainAdmittedWork = async () => {}) {
45431
- this.drainAdmittedWork = drainAdmittedWork;
45517
+ constructor(drainAdmittedWork = undefined, completedKeys = []) {
45518
+ this.drainAdmittedWork = drainAdmittedWork ?? (async () => {});
45519
+ this.completed = new Set(completedKeys);
45520
+ }
45521
+ static restore(entries, drainAdmittedWork) {
45522
+ return new WorkflowStageAdmissionBoundary(drainAdmittedWork, Array.from(entries).flatMap((entry) => entry.type === "custom_message" && typeof entry.stageAdmissionKey === "string" ? [entry.stageAdmissionKey] : []));
45432
45523
  }
45433
45524
  admit(key, deliver, routeLate) {
45434
45525
  if (key !== undefined) {
@@ -45667,7 +45758,7 @@ class AgentSessionBase {
45667
45758
  this._subagentPolicy = config.subagentPolicy;
45668
45759
  this._systemPromptTransform = config.systemPromptTransform;
45669
45760
  const stageContext = config.orchestrationContext?.kind === "workflow-stage" ? config.orchestrationContext : undefined;
45670
- this._workflowStageAdmission = stageContext?.messageAdmission?.boundary ?? (stageContext ? new WorkflowStageAdmissionBoundary : undefined);
45761
+ this._workflowStageAdmission = stageContext?.messageAdmission?.boundary ?? (stageContext ? WorkflowStageAdmissionBoundary.restore(this.sessionManager.getBranch()) : undefined);
45671
45762
  if (this._workflowStageAdmission && stageContext && stageContext.messageAdmission === undefined) {
45672
45763
  stageContext.messageAdmission = {
45673
45764
  boundary: this._workflowStageAdmission,
@@ -45999,6 +46090,9 @@ class AuthStorage {
45999
46090
  this.data = this.parseStorageData(content);
46000
46091
  } catch {}
46001
46092
  }
46093
+ peek(provider) {
46094
+ return this.data[provider];
46095
+ }
46002
46096
  async read(provider) {
46003
46097
  const credential = this.data[provider];
46004
46098
  if (credential?.type !== "api_key")
@@ -47052,6 +47146,10 @@ function getReloadableStore(store) {
47052
47146
  const reloadable = store;
47053
47147
  return typeof reloadable.reload === "function" ? reloadable : undefined;
47054
47148
  }
47149
+ function getSnapshotStore(store) {
47150
+ const snapshotStore = store;
47151
+ return typeof snapshotStore.peek === "function" ? snapshotStore : undefined;
47152
+ }
47055
47153
 
47056
47154
  class RuntimeCredentials {
47057
47155
  store;
@@ -47071,6 +47169,10 @@ class RuntimeCredentials {
47071
47169
  async reload() {
47072
47170
  await getReloadableStore(this.store)?.reload();
47073
47171
  }
47172
+ peek(providerId) {
47173
+ const override = this.overrides.get(providerId);
47174
+ return override ? { type: "api_key", key: override } : getSnapshotStore(this.store)?.peek(providerId);
47175
+ }
47074
47176
  async read(providerId, options) {
47075
47177
  const override = this.overrides.get(providerId);
47076
47178
  return override ? { type: "api_key", key: override } : this.store.read(providerId, options);
@@ -47506,6 +47608,9 @@ class ModelRuntime {
47506
47608
  hasConfiguredAuth(providerId) {
47507
47609
  return this.snapshot.configuredProviders.has(providerId);
47508
47610
  }
47611
+ getCredentialSnapshot(providerId) {
47612
+ return this.credentials.peek(providerId);
47613
+ }
47509
47614
  async getAuth(providerOrModel, overrides = {}) {
47510
47615
  if (typeof providerOrModel === "string")
47511
47616
  return this.models.getAuth(providerOrModel, overrides);
@@ -55461,7 +55566,7 @@ async function createAgentSession(options = {}) {
55461
55566
  return filtered;
55462
55567
  };
55463
55568
  const extensionRunnerRef = {};
55464
- const isCodexFastModeEnabled = (requestModel) => shouldApplyCodexFastMode(requestModel, settingsManager.getCodexFastModeSettings(), options.orchestrationContext);
55569
+ const isCodexFastModeEnabled = (requestModel) => shouldApplyCodexFastMode(requestModel, settingsManager.getCodexFastModeSettings(), options.orchestrationContext, modelRuntime.getCredentialSnapshot("github-copilot"));
55465
55570
  agent = new Agent2({
55466
55571
  initialState: {
55467
55572
  systemPrompt: "",
@@ -55497,7 +55602,7 @@ async function createAgentSession(options = {}) {
55497
55602
  const extensionProvider = modelRuntime.getRegisteredProviderConfig(requestModel.provider);
55498
55603
  const usesExtensionStream = extensionProvider?.streamSimple !== undefined && requestModel.api === extensionProvider.api;
55499
55604
  const transportHeaders = usesExtensionStream ? assembledHeaders : removeUnownedModelHeaders(assembledHeaders, model2.headers, requestHeaders);
55500
- if (fastModeEnabled && !usesExtensionStream && !authResult) {
55605
+ if (fastModeEnabled && !isGitHubCopilotModel(model2) && !usesExtensionStream && !authResult) {
55501
55606
  throw new Error(`No API key found for "${model2.provider}"`);
55502
55607
  }
55503
55608
  const codexFastModeStreamOptions = withCodexFastModeStreamOptions(requestModel, {
@@ -55514,7 +55619,7 @@ async function createAgentSession(options = {}) {
55514
55619
  if (usesExtensionStream) {
55515
55620
  return modelRuntime.streamSimple(requestModel, context, codexFastModeStreamOptions);
55516
55621
  }
55517
- if (fastModeEnabled) {
55622
+ if (fastModeEnabled && !isGitHubCopilotModel(requestModel)) {
55518
55623
  return streamWithCodexFastMode(requestModel, context, codexFastModeStreamOptions);
55519
55624
  }
55520
55625
  const transportOptions = usesChatGptCodexTransport(requestModel) && codexFastModeStreamOptions ? withChatGptCodexTransportRouting(requestModel, codexFastModeStreamOptions, false) : codexFastModeStreamOptions;
@@ -55522,7 +55627,7 @@ async function createAgentSession(options = {}) {
55522
55627
  },
55523
55628
  onPayload: async (payload, model2) => {
55524
55629
  const fastModeEnabled = isCodexFastModeEnabled(model2);
55525
- const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);
55630
+ const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled, model2);
55526
55631
  const sourceMessages = lastConvertedLlmMessages;
55527
55632
  const replayGuardedPayload = sourceMessages ? restoreAnthropicReplayThinkingBlocks(guardedPayload, sourceMessages, model2) : guardedPayload;
55528
55633
  const runner = extensionRunnerRef.current;
@@ -59987,6 +60092,31 @@ class RemoteQueuePause {
59987
60092
  var init_remote_queue_pause = () => {};
59988
60093
 
59989
60094
  // src/modes/interactive-engine/isolated-runtime.ts
60095
+ import { clampThinkingLevel as clampThinkingLevel5 } from "@bastani/pi-ai/compat";
60096
+ function applyPersistedModelDefault(session, model, alreadyInScope) {
60097
+ session.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
60098
+ if (alreadyInScope || session.scopedModels.length === 0)
60099
+ return;
60100
+ const enabledModels = session.settingsManager.getEnabledModels();
60101
+ if (!enabledModels?.length)
60102
+ return;
60103
+ const modelReference = `${model.provider}/${model.id}`;
60104
+ if (enabledModels.some((pattern) => pattern.toLowerCase() === modelReference.toLowerCase()))
60105
+ return;
60106
+ session.settingsManager.setEnabledModels([...enabledModels, modelReference]);
60107
+ }
60108
+ function thinkingPersistTarget(result) {
60109
+ if (!result.provider || !result.modelId)
60110
+ return;
60111
+ return { provider: result.provider, modelId: result.modelId };
60112
+ }
60113
+ function applyPersistedThinkingDefault(session, level, target) {
60114
+ if (target) {
60115
+ session.settingsManager.setModelThinkingLevel(target.provider, target.modelId, level);
60116
+ return;
60117
+ }
60118
+ session.settingsManager.setDefaultThinkingLevel(level);
60119
+ }
59990
60120
  var IsolatedInteractiveRuntime;
59991
60121
  var init_isolated_runtime = __esm(() => {
59992
60122
  init_agent_session_runtime();
@@ -60005,6 +60135,7 @@ var init_isolated_runtime = __esm(() => {
60005
60135
  steeringMessages = [];
60006
60136
  followUpMessages = [];
60007
60137
  queueUpdateGeneration = 0;
60138
+ thinkingEpoch = 0;
60008
60139
  pendingQueueClear;
60009
60140
  engineCallbackActive = false;
60010
60141
  queuePause;
@@ -60369,43 +60500,56 @@ var init_isolated_runtime = __esm(() => {
60369
60500
  },
60370
60501
  setModel: {
60371
60502
  configurable: true,
60372
- value: async (model) => {
60373
- const selected = await this.client.setModel(model.provider, model.id);
60374
- session.agent.state.model = session.modelRuntime.getModel(selected.provider, selected.id) ?? model;
60503
+ value: async (model, options) => {
60504
+ const selected = await this.client.setModel(model.provider, model.id, options);
60505
+ const nextModel = session.modelRuntime.getModel(selected.provider, selected.id) ?? model;
60506
+ session.agent.state.model = nextModel;
60375
60507
  this.resolveModelFallback();
60508
+ if (options?.persist === true)
60509
+ await this.syncPersistedModelCatalog(session, nextModel);
60376
60510
  }
60377
60511
  },
60378
60512
  setThinkingLevel: {
60379
60513
  configurable: true,
60380
- value: (level) => {
60381
- session.agent.state.thinkingLevel = level;
60382
- this.dispatchBestEffort("set thinking level", this.client.setThinkingLevel(level));
60514
+ value: (level, options) => {
60515
+ const availableLevels = session.getAvailableThinkingLevels();
60516
+ const effectiveLevel = availableLevels.includes(level) ? level : session.model ? clampThinkingLevel5(session.model, level) : "off";
60517
+ session.agent.state.thinkingLevel = effectiveLevel;
60518
+ const epoch = ++this.thinkingEpoch;
60519
+ this.dispatchBestEffort("set thinking level", this.client.setThinkingLevelAck(level, options).then((result) => {
60520
+ this.applyThinkingAck(session, epoch, result, options?.persist === true);
60521
+ }));
60383
60522
  }
60384
60523
  },
60385
60524
  cycleModel: {
60386
60525
  configurable: true,
60387
- value: async (direction) => {
60526
+ value: async (direction, options) => {
60388
60527
  const previousModel = session.model;
60389
- const result = await this.client.cycleModel(direction);
60528
+ const result = await this.client.cycleModel(direction, options);
60390
60529
  if (!result)
60391
60530
  return;
60392
60531
  const model = session.modelRuntime.getModel(result.model.provider, result.model.id) ?? result.model;
60393
60532
  session.agent.state.model = model;
60394
60533
  session.agent.state.thinkingLevel = result.thinkingLevel;
60395
60534
  this.resolveModelFallbackAfterExplicitModelSelection(previousModel, model);
60535
+ if (options?.persist === true)
60536
+ await this.syncPersistedModelCatalog(session, model);
60396
60537
  return { ...result, model };
60397
60538
  }
60398
60539
  },
60399
60540
  cycleThinkingLevel: {
60400
60541
  configurable: true,
60401
- value: () => {
60542
+ value: (options) => {
60402
60543
  const levels = session.getAvailableThinkingLevels();
60403
60544
  if (levels.length <= 1)
60404
60545
  return;
60405
60546
  const current = levels.indexOf(session.thinkingLevel);
60406
60547
  const level = levels[(current + 1) % levels.length];
60407
60548
  session.agent.state.thinkingLevel = level;
60408
- this.dispatchBestEffort("cycle thinking level", this.client.setThinkingLevel(level));
60549
+ const epoch = ++this.thinkingEpoch;
60550
+ this.dispatchBestEffort("cycle thinking level", this.client.setThinkingLevelAck(level, options).then((result) => {
60551
+ this.applyThinkingAck(session, epoch, result, options?.persist === true);
60552
+ }));
60409
60553
  return level;
60410
60554
  }
60411
60555
  },
@@ -60449,6 +60593,14 @@ var init_isolated_runtime = __esm(() => {
60449
60593
  this.engineCallbackActive = false;
60450
60594
  }
60451
60595
  }
60596
+ applyThinkingAck(session, epoch, result, persist) {
60597
+ if (persist) {
60598
+ applyPersistedThinkingDefault(session, result.level, thinkingPersistTarget(result));
60599
+ }
60600
+ if (epoch === this.thinkingEpoch) {
60601
+ session.agent.state.thinkingLevel = result.level;
60602
+ }
60603
+ }
60452
60604
  dispatchBestEffort(label, operation) {
60453
60605
  operation.catch((error) => {
60454
60606
  if (this.health.isRecovering())
@@ -60489,6 +60641,12 @@ var init_isolated_runtime = __esm(() => {
60489
60641
  }
60490
60642
  });
60491
60643
  }
60644
+ async syncPersistedModelCatalog(session, model) {
60645
+ const alreadyInScope = session.scopedModels.some((scoped) => scoped.model.provider === model.provider && scoped.model.id === model.id);
60646
+ const catalog = await this.client.requestInternal({ type: "get_available_models" });
60647
+ this.remoteModelCatalog.apply(catalog);
60648
+ applyPersistedModelDefault(session, model, alreadyInScope);
60649
+ }
60492
60650
  refreshSessionView() {
60493
60651
  const session = super.session;
60494
60652
  const sessionFile = session.sessionFile;
@@ -60528,6 +60686,7 @@ var init_isolated_runtime = __esm(() => {
60528
60686
  session.agent.state.model = event.model;
60529
60687
  break;
60530
60688
  case "thinking_level_changed":
60689
+ this.thinkingEpoch += 1;
60531
60690
  session.agent.state.thinkingLevel = event.level;
60532
60691
  break;
60533
60692
  case "session_info_changed":
@@ -61741,7 +61900,7 @@ var init_slash_commands = __esm(() => {
61741
61900
  { name: "tree", description: "Navigate session tree (switch branches)" },
61742
61901
  { name: "thinking", description: "Set thinking level", argumentHint: "<level>" },
61743
61902
  { name: "scoped-models", description: "Enable/disable models for ctrl+p cycling" },
61744
- { name: "fast", description: "Configure Codex fast mode for chat and workflows" },
61903
+ { name: "fast", description: "Configure OpenAI fast mode for chat and workflows in supported models" },
61745
61904
  { name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },
61746
61905
  { name: "import", description: "Import and resume a session from a JSONL file" },
61747
61906
  { name: "share", description: "Share session as a secret GitHub gist" },
@@ -63203,7 +63362,7 @@ var init_interactive_autocomplete = __esm(() => {
63203
63362
  return [...this.session.modelRuntime.getAvailableSnapshot()];
63204
63363
  };
63205
63364
  InteractiveModeBase.prototype.hasCodexFastModeSupportedModels = function() {
63206
- return hasSupportedCodexFastModeModel(this.getCodexFastModeCandidateModels());
63365
+ return hasSupportedCodexFastModeModel(this.getCodexFastModeCandidateModels(), this.session.modelRuntime.getCredentialSnapshot?.("github-copilot"));
63207
63366
  };
63208
63367
  InteractiveModeBase.prototype.buildRemoteSlashCommands = function(localCommands) {
63209
63368
  const remote = getInteractiveEngineRemoteCommands(this.runtimeHost);
@@ -63891,7 +64050,7 @@ var init_interactive_startup = __esm(() => {
63891
64050
  if (!model) {
63892
64051
  return modelLabel2;
63893
64052
  }
63894
- const fastModeEnabled = shouldApplyCodexFastMode(model, this.session.settingsManager.getCodexFastModeSettings(), this.session.orchestrationContext);
64053
+ const fastModeEnabled = shouldApplyCodexFastMode(model, this.session.settingsManager.getCodexFastModeSettings(), this.session.orchestrationContext, this.session.modelRuntime.getCredentialSnapshot?.("github-copilot"));
63895
64054
  return formatCodexFastModeModelLabel(modelLabel2, fastModeEnabled);
63896
64055
  };
63897
64056
  InteractiveModeBase.prototype.getStartupIdentityText = function(maxWidth, gap = 0, manifestoPhase = 4) {
@@ -64817,7 +64976,7 @@ var init_interactive_extension_runtime = __esm(() => {
64817
64976
  if (shortcuts.size === 0)
64818
64977
  return;
64819
64978
  const createContext = () => ({
64820
- ui: this.createExtensionUIContext(),
64979
+ ui: extensionRunner.getUIContext(),
64821
64980
  mode: "tui",
64822
64981
  hasUI: true,
64823
64982
  cwd: this.sessionManager.getCwd(),
@@ -68406,7 +68565,7 @@ var init_interactive_selectors = __esm(() => {
68406
68565
  });
68407
68566
 
68408
68567
  // src/modes/interactive/interactive-model-routing.ts
68409
- import { clampThinkingLevel as clampThinkingLevel5 } from "@bastani/pi-ai/compat";
68568
+ import { clampThinkingLevel as clampThinkingLevel6 } from "@bastani/pi-ai/compat";
68410
68569
  function resolveThinkingSelectorDefault(rawDefault, availableLevels, model) {
68411
68570
  if (rawDefault === undefined)
68412
68571
  return;
@@ -68414,7 +68573,7 @@ function resolveThinkingSelectorDefault(rawDefault, availableLevels, model) {
68414
68573
  return rawDefault;
68415
68574
  if (!model)
68416
68575
  return availableLevels.includes("off") ? "off" : availableLevels[0];
68417
- const clamped = clampThinkingLevel5(model, rawDefault);
68576
+ const clamped = clampThinkingLevel6(model, rawDefault);
68418
68577
  if (availableLevels.includes(clamped))
68419
68578
  return clamped;
68420
68579
  return availableLevels.includes("off") ? "off" : availableLevels[0];
@@ -70778,11 +70937,20 @@ class RpcClientApi {
70778
70937
  async getState() {
70779
70938
  return this.data(await this.request({ type: "get_state" }));
70780
70939
  }
70781
- async setModel(provider, modelId) {
70782
- return this.data(await this.request({ type: "set_model", provider, modelId }));
70940
+ async setModel(provider, modelId, options) {
70941
+ return this.data(await this.request({
70942
+ type: "set_model",
70943
+ provider,
70944
+ modelId,
70945
+ ...options?.persist === true ? { persist: true } : {}
70946
+ }));
70783
70947
  }
70784
- async cycleModel(direction) {
70785
- return this.data(await this.request({ type: "cycle_model", direction }));
70948
+ async cycleModel(direction, options) {
70949
+ return this.data(await this.request({
70950
+ type: "cycle_model",
70951
+ direction,
70952
+ ...options?.persist === true ? { persist: true } : {}
70953
+ }));
70786
70954
  }
70787
70955
  async getAvailableModels() {
70788
70956
  return this.data(await this.request({ type: "get_available_models" })).models;
@@ -70816,6 +70984,20 @@ class RpcClientApi {
70816
70984
  async setThinkingLevel(level) {
70817
70985
  await this.request({ type: "set_thinking_level", level });
70818
70986
  }
70987
+ async setThinkingLevelAck(level, options) {
70988
+ const response = await this.request({
70989
+ type: "set_thinking_level",
70990
+ level,
70991
+ ...options?.persist === true ? { persist: true } : {}
70992
+ });
70993
+ if (!response.success) {
70994
+ return this.data(response);
70995
+ }
70996
+ if (response.command === "set_thinking_level" && "data" in response && response.data) {
70997
+ return response.data;
70998
+ }
70999
+ return { level };
71000
+ }
70819
71001
  async cycleThinkingLevel() {
70820
71002
  return this.data(await this.request({ type: "cycle_thinking_level" }));
70821
71003
  }
@@ -72834,13 +73016,13 @@ function createRpcCommandHandler({
72834
73016
  if (!model) {
72835
73017
  return createRpcErrorResponse(id, "set_model", `Model not found: ${command.provider}/${command.modelId}`);
72836
73018
  }
72837
- await session.setModel(model);
73019
+ await session.setModel(model, command.persist === true ? { persist: true } : {});
72838
73020
  runtimeHost.resolveModelFallback();
72839
73021
  return createRpcSuccessResponse(id, "set_model", session.model ?? model);
72840
73022
  }
72841
73023
  case "cycle_model": {
72842
73024
  const previousModel = session.model;
72843
- const result = await session.cycleModel(command.direction);
73025
+ const result = await session.cycleModel(command.direction, command.persist === true ? { persist: true } : {});
72844
73026
  runtimeHost.resolveModelFallbackAfterExplicitModelSelection(previousModel, result?.model);
72845
73027
  return createRpcSuccessResponse(id, "cycle_model", result ?? null);
72846
73028
  }
@@ -72903,8 +73085,12 @@ function createRpcCommandHandler({
72903
73085
  }
72904
73086
  }
72905
73087
  case "set_thinking_level": {
72906
- session.setThinkingLevel(command.level);
72907
- return createRpcSuccessResponse(id, "set_thinking_level");
73088
+ session.setThinkingLevel(command.level, command.persist === true ? { persist: true } : {});
73089
+ const model = session.model;
73090
+ return createRpcSuccessResponse(id, "set_thinking_level", {
73091
+ level: session.thinkingLevel,
73092
+ ...model ? { provider: model.provider, modelId: model.id } : {}
73093
+ });
72908
73094
  }
72909
73095
  case "cycle_thinking_level": {
72910
73096
  const level = session.cycleThinkingLevel();
@@ -76717,6 +76903,152 @@ function subscribeIntercomControl(pi, callbacks) {
76717
76903
  };
76718
76904
  }
76719
76905
 
76906
+ // dist/builtin/workflows/src/shared/pending-stage-delivery.ts
76907
+ var PENDING_STAGE_MESSAGE_LIMIT = 50;
76908
+ var PENDING_STAGE_UNDELIVERABLE_NOTIFICATION_PREFIX = "atomic-pending-stage-undeliverable";
76909
+ function queueStageMessage(messages2, input2, senderGroup, runGroup, stageIdentity) {
76910
+ if (normalizeDeliveryGroup(senderGroup) !== normalizeDeliveryGroup(runGroup)) {
76911
+ return { ok: false, reason: "group_mismatch", runId: input2.runId, stageKey: input2.stageKey };
76912
+ }
76913
+ const messageId = input2.message.id;
76914
+ const queued = pendingStageMessagesFor(messages2, input2.runId, input2.stageKey, stageIdentity);
76915
+ const existing = messages2.find((entry2) => entry2.runId === input2.runId && entry2.id === messageId);
76916
+ if (existing !== undefined) {
76917
+ if (pendingStageMessageSignature(existing, stageIdentity) !== pendingStageMessageSignature(input2, stageIdentity)) {
76918
+ return {
76919
+ ok: false,
76920
+ reason: "message_id_conflict",
76921
+ runId: input2.runId,
76922
+ stageKey: input2.stageKey,
76923
+ messageId
76924
+ };
76925
+ }
76926
+ const queuedPosition = queued.indexOf(existing);
76927
+ return {
76928
+ ok: true,
76929
+ messages: messages2,
76930
+ entry: existing,
76931
+ ...queuedPosition >= 0 ? { position: queuedPosition + 1 } : {},
76932
+ deduplicated: true
76933
+ };
76934
+ }
76935
+ if (queued.length >= PENDING_STAGE_MESSAGE_LIMIT) {
76936
+ return {
76937
+ ok: false,
76938
+ reason: "capacity",
76939
+ limit: PENDING_STAGE_MESSAGE_LIMIT,
76940
+ runId: input2.runId,
76941
+ stageKey: input2.stageKey
76942
+ };
76943
+ }
76944
+ const entry = {
76945
+ ...input2,
76946
+ id: messageId,
76947
+ ...stageIdentity !== undefined ? { stageId: stageIdentity.id } : {},
76948
+ ...stageIdentity?.replayKey !== undefined ? { stageReplayKey: stageIdentity.replayKey } : {},
76949
+ admissionOrder: nextAdmissionOrder(messages2, input2.runId),
76950
+ status: "queued"
76951
+ };
76952
+ return {
76953
+ ok: true,
76954
+ messages: [...messages2, entry],
76955
+ entry,
76956
+ position: queued.length + 1,
76957
+ deduplicated: false
76958
+ };
76959
+ }
76960
+ function pendingStageMessagesFor(messages2, runId, stageKey, stageIdentity) {
76961
+ return messages2.map((entry, index) => ({ entry, index })).filter(({ entry }) => matchesPendingStage(entry, runId, stageKey, stageIdentity) && entry.status === "queued").sort((left, right) => (left.entry.admissionOrder ?? left.index + 1) - (right.entry.admissionOrder ?? right.index + 1) || left.index - right.index).map(({ entry }) => entry);
76962
+ }
76963
+ function markPendingStageMessageDelivered(messages2, runId, stageKey, messageId, deliveredAt, stageIdentity) {
76964
+ return updateQueuedPendingStageMessage(messages2, runId, stageKey, messageId, stageIdentity, (entry) => ({
76965
+ ...entry,
76966
+ status: "delivered",
76967
+ deliveredAt
76968
+ }));
76969
+ }
76970
+ function markPendingStageMessageUndeliverable(messages2, runId, stageKey, messageId, reason, stageIdentity) {
76971
+ return updateQueuedPendingStageMessage(messages2, runId, stageKey, messageId, stageIdentity, (entry) => ({
76972
+ ...entry,
76973
+ status: "undeliverable",
76974
+ undeliverableReason: reason,
76975
+ undeliverableNotificationId: pendingStageUndeliverableNotificationId(entry)
76976
+ }));
76977
+ }
76978
+ function markPendingStageMessageUndeliverableNotified(messages2, runId, stageKey, messageId, notificationId, notifiedAt) {
76979
+ const index = messages2.findIndex((entry) => matchesPendingStage(entry, runId, stageKey) && entry.id === messageId && entry.status === "undeliverable" && entry.undeliverableNotificationId === notificationId && entry.undeliverableNotifiedAt === undefined);
76980
+ if (index < 0)
76981
+ return messages2;
76982
+ const next = [...messages2];
76983
+ next[index] = { ...next[index], undeliverableNotifiedAt: notifiedAt };
76984
+ return next;
76985
+ }
76986
+ function pendingStageUndeliverableNotificationId(entry) {
76987
+ return [
76988
+ PENDING_STAGE_UNDELIVERABLE_NOTIFICATION_PREFIX,
76989
+ encodeURIComponent(entry.runId),
76990
+ encodeURIComponent(entry.stageKey),
76991
+ encodeURIComponent(entry.id)
76992
+ ].join(":");
76993
+ }
76994
+ function updateQueuedPendingStageMessage(messages2, runId, stageKey, messageId, stageIdentity, update2) {
76995
+ const index = messages2.findIndex((entry) => matchesPendingStage(entry, runId, stageKey, stageIdentity) && entry.id === messageId && entry.status === "queued");
76996
+ if (index < 0)
76997
+ return messages2;
76998
+ const next = [...messages2];
76999
+ next[index] = update2(next[index]);
77000
+ return next;
77001
+ }
77002
+ function matchesPendingStage(entry, runId, stageKey, stageIdentity) {
77003
+ if (entry.runId !== runId)
77004
+ return false;
77005
+ if (stageIdentity === undefined)
77006
+ return entry.stageKey === stageKey;
77007
+ return entry.stageId === stageIdentity.id || entry.stageReplayKey !== undefined && entry.stageReplayKey === stageIdentity.replayKey || entry.stageId === undefined && stageIdentity.aliases.includes(entry.stageKey);
77008
+ }
77009
+ function nextAdmissionOrder(messages2, runId) {
77010
+ let greatest = 0;
77011
+ let legacyPosition = 0;
77012
+ for (const entry of messages2) {
77013
+ if (entry.runId !== runId)
77014
+ continue;
77015
+ legacyPosition += 1;
77016
+ greatest = Math.max(greatest, entry.admissionOrder ?? legacyPosition);
77017
+ }
77018
+ return greatest + 1;
77019
+ }
77020
+ function pendingStageMessageSignature(entry, stageIdentity) {
77021
+ const storedTarget = "status" in entry ? entry.stageReplayKey ?? entry.stageId : undefined;
77022
+ const { timestamp: transportTimestamp, ...logicalMessage } = entry.message;
77023
+ return stableJson({
77024
+ target: storedTarget ?? stageIdentity?.replayKey ?? stageIdentity?.id ?? entry.stageKey,
77025
+ sender: entry.senderReturnAddress ?? entry.from.id,
77026
+ message: {
77027
+ ...logicalMessage,
77028
+ expectsReply: logicalMessage.expectsReply ?? false,
77029
+ content: {
77030
+ ...logicalMessage.content,
77031
+ attachments: logicalMessage.content.attachments ?? []
77032
+ }
77033
+ }
77034
+ });
77035
+ }
77036
+ function stableJson(value) {
77037
+ return JSON.stringify(sortJsonValue(value));
77038
+ }
77039
+ function sortJsonValue(value) {
77040
+ if (Array.isArray(value))
77041
+ return value.map(sortJsonValue);
77042
+ if (typeof value !== "object" || value === null)
77043
+ return value;
77044
+ return Object.fromEntries(Object.entries(value).filter(([, nested]) => nested !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortJsonValue(nested)]));
77045
+ }
77046
+ function normalizeDeliveryGroup(value) {
77047
+ if (typeof value !== "string")
77048
+ return "default";
77049
+ const trimmed = value.trim();
77050
+ return trimmed.length > 0 ? trimmed : "default";
77051
+ }
76720
77052
  // dist/builtin/workflows/src/shared/session-scoped-singleton.ts
76721
77053
  init_src();
76722
77054
  var SHARED_STATES_KEY = Symbol.for("atomic-workflows/session-scoped-singleton-pre-adoption@1");
@@ -77229,6 +77561,7 @@ function compactStage(stage) {
77229
77561
  inputRequest,
77230
77562
  notices,
77231
77563
  mcpScope: _mcpScope,
77564
+ pendingStageDeliveryAvailable: _pendingStageDeliveryAvailable,
77232
77565
  attemptedModels: _attemptedModels,
77233
77566
  modelAttempts: _modelAttempts,
77234
77567
  result: _result,
@@ -77484,6 +77817,133 @@ function createStoreContext(state = createStoreState()) {
77484
77817
  };
77485
77818
  }
77486
77819
 
77820
+ // dist/builtin/workflows/src/shared/store-pending-stage-delivery-methods.ts
77821
+ function createPendingStageDeliveryStoreMethods(context) {
77822
+ const transitions = new Map;
77823
+ const serialize = async (runId, transition) => {
77824
+ const previous2 = transitions.get(runId) ?? Promise.resolve();
77825
+ const result = previous2.catch(() => {
77826
+ return;
77827
+ }).then(transition);
77828
+ const settled = result.then(() => {
77829
+ return;
77830
+ }, () => {
77831
+ return;
77832
+ });
77833
+ transitions.set(runId, settled);
77834
+ settled.finally(() => {
77835
+ if (transitions.get(runId) === settled)
77836
+ transitions.delete(runId);
77837
+ });
77838
+ return await result;
77839
+ };
77840
+ return {
77841
+ async queueStageMessage(input2, senderGroup, runGroup, backend) {
77842
+ return await serialize(input2.runId, async () => {
77843
+ const run = context.findRun(input2.runId);
77844
+ if (run === undefined)
77845
+ return;
77846
+ const stageIdentity = resolvePendingStageIdentity(run, input2.stageKey);
77847
+ const result = queueStageMessage(run.pendingStageMessages ?? [], input2, senderGroup, runGroup, stageIdentity);
77848
+ if (result.ok && !result.deduplicated) {
77849
+ await persistTransition(backend, input2.runId, result.messages);
77850
+ run.pendingStageMessages = [...result.messages];
77851
+ context.bumpAndNotify();
77852
+ }
77853
+ return result;
77854
+ });
77855
+ },
77856
+ async validateLiveStageMessage(input2) {
77857
+ return await serialize(input2.runId, async () => {
77858
+ const run = context.findRun(input2.runId);
77859
+ if (run === undefined)
77860
+ return;
77861
+ const result = queueStageMessage(run.pendingStageMessages ?? [], input2, undefined, undefined, resolvePendingStageIdentity(run, input2.stageKey));
77862
+ if (!result.ok) {
77863
+ return result.reason === "message_id_conflict" ? { outcome: "message_id_conflict", messageId: result.messageId } : { outcome: "forward" };
77864
+ }
77865
+ if (!result.deduplicated)
77866
+ return { outcome: "forward" };
77867
+ if (result.entry.status === "delivered")
77868
+ return { outcome: "delivered" };
77869
+ if (result.entry.status === "undeliverable") {
77870
+ return { outcome: "undeliverable", reason: result.entry.undeliverableReason };
77871
+ }
77872
+ if (result.position === undefined) {
77873
+ throw new Error(`atomic-workflows: queued message ${input2.message.id} has no active position`);
77874
+ }
77875
+ return { outcome: "queued", position: result.position };
77876
+ });
77877
+ },
77878
+ pendingStageMessagesFor(runId, stageKey) {
77879
+ const run = context.findRun(runId);
77880
+ return pendingStageMessagesFor(run?.pendingStageMessages ?? [], runId, stageKey, run === undefined ? undefined : resolvePendingStageIdentity(run, stageKey));
77881
+ },
77882
+ async markPendingStageMessageDelivered(runId, stageKey, messageId, deliveredAt, backend) {
77883
+ return await serialize(runId, async () => {
77884
+ const run = context.findRun(runId);
77885
+ if (run === undefined)
77886
+ return false;
77887
+ const current = run.pendingStageMessages ?? [];
77888
+ const next = markPendingStageMessageDelivered(current, runId, stageKey, messageId, deliveredAt, resolvePendingStageIdentity(run, stageKey));
77889
+ if (next === current)
77890
+ return false;
77891
+ await persistTransition(backend, runId, next);
77892
+ run.pendingStageMessages = [...next];
77893
+ context.bumpAndNotify();
77894
+ return true;
77895
+ });
77896
+ },
77897
+ async markPendingStageMessageUndeliverable(runId, stageKey, messageId, reason, backend) {
77898
+ return await serialize(runId, async () => {
77899
+ const run = context.findRun(runId);
77900
+ if (run === undefined)
77901
+ return false;
77902
+ const current = run.pendingStageMessages ?? [];
77903
+ const next = markPendingStageMessageUndeliverable(current, runId, stageKey, messageId, reason, resolvePendingStageIdentity(run, stageKey));
77904
+ if (next === current)
77905
+ return false;
77906
+ await persistTransition(backend, runId, next);
77907
+ run.pendingStageMessages = [...next];
77908
+ context.bumpAndNotify();
77909
+ return true;
77910
+ });
77911
+ },
77912
+ async markPendingStageMessageUndeliverableNotified(runId, stageKey, messageId, notificationId, notifiedAt, backend) {
77913
+ return await serialize(runId, async () => {
77914
+ const run = context.findRun(runId);
77915
+ if (run === undefined)
77916
+ return false;
77917
+ const current = run.pendingStageMessages ?? [];
77918
+ const next = markPendingStageMessageUndeliverableNotified(current, runId, stageKey, messageId, notificationId, notifiedAt);
77919
+ if (next === current)
77920
+ return false;
77921
+ await persistTransition(backend, runId, next);
77922
+ run.pendingStageMessages = [...next];
77923
+ context.bumpAndNotify();
77924
+ return true;
77925
+ });
77926
+ }
77927
+ };
77928
+ }
77929
+ function resolvePendingStageIdentity(run, stageKey) {
77930
+ const exactIds = run.stages.filter((stage2) => stage2.id === stageKey);
77931
+ const candidates = exactIds.length > 0 ? exactIds : run.stages.filter((stage2) => stage2.name === stageKey);
77932
+ if (candidates.length !== 1)
77933
+ return;
77934
+ const stage = candidates[0];
77935
+ return {
77936
+ id: stage.id,
77937
+ ...stage.replayKey !== undefined ? { replayKey: stage.replayKey } : {},
77938
+ aliases: stage.id === stage.name ? [stage.id] : [stage.id, stage.name]
77939
+ };
77940
+ }
77941
+ async function persistTransition(backend, runId, messages2) {
77942
+ if (!await backend.persistPendingStageMessages(runId, messages2)) {
77943
+ throw new Error(`atomic-workflows: durable workflow ${runId} is unavailable for pending-stage persistence`);
77944
+ }
77945
+ }
77946
+
77487
77947
  // dist/builtin/workflows/src/shared/store-prompt-methods.ts
77488
77948
  function createPromptStoreMethods(context) {
77489
77949
  const { state } = context;
@@ -78401,6 +78861,7 @@ function createStore() {
78401
78861
  const context = createStoreContext();
78402
78862
  return {
78403
78863
  ...createRunStoreMethods(context),
78864
+ ...createPendingStageDeliveryStoreMethods(context),
78404
78865
  ...createStageStoreMethods(context),
78405
78866
  ...createToolNodeStoreMethods(context),
78406
78867
  ...createPromptStoreMethods(context)
@@ -90556,6 +91017,26 @@ function canonicalJsonString(value) {
90556
91017
  }
90557
91018
 
90558
91019
  // dist/builtin/workflows/src/durable/backend.ts
91020
+ function replacePendingStageMessagesForRun(existing, logicalRunId, messages2) {
91021
+ if (messages2.some((entry) => entry.runId !== logicalRunId)) {
91022
+ throw new Error(`atomic-workflows: pending-stage persistence mixed logical run ${logicalRunId}`);
91023
+ }
91024
+ const firstOwnedIndex = existing.findIndex((entry) => entry.runId === logicalRunId);
91025
+ if (firstOwnedIndex < 0)
91026
+ return [...existing, ...messages2];
91027
+ const merged = [];
91028
+ for (let index = 0;index < existing.length; index += 1) {
91029
+ const entry = existing[index];
91030
+ if (index === firstOwnedIndex)
91031
+ merged.push(...messages2);
91032
+ if (entry.runId !== logicalRunId)
91033
+ merged.push(entry);
91034
+ }
91035
+ return merged;
91036
+ }
91037
+ function pendingStageMessagesForDurableRun(backend, logicalRunId, rootWorkflowId = logicalRunId) {
91038
+ return (backend.getWorkflow(rootWorkflowId)?.pendingStageMessages ?? []).filter((entry) => entry.runId === logicalRunId);
91039
+ }
90559
91040
  function checkpointKey(c2) {
90560
91041
  return `${c2.kind}:${c2.checkpointId}`;
90561
91042
  }
@@ -90582,6 +91063,7 @@ class InMemoryDurableBackend {
90582
91063
  ...handle.workflowCwd !== undefined ? { workflowCwd: handle.workflowCwd } : existing?.handle.workflowCwd !== undefined ? { workflowCwd: existing.handle.workflowCwd } : {},
90583
91064
  ...handle.repositoryRoot !== undefined ? { repositoryRoot: handle.repositoryRoot } : existing?.handle.repositoryRoot !== undefined ? { repositoryRoot: existing.handle.repositoryRoot } : {},
90584
91065
  ...handle.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: handle.gitWorktreeRoot } : existing?.handle.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: existing.handle.gitWorktreeRoot } : {},
91066
+ pendingStageMessages: handle.pendingStageMessages ?? existing?.handle.pendingStageMessages ?? [],
90585
91067
  ...handle.sessionFile !== undefined ? { sessionFile: handle.sessionFile } : {},
90586
91068
  completedCheckpoints,
90587
91069
  pendingPrompts,
@@ -90606,6 +91088,17 @@ class InMemoryDurableBackend {
90606
91088
  if (handle.pendingPrompts !== undefined)
90607
91089
  this.promptReservations.delete(handle.workflowId);
90608
91090
  }
91091
+ async persistPendingStageMessages(workflowId, messages2, logicalRunId = workflowId) {
91092
+ const handle = this.getWorkflow(workflowId);
91093
+ if (handle === undefined)
91094
+ return false;
91095
+ this.registerWorkflow({
91096
+ ...handle,
91097
+ pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages2),
91098
+ updatedAt: nextMetadataTimestamp(handle.updatedAt)
91099
+ });
91100
+ return true;
91101
+ }
90609
91102
  recordCheckpoint(checkpoint) {
90610
91103
  const currentCheckpoint = withCurrentStageTopology(checkpoint);
90611
91104
  const rec = this.workflows.get(currentCheckpoint.workflowId);
@@ -90798,6 +91291,7 @@ class InMemoryDurableBackend {
90798
91291
  createdAt: h.createdAt,
90799
91292
  completedCheckpoints: h.completedCheckpoints,
90800
91293
  pendingPrompts: h.pendingPrompts,
91294
+ pendingStageMessages: h.pendingStageMessages ?? [],
90801
91295
  ...h.ownerExecutorId !== undefined ? { ownerExecutorId: h.ownerExecutorId } : {},
90802
91296
  ...h.sessionFile !== undefined ? { sessionFile: h.sessionFile } : {},
90803
91297
  ...h.label !== undefined ? { label: h.label } : {},
@@ -90886,6 +91380,9 @@ function toResumableEntry(handle) {
90886
91380
  updatedAt: handle.updatedAt
90887
91381
  };
90888
91382
  }
91383
+ function nextMetadataTimestamp(previous2) {
91384
+ return Math.max(Date.now(), previous2 + 1);
91385
+ }
90889
91386
 
90890
91387
  // dist/builtin/workflows/src/durable/stage-topology-validation.ts
90891
91388
  function immutableStageGroupError(stages) {
@@ -92076,6 +92573,7 @@ function encodeMetadata(metadata) {
92076
92573
  completedCheckpoints: metadata.completedCheckpoints,
92077
92574
  pendingPrompts: metadata.pendingPrompts,
92078
92575
  promptReservationEpoch: metadata.promptReservationEpoch,
92576
+ ...metadata.pendingStageMessages !== undefined ? { pendingStageMessages: serializePendingStageMessages(metadata.pendingStageMessages) } : {},
92079
92577
  ...metadata.ownerExecutorId !== undefined ? { ownerExecutorId: metadata.ownerExecutorId } : {},
92080
92578
  ...metadata.transitionClaimId !== undefined ? { transitionClaimId: metadata.transitionClaimId } : {},
92081
92579
  ...metadata.sessionFile !== undefined ? { sessionFile: metadata.sessionFile } : {},
@@ -92133,15 +92631,101 @@ function metadataTimestamp(record) {
92133
92631
  function parseDurableWorkflowMetadata(value, workflowId) {
92134
92632
  if (typeof value !== "object" || value === null || Array.isArray(value))
92135
92633
  return;
92634
+ const serialized = value;
92635
+ const pendingStageMessages = parsePendingStageMessages(serialized.pendingStageMessages);
92636
+ if (pendingStageMessages === null)
92637
+ return;
92136
92638
  const metadata = value;
92137
92639
  if (metadata.workflowId !== workflowId || typeof metadata.workflowId !== "string" || typeof metadata.name !== "string" || typeof metadata.inputs !== "object" || metadata.inputs === null || Array.isArray(metadata.inputs) || typeof metadata.status !== "string" || !isDurableWorkflowStatus(metadata.status) || typeof metadata.completedCheckpoints !== "number" || typeof metadata.createdAt !== "number" || typeof metadata.pendingPrompts !== "number" || typeof metadata.promptReservationEpoch !== "string" || typeof metadata.updatedAt !== "number" || metadata.ownerExecutorId !== undefined && typeof metadata.ownerExecutorId !== "string" || metadata.transitionClaimId !== undefined && typeof metadata.transitionClaimId !== "string" || metadata.sessionFile !== undefined && typeof metadata.sessionFile !== "string" || metadata.label !== undefined && typeof metadata.label !== "string" || metadata.rootWorkflowId !== undefined && typeof metadata.rootWorkflowId !== "string" || metadata.resumable !== undefined && typeof metadata.resumable !== "boolean" || metadata.exited !== undefined && typeof metadata.exited !== "boolean" || metadata.exitReason !== undefined && typeof metadata.exitReason !== "string" || metadata.error !== undefined && typeof metadata.error !== "string" || metadata.failureKind !== undefined && !isWorkflowFailureKind(metadata.failureKind) || metadata.failureCode !== undefined && !isWorkflowFailureCode(metadata.failureCode) || metadata.failureRecoverability !== undefined && !isWorkflowFailureRecoverability(metadata.failureRecoverability) || metadata.failureDisposition !== undefined && !isWorkflowFailureDisposition(metadata.failureDisposition) || metadata.failedToolNodeId !== undefined && typeof metadata.failedToolNodeId !== "string" || metadata.invocationCwd !== undefined && typeof metadata.invocationCwd !== "string" || metadata.workflowCwd !== undefined && typeof metadata.workflowCwd !== "string" || metadata.repositoryRoot !== undefined && typeof metadata.repositoryRoot !== "string" || metadata.gitWorktreeRoot !== undefined && typeof metadata.gitWorktreeRoot !== "string")
92138
92640
  return;
92139
92641
  const { origin, ...metadataWithoutOrigin } = metadata;
92140
92642
  return {
92141
92643
  ...metadataWithoutOrigin,
92644
+ pendingStageMessages,
92142
92645
  ...isWorkflowActor(origin) ? { origin } : {}
92143
92646
  };
92144
92647
  }
92648
+ function serializePendingStageMessages(messages2) {
92649
+ return messages2.map((entry) => ({
92650
+ id: entry.id,
92651
+ runId: entry.runId,
92652
+ stageKey: entry.stageKey,
92653
+ ...entry.stageId !== undefined ? { stageId: entry.stageId } : {},
92654
+ ...entry.stageReplayKey !== undefined ? { stageReplayKey: entry.stageReplayKey } : {},
92655
+ ...entry.senderRegistrationName !== undefined ? { senderRegistrationName: entry.senderRegistrationName } : {},
92656
+ ...entry.senderReturnAddress !== undefined ? { senderReturnAddress: entry.senderReturnAddress } : {},
92657
+ from: {
92658
+ id: entry.from.id,
92659
+ ...entry.from.name !== undefined ? { name: entry.from.name } : {},
92660
+ ...entry.from.group !== undefined ? { group: entry.from.group } : {},
92661
+ ...entry.from.cwd !== undefined ? { cwd: entry.from.cwd } : {},
92662
+ ...entry.from.model !== undefined ? { model: entry.from.model } : {},
92663
+ ...entry.from.pid !== undefined ? { pid: entry.from.pid } : {},
92664
+ ...entry.from.startedAt !== undefined ? { startedAt: entry.from.startedAt } : {},
92665
+ ...entry.from.lastActivity !== undefined ? { lastActivity: entry.from.lastActivity } : {},
92666
+ ...entry.from.status !== undefined ? { status: entry.from.status } : {}
92667
+ },
92668
+ message: {
92669
+ id: entry.message.id,
92670
+ timestamp: entry.message.timestamp,
92671
+ ...entry.message.replyTo !== undefined ? { replyTo: entry.message.replyTo } : {},
92672
+ ...entry.message.expectsReply !== undefined ? { expectsReply: entry.message.expectsReply } : {},
92673
+ ...entry.message.replyError !== undefined ? { replyError: entry.message.replyError } : {},
92674
+ ...entry.message.source !== undefined ? { source: { ...entry.message.source } } : {},
92675
+ content: {
92676
+ text: entry.message.content.text,
92677
+ ...entry.message.content.attachments !== undefined ? { attachments: entry.message.content.attachments.map((attachment) => ({ ...attachment })) } : {}
92678
+ }
92679
+ },
92680
+ queuedAt: entry.queuedAt,
92681
+ ...entry.admissionOrder !== undefined ? { admissionOrder: entry.admissionOrder } : {},
92682
+ status: entry.status,
92683
+ ...entry.deliveredAt !== undefined ? { deliveredAt: entry.deliveredAt } : {},
92684
+ ...entry.undeliverableReason !== undefined ? { undeliverableReason: entry.undeliverableReason } : {},
92685
+ ...entry.undeliverableNotificationId !== undefined ? { undeliverableNotificationId: entry.undeliverableNotificationId } : {},
92686
+ ...entry.undeliverableNotifiedAt !== undefined ? { undeliverableNotifiedAt: entry.undeliverableNotifiedAt } : {}
92687
+ }));
92688
+ }
92689
+ function parsePendingStageMessages(value) {
92690
+ if (value === undefined)
92691
+ return [];
92692
+ if (!Array.isArray(value) || !value.every(isPendingStageMessage))
92693
+ return null;
92694
+ return value;
92695
+ }
92696
+ function isPendingStageMessage(value) {
92697
+ if (!isSerializableObject(value))
92698
+ return false;
92699
+ return typeof value.id === "string" && typeof value.runId === "string" && typeof value.stageKey === "string" && (value.stageId === undefined || typeof value.stageId === "string") && (value.stageReplayKey === undefined || typeof value.stageReplayKey === "string") && (value.senderRegistrationName === undefined || typeof value.senderRegistrationName === "string") && (value.senderReturnAddress === undefined || typeof value.senderReturnAddress === "string") && typeof value.queuedAt === "string" && (value.admissionOrder === undefined || typeof value.admissionOrder === "number" && Number.isSafeInteger(value.admissionOrder) && value.admissionOrder > 0) && (value.status === "queued" || value.status === "delivered" || value.status === "undeliverable") && (value.deliveredAt === undefined || typeof value.deliveredAt === "string") && (value.undeliverableReason === undefined || typeof value.undeliverableReason === "string") && (value.undeliverableNotificationId === undefined || typeof value.undeliverableNotificationId === "string") && (value.undeliverableNotifiedAt === undefined || typeof value.undeliverableNotifiedAt === "string") && isPendingStageSender(value.from) && isPendingStageIntercomMessage(value.message);
92700
+ }
92701
+ function isPendingStageSender(value) {
92702
+ return isSerializableObject(value) && typeof value.id === "string" && (value.name === undefined || typeof value.name === "string") && (value.group === undefined || typeof value.group === "string") && (value.cwd === undefined || typeof value.cwd === "string") && (value.model === undefined || typeof value.model === "string") && (value.pid === undefined || typeof value.pid === "number") && (value.startedAt === undefined || typeof value.startedAt === "number") && (value.lastActivity === undefined || typeof value.lastActivity === "number") && (value.status === undefined || typeof value.status === "string");
92703
+ }
92704
+ function isPendingStageIntercomMessage(value) {
92705
+ if (!isSerializableObject(value) || typeof value.id !== "string" || typeof value.timestamp !== "number")
92706
+ return false;
92707
+ if (value.replyTo !== undefined && typeof value.replyTo !== "string")
92708
+ return false;
92709
+ if (value.expectsReply !== undefined && typeof value.expectsReply !== "boolean")
92710
+ return false;
92711
+ if (value.replyError !== undefined && typeof value.replyError !== "string")
92712
+ return false;
92713
+ if (!isSerializableObject(value.content) || typeof value.content.text !== "string")
92714
+ return false;
92715
+ const attachments = value.content.attachments;
92716
+ if (attachments !== undefined && (!Array.isArray(attachments) || !attachments.every(isPendingStageAttachment)))
92717
+ return false;
92718
+ return value.source === undefined || isPendingStageSource(value.source);
92719
+ }
92720
+ function isPendingStageAttachment(value) {
92721
+ return isSerializableObject(value) && (value.type === "file" || value.type === "snippet" || value.type === "context") && typeof value.name === "string" && typeof value.content === "string" && (value.language === undefined || typeof value.language === "string");
92722
+ }
92723
+ function isPendingStageSource(value) {
92724
+ return isSerializableObject(value) && typeof value.subagentRunId === "string" && (value.subagentAgent === undefined || typeof value.subagentAgent === "string") && (value.subagentIndex === undefined || typeof value.subagentIndex === "number");
92725
+ }
92726
+ function isSerializableObject(value) {
92727
+ return typeof value === "object" && value !== null && !Array.isArray(value);
92728
+ }
92145
92729
  function isWorkflowActor(value) {
92146
92730
  return value === "user" || value === "agent";
92147
92731
  }
@@ -92676,6 +93260,29 @@ class DbosDurableBackend {
92676
93260
  await this.writeMetadata(handle.workflowId);
92677
93261
  });
92678
93262
  }
93263
+ async persistPendingStageMessages(workflowId, messages2, logicalRunId = workflowId) {
93264
+ let persisted = false;
93265
+ await this.enqueueWrite(async () => {
93266
+ if (!this.isWorkflowLoadable(workflowId))
93267
+ return;
93268
+ const handle = this.mem.getWorkflow(workflowId);
93269
+ const value = this.mem.toMetadata(workflowId);
93270
+ if (handle === undefined || value === undefined)
93271
+ return;
93272
+ const updatedAt = Math.max(Date.now(), handle.updatedAt + 1);
93273
+ const pendingStageMessages = replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages2);
93274
+ const metadata = {
93275
+ ...this.promptReservations.metadata(workflowId, value),
93276
+ pendingStageMessages,
93277
+ ownerExecutorId: this.executorId,
93278
+ updatedAt
93279
+ };
93280
+ await this.sdk.recordStepOutput(workflowId, metadataStepName(updatedAt), encodeMetadata(metadata));
93281
+ this.mem.registerWorkflow({ ...handle, pendingStageMessages, updatedAt });
93282
+ persisted = true;
93283
+ });
93284
+ return persisted;
93285
+ }
92679
93286
  recordCheckpoint(checkpoint) {
92680
93287
  if (!this.isWorkflowLoadable(checkpoint.workflowId))
92681
93288
  return;
@@ -95378,6 +95985,10 @@ function withMidSessionResumePrompt(stage, enabled) {
95378
95985
  });
95379
95986
  return stage;
95380
95987
  }
95988
+ function pendingStageIdForReplay(backend, workflowId, replayKey, stageName) {
95989
+ const candidates = new Set((backend.getWorkflow(workflowId)?.pendingStageMessages ?? []).filter((entry) => entry.stageReplayKey === replayKey || entry.stageReplayKey === undefined && entry.stageKey === stageName).map((entry) => entry.stageId).filter((stageId) => stageId !== undefined));
95990
+ return candidates.size === 1 ? candidates.values().next().value : undefined;
95991
+ }
95381
95992
  function createDurableStagePrimitive(input2) {
95382
95993
  return (name, options) => {
95383
95994
  const replayKey = input2.nextReplayKey(name);
@@ -95389,13 +96000,12 @@ function createDurableStagePrimitive(input2) {
95389
96000
  const session = input2.backend.getStageSession(input2.workflowId, replayKey);
95390
96001
  const isMidSessionResume = session?.sessionFile !== undefined;
95391
96002
  const topology = activeStageTopology(input2.backend, input2.workflowId, replayKey);
96003
+ const durableStageId = topology?.stageId ?? pendingStageIdForReplay(input2.backend, input2.workflowId, replayKey, name);
95392
96004
  const liveOptions = {
95393
96005
  ...options ?? {},
95394
96006
  durableReplayKey: replayKey,
95395
- ...topology !== undefined ? {
95396
- durableStageId: topology.stageId,
95397
- durableParentIds: [...topology.parentIds]
95398
- } : {},
96007
+ ...durableStageId !== undefined ? { durableStageId } : {},
96008
+ ...topology !== undefined ? { durableParentIds: [...topology.parentIds] } : {},
95399
96009
  ...isMidSessionResume ? {
95400
96010
  resumeFromSessionFile: session.sessionFile,
95401
96011
  durableAccumulatedDurationMs: session.durationMs ?? 0
@@ -96093,6 +96703,7 @@ function appendStageStart(api, payload) {
96093
96703
  name: payload.name,
96094
96704
  parentIds: [...payload.parentIds],
96095
96705
  ...payload.model !== undefined ? { model: payload.model } : {},
96706
+ ...payload.pendingStageDeliveryAvailable !== undefined ? { pendingStageDeliveryAvailable: payload.pendingStageDeliveryAvailable } : {},
96096
96707
  ...payload.replayKey !== undefined ? { replayKey: payload.replayKey } : {},
96097
96708
  ...payload.replayedFromStageId !== undefined ? { replayedFromStageId: payload.replayedFromStageId } : {},
96098
96709
  ...payload.replayed !== undefined ? { replayed: payload.replayed } : {},
@@ -96636,6 +97247,7 @@ function installCompactionHook(api, store2) {
96636
97247
  stageId: stage.id,
96637
97248
  name: stage.name,
96638
97249
  parentIds: [...stage.parentIds],
97250
+ pendingStageDeliveryAvailable: stage.pendingStageDeliveryAvailable,
96639
97251
  ts: stage.startedAt ?? now
96640
97252
  });
96641
97253
  }
@@ -97212,7 +97824,7 @@ function formatWorkflowLifecycleNoticeText(details) {
97212
97824
  return `? Workflow "${workflowName}" needs input (run ${details.runId}).${prompt2} Respond: /workflow connect ${details.runId} to answer this run-level prompt.`;
97213
97825
  }
97214
97826
  const stage = details.stageName ?? details.stageId ?? "unknown";
97215
- const responseHint = details.stageId && details.promptId ? `/workflow connect ${details.runId} or workflow({ action: "send", runId: ${jsonString(details.runId)}, stageId: ${jsonString(details.stageId)}, promptId: ${jsonString(details.promptId)}, response: ... })` : `/workflow connect ${details.runId}`;
97827
+ const responseHint = details.stageId && details.promptId ? `/workflow connect ${details.runId} or workflow({ action: "answer", runId: ${jsonString(details.runId)}, stageId: ${jsonString(details.stageId)}, promptId: ${jsonString(details.promptId)}, response: ... })` : `/workflow connect ${details.runId}`;
97216
97828
  return `? Workflow "${workflowName}" needs input (run ${details.runId}, stage ${stage}).${prompt2} Respond: ${responseHint}.`;
97217
97829
  }
97218
97830
  function makeTerminalNotice(run, kind) {
@@ -98512,6 +99124,12 @@ function workflow(spec) {
98512
99124
  return Object.freeze(branded);
98513
99125
  }
98514
99126
 
99127
+ // dist/builtin/workflows/builtin/steering-context.ts
99128
+ var exports_steering_context = {};
99129
+ __export(exports_steering_context, {
99130
+ withSteeringPropagationContext: () => withSteeringPropagationContext
99131
+ });
99132
+
98515
99133
  // dist/builtin/workflows/src/authoring/keep-context.ts
98516
99134
  var KEEP_CONTEXT_OPEN_TAG = "<keepContext>";
98517
99135
  var KEEP_CONTEXT_CLOSE_TAG = "</keepContext>";
@@ -98525,6 +99143,12 @@ ${KEEP_CONTEXT_CLOSE_TAG}`;
98525
99143
  }
98526
99144
 
98527
99145
  // dist/builtin/workflows/builtin/shared-prompts.ts
99146
+ var PENDING_STAGE_DELIVERY_GUIDANCE_LINES = [
99147
+ "Send material updates through Intercom to every affected workflow stage, including stages that have not started.",
99148
+ "Atomic queues messages for known pending stages addressed as `<runId>:<stageKey>` and delivers them when their sessions initialize."
99149
+ ];
99150
+ var PENDING_STAGE_DELIVERY_GUIDANCE = PENDING_STAGE_DELIVERY_GUIDANCE_LINES.join(`
99151
+ `);
98528
99152
  var STEERING_PROPAGATION_CONTRACT = [
98529
99153
  "Steering propagation contract:",
98530
99154
  "- Mid-run user messages (steering, follow-ups, resume text) are authoritative and may amend this run's objective or acceptance criteria. Adopt an amendment as required behavior from the moment you receive it.",
@@ -98532,6 +99156,7 @@ var STEERING_PROPAGATION_CONTRACT = [
98532
99156
  "- Keep user-authored amendments visibly separate from your own observations, so later stages can tell a required clause from an agent proposal.",
98533
99157
  "- Treat amendments inherited from an upstream stage as contract clauses. Cover them in acceptance and traceability work; never classify inherited user amendments as beyond_objective, unrequested scope, or speculative expansion.",
98534
99158
  "- If an amendment is ambiguous, or conflicts with the launch contract or another amendment, resolve it before implementing: ask through `intercom` when a supervisor or originating stage is reachable, otherwise state the conflict in your report and implement the narrowest reading consistent with the launch contract.",
99159
+ ...PENDING_STAGE_DELIVERY_GUIDANCE_LINES.map((line) => `- ${line}`),
98535
99160
  "- Propagate nothing else this way. Guidance about how to work, tool preferences, and your own improvement ideas are not amendments; those follow the scope discipline contract."
98536
99161
  ].join(`
98537
99162
  `);
@@ -106193,6 +106818,9 @@ class ScopedDurableBackend {
106193
106818
  this.persistent = inner.persistent;
106194
106819
  }
106195
106820
  registerWorkflow(_handle) {}
106821
+ persistPendingStageMessages(workflowId, messages2, logicalRunId = workflowId) {
106822
+ return this.inner.persistPendingStageMessages(this.scope.rootWorkflowId, messages2, logicalRunId);
106823
+ }
106196
106824
  recordCheckpoint(checkpoint) {
106197
106825
  this.inner.recordCheckpoint(this.remap(checkpoint));
106198
106826
  }
@@ -109530,6 +110158,7 @@ import { randomUUID as randomUUID16 } from "node:crypto";
109530
110158
 
109531
110159
  // dist/builtin/workflows/src/shared/intercom-group.ts
109532
110160
  import { randomUUID as randomUUID15 } from "node:crypto";
110161
+ var DEFAULT_INTERCOM_GROUP = "default";
109533
110162
  function workflowInvocationIntercomGroup(rootRunId) {
109534
110163
  return `workflow:${rootRunId}`;
109535
110164
  }
@@ -109539,6 +110168,12 @@ function normalizeAutoGroupSentinel(group) {
109539
110168
  const sentinel = group.trim().toLowerCase();
109540
110169
  return sentinel === "true" || sentinel === "auto" ? true : group;
109541
110170
  }
110171
+ function normalizeGroup(value) {
110172
+ if (typeof value !== "string")
110173
+ return DEFAULT_INTERCOM_GROUP;
110174
+ const trimmed = value.trim();
110175
+ return trimmed.length > 0 ? trimmed : DEFAULT_INTERCOM_GROUP;
110176
+ }
109542
110177
  function resolveStageGroup(stageOptions, workflowGroup) {
109543
110178
  if (!stageOptions)
109544
110179
  return workflowGroup;
@@ -109563,6 +110198,11 @@ function stageHasIntercomAccess(stageOptions) {
109563
110198
  return false;
109564
110199
  return true;
109565
110200
  }
110201
+ function stageCanUseWorkflowPendingStageRoute(stageOptions, workflowGroup) {
110202
+ if (!stageHasIntercomAccess(stageOptions) || stageOptions?.group === true)
110203
+ return false;
110204
+ return normalizeGroup(resolveStageGroup(stageOptions, workflowGroup)) === normalizeGroup(workflowGroup);
110205
+ }
109566
110206
 
109567
110207
  // dist/builtin/workflows/src/shared/budget-meter.ts
109568
110208
  var usageFields = ["input", "output", "cacheRead", "cacheWrite", "cost"];
@@ -109817,9 +110457,9 @@ function createRunBudgetController(input2) {
109817
110457
  function createParallelPrimitive(input2) {
109818
110458
  return async (steps, options = {}) => {
109819
110459
  input2.runtime.exit.throwIfWorkflowExitSelected();
109820
- const normalizeGroup = (value) => value.group === undefined ? value : { ...value, group: normalizeAutoGroupSentinel(value.group) };
109821
- const normalizedOptions = normalizeGroup(options);
109822
- const normalizedSteps = steps.map(normalizeGroup);
110460
+ const normalizeGroup2 = (value) => value.group === undefined ? value : { ...value, group: normalizeAutoGroupSentinel(value.group) };
110461
+ const normalizedOptions = normalizeGroup2(options);
110462
+ const normalizedSteps = steps.map(normalizeGroup2);
109823
110463
  const needsAutoGroup = normalizedOptions.group === true || normalizedSteps.some((step) => step.group === true);
109824
110464
  const autoGroup = needsAutoGroup ? randomUUID16() : undefined;
109825
110465
  const resolveAutoGroup = (value) => value.group === true && autoGroup ? { ...value, group: autoGroup } : value;
@@ -111790,7 +112430,7 @@ function createAdmittedToolExecutionTracker(options = {}) {
111790
112430
  const inFlight = new Set;
111791
112431
  const admissions = [];
111792
112432
  const failures = [];
111793
- let nextAdmissionOrder = 0;
112433
+ let nextAdmissionOrder2 = 0;
111794
112434
  let state = "OPEN";
111795
112435
  let closing;
111796
112436
  const abandonment = Promise.withResolvers();
@@ -111841,7 +112481,7 @@ function createAdmittedToolExecutionTracker(options = {}) {
111841
112481
  return { accepted: false, error, bindNode() {}, noteCancelled() {} };
111842
112482
  }
111843
112483
  const admission = {
111844
- admissionOrder: ++nextAdmissionOrder,
112484
+ admissionOrder: ++nextAdmissionOrder2,
111845
112485
  nodeId: undefined,
111846
112486
  failure: undefined,
111847
112487
  cancelled: false,
@@ -113143,6 +113783,150 @@ function createReplayStageContext(input2) {
113143
113783
  };
113144
113784
  }
113145
113785
 
113786
+ // dist/builtin/workflows/src/durable/run-owner-backend.ts
113787
+ function durableRootRunIdForRun(runs, runId) {
113788
+ return reciprocalWorkflowRootRunId(new Map(runs.map((run2) => [run2.id, run2])), runId);
113789
+ }
113790
+ function durableBackendForRun(backend, runs, runId) {
113791
+ const rootRunId = durableRootRunIdForRun(runs, runId);
113792
+ if (rootRunId === undefined)
113793
+ return;
113794
+ const runById = new Map(runs.map((run2) => [run2.id, run2]));
113795
+ if (rootRunId === runId)
113796
+ return backend;
113797
+ const scopes = [];
113798
+ let current = runById.get(runId);
113799
+ while (current !== undefined && current.id !== rootRunId) {
113800
+ const parent = current.parentRunId === undefined ? undefined : runById.get(current.parentRunId);
113801
+ const boundary = parent?.stages.find((stage) => stage.id === current?.parentStageId);
113802
+ if (parent === undefined || boundary?.replayKey === undefined)
113803
+ return;
113804
+ scopes.push(boundary.replayKey);
113805
+ current = parent;
113806
+ }
113807
+ if (current?.id !== rootRunId)
113808
+ return;
113809
+ let view = backend;
113810
+ for (const scopePrefix of scopes.reverse()) {
113811
+ view = new ScopedDurableBackend(view, { rootWorkflowId: rootRunId, scopePrefix });
113812
+ }
113813
+ return view;
113814
+ }
113815
+
113816
+ // dist/builtin/workflows/src/shared/pending-stage-route-capability.ts
113817
+ import { randomUUID as randomUUID17 } from "node:crypto";
113818
+ var routeCapabilities = new WeakMap;
113819
+ function workflowPendingStageRouteCapability(store2, runId) {
113820
+ let capabilities = routeCapabilities.get(store2);
113821
+ if (capabilities === undefined) {
113822
+ capabilities = new Map;
113823
+ routeCapabilities.set(store2, capabilities);
113824
+ }
113825
+ let capability = capabilities.get(runId);
113826
+ if (capability === undefined) {
113827
+ capability = randomUUID17();
113828
+ capabilities.set(runId, capability);
113829
+ }
113830
+ return capability;
113831
+ }
113832
+
113833
+ // dist/builtin/workflows/src/runs/foreground/pending-stage-delivery.ts
113834
+ var pendingDeliveryClaims = new WeakMap;
113835
+ function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageName3) {
113836
+ let resolveReady;
113837
+ let rejectReady;
113838
+ let readyPromise;
113839
+ let drainError;
113840
+ let drainPromise;
113841
+ const pendingReady = () => {
113842
+ if (readyPromise === undefined) {
113843
+ readyPromise = new Promise((resolve35, reject) => {
113844
+ resolveReady = resolve35;
113845
+ rejectReady = reject;
113846
+ });
113847
+ }
113848
+ return readyPromise;
113849
+ };
113850
+ return {
113851
+ routeCapability: workflowPendingStageRouteCapability(activeStore, runId),
113852
+ deliverPending(deliver) {
113853
+ if (drainPromise === undefined) {
113854
+ drainError = undefined;
113855
+ const attempt = deliverPendingStageMessages(activeStore, runId, stageId, stageName3, deliver);
113856
+ const drain2 = attempt.then(() => resolveReady?.(), (error) => {
113857
+ drainError = error;
113858
+ rejectReady?.(error);
113859
+ readyPromise = undefined;
113860
+ resolveReady = undefined;
113861
+ rejectReady = undefined;
113862
+ if (drainPromise === drain2)
113863
+ drainPromise = undefined;
113864
+ throw error;
113865
+ });
113866
+ drainPromise = drain2;
113867
+ }
113868
+ return drainPromise;
113869
+ },
113870
+ ready() {
113871
+ if (activeStore.pendingStageMessagesFor(runId, stageId).length === 0 && (stageId === stageName3 || activeStore.pendingStageMessagesFor(runId, stageName3).length === 0)) {
113872
+ return;
113873
+ }
113874
+ return drainError === undefined ? pendingReady() : Promise.reject(drainError);
113875
+ }
113876
+ };
113877
+ }
113878
+ async function deliverPendingStageMessages(activeStore, runId, stageId, stageName3, deliver) {
113879
+ const candidateIds = new Set([
113880
+ ...activeStore.pendingStageMessagesFor(runId, stageId),
113881
+ ...activeStore.pendingStageMessagesFor(runId, stageName3)
113882
+ ].map((entry) => entry.id));
113883
+ const entries = (activeStore.runs().find((run2) => run2.id === runId)?.pendingStageMessages ?? []).map((entry, index) => ({ entry, index })).filter(({ entry }) => candidateIds.has(entry.id)).sort((left, right) => (left.entry.admissionOrder ?? left.index + 1) - (right.entry.admissionOrder ?? right.index + 1) || left.index - right.index).map(({ entry }) => entry);
113884
+ const rootBackend = getDurableBackend();
113885
+ const backend = durableBackendForRun(rootBackend, activeStore.runs(), runId);
113886
+ if (backend === undefined) {
113887
+ throw new Error(`atomic-workflows: workflow run ${runId} has no durable owner for pending-stage delivery`);
113888
+ }
113889
+ for (const entry of entries) {
113890
+ const releaseClaim = claimPendingDelivery(activeStore, runId, entry.stageKey, entry.id);
113891
+ if (releaseClaim === undefined)
113892
+ continue;
113893
+ try {
113894
+ await deliver(toPendingStageSender(entry), entry.message);
113895
+ if (!await activeStore.markPendingStageMessageDelivered(runId, entry.stageKey, entry.id, new Date().toISOString(), backend)) {
113896
+ throw new Error(`atomic-workflows: pending-stage message ${entry.id} changed during delivery`);
113897
+ }
113898
+ } finally {
113899
+ releaseClaim();
113900
+ }
113901
+ }
113902
+ }
113903
+ function claimPendingDelivery(store2, runId, stageKey, messageId) {
113904
+ let claims = pendingDeliveryClaims.get(store2);
113905
+ if (claims === undefined) {
113906
+ claims = new Set;
113907
+ pendingDeliveryClaims.set(store2, claims);
113908
+ }
113909
+ const key2 = JSON.stringify([runId, stageKey, messageId]);
113910
+ if (claims.has(key2))
113911
+ return;
113912
+ claims.add(key2);
113913
+ return () => {
113914
+ claims?.delete(key2);
113915
+ if (claims?.size === 0)
113916
+ pendingDeliveryClaims.delete(store2);
113917
+ };
113918
+ }
113919
+ function toPendingStageSender(entry) {
113920
+ return {
113921
+ ...entry.from,
113922
+ cwd: entry.from.cwd ?? "",
113923
+ model: entry.from.model ?? "unknown",
113924
+ pid: entry.from.pid ?? 0,
113925
+ startedAt: entry.from.startedAt ?? entry.message.timestamp,
113926
+ lastActivity: entry.from.lastActivity ?? entry.message.timestamp
113927
+ };
113928
+ }
113929
+
113146
113930
  // dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts
113147
113931
  init_src();
113148
113932
 
@@ -113534,7 +114318,7 @@ function workflowSessionOptions(meta2) {
113534
114318
  workflow: { runId: meta2.runId, stageId: meta2.stageId, stageName: meta2.stageName }
113535
114319
  };
113536
114320
  }
113537
- function workflowOrchestrationContext(meta2, stageOptions) {
114321
+ function workflowOrchestrationContext(meta2, stageOptions, pendingStageDelivery) {
113538
114322
  const base = {
113539
114323
  kind: "workflow-stage",
113540
114324
  workflowRunId: meta2.runId,
@@ -113543,11 +114327,12 @@ function workflowOrchestrationContext(meta2, stageOptions) {
113543
114327
  constraints: { disableWorkflowTool: true }
113544
114328
  };
113545
114329
  const intercomGroup = stageHasIntercomAccess(stageOptions) ? resolveStageGroup(stageOptions, meta2.workflowIntercomGroup) : undefined;
113546
- return intercomGroup ? { ...base, intercomGroup } : base;
114330
+ const context = intercomGroup ? { ...base, intercomGroup } : base;
114331
+ return pendingStageDelivery === undefined ? context : { ...context, pendingStageDelivery };
113547
114332
  }
113548
- function stripWorkflowOnlyOptions(options, defaultSessionDir, meta2) {
114333
+ function stripWorkflowOnlyOptions(options, defaultSessionDir, meta2, pendingStageDelivery) {
113549
114334
  const classification = workflowSessionOptions(meta2);
113550
- const orchestrationContext = workflowOrchestrationContext(meta2, options);
114335
+ const orchestrationContext = workflowOrchestrationContext(meta2, options, pendingStageDelivery);
113551
114336
  if (!options) {
113552
114337
  return defaultSessionDir === undefined ? { orchestrationContext } : {
113553
114338
  orchestrationContext,
@@ -114913,7 +115698,7 @@ class StageSessionController {
114913
115698
  });
114914
115699
  let created;
114915
115700
  try {
114916
- created = this.opts.adapters.agentSession ? await this.opts.adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptions, this.opts.defaultSessionDir, this.meta), {
115701
+ created = this.opts.adapters.agentSession ? await this.opts.adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptions, this.opts.defaultSessionDir, this.meta, this.opts.pendingStageDelivery), {
114917
115702
  ...this.meta,
114918
115703
  stageOptions,
114919
115704
  ...this.sharedOrchestrationContext !== undefined ? { orchestrationContext: this.sharedOrchestrationContext } : {}
@@ -114930,10 +115715,12 @@ class StageSessionController {
114930
115715
  throw this.staleCreationReason(startGeneration);
114931
115716
  }
114932
115717
  const session = attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) => this.attachSession(result));
114933
- if (session instanceof Promise)
114934
- return await session;
115718
+ const attachedSession = session instanceof Promise ? await session : session;
115719
+ const pendingStageDeliveryReady = this.sharedOrchestrationContext?.pendingStageDelivery?.ready();
115720
+ if (pendingStageDeliveryReady !== undefined)
115721
+ await pendingStageDeliveryReady;
114935
115722
  await this.opts.onSessionReady?.();
114936
- return session;
115723
+ return attachedSession;
114937
115724
  }
114938
115725
  attachSession(created) {
114939
115726
  const result = normalizeSessionCreateResult(created);
@@ -115685,6 +116472,12 @@ function createWorkflowStageFactory(input2) {
115685
116472
  const replaySource = replayDecision.kind === "replay" ? replayDecision.source : undefined;
115686
116473
  const executeReplaySource = replayDecision.kind === "execute" ? replayDecision.source : undefined;
115687
116474
  const shouldReplay = replaySource !== undefined;
116475
+ const stageOptionsForContext = executeReplaySource?.sessionFile === undefined ? options : {
116476
+ ...options ?? {},
116477
+ context: options?.context ?? "fork",
116478
+ forkFromSessionFile: options?.forkFromSessionFile ?? executeReplaySource.sessionFile
116479
+ };
116480
+ const pendingStageDeliveryAvailable = stageCanUseWorkflowPendingStageRoute(stageOptionsForContext, input2.workflowIntercomGroup);
115688
116481
  const stageSnapshot = {
115689
116482
  id: stageId,
115690
116483
  name,
@@ -115692,6 +116485,7 @@ function createWorkflowStageFactory(input2) {
115692
116485
  status: shouldReplay ? "completed" : "pending",
115693
116486
  parentIds: Object.freeze(parentIds),
115694
116487
  toolEvents: [],
116488
+ pendingStageDeliveryAvailable,
115695
116489
  ...shouldReplay ? {
115696
116490
  startedAt: Date.now(),
115697
116491
  endedAt: Date.now(),
@@ -115720,11 +116514,6 @@ function createWorkflowStageFactory(input2) {
115720
116514
  throwIfWorkflowExitSelected: input2.exit.throwIfWorkflowExitSelected
115721
116515
  });
115722
116516
  }
115723
- const stageOptionsForContext = executeReplaySource?.sessionFile === undefined ? options : {
115724
- ...options ?? {},
115725
- context: options?.context ?? "fork",
115726
- forkFromSessionFile: options?.forkFromSessionFile ?? executeReplaySource.sessionFile
115727
- };
115728
116517
  const applyModelFallbackMeta = (meta2) => {
115729
116518
  if (meta2.model !== undefined)
115730
116519
  stageSnapshot.model = meta2.model;
@@ -115762,6 +116551,9 @@ function createWorkflowStageFactory(input2) {
115762
116551
  workflowIntercomGroup: input2.workflowIntercomGroup,
115763
116552
  signal: input2.signal,
115764
116553
  stageOptions: stageOptionsForContext,
116554
+ ...pendingStageDeliveryAvailable ? {
116555
+ pendingStageDelivery: createWorkflowPendingStageDelivery(input2.activeStore, input2.runId, stageId, name)
116556
+ } : {},
115765
116557
  models: input2.opts.models,
115766
116558
  executionMode: input2.opts.executionMode,
115767
116559
  defaultSessionDir: input2.opts.defaultSessionDir,
@@ -115887,6 +116679,7 @@ function createWorkflowStageFactory(input2) {
115887
116679
  name,
115888
116680
  parentIds: stageSnapshot.parentIds,
115889
116681
  ...stageReplayFields(stageSnapshot),
116682
+ pendingStageDeliveryAvailable,
115890
116683
  ts: stageSnapshot.startedAt ?? Date.now()
115891
116684
  });
115892
116685
  };
@@ -116249,6 +117042,9 @@ async function run(def, inputs, opts = {}) {
116249
117042
  status: "running",
116250
117043
  stages: [],
116251
117044
  toolNodes: [],
117045
+ pendingStageMessages: [
117046
+ ...pendingStageMessagesForDurableRun(rootBackend, runId, opts.parentRun?.rootRunId ?? runId)
117047
+ ],
116252
117048
  startedAt: Date.now(),
116253
117049
  ...opts.parentRun !== undefined ? {
116254
117050
  parentRunId: opts.parentRun.runId,
@@ -116822,8 +117618,8 @@ var runWorkflow = () => {
116822
117618
  };
116823
117619
 
116824
117620
  // dist/builtin/workflows/src/extension/workflow-module-loader.ts
116825
- var WORKFLOWS_MODULE_SPECIFIER = "@bastani/workflows";
116826
- var WORKFLOWS_BUILTIN_MODULE_SPECIFIER = `${WORKFLOWS_MODULE_SPECIFIER}/builtin`;
117621
+ var WORKFLOWS_MODULE_SPECIFIER = "@bastani/atomic/workflows";
117622
+ var LEGACY_WORKFLOWS_MODULE_SPECIFIER = "@bastani/workflows";
116827
117623
  var TYPEBOX_MODULE_SPECIFIER = "typebox";
116828
117624
  var WORKFLOWS_SDK_MODULE = {
116829
117625
  ...exports_sdk_surface
@@ -116842,19 +117638,27 @@ var WORKFLOWS_BUILTIN_MODULE = {
116842
117638
  var TYPEBOX_MODULE = {
116843
117639
  ...typeboxModule
116844
117640
  };
117641
+ function createWorkflowVirtualModules(moduleSpecifier) {
117642
+ const builtinModuleSpecifier = `${moduleSpecifier}/builtin`;
117643
+ return {
117644
+ [moduleSpecifier]: WORKFLOWS_SDK_MODULE,
117645
+ [builtinModuleSpecifier]: WORKFLOWS_BUILTIN_MODULE,
117646
+ [`${builtinModuleSpecifier}/adversarial-verification`]: { default: adversarial_verification_default },
117647
+ [`${builtinModuleSpecifier}/classify-and-act`]: { default: classify_and_act_default },
117648
+ [`${builtinModuleSpecifier}/fan-out-and-synthesize`]: { default: fan_out_and_synthesize_default },
117649
+ [`${builtinModuleSpecifier}/generate-and-filter`]: { default: generate_and_filter_default },
117650
+ [`${builtinModuleSpecifier}/goal`]: { default: goal_default },
117651
+ [`${builtinModuleSpecifier}/loop-until-done`]: { default: loop_until_done_default },
117652
+ [`${builtinModuleSpecifier}/open-claude-design`]: { default: open_claude_design_default },
117653
+ [`${builtinModuleSpecifier}/ralph`]: { default: ralph_default },
117654
+ [`${builtinModuleSpecifier}/tournament`]: { default: tournament_default },
117655
+ [`${builtinModuleSpecifier}/steering-context`]: exports_steering_context
117656
+ };
117657
+ }
116845
117658
  var WORKFLOWS_VIRTUAL_MODULES = {
116846
- [WORKFLOWS_MODULE_SPECIFIER]: WORKFLOWS_SDK_MODULE,
116847
- [WORKFLOWS_BUILTIN_MODULE_SPECIFIER]: WORKFLOWS_BUILTIN_MODULE,
116848
- [TYPEBOX_MODULE_SPECIFIER]: TYPEBOX_MODULE,
116849
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/adversarial-verification`]: { default: adversarial_verification_default },
116850
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/classify-and-act`]: { default: classify_and_act_default },
116851
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/fan-out-and-synthesize`]: { default: fan_out_and_synthesize_default },
116852
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/generate-and-filter`]: { default: generate_and_filter_default },
116853
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/goal`]: { default: goal_default },
116854
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/loop-until-done`]: { default: loop_until_done_default },
116855
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/open-claude-design`]: { default: open_claude_design_default },
116856
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/ralph`]: { default: ralph_default },
116857
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/tournament`]: { default: tournament_default }
117659
+ ...createWorkflowVirtualModules(WORKFLOWS_MODULE_SPECIFIER),
117660
+ ...createWorkflowVirtualModules(LEGACY_WORKFLOWS_MODULE_SPECIFIER),
117661
+ [TYPEBOX_MODULE_SPECIFIER]: TYPEBOX_MODULE
116858
117662
  };
116859
117663
  var workflowModuleLoader = createJiti2(import.meta.url, {
116860
117664
  moduleCache: false,
@@ -117499,27 +118303,6 @@ function ensurePostMortemStageHandle(runId, stage, deps) {
117499
118303
  const handle = deps.registry.getOrCreateDetached(runId, stage.id, () => createPostMortemStageHandle(runId, stage, sessionFile, adapters, deps.cwd, deps.defaultSessionDir));
117500
118304
  return { ok: true, handle };
117501
118305
  }
117502
- function acquirePostMortemStageHandle(runId, stage, deps) {
117503
- if (!TERMINAL_POSTMORTEM_STATUSES.has(stage.status))
117504
- return { ok: false, reason: "not_terminal" };
117505
- const existing = deps.registry.peek(runId, stage.id);
117506
- if (existing !== undefined && existing.isDisposed !== true) {
117507
- return {
117508
- ok: true,
117509
- lease: deps.registry.acquireDetached(runId, stage.id, () => existing)
117510
- };
117511
- }
117512
- if (deps.adapters?.agentSession === undefined)
117513
- return { ok: false, reason: "no_adapter" };
117514
- const sessionFile = stage.sessionFile;
117515
- if (typeof sessionFile !== "string" || sessionFile.length === 0)
117516
- return { ok: false, reason: "no_session" };
117517
- if (!isReopenableSessionTranscript(sessionFile))
117518
- return { ok: false, reason: "invalid_session" };
117519
- const adapters = deps.adapters;
117520
- const lease = deps.registry.acquireDetached(runId, stage.id, () => createPostMortemStageHandle(runId, stage, sessionFile, adapters, deps.cwd, deps.defaultSessionDir));
117521
- return { ok: true, lease };
117522
- }
117523
118306
  function createPostMortemStageHandle(runId, stage, sessionFile, adapters, cwd, defaultSessionDir) {
117524
118307
  const context = createStageContext2({
117525
118308
  runId,
@@ -118249,7 +119032,7 @@ function formatWorkflowHeartbeatNoticeText(details) {
118249
119032
  const workflowName = escapeQuotedText3(details.workflowName);
118250
119033
  const elapsed = formatWorkflowHeartbeatElapsed(details);
118251
119034
  const cadence = formatWorkflowHeartbeatCadence(details.intervalMinutes);
118252
- return `${WORKFLOW_HEARTBEAT_GLYPH} Workflow "${workflowName}" heartbeat (run ${details.runId}) — still running` + `${elapsed === undefined ? "" : ` after ${elapsed}`}, on a ${cadence} cadence. ` + "This is a periodic alignment check, not a failure. Review whether the run is still on goal, then " + "continue, steer it, stop and replace it, or ask the user. " + `Inspect: /workflow status ${details.runId}`;
119035
+ return `${WORKFLOW_HEARTBEAT_GLYPH} Workflow "${workflowName}" heartbeat (run ${details.runId}) — still running` + `${elapsed === undefined ? "" : ` after ${elapsed}`}, on a ${cadence} cadence. ` + "This is a periodic alignment check. Review whether the run is still on goal. " + "When steering or communication is useful, use Intercom. Consider the expanded workflow topology and match " + "each update's reach to its impact: send a local update to its affected stage; when shared scope or acceptance " + "criteria change, send the same authoritative Intercom update to every relevant live and known unstarted " + "`<runId>:<stageKey>`, including each worker-to-reviewer loop. Intercom delivers immediately to live stages and " + "queues updates for known stages that have not started, delivering them before their first model turn, so workers " + "and reviewers begin with one consistent contract. " + "Use `ask` once the target has a live session that can reply. " + "Use workflow pause, resume, interrupt, or quit for run control. " + "Continue the progressing run when no intervention is needed, or ask the user when a decision is needed. " + `Inspect: /workflow status ${details.runId}`;
118253
119036
  }
118254
119037
  function registerWorkflowHeartbeatRenderer(options) {
118255
119038
  const register = options.registerMessageRenderer;
@@ -119481,6 +120264,241 @@ function createWorkflowExtensionRuntimeState(pi, adapters, resolveCwd = () => pi
119481
120264
  };
119482
120265
  }
119483
120266
 
120267
+ // dist/builtin/workflows/src/extension/pending-stage-intercom.ts
120268
+ var PENDING_STAGE_ROUTE_EVENT = "atomic:workflow-pending-stage-route";
120269
+ var PENDING_STAGE_MESSAGE_EVENT = "atomic:workflow-pending-stage-message";
120270
+ var PENDING_STAGE_UNDELIVERABLE_EVENT = "atomic:workflow-pending-stage-undeliverable";
120271
+ var PENDING_STAGE_ASK_REFUSAL = "Cannot ask a workflow stage whose session has not initialized. Use send; Atomic will queue the message until the stage session initializes.";
120272
+ function registerPendingStageIntercomBridge(pi, activeStore) {
120273
+ let disposed = false;
120274
+ let sweepPromise = Promise.resolve();
120275
+ const notifyUndeliverable = async (entry, reason, notificationId) => {
120276
+ const payload = {
120277
+ handled: false,
120278
+ runId: entry.runId,
120279
+ senderId: entry.from.id,
120280
+ ...entry.senderRegistrationName === undefined ? {} : { senderRegistrationName: entry.senderRegistrationName },
120281
+ ...entry.senderReturnAddress === undefined ? {} : { senderReturnAddress: entry.senderReturnAddress },
120282
+ messageId: entry.message.id,
120283
+ notificationId,
120284
+ reason
120285
+ };
120286
+ pi.events?.emit?.(PENDING_STAGE_UNDELIVERABLE_EVENT, payload);
120287
+ return payload.handled && await payload.completion === true;
120288
+ };
120289
+ const announceRoutes = () => {
120290
+ if (disposed)
120291
+ return;
120292
+ const runs = activeStore.runs();
120293
+ for (const run2 of runs) {
120294
+ const rootRunId = durableRootRunIdForRun(runs, run2.id);
120295
+ if (rootRunId === undefined)
120296
+ continue;
120297
+ pi.events?.emit?.(PENDING_STAGE_ROUTE_EVENT, {
120298
+ runId: run2.id,
120299
+ group: workflowInvocationIntercomGroup(rootRunId),
120300
+ capability: workflowPendingStageRouteCapability(activeStore, run2.id)
120301
+ });
120302
+ }
120303
+ sweepPromise = sweepPromise.then(() => settleUndeliverablePendingStageMessages(activeStore, notifyUndeliverable)).then(() => {
120304
+ return;
120305
+ }).catch((error) => console.warn("atomic-workflows: pending stage delivery sweep failed", error));
120306
+ };
120307
+ const unsubscribeStore = activeStore.subscribeInvalidation(announceRoutes);
120308
+ announceRoutes();
120309
+ const subscription = pi.events?.on?.(PENDING_STAGE_MESSAGE_EVENT, (payload) => {
120310
+ if (disposed || !isPendingStageMessageEvent(payload) || payload.handled)
120311
+ return;
120312
+ const runs = activeStore.runs();
120313
+ const run2 = runs.find((candidate) => candidate.id === payload.runId);
120314
+ if (run2 === undefined)
120315
+ return;
120316
+ const rootRunId = durableRootRunIdForRun(runs, run2.id);
120317
+ if (rootRunId === undefined)
120318
+ return;
120319
+ if (payload.live === true) {
120320
+ if (knownLiveStage(run2, payload.stageKey) === undefined)
120321
+ return;
120322
+ payload.handled = true;
120323
+ payload.completion = validateLiveDelivery(activeStore, payload);
120324
+ return;
120325
+ }
120326
+ const stage = knownUninitializedStage(run2, payload.stageKey);
120327
+ if (stage === undefined)
120328
+ return;
120329
+ payload.handled = true;
120330
+ payload.completion = queueAndPersist(activeStore, payload, workflowInvocationIntercomGroup(rootRunId), stage.pendingStageDeliveryAvailable === true);
120331
+ });
120332
+ const dispose2 = () => {
120333
+ disposed = true;
120334
+ unsubscribeStore();
120335
+ if (typeof subscription === "function")
120336
+ subscription();
120337
+ };
120338
+ pi.on?.("session_shutdown", dispose2);
120339
+ return dispose2;
120340
+ }
120341
+ function isPendingStageMessageEvent(value) {
120342
+ if (typeof value !== "object" || value === null)
120343
+ return false;
120344
+ const event = value;
120345
+ return typeof event.handled === "boolean" && (event.live === undefined || typeof event.live === "boolean") && typeof event.runId === "string" && typeof event.stageKey === "string" && typeof event.from?.id === "string" && (event.from.name === undefined || typeof event.from.name === "string") && (event.senderRegistrationName === undefined || typeof event.senderRegistrationName === "string") && (event.senderReturnAddress === undefined || typeof event.senderReturnAddress === "string") && typeof event.message?.id === "string" && typeof event.message.timestamp === "number" && typeof event.message.content?.text === "string";
120346
+ }
120347
+ function knownLiveStage(run2, stageKey) {
120348
+ const exactIds = run2.stages.filter((stage) => stage.id === stageKey);
120349
+ const candidates = exactIds.length > 0 ? exactIds : run2.stages.filter((stage) => stage.name === stageKey);
120350
+ return candidates.length === 1 ? candidates[0] : undefined;
120351
+ }
120352
+ async function validateLiveDelivery(activeStore, event) {
120353
+ const result = await activeStore.validateLiveStageMessage({
120354
+ runId: event.runId,
120355
+ stageKey: event.stageKey,
120356
+ from: event.from,
120357
+ message: event.message,
120358
+ queuedAt: new Date().toISOString()
120359
+ });
120360
+ if (result === undefined)
120361
+ return { outcome: "refused", reason: "Session not found" };
120362
+ if (result.outcome === "forward" || result.outcome === "delivered" || result.outcome === "queued")
120363
+ return result;
120364
+ if (result.outcome === "undeliverable") {
120365
+ return { outcome: "refused", reason: result.reason ?? "Pending-stage delivery was refused" };
120366
+ }
120367
+ return {
120368
+ outcome: "refused",
120369
+ reason: `Intercom message ID '${result.messageId}' conflicts with the durable identity for ${event.runId}:${event.stageKey}`,
120370
+ reasonCode: "message_id_conflict"
120371
+ };
120372
+ }
120373
+ async function queueAndPersist(activeStore, event, runGroup, pendingStageDeliveryAvailable) {
120374
+ if (event.message.expectsReply === true) {
120375
+ return { outcome: "refused", reason: PENDING_STAGE_ASK_REFUSAL };
120376
+ }
120377
+ if (!pendingStageDeliveryAvailable) {
120378
+ return {
120379
+ outcome: "refused",
120380
+ reason: `Workflow stage ${event.runId}:${event.stageKey} cannot receive Intercom messages before startup`
120381
+ };
120382
+ }
120383
+ const request = {
120384
+ runId: event.runId,
120385
+ stageKey: event.stageKey,
120386
+ from: event.from,
120387
+ ...event.senderRegistrationName === undefined ? {} : { senderRegistrationName: event.senderRegistrationName },
120388
+ ...event.senderReturnAddress === undefined ? {} : { senderReturnAddress: event.senderReturnAddress },
120389
+ message: event.message,
120390
+ queuedAt: new Date().toISOString()
120391
+ };
120392
+ const rootBackend = getDurableBackend();
120393
+ const backend = durableBackendForRun(rootBackend, activeStore.runs(), event.runId);
120394
+ if (backend === undefined)
120395
+ return { outcome: "refused", reason: "Session not found" };
120396
+ const result = await activeStore.queueStageMessage(request, event.from.group, runGroup, backend);
120397
+ if (result === undefined)
120398
+ return { outcome: "refused", reason: "Session not found" };
120399
+ if (!result.ok) {
120400
+ if (result.reason === "capacity") {
120401
+ return {
120402
+ outcome: "refused",
120403
+ reason: `Pending stage message queue is full (limit ${result.limit}) for ${result.runId}:${result.stageKey}`
120404
+ };
120405
+ }
120406
+ if (result.reason === "message_id_conflict") {
120407
+ return {
120408
+ outcome: "refused",
120409
+ reason: `Intercom message ID '${result.messageId}' was already queued for ${result.runId}:${result.stageKey} with a different target, sender, or payload`
120410
+ };
120411
+ }
120412
+ return { outcome: "refused", reason: "Target workflow run is in a different intercom group" };
120413
+ }
120414
+ if (result.entry.status === "delivered")
120415
+ return { outcome: "delivered" };
120416
+ if (result.entry.status === "undeliverable") {
120417
+ return {
120418
+ outcome: "refused",
120419
+ reason: result.entry.undeliverableReason ?? "Pending-stage delivery was refused"
120420
+ };
120421
+ }
120422
+ if (result.position === undefined)
120423
+ return { outcome: "refused", reason: "Pending-stage delivery was refused" };
120424
+ return { outcome: "queued", position: result.position };
120425
+ }
120426
+ function knownUninitializedStage(run2, stageKey) {
120427
+ const exactIds = run2.stages.filter((stage2) => stage2.id === stageKey);
120428
+ const candidates = exactIds.length > 0 ? exactIds : run2.stages.filter((stage2) => stage2.name === stageKey);
120429
+ if (candidates.length !== 1)
120430
+ return;
120431
+ const stage = candidates[0];
120432
+ return (stage.status === "pending" || stage.status === "running") && stage.sessionId === undefined && stage.sessionFile === undefined ? stage : undefined;
120433
+ }
120434
+ function pendingStageDestination(run2, entry) {
120435
+ if (entry.stageId !== undefined) {
120436
+ const candidates2 = run2.stages.filter((stage) => stage.id === entry.stageId);
120437
+ return candidates2.length === 1 ? candidates2[0] : undefined;
120438
+ }
120439
+ if (entry.stageReplayKey !== undefined) {
120440
+ const candidates2 = run2.stages.filter((stage) => stage.replayKey === entry.stageReplayKey);
120441
+ return candidates2.length === 1 ? candidates2[0] : undefined;
120442
+ }
120443
+ const exactIds = run2.stages.filter((stage) => stage.id === entry.stageKey);
120444
+ const candidates = exactIds.length > 0 ? exactIds : run2.stages.filter((stage) => stage.name === entry.stageKey);
120445
+ return candidates.length === 1 ? candidates[0] : undefined;
120446
+ }
120447
+ function pendingStageUndeliverableReason(run2, entry) {
120448
+ const stage = pendingStageDestination(run2, entry);
120449
+ if (run2.status === "cancelled") {
120450
+ return `Workflow run ${run2.id} terminated with status cancelled before stage ${entry.stageKey} started`;
120451
+ }
120452
+ if (stage?.status === "skipped") {
120453
+ return `Workflow stage ${entry.stageKey} was skipped${stage.skippedReason ? ` (${stage.skippedReason})` : ""}`;
120454
+ }
120455
+ if (isTerminalRunStatus(run2.status)) {
120456
+ return `Workflow run ${run2.id} terminated with status ${run2.status} before stage ${entry.stageKey} started`;
120457
+ }
120458
+ return;
120459
+ }
120460
+ function needsUndeliverableSettlement(run2, entry) {
120461
+ if (entry.status === "queued")
120462
+ return pendingStageUndeliverableReason(run2, entry) !== undefined;
120463
+ return entry.status === "undeliverable" && entry.undeliverableNotificationId !== undefined && entry.undeliverableNotifiedAt === undefined && entry.undeliverableReason !== undefined;
120464
+ }
120465
+ async function settleUndeliverablePendingStageMessages(activeStore, notify2) {
120466
+ const runs = activeStore.runs();
120467
+ if (!runs.some((run2) => run2.pendingStageMessages?.some((entry) => needsUndeliverableSettlement(run2, entry)))) {
120468
+ return 0;
120469
+ }
120470
+ let settled = 0;
120471
+ const rootBackend = getDurableBackend();
120472
+ for (const run2 of runs) {
120473
+ const backend = durableBackendForRun(rootBackend, runs, run2.id);
120474
+ if (backend === undefined) {
120475
+ throw new Error(`atomic-workflows: workflow run ${run2.id} has no durable owner for pending-stage settlement`);
120476
+ }
120477
+ for (const snapshotEntry of run2.pendingStageMessages ?? []) {
120478
+ let entry = snapshotEntry;
120479
+ if (entry.status === "queued") {
120480
+ const reason = pendingStageUndeliverableReason(run2, entry);
120481
+ if (reason === undefined)
120482
+ continue;
120483
+ if (await activeStore.markPendingStageMessageUndeliverable(run2.id, entry.stageKey, entry.id, reason, backend)) {
120484
+ settled += 1;
120485
+ }
120486
+ const currentRun2 = activeStore.runs().find((candidate) => candidate.id === run2.id);
120487
+ const currentEntry = currentRun2?.pendingStageMessages?.find((candidate) => candidate.stageKey === entry.stageKey && candidate.id === entry.id);
120488
+ if (currentEntry === undefined)
120489
+ continue;
120490
+ entry = currentEntry;
120491
+ }
120492
+ if (entry.status !== "undeliverable" || entry.undeliverableNotificationId === undefined || entry.undeliverableNotifiedAt !== undefined || entry.undeliverableReason === undefined)
120493
+ continue;
120494
+ if (!await notify2(entry, entry.undeliverableReason, entry.undeliverableNotificationId))
120495
+ continue;
120496
+ await activeStore.markPendingStageMessageUndeliverableNotified(run2.id, entry.stageKey, entry.id, entry.undeliverableNotificationId, new Date().toISOString(), backend);
120497
+ }
120498
+ }
120499
+ return settled;
120500
+ }
120501
+
119484
120502
  // dist/builtin/workflows/src/extension/postmortem-deps.ts
119485
120503
  function resolveStageCwd(runId) {
119486
120504
  try {
@@ -119814,7 +120832,9 @@ function withWorkflowStageSessionOptions(options, meta2, pi) {
119814
120832
  return {
119815
120833
  ...options,
119816
120834
  excludedTools,
119817
- ...meta2 ? { orchestrationContext: meta2.orchestrationContext ?? makeWorkflowStageOrchestrationContext(meta2, pi) } : {}
120835
+ ...meta2 ? {
120836
+ orchestrationContext: meta2.orchestrationContext ?? options.orchestrationContext ?? makeWorkflowStageOrchestrationContext(meta2, pi)
120837
+ } : {}
119818
120838
  };
119819
120839
  }
119820
120840
  function shouldBindStageUiContext(pi, meta2) {
@@ -119919,10 +120939,8 @@ function buildRuntimeAdapters(pi, options = {}) {
119919
120939
  const sessionOptions = withWorkflowStageSessionOptions(stripWorkflowOnlyOptions2(stageOptions) ?? {}, meta2, pi);
119920
120940
  const result = await createSession(sessionOptions);
119921
120941
  const bindable = result.session;
119922
- if (shouldBindStageUiContext(pi, meta2) && typeof bindable.bindExtensions === "function") {
119923
- await bindable.bindExtensions({
119924
- uiContext: makeStageExtensionUiContext(pi.ui ?? {}, meta2, broker)
119925
- });
120942
+ if (typeof bindable.bindExtensions === "function") {
120943
+ await bindable.bindExtensions(shouldBindStageUiContext(pi, meta2) ? { uiContext: makeStageExtensionUiContext(pi.ui ?? {}, meta2, broker) } : {});
119926
120944
  }
119927
120945
  return result;
119928
120946
  }
@@ -122016,6 +123034,139 @@ function buildWorkflowStatusListing(snapshots2, filter = "all", now = Date.now()
122016
123034
  };
122017
123035
  }
122018
123036
 
123037
+ // dist/builtin/workflows/src/extension/workflow-tool-answer.ts
123038
+ function answerResult(runId, stageId, status, message) {
123039
+ return { action: "answer", runId, stageId, status, message };
123040
+ }
123041
+ function terminalAnswerResult(runId, stageId, workflowStatus) {
123042
+ const message = `Cannot answer a prompt because workflow ${runId} has terminated with status ${workflowStatus}. If work remains, start a new workflow to address it.`;
123043
+ return {
123044
+ action: "answer",
123045
+ runId,
123046
+ stageId,
123047
+ status: "failed",
123048
+ code: "WORKFLOW_TERMINAL",
123049
+ workflowStatus,
123050
+ error: message,
123051
+ message
123052
+ };
123053
+ }
123054
+ function hasPayloadProperty(args) {
123055
+ return args.text !== undefined || args.response !== undefined || args.message !== undefined;
123056
+ }
123057
+ function promptPayloadFromArgs(args) {
123058
+ if (args.response !== undefined)
123059
+ return args.response;
123060
+ if (args.text !== undefined)
123061
+ return args.text;
123062
+ return args.message;
123063
+ }
123064
+ function textPayloadFromArgs(args) {
123065
+ if (args.text !== undefined)
123066
+ return args.text;
123067
+ if (typeof args.response === "string")
123068
+ return args.response;
123069
+ return args.message;
123070
+ }
123071
+ function brokerAnswerFromArgs(args) {
123072
+ if (args.response !== undefined) {
123073
+ const coerced = coerceStageInputAnswer(args.response);
123074
+ if (hasStageInputAnswerContent(coerced))
123075
+ return coerced;
123076
+ }
123077
+ const text = textPayloadFromArgs(args);
123078
+ return text !== undefined ? { text } : {};
123079
+ }
123080
+ function answerablePromptIds(stage) {
123081
+ const target = stage.workflowGraphTarget;
123082
+ const ids = [];
123083
+ const brokered = stageUiBroker.peekStagePrompt(target.runId, target.stageId);
123084
+ if (brokered !== undefined)
123085
+ ids.push(brokered.id);
123086
+ if (stage.pendingPrompt !== undefined)
123087
+ ids.push(stage.pendingPrompt.id);
123088
+ if (stage.status === "awaiting_input" && stage.promptFootprint?.kind === "custom")
123089
+ ids.push(stage.promptFootprint.id);
123090
+ return ids;
123091
+ }
123092
+ function inferPromptStageTarget(runId, promptId) {
123093
+ const pending = expandWorkflowGraph(readGraphStoreSnapshot(store), runId).stages.filter((stage) => {
123094
+ const ids = answerablePromptIds(stage);
123095
+ return promptId === undefined ? ids.length > 0 : ids.includes(promptId);
123096
+ });
123097
+ const only = pending[0];
123098
+ if (pending.length === 1 && only !== undefined) {
123099
+ return { ok: true, runId: only.workflowGraphTarget.runId, stageId: only.workflowGraphTarget.stageId };
123100
+ }
123101
+ if (pending.length === 0) {
123102
+ return promptId === undefined ? { ok: true, runId } : { ok: false, message: `No pending prompt ${promptId} in run ${runId}.` };
123103
+ }
123104
+ return {
123105
+ ok: false,
123106
+ message: `${pending.length} prompts pending; pass stageId: ${pending.map(expandedStageLabel).join(", ")}`
123107
+ };
123108
+ }
123109
+ async function workflowAnswerAction(args) {
123110
+ const target = resolveToolRunTarget(args, "No active run with a pending prompt.");
123111
+ if (target.kind === "all")
123112
+ return answerResult("--all", "", "noop", "Answer requires a single run.");
123113
+ if (target.kind === "malformed" || target.kind === "not_found") {
123114
+ return answerResult(target.target, "", "noop", target.message);
123115
+ }
123116
+ const runs = store.runs();
123117
+ const runById = new Map(runs.map((run2) => [run2.id, run2]));
123118
+ const rootRunId = reciprocalWorkflowRootRunId(runById, target.runId) ?? target.runId;
123119
+ const rootRun = runById.get(rootRunId);
123120
+ if (rootRun !== undefined && isTerminalRunStatus(rootRun.status)) {
123121
+ return terminalAnswerResult(rootRun.id, args.stageId?.trim() ?? "", rootRun.status);
123122
+ }
123123
+ const requested = resolveToolStageTarget(target.runId, args.stageId);
123124
+ const stage = requested.ok && requested.stageId === undefined ? inferPromptStageTarget(target.runId, args.promptId) : requested;
123125
+ if (!stage.ok || stage.stageId === undefined) {
123126
+ return answerResult(target.runId, "", "noop", stage.ok ? "Stage id or name is required." : stage.message);
123127
+ }
123128
+ const stageRunId = stage.runId ?? target.runId;
123129
+ const snapshot2 = store.runs().find((run2) => run2.id === stageRunId)?.stages.find((item) => item.id === stage.stageId);
123130
+ const brokerPrompt = stageUiBroker.peekStagePrompt(stageRunId, stage.stageId);
123131
+ if (brokerPrompt !== undefined && (args.promptId === undefined || args.promptId === brokerPrompt.id)) {
123132
+ if (!hasPayloadProperty(args))
123133
+ return answerResult(stageRunId, stage.stageId, "noop", "Answer requires text, response, or message.");
123134
+ const ok2 = stageUiBroker.answerStagePrompt(stageRunId, stage.stageId, brokerAnswerFromArgs(args), {
123135
+ answerSource: "workflow_tool"
123136
+ });
123137
+ return answerResult(stageRunId, stage.stageId, ok2 ? "ok" : "noop", ok2 ? `Answered input request ${brokerPrompt.id}.` : `No matching pending input request ${brokerPrompt.id}.`);
123138
+ }
123139
+ const customPrompt = snapshot2?.status === "awaiting_input" && snapshot2.promptFootprint?.kind === "custom" ? snapshot2.promptFootprint : undefined;
123140
+ if (customPrompt !== undefined && (args.promptId === undefined || args.promptId === customPrompt.id)) {
123141
+ return answerResult(stageRunId, stage.stageId, "noop", `Custom UI prompt ${customPrompt.id} requires the interactive workflow graph; arbitrary ctx.ui.custom<T> results cannot be answered through workflow answer.`);
123142
+ }
123143
+ const promptId = args.promptId ?? snapshot2?.pendingPrompt?.id;
123144
+ if (promptId === undefined) {
123145
+ return answerResult(stageRunId, stage.stageId, "noop", "No pending prompt to answer.");
123146
+ }
123147
+ if (!hasPayloadProperty(args))
123148
+ return answerResult(stageRunId, stage.stageId, "noop", "Answer requires text, response, or message.");
123149
+ if (stageUiBroker.wasStagePromptResolved(stageRunId, stage.stageId, promptId)) {
123150
+ return answerResult(stageRunId, stage.stageId, "ok", `Input request ${promptId} was already answered.`);
123151
+ }
123152
+ const rawPayload = promptPayloadFromArgs(args);
123153
+ const pendingPrompt = snapshot2?.pendingPrompt;
123154
+ const primitivePrompt = pendingPrompt?.id === promptId && isPrimitivePrompt(pendingPrompt) ? pendingPrompt : undefined;
123155
+ if (primitivePrompt !== undefined) {
123156
+ const coerced = coercePrimitivePromptAnswer(primitivePrompt, rawPayload);
123157
+ if (!coerced.ok)
123158
+ return answerResult(stageRunId, stage.stageId, "noop", primitivePromptAnswerRejection(promptId, primitivePrompt));
123159
+ const ok2 = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, coerced.value, {
123160
+ answerSource: "workflow_tool"
123161
+ });
123162
+ return answerResult(stageRunId, stage.stageId, ok2 ? "ok" : "noop", ok2 ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123163
+ }
123164
+ const ok = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, rawPayload, {
123165
+ answerSource: "workflow_tool"
123166
+ });
123167
+ return answerResult(stageRunId, stage.stageId, ok ? "ok" : "noop", ok ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123168
+ }
123169
+
122019
123170
  // dist/builtin/workflows/src/extension/workflow-tool-content.ts
122020
123171
  function stringifyWorkflowToolResult(result) {
122021
123172
  return JSON.stringify(result, null, 2);
@@ -122024,7 +123175,7 @@ function compactWorkflowToolMessage(result) {
122024
123175
  if (result.action === "reload") {
122025
123176
  return `${result.action}: ${result.status} — ${result.message}`;
122026
123177
  }
122027
- const target = [result.runId, result.action === "send" ? result.stageId : undefined].filter((part) => part !== undefined && part.length > 0).join("/");
123178
+ const target = [result.runId, result.action === "answer" ? result.stageId : undefined].filter((part) => part !== undefined && part.length > 0).join("/");
122028
123179
  return `${result.action}:${target ? ` ${target}` : ""} ${result.status} — ${result.message}`;
122029
123180
  }
122030
123181
  var STATUS_MESSAGE_CHAR_LIMIT = 120;
@@ -122100,7 +123251,7 @@ function renderStatusToolContent(result) {
122100
123251
  for (const entry of run2.awaitingInput)
122101
123252
  lines.push(statusAwaitingInputLine(entry));
122102
123253
  });
122103
- lines.push("hint: status with runId returns full run detail; pause/resume/interrupt/quit/send accept the same runId (send also takes stageId/promptId).");
123254
+ lines.push("hint: status with runId returns full run detail; workflow answer answers pending prompts using runId/stageId/promptId; workflow resume controls paused runs; pause/interrupt/quit also accept runId. Ordinary Intercom handles free-form workflow-stage communication at <runId>:<stageKey>: live delivery is immediate, known unstarted stages queue before their first model turn. Use Intercom ask once a reply-capable live session exists.");
122104
123255
  return lines.join(`
122105
123256
  `);
122106
123257
  }
@@ -122228,7 +123379,7 @@ function renderWorkflowToolContent(result, args) {
122228
123379
  return renderStagesToolContent(result);
122229
123380
  case "stage":
122230
123381
  return renderStageToolContent(result);
122231
- case "send":
123382
+ case "answer":
122232
123383
  case "pause":
122233
123384
  case "reload":
122234
123385
  case "interrupt":
@@ -123003,284 +124154,6 @@ function workflowTranscriptResult(args, source) {
123003
124154
  });
123004
124155
  }
123005
124156
 
123006
- // dist/builtin/workflows/src/extension/workflow-tool-send.ts
123007
- function hasPayloadProperty(args) {
123008
- return args.text !== undefined || args.response !== undefined || args.message !== undefined;
123009
- }
123010
- function promptPayloadFromArgs(args) {
123011
- if (args.response !== undefined)
123012
- return args.response;
123013
- if (args.text !== undefined)
123014
- return args.text;
123015
- return args.message;
123016
- }
123017
- function textPayloadFromArgs(args) {
123018
- if (args.text !== undefined)
123019
- return args.text;
123020
- if (typeof args.response === "string")
123021
- return args.response;
123022
- if (args.message !== undefined)
123023
- return args.message;
123024
- return;
123025
- }
123026
- function brokerAnswerFromArgs(args) {
123027
- if (args.response !== undefined) {
123028
- const coerced = coerceStageInputAnswer(args.response);
123029
- if (hasStageInputAnswerContent(coerced))
123030
- return coerced;
123031
- }
123032
- const text = textPayloadFromArgs(args);
123033
- return text !== undefined ? { text } : {};
123034
- }
123035
- function answersPendingPrompt(delivery, promptId) {
123036
- return delivery === "answer" || delivery === "auto" || promptId !== undefined;
123037
- }
123038
- function answerablePromptIds(stage) {
123039
- const target = stage.workflowGraphTarget;
123040
- const ids = [];
123041
- const brokered = stageUiBroker.peekStagePrompt(target.runId, target.stageId);
123042
- if (brokered !== undefined)
123043
- ids.push(brokered.id);
123044
- if (stage.pendingPrompt !== undefined)
123045
- ids.push(stage.pendingPrompt.id);
123046
- if (stage.status === "awaiting_input" && stage.promptFootprint?.kind === "custom") {
123047
- ids.push(stage.promptFootprint.id);
123048
- }
123049
- return ids;
123050
- }
123051
- function inferPromptStageTarget(runId, delivery, promptId) {
123052
- if (!answersPendingPrompt(delivery, promptId))
123053
- return { ok: true, runId };
123054
- const pending = expandWorkflowGraph(readGraphStoreSnapshot(store), runId).stages.filter((stage) => {
123055
- const ids = answerablePromptIds(stage);
123056
- return promptId === undefined ? ids.length > 0 : ids.includes(promptId);
123057
- });
123058
- const only = pending[0];
123059
- if (pending.length === 1 && only !== undefined) {
123060
- return { ok: true, runId: only.workflowGraphTarget.runId, stageId: only.workflowGraphTarget.stageId };
123061
- }
123062
- if (pending.length === 0) {
123063
- return promptId === undefined ? { ok: true, runId } : { ok: false, message: `No pending prompt ${promptId} in run ${runId}.` };
123064
- }
123065
- return {
123066
- ok: false,
123067
- message: `${pending.length} prompts pending; pass stageId: ${pending.map(expandedStageLabel).join(", ")}`
123068
- };
123069
- }
123070
- function workflowSendResult(runId, stageId, delivery, status, message) {
123071
- return { action: "send", runId, stageId, delivery, status, message };
123072
- }
123073
- function terminalWorkflowSendResult(runId, stageId, workflowStatus) {
123074
- const message = `Cannot send a message because workflow ${runId} has terminated with status ${workflowStatus}. If work remains, start a new workflow to address it. Complete it inline only when the remaining task is small, deterministic, and low risk.`;
123075
- return {
123076
- action: "send",
123077
- runId,
123078
- stageId,
123079
- delivery: "rejected",
123080
- status: "failed",
123081
- code: "WORKFLOW_TERMINAL",
123082
- workflowStatus,
123083
- error: message,
123084
- message
123085
- };
123086
- }
123087
-
123088
- class WorkflowSendAdmissionError extends Error {
123089
- result;
123090
- constructor(result) {
123091
- super(result.message);
123092
- this.result = result;
123093
- }
123094
- }
123095
- function terminalWorkflowSendResultForRoot(rootRunId, stageId) {
123096
- const rootRun = store.runs().find((run2) => run2.id === rootRunId);
123097
- return rootRun !== undefined && isTerminalRunStatus(rootRun.status) ? terminalWorkflowSendResult(rootRun.id, stageId, rootRun.status) : undefined;
123098
- }
123099
- async function deliverStageUserMessage(handle, text, deliverAs, beforeDelivery) {
123100
- if (handle.sendUserMessage === undefined) {
123101
- throw new Error("atomic-workflows: stage handle does not support admission-aware message delivery");
123102
- }
123103
- return handle.sendUserMessage(text, { deliverAs }, beforeDelivery);
123104
- }
123105
- function deliveryMessage(delivery) {
123106
- if (delivery === "prompt")
123107
- return "Prompt started for stage.";
123108
- if (delivery === "steer")
123109
- return "Steered live stage.";
123110
- if (delivery === "followUp")
123111
- return "Follow-up queued for stage.";
123112
- return "Message handled by stage extension.";
123113
- }
123114
- async function workflowSendAction(args, deps = {}) {
123115
- const target = resolveToolRunTarget(args, "No active run to message.");
123116
- const requestedDelivery = args.delivery ?? "auto";
123117
- if (target.kind === "all") {
123118
- return workflowSendResult("--all", "", requestedDelivery, "noop", "Send requires a single run.");
123119
- }
123120
- if (target.kind === "malformed" || target.kind === "not_found") {
123121
- return workflowSendResult(target.target, "", requestedDelivery, "noop", target.message);
123122
- }
123123
- const runs = store.runs();
123124
- const runById = new Map(runs.map((run3) => [run3.id, run3]));
123125
- const rootRunId = reciprocalWorkflowRootRunId(runById, target.runId) ?? target.runId;
123126
- const rootRun = runById.get(rootRunId);
123127
- if (rootRun !== undefined && isTerminalRunStatus(rootRun.status)) {
123128
- return terminalWorkflowSendResult(rootRun.id, args.stageId?.trim() ?? "", rootRun.status);
123129
- }
123130
- const requested = resolveToolStageTarget(target.runId, args.stageId);
123131
- const stage = requested.ok && requested.stageId === undefined ? inferPromptStageTarget(target.runId, requestedDelivery, args.promptId) : requested;
123132
- if (!stage.ok || stage.stageId === undefined) {
123133
- return workflowSendResult(target.runId, "", requestedDelivery, "noop", stage.ok ? "Stage id or name is required." : stage.message);
123134
- }
123135
- const resolvedStageId = stage.stageId;
123136
- const stageRunId = stage.runId ?? target.runId;
123137
- const run2 = store.runs().find((r) => r.id === stageRunId);
123138
- const snapshot2 = run2?.stages.find((s) => s.id === stage.stageId);
123139
- const brokerPrompt = stageUiBroker.peekStagePrompt(stageRunId, stage.stageId);
123140
- const targetsBrokerPrompt = brokerPrompt !== undefined && (args.promptId === undefined || args.promptId === brokerPrompt.id) && (requestedDelivery === "answer" || args.promptId !== undefined || requestedDelivery === "auto");
123141
- if (targetsBrokerPrompt && brokerPrompt !== undefined) {
123142
- if (!hasPayloadProperty(args)) {
123143
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", "Send requires text, response, or message.");
123144
- }
123145
- const ok = stageUiBroker.answerStagePrompt(stageRunId, stage.stageId, brokerAnswerFromArgs(args), {
123146
- answerSource: "workflow_tool"
123147
- });
123148
- return workflowSendResult(stageRunId, stage.stageId, "answer", ok ? "ok" : "noop", ok ? `Answered input request ${brokerPrompt.id}.` : `No matching pending input request ${brokerPrompt.id}.`);
123149
- }
123150
- const customPrompt = snapshot2?.status === "awaiting_input" && snapshot2.promptFootprint?.kind === "custom" ? snapshot2.promptFootprint : undefined;
123151
- const targetsCustomPrompt = customPrompt !== undefined && (args.promptId === undefined || args.promptId === customPrompt.id) && (requestedDelivery === "answer" || args.promptId !== undefined || requestedDelivery === "auto");
123152
- if (targetsCustomPrompt && customPrompt !== undefined) {
123153
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", `Custom UI prompt ${customPrompt.id} requires the interactive workflow graph; arbitrary ctx.ui.custom<T> results cannot be answered through workflow send.`);
123154
- }
123155
- const targetsPrompt = requestedDelivery === "answer" || args.promptId !== undefined || requestedDelivery === "auto" && snapshot2?.pendingPrompt !== undefined;
123156
- if (targetsPrompt) {
123157
- const promptId = args.promptId ?? snapshot2?.pendingPrompt?.id;
123158
- if (promptId === undefined) {
123159
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", "No pending prompt to answer.");
123160
- }
123161
- if (!hasPayloadProperty(args)) {
123162
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", "Send requires text, response, or message.");
123163
- }
123164
- if (stageUiBroker.wasStagePromptResolved(stageRunId, stage.stageId, promptId)) {
123165
- return workflowSendResult(stageRunId, stage.stageId, "answer", "ok", `Input request ${promptId} was already answered.`);
123166
- }
123167
- const rawPayload = promptPayloadFromArgs(args);
123168
- const pendingPrompt = snapshot2?.pendingPrompt;
123169
- const primitivePrompt = pendingPrompt?.id === promptId && isPrimitivePrompt(pendingPrompt) ? pendingPrompt : undefined;
123170
- if (primitivePrompt !== undefined) {
123171
- const coercedPayload = coercePrimitivePromptAnswer(primitivePrompt, rawPayload);
123172
- if (!coercedPayload.ok) {
123173
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", primitivePromptAnswerRejection(promptId, primitivePrompt));
123174
- }
123175
- const ok2 = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, coercedPayload.value, {
123176
- answerSource: "workflow_tool"
123177
- });
123178
- return workflowSendResult(stageRunId, stage.stageId, "answer", ok2 ? "ok" : "noop", ok2 ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123179
- }
123180
- const ok = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, rawPayload, {
123181
- answerSource: "workflow_tool"
123182
- });
123183
- return workflowSendResult(stageRunId, stage.stageId, "answer", ok ? "ok" : "noop", ok ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123184
- }
123185
- const text = textPayloadFromArgs(args);
123186
- if (text === undefined) {
123187
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "Send requires text, response, or message.");
123188
- }
123189
- let lease;
123190
- let handle;
123191
- const existingHandle = stageControlRegistry.peek(stageRunId, stage.stageId);
123192
- if (existingHandle !== undefined) {
123193
- lease = stageControlRegistry.acquireDetached(stageRunId, stage.stageId, () => existingHandle);
123194
- handle = lease.handle;
123195
- } else if (deps.resolvePostMortemDeps !== undefined && snapshot2 !== undefined) {
123196
- const acquired = acquirePostMortemStageHandle(stageRunId, snapshot2, deps.resolvePostMortemDeps(stageRunId));
123197
- if (acquired.ok) {
123198
- lease = acquired.lease;
123199
- handle = lease.handle;
123200
- }
123201
- }
123202
- if (handle === undefined) {
123203
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "No live handle for stage.");
123204
- }
123205
- let admitted = false;
123206
- const admitDelivery = () => {
123207
- const terminal = terminalWorkflowSendResultForRoot(rootRunId, args.stageId?.trim() ?? resolvedStageId);
123208
- if (terminal !== undefined)
123209
- throw new WorkflowSendAdmissionError(terminal);
123210
- if (lease !== undefined && !lease.commit()) {
123211
- throw new WorkflowSendAdmissionError(workflowSendResult(stageRunId, resolvedStageId, requestedDelivery, "noop", "Stage handle changed before message admission; retry against the current live stage."));
123212
- }
123213
- admitted = true;
123214
- };
123215
- const terminalPending = Promise.withResolvers();
123216
- const unsubscribeTerminal = subscribeStoreInvalidation(store, () => {
123217
- if (admitted)
123218
- return;
123219
- const terminal = terminalWorkflowSendResultForRoot(rootRunId, args.stageId?.trim() ?? resolvedStageId);
123220
- if (terminal !== undefined)
123221
- terminalPending.reject(new WorkflowSendAdmissionError(terminal));
123222
- });
123223
- const awaitAdmission = (operation) => Promise.race([operation, terminalPending.promise]);
123224
- try {
123225
- const isTerminalPostMortemStage = handle.status === "completed";
123226
- if (requestedDelivery === "resume" && handle.status !== "paused" && !isTerminalPostMortemStage) {
123227
- return workflowSendResult(stageRunId, stage.stageId, "resume", "noop", "Stage is not paused; no resume message was delivered.");
123228
- }
123229
- if (handle.status === "paused" && requestedDelivery !== "resume" && requestedDelivery !== "auto") {
123230
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "Stage is paused; resume it before sending a new message.");
123231
- }
123232
- const resumesStage = requestedDelivery === "resume" || requestedDelivery === "auto" && handle.status === "paused";
123233
- if (!resumesStage && handle.sendUserMessage === undefined) {
123234
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "Stage handle does not support admission-aware message delivery.");
123235
- }
123236
- if (resumesStage) {
123237
- if (isTerminalPostMortemStage) {
123238
- return workflowSendResult(stageRunId, stage.stageId, "resume", "noop", 'Cannot resume a terminal post-mortem stage; use delivery "followUp" or "prompt" to continue its retained conversation.');
123239
- }
123240
- const resumedDelivery = await awaitAdmission(handle.resume(text, admitDelivery));
123241
- if (resumedDelivery !== undefined && resumedDelivery !== "prompt") {
123242
- return workflowSendResult(stageRunId, stage.stageId, resumedDelivery, "ok", deliveryMessage(resumedDelivery));
123243
- }
123244
- if (resumedDelivery === "prompt") {
123245
- return workflowSendResult(stageRunId, stage.stageId, "prompt", "ok", "Resumed stage and started prompt.");
123246
- }
123247
- const hasResumeMessage = text.trim().length > 0;
123248
- return workflowSendResult(stageRunId, stage.stageId, "resume", "ok", hasResumeMessage ? "Resumed interrupted stage with message." : "Resumed stage.");
123249
- }
123250
- if (requestedDelivery === "steer") {
123251
- if (isTerminalPostMortemStage) {
123252
- return workflowSendResult(stageRunId, stage.stageId, "steer", "noop", 'Cannot steer a terminal post-mortem stage; use delivery "followUp" or "prompt" to continue its retained conversation.');
123253
- }
123254
- const delivery2 = await awaitAdmission(deliverStageUserMessage(handle, text, "steer", admitDelivery));
123255
- return workflowSendResult(stageRunId, stage.stageId, delivery2, "ok", deliveryMessage(delivery2));
123256
- }
123257
- if (requestedDelivery === "auto" && handle.isStreaming && !isTerminalPostMortemStage) {
123258
- const delivery2 = await awaitAdmission(deliverStageUserMessage(handle, text, "steer", admitDelivery));
123259
- return workflowSendResult(stageRunId, stage.stageId, delivery2, "ok", deliveryMessage(delivery2));
123260
- }
123261
- if (requestedDelivery === "prompt") {
123262
- const delivery2 = await awaitAdmission(deliverStageUserMessage(handle, text, undefined, admitDelivery));
123263
- const message = delivery2 === "prompt" ? "Prompt sent to stage." : deliveryMessage(delivery2);
123264
- return workflowSendResult(stageRunId, stage.stageId, delivery2, "ok", message);
123265
- }
123266
- const delivery = await awaitAdmission(deliverStageUserMessage(handle, text, "followUp", admitDelivery));
123267
- return workflowSendResult(stageRunId, stage.stageId, delivery, "ok", deliveryMessage(delivery));
123268
- } catch (error) {
123269
- if (error instanceof WorkflowSendAdmissionError)
123270
- return error.result;
123271
- throw error;
123272
- } finally {
123273
- unsubscribeTerminal();
123274
- if (!admitted) {
123275
- try {
123276
- await lease?.release();
123277
- } catch (error) {
123278
- console.warn("atomic-workflows: provisional workflow-send handle cleanup failed", error);
123279
- }
123280
- }
123281
- }
123282
- }
123283
-
123284
124157
  // dist/builtin/workflows/src/extension/workflow-tool.ts
123285
124158
  async function resolveDurableInspectionSource(args, runtime) {
123286
124159
  const target = args.runId?.trim();
@@ -123308,7 +124181,7 @@ function durableInspectionError(action, runId, message) {
123308
124181
  truncated: false
123309
124182
  };
123310
124183
  }
123311
- function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflowResourcesLoaded = () => {}, sendDeps = {}) {
124184
+ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflowResourcesLoaded = () => {}) {
123312
124185
  return async function executeWorkflowTool(args, ctx, signal, onRunAccepted) {
123313
124186
  const action = args.action ?? "run";
123314
124187
  const runId = args.runId ?? "";
@@ -123397,8 +124270,8 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
123397
124270
  return workflowStageResult(args, source);
123398
124271
  return workflowTranscriptResult(args, source);
123399
124272
  }
123400
- case "send":
123401
- return awaitRequest(workflowSendAction(args, sendDeps));
124273
+ case "answer":
124274
+ return awaitRequest(workflowAnswerAction(args));
123402
124275
  case "pause":
123403
124276
  return awaitRequest(workflowPauseAction(args));
123404
124277
  case "reload":
@@ -123459,8 +124332,8 @@ function renderCall(args, opts = {}) {
123459
124332
  case "transcript":
123460
124333
  line = name === undefined ? "workflow: read stage transcript" : `workflow: read stage transcript in ${quoted(name)}`;
123461
124334
  break;
123462
- case "send":
123463
- line = name === undefined ? "workflow: send to stage" : `workflow: send to stage in ${quoted(name)}`;
124335
+ case "answer":
124336
+ line = name === undefined ? "workflow: answer prompt" : `workflow: answer prompt in ${quoted(name)}`;
123464
124337
  break;
123465
124338
  case "pause":
123466
124339
  line = name === undefined ? "workflow: pause run" : `workflow: pause run ${quoted(name)}`;
@@ -123652,9 +124525,9 @@ function renderResult(result, opts) {
123652
124525
  const suffix = r.truncated ? " (truncated)" : "";
123653
124526
  return renderNotice("WORKFLOW TRANSCRIPT", `${r.runId}/${r.stageId} ${r.source}: ${text}${suffix}`, opts, themed);
123654
124527
  }
123655
- case "send": {
124528
+ case "answer": {
123656
124529
  const r = result;
123657
- return renderNotice("WORKFLOW SEND", `${r.runId}/${r.stageId} ${r.delivery}: ${r.message}`, opts, themed);
124530
+ return renderNotice("WORKFLOW ANSWER", `${r.runId}/${r.stageId}: ${r.message}`, opts, themed);
123658
124531
  }
123659
124532
  case "pause": {
123660
124533
  const r = result;
@@ -123697,7 +124570,7 @@ function renderResult(result, opts) {
123697
124570
  }
123698
124571
 
123699
124572
  // dist/builtin/workflows/src/extension/workflow-prompts.ts
123700
- var WORKFLOW_TOOL_DESCRIPTION = "Run named builtin, project, user, or package workflows; custom definitions may import reusable project/package workflows or builtin definitions from @bastani/workflows/builtin and nest them with ctx.workflow(...), including deeper composition within the configured maxDepth; " + "when workflow execution fits but another shape would better achieve the task, author a custom TypeScript workflow({...}) inline with normal coding tools, reload it, and run it; after successfully creating and reloading a newly authored custom workflow, report the folder containing its generated code as 'Custom workflow created. You can inspect its code at: <workflow-folder-path>'; do this only for newly created custom workflows, never builtin or pre-existing workflows; " + "discover with list/get/inputs/models, list session runs with status (no runId; statusFilter narrows the list), inspect status/stages/stage details, " + "send prompt answers or steering only while the root workflow is nonterminal, pause/resume/interrupt/quit runs, and reload workflow resources. " + "Treat a blocked run as continuable by default: resume resumable blocks, answer pending prompts, steer past the obstacle, or start a follow-up workflow past a terminal block, and escalate to the user only when the blocked result is so ambiguous that human input or steering must settle it; when ask_user_question or human input is unavailable, continue fully autonomously on the interpretation best supported by the objective and repository evidence, and record the assumption. " + "For action 'run' and 'resume', budget accepts per-field duration, token, cost, and warning overrides; fields resolve over the workflow declaration and config, and 0 disables a field. " + "Pass budget only when the user asked for a limit; otherwise omit it entirely and inherit the declaration and config rather than inventing a cap. " + "For primitive prompt answers, use booleans or the documented confirm labels, exact case-insensitive select labels or 1-based indexes, and text strings for input/editor; an invalid answer remains pending and returns guidance instead of choosing a default. " + "For large stage handoffs, write context to files/artifacts, pass paths via reads, and prompt downstream agents to 'Read the file at <path>...' instead of injecting large previous text. " + "Wrap critical parts of run inputs and steering messages in <keepContext>...</keepContext> so compaction preserves them verbatim in the stages that inherit them; tag role constraints, prohibitions, must-hold criteria, and identifiers, not background or bulk reference material. " + "For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, " + "quote the exact path without rewriting separators (Windows backslashes are valid), " + "then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview. " + "Use action 'models' to inspect models in the configured catalog; the result is a configured-auth snapshot showing what's present in the registry with configured authentication, not proof of credentials, entitlements, OAuth freshness, or live provider access. " + "When authoring a workflow that should dynamically select a model, first call workflow({ action: 'models' }) to inspect the configured catalog, then select from the returned provider/id entries considering the isCurrent marker and available thinking levels.";
124573
+ var WORKFLOW_TOOL_DESCRIPTION = "Run named builtin, project, user, or package workflows; custom definitions may import reusable project/package workflows or builtin definitions from @bastani/atomic/workflows/builtin and nest them with ctx.workflow(...), including deeper composition within the configured maxDepth; " + "when workflow execution fits but another shape would better achieve the task, author a custom TypeScript workflow({...}) inline with normal coding tools, reload it, and run it; after successfully creating and reloading a newly authored custom workflow, report the folder containing its generated code as 'Custom workflow created. You can inspect its code at: <workflow-folder-path>'; do this only for newly created custom workflows, never builtin or pre-existing workflows; " + "discover with list/get/inputs/models, list session runs with status (no runId; statusFilter narrows the list), inspect status/stages/stage details, " + "answer pending prompts only while the root workflow is nonterminal, pause/resume/interrupt/quit runs, and reload workflow resources. " + "When steering or communication is useful, use Intercom; address a workflow stage as `<runId>:<stageKey>`. Live delivery is immediate; a known stage that has not started is queued and receives the message before its first model turn. Use `ask` only for a reply-capable live session. " + "Treat a blocked run as continuable by default: resume resumable blocks, answer pending prompts, steer past the obstacle, or start a follow-up workflow past a terminal block, and escalate to the user only when the blocked result is so ambiguous that human input or steering must settle it; when ask_user_question or human input is unavailable, continue fully autonomously on the interpretation best supported by the objective and repository evidence, and record the assumption. " + "For action 'run' and 'resume', budget accepts per-field duration, token, cost, and warning overrides; fields resolve over the workflow declaration and config, and 0 disables a field. " + "Pass budget only when the user asked for a limit; otherwise omit it entirely and inherit the declaration and config rather than inventing a cap. " + "For primitive prompt answers, use booleans or the documented confirm labels, exact case-insensitive select labels or 1-based indexes, and text strings for input/editor; an invalid answer remains pending and returns guidance instead of choosing a default. " + "For large stage handoffs, write context to files/artifacts, pass paths via reads, and prompt downstream agents to 'Read the file at <path>...' instead of injecting large previous text. " + "Wrap critical parts of run inputs and steering messages in <keepContext>...</keepContext> so compaction preserves them verbatim in the stages that inherit them; tag role constraints, prohibitions, must-hold criteria, and identifiers, not background or bulk reference material. " + "For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, " + "quote the exact path without rewriting separators (Windows backslashes are valid), " + "then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview. " + "Use action 'models' to inspect models in the configured catalog; the result is a configured-auth snapshot showing what's present in the registry with configured authentication, not proof of credentials, entitlements, OAuth freshness, or live provider access. " + "When authoring a workflow that should dynamically select a model, first call workflow({ action: 'models' }) to inspect the configured catalog, then select from the returned provider/id entries considering the isCurrent marker and available thinking levels.";
123701
124574
  var DEFAULT_PROMPT_GUIDANCE = [
123702
124575
  `**Workflows**: Treat workflows as the default execution path for any non-trivial task and for any request that has inherent structure plus an objective you can make verifiable. Use the \`workflow\` tool for existing named workflows; when the task needs a graph that is not installed, author a custom TypeScript \`workflow({...})\` inline with normal coding tools, reload workflow resources, and run it.
123703
124576
  - Workflow fit check: prefer a workflow for implementation, build, debug/diagnosis, bug-fix, migration, new-feature, scoped multi-file, or docs/code changes with validation; and whenever there are multiple steps, dependencies, handoffs, uncertainty, review/validation needs, long-running work, measurable done criteria, or an outcome that needs evidence.
@@ -123729,16 +124602,16 @@ var DEFAULT_PROMPT_GUIDANCE = [
123729
124602
  `**Workflow discovery and lifecycle**:
123730
124603
  - For unfamiliar named workflows, discover with \`action: "list"\`, inspect with \`action: "get"\` or \`action: "inputs"\`, and run with \`action: "run"\`, \`workflow\`, and validated \`inputs\`; do not invent workflow names or input keys.
123731
124604
  - Do not pass a \`budget\` unless the user asked for a limit. Budget fields are overrides, not required inputs: omitting \`budget\` inherits the workflow declaration and config, which is the intended default, and assuming no budget is always the correct default. A cap you invented truncates an otherwise healthy run at a duration, token, or cost boundary the user never chose, and the resulting failure reads as a workflow problem rather than as your override. When the user does state a limit, pass only the fields they named, leave every other field inherited, and use \`0\` to disable a field they asked you to remove.
123732
- - Heartbeat cadence is 15 minutes by default. Assume that interval and do not configure, shorten, or lengthen it unless the user explicitly asks for a different cadence. A heartbeat is a periodic alignment check rather than a failure: re-read the objective, judge whether the run is still on goal, then continue, steer, replace, or ask. Do not treat it as a prompt to add a budget, poll in a loop, or intervene in a run that is progressing.
124605
+ - Heartbeat cadence is 15 minutes by default. Keep that interval unless the user explicitly asks for a different cadence. A heartbeat is a periodic alignment check: re-read the objective, judge whether the run is still on goal, and continue a progressing run when no intervention is needed. Use workflow pause, resume, interrupt, or quit when run control is useful, and ask the user when a decision is needed.
123733
124606
  - Wrap critical parts of the \`inputs\` you pass — objective, prompt, acceptance criteria — in \`<keepContext>\` / \`</keepContext>\`. Tagged text survives compaction verbatim regardless of the compression ratio, and stages inherit it because workflows inject your inputs into their prompts. A long stage is compacted repeatedly, and compaction ranks lines individually: a verbose restated objective outlives the one line that bounds it, so a stage can end up executing a coherent but unbounded version of what you asked for. Decide this per launch rather than tagging everything — tag role constraints, prohibitions, must-hold criteria, and identifiers such as a target branch, worktree path, or issue number; leave background, quoted issue text, and reference material untagged and pass bulk through files and \`reads\`. Protected lines count against the keep target rather than raising it, so an over-tagged input makes the evidence stages need compress harder.
123734
- - Tag steering the same way. A \`send\` amendment is authoritative and stages must carry it forward, but it arrives late in an already-long session and is exactly one short message competing with the whole transcript; \`<keepContext>\` around the requirement keeps it from being compacted away before the stage acts on it.
123735
- - In interactive chat, named workflow launches run in the background. Run \`/workflow connect <run>\` to see agents working and chat with and steer each stage. Inspection and control calls (\`status\`, \`stages\`, \`stage\`, \`transcript\`, \`send\`, \`pause\`, \`resume\`, \`interrupt\`, \`quit\`) remain available while work runs.
123736
- - \`workflow send\` is nonterminal-only: once the authoritative root run has completed, failed, skipped, been cancelled or killed, or ended terminal-blocked, start a new workflow if tracked work remains. Proceed inline only when the remaining task is small, deterministic, and low risk. Use explicit \`/workflow attach <run> <stage>\` for user-driven post-mortem chat; it does not resume or modify workflow execution.
124607
+ - Tag steering the same way. An Intercom \`send\` amendment is authoritative and stages must carry it forward, but it arrives late in an already-long session and is exactly one short message competing with the whole transcript; \`<keepContext>\` around the requirement keeps it from being compacted away before the stage acts on it.
124608
+ - In interactive chat, named workflow launches run in the background. Run \`/workflow connect <run>\` to see agents working and chat with and steer each stage. Inspection and control calls (\`status\`, \`stages\`, \`stage\`, \`transcript\`, \`answer\`, \`pause\`, \`resume\`, \`interrupt\`, \`quit\`) remain available while work runs.
124609
+ - \`workflow answer\` handles a pending human-input prompt. Send free-form updates through Intercom to \`<runId>:<stageKey>\`; Atomic delivers immediately to live stages and queues messages for known stages that have not started, delivering them before their first model turn. Use \`ask\` once the target has a live session that can reply. Use explicit \`/workflow attach <run> <stage>\` for user-driven post-mortem chat.
123737
124610
  - A blocked run is a changed condition, not a stop order. On a blocked status or a WORKFLOW BLOCKED lifecycle notice, keep the work moving by default: resume a resumable block, answer the pending prompt, steer the stage past the obstacle, or start a follow-up workflow that carries the remaining tracked work past a terminal block — continue inline only if the remaining work is minimal. Stop for user input only when the task is so ambiguous that competing interpretations lead to materially different outcomes and judgment cannot infer intent from the stated objective and repository evidence; then ask one grouped question or wait for steering.
123738
124611
  - A budget-exceeded stop (the resumable \`budget_exceeded\` blocked rail) is the exception: the exhausted budget is a boundary someone chose, so do not raise it silently. Summarize progress and the estimated next steps, ask the user whether to proceed — prefer the \`ask_user_question\` tool when it is available — and resume with a raised \`budget\` only after approval.
123739
124612
  - When human input is unavailable — \`ask_user_question\` is not among your tools, the session is headless, or nobody answers — do not stall on a question. Choose the interpretation best supported by the stated objective and repository evidence — mine git history, commits, PRs, issues, and the user's own comments to infer how they would decide — record the assumption and rationale in the result or an artifact, and continue fully autonomously on best judgment.
123740
124613
  - Natural-language instructions to create or use a worktree do not enable runner isolation. A named workflow must declare and implement any worktree and feature-branch inputs it supports; inspect its inputs and pass a distinct path and branch for each concurrent item. Reusable \`baseBranch\` binding creates a missing target as a detached checkout from that base or reuses an existing same-repository worktree as-is; neither case checks out a separate feature-branch input.
123741
- - Once you have started the selected single run or every run in the selected bounded wave, end the current turn and wait for user input or lifecycle notices. Do not use sleep/status polling loops: key start, finish, and failure events arrive automatically. Use targeted \`status\`/\`stages\`/\`stage\` checks only when the user asks or the next step needs them, and use \`send\`/\`pause\`/\`resume\`/\`interrupt\`/\`quit\` only to answer, steer, or honor control requests.
124614
+ - Once you have started the selected single run or every run in the selected bounded wave, end the current turn and wait for user input or lifecycle notices. Do not use sleep/status polling loops: key start, finish, and failure events arrive automatically. Use targeted \`status\`/\`stages\`/\`stage\` checks only when the user asks or the next step needs them, and use Intercom \`send\`/\`ask\` for stage communication or \`answer\`/\`pause\`/\`resume\`/\`interrupt\`/\`quit\` for workflow prompts and control.
123742
124615
  - For transcripts, avoid whole-file reads. Get \`sessionFile\`/\`transcriptPath\` from \`stages\` or \`stage\`, preserve the exact path and platform separators, search with \`rg\`/\`grep\`, and read small relevant ranges; use explicit \`tail\` or \`limit\` only for a bounded preview.`,
123743
124616
  `**Workflow authoring and handoffs**:
123744
124617
  - When a user asks to create or edit a workflow, clarify only unresolved requirements that materially affect its purpose, inputs, stages, handoffs, validation, success criteria, or starter pattern. Read the workflow docs/examples, implement the TypeScript definition with normal coding tools, reload it, and run representative test inputs before presenting it. Use the create-spec skill when it adds value; it is not mandatory when context is already sufficient.
@@ -123800,26 +124673,26 @@ var WorkflowParametersSchema = Type36.Object({
123800
124673
  Type36.Literal("stages"),
123801
124674
  Type36.Literal("stage"),
123802
124675
  Type36.Literal("transcript"),
123803
- Type36.Literal("send"),
124676
+ Type36.Literal("answer"),
123804
124677
  Type36.Literal("pause"),
123805
124678
  Type36.Literal("interrupt"),
123806
124679
  Type36.Literal("quit"),
123807
124680
  Type36.Literal("resume"),
123808
124681
  Type36.Literal("reload")
123809
124682
  ], {
123810
- description: "Workflow action: run/list/get/inputs/models/status, inspect stage metadata, send messages or prompt answers, pause/resume/interrupt/quit runs, inspect the configured model catalog, or reload workflow resources. 'status' without runId lists every workflow run in the current session with concise per-run summaries (status, timing, active stages, awaiting-input prompts); filter the listing with statusFilter. 'status' with runId returns one run's full detail. For transcript inspection, prefer status/stages/stage first to get sessionFile/transcriptPath, quote the exact path without rewriting separators (Windows backslashes are valid), then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview."
124683
+ description: "Workflow action: run/list/get/inputs/models/status, inspect stage metadata, answer pending prompts, pause/resume/interrupt/quit runs, inspect the configured model catalog, or reload workflow resources. 'status' without runId lists every workflow run in the current session with concise per-run summaries (status, timing, active stages, awaiting-input prompts); filter the listing with statusFilter. 'status' with runId returns one run's full detail. For transcript inspection, prefer status/stages/stage first to get sessionFile/transcriptPath, quote the exact path without rewriting separators (Windows backslashes are valid), then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview."
123811
124684
  })),
123812
124685
  runId: Type36.Optional(Type36.String({
123813
- description: "Full 36-character run UUID for status/stages/stage/transcript/send/pause/resume/interrupt/quit. Prefixes are not accepted; pass the id exactly as displayed. Omit runId with action 'status' to list all session runs and their statuses. Use '--all' or all:true for supported bulk run-control actions."
124686
+ description: "Full 36-character run UUID for status/stages/stage/transcript/answer/pause/resume/interrupt/quit. Prefixes are not accepted; pass the id exactly as displayed. Omit runId with action 'status' to list all session runs and their statuses. Use '--all' or all:true for supported bulk run-control actions."
123814
124687
  })),
123815
124688
  all: Type36.Optional(Type36.Boolean({
123816
124689
  description: "Apply supported run-control actions (pause/interrupt/quit) to all in-flight runs instead of one run; cannot be combined with stageId."
123817
124690
  })),
123818
124691
  stageId: Type36.Optional(Type36.String({
123819
- description: "Exact stage id or exact stage name for stage-scoped inspection, transcript, send, pause, or resume. Prefixes and partial names are not accepted. A nested stage id is the full 'runId:stageId' composite. For interrupt and quit it may also name an in-flight ctx.tool node by its exact tool:<argsHash> id or tool name, which aborts that single call."
124692
+ description: "Exact stage id or exact stage name for stage-scoped inspection, transcript, answer, pause, or resume. Prefixes and partial names are not accepted. A nested stage id is the full 'runId:stageId' composite. For interrupt and quit it may also name an in-flight ctx.tool node by its exact tool:<argsHash> id or tool name, which aborts that single call."
123820
124693
  })),
123821
124694
  message: Type36.Optional(Type36.String({
123822
- description: "Message payload for send/follow-up/prompt/steer/resume, or optional text forwarded when resuming paused work."
124695
+ description: "Prompt answer text for answer, or optional text forwarded when resuming paused work."
123823
124696
  })),
123824
124697
  statusFilter: Type36.Optional(Type36.Union([
123825
124698
  Type36.Literal("pending"),
@@ -123851,21 +124724,11 @@ var WorkflowParametersSchema = Type36.Object({
123851
124724
  description: "Transcript-only: include captured tool output entries when building inlined snapshot previews; this does not bypass the path-only default. Prefer rg/grep on the exact quoted sessionFile/transcriptPath for large outputs. Live session transcripts may not expose tool output."
123852
124725
  })),
123853
124726
  text: Type36.Optional(Type36.String({
123854
- description: "Text to send to a stage for prompt answers, steering, follow-ups, or resume messages."
124727
+ description: "Text for a pending prompt answer."
123855
124728
  })),
123856
124729
  response: Type36.Optional(WorkflowResponseSchema),
123857
- delivery: Type36.Optional(Type36.Union([
123858
- Type36.Literal("auto"),
123859
- Type36.Literal("answer"),
123860
- Type36.Literal("prompt"),
123861
- Type36.Literal("steer"),
123862
- Type36.Literal("followUp"),
123863
- Type36.Literal("resume")
123864
- ], {
123865
- description: "Delivery mode for the send action: auto answers pending prompts first, then resumes paused stages, steers streaming stages, or queues a follow-up."
123866
- })),
123867
124730
  promptId: Type36.Optional(Type36.String({
123868
- description: "Pending prompt identifier to answer when using the send action."
124731
+ description: "Pending prompt identifier for the answer action."
123869
124732
  })),
123870
124733
  reason: Type36.Optional(Type36.String({
123871
124734
  description: "Human-readable reason for the reload action, echoed in the reload result."
@@ -123877,7 +124740,7 @@ var WORKFLOW_TOOL_REQUEST_TIMEOUT_MS = 120000;
123877
124740
  var MUTATING_WORKFLOW_ACTIONS = new Set([
123878
124741
  "reload",
123879
124742
  "run",
123880
- "send",
124743
+ "answer",
123881
124744
  "pause",
123882
124745
  "resume",
123883
124746
  "interrupt",
@@ -123998,10 +124861,11 @@ function factory(pi) {
123998
124861
  const postMortemHandleResolver = createPostMortemHandleResolver(postMortemResolverDeps);
123999
124862
  const overlay = buildWorkflowOverlay(pi, postMortemHandleResolver);
124000
124863
  registerCompletedStageIntercomAskRouter(pi, postMortemHandleResolver);
124864
+ registerPendingStageIntercomBridge(pi, store);
124001
124865
  const workflowCommands = new Map;
124002
124866
  const storeWidgetRef = { current: null };
124003
124867
  const intercomControlRef = { current: null };
124004
- const executeWorkflowTool = makeExecuteWorkflowTool((ctx) => runtimeState.runtimeForContext(ctx), runtimeState.reloadWorkflowResources, runtimeState.ensureWorkflowResourcesLoaded, { resolvePostMortemDeps: (runId) => postMortemDepsForRun(runId, postMortemResolverDeps) });
124868
+ const executeWorkflowTool = makeExecuteWorkflowTool((ctx) => runtimeState.runtimeForContext(ctx), runtimeState.reloadWorkflowResources, runtimeState.ensureWorkflowResourcesLoaded);
124005
124869
  const executeWorkflowToolWithAutoAttach = async (args, ctx, signal, onRunAccepted) => {
124006
124870
  const result = await executeWorkflowTool(args, ctx, signal, onRunAccepted);
124007
124871
  if (workflowPolicyFromContext(ctx).mode === "interactive" && typeof args.workflow === "string" && result.action === "run" && result.status === "running" && result.runId.length > 0 && runtimeState.runtimeProxy.registry.get(args.workflow)?.autoAttach === true) {