@bastani/atomic 0.9.16-alpha.6 → 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 (214) 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/CHANGELOG.md +6 -0
  20. package/dist/builtin/subagents/package.json +1 -1
  21. package/dist/builtin/subagents/skills/qlty/SKILL.md +123 -0
  22. package/dist/builtin/subagents/skills/qlty/references/coding-with-ai-agents.md +64 -0
  23. package/dist/builtin/subagents/skills/qlty/references/commands.md +291 -0
  24. package/dist/builtin/subagents/skills/qlty/references/plugins-and-extensions.md +228 -0
  25. package/dist/builtin/subagents/skills/qlty/references/quickstart.md +110 -0
  26. package/dist/builtin/subagents/src/extension/index.bundle.mjs +173 -51
  27. package/dist/builtin/web-access/index.bundle.mjs +109 -35
  28. package/dist/builtin/web-access/package.json +1 -1
  29. package/dist/builtin/workflows/CHANGELOG.md +23 -0
  30. package/dist/builtin/workflows/README.md +40 -37
  31. package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
  32. package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +137 -133
  33. package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
  34. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  35. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  36. package/dist/builtin/workflows/builtin/chunk-5n10x7j2.js +69730 -0
  37. package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
  38. package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
  39. package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
  40. package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
  41. package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
  42. package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
  43. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  44. package/dist/builtin/workflows/builtin/chunk-cdtd3m3w.js +529 -0
  45. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  46. package/dist/builtin/workflows/builtin/chunk-hdpj1dkw.js +2447 -0
  47. package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
  48. package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
  49. package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
  50. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  51. package/dist/builtin/workflows/builtin/chunk-v04c5qmd.js +1207 -0
  52. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  53. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  54. package/dist/builtin/workflows/builtin/chunk-zf28603f.js +1080 -0
  55. package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
  56. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
  57. package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
  58. package/dist/builtin/workflows/builtin/goal.js +16 -0
  59. package/dist/builtin/workflows/builtin/index.js +49 -0
  60. package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
  61. package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
  62. package/dist/builtin/workflows/builtin/ralph.js +16 -0
  63. package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
  64. package/dist/builtin/workflows/builtin/steering-context.js +7 -0
  65. package/dist/builtin/workflows/builtin/tournament.js +13 -0
  66. package/dist/builtin/workflows/package.json +10 -7
  67. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  68. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1250 -428
  69. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +956 -427
  70. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
  71. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
  72. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  73. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
  74. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  75. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  76. package/dist/core/agent-session-custom-message-commit.js +12 -5
  77. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  78. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  79. package/dist/core/agent-session-persistent-custom-messages.js +2 -0
  80. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  81. package/dist/core/agent-session.d.ts.map +1 -1
  82. package/dist/core/agent-session.js +2 -1
  83. package/dist/core/agent-session.js.map +1 -1
  84. package/dist/core/auth-storage.d.ts +2 -0
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +4 -0
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/builtin-install-layout.d.ts +1 -1
  89. package/dist/core/builtin-install-layout.d.ts.map +1 -1
  90. package/dist/core/builtin-install-layout.js +1 -1
  91. package/dist/core/builtin-install-layout.js.map +1 -1
  92. package/dist/core/codex-fast-mode.d.ts +10 -7
  93. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  94. package/dist/core/codex-fast-mode.js +31 -13
  95. package/dist/core/codex-fast-mode.js.map +1 -1
  96. package/dist/core/extensions/context-types.d.ts +35 -0
  97. package/dist/core/extensions/context-types.d.ts.map +1 -1
  98. package/dist/core/extensions/context-types.js.map +1 -1
  99. package/dist/core/extensions/index.d.ts +1 -1
  100. package/dist/core/extensions/index.d.ts.map +1 -1
  101. package/dist/core/extensions/index.js.map +1 -1
  102. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  104. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  105. package/dist/core/model-runtime.d.ts +2 -0
  106. package/dist/core/model-runtime.d.ts.map +1 -1
  107. package/dist/core/model-runtime.js +4 -0
  108. package/dist/core/model-runtime.js.map +1 -1
  109. package/dist/core/runtime-credentials.d.ts +1 -0
  110. package/dist/core/runtime-credentials.d.ts.map +1 -1
  111. package/dist/core/runtime-credentials.js +8 -0
  112. package/dist/core/runtime-credentials.js.map +1 -1
  113. package/dist/core/sdk.d.ts.map +1 -1
  114. package/dist/core/sdk.js +5 -5
  115. package/dist/core/sdk.js.map +1 -1
  116. package/dist/core/session-manager-storage.d.ts +2 -0
  117. package/dist/core/session-manager-storage.d.ts.map +1 -1
  118. package/dist/core/session-manager-storage.js +4 -2
  119. package/dist/core/session-manager-storage.js.map +1 -1
  120. package/dist/core/slash-commands.js +1 -1
  121. package/dist/core/slash-commands.js.map +1 -1
  122. package/dist/core/workflow-stage-admission.d.ts +6 -1
  123. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  124. package/dist/core/workflow-stage-admission.js +8 -9
  125. package/dist/core/workflow-stage-admission.js.map +1 -1
  126. package/dist/index.d.ts +0 -1
  127. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/footer.js +1 -1
  129. package/dist/modes/interactive/components/footer.js.map +1 -1
  130. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  131. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  132. package/dist/modes/interactive/interactive-startup.js +1 -1
  133. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  134. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  135. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  136. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  137. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  138. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  139. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  140. package/dist/modes/rpc/rpc-client-api.js +33 -5
  141. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  142. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  144. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  145. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  146. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-types.js.map +1 -1
  148. package/docs/intercom.md +15 -6
  149. package/docs/packages.md +2 -2
  150. package/docs/providers.md +8 -3
  151. package/docs/quickstart.md +3 -2
  152. package/docs/rpc.md +21 -4
  153. package/docs/settings.md +6 -4
  154. package/docs/skills.md +4 -0
  155. package/docs/subagents.md +1 -1
  156. package/docs/usage.md +1 -1
  157. package/docs/workflows.md +112 -239
  158. package/npm-shrinkwrap.json +32 -32
  159. package/package.json +9 -13
  160. package/dist/builtin/workflows/ambient.d.ts +0 -61
  161. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  162. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  163. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  164. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  165. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  166. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  167. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  168. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  169. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  170. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  171. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  172. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  173. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  174. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  175. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  176. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  177. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  178. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -211
  179. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  180. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  181. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  182. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  183. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  184. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  185. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  186. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  187. package/dist/builtin/workflows/builtin/index.ts +0 -16
  188. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  189. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  190. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  191. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  192. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  193. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  194. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  195. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  196. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  197. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  198. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  199. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  200. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  201. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  202. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  203. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -100
  204. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -455
  205. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  206. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  207. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  208. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  209. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  210. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  211. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  212. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  213. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  214. 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
  `);
@@ -98572,6 +99133,19 @@ var E2E_VERIFICATION_GUIDANCE = [
98572
99133
  "If E2E remains impractical, record the commands attempted, observed failure output, smallest missing prerequisite, and narrower validation run; an unattempted assumption is never valid grounds to skip."
98573
99134
  ].join(`
98574
99135
  `);
99136
+ var CODE_QUALITY_VERIFICATION_GUIDANCE = [
99137
+ 'For code-quality verification — linting, auto-formatting, complexity and duplication metrics, and code smells — use the qlty skill or delegate with `skill: "qlty"`; it drives one CLI across the repository\'s languages instead of ad-hoc per-tool linter invocations.',
99138
+ "Weight this higher when the objective asks for verifiers or high code quality: enable the qlty plugins that fit this codebase, then run the check/format/metrics/smells loop and act on what it reports.",
99139
+ "Repository-defined checks in AGENTS.md/CLAUDE.md, package scripts, and CI stay authoritative; qlty supplements them rather than replacing them, and its findings count as evidence only with the command and observed output recorded."
99140
+ ].join(`
99141
+ `);
99142
+ var REPO_INTENT_MINING_GUIDANCE = [
99143
+ "Infer maintainer and requesting-user intent from repository behavior, not only written docs: mine git history (`git log`, `git log --show-signature`), merged PRs, issues and their comments, review comments, commit subjects and trailers, and CI/branch-protection config for unwritten conventions.",
99144
+ "Read for commit-signing habits, commit-message style and issue linking, changelog discipline, PR size and stacking norms, review etiquette, formatting and lint norms, and branch naming. Prefer the dominant, recent, intentional pattern over accidental drift; when signals conflict, the requesting user's own commits, PRs, and comments weigh highest — different users of one repository keep different preferences.",
99145
+ "Match inferred conventions in delivered work (for example, sign commits when the surrounding history is signed rather than skipping signing because no doc required it); in review, report deviations as convention findings backed by the mined evidence.",
99146
+ "Behavioral evidence fills gaps where the contract is silent; it never overrides the literal objective, acceptance criteria, or explicit AGENTS.md/CLAUDE.md guidance, and it does not license new contract requirements."
99147
+ ].join(`
99148
+ `);
98575
99149
  function renderE2eQaVideoReviewGuidance(knownVideoPath) {
98576
99150
  const target = knownVideoPath === undefined || knownVideoPath.length === 0 ? "Look for QA E2E video references in the goal ledger, implementation receipt, implementation notes, orchestrator report, or other review context artifacts." : `Known QA E2E video path for this run: ${knownVideoPath}`;
98577
99151
  return [
@@ -101269,6 +101843,8 @@ function renderGoalContinuationPrompt(ledger, ledgerPath, blockerThreshold, late
101269
101843
  ["worktree_discipline", WORKTREE_DISCIPLINE_CONTRACT],
101270
101844
  ["pr_handoff_policy", INTERMEDIATE_PR_HANDOFF_GUARDRAIL],
101271
101845
  ["e2e_verification", E2E_VERIFICATION_GUIDANCE],
101846
+ ["code_quality_verification", CODE_QUALITY_VERIFICATION_GUIDANCE],
101847
+ ["repository_intent", REPO_INTENT_MINING_GUIDANCE],
101272
101848
  ["goal_guidelines", GOAL_CONTINUATION_REFERENCE],
101273
101849
  ["objective", ["Continue working toward the active goal using the ledger as authoritative state for status, receipts, reviews, blockers, reducer decisions, and lifecycle events.", `The same blocker must repeat for at least ${blockerThreshold} controller observations before blocked status is available.`, "Reviewer quorum plus the reducer decides completion from reviewers' authoritative stop_review_loop signals."].join(`
101274
101850
  `)]
@@ -101294,6 +101870,8 @@ function renderReviewerPrompt(args) {
101294
101870
  ["pr_handoff_policy", INTERMEDIATE_PR_HANDOFF_GUARDRAIL],
101295
101871
  ["auditability", RECEIPT_EXPECTATIONS],
101296
101872
  ["e2e_verification", E2E_VERIFICATION_GUIDANCE],
101873
+ ["code_quality_verification", CODE_QUALITY_VERIFICATION_GUIDANCE],
101874
+ ["repository_intent", REPO_INTENT_MINING_GUIDANCE],
101297
101875
  ["final_action_policy", args.createPr ? "PR/MR/review creation is an authorized post-approval final action. If implementation and validation are proven and only that action remains, set goal_oracle_satisfied=true and stop_review_loop=true with no blocking findings, and record it as the remaining final action." : "PR/MR/review creation is not enabled; do not require or attempt it during review."],
101298
101876
  ["project_guidance", [
101299
101877
  "Apply AGENTS.md/CLAUDE.md and nearby code, test, script, config, generated-artifact, and CI conventions; specific project guidance overrides general guidance.",
@@ -103490,6 +104068,8 @@ function renderRalphReviewerPrompt(args) {
103490
104068
  ["reviewer_coordination", REVIEWER_INTERCOM_COORDINATION_PROTOCOL],
103491
104069
  ["regression_evidence", REGRESSION_EVIDENCE_CONTRACT],
103492
104070
  ["e2e_verification", E2E_VERIFICATION_GUIDANCE],
104071
+ ["code_quality_verification", CODE_QUALITY_VERIFICATION_GUIDANCE],
104072
+ ["repository_intent", REPO_INTENT_MINING_GUIDANCE],
103493
104073
  ["qa_e2e_video_review", renderE2eQaVideoReviewGuidance(args.qaVideoPath)],
103494
104074
  ["evidence_closure", EVIDENCE_CLOSURE_POLICY],
103495
104075
  [
@@ -103884,6 +104464,8 @@ Read it before implementation or delegation because it is the primary current im
103884
104464
  ["project_setup", WORKER_PREFLIGHT_CONTRACT],
103885
104465
  ["worktree_discipline", WORKTREE_DISCIPLINE_CONTRACT],
103886
104466
  ["qa_e2e_video", renderQaE2eVideoGuidance(qaVideoPath)],
104467
+ ["code_quality_verification", CODE_QUALITY_VERIFICATION_GUIDANCE],
104468
+ ["repository_intent", REPO_INTENT_MINING_GUIDANCE],
103887
104469
  [
103888
104470
  "delegation",
103889
104471
  [
@@ -106172,6 +106754,9 @@ class ScopedDurableBackend {
106172
106754
  this.persistent = inner.persistent;
106173
106755
  }
106174
106756
  registerWorkflow(_handle) {}
106757
+ persistPendingStageMessages(workflowId, messages2, logicalRunId = workflowId) {
106758
+ return this.inner.persistPendingStageMessages(this.scope.rootWorkflowId, messages2, logicalRunId);
106759
+ }
106175
106760
  recordCheckpoint(checkpoint) {
106176
106761
  this.inner.recordCheckpoint(this.remap(checkpoint));
106177
106762
  }
@@ -109509,6 +110094,7 @@ import { randomUUID as randomUUID16 } from "node:crypto";
109509
110094
 
109510
110095
  // dist/builtin/workflows/src/shared/intercom-group.ts
109511
110096
  import { randomUUID as randomUUID15 } from "node:crypto";
110097
+ var DEFAULT_INTERCOM_GROUP = "default";
109512
110098
  function workflowInvocationIntercomGroup(rootRunId) {
109513
110099
  return `workflow:${rootRunId}`;
109514
110100
  }
@@ -109518,6 +110104,12 @@ function normalizeAutoGroupSentinel(group) {
109518
110104
  const sentinel = group.trim().toLowerCase();
109519
110105
  return sentinel === "true" || sentinel === "auto" ? true : group;
109520
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
+ }
109521
110113
  function resolveStageGroup(stageOptions, workflowGroup) {
109522
110114
  if (!stageOptions)
109523
110115
  return workflowGroup;
@@ -109542,6 +110134,11 @@ function stageHasIntercomAccess(stageOptions) {
109542
110134
  return false;
109543
110135
  return true;
109544
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
+ }
109545
110142
 
109546
110143
  // dist/builtin/workflows/src/shared/budget-meter.ts
109547
110144
  var usageFields = ["input", "output", "cacheRead", "cacheWrite", "cost"];
@@ -109796,9 +110393,9 @@ function createRunBudgetController(input2) {
109796
110393
  function createParallelPrimitive(input2) {
109797
110394
  return async (steps, options = {}) => {
109798
110395
  input2.runtime.exit.throwIfWorkflowExitSelected();
109799
- const normalizeGroup = (value) => value.group === undefined ? value : { ...value, group: normalizeAutoGroupSentinel(value.group) };
109800
- const normalizedOptions = normalizeGroup(options);
109801
- 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);
109802
110399
  const needsAutoGroup = normalizedOptions.group === true || normalizedSteps.some((step) => step.group === true);
109803
110400
  const autoGroup = needsAutoGroup ? randomUUID16() : undefined;
109804
110401
  const resolveAutoGroup = (value) => value.group === true && autoGroup ? { ...value, group: autoGroup } : value;
@@ -111769,7 +112366,7 @@ function createAdmittedToolExecutionTracker(options = {}) {
111769
112366
  const inFlight = new Set;
111770
112367
  const admissions = [];
111771
112368
  const failures = [];
111772
- let nextAdmissionOrder = 0;
112369
+ let nextAdmissionOrder2 = 0;
111773
112370
  let state = "OPEN";
111774
112371
  let closing;
111775
112372
  const abandonment = Promise.withResolvers();
@@ -111820,7 +112417,7 @@ function createAdmittedToolExecutionTracker(options = {}) {
111820
112417
  return { accepted: false, error, bindNode() {}, noteCancelled() {} };
111821
112418
  }
111822
112419
  const admission = {
111823
- admissionOrder: ++nextAdmissionOrder,
112420
+ admissionOrder: ++nextAdmissionOrder2,
111824
112421
  nodeId: undefined,
111825
112422
  failure: undefined,
111826
112423
  cancelled: false,
@@ -113122,6 +113719,150 @@ function createReplayStageContext(input2) {
113122
113719
  };
113123
113720
  }
113124
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
+
113125
113866
  // dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts
113126
113867
  init_src();
113127
113868
 
@@ -113513,7 +114254,7 @@ function workflowSessionOptions(meta2) {
113513
114254
  workflow: { runId: meta2.runId, stageId: meta2.stageId, stageName: meta2.stageName }
113514
114255
  };
113515
114256
  }
113516
- function workflowOrchestrationContext(meta2, stageOptions) {
114257
+ function workflowOrchestrationContext(meta2, stageOptions, pendingStageDelivery) {
113517
114258
  const base = {
113518
114259
  kind: "workflow-stage",
113519
114260
  workflowRunId: meta2.runId,
@@ -113522,11 +114263,12 @@ function workflowOrchestrationContext(meta2, stageOptions) {
113522
114263
  constraints: { disableWorkflowTool: true }
113523
114264
  };
113524
114265
  const intercomGroup = stageHasIntercomAccess(stageOptions) ? resolveStageGroup(stageOptions, meta2.workflowIntercomGroup) : undefined;
113525
- return intercomGroup ? { ...base, intercomGroup } : base;
114266
+ const context = intercomGroup ? { ...base, intercomGroup } : base;
114267
+ return pendingStageDelivery === undefined ? context : { ...context, pendingStageDelivery };
113526
114268
  }
113527
- function stripWorkflowOnlyOptions(options, defaultSessionDir, meta2) {
114269
+ function stripWorkflowOnlyOptions(options, defaultSessionDir, meta2, pendingStageDelivery) {
113528
114270
  const classification = workflowSessionOptions(meta2);
113529
- const orchestrationContext = workflowOrchestrationContext(meta2, options);
114271
+ const orchestrationContext = workflowOrchestrationContext(meta2, options, pendingStageDelivery);
113530
114272
  if (!options) {
113531
114273
  return defaultSessionDir === undefined ? { orchestrationContext } : {
113532
114274
  orchestrationContext,
@@ -114892,7 +115634,7 @@ class StageSessionController {
114892
115634
  });
114893
115635
  let created;
114894
115636
  try {
114895
- 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), {
114896
115638
  ...this.meta,
114897
115639
  stageOptions,
114898
115640
  ...this.sharedOrchestrationContext !== undefined ? { orchestrationContext: this.sharedOrchestrationContext } : {}
@@ -114909,10 +115651,12 @@ class StageSessionController {
114909
115651
  throw this.staleCreationReason(startGeneration);
114910
115652
  }
114911
115653
  const session = attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) => this.attachSession(result));
114912
- if (session instanceof Promise)
114913
- 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;
114914
115658
  await this.opts.onSessionReady?.();
114915
- return session;
115659
+ return attachedSession;
114916
115660
  }
114917
115661
  attachSession(created) {
114918
115662
  const result = normalizeSessionCreateResult(created);
@@ -115664,6 +116408,12 @@ function createWorkflowStageFactory(input2) {
115664
116408
  const replaySource = replayDecision.kind === "replay" ? replayDecision.source : undefined;
115665
116409
  const executeReplaySource = replayDecision.kind === "execute" ? replayDecision.source : undefined;
115666
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);
115667
116417
  const stageSnapshot = {
115668
116418
  id: stageId,
115669
116419
  name,
@@ -115671,6 +116421,7 @@ function createWorkflowStageFactory(input2) {
115671
116421
  status: shouldReplay ? "completed" : "pending",
115672
116422
  parentIds: Object.freeze(parentIds),
115673
116423
  toolEvents: [],
116424
+ pendingStageDeliveryAvailable,
115674
116425
  ...shouldReplay ? {
115675
116426
  startedAt: Date.now(),
115676
116427
  endedAt: Date.now(),
@@ -115699,11 +116450,6 @@ function createWorkflowStageFactory(input2) {
115699
116450
  throwIfWorkflowExitSelected: input2.exit.throwIfWorkflowExitSelected
115700
116451
  });
115701
116452
  }
115702
- const stageOptionsForContext = executeReplaySource?.sessionFile === undefined ? options : {
115703
- ...options ?? {},
115704
- context: options?.context ?? "fork",
115705
- forkFromSessionFile: options?.forkFromSessionFile ?? executeReplaySource.sessionFile
115706
- };
115707
116453
  const applyModelFallbackMeta = (meta2) => {
115708
116454
  if (meta2.model !== undefined)
115709
116455
  stageSnapshot.model = meta2.model;
@@ -115741,6 +116487,9 @@ function createWorkflowStageFactory(input2) {
115741
116487
  workflowIntercomGroup: input2.workflowIntercomGroup,
115742
116488
  signal: input2.signal,
115743
116489
  stageOptions: stageOptionsForContext,
116490
+ ...pendingStageDeliveryAvailable ? {
116491
+ pendingStageDelivery: createWorkflowPendingStageDelivery(input2.activeStore, input2.runId, stageId, name)
116492
+ } : {},
115744
116493
  models: input2.opts.models,
115745
116494
  executionMode: input2.opts.executionMode,
115746
116495
  defaultSessionDir: input2.opts.defaultSessionDir,
@@ -115866,6 +116615,7 @@ function createWorkflowStageFactory(input2) {
115866
116615
  name,
115867
116616
  parentIds: stageSnapshot.parentIds,
115868
116617
  ...stageReplayFields(stageSnapshot),
116618
+ pendingStageDeliveryAvailable,
115869
116619
  ts: stageSnapshot.startedAt ?? Date.now()
115870
116620
  });
115871
116621
  };
@@ -116228,6 +116978,9 @@ async function run(def, inputs, opts = {}) {
116228
116978
  status: "running",
116229
116979
  stages: [],
116230
116980
  toolNodes: [],
116981
+ pendingStageMessages: [
116982
+ ...pendingStageMessagesForDurableRun(rootBackend, runId, opts.parentRun?.rootRunId ?? runId)
116983
+ ],
116231
116984
  startedAt: Date.now(),
116232
116985
  ...opts.parentRun !== undefined ? {
116233
116986
  parentRunId: opts.parentRun.runId,
@@ -116801,8 +117554,8 @@ var runWorkflow = () => {
116801
117554
  };
116802
117555
 
116803
117556
  // dist/builtin/workflows/src/extension/workflow-module-loader.ts
116804
- var WORKFLOWS_MODULE_SPECIFIER = "@bastani/workflows";
116805
- 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";
116806
117559
  var TYPEBOX_MODULE_SPECIFIER = "typebox";
116807
117560
  var WORKFLOWS_SDK_MODULE = {
116808
117561
  ...exports_sdk_surface
@@ -116821,19 +117574,27 @@ var WORKFLOWS_BUILTIN_MODULE = {
116821
117574
  var TYPEBOX_MODULE = {
116822
117575
  ...typeboxModule
116823
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
+ }
116824
117594
  var WORKFLOWS_VIRTUAL_MODULES = {
116825
- [WORKFLOWS_MODULE_SPECIFIER]: WORKFLOWS_SDK_MODULE,
116826
- [WORKFLOWS_BUILTIN_MODULE_SPECIFIER]: WORKFLOWS_BUILTIN_MODULE,
116827
- [TYPEBOX_MODULE_SPECIFIER]: TYPEBOX_MODULE,
116828
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/adversarial-verification`]: { default: adversarial_verification_default },
116829
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/classify-and-act`]: { default: classify_and_act_default },
116830
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/fan-out-and-synthesize`]: { default: fan_out_and_synthesize_default },
116831
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/generate-and-filter`]: { default: generate_and_filter_default },
116832
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/goal`]: { default: goal_default },
116833
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/loop-until-done`]: { default: loop_until_done_default },
116834
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/open-claude-design`]: { default: open_claude_design_default },
116835
- [`${WORKFLOWS_BUILTIN_MODULE_SPECIFIER}/ralph`]: { default: ralph_default },
116836
- [`${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
116837
117598
  };
116838
117599
  var workflowModuleLoader = createJiti2(import.meta.url, {
116839
117600
  moduleCache: false,
@@ -117478,27 +118239,6 @@ function ensurePostMortemStageHandle(runId, stage, deps) {
117478
118239
  const handle = deps.registry.getOrCreateDetached(runId, stage.id, () => createPostMortemStageHandle(runId, stage, sessionFile, adapters, deps.cwd, deps.defaultSessionDir));
117479
118240
  return { ok: true, handle };
117480
118241
  }
117481
- function acquirePostMortemStageHandle(runId, stage, deps) {
117482
- if (!TERMINAL_POSTMORTEM_STATUSES.has(stage.status))
117483
- return { ok: false, reason: "not_terminal" };
117484
- const existing = deps.registry.peek(runId, stage.id);
117485
- if (existing !== undefined && existing.isDisposed !== true) {
117486
- return {
117487
- ok: true,
117488
- lease: deps.registry.acquireDetached(runId, stage.id, () => existing)
117489
- };
117490
- }
117491
- if (deps.adapters?.agentSession === undefined)
117492
- return { ok: false, reason: "no_adapter" };
117493
- const sessionFile = stage.sessionFile;
117494
- if (typeof sessionFile !== "string" || sessionFile.length === 0)
117495
- return { ok: false, reason: "no_session" };
117496
- if (!isReopenableSessionTranscript(sessionFile))
117497
- return { ok: false, reason: "invalid_session" };
117498
- const adapters = deps.adapters;
117499
- const lease = deps.registry.acquireDetached(runId, stage.id, () => createPostMortemStageHandle(runId, stage, sessionFile, adapters, deps.cwd, deps.defaultSessionDir));
117500
- return { ok: true, lease };
117501
- }
117502
118242
  function createPostMortemStageHandle(runId, stage, sessionFile, adapters, cwd, defaultSessionDir) {
117503
118243
  const context = createStageContext2({
117504
118244
  runId,
@@ -118228,7 +118968,7 @@ function formatWorkflowHeartbeatNoticeText(details) {
118228
118968
  const workflowName = escapeQuotedText3(details.workflowName);
118229
118969
  const elapsed = formatWorkflowHeartbeatElapsed(details);
118230
118970
  const cadence = formatWorkflowHeartbeatCadence(details.intervalMinutes);
118231
- 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}`;
118232
118972
  }
118233
118973
  function registerWorkflowHeartbeatRenderer(options) {
118234
118974
  const register = options.registerMessageRenderer;
@@ -119460,6 +120200,241 @@ function createWorkflowExtensionRuntimeState(pi, adapters, resolveCwd = () => pi
119460
120200
  };
119461
120201
  }
119462
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
+
119463
120438
  // dist/builtin/workflows/src/extension/postmortem-deps.ts
119464
120439
  function resolveStageCwd(runId) {
119465
120440
  try {
@@ -119793,7 +120768,9 @@ function withWorkflowStageSessionOptions(options, meta2, pi) {
119793
120768
  return {
119794
120769
  ...options,
119795
120770
  excludedTools,
119796
- ...meta2 ? { orchestrationContext: meta2.orchestrationContext ?? makeWorkflowStageOrchestrationContext(meta2, pi) } : {}
120771
+ ...meta2 ? {
120772
+ orchestrationContext: meta2.orchestrationContext ?? options.orchestrationContext ?? makeWorkflowStageOrchestrationContext(meta2, pi)
120773
+ } : {}
119797
120774
  };
119798
120775
  }
119799
120776
  function shouldBindStageUiContext(pi, meta2) {
@@ -119898,10 +120875,8 @@ function buildRuntimeAdapters(pi, options = {}) {
119898
120875
  const sessionOptions = withWorkflowStageSessionOptions(stripWorkflowOnlyOptions2(stageOptions) ?? {}, meta2, pi);
119899
120876
  const result = await createSession(sessionOptions);
119900
120877
  const bindable = result.session;
119901
- if (shouldBindStageUiContext(pi, meta2) && typeof bindable.bindExtensions === "function") {
119902
- await bindable.bindExtensions({
119903
- uiContext: makeStageExtensionUiContext(pi.ui ?? {}, meta2, broker)
119904
- });
120878
+ if (typeof bindable.bindExtensions === "function") {
120879
+ await bindable.bindExtensions(shouldBindStageUiContext(pi, meta2) ? { uiContext: makeStageExtensionUiContext(pi.ui ?? {}, meta2, broker) } : {});
119905
120880
  }
119906
120881
  return result;
119907
120882
  }
@@ -121995,6 +122970,139 @@ function buildWorkflowStatusListing(snapshots2, filter = "all", now = Date.now()
121995
122970
  };
121996
122971
  }
121997
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
+
121998
123106
  // dist/builtin/workflows/src/extension/workflow-tool-content.ts
121999
123107
  function stringifyWorkflowToolResult(result) {
122000
123108
  return JSON.stringify(result, null, 2);
@@ -122003,7 +123111,7 @@ function compactWorkflowToolMessage(result) {
122003
123111
  if (result.action === "reload") {
122004
123112
  return `${result.action}: ${result.status} — ${result.message}`;
122005
123113
  }
122006
- 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("/");
122007
123115
  return `${result.action}:${target ? ` ${target}` : ""} ${result.status} — ${result.message}`;
122008
123116
  }
122009
123117
  var STATUS_MESSAGE_CHAR_LIMIT = 120;
@@ -122079,7 +123187,7 @@ function renderStatusToolContent(result) {
122079
123187
  for (const entry of run2.awaitingInput)
122080
123188
  lines.push(statusAwaitingInputLine(entry));
122081
123189
  });
122082
- 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.");
122083
123191
  return lines.join(`
122084
123192
  `);
122085
123193
  }
@@ -122207,7 +123315,7 @@ function renderWorkflowToolContent(result, args) {
122207
123315
  return renderStagesToolContent(result);
122208
123316
  case "stage":
122209
123317
  return renderStageToolContent(result);
122210
- case "send":
123318
+ case "answer":
122211
123319
  case "pause":
122212
123320
  case "reload":
122213
123321
  case "interrupt":
@@ -122982,284 +124090,6 @@ function workflowTranscriptResult(args, source) {
122982
124090
  });
122983
124091
  }
122984
124092
 
122985
- // dist/builtin/workflows/src/extension/workflow-tool-send.ts
122986
- function hasPayloadProperty(args) {
122987
- return args.text !== undefined || args.response !== undefined || args.message !== undefined;
122988
- }
122989
- function promptPayloadFromArgs(args) {
122990
- if (args.response !== undefined)
122991
- return args.response;
122992
- if (args.text !== undefined)
122993
- return args.text;
122994
- return args.message;
122995
- }
122996
- function textPayloadFromArgs(args) {
122997
- if (args.text !== undefined)
122998
- return args.text;
122999
- if (typeof args.response === "string")
123000
- return args.response;
123001
- if (args.message !== undefined)
123002
- return args.message;
123003
- return;
123004
- }
123005
- function brokerAnswerFromArgs(args) {
123006
- if (args.response !== undefined) {
123007
- const coerced = coerceStageInputAnswer(args.response);
123008
- if (hasStageInputAnswerContent(coerced))
123009
- return coerced;
123010
- }
123011
- const text = textPayloadFromArgs(args);
123012
- return text !== undefined ? { text } : {};
123013
- }
123014
- function answersPendingPrompt(delivery, promptId) {
123015
- return delivery === "answer" || delivery === "auto" || promptId !== undefined;
123016
- }
123017
- function answerablePromptIds(stage) {
123018
- const target = stage.workflowGraphTarget;
123019
- const ids = [];
123020
- const brokered = stageUiBroker.peekStagePrompt(target.runId, target.stageId);
123021
- if (brokered !== undefined)
123022
- ids.push(brokered.id);
123023
- if (stage.pendingPrompt !== undefined)
123024
- ids.push(stage.pendingPrompt.id);
123025
- if (stage.status === "awaiting_input" && stage.promptFootprint?.kind === "custom") {
123026
- ids.push(stage.promptFootprint.id);
123027
- }
123028
- return ids;
123029
- }
123030
- function inferPromptStageTarget(runId, delivery, promptId) {
123031
- if (!answersPendingPrompt(delivery, promptId))
123032
- return { ok: true, runId };
123033
- const pending = expandWorkflowGraph(readGraphStoreSnapshot(store), runId).stages.filter((stage) => {
123034
- const ids = answerablePromptIds(stage);
123035
- return promptId === undefined ? ids.length > 0 : ids.includes(promptId);
123036
- });
123037
- const only = pending[0];
123038
- if (pending.length === 1 && only !== undefined) {
123039
- return { ok: true, runId: only.workflowGraphTarget.runId, stageId: only.workflowGraphTarget.stageId };
123040
- }
123041
- if (pending.length === 0) {
123042
- return promptId === undefined ? { ok: true, runId } : { ok: false, message: `No pending prompt ${promptId} in run ${runId}.` };
123043
- }
123044
- return {
123045
- ok: false,
123046
- message: `${pending.length} prompts pending; pass stageId: ${pending.map(expandedStageLabel).join(", ")}`
123047
- };
123048
- }
123049
- function workflowSendResult(runId, stageId, delivery, status, message) {
123050
- return { action: "send", runId, stageId, delivery, status, message };
123051
- }
123052
- function terminalWorkflowSendResult(runId, stageId, workflowStatus) {
123053
- 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.`;
123054
- return {
123055
- action: "send",
123056
- runId,
123057
- stageId,
123058
- delivery: "rejected",
123059
- status: "failed",
123060
- code: "WORKFLOW_TERMINAL",
123061
- workflowStatus,
123062
- error: message,
123063
- message
123064
- };
123065
- }
123066
-
123067
- class WorkflowSendAdmissionError extends Error {
123068
- result;
123069
- constructor(result) {
123070
- super(result.message);
123071
- this.result = result;
123072
- }
123073
- }
123074
- function terminalWorkflowSendResultForRoot(rootRunId, stageId) {
123075
- const rootRun = store.runs().find((run2) => run2.id === rootRunId);
123076
- return rootRun !== undefined && isTerminalRunStatus(rootRun.status) ? terminalWorkflowSendResult(rootRun.id, stageId, rootRun.status) : undefined;
123077
- }
123078
- async function deliverStageUserMessage(handle, text, deliverAs, beforeDelivery) {
123079
- if (handle.sendUserMessage === undefined) {
123080
- throw new Error("atomic-workflows: stage handle does not support admission-aware message delivery");
123081
- }
123082
- return handle.sendUserMessage(text, { deliverAs }, beforeDelivery);
123083
- }
123084
- function deliveryMessage(delivery) {
123085
- if (delivery === "prompt")
123086
- return "Prompt started for stage.";
123087
- if (delivery === "steer")
123088
- return "Steered live stage.";
123089
- if (delivery === "followUp")
123090
- return "Follow-up queued for stage.";
123091
- return "Message handled by stage extension.";
123092
- }
123093
- async function workflowSendAction(args, deps = {}) {
123094
- const target = resolveToolRunTarget(args, "No active run to message.");
123095
- const requestedDelivery = args.delivery ?? "auto";
123096
- if (target.kind === "all") {
123097
- return workflowSendResult("--all", "", requestedDelivery, "noop", "Send requires a single run.");
123098
- }
123099
- if (target.kind === "malformed" || target.kind === "not_found") {
123100
- return workflowSendResult(target.target, "", requestedDelivery, "noop", target.message);
123101
- }
123102
- const runs = store.runs();
123103
- const runById = new Map(runs.map((run3) => [run3.id, run3]));
123104
- const rootRunId = reciprocalWorkflowRootRunId(runById, target.runId) ?? target.runId;
123105
- const rootRun = runById.get(rootRunId);
123106
- if (rootRun !== undefined && isTerminalRunStatus(rootRun.status)) {
123107
- return terminalWorkflowSendResult(rootRun.id, args.stageId?.trim() ?? "", rootRun.status);
123108
- }
123109
- const requested = resolveToolStageTarget(target.runId, args.stageId);
123110
- const stage = requested.ok && requested.stageId === undefined ? inferPromptStageTarget(target.runId, requestedDelivery, args.promptId) : requested;
123111
- if (!stage.ok || stage.stageId === undefined) {
123112
- return workflowSendResult(target.runId, "", requestedDelivery, "noop", stage.ok ? "Stage id or name is required." : stage.message);
123113
- }
123114
- const resolvedStageId = stage.stageId;
123115
- const stageRunId = stage.runId ?? target.runId;
123116
- const run2 = store.runs().find((r) => r.id === stageRunId);
123117
- const snapshot2 = run2?.stages.find((s) => s.id === stage.stageId);
123118
- const brokerPrompt = stageUiBroker.peekStagePrompt(stageRunId, stage.stageId);
123119
- const targetsBrokerPrompt = brokerPrompt !== undefined && (args.promptId === undefined || args.promptId === brokerPrompt.id) && (requestedDelivery === "answer" || args.promptId !== undefined || requestedDelivery === "auto");
123120
- if (targetsBrokerPrompt && brokerPrompt !== undefined) {
123121
- if (!hasPayloadProperty(args)) {
123122
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", "Send requires text, response, or message.");
123123
- }
123124
- const ok = stageUiBroker.answerStagePrompt(stageRunId, stage.stageId, brokerAnswerFromArgs(args), {
123125
- answerSource: "workflow_tool"
123126
- });
123127
- return workflowSendResult(stageRunId, stage.stageId, "answer", ok ? "ok" : "noop", ok ? `Answered input request ${brokerPrompt.id}.` : `No matching pending input request ${brokerPrompt.id}.`);
123128
- }
123129
- const customPrompt = snapshot2?.status === "awaiting_input" && snapshot2.promptFootprint?.kind === "custom" ? snapshot2.promptFootprint : undefined;
123130
- const targetsCustomPrompt = customPrompt !== undefined && (args.promptId === undefined || args.promptId === customPrompt.id) && (requestedDelivery === "answer" || args.promptId !== undefined || requestedDelivery === "auto");
123131
- if (targetsCustomPrompt && customPrompt !== undefined) {
123132
- 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.`);
123133
- }
123134
- const targetsPrompt = requestedDelivery === "answer" || args.promptId !== undefined || requestedDelivery === "auto" && snapshot2?.pendingPrompt !== undefined;
123135
- if (targetsPrompt) {
123136
- const promptId = args.promptId ?? snapshot2?.pendingPrompt?.id;
123137
- if (promptId === undefined) {
123138
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", "No pending prompt to answer.");
123139
- }
123140
- if (!hasPayloadProperty(args)) {
123141
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", "Send requires text, response, or message.");
123142
- }
123143
- if (stageUiBroker.wasStagePromptResolved(stageRunId, stage.stageId, promptId)) {
123144
- return workflowSendResult(stageRunId, stage.stageId, "answer", "ok", `Input request ${promptId} was already answered.`);
123145
- }
123146
- const rawPayload = promptPayloadFromArgs(args);
123147
- const pendingPrompt = snapshot2?.pendingPrompt;
123148
- const primitivePrompt = pendingPrompt?.id === promptId && isPrimitivePrompt(pendingPrompt) ? pendingPrompt : undefined;
123149
- if (primitivePrompt !== undefined) {
123150
- const coercedPayload = coercePrimitivePromptAnswer(primitivePrompt, rawPayload);
123151
- if (!coercedPayload.ok) {
123152
- return workflowSendResult(stageRunId, stage.stageId, "answer", "noop", primitivePromptAnswerRejection(promptId, primitivePrompt));
123153
- }
123154
- const ok2 = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, coercedPayload.value, {
123155
- answerSource: "workflow_tool"
123156
- });
123157
- return workflowSendResult(stageRunId, stage.stageId, "answer", ok2 ? "ok" : "noop", ok2 ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123158
- }
123159
- const ok = store.resolveStagePendingPrompt(stageRunId, stage.stageId, promptId, rawPayload, {
123160
- answerSource: "workflow_tool"
123161
- });
123162
- return workflowSendResult(stageRunId, stage.stageId, "answer", ok ? "ok" : "noop", ok ? `Answered prompt ${promptId}.` : `No matching pending prompt ${promptId}.`);
123163
- }
123164
- const text = textPayloadFromArgs(args);
123165
- if (text === undefined) {
123166
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "Send requires text, response, or message.");
123167
- }
123168
- let lease;
123169
- let handle;
123170
- const existingHandle = stageControlRegistry.peek(stageRunId, stage.stageId);
123171
- if (existingHandle !== undefined) {
123172
- lease = stageControlRegistry.acquireDetached(stageRunId, stage.stageId, () => existingHandle);
123173
- handle = lease.handle;
123174
- } else if (deps.resolvePostMortemDeps !== undefined && snapshot2 !== undefined) {
123175
- const acquired = acquirePostMortemStageHandle(stageRunId, snapshot2, deps.resolvePostMortemDeps(stageRunId));
123176
- if (acquired.ok) {
123177
- lease = acquired.lease;
123178
- handle = lease.handle;
123179
- }
123180
- }
123181
- if (handle === undefined) {
123182
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "No live handle for stage.");
123183
- }
123184
- let admitted = false;
123185
- const admitDelivery = () => {
123186
- const terminal = terminalWorkflowSendResultForRoot(rootRunId, args.stageId?.trim() ?? resolvedStageId);
123187
- if (terminal !== undefined)
123188
- throw new WorkflowSendAdmissionError(terminal);
123189
- if (lease !== undefined && !lease.commit()) {
123190
- throw new WorkflowSendAdmissionError(workflowSendResult(stageRunId, resolvedStageId, requestedDelivery, "noop", "Stage handle changed before message admission; retry against the current live stage."));
123191
- }
123192
- admitted = true;
123193
- };
123194
- const terminalPending = Promise.withResolvers();
123195
- const unsubscribeTerminal = subscribeStoreInvalidation(store, () => {
123196
- if (admitted)
123197
- return;
123198
- const terminal = terminalWorkflowSendResultForRoot(rootRunId, args.stageId?.trim() ?? resolvedStageId);
123199
- if (terminal !== undefined)
123200
- terminalPending.reject(new WorkflowSendAdmissionError(terminal));
123201
- });
123202
- const awaitAdmission = (operation) => Promise.race([operation, terminalPending.promise]);
123203
- try {
123204
- const isTerminalPostMortemStage = handle.status === "completed";
123205
- if (requestedDelivery === "resume" && handle.status !== "paused" && !isTerminalPostMortemStage) {
123206
- return workflowSendResult(stageRunId, stage.stageId, "resume", "noop", "Stage is not paused; no resume message was delivered.");
123207
- }
123208
- if (handle.status === "paused" && requestedDelivery !== "resume" && requestedDelivery !== "auto") {
123209
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "Stage is paused; resume it before sending a new message.");
123210
- }
123211
- const resumesStage = requestedDelivery === "resume" || requestedDelivery === "auto" && handle.status === "paused";
123212
- if (!resumesStage && handle.sendUserMessage === undefined) {
123213
- return workflowSendResult(stageRunId, stage.stageId, requestedDelivery, "noop", "Stage handle does not support admission-aware message delivery.");
123214
- }
123215
- if (resumesStage) {
123216
- if (isTerminalPostMortemStage) {
123217
- return workflowSendResult(stageRunId, stage.stageId, "resume", "noop", 'Cannot resume a terminal post-mortem stage; use delivery "followUp" or "prompt" to continue its retained conversation.');
123218
- }
123219
- const resumedDelivery = await awaitAdmission(handle.resume(text, admitDelivery));
123220
- if (resumedDelivery !== undefined && resumedDelivery !== "prompt") {
123221
- return workflowSendResult(stageRunId, stage.stageId, resumedDelivery, "ok", deliveryMessage(resumedDelivery));
123222
- }
123223
- if (resumedDelivery === "prompt") {
123224
- return workflowSendResult(stageRunId, stage.stageId, "prompt", "ok", "Resumed stage and started prompt.");
123225
- }
123226
- const hasResumeMessage = text.trim().length > 0;
123227
- return workflowSendResult(stageRunId, stage.stageId, "resume", "ok", hasResumeMessage ? "Resumed interrupted stage with message." : "Resumed stage.");
123228
- }
123229
- if (requestedDelivery === "steer") {
123230
- if (isTerminalPostMortemStage) {
123231
- return workflowSendResult(stageRunId, stage.stageId, "steer", "noop", 'Cannot steer a terminal post-mortem stage; use delivery "followUp" or "prompt" to continue its retained conversation.');
123232
- }
123233
- const delivery2 = await awaitAdmission(deliverStageUserMessage(handle, text, "steer", admitDelivery));
123234
- return workflowSendResult(stageRunId, stage.stageId, delivery2, "ok", deliveryMessage(delivery2));
123235
- }
123236
- if (requestedDelivery === "auto" && handle.isStreaming && !isTerminalPostMortemStage) {
123237
- const delivery2 = await awaitAdmission(deliverStageUserMessage(handle, text, "steer", admitDelivery));
123238
- return workflowSendResult(stageRunId, stage.stageId, delivery2, "ok", deliveryMessage(delivery2));
123239
- }
123240
- if (requestedDelivery === "prompt") {
123241
- const delivery2 = await awaitAdmission(deliverStageUserMessage(handle, text, undefined, admitDelivery));
123242
- const message = delivery2 === "prompt" ? "Prompt sent to stage." : deliveryMessage(delivery2);
123243
- return workflowSendResult(stageRunId, stage.stageId, delivery2, "ok", message);
123244
- }
123245
- const delivery = await awaitAdmission(deliverStageUserMessage(handle, text, "followUp", admitDelivery));
123246
- return workflowSendResult(stageRunId, stage.stageId, delivery, "ok", deliveryMessage(delivery));
123247
- } catch (error) {
123248
- if (error instanceof WorkflowSendAdmissionError)
123249
- return error.result;
123250
- throw error;
123251
- } finally {
123252
- unsubscribeTerminal();
123253
- if (!admitted) {
123254
- try {
123255
- await lease?.release();
123256
- } catch (error) {
123257
- console.warn("atomic-workflows: provisional workflow-send handle cleanup failed", error);
123258
- }
123259
- }
123260
- }
123261
- }
123262
-
123263
124093
  // dist/builtin/workflows/src/extension/workflow-tool.ts
123264
124094
  async function resolveDurableInspectionSource(args, runtime) {
123265
124095
  const target = args.runId?.trim();
@@ -123287,7 +124117,7 @@ function durableInspectionError(action, runId, message) {
123287
124117
  truncated: false
123288
124118
  };
123289
124119
  }
123290
- function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflowResourcesLoaded = () => {}, sendDeps = {}) {
124120
+ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflowResourcesLoaded = () => {}) {
123291
124121
  return async function executeWorkflowTool(args, ctx, signal, onRunAccepted) {
123292
124122
  const action = args.action ?? "run";
123293
124123
  const runId = args.runId ?? "";
@@ -123376,8 +124206,8 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
123376
124206
  return workflowStageResult(args, source);
123377
124207
  return workflowTranscriptResult(args, source);
123378
124208
  }
123379
- case "send":
123380
- return awaitRequest(workflowSendAction(args, sendDeps));
124209
+ case "answer":
124210
+ return awaitRequest(workflowAnswerAction(args));
123381
124211
  case "pause":
123382
124212
  return awaitRequest(workflowPauseAction(args));
123383
124213
  case "reload":
@@ -123438,8 +124268,8 @@ function renderCall(args, opts = {}) {
123438
124268
  case "transcript":
123439
124269
  line = name === undefined ? "workflow: read stage transcript" : `workflow: read stage transcript in ${quoted(name)}`;
123440
124270
  break;
123441
- case "send":
123442
- 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)}`;
123443
124273
  break;
123444
124274
  case "pause":
123445
124275
  line = name === undefined ? "workflow: pause run" : `workflow: pause run ${quoted(name)}`;
@@ -123631,9 +124461,9 @@ function renderResult(result, opts) {
123631
124461
  const suffix = r.truncated ? " (truncated)" : "";
123632
124462
  return renderNotice("WORKFLOW TRANSCRIPT", `${r.runId}/${r.stageId} ${r.source}: ${text}${suffix}`, opts, themed);
123633
124463
  }
123634
- case "send": {
124464
+ case "answer": {
123635
124465
  const r = result;
123636
- 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);
123637
124467
  }
123638
124468
  case "pause": {
123639
124469
  const r = result;
@@ -123676,13 +124506,14 @@ function renderResult(result, opts) {
123676
124506
  }
123677
124507
 
123678
124508
  // dist/builtin/workflows/src/extension/workflow-prompts.ts
123679
- 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.";
123680
124510
  var DEFAULT_PROMPT_GUIDANCE = [
123681
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.
123682
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.
123683
124513
  - Treat loop or stop-condition wording as a strong workflow signal, especially "do X until Y", "repeat until", "iterate until", "review/fix until passing", "run checks and fix until green", "keep going until done", or any prompt that names an approval gate or evidence requirement.
123684
124514
  - Do not force-fit an installed workflow. When another graph better matches the task, write a task-specific TypeScript workflow inline. Rich custom workflows may use deterministic branching, dynamic fan-out, child workflows, artifacts, structured outputs, human-in-the-loop prompts, gates, retries, and explicit stop conditions.
123685
124515
  - Before launching any workflow for a non-trivial task, perform a short workflow-architecture pass. Derive implementation lifecycle needs, whole-codebase research needs, independent slices, competing strategies, exact API/type/build contracts, schema or generated-artifact contracts, state transitions/lifecycle behavior, deterministic stop conditions, and required evidence. Enumerate implementation slices in this same pass, not a second planning ritual; give each slice its own objective, acceptance criteria, and gates, and require each to leave a buildable, testable repository state before the next slice. Use a compact internal coverage matrix: \`requirement/risk | required evidence | workflow/stage that produces it | gap\`. Add the topology row \`acyclic topology | node/edge sketch for branches and loops | architecture pass | unresolved back-edge\`. Unresolved material rows or back-edges must change the graph before launch.
124516
+ - In that same pass, for coding tasks, infer repository intent from repo-level behavior before freezing objectives and acceptance criteria: mine git history (including \`git log --show-signature\`), merged PRs, issues, commits, and review comments for unwritten conventions — commit signing, message style and issue linking, changelog discipline, PR size and review norms — weighing the requesting user's own activity highest, so authored workflow contracts capture norms no doc states. For non-coding tasks, mine the analogous available context sources (issue trackers, long-form docs, chat/comment threads, prior artifacts) the same way. Inferred conventions fill contract gaps; they never override the stated objective or explicit repository docs.
123686
124517
  - In that architecture pass, sketch each branch, loop, and nested workflow boundary. Identify which stages repeat, require distinct tracked work for every iteration, name the current frontier before each repeat, reject self-edges and ancestor edges, compose nested workflows through \`ctx.workflow(...)\` boundaries rather than recursive \`run\` invocation, and preserve stable per-iteration identity and call order for resume/replay.
123687
124518
  - Compare candidate workflows by guarantees, not broad objectives: run one named workflow only when it covers the lifecycle and produces evidence for every material requirement/risk. A generic implementation workflow may cover the lifecycle without covering exact API/type/build contracts, schemas/generated artifacts, state transitions, or domain-specific gates. Do not treat "has reviewers" as proof that a task-specific risk is covered.
123688
124519
  - Use these routing signals without adding decorative stages: broad repository uncertainty → Fan-out-and-synthesize with repository-focused branches; independent slices → Fan-out-and-synthesize; plausible-but-wrong contract risk → Adversarial verification or a task-specific verification stage; competing architectures or implementations → Generate-and-filter or Tournament; an explicit repeat-until condition → Loop until done; implementation lifecycle → a task-specific worker/reviewer loop; exact API/build/schema requirements → dedicated deterministic gates.
@@ -123707,16 +124538,16 @@ var DEFAULT_PROMPT_GUIDANCE = [
123707
124538
  `**Workflow discovery and lifecycle**:
123708
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.
123709
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.
123710
- - 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.
123711
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.
123712
- - 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.
123713
- - 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.
123714
- - \`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.
123715
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.
123716
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.
123717
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.
123718
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.
123719
- - 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.
123720
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.`,
123721
124552
  `**Workflow authoring and handoffs**:
123722
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.
@@ -123778,26 +124609,26 @@ var WorkflowParametersSchema = Type36.Object({
123778
124609
  Type36.Literal("stages"),
123779
124610
  Type36.Literal("stage"),
123780
124611
  Type36.Literal("transcript"),
123781
- Type36.Literal("send"),
124612
+ Type36.Literal("answer"),
123782
124613
  Type36.Literal("pause"),
123783
124614
  Type36.Literal("interrupt"),
123784
124615
  Type36.Literal("quit"),
123785
124616
  Type36.Literal("resume"),
123786
124617
  Type36.Literal("reload")
123787
124618
  ], {
123788
- 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."
123789
124620
  })),
123790
124621
  runId: Type36.Optional(Type36.String({
123791
- 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."
123792
124623
  })),
123793
124624
  all: Type36.Optional(Type36.Boolean({
123794
124625
  description: "Apply supported run-control actions (pause/interrupt/quit) to all in-flight runs instead of one run; cannot be combined with stageId."
123795
124626
  })),
123796
124627
  stageId: Type36.Optional(Type36.String({
123797
- 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."
123798
124629
  })),
123799
124630
  message: Type36.Optional(Type36.String({
123800
- 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."
123801
124632
  })),
123802
124633
  statusFilter: Type36.Optional(Type36.Union([
123803
124634
  Type36.Literal("pending"),
@@ -123829,21 +124660,11 @@ var WorkflowParametersSchema = Type36.Object({
123829
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."
123830
124661
  })),
123831
124662
  text: Type36.Optional(Type36.String({
123832
- description: "Text to send to a stage for prompt answers, steering, follow-ups, or resume messages."
124663
+ description: "Text for a pending prompt answer."
123833
124664
  })),
123834
124665
  response: Type36.Optional(WorkflowResponseSchema),
123835
- delivery: Type36.Optional(Type36.Union([
123836
- Type36.Literal("auto"),
123837
- Type36.Literal("answer"),
123838
- Type36.Literal("prompt"),
123839
- Type36.Literal("steer"),
123840
- Type36.Literal("followUp"),
123841
- Type36.Literal("resume")
123842
- ], {
123843
- description: "Delivery mode for the send action: auto answers pending prompts first, then resumes paused stages, steers streaming stages, or queues a follow-up."
123844
- })),
123845
124666
  promptId: Type36.Optional(Type36.String({
123846
- description: "Pending prompt identifier to answer when using the send action."
124667
+ description: "Pending prompt identifier for the answer action."
123847
124668
  })),
123848
124669
  reason: Type36.Optional(Type36.String({
123849
124670
  description: "Human-readable reason for the reload action, echoed in the reload result."
@@ -123855,7 +124676,7 @@ var WORKFLOW_TOOL_REQUEST_TIMEOUT_MS = 120000;
123855
124676
  var MUTATING_WORKFLOW_ACTIONS = new Set([
123856
124677
  "reload",
123857
124678
  "run",
123858
- "send",
124679
+ "answer",
123859
124680
  "pause",
123860
124681
  "resume",
123861
124682
  "interrupt",
@@ -123976,10 +124797,11 @@ function factory(pi) {
123976
124797
  const postMortemHandleResolver = createPostMortemHandleResolver(postMortemResolverDeps);
123977
124798
  const overlay = buildWorkflowOverlay(pi, postMortemHandleResolver);
123978
124799
  registerCompletedStageIntercomAskRouter(pi, postMortemHandleResolver);
124800
+ registerPendingStageIntercomBridge(pi, store);
123979
124801
  const workflowCommands = new Map;
123980
124802
  const storeWidgetRef = { current: null };
123981
124803
  const intercomControlRef = { current: null };
123982
- 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);
123983
124805
  const executeWorkflowToolWithAutoAttach = async (args, ctx, signal, onRunAccepted) => {
123984
124806
  const result = await executeWorkflowTool(args, ctx, signal, onRunAccepted);
123985
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) {