@bastani/atomic 0.9.5-alpha.8 → 0.9.5

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 (530) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/README.md +2 -2
  3. package/dist/builtin/cursor/CHANGELOG.md +12 -0
  4. package/dist/builtin/cursor/package.json +3 -3
  5. package/dist/builtin/cursor/src/model-mapper.ts +6 -3
  6. package/dist/builtin/intercom/CHANGELOG.md +37 -0
  7. package/dist/builtin/intercom/README.md +4 -2
  8. package/dist/builtin/intercom/broker/broker.ts +6 -114
  9. package/dist/builtin/intercom/broker/client.ts +29 -54
  10. package/dist/builtin/intercom/broker/delivered-message-cache.ts +44 -0
  11. package/dist/builtin/intercom/broker/pending-send-registry.ts +142 -0
  12. package/dist/builtin/intercom/broker/send-handler.ts +106 -0
  13. package/dist/builtin/intercom/broker/send-signature.ts +37 -0
  14. package/dist/builtin/intercom/foreground-detach-handoff.ts +136 -0
  15. package/dist/builtin/intercom/index-heavy.ts +49 -53
  16. package/dist/builtin/intercom/index.ts +278 -262
  17. package/dist/builtin/intercom/lazy-heavy-proxy.ts +114 -0
  18. package/dist/builtin/intercom/lazy-subagent-ack.ts +20 -0
  19. package/dist/builtin/intercom/lazy-tool-execution.ts +39 -0
  20. package/dist/builtin/intercom/lifecycle-lease.ts +51 -0
  21. package/dist/builtin/intercom/lifecycle.ts +37 -20
  22. package/dist/builtin/intercom/package.json +3 -6
  23. package/dist/builtin/intercom/reply-routing.ts +17 -0
  24. package/dist/builtin/intercom/subagent-relay.ts +58 -12
  25. package/dist/builtin/intercom/types.ts +3 -3
  26. package/dist/builtin/mcp/CHANGELOG.md +38 -0
  27. package/dist/builtin/mcp/OAUTH.md +1 -0
  28. package/dist/builtin/mcp/README.md +10 -6
  29. package/dist/builtin/mcp/apps-cancellation.ts +32 -0
  30. package/dist/builtin/mcp/call-tool-result.ts +9 -0
  31. package/dist/builtin/mcp/caller-wait.ts +50 -0
  32. package/dist/builtin/mcp/command-registration.ts +82 -0
  33. package/dist/builtin/mcp/direct-tool-executor.ts +279 -0
  34. package/dist/builtin/mcp/direct-tools.ts +40 -255
  35. package/dist/builtin/mcp/host-html-template.ts +4 -2
  36. package/dist/builtin/mcp/index.ts +274 -239
  37. package/dist/builtin/mcp/init.ts +96 -126
  38. package/dist/builtin/mcp/mcp-auth-flow.ts +247 -237
  39. package/dist/builtin/mcp/mcp-callback-server.ts +89 -68
  40. package/dist/builtin/mcp/mcp-oauth-provider.ts +23 -1
  41. package/dist/builtin/mcp/metadata-hydration.ts +52 -0
  42. package/dist/builtin/mcp/package.json +3 -3
  43. package/dist/builtin/mcp/proxy-auth.ts +4 -4
  44. package/dist/builtin/mcp/proxy-call.ts +111 -40
  45. package/dist/builtin/mcp/proxy-connect.ts +35 -15
  46. package/dist/builtin/mcp/proxy-info-modes.ts +106 -23
  47. package/dist/builtin/mcp/proxy-modes.ts +3 -3
  48. package/dist/builtin/mcp/session-cleanup-barrier.ts +43 -0
  49. package/dist/builtin/mcp/startup-warmup.ts +98 -0
  50. package/dist/builtin/mcp/state-lease.ts +12 -0
  51. package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
  52. package/dist/builtin/mcp/ui-server.ts +8 -8
  53. package/dist/builtin/mcp/ui-session.ts +9 -18
  54. package/dist/builtin/subagents/CHANGELOG.md +78 -0
  55. package/dist/builtin/subagents/README.md +30 -29
  56. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  57. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  58. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  59. package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
  60. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  61. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  62. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  63. package/dist/builtin/subagents/agents/debugger.md +2 -2
  64. package/dist/builtin/subagents/agents/worker.md +2 -2
  65. package/dist/builtin/subagents/package.json +5 -5
  66. package/dist/builtin/subagents/prompts/review-loop.md +1 -1
  67. package/dist/builtin/subagents/skills/subagent/SKILL.md +37 -36
  68. package/dist/builtin/subagents/src/extension/api-lifecycle.ts +64 -0
  69. package/dist/builtin/subagents/src/extension/fanout-child.ts +44 -51
  70. package/dist/builtin/subagents/src/extension/index.ts +289 -320
  71. package/dist/builtin/subagents/src/extension/prompt-guidance.ts +7 -10
  72. package/dist/builtin/subagents/src/extension/schemas.ts +22 -5
  73. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
  74. package/dist/builtin/subagents/src/extension/tool-description.ts +29 -0
  75. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +13 -2
  76. package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
  77. package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +106 -0
  78. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +26 -20
  79. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +13 -2
  80. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +3 -0
  81. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +1 -1
  82. package/dist/builtin/subagents/src/runs/background/completion-claims.ts +189 -0
  83. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +44 -7
  84. package/dist/builtin/subagents/src/runs/background/completion-notification.ts +34 -0
  85. package/dist/builtin/subagents/src/runs/background/notify.ts +72 -22
  86. package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +232 -0
  87. package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +151 -0
  88. package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +72 -0
  89. package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +48 -0
  90. package/dist/builtin/subagents/src/runs/background/result-status.ts +81 -0
  91. package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +59 -0
  92. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +177 -233
  93. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +3 -2
  94. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +73 -14
  95. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +29 -15
  96. package/dist/builtin/subagents/src/runs/background/top-level-async.ts +1 -2
  97. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
  98. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
  99. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
  100. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +7 -9
  101. package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +3 -13
  102. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +2 -15
  103. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +2 -2
  104. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +63 -46
  105. package/dist/builtin/subagents/src/runs/foreground/execution-detach-reservations.ts +48 -0
  106. package/dist/builtin/subagents/src/runs/foreground/execution-detach-route.ts +17 -0
  107. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +39 -26
  108. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +3 -1
  109. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +3 -51
  110. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +6 -11
  111. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +11 -2
  112. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
  113. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +5 -112
  114. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +2 -0
  115. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +68 -118
  116. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +79 -7
  117. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +2 -2
  118. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +3 -0
  119. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -2
  120. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +55 -12
  121. package/dist/builtin/subagents/src/shared/artifacts.ts +22 -11
  122. package/dist/builtin/subagents/src/shared/event-jsonl-writer.ts +294 -0
  123. package/dist/builtin/subagents/src/shared/exclusive-file-publication.ts +44 -0
  124. package/dist/builtin/subagents/src/shared/jsonl-writer.ts +1 -0
  125. package/dist/builtin/subagents/src/shared/model-info.ts +2 -2
  126. package/dist/builtin/subagents/src/shared/settings.ts +20 -10
  127. package/dist/builtin/subagents/src/shared/types-async.ts +3 -1
  128. package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
  129. package/dist/builtin/subagents/src/shared/types-runtime.ts +3 -2
  130. package/dist/builtin/subagents/src/slash/slash-commands.ts +9 -12
  131. package/dist/builtin/subagents/src/slash/slash-live-state.ts +63 -32
  132. package/dist/builtin/subagents/src/tui/render-result.ts +7 -0
  133. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -0
  134. package/dist/builtin/subagents/src/tui/render-widget.ts +157 -89
  135. package/dist/builtin/web-access/CHANGELOG.md +28 -0
  136. package/dist/builtin/web-access/README.md +4 -0
  137. package/dist/builtin/web-access/content-tools.ts +8 -3
  138. package/dist/builtin/web-access/index-heavy.ts +1 -1
  139. package/dist/builtin/web-access/index.ts +190 -46
  140. package/dist/builtin/web-access/lifecycle-lease.ts +38 -0
  141. package/dist/builtin/web-access/package.json +2 -2
  142. package/dist/builtin/web-access/result-renderers.ts +1 -1
  143. package/dist/builtin/web-access/summary-review.ts +1 -1
  144. package/dist/builtin/web-access/web-search-activity.ts +1 -1
  145. package/dist/builtin/web-access/web-search-return.ts +7 -0
  146. package/dist/builtin/web-access/web-search-summary.ts +1 -1
  147. package/dist/builtin/web-access/web-search-tool.ts +4 -2
  148. package/dist/builtin/workflows/CHANGELOG.md +86 -0
  149. package/dist/builtin/workflows/README.md +5 -5
  150. package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +27 -4
  151. package/dist/builtin/workflows/builtin/goal-runner.ts +86 -27
  152. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +10 -0
  153. package/dist/builtin/workflows/builtin/ralph-core.ts +9 -9
  154. package/dist/builtin/workflows/builtin/ralph-models.ts +71 -36
  155. package/dist/builtin/workflows/builtin/ralph-runner.ts +20 -31
  156. package/dist/builtin/workflows/package.json +2 -2
  157. package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
  158. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
  159. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
  160. package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
  161. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
  162. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
  163. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
  164. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
  165. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
  166. package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
  167. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
  168. package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
  169. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
  170. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
  171. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
  172. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
  173. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
  174. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
  175. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
  176. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
  177. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
  178. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
  179. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
  180. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
  181. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  182. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
  183. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
  184. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
  185. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
  186. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
  187. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
  188. package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
  189. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
  190. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
  191. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
  192. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
  193. package/dist/builtin/workflows/src/durable/backend.ts +12 -0
  194. package/dist/builtin/workflows/src/durable/dbos-backend.ts +17 -1
  195. package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
  196. package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
  197. package/dist/builtin/workflows/src/durable/stage-primitive.ts +2 -1
  198. package/dist/builtin/workflows/src/durable/types.ts +16 -0
  199. package/dist/builtin/workflows/src/engine/primitives/task.ts +3 -1
  200. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
  201. package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
  202. package/dist/builtin/workflows/src/engine/run.ts +20 -19
  203. package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
  204. package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
  205. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
  206. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
  207. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
  208. package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
  209. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
  210. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
  211. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
  212. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +17 -22
  213. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
  214. package/dist/builtin/workflows/src/extension/workflow-schema.ts +15 -2
  215. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
  216. package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
  217. package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
  218. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
  219. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +1 -1
  220. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +3 -2
  221. package/dist/builtin/workflows/src/runs/shared/model-fallback-candidates.ts +1 -1
  222. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
  223. package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
  224. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
  225. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -2
  226. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +2 -2
  227. package/dist/builtin/workflows/src/shared/resume-continuation.ts +2 -0
  228. package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
  229. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
  230. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
  231. package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
  232. package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
  233. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +1 -23
  234. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +54 -9
  235. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +5 -2
  236. package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
  237. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
  238. package/dist/cli/args.d.ts.map +1 -1
  239. package/dist/cli/args.js +2 -2
  240. package/dist/cli/args.js.map +1 -1
  241. package/dist/core/agent-session-auto-compaction.d.ts +23 -3
  242. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  243. package/dist/core/agent-session-auto-compaction.js +129 -17
  244. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  245. package/dist/core/agent-session-events.d.ts.map +1 -1
  246. package/dist/core/agent-session-events.js +9 -1
  247. package/dist/core/agent-session-events.js.map +1 -1
  248. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  249. package/dist/core/agent-session-message-queue.js +1 -1
  250. package/dist/core/agent-session-message-queue.js.map +1 -1
  251. package/dist/core/agent-session-methods.d.ts +6 -3
  252. package/dist/core/agent-session-methods.d.ts.map +1 -1
  253. package/dist/core/agent-session-methods.js.map +1 -1
  254. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  255. package/dist/core/agent-session-prompt.js +2 -2
  256. package/dist/core/agent-session-prompt.js.map +1 -1
  257. package/dist/core/agent-session-retry.d.ts.map +1 -1
  258. package/dist/core/agent-session-retry.js +1 -1
  259. package/dist/core/agent-session-retry.js.map +1 -1
  260. package/dist/core/agent-session-services.d.ts.map +1 -1
  261. package/dist/core/agent-session-services.js +3 -2
  262. package/dist/core/agent-session-services.js.map +1 -1
  263. package/dist/core/agent-session-types.d.ts.map +1 -1
  264. package/dist/core/agent-session-types.js +1 -1
  265. package/dist/core/agent-session-types.js.map +1 -1
  266. package/dist/core/agent-session.d.ts +4 -2
  267. package/dist/core/agent-session.d.ts.map +1 -1
  268. package/dist/core/agent-session.js +4 -2
  269. package/dist/core/agent-session.js.map +1 -1
  270. package/dist/core/anthropic-thinking-guard.d.ts +5 -5
  271. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  272. package/dist/core/anthropic-thinking-guard.js +42 -32
  273. package/dist/core/anthropic-thinking-guard.js.map +1 -1
  274. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  275. package/dist/core/atomic-guide-command.js +15 -15
  276. package/dist/core/atomic-guide-command.js.map +1 -1
  277. package/dist/core/auth-storage.d.ts.map +1 -1
  278. package/dist/core/auth-storage.js +14 -9
  279. package/dist/core/auth-storage.js.map +1 -1
  280. package/dist/core/compaction/compaction.d.ts +2 -0
  281. package/dist/core/compaction/compaction.d.ts.map +1 -1
  282. package/dist/core/compaction/compaction.js +79 -46
  283. package/dist/core/compaction/compaction.js.map +1 -1
  284. package/dist/core/compaction/context-assistant-turns.d.ts +42 -0
  285. package/dist/core/compaction/context-assistant-turns.d.ts.map +1 -0
  286. package/dist/core/compaction/context-assistant-turns.js +87 -0
  287. package/dist/core/compaction/context-assistant-turns.js.map +1 -0
  288. package/dist/core/compaction/context-compaction-critical.d.ts +1 -1
  289. package/dist/core/compaction/context-compaction-critical.d.ts.map +1 -1
  290. package/dist/core/compaction/context-compaction-critical.js +2 -2
  291. package/dist/core/compaction/context-compaction-critical.js.map +1 -1
  292. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts +18 -0
  293. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts.map +1 -0
  294. package/dist/core/compaction/context-compaction-eviction-alternates.js +186 -0
  295. package/dist/core/compaction/context-compaction-eviction-alternates.js.map +1 -0
  296. package/dist/core/compaction/context-compaction-eviction.d.ts +10 -2
  297. package/dist/core/compaction/context-compaction-eviction.d.ts.map +1 -1
  298. package/dist/core/compaction/context-compaction-eviction.js +196 -146
  299. package/dist/core/compaction/context-compaction-eviction.js.map +1 -1
  300. package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
  301. package/dist/core/compaction/context-compaction-prompt.js +1 -1
  302. package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
  303. package/dist/core/compaction/context-compaction-types.d.ts +2 -0
  304. package/dist/core/compaction/context-compaction-types.d.ts.map +1 -1
  305. package/dist/core/compaction/context-compaction-types.js.map +1 -1
  306. package/dist/core/compaction/context-deletion-application.d.ts +5 -4
  307. package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
  308. package/dist/core/compaction/context-deletion-application.js +17 -15
  309. package/dist/core/compaction/context-deletion-application.js.map +1 -1
  310. package/dist/core/compaction/context-deletion-store.d.ts.map +1 -1
  311. package/dist/core/compaction/context-deletion-store.js +1 -1
  312. package/dist/core/compaction/context-deletion-store.js.map +1 -1
  313. package/dist/core/compaction/context-deletion-targets.d.ts +2 -1
  314. package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
  315. package/dist/core/compaction/context-deletion-targets.js +36 -31
  316. package/dist/core/compaction/context-deletion-targets.js.map +1 -1
  317. package/dist/core/compaction/context-deletion-tool-helpers.d.ts.map +1 -1
  318. package/dist/core/compaction/context-deletion-tool-helpers.js +116 -43
  319. package/dist/core/compaction/context-deletion-tool-helpers.js.map +1 -1
  320. package/dist/core/compaction/context-transcript-analysis.d.ts +1 -2
  321. package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
  322. package/dist/core/compaction/context-transcript-analysis.js +33 -37
  323. package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
  324. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  325. package/dist/core/copilot-model-synthesis.js +3 -1
  326. package/dist/core/copilot-model-synthesis.js.map +1 -1
  327. package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
  328. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  329. package/dist/core/extensions/loader-virtual-modules.js +2 -1
  330. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  331. package/dist/core/extensions/provider-types.d.ts +2 -7
  332. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  333. package/dist/core/extensions/provider-types.js.map +1 -1
  334. package/dist/core/footer-data-provider.d.ts +6 -0
  335. package/dist/core/footer-data-provider.d.ts.map +1 -1
  336. package/dist/core/footer-data-provider.js +72 -29
  337. package/dist/core/footer-data-provider.js.map +1 -1
  338. package/dist/core/messages.d.ts +9 -0
  339. package/dist/core/messages.d.ts.map +1 -1
  340. package/dist/core/messages.js +100 -18
  341. package/dist/core/messages.js.map +1 -1
  342. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  343. package/dist/core/model-registry-builtins.js +5 -0
  344. package/dist/core/model-registry-builtins.js.map +1 -1
  345. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  346. package/dist/core/model-registry-custom-loader.js +49 -8
  347. package/dist/core/model-registry-custom-loader.js.map +1 -1
  348. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  349. package/dist/core/model-registry-dynamic.js +11 -5
  350. package/dist/core/model-registry-dynamic.js.map +1 -1
  351. package/dist/core/model-registry-loader.d.ts.map +1 -1
  352. package/dist/core/model-registry-loader.js +8 -0
  353. package/dist/core/model-registry-loader.js.map +1 -1
  354. package/dist/core/model-registry-schemas.d.ts +93 -8
  355. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  356. package/dist/core/model-registry-schemas.js +24 -12
  357. package/dist/core/model-registry-schemas.js.map +1 -1
  358. package/dist/core/model-registry-types.d.ts +7 -6
  359. package/dist/core/model-registry-types.d.ts.map +1 -1
  360. package/dist/core/model-registry-types.js.map +1 -1
  361. package/dist/core/model-registry.d.ts +5 -0
  362. package/dist/core/model-registry.d.ts.map +1 -1
  363. package/dist/core/model-registry.js +23 -0
  364. package/dist/core/model-registry.js.map +1 -1
  365. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  366. package/dist/core/model-resolver-cli.js +33 -5
  367. package/dist/core/model-resolver-cli.js.map +1 -1
  368. package/dist/core/model-resolver-initial.d.ts +6 -1
  369. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  370. package/dist/core/model-resolver-initial.js +12 -7
  371. package/dist/core/model-resolver-initial.js.map +1 -1
  372. package/dist/core/model-resolver.d.ts +1 -1
  373. package/dist/core/model-resolver.d.ts.map +1 -1
  374. package/dist/core/model-resolver.js +1 -1
  375. package/dist/core/model-resolver.js.map +1 -1
  376. package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
  377. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  378. package/dist/core/openai-responses-payload-sanitizer.js +24 -9
  379. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  380. package/dist/core/resource-loader-context-files.d.ts +1 -0
  381. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  382. package/dist/core/resource-loader-context-files.js +13 -10
  383. package/dist/core/resource-loader-context-files.js.map +1 -1
  384. package/dist/core/sdk.d.ts.map +1 -1
  385. package/dist/core/sdk.js +2 -2
  386. package/dist/core/sdk.js.map +1 -1
  387. package/dist/core/session-entry-normalization.d.ts +7 -0
  388. package/dist/core/session-entry-normalization.d.ts.map +1 -0
  389. package/dist/core/session-entry-normalization.js +14 -0
  390. package/dist/core/session-entry-normalization.js.map +1 -0
  391. package/dist/core/session-manager-core.d.ts.map +1 -1
  392. package/dist/core/session-manager-core.js +1 -0
  393. package/dist/core/session-manager-core.js.map +1 -1
  394. package/dist/core/session-manager-history.d.ts +2 -2
  395. package/dist/core/session-manager-history.d.ts.map +1 -1
  396. package/dist/core/session-manager-history.js +127 -108
  397. package/dist/core/session-manager-history.js.map +1 -1
  398. package/dist/core/settings-manager-basic-accessors.d.ts +2 -2
  399. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  400. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  401. package/dist/core/settings-types.d.ts +1 -1
  402. package/dist/core/settings-types.d.ts.map +1 -1
  403. package/dist/core/settings-types.js.map +1 -1
  404. package/dist/core/slash-commands.d.ts +2 -0
  405. package/dist/core/slash-commands.d.ts.map +1 -1
  406. package/dist/core/slash-commands.js +141 -0
  407. package/dist/core/slash-commands.js.map +1 -1
  408. package/dist/core/tools/bash.d.ts.map +1 -1
  409. package/dist/core/tools/bash.js +11 -8
  410. package/dist/core/tools/bash.js.map +1 -1
  411. package/dist/index.d.ts +1 -0
  412. package/dist/index.d.ts.map +1 -1
  413. package/dist/index.js +1 -0
  414. package/dist/index.js.map +1 -1
  415. package/dist/main-deferred-startup.d.ts.map +1 -1
  416. package/dist/main-deferred-startup.js +2 -2
  417. package/dist/main-deferred-startup.js.map +1 -1
  418. package/dist/main-early-input.d.ts.map +1 -1
  419. package/dist/main-early-input.js +7 -0
  420. package/dist/main-early-input.js.map +1 -1
  421. package/dist/main-session.d.ts.map +1 -1
  422. package/dist/main-session.js +1 -0
  423. package/dist/main-session.js.map +1 -1
  424. package/dist/main.d.ts.map +1 -1
  425. package/dist/main.js +1 -1
  426. package/dist/main.js.map +1 -1
  427. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
  429. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  430. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  431. package/dist/modes/interactive/components/settings-selector-options.js +3 -1
  432. package/dist/modes/interactive/components/settings-selector-options.js.map +1 -1
  433. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  434. package/dist/modes/interactive/components/thinking-selector.js +2 -1
  435. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  436. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  437. package/dist/modes/interactive/interactive-agent-events.js +3 -1
  438. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  439. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  440. package/dist/modes/interactive/interactive-autocomplete.js +21 -9
  441. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  442. package/dist/modes/interactive/interactive-child-ordering.d.ts +7 -0
  443. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -0
  444. package/dist/modes/interactive/interactive-child-ordering.js +26 -0
  445. package/dist/modes/interactive/interactive-child-ordering.js.map +1 -0
  446. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  447. package/dist/modes/interactive/interactive-deferred-startup.js +43 -30
  448. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  449. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  450. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
  451. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  452. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  453. package/dist/modes/interactive/interactive-input-handling.js +41 -6
  454. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  455. package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
  456. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  457. package/dist/modes/interactive/interactive-mode-base.js +3 -0
  458. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  459. package/dist/modes/interactive/interactive-mode-deps.d.ts +2 -2
  460. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  461. package/dist/modes/interactive/interactive-mode-deps.js +2 -2
  462. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  463. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -2
  464. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  465. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  466. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  467. package/dist/modes/interactive/interactive-model-routing.js +11 -4
  468. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  469. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  470. package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
  471. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  472. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  473. package/dist/modes/interactive/interactive-render-chat.js +15 -1
  474. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  475. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  476. package/dist/modes/interactive/interactive-session-routing.js +5 -1
  477. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  478. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  479. package/dist/modes/interactive/interactive-startup.js +20 -21
  480. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  481. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  482. package/dist/modes/interactive/theme/global-theme.js +26 -2
  483. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  484. package/dist/modes/interactive/theme/theme-class.d.ts +1 -1
  485. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  486. package/dist/modes/interactive/theme/theme-class.js +2 -0
  487. package/dist/modes/interactive/theme/theme-class.js.map +1 -1
  488. package/dist/utils/clipboard-image.d.ts.map +1 -1
  489. package/dist/utils/clipboard-image.js +3 -0
  490. package/dist/utils/clipboard-image.js.map +1 -1
  491. package/dist/utils/fs-watch.d.ts +24 -1
  492. package/dist/utils/fs-watch.d.ts.map +1 -1
  493. package/dist/utils/fs-watch.js +60 -5
  494. package/dist/utils/fs-watch.js.map +1 -1
  495. package/docs/changelog.mdx +19 -0
  496. package/docs/compaction.md +60 -49
  497. package/docs/custom-provider.md +18 -5
  498. package/docs/development.md +4 -0
  499. package/docs/extensions.md +11 -2
  500. package/docs/json.md +1 -1
  501. package/docs/models.md +69 -10
  502. package/docs/providers.md +1 -1
  503. package/docs/quickstart.md +12 -8
  504. package/docs/rpc.md +2 -2
  505. package/docs/sdk.md +1 -1
  506. package/docs/settings.md +4 -4
  507. package/docs/subagents.md +40 -5
  508. package/docs/tools.md +1 -1
  509. package/docs/usage.md +4 -2
  510. package/docs/windows.md +4 -0
  511. package/docs/workflows.md +33 -48
  512. package/examples/extensions/preset.ts +2 -2
  513. package/examples/extensions/subagent/README.md +2 -2
  514. package/examples/extensions/subagent/index.ts +1 -2
  515. package/examples/extensions/subagent/schemas.ts +4 -1
  516. package/examples/sdk/12-full-control.ts +1 -1
  517. package/npm-shrinkwrap.json +48 -51
  518. package/package.json +7 -7
  519. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-behavior.ts +0 -75
  520. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-component.ts +0 -202
  521. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-edit.ts +0 -97
  522. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-editor.ts +0 -160
  523. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-frame.ts +0 -72
  524. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-modes.ts +0 -161
  525. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-selectors.ts +0 -203
  526. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-selectors.ts +0 -234
  527. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-state.ts +0 -103
  528. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-types.ts +0 -29
  529. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +0 -9
  530. package/dist/builtin/subagents/src/runs/foreground/chain-execution-clarify.ts +0 -117
@@ -4,6 +4,7 @@ import { randomUUID } from "crypto";
4
4
  import { writeMessage, createMessageReader } from "./framing.js";
5
5
  import { getBrokerSocketPath } from "./paths.js";
6
6
  import type { SessionInfo, Message, Attachment } from "../types.js";
7
+ import { buildSendSignature, PendingSendRegistry } from "./pending-send-registry.js";
7
8
 
8
9
  const BROKER_SOCKET = getBrokerSocketPath();
9
10
 
@@ -107,19 +108,14 @@ function isSessionInfo(value: unknown): value is SessionInfo {
107
108
  export class IntercomClient extends EventEmitter {
108
109
  private socket: net.Socket | null = null;
109
110
  private _sessionId: string | null = null;
110
- private pendingSends = new Map<string, { resolve: (r: SendResult) => void; reject: (e: Error) => void }>();
111
+ private pendingSends = new PendingSendRegistry();
111
112
  private pendingLists = new Map<string, { resolve: (sessions: SessionInfo[]) => void; reject: (e: Error) => void }>();
112
113
  private disconnecting = false;
113
114
  private disconnectError: Error | null = null;
114
115
 
115
116
  private failPending(error: Error): void {
116
- for (const pending of this.pendingSends.values()) {
117
- pending.reject(error);
118
- }
119
- this.pendingSends.clear();
120
- for (const pending of this.pendingLists.values()) {
121
- pending.reject(error);
122
- }
117
+ this.pendingSends.rejectAll(error);
118
+ for (const pending of this.pendingLists.values()) pending.reject(error);
123
119
  this.pendingLists.clear();
124
120
  }
125
121
 
@@ -314,31 +310,23 @@ export class IntercomClient extends EventEmitter {
314
310
  break;
315
311
  }
316
312
  case "delivered": {
317
- const { messageId } = brokerMessage;
318
- if (typeof messageId !== "string") {
313
+ const { messageId, attemptId } = brokerMessage;
314
+ if (typeof messageId !== "string" || (attemptId !== undefined && typeof attemptId !== "string")) {
319
315
  throw new Error("Invalid delivered message");
320
316
  }
321
- const pending = this.pendingSends.get(messageId);
322
- if (!pending) {
323
- // Late send responses are harmless once the caller has already timed out.
324
- return;
325
- }
326
- this.pendingSends.delete(messageId);
327
- pending.resolve({ id: messageId, delivered: true });
317
+ const result = { id: messageId, delivered: true } as const;
318
+ if (attemptId === undefined) this.pendingSends.resolveLegacy(messageId, result);
319
+ else this.pendingSends.resolve(messageId, attemptId, result);
328
320
  break;
329
321
  }
330
322
  case "delivery_failed": {
331
- const { messageId, reason } = brokerMessage;
332
- if (typeof messageId !== "string" || typeof reason !== "string") {
323
+ const { messageId, attemptId, reason } = brokerMessage;
324
+ if (typeof messageId !== "string" || (attemptId !== undefined && typeof attemptId !== "string") || typeof reason !== "string") {
333
325
  throw new Error("Invalid delivery_failed message");
334
326
  }
335
- const pending = this.pendingSends.get(messageId);
336
- if (!pending) {
337
- // Late send responses are harmless once the caller has already timed out.
338
- return;
339
- }
340
- this.pendingSends.delete(messageId);
341
- pending.resolve({ id: messageId, delivered: false, reason });
327
+ const result = { id: messageId, delivered: false, reason } as const;
328
+ if (attemptId === undefined) this.pendingSends.resolveLegacy(messageId, result);
329
+ else this.pendingSends.resolve(messageId, attemptId, result);
342
330
  break;
343
331
  }
344
332
  case "session_joined": {
@@ -452,40 +440,27 @@ export class IntercomClient extends EventEmitter {
452
440
  return Promise.reject(toError(error));
453
441
  }
454
442
  const messageId = options.messageId ?? randomUUID();
443
+ let acquired;
444
+ try {
445
+ acquired = this.pendingSends.acquire(messageId, buildSendSignature(to, options), 10000);
446
+ } catch (error) {
447
+ return Promise.reject(toError(error));
448
+ }
449
+ if (!acquired.owner) return acquired.attempt.promise;
450
+
455
451
  const message: Message = {
456
452
  id: messageId,
457
453
  timestamp: Date.now(),
458
454
  replyTo: options.replyTo,
459
455
  expectsReply: options.expectsReply,
460
- content: {
461
- text: options.text,
462
- attachments: options.attachments,
463
- },
456
+ content: { text: options.text, attachments: options.attachments },
464
457
  };
465
- return new Promise((resolve, reject) => {
466
- const wrappedResolve = (result: SendResult) => {
467
- clearTimeout(timeout);
468
- resolve(result);
469
- };
470
- const wrappedReject = (error: Error) => {
471
- clearTimeout(timeout);
472
- reject(error);
473
- };
474
- const timeout = setTimeout(() => {
475
- if (this.pendingSends.has(messageId)) {
476
- this.pendingSends.delete(messageId);
477
- wrappedReject(new Error("Send timeout"));
478
- }
479
- }, 10000);
480
- this.pendingSends.set(messageId, { resolve: wrappedResolve, reject: wrappedReject });
481
- try {
482
- writeMessage(socket, { type: "send", to, message });
483
- } catch (error) {
484
- clearTimeout(timeout);
485
- this.pendingSends.delete(messageId);
486
- reject(toError(error));
487
- }
488
- });
458
+ try {
459
+ writeMessage(socket, { type: "send", to, message, attemptId: acquired.attempt.attemptId });
460
+ } catch (error) {
461
+ this.pendingSends.reject(acquired.attempt, toError(error));
462
+ }
463
+ return acquired.attempt.promise;
489
464
  }
490
465
  updatePresence(updates: { name?: string; status?: string; model?: string }): void {
491
466
  if (this.disconnecting) {
@@ -0,0 +1,44 @@
1
+ const DEFAULT_TTL_MS = 10 * 60 * 1000;
2
+ const DEFAULT_MAX_ENTRIES = 10_000;
3
+
4
+ export type DeliveredMessageMatch = "miss" | "match" | "conflict";
5
+
6
+ interface DeliveredMessage {
7
+ deliveredAt: number;
8
+ signature: string;
9
+ }
10
+
11
+ /** Bounded successful-delivery cache used to make broker and local retries idempotent. */
12
+ export class DeliveredMessageCache {
13
+ private readonly delivered = new Map<string, DeliveredMessage>();
14
+
15
+ constructor(
16
+ private readonly ttlMs = DEFAULT_TTL_MS,
17
+ private readonly maxEntries = DEFAULT_MAX_ENTRIES,
18
+ ) {}
19
+
20
+ lookup(messageId: string, signature: string, now = Date.now()): DeliveredMessageMatch {
21
+ this.prune(now);
22
+ const delivered = this.delivered.get(messageId);
23
+ if (!delivered || now - delivered.deliveredAt > this.ttlMs) return "miss";
24
+ return delivered.signature === signature ? "match" : "conflict";
25
+ }
26
+
27
+ record(messageId: string, signature: string, now = Date.now()): void {
28
+ this.prune(now);
29
+ this.delivered.delete(messageId);
30
+ this.delivered.set(messageId, { deliveredAt: now, signature });
31
+ while (this.delivered.size > this.maxEntries) {
32
+ const oldest = this.delivered.keys().next().value as string | undefined;
33
+ if (oldest === undefined) break;
34
+ this.delivered.delete(oldest);
35
+ }
36
+ }
37
+
38
+ private prune(now: number): void {
39
+ for (const [messageId, delivered] of this.delivered) {
40
+ if (now - delivered.deliveredAt <= this.ttlMs) break;
41
+ this.delivered.delete(messageId);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,142 @@
1
+ import { randomUUID } from "crypto";
2
+ import type { LogicalSendOptions } from "./send-signature.js";
3
+ export { buildSendSignature } from "./send-signature.js";
4
+
5
+ const DEFAULT_GENERATION_TTL_MS = 10 * 60 * 1000;
6
+ const DEFAULT_MAX_GENERATIONS = 10_000;
7
+
8
+ export interface SendOptionsLike extends LogicalSendOptions {}
9
+
10
+ export interface SendResultLike {
11
+ id: string;
12
+ delivered: boolean;
13
+ reason?: string;
14
+ }
15
+
16
+ export interface PendingSendAttempt {
17
+ readonly messageId: string;
18
+ readonly attemptId: string;
19
+ readonly signature: string;
20
+ readonly promise: Promise<SendResultLike>;
21
+ }
22
+
23
+ interface OwnedPendingSend extends PendingSendAttempt {
24
+ legacyEligible: boolean;
25
+ generation: number;
26
+ resolve(result: SendResultLike): void;
27
+ reject(error: Error): void;
28
+ timer?: ReturnType<typeof setTimeout>;
29
+ }
30
+
31
+ export interface PendingSendAcquisition {
32
+ attempt: PendingSendAttempt;
33
+ owner: boolean;
34
+ }
35
+
36
+ interface SendGeneration {
37
+ generation: number;
38
+ touchedAt: number;
39
+ }
40
+
41
+
42
+ export class PendingSendRegistry {
43
+ private readonly attempts = new Map<string, OwnedPendingSend>();
44
+ private readonly generations = new Map<string, SendGeneration>();
45
+
46
+ constructor(
47
+ private readonly generationTtlMs = DEFAULT_GENERATION_TTL_MS,
48
+ private readonly maxGenerations = DEFAULT_MAX_GENERATIONS,
49
+ private readonly now: () => number = Date.now,
50
+ ) {}
51
+
52
+ private nextGeneration(messageId: string): number {
53
+ const now = this.now();
54
+ for (const [id, record] of this.generations) {
55
+ if (now - record.touchedAt <= this.generationTtlMs) break;
56
+ this.generations.delete(id);
57
+ }
58
+ const previous = this.generations.get(messageId);
59
+ const generation = previous && now - previous.touchedAt <= this.generationTtlMs ? previous.generation + 1 : 1;
60
+ this.generations.delete(messageId);
61
+ this.generations.set(messageId, { generation, touchedAt: now });
62
+ while (this.generations.size > Math.max(1, this.maxGenerations)) {
63
+ const oldest = this.generations.keys().next().value as string | undefined;
64
+ if (oldest === undefined) break;
65
+ this.generations.delete(oldest);
66
+ }
67
+ return generation;
68
+ }
69
+
70
+ private touchGeneration(attempt: OwnedPendingSend): void {
71
+ this.generations.delete(attempt.messageId);
72
+ this.generations.set(attempt.messageId, { generation: attempt.generation, touchedAt: this.now() });
73
+ while (this.generations.size > Math.max(1, this.maxGenerations)) {
74
+ const oldest = this.generations.keys().next().value as string | undefined;
75
+ if (oldest === undefined) break;
76
+ this.generations.delete(oldest);
77
+ }
78
+ }
79
+ acquire(messageId: string, signature: string, timeoutMs: number): PendingSendAcquisition {
80
+ const existing = this.attempts.get(messageId);
81
+ if (existing) {
82
+ if (existing.signature !== signature) {
83
+ throw new Error(`Intercom message ID '${messageId}' is already pending with a different target or payload`);
84
+ }
85
+ return { attempt: existing, owner: false };
86
+ }
87
+
88
+ let resolvePromise!: (result: SendResultLike) => void;
89
+ let rejectPromise!: (error: Error) => void;
90
+ const promise = new Promise<SendResultLike>((resolve, reject) => {
91
+ resolvePromise = resolve;
92
+ rejectPromise = reject;
93
+ });
94
+ const generation = this.nextGeneration(messageId);
95
+ const attempt: OwnedPendingSend = {
96
+ messageId,
97
+ attemptId: randomUUID(),
98
+ signature,
99
+ promise,
100
+ resolve: resolvePromise,
101
+ reject: rejectPromise,
102
+ legacyEligible: generation === 1,
103
+ generation,
104
+ };
105
+ attempt.timer = setTimeout(() => {
106
+ this.reject(attempt, new Error("Send timeout"));
107
+ }, timeoutMs);
108
+ this.attempts.set(messageId, attempt);
109
+ return { attempt, owner: true };
110
+ }
111
+
112
+ resolve(messageId: string, attemptId: string, result: SendResultLike): boolean {
113
+ const attempt = this.attempts.get(messageId);
114
+ if (!attempt || attempt.attemptId !== attemptId) return false;
115
+ this.attempts.delete(messageId);
116
+ if (attempt.timer) clearTimeout(attempt.timer);
117
+ this.touchGeneration(attempt);
118
+ attempt.resolve(result);
119
+ return true;
120
+ }
121
+
122
+ /** Resolve a pre-attemptId response only for an ID's first active generation. */
123
+ resolveLegacy(messageId: string, result: SendResultLike): boolean {
124
+ const attempt = this.attempts.get(messageId);
125
+ if (!attempt?.legacyEligible) return false;
126
+ return this.resolve(messageId, attempt.attemptId, result);
127
+ }
128
+
129
+ reject(attempt: PendingSendAttempt, error: Error): boolean {
130
+ const current = this.attempts.get(attempt.messageId);
131
+ if (current !== attempt) return false;
132
+ this.attempts.delete(attempt.messageId);
133
+ if (current.timer) clearTimeout(current.timer);
134
+ this.touchGeneration(current);
135
+ current.reject(error);
136
+ return true;
137
+ }
138
+
139
+ rejectAll(error: Error): void {
140
+ for (const attempt of [...this.attempts.values()]) this.reject(attempt, error);
141
+ }
142
+ }
@@ -0,0 +1,106 @@
1
+ import type net from "node:net";
2
+ import type { Attachment, BrokerMessage, Message, SessionInfo } from "../types.js";
3
+ import { DeliveredMessageCache } from "./delivered-message-cache.js";
4
+ import { buildMessageSendSignature } from "./send-signature.js";
5
+
6
+ export interface BrokerConnectedSession {
7
+ socket: net.Socket;
8
+ info: SessionInfo;
9
+ }
10
+
11
+ interface SendClientMessage extends Record<string, unknown> {
12
+ type: string;
13
+ }
14
+
15
+ function isAttachment(value: unknown): value is Attachment {
16
+ if (typeof value !== "object" || value === null) return false;
17
+ const attachment = value as Record<string, unknown>;
18
+ if (attachment.type !== "file" && attachment.type !== "snippet" && attachment.type !== "context") return false;
19
+ if (typeof attachment.name !== "string" || typeof attachment.content !== "string") return false;
20
+ return attachment.language === undefined || typeof attachment.language === "string";
21
+ }
22
+
23
+ function isMessage(value: unknown): value is Message {
24
+ if (typeof value !== "object" || value === null) return false;
25
+ const message = value as Record<string, unknown>;
26
+ if (typeof message.id !== "string" || typeof message.timestamp !== "number") return false;
27
+ if (message.replyTo !== undefined && typeof message.replyTo !== "string") return false;
28
+ if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean") return false;
29
+ if (typeof message.content !== "object" || message.content === null) return false;
30
+ const content = message.content as Record<string, unknown>;
31
+ if (typeof content.text !== "string") return false;
32
+ return content.attachments === undefined || (Array.isArray(content.attachments) && content.attachments.every(isAttachment));
33
+ }
34
+
35
+ function findSessions(sessions: Map<string, BrokerConnectedSession>, nameOrId: string): BrokerConnectedSession[] {
36
+ const byId = sessions.get(nameOrId);
37
+ if (byId) return [byId];
38
+ const lowerName = nameOrId.toLowerCase();
39
+ return Array.from(sessions.values()).filter((session) => session.info.name?.toLowerCase() === lowerName);
40
+ }
41
+
42
+ /** Validate and route one wire-level send request. */
43
+ export function handleBrokerSend(
44
+ socket: net.Socket,
45
+ clientMessage: SendClientMessage,
46
+ currentId: string | null,
47
+ sessions: Map<string, BrokerConnectedSession>,
48
+ deliveredMessages: DeliveredMessageCache,
49
+ write: (target: net.Socket, message: BrokerMessage) => void,
50
+ ): void {
51
+ const message = clientMessage.message;
52
+ const messageId = isMessage(message) ? message.id : "unknown";
53
+ const hasAttemptId = Object.prototype.hasOwnProperty.call(clientMessage, "attemptId");
54
+ if (hasAttemptId && typeof clientMessage.attemptId !== "string") {
55
+ write(socket, {
56
+ type: "delivery_failed",
57
+ messageId,
58
+ reason: "Invalid attemptId format",
59
+ });
60
+ return;
61
+ }
62
+ const attemptId = typeof clientMessage.attemptId === "string" ? clientMessage.attemptId : undefined;
63
+ if (typeof clientMessage.to !== "string" || !isMessage(message)) {
64
+ write(socket, { type: "delivery_failed", messageId, attemptId, reason: "Invalid message format" });
65
+ return;
66
+ }
67
+
68
+ const signature = buildMessageSendSignature(clientMessage.to, message);
69
+ const deliveredMatch = deliveredMessages.lookup(message.id, signature);
70
+ if (deliveredMatch === "match") {
71
+ write(socket, { type: "delivered", messageId: message.id, attemptId });
72
+ return;
73
+ }
74
+ if (deliveredMatch === "conflict") {
75
+ write(socket, {
76
+ type: "delivery_failed",
77
+ messageId: message.id,
78
+ attemptId,
79
+ reason: `Intercom message ID '${message.id}' was already delivered with a different target or payload`,
80
+ });
81
+ return;
82
+ }
83
+
84
+ const targets = findSessions(sessions, clientMessage.to);
85
+ if (targets.length === 1) {
86
+ const fromSession = currentId ? sessions.get(currentId) : undefined;
87
+ if (!fromSession) {
88
+ write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Sender session not found" });
89
+ return;
90
+ }
91
+ write(targets[0].socket, { type: "message", from: fromSession.info, message });
92
+ deliveredMessages.record(message.id, signature);
93
+ write(socket, { type: "delivered", messageId: message.id, attemptId });
94
+ return;
95
+ }
96
+ if (targets.length > 1) {
97
+ write(socket, {
98
+ type: "delivery_failed",
99
+ messageId: message.id,
100
+ attemptId,
101
+ reason: `Multiple sessions named \"${clientMessage.to}\" are connected. Use the session ID instead.`,
102
+ });
103
+ return;
104
+ }
105
+ write(socket, { type: "delivery_failed", messageId: message.id, attemptId, reason: "Session not found" });
106
+ }
@@ -0,0 +1,37 @@
1
+ import type { Attachment, Message } from "../types.js";
2
+
3
+ export interface LogicalSendOptions {
4
+ text: string;
5
+ attachments?: Attachment[];
6
+ replyTo?: string;
7
+ expectsReply?: boolean;
8
+ }
9
+
10
+ function normalizeAttachments(attachments: Attachment[] | undefined): Array<Record<string, string>> | undefined {
11
+ return attachments?.map((attachment) => ({
12
+ type: attachment.type,
13
+ name: attachment.name,
14
+ content: attachment.content,
15
+ ...(attachment.language === undefined ? {} : { language: attachment.language }),
16
+ }));
17
+ }
18
+
19
+ /** Canonical identity for one logical send; transport attempt metadata is deliberately excluded. */
20
+ export function buildSendSignature(to: string, options: LogicalSendOptions): string {
21
+ return JSON.stringify({
22
+ to,
23
+ text: options.text,
24
+ attachments: normalizeAttachments(options.attachments) ?? [],
25
+ replyTo: options.replyTo ?? null,
26
+ expectsReply: options.expectsReply ?? false,
27
+ });
28
+ }
29
+
30
+ export function buildMessageSendSignature(to: string, message: Message): string {
31
+ return buildSendSignature(to, {
32
+ text: message.content.text,
33
+ attachments: message.content.attachments,
34
+ replyTo: message.replyTo,
35
+ expectsReply: message.expectsReply,
36
+ });
37
+ }
@@ -0,0 +1,136 @@
1
+ import type { ExtensionAPI } from "@bastani/atomic";
2
+ import type { Message, SessionInfo } from "./types.js";
3
+
4
+ export const INTERCOM_DETACH_REQUEST_EVENT = "pi-intercom:detach-request";
5
+ export const INTERCOM_DETACH_RESPONSE_EVENT = "pi-intercom:detach-response";
6
+
7
+ interface DetachHandshake {
8
+ phase: "probe" | "commit";
9
+ requestId: string;
10
+ messageId: string;
11
+ childIntercomTarget: string;
12
+ senderId: string;
13
+ runtimeGeneration: number;
14
+ }
15
+
16
+ type ForegroundDeliveryDisposition = "delivered" | "unclaimed" | "abandoned";
17
+
18
+ /** Claims a busy inbound message only when its exact foreground owner acknowledges it. */
19
+ export class ForegroundDetachHandoff {
20
+ private generation = -1;
21
+ private readonly delivered = new Set<string>();
22
+ private readonly pending = new Map<string, Promise<ForegroundDeliveryDisposition>>();
23
+ private generationCancellation = new AbortController();
24
+
25
+ constructor(
26
+ private readonly pi: Pick<ExtensionAPI, "events">,
27
+ private readonly ackTimeoutMs = 50,
28
+ ) {}
29
+
30
+ deliver(input: {
31
+ from: SessionInfo;
32
+ message: Message;
33
+ generation: number;
34
+ surface: () => void;
35
+ isCurrent: () => boolean;
36
+ }): Promise<ForegroundDeliveryDisposition> {
37
+ if (!input.isCurrent()) return Promise.resolve("abandoned");
38
+ if (this.generation !== input.generation) this.resetForGeneration(input.generation);
39
+ const deliveryKey = `${input.from.id}\0${input.message.id}`;
40
+ const pendingKey = `${input.generation}\0${deliveryKey}`;
41
+ if (this.delivered.has(deliveryKey)) return Promise.resolve("delivered");
42
+ const existing = this.pending.get(pendingKey);
43
+ if (existing) return existing;
44
+
45
+ const attempt = this.claimAndDeliver(input, deliveryKey).finally(() => {
46
+ if (this.pending.get(pendingKey) === attempt) this.pending.delete(pendingKey);
47
+ });
48
+ this.pending.set(pendingKey, attempt);
49
+ return attempt;
50
+ }
51
+
52
+ reset(): void { this.resetForGeneration(-1); }
53
+
54
+ private async claimAndDeliver(
55
+ input: { from: SessionInfo; message: Message; generation: number; surface: () => void; isCurrent: () => boolean },
56
+ deliveryKey: string,
57
+ ): Promise<ForegroundDeliveryDisposition> {
58
+ const route: DetachHandshake = {
59
+ phase: "probe",
60
+ requestId: input.message.id,
61
+ messageId: input.message.id,
62
+ childIntercomTarget: input.from.name || input.from.id,
63
+ senderId: input.from.id,
64
+ runtimeGeneration: input.generation,
65
+ };
66
+ const signal = this.generationCancellation.signal;
67
+ const probed = await this.awaitAcknowledgement(route, signal);
68
+ if (probed === "cancelled" || !input.isCurrent() || this.generation !== input.generation) return "abandoned";
69
+ if (probed === "timed-out") return "unclaimed";
70
+
71
+ const committed = await this.awaitAcknowledgement({ ...route, phase: "commit" }, signal);
72
+ if (committed !== "acknowledged" || !input.isCurrent() || this.generation !== input.generation) return "abandoned";
73
+ input.surface();
74
+ this.delivered.add(deliveryKey);
75
+ return "delivered";
76
+ }
77
+
78
+ private awaitAcknowledgement(
79
+ route: DetachHandshake,
80
+ signal: AbortSignal,
81
+ ): Promise<"acknowledged" | "timed-out" | "cancelled"> {
82
+ return new Promise((resolve) => {
83
+ let settled = false;
84
+ let unsubscribe: (() => void) | undefined;
85
+ let timeout: ReturnType<typeof setTimeout> | undefined;
86
+ const finish = (disposition: "acknowledged" | "timed-out" | "cancelled") => {
87
+ if (settled) return;
88
+ settled = true;
89
+ if (timeout) clearTimeout(timeout);
90
+ unsubscribe?.();
91
+ signal.removeEventListener("abort", cancel);
92
+ resolve(disposition);
93
+ };
94
+ const cancel = () => finish("cancelled");
95
+ unsubscribe = this.pi.events?.on(INTERCOM_DETACH_RESPONSE_EVENT, (payload) => {
96
+ if (!payload || typeof payload !== "object") return;
97
+ const response = payload as Partial<DetachHandshake> & { accepted?: unknown };
98
+ if (response.accepted === true
99
+ && response.phase === route.phase
100
+ && response.requestId === route.requestId
101
+ && response.messageId === route.messageId
102
+ && response.childIntercomTarget === route.childIntercomTarget
103
+ && response.senderId === route.senderId
104
+ && response.runtimeGeneration === route.runtimeGeneration) finish("acknowledged");
105
+ });
106
+ signal.addEventListener("abort", cancel, { once: true });
107
+ timeout = setTimeout(() => finish("timed-out"), this.ackTimeoutMs);
108
+ if (signal.aborted) {
109
+ cancel();
110
+ return;
111
+ }
112
+ this.pi.events?.emit(INTERCOM_DETACH_REQUEST_EVENT, route);
113
+ });
114
+ }
115
+
116
+ private resetForGeneration(generation: number): void {
117
+ this.generationCancellation.abort();
118
+ this.generationCancellation = new AbortController();
119
+ this.pending.clear();
120
+ this.delivered.clear();
121
+ this.generation = generation;
122
+ }
123
+ }
124
+
125
+ export async function handleForegroundInboundDelivery(input: {
126
+ handoff: ForegroundDetachHandoff;
127
+ from: SessionInfo;
128
+ message: Message;
129
+ generation: number;
130
+ surface: () => void;
131
+ isCurrent: () => boolean;
132
+ onUnclaimed: () => void;
133
+ }): Promise<void> {
134
+ const disposition = await input.handoff.deliver(input);
135
+ if (disposition === "unclaimed" && input.isCurrent()) input.onUnclaimed();
136
+ }