@astralyn/pi 1.4.5 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/bundle/chunks/chunk-FODCTZXW.js +230 -0
  3. package/dist/bundle/chunks/{chunk-I5S7H7GA.js → chunk-FYKNSJG6.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-AXFWZIHH.js → chunk-MG5XDTXV.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-ALHAFUHP.js → chunk-PU2IWWXV.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-5M3YJ54Z.js → chunk-RO2VH6UZ.js} +1 -1
  7. package/dist/bundle/chunks/chunk-YJTSOLP2.js +1499 -0
  8. package/dist/bundle/chunks/{cli-FH77R7JR.js → cli-MEL7SGJD.js} +1 -1
  9. package/dist/bundle/chunks/{execute-GMFGC27P.js → execute-4CWRNHPO.js} +1 -1
  10. package/dist/bundle/chunks/{runtime-R5CMHJ5P.js → runtime-R3X67XMR.js} +1 -1
  11. package/dist/bundle/chunks/{virtual-modules-6FUV7ZDV.js → virtual-modules-PKEOKGYI.js} +1 -1
  12. package/dist/bundle/cli.js +1 -1
  13. package/dist/bundle/index.js +1 -1
  14. package/dist/bundle/rpc-entry.js +1 -1
  15. package/dist/core/agent-session.d.ts +0 -3
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +0 -4
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/extensions/index.d.ts +1 -1
  20. package/dist/core/extensions/index.d.ts.map +1 -1
  21. package/dist/core/extensions/index.js.map +1 -1
  22. package/dist/core/isolated-session.d.ts +23 -0
  23. package/dist/core/isolated-session.d.ts.map +1 -0
  24. package/dist/core/isolated-session.js +55 -0
  25. package/dist/core/isolated-session.js.map +1 -0
  26. package/dist/core/package-manager.d.ts +7 -1
  27. package/dist/core/package-manager.d.ts.map +1 -1
  28. package/dist/core/package-manager.js +72 -1
  29. package/dist/core/package-manager.js.map +1 -1
  30. package/dist/core/resource-loader.d.ts +1 -0
  31. package/dist/core/resource-loader.d.ts.map +1 -1
  32. package/dist/core/resource-loader.js +23 -7
  33. package/dist/core/resource-loader.js.map +1 -1
  34. package/dist/core/settings-manager.d.ts +4 -2
  35. package/dist/core/settings-manager.d.ts.map +1 -1
  36. package/dist/core/settings-manager.js +11 -0
  37. package/dist/core/settings-manager.js.map +1 -1
  38. package/dist/core/settings-schema.d.ts +7 -0
  39. package/dist/core/settings-schema.d.ts.map +1 -1
  40. package/dist/core/settings-schema.js +6 -0
  41. package/dist/core/settings-schema.js.map +1 -1
  42. package/dist/core/skills.d.ts +10 -0
  43. package/dist/core/skills.d.ts.map +1 -1
  44. package/dist/core/skills.js +23 -0
  45. package/dist/core/skills.js.map +1 -1
  46. package/dist/core/tasks/codec.d.ts +12 -0
  47. package/dist/core/tasks/codec.d.ts.map +1 -0
  48. package/dist/core/tasks/codec.js +139 -0
  49. package/dist/core/tasks/codec.js.map +1 -0
  50. package/dist/core/tasks/delivery.d.ts +11 -4
  51. package/dist/core/tasks/delivery.d.ts.map +1 -1
  52. package/dist/core/tasks/delivery.js +9 -9
  53. package/dist/core/tasks/delivery.js.map +1 -1
  54. package/dist/core/tasks/execution.d.ts +71 -0
  55. package/dist/core/tasks/execution.d.ts.map +1 -0
  56. package/dist/core/tasks/execution.js +292 -0
  57. package/dist/core/tasks/execution.js.map +1 -0
  58. package/dist/core/tasks/format.d.ts +0 -2
  59. package/dist/core/tasks/format.d.ts.map +1 -1
  60. package/dist/core/tasks/format.js +1 -16
  61. package/dist/core/tasks/format.js.map +1 -1
  62. package/dist/core/tasks/limits.d.ts +12 -1
  63. package/dist/core/tasks/limits.d.ts.map +1 -1
  64. package/dist/core/tasks/limits.js +20 -1
  65. package/dist/core/tasks/limits.js.map +1 -1
  66. package/dist/core/tasks/output.d.ts +17 -17
  67. package/dist/core/tasks/output.d.ts.map +1 -1
  68. package/dist/core/tasks/output.js +72 -69
  69. package/dist/core/tasks/output.js.map +1 -1
  70. package/dist/core/tasks/presentation.d.ts +14 -5
  71. package/dist/core/tasks/presentation.d.ts.map +1 -1
  72. package/dist/core/tasks/presentation.js +56 -185
  73. package/dist/core/tasks/presentation.js.map +1 -1
  74. package/dist/core/tasks/record.d.ts +45 -0
  75. package/dist/core/tasks/record.d.ts.map +1 -0
  76. package/dist/core/tasks/record.js +80 -0
  77. package/dist/core/tasks/record.js.map +1 -0
  78. package/dist/core/tasks/runtime.d.ts +22 -22
  79. package/dist/core/tasks/runtime.d.ts.map +1 -1
  80. package/dist/core/tasks/runtime.js +170 -343
  81. package/dist/core/tasks/runtime.js.map +1 -1
  82. package/dist/core/tasks/scheduler.d.ts +16 -0
  83. package/dist/core/tasks/scheduler.d.ts.map +1 -0
  84. package/dist/core/tasks/scheduler.js +42 -0
  85. package/dist/core/tasks/scheduler.js.map +1 -0
  86. package/dist/core/tasks/session.d.ts +1 -1
  87. package/dist/core/tasks/session.d.ts.map +1 -1
  88. package/dist/core/tasks/session.js +22 -35
  89. package/dist/core/tasks/session.js.map +1 -1
  90. package/dist/core/tasks/store.d.ts +18 -25
  91. package/dist/core/tasks/store.d.ts.map +1 -1
  92. package/dist/core/tasks/store.js +45 -43
  93. package/dist/core/tasks/store.js.map +1 -1
  94. package/dist/core/tasks/types.d.ts +159 -70
  95. package/dist/core/tasks/types.d.ts.map +1 -1
  96. package/dist/core/tasks/types.js +3 -9
  97. package/dist/core/tasks/types.js.map +1 -1
  98. package/dist/core/tools/shell-execution.d.ts +8 -7
  99. package/dist/core/tools/shell-execution.d.ts.map +1 -1
  100. package/dist/core/tools/shell-execution.js +19 -27
  101. package/dist/core/tools/shell-execution.js.map +1 -1
  102. package/dist/core/tools/shell-tool.d.ts.map +1 -1
  103. package/dist/core/tools/shell-tool.js +42 -47
  104. package/dist/core/tools/shell-tool.js.map +1 -1
  105. package/dist/core/usage-totals.d.ts +4 -3
  106. package/dist/core/usage-totals.d.ts.map +1 -1
  107. package/dist/core/usage-totals.js +32 -8
  108. package/dist/core/usage-totals.js.map +1 -1
  109. package/dist/extensions/agent/activity.d.ts +4 -4
  110. package/dist/extensions/agent/activity.d.ts.map +1 -1
  111. package/dist/extensions/agent/activity.js +15 -24
  112. package/dist/extensions/agent/activity.js.map +1 -1
  113. package/dist/extensions/agent/index.d.ts +2 -11
  114. package/dist/extensions/agent/index.d.ts.map +1 -1
  115. package/dist/extensions/agent/index.js +3 -88
  116. package/dist/extensions/agent/index.js.map +1 -1
  117. package/dist/extensions/agent/progress.d.ts +31 -0
  118. package/dist/extensions/agent/progress.d.ts.map +1 -0
  119. package/dist/extensions/agent/progress.js +135 -0
  120. package/dist/extensions/agent/progress.js.map +1 -0
  121. package/dist/extensions/agent/render.d.ts.map +1 -1
  122. package/dist/extensions/agent/render.js +8 -17
  123. package/dist/extensions/agent/render.js.map +1 -1
  124. package/dist/extensions/agent/report.d.ts +9 -0
  125. package/dist/extensions/agent/report.d.ts.map +1 -0
  126. package/dist/extensions/agent/report.js +40 -0
  127. package/dist/extensions/agent/report.js.map +1 -0
  128. package/dist/extensions/agent/settings-panel.d.ts +43 -0
  129. package/dist/extensions/agent/settings-panel.d.ts.map +1 -0
  130. package/dist/extensions/agent/settings-panel.js +125 -0
  131. package/dist/extensions/agent/settings-panel.js.map +1 -0
  132. package/dist/extensions/agent/settings.d.ts +1 -1
  133. package/dist/extensions/agent/settings.d.ts.map +1 -1
  134. package/dist/extensions/agent/settings.js +101 -83
  135. package/dist/extensions/agent/settings.js.map +1 -1
  136. package/dist/extensions/agent/tool.d.ts +13 -0
  137. package/dist/extensions/agent/tool.d.ts.map +1 -0
  138. package/dist/extensions/agent/tool.js +88 -0
  139. package/dist/extensions/agent/tool.js.map +1 -0
  140. package/dist/extensions/agent/types.d.ts +19 -21
  141. package/dist/extensions/agent/types.d.ts.map +1 -1
  142. package/dist/extensions/agent/types.js +2 -10
  143. package/dist/extensions/agent/types.js.map +1 -1
  144. package/dist/extensions/agent/worker.d.ts +16 -0
  145. package/dist/extensions/agent/worker.d.ts.map +1 -0
  146. package/dist/extensions/agent/worker.js +66 -0
  147. package/dist/extensions/agent/worker.js.map +1 -0
  148. package/dist/extensions/btw/agent.d.ts +1 -1
  149. package/dist/extensions/btw/agent.d.ts.map +1 -1
  150. package/dist/extensions/btw/agent.js +3 -20
  151. package/dist/extensions/btw/agent.js.map +1 -1
  152. package/dist/extensions/cached-compaction/index.d.ts.map +1 -1
  153. package/dist/extensions/cached-compaction/index.js +3 -22
  154. package/dist/extensions/cached-compaction/index.js.map +1 -1
  155. package/dist/extensions/cached-compaction/render.d.ts +1 -0
  156. package/dist/extensions/cached-compaction/render.d.ts.map +1 -1
  157. package/dist/extensions/cached-compaction/render.js +42 -14
  158. package/dist/extensions/cached-compaction/render.js.map +1 -1
  159. package/dist/extensions/deepwiki/client.d.ts.map +1 -1
  160. package/dist/extensions/deepwiki/client.js +6 -78
  161. package/dist/extensions/deepwiki/client.js.map +1 -1
  162. package/dist/extensions/deepwiki/transport.d.ts +3 -0
  163. package/dist/extensions/deepwiki/transport.d.ts.map +1 -0
  164. package/dist/extensions/deepwiki/transport.js +96 -0
  165. package/dist/extensions/deepwiki/transport.js.map +1 -0
  166. package/dist/extensions/provider/ui/builtin-data.d.ts +3 -3
  167. package/dist/extensions/provider/ui/builtin-data.d.ts.map +1 -1
  168. package/dist/extensions/provider/ui/builtin-data.js.map +1 -1
  169. package/dist/extensions/provider/ui/compat.d.ts +3 -3
  170. package/dist/extensions/provider/ui/compat.d.ts.map +1 -1
  171. package/dist/extensions/provider/ui/compat.js.map +1 -1
  172. package/dist/extensions/provider/ui/controls.d.ts +2 -2
  173. package/dist/extensions/provider/ui/controls.d.ts.map +1 -1
  174. package/dist/extensions/provider/ui/controls.js.map +1 -1
  175. package/dist/extensions/provider/ui/dialogs.d.ts +3 -3
  176. package/dist/extensions/provider/ui/dialogs.d.ts.map +1 -1
  177. package/dist/extensions/provider/ui/dialogs.js.map +1 -1
  178. package/dist/extensions/provider/ui/dictionary.d.ts +2 -2
  179. package/dist/extensions/provider/ui/dictionary.d.ts.map +1 -1
  180. package/dist/extensions/provider/ui/dictionary.js.map +1 -1
  181. package/dist/extensions/provider/ui/editor.d.ts.map +1 -1
  182. package/dist/extensions/provider/ui/editor.js +1 -3
  183. package/dist/extensions/provider/ui/editor.js.map +1 -1
  184. package/dist/extensions/provider/ui/fetch-models.d.ts +2 -2
  185. package/dist/extensions/provider/ui/fetch-models.d.ts.map +1 -1
  186. package/dist/extensions/provider/ui/fetch-models.js.map +1 -1
  187. package/dist/extensions/provider/ui/model-fields.d.ts +2 -2
  188. package/dist/extensions/provider/ui/model-fields.d.ts.map +1 -1
  189. package/dist/extensions/provider/ui/model-fields.js.map +1 -1
  190. package/dist/extensions/provider/ui/model-options.d.ts +6 -6
  191. package/dist/extensions/provider/ui/model-options.d.ts.map +1 -1
  192. package/dist/extensions/provider/ui/model-options.js.map +1 -1
  193. package/dist/extensions/provider/ui/pane.d.ts +21 -12
  194. package/dist/extensions/provider/ui/pane.d.ts.map +1 -1
  195. package/dist/extensions/provider/ui/pane.js.map +1 -1
  196. package/dist/extensions/provider/ui/provider-fields.d.ts +3 -3
  197. package/dist/extensions/provider/ui/provider-fields.d.ts.map +1 -1
  198. package/dist/extensions/provider/ui/provider-fields.js.map +1 -1
  199. package/dist/extensions/provider/ui/thinking-map.d.ts +2 -2
  200. package/dist/extensions/provider/ui/thinking-map.d.ts.map +1 -1
  201. package/dist/extensions/provider/ui/thinking-map.js.map +1 -1
  202. package/dist/extensions/run-summary/format.js +1 -1
  203. package/dist/extensions/run-summary/format.js.map +1 -1
  204. package/dist/extensions/statusline/usage.d.ts.map +1 -1
  205. package/dist/extensions/statusline/usage.js +2 -6
  206. package/dist/extensions/statusline/usage.js.map +1 -1
  207. package/dist/extensions/tasks/tool/actions.d.ts +1 -1
  208. package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
  209. package/dist/extensions/tasks/tool/actions.js +50 -115
  210. package/dist/extensions/tasks/tool/actions.js.map +1 -1
  211. package/dist/extensions/tasks/tool/constants.d.ts +1 -6
  212. package/dist/extensions/tasks/tool/constants.d.ts.map +1 -1
  213. package/dist/extensions/tasks/tool/constants.js +1 -6
  214. package/dist/extensions/tasks/tool/constants.js.map +1 -1
  215. package/dist/extensions/tasks/tool/schema.d.ts +0 -3
  216. package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
  217. package/dist/extensions/tasks/tool/schema.js +5 -14
  218. package/dist/extensions/tasks/tool/schema.js.map +1 -1
  219. package/dist/extensions/tasks/tool/types.d.ts +2 -8
  220. package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
  221. package/dist/extensions/tasks/tool/types.js +0 -2
  222. package/dist/extensions/tasks/tool/types.js.map +1 -1
  223. package/dist/extensions/tasks/tool.d.ts.map +1 -1
  224. package/dist/extensions/tasks/tool.js +2 -2
  225. package/dist/extensions/tasks/tool.js.map +1 -1
  226. package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
  227. package/dist/extensions/tasks/ui/completion-render.js +46 -47
  228. package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
  229. package/dist/extensions/tasks/ui/inspector.d.ts +5 -3
  230. package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
  231. package/dist/extensions/tasks/ui/inspector.js +18 -6
  232. package/dist/extensions/tasks/ui/inspector.js.map +1 -1
  233. package/dist/extensions/tasks/ui/layout.d.ts +7 -7
  234. package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
  235. package/dist/extensions/tasks/ui/layout.js +118 -84
  236. package/dist/extensions/tasks/ui/layout.js.map +1 -1
  237. package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
  238. package/dist/extensions/tasks/ui/list.js +45 -31
  239. package/dist/extensions/tasks/ui/list.js.map +1 -1
  240. package/dist/extensions/tasks/ui/manager.d.ts +16 -7
  241. package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
  242. package/dist/extensions/tasks/ui/manager.js +167 -129
  243. package/dist/extensions/tasks/ui/manager.js.map +1 -1
  244. package/dist/extensions/tasks/ui/model.d.ts +29 -0
  245. package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
  246. package/dist/extensions/tasks/ui/model.js +16 -1
  247. package/dist/extensions/tasks/ui/model.js.map +1 -1
  248. package/dist/extensions/tasks/ui/render.d.ts +1 -1
  249. package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
  250. package/dist/extensions/tasks/ui/render.js +12 -9
  251. package/dist/extensions/tasks/ui/render.js.map +1 -1
  252. package/dist/extensions/tasks/ui/status.d.ts +15 -0
  253. package/dist/extensions/tasks/ui/status.d.ts.map +1 -0
  254. package/dist/extensions/tasks/ui/status.js +16 -0
  255. package/dist/extensions/tasks/ui/status.js.map +1 -0
  256. package/dist/extensions/tasks/ui/tree.js +1 -1
  257. package/dist/extensions/tasks/ui/tree.js.map +1 -1
  258. package/dist/extensions/tasks/ui/views.d.ts +10 -5
  259. package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
  260. package/dist/extensions/tasks/ui/views.js +61 -36
  261. package/dist/extensions/tasks/ui/views.js.map +1 -1
  262. package/dist/index.d.ts +2 -1
  263. package/dist/index.d.ts.map +1 -1
  264. package/dist/index.js.map +1 -1
  265. package/dist/modes/interactive/components/config-selector.d.ts +14 -1
  266. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  267. package/dist/modes/interactive/components/config-selector.js +258 -7
  268. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  269. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  270. package/dist/modes/interactive/components/keybinding-hints.js +2 -1
  271. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  272. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  273. package/dist/modes/interactive/components/model-selector.js +7 -4
  274. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  275. package/dist/modes/interactive/components/status-marker.d.ts +2 -1
  276. package/dist/modes/interactive/components/status-marker.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/status-marker.js +0 -1
  278. package/dist/modes/interactive/components/status-marker.js.map +1 -1
  279. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/thinking-selector.js +10 -3
  281. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  282. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  283. package/dist/modes/interactive/interactive-mode.js +7 -1
  284. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  285. package/dist/utils/duration.d.ts +3 -0
  286. package/dist/utils/duration.d.ts.map +1 -0
  287. package/dist/utils/duration.js +17 -0
  288. package/dist/utils/duration.js.map +1 -0
  289. package/dist/utils/number.d.ts +3 -0
  290. package/dist/utils/number.d.ts.map +1 -0
  291. package/dist/utils/number.js +5 -0
  292. package/dist/utils/number.js.map +1 -0
  293. package/docs/bundled/extensions/agent.md +14 -12
  294. package/docs/bundled/extensions/cached-compaction.md +13 -4
  295. package/docs/bundled/extensions/deepwiki.md +3 -1
  296. package/docs/bundled/extensions/statusline.md +1 -1
  297. package/docs/bundled/tasks.md +15 -14
  298. package/docs/extensions.md +26 -10
  299. package/docs/packages.md +5 -3
  300. package/docs/sdk.md +3 -3
  301. package/docs/session-format.md +28 -24
  302. package/docs/settings.md +11 -0
  303. package/docs/skills.md +23 -0
  304. package/npm-shrinkwrap.json +2 -2
  305. package/package.json +1 -1
  306. package/schemas/settings.schema.json +36 -0
  307. package/dist/bundle/chunks/chunk-FSJ45CRP.js +0 -229
  308. package/dist/bundle/chunks/chunk-W3FIUMGM.js +0 -1501
  309. package/dist/core/tasks/history.d.ts +0 -4
  310. package/dist/core/tasks/history.d.ts.map +0 -1
  311. package/dist/core/tasks/history.js +0 -131
  312. package/dist/core/tasks/history.js.map +0 -1
  313. package/dist/extensions/agent/runner.d.ts +0 -24
  314. package/dist/extensions/agent/runner.d.ts.map +0 -1
  315. package/dist/extensions/agent/runner.js +0 -274
  316. package/dist/extensions/agent/runner.js.map +0 -1
  317. package/dist/extensions/deepwiki/http.d.ts +0 -13
  318. package/dist/extensions/deepwiki/http.d.ts.map +0 -1
  319. package/dist/extensions/deepwiki/http.js +0 -88
  320. package/dist/extensions/deepwiki/http.js.map +0 -1
  321. package/dist/extensions/tasks/ui/task-view.d.ts +0 -13
  322. package/dist/extensions/tasks/ui/task-view.d.ts.map +0 -1
  323. package/dist/extensions/tasks/ui/task-view.js +0 -20
  324. package/dist/extensions/tasks/ui/task-view.js.map +0 -1
@@ -1,40 +1,50 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { TASKS_WAIT_DEFAULT_MS, TASKS_WAIT_MAX_MS } from "./limits.js";
3
- import { boundText, finiteLimit, readOutputSlice, retainResult, sliceText, TASK_INFO_BYTES, TASK_TITLE_BYTES, } from "./output.js";
4
- import { changed, snapshotOf, TaskStore } from "./store.js";
5
- import { TASK_BACKGROUND_REJECTION, TaskExecutionError, TaskLookupError, } from "./types.js";
6
- function errorText(error) {
7
- try {
8
- return boundText(error instanceof Error ? error.message : String(error), 4096);
9
- }
10
- catch {
11
- return "Task execution failed (unprintable error)";
12
- }
13
- }
2
+ import { finiteLimit } from "../../utils/number.js";
3
+ import { Execution } from "./execution.js";
4
+ import { boundWaitMs, TASKS_WAIT_DEFAULT_MS } from "./limits.js";
5
+ import { boundFacts, boundInput, boundText, errorText, readOutputSlice, sliceText, TASK_LABEL_BYTES, TASK_TITLE_BYTES, } from "./output.js";
6
+ import { TaskRecord } from "./record.js";
7
+ import { TaskScheduler } from "./scheduler.js";
8
+ import { TaskStore } from "./store.js";
9
+ import { TASK_BACKGROUND_REJECTION, TASK_KIND_PATTERN, TaskLookupError, } from "./types.js";
14
10
  /** Session-local supervision. Executors own their processes, items and output files. */
15
11
  export class TaskRuntime {
16
- get closed() {
17
- return this._closed;
18
- }
19
- get session() {
20
- return this.options.session;
21
- }
22
12
  constructor(options = {}) {
23
13
  this.executions = new Map();
14
+ this.scheduler = new TaskScheduler();
24
15
  this.listeners = new Set();
25
16
  this._closed = false;
26
- this.pauses = 0;
27
17
  this.options = options;
28
18
  this.configuredEnabled = options.enabled ?? false;
29
- this.maxActive = Math.max(1, finiteLimit(options.maxActive, 8, 128));
19
+ this.maxBackground = Math.max(1, finiteLimit(options.maxBackground, 8, 128));
30
20
  this.maxHistory = finiteLimit(options.maxHistory, 32, 1024);
31
21
  // Separate foreground and background histories, plus the protected-record allowance.
32
- this.maxRetained = this.maxActive + 2 * Math.max(1, this.maxHistory) + this.maxHistory;
22
+ this.maxRetained = this.maxBackground + 2 * Math.max(1, this.maxHistory) + this.maxHistory;
33
23
  this.store = new TaskStore(this.maxHistory, this.maxRetained, () => this.closed, options.onCleanupError);
24
+ const runtime = this;
25
+ this.host = {
26
+ get closed() {
27
+ return runtime.closed;
28
+ },
29
+ emit: () => this.emit(),
30
+ settle: (task, usage) => this.options.onSettled?.(task, usage),
31
+ released: (execution) => {
32
+ this.executions.delete(execution.task.id);
33
+ this.scheduler.release(execution);
34
+ this.store.trim();
35
+ this.emit();
36
+ },
37
+ };
38
+ }
39
+ get closed() {
40
+ return this._closed;
41
+ }
42
+ get session() {
43
+ return this.options.session;
34
44
  }
35
45
  /**
36
- * Rehydrate terminal version-2 custom data only, newest endedAt in each history
37
- * (later input wins ties). Existing runtime records win ID collisions. No execution, accounting,
46
+ * Rehydrate terminal snapshots only, newest endedAt in each history (later input wins
47
+ * ties). Existing runtime records win ID collisions. No execution, accounting,
38
48
  * notification, or deletion ownership is restored. Foreground records take their output
39
49
  * from the branch's tool results. The host supplies the current branch; closed services
40
50
  * ignore restoration.
@@ -49,16 +59,15 @@ export class TaskRuntime {
49
59
  this.configuredEnabled = enabled;
50
60
  this.emit();
51
61
  }
52
- /** Foreground work is bounded by its caller; only background work holds a slot. */
62
+ /** Foreground work is bounded by its caller; only background work, queued or running, holds a slot. */
63
+ activeBackground() {
64
+ return this.store.unsettled().filter((record) => record.task.mode === "background");
65
+ }
53
66
  backgroundActive() {
54
- let count = 0;
55
- for (const record of this.store.records.values())
56
- if (!record.settled && record.task.mode === "background")
57
- count++;
58
- return count;
67
+ return this.activeBackground().length;
59
68
  }
60
69
  limitError() {
61
- return new Error(`Background task limit reached (${this.maxActive}); stop or wait for a background task, or run in the foreground.`);
70
+ return new Error(`Background task limit reached (${this.maxBackground}); stop or wait for a background task, or run in the foreground.`);
62
71
  }
63
72
  async execute(execution) {
64
73
  // Admission and registration happen synchronously, before invoking user code or awaiting anything.
@@ -69,302 +78,106 @@ export class TaskRuntime {
69
78
  if (execution.background) {
70
79
  if (!this.enabled)
71
80
  throw new Error("Background execution is not available in this host");
72
- if (this.backgroundActive() >= this.maxActive)
81
+ if (this.backgroundActive() >= this.maxBackground)
73
82
  throw this.limitError();
74
- if (this.store.records.size + this.store.cleanups.size >= this.maxRetained) {
83
+ if (this.store.retainedCount() >= this.maxRetained) {
75
84
  throw new Error("Task history retention limit reached; deliver pending notifications or release pinned or claimed records");
76
85
  }
77
86
  }
78
87
  if (execution.signal?.aborted)
79
88
  throw execution.signal.reason ?? new Error("Execution aborted");
80
- if (!/^[a-z][a-z0-9-]{0,63}$/.test(execution.kind))
89
+ if (!TASK_KIND_PATTERN.test(execution.kind))
81
90
  throw new Error("Invalid task source");
91
+ const concurrency = execution.concurrency;
92
+ if (concurrency && (!concurrency.key || !Number.isSafeInteger(concurrency.limit) || concurrency.limit < 1))
93
+ throw new Error("Invalid task concurrency");
82
94
  const anchorId = this.options.anchor?.() ?? null;
83
95
  if (anchorId !== null && Buffer.byteLength(anchorId) > 8192)
84
96
  throw new Error("Task branch anchor is too large");
85
- const info = execution.info === undefined ? undefined : boundText(execution.info, TASK_INFO_BYTES);
86
97
  const task = {
87
98
  id: `${execution.kind}-${randomUUID()}`,
88
99
  kind: execution.kind,
100
+ label: boundText(execution.label ?? execution.kind, TASK_LABEL_BYTES),
89
101
  title: boundText(execution.title, TASK_TITLE_BYTES),
102
+ output: execution.output ?? "log",
103
+ ...(execution.input ? { input: boundInput(execution.input) } : {}),
104
+ ...(execution.facts?.length ? { facts: boundFacts(execution.facts) } : {}),
90
105
  toolCallId: boundText(execution.toolCallId, 512),
91
106
  anchorId,
92
107
  mode: execution.background ? "background" : "foreground",
93
108
  status: "queued",
94
109
  startedAt: Date.now(),
95
- command: execution.command === undefined ? undefined : boundText(execution.command, 8192),
96
- commandTruncated: execution.command === undefined ? undefined : boundText(execution.command, 8192) !== execution.command,
97
- cwd: execution.cwd === undefined ? undefined : boundText(execution.cwd, 4096),
98
- info: info === undefined ? undefined : { text: info, truncated: info !== execution.info },
99
- };
100
- let resolveCaller;
101
- let rejectCaller;
102
- const caller = new Promise((resolve, reject) => {
103
- resolveCaller = resolve;
104
- rejectCaller = reject;
105
- });
106
- let resolveDone;
107
- const record = {
108
- task,
109
- settled: false,
110
- visible: true,
111
- delivery: "none",
112
- pins: 0,
113
- deliveryHolds: 0,
114
- waiters: new Set(),
115
110
  };
116
- const active = {
117
- controller: new AbortController(),
118
- accepted: false,
119
- handedOff: false,
120
- detachRequested: execution.background ?? false,
121
- modeListeners: new Set(),
122
- removeParent: () => { },
123
- handoff: () => {
124
- if (!active.accepted ||
125
- !active.detachRequested ||
126
- record.settled ||
127
- active.handedOff ||
128
- this.closed ||
129
- !record.visible ||
130
- active.controller.signal.aborted)
131
- return;
132
- active.handedOff = true;
133
- task.mode = "background";
134
- changed(record);
135
- active.removeParent();
136
- resolveCaller({ kind: "background", task: snapshotOf(record) });
137
- this.emit();
138
- },
139
- done: new Promise((resolve) => {
140
- resolveDone = resolve;
141
- }),
142
- };
143
- const parentSignal = execution.signal;
144
- let onUpdate = execution.background ? undefined : execution.onUpdate;
145
- const parentAbort = () => {
146
- if (!active.detachRequested || !active.accepted)
147
- this.cancel(record);
148
- };
149
- parentSignal?.addEventListener("abort", parentAbort, { once: true });
150
- active.removeParent = () => {
151
- parentSignal?.removeEventListener("abort", parentAbort);
152
- onUpdate = undefined;
153
- };
154
- this.store.records.set(task.id, record);
111
+ const record = new TaskRecord(task);
112
+ const active = new Execution(this.host, record, execution);
113
+ this.store.add(record);
155
114
  this.executions.set(task.id, active);
156
- const control = {
157
- id: task.id,
158
- signal: active.controller.signal,
159
- get mode() {
160
- return task.mode;
161
- },
162
- onModeChange: (listener) => {
163
- if (record.settled || this.closed)
164
- return () => { };
165
- active.modeListeners.add(listener);
166
- return () => {
167
- active.modeListeners.delete(listener);
168
- };
169
- },
170
- requestCancel: () => this.cancel(record),
171
- accept: () => {
172
- if (record.settled || active.accepted || this.closed || active.controller.signal.aborted)
173
- return;
174
- active.accepted = true;
175
- if (task.status === "queued")
176
- task.status = "running";
177
- changed(record);
178
- if (active.detachRequested)
179
- active.removeParent();
180
- // Let an already available final result win over a handoff.
181
- queueMicrotask(() => queueMicrotask(active.handoff));
182
- this.emit();
183
- },
184
- publish: (result) => {
185
- if (record.settled || this.closed)
186
- return;
187
- retainResult(task, result);
188
- changed(record);
189
- if (result.usage !== undefined)
190
- active.publishedUsage = structuredClone(result.usage);
191
- if (!active.detachRequested && !active.handedOff) {
192
- try {
193
- onUpdate?.(result);
194
- }
195
- catch {
196
- /* UI observers cannot stop execution. */
197
- }
198
- }
199
- this.emit();
200
- },
201
- setOutputPath: (path, cleanup) => {
202
- if (record.settled) {
203
- if (cleanup)
204
- throw new Error("Task execution has settled");
205
- return;
206
- }
207
- if (record.cleanup)
208
- throw new Error("Managed output is already registered");
209
- // Never truncate a real filesystem path into a different path.
210
- if (Buffer.byteLength(path) > 8192)
211
- throw new Error("Task output path is too large");
212
- record.cleanup = cleanup;
213
- task.outputPath = path;
214
- changed(record);
215
- this.emit();
216
- },
217
- };
218
- const finish = (completion, error) => {
219
- if (record.settled)
220
- return;
221
- record.settled = true;
222
- active.modeListeners.clear();
223
- active.removeParent();
224
- task.endedAt = Date.now();
225
- const failed = completion === undefined;
226
- task.status =
227
- completion?.status ??
228
- (error instanceof TaskExecutionError
229
- ? error.status
230
- : active.controller.signal.aborted
231
- ? "cancelled"
232
- : failed
233
- ? "failed"
234
- : "completed");
235
- if (completion) {
236
- retainResult(task, completion.result);
237
- if (completion.error !== undefined)
238
- task.error = boundText(completion.error, 4096);
239
- if (completion.exitCode !== undefined)
240
- task.exitCode = completion.exitCode;
241
- }
242
- if (failed) {
243
- task.error = errorText(error);
244
- retainResult(task, { content: [{ type: "text", text: task.error }, ...(task.result?.content ?? [])], details: undefined }, task.resultTruncated);
245
- }
246
- changed(record);
247
- let settlementWarning;
248
- try {
249
- this.options.onSettled?.(snapshotOf(record), completion?.usage ?? completion?.result.usage ?? active.publishedUsage);
250
- }
251
- catch (accountingError) {
252
- const warning = `Usage settlement failed: ${errorText(accountingError)}`;
253
- settlementWarning = warning;
254
- task.error = boundText([task.error, warning].filter(Boolean).join("\n"), 8192);
255
- retainResult(task, { content: [{ type: "text", text: warning }, ...(task.result?.content ?? [])], details: undefined }, task.resultTruncated);
256
- changed(record);
257
- }
258
- active.publishedUsage = undefined;
259
- // Delivery becomes eligible only after settlement is persisted.
260
- record.delivery = active.handedOff ? "pending" : "delivered";
261
- if (!active.handedOff) {
262
- if (failed)
263
- rejectCaller(error);
264
- else {
265
- const { usage: _usage, ...result } = completion.result;
266
- if (settlementWarning)
267
- result.content = [{ type: "text", text: settlementWarning }, ...result.content];
268
- resolveCaller({ kind: "result", result, status: task.status, error: task.error });
269
- }
270
- }
271
- for (const waiter of [...record.waiters])
272
- waiter();
273
- resolveDone();
274
- this.executions.delete(task.id);
275
- this.store.trim();
276
- this.emit();
277
- };
278
- // The rejection handler is installed immediately, including for synchronous preflight throws.
279
- try {
280
- const running = execution.run(control);
281
- void Promise.resolve(running).then((result) => finish(result), (error) => finish(undefined, error));
282
- }
283
- catch (error) {
284
- finish(undefined, error);
285
- }
115
+ this.scheduler.schedule(active);
116
+ // Queued background work returns at once; started work hands off after its own accept().
117
+ if (active.queued)
118
+ active.handoff();
286
119
  this.emit();
287
- return caller;
120
+ return active.caller;
288
121
  }
289
122
  detachable(record) {
290
- const active = this.executions.get(record.task.id);
291
- return !!active && !record.settled && !active.detachRequested && !active.controller.signal.aborted;
123
+ return this.executions.get(record.task.id)?.detachable ?? false;
292
124
  }
293
125
  detachForeground() {
294
126
  if (!this.enabled)
295
127
  return 0;
296
- const eligible = [...this.store.records.values()]
297
- .filter((record) => this.detachable(record))
128
+ const eligible = [...this.executions.values()]
129
+ .filter((execution) => execution.detachable)
298
130
  .sort((left, right) => left.task.startedAt - right.task.startedAt);
299
131
  if (!eligible.length)
300
132
  return 0;
301
- const free = this.maxActive - this.backgroundActive();
133
+ const free = this.maxBackground - this.backgroundActive();
302
134
  if (free <= 0)
303
135
  throw this.limitError();
304
- const records = eligible.slice(0, free);
305
- for (const record of records)
306
- this.prepareDetach(record);
307
- for (const record of records)
308
- this.finishDetach(record);
136
+ const executions = eligible.slice(0, free);
137
+ for (const execution of executions)
138
+ execution.requestDetach();
139
+ for (const execution of executions)
140
+ execution.completeDetach();
309
141
  this.emit();
310
- return records.length;
142
+ return executions.length;
311
143
  }
312
144
  canDetach(id) {
313
145
  const record = this.lookup(id);
314
- return this.enabled && this.backgroundActive() < this.maxActive && this.detachable(record);
146
+ return this.enabled && this.backgroundActive() < this.maxBackground && this.detachable(record);
315
147
  }
316
148
  detach(id) {
317
149
  const record = this.lookup(id);
318
150
  if (!this.enabled || !this.detachable(record))
319
151
  return false;
320
- if (this.backgroundActive() >= this.maxActive)
152
+ if (this.backgroundActive() >= this.maxBackground)
321
153
  throw this.limitError();
322
- this.prepareDetach(record);
323
- this.finishDetach(record);
154
+ const execution = this.executions.get(record.task.id);
155
+ execution.requestDetach();
156
+ execution.completeDetach();
324
157
  this.emit();
325
158
  return true;
326
159
  }
327
- prepareDetach(record) {
328
- const active = this.executions.get(record.task.id);
329
- active.detachRequested = true;
330
- record.task.mode = "background";
331
- changed(record);
332
- if (active.accepted)
333
- active.removeParent();
334
- }
335
- finishDetach(record) {
336
- const active = this.executions.get(record.task.id);
337
- active.handoff();
338
- for (const listener of active.modeListeners) {
339
- try {
340
- listener(record.task.mode);
341
- }
342
- catch {
343
- /* Executors own their diagnostics. */
344
- }
345
- }
346
- }
347
160
  list() {
348
- return [...this.store.records.values()].filter((record) => record.visible).map(snapshotOf);
161
+ return this.store.visible().map((record) => record.snapshot());
349
162
  }
350
163
  lookup(id) {
351
- const exact = this.store.records.get(id);
164
+ const exact = this.store.get(id);
352
165
  if (exact)
353
166
  return exact;
354
- const matches = [...this.store.records.values()].filter(({ task }) => task.id.startsWith(id) || task.id.slice(task.kind.length + 1).startsWith(id));
167
+ const matches = this.store.match(id);
355
168
  if (!id || matches.length !== 1) {
356
- throw new TaskLookupError(matches.length > 1 ? "ambiguous" : "unknown", matches.map(snapshotOf));
169
+ throw new TaskLookupError(matches.length > 1 ? "ambiguous" : "unknown", matches.map((record) => record.snapshot()));
357
170
  }
358
171
  return matches[0];
359
172
  }
360
173
  get(id) {
361
- return snapshotOf(this.lookup(id));
174
+ return this.lookup(id).snapshot();
362
175
  }
363
176
  async read(id, options = {}) {
364
177
  const record = this.lookup(id);
365
178
  const release = this.retain(record.task.id);
366
179
  try {
367
- const task = snapshotOf(record);
180
+ const task = record.snapshot();
368
181
  if (task.outputPath) {
369
182
  try {
370
183
  return { task, ...(await readOutputSlice(task.outputPath, options)) };
@@ -384,12 +197,7 @@ export class TaskRuntime {
384
197
  }
385
198
  }
386
199
  resultText(task) {
387
- return boundText(task.result?.content
388
- .filter((block) => block.type === "text")
389
- .map((block) => block.text)
390
- .join("\n") ||
391
- task.error ||
392
- "No output yet.");
200
+ return task.result?.text || task.error || "No output yet.";
393
201
  }
394
202
  /** One event-driven waiter can observe a whole waitAny cohort without per-task timers. */
395
203
  waitForRecords(records, timeoutMs, signal, interruptSignal) {
@@ -422,47 +230,82 @@ export class TaskRuntime {
422
230
  record.waiters.add(wake);
423
231
  signal?.addEventListener("abort", abort, { once: true });
424
232
  interruptSignal?.addEventListener("abort", interrupt, { once: true });
425
- timer = setTimeout(wake, finiteLimit(timeoutMs, TASKS_WAIT_DEFAULT_MS, TASKS_WAIT_MAX_MS));
233
+ timer = setTimeout(wake, boundWaitMs(timeoutMs));
426
234
  if (this.closed || interruptSignal?.aborted)
427
235
  wake();
428
236
  if (signal?.aborted)
429
237
  abort();
430
238
  });
431
239
  }
432
- /** Observe completion without consuming delivery; the host acknowledges only persisted tool results. */
433
- async wait(id, timeoutMs = TASKS_WAIT_DEFAULT_MS, signal, interruptSignal) {
434
- if (signal?.aborted)
435
- throw signal.reason ?? new Error("Wait aborted");
240
+ /**
241
+ * One observation: hold delivery for the observed records, wait, read, then register the
242
+ * receipt with the host, and only then release. Nothing the caller does between those steps
243
+ * can leak a settled result into an automatic notification or lose it.
244
+ */
245
+ async observe(records, options,
246
+ /** Record to read when none settled; a cohort has none. */
247
+ fallback) {
248
+ const { signal } = options;
249
+ const releases = records.map((record) => this.holdRecord(record));
250
+ try {
251
+ const start = Date.now();
252
+ const interrupt = this.options.waits?.interruptSignal;
253
+ await this.waitForRecords(records, options.timeoutMs ?? TASKS_WAIT_DEFAULT_MS, signal, interrupt);
254
+ const waitedMs = Date.now() - start;
255
+ const settled = records
256
+ .filter((record) => record.settled)
257
+ .sort((left, right) => (left.task.endedAt ?? 0) - (right.task.endedAt ?? 0))[0];
258
+ const outcome = settled
259
+ ? "settled"
260
+ : this.closed
261
+ ? "closed"
262
+ : interrupt?.aborted
263
+ ? "interrupted"
264
+ : "timed_out";
265
+ const running = records
266
+ .filter((record) => record.visible && !record.settled)
267
+ .map((record) => record.snapshot());
268
+ const observed = settled ?? fallback;
269
+ const read = options.read && observed && outcome !== "interrupted"
270
+ ? await this.read(observed.task.id, options.read)
271
+ : undefined;
272
+ signal?.throwIfAborted();
273
+ if (settled && settled.task.mode === "background" && options.toolCallId)
274
+ this.options.waits?.bind(options.toolCallId, settled.task.id);
275
+ return { outcome, waitedMs, settled, running, read };
276
+ }
277
+ finally {
278
+ for (const release of releases)
279
+ release();
280
+ }
281
+ }
282
+ async wait(id, options = {}) {
283
+ if (options.signal?.aborted)
284
+ throw options.signal.reason ?? new Error("Wait aborted");
436
285
  const record = this.lookup(id);
437
- if (record.settled)
438
- return snapshotOf(record);
439
- await this.waitForRecords([record], timeoutMs, signal, interruptSignal);
440
- return snapshotOf(record);
441
- }
442
- async waitAny(timeoutMs = TASKS_WAIT_DEFAULT_MS, signal, interruptSignal) {
443
- if (signal?.aborted)
444
- throw signal.reason ?? new Error("Wait aborted");
445
- const records = [...this.store.records.values()].filter((record) => record.visible && record.task.mode === "background" && !record.settled);
446
- if (records.length === 0 || interruptSignal?.aborted)
447
- return undefined;
448
- await this.waitForRecords(records, timeoutMs, signal, interruptSignal);
449
- const settled = records
450
- .filter((record) => record.settled)
451
- .sort((left, right) => (left.task.endedAt ?? 0) - (right.task.endedAt ?? 0))[0];
452
- return settled ? snapshotOf(settled) : undefined;
286
+ const { outcome, waitedMs, read } = await this.observe([record], options, record);
287
+ return { outcome, waitedMs, task: record.snapshot(), ...(read ? { read } : {}) };
288
+ }
289
+ async waitAny(options = {}) {
290
+ if (options.signal?.aborted)
291
+ throw options.signal.reason ?? new Error("Wait aborted");
292
+ const records = this.activeBackground().filter((record) => record.visible);
293
+ if (records.length === 0)
294
+ return { outcome: "no_tasks", waitedMs: 0, running: [] };
295
+ const { outcome, waitedMs, settled, running, read } = await this.observe(records, options);
296
+ return {
297
+ outcome,
298
+ waitedMs,
299
+ running,
300
+ ...(settled ? { task: settled.snapshot() } : {}),
301
+ ...(read ? { read } : {}),
302
+ };
453
303
  }
454
304
  kill(id) {
455
305
  return this.cancel(this.lookup(id));
456
306
  }
457
307
  cancel(record) {
458
- const active = this.executions.get(record.task.id);
459
- if (!active || record.settled || active.controller.signal.aborted)
460
- return false;
461
- record.task.status = "stopping";
462
- changed(record);
463
- active.controller.abort();
464
- this.emit();
465
- return true;
308
+ return this.executions.get(record.task.id)?.cancel() ?? false;
466
309
  }
467
310
  subscribe(listener) {
468
311
  if (this.closed)
@@ -476,7 +319,9 @@ export class TaskRuntime {
476
319
  * Retention protects snapshots and logs only. Delivery has its own explicit hold.
477
320
  */
478
321
  retain(id) {
479
- const record = this.lookup(id);
322
+ return this.pin(this.lookup(id));
323
+ }
324
+ pin(record) {
480
325
  record.pins++;
481
326
  let released = false;
482
327
  return () => {
@@ -485,13 +330,16 @@ export class TaskRuntime {
485
330
  released = true;
486
331
  record.pins--;
487
332
  this.store.trim();
488
- if (!this.closed && this.pauses === 0 && this.candidate(record))
333
+ // Listeners decide whether delivery may proceed; TaskDelivery owns pausing.
334
+ if (!this.closed && record.deliverable)
489
335
  this.emit();
490
336
  };
491
337
  }
492
338
  holdDelivery(id) {
493
- const record = this.lookup(id);
494
- const releaseRetention = this.retain(id);
339
+ return this.holdRecord(this.lookup(id));
340
+ }
341
+ holdRecord(record) {
342
+ const releasePin = this.pin(record);
495
343
  record.deliveryHolds++;
496
344
  let released = false;
497
345
  return () => {
@@ -499,50 +347,27 @@ export class TaskRuntime {
499
347
  return;
500
348
  released = true;
501
349
  record.deliveryHolds--;
502
- releaseRetention();
503
- };
504
- }
505
- pause() {
506
- this.pauses++;
507
- let resumed = false;
508
- return () => {
509
- if (resumed)
510
- return;
511
- resumed = true;
512
- this.pauses--;
513
- this.emit();
350
+ releasePin();
514
351
  };
515
352
  }
516
353
  pendingNotifications() {
517
- if (this.closed || this.pauses > 0)
354
+ if (this.closed)
518
355
  return [];
519
- return [...this.store.records.values()].filter((record) => this.candidate(record)).map(snapshotOf);
520
- }
521
- /** Only explicit result-delivery holds delay an automatic completion. */
522
- candidate(record) {
523
- return record.visible && record.delivery === "pending" && record.waiters.size === 0 && record.deliveryHolds === 0;
356
+ return this.store.deliverable().map((record) => record.snapshot());
524
357
  }
525
358
  claimNotification(id) {
526
- if (this.closed || this.pauses > 0)
527
- return false;
528
- const record = this.store.records.get(id);
529
- if (!record || !this.candidate(record))
530
- return false;
531
- record.delivery = "claimed";
532
- return true;
359
+ return !this.closed && (this.store.get(id)?.claim() ?? false);
533
360
  }
534
361
  markDelivered(id) {
535
- const record = this.store.records.get(id);
362
+ const record = this.store.get(id);
536
363
  if (!record)
537
364
  return;
538
- record.delivery = "delivered";
365
+ record.acknowledge();
539
366
  this.store.trim();
540
367
  }
541
368
  releaseNotification(id) {
542
- const record = this.store.records.get(id);
543
- if (!record || record.delivery !== "claimed")
369
+ if (!this.store.get(id)?.unclaim())
544
370
  return;
545
- record.delivery = "pending";
546
371
  this.store.trim();
547
372
  this.emit();
548
373
  }
@@ -551,8 +376,8 @@ export class TaskRuntime {
551
376
  return;
552
377
  this._closed = true;
553
378
  this.listeners.clear();
554
- for (const record of this.store.records.values()) {
555
- this.executions.get(record.task.id)?.removeParent();
379
+ for (const record of this.store.all()) {
380
+ this.executions.get(record.task.id)?.releaseParent();
556
381
  this.cancel(record);
557
382
  for (const waiter of [...record.waiters])
558
383
  waiter();
@@ -561,22 +386,24 @@ export class TaskRuntime {
561
386
  }
562
387
  async shutdown(graceMs = 2000) {
563
388
  this.close();
564
- await this.drain([...this.store.records.values()], graceMs);
389
+ await this.drain(this.store.all(), graceMs);
565
390
  }
566
391
  /** Running tasks whose launch anchor is outside the branch made of `ancestors`. */
567
392
  activeOutsideBranch(ancestors) {
568
- return [...this.store.records.values()]
569
- .filter((record) => !record.settled && record.task.anchorId !== null && !ancestors.has(record.task.anchorId))
570
- .map(snapshotOf);
393
+ return this.store
394
+ .unsettled()
395
+ .filter((record) => record.task.anchorId !== null && !ancestors.has(record.task.anchorId))
396
+ .map((record) => record.snapshot());
571
397
  }
572
398
  /**
573
399
  * Hidden results stay pending: returning to their branch delivers them again.
574
400
  * Restored history is already delivered.
575
401
  */
576
402
  async cancelOutsideBranch(ancestors) {
577
- for (const record of this.store.records.values())
403
+ const records = this.store.all();
404
+ for (const record of records)
578
405
  record.visible = record.task.anchorId === null || ancestors.has(record.task.anchorId);
579
- const outside = [...this.store.records.values()].filter((record) => !record.visible);
406
+ const outside = records.filter((record) => !record.visible);
580
407
  for (const record of outside)
581
408
  this.cancel(record);
582
409
  this.store.trim();
@@ -587,7 +414,7 @@ export class TaskRuntime {
587
414
  let timer;
588
415
  try {
589
416
  await Promise.race([
590
- Promise.all(records.map((record) => this.executions.get(record.task.id)?.done)).then(() => Promise.all(this.store.cleanups)),
417
+ Promise.all(records.map((record) => this.executions.get(record.task.id)?.done)).then(() => this.store.cleanupsDone()),
591
418
  new Promise((resolve) => {
592
419
  timer = setTimeout(resolve, finiteLimit(graceMs, 2000, 60_000));
593
420
  }),