@bastani/atomic 0.9.4-alpha.1 → 0.9.4-alpha.11

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 (345) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +24 -0
  3. package/dist/builtin/cursor/package.json +4 -4
  4. package/dist/builtin/intercom/CHANGELOG.md +24 -0
  5. package/dist/builtin/intercom/package.json +2 -2
  6. package/dist/builtin/mcp/CHANGELOG.md +24 -0
  7. package/dist/builtin/mcp/package.json +4 -4
  8. package/dist/builtin/subagents/CHANGELOG.md +52 -0
  9. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  10. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  11. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  12. package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
  13. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  14. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  15. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  16. package/dist/builtin/subagents/agents/debugger.md +1 -1
  17. package/dist/builtin/subagents/agents/worker.md +2 -2
  18. package/dist/builtin/subagents/package.json +4 -4
  19. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +11 -1
  20. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +11 -1
  21. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +2 -0
  22. package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +27 -4
  23. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +25 -0
  24. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
  25. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
  26. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
  27. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +1 -0
  28. package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +2 -0
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +3 -1
  30. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +22 -0
  31. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +15 -5
  32. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +5 -1
  33. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
  34. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -1
  35. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +4 -1
  36. package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +126 -0
  37. package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +53 -0
  38. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +52 -53
  39. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +2 -0
  40. package/dist/builtin/subagents/src/shared/model-info.ts +16 -0
  41. package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
  42. package/dist/builtin/subagents/src/slash/slash-commands.ts +2 -2
  43. package/dist/builtin/subagents/src/tui/render-result-compact.ts +3 -2
  44. package/dist/builtin/subagents/src/tui/render-result.ts +3 -3
  45. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +4 -3
  46. package/dist/builtin/subagents/src/tui/render-widget.ts +10 -4
  47. package/dist/builtin/web-access/CHANGELOG.md +24 -0
  48. package/dist/builtin/web-access/package.json +2 -2
  49. package/dist/builtin/workflows/CHANGELOG.md +54 -0
  50. package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +24 -22
  51. package/dist/builtin/workflows/builtin/goal-runner.ts +12 -23
  52. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +4 -4
  53. package/dist/builtin/workflows/builtin/ralph-models.ts +53 -75
  54. package/dist/builtin/workflows/package.json +2 -2
  55. package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +59 -1
  56. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +4 -0
  57. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -0
  58. package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +12 -1
  59. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +2 -0
  60. package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +6 -0
  61. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +4 -0
  62. package/dist/builtin/workflows/src/tui/workflow-attach-pane-types.ts +4 -0
  63. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +8 -9
  64. package/dist/bun/cli.d.ts.map +1 -1
  65. package/dist/bun/cli.js +3 -2
  66. package/dist/bun/cli.js.map +1 -1
  67. package/dist/cli/file-processor.d.ts.map +1 -1
  68. package/dist/cli/file-processor.js +13 -25
  69. package/dist/cli/file-processor.js.map +1 -1
  70. package/dist/cli.d.ts.map +1 -1
  71. package/dist/cli.js +15 -5
  72. package/dist/cli.js.map +1 -1
  73. package/dist/config.d.ts +10 -3
  74. package/dist/config.d.ts.map +1 -1
  75. package/dist/config.js +18 -1
  76. package/dist/config.js.map +1 -1
  77. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  78. package/dist/core/agent-session-auto-compaction.js +8 -1
  79. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  80. package/dist/core/agent-session-events.d.ts.map +1 -1
  81. package/dist/core/agent-session-events.js +16 -8
  82. package/dist/core/agent-session-events.js.map +1 -1
  83. package/dist/core/agent-session-export.d.ts.map +1 -1
  84. package/dist/core/agent-session-export.js +4 -2
  85. package/dist/core/agent-session-export.js.map +1 -1
  86. package/dist/core/agent-session-extension-bindings.d.ts +5 -1
  87. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  88. package/dist/core/agent-session-extension-bindings.js +18 -7
  89. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  90. package/dist/core/agent-session-methods.d.ts +13 -3
  91. package/dist/core/agent-session-methods.d.ts.map +1 -1
  92. package/dist/core/agent-session-methods.js.map +1 -1
  93. package/dist/core/agent-session-prompt.d.ts +2 -0
  94. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  95. package/dist/core/agent-session-prompt.js +15 -2
  96. package/dist/core/agent-session-prompt.js.map +1 -1
  97. package/dist/core/agent-session-retry.d.ts +2 -0
  98. package/dist/core/agent-session-retry.d.ts.map +1 -1
  99. package/dist/core/agent-session-retry.js +66 -7
  100. package/dist/core/agent-session-retry.js.map +1 -1
  101. package/dist/core/agent-session-state.d.ts.map +1 -1
  102. package/dist/core/agent-session-state.js +2 -2
  103. package/dist/core/agent-session-state.js.map +1 -1
  104. package/dist/core/agent-session-tool-hooks.d.ts +7 -1
  105. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  106. package/dist/core/agent-session-tool-hooks.js +26 -4
  107. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  108. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  109. package/dist/core/agent-session-tool-registry.js +4 -0
  110. package/dist/core/agent-session-tool-registry.js.map +1 -1
  111. package/dist/core/agent-session-tree.d.ts.map +1 -1
  112. package/dist/core/agent-session-tree.js +3 -1
  113. package/dist/core/agent-session-tree.js.map +1 -1
  114. package/dist/core/agent-session-types.d.ts +4 -0
  115. package/dist/core/agent-session-types.d.ts.map +1 -1
  116. package/dist/core/agent-session-types.js.map +1 -1
  117. package/dist/core/agent-session.d.ts +5 -1
  118. package/dist/core/agent-session.d.ts.map +1 -1
  119. package/dist/core/agent-session.js +5 -0
  120. package/dist/core/agent-session.js.map +1 -1
  121. package/dist/core/async/format.d.ts +3 -0
  122. package/dist/core/async/format.d.ts.map +1 -0
  123. package/dist/core/async/format.js +50 -0
  124. package/dist/core/async/format.js.map +1 -0
  125. package/dist/core/async/job-manager.d.ts +39 -0
  126. package/dist/core/async/job-manager.d.ts.map +1 -0
  127. package/dist/core/async/job-manager.js +254 -0
  128. package/dist/core/async/job-manager.js.map +1 -0
  129. package/dist/core/async/session-manager.d.ts +18 -0
  130. package/dist/core/async/session-manager.d.ts.map +1 -0
  131. package/dist/core/async/session-manager.js +61 -0
  132. package/dist/core/async/session-manager.js.map +1 -0
  133. package/dist/core/async/types.d.ts +23 -0
  134. package/dist/core/async/types.d.ts.map +1 -0
  135. package/dist/core/async/types.js +2 -0
  136. package/dist/core/async/types.js.map +1 -0
  137. package/dist/core/copilot-model-catalog.d.ts +38 -74
  138. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  139. package/dist/core/copilot-model-catalog.js +177 -68
  140. package/dist/core/copilot-model-catalog.js.map +1 -1
  141. package/dist/core/copilot-model-static-fallbacks.d.ts +43 -0
  142. package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -0
  143. package/dist/core/copilot-model-static-fallbacks.js +50 -0
  144. package/dist/core/copilot-model-static-fallbacks.js.map +1 -0
  145. package/dist/core/copilot-model-synthesis.d.ts +10 -0
  146. package/dist/core/copilot-model-synthesis.d.ts.map +1 -0
  147. package/dist/core/copilot-model-synthesis.js +89 -0
  148. package/dist/core/copilot-model-synthesis.js.map +1 -0
  149. package/dist/core/extensions/api-types.d.ts +2 -1
  150. package/dist/core/extensions/api-types.d.ts.map +1 -1
  151. package/dist/core/extensions/api-types.js.map +1 -1
  152. package/dist/core/extensions/index.d.ts +1 -1
  153. package/dist/core/extensions/index.d.ts.map +1 -1
  154. package/dist/core/extensions/index.js.map +1 -1
  155. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  156. package/dist/core/extensions/loader-core.js +3 -3
  157. package/dist/core/extensions/loader-core.js.map +1 -1
  158. package/dist/core/extensions/loader-virtual-modules.d.ts +11 -0
  159. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  160. package/dist/core/extensions/loader-virtual-modules.js +79 -11
  161. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  162. package/dist/core/extensions/session-events.d.ts +7 -1
  163. package/dist/core/extensions/session-events.d.ts.map +1 -1
  164. package/dist/core/extensions/session-events.js.map +1 -1
  165. package/dist/core/http-dispatcher.d.ts +2 -0
  166. package/dist/core/http-dispatcher.d.ts.map +1 -1
  167. package/dist/core/http-dispatcher.js +38 -5
  168. package/dist/core/http-dispatcher.js.map +1 -1
  169. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  170. package/dist/core/model-registry-builtins.js +40 -6
  171. package/dist/core/model-registry-builtins.js.map +1 -1
  172. package/dist/core/model-registry.d.ts.map +1 -1
  173. package/dist/core/model-registry.js +4 -3
  174. package/dist/core/model-registry.js.map +1 -1
  175. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  176. package/dist/core/model-resolver-defaults.js +1 -1
  177. package/dist/core/model-resolver-defaults.js.map +1 -1
  178. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  179. package/dist/core/resource-loader-reload.js +6 -3
  180. package/dist/core/resource-loader-reload.js.map +1 -1
  181. package/dist/core/resource-loader-types.d.ts +6 -0
  182. package/dist/core/resource-loader-types.d.ts.map +1 -1
  183. package/dist/core/resource-loader-types.js.map +1 -1
  184. package/dist/core/session-manager-core.d.ts +2 -12
  185. package/dist/core/session-manager-core.d.ts.map +1 -1
  186. package/dist/core/session-manager-core.js +8 -16
  187. package/dist/core/session-manager-core.js.map +1 -1
  188. package/dist/core/settings-manager-resource-accessors.d.ts +1 -0
  189. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  190. package/dist/core/settings-manager-resource-accessors.js +11 -0
  191. package/dist/core/settings-manager-resource-accessors.js.map +1 -1
  192. package/dist/core/settings-manager-ui-accessors.d.ts +2 -0
  193. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  194. package/dist/core/settings-manager-ui-accessors.js +9 -0
  195. package/dist/core/settings-manager-ui-accessors.js.map +1 -1
  196. package/dist/core/settings-types.d.ts +2 -0
  197. package/dist/core/settings-types.d.ts.map +1 -1
  198. package/dist/core/settings-types.js.map +1 -1
  199. package/dist/core/timings.d.ts +7 -5
  200. package/dist/core/timings.d.ts.map +1 -1
  201. package/dist/core/timings.js +42 -24
  202. package/dist/core/timings.js.map +1 -1
  203. package/dist/core/tools/bash-async-execution.d.ts +25 -0
  204. package/dist/core/tools/bash-async-execution.d.ts.map +1 -0
  205. package/dist/core/tools/bash-async-execution.js +71 -0
  206. package/dist/core/tools/bash-async-execution.js.map +1 -0
  207. package/dist/core/tools/bash-async-jobs.d.ts +10 -0
  208. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -1
  209. package/dist/core/tools/bash-async-jobs.js +18 -2
  210. package/dist/core/tools/bash-async-jobs.js.map +1 -1
  211. package/dist/core/tools/bash-async-output.d.ts +3 -1
  212. package/dist/core/tools/bash-async-output.d.ts.map +1 -1
  213. package/dist/core/tools/bash-async-output.js +4 -1
  214. package/dist/core/tools/bash-async-output.js.map +1 -1
  215. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  216. package/dist/core/tools/bash-pty-native.js +2 -2
  217. package/dist/core/tools/bash-pty-native.js.map +1 -1
  218. package/dist/core/tools/bash.d.ts +10 -4
  219. package/dist/core/tools/bash.d.ts.map +1 -1
  220. package/dist/core/tools/bash.js +20 -38
  221. package/dist/core/tools/bash.js.map +1 -1
  222. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  223. package/dist/core/tools/read-selectors.js +19 -1
  224. package/dist/core/tools/read-selectors.js.map +1 -1
  225. package/dist/core/tools/read.d.ts.map +1 -1
  226. package/dist/core/tools/read.js +12 -23
  227. package/dist/core/tools/read.js.map +1 -1
  228. package/dist/core/tools/search-native.d.ts.map +1 -1
  229. package/dist/core/tools/search-native.js +2 -2
  230. package/dist/core/tools/search-native.js.map +1 -1
  231. package/dist/index-extensions.d.ts +1 -1
  232. package/dist/index-extensions.d.ts.map +1 -1
  233. package/dist/index-extensions.js.map +1 -1
  234. package/dist/index.d.ts +1 -1
  235. package/dist/index.d.ts.map +1 -1
  236. package/dist/index.js.map +1 -1
  237. package/dist/main-session.d.ts.map +1 -1
  238. package/dist/main-session.js +14 -5
  239. package/dist/main-session.js.map +1 -1
  240. package/dist/main.d.ts.map +1 -1
  241. package/dist/main.js +56 -32
  242. package/dist/main.js.map +1 -1
  243. package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  244. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  245. package/dist/modes/interactive/components/assistant-message.js +22 -15
  246. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  247. package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
  248. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/chat-message-renderer.js +5 -5
  250. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  251. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/chat-session-host-events.js +8 -0
  253. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  254. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  255. package/dist/modes/interactive/components/chat-session-host-rendering.js +1 -0
  256. package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
  257. package/dist/modes/interactive/components/user-message.d.ts +6 -2
  258. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  259. package/dist/modes/interactive/components/user-message.js +16 -5
  260. package/dist/modes/interactive/components/user-message.js.map +1 -1
  261. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  262. package/dist/modes/interactive/interactive-agent-events.js +6 -1
  263. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  264. package/dist/modes/interactive/interactive-deferred-startup.d.ts +2 -0
  265. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -0
  266. package/dist/modes/interactive/interactive-deferred-startup.js +59 -0
  267. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -0
  268. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  269. package/dist/modes/interactive/interactive-editor-actions.js +1 -0
  270. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  271. package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
  272. package/dist/modes/interactive/interactive-extension-context.js +1 -0
  273. package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
  274. package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
  275. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  276. package/dist/modes/interactive/interactive-mode-base.js +6 -0
  277. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  278. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  279. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  280. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  281. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  282. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -0
  283. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  284. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  285. package/dist/modes/interactive/interactive-mode-types.d.ts +2 -0
  286. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  287. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  288. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  289. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  290. package/dist/modes/interactive/interactive-mode.js +1 -0
  291. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  292. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  293. package/dist/modes/interactive/interactive-model-routing.js +2 -4
  294. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  295. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  296. package/dist/modes/interactive/interactive-render-chat.js +4 -3
  297. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  298. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  299. package/dist/modes/interactive/interactive-session-runtime.js +5 -2
  300. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  301. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  302. package/dist/modes/interactive/interactive-startup.js +11 -1
  303. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  304. package/dist/modes/rpc/rpc-client.d.ts +28 -51
  305. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  306. package/dist/modes/rpc/rpc-client.js +27 -51
  307. package/dist/modes/rpc/rpc-client.js.map +1 -1
  308. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  309. package/dist/modes/rpc/rpc-command-handler.js +19 -0
  310. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  311. package/dist/modes/rpc/rpc-types.d.ts +26 -0
  312. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  313. package/dist/modes/rpc/rpc-types.js.map +1 -1
  314. package/dist/rpc-entry.d.ts +3 -0
  315. package/dist/rpc-entry.d.ts.map +1 -0
  316. package/dist/rpc-entry.js +12 -0
  317. package/dist/rpc-entry.js.map +1 -0
  318. package/dist/utils/image-convert.d.ts +1 -0
  319. package/dist/utils/image-convert.d.ts.map +1 -1
  320. package/dist/utils/image-convert.js +21 -15
  321. package/dist/utils/image-convert.js.map +1 -1
  322. package/dist/utils/image-process.d.ts +18 -0
  323. package/dist/utils/image-process.d.ts.map +1 -0
  324. package/dist/utils/image-process.js +83 -0
  325. package/dist/utils/image-process.js.map +1 -0
  326. package/dist/utils/mime.d.ts.map +1 -1
  327. package/dist/utils/mime.js +41 -0
  328. package/dist/utils/mime.js.map +1 -1
  329. package/dist/utils/module-require.d.ts +11 -0
  330. package/dist/utils/module-require.d.ts.map +1 -0
  331. package/dist/utils/module-require.js +16 -0
  332. package/dist/utils/module-require.js.map +1 -0
  333. package/docs/compaction.md +2 -0
  334. package/docs/development.md +10 -0
  335. package/docs/extensions.md +11 -0
  336. package/docs/models.md +3 -3
  337. package/docs/providers.md +2 -1
  338. package/docs/rpc.md +60 -2
  339. package/docs/sdk.md +1 -1
  340. package/docs/settings.md +4 -1
  341. package/docs/subagents.md +6 -0
  342. package/docs/tools.md +4 -2
  343. package/docs/workflows.md +4 -2
  344. package/npm-shrinkwrap.json +138 -171
  345. package/package.json +19 -12
@@ -0,0 +1,50 @@
1
+ const INLINE_OUTPUT_LIMIT = 12_000;
2
+ const LARGE_OUTPUT_PREVIEW_LIMIT = 4_000;
3
+ function truncateText(text, maxChars) {
4
+ if (text.length <= maxChars)
5
+ return text;
6
+ return text.slice(0, maxChars).trimEnd();
7
+ }
8
+ function jobOutput(job) {
9
+ const output = job.output.trimEnd();
10
+ if (output.length > 0)
11
+ return output;
12
+ return job.error ? `(no output)\n\n${job.error}` : "(no output)";
13
+ }
14
+ function statusLine(job) {
15
+ if (job.error)
16
+ return `Error: ${job.error}`;
17
+ if (job.exitCode !== undefined && job.exitCode !== null && job.exitCode !== 0) {
18
+ return `Command exited with code ${job.exitCode}`;
19
+ }
20
+ return undefined;
21
+ }
22
+ export function formatAsyncResultForFollowUp(job) {
23
+ const elapsedMs = (job.endedAt ?? Date.now()) - job.startedAt;
24
+ const header = `Async bash job ${job.jobId} ${job.status}: ${job.command}`;
25
+ const body = jobOutput(job);
26
+ const status = statusLine(job);
27
+ const inline = [header, body, status].filter((part) => part !== undefined && part.length > 0).join("\n\n");
28
+ let content = inline;
29
+ if (inline.length > INLINE_OUTPUT_LIMIT && job.fullOutputPath) {
30
+ const preview = truncateText(body, LARGE_OUTPUT_PREVIEW_LIMIT);
31
+ content = [header, preview, `[Output truncated for async follow-up. Full output: ${job.fullOutputPath}]`, status]
32
+ .filter((part) => part !== undefined && part.length > 0)
33
+ .join("\n\n");
34
+ }
35
+ return {
36
+ customType: "async-job-result",
37
+ content,
38
+ display: true,
39
+ details: {
40
+ jobId: job.jobId,
41
+ type: "bash",
42
+ status: job.status,
43
+ command: job.command,
44
+ exitCode: job.exitCode,
45
+ fullOutputPath: job.fullOutputPath,
46
+ wallTimeMs: elapsedMs,
47
+ },
48
+ };
49
+ }
50
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../../src/core/async/format.ts"],"names":[],"mappings":"AAEA,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,0BAA0B,GAAG,KAAK,CAAC;AAEzC,SAAS,YAAY,CAAC,IAAY,EAAE,QAAgB;IACnD,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;AAC1C,CAAC;AAED,SAAS,SAAS,CAAC,GAAwB;IAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IACrC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,kBAAkB,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;AAClE,CAAC;AAED,SAAS,UAAU,CAAC,GAAwB;IAC3C,IAAI,GAAG,CAAC,KAAK;QAAE,OAAO,UAAU,GAAG,CAAC,KAAK,EAAE,CAAC;IAC5C,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC/E,OAAO,4BAA4B,GAAG,CAAC,QAAQ,EAAE,CAAC;IACnD,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,GAAwB;IACpE,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC;IAC9D,MAAM,MAAM,GAAG,kBAAkB,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;IAC3E,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3H,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,MAAM,CAAC,MAAM,GAAG,mBAAmB,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC;QAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,0BAA0B,CAAC,CAAC;QAC/D,OAAO,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,uDAAuD,GAAG,CAAC,cAAc,GAAG,EAAE,MAAM,CAAC;aAC/G,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACvE,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IACD,OAAO;QACN,UAAU,EAAE,kBAAkB;QAC9B,OAAO;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE;YACR,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,UAAU,EAAE,SAAS;SACrB;KACD,CAAC;AACH,CAAC","sourcesContent":["import type { ManagedAsyncBashJob, AsyncJobDeliveryMessage } from \"./types.js\";\n\nconst INLINE_OUTPUT_LIMIT = 12_000;\nconst LARGE_OUTPUT_PREVIEW_LIMIT = 4_000;\n\nfunction truncateText(text: string, maxChars: number): string {\n\tif (text.length <= maxChars) return text;\n\treturn text.slice(0, maxChars).trimEnd();\n}\n\nfunction jobOutput(job: ManagedAsyncBashJob): string {\n\tconst output = job.output.trimEnd();\n\tif (output.length > 0) return output;\n\treturn job.error ? `(no output)\\n\\n${job.error}` : \"(no output)\";\n}\n\nfunction statusLine(job: ManagedAsyncBashJob): string | undefined {\n\tif (job.error) return `Error: ${job.error}`;\n\tif (job.exitCode !== undefined && job.exitCode !== null && job.exitCode !== 0) {\n\t\treturn `Command exited with code ${job.exitCode}`;\n\t}\n\treturn undefined;\n}\n\nexport function formatAsyncResultForFollowUp(job: ManagedAsyncBashJob): AsyncJobDeliveryMessage {\n\tconst elapsedMs = (job.endedAt ?? Date.now()) - job.startedAt;\n\tconst header = `Async bash job ${job.jobId} ${job.status}: ${job.command}`;\n\tconst body = jobOutput(job);\n\tconst status = statusLine(job);\n\tconst inline = [header, body, status].filter((part): part is string => part !== undefined && part.length > 0).join(\"\\n\\n\");\n\tlet content = inline;\n\tif (inline.length > INLINE_OUTPUT_LIMIT && job.fullOutputPath) {\n\t\tconst preview = truncateText(body, LARGE_OUTPUT_PREVIEW_LIMIT);\n\t\tcontent = [header, preview, `[Output truncated for async follow-up. Full output: ${job.fullOutputPath}]`, status]\n\t\t\t.filter((part): part is string => part !== undefined && part.length > 0)\n\t\t\t.join(\"\\n\\n\");\n\t}\n\treturn {\n\t\tcustomType: \"async-job-result\",\n\t\tcontent,\n\t\tdisplay: true,\n\t\tdetails: {\n\t\t\tjobId: job.jobId,\n\t\t\ttype: \"bash\",\n\t\t\tstatus: job.status,\n\t\t\tcommand: job.command,\n\t\t\texitCode: job.exitCode,\n\t\t\tfullOutputPath: job.fullOutputPath,\n\t\t\twallTimeMs: elapsedMs,\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import { type ManagedBashJob } from "../tools/bash-async-jobs.js";
2
+ import type { AsyncJobDeliveryCallback, AsyncJobDeliveryHandler } from "./types.js";
3
+ interface AsyncJobManagerOptions {
4
+ onJobComplete: AsyncJobDeliveryCallback;
5
+ maxRunningJobs?: number;
6
+ maxRetainedJobs?: number;
7
+ completedJobTtlMs?: number;
8
+ }
9
+ export declare class AsyncJobManager {
10
+ #private;
11
+ static instance(): AsyncJobManager | undefined;
12
+ static setInstance(value: AsyncJobManager | undefined): void;
13
+ static resetForTests(): void;
14
+ constructor(options: AsyncJobManagerOptions);
15
+ get disposed(): boolean;
16
+ get atCapacity(): boolean;
17
+ registerSession(): symbol;
18
+ releaseSession(sessionId: symbol): void;
19
+ isSessionDisposed(sessionId: symbol): boolean;
20
+ registerBashJob(job: ManagedBashJob, onComplete?: AsyncJobDeliveryHandler, sessionId?: symbol): void;
21
+ completeBashJob(job: ManagedBashJob): void;
22
+ acknowledgeDeliveries(jobIds: readonly string[]): void;
23
+ isDeliverySuppressed(jobId: string): boolean;
24
+ deliveryState(): {
25
+ queued: number;
26
+ delivering: boolean;
27
+ pendingJobIds: string[];
28
+ };
29
+ retentionState(): {
30
+ jobs: number;
31
+ suppressions: number;
32
+ handlers: number;
33
+ queued: number;
34
+ sessions: number;
35
+ };
36
+ dispose(): void;
37
+ }
38
+ export {};
39
+ //# sourceMappingURL=job-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-manager.d.ts","sourceRoot":"","sources":["../../../src/core/async/job-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE/G,OAAO,KAAK,EAAE,wBAAwB,EAAE,uBAAuB,EAAgD,MAAM,YAAY,CAAC;AAOlI,UAAU,sBAAsB;IAC/B,aAAa,EAAE,wBAAwB,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAeD,qBAAa,eAAe;;IAG3B,MAAM,CAAC,QAAQ,IAAI,eAAe,GAAG,SAAS,CAE7C;IAED,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAE3D;IAED,MAAM,CAAC,aAAa,IAAI,IAAI,CAE3B;IAiBD,YAAY,OAAO,EAAE,sBAAsB,EAK1C;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,UAAU,IAAI,OAAO,CAIxB;IAGD,eAAe,IAAI,MAAM,CAKxB;IAED,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAOtC;IAED,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5C;IACD,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,UAAU,CAAC,EAAE,uBAAuB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAcnG;IAED,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAMzC;IAED,qBAAqB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAUrD;IAED,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3C;IAED,aAAa,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAA;KAAE,CAOhF;IAED,cAAc,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAG3G;IAED,OAAO,IAAI,IAAI,CAWd;CAqGD","sourcesContent":["import { COMPLETED_JOB_TTL_MS, MAX_MANAGED_BASH_JOBS, type ManagedBashJob } from \"../tools/bash-async-jobs.js\";\nimport { formatAsyncResultForFollowUp } from \"./format.js\";\nimport type { AsyncJobDeliveryCallback, AsyncJobDeliveryHandler, AsyncJobDeliveryMessage, ManagedAsyncBashJob } from \"./types.js\";\n\nconst DEFAULT_MAX_RUNNING_JOBS = 15;\nconst DELIVERY_RETRY_BASE_MS = 500;\nconst DELIVERY_RETRY_MAX_MS = 30_000;\nconst DELIVERY_RETRY_JITTER_MS = 200;\n\ninterface AsyncJobManagerOptions {\n\tonJobComplete: AsyncJobDeliveryCallback;\n\tmaxRunningJobs?: number;\n\tmaxRetainedJobs?: number;\n\tcompletedJobTtlMs?: number;\n}\n\ninterface Delivery {\n\tjobId: string;\n\tmessage: AsyncJobDeliveryMessage;\n\tattempt: number;\n\tnextAttemptAt: number;\n\tpromise?: Promise<void>;\n}\n\ninterface RegisteredSession {\n\tdisposed: boolean;\n\tactiveJobIds: Set<string>;\n}\n\nexport class AsyncJobManager {\n\tstatic #instance: AsyncJobManager | undefined;\n\n\tstatic instance(): AsyncJobManager | undefined {\n\t\treturn AsyncJobManager.#instance;\n\t}\n\n\tstatic setInstance(value: AsyncJobManager | undefined): void {\n\t\tAsyncJobManager.#instance = value;\n\t}\n\n\tstatic resetForTests(): void {\n\t\tAsyncJobManager.#instance = undefined;\n\t}\n\n\treadonly #jobs = new Map<string, ManagedBashJob>();\n\treadonly #deliveries: Delivery[] = [];\n\treadonly #suppressedDeliveries = new Set<string>();\n\treadonly #inFlightDeliveries = new Map<string, Delivery>();\n\treadonly #deliveryHandlers = new Map<string, AsyncJobDeliveryHandler>();\n\treadonly #jobSessions = new Map<string, symbol>();\n\treadonly #sessions = new Map<symbol, RegisteredSession>();\n\treadonly #onJobComplete: AsyncJobDeliveryCallback;\n\treadonly #maxRunningJobs: number;\n\treadonly #maxRetainedJobs: number;\n\treadonly #completedJobTtlMs: number;\n\t#timer: NodeJS.Timeout | undefined;\n\t#disposed = false;\n\t#runningDeliveryLoop = false;\n\n\tconstructor(options: AsyncJobManagerOptions) {\n\t\tthis.#onJobComplete = options.onJobComplete;\n\t\tthis.#maxRunningJobs = Math.max(1, Math.floor(options.maxRunningJobs ?? DEFAULT_MAX_RUNNING_JOBS));\n\t\tthis.#maxRetainedJobs = Math.max(1, Math.floor(options.maxRetainedJobs ?? MAX_MANAGED_BASH_JOBS));\n\t\tthis.#completedJobTtlMs = Math.max(0, Math.floor(options.completedJobTtlMs ?? COMPLETED_JOB_TTL_MS));\n\t}\n\n\tget disposed(): boolean {\n\t\treturn this.#disposed;\n\t}\n\n\tget atCapacity(): boolean {\n\t\tlet running = 0;\n\t\tfor (const job of this.#jobs.values()) if (job.status === \"running\") running += 1;\n\t\treturn running >= this.#maxRunningJobs;\n\t}\n\n\n\tregisterSession(): symbol {\n\t\tif (this.#disposed) throw new Error(\"Async job manager is disposed\");\n\t\tconst sessionId = Symbol(\"async-job-session\");\n\t\tthis.#sessions.set(sessionId, { disposed: false, activeJobIds: new Set() });\n\t\treturn sessionId;\n\t}\n\n\treleaseSession(sessionId: symbol): void {\n\t\tconst session = this.#sessions.get(sessionId);\n\t\tif (!session) return;\n\t\tsession.disposed = true;\n\t\tthis.acknowledgeDeliveries([...session.activeJobIds]);\n\t\tthis.#sessions.delete(sessionId);\n\t\tif (this.#sessions.size === 0) this.dispose();\n\t}\n\n\tisSessionDisposed(sessionId: symbol): boolean {\n\t\treturn this.#disposed || this.#sessions.get(sessionId)?.disposed !== false;\n\t}\n\tregisterBashJob(job: ManagedBashJob, onComplete?: AsyncJobDeliveryHandler, sessionId?: symbol): void {\n\t\tif (this.#disposed) throw new Error(\"Async job manager is disposed\");\n\t\tthis.#pruneRetention();\n\t\tif (this.atCapacity) throw new Error(`Background job limit reached (${this.#maxRunningJobs}). Wait for running jobs to finish or cancel one.`);\n\t\tif (sessionId !== undefined && this.isSessionDisposed(sessionId)) throw new Error(\"Async job session is disposed\");\n\t\tthis.#suppressedDeliveries.delete(job.jobId);\n\t\tif (onComplete) this.#deliveryHandlers.set(job.jobId, onComplete);\n\t\telse this.#deliveryHandlers.delete(job.jobId);\n\t\tif (sessionId !== undefined) {\n\t\t\tthis.#jobSessions.set(job.jobId, sessionId);\n\t\t\tthis.#sessions.get(sessionId)?.activeJobIds.add(job.jobId);\n\t\t} else this.#jobSessions.delete(job.jobId);\n\t\tthis.#jobs.set(job.jobId, job);\n\t\tthis.#pruneRetention();\n\t}\n\n\tcompleteBashJob(job: ManagedBashJob): void {\n\t\tif (this.#disposed || this.#suppressedDeliveries.has(job.jobId)) return;\n\t\tif (job.status === \"running\") return;\n\t\tthis.#jobs.set(job.jobId, job);\n\t\tthis.#pruneRetention();\n\t\tif (this.#jobs.has(job.jobId)) this.#enqueueDelivery(job as ManagedAsyncBashJob);\n\t}\n\n\tacknowledgeDeliveries(jobIds: readonly string[]): void {\n\t\tfor (const jobId of jobIds) {\n\t\t\tthis.#suppressedDeliveries.add(jobId);\n\t\t\tfor (let index = this.#deliveries.length - 1; index >= 0; index -= 1) {\n\t\t\t\tif (this.#deliveries[index]?.jobId === jobId) this.#deliveries.splice(index, 1);\n\t\t\t}\n\t\t\tthis.#deliveryHandlers.delete(jobId);\n\t\t\tthis.#completeTrackedJob(jobId);\n\t\t}\n\t\tthis.#pruneRetention();\n\t}\n\n\tisDeliverySuppressed(jobId: string): boolean {\n\t\treturn this.#suppressedDeliveries.has(jobId);\n\t}\n\n\tdeliveryState(): { queued: number; delivering: boolean; pendingJobIds: string[] } {\n\t\tthis.#pruneRetention();\n\t\treturn {\n\t\t\tqueued: this.#deliveries.length,\n\t\t\tdelivering: this.#runningDeliveryLoop || this.#inFlightDeliveries.size > 0,\n\t\t\tpendingJobIds: [...this.#deliveries.map((delivery) => delivery.jobId), ...this.#inFlightDeliveries.keys()],\n\t\t};\n\t}\n\n\tretentionState(): { jobs: number; suppressions: number; handlers: number; queued: number; sessions: number } {\n\t\tthis.#pruneRetention();\n\t\treturn { jobs: this.#jobs.size, suppressions: this.#suppressedDeliveries.size, handlers: this.#deliveryHandlers.size, queued: this.#deliveries.length + this.#inFlightDeliveries.size, sessions: this.#sessions.size };\n\t}\n\n\tdispose(): void {\n\t\tthis.#disposed = true;\n\t\tif (this.#timer) clearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tthis.#deliveries.length = 0;\n\t\tthis.#inFlightDeliveries.clear();\n\t\tthis.#deliveryHandlers.clear();\n\t\tthis.#jobSessions.clear();\n\t\tthis.#sessions.clear();\n\t\tthis.#jobs.clear();\n\t\tthis.#suppressedDeliveries.clear();\n\t}\n\n\t#enqueueDelivery(job: ManagedAsyncBashJob): void {\n\t\tif (this.#inFlightDeliveries.has(job.jobId)) return;\n\t\tfor (let index = this.#deliveries.length - 1; index >= 0; index -= 1) if (this.#deliveries[index]?.jobId === job.jobId) this.#deliveries.splice(index, 1);\n\t\tthis.#deliveries.push({ jobId: job.jobId, message: formatAsyncResultForFollowUp(job), attempt: 0, nextAttemptAt: Date.now() });\n\t\tthis.#pruneRetention();\n\t\tthis.#scheduleDeliveryLoop(25);\n\t}\n\n\n\t#completeTrackedJob(jobId: string): void {\n\t\tconst sessionId = this.#jobSessions.get(jobId);\n\t\tif (sessionId !== undefined) this.#sessions.get(sessionId)?.activeJobIds.delete(jobId);\n\t\tthis.#jobSessions.delete(jobId);\n\t}\n\t#pruneRetention(now = Date.now()): void {\n\t\tfor (const [jobId, job] of this.#jobs) {\n\t\t\tif (job.status !== \"running\" && job.endedAt !== undefined && now - job.endedAt > this.#completedJobTtlMs) {\n\t\t\t\tthis.#jobs.delete(jobId);\n\t\t\t\tthis.#completeTrackedJob(jobId);\n\t\t\t}\n\t\t}\n\t\tfor (const jobId of this.#suppressedDeliveries) {\n\t\t\tif (!this.#jobs.has(jobId)) this.#suppressedDeliveries.delete(jobId);\n\t\t}\n\t\tconst oldestTerminalJobs = [...this.#jobs.values()]\n\t\t\t.filter((job) => job.status !== \"running\")\n\t\t\t.sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt));\n\t\twhile (this.#jobs.size > this.#maxRetainedJobs && oldestTerminalJobs.length > 0) {\n\t\t\tconst job = oldestTerminalJobs.shift();\n\t\t\tif (job) {\n\t\t\t\tthis.#jobs.delete(job.jobId);\n\t\t\t\tthis.#completeTrackedJob(job.jobId);\n\t\t\t}\n\t\t}\n\t\tfor (let index = this.#deliveries.length - 1; index >= 0; index -= 1) {\n\t\t\tconst delivery = this.#deliveries[index];\n\t\t\tif (!delivery || !this.#jobs.has(delivery.jobId) || this.#suppressedDeliveries.has(delivery.jobId)) this.#deliveries.splice(index, 1);\n\t\t}\n\t\twhile (this.#deliveries.length + this.#inFlightDeliveries.size > this.#maxRetainedJobs && this.#deliveries.length > 0) this.#deliveries.shift();\n\t\tfor (const jobId of this.#deliveryHandlers.keys()) if (!this.#jobs.has(jobId) || this.#suppressedDeliveries.has(jobId)) this.#deliveryHandlers.delete(jobId);\n\t\tfor (const session of this.#sessions.values()) for (const jobId of session.activeJobIds) if (!this.#jobs.has(jobId)) session.activeJobIds.delete(jobId);\n\t}\n\n\t#scheduleDeliveryLoop(delayMs: number): void {\n\t\tif (this.#disposed) return;\n\t\tif (this.#timer) clearTimeout(this.#timer);\n\t\tthis.#timer = setTimeout(() => {\n\t\t\tthis.#timer = undefined;\n\t\t\tvoid this.#runDeliveryLoop();\n\t\t}, delayMs);\n\t\tthis.#timer.unref?.();\n\t}\n\n\tasync #runDeliveryLoop(): Promise<void> {\n\t\tif (this.#runningDeliveryLoop || this.#disposed) return;\n\t\tthis.#runningDeliveryLoop = true;\n\t\ttry {\n\t\t\twhile (!this.#disposed) {\n\t\t\t\tconst now = Date.now();\n\t\t\t\tconst delivery = this.#deliveries.find((candidate) => candidate.nextAttemptAt <= now);\n\t\t\t\tif (!delivery) break;\n\t\t\t\tthis.#deliveries.splice(this.#deliveries.indexOf(delivery), 1);\n\t\t\t\tthis.#startDelivery(delivery);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#runningDeliveryLoop = false;\n\t\t}\n\t\tthis.#scheduleNextDelivery();\n\t}\n\n\t#startDelivery(delivery: Delivery): void {\n\t\tif (this.#suppressedDeliveries.has(delivery.jobId) || !this.#jobs.has(delivery.jobId) || this.#inFlightDeliveries.has(delivery.jobId)) return;\n\t\tconst handler = this.#deliveryHandlers.get(delivery.jobId) ?? this.#onJobComplete;\n\t\tthis.#inFlightDeliveries.set(delivery.jobId, delivery);\n\t\tdelivery.promise = (async () => handler(delivery.message))()\n\t\t\t.then(() => {\n\t\t\t\tif (!this.#suppressedDeliveries.has(delivery.jobId) && this.#jobs.has(delivery.jobId)) {\n\t\t\t\t\tthis.#deliveryHandlers.delete(delivery.jobId);\n\t\t\t\t\tthis.#completeTrackedJob(delivery.jobId);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tif (this.#disposed || this.#suppressedDeliveries.has(delivery.jobId) || !this.#jobs.has(delivery.jobId)) return;\n\t\t\t\tdelivery.attempt += 1;\n\t\t\t\tconst jitter = Math.floor(Math.random() * DELIVERY_RETRY_JITTER_MS);\n\t\t\t\tdelivery.nextAttemptAt = Date.now() + Math.min(DELIVERY_RETRY_MAX_MS, DELIVERY_RETRY_BASE_MS * 2 ** delivery.attempt) + jitter;\n\t\t\t\tthis.#deliveries.push(delivery);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tthis.#inFlightDeliveries.delete(delivery.jobId);\n\t\t\t\tthis.#pruneRetention();\n\t\t\t\tthis.#scheduleNextDelivery();\n\t\t\t});\n\t}\n\n\t#scheduleNextDelivery(): void {\n\t\tconst next = this.#deliveries.reduce<number | undefined>((soonest, delivery) => soonest === undefined ? delivery.nextAttemptAt : Math.min(soonest, delivery.nextAttemptAt), undefined);\n\t\tif (next !== undefined) this.#scheduleDeliveryLoop(Math.max(0, next - Date.now()));\n\t}\n}\n"]}
@@ -0,0 +1,254 @@
1
+ import { COMPLETED_JOB_TTL_MS, MAX_MANAGED_BASH_JOBS } from "../tools/bash-async-jobs.js";
2
+ import { formatAsyncResultForFollowUp } from "./format.js";
3
+ const DEFAULT_MAX_RUNNING_JOBS = 15;
4
+ const DELIVERY_RETRY_BASE_MS = 500;
5
+ const DELIVERY_RETRY_MAX_MS = 30_000;
6
+ const DELIVERY_RETRY_JITTER_MS = 200;
7
+ export class AsyncJobManager {
8
+ static #instance;
9
+ static instance() {
10
+ return AsyncJobManager.#instance;
11
+ }
12
+ static setInstance(value) {
13
+ AsyncJobManager.#instance = value;
14
+ }
15
+ static resetForTests() {
16
+ AsyncJobManager.#instance = undefined;
17
+ }
18
+ #jobs = new Map();
19
+ #deliveries = [];
20
+ #suppressedDeliveries = new Set();
21
+ #inFlightDeliveries = new Map();
22
+ #deliveryHandlers = new Map();
23
+ #jobSessions = new Map();
24
+ #sessions = new Map();
25
+ #onJobComplete;
26
+ #maxRunningJobs;
27
+ #maxRetainedJobs;
28
+ #completedJobTtlMs;
29
+ #timer;
30
+ #disposed = false;
31
+ #runningDeliveryLoop = false;
32
+ constructor(options) {
33
+ this.#onJobComplete = options.onJobComplete;
34
+ this.#maxRunningJobs = Math.max(1, Math.floor(options.maxRunningJobs ?? DEFAULT_MAX_RUNNING_JOBS));
35
+ this.#maxRetainedJobs = Math.max(1, Math.floor(options.maxRetainedJobs ?? MAX_MANAGED_BASH_JOBS));
36
+ this.#completedJobTtlMs = Math.max(0, Math.floor(options.completedJobTtlMs ?? COMPLETED_JOB_TTL_MS));
37
+ }
38
+ get disposed() {
39
+ return this.#disposed;
40
+ }
41
+ get atCapacity() {
42
+ let running = 0;
43
+ for (const job of this.#jobs.values())
44
+ if (job.status === "running")
45
+ running += 1;
46
+ return running >= this.#maxRunningJobs;
47
+ }
48
+ registerSession() {
49
+ if (this.#disposed)
50
+ throw new Error("Async job manager is disposed");
51
+ const sessionId = Symbol("async-job-session");
52
+ this.#sessions.set(sessionId, { disposed: false, activeJobIds: new Set() });
53
+ return sessionId;
54
+ }
55
+ releaseSession(sessionId) {
56
+ const session = this.#sessions.get(sessionId);
57
+ if (!session)
58
+ return;
59
+ session.disposed = true;
60
+ this.acknowledgeDeliveries([...session.activeJobIds]);
61
+ this.#sessions.delete(sessionId);
62
+ if (this.#sessions.size === 0)
63
+ this.dispose();
64
+ }
65
+ isSessionDisposed(sessionId) {
66
+ return this.#disposed || this.#sessions.get(sessionId)?.disposed !== false;
67
+ }
68
+ registerBashJob(job, onComplete, sessionId) {
69
+ if (this.#disposed)
70
+ throw new Error("Async job manager is disposed");
71
+ this.#pruneRetention();
72
+ if (this.atCapacity)
73
+ throw new Error(`Background job limit reached (${this.#maxRunningJobs}). Wait for running jobs to finish or cancel one.`);
74
+ if (sessionId !== undefined && this.isSessionDisposed(sessionId))
75
+ throw new Error("Async job session is disposed");
76
+ this.#suppressedDeliveries.delete(job.jobId);
77
+ if (onComplete)
78
+ this.#deliveryHandlers.set(job.jobId, onComplete);
79
+ else
80
+ this.#deliveryHandlers.delete(job.jobId);
81
+ if (sessionId !== undefined) {
82
+ this.#jobSessions.set(job.jobId, sessionId);
83
+ this.#sessions.get(sessionId)?.activeJobIds.add(job.jobId);
84
+ }
85
+ else
86
+ this.#jobSessions.delete(job.jobId);
87
+ this.#jobs.set(job.jobId, job);
88
+ this.#pruneRetention();
89
+ }
90
+ completeBashJob(job) {
91
+ if (this.#disposed || this.#suppressedDeliveries.has(job.jobId))
92
+ return;
93
+ if (job.status === "running")
94
+ return;
95
+ this.#jobs.set(job.jobId, job);
96
+ this.#pruneRetention();
97
+ if (this.#jobs.has(job.jobId))
98
+ this.#enqueueDelivery(job);
99
+ }
100
+ acknowledgeDeliveries(jobIds) {
101
+ for (const jobId of jobIds) {
102
+ this.#suppressedDeliveries.add(jobId);
103
+ for (let index = this.#deliveries.length - 1; index >= 0; index -= 1) {
104
+ if (this.#deliveries[index]?.jobId === jobId)
105
+ this.#deliveries.splice(index, 1);
106
+ }
107
+ this.#deliveryHandlers.delete(jobId);
108
+ this.#completeTrackedJob(jobId);
109
+ }
110
+ this.#pruneRetention();
111
+ }
112
+ isDeliverySuppressed(jobId) {
113
+ return this.#suppressedDeliveries.has(jobId);
114
+ }
115
+ deliveryState() {
116
+ this.#pruneRetention();
117
+ return {
118
+ queued: this.#deliveries.length,
119
+ delivering: this.#runningDeliveryLoop || this.#inFlightDeliveries.size > 0,
120
+ pendingJobIds: [...this.#deliveries.map((delivery) => delivery.jobId), ...this.#inFlightDeliveries.keys()],
121
+ };
122
+ }
123
+ retentionState() {
124
+ this.#pruneRetention();
125
+ return { jobs: this.#jobs.size, suppressions: this.#suppressedDeliveries.size, handlers: this.#deliveryHandlers.size, queued: this.#deliveries.length + this.#inFlightDeliveries.size, sessions: this.#sessions.size };
126
+ }
127
+ dispose() {
128
+ this.#disposed = true;
129
+ if (this.#timer)
130
+ clearTimeout(this.#timer);
131
+ this.#timer = undefined;
132
+ this.#deliveries.length = 0;
133
+ this.#inFlightDeliveries.clear();
134
+ this.#deliveryHandlers.clear();
135
+ this.#jobSessions.clear();
136
+ this.#sessions.clear();
137
+ this.#jobs.clear();
138
+ this.#suppressedDeliveries.clear();
139
+ }
140
+ #enqueueDelivery(job) {
141
+ if (this.#inFlightDeliveries.has(job.jobId))
142
+ return;
143
+ for (let index = this.#deliveries.length - 1; index >= 0; index -= 1)
144
+ if (this.#deliveries[index]?.jobId === job.jobId)
145
+ this.#deliveries.splice(index, 1);
146
+ this.#deliveries.push({ jobId: job.jobId, message: formatAsyncResultForFollowUp(job), attempt: 0, nextAttemptAt: Date.now() });
147
+ this.#pruneRetention();
148
+ this.#scheduleDeliveryLoop(25);
149
+ }
150
+ #completeTrackedJob(jobId) {
151
+ const sessionId = this.#jobSessions.get(jobId);
152
+ if (sessionId !== undefined)
153
+ this.#sessions.get(sessionId)?.activeJobIds.delete(jobId);
154
+ this.#jobSessions.delete(jobId);
155
+ }
156
+ #pruneRetention(now = Date.now()) {
157
+ for (const [jobId, job] of this.#jobs) {
158
+ if (job.status !== "running" && job.endedAt !== undefined && now - job.endedAt > this.#completedJobTtlMs) {
159
+ this.#jobs.delete(jobId);
160
+ this.#completeTrackedJob(jobId);
161
+ }
162
+ }
163
+ for (const jobId of this.#suppressedDeliveries) {
164
+ if (!this.#jobs.has(jobId))
165
+ this.#suppressedDeliveries.delete(jobId);
166
+ }
167
+ const oldestTerminalJobs = [...this.#jobs.values()]
168
+ .filter((job) => job.status !== "running")
169
+ .sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt));
170
+ while (this.#jobs.size > this.#maxRetainedJobs && oldestTerminalJobs.length > 0) {
171
+ const job = oldestTerminalJobs.shift();
172
+ if (job) {
173
+ this.#jobs.delete(job.jobId);
174
+ this.#completeTrackedJob(job.jobId);
175
+ }
176
+ }
177
+ for (let index = this.#deliveries.length - 1; index >= 0; index -= 1) {
178
+ const delivery = this.#deliveries[index];
179
+ if (!delivery || !this.#jobs.has(delivery.jobId) || this.#suppressedDeliveries.has(delivery.jobId))
180
+ this.#deliveries.splice(index, 1);
181
+ }
182
+ while (this.#deliveries.length + this.#inFlightDeliveries.size > this.#maxRetainedJobs && this.#deliveries.length > 0)
183
+ this.#deliveries.shift();
184
+ for (const jobId of this.#deliveryHandlers.keys())
185
+ if (!this.#jobs.has(jobId) || this.#suppressedDeliveries.has(jobId))
186
+ this.#deliveryHandlers.delete(jobId);
187
+ for (const session of this.#sessions.values())
188
+ for (const jobId of session.activeJobIds)
189
+ if (!this.#jobs.has(jobId))
190
+ session.activeJobIds.delete(jobId);
191
+ }
192
+ #scheduleDeliveryLoop(delayMs) {
193
+ if (this.#disposed)
194
+ return;
195
+ if (this.#timer)
196
+ clearTimeout(this.#timer);
197
+ this.#timer = setTimeout(() => {
198
+ this.#timer = undefined;
199
+ void this.#runDeliveryLoop();
200
+ }, delayMs);
201
+ this.#timer.unref?.();
202
+ }
203
+ async #runDeliveryLoop() {
204
+ if (this.#runningDeliveryLoop || this.#disposed)
205
+ return;
206
+ this.#runningDeliveryLoop = true;
207
+ try {
208
+ while (!this.#disposed) {
209
+ const now = Date.now();
210
+ const delivery = this.#deliveries.find((candidate) => candidate.nextAttemptAt <= now);
211
+ if (!delivery)
212
+ break;
213
+ this.#deliveries.splice(this.#deliveries.indexOf(delivery), 1);
214
+ this.#startDelivery(delivery);
215
+ }
216
+ }
217
+ finally {
218
+ this.#runningDeliveryLoop = false;
219
+ }
220
+ this.#scheduleNextDelivery();
221
+ }
222
+ #startDelivery(delivery) {
223
+ if (this.#suppressedDeliveries.has(delivery.jobId) || !this.#jobs.has(delivery.jobId) || this.#inFlightDeliveries.has(delivery.jobId))
224
+ return;
225
+ const handler = this.#deliveryHandlers.get(delivery.jobId) ?? this.#onJobComplete;
226
+ this.#inFlightDeliveries.set(delivery.jobId, delivery);
227
+ delivery.promise = (async () => handler(delivery.message))()
228
+ .then(() => {
229
+ if (!this.#suppressedDeliveries.has(delivery.jobId) && this.#jobs.has(delivery.jobId)) {
230
+ this.#deliveryHandlers.delete(delivery.jobId);
231
+ this.#completeTrackedJob(delivery.jobId);
232
+ }
233
+ })
234
+ .catch(() => {
235
+ if (this.#disposed || this.#suppressedDeliveries.has(delivery.jobId) || !this.#jobs.has(delivery.jobId))
236
+ return;
237
+ delivery.attempt += 1;
238
+ const jitter = Math.floor(Math.random() * DELIVERY_RETRY_JITTER_MS);
239
+ delivery.nextAttemptAt = Date.now() + Math.min(DELIVERY_RETRY_MAX_MS, DELIVERY_RETRY_BASE_MS * 2 ** delivery.attempt) + jitter;
240
+ this.#deliveries.push(delivery);
241
+ })
242
+ .finally(() => {
243
+ this.#inFlightDeliveries.delete(delivery.jobId);
244
+ this.#pruneRetention();
245
+ this.#scheduleNextDelivery();
246
+ });
247
+ }
248
+ #scheduleNextDelivery() {
249
+ const next = this.#deliveries.reduce((soonest, delivery) => soonest === undefined ? delivery.nextAttemptAt : Math.min(soonest, delivery.nextAttemptAt), undefined);
250
+ if (next !== undefined)
251
+ this.#scheduleDeliveryLoop(Math.max(0, next - Date.now()));
252
+ }
253
+ }
254
+ //# sourceMappingURL=job-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-manager.js","sourceRoot":"","sources":["../../../src/core/async/job-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { COMPLETED_JOB_TTL_MS, MAX_MANAGED_BASH_JOBS, type ManagedBashJob } from \"../tools/bash-async-jobs.js\";\nimport { formatAsyncResultForFollowUp } from \"./format.js\";\nimport type { AsyncJobDeliveryCallback, AsyncJobDeliveryHandler, AsyncJobDeliveryMessage, ManagedAsyncBashJob } from \"./types.js\";\n\nconst DEFAULT_MAX_RUNNING_JOBS = 15;\nconst DELIVERY_RETRY_BASE_MS = 500;\nconst DELIVERY_RETRY_MAX_MS = 30_000;\nconst DELIVERY_RETRY_JITTER_MS = 200;\n\ninterface AsyncJobManagerOptions {\n\tonJobComplete: AsyncJobDeliveryCallback;\n\tmaxRunningJobs?: number;\n\tmaxRetainedJobs?: number;\n\tcompletedJobTtlMs?: number;\n}\n\ninterface Delivery {\n\tjobId: string;\n\tmessage: AsyncJobDeliveryMessage;\n\tattempt: number;\n\tnextAttemptAt: number;\n\tpromise?: Promise<void>;\n}\n\ninterface RegisteredSession {\n\tdisposed: boolean;\n\tactiveJobIds: Set<string>;\n}\n\nexport class AsyncJobManager {\n\tstatic #instance: AsyncJobManager | undefined;\n\n\tstatic instance(): AsyncJobManager | undefined {\n\t\treturn AsyncJobManager.#instance;\n\t}\n\n\tstatic setInstance(value: AsyncJobManager | undefined): void {\n\t\tAsyncJobManager.#instance = value;\n\t}\n\n\tstatic resetForTests(): void {\n\t\tAsyncJobManager.#instance = undefined;\n\t}\n\n\treadonly #jobs = new Map<string, ManagedBashJob>();\n\treadonly #deliveries: Delivery[] = [];\n\treadonly #suppressedDeliveries = new Set<string>();\n\treadonly #inFlightDeliveries = new Map<string, Delivery>();\n\treadonly #deliveryHandlers = new Map<string, AsyncJobDeliveryHandler>();\n\treadonly #jobSessions = new Map<string, symbol>();\n\treadonly #sessions = new Map<symbol, RegisteredSession>();\n\treadonly #onJobComplete: AsyncJobDeliveryCallback;\n\treadonly #maxRunningJobs: number;\n\treadonly #maxRetainedJobs: number;\n\treadonly #completedJobTtlMs: number;\n\t#timer: NodeJS.Timeout | undefined;\n\t#disposed = false;\n\t#runningDeliveryLoop = false;\n\n\tconstructor(options: AsyncJobManagerOptions) {\n\t\tthis.#onJobComplete = options.onJobComplete;\n\t\tthis.#maxRunningJobs = Math.max(1, Math.floor(options.maxRunningJobs ?? DEFAULT_MAX_RUNNING_JOBS));\n\t\tthis.#maxRetainedJobs = Math.max(1, Math.floor(options.maxRetainedJobs ?? MAX_MANAGED_BASH_JOBS));\n\t\tthis.#completedJobTtlMs = Math.max(0, Math.floor(options.completedJobTtlMs ?? COMPLETED_JOB_TTL_MS));\n\t}\n\n\tget disposed(): boolean {\n\t\treturn this.#disposed;\n\t}\n\n\tget atCapacity(): boolean {\n\t\tlet running = 0;\n\t\tfor (const job of this.#jobs.values()) if (job.status === \"running\") running += 1;\n\t\treturn running >= this.#maxRunningJobs;\n\t}\n\n\n\tregisterSession(): symbol {\n\t\tif (this.#disposed) throw new Error(\"Async job manager is disposed\");\n\t\tconst sessionId = Symbol(\"async-job-session\");\n\t\tthis.#sessions.set(sessionId, { disposed: false, activeJobIds: new Set() });\n\t\treturn sessionId;\n\t}\n\n\treleaseSession(sessionId: symbol): void {\n\t\tconst session = this.#sessions.get(sessionId);\n\t\tif (!session) return;\n\t\tsession.disposed = true;\n\t\tthis.acknowledgeDeliveries([...session.activeJobIds]);\n\t\tthis.#sessions.delete(sessionId);\n\t\tif (this.#sessions.size === 0) this.dispose();\n\t}\n\n\tisSessionDisposed(sessionId: symbol): boolean {\n\t\treturn this.#disposed || this.#sessions.get(sessionId)?.disposed !== false;\n\t}\n\tregisterBashJob(job: ManagedBashJob, onComplete?: AsyncJobDeliveryHandler, sessionId?: symbol): void {\n\t\tif (this.#disposed) throw new Error(\"Async job manager is disposed\");\n\t\tthis.#pruneRetention();\n\t\tif (this.atCapacity) throw new Error(`Background job limit reached (${this.#maxRunningJobs}). Wait for running jobs to finish or cancel one.`);\n\t\tif (sessionId !== undefined && this.isSessionDisposed(sessionId)) throw new Error(\"Async job session is disposed\");\n\t\tthis.#suppressedDeliveries.delete(job.jobId);\n\t\tif (onComplete) this.#deliveryHandlers.set(job.jobId, onComplete);\n\t\telse this.#deliveryHandlers.delete(job.jobId);\n\t\tif (sessionId !== undefined) {\n\t\t\tthis.#jobSessions.set(job.jobId, sessionId);\n\t\t\tthis.#sessions.get(sessionId)?.activeJobIds.add(job.jobId);\n\t\t} else this.#jobSessions.delete(job.jobId);\n\t\tthis.#jobs.set(job.jobId, job);\n\t\tthis.#pruneRetention();\n\t}\n\n\tcompleteBashJob(job: ManagedBashJob): void {\n\t\tif (this.#disposed || this.#suppressedDeliveries.has(job.jobId)) return;\n\t\tif (job.status === \"running\") return;\n\t\tthis.#jobs.set(job.jobId, job);\n\t\tthis.#pruneRetention();\n\t\tif (this.#jobs.has(job.jobId)) this.#enqueueDelivery(job as ManagedAsyncBashJob);\n\t}\n\n\tacknowledgeDeliveries(jobIds: readonly string[]): void {\n\t\tfor (const jobId of jobIds) {\n\t\t\tthis.#suppressedDeliveries.add(jobId);\n\t\t\tfor (let index = this.#deliveries.length - 1; index >= 0; index -= 1) {\n\t\t\t\tif (this.#deliveries[index]?.jobId === jobId) this.#deliveries.splice(index, 1);\n\t\t\t}\n\t\t\tthis.#deliveryHandlers.delete(jobId);\n\t\t\tthis.#completeTrackedJob(jobId);\n\t\t}\n\t\tthis.#pruneRetention();\n\t}\n\n\tisDeliverySuppressed(jobId: string): boolean {\n\t\treturn this.#suppressedDeliveries.has(jobId);\n\t}\n\n\tdeliveryState(): { queued: number; delivering: boolean; pendingJobIds: string[] } {\n\t\tthis.#pruneRetention();\n\t\treturn {\n\t\t\tqueued: this.#deliveries.length,\n\t\t\tdelivering: this.#runningDeliveryLoop || this.#inFlightDeliveries.size > 0,\n\t\t\tpendingJobIds: [...this.#deliveries.map((delivery) => delivery.jobId), ...this.#inFlightDeliveries.keys()],\n\t\t};\n\t}\n\n\tretentionState(): { jobs: number; suppressions: number; handlers: number; queued: number; sessions: number } {\n\t\tthis.#pruneRetention();\n\t\treturn { jobs: this.#jobs.size, suppressions: this.#suppressedDeliveries.size, handlers: this.#deliveryHandlers.size, queued: this.#deliveries.length + this.#inFlightDeliveries.size, sessions: this.#sessions.size };\n\t}\n\n\tdispose(): void {\n\t\tthis.#disposed = true;\n\t\tif (this.#timer) clearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tthis.#deliveries.length = 0;\n\t\tthis.#inFlightDeliveries.clear();\n\t\tthis.#deliveryHandlers.clear();\n\t\tthis.#jobSessions.clear();\n\t\tthis.#sessions.clear();\n\t\tthis.#jobs.clear();\n\t\tthis.#suppressedDeliveries.clear();\n\t}\n\n\t#enqueueDelivery(job: ManagedAsyncBashJob): void {\n\t\tif (this.#inFlightDeliveries.has(job.jobId)) return;\n\t\tfor (let index = this.#deliveries.length - 1; index >= 0; index -= 1) if (this.#deliveries[index]?.jobId === job.jobId) this.#deliveries.splice(index, 1);\n\t\tthis.#deliveries.push({ jobId: job.jobId, message: formatAsyncResultForFollowUp(job), attempt: 0, nextAttemptAt: Date.now() });\n\t\tthis.#pruneRetention();\n\t\tthis.#scheduleDeliveryLoop(25);\n\t}\n\n\n\t#completeTrackedJob(jobId: string): void {\n\t\tconst sessionId = this.#jobSessions.get(jobId);\n\t\tif (sessionId !== undefined) this.#sessions.get(sessionId)?.activeJobIds.delete(jobId);\n\t\tthis.#jobSessions.delete(jobId);\n\t}\n\t#pruneRetention(now = Date.now()): void {\n\t\tfor (const [jobId, job] of this.#jobs) {\n\t\t\tif (job.status !== \"running\" && job.endedAt !== undefined && now - job.endedAt > this.#completedJobTtlMs) {\n\t\t\t\tthis.#jobs.delete(jobId);\n\t\t\t\tthis.#completeTrackedJob(jobId);\n\t\t\t}\n\t\t}\n\t\tfor (const jobId of this.#suppressedDeliveries) {\n\t\t\tif (!this.#jobs.has(jobId)) this.#suppressedDeliveries.delete(jobId);\n\t\t}\n\t\tconst oldestTerminalJobs = [...this.#jobs.values()]\n\t\t\t.filter((job) => job.status !== \"running\")\n\t\t\t.sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt));\n\t\twhile (this.#jobs.size > this.#maxRetainedJobs && oldestTerminalJobs.length > 0) {\n\t\t\tconst job = oldestTerminalJobs.shift();\n\t\t\tif (job) {\n\t\t\t\tthis.#jobs.delete(job.jobId);\n\t\t\t\tthis.#completeTrackedJob(job.jobId);\n\t\t\t}\n\t\t}\n\t\tfor (let index = this.#deliveries.length - 1; index >= 0; index -= 1) {\n\t\t\tconst delivery = this.#deliveries[index];\n\t\t\tif (!delivery || !this.#jobs.has(delivery.jobId) || this.#suppressedDeliveries.has(delivery.jobId)) this.#deliveries.splice(index, 1);\n\t\t}\n\t\twhile (this.#deliveries.length + this.#inFlightDeliveries.size > this.#maxRetainedJobs && this.#deliveries.length > 0) this.#deliveries.shift();\n\t\tfor (const jobId of this.#deliveryHandlers.keys()) if (!this.#jobs.has(jobId) || this.#suppressedDeliveries.has(jobId)) this.#deliveryHandlers.delete(jobId);\n\t\tfor (const session of this.#sessions.values()) for (const jobId of session.activeJobIds) if (!this.#jobs.has(jobId)) session.activeJobIds.delete(jobId);\n\t}\n\n\t#scheduleDeliveryLoop(delayMs: number): void {\n\t\tif (this.#disposed) return;\n\t\tif (this.#timer) clearTimeout(this.#timer);\n\t\tthis.#timer = setTimeout(() => {\n\t\t\tthis.#timer = undefined;\n\t\t\tvoid this.#runDeliveryLoop();\n\t\t}, delayMs);\n\t\tthis.#timer.unref?.();\n\t}\n\n\tasync #runDeliveryLoop(): Promise<void> {\n\t\tif (this.#runningDeliveryLoop || this.#disposed) return;\n\t\tthis.#runningDeliveryLoop = true;\n\t\ttry {\n\t\t\twhile (!this.#disposed) {\n\t\t\t\tconst now = Date.now();\n\t\t\t\tconst delivery = this.#deliveries.find((candidate) => candidate.nextAttemptAt <= now);\n\t\t\t\tif (!delivery) break;\n\t\t\t\tthis.#deliveries.splice(this.#deliveries.indexOf(delivery), 1);\n\t\t\t\tthis.#startDelivery(delivery);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#runningDeliveryLoop = false;\n\t\t}\n\t\tthis.#scheduleNextDelivery();\n\t}\n\n\t#startDelivery(delivery: Delivery): void {\n\t\tif (this.#suppressedDeliveries.has(delivery.jobId) || !this.#jobs.has(delivery.jobId) || this.#inFlightDeliveries.has(delivery.jobId)) return;\n\t\tconst handler = this.#deliveryHandlers.get(delivery.jobId) ?? this.#onJobComplete;\n\t\tthis.#inFlightDeliveries.set(delivery.jobId, delivery);\n\t\tdelivery.promise = (async () => handler(delivery.message))()\n\t\t\t.then(() => {\n\t\t\t\tif (!this.#suppressedDeliveries.has(delivery.jobId) && this.#jobs.has(delivery.jobId)) {\n\t\t\t\t\tthis.#deliveryHandlers.delete(delivery.jobId);\n\t\t\t\t\tthis.#completeTrackedJob(delivery.jobId);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tif (this.#disposed || this.#suppressedDeliveries.has(delivery.jobId) || !this.#jobs.has(delivery.jobId)) return;\n\t\t\t\tdelivery.attempt += 1;\n\t\t\t\tconst jitter = Math.floor(Math.random() * DELIVERY_RETRY_JITTER_MS);\n\t\t\t\tdelivery.nextAttemptAt = Date.now() + Math.min(DELIVERY_RETRY_MAX_MS, DELIVERY_RETRY_BASE_MS * 2 ** delivery.attempt) + jitter;\n\t\t\t\tthis.#deliveries.push(delivery);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tthis.#inFlightDeliveries.delete(delivery.jobId);\n\t\t\t\tthis.#pruneRetention();\n\t\t\t\tthis.#scheduleNextDelivery();\n\t\t\t});\n\t}\n\n\t#scheduleNextDelivery(): void {\n\t\tconst next = this.#deliveries.reduce<number | undefined>((soonest, delivery) => soonest === undefined ? delivery.nextAttemptAt : Math.min(soonest, delivery.nextAttemptAt), undefined);\n\t\tif (next !== undefined) this.#scheduleDeliveryLoop(Math.max(0, next - Date.now()));\n\t}\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { CustomMessage } from "../messages.js";
2
+ import type { SendMessageOptions } from "../extensions/index.js";
3
+ import { AsyncJobManager } from "./job-manager.js";
4
+ import type { AsyncJobDeliveryHandler } from "./types.js";
5
+ export interface SessionAsyncJobManagerHandle {
6
+ manager: AsyncJobManager;
7
+ owns: boolean;
8
+ sessionId: symbol;
9
+ }
10
+ interface AsyncDeliverySession {
11
+ readonly isStreaming?: boolean;
12
+ sendCustomMessage<T>(message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details">, options?: SendMessageOptions): Promise<void>;
13
+ }
14
+ export declare function createSessionAsyncDeliveryHandler(session: AsyncDeliverySession, manager?: AsyncJobManager, sessionId?: symbol): AsyncJobDeliveryHandler;
15
+ export declare function createSessionAsyncJobManager(session: AsyncDeliverySession): SessionAsyncJobManagerHandle;
16
+ export declare function disposeSessionAsyncJobManager(manager: AsyncJobManager | undefined, sessionId: symbol | undefined): void;
17
+ export {};
18
+ //# sourceMappingURL=session-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/core/async/session-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAA2B,MAAM,YAAY,CAAC;AAEnF,MAAM,WAAW,4BAA4B;IAC5C,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CAClB;AACD,UAAU,oBAAoB;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,CAAC,EAClB,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC,EACjF,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB;AAiCD,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,uBAAuB,CAUvJ;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,oBAAoB,GAAG,4BAA4B,CASxG;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAIvH","sourcesContent":["import type { CustomMessage } from \"../messages.js\";\nimport type { SendMessageOptions } from \"../extensions/index.js\";\nimport { AsyncJobManager } from \"./job-manager.js\";\nimport type { AsyncJobDeliveryHandler, AsyncJobDeliveryMessage } from \"./types.js\";\n\nexport interface SessionAsyncJobManagerHandle {\n\tmanager: AsyncJobManager;\n\towns: boolean;\n\tsessionId: symbol;\n}\ninterface AsyncDeliverySession {\n\treadonly isStreaming?: boolean;\n\tsendCustomMessage<T>(\n\t\tmessage: Pick<CustomMessage<T>, \"customType\" | \"content\" | \"display\" | \"details\">,\n\t\toptions?: SendMessageOptions,\n\t): Promise<void>;\n}\n\nconst STREAMING_DELIVERY_POLL_MS = 10;\n\nfunction scheduleBoundaryCheck(callback: () => void): NodeJS.Timeout {\n\tconst timer = setTimeout(callback, STREAMING_DELIVERY_POLL_MS);\n\ttimer.unref?.();\n\treturn timer;\n}\n\nfunction waitForStreamingBoundary(session: AsyncDeliverySession, isStale: () => boolean): Promise<\"ready\" | \"stale\"> {\n\treturn new Promise((resolve) => {\n\t\tlet timer: NodeJS.Timeout | undefined;\n\t\tconst settle = (value: \"ready\" | \"stale\") => {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tresolve(value);\n\t\t};\n\t\tconst check = () => {\n\t\t\ttimer = undefined;\n\t\t\tif (isStale()) {\n\t\t\t\tsettle(\"stale\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.isStreaming !== true) {\n\t\t\t\tsettle(\"ready\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttimer = scheduleBoundaryCheck(check);\n\t\t};\n\t\tcheck();\n\t});\n}\n\nexport function createSessionAsyncDeliveryHandler(session: AsyncDeliverySession, manager?: AsyncJobManager, sessionId?: symbol): AsyncJobDeliveryHandler {\n\treturn async (message: AsyncJobDeliveryMessage) => {\n\t\tconst isStale = () =>\n\t\t\tmanager?.disposed === true ||\n\t\t\tmanager?.isDeliverySuppressed(message.details.jobId) === true ||\n\t\t\t(sessionId !== undefined && manager?.isSessionDisposed(sessionId) === true);\n\t\tif (await waitForStreamingBoundary(session, isStale) === \"stale\") return;\n\t\tif (isStale()) return;\n\t\tawait session.sendCustomMessage(message, { deliverAs: \"followUp\", triggerTurn: true });\n\t};\n}\n\nexport function createSessionAsyncJobManager(session: AsyncDeliverySession): SessionAsyncJobManagerHandle {\n\tconst existing = AsyncJobManager.instance();\n\tif (existing) return { manager: existing, owns: false, sessionId: existing.registerSession() };\n\tlet manager: AsyncJobManager;\n\tmanager = new AsyncJobManager({\n\t\tonJobComplete: (message) => createSessionAsyncDeliveryHandler(session, manager)(message),\n\t});\n\tAsyncJobManager.setInstance(manager);\n\treturn { manager, owns: true, sessionId: manager.registerSession() };\n}\n\nexport function disposeSessionAsyncJobManager(manager: AsyncJobManager | undefined, sessionId: symbol | undefined): void {\n\tif (!manager || !sessionId) return;\n\tmanager.releaseSession(sessionId);\n\tif (manager.disposed && AsyncJobManager.instance() === manager) AsyncJobManager.setInstance(undefined);\n}\n\n"]}
@@ -0,0 +1,61 @@
1
+ import { AsyncJobManager } from "./job-manager.js";
2
+ const STREAMING_DELIVERY_POLL_MS = 10;
3
+ function scheduleBoundaryCheck(callback) {
4
+ const timer = setTimeout(callback, STREAMING_DELIVERY_POLL_MS);
5
+ timer.unref?.();
6
+ return timer;
7
+ }
8
+ function waitForStreamingBoundary(session, isStale) {
9
+ return new Promise((resolve) => {
10
+ let timer;
11
+ const settle = (value) => {
12
+ if (timer)
13
+ clearTimeout(timer);
14
+ resolve(value);
15
+ };
16
+ const check = () => {
17
+ timer = undefined;
18
+ if (isStale()) {
19
+ settle("stale");
20
+ return;
21
+ }
22
+ if (session.isStreaming !== true) {
23
+ settle("ready");
24
+ return;
25
+ }
26
+ timer = scheduleBoundaryCheck(check);
27
+ };
28
+ check();
29
+ });
30
+ }
31
+ export function createSessionAsyncDeliveryHandler(session, manager, sessionId) {
32
+ return async (message) => {
33
+ const isStale = () => manager?.disposed === true ||
34
+ manager?.isDeliverySuppressed(message.details.jobId) === true ||
35
+ (sessionId !== undefined && manager?.isSessionDisposed(sessionId) === true);
36
+ if (await waitForStreamingBoundary(session, isStale) === "stale")
37
+ return;
38
+ if (isStale())
39
+ return;
40
+ await session.sendCustomMessage(message, { deliverAs: "followUp", triggerTurn: true });
41
+ };
42
+ }
43
+ export function createSessionAsyncJobManager(session) {
44
+ const existing = AsyncJobManager.instance();
45
+ if (existing)
46
+ return { manager: existing, owns: false, sessionId: existing.registerSession() };
47
+ let manager;
48
+ manager = new AsyncJobManager({
49
+ onJobComplete: (message) => createSessionAsyncDeliveryHandler(session, manager)(message),
50
+ });
51
+ AsyncJobManager.setInstance(manager);
52
+ return { manager, owns: true, sessionId: manager.registerSession() };
53
+ }
54
+ export function disposeSessionAsyncJobManager(manager, sessionId) {
55
+ if (!manager || !sessionId)
56
+ return;
57
+ manager.releaseSession(sessionId);
58
+ if (manager.disposed && AsyncJobManager.instance() === manager)
59
+ AsyncJobManager.setInstance(undefined);
60
+ }
61
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/core/async/session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CustomMessage } from \"../messages.js\";\nimport type { SendMessageOptions } from \"../extensions/index.js\";\nimport { AsyncJobManager } from \"./job-manager.js\";\nimport type { AsyncJobDeliveryHandler, AsyncJobDeliveryMessage } from \"./types.js\";\n\nexport interface SessionAsyncJobManagerHandle {\n\tmanager: AsyncJobManager;\n\towns: boolean;\n\tsessionId: symbol;\n}\ninterface AsyncDeliverySession {\n\treadonly isStreaming?: boolean;\n\tsendCustomMessage<T>(\n\t\tmessage: Pick<CustomMessage<T>, \"customType\" | \"content\" | \"display\" | \"details\">,\n\t\toptions?: SendMessageOptions,\n\t): Promise<void>;\n}\n\nconst STREAMING_DELIVERY_POLL_MS = 10;\n\nfunction scheduleBoundaryCheck(callback: () => void): NodeJS.Timeout {\n\tconst timer = setTimeout(callback, STREAMING_DELIVERY_POLL_MS);\n\ttimer.unref?.();\n\treturn timer;\n}\n\nfunction waitForStreamingBoundary(session: AsyncDeliverySession, isStale: () => boolean): Promise<\"ready\" | \"stale\"> {\n\treturn new Promise((resolve) => {\n\t\tlet timer: NodeJS.Timeout | undefined;\n\t\tconst settle = (value: \"ready\" | \"stale\") => {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\tresolve(value);\n\t\t};\n\t\tconst check = () => {\n\t\t\ttimer = undefined;\n\t\t\tif (isStale()) {\n\t\t\t\tsettle(\"stale\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.isStreaming !== true) {\n\t\t\t\tsettle(\"ready\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttimer = scheduleBoundaryCheck(check);\n\t\t};\n\t\tcheck();\n\t});\n}\n\nexport function createSessionAsyncDeliveryHandler(session: AsyncDeliverySession, manager?: AsyncJobManager, sessionId?: symbol): AsyncJobDeliveryHandler {\n\treturn async (message: AsyncJobDeliveryMessage) => {\n\t\tconst isStale = () =>\n\t\t\tmanager?.disposed === true ||\n\t\t\tmanager?.isDeliverySuppressed(message.details.jobId) === true ||\n\t\t\t(sessionId !== undefined && manager?.isSessionDisposed(sessionId) === true);\n\t\tif (await waitForStreamingBoundary(session, isStale) === \"stale\") return;\n\t\tif (isStale()) return;\n\t\tawait session.sendCustomMessage(message, { deliverAs: \"followUp\", triggerTurn: true });\n\t};\n}\n\nexport function createSessionAsyncJobManager(session: AsyncDeliverySession): SessionAsyncJobManagerHandle {\n\tconst existing = AsyncJobManager.instance();\n\tif (existing) return { manager: existing, owns: false, sessionId: existing.registerSession() };\n\tlet manager: AsyncJobManager;\n\tmanager = new AsyncJobManager({\n\t\tonJobComplete: (message) => createSessionAsyncDeliveryHandler(session, manager)(message),\n\t});\n\tAsyncJobManager.setInstance(manager);\n\treturn { manager, owns: true, sessionId: manager.registerSession() };\n}\n\nexport function disposeSessionAsyncJobManager(manager: AsyncJobManager | undefined, sessionId: symbol | undefined): void {\n\tif (!manager || !sessionId) return;\n\tmanager.releaseSession(sessionId);\n\tif (manager.disposed && AsyncJobManager.instance() === manager) AsyncJobManager.setInstance(undefined);\n}\n\n"]}
@@ -0,0 +1,23 @@
1
+ import type { ManagedBashJob } from "../tools/bash-async-jobs.js";
2
+ export type AsyncJobStatus = "running" | "completed" | "failed";
3
+ export interface AsyncJobDeliveryDetails {
4
+ jobId: string;
5
+ type: "bash";
6
+ status: AsyncJobStatus;
7
+ command: string;
8
+ exitCode?: number | null;
9
+ fullOutputPath?: string;
10
+ wallTimeMs?: number;
11
+ }
12
+ export interface AsyncJobDeliveryMessage {
13
+ customType: "async-job-result";
14
+ content: string;
15
+ display: true;
16
+ details: AsyncJobDeliveryDetails;
17
+ }
18
+ export type AsyncJobDeliveryCallback = (message: AsyncJobDeliveryMessage) => void | Promise<void>;
19
+ export type AsyncJobDeliveryHandler = (message: AsyncJobDeliveryMessage) => void | Promise<void>;
20
+ export interface ManagedAsyncBashJob extends ManagedBashJob {
21
+ status: AsyncJobStatus;
22
+ }
23
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/async/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEhE,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACvC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,uBAAuB,CAAC;CACjC;AAED,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAElG,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEjG,MAAM,WAAW,mBAAoB,SAAQ,cAAc;IAC1D,MAAM,EAAE,cAAc,CAAC;CACvB","sourcesContent":["import type { ManagedBashJob } from \"../tools/bash-async-jobs.js\";\n\nexport type AsyncJobStatus = \"running\" | \"completed\" | \"failed\";\n\nexport interface AsyncJobDeliveryDetails {\n\tjobId: string;\n\ttype: \"bash\";\n\tstatus: AsyncJobStatus;\n\tcommand: string;\n\texitCode?: number | null;\n\tfullOutputPath?: string;\n\twallTimeMs?: number;\n}\n\nexport interface AsyncJobDeliveryMessage {\n\tcustomType: \"async-job-result\";\n\tcontent: string;\n\tdisplay: true;\n\tdetails: AsyncJobDeliveryDetails;\n}\n\nexport type AsyncJobDeliveryCallback = (message: AsyncJobDeliveryMessage) => void | Promise<void>;\n\nexport type AsyncJobDeliveryHandler = (message: AsyncJobDeliveryMessage) => void | Promise<void>;\n\nexport interface ManagedAsyncBashJob extends ManagedBashJob {\n\tstatus: AsyncJobStatus;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/async/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ManagedBashJob } from \"../tools/bash-async-jobs.js\";\n\nexport type AsyncJobStatus = \"running\" | \"completed\" | \"failed\";\n\nexport interface AsyncJobDeliveryDetails {\n\tjobId: string;\n\ttype: \"bash\";\n\tstatus: AsyncJobStatus;\n\tcommand: string;\n\texitCode?: number | null;\n\tfullOutputPath?: string;\n\twallTimeMs?: number;\n}\n\nexport interface AsyncJobDeliveryMessage {\n\tcustomType: \"async-job-result\";\n\tcontent: string;\n\tdisplay: true;\n\tdetails: AsyncJobDeliveryDetails;\n}\n\nexport type AsyncJobDeliveryCallback = (message: AsyncJobDeliveryMessage) => void | Promise<void>;\n\nexport type AsyncJobDeliveryHandler = (message: AsyncJobDeliveryMessage) => void | Promise<void>;\n\nexport interface ManagedAsyncBashJob extends ManagedBashJob {\n\tstatus: AsyncJobStatus;\n}\n"]}