@bastani/atomic 0.9.18-alpha.5 → 0.9.18-alpha.7

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 (376) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +3 -3
  3. package/dist/builtin/intercom/CHANGELOG.md +39 -0
  4. package/dist/builtin/intercom/README.md +22 -2
  5. package/dist/builtin/intercom/broker/broker.ts +676 -128
  6. package/dist/builtin/intercom/broker/client.ts +151 -35
  7. package/dist/builtin/intercom/broker/delivered-message-cache.ts +497 -37
  8. package/dist/builtin/intercom/broker/framing.ts +4 -2
  9. package/dist/builtin/intercom/broker/paths.ts +10 -0
  10. package/dist/builtin/intercom/broker/pending-send-registry.ts +1 -2
  11. package/dist/builtin/intercom/broker/send-handler.ts +311 -52
  12. package/dist/builtin/intercom/broker/send-signature.ts +4 -2
  13. package/dist/builtin/intercom/broker/socket-writes.ts +103 -0
  14. package/dist/builtin/intercom/broker/spawn.ts +9 -4
  15. package/dist/builtin/intercom/index.bundle.mjs +1410 -212
  16. package/dist/builtin/intercom/package.json +2 -2
  17. package/dist/builtin/intercom/recoverable-disconnect.ts +52 -0
  18. package/dist/builtin/intercom/retry-policy.ts +8 -0
  19. package/dist/builtin/intercom/skills/intercom/SKILL.md +17 -9
  20. package/dist/builtin/intercom/types.ts +27 -8
  21. package/dist/builtin/intercom/workflow-stage-path-matching.ts +90 -0
  22. package/dist/builtin/intercom/workflow-stage-target.ts +57 -0
  23. package/dist/builtin/mcp/index.bundle.mjs +427 -126
  24. package/dist/builtin/mcp/package.json +2 -2
  25. package/dist/builtin/subagents/CHANGELOG.md +19 -0
  26. package/dist/builtin/subagents/README.md +3 -3
  27. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  28. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  29. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  30. package/dist/builtin/subagents/agents/codebase-online-researcher.md +11 -11
  31. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  32. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  33. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  34. package/dist/builtin/subagents/agents/debugger.md +3 -3
  35. package/dist/builtin/subagents/agents/worker.md +2 -2
  36. package/dist/builtin/subagents/package.json +3 -3
  37. package/dist/builtin/subagents/skills/qlty/SKILL.md +29 -101
  38. package/dist/builtin/subagents/skills/qlty/references/manual-configuration.md +52 -0
  39. package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
  40. package/dist/builtin/subagents/src/extension/index.bundle.mjs +49 -132
  41. package/dist/builtin/web-access/package.json +2 -2
  42. package/dist/builtin/workflows/CHANGELOG.md +46 -0
  43. package/dist/builtin/workflows/README.md +7 -7
  44. package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
  45. package/dist/builtin/workflows/builtin/{chunk-szz1ajz9.js → chunk-42s7sw0b.js} +9 -2
  46. package/dist/builtin/workflows/builtin/{chunk-2dqb5s2q.js → chunk-79fjkcvh.js} +1 -1
  47. package/dist/builtin/workflows/builtin/{chunk-n58a7v26.js → chunk-b0v2xab1.js} +181 -10
  48. package/dist/builtin/workflows/builtin/{chunk-fghhy2a5.js → chunk-ed4jfn5t.js} +23 -5
  49. package/dist/builtin/workflows/builtin/{chunk-h3r2vkzc.js → chunk-hqpe2cyx.js} +60 -21
  50. package/dist/builtin/workflows/builtin/{chunk-hzzn6adg.js → chunk-k3w87x8y.js} +1 -1
  51. package/dist/builtin/workflows/builtin/{chunk-82ha8p41.js → chunk-n8h4yyew.js} +1 -1
  52. package/dist/builtin/workflows/builtin/{chunk-qwzvgxnq.js → chunk-reqr3sr3.js} +1 -1
  53. package/dist/builtin/workflows/builtin/{chunk-c53y8bdh.js → chunk-wgccch41.js} +1 -1
  54. package/dist/builtin/workflows/builtin/{chunk-0x6e303p.js → chunk-zbekjqew.js} +20 -7
  55. package/dist/builtin/workflows/builtin/{chunk-29wrp38a.js → chunk-ze5x3d1r.js} +1 -1
  56. package/dist/builtin/workflows/builtin/classify-and-act.js +2 -2
  57. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +2 -2
  58. package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
  59. package/dist/builtin/workflows/builtin/goal.js +3 -3
  60. package/dist/builtin/workflows/builtin/index.js +11 -11
  61. package/dist/builtin/workflows/builtin/loop-until-done.js +2 -2
  62. package/dist/builtin/workflows/builtin/open-claude-design.js +2 -2
  63. package/dist/builtin/workflows/builtin/ralph.js +3 -3
  64. package/dist/builtin/workflows/builtin/steering-context.js +1 -1
  65. package/dist/builtin/workflows/builtin/tournament.js +2 -2
  66. package/dist/builtin/workflows/package.json +2 -2
  67. package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +39 -52
  68. package/dist/builtin/workflows/skills/prompt-engineer/references/advanced_patterns.md +9 -37
  69. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_fable_5.md +49 -0
  70. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_fable_5_1.md +57 -0
  71. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_opus_4_8.md +57 -0
  72. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_opus_5.md +47 -0
  73. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_sonnet_5.md +57 -0
  74. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_5_5.md +60 -0
  75. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_5_6.md +58 -0
  76. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_astra.md +58 -0
  77. package/dist/builtin/workflows/skills/prompt-engineer/references/quality_improvement.md +18 -4
  78. package/dist/builtin/workflows/src/extension/index.bundle.mjs +3194 -737
  79. package/dist/builtin/workflows/src/index.js +899 -436
  80. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +0 -10
  81. package/dist/cli/list-models.d.ts.map +1 -1
  82. package/dist/cli/list-models.js +4 -0
  83. package/dist/cli/list-models.js.map +1 -1
  84. package/dist/client/index.d.ts +1 -2
  85. package/dist/client/index.d.ts.map +1 -1
  86. package/dist/client/index.js +1 -2
  87. package/dist/client/index.js.map +1 -1
  88. package/dist/config.d.ts +0 -9
  89. package/dist/config.d.ts.map +1 -1
  90. package/dist/config.js +0 -45
  91. package/dist/config.js.map +1 -1
  92. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  93. package/dist/core/agent-session-compaction.js +8 -6
  94. package/dist/core/agent-session-compaction.js.map +1 -1
  95. package/dist/core/agent-session-methods.d.ts +1 -0
  96. package/dist/core/agent-session-methods.d.ts.map +1 -1
  97. package/dist/core/agent-session-methods.js.map +1 -1
  98. package/dist/core/agent-session-models.d.ts.map +1 -1
  99. package/dist/core/agent-session-models.js +3 -4
  100. package/dist/core/agent-session-models.js.map +1 -1
  101. package/dist/core/agent-session-queue-pause.d.ts.map +1 -1
  102. package/dist/core/agent-session-queue-pause.js +6 -0
  103. package/dist/core/agent-session-queue-pause.js.map +1 -1
  104. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  105. package/dist/core/agent-session-runtime.js +13 -5
  106. package/dist/core/agent-session-runtime.js.map +1 -1
  107. package/dist/core/agent-session-tool-hooks.js +1 -1
  108. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  109. package/dist/core/agent-session-tree.d.ts.map +1 -1
  110. package/dist/core/agent-session-tree.js +8 -0
  111. package/dist/core/agent-session-tree.js.map +1 -1
  112. package/dist/core/agent-session.d.ts +2 -0
  113. package/dist/core/agent-session.d.ts.map +1 -1
  114. package/dist/core/agent-session.js +2 -0
  115. package/dist/core/agent-session.js.map +1 -1
  116. package/dist/core/compaction/branch-summarization.d.ts +2 -2
  117. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  118. package/dist/core/compaction/branch-summarization.js +4 -4
  119. package/dist/core/compaction/branch-summarization.js.map +1 -1
  120. package/dist/core/extensions/api-types.d.ts +8 -2
  121. package/dist/core/extensions/api-types.d.ts.map +1 -1
  122. package/dist/core/extensions/api-types.js.map +1 -1
  123. package/dist/core/extensions/context-types.d.ts +12 -0
  124. package/dist/core/extensions/context-types.d.ts.map +1 -1
  125. package/dist/core/extensions/context-types.js.map +1 -1
  126. package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
  127. package/dist/core/extensions/runner-shortcuts.js +0 -1
  128. package/dist/core/extensions/runner-shortcuts.js.map +1 -1
  129. package/dist/core/fast-model-routing-transport.d.ts +12 -0
  130. package/dist/core/fast-model-routing-transport.d.ts.map +1 -0
  131. package/dist/core/{codex-fast-mode-transport.js → fast-model-routing-transport.js} +29 -29
  132. package/dist/core/fast-model-routing-transport.js.map +1 -0
  133. package/dist/core/fast-model-routing.d.ts +57 -0
  134. package/dist/core/fast-model-routing.d.ts.map +1 -0
  135. package/dist/core/fast-model-routing.js +201 -0
  136. package/dist/core/fast-model-routing.js.map +1 -0
  137. package/dist/core/fast-model-variants.d.ts +98 -0
  138. package/dist/core/fast-model-variants.d.ts.map +1 -0
  139. package/dist/core/fast-model-variants.js +148 -0
  140. package/dist/core/fast-model-variants.js.map +1 -0
  141. package/dist/core/http-dispatcher.d.ts.map +1 -1
  142. package/dist/core/http-dispatcher.js +4 -2
  143. package/dist/core/http-dispatcher.js.map +1 -1
  144. package/dist/core/keybindings.d.ts +0 -10
  145. package/dist/core/keybindings.d.ts.map +1 -1
  146. package/dist/core/keybindings.js +0 -8
  147. package/dist/core/keybindings.js.map +1 -1
  148. package/dist/core/messages.d.ts +1 -1
  149. package/dist/core/messages.d.ts.map +1 -1
  150. package/dist/core/messages.js.map +1 -1
  151. package/dist/core/model-config.d.ts +15 -0
  152. package/dist/core/model-config.d.ts.map +1 -1
  153. package/dist/core/model-config.js +3 -0
  154. package/dist/core/model-config.js.map +1 -1
  155. package/dist/core/model-resolver-initial.js +1 -1
  156. package/dist/core/model-resolver-initial.js.map +1 -1
  157. package/dist/core/model-runtime-streaming.d.ts +14 -1
  158. package/dist/core/model-runtime-streaming.d.ts.map +1 -1
  159. package/dist/core/model-runtime-streaming.js +25 -3
  160. package/dist/core/model-runtime-streaming.js.map +1 -1
  161. package/dist/core/model-runtime.d.ts +39 -2
  162. package/dist/core/model-runtime.d.ts.map +1 -1
  163. package/dist/core/model-runtime.js +91 -7
  164. package/dist/core/model-runtime.js.map +1 -1
  165. package/dist/core/provider-composer-internal.d.ts.map +1 -1
  166. package/dist/core/provider-composer-internal.js +8 -2
  167. package/dist/core/provider-composer-internal.js.map +1 -1
  168. package/dist/core/sdk.d.ts.map +1 -1
  169. package/dist/core/sdk.js +18 -18
  170. package/dist/core/sdk.js.map +1 -1
  171. package/dist/core/session-manager-archive.d.ts.map +1 -1
  172. package/dist/core/session-manager-archive.js +25 -2
  173. package/dist/core/session-manager-archive.js.map +1 -1
  174. package/dist/core/settings-manager-core.d.ts +0 -2
  175. package/dist/core/settings-manager-core.d.ts.map +1 -1
  176. package/dist/core/settings-manager-core.js +2 -9
  177. package/dist/core/settings-manager-core.js.map +1 -1
  178. package/dist/core/settings-manager-internals.d.ts +0 -1
  179. package/dist/core/settings-manager-internals.d.ts.map +1 -1
  180. package/dist/core/settings-manager-internals.js.map +1 -1
  181. package/dist/core/settings-manager-ui-accessors.d.ts +0 -8
  182. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  183. package/dist/core/settings-manager-ui-accessors.js +0 -53
  184. package/dist/core/settings-manager-ui-accessors.js.map +1 -1
  185. package/dist/core/settings-manager.d.ts +1 -1
  186. package/dist/core/settings-manager.d.ts.map +1 -1
  187. package/dist/core/settings-manager.js.map +1 -1
  188. package/dist/core/settings-types.d.ts +0 -5
  189. package/dist/core/settings-types.d.ts.map +1 -1
  190. package/dist/core/settings-types.js.map +1 -1
  191. package/dist/core/skills.d.ts +1 -1
  192. package/dist/core/skills.d.ts.map +1 -1
  193. package/dist/core/skills.js +4 -2
  194. package/dist/core/skills.js.map +1 -1
  195. package/dist/core/slash-commands.d.ts.map +1 -1
  196. package/dist/core/slash-commands.js +0 -1
  197. package/dist/core/slash-commands.js.map +1 -1
  198. package/dist/core/system-prompt.js +7 -7
  199. package/dist/core/system-prompt.js.map +1 -1
  200. package/dist/core/tools/edit.d.ts +1 -1
  201. package/dist/core/tools/edit.d.ts.map +1 -1
  202. package/dist/core/tools/edit.js +128 -3
  203. package/dist/core/tools/edit.js.map +1 -1
  204. package/dist/core/tools/hashline.d.ts +2 -2
  205. package/dist/core/tools/hashline.d.ts.map +1 -1
  206. package/dist/core/tools/hashline.js +40 -10
  207. package/dist/core/tools/hashline.js.map +1 -1
  208. package/dist/core/tools/write.d.ts.map +1 -1
  209. package/dist/core/tools/write.js +8 -8
  210. package/dist/core/tools/write.js.map +1 -1
  211. package/dist/core/workflow-stage-admission.d.ts +6 -0
  212. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  213. package/dist/core/workflow-stage-admission.js +14 -0
  214. package/dist/core/workflow-stage-admission.js.map +1 -1
  215. package/dist/index-extensions.d.ts +1 -1
  216. package/dist/index-extensions.d.ts.map +1 -1
  217. package/dist/index-extensions.js.map +1 -1
  218. package/dist/index.d.ts +5 -5
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +4 -5
  221. package/dist/index.js.map +1 -1
  222. package/dist/modes/interactive/components/atomic-working-status.d.ts +4 -0
  223. package/dist/modes/interactive/components/atomic-working-status.d.ts.map +1 -1
  224. package/dist/modes/interactive/components/atomic-working-status.js +26 -1
  225. package/dist/modes/interactive/components/atomic-working-status.js.map +1 -1
  226. package/dist/modes/interactive/components/custom-editor.d.ts +7 -0
  227. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  228. package/dist/modes/interactive/components/custom-editor.js +43 -0
  229. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  230. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  231. package/dist/modes/interactive/components/footer.js +0 -6
  232. package/dist/modes/interactive/components/footer.js.map +1 -1
  233. package/dist/modes/interactive/components/index.d.ts +1 -2
  234. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  235. package/dist/modes/interactive/components/index.js +0 -1
  236. package/dist/modes/interactive/components/index.js.map +1 -1
  237. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  238. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  239. package/dist/modes/interactive/components/model-selector.js +6 -11
  240. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  241. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -5
  242. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  243. package/dist/modes/interactive/components/scoped-models-selector.js +3 -19
  244. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  245. package/dist/modes/interactive/components/settings-selector-items.js +1 -1
  246. package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
  247. package/dist/modes/interactive/components/thinking-selector.d.ts +1 -2
  248. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/thinking-selector.js +4 -11
  250. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  251. package/dist/modes/interactive/components/transcript-follow-indicator.js +3 -3
  252. package/dist/modes/interactive/components/transcript-follow-indicator.js.map +1 -1
  253. package/dist/modes/interactive/interactive-agent-events.js +8 -8
  254. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  255. package/dist/modes/interactive/interactive-autocomplete.js +5 -16
  256. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  257. package/dist/modes/interactive/interactive-bash-compact.js +2 -9
  258. package/dist/modes/interactive/interactive-bash-compact.js.map +1 -1
  259. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  260. package/dist/modes/interactive/interactive-deferred-startup.js +3 -0
  261. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  262. package/dist/modes/interactive/interactive-editor-actions.js +2 -2
  263. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  264. package/dist/modes/interactive/interactive-extension-custom-ui.js +1 -0
  265. package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
  266. package/dist/modes/interactive/interactive-extension-dialogs.js +7 -0
  267. package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
  268. package/dist/modes/interactive/interactive-extension-runtime.js +20 -10
  269. package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
  270. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -5
  271. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  272. package/dist/modes/interactive/interactive-initial-session-binding.d.ts.map +1 -1
  273. package/dist/modes/interactive/interactive-initial-session-binding.js +3 -0
  274. package/dist/modes/interactive/interactive-initial-session-binding.js.map +1 -1
  275. package/dist/modes/interactive/interactive-input-handling.js +0 -8
  276. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  277. package/dist/modes/interactive/interactive-mode-base.d.ts +5 -1
  278. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  279. package/dist/modes/interactive/interactive-mode-base.js +15 -9
  280. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  281. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -3
  282. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  283. package/dist/modes/interactive/interactive-mode-deps.js +1 -3
  284. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  285. package/dist/modes/interactive/interactive-mode-surface.d.ts +9 -12
  286. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  287. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  288. package/dist/modes/interactive/interactive-model-routing.js +10 -13
  289. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  290. package/dist/modes/interactive/interactive-render-chat.js +29 -0
  291. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  292. package/dist/modes/interactive/interactive-selectors.js +2 -32
  293. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  294. package/dist/modes/interactive/interactive-session-routing.js +1 -5
  295. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  296. package/dist/modes/interactive/interactive-session-runtime.js +1 -5
  297. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  298. package/dist/modes/interactive/interactive-slash-commands.js +6 -13
  299. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  300. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  301. package/dist/modes/interactive/interactive-startup.js +19 -13
  302. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  303. package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
  304. package/dist/modes/interactive/interactive-tui.js +27 -3
  305. package/dist/modes/interactive/interactive-tui.js.map +1 -1
  306. package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
  307. package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
  308. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  309. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  310. package/dist/modes/interactive-engine/isolated-runtime.js +4 -1
  311. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  312. package/dist/utils/child-process.d.ts.map +1 -1
  313. package/dist/utils/child-process.js +13 -6
  314. package/dist/utils/child-process.js.map +1 -1
  315. package/dist/utils/exif-orientation.d.ts.map +1 -1
  316. package/dist/utils/exif-orientation.js +2 -3
  317. package/dist/utils/exif-orientation.js.map +1 -1
  318. package/dist/utils/tools-manager.d.ts +1 -0
  319. package/dist/utils/tools-manager.d.ts.map +1 -1
  320. package/dist/utils/tools-manager.js +33 -18
  321. package/dist/utils/tools-manager.js.map +1 -1
  322. package/docs/custom-provider.md +2 -0
  323. package/docs/development.md +17 -0
  324. package/docs/docs.json +14 -2
  325. package/docs/extensions.md +9 -4
  326. package/docs/intercom.md +41 -7
  327. package/docs/keybindings.md +8 -6
  328. package/docs/models/evals.md +394 -0
  329. package/docs/models/model-selection.md +54 -27
  330. package/docs/models/pareto-efficiency.md +33 -16
  331. package/docs/models.md +37 -1
  332. package/docs/packages.md +2 -2
  333. package/docs/providers.md +44 -20
  334. package/docs/quickstart.md +8 -6
  335. package/docs/rpc.md +3 -3
  336. package/docs/sdk.md +2 -16
  337. package/docs/settings.md +19 -22
  338. package/docs/skills.md +4 -2
  339. package/docs/subagents.md +9 -3
  340. package/docs/tools/edit.md +373 -0
  341. package/docs/tools.md +3 -7
  342. package/docs/tui.md +2 -1
  343. package/docs/usage.md +3 -5
  344. package/docs/workflows/api-reference.md +1395 -0
  345. package/docs/workflows/authoring.md +678 -0
  346. package/docs/workflows/builtins.md +245 -0
  347. package/docs/workflows/operations.md +811 -0
  348. package/docs/workflows/reliable-design.md +2099 -0
  349. package/docs/workflows/verification.md +62 -0
  350. package/docs/workflows.md +15 -5183
  351. package/npm-shrinkwrap.json +584 -69
  352. package/package.json +7 -7
  353. package/dist/client/remote-session.d.ts +0 -64
  354. package/dist/client/remote-session.d.ts.map +0 -1
  355. package/dist/client/remote-session.js +0 -351
  356. package/dist/client/remote-session.js.map +0 -1
  357. package/dist/client/transcript.d.ts +0 -12
  358. package/dist/client/transcript.d.ts.map +0 -1
  359. package/dist/client/transcript.js +0 -98
  360. package/dist/client/transcript.js.map +0 -1
  361. package/dist/core/codex-fast-mode-transport.d.ts +0 -12
  362. package/dist/core/codex-fast-mode-transport.d.ts.map +0 -1
  363. package/dist/core/codex-fast-mode-transport.js.map +0 -1
  364. package/dist/core/codex-fast-mode.d.ts +0 -53
  365. package/dist/core/codex-fast-mode.d.ts.map +0 -1
  366. package/dist/core/codex-fast-mode.js +0 -249
  367. package/dist/core/codex-fast-mode.js.map +0 -1
  368. package/dist/modes/interactive/components/fast-mode-selector.d.ts +0 -27
  369. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +0 -1
  370. package/dist/modes/interactive/components/fast-mode-selector.js +0 -107
  371. package/dist/modes/interactive/components/fast-mode-selector.js.map +0 -1
  372. package/dist/server/create-harness.d.ts +0 -53
  373. package/dist/server/create-harness.d.ts.map +0 -1
  374. package/dist/server/create-harness.js +0 -360
  375. package/dist/server/create-harness.js.map +0 -1
  376. package/docs/models/artificial-analysis-index.md +0 -83
@@ -68,13 +68,49 @@ var renderContactSupervisorResult = (result, { isPartial }, theme, context) => {
68
68
  };
69
69
  var init_result_renderers = () => {};
70
70
 
71
+ // dist/builtin/intercom/recoverable-disconnect.ts
72
+ function isRecoverableIntercomDisconnect(error) {
73
+ let current = error;
74
+ for (let depth = 0;depth <= MAX_CAUSE_DEPTH; depth += 1) {
75
+ if (!(current instanceof Error))
76
+ return false;
77
+ if (current instanceof IntercomClientDisconnectedError)
78
+ return true;
79
+ current = current.cause;
80
+ }
81
+ return false;
82
+ }
83
+ var RECOVERABLE_DISCONNECT_MESSAGE = "Client disconnected. Retry the same Intercom call up to three times to reconnect.", MAX_CAUSE_DEPTH = 8, IntercomClientDisconnectedError;
84
+ var init_recoverable_disconnect = __esm(() => {
85
+ IntercomClientDisconnectedError = class IntercomClientDisconnectedError extends Error {
86
+ intercomRecoverableDisconnect = true;
87
+ constructor(options) {
88
+ super(RECOVERABLE_DISCONNECT_MESSAGE, options);
89
+ this.name = "IntercomClientDisconnectedError";
90
+ }
91
+ };
92
+ });
93
+
94
+ // dist/builtin/intercom/reconnect-backoff.ts
95
+ function reconnectDelayMs(attempt) {
96
+ return BACKOFF_MS[Math.min(attempt, BACKOFF_MS.length - 1)];
97
+ }
98
+ var BACKOFF_MS;
99
+ var init_reconnect_backoff = __esm(() => {
100
+ BACKOFF_MS = [1000, 2000, 5000, 1e4, 30000];
101
+ });
102
+
71
103
  // dist/builtin/intercom/broker/framing.ts
72
- function writeMessage(socket, msg) {
104
+ function writeMessage(socket, msg, callback) {
73
105
  const json = JSON.stringify(msg);
74
106
  const payload = Buffer.from(json, "utf-8");
75
107
  const header = Buffer.alloc(4);
76
108
  header.writeUInt32BE(payload.length, 0);
77
- socket.write(Buffer.concat([header, payload]));
109
+ const frame = Buffer.concat([header, payload]);
110
+ if (callback === undefined)
111
+ socket.write(frame);
112
+ else
113
+ socket.write(frame, callback);
78
114
  }
79
115
  function createMessageReader(onMessage, onError) {
80
116
  let buffer = Buffer.alloc(0);
@@ -162,7 +198,7 @@ function getBrokerSocketPath(platform = process.platform, agentDir = getAgentDir
162
198
  var init_paths = () => {};
163
199
 
164
200
  // dist/builtin/intercom/broker/send-signature.ts
165
- function normalizeAttachments(attachments) {
201
+ function canonicalizeAttachmentsForSendSignature(attachments) {
166
202
  return attachments?.map((attachment) => ({
167
203
  type: attachment.type,
168
204
  name: attachment.name,
@@ -174,7 +210,7 @@ function buildSendSignature(to, options) {
174
210
  return JSON.stringify({
175
211
  to,
176
212
  text: options.text,
177
- attachments: normalizeAttachments(options.attachments) ?? [],
213
+ attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
178
214
  replyTo: options.replyTo ?? null,
179
215
  expectsReply: options.expectsReply ?? false,
180
216
  replyError: options.replyError ?? null
@@ -416,6 +452,9 @@ import { randomUUID as randomUUID2 } from "crypto";
416
452
  function toError(error) {
417
453
  return error instanceof Error ? error : new Error(String(error));
418
454
  }
455
+ function isWorkflowFutureStageRosterEntries(value) {
456
+ return Array.isArray(value) && value.every((entry) => typeof entry === "object" && entry !== null && entry.kind === "workflow-future-stage" && typeof entry.runId === "string" && typeof entry.target === "string" && typeof entry.queuedCount === "number" && Number.isInteger(entry.queuedCount) && entry.queuedCount >= 0 && typeof entry.group === "string");
457
+ }
419
458
  function isWorkflowStageRosterEntries(value) {
420
459
  return Array.isArray(value) && value.every((entry) => typeof entry === "object" && entry !== null && entry.kind === "workflow-stage" && typeof entry.runId === "string" && typeof entry.stageId === "string" && typeof entry.stageName === "string" && typeof entry.target === "string" && (entry.lifecycle === "pending" || entry.lifecycle === "running") && typeof entry.group === "string" && (entry.sessionId === undefined || typeof entry.sessionId === "string"));
421
460
  }
@@ -424,6 +463,7 @@ var init_client = __esm(() => {
424
463
  init_paths();
425
464
  init_pending_send_registry();
426
465
  init_group();
466
+ init_recoverable_disconnect();
427
467
  BROKER_SOCKET = getBrokerSocketPath();
428
468
  IntercomClient = class IntercomClient extends EventEmitter {
429
469
  returnAddress;
@@ -490,11 +530,11 @@ var init_client = __esm(() => {
490
530
  throw new Error("Not connected");
491
531
  }
492
532
  if (socket.destroyed || socket.writableEnded || !socket.writable) {
493
- throw new Error("Client disconnected");
533
+ throw new IntercomClientDisconnectedError;
494
534
  }
495
535
  return socket;
496
536
  }
497
- connect(session, supervisor, supervisorOwnerToken, messageSource) {
537
+ connect(session, supervisor, supervisorOwnerToken, messageSource, registrationGroup) {
498
538
  if (this.socket) {
499
539
  return Promise.reject(new Error("Already connected"));
500
540
  }
@@ -537,7 +577,7 @@ var init_client = __esm(() => {
537
577
  const onClose = () => {
538
578
  const wasConnecting = !settled && !this._sessionId;
539
579
  const wasDisconnecting = this.disconnecting;
540
- const disconnectError = this.disconnectError ?? new Error("Client disconnected");
580
+ const disconnectError = this.disconnectError ?? new IntercomClientDisconnectedError;
541
581
  this.disconnecting = false;
542
582
  cleanupConnectionAttempt();
543
583
  cleanupSocketListeners();
@@ -558,7 +598,7 @@ var init_client = __esm(() => {
558
598
  };
559
599
  const onSocketError = (err) => {
560
600
  if (connectionEstablished) {
561
- this.disconnectError = err;
601
+ this.disconnectError ??= new IntercomClientDisconnectedError({ cause: err });
562
602
  this.emit("error", err);
563
603
  }
564
604
  };
@@ -596,6 +636,7 @@ var init_client = __esm(() => {
596
636
  writeMessage(socket, {
597
637
  type: "register",
598
638
  session,
639
+ ...registrationGroup !== undefined ? { registrationGroup } : {},
599
640
  returnAddress: this.returnAddress,
600
641
  ...supervisor ? { supervisor } : {},
601
642
  ...supervisorOwnerToken ? { supervisorOwnerToken } : {}
@@ -639,15 +680,19 @@ var init_client = __esm(() => {
639
680
  break;
640
681
  }
641
682
  case "sessions": {
642
- const { requestId, sessions, workflowStages } = brokerMessage;
643
- if (typeof requestId !== "string" || !Array.isArray(sessions) || !sessions.every(isSessionInfo) || workflowStages !== undefined && !isWorkflowStageRosterEntries(workflowStages)) {
683
+ const { requestId, sessions, workflowStages, workflowFutureStages } = brokerMessage;
684
+ if (typeof requestId !== "string" || !Array.isArray(sessions) || !sessions.every(isSessionInfo) || workflowStages !== undefined && !isWorkflowStageRosterEntries(workflowStages) || workflowFutureStages !== undefined && !isWorkflowFutureStageRosterEntries(workflowFutureStages)) {
644
685
  throw new Error("Invalid sessions message");
645
686
  }
646
687
  const pending = this.pendingLists.get(requestId);
647
688
  if (!pending)
648
689
  return;
649
690
  this.pendingLists.delete(requestId);
650
- pending.resolve({ sessions, workflowStages: workflowStages ?? [] });
691
+ pending.resolve({
692
+ sessions,
693
+ workflowStages: workflowStages ?? [],
694
+ workflowFutureStages: workflowFutureStages ?? []
695
+ });
651
696
  break;
652
697
  }
653
698
  case "groups": {
@@ -683,8 +728,8 @@ var init_client = __esm(() => {
683
728
  break;
684
729
  }
685
730
  case "pending_stage_message": {
686
- const { requestId, from, senderRegistrationName, senderReturnAddress, runId, stageKey, message, live } = brokerMessage;
687
- 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") {
731
+ const { requestId, from, senderRegistrationName, senderReturnAddress, runId, target, message, live } = brokerMessage;
732
+ if (typeof requestId !== "string" || !isSessionInfo(from) || senderRegistrationName !== undefined && typeof senderRegistrationName !== "string" || senderReturnAddress !== undefined && typeof senderReturnAddress !== "string" || typeof runId !== "string" || typeof target !== "string" || !isMessage(message) || live !== undefined && typeof live !== "boolean") {
688
733
  throw new Error("Invalid pending-stage message event");
689
734
  }
690
735
  this.emit("pending_stage_message", {
@@ -693,7 +738,7 @@ var init_client = __esm(() => {
693
738
  ...senderRegistrationName === undefined ? {} : { senderRegistrationName },
694
739
  ...senderReturnAddress === undefined ? {} : { senderReturnAddress },
695
740
  runId,
696
- stageKey,
741
+ target,
697
742
  message,
698
743
  ...live === true ? { live: true } : {}
699
744
  });
@@ -711,6 +756,19 @@ var init_client = __esm(() => {
711
756
  });
712
757
  break;
713
758
  }
759
+ case "sticky_live_delivered": {
760
+ const { runId, messageId, target, deliveredTargets } = brokerMessage;
761
+ if (typeof runId !== "string" || typeof messageId !== "string" || typeof target !== "string" || !Array.isArray(deliveredTargets) || !deliveredTargets.every((entry) => typeof entry === "string")) {
762
+ throw new Error("Invalid sticky live-delivery event");
763
+ }
764
+ this.emit("sticky_live_delivered", {
765
+ runId,
766
+ messageId,
767
+ target,
768
+ deliveredTargets
769
+ });
770
+ break;
771
+ }
714
772
  case "live_workflow_stage_route_registered": {
715
773
  const { requestId } = brokerMessage;
716
774
  if (typeof requestId !== "string")
@@ -723,11 +781,18 @@ var init_client = __esm(() => {
723
781
  break;
724
782
  }
725
783
  case "queued": {
726
- const { messageId, attemptId, runId, stageKey, position } = brokerMessage;
727
- if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof runId !== "string" || typeof stageKey !== "string" || typeof position !== "number" || position < 1) {
784
+ const { messageId, attemptId, target, position, notInKnownSet } = brokerMessage;
785
+ if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof target !== "string" || typeof position !== "number" || position < 1 || notInKnownSet !== undefined && notInKnownSet !== true) {
728
786
  throw new Error("Invalid queued message");
729
787
  }
730
- const result = { id: messageId, delivered: false, queued: true, runId, stageKey, position };
788
+ const result = {
789
+ id: messageId,
790
+ delivered: false,
791
+ queued: true,
792
+ target,
793
+ position,
794
+ ...notInKnownSet === true ? { notInKnownSet: true } : {}
795
+ };
731
796
  if (attemptId === undefined)
732
797
  this.pendingSends.resolveLegacy(messageId, result);
733
798
  else
@@ -748,7 +813,7 @@ var init_client = __esm(() => {
748
813
  }
749
814
  case "delivery_failed": {
750
815
  const { messageId, attemptId, reason, reasonCode } = brokerMessage;
751
- if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof reason !== "string" || reasonCode !== undefined && reasonCode !== "message_id_conflict") {
816
+ if (typeof messageId !== "string" || attemptId !== undefined && typeof attemptId !== "string" || typeof reason !== "string" || reasonCode !== undefined && reasonCode !== "message_id_conflict" && reasonCode !== "session_not_found") {
752
817
  throw new Error("Invalid delivery_failed message");
753
818
  }
754
819
  const result = {
@@ -857,7 +922,7 @@ var init_client = __esm(() => {
857
922
  }
858
923
  this.disconnecting = true;
859
924
  this.disconnectError = null;
860
- this.failPending(new Error("Client disconnected"));
925
+ this.failPending(new IntercomClientDisconnectedError);
861
926
  await new Promise((resolve) => {
862
927
  let settled = false;
863
928
  const finish = () => {
@@ -1031,8 +1096,15 @@ var init_client = __esm(() => {
1031
1096
  }
1032
1097
  });
1033
1098
  }
1034
- registerPendingStageRoute(runId, group, capability, stages) {
1035
- writeMessage(this.requireActiveSocket(), { type: "register_pending_stage_route", runId, group, capability, stages });
1099
+ registerPendingStageRoute(runId, group, capability, stages, possibleStages) {
1100
+ writeMessage(this.requireActiveSocket(), {
1101
+ type: "register_pending_stage_route",
1102
+ runId,
1103
+ group,
1104
+ capability,
1105
+ stages,
1106
+ possibleStages
1107
+ });
1036
1108
  }
1037
1109
  registerLiveWorkflowStageRoute(runId, stageKeys, capability) {
1038
1110
  let socket;
@@ -1060,7 +1132,14 @@ var init_client = __esm(() => {
1060
1132
  }
1061
1133
  respondPendingStageMessage(requestId, result) {
1062
1134
  const socket = this.requireActiveSocket();
1063
- writeMessage(socket, result.outcome === "queued" ? { type: "pending_stage_message_result", requestId, outcome: "queued", position: result.position } : result.outcome === "refused" ? {
1135
+ writeMessage(socket, result.outcome === "queued" ? {
1136
+ type: "pending_stage_message_result",
1137
+ requestId,
1138
+ outcome: "queued",
1139
+ position: result.position,
1140
+ ...result.notInKnownSet === true ? { notInKnownSet: true } : {},
1141
+ ...result.forwardTargets === undefined ? {} : { forwardTargets: [...result.forwardTargets] }
1142
+ } : result.outcome === "forward" ? { type: "pending_stage_message_result", requestId, outcome: "forward", target: result.target } : result.outcome === "refused" ? {
1064
1143
  type: "pending_stage_message_result",
1065
1144
  requestId,
1066
1145
  outcome: "refused",
@@ -1095,7 +1174,12 @@ var init_client = __esm(() => {
1095
1174
  const messageId = options.messageId ?? randomUUID2();
1096
1175
  let acquired;
1097
1176
  try {
1098
- acquired = this.pendingSends.acquire(messageId, buildSendSignature(to, options), 1e4);
1177
+ const pendingSignature = JSON.stringify({
1178
+ transportTarget: to,
1179
+ logicalSignature: buildSendSignature(options.logicalTarget ?? to, options),
1180
+ requirePendingReply: options.requirePendingReply ?? false
1181
+ });
1182
+ acquired = this.pendingSends.acquire(messageId, pendingSignature, 1e4);
1099
1183
  } catch (error) {
1100
1184
  return Promise.reject(toError(error));
1101
1185
  }
@@ -1111,7 +1195,15 @@ var init_client = __esm(() => {
1111
1195
  content: { text: options.text, attachments: options.attachments }
1112
1196
  };
1113
1197
  try {
1114
- writeMessage(socket, { type, to, message, attemptId: acquired.attempt.attemptId, ...extra });
1198
+ writeMessage(socket, {
1199
+ type,
1200
+ to,
1201
+ ...options.logicalTarget === undefined ? {} : { logicalTarget: options.logicalTarget },
1202
+ ...options.requirePendingReply === undefined ? {} : { requirePendingReply: options.requirePendingReply },
1203
+ message,
1204
+ attemptId: acquired.attempt.attemptId,
1205
+ ...extra
1206
+ });
1115
1207
  } catch (error) {
1116
1208
  this.pendingSends.reject(acquired.attempt, toError(error));
1117
1209
  }
@@ -1173,7 +1265,7 @@ var init_client = __esm(() => {
1173
1265
 
1174
1266
  // dist/builtin/intercom/broker/spawn.ts
1175
1267
  import { spawn } from "child_process";
1176
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
1268
+ import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
1177
1269
  import { join as join2, dirname } from "path";
1178
1270
  import { fileURLToPath } from "url";
1179
1271
  import { createRequire } from "module";
@@ -1192,6 +1284,11 @@ function resolveBrokerEntrypoint(packageRoot) {
1192
1284
  const bundled = join2(packageRoot, "broker", "broker.bundle.mjs");
1193
1285
  return existsSync(bundled) ? bundled : join2(packageRoot, "broker", "broker.ts");
1194
1286
  }
1287
+ function ensurePrivateDirectory(path) {
1288
+ mkdirSync(path, { recursive: true, mode: 448 });
1289
+ if (process.platform !== "win32")
1290
+ chmodSync(path, 448);
1291
+ }
1195
1292
  function sleep(ms) {
1196
1293
  return new Promise((resolve) => setTimeout(resolve, ms));
1197
1294
  }
@@ -1257,7 +1354,7 @@ function getWindowsHiddenLauncherScript(commandLine) {
1257
1354
  `);
1258
1355
  }
1259
1356
  function writeWindowsHiddenLauncher(commandLine, launcherPath = getWindowsHiddenLauncherPath()) {
1260
- mkdirSync(dirname(launcherPath), { recursive: true });
1357
+ ensurePrivateDirectory(dirname(launcherPath));
1261
1358
  writeFileSync(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
1262
1359
  return launcherPath;
1263
1360
  }
@@ -1296,7 +1393,7 @@ function getBrokerSpawnOptions(extensionDir = EXTENSION_DIR, stderr = "ignore")
1296
1393
  };
1297
1394
  }
1298
1395
  function resetBrokerLog(logPath = BROKER_LOG) {
1299
- mkdirSync(dirname(logPath), { recursive: true });
1396
+ ensurePrivateDirectory(dirname(logPath));
1300
1397
  closeSync(openSync(logPath, "w"));
1301
1398
  }
1302
1399
  function readBrokerLogTail(logPath = BROKER_LOG, maxBytes = BROKER_LOG_TAIL_BYTES) {
@@ -1332,7 +1429,7 @@ function toError2(error) {
1332
1429
  return error instanceof Error ? error : new Error(String(error));
1333
1430
  }
1334
1431
  async function spawnBrokerIfNeeded(brokerCommand, brokerArgs) {
1335
- mkdirSync(INTERCOM_DIR, { recursive: true });
1432
+ ensurePrivateDirectory(INTERCOM_DIR);
1336
1433
  if (await isBrokerRunning()) {
1337
1434
  return;
1338
1435
  }
@@ -1579,6 +1676,15 @@ class InlineMessageComponent {
1579
1676
  }
1580
1677
  var init_inline_message = () => {};
1581
1678
 
1679
+ // dist/builtin/intercom/retry-policy.ts
1680
+ var ASK_REPLY_TIMEOUT_MS, RETRY_IDENTITY_RETRY_OPPORTUNITY_MS, RETRY_IDENTITY_TTL_MS, DELIVERED_MESSAGE_TTL_MS;
1681
+ var init_retry_policy = __esm(() => {
1682
+ ASK_REPLY_TIMEOUT_MS = 10 * 60 * 1000;
1683
+ RETRY_IDENTITY_RETRY_OPPORTUNITY_MS = 60 * 1000;
1684
+ RETRY_IDENTITY_TTL_MS = ASK_REPLY_TIMEOUT_MS + RETRY_IDENTITY_RETRY_OPPORTUNITY_MS;
1685
+ DELIVERED_MESSAGE_TTL_MS = RETRY_IDENTITY_TTL_MS + RETRY_IDENTITY_RETRY_OPPORTUNITY_MS;
1686
+ });
1687
+
1582
1688
  // dist/builtin/intercom/reply-waiter.ts
1583
1689
  class ReplyWaiterRegistry {
1584
1690
  timeoutMs;
@@ -1657,7 +1763,8 @@ class ReplyWaiterRegistry {
1657
1763
  }
1658
1764
  var DEFAULT_REPLY_TIMEOUT_MS, DEFAULT_MAX_PENDING_REPLY_WAITS = 6;
1659
1765
  var init_reply_waiter = __esm(() => {
1660
- DEFAULT_REPLY_TIMEOUT_MS = 10 * 60 * 1000;
1766
+ init_retry_policy();
1767
+ DEFAULT_REPLY_TIMEOUT_MS = ASK_REPLY_TIMEOUT_MS;
1661
1768
  });
1662
1769
 
1663
1770
  // dist/builtin/intercom/config.ts
@@ -1786,7 +1893,7 @@ class ReplyTracker {
1786
1893
  pendingAsks = new Map;
1787
1894
  pendingTurnContexts = [];
1788
1895
  currentTurnContext = null;
1789
- constructor(askTimeoutMs = 10 * 60 * 1000) {
1896
+ constructor(askTimeoutMs = ASK_REPLY_TIMEOUT_MS) {
1790
1897
  this.askTimeoutMs = askTimeoutMs;
1791
1898
  }
1792
1899
  recordIncomingMessage(from, message, receivedAt = Date.now()) {
@@ -1885,7 +1992,9 @@ class ReplyTracker {
1885
1992
  }
1886
1993
  }
1887
1994
  }
1888
- var init_reply_tracker = () => {};
1995
+ var init_reply_tracker = __esm(() => {
1996
+ init_retry_policy();
1997
+ });
1889
1998
 
1890
1999
  // dist/builtin/intercom/parent-ask-handoff.ts
1891
2000
  import { isStaleExtensionContextError as isStaleExtensionContextError2 } from "@bastani/atomic";
@@ -2579,59 +2688,381 @@ var init_contact_supervisor_tool = __esm(() => {
2579
2688
  init_intercom_utils();
2580
2689
  });
2581
2690
 
2582
- // dist/builtin/intercom/broker/supervisor-channel.ts
2583
- var DEFAULT_TTL_MS;
2584
- var init_supervisor_channel = __esm(() => {
2585
- DEFAULT_TTL_MS = 10 * 60 * 1000;
2691
+ // dist/builtin/intercom/workflow-stage-target.ts
2692
+ function parseWorkflowStageTarget(target) {
2693
+ if (!target.startsWith(WORKFLOW_TARGET_PREFIX))
2694
+ return;
2695
+ const slash = target.indexOf("/", WORKFLOW_TARGET_PREFIX.length);
2696
+ if (slash < 0)
2697
+ return;
2698
+ const rootRunId = target.slice(WORKFLOW_TARGET_PREFIX.length, slash);
2699
+ if (!WORKFLOW_RUN_ID_PATTERN.test(rootRunId))
2700
+ return;
2701
+ const segments = target.slice(slash + 1).split("/");
2702
+ if (segments.length === 0 || segments.some((segment) => segment.length === 0))
2703
+ return;
2704
+ const kind = segments.includes("**") ? "deep-pattern" : segments.some((segment) => segment.includes("*")) ? "pattern" : "path";
2705
+ return { rootRunId, segments, kind };
2706
+ }
2707
+ function formatWorkflowStageTarget(rootRunId, ...segments) {
2708
+ return `${WORKFLOW_TARGET_PREFIX}${rootRunId}/${segments.join("/")}`;
2709
+ }
2710
+ function withWorkflowStageTargetFinalSegment(target, segment) {
2711
+ const parsed = parseWorkflowStageTarget(target);
2712
+ if (parsed === undefined || segment.length === 0 || segment.includes("/"))
2713
+ return;
2714
+ return formatWorkflowStageTarget(parsed.rootRunId, ...parsed.segments.slice(0, -1), segment);
2715
+ }
2716
+ var WORKFLOW_RUN_ID_PATTERN, WORKFLOW_TARGET_PREFIX = "workflow:";
2717
+ var init_workflow_stage_target = __esm(() => {
2718
+ WORKFLOW_RUN_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2586
2719
  });
2587
2720
 
2588
- // dist/builtin/intercom/broker/group-membership.ts
2589
- var init_group_membership = __esm(() => {
2590
- init_group();
2591
- });
2721
+ // dist/builtin/intercom/retry-identity.ts
2722
+ import { randomUUID as randomUUID4 } from "node:crypto";
2723
+ function operationKey(input) {
2724
+ return JSON.stringify([
2725
+ input.sessionId,
2726
+ input.action,
2727
+ input.target,
2728
+ input.text,
2729
+ canonicalizeAttachmentsForSendSignature(input.attachments),
2730
+ input.replyTo,
2731
+ input.expectsReply
2732
+ ]);
2733
+ }
2592
2734
 
2593
- // dist/builtin/intercom/broker/group-isolation.ts
2594
- var init_group_isolation = __esm(() => {
2595
- init_group_membership();
2596
- });
2597
- // dist/builtin/intercom/broker/send-handler.ts
2598
- function parsePendingStageTarget(target) {
2599
- const separator = target.indexOf(":");
2600
- if (separator < 0)
2601
- return;
2602
- const runId = target.slice(0, separator);
2603
- const stageKey = target.slice(separator + 1);
2604
- return WORKFLOW_RUN_ID_PATTERN.test(runId) && stageKey.length > 0 ? { runId, stageKey } : undefined;
2605
- }
2606
- var WORKFLOW_RUN_ID_PATTERN;
2607
- var init_send_handler = __esm(() => {
2608
- init_supervisor_channel();
2609
- init_group_isolation();
2610
- init_group_membership();
2611
- WORKFLOW_RUN_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2735
+ class RetryIdentityReservations {
2736
+ tokenReservations = new Map;
2737
+ attemptStates = new WeakMap;
2738
+ ttlMs;
2739
+ maxEntries;
2740
+ maxReuses;
2741
+ now;
2742
+ createId;
2743
+ createToken;
2744
+ freshInFlightCount = 0;
2745
+ constructor(options = {}) {
2746
+ this.ttlMs = options.ttlMs ?? RETRY_IDENTITY_TTL_MS;
2747
+ this.maxEntries = options.maxEntries ?? RETRY_IDENTITY_MAX_ENTRIES;
2748
+ this.maxReuses = options.maxReuses ?? RETRY_IDENTITY_MAX_REUSES;
2749
+ this.now = options.now ?? Date.now;
2750
+ this.createId = options.createId ?? randomUUID4;
2751
+ this.createToken = options.createToken ?? randomUUID4;
2752
+ }
2753
+ begin(input, retryToken) {
2754
+ const now = this.now();
2755
+ const key = operationKey(input);
2756
+ if (retryToken !== undefined)
2757
+ return this.claimRetry(key, retryToken, now);
2758
+ this.prune(now);
2759
+ this.discardSettledTombstonesForCapacity();
2760
+ if (this.tokenReservations.size + this.freshInFlightCount >= this.maxEntries) {
2761
+ throw new RetryIdentityCapacityError;
2762
+ }
2763
+ const reservation = {
2764
+ key,
2765
+ messageId: this.createId(),
2766
+ expiresAt: now + this.ttlMs,
2767
+ sessionId: input.sessionId,
2768
+ action: input.action,
2769
+ reuseCount: 0,
2770
+ state: "fresh-in-flight"
2771
+ };
2772
+ this.freshInFlightCount += 1;
2773
+ return this.attempt(reservation);
2774
+ }
2775
+ validateRetryToken(retryToken, sessionId, action) {
2776
+ const reservation = this.tokenReservation(retryToken, this.now());
2777
+ if (reservation.sessionId !== sessionId || reservation.action !== action) {
2778
+ throw new RetryTokenError("mismatch");
2779
+ }
2780
+ this.requireClaimable(reservation);
2781
+ return Math.max(0, this.maxReuses - reservation.reuseCount);
2782
+ }
2783
+ bindReplyRoute(attempt, route) {
2784
+ const reservation = this.currentReservation(attempt);
2785
+ if (reservation === undefined || reservation.action !== "reply" || reservation.state !== "fresh-in-flight" || reservation.replyRoute !== undefined || route.senderId.length === 0 || route.messageId.length === 0) {
2786
+ throw new RetryTokenError("invalid");
2787
+ }
2788
+ reservation.replyRoute = { ...route };
2789
+ }
2790
+ replyRoute(attempt) {
2791
+ const route = this.currentReservation(attempt)?.replyRoute;
2792
+ return route === undefined ? undefined : { ...route };
2793
+ }
2794
+ retainAfterRecoverableDisconnect(attempt) {
2795
+ return this.retain(attempt, true);
2796
+ }
2797
+ retainAfterInconclusiveRetry(attempt) {
2798
+ return this.retain(attempt, false);
2799
+ }
2800
+ release(attempt) {
2801
+ const reservation = this.currentReservation(attempt);
2802
+ if (reservation === undefined || reservation.state === "released" || reservation.state === "settled")
2803
+ return;
2804
+ if (reservation.retryToken === undefined) {
2805
+ this.releaseFreshCapacity(reservation);
2806
+ reservation.state = "released";
2807
+ return;
2808
+ }
2809
+ this.finishRetained(reservation, "settled");
2810
+ }
2811
+ remainingRetries(attempt) {
2812
+ const reservation = this.currentReservation(attempt);
2813
+ return reservation === undefined ? 0 : Math.max(0, this.maxReuses - reservation.reuseCount);
2814
+ }
2815
+ claimRetry(key, retryToken, now) {
2816
+ const reservation = this.tokenReservation(retryToken, now);
2817
+ if (reservation.key !== key)
2818
+ throw new RetryTokenError("mismatch");
2819
+ this.requireClaimable(reservation);
2820
+ reservation.state = "retry-in-flight";
2821
+ reservation.reuseCount += 1;
2822
+ return this.attempt(reservation);
2823
+ }
2824
+ tokenReservation(retryToken, now) {
2825
+ const reservation = this.tokenReservations.get(retryToken);
2826
+ if (reservation === undefined)
2827
+ throw new RetryTokenError("invalid");
2828
+ if (reservation.expiresAt <= now) {
2829
+ this.expire(reservation);
2830
+ throw new RetryTokenError("expired");
2831
+ }
2832
+ return reservation;
2833
+ }
2834
+ requireClaimable(reservation) {
2835
+ if (reservation.state === "settled")
2836
+ throw new RetryTokenError("settled");
2837
+ if (reservation.state === "exhausted" || reservation.reuseCount >= this.maxReuses) {
2838
+ reservation.state = "exhausted";
2839
+ throw new RetryTokenError("exhausted");
2840
+ }
2841
+ if (reservation.state === "retry-in-flight")
2842
+ throw new RetryTokenError("in_flight");
2843
+ if (reservation.state !== "retained")
2844
+ throw new RetryTokenError("invalid");
2845
+ }
2846
+ retain(attempt, allowFresh) {
2847
+ const now = this.now();
2848
+ this.prune(now);
2849
+ const reservation = this.currentReservation(attempt);
2850
+ if (reservation === undefined)
2851
+ return;
2852
+ const fresh = reservation.state === "fresh-in-flight";
2853
+ if (!fresh && reservation.state !== "retry-in-flight")
2854
+ return;
2855
+ if (fresh && !allowFresh) {
2856
+ this.releaseFreshCapacity(reservation);
2857
+ reservation.state = "released";
2858
+ return;
2859
+ }
2860
+ if (reservation.expiresAt <= now) {
2861
+ if (reservation.retryToken === undefined) {
2862
+ this.releaseFreshCapacity(reservation);
2863
+ reservation.state = "released";
2864
+ } else
2865
+ this.expire(reservation);
2866
+ return;
2867
+ }
2868
+ if (reservation.reuseCount >= this.maxReuses) {
2869
+ if (reservation.retryToken === undefined) {
2870
+ this.releaseFreshCapacity(reservation);
2871
+ reservation.state = "released";
2872
+ } else
2873
+ reservation.state = "exhausted";
2874
+ return;
2875
+ }
2876
+ if (fresh) {
2877
+ reservation.retryToken = this.uniqueToken();
2878
+ this.tokenReservations.set(reservation.retryToken, reservation);
2879
+ this.releaseFreshCapacity(reservation);
2880
+ }
2881
+ reservation.state = "retained";
2882
+ return reservation.retryToken;
2883
+ }
2884
+ discardSettledTombstonesForCapacity() {
2885
+ if (this.tokenReservations.size + this.freshInFlightCount < this.maxEntries)
2886
+ return;
2887
+ for (const [token, reservation] of this.tokenReservations) {
2888
+ if (reservation.state !== "settled")
2889
+ continue;
2890
+ this.tokenReservations.delete(token);
2891
+ if (this.tokenReservations.size + this.freshInFlightCount < this.maxEntries)
2892
+ return;
2893
+ }
2894
+ }
2895
+ uniqueToken() {
2896
+ for (;; ) {
2897
+ const token = this.createToken();
2898
+ if (token.length > 0 && !this.tokenReservations.has(token))
2899
+ return token;
2900
+ }
2901
+ }
2902
+ attempt(reservation) {
2903
+ const attempt = {
2904
+ key: reservation.key,
2905
+ messageId: reservation.messageId,
2906
+ reuseCount: reservation.reuseCount,
2907
+ expiresAt: reservation.expiresAt,
2908
+ ...reservation.state === "retry-in-flight" && reservation.retryToken !== undefined ? { retryToken: reservation.retryToken } : {}
2909
+ };
2910
+ this.attemptStates.set(attempt, reservation);
2911
+ return attempt;
2912
+ }
2913
+ currentReservation(attempt) {
2914
+ const reservation = this.attemptStates.get(attempt);
2915
+ return reservation?.reuseCount === attempt.reuseCount ? reservation : undefined;
2916
+ }
2917
+ releaseFreshCapacity(reservation) {
2918
+ if (reservation.state !== "fresh-in-flight")
2919
+ return;
2920
+ this.freshInFlightCount = Math.max(0, this.freshInFlightCount - 1);
2921
+ }
2922
+ finishRetained(reservation, state) {
2923
+ reservation.state = state;
2924
+ }
2925
+ expire(reservation) {
2926
+ if (reservation.retryToken !== undefined)
2927
+ this.tokenReservations.delete(reservation.retryToken);
2928
+ this.finishRetained(reservation, "released");
2929
+ }
2930
+ prune(now) {
2931
+ for (const reservation of this.tokenReservations.values()) {
2932
+ if (reservation.expiresAt <= now)
2933
+ this.expire(reservation);
2934
+ }
2935
+ }
2936
+ }
2937
+ var RETRY_IDENTITY_MAX_ENTRIES = 1000, RETRY_IDENTITY_MAX_REUSES = 3, RetryIdentityCapacityError, RetryTokenError;
2938
+ var init_retry_identity = __esm(() => {
2939
+ init_retry_policy();
2940
+ RetryIdentityCapacityError = class RetryIdentityCapacityError extends Error {
2941
+ constructor() {
2942
+ super("Intercom retry identity capacity is exhausted; refusing delivery until an in-flight or retained identity is released or expires");
2943
+ this.name = "RetryIdentityCapacityError";
2944
+ }
2945
+ };
2946
+ RetryTokenError = class RetryTokenError extends Error {
2947
+ code;
2948
+ constructor(code) {
2949
+ const reason = {
2950
+ invalid: "is invalid or belongs to another Intercom tool session",
2951
+ expired: "has expired",
2952
+ mismatch: "does not match this Intercom operation",
2953
+ in_flight: "is already claimed by an in-flight retry",
2954
+ settled: "belongs to an operation that is already settled",
2955
+ exhausted: `has exhausted its ${RETRY_IDENTITY_MAX_REUSES} retry attempts`
2956
+ }[code];
2957
+ super(`Intercom retry token ${reason}; refusing delivery`);
2958
+ this.code = code;
2959
+ this.name = "RetryTokenError";
2960
+ }
2961
+ };
2612
2962
  });
2613
2963
 
2614
2964
  // dist/builtin/intercom/intercom-tool.ts
2615
- import { randomUUID as randomUUID4 } from "crypto";
2616
2965
  import { Type as Type2 } from "typebox";
2617
2966
  import { Text as Text3 } from "@earendil-works/pi-tui";
2967
+ function retryTokenGuidance(retryToken, remainingRetries) {
2968
+ if (retryToken === undefined)
2969
+ return "";
2970
+ return ` Retry this exact operation with retryToken \`${retryToken}\` (${remainingRetries} claimed ${remainingRetries === 1 ? "attempt" : "attempts"} remain; the original deadline is unchanged).`;
2971
+ }
2972
+ function retryErrorDetails(retryToken) {
2973
+ return retryToken === undefined ? { error: true } : { error: true, retryToken };
2974
+ }
2975
+ function retainRecoverableRetry(reservations, attempt) {
2976
+ const retryToken = reservations.retainAfterRecoverableDisconnect(attempt);
2977
+ return {
2978
+ ...retryToken === undefined ? {} : { retryToken },
2979
+ remainingRetries: reservations.remainingRetries(attempt)
2980
+ };
2981
+ }
2982
+ function retainInconclusiveRetry(reservations, attempt) {
2983
+ const retryToken = reservations.retainAfterInconclusiveRetry(attempt);
2984
+ return {
2985
+ ...retryToken === undefined ? {} : { retryToken },
2986
+ remainingRetries: reservations.remainingRetries(attempt)
2987
+ };
2988
+ }
2618
2989
  async function listDirectory(client, group) {
2619
2990
  if (typeof client.listDirectory === "function")
2620
2991
  return client.listDirectory(group);
2621
- return { sessions: await client.listSessions(group), workflowStages: [] };
2992
+ return { sessions: await client.listSessions(group), workflowStages: [], workflowFutureStages: [] };
2622
2993
  }
2623
2994
  async function resolveReplySender(client, logicalTarget, sendTarget) {
2624
2995
  if (logicalTarget !== sendTarget)
2625
- return sendTarget;
2626
- if (parsePendingStageTarget(logicalTarget) === undefined)
2627
- return sendTarget;
2628
- const stage = (await listDirectory(client)).workflowStages.find((candidate) => candidate.sessionId !== undefined && (candidate.target === logicalTarget || `${candidate.runId}:${candidate.stageName}` === logicalTarget));
2629
- return stage?.sessionId ?? sendTarget;
2996
+ return { kind: "unresolved" };
2997
+ const parsedTarget = parseWorkflowStageTarget(logicalTarget);
2998
+ if (parsedTarget?.kind !== "path")
2999
+ return { kind: "unresolved" };
3000
+ const stages = (await listDirectory(client)).workflowStages;
3001
+ const aliasMatches = stages.filter((candidate) => candidate.sessionId !== undefined && (candidate.target === logicalTarget || withWorkflowStageTargetFinalSegment(candidate.target, candidate.stageName) === logicalTarget));
3002
+ const aliasSessionIds = new Set(aliasMatches.map((candidate) => candidate.sessionId));
3003
+ if (aliasSessionIds.size > 1) {
3004
+ return {
3005
+ kind: "ambiguous",
3006
+ reason: `multiple live workflow stages share the target "${logicalTarget}"`
3007
+ };
3008
+ }
3009
+ if (aliasSessionIds.size === 1) {
3010
+ const [onlySessionId] = aliasSessionIds;
3011
+ if (onlySessionId !== undefined)
3012
+ return { kind: "resolved", sessionId: onlySessionId };
3013
+ }
3014
+ const finalSegment = parsedTarget.segments.at(-1);
3015
+ const middleSegments = parsedTarget.segments.slice(0, -1);
3016
+ const boundaryAgrees = (candidate) => {
3017
+ const parsedCandidate = parseWorkflowStageTarget(candidate.target);
3018
+ if (parsedCandidate === undefined || parsedCandidate.rootRunId !== parsedTarget.rootRunId || parsedCandidate.segments.length < 2) {
3019
+ return false;
3020
+ }
3021
+ if (middleSegments.length === 0)
3022
+ return true;
3023
+ const candidateMiddles = parsedCandidate.segments.slice(0, -1);
3024
+ return middleSegments.every((segment, index) => {
3025
+ const owningRunIdentity = index === candidateMiddles.length - 1 ? candidate.runId : undefined;
3026
+ return segment === candidateMiddles[index] || segment === owningRunIdentity;
3027
+ });
3028
+ };
3029
+ const looseCandidates = stages.filter((candidate) => {
3030
+ if (candidate.sessionId === undefined)
3031
+ return false;
3032
+ if (candidate.stageId !== finalSegment && candidate.stageName !== finalSegment)
3033
+ return false;
3034
+ const parsedCandidate = parseWorkflowStageTarget(candidate.target);
3035
+ return parsedCandidate !== undefined && parsedCandidate.rootRunId === parsedTarget.rootRunId && parsedCandidate.segments.length >= 2;
3036
+ });
3037
+ const strictMatches = looseCandidates.filter(boundaryAgrees);
3038
+ const strictSessionIds = new Set(strictMatches.map((candidate) => candidate.sessionId));
3039
+ if (strictSessionIds.size === 1) {
3040
+ const [onlySessionId] = strictSessionIds;
3041
+ if (onlySessionId !== undefined)
3042
+ return { kind: "resolved", sessionId: onlySessionId };
3043
+ }
3044
+ const looseSessionIds = new Set(looseCandidates.map((candidate) => candidate.sessionId));
3045
+ if (looseSessionIds.size > 1 || strictSessionIds.size > 1) {
3046
+ return {
3047
+ kind: "ambiguous",
3048
+ reason: `multiple live workflow stages share the final segment "${finalSegment}" under different boundaries`
3049
+ };
3050
+ }
3051
+ if (looseSessionIds.size === 1) {
3052
+ const [onlySessionId] = looseSessionIds;
3053
+ if (onlySessionId !== undefined)
3054
+ return { kind: "resolved", sessionId: onlySessionId };
3055
+ }
3056
+ return { kind: "unresolved" };
2630
3057
  }
2631
3058
  function formatWorkflowStageRow(stage) {
2632
3059
  return `- **${stage.stageName}** — workflow stage [${stage.lifecycle.toUpperCase()}] — target: \`${stage.target}\`${stage.sessionId === undefined ? "" : ` — intercom session: ${stage.sessionId}`}`;
2633
3060
  }
3061
+ function formatWorkflowFutureStageRow(stage) {
3062
+ return `- future workflow stage \`${stage.target}\` — ${stage.queuedCount} queued message${stage.queuedCount === 1 ? "" : "s"}`;
3063
+ }
2634
3064
  function registerIntercomTool(pi, deps) {
3065
+ const retryIdentities = new RetryIdentityReservations({ maxEntries: deps.retryIdentityMaxEntries });
2635
3066
  const { childOrchestratorMetadata, ensureConnected, syncPresenceIdentity, beginReplyWait } = deps;
2636
3067
  const resolveTarget = deps.resolveSessionTarget ?? resolveSessionTargetId;
2637
3068
  const getMetadata = typeof childOrchestratorMetadata === "function" ? childOrchestratorMetadata : () => childOrchestratorMetadata ?? null;
@@ -2645,9 +3076,17 @@ Use this to communicate findings, request help, or coordinate work with other se
2645
3076
  Sessions belong to an intercom group and can ONLY message sessions in the same group;
2646
3077
  cross-group sends are rejected by the broker. Ungrouped sessions share the "default" group.
2647
3078
 
2648
- For send, live session names and exact full session IDs remain supported. For a known
2649
- workflow stage, use the exact \`<runId>:<stageKey>\` target; send messages to pending stages
2650
- queue automatically.
3079
+ For send, live session names and exact full session IDs remain supported. Workflow-stage targets use
3080
+ \`workflow:<rootRunId>/<segment>[/<segment>...]\`; a segment may be a stage name, run id, or glob
3081
+ (\`*\` matches one segment and may be embedded, while \`**\` matches any depth). Use \`intercom list\`
3082
+ inside the invocation group to see live, pending, and possible future targets with queued counts.
3083
+ \`workflow:<rootRunId>/**\` reaches every live stage immediately and remains sticky for every future
3084
+ stage until the root run terminates; narrower name or pattern sends likewise reach every future match.
3085
+ A valid target outside the known set queues speculatively with a \`notInKnownSet\` warning and settles
3086
+ undeliverable at terminal only if never delivered. Use \`ask\` only for a live, reply-capable target.
3087
+ Stage paths win over same-named owned subgroups for send/ask; \`list\` with \`group\` continues to
3088
+ select the group.
3089
+ If a \`send\`, \`ask\`, or \`reply\` fails with \`Client disconnected\`, retry that exact operation with the returned \`retryToken\`, up to three claimed attempts; a call without the token is always a distinct operation.
2651
3090
 
2652
3091
  Usage:
2653
3092
  intercom({ action: "list" }) → List sessions visible through your groups
@@ -2659,19 +3098,20 @@ Usage:
2659
3098
  intercom({ action: "send", to: "session-name", message: "..." }) → Send message (shared group only)
2660
3099
  intercom({ action: "ask", to: "session-name", message: "..." }) → Ask and wait for reply
2661
3100
  intercom({ action: "reply", message: "..." }) → Reply to the active or exact pending ask
3101
+ intercom({ action: "send", to: "session-name", message: "...", retryToken: "..." }) → Claim the exact retry returned by a retryable failure
2662
3102
  intercom({ action: "pending" }) → List unresolved inbound asks
2663
3103
  intercom({ action: "status" }) → Show connection status and all your groups
2664
3104
 
2665
3105
  The "join" action is additive. "default" is the shared default group; "true" and
2666
3106
  "auto" are reserved for subagent auto-groups. Ordinary delivery requires at least
2667
3107
  one shared membership; contact_supervisor remains the only cross-group path.`,
2668
- promptSnippet: "Use to coordinate with other local agent sessions that share an intercom group: discover groups or peers, send updates, ask for help, or check connectivity. Ordinary messages require a shared membership.",
3108
+ promptSnippet: "Use to coordinate with other local agent sessions that share an intercom group. For a retryable send/ask/reply failure, repeat the exact operation with its returned retryToken; omit retryToken for fresh operations.",
2669
3109
  parameters: Type2.Object({
2670
3110
  action: Type2.String({
2671
3111
  description: "Action: 'list', 'groups', 'join', 'leave', 'send', 'ask', 'reply', 'pending', or 'status'"
2672
3112
  }),
2673
3113
  to: Type2.Optional(Type2.String({
2674
- 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')"
3114
+ description: "Live session name, exact full session ID, or `workflow:<rootRunId>/<segment>[/<segment>...]` path; `*` matches one segment and `**` any depth. Send queues sticky delivery for pending/future matches; `workflow:<rootRunId>/**` broadcasts to live and future stages. Use `ask` only on live targets (for 'send', 'ask', or targeted 'reply')"
2675
3115
  })),
2676
3116
  message: Type2.Optional(Type2.String({
2677
3117
  description: "Message to send (for 'send', 'ask', or 'reply' action)"
@@ -2685,23 +3125,53 @@ one shared membership; contact_supervisor remains the only cross-group path.`,
2685
3125
  replyTo: Type2.Optional(Type2.String({
2686
3126
  description: "Exact pending-ask message ID; disambiguates concurrent asks, including asks from one sender"
2687
3127
  })),
3128
+ retryToken: Type2.Optional(Type2.String({
3129
+ minLength: 1,
3130
+ maxLength: 128,
3131
+ description: "Opaque token returned after a retryable send/ask/reply failure. Repeat the exact same action and arguments with this token; omit it for every fresh operation."
3132
+ })),
2688
3133
  group: Type2.Optional(Type2.String({
2689
3134
  description: "Group name for 'join' or optional targeted 'leave'; read-only group filter for 'list'/'status'. 'send'/'ask' use shared memberships."
2690
3135
  }))
2691
3136
  }),
2692
3137
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
3138
+ const toolSessionId = ctx.sessionManager.getSessionId();
3139
+ const { action, to, message, attachments, replyTo, retryToken, group } = params;
3140
+ let retryPreflightRemaining;
3141
+ if (retryToken !== undefined) {
3142
+ if (action !== "send" && action !== "ask" && action !== "reply") {
3143
+ const error = new RetryTokenError("mismatch");
3144
+ return {
3145
+ content: [{ type: "text", text: getErrorMessage(error) }],
3146
+ isError: true,
3147
+ details: { error: true }
3148
+ };
3149
+ }
3150
+ try {
3151
+ retryPreflightRemaining = retryIdentities.validateRetryToken(retryToken, toolSessionId, action);
3152
+ } catch (error) {
3153
+ return {
3154
+ content: [{ type: "text", text: getErrorMessage(error) }],
3155
+ isError: true,
3156
+ details: { error: true }
3157
+ };
3158
+ }
3159
+ }
2693
3160
  let connectedClient;
2694
3161
  try {
2695
3162
  connectedClient = await ensureConnected("tool");
2696
3163
  } catch (error) {
3164
+ const retainedToken = retryToken !== undefined && isRecoverableIntercomDisconnect(error) ? retryToken : undefined;
2697
3165
  return {
2698
- content: [{ type: "text", text: `Intercom not connected: ${getErrorMessage(error)}` }],
3166
+ content: [{
3167
+ type: "text",
3168
+ text: `Intercom not connected: ${getErrorMessage(error)}${retryTokenGuidance(retainedToken, retryPreflightRemaining ?? 0)}`
3169
+ }],
2699
3170
  isError: true,
2700
- details: { error: true }
3171
+ details: retryErrorDetails(retainedToken)
2701
3172
  };
2702
3173
  }
2703
- syncPresenceIdentity(ctx.sessionManager.getSessionId());
2704
- const { action, to, message, attachments, replyTo, group } = params;
3174
+ syncPresenceIdentity(toolSessionId);
2705
3175
  const resolveOwnGroups = (sessions) => {
2706
3176
  if (Array.isArray(connectedClient.groups)) {
2707
3177
  return connectedClient.groups.map((membership) => normalizeGroup(membership));
@@ -2826,7 +3296,8 @@ ${lines.join(`
2826
3296
  const peeked = await listDirectory(connectedClient, requestedGroup);
2827
3297
  const rows = [
2828
3298
  ...peeked.sessions.map((session) => formatSessionListRow(session, currentSession.cwd, session.id === mySessionId)),
2829
- ...peeked.workflowStages.map(formatWorkflowStageRow)
3299
+ ...peeked.workflowStages.map(formatWorkflowStageRow),
3300
+ ...(peeked.workflowFutureStages ?? []).map(formatWorkflowFutureStageRow)
2830
3301
  ];
2831
3302
  const section = rows.length === 0 ? `**Group [${requestedGroup}] (read-only peek):**
2832
3303
  No sessions or workflow stages in this group.` : `**Group [${requestedGroup}] (read-only peek):**
@@ -2841,7 +3312,8 @@ ${section}` }],
2841
3312
  group: ownGroups.at(-1),
2842
3313
  groups: ownGroups,
2843
3314
  peekGroup: requestedGroup,
2844
- workflowStages: peeked.workflowStages
3315
+ workflowStages: peeked.workflowStages,
3316
+ ...(peeked.workflowFutureStages?.length ?? 0) === 0 ? {} : { workflowFutureStages: peeked.workflowFutureStages }
2845
3317
  }
2846
3318
  };
2847
3319
  }
@@ -2850,7 +3322,8 @@ ${section}` }],
2850
3322
  ${formatSessionListRow(currentSession, currentSession.cwd, true)}`;
2851
3323
  const visibleRows = [
2852
3324
  ...otherSessions.map((session) => formatSessionListRow(session, currentSession.cwd, false)),
2853
- ...ownDirectory.workflowStages.map(formatWorkflowStageRow)
3325
+ ...ownDirectory.workflowStages.map(formatWorkflowStageRow),
3326
+ ...(ownDirectory.workflowFutureStages ?? []).map(formatWorkflowFutureStageRow)
2854
3327
  ];
2855
3328
  const otherSection = visibleRows.length === 0 ? `**Other sessions and workflow stages:**
2856
3329
  No other sessions or workflow stages share any of your groups.` : `**Other visible sessions and workflow stages:**
@@ -2864,7 +3337,8 @@ ${otherSection}` }],
2864
3337
  details: {
2865
3338
  group: ownGroups.at(-1),
2866
3339
  groups: ownGroups,
2867
- ...ownDirectory.workflowStages.length === 0 ? {} : { workflowStages: ownDirectory.workflowStages }
3340
+ ...ownDirectory.workflowStages.length === 0 ? {} : { workflowStages: ownDirectory.workflowStages },
3341
+ ...(ownDirectory.workflowFutureStages?.length ?? 0) === 0 ? {} : { workflowFutureStages: ownDirectory.workflowFutureStages }
2868
3342
  }
2869
3343
  };
2870
3344
  } catch (error) {
@@ -2877,20 +3351,35 @@ ${otherSection}` }],
2877
3351
  }
2878
3352
  case "send": {
2879
3353
  if (!to || !message) {
3354
+ const errorText = retryToken === undefined ? "Missing 'to' or 'message' parameter" : getErrorMessage(new RetryTokenError("mismatch"));
2880
3355
  return {
2881
- content: [{ type: "text", text: "Missing 'to' or 'message' parameter" }],
3356
+ content: [{ type: "text", text: errorText }],
2882
3357
  isError: true,
2883
3358
  details: { error: true }
2884
3359
  };
2885
3360
  }
3361
+ if (_signal?.aborted) {
3362
+ return {
3363
+ content: [{
3364
+ type: "text",
3365
+ text: `Cancelled${retryTokenGuidance(retryToken, retryPreflightRemaining ?? 0)}`
3366
+ }],
3367
+ isError: true,
3368
+ details: retryErrorDetails(retryToken)
3369
+ };
3370
+ }
3371
+ let retryIdentity;
2886
3372
  try {
2887
- const sendTo = await resolveTarget(connectedClient, to) ?? to;
2888
- if (sendTo === connectedClient.sessionId) {
2889
- return {
2890
- content: [{ type: "text", text: "Cannot message the current session" }],
2891
- isError: true,
2892
- details: { error: true }
2893
- };
3373
+ if (retryToken === undefined) {
3374
+ retryIdentity = retryIdentities.begin({
3375
+ sessionId: toolSessionId,
3376
+ action: "send",
3377
+ target: to,
3378
+ text: message,
3379
+ attachments,
3380
+ replyTo,
3381
+ expectsReply: false
3382
+ });
2894
3383
  }
2895
3384
  if (!replyTo && deps.confirmSend && ctx.hasUI) {
2896
3385
  const attachmentText = attachments?.length ? formatAttachments(attachments) : "";
@@ -2898,6 +3387,9 @@ ${otherSection}` }],
2898
3387
 
2899
3388
  ${message}${attachmentText}`);
2900
3389
  if (!confirmed) {
3390
+ if (retryIdentity !== undefined)
3391
+ retryIdentities.release(retryIdentity);
3392
+ retryIdentity = undefined;
2901
3393
  return {
2902
3394
  content: [{ type: "text", text: "Message cancelled by user" }],
2903
3395
  isError: false,
@@ -2905,78 +3397,159 @@ ${message}${attachmentText}`);
2905
3397
  };
2906
3398
  }
2907
3399
  }
3400
+ retryIdentity ??= retryIdentities.begin({
3401
+ sessionId: toolSessionId,
3402
+ action: "send",
3403
+ target: to,
3404
+ text: message,
3405
+ attachments,
3406
+ replyTo,
3407
+ expectsReply: false
3408
+ }, retryToken);
3409
+ const sendTo = await resolveTarget(connectedClient, to) ?? to;
3410
+ if (_signal?.aborted) {
3411
+ const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
3412
+ retryIdentity = undefined;
3413
+ return {
3414
+ content: [{ type: "text", text: `Cancelled${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}` }],
3415
+ isError: true,
3416
+ details: retryErrorDetails(retained.retryToken)
3417
+ };
3418
+ }
3419
+ if (sendTo === connectedClient.sessionId) {
3420
+ const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
3421
+ retryIdentity = undefined;
3422
+ return {
3423
+ content: [{
3424
+ type: "text",
3425
+ text: `Cannot message the current session${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
3426
+ }],
3427
+ isError: true,
3428
+ details: retryErrorDetails(retained.retryToken)
3429
+ };
3430
+ }
2908
3431
  const result = await connectedClient.send(sendTo, {
3432
+ messageId: retryIdentity.messageId,
3433
+ logicalTarget: to,
2909
3434
  text: message,
2910
3435
  attachments,
2911
3436
  replyTo
2912
3437
  });
2913
3438
  if (result.queued === true) {
3439
+ retryIdentities.release(retryIdentity);
3440
+ retryIdentity = undefined;
2914
3441
  pi.appendEntry("intercom_sent", {
2915
3442
  to,
2916
3443
  message: { text: message, attachments, replyTo },
2917
3444
  messageId: result.id,
2918
3445
  timestamp: Date.now()
2919
3446
  });
3447
+ const notInKnownSet = result.notInKnownSet === true;
2920
3448
  return {
2921
- content: [{ type: "text", text: `Message queued for ${to}` }],
3449
+ content: [{
3450
+ type: "text",
3451
+ text: notInKnownSet ? `Message queued for ${to} (not in the workflow's known stage set; it will be delivered to every matching stage that starts before the run terminates)` : `Message queued for ${to}`
3452
+ }],
2922
3453
  isError: false,
2923
3454
  details: {
2924
3455
  messageId: result.id,
2925
3456
  delivered: false,
2926
3457
  queued: true,
2927
- runId: result.runId,
2928
- stageKey: result.stageKey,
2929
- position: result.position
3458
+ target: result.target,
3459
+ position: result.position,
3460
+ ...notInKnownSet ? { notInKnownSet: true } : {}
2930
3461
  }
2931
3462
  };
2932
3463
  }
2933
3464
  if (!result.delivered) {
3465
+ const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
3466
+ retryIdentity = undefined;
2934
3467
  const errorText = result.reason ?? "Session may not exist or has disconnected.";
2935
3468
  return {
2936
- content: [{ type: "text", text: `Message to "${to}" was not delivered: ${errorText}` }],
3469
+ content: [{
3470
+ type: "text",
3471
+ text: `Message to "${to}" was not delivered: ${errorText}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
3472
+ }],
2937
3473
  isError: true,
2938
- details: { messageId: result.id, delivered: false, reason: result.reason }
3474
+ details: retryToken === undefined ? { messageId: result.id, delivered: false, reason: result.reason } : {
3475
+ delivered: false,
3476
+ reason: result.reason,
3477
+ ...retained.retryToken === undefined ? {} : { retryToken: retained.retryToken }
3478
+ }
2939
3479
  };
2940
3480
  }
3481
+ retryIdentities.release(retryIdentity);
3482
+ retryIdentity = undefined;
2941
3483
  pi.appendEntry("intercom_sent", {
2942
3484
  to,
2943
3485
  message: { text: message, attachments, replyTo },
2944
3486
  messageId: result.id,
2945
3487
  timestamp: Date.now()
2946
3488
  });
2947
- if (replyTo) {
3489
+ if (replyTo)
2948
3490
  activeReplyTracker().markReplied(replyTo);
2949
- }
2950
3491
  return {
2951
3492
  content: [{ type: "text", text: `Message sent to ${to}` }],
2952
3493
  isError: false,
2953
3494
  details: { messageId: result.id, delivered: true }
2954
3495
  };
2955
3496
  } catch (error) {
3497
+ let failure = error;
3498
+ let retained;
3499
+ if (retryIdentity !== undefined) {
3500
+ if (isRecoverableIntercomDisconnect(error)) {
3501
+ try {
3502
+ retained = retainRecoverableRetry(retryIdentities, retryIdentity);
3503
+ } catch (retentionError) {
3504
+ failure = retentionError;
3505
+ }
3506
+ } else {
3507
+ retryIdentities.release(retryIdentity);
3508
+ }
3509
+ }
2956
3510
  return {
2957
- content: [{ type: "text", text: `Failed to send: ${getErrorMessage(error)}` }],
3511
+ content: [{
3512
+ type: "text",
3513
+ text: `Failed to send: ${getErrorMessage(failure)}${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
3514
+ }],
2958
3515
  isError: true,
2959
- details: { error: true }
3516
+ details: retryErrorDetails(retained?.retryToken)
2960
3517
  };
2961
3518
  }
2962
3519
  }
2963
3520
  case "ask": {
2964
3521
  if (!to) {
3522
+ const errorText = retryToken === undefined ? "Missing 'to' or 'message' parameter" : getErrorMessage(new RetryTokenError("mismatch"));
2965
3523
  return {
2966
- content: [{ type: "text", text: "Missing 'to' or 'message' parameter" }],
3524
+ content: [{ type: "text", text: errorText }],
2967
3525
  isError: true,
2968
3526
  details: { error: true }
2969
3527
  };
2970
3528
  }
2971
3529
  if (_signal?.aborted) {
2972
3530
  return {
2973
- content: [{ type: "text", text: "Cancelled" }],
3531
+ content: [{
3532
+ type: "text",
3533
+ text: `Cancelled${retryTokenGuidance(retryToken, retryPreflightRemaining ?? 0)}`
3534
+ }],
2974
3535
  isError: true,
2975
- details: { error: true }
3536
+ details: retryErrorDetails(retryToken)
2976
3537
  };
2977
3538
  }
2978
3539
  let wait = null;
3540
+ let retryIdentity;
2979
3541
  try {
3542
+ if (retryToken !== undefined && !message)
3543
+ throw new RetryTokenError("mismatch");
3544
+ retryIdentity = retryIdentities.begin({
3545
+ sessionId: toolSessionId,
3546
+ action: "ask",
3547
+ target: to,
3548
+ text: message ?? "",
3549
+ attachments,
3550
+ replyTo,
3551
+ expectsReply: true
3552
+ }, retryToken);
2980
3553
  const metadata = getMetadata();
2981
3554
  const currentSupervisorId = connectedClient.supervisorSessionId ?? undefined;
2982
3555
  const metadataSupervisorId = metadata?.supervisor?.supervisorSessionId;
@@ -2987,7 +3560,9 @@ ${message}${attachmentText}`);
2987
3560
  attachments,
2988
3561
  resolvedTargetId
2989
3562
  }));
2990
- if (directParentTarget && claimParentAsk(currentSupervisorId ?? metadataSupervisorId ?? to)) {
3563
+ if (retryToken === undefined && directParentTarget && claimParentAsk(currentSupervisorId ?? metadataSupervisorId ?? to)) {
3564
+ retryIdentities.release(retryIdentity);
3565
+ retryIdentity = undefined;
2991
3566
  return {
2992
3567
  content: [{ type: "text", text: "Parent ask claimed; this child is ending for a fresh subagent start." }],
2993
3568
  isError: false,
@@ -2996,23 +3571,35 @@ ${message}${attachmentText}`);
2996
3571
  }
2997
3572
  const sendTo = await resolveTarget(connectedClient, to) ?? to;
2998
3573
  if (_signal?.aborted) {
3574
+ const retained = retryIdentity === undefined ? undefined : retainInconclusiveRetry(retryIdentities, retryIdentity);
3575
+ retryIdentity = undefined;
2999
3576
  return {
3000
- content: [{ type: "text", text: "Cancelled" }],
3577
+ content: [{
3578
+ type: "text",
3579
+ text: `Cancelled${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
3580
+ }],
3001
3581
  isError: true,
3002
- details: { error: true }
3582
+ details: retryErrorDetails(retained?.retryToken)
3003
3583
  };
3004
3584
  }
3005
3585
  if (sendTo === connectedClient.sessionId) {
3586
+ const retained = retryIdentity === undefined ? undefined : retainInconclusiveRetry(retryIdentities, retryIdentity);
3587
+ retryIdentity = undefined;
3006
3588
  return {
3007
- content: [{ type: "text", text: "Cannot message the current session" }],
3589
+ content: [{
3590
+ type: "text",
3591
+ text: `Cannot message the current session${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
3592
+ }],
3008
3593
  isError: true,
3009
- details: { error: true }
3594
+ details: retryErrorDetails(retained?.retryToken)
3010
3595
  };
3011
3596
  }
3012
- if (metadata && !directParentTarget) {
3597
+ if (retryToken === undefined && metadata && !directParentTarget) {
3013
3598
  const authoritativeParent = [currentSupervisorId, metadataSupervisorId].find((candidate) => candidate === sendTo);
3014
3599
  const resolvedParent = authoritativeParent ?? await resolveTarget(connectedClient, metadata.orchestratorTarget);
3015
3600
  if (resolvedParent !== null && resolvedParent === sendTo && claimParentAsk(sendTo)) {
3601
+ retryIdentities.release(retryIdentity);
3602
+ retryIdentity = undefined;
3016
3603
  return {
3017
3604
  content: [{ type: "text", text: "Parent ask claimed; this child is ending for a fresh subagent start." }],
3018
3605
  isError: false,
@@ -3021,44 +3608,94 @@ ${message}${attachmentText}`);
3021
3608
  }
3022
3609
  }
3023
3610
  if (!message) {
3611
+ retryIdentities.release(retryIdentity);
3612
+ retryIdentity = undefined;
3024
3613
  return {
3025
3614
  content: [{ type: "text", text: "Missing 'to' or 'message' parameter" }],
3026
3615
  isError: true,
3027
3616
  details: { error: true }
3028
3617
  };
3029
3618
  }
3030
- const questionId = randomUUID4();
3031
- const replyFrom = await resolveReplySender(connectedClient, to, sendTo);
3619
+ const replySender = await resolveReplySender(connectedClient, to, sendTo);
3620
+ if (replySender.kind === "ambiguous") {
3621
+ const retained = retryIdentity === undefined ? undefined : retainInconclusiveRetry(retryIdentities, retryIdentity);
3622
+ retryIdentity = undefined;
3623
+ return {
3624
+ content: [{
3625
+ type: "text",
3626
+ text: `Message to "${to}" is ambiguous: ${replySender.reason}. Use the exact target shown by intercom list.${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
3627
+ }],
3628
+ isError: true,
3629
+ details: retryErrorDetails(retained?.retryToken)
3630
+ };
3631
+ }
3632
+ const questionId = retryIdentity.messageId;
3633
+ const replyFrom = replySender.kind === "resolved" ? replySender.sessionId : sendTo;
3032
3634
  const admission = beginReplyWait(replyFrom, questionId, _signal);
3033
3635
  if (!admission.ok) {
3636
+ const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
3637
+ retryIdentity = undefined;
3034
3638
  const text = admission.reason === "busy" ? `Too many pending asks (${admission.limit}); reply-wait slots are full` : "Cancelled";
3035
3639
  return {
3036
- content: [{ type: "text", text }],
3640
+ content: [{
3641
+ type: "text",
3642
+ text: `${text}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
3643
+ }],
3037
3644
  isError: true,
3038
- details: { error: true }
3645
+ details: retryErrorDetails(retained.retryToken)
3039
3646
  };
3040
3647
  }
3041
3648
  wait = admission.wait;
3042
3649
  const sendResult = await connectedClient.send(sendTo, {
3043
3650
  messageId: questionId,
3651
+ logicalTarget: to,
3044
3652
  text: message,
3045
3653
  attachments,
3046
3654
  replyTo,
3047
3655
  expectsReply: true
3048
3656
  });
3657
+ if (sendResult.queued === true) {
3658
+ retryIdentities.release(retryIdentity);
3659
+ retryIdentity = undefined;
3660
+ wait.cancel(new Error(`Message queued for ${to}`));
3661
+ pi.appendEntry("intercom_sent", {
3662
+ to,
3663
+ message: { text: message, attachments, replyTo },
3664
+ messageId: sendResult.id,
3665
+ timestamp: Date.now()
3666
+ });
3667
+ return {
3668
+ content: [{ type: "text", text: `Message queued for ${to}` }],
3669
+ isError: false,
3670
+ details: {
3671
+ messageId: sendResult.id,
3672
+ delivered: false,
3673
+ queued: true,
3674
+ target: sendResult.target,
3675
+ position: sendResult.position
3676
+ }
3677
+ };
3678
+ }
3049
3679
  if (!sendResult.delivered) {
3680
+ const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
3681
+ retryIdentity = undefined;
3050
3682
  const errorText = sendResult.reason ?? "Session may not exist or has disconnected.";
3051
3683
  wait.cancel(new Error(`Message to "${to}" was not delivered: ${errorText}`));
3052
3684
  const pendingStageAskRefusal = errorText.startsWith("Cannot ask a workflow stage whose session has not initialized.");
3053
3685
  return {
3054
- content: [{ type: "text", text: `Message to "${to}" was not delivered: ${errorText}` }],
3686
+ content: [{
3687
+ type: "text",
3688
+ text: `Message to "${to}" was not delivered: ${errorText}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
3689
+ }],
3055
3690
  isError: true,
3056
- details: pendingStageAskRefusal ? {
3057
- error: true,
3058
- refusal: "pending_stage_ask_unsupported",
3059
- recommendedAction: "send",
3060
- reason: errorText
3061
- } : { error: true }
3691
+ details: {
3692
+ ...pendingStageAskRefusal ? {
3693
+ refusal: "pending_stage_ask_unsupported",
3694
+ recommendedAction: "send",
3695
+ reason: errorText
3696
+ } : {},
3697
+ ...retryErrorDetails(retained.retryToken)
3698
+ }
3062
3699
  };
3063
3700
  }
3064
3701
  pi.appendEntry("intercom_sent", {
@@ -3068,6 +3705,8 @@ ${message}${attachmentText}`);
3068
3705
  timestamp: Date.now()
3069
3706
  });
3070
3707
  const replyMessage = await wait.promise;
3708
+ retryIdentities.release(retryIdentity);
3709
+ retryIdentity = undefined;
3071
3710
  const replyText = replyMessage.content.text;
3072
3711
  const replyAttachments = replyMessage.content.attachments?.length ? formatAttachments(replyMessage.content.attachments) : "";
3073
3712
  pi.appendEntry("intercom_received", {
@@ -3091,59 +3730,164 @@ ${replyText}${replyAttachments}` }],
3091
3730
  };
3092
3731
  } catch (error) {
3093
3732
  wait?.cancel(toError3(error));
3733
+ let failure = error;
3734
+ let retained;
3735
+ if (retryIdentity !== undefined) {
3736
+ if (isRecoverableIntercomDisconnect(error)) {
3737
+ try {
3738
+ retained = retainRecoverableRetry(retryIdentities, retryIdentity);
3739
+ } catch (retentionError) {
3740
+ failure = retentionError;
3741
+ }
3742
+ } else {
3743
+ retryIdentities.release(retryIdentity);
3744
+ }
3745
+ }
3094
3746
  return {
3095
- content: [{ type: "text", text: `Failed: ${getErrorMessage(error)}` }],
3747
+ content: [{
3748
+ type: "text",
3749
+ text: `Failed: ${getErrorMessage(failure)}${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
3750
+ }],
3096
3751
  isError: true,
3097
- details: { error: true }
3752
+ details: retryErrorDetails(retained?.retryToken)
3098
3753
  };
3099
3754
  }
3100
3755
  }
3101
3756
  case "reply": {
3102
3757
  if (!message) {
3758
+ const errorText = retryToken === undefined ? "Missing 'message' parameter" : getErrorMessage(new RetryTokenError("mismatch"));
3103
3759
  return {
3104
- content: [{ type: "text", text: "Missing 'message' parameter" }],
3760
+ content: [{ type: "text", text: errorText }],
3105
3761
  isError: true,
3106
3762
  details: { error: true }
3107
3763
  };
3108
3764
  }
3765
+ let retryIdentity;
3109
3766
  try {
3110
- const target = activeReplyTracker().resolveReplyTarget({ to, replyTo });
3111
- if (target.from.id === connectedClient.sessionId) {
3767
+ retryIdentity = retryIdentities.begin({
3768
+ sessionId: toolSessionId,
3769
+ action: "reply",
3770
+ target: to,
3771
+ text: message,
3772
+ attachments,
3773
+ replyTo,
3774
+ expectsReply: false
3775
+ }, retryToken);
3776
+ if (retryToken === undefined) {
3777
+ const target = activeReplyTracker().resolveReplyTarget({ to, replyTo });
3778
+ retryIdentities.bindReplyRoute(retryIdentity, {
3779
+ senderId: target.from.id,
3780
+ ...target.from.name === undefined ? {} : { senderName: target.from.name },
3781
+ messageId: target.message.id,
3782
+ expectsReply: target.message.expectsReply === true
3783
+ });
3784
+ }
3785
+ const route = retryIdentities.replyRoute(retryIdentity);
3786
+ if (route === undefined)
3787
+ throw new RetryTokenError("invalid");
3788
+ const replySendTo = to ?? route.senderId;
3789
+ const replyLogicalTarget = to ?? route.senderId;
3790
+ const displayTarget = route.senderName || route.senderId;
3791
+ if (route.senderId === connectedClient.sessionId) {
3792
+ const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
3793
+ retryIdentity = undefined;
3112
3794
  return {
3113
- content: [{ type: "text", text: "Cannot message the current session" }],
3795
+ content: [{
3796
+ type: "text",
3797
+ text: `Cannot message the current session${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
3798
+ }],
3114
3799
  isError: true,
3115
- details: { error: true }
3800
+ details: retryErrorDetails(retained.retryToken)
3116
3801
  };
3117
3802
  }
3118
- const result = await connectedClient.send(target.from.id, {
3803
+ const replyMessageId = retryIdentity.messageId;
3804
+ const sendReply = (targetId) => connectedClient.send(targetId, {
3805
+ messageId: replyMessageId,
3806
+ logicalTarget: replyLogicalTarget,
3807
+ ...route.expectsReply ? { requirePendingReply: true } : {},
3119
3808
  text: message,
3120
- replyTo: target.message.id
3809
+ attachments,
3810
+ replyTo: route.messageId
3121
3811
  });
3812
+ let result = await sendReply(replySendTo);
3813
+ if (to === undefined && result.delivered === false && result.reasonCode === "session_not_found" && route.senderName !== undefined) {
3814
+ result = await sendReply(route.senderName);
3815
+ }
3816
+ if (result.queued === true) {
3817
+ retryIdentities.release(retryIdentity);
3818
+ retryIdentity = undefined;
3819
+ activeReplyTracker().markReplied(route.messageId);
3820
+ pi.appendEntry("intercom_sent", {
3821
+ to: displayTarget,
3822
+ message: { text: message, attachments, replyTo: route.messageId },
3823
+ messageId: result.id,
3824
+ timestamp: Date.now()
3825
+ });
3826
+ return {
3827
+ content: [{ type: "text", text: `Reply queued for ${displayTarget}` }],
3828
+ isError: false,
3829
+ details: {
3830
+ messageId: result.id,
3831
+ delivered: false,
3832
+ queued: true,
3833
+ replyTo: route.messageId,
3834
+ target: result.target,
3835
+ position: result.position
3836
+ }
3837
+ };
3838
+ }
3122
3839
  if (!result.delivered) {
3840
+ const retained = retainInconclusiveRetry(retryIdentities, retryIdentity);
3841
+ retryIdentity = undefined;
3123
3842
  const errorText = result.reason ?? "Session may not exist or has disconnected.";
3124
3843
  return {
3125
- content: [{ type: "text", text: `Reply to "${target.from.name || target.from.id}" was not delivered: ${errorText}` }],
3844
+ content: [{
3845
+ type: "text",
3846
+ text: `Reply to "${displayTarget}" was not delivered: ${errorText}${retryTokenGuidance(retained.retryToken, retained.remainingRetries)}`
3847
+ }],
3126
3848
  isError: true,
3127
- details: { messageId: result.id, delivered: false, reason: result.reason }
3849
+ details: retryToken === undefined ? { messageId: result.id, delivered: false, reason: result.reason } : {
3850
+ delivered: false,
3851
+ reason: result.reason,
3852
+ ...retained.retryToken === undefined ? {} : { retryToken: retained.retryToken }
3853
+ }
3128
3854
  };
3129
3855
  }
3130
- activeReplyTracker().markReplied(target.message.id);
3856
+ retryIdentities.release(retryIdentity);
3857
+ retryIdentity = undefined;
3858
+ activeReplyTracker().markReplied(route.messageId);
3131
3859
  pi.appendEntry("intercom_sent", {
3132
- to: target.from.name || target.from.id,
3133
- message: { text: message, replyTo: target.message.id },
3860
+ to: displayTarget,
3861
+ message: { text: message, attachments, replyTo: route.messageId },
3134
3862
  messageId: result.id,
3135
3863
  timestamp: Date.now()
3136
3864
  });
3137
3865
  return {
3138
- content: [{ type: "text", text: `Reply sent to ${target.from.name || target.from.id}` }],
3866
+ content: [{ type: "text", text: `Reply sent to ${displayTarget}` }],
3139
3867
  isError: false,
3140
- details: { messageId: result.id, delivered: true, replyTo: target.message.id }
3868
+ details: { messageId: result.id, delivered: true, replyTo: route.messageId }
3141
3869
  };
3142
3870
  } catch (error) {
3871
+ let failure = error;
3872
+ let retained;
3873
+ if (retryIdentity !== undefined) {
3874
+ if (isRecoverableIntercomDisconnect(error)) {
3875
+ try {
3876
+ retained = retainRecoverableRetry(retryIdentities, retryIdentity);
3877
+ } catch (retentionError) {
3878
+ failure = retentionError;
3879
+ }
3880
+ } else {
3881
+ retryIdentities.release(retryIdentity);
3882
+ }
3883
+ }
3143
3884
  return {
3144
- content: [{ type: "text", text: `Failed to reply: ${getErrorMessage(error)}` }],
3885
+ content: [{
3886
+ type: "text",
3887
+ text: `Failed to reply: ${getErrorMessage(failure)}${retryTokenGuidance(retained?.retryToken, retained?.remainingRetries ?? 0)}`
3888
+ }],
3145
3889
  isError: true,
3146
- details: { error: true }
3890
+ details: retryErrorDetails(retained?.retryToken)
3147
3891
  };
3148
3892
  }
3149
3893
  }
@@ -3238,7 +3982,9 @@ var init_intercom_tool = __esm(() => {
3238
3982
  init_result_renderers();
3239
3983
  init_intercom_utils();
3240
3984
  init_group();
3241
- init_send_handler();
3985
+ init_workflow_stage_target();
3986
+ init_recoverable_disconnect();
3987
+ init_retry_identity();
3242
3988
  });
3243
3989
 
3244
3990
  // dist/builtin/intercom/ui/compose.ts
@@ -3747,43 +4493,379 @@ var init_lifecycle = __esm(() => {
3747
4493
  });
3748
4494
 
3749
4495
  // dist/builtin/intercom/broker/delivered-message-cache.ts
4496
+ import { createHmac, randomBytes } from "node:crypto";
4497
+ import {
4498
+ chmodSync as chmodSync2,
4499
+ closeSync as closeSync2,
4500
+ existsSync as existsSync3,
4501
+ mkdirSync as mkdirSync2,
4502
+ openSync as openSync2,
4503
+ readFileSync as readFileSync3,
4504
+ writeFileSync as writeFileSync2
4505
+ } from "node:fs";
4506
+ import { dirname as dirname2 } from "node:path";
4507
+ import { DatabaseSync } from "node:sqlite";
4508
+ function validStoredString(value) {
4509
+ return typeof value === "string" && value.length > 0;
4510
+ }
4511
+ function entryByteSize(messageId, delivered) {
4512
+ return [
4513
+ messageId,
4514
+ delivered.signature,
4515
+ delivered.targetIdentity,
4516
+ delivered.questionRoute?.targetSessionId,
4517
+ delivered.questionRoute?.senderGroupIdentity
4518
+ ].reduce((total, value) => total + (value === undefined ? 0 : Buffer.byteLength(value)), 0);
4519
+ }
4520
+ function parseStoredMessage(row) {
4521
+ if (row === undefined)
4522
+ return;
4523
+ if (row.state !== "reserved" && row.state !== "accepted" || !validStoredString(row.signature) || !AUTHORITY_DIGEST_PATTERN.test(row.signature) || typeof row.delivered_at !== "number" || !Number.isSafeInteger(row.delivered_at) || row.delivered_at < 0 || row.target_identity !== null && !validStoredString(row.target_identity)) {
4524
+ return "invalid";
4525
+ }
4526
+ const questionValues = [row.question_target_session_id, row.question_sender_group_identity];
4527
+ const hasQuestionRoute = questionValues.some((value) => value !== null);
4528
+ if (hasQuestionRoute && !questionValues.every(validStoredString))
4529
+ return "invalid";
4530
+ return {
4531
+ state: row.state,
4532
+ deliveredAt: row.delivered_at,
4533
+ signature: row.signature,
4534
+ ...typeof row.target_identity === "string" ? { targetIdentity: row.target_identity } : {},
4535
+ ...hasQuestionRoute ? {
4536
+ questionRoute: {
4537
+ targetSessionId: row.question_target_session_id,
4538
+ senderGroupIdentity: row.question_sender_group_identity
4539
+ }
4540
+ } : {}
4541
+ };
4542
+ }
4543
+ function getDeliveredMessageAuthorityKeyPath(storagePath) {
4544
+ return storagePath.endsWith(".sqlite") ? `${storagePath.slice(0, -".sqlite".length)}.key` : `${storagePath}.key`;
4545
+ }
4546
+ function restrictMode(path, mode) {
4547
+ if (process.platform !== "win32")
4548
+ chmodSync2(path, mode);
4549
+ }
4550
+ function restrictExistingAuthorityArtifacts(storagePath, keyPath) {
4551
+ for (const path of [storagePath, `${storagePath}-wal`, `${storagePath}-shm`, keyPath]) {
4552
+ if (existsSync3(path))
4553
+ restrictMode(path, 384);
4554
+ }
4555
+ }
4556
+ function prepareDurableAuthority(storagePath) {
4557
+ const directory = dirname2(storagePath);
4558
+ const keyPath = getDeliveredMessageAuthorityKeyPath(storagePath);
4559
+ mkdirSync2(directory, { recursive: true, mode: 448 });
4560
+ restrictMode(directory, 448);
4561
+ restrictExistingAuthorityArtifacts(storagePath, keyPath);
4562
+ const databaseExists = existsSync3(storagePath);
4563
+ const keyExists = existsSync3(keyPath);
4564
+ if (databaseExists !== keyExists)
4565
+ return;
4566
+ if (!databaseExists) {
4567
+ const key2 = randomBytes(AUTHORITY_KEY_BYTES);
4568
+ writeFileSync2(keyPath, key2, { flag: "wx", mode: 384 });
4569
+ const databaseDescriptor = openSync2(storagePath, "wx", 384);
4570
+ closeSync2(databaseDescriptor);
4571
+ restrictExistingAuthorityArtifacts(storagePath, keyPath);
4572
+ return key2;
4573
+ }
4574
+ const key = readFileSync3(keyPath);
4575
+ if (key.length !== AUTHORITY_KEY_BYTES)
4576
+ return;
4577
+ return key;
4578
+ }
4579
+
3750
4580
  class DeliveredMessageCache {
3751
4581
  ttlMs;
3752
4582
  maxEntries;
4583
+ maxBytes;
3753
4584
  delivered = new Map;
3754
- constructor(ttlMs = DEFAULT_TTL_MS2, maxEntries = DEFAULT_MAX_ENTRIES) {
4585
+ retainedBytes = 0;
4586
+ database;
4587
+ authorityKey;
4588
+ storagePath;
4589
+ storageInvalid = false;
4590
+ constructor(ttlMs = DELIVERED_MESSAGE_TTL_MS, maxEntries = DELIVERED_MESSAGE_MAX_ENTRIES, storagePath, maxBytes = DELIVERED_MESSAGE_MAX_BYTES) {
3755
4591
  this.ttlMs = ttlMs;
3756
4592
  this.maxEntries = maxEntries;
4593
+ this.maxBytes = maxBytes;
4594
+ if (storagePath === undefined)
4595
+ return;
4596
+ this.storagePath = storagePath;
4597
+ let database;
4598
+ try {
4599
+ const authorityKey = prepareDurableAuthority(storagePath);
4600
+ if (authorityKey === undefined) {
4601
+ this.storageInvalid = true;
4602
+ return;
4603
+ }
4604
+ this.authorityKey = authorityKey;
4605
+ database = new DatabaseSync(storagePath);
4606
+ database.exec("PRAGMA busy_timeout = 5000");
4607
+ database.exec("PRAGMA journal_mode = WAL");
4608
+ database.exec("PRAGMA synchronous = FULL");
4609
+ database.exec("PRAGMA trusted_schema = OFF");
4610
+ database.exec(`
4611
+ CREATE TABLE IF NOT EXISTS delivered_messages (
4612
+ state TEXT NOT NULL CHECK (state IN ('reserved', 'accepted')),
4613
+ message_id TEXT PRIMARY KEY NOT NULL,
4614
+ signature TEXT NOT NULL,
4615
+ delivered_at INTEGER NOT NULL,
4616
+ target_identity TEXT,
4617
+ question_target_session_id TEXT,
4618
+ question_sender_group_identity TEXT
4619
+ ) STRICT;
4620
+ CREATE INDEX IF NOT EXISTS delivered_messages_age
4621
+ ON delivered_messages(delivered_at);
4622
+ `);
4623
+ database.prepare("SELECT state, signature, delivered_at, target_identity FROM delivered_messages LIMIT 0").get();
4624
+ restrictExistingAuthorityArtifacts(storagePath, getDeliveredMessageAuthorityKeyPath(storagePath));
4625
+ } catch {
4626
+ database?.close();
4627
+ try {
4628
+ restrictExistingAuthorityArtifacts(storagePath, getDeliveredMessageAuthorityKeyPath(storagePath));
4629
+ } catch {}
4630
+ this.storageInvalid = true;
4631
+ return;
4632
+ }
4633
+ this.database = database;
3757
4634
  }
3758
4635
  lookup(messageId, signature, now = Date.now()) {
4636
+ const authority = this.signatureAuthority(signature);
4637
+ return authority === undefined ? "invalid" : this.lookupEntry(messageId, authority, undefined, false, now);
4638
+ }
4639
+ lookupForTarget(messageId, signature, targetIdentity, now = Date.now()) {
4640
+ const authority = this.signatureAuthority(signature);
4641
+ return authority === undefined ? "invalid" : this.lookupEntry(messageId, authority, targetIdentity, true, now);
4642
+ }
4643
+ lookupQuestionTarget(messageId, signature, senderGroupIdentity, now = Date.now()) {
4644
+ const authority = this.signatureAuthority(signature);
4645
+ if (authority === undefined)
4646
+ return;
3759
4647
  this.prune(now);
3760
- const delivered = this.delivered.get(messageId);
3761
- if (!delivered || now - delivered.deliveredAt > this.ttlMs)
3762
- return "miss";
3763
- return delivered.signature === signature ? "match" : "conflict";
4648
+ const delivered = this.read(messageId);
4649
+ if (delivered === undefined || delivered === "invalid" || delivered.state !== "accepted" || now - delivered.deliveredAt > this.ttlMs || delivered.signature !== authority || delivered.questionRoute?.senderGroupIdentity !== senderGroupIdentity) {
4650
+ return;
4651
+ }
4652
+ return delivered.questionRoute.targetSessionId;
4653
+ }
4654
+ record(messageId, signature, now = Date.now(), targetIdentity) {
4655
+ return this.recordWithSignature(messageId, { state: "accepted", deliveredAt: now, signature, targetIdentity });
4656
+ }
4657
+ recordQuestion(messageId, signature, questionRoute, now = Date.now(), targetIdentity) {
4658
+ return this.recordWithSignature(messageId, {
4659
+ state: "accepted",
4660
+ deliveredAt: now,
4661
+ signature,
4662
+ targetIdentity,
4663
+ questionRoute
4664
+ });
4665
+ }
4666
+ reserve(messageId, signature, now = Date.now(), targetIdentity) {
4667
+ return this.recordWithSignature(messageId, { state: "reserved", deliveredAt: now, signature, targetIdentity });
4668
+ }
4669
+ reserveQuestion(messageId, signature, questionRoute, now = Date.now(), targetIdentity) {
4670
+ return this.recordWithSignature(messageId, {
4671
+ state: "reserved",
4672
+ deliveredAt: now,
4673
+ signature,
4674
+ targetIdentity,
4675
+ questionRoute
4676
+ });
4677
+ }
4678
+ accept(messageId, signature, targetIdentity) {
4679
+ const authority = this.signatureAuthority(signature);
4680
+ if (authority === undefined)
4681
+ return "invalid";
4682
+ const existing = this.read(messageId);
4683
+ if (existing === undefined || existing === "invalid")
4684
+ return "invalid";
4685
+ if (existing.signature !== authority || existing.targetIdentity !== targetIdentity)
4686
+ return "conflict";
4687
+ if (existing.state === "accepted")
4688
+ return "accepted";
4689
+ if (this.database === undefined) {
4690
+ existing.state = "accepted";
4691
+ return "accepted";
4692
+ }
4693
+ const updated = this.database.prepare("UPDATE delivered_messages SET state = 'accepted' WHERE message_id = ? AND signature = ?").run(messageId, authority);
4694
+ this.restrictArtifacts();
4695
+ return Number(updated.changes) === 1 ? "accepted" : "invalid";
4696
+ }
4697
+ forget(messageId, signature) {
4698
+ const authority = this.signatureAuthority(signature);
4699
+ if (authority === undefined)
4700
+ return;
4701
+ if (this.database === undefined) {
4702
+ const delivered = this.delivered.get(messageId);
4703
+ if (delivered?.signature === authority) {
4704
+ this.retainedBytes -= entryByteSize(messageId, delivered);
4705
+ this.delivered.delete(messageId);
4706
+ }
4707
+ return;
4708
+ }
4709
+ this.database.prepare("DELETE FROM delivered_messages WHERE message_id = ? AND signature = ?").run(messageId, authority);
4710
+ this.restrictArtifacts();
3764
4711
  }
3765
- record(messageId, signature, now = Date.now()) {
4712
+ close() {
4713
+ this.restrictArtifacts();
4714
+ this.database?.close();
4715
+ this.restrictArtifacts();
4716
+ }
4717
+ signatureAuthority(signature) {
4718
+ if (this.storageInvalid || !validStoredString(signature))
4719
+ return;
4720
+ if (this.storagePath === undefined)
4721
+ return signature;
4722
+ if (this.authorityKey === undefined)
4723
+ return;
4724
+ return createHmac("sha256", this.authorityKey).update(signature).digest("hex");
4725
+ }
4726
+ recordWithSignature(messageId, delivered) {
4727
+ const authority = this.signatureAuthority(delivered.signature);
4728
+ if (authority === undefined)
4729
+ return "invalid";
4730
+ return this.recordEntry(messageId, { ...delivered, signature: authority });
4731
+ }
4732
+ lookupEntry(messageId, signature, targetIdentity, requireTargetIdentity, now) {
4733
+ if (this.storageInvalid)
4734
+ return "invalid";
3766
4735
  this.prune(now);
3767
- this.delivered.delete(messageId);
3768
- this.delivered.set(messageId, { deliveredAt: now, signature });
3769
- while (this.delivered.size > this.maxEntries) {
3770
- const oldest = this.delivered.keys().next().value;
3771
- if (oldest === undefined)
3772
- break;
3773
- this.delivered.delete(oldest);
4736
+ const delivered = this.read(messageId);
4737
+ if (delivered === "invalid")
4738
+ return "invalid";
4739
+ if (delivered === undefined || now - delivered.deliveredAt > this.ttlMs)
4740
+ return "miss";
4741
+ if (delivered.signature !== signature)
4742
+ return "conflict";
4743
+ if (requireTargetIdentity && delivered.targetIdentity !== targetIdentity)
4744
+ return "conflict";
4745
+ if (delivered.state === "reserved")
4746
+ return "uncertain";
4747
+ return "match";
4748
+ }
4749
+ recordEntry(messageId, delivered) {
4750
+ if (this.storageInvalid)
4751
+ return "invalid";
4752
+ if (!validStoredString(messageId) || !validStoredString(delivered.signature))
4753
+ return "invalid";
4754
+ if (!Number.isSafeInteger(delivered.deliveredAt) || delivered.deliveredAt < 0 || delivered.targetIdentity !== undefined && !validStoredString(delivered.targetIdentity) || delivered.questionRoute !== undefined && (!validStoredString(delivered.questionRoute.targetSessionId) || !validStoredString(delivered.questionRoute.senderGroupIdentity))) {
4755
+ return "invalid";
4756
+ }
4757
+ if (entryByteSize(messageId, delivered) > this.maxBytes)
4758
+ return "capacity";
4759
+ if (this.database === undefined)
4760
+ return this.recordMemory(messageId, delivered);
4761
+ return this.recordDurable(messageId, delivered);
4762
+ }
4763
+ recordMemory(messageId, delivered) {
4764
+ this.pruneMemory(delivered.deliveredAt);
4765
+ const existing = this.delivered.get(messageId);
4766
+ if (existing !== undefined)
4767
+ return this.compare(existing, delivered);
4768
+ const size = entryByteSize(messageId, delivered);
4769
+ if (this.delivered.size >= this.maxEntries || this.retainedBytes + size > this.maxBytes)
4770
+ return "capacity";
4771
+ this.delivered.set(messageId, delivered);
4772
+ this.retainedBytes += size;
4773
+ return "recorded";
4774
+ }
4775
+ recordDurable(messageId, delivered) {
4776
+ const database = this.database;
4777
+ if (database === undefined)
4778
+ return "invalid";
4779
+ database.exec("BEGIN IMMEDIATE");
4780
+ try {
4781
+ this.pruneDurable(delivered.deliveredAt);
4782
+ const existing = this.readDurable(messageId);
4783
+ if (existing !== undefined) {
4784
+ database.exec("COMMIT");
4785
+ return existing === "invalid" ? "invalid" : this.compare(existing, delivered);
4786
+ }
4787
+ const totals = database.prepare(`
4788
+ SELECT COUNT(*) AS count,
4789
+ COALESCE(SUM(
4790
+ length(CAST(message_id AS BLOB)) + length(CAST(signature AS BLOB)) +
4791
+ COALESCE(length(CAST(target_identity AS BLOB)), 0) +
4792
+ COALESCE(length(CAST(question_target_session_id AS BLOB)), 0) +
4793
+ COALESCE(length(CAST(question_sender_group_identity AS BLOB)), 0)
4794
+ ), 0) AS bytes
4795
+ FROM delivered_messages
4796
+ `).get();
4797
+ const count = totals?.count;
4798
+ const bytes = totals?.bytes;
4799
+ if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0 || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0) {
4800
+ database.exec("COMMIT");
4801
+ return "invalid";
4802
+ }
4803
+ if (count >= this.maxEntries || bytes + entryByteSize(messageId, delivered) > this.maxBytes) {
4804
+ database.exec("COMMIT");
4805
+ return "capacity";
4806
+ }
4807
+ database.prepare(`
4808
+ INSERT INTO delivered_messages (
4809
+ message_id, state, signature, delivered_at, target_identity,
4810
+ question_target_session_id, question_sender_group_identity
4811
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
4812
+ `).run(messageId, delivered.state, delivered.signature, delivered.deliveredAt, delivered.targetIdentity ?? null, delivered.questionRoute?.targetSessionId ?? null, delivered.questionRoute?.senderGroupIdentity ?? null);
4813
+ database.exec("COMMIT");
4814
+ this.restrictArtifacts();
4815
+ return "recorded";
4816
+ } catch (error) {
4817
+ try {
4818
+ database.exec("ROLLBACK");
4819
+ } catch {}
4820
+ throw error;
4821
+ }
4822
+ }
4823
+ compare(existing, incoming) {
4824
+ if (existing.signature !== incoming.signature || existing.targetIdentity !== incoming.targetIdentity) {
4825
+ return "conflict";
3774
4826
  }
4827
+ return existing.state === "accepted" ? "match" : "uncertain";
4828
+ }
4829
+ read(messageId) {
4830
+ return this.database === undefined ? this.delivered.get(messageId) : this.readDurable(messageId);
4831
+ }
4832
+ readDurable(messageId) {
4833
+ const row = this.database?.prepare(`
4834
+ SELECT state, signature, delivered_at, target_identity,
4835
+ question_target_session_id, question_sender_group_identity
4836
+ FROM delivered_messages WHERE message_id = ?
4837
+ `).get(messageId);
4838
+ return parseStoredMessage(row);
3775
4839
  }
3776
4840
  prune(now) {
4841
+ if (this.database === undefined)
4842
+ this.pruneMemory(now);
4843
+ else
4844
+ this.pruneDurable(now);
4845
+ }
4846
+ pruneMemory(now) {
3777
4847
  for (const [messageId, delivered] of this.delivered) {
3778
4848
  if (now - delivered.deliveredAt <= this.ttlMs)
3779
- break;
4849
+ continue;
3780
4850
  this.delivered.delete(messageId);
4851
+ this.retainedBytes -= entryByteSize(messageId, delivered);
3781
4852
  }
3782
4853
  }
4854
+ pruneDurable(now) {
4855
+ this.database?.prepare("DELETE FROM delivered_messages WHERE typeof(delivered_at) = 'integer' AND ? - delivered_at > ?").run(now, this.ttlMs);
4856
+ this.restrictArtifacts();
4857
+ }
4858
+ restrictArtifacts() {
4859
+ if (this.storagePath === undefined || this.storageInvalid)
4860
+ return;
4861
+ restrictExistingAuthorityArtifacts(this.storagePath, getDeliveredMessageAuthorityKeyPath(this.storagePath));
4862
+ }
3783
4863
  }
3784
- var DEFAULT_TTL_MS2, DEFAULT_MAX_ENTRIES = 1e4;
4864
+ var DELIVERED_MESSAGE_MAX_ENTRIES = 1e4, DELIVERED_MESSAGE_MAX_BYTES, AUTHORITY_KEY_BYTES = 32, AUTHORITY_DIGEST_PATTERN;
3785
4865
  var init_delivered_message_cache = __esm(() => {
3786
- DEFAULT_TTL_MS2 = 10 * 60 * 1000;
4866
+ init_retry_policy();
4867
+ DELIVERED_MESSAGE_MAX_BYTES = 64 * 1024 * 1024;
4868
+ AUTHORITY_DIGEST_PATTERN = /^[a-f0-9]{64}$/u;
3787
4869
  });
3788
4870
 
3789
4871
  // dist/builtin/intercom/terminal-ordering-barrier.ts
@@ -4030,8 +5112,7 @@ async function dispatchRelayFallback(pi, prefix, terminalMessage, stageAdmission
4030
5112
  }
4031
5113
  function registerSubagentRelay(pi, deps) {
4032
5114
  const { getLiveContext, currentSessionTargetMatches, sendIncomingMessage, ensureConnected, resolveSessionTarget: resolveSessionTarget2 } = deps;
4033
- const localDeliveries = new DeliveredMessageCache;
4034
- const localDeliveriesInFlight = new Map;
5115
+ const localDeliveries = deps.localDeliveryAuthority ?? new DeliveredMessageCache;
4035
5116
  function deliverLocalSubagentRelayMessage(sender, status, messageText, terminalBarrier, requestId) {
4036
5117
  const now = Date.now();
4037
5118
  const entry = {
@@ -4124,37 +5205,33 @@ ${messageText}`,
4124
5205
  }
4125
5206
  async function deliverLocal(parsed, options) {
4126
5207
  try {
4127
- const signature = buildSendSignature(parsed.to, { text: parsed.message });
4128
- const match = parsed.requestId ? localDeliveries.lookup(parsed.requestId, signature) : "miss";
4129
- if (match === "conflict") {
4130
- throw new Error(`Intercom message ID '${parsed.requestId}' was already delivered with a different target or payload`);
5208
+ const signature = JSON.stringify({
5209
+ sender: options.sender,
5210
+ send: buildSendSignature(parsed.to, { text: parsed.message })
5211
+ });
5212
+ if (!parsed.requestId) {
5213
+ await deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier);
5214
+ acknowledgeResult(options, parsed.requestId, true);
5215
+ return;
4131
5216
  }
4132
- if (match === "miss") {
4133
- const pending = parsed.requestId ? localDeliveriesInFlight.get(parsed.requestId) : undefined;
4134
- if (pending && pending.signature !== signature) {
4135
- await pending.completion;
4136
- throw new Error(`Intercom message ID '${parsed.requestId}' was already delivered with a different target or payload`);
4137
- }
4138
- let delivery = pending?.completion;
4139
- if (!delivery && parsed.requestId) {
4140
- const deferred = Promise.withResolvers();
4141
- delivery = deferred.promise;
4142
- delivery.catch(() => {});
4143
- localDeliveriesInFlight.set(parsed.requestId, { signature, completion: delivery });
4144
- try {
4145
- deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier, parsed.requestId).then(deferred.resolve, deferred.reject);
4146
- } catch (error) {
4147
- deferred.reject(error);
4148
- }
4149
- delivery.finally(() => {
4150
- localDeliveriesInFlight.delete(parsed.requestId);
4151
- }).catch(() => {});
4152
- } else if (!delivery) {
4153
- delivery = deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier, parsed.requestId);
4154
- }
4155
- await delivery;
4156
- if (parsed.requestId)
4157
- localDeliveries.record(parsed.requestId, signature);
5217
+ const reservation = localDeliveries.reserve(parsed.requestId, signature);
5218
+ if (reservation === "match") {
5219
+ acknowledgeResult(options, parsed.requestId, true);
5220
+ return;
5221
+ }
5222
+ if (reservation !== "recorded") {
5223
+ const reason = reservation === "conflict" ? `Intercom message ID '${parsed.requestId}' was already delivered with a different sender, target, or payload` : reservation === "capacity" ? "Local Intercom delivery authority is at capacity; refusing delivery" : reservation === "uncertain" ? `Intercom message ID '${parsed.requestId}' has uncertain local delivery state; refusing replay` : "Local Intercom delivery authority is invalid; refusing delivery";
5224
+ throw new Error(reason);
5225
+ }
5226
+ try {
5227
+ await deliverLocalSubagentRelayMessage(options.sender, options.status, parsed.message, options.terminalBarrier, parsed.requestId);
5228
+ } catch (error) {
5229
+ localDeliveries.forget(parsed.requestId, signature);
5230
+ throw error;
5231
+ }
5232
+ const accepted = localDeliveries.accept(parsed.requestId, signature);
5233
+ if (accepted !== "accepted") {
5234
+ throw new Error(`Local Intercom delivery authority could not accept message '${parsed.requestId}'; refusing acknowledgement`);
4158
5235
  }
4159
5236
  acknowledgeResult(options, parsed.requestId, true);
4160
5237
  } catch (error) {
@@ -4873,7 +5950,10 @@ async function settleOpenStageDeliveryFailure(input, error) {
4873
5950
  }
4874
5951
 
4875
5952
  // dist/builtin/intercom/closed-workflow-stage-message.ts
4876
- function routeClosedWorkflowStageMessage(entry, admission, tracker, waiter, deliver, currentClient, isCurrent) {
5953
+ function routeClosedWorkflowStageMessage(entry, admission, tracker, waiter, deliver, currentClient, isCurrent, ownsSubagentRun) {
5954
+ const sourceRunId = entry.message.source?.subagentRunId;
5955
+ if (sourceRunId !== undefined && ownsSubagentRun(sourceRunId))
5956
+ return;
4877
5957
  if (entry.message.expectsReply !== true) {
4878
5958
  retryStableDelivery({ deliver, isCurrent }).catch(() => {});
4879
5959
  return;
@@ -4920,15 +6000,6 @@ function clearRuntimeIntercomGroup(sessionId) {
4920
6000
  }
4921
6001
  var init_runtime_group = () => {};
4922
6002
 
4923
- // dist/builtin/intercom/reconnect-backoff.ts
4924
- function reconnectDelayMs(attempt) {
4925
- return BACKOFF_MS[Math.min(attempt, BACKOFF_MS.length - 1)];
4926
- }
4927
- var BACKOFF_MS;
4928
- var init_reconnect_backoff = __esm(() => {
4929
- BACKOFF_MS = [1000, 2000, 5000, 1e4, 30000];
4930
- });
4931
-
4932
6003
  // dist/builtin/intercom/supervisor-authorization-registry.ts
4933
6004
  import { randomUUID as randomUUID6 } from "node:crypto";
4934
6005
 
@@ -5190,9 +6261,10 @@ function piIntercomExtension(pi, testOverrides = {}) {
5190
6261
  if (receivedBeforeStageStart) {
5191
6262
  return sendIncomingMessage(entry, "prelude", messageGeneration, false);
5192
6263
  }
5193
- const stageClosed = liveContext.orchestrationContext?.kind === "workflow-stage" && liveContext.orchestrationContext.messageAdmission?.isOpen() === false;
6264
+ const stageAdmission = liveContext.orchestrationContext?.kind === "workflow-stage" ? liveContext.orchestrationContext.messageAdmission : undefined;
6265
+ const stageClosed = stageAdmission?.isOpen() === false;
5194
6266
  if (stageClosed) {
5195
- routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => sendIncomingMessage(entry, "trigger", messageGeneration, false), () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)));
6267
+ routeClosedWorkflowStageMessage(entry, inboundDeliveries, replyTracker, replyWaiters.pending(), () => sendIncomingMessage(entry, "trigger", messageGeneration, false), () => client, () => Boolean(getLiveContext(liveContext, messageGeneration)), (runId) => stageAdmission.boundary.ownsSubagentRun(runId));
5196
6268
  return;
5197
6269
  }
5198
6270
  const admission = inboundDeliveries.admit(from, message);
@@ -5378,6 +6450,9 @@ function piIntercomExtension(pi, testOverrides = {}) {
5378
6450
  return reject(error);
5379
6451
  }
5380
6452
  }
6453
+ function relayStickyLiveDelivered(notice) {
6454
+ pi.events.emit(STICKY_LIVE_DELIVERED_EVENT, { handled: false, ...notice });
6455
+ }
5381
6456
  function handlePendingStageNotification(nextClient, request) {
5382
6457
  admitPendingStageNotification(nextClient, request).catch(() => false).then((delivered) => {
5383
6458
  if (client !== nextClient || !nextClient.isConnected())
@@ -5401,6 +6476,10 @@ function piIntercomExtension(pi, testOverrides = {}) {
5401
6476
  nextClient.on("pending_stage_notification", (request) => {
5402
6477
  handlePendingStageNotification(nextClient, request);
5403
6478
  });
6479
+ nextClient.on("sticky_live_delivered", (notice) => {
6480
+ if (client === nextClient)
6481
+ relayStickyLiveDelivered(notice);
6482
+ });
5404
6483
  nextClient.on("peer_disconnected", (notice) => {
5405
6484
  if (client !== nextClient) {
5406
6485
  return;
@@ -5466,6 +6545,10 @@ function piIntercomExtension(pi, testOverrides = {}) {
5466
6545
  schedulePendingStageRouteReconnect(runId, state);
5467
6546
  });
5468
6547
  nextClient.on("error", () => {});
6548
+ nextClient.on("sticky_live_delivered", (notice) => {
6549
+ if (state.client === nextClient)
6550
+ relayStickyLiveDelivered(notice);
6551
+ });
5469
6552
  }
5470
6553
  async function ensurePendingStageRouteClient(runId, route) {
5471
6554
  const contextAtStart = getLiveContext();
@@ -5498,7 +6581,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
5498
6581
  pendingStageRouteClients.set(runId, state);
5499
6582
  if (state.client?.isConnected()) {
5500
6583
  state.route = route;
5501
- state.client.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages);
6584
+ state.client.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages, route.possibleStages);
5502
6585
  return;
5503
6586
  }
5504
6587
  if (state.promise) {
@@ -5515,7 +6598,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
5515
6598
  await nextClient.disconnect();
5516
6599
  throw new Error("Intercom runtime no longer active");
5517
6600
  }
5518
- nextClient.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages);
6601
+ nextClient.registerPendingStageRoute(runId, normalizeGroup(route.group), route.capability, route.stages, route.possibleStages);
5519
6602
  await nextClient.listSessions();
5520
6603
  if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
5521
6604
  await nextClient.disconnect();
@@ -5563,7 +6646,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
5563
6646
  async function registerPendingStageRoute(activeClient, runId, route) {
5564
6647
  const routeGroup = normalizeGroup(route.group);
5565
6648
  if (clientRegistrationGroup === routeGroup) {
5566
- activeClient.registerPendingStageRoute(runId, routeGroup, route.capability, route.stages);
6649
+ activeClient.registerPendingStageRoute(runId, routeGroup, route.capability, route.stages, route.possibleStages);
5567
6650
  return;
5568
6651
  }
5569
6652
  await ensurePendingStageRouteClient(runId, { ...route, group: routeGroup });
@@ -5596,15 +6679,18 @@ function piIntercomExtension(pi, testOverrides = {}) {
5596
6679
  await testOverrides.beforeConnectAttempt?.(reason);
5597
6680
  await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
5598
6681
  const childMetadata = currentChildOrchestratorMetadata();
5599
- await nextClient.connect(registration, childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy));
5600
- clientRegistrationGroup = normalizeGroup(registration.group);
6682
+ await nextClient.connect(registration, childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy), resolveSessionHomeGroup());
6683
+ clientRegistrationGroup = resolveSessionHomeGroup();
5601
6684
  await supervisorAuthorizations.restore(nextClient);
5602
6685
  for (const [runId, route] of pendingStageRoutes) {
5603
6686
  await registerPendingStageRoute(nextClient, runId, route);
5604
6687
  }
5605
6688
  const orchestration = contextAtStart.orchestrationContext;
5606
6689
  if (orchestration?.kind === "workflow-stage" && orchestration.pendingStageDelivery !== undefined) {
5607
- await nextClient.registerLiveWorkflowStageRoute(orchestration.workflowRunId, [orchestration.workflowStageId, orchestration.workflowStageName], orchestration.pendingStageDelivery.routeCapability);
6690
+ const liveStageKeys = [orchestration.workflowStageId, orchestration.workflowStageName].filter((stageKey) => !stageKey.includes("/") && !stageKey.includes("*"));
6691
+ if (liveStageKeys.length > 0) {
6692
+ await nextClient.registerLiveWorkflowStageRoute(orchestration.workflowRunId, liveStageKeys, orchestration.pendingStageDelivery.routeCapability);
6693
+ }
5608
6694
  }
5609
6695
  if (!getLiveContext(contextAtStart, generationAtStart)) {
5610
6696
  await nextClient.disconnect();
@@ -5647,7 +6733,8 @@ function piIntercomExtension(pi, testOverrides = {}) {
5647
6733
  pendingStageRoutes.set(payload.runId, {
5648
6734
  group: payload.group,
5649
6735
  capability: payload.capability,
5650
- ...payload.stages === undefined ? {} : { stages: payload.stages }
6736
+ ...payload.stages === undefined ? {} : { stages: payload.stages },
6737
+ ...payload.possibleStages === undefined ? {} : { possibleStages: payload.possibleStages }
5651
6738
  });
5652
6739
  const completion = ensureConnected("background").then((activeClient) => registerPendingStageRoute(activeClient, payload.runId, payload));
5653
6740
  payload.completion = completion;
@@ -5670,6 +6757,22 @@ function piIntercomExtension(pi, testOverrides = {}) {
5670
6757
  return;
5671
6758
  payload.handled = true;
5672
6759
  const actionable = `Pending workflow stage could not receive intercom message: ${payload.reason}`;
6760
+ const selfClient = client;
6761
+ const notifiesThisSession = selfClient !== null && selfClient.isConnected() && (payload.senderId === selfClient.sessionId || payload.senderReturnAddress !== undefined && payload.senderReturnAddress === currentSessionId);
6762
+ if (notifiesThisSession && selfClient.sessionId !== null) {
6763
+ payload.completion = admitPendingStageNotification(selfClient, {
6764
+ requestId: `local:${payload.notificationId}`,
6765
+ from: { id: selfClient.sessionId, ...buildRegistration() },
6766
+ message: {
6767
+ id: payload.notificationId,
6768
+ timestamp: Date.now(),
6769
+ replyTo: payload.messageId,
6770
+ replyError: actionable,
6771
+ content: { text: actionable }
6772
+ }
6773
+ }).catch(() => false);
6774
+ return;
6775
+ }
5673
6776
  payload.completion = pendingStageNotificationClient(payload.runId).then((activeClient) => {
5674
6777
  const route = pendingStageRoutes.get(payload.runId);
5675
6778
  if (route === undefined)
@@ -5798,7 +6901,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
5798
6901
  syncPresenceIdentity
5799
6902
  });
5800
6903
  }
5801
- 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";
6904
+ 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", STICKY_LIVE_DELIVERED_EVENT = "atomic:workflow-sticky-live-delivered";
5802
6905
  var init_index_heavy = __esm(() => {
5803
6906
  init_client();
5804
6907
  init_spawn();
@@ -5920,6 +7023,22 @@ function rejectLazyResultRelay(pi, eventName, payload, error) {
5920
7023
  }
5921
7024
  }
5922
7025
 
7026
+ // dist/builtin/intercom/index.ts
7027
+ init_recoverable_disconnect();
7028
+
7029
+ // dist/builtin/intercom/warm-up-exhaustion.ts
7030
+ class IntercomWarmUpExhaustedError extends Error {
7031
+ attempts;
7032
+ constructor(attempts, options) {
7033
+ super(`Intercom could not reach the broker after ${attempts} warm-up attempts.`, options);
7034
+ this.name = "IntercomWarmUpExhaustedError";
7035
+ this.attempts = attempts;
7036
+ }
7037
+ }
7038
+
7039
+ // dist/builtin/intercom/index.ts
7040
+ init_reconnect_backoff();
7041
+
5923
7042
  // dist/builtin/intercom/lazy-heavy-proxy.ts
5924
7043
  var LIVE_EVENT_SUBSCRIPTIONS = new Set(["pi-intercom:detach-response"]);
5925
7044
  function createForwardedHandlerMap() {
@@ -6050,9 +7169,12 @@ function renderHeavyToolResult(loadedHeavy, name, args) {
6050
7169
  return renderer(...args);
6051
7170
  return renderIntercomToolResult(name, args);
6052
7171
  }
6053
- function isRecoverableHeavyInitializationDisconnect(error) {
6054
- return error instanceof Error && error.message === "Client disconnected";
7172
+ function reportRelayFailure(eventName, error) {
7173
+ if (isRecoverableIntercomDisconnect(error))
7174
+ return;
7175
+ console.error(`Intercom event relay failed (${eventName}):`, error);
6055
7176
  }
7177
+ var WARM_UP_RETRY_ATTEMPTS = 5;
6056
7178
  function intercom(pi, options = {}) {
6057
7179
  const inheritedDelegatedSessionName = readSubagentEnv("INTERCOM_SESSION_NAME");
6058
7180
  let heavyAttempt = null;
@@ -6206,13 +7328,73 @@ function intercom(pi, options = {}) {
6206
7328
  }, (error) => {
6207
7329
  if (heavyAttempt?.promise === promise)
6208
7330
  heavyAttempt = null;
6209
- if (!isRecoverableHeavyInitializationDisconnect(error)) {
7331
+ if (!isRecoverableIntercomDisconnect(error)) {
6210
7332
  const message = error instanceof Error ? error.message : String(error);
6211
7333
  console.error(`Intercom heavy initialization failed; a later call will retry: ${message}`, error);
6212
7334
  }
6213
7335
  });
6214
7336
  return promise;
6215
7337
  }
7338
+ let warmUpRetry = null;
7339
+ function cancelWarmUpRetry() {
7340
+ if (!warmUpRetry)
7341
+ return;
7342
+ warmUpRetry.cancelled = true;
7343
+ if (warmUpRetry.timer)
7344
+ clearTimeout(warmUpRetry.timer);
7345
+ warmUpRetry = null;
7346
+ }
7347
+ function warmUpRetryDelay(attempt) {
7348
+ const schedule = options.warmUpRetryDelaysMs;
7349
+ if (schedule)
7350
+ return schedule[attempt];
7351
+ return attempt < WARM_UP_RETRY_ATTEMPTS ? reconnectDelayMs(attempt) : undefined;
7352
+ }
7353
+ function scheduleWarmUpRetry(ctx, lease, generation, pendingStageDelivery, initialError) {
7354
+ if (warmUpRetry)
7355
+ return;
7356
+ const state = { lease, generation, timer: null, cancelled: false };
7357
+ warmUpRetry = state;
7358
+ let lastError = initialError;
7359
+ const clearOwner = () => {
7360
+ if (warmUpRetry === state)
7361
+ warmUpRetry = null;
7362
+ };
7363
+ const stale = () => state.cancelled || activeLease !== lease || lease.retired || sessionSnapshot?.generation !== generation;
7364
+ const attemptAt = (attempt) => {
7365
+ const delay = warmUpRetryDelay(attempt);
7366
+ if (delay === undefined) {
7367
+ clearOwner();
7368
+ const exhausted = new IntercomWarmUpExhaustedError(attempt, lastError instanceof Error ? { cause: lastError } : undefined);
7369
+ try {
7370
+ pendingStageDelivery.fail(exhausted);
7371
+ } catch {}
7372
+ return;
7373
+ }
7374
+ const timer = setTimeout(() => {
7375
+ state.timer = null;
7376
+ if (stale()) {
7377
+ clearOwner();
7378
+ return;
7379
+ }
7380
+ loadHeavy(ctx).then(clearOwner, (error) => {
7381
+ lastError = error;
7382
+ if (stale()) {
7383
+ clearOwner();
7384
+ return;
7385
+ }
7386
+ if (!isRecoverableIntercomDisconnect(error)) {
7387
+ clearOwner();
7388
+ return;
7389
+ }
7390
+ attemptAt(attempt + 1);
7391
+ });
7392
+ }, delay);
7393
+ timer.unref?.();
7394
+ state.timer = timer;
7395
+ };
7396
+ attemptAt(0);
7397
+ }
6216
7398
  let typedContactSupervisorRegistered = hasSubagentIntercomEnv();
6217
7399
  const activateTypedContactSupervisor = () => {
6218
7400
  const activeTools = pi.getActiveTools();
@@ -6259,8 +7441,16 @@ function intercom(pi, options = {}) {
6259
7441
  }
6260
7442
  const generation = ++lifecycleGeneration;
6261
7443
  sessionSnapshot = { event, ctx, generation, lease };
7444
+ cancelWarmUpRetry();
6262
7445
  if (ctx.orchestrationContext?.kind === "workflow-stage" && ctx.orchestrationContext.pendingStageDelivery !== undefined) {
6263
- await loadHeavy(ctx);
7446
+ const pendingStageDelivery = ctx.orchestrationContext.pendingStageDelivery;
7447
+ try {
7448
+ await loadHeavy(ctx);
7449
+ } catch (error) {
7450
+ if (!isRecoverableIntercomDisconnect(error))
7451
+ throw error;
7452
+ scheduleWarmUpRetry(ctx, lease, generation, pendingStageDelivery, error);
7453
+ }
6264
7454
  } else if (loadedHeavy) {
6265
7455
  await ensureSessionStartReplayed(loadedHeavy.heavy, lease);
6266
7456
  }
@@ -6269,6 +7459,7 @@ function intercom(pi, options = {}) {
6269
7459
  const lease = activeLease;
6270
7460
  const generation = ++lifecycleGeneration;
6271
7461
  retireLifecycleLease(lease, { event, ctx, generation });
7462
+ cancelWarmUpRetry();
6272
7463
  const retiredHeavy = loadedHeavy?.heavy ?? null;
6273
7464
  const retiredAttempt = heavyAttempt?.lease === lease ? heavyAttempt.promise : null;
6274
7465
  const retiredReplay = replayAttempt?.lease === lease ? replayAttempt.promise : null;
@@ -6377,6 +7568,10 @@ function intercom(pi, options = {}) {
6377
7568
  if (!forwarded.completion)
6378
7569
  throw new Error("Intercom supervisor authorization provider is unavailable");
6379
7570
  return await forwarded.completion;
7571
+ }).catch((error) => {
7572
+ if (isRecoverableIntercomDisconnect(error))
7573
+ return;
7574
+ throw error;
6380
7575
  });
6381
7576
  });
6382
7577
  pi.events.on(PENDING_STAGE_UNDELIVERABLE_EVENT2, (payload) => {
@@ -6390,7 +7585,7 @@ function intercom(pi, options = {}) {
6390
7585
  handle.assertCurrent();
6391
7586
  return forwarded.handled === true && forwarded.completion !== undefined ? await forwarded.completion : false;
6392
7587
  }).catch((error) => {
6393
- console.error(`Intercom event relay failed (${PENDING_STAGE_UNDELIVERABLE_EVENT2}):`, error);
7588
+ reportRelayFailure(PENDING_STAGE_UNDELIVERABLE_EVENT2, error);
6394
7589
  return false;
6395
7590
  });
6396
7591
  });
@@ -6415,7 +7610,7 @@ function intercom(pi, options = {}) {
6415
7610
  }
6416
7611
  completion.catch((error) => {
6417
7612
  rejectLazyResultRelay(pi, eventName, payload, error);
6418
- console.error(`Intercom event relay failed (${eventName}):`, error);
7613
+ reportRelayFailure(eventName, error);
6419
7614
  });
6420
7615
  });
6421
7616
  }
@@ -6438,7 +7633,8 @@ function intercom(pi, options = {}) {
6438
7633
  description: `Send a message to another local agent session running on this machine.
6439
7634
  Use this to communicate findings, request help, or coordinate work with other sessions.
6440
7635
  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.
6441
- 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.
7636
+ For send, live session names and exact full session IDs remain supported. Workflow-stage targets use \`workflow:<rootRunId>/<segment>[/<segment>...]\`; \`*\` matches one segment and \`**\` any depth. Use \`intercom list\` inside the invocation group to see live, pending, and possible future targets with queued counts. \`workflow:<rootRunId>/**\` reaches live stages now and remains sticky for every future stage until root termination; valid targets outside the known set queue with a \`notInKnownSet\` warning and settle undeliverable at terminal only if never delivered. Use \`ask\` only on live targets.
7637
+ If a \`send\`, \`ask\`, or \`reply\` fails with \`Client disconnected\`, retry that exact operation with the returned \`retryToken\`, up to three claimed attempts; omit the token for every fresh operation.
6442
7638
  Usage:
6443
7639
  intercom({ action: "list" }) → List sessions in your group
6444
7640
  intercom({ action: "list", group: "name" }) → Read-only peek at another group's sessions
@@ -6447,14 +7643,15 @@ Usage:
6447
7643
  intercom({ action: "send", to: "session-name", message: "..." }) → Send message (own group only)
6448
7644
  intercom({ action: "ask", to: "session-name", message: "..." }) → Ask and wait for reply
6449
7645
  intercom({ action: "reply", message: "..." }) → Reply to the active or exact pending ask
7646
+ intercom({ action: "send", to: "session-name", message: "...", retryToken: "..." }) → Claim the exact retry returned by a retryable failure
6450
7647
  intercom({ action: "pending" }) → List unresolved inbound asks
6451
7648
  intercom({ action: "status" }) → Show connection status and your group
6452
7649
 
6453
7650
  "default" is the shared group; "true" and "auto" are reserved for subagent auto-groups. Joining does not grant cross-group access; contact_supervisor remains the only cross-group path.`,
6454
- 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.",
7651
+ promptSnippet: "Use to coordinate with other local agent sessions in your intercom group. For a retryable send/ask/reply failure, repeat the exact operation with its returned retryToken; omit retryToken for fresh operations.",
6455
7652
  parameters: Type3.Object({
6456
7653
  action: Type3.String({ description: "Action: 'list', 'join', 'leave', 'send', 'ask', 'reply', 'pending', or 'status'" }),
6457
- to: Type3.Optional(Type3.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')" })),
7654
+ to: Type3.Optional(Type3.String({ description: "Live session name, exact full session ID, or `workflow:<rootRunId>/<segment>[/<segment>...]` path; `*` matches one segment and `**` any depth. Send queues sticky pending/future delivery and `workflow:<rootRunId>/**` broadcasts to live and future stages; use `ask` only on live targets (for 'send', 'ask', or targeted 'reply')" })),
6458
7655
  message: Type3.Optional(Type3.String({ description: "Message to send (for 'send', 'ask', or 'reply' action)" })),
6459
7656
  attachments: Type3.Optional(Type3.Array(Type3.Object({
6460
7657
  type: Type3.Union([Type3.Literal("file"), Type3.Literal("snippet"), Type3.Literal("context")]),
@@ -6463,6 +7660,7 @@ Usage:
6463
7660
  language: Type3.Optional(Type3.String())
6464
7661
  }))),
6465
7662
  replyTo: Type3.Optional(Type3.String({ description: "Exact pending-ask message ID; disambiguates concurrent asks, including asks from one sender" })),
7663
+ retryToken: Type3.Optional(Type3.String({ minLength: 1, maxLength: 128, description: "Opaque token returned after a retryable send/ask/reply failure. Repeat the exact same action and arguments with it; omit it for fresh operations." })),
6466
7664
  group: Type3.Optional(Type3.String({ description: "Group name for 'join'; read-only group filter for 'list'/'status'. 'send'/'ask' are locked to your own group." }))
6467
7665
  }),
6468
7666
  execute: (...args) => executeHeavyTool(loadHeavy, "intercom", args),