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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +3 -3
  3. package/dist/builtin/intercom/CHANGELOG.md +39 -0
  4. package/dist/builtin/intercom/README.md +22 -2
  5. package/dist/builtin/intercom/broker/broker.ts +676 -128
  6. package/dist/builtin/intercom/broker/client.ts +151 -35
  7. package/dist/builtin/intercom/broker/delivered-message-cache.ts +497 -37
  8. package/dist/builtin/intercom/broker/framing.ts +4 -2
  9. package/dist/builtin/intercom/broker/paths.ts +10 -0
  10. package/dist/builtin/intercom/broker/pending-send-registry.ts +1 -2
  11. package/dist/builtin/intercom/broker/send-handler.ts +311 -52
  12. package/dist/builtin/intercom/broker/send-signature.ts +4 -2
  13. package/dist/builtin/intercom/broker/socket-writes.ts +103 -0
  14. package/dist/builtin/intercom/broker/spawn.ts +9 -4
  15. package/dist/builtin/intercom/index.bundle.mjs +1410 -212
  16. package/dist/builtin/intercom/package.json +2 -2
  17. package/dist/builtin/intercom/recoverable-disconnect.ts +52 -0
  18. package/dist/builtin/intercom/retry-policy.ts +8 -0
  19. package/dist/builtin/intercom/skills/intercom/SKILL.md +17 -9
  20. package/dist/builtin/intercom/types.ts +27 -8
  21. package/dist/builtin/intercom/workflow-stage-path-matching.ts +90 -0
  22. package/dist/builtin/intercom/workflow-stage-target.ts +57 -0
  23. package/dist/builtin/mcp/index.bundle.mjs +427 -126
  24. package/dist/builtin/mcp/package.json +2 -2
  25. package/dist/builtin/subagents/CHANGELOG.md +19 -0
  26. package/dist/builtin/subagents/README.md +3 -3
  27. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  28. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  29. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  30. package/dist/builtin/subagents/agents/codebase-online-researcher.md +11 -11
  31. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  32. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  33. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  34. package/dist/builtin/subagents/agents/debugger.md +3 -3
  35. package/dist/builtin/subagents/agents/worker.md +2 -2
  36. package/dist/builtin/subagents/package.json +3 -3
  37. package/dist/builtin/subagents/skills/qlty/SKILL.md +29 -101
  38. package/dist/builtin/subagents/skills/qlty/references/manual-configuration.md +52 -0
  39. package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
  40. package/dist/builtin/subagents/src/extension/index.bundle.mjs +49 -132
  41. package/dist/builtin/web-access/package.json +2 -2
  42. package/dist/builtin/workflows/CHANGELOG.md +46 -0
  43. package/dist/builtin/workflows/README.md +7 -7
  44. package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
  45. package/dist/builtin/workflows/builtin/{chunk-szz1ajz9.js → chunk-42s7sw0b.js} +9 -2
  46. package/dist/builtin/workflows/builtin/{chunk-2dqb5s2q.js → chunk-79fjkcvh.js} +1 -1
  47. package/dist/builtin/workflows/builtin/{chunk-n58a7v26.js → chunk-b0v2xab1.js} +181 -10
  48. package/dist/builtin/workflows/builtin/{chunk-fghhy2a5.js → chunk-ed4jfn5t.js} +23 -5
  49. package/dist/builtin/workflows/builtin/{chunk-h3r2vkzc.js → chunk-hqpe2cyx.js} +60 -21
  50. package/dist/builtin/workflows/builtin/{chunk-hzzn6adg.js → chunk-k3w87x8y.js} +1 -1
  51. package/dist/builtin/workflows/builtin/{chunk-82ha8p41.js → chunk-n8h4yyew.js} +1 -1
  52. package/dist/builtin/workflows/builtin/{chunk-qwzvgxnq.js → chunk-reqr3sr3.js} +1 -1
  53. package/dist/builtin/workflows/builtin/{chunk-c53y8bdh.js → chunk-wgccch41.js} +1 -1
  54. package/dist/builtin/workflows/builtin/{chunk-0x6e303p.js → chunk-zbekjqew.js} +20 -7
  55. package/dist/builtin/workflows/builtin/{chunk-29wrp38a.js → chunk-ze5x3d1r.js} +1 -1
  56. package/dist/builtin/workflows/builtin/classify-and-act.js +2 -2
  57. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +2 -2
  58. package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
  59. package/dist/builtin/workflows/builtin/goal.js +3 -3
  60. package/dist/builtin/workflows/builtin/index.js +11 -11
  61. package/dist/builtin/workflows/builtin/loop-until-done.js +2 -2
  62. package/dist/builtin/workflows/builtin/open-claude-design.js +2 -2
  63. package/dist/builtin/workflows/builtin/ralph.js +3 -3
  64. package/dist/builtin/workflows/builtin/steering-context.js +1 -1
  65. package/dist/builtin/workflows/builtin/tournament.js +2 -2
  66. package/dist/builtin/workflows/package.json +2 -2
  67. package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +39 -52
  68. package/dist/builtin/workflows/skills/prompt-engineer/references/advanced_patterns.md +9 -37
  69. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_fable_5.md +49 -0
  70. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_fable_5_1.md +57 -0
  71. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_opus_4_8.md +57 -0
  72. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_opus_5.md +47 -0
  73. package/dist/builtin/workflows/skills/prompt-engineer/references/claude_sonnet_5.md +57 -0
  74. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_5_5.md +60 -0
  75. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_5_6.md +58 -0
  76. package/dist/builtin/workflows/skills/prompt-engineer/references/gpt_6_astra.md +58 -0
  77. package/dist/builtin/workflows/skills/prompt-engineer/references/quality_improvement.md +18 -4
  78. package/dist/builtin/workflows/src/extension/index.bundle.mjs +3194 -737
  79. package/dist/builtin/workflows/src/index.js +899 -436
  80. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +0 -10
  81. package/dist/cli/list-models.d.ts.map +1 -1
  82. package/dist/cli/list-models.js +4 -0
  83. package/dist/cli/list-models.js.map +1 -1
  84. package/dist/client/index.d.ts +1 -2
  85. package/dist/client/index.d.ts.map +1 -1
  86. package/dist/client/index.js +1 -2
  87. package/dist/client/index.js.map +1 -1
  88. package/dist/config.d.ts +0 -9
  89. package/dist/config.d.ts.map +1 -1
  90. package/dist/config.js +0 -45
  91. package/dist/config.js.map +1 -1
  92. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  93. package/dist/core/agent-session-compaction.js +8 -6
  94. package/dist/core/agent-session-compaction.js.map +1 -1
  95. package/dist/core/agent-session-methods.d.ts +1 -0
  96. package/dist/core/agent-session-methods.d.ts.map +1 -1
  97. package/dist/core/agent-session-methods.js.map +1 -1
  98. package/dist/core/agent-session-models.d.ts.map +1 -1
  99. package/dist/core/agent-session-models.js +3 -4
  100. package/dist/core/agent-session-models.js.map +1 -1
  101. package/dist/core/agent-session-queue-pause.d.ts.map +1 -1
  102. package/dist/core/agent-session-queue-pause.js +6 -0
  103. package/dist/core/agent-session-queue-pause.js.map +1 -1
  104. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  105. package/dist/core/agent-session-runtime.js +13 -5
  106. package/dist/core/agent-session-runtime.js.map +1 -1
  107. package/dist/core/agent-session-tool-hooks.js +1 -1
  108. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  109. package/dist/core/agent-session-tree.d.ts.map +1 -1
  110. package/dist/core/agent-session-tree.js +8 -0
  111. package/dist/core/agent-session-tree.js.map +1 -1
  112. package/dist/core/agent-session.d.ts +2 -0
  113. package/dist/core/agent-session.d.ts.map +1 -1
  114. package/dist/core/agent-session.js +2 -0
  115. package/dist/core/agent-session.js.map +1 -1
  116. package/dist/core/compaction/branch-summarization.d.ts +2 -2
  117. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  118. package/dist/core/compaction/branch-summarization.js +4 -4
  119. package/dist/core/compaction/branch-summarization.js.map +1 -1
  120. package/dist/core/extensions/api-types.d.ts +8 -2
  121. package/dist/core/extensions/api-types.d.ts.map +1 -1
  122. package/dist/core/extensions/api-types.js.map +1 -1
  123. package/dist/core/extensions/context-types.d.ts +12 -0
  124. package/dist/core/extensions/context-types.d.ts.map +1 -1
  125. package/dist/core/extensions/context-types.js.map +1 -1
  126. package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
  127. package/dist/core/extensions/runner-shortcuts.js +0 -1
  128. package/dist/core/extensions/runner-shortcuts.js.map +1 -1
  129. package/dist/core/fast-model-routing-transport.d.ts +12 -0
  130. package/dist/core/fast-model-routing-transport.d.ts.map +1 -0
  131. package/dist/core/{codex-fast-mode-transport.js → fast-model-routing-transport.js} +29 -29
  132. package/dist/core/fast-model-routing-transport.js.map +1 -0
  133. package/dist/core/fast-model-routing.d.ts +57 -0
  134. package/dist/core/fast-model-routing.d.ts.map +1 -0
  135. package/dist/core/fast-model-routing.js +201 -0
  136. package/dist/core/fast-model-routing.js.map +1 -0
  137. package/dist/core/fast-model-variants.d.ts +98 -0
  138. package/dist/core/fast-model-variants.d.ts.map +1 -0
  139. package/dist/core/fast-model-variants.js +148 -0
  140. package/dist/core/fast-model-variants.js.map +1 -0
  141. package/dist/core/http-dispatcher.d.ts.map +1 -1
  142. package/dist/core/http-dispatcher.js +4 -2
  143. package/dist/core/http-dispatcher.js.map +1 -1
  144. package/dist/core/keybindings.d.ts +0 -10
  145. package/dist/core/keybindings.d.ts.map +1 -1
  146. package/dist/core/keybindings.js +0 -8
  147. package/dist/core/keybindings.js.map +1 -1
  148. package/dist/core/messages.d.ts +1 -1
  149. package/dist/core/messages.d.ts.map +1 -1
  150. package/dist/core/messages.js.map +1 -1
  151. package/dist/core/model-config.d.ts +15 -0
  152. package/dist/core/model-config.d.ts.map +1 -1
  153. package/dist/core/model-config.js +3 -0
  154. package/dist/core/model-config.js.map +1 -1
  155. package/dist/core/model-resolver-initial.js +1 -1
  156. package/dist/core/model-resolver-initial.js.map +1 -1
  157. package/dist/core/model-runtime-streaming.d.ts +14 -1
  158. package/dist/core/model-runtime-streaming.d.ts.map +1 -1
  159. package/dist/core/model-runtime-streaming.js +25 -3
  160. package/dist/core/model-runtime-streaming.js.map +1 -1
  161. package/dist/core/model-runtime.d.ts +39 -2
  162. package/dist/core/model-runtime.d.ts.map +1 -1
  163. package/dist/core/model-runtime.js +91 -7
  164. package/dist/core/model-runtime.js.map +1 -1
  165. package/dist/core/provider-composer-internal.d.ts.map +1 -1
  166. package/dist/core/provider-composer-internal.js +8 -2
  167. package/dist/core/provider-composer-internal.js.map +1 -1
  168. package/dist/core/sdk.d.ts.map +1 -1
  169. package/dist/core/sdk.js +18 -18
  170. package/dist/core/sdk.js.map +1 -1
  171. package/dist/core/session-manager-archive.d.ts.map +1 -1
  172. package/dist/core/session-manager-archive.js +25 -2
  173. package/dist/core/session-manager-archive.js.map +1 -1
  174. package/dist/core/settings-manager-core.d.ts +0 -2
  175. package/dist/core/settings-manager-core.d.ts.map +1 -1
  176. package/dist/core/settings-manager-core.js +2 -9
  177. package/dist/core/settings-manager-core.js.map +1 -1
  178. package/dist/core/settings-manager-internals.d.ts +0 -1
  179. package/dist/core/settings-manager-internals.d.ts.map +1 -1
  180. package/dist/core/settings-manager-internals.js.map +1 -1
  181. package/dist/core/settings-manager-ui-accessors.d.ts +0 -8
  182. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  183. package/dist/core/settings-manager-ui-accessors.js +0 -53
  184. package/dist/core/settings-manager-ui-accessors.js.map +1 -1
  185. package/dist/core/settings-manager.d.ts +1 -1
  186. package/dist/core/settings-manager.d.ts.map +1 -1
  187. package/dist/core/settings-manager.js.map +1 -1
  188. package/dist/core/settings-types.d.ts +0 -5
  189. package/dist/core/settings-types.d.ts.map +1 -1
  190. package/dist/core/settings-types.js.map +1 -1
  191. package/dist/core/skills.d.ts +1 -1
  192. package/dist/core/skills.d.ts.map +1 -1
  193. package/dist/core/skills.js +4 -2
  194. package/dist/core/skills.js.map +1 -1
  195. package/dist/core/slash-commands.d.ts.map +1 -1
  196. package/dist/core/slash-commands.js +0 -1
  197. package/dist/core/slash-commands.js.map +1 -1
  198. package/dist/core/system-prompt.js +7 -7
  199. package/dist/core/system-prompt.js.map +1 -1
  200. package/dist/core/tools/edit.d.ts +1 -1
  201. package/dist/core/tools/edit.d.ts.map +1 -1
  202. package/dist/core/tools/edit.js +128 -3
  203. package/dist/core/tools/edit.js.map +1 -1
  204. package/dist/core/tools/hashline.d.ts +2 -2
  205. package/dist/core/tools/hashline.d.ts.map +1 -1
  206. package/dist/core/tools/hashline.js +40 -10
  207. package/dist/core/tools/hashline.js.map +1 -1
  208. package/dist/core/tools/write.d.ts.map +1 -1
  209. package/dist/core/tools/write.js +8 -8
  210. package/dist/core/tools/write.js.map +1 -1
  211. package/dist/core/workflow-stage-admission.d.ts +6 -0
  212. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  213. package/dist/core/workflow-stage-admission.js +14 -0
  214. package/dist/core/workflow-stage-admission.js.map +1 -1
  215. package/dist/index-extensions.d.ts +1 -1
  216. package/dist/index-extensions.d.ts.map +1 -1
  217. package/dist/index-extensions.js.map +1 -1
  218. package/dist/index.d.ts +5 -5
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +4 -5
  221. package/dist/index.js.map +1 -1
  222. package/dist/modes/interactive/components/atomic-working-status.d.ts +4 -0
  223. package/dist/modes/interactive/components/atomic-working-status.d.ts.map +1 -1
  224. package/dist/modes/interactive/components/atomic-working-status.js +26 -1
  225. package/dist/modes/interactive/components/atomic-working-status.js.map +1 -1
  226. package/dist/modes/interactive/components/custom-editor.d.ts +7 -0
  227. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  228. package/dist/modes/interactive/components/custom-editor.js +43 -0
  229. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  230. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  231. package/dist/modes/interactive/components/footer.js +0 -6
  232. package/dist/modes/interactive/components/footer.js.map +1 -1
  233. package/dist/modes/interactive/components/index.d.ts +1 -2
  234. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  235. package/dist/modes/interactive/components/index.js +0 -1
  236. package/dist/modes/interactive/components/index.js.map +1 -1
  237. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  238. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  239. package/dist/modes/interactive/components/model-selector.js +6 -11
  240. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  241. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -5
  242. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  243. package/dist/modes/interactive/components/scoped-models-selector.js +3 -19
  244. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  245. package/dist/modes/interactive/components/settings-selector-items.js +1 -1
  246. package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
  247. package/dist/modes/interactive/components/thinking-selector.d.ts +1 -2
  248. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/thinking-selector.js +4 -11
  250. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  251. package/dist/modes/interactive/components/transcript-follow-indicator.js +3 -3
  252. package/dist/modes/interactive/components/transcript-follow-indicator.js.map +1 -1
  253. package/dist/modes/interactive/interactive-agent-events.js +8 -8
  254. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  255. package/dist/modes/interactive/interactive-autocomplete.js +5 -16
  256. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  257. package/dist/modes/interactive/interactive-bash-compact.js +2 -9
  258. package/dist/modes/interactive/interactive-bash-compact.js.map +1 -1
  259. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  260. package/dist/modes/interactive/interactive-deferred-startup.js +3 -0
  261. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  262. package/dist/modes/interactive/interactive-editor-actions.js +2 -2
  263. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  264. package/dist/modes/interactive/interactive-extension-custom-ui.js +1 -0
  265. package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
  266. package/dist/modes/interactive/interactive-extension-dialogs.js +7 -0
  267. package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
  268. package/dist/modes/interactive/interactive-extension-runtime.js +20 -10
  269. package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
  270. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -5
  271. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  272. package/dist/modes/interactive/interactive-initial-session-binding.d.ts.map +1 -1
  273. package/dist/modes/interactive/interactive-initial-session-binding.js +3 -0
  274. package/dist/modes/interactive/interactive-initial-session-binding.js.map +1 -1
  275. package/dist/modes/interactive/interactive-input-handling.js +0 -8
  276. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  277. package/dist/modes/interactive/interactive-mode-base.d.ts +5 -1
  278. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  279. package/dist/modes/interactive/interactive-mode-base.js +15 -9
  280. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  281. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -3
  282. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  283. package/dist/modes/interactive/interactive-mode-deps.js +1 -3
  284. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  285. package/dist/modes/interactive/interactive-mode-surface.d.ts +9 -12
  286. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  287. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  288. package/dist/modes/interactive/interactive-model-routing.js +10 -13
  289. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  290. package/dist/modes/interactive/interactive-render-chat.js +29 -0
  291. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  292. package/dist/modes/interactive/interactive-selectors.js +2 -32
  293. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  294. package/dist/modes/interactive/interactive-session-routing.js +1 -5
  295. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  296. package/dist/modes/interactive/interactive-session-runtime.js +1 -5
  297. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  298. package/dist/modes/interactive/interactive-slash-commands.js +6 -13
  299. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  300. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  301. package/dist/modes/interactive/interactive-startup.js +19 -13
  302. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  303. package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
  304. package/dist/modes/interactive/interactive-tui.js +27 -3
  305. package/dist/modes/interactive/interactive-tui.js.map +1 -1
  306. package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
  307. package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
  308. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  309. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  310. package/dist/modes/interactive-engine/isolated-runtime.js +4 -1
  311. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  312. package/dist/utils/child-process.d.ts.map +1 -1
  313. package/dist/utils/child-process.js +13 -6
  314. package/dist/utils/child-process.js.map +1 -1
  315. package/dist/utils/exif-orientation.d.ts.map +1 -1
  316. package/dist/utils/exif-orientation.js +2 -3
  317. package/dist/utils/exif-orientation.js.map +1 -1
  318. package/dist/utils/tools-manager.d.ts +1 -0
  319. package/dist/utils/tools-manager.d.ts.map +1 -1
  320. package/dist/utils/tools-manager.js +33 -18
  321. package/dist/utils/tools-manager.js.map +1 -1
  322. package/docs/custom-provider.md +2 -0
  323. package/docs/development.md +17 -0
  324. package/docs/docs.json +14 -2
  325. package/docs/extensions.md +9 -4
  326. package/docs/intercom.md +41 -7
  327. package/docs/keybindings.md +8 -6
  328. package/docs/models/evals.md +394 -0
  329. package/docs/models/model-selection.md +54 -27
  330. package/docs/models/pareto-efficiency.md +33 -16
  331. package/docs/models.md +37 -1
  332. package/docs/packages.md +2 -2
  333. package/docs/providers.md +44 -20
  334. package/docs/quickstart.md +8 -6
  335. package/docs/rpc.md +3 -3
  336. package/docs/sdk.md +2 -16
  337. package/docs/settings.md +19 -22
  338. package/docs/skills.md +4 -2
  339. package/docs/subagents.md +9 -3
  340. package/docs/tools/edit.md +373 -0
  341. package/docs/tools.md +3 -7
  342. package/docs/tui.md +2 -1
  343. package/docs/usage.md +3 -5
  344. package/docs/workflows/api-reference.md +1395 -0
  345. package/docs/workflows/authoring.md +678 -0
  346. package/docs/workflows/builtins.md +245 -0
  347. package/docs/workflows/operations.md +811 -0
  348. package/docs/workflows/reliable-design.md +2099 -0
  349. package/docs/workflows/verification.md +62 -0
  350. package/docs/workflows.md +15 -5183
  351. package/npm-shrinkwrap.json +584 -69
  352. package/package.json +7 -7
  353. package/dist/client/remote-session.d.ts +0 -64
  354. package/dist/client/remote-session.d.ts.map +0 -1
  355. package/dist/client/remote-session.js +0 -351
  356. package/dist/client/remote-session.js.map +0 -1
  357. package/dist/client/transcript.d.ts +0 -12
  358. package/dist/client/transcript.d.ts.map +0 -1
  359. package/dist/client/transcript.js +0 -98
  360. package/dist/client/transcript.js.map +0 -1
  361. package/dist/core/codex-fast-mode-transport.d.ts +0 -12
  362. package/dist/core/codex-fast-mode-transport.d.ts.map +0 -1
  363. package/dist/core/codex-fast-mode-transport.js.map +0 -1
  364. package/dist/core/codex-fast-mode.d.ts +0 -53
  365. package/dist/core/codex-fast-mode.d.ts.map +0 -1
  366. package/dist/core/codex-fast-mode.js +0 -249
  367. package/dist/core/codex-fast-mode.js.map +0 -1
  368. package/dist/modes/interactive/components/fast-mode-selector.d.ts +0 -27
  369. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +0 -1
  370. package/dist/modes/interactive/components/fast-mode-selector.js +0 -107
  371. package/dist/modes/interactive/components/fast-mode-selector.js.map +0 -1
  372. package/dist/server/create-harness.d.ts +0 -53
  373. package/dist/server/create-harness.d.ts.map +0 -1
  374. package/dist/server/create-harness.js +0 -360
  375. package/dist/server/create-harness.js.map +0 -1
  376. package/docs/models/artificial-analysis-index.md +0 -83
@@ -1,12 +1,18 @@
1
1
  import type net from "node:net";
2
2
  import type { BrokerMessage, Message, SessionInfo } from "../types.js";
3
+ import {
4
+ legacyWorkflowStageTargetMigrationHint,
5
+ parseLegacyWorkflowStageTarget,
6
+ parseWorkflowStageTarget,
7
+ } from "../workflow-stage-target.js";
8
+ import { normalizeGroup } from "../group.js";
3
9
  import { isMessage } from "./client-message-validation.js";
4
10
  import { resolveSessionTarget, sessionTargetFailureReason } from "../session-target.js";
5
11
  import { DeliveredMessageCache } from "./delivered-message-cache.js";
6
12
  import { buildMessageSendSignature } from "./send-signature.js";
7
13
  import { SupervisorChannelCache } from "./supervisor-channel.js";
8
14
  import { isVerticalBypass, sameGroup } from "./group-isolation.js";
9
- import { sessionsShareGroup } from "./group-membership.js";
15
+ import { sessionGroups, sessionsShareGroup } from "./group-membership.js";
10
16
  import { PendingQuestionIndex } from "./pending-question-index.js";
11
17
 
12
18
  export interface BrokerConnectedSession {
@@ -28,11 +34,25 @@ export interface BrokerConnectedSession {
28
34
  supervisorOwnerToken?: string;
29
35
  }
30
36
 
37
+ /** Immutable registration authority plus normalized memberships used when an accepted ask is rebound. */
38
+ export function senderGroupIdentity(session: BrokerConnectedSession): string {
39
+ return JSON.stringify([
40
+ normalizeGroup(session.registrationGroup),
41
+ [...sessionGroups(session.info)].sort((left, right) => left.localeCompare(right)),
42
+ ]);
43
+ }
44
+
45
+
46
+ /** Stable recipient authority used independently from the freshly resolved transport ID. */
47
+ export function deliveryTargetIdentity(session: BrokerConnectedSession): string {
48
+ return session.registrationReturnAddress === undefined
49
+ ? JSON.stringify(["session", session.info.id])
50
+ : JSON.stringify(["return", session.registrationReturnAddress, normalizeGroup(session.registrationGroup)]);
51
+ }
31
52
  export interface PendingStageRoute {
32
53
  readonly socket: net.Socket;
33
54
  readonly from: BrokerConnectedSession;
34
- readonly runId: string;
35
- readonly stageKey: string;
55
+ readonly target: string;
36
56
  readonly message: Message;
37
57
  readonly attemptId?: string;
38
58
  readonly liveTargetId?: string;
@@ -40,6 +60,11 @@ export interface PendingStageRoute {
40
60
  }
41
61
 
42
62
  export type PendingStageRouter = (route: PendingStageRoute) => boolean;
63
+ export type ConfirmedMessageWriter = (
64
+ target: net.Socket,
65
+ message: BrokerMessage,
66
+ onSettled: (written: boolean) => void,
67
+ ) => void;
43
68
 
44
69
  export type LiveWorkflowStageResolver = (target: string) => BrokerConnectedSession | undefined;
45
70
  export type LiveWorkflowStageController = (
@@ -48,15 +73,7 @@ export type LiveWorkflowStageController = (
48
73
  logicalTarget: string,
49
74
  ) => boolean;
50
75
 
51
- const 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;
52
-
53
- export function parsePendingStageTarget(target: string): { runId: string; stageKey: string } | undefined {
54
- const separator = target.indexOf(":");
55
- if (separator < 0) return undefined;
56
- const runId = target.slice(0, separator);
57
- const stageKey = target.slice(separator + 1);
58
- return WORKFLOW_RUN_ID_PATTERN.test(runId) && stageKey.length > 0 ? { runId, stageKey } : undefined;
59
- }
76
+ export type LegacyWorkflowStageTargetResolver = (runId: string, stageKey: string) => string | undefined;
60
77
  export const PENDING_STAGE_ASK_REFUSAL =
61
78
  "Cannot ask a workflow stage whose session has not initialized. Use send; Atomic will queue the message until the stage session initializes.";
62
79
 
@@ -80,12 +97,20 @@ export function handleBrokerSend(
80
97
  currentId: string | null,
81
98
  sessions: Map<string, BrokerConnectedSession>,
82
99
  deliveredMessages: DeliveredMessageCache,
83
- write: (target: net.Socket, message: BrokerMessage) => void,
100
+ /**
101
+ * Hand one frame to a socket. Returns whether the frame was actually written.
102
+ * Durable acceptance is reserved before this call to close the restart window;
103
+ * a `false` answer removes that exact reservation, while only `true` is
104
+ * acknowledged to the sender.
105
+ */
106
+ write: (target: net.Socket, message: BrokerMessage) => boolean,
84
107
  supervisorCache: SupervisorChannelCache = new SupervisorChannelCache(),
85
108
  pendingQuestions: PendingQuestionIndex = new PendingQuestionIndex(),
86
109
  routePendingStage?: PendingStageRouter,
87
110
  resolveLiveWorkflowStage?: LiveWorkflowStageResolver,
88
111
  canControlLiveWorkflowStage?: LiveWorkflowStageController,
112
+ resolveLegacyWorkflowStageTarget?: LegacyWorkflowStageTargetResolver,
113
+ writeConfirmed?: ConfirmedMessageWriter,
89
114
  ): void {
90
115
  const message = clientMessage.message;
91
116
  const messageId = wireMessageId(message);
@@ -99,15 +124,30 @@ export function handleBrokerSend(
99
124
  return;
100
125
  }
101
126
  const attemptId = typeof clientMessage.attemptId === "string" ? clientMessage.attemptId : undefined;
127
+ const hasLogicalTarget = Object.prototype.hasOwnProperty.call(clientMessage, "logicalTarget");
128
+ if (hasLogicalTarget && typeof clientMessage.logicalTarget !== "string") {
129
+ write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid logicalTarget format" });
130
+ return;
131
+ }
102
132
  if (typeof clientMessage.to !== "string" || !isMessage(message)) {
103
133
  write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid message format" });
104
134
  return;
105
135
  }
136
+ const hasRequirePendingReply = Object.prototype.hasOwnProperty.call(clientMessage, "requirePendingReply");
137
+ if (hasRequirePendingReply && clientMessage.requirePendingReply !== true) {
138
+ write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid requirePendingReply format" });
139
+ return;
140
+ }
141
+ const requirePendingReply = clientMessage.requirePendingReply === true;
106
142
  if (Object.prototype.hasOwnProperty.call(clientMessage, "channel")) {
107
143
  write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Invalid channel" });
108
144
  return;
109
145
  }
110
146
  const supervisorSend = clientMessage.type === "supervisor_send";
147
+ if (requirePendingReply && (supervisorSend || message.replyTo === undefined || message.expectsReply === true)) {
148
+ write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid requirePendingReply message" });
149
+ return;
150
+ }
111
151
 
112
152
 
113
153
  const fromSession = currentId ? sessions.get(currentId) : undefined;
@@ -115,22 +155,16 @@ export function handleBrokerSend(
115
155
  write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Sender session not found" });
116
156
  return;
117
157
  }
118
- const signature = buildMessageSendSignature(clientMessage.to, message, fromSession.info.id);
119
- const deliveredMatch = deliveredMessages.lookup(message.id, signature);
120
- if (deliveredMatch === "match") {
121
- write(socket, { type: "delivered", messageId: message.id, attemptId });
122
- return;
123
- }
124
- if (deliveredMatch === "conflict") {
125
- write(socket, {
126
- type: "delivery_failed",
127
- messageId: message.id,
128
- attemptId,
129
- reason: `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
130
- reasonCode: "message_id_conflict",
131
- });
132
- return;
133
- }
158
+ const senderIdentity = fromSession.registrationReturnAddress ?? fromSession.info.id;
159
+ const logicalTarget = typeof clientMessage.logicalTarget === "string" ? clientMessage.logicalTarget : clientMessage.to;
160
+ const baseSignature = buildMessageSendSignature(logicalTarget, message, senderIdentity);
161
+ const signature = requirePendingReply
162
+ ? JSON.stringify({
163
+ requirePendingReply: true,
164
+ senderGroupIdentity: senderGroupIdentity(fromSession),
165
+ baseSignature,
166
+ })
167
+ : baseSignature;
134
168
  const trimmedTo = clientMessage.to.trim();
135
169
  if (supervisorSend && !fromSession.supervisorId) {
136
170
  write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Supervisor channel is not authorized" });
@@ -140,6 +174,30 @@ export function handleBrokerSend(
140
174
  write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Supervisor target does not match the authorized relationship" });
141
175
  return;
142
176
  }
177
+ const workflowTarget = parseWorkflowStageTarget(trimmedTo);
178
+ // Slice 3 (D3): asks to pattern/future stage targets stay refused; `send` is queued
179
+ // sticky by the workflow host, so the refusal must happen before any live delivery.
180
+ if (workflowTarget !== undefined && workflowTarget.kind !== "path" && message.expectsReply === true) {
181
+ write(socket, {
182
+ type: "delivery_failed",
183
+ messageId: message.id,
184
+ ...(attemptId ? { attemptId } : {}),
185
+ reason: PENDING_STAGE_ASK_REFUSAL,
186
+ });
187
+ return;
188
+ }
189
+ const legacyTarget = parseLegacyWorkflowStageTarget(trimmedTo);
190
+ if (legacyTarget !== undefined) {
191
+ write(socket, {
192
+ type: "delivery_failed",
193
+ messageId: message.id,
194
+ ...(attemptId ? { attemptId } : {}),
195
+ reason: legacyWorkflowStageTargetMigrationHint(
196
+ resolveLegacyWorkflowStageTarget?.(legacyTarget.runId, legacyTarget.stageKey),
197
+ ),
198
+ });
199
+ return;
200
+ }
143
201
 
144
202
 
145
203
  // Exact-id targeting always resolves against the full pool so a cross-group id
@@ -159,16 +217,65 @@ export function handleBrokerSend(
159
217
  if (resolution.kind === "resolved") {
160
218
  const target = sessions.get(resolution.session.id);
161
219
  if (!target) {
162
- write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Session not found" });
220
+ write(socket, {
221
+ type: "delivery_failed",
222
+ messageId: message.id,
223
+ attemptId,
224
+ reason: "Session not found",
225
+ ...(requirePendingReply ? { reasonCode: "session_not_found" as const } : {}),
226
+ });
163
227
  return;
164
228
  }
165
229
  if (target.info.id === fromSession.info.id) {
166
230
  write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Cannot message the current session" });
167
231
  return;
168
232
  }
233
+ const targetIdentity = deliveryTargetIdentity(target);
234
+ const deliveredMatch = deliveredMessages.lookupForTarget(message.id, signature, targetIdentity);
235
+ if (requirePendingReply && deliveredMatch === "match") {
236
+ write(socket, { type: "delivered", messageId: message.id, attemptId });
237
+ return;
238
+ }
239
+ if (requirePendingReply && deliveredMatch === "conflict") {
240
+ write(socket, {
241
+ type: "delivery_failed",
242
+ messageId: message.id,
243
+ attemptId,
244
+ reason: `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
245
+ reasonCode: "message_id_conflict",
246
+ });
247
+ return;
248
+ }
249
+ if (requirePendingReply && deliveredMatch === "invalid") {
250
+ write(socket, {
251
+ type: "delivery_failed",
252
+ messageId: message.id,
253
+ attemptId,
254
+ reason: "Intercom accepted-delivery authority is invalid; refusing possible duplicate delivery",
255
+ });
256
+ return;
257
+ }
258
+ if (requirePendingReply && deliveredMatch === "uncertain") {
259
+ write(socket, {
260
+ type: "delivery_failed",
261
+ messageId: message.id,
262
+ attemptId,
263
+ reason: "Intercom cannot prove whether this reserved operation reached its recipient; refusing redelivery",
264
+ });
265
+ return;
266
+ }
169
267
  const correlatedReply =
170
268
  message.replyTo !== undefined &&
171
269
  pendingQuestions.matchesReply(fromSession.info.id, target.info.id, message.replyTo);
270
+ if (requirePendingReply && !correlatedReply) {
271
+ write(socket, {
272
+ type: "delivery_failed",
273
+ messageId: message.id,
274
+ attemptId,
275
+ reason: "Pending question route does not authorize this public reply",
276
+ });
277
+ return;
278
+ }
172
279
  const bypass =
173
280
  supervisorSend ||
174
281
  isVerticalBypass({
@@ -188,14 +295,62 @@ export function handleBrokerSend(
188
295
  });
189
296
  return;
190
297
  }
191
- const liveTarget = parsePendingStageTarget(trimmedTo);
298
+ if (deliveredMatch === "match") {
299
+ if (message.expectsReply === true) {
300
+ const acceptedQuestionTarget = deliveredMessages.lookupQuestionTarget(
301
+ message.id,
302
+ signature,
303
+ senderGroupIdentity(fromSession),
304
+ );
305
+ if (acceptedQuestionTarget === undefined) {
306
+ write(socket, {
307
+ type: "delivery_failed",
308
+ messageId: message.id,
309
+ attemptId,
310
+ reason: "Accepted Intercom question route could not be securely rebound",
311
+ });
312
+ return;
313
+ }
314
+ pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
315
+ }
316
+ write(socket, { type: "delivered", messageId: message.id, attemptId });
317
+ return;
318
+ }
319
+ if (deliveredMatch === "conflict") {
320
+ write(socket, {
321
+ type: "delivery_failed",
322
+ messageId: message.id,
323
+ attemptId,
324
+ reason: `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
325
+ reasonCode: "message_id_conflict",
326
+ });
327
+ return;
328
+ }
329
+ if (deliveredMatch === "invalid") {
330
+ write(socket, {
331
+ type: "delivery_failed",
332
+ messageId: message.id,
333
+ attemptId,
334
+ reason: "Intercom accepted-delivery authority is invalid; refusing possible duplicate delivery",
335
+ });
336
+ return;
337
+ }
338
+ if (deliveredMatch === "uncertain") {
339
+ write(socket, {
340
+ type: "delivery_failed",
341
+ messageId: message.id,
342
+ attemptId,
343
+ reason: "Intercom cannot prove whether this reserved operation reached its recipient; refusing redelivery",
344
+ });
345
+ return;
346
+ }
192
347
  if (
193
348
  liveWorkflowTarget !== undefined &&
194
- liveTarget !== undefined &&
349
+ workflowTarget !== undefined &&
195
350
  routePendingStage?.({
196
351
  socket,
197
352
  from: fromSession,
198
- ...liveTarget,
353
+ target: trimmedTo,
199
354
  message,
200
355
  ...(attemptId ? { attemptId } : {}),
201
356
  liveTargetId: target.info.id,
@@ -204,33 +359,137 @@ export function handleBrokerSend(
204
359
  ) {
205
360
  return;
206
361
  }
207
- write(target.socket, supervisorSend
208
- ? { type: "message", from: fromSession.info, message, channel: "supervisor" }
209
- : { type: "message", from: fromSession.info, message });
210
- deliveredMessages.record(message.id, signature);
211
- if (message.expectsReply === true) {
212
- pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
213
- }
214
- if (message.replyTo !== undefined) {
215
- pendingQuestions.clearReply(fromSession.info.id, target.info.id, message.replyTo);
362
+ const outbound = supervisorSend
363
+ ? ({ type: "message", from: fromSession.info, message, channel: "supervisor" } as const)
364
+ : ({ type: "message", from: fromSession.info, message } as const);
365
+ const failDelivery = (): void => {
366
+ if (reservation === "recorded") deliveredMessages.forget(message.id, signature);
367
+ write(socket, {
368
+ type: "delivery_failed",
369
+ messageId: message.id,
370
+ attemptId,
371
+ reason: "Session not found",
372
+ ...(requirePendingReply ? { reasonCode: "session_not_found" as const } : {}),
373
+ });
374
+ };
375
+ const reservation = message.expectsReply === true
376
+ ? deliveredMessages.reserveQuestion(
377
+ message.id,
378
+ signature,
379
+ {
380
+ targetSessionId: target.info.id,
381
+ senderGroupIdentity: senderGroupIdentity(fromSession),
382
+ },
383
+ Date.now(),
384
+ targetIdentity,
385
+ )
386
+ : deliveredMessages.reserve(message.id, signature, Date.now(), targetIdentity);
387
+ if (reservation === "match") {
388
+ if (
389
+ message.expectsReply === true &&
390
+ deliveredMessages.lookupQuestionTarget(
391
+ message.id,
392
+ signature,
393
+ senderGroupIdentity(fromSession),
394
+ ) === undefined
395
+ ) {
396
+ write(socket, {
397
+ type: "delivery_failed",
398
+ messageId: message.id,
399
+ attemptId,
400
+ reason: "Accepted Intercom question route could not be securely rebound",
401
+ });
402
+ return;
403
+ }
404
+ if (message.expectsReply === true) pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
405
+ write(socket, { type: "delivered", messageId: message.id, attemptId });
406
+ return;
407
+ }
408
+ if (reservation !== "recorded") {
409
+ write(socket, {
410
+ type: "delivery_failed",
411
+ messageId: message.id,
412
+ attemptId,
413
+ reason: reservation === "capacity"
414
+ ? "Intercom accepted-delivery capacity is full; refusing delivery without evicting live retry authority"
415
+ : reservation === "uncertain"
416
+ ? "Intercom cannot prove whether this reserved operation reached its recipient; refusing redelivery"
417
+ : "Intercom accepted-delivery authority changed before forwarding; refusing possible duplicate delivery",
418
+ ...(reservation === "conflict" ? { reasonCode: "message_id_conflict" as const } : {}),
419
+ });
420
+ return;
421
+ }
422
+ const finishDelivery = (): void => {
423
+ let accepted: ReturnType<DeliveredMessageCache["accept"]>;
424
+ try {
425
+ accepted = deliveredMessages.accept(message.id, signature, targetIdentity);
426
+ } catch {
427
+ write(socket, {
428
+ type: "delivery_failed",
429
+ messageId: message.id,
430
+ attemptId,
431
+ reason: "Intercom could not durably confirm the forwarded operation; refusing a possibly unsafe retry",
432
+ });
433
+ return;
434
+ }
435
+ if (accepted !== "accepted") {
436
+ write(socket, {
437
+ type: "delivery_failed",
438
+ messageId: message.id,
439
+ attemptId,
440
+ reason: "Intercom could not durably confirm the forwarded operation; refusing a possibly unsafe retry",
441
+ ...(accepted === "conflict" ? { reasonCode: "message_id_conflict" as const } : {}),
442
+ });
443
+ return;
444
+ }
445
+ if (message.expectsReply === true) {
446
+ pendingQuestions.record(fromSession.info.id, target.info.id, message.id);
447
+ }
448
+ if (message.replyTo !== undefined) {
449
+ pendingQuestions.clearReply(fromSession.info.id, target.info.id, message.replyTo);
450
+ }
451
+ if (supervisorSend) supervisorCache.record(message.id, fromSession.info.id, target.info.id);
452
+ write(socket, { type: "delivered", messageId: message.id, attemptId });
453
+ };
454
+ if (writeConfirmed !== undefined) {
455
+ writeConfirmed(target.socket, outbound, (written) => {
456
+ if (written) finishDelivery();
457
+ else failDelivery();
458
+ });
459
+ return;
216
460
  }
217
- if (supervisorSend) supervisorCache.record(message.id, fromSession.info.id, target.info.id);
218
- write(socket, { type: "delivered", messageId: message.id, attemptId });
219
- return;
220
- }
221
- if (resolution.kind === "not_found" && !supervisorSend && routePendingStage !== undefined) {
222
- const pendingTarget = parsePendingStageTarget(trimmedTo);
223
- if (
224
- pendingTarget !== undefined &&
225
- routePendingStage({ socket, from: fromSession, ...pendingTarget, message, ...(attemptId ? { attemptId } : {}) })
226
- ) {
461
+ if (!write(target.socket, outbound)) {
462
+ // Nothing was written, so the message id and reply authorization remain
463
+ // available for an honest retry.
464
+ failDelivery();
227
465
  return;
228
466
  }
467
+ finishDelivery();
468
+ return;
229
469
  }
470
+ if (
471
+ resolution.kind === "not_found" &&
472
+ !supervisorSend &&
473
+ routePendingStage !== undefined &&
474
+ workflowTarget !== undefined &&
475
+ routePendingStage({
476
+ socket,
477
+ from: fromSession,
478
+ target: trimmedTo,
479
+ message,
480
+ ...(attemptId ? { attemptId } : {}),
481
+ signature,
482
+ })
483
+ ) {
484
+ return;
485
+ }
230
486
  write(socket, {
231
487
  type: "delivery_failed",
232
488
  messageId: message.id,
233
489
  attemptId,
234
490
  reason: sessionTargetFailureReason(clientMessage.to, resolution),
491
+ ...(requirePendingReply && resolution.kind === "not_found"
492
+ ? { reasonCode: "session_not_found" as const }
493
+ : {}),
235
494
  });
236
495
  }
@@ -8,7 +8,9 @@ export interface LogicalSendOptions {
8
8
  replyError?: string;
9
9
  }
10
10
 
11
- function normalizeAttachments(attachments: Attachment[] | undefined): Array<Record<string, string>> | undefined {
11
+ export function canonicalizeAttachmentsForSendSignature(
12
+ attachments: readonly Attachment[] | undefined,
13
+ ): Array<Record<string, string>> | undefined {
12
14
  return attachments?.map((attachment) => ({
13
15
  type: attachment.type,
14
16
  name: attachment.name,
@@ -22,7 +24,7 @@ export function buildSendSignature(to: string, options: LogicalSendOptions): str
22
24
  return JSON.stringify({
23
25
  to,
24
26
  text: options.text,
25
- attachments: normalizeAttachments(options.attachments) ?? [],
27
+ attachments: canonicalizeAttachmentsForSendSignature(options.attachments) ?? [],
26
28
  replyTo: options.replyTo ?? null,
27
29
  expectsReply: options.expectsReply ?? false,
28
30
  replyError: options.replyError ?? null,
@@ -0,0 +1,103 @@
1
+ import type net from "node:net";
2
+ import type { BrokerMessage } from "../types.js";
3
+ import { writeMessage } from "./framing.js";
4
+
5
+ /**
6
+ * Broker-side socket writes.
7
+ *
8
+ * `socket.write()` on a socket whose writable side has already ended does not
9
+ * throw: it returns `false`, hands `ERR_STREAM_WRITE_AFTER_END` to the write
10
+ * callback, **destroys the socket synchronously**, and only then emits
11
+ * `'error'`. A caller-side `try`/`catch` is therefore dead code, and a
12
+ * writability snapshot taken anywhere other than immediately before the write
13
+ * can go stale — the broker stores sender sockets in its pending-acknowledgment
14
+ * maps and writes to them up to ten seconds later.
15
+ *
16
+ * So the check lives *inside* the write. Every broker write goes through
17
+ * `writeMessageIfOpen`, which reports whether the frame was actually handed to
18
+ * the socket. Callers whose next step claims a delivery — recording it in the
19
+ * delivered-message cache, opening a reply authorization, answering
20
+ * `delivered` — must branch on that answer instead of assuming it.
21
+ *
22
+ * `destroyed || writableEnded` is the whole test, matching the pre-existing
23
+ * guards in `broker.ts`. `!socket.writable` is deliberately not part of it: it
24
+ * is `undefined` on the socket doubles the broker unit suites inject, so adding
25
+ * it would classify every fake as dead. Failing open on an unknown shape is the
26
+ * safe direction here — a real ended socket always sets one of the two flags.
27
+ */
28
+
29
+ /** True while `socket` can still accept a frame. */
30
+ export function isSocketOpenForWrite(socket: net.Socket): boolean {
31
+ return !socket.destroyed && !socket.writableEnded;
32
+ }
33
+
34
+ /**
35
+ * Frames the broker writes to a client socket.
36
+ *
37
+ * `BrokerMessage` is the client-validated union. Two frames the broker already
38
+ * emits are absent from it — `sticky_live_delivered`, and `queued` carrying
39
+ * `notInKnownSet` — because `writeMessage` accepts `unknown` and never checked
40
+ * them. Widening the shared union is a separate change, so they are named here
41
+ * rather than typed away with a cast.
42
+ */
43
+ export type BrokerOutboundFrame =
44
+ | BrokerMessage
45
+ | {
46
+ type: "sticky_live_delivered";
47
+ runId: string;
48
+ messageId: string;
49
+ target: string;
50
+ deliveredTargets: string[];
51
+ }
52
+ | {
53
+ type: "queued";
54
+ messageId: string;
55
+ attemptId?: string;
56
+ target: string;
57
+ position: number;
58
+ notInKnownSet?: true;
59
+ };
60
+
61
+ /**
62
+ * Write one broker frame unless the socket's writable side is already gone.
63
+ *
64
+ * Returns `true` only when the frame was handed to the socket. A `false` answer
65
+ * means nothing was written and nothing may be recorded as delivered; it never
66
+ * means the peer acknowledged anything. The converse under-approximates on
67
+ * purpose: a `true` answer is not proof of receipt either, which is why the
68
+ * sender-side send timeout remains the terminal authority.
69
+ */
70
+ export function writeMessageIfOpen(socket: net.Socket, message: BrokerOutboundFrame): boolean {
71
+ if (!isSocketOpenForWrite(socket)) return false;
72
+ writeMessage(socket, message);
73
+ return true;
74
+ }
75
+
76
+ /**
77
+ * Write one broker frame and report the socket write callback's outcome.
78
+ *
79
+ * Delivery-producing paths use this instead of treating `socket.write()`'s
80
+ * synchronous return as success. That return only reports backpressure; an
81
+ * immediate peer reset is reported asynchronously to the callback.
82
+ */
83
+ export function writeMessageWithOutcome(
84
+ socket: net.Socket,
85
+ message: BrokerOutboundFrame,
86
+ onSettled: (written: boolean) => void,
87
+ ): void {
88
+ if (!isSocketOpenForWrite(socket)) {
89
+ onSettled(false);
90
+ return;
91
+ }
92
+ let settled = false;
93
+ const finish = (written: boolean): void => {
94
+ if (settled) return;
95
+ settled = true;
96
+ onSettled(written);
97
+ };
98
+ try {
99
+ writeMessage(socket, message, (error) => finish(error == null));
100
+ } catch {
101
+ finish(false);
102
+ }
103
+ }
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "child_process";
2
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
2
+ import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, unlinkSync, writeFileSync } from "fs";
3
3
  import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { createRequire } from "module";
@@ -37,6 +37,11 @@ const BROKER_PID = getBrokerPidPath();
37
37
  const BROKER_SPAWN_LOCK = getBrokerSpawnLockPath();
38
38
  const BROKER_LOG = getBrokerLogPath();
39
39
 
40
+ function ensurePrivateDirectory(path: string): void {
41
+ mkdirSync(path, { recursive: true, mode: 0o700 });
42
+ if (process.platform !== "win32") chmodSync(path, 0o700);
43
+ }
44
+
40
45
  /**
41
46
  * How much of the broker startup log is read back into an error message.
42
47
  *
@@ -182,7 +187,7 @@ function writeWindowsHiddenLauncher(
182
187
  commandLine: string,
183
188
  launcherPath: string = getWindowsHiddenLauncherPath(),
184
189
  ): string {
185
- mkdirSync(dirname(launcherPath), { recursive: true });
190
+ ensurePrivateDirectory(dirname(launcherPath));
186
191
  writeFileSync(launcherPath, getWindowsHiddenLauncherScript(commandLine), "utf-8");
187
192
  return launcherPath;
188
193
  }
@@ -254,7 +259,7 @@ export function getBrokerSpawnOptions(
254
259
 
255
260
  /** Truncate (or create) the broker log so each spawn starts from a bounded, current file. */
256
261
  function resetBrokerLog(logPath: string = BROKER_LOG): void {
257
- mkdirSync(dirname(logPath), { recursive: true });
262
+ ensurePrivateDirectory(dirname(logPath));
258
263
  closeSync(openSync(logPath, "w"));
259
264
  }
260
265
 
@@ -295,7 +300,7 @@ function toError(error: unknown): Error {
295
300
  }
296
301
 
297
302
  export async function spawnBrokerIfNeeded(brokerCommand: string, brokerArgs: string[]): Promise<void> {
298
- mkdirSync(INTERCOM_DIR, { recursive: true });
303
+ ensurePrivateDirectory(INTERCOM_DIR);
299
304
 
300
305
  if (await isBrokerRunning()) {
301
306
  return;