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

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 (207) hide show
  1. package/CHANGELOG.md +19 -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 +680 -53
  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 +109 -35
  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 +173 -51
  21. package/dist/builtin/web-access/index.bundle.mjs +109 -35
  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-29wrp38a.js +199 -0
  28. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  29. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  30. package/dist/builtin/workflows/builtin/chunk-5n10x7j2.js +69730 -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-bfkmzv9h.js +156 -0
  37. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  38. package/dist/builtin/workflows/builtin/chunk-cdtd3m3w.js +529 -0
  39. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  40. package/dist/builtin/workflows/builtin/chunk-hdpj1dkw.js +2447 -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-qwzvgxnq.js +404 -0
  44. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  45. package/dist/builtin/workflows/builtin/chunk-v04c5qmd.js +1207 -0
  46. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  47. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  48. package/dist/builtin/workflows/builtin/chunk-zf28603f.js +1080 -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 +1228 -428
  63. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +956 -427
  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/context-types.d.ts +35 -0
  91. package/dist/core/extensions/context-types.d.ts.map +1 -1
  92. package/dist/core/extensions/context-types.js.map +1 -1
  93. package/dist/core/extensions/index.d.ts +1 -1
  94. package/dist/core/extensions/index.d.ts.map +1 -1
  95. package/dist/core/extensions/index.js.map +1 -1
  96. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  97. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  98. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  99. package/dist/core/model-runtime.d.ts +2 -0
  100. package/dist/core/model-runtime.d.ts.map +1 -1
  101. package/dist/core/model-runtime.js +4 -0
  102. package/dist/core/model-runtime.js.map +1 -1
  103. package/dist/core/runtime-credentials.d.ts +1 -0
  104. package/dist/core/runtime-credentials.d.ts.map +1 -1
  105. package/dist/core/runtime-credentials.js +8 -0
  106. package/dist/core/runtime-credentials.js.map +1 -1
  107. package/dist/core/sdk.d.ts.map +1 -1
  108. package/dist/core/sdk.js +5 -5
  109. package/dist/core/sdk.js.map +1 -1
  110. package/dist/core/session-manager-storage.d.ts +2 -0
  111. package/dist/core/session-manager-storage.d.ts.map +1 -1
  112. package/dist/core/session-manager-storage.js +4 -2
  113. package/dist/core/session-manager-storage.js.map +1 -1
  114. package/dist/core/slash-commands.js +1 -1
  115. package/dist/core/slash-commands.js.map +1 -1
  116. package/dist/core/workflow-stage-admission.d.ts +6 -1
  117. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  118. package/dist/core/workflow-stage-admission.js +8 -9
  119. package/dist/core/workflow-stage-admission.js.map +1 -1
  120. package/dist/index.d.ts +0 -1
  121. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  122. package/dist/modes/interactive/components/footer.js +1 -1
  123. package/dist/modes/interactive/components/footer.js.map +1 -1
  124. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  125. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  126. package/dist/modes/interactive/interactive-startup.js +1 -1
  127. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  128. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  129. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  130. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  131. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  132. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  133. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  134. package/dist/modes/rpc/rpc-client-api.js +33 -5
  135. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  136. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  137. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  138. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  139. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  140. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  141. package/dist/modes/rpc/rpc-types.js.map +1 -1
  142. package/docs/intercom.md +15 -6
  143. package/docs/packages.md +2 -2
  144. package/docs/providers.md +8 -3
  145. package/docs/quickstart.md +2 -2
  146. package/docs/rpc.md +21 -4
  147. package/docs/settings.md +6 -4
  148. package/docs/subagents.md +1 -1
  149. package/docs/usage.md +1 -1
  150. package/docs/workflows.md +108 -239
  151. package/npm-shrinkwrap.json +32 -32
  152. package/package.json +9 -13
  153. package/dist/builtin/workflows/ambient.d.ts +0 -61
  154. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  155. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  156. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  157. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  158. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  159. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  160. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  161. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  162. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  163. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  164. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  165. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  166. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  167. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  168. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  169. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  170. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  171. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -217
  172. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  173. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  174. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  175. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  176. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  177. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  178. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  179. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  180. package/dist/builtin/workflows/builtin/index.ts +0 -16
  181. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  182. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  183. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  184. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  185. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  186. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  187. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  188. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  189. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  190. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  191. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  192. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  193. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  194. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  195. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  196. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -104
  197. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -459
  198. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  199. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  200. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  201. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  202. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  203. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  204. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  205. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  206. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  207. 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) {
@@ -19160,6 +19167,7 @@ async function loadVirtualModules() {
19160
19167
  piAi,
19161
19168
  piAiOauth,
19162
19169
  piAiCloudflareGatewayBinding,
19170
+ properLockfile,
19163
19171
  piCodingAgent
19164
19172
  ] = await Promise.all([
19165
19173
  import("typebox"),
@@ -19171,6 +19179,7 @@ async function loadVirtualModules() {
19171
19179
  import("@bastani/pi-ai/compat"),
19172
19180
  import("@bastani/pi-ai/oauth"),
19173
19181
  import("@bastani/pi-ai/api/cloudflare-gateway-binding"),
19182
+ import("proper-lockfile"),
19174
19183
  Promise.resolve().then(() => (init_src(), exports_src))
19175
19184
  ]);
19176
19185
  return {
@@ -19191,6 +19200,7 @@ async function loadVirtualModules() {
19191
19200
  "@earendil-works/pi-ai/compat": piAi,
19192
19201
  "@earendil-works/pi-ai/oauth": piAiOauth,
19193
19202
  "@earendil-works/pi-ai/api/cloudflare-gateway-binding": piAiCloudflareGatewayBinding,
19203
+ "proper-lockfile": properLockfile,
19194
19204
  "@bastani/atomic": piCodingAgent,
19195
19205
  "@mariozechner/pi-agent-core": piAgentCore,
19196
19206
  "@mariozechner/pi-tui": piTui,
@@ -26320,11 +26330,20 @@ function isCodexFastModeCandidateModelId(modelId) {
26320
26330
  const provider = modelId?.split("/", 1)[0];
26321
26331
  return provider !== undefined && isCodexFastModeSupportedProvider(provider);
26322
26332
  }
26323
- function isCodexFastModeSupportedModel(model) {
26324
- return isCodexFastModeSupportedProvider(model.provider) || model.api === "openai-codex-responses";
26333
+ function isGitHubCopilotModel(model) {
26334
+ return model.provider === "github-copilot";
26325
26335
  }
26326
- function hasSupportedCodexFastModeModel(models) {
26327
- return models.some(isCodexFastModeSupportedModel);
26336
+ function isGitHubCopilotFastModeSupportedModel(model, credential) {
26337
+ if (!isGitHubCopilotModel(model) || credential?.type !== "oauth")
26338
+ return false;
26339
+ const fastModelIds = credential.fastModelIds;
26340
+ return Array.isArray(fastModelIds) && fastModelIds.every((modelId) => typeof modelId === "string") && fastModelIds.includes(`${model.id}-fast`);
26341
+ }
26342
+ function isCodexFastModeSupportedModel(model, copilotCredential) {
26343
+ return isCodexFastModeSupportedProvider(model.provider) || model.api === "openai-codex-responses" || model.id !== undefined && isGitHubCopilotFastModeSupportedModel({ provider: model.provider, id: model.id }, copilotCredential);
26344
+ }
26345
+ function hasSupportedCodexFastModeModel(models, copilotCredential) {
26346
+ return models.some((model) => isCodexFastModeSupportedModel(model, copilotCredential));
26328
26347
  }
26329
26348
  function isWorkflowStageOrchestrationContext(context) {
26330
26349
  return context?.kind === "workflow-stage";
@@ -26335,11 +26354,11 @@ function getCodexFastModeScope(context) {
26335
26354
  function isCodexFastModeEnabledForScope(settings, scope) {
26336
26355
  return settings[scope];
26337
26356
  }
26338
- function shouldApplyCodexFastModeForScope(model, settings, scope) {
26339
- return isCodexFastModeSupportedModel(model) && isCodexFastModeEnabledForScope(settings, scope);
26357
+ function shouldApplyCodexFastModeForScope(model, settings, scope, copilotCredential) {
26358
+ return isCodexFastModeSupportedModel(model, copilotCredential) && isCodexFastModeEnabledForScope(settings, scope);
26340
26359
  }
26341
- function shouldApplyCodexFastMode(model, settings, context) {
26342
- return shouldApplyCodexFastModeForScope(model, settings, getCodexFastModeScope(context));
26360
+ function shouldApplyCodexFastMode(model, settings, context, copilotCredential) {
26361
+ return shouldApplyCodexFastModeForScope(model, settings, getCodexFastModeScope(context), copilotCredential);
26343
26362
  }
26344
26363
  function usesFirstPartyCodexRouting(model) {
26345
26364
  return model.provider === "openai-codex" && isFirstPartyCodexBaseUrl(model.baseUrl);
@@ -26365,8 +26384,8 @@ function withCodexFastModeHeaders(model, headers, enabled) {
26365
26384
  setHeader(fastHeaders, CODEX_FAST_MODE_ROUTING_HEADER, `model=${model.id};tier=${CODEX_FAST_MODE_SERVICE_TIER}`);
26366
26385
  return fastHeaders;
26367
26386
  }
26368
- function withCodexFastModeStreamOptions(_model, options, enabled) {
26369
- if (!enabled) {
26387
+ function withCodexFastModeStreamOptions(model, options, enabled) {
26388
+ if (!enabled || model.provider === "github-copilot") {
26370
26389
  return options;
26371
26390
  }
26372
26391
  return {
@@ -26500,10 +26519,14 @@ function streamWithCodexFastMode(model, context, options, streamers = DEFAULT_CO
26500
26519
  function isObjectPayload(payload) {
26501
26520
  return typeof payload === "object" && payload !== null && !Array.isArray(payload);
26502
26521
  }
26503
- function withCodexFastModePayload(payload, enabled) {
26504
- if (!enabled || !isObjectPayload(payload) || payload.service_tier !== undefined) {
26522
+ function withCodexFastModePayload(payload, enabled, model) {
26523
+ if (!enabled || !isObjectPayload(payload))
26505
26524
  return payload;
26525
+ if (model?.provider === "github-copilot") {
26526
+ return payload.model === model.id ? { ...payload, model: `${model.id}-fast` } : payload;
26506
26527
  }
26528
+ if (payload.service_tier !== undefined)
26529
+ return payload;
26507
26530
  return {
26508
26531
  ...payload,
26509
26532
  service_tier: CODEX_FAST_MODE_SERVICE_TIER
@@ -26653,7 +26676,7 @@ class FooterComponent {
26653
26676
  pwd += ` • ${sessionName}`;
26654
26677
  const modelName = state.model?.id || "no-model";
26655
26678
  const fastModeSettings = this.session.settingsManager.getCodexFastModeSettings();
26656
- const fastModeEnabled = state.model ? shouldApplyCodexFastMode(state.model, fastModeSettings, this.session.orchestrationContext) : false;
26679
+ const fastModeEnabled = state.model ? shouldApplyCodexFastMode(state.model, fastModeSettings, this.session.orchestrationContext, this.session.modelRuntime.getCredentialSnapshot?.("github-copilot")) : false;
26657
26680
  let modelLabel2 = modelName;
26658
26681
  if (state.model?.reasoning) {
26659
26682
  const thinkingLevel = state.thinkingLevel || "off";
@@ -45421,14 +45444,18 @@ import { AsyncLocalStorage } from "node:async_hooks";
45421
45444
 
45422
45445
  class WorkflowStageAdmissionBoundary {
45423
45446
  open = true;
45424
- completed = new Set;
45447
+ completed;
45425
45448
  inFlight = new Map;
45426
45449
  pending = new Set;
45427
45450
  invocationContext = new AsyncLocalStorage;
45428
45451
  closePromise;
45429
45452
  drainAdmittedWork;
45430
- constructor(drainAdmittedWork = async () => {}) {
45431
- this.drainAdmittedWork = drainAdmittedWork;
45453
+ constructor(drainAdmittedWork = undefined, completedKeys = []) {
45454
+ this.drainAdmittedWork = drainAdmittedWork ?? (async () => {});
45455
+ this.completed = new Set(completedKeys);
45456
+ }
45457
+ static restore(entries, drainAdmittedWork) {
45458
+ return new WorkflowStageAdmissionBoundary(drainAdmittedWork, Array.from(entries).flatMap((entry) => entry.type === "custom_message" && typeof entry.stageAdmissionKey === "string" ? [entry.stageAdmissionKey] : []));
45432
45459
  }
45433
45460
  admit(key, deliver, routeLate) {
45434
45461
  if (key !== undefined) {
@@ -45667,7 +45694,7 @@ class AgentSessionBase {
45667
45694
  this._subagentPolicy = config.subagentPolicy;
45668
45695
  this._systemPromptTransform = config.systemPromptTransform;
45669
45696
  const stageContext = config.orchestrationContext?.kind === "workflow-stage" ? config.orchestrationContext : undefined;
45670
- this._workflowStageAdmission = stageContext?.messageAdmission?.boundary ?? (stageContext ? new WorkflowStageAdmissionBoundary : undefined);
45697
+ this._workflowStageAdmission = stageContext?.messageAdmission?.boundary ?? (stageContext ? WorkflowStageAdmissionBoundary.restore(this.sessionManager.getBranch()) : undefined);
45671
45698
  if (this._workflowStageAdmission && stageContext && stageContext.messageAdmission === undefined) {
45672
45699
  stageContext.messageAdmission = {
45673
45700
  boundary: this._workflowStageAdmission,
@@ -45999,6 +46026,9 @@ class AuthStorage {
45999
46026
  this.data = this.parseStorageData(content);
46000
46027
  } catch {}
46001
46028
  }
46029
+ peek(provider) {
46030
+ return this.data[provider];
46031
+ }
46002
46032
  async read(provider) {
46003
46033
  const credential = this.data[provider];
46004
46034
  if (credential?.type !== "api_key")
@@ -47052,6 +47082,10 @@ function getReloadableStore(store) {
47052
47082
  const reloadable = store;
47053
47083
  return typeof reloadable.reload === "function" ? reloadable : undefined;
47054
47084
  }
47085
+ function getSnapshotStore(store) {
47086
+ const snapshotStore = store;
47087
+ return typeof snapshotStore.peek === "function" ? snapshotStore : undefined;
47088
+ }
47055
47089
 
47056
47090
  class RuntimeCredentials {
47057
47091
  store;
@@ -47071,6 +47105,10 @@ class RuntimeCredentials {
47071
47105
  async reload() {
47072
47106
  await getReloadableStore(this.store)?.reload();
47073
47107
  }
47108
+ peek(providerId) {
47109
+ const override = this.overrides.get(providerId);
47110
+ return override ? { type: "api_key", key: override } : getSnapshotStore(this.store)?.peek(providerId);
47111
+ }
47074
47112
  async read(providerId, options) {
47075
47113
  const override = this.overrides.get(providerId);
47076
47114
  return override ? { type: "api_key", key: override } : this.store.read(providerId, options);
@@ -47506,6 +47544,9 @@ class ModelRuntime {
47506
47544
  hasConfiguredAuth(providerId) {
47507
47545
  return this.snapshot.configuredProviders.has(providerId);
47508
47546
  }
47547
+ getCredentialSnapshot(providerId) {
47548
+ return this.credentials.peek(providerId);
47549
+ }
47509
47550
  async getAuth(providerOrModel, overrides = {}) {
47510
47551
  if (typeof providerOrModel === "string")
47511
47552
  return this.models.getAuth(providerOrModel, overrides);
@@ -55461,7 +55502,7 @@ async function createAgentSession(options = {}) {
55461
55502
  return filtered;
55462
55503
  };
55463
55504
  const extensionRunnerRef = {};
55464
- const isCodexFastModeEnabled = (requestModel) => shouldApplyCodexFastMode(requestModel, settingsManager.getCodexFastModeSettings(), options.orchestrationContext);
55505
+ const isCodexFastModeEnabled = (requestModel) => shouldApplyCodexFastMode(requestModel, settingsManager.getCodexFastModeSettings(), options.orchestrationContext, modelRuntime.getCredentialSnapshot("github-copilot"));
55465
55506
  agent = new Agent2({
55466
55507
  initialState: {
55467
55508
  systemPrompt: "",
@@ -55497,7 +55538,7 @@ async function createAgentSession(options = {}) {
55497
55538
  const extensionProvider = modelRuntime.getRegisteredProviderConfig(requestModel.provider);
55498
55539
  const usesExtensionStream = extensionProvider?.streamSimple !== undefined && requestModel.api === extensionProvider.api;
55499
55540
  const transportHeaders = usesExtensionStream ? assembledHeaders : removeUnownedModelHeaders(assembledHeaders, model2.headers, requestHeaders);
55500
- if (fastModeEnabled && !usesExtensionStream && !authResult) {
55541
+ if (fastModeEnabled && !isGitHubCopilotModel(model2) && !usesExtensionStream && !authResult) {
55501
55542
  throw new Error(`No API key found for "${model2.provider}"`);
55502
55543
  }
55503
55544
  const codexFastModeStreamOptions = withCodexFastModeStreamOptions(requestModel, {
@@ -55514,7 +55555,7 @@ async function createAgentSession(options = {}) {
55514
55555
  if (usesExtensionStream) {
55515
55556
  return modelRuntime.streamSimple(requestModel, context, codexFastModeStreamOptions);
55516
55557
  }
55517
- if (fastModeEnabled) {
55558
+ if (fastModeEnabled && !isGitHubCopilotModel(requestModel)) {
55518
55559
  return streamWithCodexFastMode(requestModel, context, codexFastModeStreamOptions);
55519
55560
  }
55520
55561
  const transportOptions = usesChatGptCodexTransport(requestModel) && codexFastModeStreamOptions ? withChatGptCodexTransportRouting(requestModel, codexFastModeStreamOptions, false) : codexFastModeStreamOptions;
@@ -55522,7 +55563,7 @@ async function createAgentSession(options = {}) {
55522
55563
  },
55523
55564
  onPayload: async (payload, model2) => {
55524
55565
  const fastModeEnabled = isCodexFastModeEnabled(model2);
55525
- const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled);
55566
+ const guardedPayload = withCodexFastModePayload(payload, fastModeEnabled, model2);
55526
55567
  const sourceMessages = lastConvertedLlmMessages;
55527
55568
  const replayGuardedPayload = sourceMessages ? restoreAnthropicReplayThinkingBlocks(guardedPayload, sourceMessages, model2) : guardedPayload;
55528
55569
  const runner = extensionRunnerRef.current;
@@ -59987,6 +60028,31 @@ class RemoteQueuePause {
59987
60028
  var init_remote_queue_pause = () => {};
59988
60029
 
59989
60030
  // src/modes/interactive-engine/isolated-runtime.ts
60031
+ import { clampThinkingLevel as clampThinkingLevel5 } from "@bastani/pi-ai/compat";
60032
+ function applyPersistedModelDefault(session, model, alreadyInScope) {
60033
+ session.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
60034
+ if (alreadyInScope || session.scopedModels.length === 0)
60035
+ return;
60036
+ const enabledModels = session.settingsManager.getEnabledModels();
60037
+ if (!enabledModels?.length)
60038
+ return;
60039
+ const modelReference = `${model.provider}/${model.id}`;
60040
+ if (enabledModels.some((pattern) => pattern.toLowerCase() === modelReference.toLowerCase()))
60041
+ return;
60042
+ session.settingsManager.setEnabledModels([...enabledModels, modelReference]);
60043
+ }
60044
+ function thinkingPersistTarget(result) {
60045
+ if (!result.provider || !result.modelId)
60046
+ return;
60047
+ return { provider: result.provider, modelId: result.modelId };
60048
+ }
60049
+ function applyPersistedThinkingDefault(session, level, target) {
60050
+ if (target) {
60051
+ session.settingsManager.setModelThinkingLevel(target.provider, target.modelId, level);
60052
+ return;
60053
+ }
60054
+ session.settingsManager.setDefaultThinkingLevel(level);
60055
+ }
59990
60056
  var IsolatedInteractiveRuntime;
59991
60057
  var init_isolated_runtime = __esm(() => {
59992
60058
  init_agent_session_runtime();
@@ -60005,6 +60071,7 @@ var init_isolated_runtime = __esm(() => {
60005
60071
  steeringMessages = [];
60006
60072
  followUpMessages = [];
60007
60073
  queueUpdateGeneration = 0;
60074
+ thinkingEpoch = 0;
60008
60075
  pendingQueueClear;
60009
60076
  engineCallbackActive = false;
60010
60077
  queuePause;
@@ -60369,43 +60436,56 @@ var init_isolated_runtime = __esm(() => {
60369
60436
  },
60370
60437
  setModel: {
60371
60438
  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;
60439
+ value: async (model, options) => {
60440
+ const selected = await this.client.setModel(model.provider, model.id, options);
60441
+ const nextModel = session.modelRuntime.getModel(selected.provider, selected.id) ?? model;
60442
+ session.agent.state.model = nextModel;
60375
60443
  this.resolveModelFallback();
60444
+ if (options?.persist === true)
60445
+ await this.syncPersistedModelCatalog(session, nextModel);
60376
60446
  }
60377
60447
  },
60378
60448
  setThinkingLevel: {
60379
60449
  configurable: true,
60380
- value: (level) => {
60381
- session.agent.state.thinkingLevel = level;
60382
- this.dispatchBestEffort("set thinking level", this.client.setThinkingLevel(level));
60450
+ value: (level, options) => {
60451
+ const availableLevels = session.getAvailableThinkingLevels();
60452
+ const effectiveLevel = availableLevels.includes(level) ? level : session.model ? clampThinkingLevel5(session.model, level) : "off";
60453
+ session.agent.state.thinkingLevel = effectiveLevel;
60454
+ const epoch = ++this.thinkingEpoch;
60455
+ this.dispatchBestEffort("set thinking level", this.client.setThinkingLevelAck(level, options).then((result) => {
60456
+ this.applyThinkingAck(session, epoch, result, options?.persist === true);
60457
+ }));
60383
60458
  }
60384
60459
  },
60385
60460
  cycleModel: {
60386
60461
  configurable: true,
60387
- value: async (direction) => {
60462
+ value: async (direction, options) => {
60388
60463
  const previousModel = session.model;
60389
- const result = await this.client.cycleModel(direction);
60464
+ const result = await this.client.cycleModel(direction, options);
60390
60465
  if (!result)
60391
60466
  return;
60392
60467
  const model = session.modelRuntime.getModel(result.model.provider, result.model.id) ?? result.model;
60393
60468
  session.agent.state.model = model;
60394
60469
  session.agent.state.thinkingLevel = result.thinkingLevel;
60395
60470
  this.resolveModelFallbackAfterExplicitModelSelection(previousModel, model);
60471
+ if (options?.persist === true)
60472
+ await this.syncPersistedModelCatalog(session, model);
60396
60473
  return { ...result, model };
60397
60474
  }
60398
60475
  },
60399
60476
  cycleThinkingLevel: {
60400
60477
  configurable: true,
60401
- value: () => {
60478
+ value: (options) => {
60402
60479
  const levels = session.getAvailableThinkingLevels();
60403
60480
  if (levels.length <= 1)
60404
60481
  return;
60405
60482
  const current = levels.indexOf(session.thinkingLevel);
60406
60483
  const level = levels[(current + 1) % levels.length];
60407
60484
  session.agent.state.thinkingLevel = level;
60408
- this.dispatchBestEffort("cycle thinking level", this.client.setThinkingLevel(level));
60485
+ const epoch = ++this.thinkingEpoch;
60486
+ this.dispatchBestEffort("cycle thinking level", this.client.setThinkingLevelAck(level, options).then((result) => {
60487
+ this.applyThinkingAck(session, epoch, result, options?.persist === true);
60488
+ }));
60409
60489
  return level;
60410
60490
  }
60411
60491
  },
@@ -60449,6 +60529,14 @@ var init_isolated_runtime = __esm(() => {
60449
60529
  this.engineCallbackActive = false;
60450
60530
  }
60451
60531
  }
60532
+ applyThinkingAck(session, epoch, result, persist) {
60533
+ if (persist) {
60534
+ applyPersistedThinkingDefault(session, result.level, thinkingPersistTarget(result));
60535
+ }
60536
+ if (epoch === this.thinkingEpoch) {
60537
+ session.agent.state.thinkingLevel = result.level;
60538
+ }
60539
+ }
60452
60540
  dispatchBestEffort(label, operation) {
60453
60541
  operation.catch((error) => {
60454
60542
  if (this.health.isRecovering())
@@ -60489,6 +60577,12 @@ var init_isolated_runtime = __esm(() => {
60489
60577
  }
60490
60578
  });
60491
60579
  }
60580
+ async syncPersistedModelCatalog(session, model) {
60581
+ const alreadyInScope = session.scopedModels.some((scoped) => scoped.model.provider === model.provider && scoped.model.id === model.id);
60582
+ const catalog = await this.client.requestInternal({ type: "get_available_models" });
60583
+ this.remoteModelCatalog.apply(catalog);
60584
+ applyPersistedModelDefault(session, model, alreadyInScope);
60585
+ }
60492
60586
  refreshSessionView() {
60493
60587
  const session = super.session;
60494
60588
  const sessionFile = session.sessionFile;
@@ -60528,6 +60622,7 @@ var init_isolated_runtime = __esm(() => {
60528
60622
  session.agent.state.model = event.model;
60529
60623
  break;
60530
60624
  case "thinking_level_changed":
60625
+ this.thinkingEpoch += 1;
60531
60626
  session.agent.state.thinkingLevel = event.level;
60532
60627
  break;
60533
60628
  case "session_info_changed":
@@ -61741,7 +61836,7 @@ var init_slash_commands = __esm(() => {
61741
61836
  { name: "tree", description: "Navigate session tree (switch branches)" },
61742
61837
  { name: "thinking", description: "Set thinking level", argumentHint: "<level>" },
61743
61838
  { name: "scoped-models", description: "Enable/disable models for ctrl+p cycling" },
61744
- { name: "fast", description: "Configure Codex fast mode for chat and workflows" },
61839
+ { name: "fast", description: "Configure OpenAI fast mode for chat and workflows in supported models" },
61745
61840
  { name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },
61746
61841
  { name: "import", description: "Import and resume a session from a JSONL file" },
61747
61842
  { name: "share", description: "Share session as a secret GitHub gist" },
@@ -63203,7 +63298,7 @@ var init_interactive_autocomplete = __esm(() => {
63203
63298
  return [...this.session.modelRuntime.getAvailableSnapshot()];
63204
63299
  };
63205
63300
  InteractiveModeBase.prototype.hasCodexFastModeSupportedModels = function() {
63206
- return hasSupportedCodexFastModeModel(this.getCodexFastModeCandidateModels());
63301
+ return hasSupportedCodexFastModeModel(this.getCodexFastModeCandidateModels(), this.session.modelRuntime.getCredentialSnapshot?.("github-copilot"));
63207
63302
  };
63208
63303
  InteractiveModeBase.prototype.buildRemoteSlashCommands = function(localCommands) {
63209
63304
  const remote = getInteractiveEngineRemoteCommands(this.runtimeHost);
@@ -63891,7 +63986,7 @@ var init_interactive_startup = __esm(() => {
63891
63986
  if (!model) {
63892
63987
  return modelLabel2;
63893
63988
  }
63894
- const fastModeEnabled = shouldApplyCodexFastMode(model, this.session.settingsManager.getCodexFastModeSettings(), this.session.orchestrationContext);
63989
+ const fastModeEnabled = shouldApplyCodexFastMode(model, this.session.settingsManager.getCodexFastModeSettings(), this.session.orchestrationContext, this.session.modelRuntime.getCredentialSnapshot?.("github-copilot"));
63895
63990
  return formatCodexFastModeModelLabel(modelLabel2, fastModeEnabled);
63896
63991
  };
63897
63992
  InteractiveModeBase.prototype.getStartupIdentityText = function(maxWidth, gap = 0, manifestoPhase = 4) {
@@ -68406,7 +68501,7 @@ var init_interactive_selectors = __esm(() => {
68406
68501
  });
68407
68502
 
68408
68503
  // src/modes/interactive/interactive-model-routing.ts
68409
- import { clampThinkingLevel as clampThinkingLevel5 } from "@bastani/pi-ai/compat";
68504
+ import { clampThinkingLevel as clampThinkingLevel6 } from "@bastani/pi-ai/compat";
68410
68505
  function resolveThinkingSelectorDefault(rawDefault, availableLevels, model) {
68411
68506
  if (rawDefault === undefined)
68412
68507
  return;
@@ -68414,7 +68509,7 @@ function resolveThinkingSelectorDefault(rawDefault, availableLevels, model) {
68414
68509
  return rawDefault;
68415
68510
  if (!model)
68416
68511
  return availableLevels.includes("off") ? "off" : availableLevels[0];
68417
- const clamped = clampThinkingLevel5(model, rawDefault);
68512
+ const clamped = clampThinkingLevel6(model, rawDefault);
68418
68513
  if (availableLevels.includes(clamped))
68419
68514
  return clamped;
68420
68515
  return availableLevels.includes("off") ? "off" : availableLevels[0];
@@ -70778,11 +70873,20 @@ class RpcClientApi {
70778
70873
  async getState() {
70779
70874
  return this.data(await this.request({ type: "get_state" }));
70780
70875
  }
70781
- async setModel(provider, modelId) {
70782
- return this.data(await this.request({ type: "set_model", provider, modelId }));
70876
+ async setModel(provider, modelId, options) {
70877
+ return this.data(await this.request({
70878
+ type: "set_model",
70879
+ provider,
70880
+ modelId,
70881
+ ...options?.persist === true ? { persist: true } : {}
70882
+ }));
70783
70883
  }
70784
- async cycleModel(direction) {
70785
- return this.data(await this.request({ type: "cycle_model", direction }));
70884
+ async cycleModel(direction, options) {
70885
+ return this.data(await this.request({
70886
+ type: "cycle_model",
70887
+ direction,
70888
+ ...options?.persist === true ? { persist: true } : {}
70889
+ }));
70786
70890
  }
70787
70891
  async getAvailableModels() {
70788
70892
  return this.data(await this.request({ type: "get_available_models" })).models;
@@ -70816,6 +70920,20 @@ class RpcClientApi {
70816
70920
  async setThinkingLevel(level) {
70817
70921
  await this.request({ type: "set_thinking_level", level });
70818
70922
  }
70923
+ async setThinkingLevelAck(level, options) {
70924
+ const response = await this.request({
70925
+ type: "set_thinking_level",
70926
+ level,
70927
+ ...options?.persist === true ? { persist: true } : {}
70928
+ });
70929
+ if (!response.success) {
70930
+ return this.data(response);
70931
+ }
70932
+ if (response.command === "set_thinking_level" && "data" in response && response.data) {
70933
+ return response.data;
70934
+ }
70935
+ return { level };
70936
+ }
70819
70937
  async cycleThinkingLevel() {
70820
70938
  return this.data(await this.request({ type: "cycle_thinking_level" }));
70821
70939
  }
@@ -72834,13 +72952,13 @@ function createRpcCommandHandler({
72834
72952
  if (!model) {
72835
72953
  return createRpcErrorResponse(id, "set_model", `Model not found: ${command.provider}/${command.modelId}`);
72836
72954
  }
72837
- await session.setModel(model);
72955
+ await session.setModel(model, command.persist === true ? { persist: true } : {});
72838
72956
  runtimeHost.resolveModelFallback();
72839
72957
  return createRpcSuccessResponse(id, "set_model", session.model ?? model);
72840
72958
  }
72841
72959
  case "cycle_model": {
72842
72960
  const previousModel = session.model;
72843
- const result = await session.cycleModel(command.direction);
72961
+ const result = await session.cycleModel(command.direction, command.persist === true ? { persist: true } : {});
72844
72962
  runtimeHost.resolveModelFallbackAfterExplicitModelSelection(previousModel, result?.model);
72845
72963
  return createRpcSuccessResponse(id, "cycle_model", result ?? null);
72846
72964
  }
@@ -72903,8 +73021,12 @@ function createRpcCommandHandler({
72903
73021
  }
72904
73022
  }
72905
73023
  case "set_thinking_level": {
72906
- session.setThinkingLevel(command.level);
72907
- return createRpcSuccessResponse(id, "set_thinking_level");
73024
+ session.setThinkingLevel(command.level, command.persist === true ? { persist: true } : {});
73025
+ const model = session.model;
73026
+ return createRpcSuccessResponse(id, "set_thinking_level", {
73027
+ level: session.thinkingLevel,
73028
+ ...model ? { provider: model.provider, modelId: model.id } : {}
73029
+ });
72908
73030
  }
72909
73031
  case "cycle_thinking_level": {
72910
73032
  const level = session.cycleThinkingLevel();
@@ -76717,6 +76839,152 @@ function subscribeIntercomControl(pi, callbacks) {
76717
76839
  };
76718
76840
  }
76719
76841
 
76842
+ // dist/builtin/workflows/src/shared/pending-stage-delivery.ts
76843
+ var PENDING_STAGE_MESSAGE_LIMIT = 50;
76844
+ var PENDING_STAGE_UNDELIVERABLE_NOTIFICATION_PREFIX = "atomic-pending-stage-undeliverable";
76845
+ function queueStageMessage(messages2, input2, senderGroup, runGroup, stageIdentity) {
76846
+ if (normalizeDeliveryGroup(senderGroup) !== normalizeDeliveryGroup(runGroup)) {
76847
+ return { ok: false, reason: "group_mismatch", runId: input2.runId, stageKey: input2.stageKey };
76848
+ }
76849
+ const messageId = input2.message.id;
76850
+ const queued = pendingStageMessagesFor(messages2, input2.runId, input2.stageKey, stageIdentity);
76851
+ const existing = messages2.find((entry2) => entry2.runId === input2.runId && entry2.id === messageId);
76852
+ if (existing !== undefined) {
76853
+ if (pendingStageMessageSignature(existing, stageIdentity) !== pendingStageMessageSignature(input2, stageIdentity)) {
76854
+ return {
76855
+ ok: false,
76856
+ reason: "message_id_conflict",
76857
+ runId: input2.runId,
76858
+ stageKey: input2.stageKey,
76859
+ messageId
76860
+ };
76861
+ }
76862
+ const queuedPosition = queued.indexOf(existing);
76863
+ return {
76864
+ ok: true,
76865
+ messages: messages2,
76866
+ entry: existing,
76867
+ ...queuedPosition >= 0 ? { position: queuedPosition + 1 } : {},
76868
+ deduplicated: true
76869
+ };
76870
+ }
76871
+ if (queued.length >= PENDING_STAGE_MESSAGE_LIMIT) {
76872
+ return {
76873
+ ok: false,
76874
+ reason: "capacity",
76875
+ limit: PENDING_STAGE_MESSAGE_LIMIT,
76876
+ runId: input2.runId,
76877
+ stageKey: input2.stageKey
76878
+ };
76879
+ }
76880
+ const entry = {
76881
+ ...input2,
76882
+ id: messageId,
76883
+ ...stageIdentity !== undefined ? { stageId: stageIdentity.id } : {},
76884
+ ...stageIdentity?.replayKey !== undefined ? { stageReplayKey: stageIdentity.replayKey } : {},
76885
+ admissionOrder: nextAdmissionOrder(messages2, input2.runId),
76886
+ status: "queued"
76887
+ };
76888
+ return {
76889
+ ok: true,
76890
+ messages: [...messages2, entry],
76891
+ entry,
76892
+ position: queued.length + 1,
76893
+ deduplicated: false
76894
+ };
76895
+ }
76896
+ function pendingStageMessagesFor(messages2, runId, stageKey, stageIdentity) {
76897
+ 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);
76898
+ }
76899
+ function markPendingStageMessageDelivered(messages2, runId, stageKey, messageId, deliveredAt, stageIdentity) {
76900
+ return updateQueuedPendingStageMessage(messages2, runId, stageKey, messageId, stageIdentity, (entry) => ({
76901
+ ...entry,
76902
+ status: "delivered",
76903
+ deliveredAt
76904
+ }));
76905
+ }
76906
+ function markPendingStageMessageUndeliverable(messages2, runId, stageKey, messageId, reason, stageIdentity) {
76907
+ return updateQueuedPendingStageMessage(messages2, runId, stageKey, messageId, stageIdentity, (entry) => ({
76908
+ ...entry,
76909
+ status: "undeliverable",
76910
+ undeliverableReason: reason,
76911
+ undeliverableNotificationId: pendingStageUndeliverableNotificationId(entry)
76912
+ }));
76913
+ }
76914
+ function markPendingStageMessageUndeliverableNotified(messages2, runId, stageKey, messageId, notificationId, notifiedAt) {
76915
+ const index = messages2.findIndex((entry) => matchesPendingStage(entry, runId, stageKey) && entry.id === messageId && entry.status === "undeliverable" && entry.undeliverableNotificationId === notificationId && entry.undeliverableNotifiedAt === undefined);
76916
+ if (index < 0)
76917
+ return messages2;
76918
+ const next = [...messages2];
76919
+ next[index] = { ...next[index], undeliverableNotifiedAt: notifiedAt };
76920
+ return next;
76921
+ }
76922
+ function pendingStageUndeliverableNotificationId(entry) {
76923
+ return [
76924
+ PENDING_STAGE_UNDELIVERABLE_NOTIFICATION_PREFIX,
76925
+ encodeURIComponent(entry.runId),
76926
+ encodeURIComponent(entry.stageKey),
76927
+ encodeURIComponent(entry.id)
76928
+ ].join(":");
76929
+ }
76930
+ function updateQueuedPendingStageMessage(messages2, runId, stageKey, messageId, stageIdentity, update2) {
76931
+ const index = messages2.findIndex((entry) => matchesPendingStage(entry, runId, stageKey, stageIdentity) && entry.id === messageId && entry.status === "queued");
76932
+ if (index < 0)
76933
+ return messages2;
76934
+ const next = [...messages2];
76935
+ next[index] = update2(next[index]);
76936
+ return next;
76937
+ }
76938
+ function matchesPendingStage(entry, runId, stageKey, stageIdentity) {
76939
+ if (entry.runId !== runId)
76940
+ return false;
76941
+ if (stageIdentity === undefined)
76942
+ return entry.stageKey === stageKey;
76943
+ return entry.stageId === stageIdentity.id || entry.stageReplayKey !== undefined && entry.stageReplayKey === stageIdentity.replayKey || entry.stageId === undefined && stageIdentity.aliases.includes(entry.stageKey);
76944
+ }
76945
+ function nextAdmissionOrder(messages2, runId) {
76946
+ let greatest = 0;
76947
+ let legacyPosition = 0;
76948
+ for (const entry of messages2) {
76949
+ if (entry.runId !== runId)
76950
+ continue;
76951
+ legacyPosition += 1;
76952
+ greatest = Math.max(greatest, entry.admissionOrder ?? legacyPosition);
76953
+ }
76954
+ return greatest + 1;
76955
+ }
76956
+ function pendingStageMessageSignature(entry, stageIdentity) {
76957
+ const storedTarget = "status" in entry ? entry.stageReplayKey ?? entry.stageId : undefined;
76958
+ const { timestamp: transportTimestamp, ...logicalMessage } = entry.message;
76959
+ return stableJson({
76960
+ target: storedTarget ?? stageIdentity?.replayKey ?? stageIdentity?.id ?? entry.stageKey,
76961
+ sender: entry.senderReturnAddress ?? entry.from.id,
76962
+ message: {
76963
+ ...logicalMessage,
76964
+ expectsReply: logicalMessage.expectsReply ?? false,
76965
+ content: {
76966
+ ...logicalMessage.content,
76967
+ attachments: logicalMessage.content.attachments ?? []
76968
+ }
76969
+ }
76970
+ });
76971
+ }
76972
+ function stableJson(value) {
76973
+ return JSON.stringify(sortJsonValue(value));
76974
+ }
76975
+ function sortJsonValue(value) {
76976
+ if (Array.isArray(value))
76977
+ return value.map(sortJsonValue);
76978
+ if (typeof value !== "object" || value === null)
76979
+ return value;
76980
+ return Object.fromEntries(Object.entries(value).filter(([, nested]) => nested !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortJsonValue(nested)]));
76981
+ }
76982
+ function normalizeDeliveryGroup(value) {
76983
+ if (typeof value !== "string")
76984
+ return "default";
76985
+ const trimmed = value.trim();
76986
+ return trimmed.length > 0 ? trimmed : "default";
76987
+ }
76720
76988
  // dist/builtin/workflows/src/shared/session-scoped-singleton.ts
76721
76989
  init_src();
76722
76990
  var SHARED_STATES_KEY = Symbol.for("atomic-workflows/session-scoped-singleton-pre-adoption@1");
@@ -77229,6 +77497,7 @@ function compactStage(stage) {
77229
77497
  inputRequest,
77230
77498
  notices,
77231
77499
  mcpScope: _mcpScope,
77500
+ pendingStageDeliveryAvailable: _pendingStageDeliveryAvailable,
77232
77501
  attemptedModels: _attemptedModels,
77233
77502
  modelAttempts: _modelAttempts,
77234
77503
  result: _result,
@@ -77484,6 +77753,133 @@ function createStoreContext(state = createStoreState()) {
77484
77753
  };
77485
77754
  }
77486
77755
 
77756
+ // dist/builtin/workflows/src/shared/store-pending-stage-delivery-methods.ts
77757
+ function createPendingStageDeliveryStoreMethods(context) {
77758
+ const transitions = new Map;
77759
+ const serialize = async (runId, transition) => {
77760
+ const previous2 = transitions.get(runId) ?? Promise.resolve();
77761
+ const result = previous2.catch(() => {
77762
+ return;
77763
+ }).then(transition);
77764
+ const settled = result.then(() => {
77765
+ return;
77766
+ }, () => {
77767
+ return;
77768
+ });
77769
+ transitions.set(runId, settled);
77770
+ settled.finally(() => {
77771
+ if (transitions.get(runId) === settled)
77772
+ transitions.delete(runId);
77773
+ });
77774
+ return await result;
77775
+ };
77776
+ return {
77777
+ async queueStageMessage(input2, senderGroup, runGroup, backend) {
77778
+ return await serialize(input2.runId, async () => {
77779
+ const run = context.findRun(input2.runId);
77780
+ if (run === undefined)
77781
+ return;
77782
+ const stageIdentity = resolvePendingStageIdentity(run, input2.stageKey);
77783
+ const result = queueStageMessage(run.pendingStageMessages ?? [], input2, senderGroup, runGroup, stageIdentity);
77784
+ if (result.ok && !result.deduplicated) {
77785
+ await persistTransition(backend, input2.runId, result.messages);
77786
+ run.pendingStageMessages = [...result.messages];
77787
+ context.bumpAndNotify();
77788
+ }
77789
+ return result;
77790
+ });
77791
+ },
77792
+ async validateLiveStageMessage(input2) {
77793
+ return await serialize(input2.runId, async () => {
77794
+ const run = context.findRun(input2.runId);
77795
+ if (run === undefined)
77796
+ return;
77797
+ const result = queueStageMessage(run.pendingStageMessages ?? [], input2, undefined, undefined, resolvePendingStageIdentity(run, input2.stageKey));
77798
+ if (!result.ok) {
77799
+ return result.reason === "message_id_conflict" ? { outcome: "message_id_conflict", messageId: result.messageId } : { outcome: "forward" };
77800
+ }
77801
+ if (!result.deduplicated)
77802
+ return { outcome: "forward" };
77803
+ if (result.entry.status === "delivered")
77804
+ return { outcome: "delivered" };
77805
+ if (result.entry.status === "undeliverable") {
77806
+ return { outcome: "undeliverable", reason: result.entry.undeliverableReason };
77807
+ }
77808
+ if (result.position === undefined) {
77809
+ throw new Error(`atomic-workflows: queued message ${input2.message.id} has no active position`);
77810
+ }
77811
+ return { outcome: "queued", position: result.position };
77812
+ });
77813
+ },
77814
+ pendingStageMessagesFor(runId, stageKey) {
77815
+ const run = context.findRun(runId);
77816
+ return pendingStageMessagesFor(run?.pendingStageMessages ?? [], runId, stageKey, run === undefined ? undefined : resolvePendingStageIdentity(run, stageKey));
77817
+ },
77818
+ async markPendingStageMessageDelivered(runId, stageKey, messageId, deliveredAt, backend) {
77819
+ return await serialize(runId, async () => {
77820
+ const run = context.findRun(runId);
77821
+ if (run === undefined)
77822
+ return false;
77823
+ const current = run.pendingStageMessages ?? [];
77824
+ const next = markPendingStageMessageDelivered(current, runId, stageKey, messageId, deliveredAt, resolvePendingStageIdentity(run, stageKey));
77825
+ if (next === current)
77826
+ return false;
77827
+ await persistTransition(backend, runId, next);
77828
+ run.pendingStageMessages = [...next];
77829
+ context.bumpAndNotify();
77830
+ return true;
77831
+ });
77832
+ },
77833
+ async markPendingStageMessageUndeliverable(runId, stageKey, messageId, reason, backend) {
77834
+ return await serialize(runId, async () => {
77835
+ const run = context.findRun(runId);
77836
+ if (run === undefined)
77837
+ return false;
77838
+ const current = run.pendingStageMessages ?? [];
77839
+ const next = markPendingStageMessageUndeliverable(current, runId, stageKey, messageId, reason, resolvePendingStageIdentity(run, stageKey));
77840
+ if (next === current)
77841
+ return false;
77842
+ await persistTransition(backend, runId, next);
77843
+ run.pendingStageMessages = [...next];
77844
+ context.bumpAndNotify();
77845
+ return true;
77846
+ });
77847
+ },
77848
+ async markPendingStageMessageUndeliverableNotified(runId, stageKey, messageId, notificationId, notifiedAt, backend) {
77849
+ return await serialize(runId, async () => {
77850
+ const run = context.findRun(runId);
77851
+ if (run === undefined)
77852
+ return false;
77853
+ const current = run.pendingStageMessages ?? [];
77854
+ const next = markPendingStageMessageUndeliverableNotified(current, runId, stageKey, messageId, notificationId, notifiedAt);
77855
+ if (next === current)
77856
+ return false;
77857
+ await persistTransition(backend, runId, next);
77858
+ run.pendingStageMessages = [...next];
77859
+ context.bumpAndNotify();
77860
+ return true;
77861
+ });
77862
+ }
77863
+ };
77864
+ }
77865
+ function resolvePendingStageIdentity(run, stageKey) {
77866
+ const exactIds = run.stages.filter((stage2) => stage2.id === stageKey);
77867
+ const candidates = exactIds.length > 0 ? exactIds : run.stages.filter((stage2) => stage2.name === stageKey);
77868
+ if (candidates.length !== 1)
77869
+ return;
77870
+ const stage = candidates[0];
77871
+ return {
77872
+ id: stage.id,
77873
+ ...stage.replayKey !== undefined ? { replayKey: stage.replayKey } : {},
77874
+ aliases: stage.id === stage.name ? [stage.id] : [stage.id, stage.name]
77875
+ };
77876
+ }
77877
+ async function persistTransition(backend, runId, messages2) {
77878
+ if (!await backend.persistPendingStageMessages(runId, messages2)) {
77879
+ throw new Error(`atomic-workflows: durable workflow ${runId} is unavailable for pending-stage persistence`);
77880
+ }
77881
+ }
77882
+
77487
77883
  // dist/builtin/workflows/src/shared/store-prompt-methods.ts
77488
77884
  function createPromptStoreMethods(context) {
77489
77885
  const { state } = context;
@@ -78401,6 +78797,7 @@ function createStore() {
78401
78797
  const context = createStoreContext();
78402
78798
  return {
78403
78799
  ...createRunStoreMethods(context),
78800
+ ...createPendingStageDeliveryStoreMethods(context),
78404
78801
  ...createStageStoreMethods(context),
78405
78802
  ...createToolNodeStoreMethods(context),
78406
78803
  ...createPromptStoreMethods(context)
@@ -90556,6 +90953,26 @@ function canonicalJsonString(value) {
90556
90953
  }
90557
90954
 
90558
90955
  // dist/builtin/workflows/src/durable/backend.ts
90956
+ function replacePendingStageMessagesForRun(existing, logicalRunId, messages2) {
90957
+ if (messages2.some((entry) => entry.runId !== logicalRunId)) {
90958
+ throw new Error(`atomic-workflows: pending-stage persistence mixed logical run ${logicalRunId}`);
90959
+ }
90960
+ const firstOwnedIndex = existing.findIndex((entry) => entry.runId === logicalRunId);
90961
+ if (firstOwnedIndex < 0)
90962
+ return [...existing, ...messages2];
90963
+ const merged = [];
90964
+ for (let index = 0;index < existing.length; index += 1) {
90965
+ const entry = existing[index];
90966
+ if (index === firstOwnedIndex)
90967
+ merged.push(...messages2);
90968
+ if (entry.runId !== logicalRunId)
90969
+ merged.push(entry);
90970
+ }
90971
+ return merged;
90972
+ }
90973
+ function pendingStageMessagesForDurableRun(backend, logicalRunId, rootWorkflowId = logicalRunId) {
90974
+ return (backend.getWorkflow(rootWorkflowId)?.pendingStageMessages ?? []).filter((entry) => entry.runId === logicalRunId);
90975
+ }
90559
90976
  function checkpointKey(c2) {
90560
90977
  return `${c2.kind}:${c2.checkpointId}`;
90561
90978
  }
@@ -90582,6 +90999,7 @@ class InMemoryDurableBackend {
90582
90999
  ...handle.workflowCwd !== undefined ? { workflowCwd: handle.workflowCwd } : existing?.handle.workflowCwd !== undefined ? { workflowCwd: existing.handle.workflowCwd } : {},
90583
91000
  ...handle.repositoryRoot !== undefined ? { repositoryRoot: handle.repositoryRoot } : existing?.handle.repositoryRoot !== undefined ? { repositoryRoot: existing.handle.repositoryRoot } : {},
90584
91001
  ...handle.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: handle.gitWorktreeRoot } : existing?.handle.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: existing.handle.gitWorktreeRoot } : {},
91002
+ pendingStageMessages: handle.pendingStageMessages ?? existing?.handle.pendingStageMessages ?? [],
90585
91003
  ...handle.sessionFile !== undefined ? { sessionFile: handle.sessionFile } : {},
90586
91004
  completedCheckpoints,
90587
91005
  pendingPrompts,
@@ -90606,6 +91024,17 @@ class InMemoryDurableBackend {
90606
91024
  if (handle.pendingPrompts !== undefined)
90607
91025
  this.promptReservations.delete(handle.workflowId);
90608
91026
  }
91027
+ async persistPendingStageMessages(workflowId, messages2, logicalRunId = workflowId) {
91028
+ const handle = this.getWorkflow(workflowId);
91029
+ if (handle === undefined)
91030
+ return false;
91031
+ this.registerWorkflow({
91032
+ ...handle,
91033
+ pendingStageMessages: replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages2),
91034
+ updatedAt: nextMetadataTimestamp(handle.updatedAt)
91035
+ });
91036
+ return true;
91037
+ }
90609
91038
  recordCheckpoint(checkpoint) {
90610
91039
  const currentCheckpoint = withCurrentStageTopology(checkpoint);
90611
91040
  const rec = this.workflows.get(currentCheckpoint.workflowId);
@@ -90798,6 +91227,7 @@ class InMemoryDurableBackend {
90798
91227
  createdAt: h.createdAt,
90799
91228
  completedCheckpoints: h.completedCheckpoints,
90800
91229
  pendingPrompts: h.pendingPrompts,
91230
+ pendingStageMessages: h.pendingStageMessages ?? [],
90801
91231
  ...h.ownerExecutorId !== undefined ? { ownerExecutorId: h.ownerExecutorId } : {},
90802
91232
  ...h.sessionFile !== undefined ? { sessionFile: h.sessionFile } : {},
90803
91233
  ...h.label !== undefined ? { label: h.label } : {},
@@ -90886,6 +91316,9 @@ function toResumableEntry(handle) {
90886
91316
  updatedAt: handle.updatedAt
90887
91317
  };
90888
91318
  }
91319
+ function nextMetadataTimestamp(previous2) {
91320
+ return Math.max(Date.now(), previous2 + 1);
91321
+ }
90889
91322
 
90890
91323
  // dist/builtin/workflows/src/durable/stage-topology-validation.ts
90891
91324
  function immutableStageGroupError(stages) {
@@ -92076,6 +92509,7 @@ function encodeMetadata(metadata) {
92076
92509
  completedCheckpoints: metadata.completedCheckpoints,
92077
92510
  pendingPrompts: metadata.pendingPrompts,
92078
92511
  promptReservationEpoch: metadata.promptReservationEpoch,
92512
+ ...metadata.pendingStageMessages !== undefined ? { pendingStageMessages: serializePendingStageMessages(metadata.pendingStageMessages) } : {},
92079
92513
  ...metadata.ownerExecutorId !== undefined ? { ownerExecutorId: metadata.ownerExecutorId } : {},
92080
92514
  ...metadata.transitionClaimId !== undefined ? { transitionClaimId: metadata.transitionClaimId } : {},
92081
92515
  ...metadata.sessionFile !== undefined ? { sessionFile: metadata.sessionFile } : {},
@@ -92133,15 +92567,101 @@ function metadataTimestamp(record) {
92133
92567
  function parseDurableWorkflowMetadata(value, workflowId) {
92134
92568
  if (typeof value !== "object" || value === null || Array.isArray(value))
92135
92569
  return;
92570
+ const serialized = value;
92571
+ const pendingStageMessages = parsePendingStageMessages(serialized.pendingStageMessages);
92572
+ if (pendingStageMessages === null)
92573
+ return;
92136
92574
  const metadata = value;
92137
92575
  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
92576
  return;
92139
92577
  const { origin, ...metadataWithoutOrigin } = metadata;
92140
92578
  return {
92141
92579
  ...metadataWithoutOrigin,
92580
+ pendingStageMessages,
92142
92581
  ...isWorkflowActor(origin) ? { origin } : {}
92143
92582
  };
92144
92583
  }
92584
+ function serializePendingStageMessages(messages2) {
92585
+ return messages2.map((entry) => ({
92586
+ id: entry.id,
92587
+ runId: entry.runId,
92588
+ stageKey: entry.stageKey,
92589
+ ...entry.stageId !== undefined ? { stageId: entry.stageId } : {},
92590
+ ...entry.stageReplayKey !== undefined ? { stageReplayKey: entry.stageReplayKey } : {},
92591
+ ...entry.senderRegistrationName !== undefined ? { senderRegistrationName: entry.senderRegistrationName } : {},
92592
+ ...entry.senderReturnAddress !== undefined ? { senderReturnAddress: entry.senderReturnAddress } : {},
92593
+ from: {
92594
+ id: entry.from.id,
92595
+ ...entry.from.name !== undefined ? { name: entry.from.name } : {},
92596
+ ...entry.from.group !== undefined ? { group: entry.from.group } : {},
92597
+ ...entry.from.cwd !== undefined ? { cwd: entry.from.cwd } : {},
92598
+ ...entry.from.model !== undefined ? { model: entry.from.model } : {},
92599
+ ...entry.from.pid !== undefined ? { pid: entry.from.pid } : {},
92600
+ ...entry.from.startedAt !== undefined ? { startedAt: entry.from.startedAt } : {},
92601
+ ...entry.from.lastActivity !== undefined ? { lastActivity: entry.from.lastActivity } : {},
92602
+ ...entry.from.status !== undefined ? { status: entry.from.status } : {}
92603
+ },
92604
+ message: {
92605
+ id: entry.message.id,
92606
+ timestamp: entry.message.timestamp,
92607
+ ...entry.message.replyTo !== undefined ? { replyTo: entry.message.replyTo } : {},
92608
+ ...entry.message.expectsReply !== undefined ? { expectsReply: entry.message.expectsReply } : {},
92609
+ ...entry.message.replyError !== undefined ? { replyError: entry.message.replyError } : {},
92610
+ ...entry.message.source !== undefined ? { source: { ...entry.message.source } } : {},
92611
+ content: {
92612
+ text: entry.message.content.text,
92613
+ ...entry.message.content.attachments !== undefined ? { attachments: entry.message.content.attachments.map((attachment) => ({ ...attachment })) } : {}
92614
+ }
92615
+ },
92616
+ queuedAt: entry.queuedAt,
92617
+ ...entry.admissionOrder !== undefined ? { admissionOrder: entry.admissionOrder } : {},
92618
+ status: entry.status,
92619
+ ...entry.deliveredAt !== undefined ? { deliveredAt: entry.deliveredAt } : {},
92620
+ ...entry.undeliverableReason !== undefined ? { undeliverableReason: entry.undeliverableReason } : {},
92621
+ ...entry.undeliverableNotificationId !== undefined ? { undeliverableNotificationId: entry.undeliverableNotificationId } : {},
92622
+ ...entry.undeliverableNotifiedAt !== undefined ? { undeliverableNotifiedAt: entry.undeliverableNotifiedAt } : {}
92623
+ }));
92624
+ }
92625
+ function parsePendingStageMessages(value) {
92626
+ if (value === undefined)
92627
+ return [];
92628
+ if (!Array.isArray(value) || !value.every(isPendingStageMessage))
92629
+ return null;
92630
+ return value;
92631
+ }
92632
+ function isPendingStageMessage(value) {
92633
+ if (!isSerializableObject(value))
92634
+ return false;
92635
+ 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);
92636
+ }
92637
+ function isPendingStageSender(value) {
92638
+ 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");
92639
+ }
92640
+ function isPendingStageIntercomMessage(value) {
92641
+ if (!isSerializableObject(value) || typeof value.id !== "string" || typeof value.timestamp !== "number")
92642
+ return false;
92643
+ if (value.replyTo !== undefined && typeof value.replyTo !== "string")
92644
+ return false;
92645
+ if (value.expectsReply !== undefined && typeof value.expectsReply !== "boolean")
92646
+ return false;
92647
+ if (value.replyError !== undefined && typeof value.replyError !== "string")
92648
+ return false;
92649
+ if (!isSerializableObject(value.content) || typeof value.content.text !== "string")
92650
+ return false;
92651
+ const attachments = value.content.attachments;
92652
+ if (attachments !== undefined && (!Array.isArray(attachments) || !attachments.every(isPendingStageAttachment)))
92653
+ return false;
92654
+ return value.source === undefined || isPendingStageSource(value.source);
92655
+ }
92656
+ function isPendingStageAttachment(value) {
92657
+ 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");
92658
+ }
92659
+ function isPendingStageSource(value) {
92660
+ return isSerializableObject(value) && typeof value.subagentRunId === "string" && (value.subagentAgent === undefined || typeof value.subagentAgent === "string") && (value.subagentIndex === undefined || typeof value.subagentIndex === "number");
92661
+ }
92662
+ function isSerializableObject(value) {
92663
+ return typeof value === "object" && value !== null && !Array.isArray(value);
92664
+ }
92145
92665
  function isWorkflowActor(value) {
92146
92666
  return value === "user" || value === "agent";
92147
92667
  }
@@ -92676,6 +93196,29 @@ class DbosDurableBackend {
92676
93196
  await this.writeMetadata(handle.workflowId);
92677
93197
  });
92678
93198
  }
93199
+ async persistPendingStageMessages(workflowId, messages2, logicalRunId = workflowId) {
93200
+ let persisted = false;
93201
+ await this.enqueueWrite(async () => {
93202
+ if (!this.isWorkflowLoadable(workflowId))
93203
+ return;
93204
+ const handle = this.mem.getWorkflow(workflowId);
93205
+ const value = this.mem.toMetadata(workflowId);
93206
+ if (handle === undefined || value === undefined)
93207
+ return;
93208
+ const updatedAt = Math.max(Date.now(), handle.updatedAt + 1);
93209
+ const pendingStageMessages = replacePendingStageMessagesForRun(handle.pendingStageMessages ?? [], logicalRunId, messages2);
93210
+ const metadata = {
93211
+ ...this.promptReservations.metadata(workflowId, value),
93212
+ pendingStageMessages,
93213
+ ownerExecutorId: this.executorId,
93214
+ updatedAt
93215
+ };
93216
+ await this.sdk.recordStepOutput(workflowId, metadataStepName(updatedAt), encodeMetadata(metadata));
93217
+ this.mem.registerWorkflow({ ...handle, pendingStageMessages, updatedAt });
93218
+ persisted = true;
93219
+ });
93220
+ return persisted;
93221
+ }
92679
93222
  recordCheckpoint(checkpoint) {
92680
93223
  if (!this.isWorkflowLoadable(checkpoint.workflowId))
92681
93224
  return;
@@ -95378,6 +95921,10 @@ function withMidSessionResumePrompt(stage, enabled) {
95378
95921
  });
95379
95922
  return stage;
95380
95923
  }
95924
+ function pendingStageIdForReplay(backend, workflowId, replayKey, stageName) {
95925
+ 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));
95926
+ return candidates.size === 1 ? candidates.values().next().value : undefined;
95927
+ }
95381
95928
  function createDurableStagePrimitive(input2) {
95382
95929
  return (name, options) => {
95383
95930
  const replayKey = input2.nextReplayKey(name);
@@ -95389,13 +95936,12 @@ function createDurableStagePrimitive(input2) {
95389
95936
  const session = input2.backend.getStageSession(input2.workflowId, replayKey);
95390
95937
  const isMidSessionResume = session?.sessionFile !== undefined;
95391
95938
  const topology = activeStageTopology(input2.backend, input2.workflowId, replayKey);
95939
+ const durableStageId = topology?.stageId ?? pendingStageIdForReplay(input2.backend, input2.workflowId, replayKey, name);
95392
95940
  const liveOptions = {
95393
95941
  ...options ?? {},
95394
95942
  durableReplayKey: replayKey,
95395
- ...topology !== undefined ? {
95396
- durableStageId: topology.stageId,
95397
- durableParentIds: [...topology.parentIds]
95398
- } : {},
95943
+ ...durableStageId !== undefined ? { durableStageId } : {},
95944
+ ...topology !== undefined ? { durableParentIds: [...topology.parentIds] } : {},
95399
95945
  ...isMidSessionResume ? {
95400
95946
  resumeFromSessionFile: session.sessionFile,
95401
95947
  durableAccumulatedDurationMs: session.durationMs ?? 0
@@ -96093,6 +96639,7 @@ function appendStageStart(api, payload) {
96093
96639
  name: payload.name,
96094
96640
  parentIds: [...payload.parentIds],
96095
96641
  ...payload.model !== undefined ? { model: payload.model } : {},
96642
+ ...payload.pendingStageDeliveryAvailable !== undefined ? { pendingStageDeliveryAvailable: payload.pendingStageDeliveryAvailable } : {},
96096
96643
  ...payload.replayKey !== undefined ? { replayKey: payload.replayKey } : {},
96097
96644
  ...payload.replayedFromStageId !== undefined ? { replayedFromStageId: payload.replayedFromStageId } : {},
96098
96645
  ...payload.replayed !== undefined ? { replayed: payload.replayed } : {},
@@ -96636,6 +97183,7 @@ function installCompactionHook(api, store2) {
96636
97183
  stageId: stage.id,
96637
97184
  name: stage.name,
96638
97185
  parentIds: [...stage.parentIds],
97186
+ pendingStageDeliveryAvailable: stage.pendingStageDeliveryAvailable,
96639
97187
  ts: stage.startedAt ?? now
96640
97188
  });
96641
97189
  }
@@ -97212,7 +97760,7 @@ function formatWorkflowLifecycleNoticeText(details) {
97212
97760
  return `? Workflow "${workflowName}" needs input (run ${details.runId}).${prompt2} Respond: /workflow connect ${details.runId} to answer this run-level prompt.`;
97213
97761
  }
97214
97762
  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}`;
97763
+ 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
97764
  return `? Workflow "${workflowName}" needs input (run ${details.runId}, stage ${stage}).${prompt2} Respond: ${responseHint}.`;
97217
97765
  }
97218
97766
  function makeTerminalNotice(run, kind) {
@@ -98512,6 +99060,12 @@ function workflow(spec) {
98512
99060
  return Object.freeze(branded);
98513
99061
  }
98514
99062
 
99063
+ // dist/builtin/workflows/builtin/steering-context.ts
99064
+ var exports_steering_context = {};
99065
+ __export(exports_steering_context, {
99066
+ withSteeringPropagationContext: () => withSteeringPropagationContext
99067
+ });
99068
+
98515
99069
  // dist/builtin/workflows/src/authoring/keep-context.ts
98516
99070
  var KEEP_CONTEXT_OPEN_TAG = "<keepContext>";
98517
99071
  var KEEP_CONTEXT_CLOSE_TAG = "</keepContext>";
@@ -98525,6 +99079,12 @@ ${KEEP_CONTEXT_CLOSE_TAG}`;
98525
99079
  }
98526
99080
 
98527
99081
  // dist/builtin/workflows/builtin/shared-prompts.ts
99082
+ var PENDING_STAGE_DELIVERY_GUIDANCE_LINES = [
99083
+ "Send material updates through Intercom to every affected workflow stage, including stages that have not started.",
99084
+ "Atomic queues messages for known pending stages addressed as `<runId>:<stageKey>` and delivers them when their sessions initialize."
99085
+ ];
99086
+ var PENDING_STAGE_DELIVERY_GUIDANCE = PENDING_STAGE_DELIVERY_GUIDANCE_LINES.join(`
99087
+ `);
98528
99088
  var STEERING_PROPAGATION_CONTRACT = [
98529
99089
  "Steering propagation contract:",
98530
99090
  "- 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 +99092,7 @@ var STEERING_PROPAGATION_CONTRACT = [
98532
99092
  "- Keep user-authored amendments visibly separate from your own observations, so later stages can tell a required clause from an agent proposal.",
98533
99093
  "- 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
99094
  "- 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.",
99095
+ ...PENDING_STAGE_DELIVERY_GUIDANCE_LINES.map((line) => `- ${line}`),
98535
99096
  "- 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
99097
  ].join(`
98537
99098
  `);
@@ -106193,6 +106754,9 @@ class ScopedDurableBackend {
106193
106754
  this.persistent = inner.persistent;
106194
106755
  }
106195
106756
  registerWorkflow(_handle) {}
106757
+ persistPendingStageMessages(workflowId, messages2, logicalRunId = workflowId) {
106758
+ return this.inner.persistPendingStageMessages(this.scope.rootWorkflowId, messages2, logicalRunId);
106759
+ }
106196
106760
  recordCheckpoint(checkpoint) {
106197
106761
  this.inner.recordCheckpoint(this.remap(checkpoint));
106198
106762
  }
@@ -109530,6 +110094,7 @@ import { randomUUID as randomUUID16 } from "node:crypto";
109530
110094
 
109531
110095
  // dist/builtin/workflows/src/shared/intercom-group.ts
109532
110096
  import { randomUUID as randomUUID15 } from "node:crypto";
110097
+ var DEFAULT_INTERCOM_GROUP = "default";
109533
110098
  function workflowInvocationIntercomGroup(rootRunId) {
109534
110099
  return `workflow:${rootRunId}`;
109535
110100
  }
@@ -109539,6 +110104,12 @@ function normalizeAutoGroupSentinel(group) {
109539
110104
  const sentinel = group.trim().toLowerCase();
109540
110105
  return sentinel === "true" || sentinel === "auto" ? true : group;
109541
110106
  }
110107
+ function normalizeGroup(value) {
110108
+ if (typeof value !== "string")
110109
+ return DEFAULT_INTERCOM_GROUP;
110110
+ const trimmed = value.trim();
110111
+ return trimmed.length > 0 ? trimmed : DEFAULT_INTERCOM_GROUP;
110112
+ }
109542
110113
  function resolveStageGroup(stageOptions, workflowGroup) {
109543
110114
  if (!stageOptions)
109544
110115
  return workflowGroup;
@@ -109563,6 +110134,11 @@ function stageHasIntercomAccess(stageOptions) {
109563
110134
  return false;
109564
110135
  return true;
109565
110136
  }
110137
+ function stageCanUseWorkflowPendingStageRoute(stageOptions, workflowGroup) {
110138
+ if (!stageHasIntercomAccess(stageOptions) || stageOptions?.group === true)
110139
+ return false;
110140
+ return normalizeGroup(resolveStageGroup(stageOptions, workflowGroup)) === normalizeGroup(workflowGroup);
110141
+ }
109566
110142
 
109567
110143
  // dist/builtin/workflows/src/shared/budget-meter.ts
109568
110144
  var usageFields = ["input", "output", "cacheRead", "cacheWrite", "cost"];
@@ -109817,9 +110393,9 @@ function createRunBudgetController(input2) {
109817
110393
  function createParallelPrimitive(input2) {
109818
110394
  return async (steps, options = {}) => {
109819
110395
  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);
110396
+ const normalizeGroup2 = (value) => value.group === undefined ? value : { ...value, group: normalizeAutoGroupSentinel(value.group) };
110397
+ const normalizedOptions = normalizeGroup2(options);
110398
+ const normalizedSteps = steps.map(normalizeGroup2);
109823
110399
  const needsAutoGroup = normalizedOptions.group === true || normalizedSteps.some((step) => step.group === true);
109824
110400
  const autoGroup = needsAutoGroup ? randomUUID16() : undefined;
109825
110401
  const resolveAutoGroup = (value) => value.group === true && autoGroup ? { ...value, group: autoGroup } : value;
@@ -111790,7 +112366,7 @@ function createAdmittedToolExecutionTracker(options = {}) {
111790
112366
  const inFlight = new Set;
111791
112367
  const admissions = [];
111792
112368
  const failures = [];
111793
- let nextAdmissionOrder = 0;
112369
+ let nextAdmissionOrder2 = 0;
111794
112370
  let state = "OPEN";
111795
112371
  let closing;
111796
112372
  const abandonment = Promise.withResolvers();
@@ -111841,7 +112417,7 @@ function createAdmittedToolExecutionTracker(options = {}) {
111841
112417
  return { accepted: false, error, bindNode() {}, noteCancelled() {} };
111842
112418
  }
111843
112419
  const admission = {
111844
- admissionOrder: ++nextAdmissionOrder,
112420
+ admissionOrder: ++nextAdmissionOrder2,
111845
112421
  nodeId: undefined,
111846
112422
  failure: undefined,
111847
112423
  cancelled: false,
@@ -113143,6 +113719,150 @@ function createReplayStageContext(input2) {
113143
113719
  };
113144
113720
  }
113145
113721
 
113722
+ // dist/builtin/workflows/src/durable/run-owner-backend.ts
113723
+ function durableRootRunIdForRun(runs, runId) {
113724
+ return reciprocalWorkflowRootRunId(new Map(runs.map((run2) => [run2.id, run2])), runId);
113725
+ }
113726
+ function durableBackendForRun(backend, runs, runId) {
113727
+ const rootRunId = durableRootRunIdForRun(runs, runId);
113728
+ if (rootRunId === undefined)
113729
+ return;
113730
+ const runById = new Map(runs.map((run2) => [run2.id, run2]));
113731
+ if (rootRunId === runId)
113732
+ return backend;
113733
+ const scopes = [];
113734
+ let current = runById.get(runId);
113735
+ while (current !== undefined && current.id !== rootRunId) {
113736
+ const parent = current.parentRunId === undefined ? undefined : runById.get(current.parentRunId);
113737
+ const boundary = parent?.stages.find((stage) => stage.id === current?.parentStageId);
113738
+ if (parent === undefined || boundary?.replayKey === undefined)
113739
+ return;
113740
+ scopes.push(boundary.replayKey);
113741
+ current = parent;
113742
+ }
113743
+ if (current?.id !== rootRunId)
113744
+ return;
113745
+ let view = backend;
113746
+ for (const scopePrefix of scopes.reverse()) {
113747
+ view = new ScopedDurableBackend(view, { rootWorkflowId: rootRunId, scopePrefix });
113748
+ }
113749
+ return view;
113750
+ }
113751
+
113752
+ // dist/builtin/workflows/src/shared/pending-stage-route-capability.ts
113753
+ import { randomUUID as randomUUID17 } from "node:crypto";
113754
+ var routeCapabilities = new WeakMap;
113755
+ function workflowPendingStageRouteCapability(store2, runId) {
113756
+ let capabilities = routeCapabilities.get(store2);
113757
+ if (capabilities === undefined) {
113758
+ capabilities = new Map;
113759
+ routeCapabilities.set(store2, capabilities);
113760
+ }
113761
+ let capability = capabilities.get(runId);
113762
+ if (capability === undefined) {
113763
+ capability = randomUUID17();
113764
+ capabilities.set(runId, capability);
113765
+ }
113766
+ return capability;
113767
+ }
113768
+
113769
+ // dist/builtin/workflows/src/runs/foreground/pending-stage-delivery.ts
113770
+ var pendingDeliveryClaims = new WeakMap;
113771
+ function createWorkflowPendingStageDelivery(activeStore, runId, stageId, stageName3) {
113772
+ let resolveReady;
113773
+ let rejectReady;
113774
+ let readyPromise;
113775
+ let drainError;
113776
+ let drainPromise;
113777
+ const pendingReady = () => {
113778
+ if (readyPromise === undefined) {
113779
+ readyPromise = new Promise((resolve35, reject) => {
113780
+ resolveReady = resolve35;
113781
+ rejectReady = reject;
113782
+ });
113783
+ }
113784
+ return readyPromise;
113785
+ };
113786
+ return {
113787
+ routeCapability: workflowPendingStageRouteCapability(activeStore, runId),
113788
+ deliverPending(deliver) {
113789
+ if (drainPromise === undefined) {
113790
+ drainError = undefined;
113791
+ const attempt = deliverPendingStageMessages(activeStore, runId, stageId, stageName3, deliver);
113792
+ const drain2 = attempt.then(() => resolveReady?.(), (error) => {
113793
+ drainError = error;
113794
+ rejectReady?.(error);
113795
+ readyPromise = undefined;
113796
+ resolveReady = undefined;
113797
+ rejectReady = undefined;
113798
+ if (drainPromise === drain2)
113799
+ drainPromise = undefined;
113800
+ throw error;
113801
+ });
113802
+ drainPromise = drain2;
113803
+ }
113804
+ return drainPromise;
113805
+ },
113806
+ ready() {
113807
+ if (activeStore.pendingStageMessagesFor(runId, stageId).length === 0 && (stageId === stageName3 || activeStore.pendingStageMessagesFor(runId, stageName3).length === 0)) {
113808
+ return;
113809
+ }
113810
+ return drainError === undefined ? pendingReady() : Promise.reject(drainError);
113811
+ }
113812
+ };
113813
+ }
113814
+ async function deliverPendingStageMessages(activeStore, runId, stageId, stageName3, deliver) {
113815
+ const candidateIds = new Set([
113816
+ ...activeStore.pendingStageMessagesFor(runId, stageId),
113817
+ ...activeStore.pendingStageMessagesFor(runId, stageName3)
113818
+ ].map((entry) => entry.id));
113819
+ 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);
113820
+ const rootBackend = getDurableBackend();
113821
+ const backend = durableBackendForRun(rootBackend, activeStore.runs(), runId);
113822
+ if (backend === undefined) {
113823
+ throw new Error(`atomic-workflows: workflow run ${runId} has no durable owner for pending-stage delivery`);
113824
+ }
113825
+ for (const entry of entries) {
113826
+ const releaseClaim = claimPendingDelivery(activeStore, runId, entry.stageKey, entry.id);
113827
+ if (releaseClaim === undefined)
113828
+ continue;
113829
+ try {
113830
+ await deliver(toPendingStageSender(entry), entry.message);
113831
+ if (!await activeStore.markPendingStageMessageDelivered(runId, entry.stageKey, entry.id, new Date().toISOString(), backend)) {
113832
+ throw new Error(`atomic-workflows: pending-stage message ${entry.id} changed during delivery`);
113833
+ }
113834
+ } finally {
113835
+ releaseClaim();
113836
+ }
113837
+ }
113838
+ }
113839
+ function claimPendingDelivery(store2, runId, stageKey, messageId) {
113840
+ let claims = pendingDeliveryClaims.get(store2);
113841
+ if (claims === undefined) {
113842
+ claims = new Set;
113843
+ pendingDeliveryClaims.set(store2, claims);
113844
+ }
113845
+ const key2 = JSON.stringify([runId, stageKey, messageId]);
113846
+ if (claims.has(key2))
113847
+ return;
113848
+ claims.add(key2);
113849
+ return () => {
113850
+ claims?.delete(key2);
113851
+ if (claims?.size === 0)
113852
+ pendingDeliveryClaims.delete(store2);
113853
+ };
113854
+ }
113855
+ function toPendingStageSender(entry) {
113856
+ return {
113857
+ ...entry.from,
113858
+ cwd: entry.from.cwd ?? "",
113859
+ model: entry.from.model ?? "unknown",
113860
+ pid: entry.from.pid ?? 0,
113861
+ startedAt: entry.from.startedAt ?? entry.message.timestamp,
113862
+ lastActivity: entry.from.lastActivity ?? entry.message.timestamp
113863
+ };
113864
+ }
113865
+
113146
113866
  // dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts
113147
113867
  init_src();
113148
113868
 
@@ -113534,7 +114254,7 @@ function workflowSessionOptions(meta2) {
113534
114254
  workflow: { runId: meta2.runId, stageId: meta2.stageId, stageName: meta2.stageName }
113535
114255
  };
113536
114256
  }
113537
- function workflowOrchestrationContext(meta2, stageOptions) {
114257
+ function workflowOrchestrationContext(meta2, stageOptions, pendingStageDelivery) {
113538
114258
  const base = {
113539
114259
  kind: "workflow-stage",
113540
114260
  workflowRunId: meta2.runId,
@@ -113543,11 +114263,12 @@ function workflowOrchestrationContext(meta2, stageOptions) {
113543
114263
  constraints: { disableWorkflowTool: true }
113544
114264
  };
113545
114265
  const intercomGroup = stageHasIntercomAccess(stageOptions) ? resolveStageGroup(stageOptions, meta2.workflowIntercomGroup) : undefined;
113546
- return intercomGroup ? { ...base, intercomGroup } : base;
114266
+ const context = intercomGroup ? { ...base, intercomGroup } : base;
114267
+ return pendingStageDelivery === undefined ? context : { ...context, pendingStageDelivery };
113547
114268
  }
113548
- function stripWorkflowOnlyOptions(options, defaultSessionDir, meta2) {
114269
+ function stripWorkflowOnlyOptions(options, defaultSessionDir, meta2, pendingStageDelivery) {
113549
114270
  const classification = workflowSessionOptions(meta2);
113550
- const orchestrationContext = workflowOrchestrationContext(meta2, options);
114271
+ const orchestrationContext = workflowOrchestrationContext(meta2, options, pendingStageDelivery);
113551
114272
  if (!options) {
113552
114273
  return defaultSessionDir === undefined ? { orchestrationContext } : {
113553
114274
  orchestrationContext,
@@ -114913,7 +115634,7 @@ class StageSessionController {
114913
115634
  });
114914
115635
  let created;
114915
115636
  try {
114916
- created = this.opts.adapters.agentSession ? await this.opts.adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptions, this.opts.defaultSessionDir, this.meta), {
115637
+ created = this.opts.adapters.agentSession ? await this.opts.adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptions, this.opts.defaultSessionDir, this.meta, this.opts.pendingStageDelivery), {
114917
115638
  ...this.meta,
114918
115639
  stageOptions,
114919
115640
  ...this.sharedOrchestrationContext !== undefined ? { orchestrationContext: this.sharedOrchestrationContext } : {}
@@ -114930,10 +115651,12 @@ class StageSessionController {
114930
115651
  throw this.staleCreationReason(startGeneration);
114931
115652
  }
114932
115653
  const session = attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) => this.attachSession(result));
114933
- if (session instanceof Promise)
114934
- return await session;
115654
+ const attachedSession = session instanceof Promise ? await session : session;
115655
+ const pendingStageDeliveryReady = this.sharedOrchestrationContext?.pendingStageDelivery?.ready();
115656
+ if (pendingStageDeliveryReady !== undefined)
115657
+ await pendingStageDeliveryReady;
114935
115658
  await this.opts.onSessionReady?.();
114936
- return session;
115659
+ return attachedSession;
114937
115660
  }
114938
115661
  attachSession(created) {
114939
115662
  const result = normalizeSessionCreateResult(created);
@@ -115685,6 +116408,12 @@ function createWorkflowStageFactory(input2) {
115685
116408
  const replaySource = replayDecision.kind === "replay" ? replayDecision.source : undefined;
115686
116409
  const executeReplaySource = replayDecision.kind === "execute" ? replayDecision.source : undefined;
115687
116410
  const shouldReplay = replaySource !== undefined;
116411
+ const stageOptionsForContext = executeReplaySource?.sessionFile === undefined ? options : {
116412
+ ...options ?? {},
116413
+ context: options?.context ?? "fork",
116414
+ forkFromSessionFile: options?.forkFromSessionFile ?? executeReplaySource.sessionFile
116415
+ };
116416
+ const pendingStageDeliveryAvailable = stageCanUseWorkflowPendingStageRoute(stageOptionsForContext, input2.workflowIntercomGroup);
115688
116417
  const stageSnapshot = {
115689
116418
  id: stageId,
115690
116419
  name,
@@ -115692,6 +116421,7 @@ function createWorkflowStageFactory(input2) {
115692
116421
  status: shouldReplay ? "completed" : "pending",
115693
116422
  parentIds: Object.freeze(parentIds),
115694
116423
  toolEvents: [],
116424
+ pendingStageDeliveryAvailable,
115695
116425
  ...shouldReplay ? {
115696
116426
  startedAt: Date.now(),
115697
116427
  endedAt: Date.now(),
@@ -115720,11 +116450,6 @@ function createWorkflowStageFactory(input2) {
115720
116450
  throwIfWorkflowExitSelected: input2.exit.throwIfWorkflowExitSelected
115721
116451
  });
115722
116452
  }
115723
- const stageOptionsForContext = executeReplaySource?.sessionFile === undefined ? options : {
115724
- ...options ?? {},
115725
- context: options?.context ?? "fork",
115726
- forkFromSessionFile: options?.forkFromSessionFile ?? executeReplaySource.sessionFile
115727
- };
115728
116453
  const applyModelFallbackMeta = (meta2) => {
115729
116454
  if (meta2.model !== undefined)
115730
116455
  stageSnapshot.model = meta2.model;
@@ -115762,6 +116487,9 @@ function createWorkflowStageFactory(input2) {
115762
116487
  workflowIntercomGroup: input2.workflowIntercomGroup,
115763
116488
  signal: input2.signal,
115764
116489
  stageOptions: stageOptionsForContext,
116490
+ ...pendingStageDeliveryAvailable ? {
116491
+ pendingStageDelivery: createWorkflowPendingStageDelivery(input2.activeStore, input2.runId, stageId, name)
116492
+ } : {},
115765
116493
  models: input2.opts.models,
115766
116494
  executionMode: input2.opts.executionMode,
115767
116495
  defaultSessionDir: input2.opts.defaultSessionDir,
@@ -115887,6 +116615,7 @@ function createWorkflowStageFactory(input2) {
115887
116615
  name,
115888
116616
  parentIds: stageSnapshot.parentIds,
115889
116617
  ...stageReplayFields(stageSnapshot),
116618
+ pendingStageDeliveryAvailable,
115890
116619
  ts: stageSnapshot.startedAt ?? Date.now()
115891
116620
  });
115892
116621
  };
@@ -116249,6 +116978,9 @@ async function run(def, inputs, opts = {}) {
116249
116978
  status: "running",
116250
116979
  stages: [],
116251
116980
  toolNodes: [],
116981
+ pendingStageMessages: [
116982
+ ...pendingStageMessagesForDurableRun(rootBackend, runId, opts.parentRun?.rootRunId ?? runId)
116983
+ ],
116252
116984
  startedAt: Date.now(),
116253
116985
  ...opts.parentRun !== undefined ? {
116254
116986
  parentRunId: opts.parentRun.runId,
@@ -116822,8 +117554,8 @@ var runWorkflow = () => {
116822
117554
  };
116823
117555
 
116824
117556
  // 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`;
117557
+ var WORKFLOWS_MODULE_SPECIFIER = "@bastani/atomic/workflows";
117558
+ var LEGACY_WORKFLOWS_MODULE_SPECIFIER = "@bastani/workflows";
116827
117559
  var TYPEBOX_MODULE_SPECIFIER = "typebox";
116828
117560
  var WORKFLOWS_SDK_MODULE = {
116829
117561
  ...exports_sdk_surface
@@ -116842,19 +117574,27 @@ var WORKFLOWS_BUILTIN_MODULE = {
116842
117574
  var TYPEBOX_MODULE = {
116843
117575
  ...typeboxModule
116844
117576
  };
117577
+ function createWorkflowVirtualModules(moduleSpecifier) {
117578
+ const builtinModuleSpecifier = `${moduleSpecifier}/builtin`;
117579
+ return {
117580
+ [moduleSpecifier]: WORKFLOWS_SDK_MODULE,
117581
+ [builtinModuleSpecifier]: WORKFLOWS_BUILTIN_MODULE,
117582
+ [`${builtinModuleSpecifier}/adversarial-verification`]: { default: adversarial_verification_default },
117583
+ [`${builtinModuleSpecifier}/classify-and-act`]: { default: classify_and_act_default },
117584
+ [`${builtinModuleSpecifier}/fan-out-and-synthesize`]: { default: fan_out_and_synthesize_default },
117585
+ [`${builtinModuleSpecifier}/generate-and-filter`]: { default: generate_and_filter_default },
117586
+ [`${builtinModuleSpecifier}/goal`]: { default: goal_default },
117587
+ [`${builtinModuleSpecifier}/loop-until-done`]: { default: loop_until_done_default },
117588
+ [`${builtinModuleSpecifier}/open-claude-design`]: { default: open_claude_design_default },
117589
+ [`${builtinModuleSpecifier}/ralph`]: { default: ralph_default },
117590
+ [`${builtinModuleSpecifier}/tournament`]: { default: tournament_default },
117591
+ [`${builtinModuleSpecifier}/steering-context`]: exports_steering_context
117592
+ };
117593
+ }
116845
117594
  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 }
117595
+ ...createWorkflowVirtualModules(WORKFLOWS_MODULE_SPECIFIER),
117596
+ ...createWorkflowVirtualModules(LEGACY_WORKFLOWS_MODULE_SPECIFIER),
117597
+ [TYPEBOX_MODULE_SPECIFIER]: TYPEBOX_MODULE
116858
117598
  };
116859
117599
  var workflowModuleLoader = createJiti2(import.meta.url, {
116860
117600
  moduleCache: false,
@@ -117499,27 +118239,6 @@ function ensurePostMortemStageHandle(runId, stage, deps) {
117499
118239
  const handle = deps.registry.getOrCreateDetached(runId, stage.id, () => createPostMortemStageHandle(runId, stage, sessionFile, adapters, deps.cwd, deps.defaultSessionDir));
117500
118240
  return { ok: true, handle };
117501
118241
  }
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
118242
  function createPostMortemStageHandle(runId, stage, sessionFile, adapters, cwd, defaultSessionDir) {
117524
118243
  const context = createStageContext2({
117525
118244
  runId,
@@ -118249,7 +118968,7 @@ function formatWorkflowHeartbeatNoticeText(details) {
118249
118968
  const workflowName = escapeQuotedText3(details.workflowName);
118250
118969
  const elapsed = formatWorkflowHeartbeatElapsed(details);
118251
118970
  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}`;
118971
+ 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
118972
  }
118254
118973
  function registerWorkflowHeartbeatRenderer(options) {
118255
118974
  const register = options.registerMessageRenderer;
@@ -119481,6 +120200,241 @@ function createWorkflowExtensionRuntimeState(pi, adapters, resolveCwd = () => pi
119481
120200
  };
119482
120201
  }
119483
120202
 
120203
+ // dist/builtin/workflows/src/extension/pending-stage-intercom.ts
120204
+ var PENDING_STAGE_ROUTE_EVENT = "atomic:workflow-pending-stage-route";
120205
+ var PENDING_STAGE_MESSAGE_EVENT = "atomic:workflow-pending-stage-message";
120206
+ var PENDING_STAGE_UNDELIVERABLE_EVENT = "atomic:workflow-pending-stage-undeliverable";
120207
+ 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.";
120208
+ function registerPendingStageIntercomBridge(pi, activeStore) {
120209
+ let disposed = false;
120210
+ let sweepPromise = Promise.resolve();
120211
+ const notifyUndeliverable = async (entry, reason, notificationId) => {
120212
+ const payload = {
120213
+ handled: false,
120214
+ runId: entry.runId,
120215
+ senderId: entry.from.id,
120216
+ ...entry.senderRegistrationName === undefined ? {} : { senderRegistrationName: entry.senderRegistrationName },
120217
+ ...entry.senderReturnAddress === undefined ? {} : { senderReturnAddress: entry.senderReturnAddress },
120218
+ messageId: entry.message.id,
120219
+ notificationId,
120220
+ reason
120221
+ };
120222
+ pi.events?.emit?.(PENDING_STAGE_UNDELIVERABLE_EVENT, payload);
120223
+ return payload.handled && await payload.completion === true;
120224
+ };
120225
+ const announceRoutes = () => {
120226
+ if (disposed)
120227
+ return;
120228
+ const runs = activeStore.runs();
120229
+ for (const run2 of runs) {
120230
+ const rootRunId = durableRootRunIdForRun(runs, run2.id);
120231
+ if (rootRunId === undefined)
120232
+ continue;
120233
+ pi.events?.emit?.(PENDING_STAGE_ROUTE_EVENT, {
120234
+ runId: run2.id,
120235
+ group: workflowInvocationIntercomGroup(rootRunId),
120236
+ capability: workflowPendingStageRouteCapability(activeStore, run2.id)
120237
+ });
120238
+ }
120239
+ sweepPromise = sweepPromise.then(() => settleUndeliverablePendingStageMessages(activeStore, notifyUndeliverable)).then(() => {
120240
+ return;
120241
+ }).catch((error) => console.warn("atomic-workflows: pending stage delivery sweep failed", error));
120242
+ };
120243
+ const unsubscribeStore = activeStore.subscribeInvalidation(announceRoutes);
120244
+ announceRoutes();
120245
+ const subscription = pi.events?.on?.(PENDING_STAGE_MESSAGE_EVENT, (payload) => {
120246
+ if (disposed || !isPendingStageMessageEvent(payload) || payload.handled)
120247
+ return;
120248
+ const runs = activeStore.runs();
120249
+ const run2 = runs.find((candidate) => candidate.id === payload.runId);
120250
+ if (run2 === undefined)
120251
+ return;
120252
+ const rootRunId = durableRootRunIdForRun(runs, run2.id);
120253
+ if (rootRunId === undefined)
120254
+ return;
120255
+ if (payload.live === true) {
120256
+ if (knownLiveStage(run2, payload.stageKey) === undefined)
120257
+ return;
120258
+ payload.handled = true;
120259
+ payload.completion = validateLiveDelivery(activeStore, payload);
120260
+ return;
120261
+ }
120262
+ const stage = knownUninitializedStage(run2, payload.stageKey);
120263
+ if (stage === undefined)
120264
+ return;
120265
+ payload.handled = true;
120266
+ payload.completion = queueAndPersist(activeStore, payload, workflowInvocationIntercomGroup(rootRunId), stage.pendingStageDeliveryAvailable === true);
120267
+ });
120268
+ const dispose2 = () => {
120269
+ disposed = true;
120270
+ unsubscribeStore();
120271
+ if (typeof subscription === "function")
120272
+ subscription();
120273
+ };
120274
+ pi.on?.("session_shutdown", dispose2);
120275
+ return dispose2;
120276
+ }
120277
+ function isPendingStageMessageEvent(value) {
120278
+ if (typeof value !== "object" || value === null)
120279
+ return false;
120280
+ const event = value;
120281
+ 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";
120282
+ }
120283
+ function knownLiveStage(run2, stageKey) {
120284
+ const exactIds = run2.stages.filter((stage) => stage.id === stageKey);
120285
+ const candidates = exactIds.length > 0 ? exactIds : run2.stages.filter((stage) => stage.name === stageKey);
120286
+ return candidates.length === 1 ? candidates[0] : undefined;
120287
+ }
120288
+ async function validateLiveDelivery(activeStore, event) {
120289
+ const result = await activeStore.validateLiveStageMessage({
120290
+ runId: event.runId,
120291
+ stageKey: event.stageKey,
120292
+ from: event.from,
120293
+ message: event.message,
120294
+ queuedAt: new Date().toISOString()
120295
+ });
120296
+ if (result === undefined)
120297
+ return { outcome: "refused", reason: "Session not found" };
120298
+ if (result.outcome === "forward" || result.outcome === "delivered" || result.outcome === "queued")
120299
+ return result;
120300
+ if (result.outcome === "undeliverable") {
120301
+ return { outcome: "refused", reason: result.reason ?? "Pending-stage delivery was refused" };
120302
+ }
120303
+ return {
120304
+ outcome: "refused",
120305
+ reason: `Intercom message ID '${result.messageId}' conflicts with the durable identity for ${event.runId}:${event.stageKey}`,
120306
+ reasonCode: "message_id_conflict"
120307
+ };
120308
+ }
120309
+ async function queueAndPersist(activeStore, event, runGroup, pendingStageDeliveryAvailable) {
120310
+ if (event.message.expectsReply === true) {
120311
+ return { outcome: "refused", reason: PENDING_STAGE_ASK_REFUSAL };
120312
+ }
120313
+ if (!pendingStageDeliveryAvailable) {
120314
+ return {
120315
+ outcome: "refused",
120316
+ reason: `Workflow stage ${event.runId}:${event.stageKey} cannot receive Intercom messages before startup`
120317
+ };
120318
+ }
120319
+ const request = {
120320
+ runId: event.runId,
120321
+ stageKey: event.stageKey,
120322
+ from: event.from,
120323
+ ...event.senderRegistrationName === undefined ? {} : { senderRegistrationName: event.senderRegistrationName },
120324
+ ...event.senderReturnAddress === undefined ? {} : { senderReturnAddress: event.senderReturnAddress },
120325
+ message: event.message,
120326
+ queuedAt: new Date().toISOString()
120327
+ };
120328
+ const rootBackend = getDurableBackend();
120329
+ const backend = durableBackendForRun(rootBackend, activeStore.runs(), event.runId);
120330
+ if (backend === undefined)
120331
+ return { outcome: "refused", reason: "Session not found" };
120332
+ const result = await activeStore.queueStageMessage(request, event.from.group, runGroup, backend);
120333
+ if (result === undefined)
120334
+ return { outcome: "refused", reason: "Session not found" };
120335
+ if (!result.ok) {
120336
+ if (result.reason === "capacity") {
120337
+ return {
120338
+ outcome: "refused",
120339
+ reason: `Pending stage message queue is full (limit ${result.limit}) for ${result.runId}:${result.stageKey}`
120340
+ };
120341
+ }
120342
+ if (result.reason === "message_id_conflict") {
120343
+ return {
120344
+ outcome: "refused",
120345
+ reason: `Intercom message ID '${result.messageId}' was already queued for ${result.runId}:${result.stageKey} with a different target, sender, or payload`
120346
+ };
120347
+ }
120348
+ return { outcome: "refused", reason: "Target workflow run is in a different intercom group" };
120349
+ }
120350
+ if (result.entry.status === "delivered")
120351
+ return { outcome: "delivered" };
120352
+ if (result.entry.status === "undeliverable") {
120353
+ return {
120354
+ outcome: "refused",
120355
+ reason: result.entry.undeliverableReason ?? "Pending-stage delivery was refused"
120356
+ };
120357
+ }
120358
+ if (result.position === undefined)
120359
+ return { outcome: "refused", reason: "Pending-stage delivery was refused" };
120360
+ return { outcome: "queued", position: result.position };
120361
+ }
120362
+ function knownUninitializedStage(run2, stageKey) {
120363
+ const exactIds = run2.stages.filter((stage2) => stage2.id === stageKey);
120364
+ const candidates = exactIds.length > 0 ? exactIds : run2.stages.filter((stage2) => stage2.name === stageKey);
120365
+ if (candidates.length !== 1)
120366
+ return;
120367
+ const stage = candidates[0];
120368
+ return (stage.status === "pending" || stage.status === "running") && stage.sessionId === undefined && stage.sessionFile === undefined ? stage : undefined;
120369
+ }
120370
+ function pendingStageDestination(run2, entry) {
120371
+ if (entry.stageId !== undefined) {
120372
+ const candidates2 = run2.stages.filter((stage) => stage.id === entry.stageId);
120373
+ return candidates2.length === 1 ? candidates2[0] : undefined;
120374
+ }
120375
+ if (entry.stageReplayKey !== undefined) {
120376
+ const candidates2 = run2.stages.filter((stage) => stage.replayKey === entry.stageReplayKey);
120377
+ return candidates2.length === 1 ? candidates2[0] : undefined;
120378
+ }
120379
+ const exactIds = run2.stages.filter((stage) => stage.id === entry.stageKey);
120380
+ const candidates = exactIds.length > 0 ? exactIds : run2.stages.filter((stage) => stage.name === entry.stageKey);
120381
+ return candidates.length === 1 ? candidates[0] : undefined;
120382
+ }
120383
+ function pendingStageUndeliverableReason(run2, entry) {
120384
+ const stage = pendingStageDestination(run2, entry);
120385
+ if (run2.status === "cancelled") {
120386
+ return `Workflow run ${run2.id} terminated with status cancelled before stage ${entry.stageKey} started`;
120387
+ }
120388
+ if (stage?.status === "skipped") {
120389
+ return `Workflow stage ${entry.stageKey} was skipped${stage.skippedReason ? ` (${stage.skippedReason})` : ""}`;
120390
+ }
120391
+ if (isTerminalRunStatus(run2.status)) {
120392
+ return `Workflow run ${run2.id} terminated with status ${run2.status} before stage ${entry.stageKey} started`;
120393
+ }
120394
+ return;
120395
+ }
120396
+ function needsUndeliverableSettlement(run2, entry) {
120397
+ if (entry.status === "queued")
120398
+ return pendingStageUndeliverableReason(run2, entry) !== undefined;
120399
+ return entry.status === "undeliverable" && entry.undeliverableNotificationId !== undefined && entry.undeliverableNotifiedAt === undefined && entry.undeliverableReason !== undefined;
120400
+ }
120401
+ async function settleUndeliverablePendingStageMessages(activeStore, notify2) {
120402
+ const runs = activeStore.runs();
120403
+ if (!runs.some((run2) => run2.pendingStageMessages?.some((entry) => needsUndeliverableSettlement(run2, entry)))) {
120404
+ return 0;
120405
+ }
120406
+ let settled = 0;
120407
+ const rootBackend = getDurableBackend();
120408
+ for (const run2 of runs) {
120409
+ const backend = durableBackendForRun(rootBackend, runs, run2.id);
120410
+ if (backend === undefined) {
120411
+ throw new Error(`atomic-workflows: workflow run ${run2.id} has no durable owner for pending-stage settlement`);
120412
+ }
120413
+ for (const snapshotEntry of run2.pendingStageMessages ?? []) {
120414
+ let entry = snapshotEntry;
120415
+ if (entry.status === "queued") {
120416
+ const reason = pendingStageUndeliverableReason(run2, entry);
120417
+ if (reason === undefined)
120418
+ continue;
120419
+ if (await activeStore.markPendingStageMessageUndeliverable(run2.id, entry.stageKey, entry.id, reason, backend)) {
120420
+ settled += 1;
120421
+ }
120422
+ const currentRun2 = activeStore.runs().find((candidate) => candidate.id === run2.id);
120423
+ const currentEntry = currentRun2?.pendingStageMessages?.find((candidate) => candidate.stageKey === entry.stageKey && candidate.id === entry.id);
120424
+ if (currentEntry === undefined)
120425
+ continue;
120426
+ entry = currentEntry;
120427
+ }
120428
+ if (entry.status !== "undeliverable" || entry.undeliverableNotificationId === undefined || entry.undeliverableNotifiedAt !== undefined || entry.undeliverableReason === undefined)
120429
+ continue;
120430
+ if (!await notify2(entry, entry.undeliverableReason, entry.undeliverableNotificationId))
120431
+ continue;
120432
+ await activeStore.markPendingStageMessageUndeliverableNotified(run2.id, entry.stageKey, entry.id, entry.undeliverableNotificationId, new Date().toISOString(), backend);
120433
+ }
120434
+ }
120435
+ return settled;
120436
+ }
120437
+
119484
120438
  // dist/builtin/workflows/src/extension/postmortem-deps.ts
119485
120439
  function resolveStageCwd(runId) {
119486
120440
  try {
@@ -119814,7 +120768,9 @@ function withWorkflowStageSessionOptions(options, meta2, pi) {
119814
120768
  return {
119815
120769
  ...options,
119816
120770
  excludedTools,
119817
- ...meta2 ? { orchestrationContext: meta2.orchestrationContext ?? makeWorkflowStageOrchestrationContext(meta2, pi) } : {}
120771
+ ...meta2 ? {
120772
+ orchestrationContext: meta2.orchestrationContext ?? options.orchestrationContext ?? makeWorkflowStageOrchestrationContext(meta2, pi)
120773
+ } : {}
119818
120774
  };
119819
120775
  }
119820
120776
  function shouldBindStageUiContext(pi, meta2) {
@@ -119919,10 +120875,8 @@ function buildRuntimeAdapters(pi, options = {}) {
119919
120875
  const sessionOptions = withWorkflowStageSessionOptions(stripWorkflowOnlyOptions2(stageOptions) ?? {}, meta2, pi);
119920
120876
  const result = await createSession(sessionOptions);
119921
120877
  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
- });
120878
+ if (typeof bindable.bindExtensions === "function") {
120879
+ await bindable.bindExtensions(shouldBindStageUiContext(pi, meta2) ? { uiContext: makeStageExtensionUiContext(pi.ui ?? {}, meta2, broker) } : {});
119926
120880
  }
119927
120881
  return result;
119928
120882
  }
@@ -122016,6 +122970,139 @@ function buildWorkflowStatusListing(snapshots2, filter = "all", now = Date.now()
122016
122970
  };
122017
122971
  }
122018
122972
 
122973
+ // dist/builtin/workflows/src/extension/workflow-tool-answer.ts
122974
+ function answerResult(runId, stageId, status, message) {
122975
+ return { action: "answer", runId, stageId, status, message };
122976
+ }
122977
+ function terminalAnswerResult(runId, stageId, workflowStatus) {
122978
+ const message = `Cannot answer a prompt because workflow ${runId} has terminated with status ${workflowStatus}. If work remains, start a new workflow to address it.`;
122979
+ return {
122980
+ action: "answer",
122981
+ runId,
122982
+ stageId,
122983
+ status: "failed",
122984
+ code: "WORKFLOW_TERMINAL",
122985
+ workflowStatus,
122986
+ error: message,
122987
+ message
122988
+ };
122989
+ }
122990
+ function hasPayloadProperty(args) {
122991
+ return args.text !== undefined || args.response !== undefined || args.message !== undefined;
122992
+ }
122993
+ function promptPayloadFromArgs(args) {
122994
+ if (args.response !== undefined)
122995
+ return args.response;
122996
+ if (args.text !== undefined)
122997
+ return args.text;
122998
+ return args.message;
122999
+ }
123000
+ function textPayloadFromArgs(args) {
123001
+ if (args.text !== undefined)
123002
+ return args.text;
123003
+ if (typeof args.response === "string")
123004
+ return args.response;
123005
+ return args.message;
123006
+ }
123007
+ function brokerAnswerFromArgs(args) {
123008
+ if (args.response !== undefined) {
123009
+ const coerced = coerceStageInputAnswer(args.response);
123010
+ if (hasStageInputAnswerContent(coerced))
123011
+ return coerced;
123012
+ }
123013
+ const text = textPayloadFromArgs(args);
123014
+ return text !== undefined ? { text } : {};
123015
+ }
123016
+ function answerablePromptIds(stage) {
123017
+ const target = stage.workflowGraphTarget;
123018
+ const ids = [];
123019
+ const brokered = stageUiBroker.peekStagePrompt(target.runId, target.stageId);
123020
+ if (brokered !== undefined)
123021
+ ids.push(brokered.id);
123022
+ if (stage.pendingPrompt !== undefined)
123023
+ ids.push(stage.pendingPrompt.id);
123024
+ if (stage.status === "awaiting_input" && stage.promptFootprint?.kind === "custom")
123025
+ ids.push(stage.promptFootprint.id);
123026
+ return ids;
123027
+ }
123028
+ function inferPromptStageTarget(runId, promptId) {
123029
+ const pending = expandWorkflowGraph(readGraphStoreSnapshot(store), runId).stages.filter((stage) => {
123030
+ const ids = answerablePromptIds(stage);
123031
+ return promptId === undefined ? ids.length > 0 : ids.includes(promptId);
123032
+ });
123033
+ const only = pending[0];
123034
+ if (pending.length === 1 && only !== undefined) {
123035
+ return { ok: true, runId: only.workflowGraphTarget.runId, stageId: only.workflowGraphTarget.stageId };
123036
+ }
123037
+ if (pending.length === 0) {
123038
+ return promptId === undefined ? { ok: true, runId } : { ok: false, message: `No pending prompt ${promptId} in run ${runId}.` };
123039
+ }
123040
+ return {
123041
+ ok: false,
123042
+ message: `${pending.length} prompts pending; pass stageId: ${pending.map(expandedStageLabel).join(", ")}`
123043
+ };
123044
+ }
123045
+ async function workflowAnswerAction(args) {
123046
+ const target = resolveToolRunTarget(args, "No active run with a pending prompt.");
123047
+ if (target.kind === "all")
123048
+ return answerResult("--all", "", "noop", "Answer requires a single run.");
123049
+ if (target.kind === "malformed" || target.kind === "not_found") {
123050
+ return answerResult(target.target, "", "noop", target.message);
123051
+ }
123052
+ const runs = store.runs();
123053
+ const runById = new Map(runs.map((run2) => [run2.id, run2]));
123054
+ const rootRunId = reciprocalWorkflowRootRunId(runById, target.runId) ?? target.runId;
123055
+ const rootRun = runById.get(rootRunId);
123056
+ if (rootRun !== undefined && isTerminalRunStatus(rootRun.status)) {
123057
+ return terminalAnswerResult(rootRun.id, args.stageId?.trim() ?? "", rootRun.status);
123058
+ }
123059
+ const requested = resolveToolStageTarget(target.runId, args.stageId);
123060
+ const stage = requested.ok && requested.stageId === undefined ? inferPromptStageTarget(target.runId, args.promptId) : requested;
123061
+ if (!stage.ok || stage.stageId === undefined) {
123062
+ return answerResult(target.runId, "", "noop", stage.ok ? "Stage id or name is required." : stage.message);
123063
+ }
123064
+ const stageRunId = stage.runId ?? target.runId;
123065
+ const snapshot2 = store.runs().find((run2) => run2.id === stageRunId)?.stages.find((item) => item.id === stage.stageId);
123066
+ const brokerPrompt = stageUiBroker.peekStagePrompt(stageRunId, stage.stageId);
123067
+ if (brokerPrompt !== undefined && (args.promptId === undefined || args.promptId === brokerPrompt.id)) {
123068
+ if (!hasPayloadProperty(args))
123069
+ return answerResult(stageRunId, stage.stageId, "noop", "Answer requires text, response, or message.");
123070
+ const ok2 = stageUiBroker.answerStagePrompt(stageRunId, stage.stageId, brokerAnswerFromArgs(args), {
123071
+ answerSource: "workflow_tool"
123072
+ });
123073
+ return answerResult(stageRunId, stage.stageId, ok2 ? "ok" : "noop", ok2 ? `Answered input request ${brokerPrompt.id}.` : `No matching pending input request ${brokerPrompt.id}.`);
123074
+ }
123075
+ const customPrompt = snapshot2?.status === "awaiting_input" && snapshot2.promptFootprint?.kind === "custom" ? snapshot2.promptFootprint : undefined;
123076
+ if (customPrompt !== undefined && (args.promptId === undefined || args.promptId === customPrompt.id)) {
123077
+ 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.`);
123078
+ }
123079
+ const promptId = args.promptId ?? snapshot2?.pendingPrompt?.id;
123080
+ if (promptId === undefined) {
123081
+ return answerResult(stageRunId, stage.stageId, "noop", "No pending prompt to answer.");
123082
+ }
123083
+ if (!hasPayloadProperty(args))
123084
+ return answerResult(stageRunId, stage.stageId, "noop", "Answer requires text, response, or message.");
123085
+ if (stageUiBroker.wasStagePromptResolved(stageRunId, stage.stageId, promptId)) {
123086
+ return answerResult(stageRunId, stage.stageId, "ok", `Input request ${promptId} was already answered.`);
123087
+ }
123088
+ const rawPayload = promptPayloadFromArgs(args);
123089
+ const pendingPrompt = snapshot2?.pendingPrompt;
123090
+ const primitivePrompt = pendingPrompt?.id === promptId && isPrimitivePrompt(pendingPrompt) ? pendingPrompt : undefined;
123091
+ if (primitivePrompt !== undefined) {
123092
+ const coerced = coercePrimitivePromptAnswer(primitivePrompt, rawPayload);
123093
+ if (!coerced.ok)
123094
+ return answerResult(stageRunId, stage.stageId, "noop", primitivePromptAnswerRejection(promptId, primitivePrompt));
123095
+ const ok2 = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, coerced.value, {
123096
+ answerSource: "workflow_tool"
123097
+ });
123098
+ return answerResult(stageRunId, stage.stageId, ok2 ? "ok" : "noop", ok2 ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123099
+ }
123100
+ const ok = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, rawPayload, {
123101
+ answerSource: "workflow_tool"
123102
+ });
123103
+ return answerResult(stageRunId, stage.stageId, ok ? "ok" : "noop", ok ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123104
+ }
123105
+
122019
123106
  // dist/builtin/workflows/src/extension/workflow-tool-content.ts
122020
123107
  function stringifyWorkflowToolResult(result) {
122021
123108
  return JSON.stringify(result, null, 2);
@@ -122024,7 +123111,7 @@ function compactWorkflowToolMessage(result) {
122024
123111
  if (result.action === "reload") {
122025
123112
  return `${result.action}: ${result.status} — ${result.message}`;
122026
123113
  }
122027
- const target = [result.runId, result.action === "send" ? result.stageId : undefined].filter((part) => part !== undefined && part.length > 0).join("/");
123114
+ const target = [result.runId, result.action === "answer" ? result.stageId : undefined].filter((part) => part !== undefined && part.length > 0).join("/");
122028
123115
  return `${result.action}:${target ? ` ${target}` : ""} ${result.status} — ${result.message}`;
122029
123116
  }
122030
123117
  var STATUS_MESSAGE_CHAR_LIMIT = 120;
@@ -122100,7 +123187,7 @@ function renderStatusToolContent(result) {
122100
123187
  for (const entry of run2.awaitingInput)
122101
123188
  lines.push(statusAwaitingInputLine(entry));
122102
123189
  });
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).");
123190
+ 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
123191
  return lines.join(`
122105
123192
  `);
122106
123193
  }
@@ -122228,7 +123315,7 @@ function renderWorkflowToolContent(result, args) {
122228
123315
  return renderStagesToolContent(result);
122229
123316
  case "stage":
122230
123317
  return renderStageToolContent(result);
122231
- case "send":
123318
+ case "answer":
122232
123319
  case "pause":
122233
123320
  case "reload":
122234
123321
  case "interrupt":
@@ -123003,284 +124090,6 @@ function workflowTranscriptResult(args, source) {
123003
124090
  });
123004
124091
  }
123005
124092
 
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
124093
  // dist/builtin/workflows/src/extension/workflow-tool.ts
123285
124094
  async function resolveDurableInspectionSource(args, runtime) {
123286
124095
  const target = args.runId?.trim();
@@ -123308,7 +124117,7 @@ function durableInspectionError(action, runId, message) {
123308
124117
  truncated: false
123309
124118
  };
123310
124119
  }
123311
- function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflowResourcesLoaded = () => {}, sendDeps = {}) {
124120
+ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflowResourcesLoaded = () => {}) {
123312
124121
  return async function executeWorkflowTool(args, ctx, signal, onRunAccepted) {
123313
124122
  const action = args.action ?? "run";
123314
124123
  const runId = args.runId ?? "";
@@ -123397,8 +124206,8 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
123397
124206
  return workflowStageResult(args, source);
123398
124207
  return workflowTranscriptResult(args, source);
123399
124208
  }
123400
- case "send":
123401
- return awaitRequest(workflowSendAction(args, sendDeps));
124209
+ case "answer":
124210
+ return awaitRequest(workflowAnswerAction(args));
123402
124211
  case "pause":
123403
124212
  return awaitRequest(workflowPauseAction(args));
123404
124213
  case "reload":
@@ -123459,8 +124268,8 @@ function renderCall(args, opts = {}) {
123459
124268
  case "transcript":
123460
124269
  line = name === undefined ? "workflow: read stage transcript" : `workflow: read stage transcript in ${quoted(name)}`;
123461
124270
  break;
123462
- case "send":
123463
- line = name === undefined ? "workflow: send to stage" : `workflow: send to stage in ${quoted(name)}`;
124271
+ case "answer":
124272
+ line = name === undefined ? "workflow: answer prompt" : `workflow: answer prompt in ${quoted(name)}`;
123464
124273
  break;
123465
124274
  case "pause":
123466
124275
  line = name === undefined ? "workflow: pause run" : `workflow: pause run ${quoted(name)}`;
@@ -123652,9 +124461,9 @@ function renderResult(result, opts) {
123652
124461
  const suffix = r.truncated ? " (truncated)" : "";
123653
124462
  return renderNotice("WORKFLOW TRANSCRIPT", `${r.runId}/${r.stageId} ${r.source}: ${text}${suffix}`, opts, themed);
123654
124463
  }
123655
- case "send": {
124464
+ case "answer": {
123656
124465
  const r = result;
123657
- return renderNotice("WORKFLOW SEND", `${r.runId}/${r.stageId} ${r.delivery}: ${r.message}`, opts, themed);
124466
+ return renderNotice("WORKFLOW ANSWER", `${r.runId}/${r.stageId}: ${r.message}`, opts, themed);
123658
124467
  }
123659
124468
  case "pause": {
123660
124469
  const r = result;
@@ -123697,7 +124506,7 @@ function renderResult(result, opts) {
123697
124506
  }
123698
124507
 
123699
124508
  // 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.";
124509
+ 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
124510
  var DEFAULT_PROMPT_GUIDANCE = [
123702
124511
  `**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
124512
  - 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 +124538,16 @@ var DEFAULT_PROMPT_GUIDANCE = [
123729
124538
  `**Workflow discovery and lifecycle**:
123730
124539
  - 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
124540
  - 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.
124541
+ - 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
124542
  - 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.
124543
+ - 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.
124544
+ - 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.
124545
+ - \`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
124546
  - 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
124547
  - 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
124548
  - 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
124549
  - 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.
124550
+ - 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
124551
  - 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
124552
  `**Workflow authoring and handoffs**:
123744
124553
  - 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 +124609,26 @@ var WorkflowParametersSchema = Type36.Object({
123800
124609
  Type36.Literal("stages"),
123801
124610
  Type36.Literal("stage"),
123802
124611
  Type36.Literal("transcript"),
123803
- Type36.Literal("send"),
124612
+ Type36.Literal("answer"),
123804
124613
  Type36.Literal("pause"),
123805
124614
  Type36.Literal("interrupt"),
123806
124615
  Type36.Literal("quit"),
123807
124616
  Type36.Literal("resume"),
123808
124617
  Type36.Literal("reload")
123809
124618
  ], {
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."
124619
+ 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
124620
  })),
123812
124621
  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."
124622
+ 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
124623
  })),
123815
124624
  all: Type36.Optional(Type36.Boolean({
123816
124625
  description: "Apply supported run-control actions (pause/interrupt/quit) to all in-flight runs instead of one run; cannot be combined with stageId."
123817
124626
  })),
123818
124627
  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."
124628
+ 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
124629
  })),
123821
124630
  message: Type36.Optional(Type36.String({
123822
- description: "Message payload for send/follow-up/prompt/steer/resume, or optional text forwarded when resuming paused work."
124631
+ description: "Prompt answer text for answer, or optional text forwarded when resuming paused work."
123823
124632
  })),
123824
124633
  statusFilter: Type36.Optional(Type36.Union([
123825
124634
  Type36.Literal("pending"),
@@ -123851,21 +124660,11 @@ var WorkflowParametersSchema = Type36.Object({
123851
124660
  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
124661
  })),
123853
124662
  text: Type36.Optional(Type36.String({
123854
- description: "Text to send to a stage for prompt answers, steering, follow-ups, or resume messages."
124663
+ description: "Text for a pending prompt answer."
123855
124664
  })),
123856
124665
  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
124666
  promptId: Type36.Optional(Type36.String({
123868
- description: "Pending prompt identifier to answer when using the send action."
124667
+ description: "Pending prompt identifier for the answer action."
123869
124668
  })),
123870
124669
  reason: Type36.Optional(Type36.String({
123871
124670
  description: "Human-readable reason for the reload action, echoed in the reload result."
@@ -123877,7 +124676,7 @@ var WORKFLOW_TOOL_REQUEST_TIMEOUT_MS = 120000;
123877
124676
  var MUTATING_WORKFLOW_ACTIONS = new Set([
123878
124677
  "reload",
123879
124678
  "run",
123880
- "send",
124679
+ "answer",
123881
124680
  "pause",
123882
124681
  "resume",
123883
124682
  "interrupt",
@@ -123998,10 +124797,11 @@ function factory(pi) {
123998
124797
  const postMortemHandleResolver = createPostMortemHandleResolver(postMortemResolverDeps);
123999
124798
  const overlay = buildWorkflowOverlay(pi, postMortemHandleResolver);
124000
124799
  registerCompletedStageIntercomAskRouter(pi, postMortemHandleResolver);
124800
+ registerPendingStageIntercomBridge(pi, store);
124001
124801
  const workflowCommands = new Map;
124002
124802
  const storeWidgetRef = { current: null };
124003
124803
  const intercomControlRef = { current: null };
124004
- const executeWorkflowTool = makeExecuteWorkflowTool((ctx) => runtimeState.runtimeForContext(ctx), runtimeState.reloadWorkflowResources, runtimeState.ensureWorkflowResourcesLoaded, { resolvePostMortemDeps: (runId) => postMortemDepsForRun(runId, postMortemResolverDeps) });
124804
+ const executeWorkflowTool = makeExecuteWorkflowTool((ctx) => runtimeState.runtimeForContext(ctx), runtimeState.reloadWorkflowResources, runtimeState.ensureWorkflowResourcesLoaded);
124005
124805
  const executeWorkflowToolWithAutoAttach = async (args, ctx, signal, onRunAccepted) => {
124006
124806
  const result = await executeWorkflowTool(args, ctx, signal, onRunAccepted);
124007
124807
  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) {