@bastani/atomic 0.9.18-alpha.4 → 0.9.18-alpha.6

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