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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +4 -4
  4. package/dist/builtin/intercom/broker/broker.ts +617 -22
  5. package/dist/builtin/intercom/broker/client-message-validation.ts +9 -0
  6. package/dist/builtin/intercom/broker/client.ts +200 -10
  7. package/dist/builtin/intercom/broker/pending-send-registry.ts +4 -0
  8. package/dist/builtin/intercom/broker/send-handler.ts +90 -36
  9. package/dist/builtin/intercom/broker/send-signature.ts +20 -8
  10. package/dist/builtin/intercom/group.ts +85 -0
  11. package/dist/builtin/intercom/index.bundle.mjs +746 -55
  12. package/dist/builtin/intercom/package.json +1 -1
  13. package/dist/builtin/intercom/session-target.ts +54 -0
  14. package/dist/builtin/intercom/skills/intercom/SKILL.md +18 -3
  15. package/dist/builtin/intercom/source-ownership.ts +31 -0
  16. package/dist/builtin/intercom/types.ts +115 -0
  17. package/dist/builtin/mcp/index.bundle.mjs +175 -37
  18. package/dist/builtin/mcp/package.json +1 -1
  19. package/dist/builtin/subagents/package.json +1 -1
  20. package/dist/builtin/subagents/src/extension/index.bundle.mjs +239 -53
  21. package/dist/builtin/web-access/index.bundle.mjs +175 -37
  22. package/dist/builtin/web-access/package.json +1 -1
  23. package/dist/builtin/workflows/CHANGELOG.md +15 -0
  24. package/dist/builtin/workflows/README.md +40 -37
  25. package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
  26. package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +133 -154
  27. package/dist/builtin/workflows/builtin/chunk-17zk6ffd.js +69794 -0
  28. package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
  29. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  30. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  31. package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
  32. package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
  33. package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
  34. package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
  35. package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
  36. package/dist/builtin/workflows/builtin/chunk-8v8wmb7z.js +529 -0
  37. package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
  38. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  39. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  40. package/dist/builtin/workflows/builtin/chunk-h5e65g7g.js +1080 -0
  41. package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
  42. package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
  43. package/dist/builtin/workflows/builtin/chunk-qe6bfpbd.js +1207 -0
  44. package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
  45. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  46. package/dist/builtin/workflows/builtin/chunk-rbnj9621.js +2447 -0
  47. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  48. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  49. package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
  50. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
  51. package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
  52. package/dist/builtin/workflows/builtin/goal.js +16 -0
  53. package/dist/builtin/workflows/builtin/index.js +49 -0
  54. package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
  55. package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
  56. package/dist/builtin/workflows/builtin/ralph.js +16 -0
  57. package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
  58. package/dist/builtin/workflows/builtin/steering-context.js +7 -0
  59. package/dist/builtin/workflows/builtin/tournament.js +13 -0
  60. package/dist/builtin/workflows/package.json +10 -7
  61. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  62. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1294 -430
  63. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +1022 -429
  64. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
  65. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
  66. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  67. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
  68. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  69. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  70. package/dist/core/agent-session-custom-message-commit.js +12 -5
  71. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  72. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  73. package/dist/core/agent-session-persistent-custom-messages.js +2 -0
  74. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  75. package/dist/core/agent-session.d.ts.map +1 -1
  76. package/dist/core/agent-session.js +2 -1
  77. package/dist/core/agent-session.js.map +1 -1
  78. package/dist/core/auth-storage.d.ts +2 -0
  79. package/dist/core/auth-storage.d.ts.map +1 -1
  80. package/dist/core/auth-storage.js +4 -0
  81. package/dist/core/auth-storage.js.map +1 -1
  82. package/dist/core/builtin-install-layout.d.ts +1 -1
  83. package/dist/core/builtin-install-layout.d.ts.map +1 -1
  84. package/dist/core/builtin-install-layout.js +1 -1
  85. package/dist/core/builtin-install-layout.js.map +1 -1
  86. package/dist/core/codex-fast-mode.d.ts +10 -7
  87. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  88. package/dist/core/codex-fast-mode.js +31 -13
  89. package/dist/core/codex-fast-mode.js.map +1 -1
  90. package/dist/core/extensions/agent-events.d.ts +15 -0
  91. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  92. package/dist/core/extensions/agent-events.js.map +1 -1
  93. package/dist/core/extensions/api-types.d.ts +3 -1
  94. package/dist/core/extensions/api-types.d.ts.map +1 -1
  95. package/dist/core/extensions/api-types.js.map +1 -1
  96. package/dist/core/extensions/context-types.d.ts +35 -0
  97. package/dist/core/extensions/context-types.d.ts.map +1 -1
  98. package/dist/core/extensions/context-types.js.map +1 -1
  99. package/dist/core/extensions/event-types.d.ts +2 -2
  100. package/dist/core/extensions/event-types.d.ts.map +1 -1
  101. package/dist/core/extensions/event-types.js.map +1 -1
  102. package/dist/core/extensions/index.d.ts +1 -1
  103. package/dist/core/extensions/index.d.ts.map +1 -1
  104. package/dist/core/extensions/index.js.map +1 -1
  105. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  106. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  107. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  108. package/dist/core/extensions/runner.d.ts +6 -0
  109. package/dist/core/extensions/runner.d.ts.map +1 -1
  110. package/dist/core/extensions/runner.js +65 -1
  111. package/dist/core/extensions/runner.js.map +1 -1
  112. package/dist/core/model-runtime.d.ts +2 -0
  113. package/dist/core/model-runtime.d.ts.map +1 -1
  114. package/dist/core/model-runtime.js +4 -0
  115. package/dist/core/model-runtime.js.map +1 -1
  116. package/dist/core/runtime-credentials.d.ts +1 -0
  117. package/dist/core/runtime-credentials.d.ts.map +1 -1
  118. package/dist/core/runtime-credentials.js +8 -0
  119. package/dist/core/runtime-credentials.js.map +1 -1
  120. package/dist/core/sdk.d.ts.map +1 -1
  121. package/dist/core/sdk.js +5 -5
  122. package/dist/core/sdk.js.map +1 -1
  123. package/dist/core/session-manager-storage.d.ts +2 -0
  124. package/dist/core/session-manager-storage.d.ts.map +1 -1
  125. package/dist/core/session-manager-storage.js +4 -2
  126. package/dist/core/session-manager-storage.js.map +1 -1
  127. package/dist/core/slash-commands.js +1 -1
  128. package/dist/core/slash-commands.js.map +1 -1
  129. package/dist/core/workflow-stage-admission.d.ts +6 -1
  130. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  131. package/dist/core/workflow-stage-admission.js +8 -9
  132. package/dist/core/workflow-stage-admission.js.map +1 -1
  133. package/dist/index-extensions.d.ts +1 -1
  134. package/dist/index-extensions.d.ts.map +1 -1
  135. package/dist/index-extensions.js.map +1 -1
  136. package/dist/index.d.ts +0 -1
  137. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  138. package/dist/modes/interactive/components/footer.js +1 -1
  139. package/dist/modes/interactive/components/footer.js.map +1 -1
  140. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  141. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  142. package/dist/modes/interactive/interactive-extension-runtime.js +1 -1
  143. package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
  144. package/dist/modes/interactive/interactive-startup.js +1 -1
  145. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  146. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  147. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  148. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  149. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  150. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  151. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  152. package/dist/modes/rpc/rpc-client-api.js +33 -5
  153. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  154. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  155. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  156. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  157. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  158. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  159. package/dist/modes/rpc/rpc-types.js.map +1 -1
  160. package/docs/extensions.md +19 -0
  161. package/docs/intercom.md +15 -6
  162. package/docs/packages.md +2 -2
  163. package/docs/providers.md +8 -3
  164. package/docs/quickstart.md +2 -2
  165. package/docs/rpc.md +21 -4
  166. package/docs/settings.md +6 -4
  167. package/docs/subagents.md +1 -1
  168. package/docs/usage.md +1 -1
  169. package/docs/workflows.md +108 -239
  170. package/npm-shrinkwrap.json +32 -32
  171. package/package.json +9 -13
  172. package/dist/builtin/workflows/ambient.d.ts +0 -61
  173. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  174. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  175. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  176. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  177. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  178. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  179. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  180. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  181. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  182. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  183. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  184. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  185. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  186. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  187. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  188. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  189. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  190. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -217
  191. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  192. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  193. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  194. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  195. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  196. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  197. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  198. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  199. package/dist/builtin/workflows/builtin/index.ts +0 -16
  200. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  201. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  202. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  203. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  204. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  205. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  206. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  207. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  208. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  209. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  210. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  211. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  212. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  213. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  214. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  215. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -104
  216. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -459
  217. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  218. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  219. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  220. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  221. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  222. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  223. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  224. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  225. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  226. package/dist/builtin/workflows/builtin/verification-usage.ts +0 -44
@@ -3854,10 +3854,10 @@ function hasAssistantMessage(entries) {
3854
3854
  return entries.some((entry) => entry.type === "message" && entry.message.role === "assistant");
3855
3855
  }
3856
3856
  function persistAppendedEntry(filePath, entries, entry, flushed) {
3857
- if (!hasAssistantMessage(entries))
3858
- return false;
3859
3857
  if (flushed)
3860
3858
  appendSessionEntry(filePath, entry);
3859
+ else if (!hasAssistantMessage(entries))
3860
+ return false;
3861
3861
  else
3862
3862
  appendSessionEntries(filePath, entries);
3863
3863
  return true;
@@ -9925,6 +9925,8 @@ function appendDurableDisplayCard(session, message, delivery) {
9925
9925
  const card = { ...message, excludeFromContext: true };
9926
9926
  const admitted = message;
9927
9927
  const intentEntryId = session.sessionManager.appendCustomMessageEntry(card.customType, card.content, card.display, card.details, true, { delivery }, admitted.stageAdmissionKey);
9928
+ if (admitted.stageAdmissionKey !== undefined)
9929
+ session.sessionManager.flush();
9928
9930
  session.agent.state.messages.push(card);
9929
9931
  return { card, intentEntryId };
9930
9932
  }
@@ -10150,12 +10152,17 @@ var init_agent_session_persistent_custom_messages = __esm(() => {
10150
10152
  });
10151
10153
 
10152
10154
  // src/core/agent-session-custom-message-commit.ts
10155
+ function appendDurableStageAdmission(session, message) {
10156
+ session._appendCustomMessage(message);
10157
+ if (message.stageAdmissionKey !== undefined)
10158
+ session.sessionManager?.flush();
10159
+ }
10153
10160
  async function commitAdmittedCustomMessage(session, appMessage, options) {
10154
10161
  const owner = resolveWorkflowStageDeliveryTarget(session);
10155
10162
  if (owner !== session)
10156
10163
  return commitAdmittedCustomMessage(owner, appMessage, options);
10157
10164
  const self = session;
10158
- const useProtectedReconciliation = options?.persistWhenStreaming === true && options.triggerTurn === true && options.excludeFromContext !== true;
10165
+ const useProtectedReconciliation = options?.persistWhenStreaming === true && options.excludeFromContext !== true && (options.triggerTurn === true || options.stageAdmissionKey !== undefined);
10159
10166
  if (options?.deliverAs === "nextTurn") {
10160
10167
  self._pendingNextTurnMessages.push(appMessage);
10161
10168
  } else if (self._queuedMessagesPaused) {
@@ -10169,20 +10176,20 @@ async function commitAdmittedCustomMessage(session, appMessage, options) {
10169
10176
  } else if (self.isStreaming) {
10170
10177
  self._pendingCustomMessages.push(appMessage);
10171
10178
  } else {
10172
- self._appendCustomMessage(appMessage);
10179
+ appendDurableStageAdmission(self, appMessage);
10173
10180
  }
10174
10181
  } else if (options?.deliverAs === "interrupt" && options.triggerTurn) {
10175
10182
  const interrupt = self._enqueueInterruptCustomMessage(appMessage, options);
10176
10183
  self._workflowStageAdmission?.trackAdmittedWork(interrupt);
10177
10184
  interrupt.catch(() => {});
10178
10185
  } else if (self.isStreaming && options?.excludeFromContext === true && options.triggerTurn !== true && options.deliverAs === undefined) {
10179
- self._appendCustomMessage(appMessage);
10186
+ appendDurableStageAdmission(self, appMessage);
10180
10187
  } else if (self.isStreaming && options?.triggerTurn === false) {
10181
10188
  self._pendingCustomMessages.push(appMessage);
10182
10189
  } else if (self.isStreaming && useProtectedReconciliation) {
10183
10190
  await queueProtectedStreamingCustomMessage(self, appMessage, options?.deliverAs === "followUp" ? "followUp" : "steer");
10184
10191
  } else if (self.isStreaming && options?.persistWhenStreaming === true) {
10185
- self._appendCustomMessage(appMessage);
10192
+ appendDurableStageAdmission(self, appMessage);
10186
10193
  } else if (self.isStreaming && options?.triggerTurn !== false) {
10187
10194
  self._queueAgentMessage(appMessage, options?.deliverAs === "followUp" ? "followUp" : "steer");
10188
10195
  } else if (options?.triggerTurn) {
@@ -10205,7 +10212,7 @@ async function commitAdmittedCustomMessage(session, appMessage, options) {
10205
10212
  self._continueQueuedAgentMessages().catch(() => {});
10206
10213
  }
10207
10214
  } else {
10208
- self._appendCustomMessage(appMessage);
10215
+ appendDurableStageAdmission(self, appMessage);
10209
10216
  }
10210
10217
  }
10211
10218
  function _commitAdmittedCustomMessage(appMessage, options) {
@@ -12229,6 +12236,8 @@ class ExtensionRunner {
12229
12236
  shortcutDiagnostics = [];
12230
12237
  commandDiagnostics = [];
12231
12238
  staleMessage;
12239
+ uiPromptBinding = 0;
12240
+ activeUIPrompt;
12232
12241
  constructor(extensions, runtime, cwd, sessionManager, modelRegistry, orchestrationContext, subagentPolicy) {
12233
12242
  this.extensions = extensions;
12234
12243
  this.runtime = runtime;
@@ -12323,9 +12332,71 @@ class ExtensionRunner {
12323
12332
  this.reloadHandler = async () => {};
12324
12333
  }
12325
12334
  setUIContext(uiContext, mode = "print") {
12326
- this.uiContext = uiContext ?? noOpUIContext;
12335
+ this.endActiveUIPrompt();
12336
+ const binding = ++this.uiPromptBinding;
12337
+ this.uiContext = uiContext ? this.wrapUIPromptContext(uiContext, binding) : noOpUIContext;
12327
12338
  this.mode = mode;
12328
12339
  }
12340
+ wrapUIPromptContext(ui, binding) {
12341
+ return {
12342
+ ...ui,
12343
+ select: (title, options, opts) => this.withUIPrompt(binding, "select", title, () => ui.select(title, options, opts)),
12344
+ confirm: (title, message, opts) => this.withUIPrompt(binding, "confirm", title, () => ui.confirm(title, message, opts)),
12345
+ input: (title, placeholder, opts) => this.withUIPrompt(binding, "input", title, () => ui.input(title, placeholder, opts)),
12346
+ editor: (title, prefill, opts) => this.withUIPrompt(binding, "editor", title, () => ui.editor(title, prefill, opts)),
12347
+ custom: (factory, options) => this.withUIPrompt(binding, "custom", undefined, () => ui.custom(factory, options))
12348
+ };
12349
+ }
12350
+ withUIPrompt(binding, kind, title, run) {
12351
+ if (binding !== this.uiPromptBinding)
12352
+ return run();
12353
+ let prompt = this.activeUIPrompt;
12354
+ if (!prompt) {
12355
+ prompt = { depth: 0, kind, title };
12356
+ this.activeUIPrompt = prompt;
12357
+ this.emitUIPromptEvent({
12358
+ type: "ui_prompt_start",
12359
+ reason: "ui_prompt",
12360
+ kind,
12361
+ ...title === undefined ? {} : { title }
12362
+ });
12363
+ }
12364
+ prompt.depth += 1;
12365
+ let finished = false;
12366
+ const finish = () => {
12367
+ if (finished)
12368
+ return;
12369
+ finished = true;
12370
+ if (this.activeUIPrompt !== prompt)
12371
+ return;
12372
+ prompt.depth -= 1;
12373
+ if (prompt.depth === 0)
12374
+ this.endActiveUIPrompt(prompt);
12375
+ };
12376
+ try {
12377
+ return run().finally(finish);
12378
+ } catch (error) {
12379
+ finish();
12380
+ throw error;
12381
+ }
12382
+ }
12383
+ endActiveUIPrompt(prompt = this.activeUIPrompt) {
12384
+ if (!prompt || this.activeUIPrompt !== prompt)
12385
+ return;
12386
+ this.activeUIPrompt = undefined;
12387
+ prompt.depth = 0;
12388
+ this.emitUIPromptEvent({
12389
+ type: "ui_prompt_end",
12390
+ reason: "ui_prompt",
12391
+ kind: prompt.kind,
12392
+ ...prompt.title === undefined ? {} : { title: prompt.title }
12393
+ });
12394
+ }
12395
+ emitUIPromptEvent(event) {
12396
+ queueMicrotask(() => {
12397
+ this.emit(event);
12398
+ });
12399
+ }
12329
12400
  getUIContext() {
12330
12401
  return this.uiContext;
12331
12402
  }
@@ -21607,11 +21678,20 @@ import {
21607
21678
  function isCodexFastModeSupportedProvider(provider) {
21608
21679
  return provider === "openai" || provider === "openai-codex";
21609
21680
  }
21610
- function isCodexFastModeSupportedModel(model) {
21611
- return isCodexFastModeSupportedProvider(model.provider) || model.api === "openai-codex-responses";
21681
+ function isGitHubCopilotModel(model) {
21682
+ return model.provider === "github-copilot";
21683
+ }
21684
+ function isGitHubCopilotFastModeSupportedModel(model, credential) {
21685
+ if (!isGitHubCopilotModel(model) || credential?.type !== "oauth")
21686
+ return false;
21687
+ const fastModelIds = credential.fastModelIds;
21688
+ return Array.isArray(fastModelIds) && fastModelIds.every((modelId) => typeof modelId === "string") && fastModelIds.includes(`${model.id}-fast`);
21612
21689
  }
21613
- function hasSupportedCodexFastModeModel(models) {
21614
- return models.some(isCodexFastModeSupportedModel);
21690
+ function isCodexFastModeSupportedModel(model, copilotCredential) {
21691
+ return isCodexFastModeSupportedProvider(model.provider) || model.api === "openai-codex-responses" || model.id !== undefined && isGitHubCopilotFastModeSupportedModel({ provider: model.provider, id: model.id }, copilotCredential);
21692
+ }
21693
+ function hasSupportedCodexFastModeModel(models, copilotCredential) {
21694
+ return models.some((model) => isCodexFastModeSupportedModel(model, copilotCredential));
21615
21695
  }
21616
21696
  function isWorkflowStageOrchestrationContext(context) {
21617
21697
  return context?.kind === "workflow-stage";
@@ -21622,11 +21702,11 @@ function getCodexFastModeScope(context) {
21622
21702
  function isCodexFastModeEnabledForScope(settings, scope) {
21623
21703
  return settings[scope];
21624
21704
  }
21625
- function shouldApplyCodexFastModeForScope(model, settings, scope) {
21626
- return isCodexFastModeSupportedModel(model) && isCodexFastModeEnabledForScope(settings, scope);
21705
+ function shouldApplyCodexFastModeForScope(model, settings, scope, copilotCredential) {
21706
+ return isCodexFastModeSupportedModel(model, copilotCredential) && isCodexFastModeEnabledForScope(settings, scope);
21627
21707
  }
21628
- function shouldApplyCodexFastMode(model, settings, context) {
21629
- return shouldApplyCodexFastModeForScope(model, settings, getCodexFastModeScope(context));
21708
+ function shouldApplyCodexFastMode(model, settings, context, copilotCredential) {
21709
+ return shouldApplyCodexFastModeForScope(model, settings, getCodexFastModeScope(context), copilotCredential);
21630
21710
  }
21631
21711
  function formatCodexFastModeModelLabel(modelName, enabled) {
21632
21712
  return enabled ? `${modelName} fast` : modelName;
@@ -21767,7 +21847,7 @@ class FooterComponent {
21767
21847
  pwd += ` • ${sessionName}`;
21768
21848
  const modelName = state.model?.id || "no-model";
21769
21849
  const fastModeSettings = this.session.settingsManager.getCodexFastModeSettings();
21770
- const fastModeEnabled = state.model ? shouldApplyCodexFastMode(state.model, fastModeSettings, this.session.orchestrationContext) : false;
21850
+ const fastModeEnabled = state.model ? shouldApplyCodexFastMode(state.model, fastModeSettings, this.session.orchestrationContext, this.session.modelRuntime.getCredentialSnapshot?.("github-copilot")) : false;
21771
21851
  let modelLabel2 = modelName;
21772
21852
  if (state.model?.reasoning) {
21773
21853
  const thinkingLevel = state.thinkingLevel || "off";
@@ -39355,14 +39435,18 @@ import { AsyncLocalStorage } from "node:async_hooks";
39355
39435
 
39356
39436
  class WorkflowStageAdmissionBoundary {
39357
39437
  open = true;
39358
- completed = new Set;
39438
+ completed;
39359
39439
  inFlight = new Map;
39360
39440
  pending = new Set;
39361
39441
  invocationContext = new AsyncLocalStorage;
39362
39442
  closePromise;
39363
39443
  drainAdmittedWork;
39364
- constructor(drainAdmittedWork = async () => {}) {
39365
- this.drainAdmittedWork = drainAdmittedWork;
39444
+ constructor(drainAdmittedWork = undefined, completedKeys = []) {
39445
+ this.drainAdmittedWork = drainAdmittedWork ?? (async () => {});
39446
+ this.completed = new Set(completedKeys);
39447
+ }
39448
+ static restore(entries, drainAdmittedWork) {
39449
+ return new WorkflowStageAdmissionBoundary(drainAdmittedWork, Array.from(entries).flatMap((entry) => entry.type === "custom_message" && typeof entry.stageAdmissionKey === "string" ? [entry.stageAdmissionKey] : []));
39366
39450
  }
39367
39451
  admit(key, deliver, routeLate) {
39368
39452
  if (key !== undefined) {
@@ -39601,7 +39685,7 @@ class AgentSessionBase {
39601
39685
  this._subagentPolicy = config.subagentPolicy;
39602
39686
  this._systemPromptTransform = config.systemPromptTransform;
39603
39687
  const stageContext = config.orchestrationContext?.kind === "workflow-stage" ? config.orchestrationContext : undefined;
39604
- this._workflowStageAdmission = stageContext?.messageAdmission?.boundary ?? (stageContext ? new WorkflowStageAdmissionBoundary : undefined);
39688
+ this._workflowStageAdmission = stageContext?.messageAdmission?.boundary ?? (stageContext ? WorkflowStageAdmissionBoundary.restore(this.sessionManager.getBranch()) : undefined);
39605
39689
  if (this._workflowStageAdmission && stageContext && stageContext.messageAdmission === undefined) {
39606
39690
  stageContext.messageAdmission = {
39607
39691
  boundary: this._workflowStageAdmission,
@@ -46560,6 +46644,31 @@ class RemoteQueuePause {
46560
46644
  var init_remote_queue_pause = () => {};
46561
46645
 
46562
46646
  // src/modes/interactive-engine/isolated-runtime.ts
46647
+ import { clampThinkingLevel as clampThinkingLevel5 } from "@bastani/pi-ai/compat";
46648
+ function applyPersistedModelDefault(session, model, alreadyInScope) {
46649
+ session.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
46650
+ if (alreadyInScope || session.scopedModels.length === 0)
46651
+ return;
46652
+ const enabledModels = session.settingsManager.getEnabledModels();
46653
+ if (!enabledModels?.length)
46654
+ return;
46655
+ const modelReference = `${model.provider}/${model.id}`;
46656
+ if (enabledModels.some((pattern) => pattern.toLowerCase() === modelReference.toLowerCase()))
46657
+ return;
46658
+ session.settingsManager.setEnabledModels([...enabledModels, modelReference]);
46659
+ }
46660
+ function thinkingPersistTarget(result) {
46661
+ if (!result.provider || !result.modelId)
46662
+ return;
46663
+ return { provider: result.provider, modelId: result.modelId };
46664
+ }
46665
+ function applyPersistedThinkingDefault(session, level, target) {
46666
+ if (target) {
46667
+ session.settingsManager.setModelThinkingLevel(target.provider, target.modelId, level);
46668
+ return;
46669
+ }
46670
+ session.settingsManager.setDefaultThinkingLevel(level);
46671
+ }
46563
46672
  var IsolatedInteractiveRuntime;
46564
46673
  var init_isolated_runtime = __esm(() => {
46565
46674
  init_agent_session_runtime();
@@ -46578,6 +46687,7 @@ var init_isolated_runtime = __esm(() => {
46578
46687
  steeringMessages = [];
46579
46688
  followUpMessages = [];
46580
46689
  queueUpdateGeneration = 0;
46690
+ thinkingEpoch = 0;
46581
46691
  pendingQueueClear;
46582
46692
  engineCallbackActive = false;
46583
46693
  queuePause;
@@ -46942,43 +47052,56 @@ var init_isolated_runtime = __esm(() => {
46942
47052
  },
46943
47053
  setModel: {
46944
47054
  configurable: true,
46945
- value: async (model) => {
46946
- const selected = await this.client.setModel(model.provider, model.id);
46947
- session.agent.state.model = session.modelRuntime.getModel(selected.provider, selected.id) ?? model;
47055
+ value: async (model, options) => {
47056
+ const selected = await this.client.setModel(model.provider, model.id, options);
47057
+ const nextModel = session.modelRuntime.getModel(selected.provider, selected.id) ?? model;
47058
+ session.agent.state.model = nextModel;
46948
47059
  this.resolveModelFallback();
47060
+ if (options?.persist === true)
47061
+ await this.syncPersistedModelCatalog(session, nextModel);
46949
47062
  }
46950
47063
  },
46951
47064
  setThinkingLevel: {
46952
47065
  configurable: true,
46953
- value: (level) => {
46954
- session.agent.state.thinkingLevel = level;
46955
- this.dispatchBestEffort("set thinking level", this.client.setThinkingLevel(level));
47066
+ value: (level, options) => {
47067
+ const availableLevels = session.getAvailableThinkingLevels();
47068
+ const effectiveLevel = availableLevels.includes(level) ? level : session.model ? clampThinkingLevel5(session.model, level) : "off";
47069
+ session.agent.state.thinkingLevel = effectiveLevel;
47070
+ const epoch = ++this.thinkingEpoch;
47071
+ this.dispatchBestEffort("set thinking level", this.client.setThinkingLevelAck(level, options).then((result) => {
47072
+ this.applyThinkingAck(session, epoch, result, options?.persist === true);
47073
+ }));
46956
47074
  }
46957
47075
  },
46958
47076
  cycleModel: {
46959
47077
  configurable: true,
46960
- value: async (direction) => {
47078
+ value: async (direction, options) => {
46961
47079
  const previousModel = session.model;
46962
- const result = await this.client.cycleModel(direction);
47080
+ const result = await this.client.cycleModel(direction, options);
46963
47081
  if (!result)
46964
47082
  return;
46965
47083
  const model = session.modelRuntime.getModel(result.model.provider, result.model.id) ?? result.model;
46966
47084
  session.agent.state.model = model;
46967
47085
  session.agent.state.thinkingLevel = result.thinkingLevel;
46968
47086
  this.resolveModelFallbackAfterExplicitModelSelection(previousModel, model);
47087
+ if (options?.persist === true)
47088
+ await this.syncPersistedModelCatalog(session, model);
46969
47089
  return { ...result, model };
46970
47090
  }
46971
47091
  },
46972
47092
  cycleThinkingLevel: {
46973
47093
  configurable: true,
46974
- value: () => {
47094
+ value: (options) => {
46975
47095
  const levels = session.getAvailableThinkingLevels();
46976
47096
  if (levels.length <= 1)
46977
47097
  return;
46978
47098
  const current = levels.indexOf(session.thinkingLevel);
46979
47099
  const level = levels[(current + 1) % levels.length];
46980
47100
  session.agent.state.thinkingLevel = level;
46981
- this.dispatchBestEffort("cycle thinking level", this.client.setThinkingLevel(level));
47101
+ const epoch = ++this.thinkingEpoch;
47102
+ this.dispatchBestEffort("cycle thinking level", this.client.setThinkingLevelAck(level, options).then((result) => {
47103
+ this.applyThinkingAck(session, epoch, result, options?.persist === true);
47104
+ }));
46982
47105
  return level;
46983
47106
  }
46984
47107
  },
@@ -47022,6 +47145,14 @@ var init_isolated_runtime = __esm(() => {
47022
47145
  this.engineCallbackActive = false;
47023
47146
  }
47024
47147
  }
47148
+ applyThinkingAck(session, epoch, result, persist) {
47149
+ if (persist) {
47150
+ applyPersistedThinkingDefault(session, result.level, thinkingPersistTarget(result));
47151
+ }
47152
+ if (epoch === this.thinkingEpoch) {
47153
+ session.agent.state.thinkingLevel = result.level;
47154
+ }
47155
+ }
47025
47156
  dispatchBestEffort(label, operation) {
47026
47157
  operation.catch((error) => {
47027
47158
  if (this.health.isRecovering())
@@ -47062,6 +47193,12 @@ var init_isolated_runtime = __esm(() => {
47062
47193
  }
47063
47194
  });
47064
47195
  }
47196
+ async syncPersistedModelCatalog(session, model) {
47197
+ const alreadyInScope = session.scopedModels.some((scoped) => scoped.model.provider === model.provider && scoped.model.id === model.id);
47198
+ const catalog = await this.client.requestInternal({ type: "get_available_models" });
47199
+ this.remoteModelCatalog.apply(catalog);
47200
+ applyPersistedModelDefault(session, model, alreadyInScope);
47201
+ }
47065
47202
  refreshSessionView() {
47066
47203
  const session = super.session;
47067
47204
  const sessionFile = session.sessionFile;
@@ -47101,6 +47238,7 @@ var init_isolated_runtime = __esm(() => {
47101
47238
  session.agent.state.model = event.model;
47102
47239
  break;
47103
47240
  case "thinking_level_changed":
47241
+ this.thinkingEpoch += 1;
47104
47242
  session.agent.state.thinkingLevel = event.level;
47105
47243
  break;
47106
47244
  case "session_info_changed":
@@ -48314,7 +48452,7 @@ var init_slash_commands = __esm(() => {
48314
48452
  { name: "tree", description: "Navigate session tree (switch branches)" },
48315
48453
  { name: "thinking", description: "Set thinking level", argumentHint: "<level>" },
48316
48454
  { name: "scoped-models", description: "Enable/disable models for ctrl+p cycling" },
48317
- { name: "fast", description: "Configure Codex fast mode for chat and workflows" },
48455
+ { name: "fast", description: "Configure OpenAI fast mode for chat and workflows in supported models" },
48318
48456
  { name: "export", description: "Export session (HTML default, or specify path: .html/.jsonl)" },
48319
48457
  { name: "import", description: "Import and resume a session from a JSONL file" },
48320
48458
  { name: "share", description: "Share session as a secret GitHub gist" },
@@ -49766,7 +49904,7 @@ var init_interactive_autocomplete = __esm(() => {
49766
49904
  return [...this.session.modelRuntime.getAvailableSnapshot()];
49767
49905
  };
49768
49906
  InteractiveModeBase.prototype.hasCodexFastModeSupportedModels = function() {
49769
- return hasSupportedCodexFastModeModel(this.getCodexFastModeCandidateModels());
49907
+ return hasSupportedCodexFastModeModel(this.getCodexFastModeCandidateModels(), this.session.modelRuntime.getCredentialSnapshot?.("github-copilot"));
49770
49908
  };
49771
49909
  InteractiveModeBase.prototype.buildRemoteSlashCommands = function(localCommands) {
49772
49910
  const remote = getInteractiveEngineRemoteCommands(this.runtimeHost);
@@ -50454,7 +50592,7 @@ var init_interactive_startup = __esm(() => {
50454
50592
  if (!model) {
50455
50593
  return modelLabel2;
50456
50594
  }
50457
- const fastModeEnabled = shouldApplyCodexFastMode(model, this.session.settingsManager.getCodexFastModeSettings(), this.session.orchestrationContext);
50595
+ const fastModeEnabled = shouldApplyCodexFastMode(model, this.session.settingsManager.getCodexFastModeSettings(), this.session.orchestrationContext, this.session.modelRuntime.getCredentialSnapshot?.("github-copilot"));
50458
50596
  return formatCodexFastModeModelLabel(modelLabel2, fastModeEnabled);
50459
50597
  };
50460
50598
  InteractiveModeBase.prototype.getStartupIdentityText = function(maxWidth, gap = 0, manifestoPhase = 4) {
@@ -51380,7 +51518,7 @@ var init_interactive_extension_runtime = __esm(() => {
51380
51518
  if (shortcuts.size === 0)
51381
51519
  return;
51382
51520
  const createContext = () => ({
51383
- ui: this.createExtensionUIContext(),
51521
+ ui: extensionRunner.getUIContext(),
51384
51522
  mode: "tui",
51385
51523
  hasUI: true,
51386
51524
  cwd: this.sessionManager.getCwd(),
@@ -54969,7 +55107,7 @@ var init_interactive_selectors = __esm(() => {
54969
55107
  });
54970
55108
 
54971
55109
  // src/modes/interactive/interactive-model-routing.ts
54972
- import { clampThinkingLevel as clampThinkingLevel5 } from "@bastani/pi-ai/compat";
55110
+ import { clampThinkingLevel as clampThinkingLevel6 } from "@bastani/pi-ai/compat";
54973
55111
  function resolveThinkingSelectorDefault(rawDefault, availableLevels, model) {
54974
55112
  if (rawDefault === undefined)
54975
55113
  return;
@@ -54977,7 +55115,7 @@ function resolveThinkingSelectorDefault(rawDefault, availableLevels, model) {
54977
55115
  return rawDefault;
54978
55116
  if (!model)
54979
55117
  return availableLevels.includes("off") ? "off" : availableLevels[0];
54980
- const clamped = clampThinkingLevel5(model, rawDefault);
55118
+ const clamped = clampThinkingLevel6(model, rawDefault);
54981
55119
  if (availableLevels.includes(clamped))
54982
55120
  return clamped;
54983
55121
  return availableLevels.includes("off") ? "off" : availableLevels[0];
@@ -57195,6 +57333,12 @@ function isAttachment(value) {
57195
57333
  return false;
57196
57334
  return attachment.language === undefined || typeof attachment.language === "string";
57197
57335
  }
57336
+ function isMessageSource(value) {
57337
+ if (typeof value !== "object" || value === null || Array.isArray(value))
57338
+ return false;
57339
+ const source = value;
57340
+ return typeof source.subagentRunId === "string" && (source.subagentAgent === undefined || typeof source.subagentAgent === "string") && (source.subagentIndex === undefined || typeof source.subagentIndex === "number");
57341
+ }
57198
57342
  function isMessage(value) {
57199
57343
  if (typeof value !== "object" || value === null)
57200
57344
  return false;
@@ -57207,6 +57351,8 @@ function isMessage(value) {
57207
57351
  return false;
57208
57352
  if (message.replyError !== undefined && typeof message.replyError !== "string")
57209
57353
  return false;
57354
+ if (message.source !== undefined && !isMessageSource(message.source))
57355
+ return false;
57210
57356
  if (typeof message.content !== "object" || message.content === null)
57211
57357
  return false;
57212
57358
  const content = message.content;
@@ -57240,6 +57386,7 @@ var init_client = __esm(() => {
57240
57386
  init_pending_send_registry();
57241
57387
  BROKER_SOCKET = getBrokerSocketPath();
57242
57388
  IntercomClient = class IntercomClient extends EventEmitter2 {
57389
+ returnAddress;
57243
57390
  socket = null;
57244
57391
  _sessionId = null;
57245
57392
  _supervisorSessionId = null;
@@ -57247,9 +57394,16 @@ var init_client = __esm(() => {
57247
57394
  pendingSends = new PendingSendRegistry;
57248
57395
  pendingLists = new Map;
57249
57396
  pendingPresence = new Map;
57397
+ pendingLiveWorkflowStageRoutes = new Map;
57250
57398
  pendingSupervisorAuthorizations = new Map;
57251
57399
  disconnecting = false;
57252
57400
  disconnectError = null;
57401
+ constructor(returnAddress = randomUUID8()) {
57402
+ super();
57403
+ if (returnAddress.length === 0)
57404
+ throw new Error("Intercom return address must not be empty");
57405
+ this.returnAddress = returnAddress;
57406
+ }
57253
57407
  failPending(error) {
57254
57408
  this.pendingSends.rejectAll(error);
57255
57409
  for (const pending of this.pendingLists.values())
@@ -57258,9 +57412,12 @@ var init_client = __esm(() => {
57258
57412
  pending.reject(error);
57259
57413
  for (const pending of this.pendingPresence.values())
57260
57414
  pending.reject(error);
57415
+ for (const pending of this.pendingLiveWorkflowStageRoutes.values())
57416
+ pending.reject(error);
57261
57417
  this.pendingLists.clear();
57262
57418
  this.pendingSupervisorAuthorizations.clear();
57263
57419
  this.pendingPresence.clear();
57420
+ this.pendingLiveWorkflowStageRoutes.clear();
57264
57421
  }
57265
57422
  get sessionId() {
57266
57423
  return this._sessionId;
@@ -57386,6 +57543,7 @@ var init_client = __esm(() => {
57386
57543
  writeMessage(socket, {
57387
57544
  type: "register",
57388
57545
  session,
57546
+ returnAddress: this.returnAddress,
57389
57547
  ...supervisor ? { supervisor } : {},
57390
57548
  ...supervisorOwnerToken ? { supervisorOwnerToken } : {}
57391
57549
  });
@@ -57460,6 +57618,58 @@ var init_client = __esm(() => {
57460
57618
  this.emit("message", from, message, channel);
57461
57619
  break;
57462
57620
  }
57621
+ case "pending_stage_message": {
57622
+ const { requestId, from, senderRegistrationName, senderReturnAddress, runId, stageKey, message, live } = brokerMessage;
57623
+ if (typeof requestId !== "string" || !isSessionInfo(from) || senderRegistrationName !== undefined && typeof senderRegistrationName !== "string" || senderReturnAddress !== undefined && typeof senderReturnAddress !== "string" || typeof runId !== "string" || typeof stageKey !== "string" || !isMessage(message) || live !== undefined && typeof live !== "boolean") {
57624
+ throw new Error("Invalid pending-stage message event");
57625
+ }
57626
+ this.emit("pending_stage_message", {
57627
+ requestId,
57628
+ from,
57629
+ ...senderRegistrationName === undefined ? {} : { senderRegistrationName },
57630
+ ...senderReturnAddress === undefined ? {} : { senderReturnAddress },
57631
+ runId,
57632
+ stageKey,
57633
+ message,
57634
+ ...live === true ? { live: true } : {}
57635
+ });
57636
+ break;
57637
+ }
57638
+ case "pending_stage_notification": {
57639
+ const { requestId, from, message } = brokerMessage;
57640
+ if (typeof requestId !== "string" || !isSessionInfo(from) || !isMessage(message)) {
57641
+ throw new Error("Invalid pending-stage notification event");
57642
+ }
57643
+ this.emit("pending_stage_notification", {
57644
+ requestId,
57645
+ from,
57646
+ message
57647
+ });
57648
+ break;
57649
+ }
57650
+ case "live_workflow_stage_route_registered": {
57651
+ const { requestId } = brokerMessage;
57652
+ if (typeof requestId !== "string")
57653
+ throw new Error("Invalid live workflow-stage route registration");
57654
+ const pending = this.pendingLiveWorkflowStageRoutes.get(requestId);
57655
+ if (!pending)
57656
+ return;
57657
+ this.pendingLiveWorkflowStageRoutes.delete(requestId);
57658
+ pending.resolve();
57659
+ break;
57660
+ }
57661
+ case "queued": {
57662
+ const { messageId, attemptId, runId, stageKey, position } = brokerMessage;
57663
+ if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof runId !== "string" || typeof stageKey !== "string" || typeof position !== "number" || position < 1) {
57664
+ throw new Error("Invalid queued message");
57665
+ }
57666
+ const result = { id: messageId, delivered: false, queued: true, runId, stageKey, position };
57667
+ if (attemptId === undefined)
57668
+ this.pendingSends.resolveLegacy(messageId, result);
57669
+ else
57670
+ this.pendingSends.resolve(messageId, attemptId, result);
57671
+ break;
57672
+ }
57463
57673
  case "delivered": {
57464
57674
  const { messageId, attemptId } = brokerMessage;
57465
57675
  if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string") {
@@ -57473,11 +57683,16 @@ var init_client = __esm(() => {
57473
57683
  break;
57474
57684
  }
57475
57685
  case "delivery_failed": {
57476
- const { messageId, attemptId, reason } = brokerMessage;
57477
- if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof reason !== "string") {
57686
+ const { messageId, attemptId, reason, reasonCode } = brokerMessage;
57687
+ if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof reason !== "string" || reasonCode !== undefined && reasonCode !== "message_id_conflict") {
57478
57688
  throw new Error("Invalid delivery_failed message");
57479
57689
  }
57480
- const result = { id: messageId, delivered: false, reason };
57690
+ const result = {
57691
+ id: messageId,
57692
+ delivered: false,
57693
+ reason,
57694
+ ...reasonCode === undefined ? {} : { reasonCode }
57695
+ };
57481
57696
  if (attemptId === undefined)
57482
57697
  this.pendingSends.resolveLegacy(messageId, result);
57483
57698
  else
@@ -57656,13 +57871,61 @@ var init_client = __esm(() => {
57656
57871
  }
57657
57872
  });
57658
57873
  }
57874
+ registerPendingStageRoute(runId, group, capability) {
57875
+ writeMessage(this.requireActiveSocket(), { type: "register_pending_stage_route", runId, group, capability });
57876
+ }
57877
+ registerLiveWorkflowStageRoute(runId, stageKeys, capability) {
57878
+ let socket;
57879
+ try {
57880
+ socket = this.requireActiveSocket();
57881
+ } catch (error) {
57882
+ return Promise.reject(toError(error));
57883
+ }
57884
+ const requestId = randomUUID8();
57885
+ return new Promise((resolve15, reject) => {
57886
+ this.pendingLiveWorkflowStageRoutes.set(requestId, { resolve: resolve15, reject });
57887
+ try {
57888
+ writeMessage(socket, {
57889
+ type: "register_live_workflow_stage_route",
57890
+ requestId,
57891
+ runId,
57892
+ stageKeys: [...stageKeys],
57893
+ capability
57894
+ });
57895
+ } catch (error) {
57896
+ this.pendingLiveWorkflowStageRoutes.delete(requestId);
57897
+ reject(toError(error));
57898
+ }
57899
+ });
57900
+ }
57901
+ respondPendingStageMessage(requestId, result) {
57902
+ const socket = this.requireActiveSocket();
57903
+ writeMessage(socket, result.outcome === "queued" ? { type: "pending_stage_message_result", requestId, outcome: "queued", position: result.position } : result.outcome === "refused" ? {
57904
+ type: "pending_stage_message_result",
57905
+ requestId,
57906
+ outcome: "refused",
57907
+ reason: result.reason,
57908
+ ...result.reasonCode === undefined ? {} : { reasonCode: result.reasonCode }
57909
+ } : { type: "pending_stage_message_result", requestId, outcome: result.outcome });
57910
+ }
57911
+ respondPendingStageNotification(requestId, delivered) {
57912
+ writeMessage(this.requireActiveSocket(), { type: "pending_stage_notification_result", requestId, delivered });
57913
+ }
57659
57914
  send(to, options) {
57660
57915
  return this.sendFrame("send", to, options);
57661
57916
  }
57917
+ sendPendingStageNotification(runId, capability, to, senderRegistrationName, options, senderReturnAddress) {
57918
+ return this.sendFrame("send_pending_stage_notification", to, options, {
57919
+ runId,
57920
+ capability,
57921
+ ...senderRegistrationName === undefined ? {} : { senderRegistrationName },
57922
+ ...senderReturnAddress === undefined ? {} : { senderReturnAddress }
57923
+ });
57924
+ }
57662
57925
  sendToSupervisor(to, options) {
57663
57926
  return this.sendFrame("supervisor_send", to, options);
57664
57927
  }
57665
- sendFrame(type, to, options) {
57928
+ sendFrame(type, to, options, extra = undefined) {
57666
57929
  let socket;
57667
57930
  try {
57668
57931
  socket = this.requireActiveSocket();
@@ -57688,7 +57951,7 @@ var init_client = __esm(() => {
57688
57951
  content: { text: options.text, attachments: options.attachments }
57689
57952
  };
57690
57953
  try {
57691
- writeMessage(socket, { type, to, message, attemptId: acquired.attempt.attemptId });
57954
+ writeMessage(socket, { type, to, message, attemptId: acquired.attempt.attemptId, ...extra });
57692
57955
  } catch (error) {
57693
57956
  this.pendingSends.reject(acquired.attempt, toError(error));
57694
57957
  }
@@ -59216,6 +59479,10 @@ Use this to communicate findings, request help, or coordinate work with other se
59216
59479
  Sessions belong to an intercom group and can ONLY message sessions in the same group;
59217
59480
  cross-group sends are rejected by the broker. Ungrouped sessions share the "default" group.
59218
59481
 
59482
+ For send, live session names and exact full session IDs remain supported. For a known
59483
+ workflow stage, use the exact \`<runId>:<stageKey>\` target; send messages to pending stages
59484
+ queue automatically.
59485
+
59219
59486
  Usage:
59220
59487
  intercom({ action: "list" }) → List sessions in your group
59221
59488
  intercom({ action: "list", group: "name" }) → Read-only peek at another group's sessions
@@ -59236,7 +59503,7 @@ does not grant cross-group access: contact_supervisor is the only cross-group pa
59236
59503
  description: "Action: 'list', 'join', 'leave', 'send', 'ask', 'reply', 'pending', or 'status'"
59237
59504
  }),
59238
59505
  to: Type14.Optional(Type14.String({
59239
- description: "Exact session name or exact full session ID (for 'send', 'ask', or targeted 'reply')"
59506
+ description: "Live session name, exact full session ID, or exact `<runId>:<stageKey>` for a known workflow stage; send messages to pending stages queue automatically (for 'send', 'ask', or targeted 'reply')"
59240
59507
  })),
59241
59508
  message: Type14.Optional(Type14.String({
59242
59509
  description: "Message to send (for 'send', 'ask', or 'reply' action)"
@@ -59435,6 +59702,26 @@ ${message}${attachmentText}`);
59435
59702
  attachments,
59436
59703
  replyTo
59437
59704
  });
59705
+ if (result.queued === true) {
59706
+ pi.appendEntry("intercom_sent", {
59707
+ to,
59708
+ message: { text: message, attachments, replyTo },
59709
+ messageId: result.id,
59710
+ timestamp: Date.now()
59711
+ });
59712
+ return {
59713
+ content: [{ type: "text", text: `Message queued for ${to}` }],
59714
+ isError: false,
59715
+ details: {
59716
+ messageId: result.id,
59717
+ delivered: false,
59718
+ queued: true,
59719
+ runId: result.runId,
59720
+ stageKey: result.stageKey,
59721
+ position: result.position
59722
+ }
59723
+ };
59724
+ }
59438
59725
  if (!result.delivered) {
59439
59726
  const errorText = result.reason ?? "Session may not exist or has disconnected.";
59440
59727
  return {
@@ -59553,10 +59840,16 @@ ${message}${attachmentText}`);
59553
59840
  if (!sendResult.delivered) {
59554
59841
  const errorText = sendResult.reason ?? "Session may not exist or has disconnected.";
59555
59842
  wait.cancel(new Error(`Message to "${to}" was not delivered: ${errorText}`));
59843
+ const pendingStageAskRefusal = errorText.startsWith("Cannot ask a workflow stage whose session has not initialized.");
59556
59844
  return {
59557
59845
  content: [{ type: "text", text: `Message to "${to}" was not delivered: ${errorText}` }],
59558
59846
  isError: true,
59559
- details: { error: true }
59847
+ details: pendingStageAskRefusal ? {
59848
+ error: true,
59849
+ refusal: "pending_stage_ask_unsupported",
59850
+ recommendedAction: "send",
59851
+ reason: errorText
59852
+ } : { error: true }
59560
59853
  };
59561
59854
  }
59562
59855
  pi.appendEntry("intercom_sent", {
@@ -60127,6 +60420,13 @@ function registerIntercomLifecycle(pi, deps) {
60127
60420
  console.error(`Intercom failed to disconnect during ${reason.toLowerCase()}; continuing cleanup:`, error);
60128
60421
  }
60129
60422
  }
60423
+ if (deps.disconnectAuxiliaryClients) {
60424
+ try {
60425
+ await deps.disconnectAuxiliaryClients();
60426
+ } catch (error) {
60427
+ console.error(`Intercom failed to disconnect auxiliary clients during ${reason.toLowerCase()}; continuing cleanup:`, error);
60428
+ }
60429
+ }
60130
60430
  deps.restoreIntercomSessionIdEnv?.();
60131
60431
  deps.setRuntimeContext(null);
60132
60432
  deps.setCurrentSessionId(null);
@@ -60921,12 +61221,27 @@ function createIncomingMessageSender(input2) {
60921
61221
  }
60922
61222
  const baseOptions = {
60923
61223
  stageAdmissionKey: `intercom:${entry.message.id}`,
61224
+ persistWhenStreaming: true,
60924
61225
  ...stageAdmissionBarrier ? { stageAdmissionBarrier } : {}
60925
61226
  };
60926
61227
  const options = delivery === "trigger" ? { ...baseOptions, triggerTurn: true } : delivery === "followUp" ? { ...baseOptions, deliverAs: "followUp" } : baseOptions;
60927
61228
  return Promise.resolve(input2.pi.sendMessage(buildIncomingCustomMessage(entry), options));
60928
61229
  };
60929
61230
  }
61231
+ function framePendingStageTimestamp(timestamp) {
61232
+ const date = new Date(timestamp);
61233
+ return Number.isNaN(date.getTime()) ? String(timestamp) : date.toISOString();
61234
+ }
61235
+ function framePreStartPendingStageMessage(entry) {
61236
+ return {
61237
+ ...entry,
61238
+ bodyText: `**Messages received before you started**
61239
+
61240
+ Sent: ${framePendingStageTimestamp(entry.message.timestamp)}
61241
+
61242
+ ${entry.bodyText}`
61243
+ };
61244
+ }
60930
61245
  function buildIncomingCustomMessage(entry) {
60931
61246
  const senderDisplay = entry.from.name || entry.from.id;
60932
61247
  const replyInstruction = entry.replyCommand ? `
@@ -61016,7 +61331,28 @@ class InboundIdleQueue {
61016
61331
  class InboundMessageAdmission {
61017
61332
  delivered = new DeliveredMessageCache;
61018
61333
  pending = new Map;
61334
+ persistentlyDelivered = new Set;
61335
+ restore(entries) {
61336
+ this.persistentlyDelivered.clear();
61337
+ for (const value of entries) {
61338
+ if (typeof value !== "object" || value === null)
61339
+ continue;
61340
+ const entry = value;
61341
+ if (entry.customType !== "intercom_message" || typeof entry.stageAdmissionKey !== "string")
61342
+ continue;
61343
+ if (typeof entry.details !== "object" || entry.details === null)
61344
+ continue;
61345
+ const details = entry.details;
61346
+ if (!isSessionInfo(details.from) || !isMessage(details.message))
61347
+ continue;
61348
+ if (entry.stageAdmissionKey !== `intercom:${details.message.id}`)
61349
+ continue;
61350
+ this.persistentlyDelivered.add(details.message.id);
61351
+ }
61352
+ }
61019
61353
  admit(from, message) {
61354
+ if (this.persistentlyDelivered.has(message.id))
61355
+ return { kind: "duplicate" };
61020
61356
  const signature = JSON.stringify({ from: from.id, message });
61021
61357
  if (this.delivered.lookup(message.id, signature) !== "miss")
61022
61358
  return { kind: "duplicate" };
@@ -61052,6 +61388,7 @@ class InboundMessageAdmission {
61052
61388
  return;
61053
61389
  this.pending.delete(reservation.messageId);
61054
61390
  this.delivered.record(reservation.messageId, reservation.signature);
61391
+ this.persistentlyDelivered.add(reservation.messageId);
61055
61392
  pending.resolve();
61056
61393
  }
61057
61394
  release(reservation, error = new Error("Inbound message admission released")) {
@@ -61391,6 +61728,18 @@ __export(exports_index_heavy, {
61391
61728
  default: () => piIntercomExtension
61392
61729
  });
61393
61730
  import { appendFileSync as appendFileSync2 } from "node:fs";
61731
+ function isPendingStageUndeliverableEvent(value) {
61732
+ if (typeof value !== "object" || value === null)
61733
+ return false;
61734
+ const event = value;
61735
+ return typeof event.runId === "string" && typeof event.handled === "boolean" && typeof event.senderId === "string" && (event.senderRegistrationName === undefined || typeof event.senderRegistrationName === "string") && (event.senderReturnAddress === undefined || typeof event.senderReturnAddress === "string") && typeof event.messageId === "string" && typeof event.notificationId === "string" && typeof event.reason === "string";
61736
+ }
61737
+ function isPendingStageRouteRegistrationEvent(value) {
61738
+ if (typeof value !== "object" || value === null)
61739
+ return false;
61740
+ const event = value;
61741
+ return typeof event.runId === "string" && event.runId.length > 0 && typeof event.group === "string" && typeof event.capability === "string" && event.capability.length > 0;
61742
+ }
61394
61743
  function piIntercomExtension(pi, testOverrides = {}) {
61395
61744
  const inheritedIntercomSessionId = process.env[INTERCOM_SESSION_ID_ENV];
61396
61745
  const restoreIntercomSessionIdEnv = () => {
@@ -61400,6 +61749,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
61400
61749
  process.env[INTERCOM_SESSION_ID_ENV] = inheritedIntercomSessionId;
61401
61750
  };
61402
61751
  let client = null;
61752
+ let clientRegistrationGroup = null;
61403
61753
  const config = loadConfig2();
61404
61754
  const legacyChildOrchestratorMetadata = readChildOrchestratorMetadata();
61405
61755
  let runtimeContext = null;
@@ -61426,6 +61776,8 @@ function piIntercomExtension(pi, testOverrides = {}) {
61426
61776
  const foregroundDetachHandoff = new ForegroundDetachHandoff(pi);
61427
61777
  const pendingIdleMessages = new InboundIdleQueue;
61428
61778
  const inboundDeliveries = new InboundMessageAdmission;
61779
+ const pendingStageRoutes = new Map;
61780
+ const pendingStageRouteClients = new Map;
61429
61781
  const supervisorAuthorizations = new SupervisorAuthorizationRegistry;
61430
61782
  let inboundFlushTimer = null;
61431
61783
  function rejectReplyWaiter(error) {
@@ -61591,7 +61943,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
61591
61943
  }).catch(() => {});
61592
61944
  }
61593
61945
  testOverrides.captureInboundHandler?.(handleIncomingMessage);
61594
- function handleIncomingMessage(ctx, from, message, channel) {
61946
+ function handleIncomingMessage(ctx, from, message, channel, receivedBeforeStageStart = false) {
61595
61947
  const messageGeneration = runtimeGeneration;
61596
61948
  const liveContext = getLiveContext(ctx, messageGeneration);
61597
61949
  if (!liveContext) {
@@ -61601,7 +61953,11 @@ function piIntercomExtension(pi, testOverrides = {}) {
61601
61953
  const attachmentText = message.content.attachments?.length ? formatAttachments(message.content.attachments) : "";
61602
61954
  const bodyText = `${message.content.text}${attachmentText}`;
61603
61955
  const replyCommand = config.replyHint && message.expectsReply ? `intercom({ action: "reply", message: "..." })` : undefined;
61604
- const entry = { from, message, replyCommand, bodyText, ...channel ? { channel } : {} };
61956
+ const rawEntry = { from, message, replyCommand, bodyText, ...channel ? { channel } : {} };
61957
+ const entry = receivedBeforeStageStart ? framePreStartPendingStageMessage(rawEntry) : rawEntry;
61958
+ if (receivedBeforeStageStart) {
61959
+ return sendIncomingMessage(entry, "prelude", messageGeneration, false);
61960
+ }
61605
61961
  const stageClosed = liveContext.orchestrationContext?.kind === "workflow-stage" && liveContext.orchestrationContext.messageAdmission?.isOpen() === false;
61606
61962
  if (stageClosed) {
61607
61963
  routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => sendIncomingMessage(entry, "trigger", messageGeneration, false), () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)));
@@ -61639,7 +61995,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
61639
61995
  stageDelivery.then(commit, release);
61640
61996
  return;
61641
61997
  }
61642
- (async () => {
61998
+ return (async () => {
61643
61999
  try {
61644
62000
  const activeContext = getLiveContext(liveContext, messageGeneration);
61645
62001
  if (!activeContext) {
@@ -61695,6 +62051,110 @@ function piIntercomExtension(pi, testOverrides = {}) {
61695
62051
  }
61696
62052
  })();
61697
62053
  }
62054
+ function handlePendingStageMessage(nextClient, request, isCurrent) {
62055
+ if (!isCurrent())
62056
+ return;
62057
+ const event = { ...request, handled: false };
62058
+ try {
62059
+ pi.events.emit(PENDING_STAGE_MESSAGE_EVENT, event);
62060
+ } catch (error) {
62061
+ nextClient.respondPendingStageMessage(request.requestId, { outcome: "refused", reason: toError3(error).message });
62062
+ return;
62063
+ }
62064
+ if (!event.handled || event.completion === undefined) {
62065
+ nextClient.respondPendingStageMessage(request.requestId, {
62066
+ outcome: "refused",
62067
+ reason: "Session not found"
62068
+ });
62069
+ return;
62070
+ }
62071
+ event.completion.then((result) => nextClient.respondPendingStageMessage(request.requestId, result), (error) => nextClient.respondPendingStageMessage(request.requestId, {
62072
+ outcome: "refused",
62073
+ reason: toError3(error).message
62074
+ }));
62075
+ }
62076
+ async function admitPendingStageNotification(nextClient, request) {
62077
+ const messageGeneration = runtimeGeneration;
62078
+ const liveContext = client === nextClient ? getLiveContext() : null;
62079
+ if (liveContext === null)
62080
+ return false;
62081
+ const attachmentText = request.message.content.attachments?.length ? formatAttachments(request.message.content.attachments) : "";
62082
+ const bodyText = `${request.message.content.text}${attachmentText}`;
62083
+ const replyCommand = config.replyHint && request.message.expectsReply ? `intercom({ action: "reply", message: "..." })` : undefined;
62084
+ const entry = { from: request.from, message: request.message, replyCommand, bodyText };
62085
+ const admission = inboundDeliveries.admit(request.from, request.message);
62086
+ if (admission.kind === "duplicate")
62087
+ return true;
62088
+ if (admission.kind === "pending") {
62089
+ return admission.completion.then(() => true, () => false);
62090
+ }
62091
+ const reservation = admission.reservation;
62092
+ const commit = () => {
62093
+ inboundDeliveries.commit(reservation);
62094
+ };
62095
+ replyTracker = bindWorkflowReplyTracker(liveContext, replyTracker);
62096
+ if (routeIncomingReply(replyWaiters.pending(), request.from, request.message)) {
62097
+ commit();
62098
+ return true;
62099
+ }
62100
+ const replyContext = replyTracker.recordIncomingMessage(request.from, request.message);
62101
+ const release = createWorkflowStageDeliveryFailureHandler({
62102
+ entry,
62103
+ admission: inboundDeliveries,
62104
+ reservation,
62105
+ tracker: replyTracker,
62106
+ replyContext,
62107
+ currentClient: () => client,
62108
+ commit
62109
+ });
62110
+ const reject = async (error) => {
62111
+ await release(error);
62112
+ return false;
62113
+ };
62114
+ const stageClosed = liveContext.orchestrationContext?.kind === "workflow-stage" && liveContext.orchestrationContext.messageAdmission?.isOpen() === false;
62115
+ if (stageClosed)
62116
+ return reject(new Error("Workflow stage is closed before notification admission"));
62117
+ const stageDelivery = admitWorkflowStageInbound(liveContext, (admissionBarrier) => {
62118
+ replyTracker.queueTurnContext(replyContext);
62119
+ return retryStableDelivery({
62120
+ deliver: () => sendIncomingMessage(entry, "trigger", messageGeneration, false, undefined, admissionBarrier),
62121
+ isCurrent: () => Boolean(getLiveContext(liveContext, messageGeneration))
62122
+ });
62123
+ }, () => foregroundDetachHandoff.claim(request.from, request.message, messageGeneration, () => Boolean(getLiveContext(liveContext, messageGeneration))), release);
62124
+ if (stageDelivery !== false) {
62125
+ try {
62126
+ await stageDelivery;
62127
+ commit();
62128
+ return true;
62129
+ } catch (error) {
62130
+ return reject(error);
62131
+ }
62132
+ }
62133
+ try {
62134
+ const activeContext = getLiveContext(liveContext, messageGeneration);
62135
+ if (activeContext === null || !activeContext.isIdle()) {
62136
+ return reject(new Error("Intercom session is not ready for acknowledged notification delivery"));
62137
+ }
62138
+ replyTracker.queueTurnContext(replyContext);
62139
+ await retryStableDelivery({
62140
+ deliver: () => sendIncomingMessage(entry, "trigger", messageGeneration, false),
62141
+ isCurrent: () => Boolean(getLiveContext(liveContext, messageGeneration))
62142
+ });
62143
+ commit();
62144
+ return true;
62145
+ } catch (error) {
62146
+ return reject(error);
62147
+ }
62148
+ }
62149
+ function handlePendingStageNotification(nextClient, request) {
62150
+ admitPendingStageNotification(nextClient, request).catch(() => false).then((delivered) => {
62151
+ if (client !== nextClient || !nextClient.isConnected())
62152
+ return;
62153
+ try {
62154
+ nextClient.respondPendingStageNotification(request.requestId, delivered);
62155
+ } catch {}
62156
+ });
62157
+ }
61698
62158
  function attachClientHandlers(nextClient) {
61699
62159
  nextClient.on("message", (from, message, channel) => {
61700
62160
  const liveContext = getLiveContext();
@@ -61703,6 +62163,12 @@ function piIntercomExtension(pi, testOverrides = {}) {
61703
62163
  }
61704
62164
  handleIncomingMessage(liveContext, from, message, channel);
61705
62165
  });
62166
+ nextClient.on("pending_stage_message", (request) => {
62167
+ handlePendingStageMessage(nextClient, request, () => client === nextClient);
62168
+ });
62169
+ nextClient.on("pending_stage_notification", (request) => {
62170
+ handlePendingStageNotification(nextClient, request);
62171
+ });
61706
62172
  nextClient.on("peer_disconnected", (notice) => {
61707
62173
  if (client !== nextClient) {
61708
62174
  return;
@@ -61715,6 +62181,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
61715
62181
  }
61716
62182
  rejectReplyWaiter(new Error(`Disconnected while waiting for reply: ${error.message}`, { cause: error }));
61717
62183
  client = null;
62184
+ clientRegistrationGroup = null;
61718
62185
  if (process.env[INTERCOM_SESSION_ID_ENV] === nextClient.sessionId)
61719
62186
  restoreIntercomSessionIdEnv();
61720
62187
  if (!shuttingDown && !disposed) {
@@ -61738,6 +62205,132 @@ function piIntercomExtension(pi, testOverrides = {}) {
61738
62205
  ensureConnected("background").catch(() => {});
61739
62206
  }, reconnectDelayMs(reconnectAttempt));
61740
62207
  }
62208
+ function samePendingStageRoute(left, right) {
62209
+ return left !== undefined && normalizeGroup(left.group) === normalizeGroup(right.group) && left.capability === right.capability;
62210
+ }
62211
+ function pendingStageRouteClientIsCurrent(runId, state, context = runtimeContext, generation = runtimeGeneration) {
62212
+ return pendingStageRouteClients.get(runId) === state && samePendingStageRoute(pendingStageRoutes.get(runId), state.route) && Boolean(getLiveContext(context, generation));
62213
+ }
62214
+ function schedulePendingStageRouteReconnect(runId, state) {
62215
+ if (state.reconnectTimer || state.promise || !pendingStageRouteClientIsCurrent(runId, state))
62216
+ return;
62217
+ const scheduledGeneration = runtimeGeneration;
62218
+ state.reconnectTimer = setTimeout(() => {
62219
+ state.reconnectTimer = null;
62220
+ if (!pendingStageRouteClientIsCurrent(runId, state, runtimeContext, scheduledGeneration))
62221
+ return;
62222
+ state.reconnectAttempt += 1;
62223
+ ensurePendingStageRouteClient(runId, state.route).catch(() => {});
62224
+ }, reconnectDelayMs(state.reconnectAttempt));
62225
+ }
62226
+ function attachPendingStageRouteClientHandlers(runId, state, nextClient) {
62227
+ nextClient.on("pending_stage_message", (request) => {
62228
+ handlePendingStageMessage(nextClient, request, () => state.client === nextClient && pendingStageRouteClientIsCurrent(runId, state));
62229
+ });
62230
+ nextClient.on("disconnected", () => {
62231
+ if (state.client !== nextClient)
62232
+ return;
62233
+ state.client = null;
62234
+ schedulePendingStageRouteReconnect(runId, state);
62235
+ });
62236
+ nextClient.on("error", () => {});
62237
+ }
62238
+ async function ensurePendingStageRouteClient(runId, route) {
62239
+ const contextAtStart = getLiveContext();
62240
+ const generationAtStart = runtimeGeneration;
62241
+ if (!contextAtStart)
62242
+ throw new Error("Intercom runtime not initialized");
62243
+ let existing = pendingStageRouteClients.get(runId);
62244
+ if (existing && !samePendingStageRoute(existing.route, route)) {
62245
+ pendingStageRouteClients.delete(runId);
62246
+ if (existing.reconnectTimer)
62247
+ clearTimeout(existing.reconnectTimer);
62248
+ try {
62249
+ await existing.promise;
62250
+ } catch {}
62251
+ if (existing.client) {
62252
+ try {
62253
+ await existing.client.disconnect();
62254
+ } catch {}
62255
+ }
62256
+ existing = undefined;
62257
+ }
62258
+ const state = existing ?? {
62259
+ route,
62260
+ client: null,
62261
+ promise: null,
62262
+ reconnectTimer: null,
62263
+ reconnectAttempt: 0
62264
+ };
62265
+ if (!existing)
62266
+ pendingStageRouteClients.set(runId, state);
62267
+ if (state.client?.isConnected())
62268
+ return;
62269
+ if (state.promise)
62270
+ return state.promise;
62271
+ const promise = (async () => {
62272
+ await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
62273
+ const nextClient = new IntercomClient(`${currentSessionId}:pending-stage-route:${runId}`);
62274
+ state.client = nextClient;
62275
+ attachPendingStageRouteClientHandlers(runId, state, nextClient);
62276
+ await nextClient.connect({ ...buildRegistration(), name: undefined, group: normalizeGroup(route.group) }, undefined, undefined, readSubagentMessageSource(runtimeContext?.subagentPolicy));
62277
+ if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
62278
+ await nextClient.disconnect();
62279
+ throw new Error("Intercom runtime no longer active");
62280
+ }
62281
+ nextClient.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability);
62282
+ await nextClient.listSessions();
62283
+ if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
62284
+ await nextClient.disconnect();
62285
+ throw new Error("Intercom runtime no longer active");
62286
+ }
62287
+ state.reconnectAttempt = 0;
62288
+ })();
62289
+ state.promise = promise;
62290
+ try {
62291
+ await promise;
62292
+ } catch (error) {
62293
+ const failedClient = state.client;
62294
+ state.client = null;
62295
+ if (failedClient) {
62296
+ try {
62297
+ await failedClient.disconnect();
62298
+ } catch {}
62299
+ }
62300
+ if (state.promise === promise)
62301
+ state.promise = null;
62302
+ schedulePendingStageRouteReconnect(runId, state);
62303
+ throw toError3(error);
62304
+ } finally {
62305
+ if (state.promise === promise)
62306
+ state.promise = null;
62307
+ }
62308
+ }
62309
+ async function disconnectPendingStageRouteClients() {
62310
+ const states = [...pendingStageRouteClients.values()];
62311
+ pendingStageRouteClients.clear();
62312
+ for (const state of states) {
62313
+ if (state.reconnectTimer)
62314
+ clearTimeout(state.reconnectTimer);
62315
+ try {
62316
+ await state.promise;
62317
+ } catch {}
62318
+ if (!state.client)
62319
+ continue;
62320
+ try {
62321
+ await state.client.disconnect();
62322
+ } catch {}
62323
+ state.client = null;
62324
+ }
62325
+ }
62326
+ async function registerPendingStageRoute(activeClient, runId, route) {
62327
+ const routeGroup = normalizeGroup(route.group);
62328
+ if (clientRegistrationGroup === routeGroup) {
62329
+ activeClient.registerPendingStageRoute(runId, routeGroup, route.capability);
62330
+ return;
62331
+ }
62332
+ await ensurePendingStageRouteClient(runId, { ...route, group: routeGroup });
62333
+ }
61741
62334
  async function ensureConnected(reason) {
61742
62335
  if (!config.enabled) {
61743
62336
  throw new Error("Intercom disabled");
@@ -61758,14 +62351,23 @@ function piIntercomExtension(pi, testOverrides = {}) {
61758
62351
  return reconnectPromise;
61759
62352
  }
61760
62353
  const nextReconnectPromise = (async () => {
61761
- const nextClient = new IntercomClient;
62354
+ const nextClient = new IntercomClient(currentSessionId);
62355
+ const registration = buildRegistration();
61762
62356
  client = nextClient;
61763
62357
  attachClientHandlers(nextClient);
61764
62358
  try {
61765
62359
  await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
61766
62360
  const childMetadata = currentChildOrchestratorMetadata();
61767
- await nextClient.connect(buildRegistration(), childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy));
62361
+ await nextClient.connect(registration, childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy));
62362
+ clientRegistrationGroup = normalizeGroup(registration.group);
61768
62363
  await supervisorAuthorizations.restore(nextClient);
62364
+ for (const [runId, route] of pendingStageRoutes) {
62365
+ await registerPendingStageRoute(nextClient, runId, route);
62366
+ }
62367
+ const orchestration = contextAtStart.orchestrationContext;
62368
+ if (orchestration?.kind === "workflow-stage" && orchestration.pendingStageDelivery !== undefined) {
62369
+ await nextClient.registerLiveWorkflowStageRoute(orchestration.workflowRunId, [orchestration.workflowStageId, orchestration.workflowStageName], orchestration.pendingStageDelivery.routeCapability);
62370
+ }
61769
62371
  if (!getLiveContext(contextAtStart, generationAtStart)) {
61770
62372
  await nextClient.disconnect();
61771
62373
  throw new Error("Intercom runtime no longer active");
@@ -61779,6 +62381,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
61779
62381
  } catch (error) {
61780
62382
  if (client === nextClient) {
61781
62383
  client = null;
62384
+ clientRegistrationGroup = null;
61782
62385
  }
61783
62386
  if (reason === "background" && getLiveContext(contextAtStart, generationAtStart)) {
61784
62387
  scheduleReconnect();
@@ -61795,6 +62398,43 @@ function piIntercomExtension(pi, testOverrides = {}) {
61795
62398
  reconnectPromiseGeneration = generationAtStart;
61796
62399
  return nextReconnectPromise;
61797
62400
  }
62401
+ pi.events.on(PENDING_STAGE_ROUTE_EVENT, (payload) => {
62402
+ if (!isPendingStageRouteRegistrationEvent(payload))
62403
+ return;
62404
+ pendingStageRoutes.set(payload.runId, { group: payload.group, capability: payload.capability });
62405
+ const completion = ensureConnected("background").then((activeClient) => registerPendingStageRoute(activeClient, payload.runId, payload));
62406
+ payload.completion = completion;
62407
+ completion.catch(() => {});
62408
+ });
62409
+ async function pendingStageNotificationClient(runId) {
62410
+ const route = pendingStageRoutes.get(runId);
62411
+ if (route === undefined)
62412
+ throw new Error("Pending workflow route is unavailable");
62413
+ if (client?.isConnected() && clientRegistrationGroup === normalizeGroup(route.group))
62414
+ return client;
62415
+ await ensurePendingStageRouteClient(runId, route);
62416
+ const routeClient = pendingStageRouteClients.get(runId)?.client;
62417
+ if (!routeClient?.isConnected())
62418
+ throw new Error("Pending workflow route is disconnected");
62419
+ return routeClient;
62420
+ }
62421
+ pi.events.on(PENDING_STAGE_UNDELIVERABLE_EVENT, (payload) => {
62422
+ if (!isPendingStageUndeliverableEvent(payload) || payload.handled)
62423
+ return;
62424
+ payload.handled = true;
62425
+ const actionable = `Pending workflow stage could not receive intercom message: ${payload.reason}`;
62426
+ payload.completion = pendingStageNotificationClient(payload.runId).then((activeClient) => {
62427
+ const route = pendingStageRoutes.get(payload.runId);
62428
+ if (route === undefined)
62429
+ throw new Error("Pending workflow route is unavailable");
62430
+ return activeClient.sendPendingStageNotification(payload.runId, route.capability, payload.senderId, payload.senderRegistrationName, {
62431
+ text: actionable,
62432
+ replyTo: payload.messageId,
62433
+ replyError: actionable,
62434
+ messageId: payload.notificationId
62435
+ }, payload.senderReturnAddress);
62436
+ }).then((result) => result.delivered).catch(() => false);
62437
+ });
61798
62438
  registerSubagentRelay(pi, {
61799
62439
  runtimeGeneration: () => runtimeGeneration,
61800
62440
  runtimeStarted: () => runtimeStarted,
@@ -61810,8 +62450,11 @@ function piIntercomExtension(pi, testOverrides = {}) {
61810
62450
  registerIntercomLifecycle(pi, {
61811
62451
  config,
61812
62452
  client: () => client,
62453
+ disconnectAuxiliaryClients: disconnectPendingStageRouteClients,
61813
62454
  setClient: (value) => {
61814
62455
  client = value;
62456
+ if (value === null)
62457
+ clientRegistrationGroup = null;
61815
62458
  },
61816
62459
  setShuttingDown: (value) => {
61817
62460
  shuttingDown = value;
@@ -61837,6 +62480,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
61837
62480
  sessionHomeGroup = null;
61838
62481
  } else {
61839
62482
  sessionHomeGroup = resolveHomeGroup(config, value);
62483
+ inboundDeliveries.restore(value.sessionManager.getBranch());
61840
62484
  }
61841
62485
  runtimeContext = value;
61842
62486
  },
@@ -61868,6 +62512,13 @@ function piIntercomExtension(pi, testOverrides = {}) {
61868
62512
  restoreIntercomSessionIdEnv,
61869
62513
  currentStatus
61870
62514
  });
62515
+ pi.on("session_start", async (_event, ctx) => {
62516
+ const pendingStageDelivery = ctx.orchestrationContext?.kind === "workflow-stage" ? ctx.orchestrationContext.pendingStageDelivery : undefined;
62517
+ if (pendingStageDelivery === undefined)
62518
+ return;
62519
+ await ensureConnected("startup");
62520
+ await pendingStageDelivery.deliverPending((from, message) => handleIncomingMessage(ctx, from, message, undefined, true));
62521
+ });
61871
62522
  pi.registerMessageRenderer("intercom_message", (message, _options, theme2) => {
61872
62523
  const details = message.details;
61873
62524
  if (!details)
@@ -61900,7 +62551,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
61900
62551
  syncPresenceIdentity
61901
62552
  });
61902
62553
  }
61903
- var INTERCOM_SESSION_ID_ENV;
62554
+ var INTERCOM_SESSION_ID_ENV, PENDING_STAGE_ROUTE_EVENT = "atomic:workflow-pending-stage-route", PENDING_STAGE_MESSAGE_EVENT = "atomic:workflow-pending-stage-message", PENDING_STAGE_UNDELIVERABLE_EVENT = "atomic:workflow-pending-stage-undeliverable";
61904
62555
  var init_index_heavy = __esm(() => {
61905
62556
  init_src();
61906
62557
  init_client();
@@ -62124,6 +62775,14 @@ function createHeavyProxy(pi, captured) {
62124
62775
  var SUBAGENT_CONTROL_INTERCOM_EVENT2 = "subagent:control-intercom";
62125
62776
  var SUBAGENT_SUPERVISOR_AUTHORIZATION_EVENT2 = "subagent:supervisor-authorization";
62126
62777
  var WORKFLOW_STAGE_LATE_MESSAGE_EVENT = "atomic:workflow-stage-late-message";
62778
+ var PENDING_STAGE_ROUTE_EVENT2 = "atomic:workflow-pending-stage-route";
62779
+ var PENDING_STAGE_UNDELIVERABLE_EVENT2 = "atomic:workflow-pending-stage-undeliverable";
62780
+ function isPendingStageUndeliverableRelay(value) {
62781
+ if (typeof value !== "object" || value === null)
62782
+ return false;
62783
+ const event = value;
62784
+ return typeof event.runId === "string" && typeof event.senderId === "string" && (event.senderRegistrationName === undefined || typeof event.senderRegistrationName === "string") && (event.senderReturnAddress === undefined || typeof event.senderReturnAddress === "string") && typeof event.messageId === "string" && typeof event.notificationId === "string" && typeof event.reason === "string";
62785
+ }
62127
62786
  function isCompletedStageAskRoute2(event) {
62128
62787
  return typeof event.workflowRunId === "string" && event.workflowRunId.length > 0 && typeof event.workflowStageId === "string" && event.workflowStageId.length > 0 && Array.isArray(event.messages) && event.messages.length > 0 && event.messages.every((message) => message.customType === "intercom_message" && typeof message.content === "string" && message.details?.message?.expectsReply === true);
62129
62788
  }
@@ -62349,8 +63008,11 @@ function intercom(pi, options = {}) {
62349
63008
  }
62350
63009
  const generation = ++lifecycleGeneration;
62351
63010
  sessionSnapshot = { event, ctx, generation, lease };
62352
- if (loadedHeavy)
63011
+ if (ctx.orchestrationContext?.kind === "workflow-stage" && ctx.orchestrationContext.pendingStageDelivery !== undefined) {
63012
+ await loadHeavy(ctx);
63013
+ } else if (loadedHeavy) {
62353
63014
  await ensureSessionStartReplayed(loadedHeavy.heavy, lease);
63015
+ }
62354
63016
  });
62355
63017
  pi.on("session_shutdown", async (event, ctx) => {
62356
63018
  const lease = activeLease;
@@ -62466,13 +63128,41 @@ function intercom(pi, options = {}) {
62466
63128
  return await forwarded.completion;
62467
63129
  });
62468
63130
  });
62469
- for (const eventName of [SUBAGENT_CONTROL_INTERCOM_EVENT2, SUBAGENT_RESULT_INTERCOM_EVENT3]) {
63131
+ pi.events.on(PENDING_STAGE_UNDELIVERABLE_EVENT2, (payload) => {
63132
+ if (!isPendingStageUndeliverableRelay(payload) || payload.handled === true)
63133
+ return;
63134
+ payload.handled = true;
63135
+ const forwarded = { ...payload, handled: false, completion: undefined };
63136
+ payload.completion = loadHeavy(latestLifecycleContext()).then(async (handle) => {
63137
+ handle.assertCurrent();
63138
+ await dispatchEventHandlers(handle.heavy, PENDING_STAGE_UNDELIVERABLE_EVENT2, forwarded);
63139
+ handle.assertCurrent();
63140
+ return forwarded.handled === true && forwarded.completion !== undefined ? await forwarded.completion : false;
63141
+ }).catch((error) => {
63142
+ console.error(`Intercom event relay failed (${PENDING_STAGE_UNDELIVERABLE_EVENT2}):`, error);
63143
+ return false;
63144
+ });
63145
+ });
63146
+ for (const eventName of [
63147
+ SUBAGENT_CONTROL_INTERCOM_EVENT2,
63148
+ SUBAGENT_RESULT_INTERCOM_EVENT3,
63149
+ PENDING_STAGE_ROUTE_EVENT2
63150
+ ]) {
62470
63151
  pi.events.on(eventName, (payload) => {
62471
- loadHeavy(latestLifecycleContext()).then(async (handle) => {
63152
+ const completion = loadHeavy(latestLifecycleContext()).then(async (handle) => {
62472
63153
  handle.assertCurrent();
62473
63154
  await dispatchEventHandlers(handle.heavy, eventName, payload);
62474
63155
  handle.assertCurrent();
62475
- }).catch((error) => {
63156
+ if (eventName === PENDING_STAGE_ROUTE_EVENT2 && payload && typeof payload === "object") {
63157
+ const routeCompletion = payload.completion;
63158
+ if (routeCompletion !== undefined && routeCompletion !== completion)
63159
+ await routeCompletion;
63160
+ }
63161
+ });
63162
+ if (eventName === PENDING_STAGE_ROUTE_EVENT2 && payload && typeof payload === "object") {
63163
+ payload.completion = completion;
63164
+ }
63165
+ completion.catch((error) => {
62476
63166
  rejectLazyResultRelay(pi, eventName, payload, error);
62477
63167
  console.error(`Intercom event relay failed (${eventName}):`, error);
62478
63168
  });
@@ -62497,6 +63187,7 @@ function intercom(pi, options = {}) {
62497
63187
  description: `Send a message to another local agent session running on this machine.
62498
63188
  Use this to communicate findings, request help, or coordinate work with other sessions.
62499
63189
  Sessions belong to an intercom group and can ONLY message sessions in the same group; cross-group sends are rejected by the broker. Ungrouped sessions share the "default" group.
63190
+ For send, live session names and exact full session IDs remain supported. For a known workflow stage, use the exact \`<runId>:<stageKey>\` target; send messages to pending stages queue automatically.
62500
63191
  Usage:
62501
63192
  intercom({ action: "list" }) → List sessions in your group
62502
63193
  intercom({ action: "list", group: "name" }) → Read-only peek at another group's sessions
@@ -62512,7 +63203,7 @@ Usage:
62512
63203
  promptSnippet: "Use to coordinate with other local agent sessions in your intercom group: list peers, send updates, ask for help, or check intercom connectivity. Groups are isolated; you can only message sessions in your own group.",
62513
63204
  parameters: Type15.Object({
62514
63205
  action: Type15.String({ description: "Action: 'list', 'join', 'leave', 'send', 'ask', 'reply', 'pending', or 'status'" }),
62515
- to: Type15.Optional(Type15.String({ description: "Exact session name or exact full session ID (for 'send', 'ask', or targeted 'reply')" })),
63206
+ to: Type15.Optional(Type15.String({ description: "Live session name, exact full session ID, or exact `<runId>:<stageKey>` for a known workflow stage; send messages to pending stages queue automatically (for 'send', 'ask', or targeted 'reply')" })),
62516
63207
  message: Type15.Optional(Type15.String({ description: "Message to send (for 'send', 'ask', or 'reply' action)" })),
62517
63208
  attachments: Type15.Optional(Type15.Array(Type15.Object({
62518
63209
  type: Type15.Union([Type15.Literal("file"), Type15.Literal("snippet"), Type15.Literal("context")]),