visionclaw 0.1.219-beta.0 → 0.1.219-dev.codex-issue-2805-worker-runtime.1

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 (520) hide show
  1. package/dist/agent/api-proxy.d.ts +0 -16
  2. package/dist/agent/api-proxy.d.ts.map +1 -1
  3. package/dist/agent/api-proxy.js +9 -8
  4. package/dist/agent/api-proxy.js.map +1 -1
  5. package/dist/agent/browser-launcher.d.ts +3 -0
  6. package/dist/agent/browser-launcher.d.ts.map +1 -1
  7. package/dist/agent/browser-launcher.js +101 -43
  8. package/dist/agent/browser-launcher.js.map +1 -1
  9. package/dist/agent/coding-conventions.d.ts +8 -0
  10. package/dist/agent/coding-conventions.d.ts.map +1 -0
  11. package/dist/agent/coding-conventions.js +19 -0
  12. package/dist/agent/coding-conventions.js.map +1 -0
  13. package/dist/agent/command-handlers.d.ts.map +1 -1
  14. package/dist/agent/command-handlers.js +4 -16
  15. package/dist/agent/command-handlers.js.map +1 -1
  16. package/dist/agent/context-action-restart-fallback.d.ts.map +1 -1
  17. package/dist/agent/context-action-restart-fallback.js +9 -3
  18. package/dist/agent/context-action-restart-fallback.js.map +1 -1
  19. package/dist/agent/context-refresh.d.ts.map +1 -1
  20. package/dist/agent/context-refresh.js +0 -7
  21. package/dist/agent/context-refresh.js.map +1 -1
  22. package/dist/agent/context.d.ts +2 -4
  23. package/dist/agent/context.d.ts.map +1 -1
  24. package/dist/agent/context.js +12 -21
  25. package/dist/agent/context.js.map +1 -1
  26. package/dist/agent/engine-fallback.d.ts +0 -35
  27. package/dist/agent/engine-fallback.d.ts.map +1 -1
  28. package/dist/agent/engine-fallback.js +4 -3
  29. package/dist/agent/engine-fallback.js.map +1 -1
  30. package/dist/agent/engine-switch.d.ts +19 -38
  31. package/dist/agent/engine-switch.d.ts.map +1 -1
  32. package/dist/agent/engine-switch.js +46 -111
  33. package/dist/agent/engine-switch.js.map +1 -1
  34. package/dist/agent/engines/browser-tool-policy.d.ts +12 -1
  35. package/dist/agent/engines/browser-tool-policy.d.ts.map +1 -1
  36. package/dist/agent/engines/browser-tool-policy.js +17 -2
  37. package/dist/agent/engines/browser-tool-policy.js.map +1 -1
  38. package/dist/agent/engines/claude/executor-shell-groups.d.ts +11 -0
  39. package/dist/agent/engines/claude/executor-shell-groups.d.ts.map +1 -0
  40. package/dist/agent/engines/claude/executor-shell-groups.js +22 -0
  41. package/dist/agent/engines/claude/executor-shell-groups.js.map +1 -0
  42. package/dist/agent/engines/claude/session.d.ts +13 -4
  43. package/dist/agent/engines/claude/session.d.ts.map +1 -1
  44. package/dist/agent/engines/claude/session.js +165 -84
  45. package/dist/agent/engines/claude/session.js.map +1 -1
  46. package/dist/agent/engines/pi/shared/usage.d.ts +6 -8
  47. package/dist/agent/engines/pi/shared/usage.d.ts.map +1 -1
  48. package/dist/agent/engines/pi/shared/usage.js +24 -7
  49. package/dist/agent/engines/pi/shared/usage.js.map +1 -1
  50. package/dist/agent/engines/pi/tools.d.ts +3 -10
  51. package/dist/agent/engines/pi/tools.d.ts.map +1 -1
  52. package/dist/agent/engines/pi/tools.js +6 -20
  53. package/dist/agent/engines/pi/tools.js.map +1 -1
  54. package/dist/agent/engines/pi-coding/executor-shell.d.ts +4 -0
  55. package/dist/agent/engines/pi-coding/executor-shell.d.ts.map +1 -0
  56. package/dist/agent/engines/pi-coding/executor-shell.js +175 -0
  57. package/dist/agent/engines/pi-coding/executor-shell.js.map +1 -0
  58. package/dist/agent/engines/pi-coding/session.d.ts +4 -4
  59. package/dist/agent/engines/pi-coding/session.d.ts.map +1 -1
  60. package/dist/agent/engines/pi-coding/session.js +118 -66
  61. package/dist/agent/engines/pi-coding/session.js.map +1 -1
  62. package/dist/agent/engines/pi-coding/tools.d.ts +2 -5
  63. package/dist/agent/engines/pi-coding/tools.d.ts.map +1 -1
  64. package/dist/agent/engines/pi-coding/tools.js +7 -5
  65. package/dist/agent/engines/pi-coding/tools.js.map +1 -1
  66. package/dist/agent/engines/session-types.d.ts +8 -8
  67. package/dist/agent/engines/session-types.d.ts.map +1 -1
  68. package/dist/agent/external-context/interrupt.d.ts +0 -2
  69. package/dist/agent/external-context/interrupt.d.ts.map +1 -1
  70. package/dist/agent/external-context/interrupt.js +0 -1
  71. package/dist/agent/external-context/interrupt.js.map +1 -1
  72. package/dist/agent/external-context/registry.d.ts +0 -2
  73. package/dist/agent/external-context/registry.d.ts.map +1 -1
  74. package/dist/agent/external-context/registry.js +2 -4
  75. package/dist/agent/external-context/registry.js.map +1 -1
  76. package/dist/agent/external-context/runtime.d.ts.map +1 -1
  77. package/dist/agent/external-context/runtime.js +15 -14
  78. package/dist/agent/external-context/runtime.js.map +1 -1
  79. package/dist/agent/external-context/types.d.ts +0 -2
  80. package/dist/agent/external-context/types.d.ts.map +1 -1
  81. package/dist/agent/fast-responder.d.ts +4 -20
  82. package/dist/agent/fast-responder.d.ts.map +1 -1
  83. package/dist/agent/fast-responder.js +28 -42
  84. package/dist/agent/fast-responder.js.map +1 -1
  85. package/dist/agent/interrupt-handler.d.ts +0 -6
  86. package/dist/agent/interrupt-handler.d.ts.map +1 -1
  87. package/dist/agent/interrupt-handler.js +5 -47
  88. package/dist/agent/interrupt-handler.js.map +1 -1
  89. package/dist/agent/lab-run-system-prompt.d.ts.map +1 -1
  90. package/dist/agent/lab-run-system-prompt.js +4 -1
  91. package/dist/agent/lab-run-system-prompt.js.map +1 -1
  92. package/dist/agent/lifecycle-trace.d.ts.map +1 -1
  93. package/dist/agent/lifecycle-trace.js +6 -5
  94. package/dist/agent/lifecycle-trace.js.map +1 -1
  95. package/dist/agent/loop.d.ts +8 -2
  96. package/dist/agent/loop.d.ts.map +1 -1
  97. package/dist/agent/loop.js +287 -156
  98. package/dist/agent/loop.js.map +1 -1
  99. package/dist/agent/memory-protocol.d.ts +2 -2
  100. package/dist/agent/memory-protocol.d.ts.map +1 -1
  101. package/dist/agent/memory-protocol.js +3 -15
  102. package/dist/agent/memory-protocol.js.map +1 -1
  103. package/dist/agent/memory-store.d.ts +58 -0
  104. package/dist/agent/memory-store.d.ts.map +1 -0
  105. package/dist/agent/memory-store.js +152 -0
  106. package/dist/agent/memory-store.js.map +1 -0
  107. package/dist/agent/model-provider.d.ts +18 -5
  108. package/dist/agent/model-provider.d.ts.map +1 -1
  109. package/dist/agent/model-provider.js +58 -18
  110. package/dist/agent/model-provider.js.map +1 -1
  111. package/dist/agent/nightly-maintenance/dreaming-batch.d.ts +184 -0
  112. package/dist/agent/nightly-maintenance/dreaming-batch.d.ts.map +1 -0
  113. package/dist/agent/nightly-maintenance/dreaming-batch.js +192 -0
  114. package/dist/agent/nightly-maintenance/dreaming-batch.js.map +1 -0
  115. package/dist/agent/nightly-maintenance/dreaming-files.d.ts +48 -0
  116. package/dist/agent/nightly-maintenance/dreaming-files.d.ts.map +1 -0
  117. package/dist/agent/nightly-maintenance/dreaming-files.js +109 -0
  118. package/dist/agent/nightly-maintenance/dreaming-files.js.map +1 -0
  119. package/dist/agent/nightly-maintenance/dreaming-ledger.d.ts +62 -0
  120. package/dist/agent/nightly-maintenance/dreaming-ledger.d.ts.map +1 -0
  121. package/dist/agent/nightly-maintenance/dreaming-ledger.js +146 -0
  122. package/dist/agent/nightly-maintenance/dreaming-ledger.js.map +1 -0
  123. package/dist/agent/nightly-maintenance/dreaming-planner.d.ts +86 -0
  124. package/dist/agent/nightly-maintenance/dreaming-planner.d.ts.map +1 -0
  125. package/dist/agent/nightly-maintenance/dreaming-planner.js +304 -0
  126. package/dist/agent/nightly-maintenance/dreaming-planner.js.map +1 -0
  127. package/dist/agent/nightly-maintenance/dreaming-runner.d.ts +93 -0
  128. package/dist/agent/nightly-maintenance/dreaming-runner.d.ts.map +1 -0
  129. package/dist/agent/nightly-maintenance/dreaming-runner.js +156 -0
  130. package/dist/agent/nightly-maintenance/dreaming-runner.js.map +1 -0
  131. package/dist/agent/nightly-maintenance/dreaming-sources.d.ts +115 -0
  132. package/dist/agent/nightly-maintenance/dreaming-sources.d.ts.map +1 -0
  133. package/dist/agent/nightly-maintenance/dreaming-sources.js +270 -0
  134. package/dist/agent/nightly-maintenance/dreaming-sources.js.map +1 -0
  135. package/dist/agent/nightly-maintenance/index.d.ts +28 -8
  136. package/dist/agent/nightly-maintenance/index.d.ts.map +1 -1
  137. package/dist/agent/nightly-maintenance/index.js +42 -15
  138. package/dist/agent/nightly-maintenance/index.js.map +1 -1
  139. package/dist/agent/nightly-maintenance/skill-learning-gate.js +1 -1
  140. package/dist/agent/nightly-maintenance/skill-learning-gate.js.map +1 -1
  141. package/dist/agent/nightly-maintenance/vocabulary-processor.d.ts +1 -1
  142. package/dist/agent/nightly-maintenance/vocabulary-processor.d.ts.map +1 -1
  143. package/dist/agent/product-identity.d.ts +5 -4
  144. package/dist/agent/product-identity.d.ts.map +1 -1
  145. package/dist/agent/product-identity.js +7 -6
  146. package/dist/agent/product-identity.js.map +1 -1
  147. package/dist/agent/public-activity.js +1 -1
  148. package/dist/agent/public-activity.js.map +1 -1
  149. package/dist/agent/runtime-context.d.ts +27 -0
  150. package/dist/agent/runtime-context.d.ts.map +1 -0
  151. package/dist/agent/runtime-context.js +60 -0
  152. package/dist/agent/runtime-context.js.map +1 -0
  153. package/dist/agent/runtime-disclosure-policy.d.ts +6 -0
  154. package/dist/agent/runtime-disclosure-policy.d.ts.map +1 -0
  155. package/dist/agent/runtime-disclosure-policy.js +16 -0
  156. package/dist/agent/runtime-disclosure-policy.js.map +1 -0
  157. package/dist/agent/runtime-surface.d.ts +15 -4
  158. package/dist/agent/runtime-surface.d.ts.map +1 -1
  159. package/dist/agent/runtime-surface.js +0 -1
  160. package/dist/agent/runtime-surface.js.map +1 -1
  161. package/dist/agent/session-manager.d.ts +30 -126
  162. package/dist/agent/session-manager.d.ts.map +1 -1
  163. package/dist/agent/session-manager.js +60 -484
  164. package/dist/agent/session-manager.js.map +1 -1
  165. package/dist/agent/state.d.ts +42 -2
  166. package/dist/agent/state.d.ts.map +1 -1
  167. package/dist/agent/state.js +21 -11
  168. package/dist/agent/state.js.map +1 -1
  169. package/dist/agent/stream-handler.d.ts +6 -13
  170. package/dist/agent/stream-handler.d.ts.map +1 -1
  171. package/dist/agent/stream-handler.js +8 -62
  172. package/dist/agent/stream-handler.js.map +1 -1
  173. package/dist/agent/system-prompt.d.ts +2 -0
  174. package/dist/agent/system-prompt.d.ts.map +1 -1
  175. package/dist/agent/system-prompt.js +21 -20
  176. package/dist/agent/system-prompt.js.map +1 -1
  177. package/dist/agent/tool-usage.d.ts.map +1 -1
  178. package/dist/agent/tool-usage.js +0 -1
  179. package/dist/agent/tool-usage.js.map +1 -1
  180. package/dist/agent/transcript/transcript-backfill.js +3 -3
  181. package/dist/agent/transcript/transcript-backfill.js.map +1 -1
  182. package/dist/agent/transcript/transcript-indexer.d.ts +3 -31
  183. package/dist/agent/transcript/transcript-indexer.d.ts.map +1 -1
  184. package/dist/agent/transcript/transcript-indexer.js +58 -57
  185. package/dist/agent/transcript/transcript-indexer.js.map +1 -1
  186. package/dist/agent/transcript-parsers/trivial-tools.d.ts.map +1 -1
  187. package/dist/agent/transcript-parsers/trivial-tools.js +0 -2
  188. package/dist/agent/transcript-parsers/trivial-tools.js.map +1 -1
  189. package/dist/agent/transcript-parsers/types.d.ts +1 -1
  190. package/dist/agent/transcript-summarizer.d.ts +14 -0
  191. package/dist/agent/transcript-summarizer.d.ts.map +1 -1
  192. package/dist/agent/transcript-summarizer.js +69 -45
  193. package/dist/agent/transcript-summarizer.js.map +1 -1
  194. package/dist/agent/usage/usage-report-queue.d.ts.map +1 -1
  195. package/dist/agent/usage/usage-report-queue.js +53 -52
  196. package/dist/agent/usage/usage-report-queue.js.map +1 -1
  197. package/dist/agent/usage/usage-report-types.d.ts +1 -1
  198. package/dist/agent/usage/usage-report-types.d.ts.map +1 -1
  199. package/dist/agent/vocabulary-glossary.d.ts +3 -3
  200. package/dist/agent/wake-cycle-runner.d.ts +14 -17
  201. package/dist/agent/wake-cycle-runner.d.ts.map +1 -1
  202. package/dist/agent/wake-cycle-runner.js +121 -290
  203. package/dist/agent/wake-cycle-runner.js.map +1 -1
  204. package/dist/builtin-skills/owner-manual/SKILL.md +2 -2
  205. package/dist/calendar/occurrence-ownership.d.ts.map +1 -1
  206. package/dist/calendar/occurrence-ownership.js +6 -5
  207. package/dist/calendar/occurrence-ownership.js.map +1 -1
  208. package/dist/calendar/scheduler.d.ts.map +1 -1
  209. package/dist/calendar/scheduler.js +4 -3
  210. package/dist/calendar/scheduler.js.map +1 -1
  211. package/dist/channels/interface.d.ts +8 -2
  212. package/dist/channels/interface.d.ts.map +1 -1
  213. package/dist/channels/interface.js +8 -1
  214. package/dist/channels/interface.js.map +1 -1
  215. package/dist/channels/manager.d.ts.map +1 -1
  216. package/dist/channels/manager.js +5 -1
  217. package/dist/channels/manager.js.map +1 -1
  218. package/dist/channels/queue.d.ts.map +1 -1
  219. package/dist/channels/queue.js +8 -0
  220. package/dist/channels/queue.js.map +1 -1
  221. package/dist/channels/tui.d.ts +14 -1
  222. package/dist/channels/tui.d.ts.map +1 -1
  223. package/dist/channels/tui.js +64 -2
  224. package/dist/channels/tui.js.map +1 -1
  225. package/dist/cli/index.d.ts +2 -2
  226. package/dist/cli/index.d.ts.map +1 -1
  227. package/dist/cli/index.js +22 -53
  228. package/dist/cli/index.js.map +1 -1
  229. package/dist/config/index.d.ts +13 -7
  230. package/dist/config/index.d.ts.map +1 -1
  231. package/dist/config/index.js +51 -64
  232. package/dist/config/index.js.map +1 -1
  233. package/dist/config/types.d.ts +28 -7
  234. package/dist/config/types.d.ts.map +1 -1
  235. package/dist/config/types.js +22 -20
  236. package/dist/config/types.js.map +1 -1
  237. package/dist/conversations/lazy-runtime.d.ts +5 -0
  238. package/dist/conversations/lazy-runtime.d.ts.map +1 -0
  239. package/dist/conversations/lazy-runtime.js +12 -0
  240. package/dist/conversations/lazy-runtime.js.map +1 -0
  241. package/dist/conversations/runtime.d.ts +17 -0
  242. package/dist/conversations/runtime.d.ts.map +1 -0
  243. package/dist/conversations/runtime.js +92 -0
  244. package/dist/conversations/runtime.js.map +1 -0
  245. package/dist/conversations/scheduler.d.ts +57 -0
  246. package/dist/conversations/scheduler.d.ts.map +1 -0
  247. package/dist/conversations/scheduler.js +175 -0
  248. package/dist/conversations/scheduler.js.map +1 -0
  249. package/dist/data-processing/pipeline.d.ts +0 -2
  250. package/dist/data-processing/pipeline.d.ts.map +1 -1
  251. package/dist/data-processing/pipeline.js +19 -24
  252. package/dist/data-processing/pipeline.js.map +1 -1
  253. package/dist/data-processing/restructure.d.ts +0 -2
  254. package/dist/data-processing/restructure.d.ts.map +1 -1
  255. package/dist/data-processing/restructure.js +6 -9
  256. package/dist/data-processing/restructure.js.map +1 -1
  257. package/dist/data-processing/types.d.ts +2 -9
  258. package/dist/data-processing/types.d.ts.map +1 -1
  259. package/dist/data-processing/types.js +2 -13
  260. package/dist/data-processing/types.js.map +1 -1
  261. package/dist/email/email-route-store.d.ts.map +1 -1
  262. package/dist/email/email-route-store.js +6 -5
  263. package/dist/email/email-route-store.js.map +1 -1
  264. package/dist/i18n/messages.d.ts +26 -2
  265. package/dist/i18n/messages.d.ts.map +1 -1
  266. package/dist/i18n/messages.js +30 -5
  267. package/dist/i18n/messages.js.map +1 -1
  268. package/dist/index.js.map +1 -1
  269. package/dist/logger.d.ts +11 -0
  270. package/dist/logger.d.ts.map +1 -1
  271. package/dist/logger.js +49 -1
  272. package/dist/logger.js.map +1 -1
  273. package/dist/obs/mini-app/status.d.ts.map +1 -1
  274. package/dist/obs/mini-app/status.js +0 -1
  275. package/dist/obs/mini-app/status.js.map +1 -1
  276. package/dist/onboarding/onboarding-service-client.d.ts +1 -0
  277. package/dist/onboarding/onboarding-service-client.d.ts.map +1 -1
  278. package/dist/onboarding/onboarding-service-client.js.map +1 -1
  279. package/dist/onboarding/remote-set-owner.d.ts.map +1 -1
  280. package/dist/onboarding/remote-set-owner.js +0 -1
  281. package/dist/onboarding/remote-set-owner.js.map +1 -1
  282. package/dist/onboarding/set-owner.d.ts +1 -1
  283. package/dist/onboarding/set-owner.d.ts.map +1 -1
  284. package/dist/onboarding/set-owner.js +3 -6
  285. package/dist/onboarding/set-owner.js.map +1 -1
  286. package/dist/reconfigure.d.ts.map +1 -1
  287. package/dist/reconfigure.js +1 -32
  288. package/dist/reconfigure.js.map +1 -1
  289. package/dist/reset.d.ts.map +1 -1
  290. package/dist/reset.js +3 -1
  291. package/dist/reset.js.map +1 -1
  292. package/dist/threads/active-threads-store.d.ts.map +1 -1
  293. package/dist/threads/active-threads-store.js +8 -27
  294. package/dist/threads/active-threads-store.js.map +1 -1
  295. package/dist/threads/dreaming-execution.d.ts +10 -0
  296. package/dist/threads/dreaming-execution.d.ts.map +1 -0
  297. package/dist/threads/dreaming-execution.js +78 -0
  298. package/dist/threads/dreaming-execution.js.map +1 -0
  299. package/dist/threads/execution-authority.d.ts +43 -0
  300. package/dist/threads/execution-authority.d.ts.map +1 -0
  301. package/dist/threads/execution-authority.js +75 -0
  302. package/dist/threads/execution-authority.js.map +1 -0
  303. package/dist/threads/execution-budget.d.ts +107 -0
  304. package/dist/threads/execution-budget.d.ts.map +1 -0
  305. package/dist/threads/execution-budget.js +257 -0
  306. package/dist/threads/execution-budget.js.map +1 -0
  307. package/dist/threads/execution-host.d.ts +17 -0
  308. package/dist/threads/execution-host.d.ts.map +1 -0
  309. package/dist/threads/execution-host.js +66 -0
  310. package/dist/threads/execution-host.js.map +1 -0
  311. package/dist/threads/execution-scheduler.d.ts +162 -0
  312. package/dist/threads/execution-scheduler.d.ts.map +1 -0
  313. package/dist/threads/execution-scheduler.js +413 -0
  314. package/dist/threads/execution-scheduler.js.map +1 -0
  315. package/dist/threads/execution-snapshot.d.ts +91 -0
  316. package/dist/threads/execution-snapshot.d.ts.map +1 -0
  317. package/dist/threads/execution-snapshot.js +146 -0
  318. package/dist/threads/execution-snapshot.js.map +1 -0
  319. package/dist/threads/execution-store.d.ts +374 -0
  320. package/dist/threads/execution-store.d.ts.map +1 -0
  321. package/dist/threads/execution-store.js +910 -0
  322. package/dist/threads/execution-store.js.map +1 -0
  323. package/dist/threads/execution-thread-error.d.ts +9 -0
  324. package/dist/threads/execution-thread-error.d.ts.map +1 -0
  325. package/dist/threads/execution-thread-error.js +12 -0
  326. package/dist/threads/execution-thread-error.js.map +1 -0
  327. package/dist/threads/executor-exit.d.ts +18 -0
  328. package/dist/threads/executor-exit.d.ts.map +1 -0
  329. package/dist/threads/executor-exit.js +21 -0
  330. package/dist/threads/executor-exit.js.map +1 -0
  331. package/dist/threads/foreground-task.d.ts +91 -0
  332. package/dist/threads/foreground-task.d.ts.map +1 -0
  333. package/dist/threads/foreground-task.js +191 -0
  334. package/dist/threads/foreground-task.js.map +1 -0
  335. package/dist/threads/foreman-business.d.ts +9 -0
  336. package/dist/threads/foreman-business.d.ts.map +1 -0
  337. package/dist/threads/foreman-business.js +117 -0
  338. package/dist/threads/foreman-business.js.map +1 -0
  339. package/dist/threads/foreman-client.d.ts +18 -0
  340. package/dist/threads/foreman-client.d.ts.map +1 -0
  341. package/dist/threads/foreman-client.js +51 -0
  342. package/dist/threads/foreman-client.js.map +1 -0
  343. package/dist/threads/foreman-delivery.d.ts +5 -0
  344. package/dist/threads/foreman-delivery.d.ts.map +1 -0
  345. package/dist/threads/foreman-delivery.js +21 -0
  346. package/dist/threads/foreman-delivery.js.map +1 -0
  347. package/dist/threads/foreman-execution.d.ts +32 -0
  348. package/dist/threads/foreman-execution.d.ts.map +1 -0
  349. package/dist/threads/foreman-execution.js +90 -0
  350. package/dist/threads/foreman-execution.js.map +1 -0
  351. package/dist/threads/foreman-host.d.ts +77 -0
  352. package/dist/threads/foreman-host.d.ts.map +1 -0
  353. package/dist/threads/foreman-host.js +891 -0
  354. package/dist/threads/foreman-host.js.map +1 -0
  355. package/dist/threads/foreman-protocol.d.ts +242 -0
  356. package/dist/threads/foreman-protocol.d.ts.map +1 -0
  357. package/dist/threads/foreman-protocol.js +116 -0
  358. package/dist/threads/foreman-protocol.js.map +1 -0
  359. package/dist/threads/foreman-store.d.ts +151 -0
  360. package/dist/threads/foreman-store.d.ts.map +1 -0
  361. package/dist/threads/foreman-store.js +299 -0
  362. package/dist/threads/foreman-store.js.map +1 -0
  363. package/dist/threads/generic-execution.d.ts +25 -0
  364. package/dist/threads/generic-execution.d.ts.map +1 -0
  365. package/dist/threads/generic-execution.js +86 -0
  366. package/dist/threads/generic-execution.js.map +1 -0
  367. package/dist/threads/init-thread-dir.d.ts +2 -2
  368. package/dist/threads/init-thread-dir.d.ts.map +1 -1
  369. package/dist/threads/init-thread-dir.js +6 -6
  370. package/dist/threads/init-thread-dir.js.map +1 -1
  371. package/dist/threads/ipc.d.ts +108 -2
  372. package/dist/threads/ipc.d.ts.map +1 -1
  373. package/dist/threads/ipc.js +14 -2
  374. package/dist/threads/ipc.js.map +1 -1
  375. package/dist/threads/lab-run-thread.d.ts +3 -5
  376. package/dist/threads/lab-run-thread.d.ts.map +1 -1
  377. package/dist/threads/lab-run-thread.js +3 -5
  378. package/dist/threads/lab-run-thread.js.map +1 -1
  379. package/dist/threads/lab-runtime-policy.d.ts +26 -21
  380. package/dist/threads/lab-runtime-policy.d.ts.map +1 -1
  381. package/dist/threads/lab-runtime-policy.js +31 -126
  382. package/dist/threads/lab-runtime-policy.js.map +1 -1
  383. package/dist/threads/process-group.d.ts +41 -0
  384. package/dist/threads/process-group.d.ts.map +1 -0
  385. package/dist/threads/process-group.js +161 -0
  386. package/dist/threads/process-group.js.map +1 -0
  387. package/dist/threads/relay-adapter.d.ts +8 -0
  388. package/dist/threads/relay-adapter.d.ts.map +1 -1
  389. package/dist/threads/relay-adapter.js +26 -1
  390. package/dist/threads/relay-adapter.js.map +1 -1
  391. package/dist/threads/supervisor.d.ts +191 -57
  392. package/dist/threads/supervisor.d.ts.map +1 -1
  393. package/dist/threads/supervisor.js +766 -427
  394. package/dist/threads/supervisor.js.map +1 -1
  395. package/dist/threads/task-workspace.d.ts +8 -0
  396. package/dist/threads/task-workspace.d.ts.map +1 -0
  397. package/dist/threads/task-workspace.js +52 -0
  398. package/dist/threads/task-workspace.js.map +1 -0
  399. package/dist/threads/worker-entry.d.ts.map +1 -1
  400. package/dist/threads/worker-entry.js +314 -34
  401. package/dist/threads/worker-entry.js.map +1 -1
  402. package/dist/threads/worker-execution-runtime.d.ts +75 -0
  403. package/dist/threads/worker-execution-runtime.d.ts.map +1 -0
  404. package/dist/threads/worker-execution-runtime.js +142 -0
  405. package/dist/threads/worker-execution-runtime.js.map +1 -0
  406. package/dist/threads/worker-execution.d.ts +35 -0
  407. package/dist/threads/worker-execution.d.ts.map +1 -0
  408. package/dist/threads/worker-execution.js +2 -0
  409. package/dist/threads/worker-execution.js.map +1 -0
  410. package/dist/threads/worker-runtime-policy.d.ts +42 -0
  411. package/dist/threads/worker-runtime-policy.d.ts.map +1 -0
  412. package/dist/threads/worker-runtime-policy.js +98 -0
  413. package/dist/threads/worker-runtime-policy.js.map +1 -0
  414. package/dist/tools/adapter-tool.d.ts +12 -0
  415. package/dist/tools/adapter-tool.d.ts.map +1 -0
  416. package/dist/tools/adapter-tool.js +2 -0
  417. package/dist/tools/adapter-tool.js.map +1 -0
  418. package/dist/tools/data-service.d.ts +2 -2
  419. package/dist/tools/delegation.d.ts +121 -0
  420. package/dist/tools/delegation.d.ts.map +1 -0
  421. package/dist/tools/delegation.js +106 -0
  422. package/dist/tools/delegation.js.map +1 -0
  423. package/dist/tools/dream-memory.d.ts +122 -0
  424. package/dist/tools/dream-memory.d.ts.map +1 -0
  425. package/dist/tools/dream-memory.js +513 -0
  426. package/dist/tools/dream-memory.js.map +1 -0
  427. package/dist/tools/email.d.ts +1 -1
  428. package/dist/tools/execution-control.d.ts +31 -0
  429. package/dist/tools/execution-control.d.ts.map +1 -0
  430. package/dist/tools/execution-control.js +71 -0
  431. package/dist/tools/execution-control.js.map +1 -0
  432. package/dist/tools/index.d.ts +5 -7
  433. package/dist/tools/index.d.ts.map +1 -1
  434. package/dist/tools/index.js +7 -13
  435. package/dist/tools/index.js.map +1 -1
  436. package/dist/tools/mcp-manager.d.ts +1 -1
  437. package/dist/tools/memory.d.ts +1 -1
  438. package/dist/tools/memory.d.ts.map +1 -1
  439. package/dist/tools/memory.js +69 -45
  440. package/dist/tools/memory.js.map +1 -1
  441. package/dist/tools/notify.d.ts +5 -0
  442. package/dist/tools/notify.d.ts.map +1 -1
  443. package/dist/tools/notify.js +45 -10
  444. package/dist/tools/notify.js.map +1 -1
  445. package/dist/tools/skill.d.ts +1 -1
  446. package/dist/tools/totp.d.ts +1 -1
  447. package/dist/tui/client.d.ts +1 -16
  448. package/dist/tui/client.d.ts.map +1 -1
  449. package/dist/tui/client.js +0 -4
  450. package/dist/tui/client.js.map +1 -1
  451. package/dist/tui/index.d.ts.map +1 -1
  452. package/dist/tui/index.js +0 -41
  453. package/dist/tui/index.js.map +1 -1
  454. package/dist/tui/protocol.d.ts +2 -1
  455. package/dist/tui/protocol.d.ts.map +1 -1
  456. package/dist/tui/protocol.js +2 -1
  457. package/dist/tui/protocol.js.map +1 -1
  458. package/dist/tui/renderer.d.ts.map +1 -1
  459. package/dist/tui/renderer.js +0 -5
  460. package/dist/tui/renderer.js.map +1 -1
  461. package/dist/tui/request-store.d.ts +1 -1
  462. package/dist/tui/request-store.d.ts.map +1 -1
  463. package/dist/tui/request-store.js +2 -2
  464. package/dist/tui/request-store.js.map +1 -1
  465. package/dist/tui/server.d.ts.map +1 -1
  466. package/dist/tui/server.js +137 -25
  467. package/dist/tui/server.js.map +1 -1
  468. package/dist/utils/claude-transcripts.d.ts +6 -0
  469. package/dist/utils/claude-transcripts.d.ts.map +1 -1
  470. package/dist/utils/claude-transcripts.js +8 -2
  471. package/dist/utils/claude-transcripts.js.map +1 -1
  472. package/dist/utils/restart.d.ts.map +1 -1
  473. package/dist/utils/restart.js +6 -0
  474. package/dist/utils/restart.js.map +1 -1
  475. package/dist-agent/bundle.cjs +2 -2
  476. package/package.json +1 -1
  477. package/dist/agent/mailbox.d.ts +0 -50
  478. package/dist/agent/mailbox.d.ts.map +0 -1
  479. package/dist/agent/mailbox.js +0 -124
  480. package/dist/agent/mailbox.js.map +0 -1
  481. package/dist/agent/nightly-maintenance/memory-dreaming-sweep.d.ts +0 -77
  482. package/dist/agent/nightly-maintenance/memory-dreaming-sweep.d.ts.map +0 -1
  483. package/dist/agent/nightly-maintenance/memory-dreaming-sweep.js +0 -158
  484. package/dist/agent/nightly-maintenance/memory-dreaming-sweep.js.map +0 -1
  485. package/dist/agent/nightly-maintenance/memory-dreaming.d.ts +0 -116
  486. package/dist/agent/nightly-maintenance/memory-dreaming.d.ts.map +0 -1
  487. package/dist/agent/nightly-maintenance/memory-dreaming.js +0 -458
  488. package/dist/agent/nightly-maintenance/memory-dreaming.js.map +0 -1
  489. package/dist/threads/task-contracts.d.ts +0 -194
  490. package/dist/threads/task-contracts.d.ts.map +0 -1
  491. package/dist/threads/task-contracts.js +0 -82
  492. package/dist/threads/task-contracts.js.map +0 -1
  493. package/dist/threads/task-sidecar.d.ts +0 -145
  494. package/dist/threads/task-sidecar.d.ts.map +0 -1
  495. package/dist/threads/task-sidecar.js +0 -311
  496. package/dist/threads/task-sidecar.js.map +0 -1
  497. package/dist/threads/task-store.d.ts +0 -172
  498. package/dist/threads/task-store.d.ts.map +0 -1
  499. package/dist/threads/task-store.js +0 -599
  500. package/dist/threads/task-store.js.map +0 -1
  501. package/dist/threads/thread-task-context.d.ts +0 -40
  502. package/dist/threads/thread-task-context.d.ts.map +0 -1
  503. package/dist/threads/thread-task-context.js +0 -160
  504. package/dist/threads/thread-task-context.js.map +0 -1
  505. package/dist/threads/thread-task-controller.d.ts +0 -122
  506. package/dist/threads/thread-task-controller.d.ts.map +0 -1
  507. package/dist/threads/thread-task-controller.js +0 -276
  508. package/dist/threads/thread-task-controller.js.map +0 -1
  509. package/dist/threads/thread-task-runtime.d.ts +0 -354
  510. package/dist/threads/thread-task-runtime.d.ts.map +0 -1
  511. package/dist/threads/thread-task-runtime.js +0 -917
  512. package/dist/threads/thread-task-runtime.js.map +0 -1
  513. package/dist/tools/switch-session.d.ts +0 -15
  514. package/dist/tools/switch-session.d.ts.map +0 -1
  515. package/dist/tools/switch-session.js +0 -88
  516. package/dist/tools/switch-session.js.map +0 -1
  517. package/dist/tools/thread-tasks.d.ts +0 -23
  518. package/dist/tools/thread-tasks.d.ts.map +0 -1
  519. package/dist/tools/thread-tasks.js +0 -122
  520. package/dist/tools/thread-tasks.js.map +0 -1
@@ -0,0 +1,891 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { DEFAULT_THREAD_ID } from "../channels/interface.js";
3
+ import { isDeliveryTarget } from "../channels/delivery-target.js";
4
+ import { taskContinuationRouting } from "../agent/reply-target.js";
5
+ import { resolveWorkspaceCwd } from "../agent/workspace-cwd.js";
6
+ import { t } from "../i18n/index.js";
7
+ import { logger } from "../logger.js";
8
+ import { ExecutionCommandConflictError, isTerminalExecutionState, } from "./execution-store.js";
9
+ import { DEFAULT_LAB_RUN_BUDGET } from "./execution-snapshot.js";
10
+ import { prepareTaskWorkspace } from "./task-workspace.js";
11
+ import { publicTaskText, taskDeliveryMode } from "./foreman-delivery.js";
12
+ import { CancelArgsSchema, DelegateArgsSchema, DelegateBatchArgsSchema, FOREMAN_EVENT_META_KEY, FOREMAN_SCOPE, InputArgsSchema, StatusArgsSchema, } from "./foreman-protocol.js";
13
+ /**
14
+ * The Foreman host (#2805 stage 3), in the main process.
15
+ *
16
+ * General's background-task tools reach it directly (default thread) or over
17
+ * IPC (other conversational threads; the supervisor fills in the thread id
18
+ * from the worker it talks to). It turns their commands into execution
19
+ * protocol commands with caller `{ kind: "general", ref: threadId }`, so a
20
+ * thread can only see and steer the tasks it delegated itself.
21
+ *
22
+ * It is also the `general` caller's delivery handler: a worker's question or
23
+ * result becomes a durable system message in the delegating thread's queue,
24
+ * and opens the owner delivery obligation that the fallback sweep enforces.
25
+ */
26
+ const SWEEP_INTERVAL_MS = 30_000;
27
+ /** About 4k tokens, the issue's initial budget for General's active-task summary. */
28
+ const SUMMARY_CHAR_BUDGET = 12_000;
29
+ function errorMessage(error) {
30
+ return error instanceof Error ? error.message : String(error);
31
+ }
32
+ function fail(text) {
33
+ return { ok: false, text: `Error: ${text}` };
34
+ }
35
+ function invalid(error) {
36
+ return fail(`invalid arguments: ${error.issues.map((issue) => `${issue.path.map(String).join(".") || "args"}: ${issue.message}`).join("; ")}`);
37
+ }
38
+ /** Event payload fields are untyped JSON; only a string is shown as text. */
39
+ function textOf(value, fallback = "") {
40
+ return typeof value === "string" ? value : fallback;
41
+ }
42
+ function clip(text, max) {
43
+ return text.length <= max ? text : `${text.slice(0, max)}…`;
44
+ }
45
+ function identity(...parts) {
46
+ return createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
47
+ }
48
+ function taskIdFor(conversation, dispatch, key) {
49
+ return `task-${identity(conversation, dispatch, key)}`;
50
+ }
51
+ function remainingBudget(limits, attempts) {
52
+ const used = (field) => attempts.reduce((sum, attempt) => {
53
+ const value = (attempt.usage ?? attempt.result?.usage)?.[field];
54
+ return sum + (typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0);
55
+ }, 0);
56
+ return {
57
+ maxModelAttempts: limits.maxModelAttempts - used("modelAttempts"),
58
+ maxToolCalls: limits.maxToolCalls - used("toolCalls"),
59
+ maxDurationMs: limits.maxDurationMs - used("elapsedMs"),
60
+ };
61
+ }
62
+ function describeState(record) {
63
+ switch (record.state) {
64
+ case "queued": return "queued, waiting for a worker slot";
65
+ case "running": return "running";
66
+ case "waiting_input": return "waiting for an answer";
67
+ case "cancel_requested": return "stopping: cancel requested, the worker has not confirmed it stopped yet";
68
+ case "succeeded": return "succeeded";
69
+ case "failed": return "failed";
70
+ case "cancelled": return "cancelled";
71
+ }
72
+ }
73
+ function fallbackKey(obligation) {
74
+ if (obligation.kind === "question")
75
+ return "foreman.fallback.question";
76
+ if (obligation.status === "succeeded")
77
+ return "foreman.fallback.succeeded";
78
+ if (obligation.status === "cancelled")
79
+ return "foreman.fallback.cancelled";
80
+ if (obligation.status === "partial")
81
+ return "foreman.fallback.partial";
82
+ return "foreman.fallback.failed";
83
+ }
84
+ export class ForemanHost {
85
+ deps;
86
+ pendingBatches = new Map();
87
+ timer = null;
88
+ sweeping = false;
89
+ now;
90
+ constructor(deps) {
91
+ this.deps = deps;
92
+ this.now = deps.now ?? (() => new Date());
93
+ }
94
+ /** Start the fallback sweep. The first pass finds obligations a restart left overdue. */
95
+ start() {
96
+ if (this.timer)
97
+ return;
98
+ this.timer = setInterval(() => { void this.sweep(); }, SWEEP_INTERVAL_MS);
99
+ this.timer.unref();
100
+ void this.sweep();
101
+ }
102
+ stop() {
103
+ if (this.timer)
104
+ clearInterval(this.timer);
105
+ this.timer = null;
106
+ }
107
+ /** The supervisor supplies executor identity; model arguments never select an account. */
108
+ async readBusiness(executionId, tool, args, workspace) {
109
+ const record = this.deps.executions.get(executionId);
110
+ const dispatch = record ? this.ownedDispatch(record.caller.ref, record.taskId) : null;
111
+ if (record?.caller.kind !== "general" || record.state !== "running" || !record.payload.businessRead || !dispatch?.metadata.businessRead
112
+ || !this.deps.canReadBusiness?.(dispatch.threadId, dispatch.replyTarget) || !this.deps.readBusiness) {
113
+ return fail("This execution is not authorized to read the owner's workspace");
114
+ }
115
+ if (record.payload.workspaceMode === "read_only" && tool === "manage_drive"
116
+ && typeof args === "object" && args !== null && "action" in args && args.action === "download_file") {
117
+ return fail("A read-only Task cannot download files into its workspace");
118
+ }
119
+ const stillAuthorized = () => {
120
+ const current = this.deps.executions.get(executionId);
121
+ return current?.state === "running" && current.workerInstanceId === record.workerInstanceId
122
+ && this.deps.canReadBusiness?.(dispatch.threadId, dispatch.replyTarget) === true;
123
+ };
124
+ const result = await this.deps.readBusiness(tool, args, workspace, stillAuthorized);
125
+ return stillAuthorized() ? result : fail("The execution stopped or its business-read permission was revoked");
126
+ }
127
+ async handle(threadId, command) {
128
+ try {
129
+ return await this.run(threadId, command);
130
+ }
131
+ catch (error) {
132
+ logger.warn(`[foreman] ${command.type} from thread=${threadId} failed: ${errorMessage(error)}`);
133
+ return fail(errorMessage(error));
134
+ }
135
+ }
136
+ // ----------------------------------------------------------- delivery
137
+ /**
138
+ * The execution outbox delivers a `general` caller's event. Only a question
139
+ * or a result asks something of General; acceptance and progress are read
140
+ * on demand through background_task_status. Resolves true once the event is
141
+ * in the thread's durable queue; a throw makes the outbox retry it.
142
+ */
143
+ deliverEvent(event, caller) {
144
+ if (caller.kind !== "general")
145
+ return Promise.resolve(false);
146
+ if (event.type !== "question" && event.type !== "result")
147
+ return Promise.resolve(true);
148
+ const threadId = caller.ref;
149
+ const dispatch = this.ownedDispatch(threadId, event.taskId);
150
+ if (dispatch?.metadata.parentTaskId && event.type === "result") {
151
+ return this.deliverParentResult(threadId, dispatch.metadata.parentTaskId);
152
+ }
153
+ // A retried Task may still have an older result/question in the outbox.
154
+ // A transport replay must not revive that superseded attempt's delivery.
155
+ const latest = dispatch && !dispatch.metadata.children?.length ? this.latest(dispatch.taskId) : null;
156
+ if (latest && latest.executionId !== event.executionId)
157
+ return Promise.resolve(true);
158
+ if (event.type === "result" && this.deps.store.isUserCancelled(event.taskId))
159
+ return Promise.resolve(true);
160
+ const userCancelled = event.type === "result" && event.payload.status === "cancelled"
161
+ && (event.payload.userInitiated === true || this.deps.store.isUserCancelled(event.taskId));
162
+ const publicText = publicTaskText(event);
163
+ const direct = Boolean(dispatch?.replyTarget && !userCancelled && publicText !== null && taskDeliveryMode(event) === "direct"
164
+ && !dispatch.metadata.children?.length);
165
+ // Queue first, then open the obligation: both are idempotent per event, so
166
+ // a crash in between is repaired by the outbox's redelivery.
167
+ if (!direct)
168
+ this.deps.enqueueToGeneral(threadId, this.eventMessage(event, threadId, dispatch, dispatch !== null && !userCancelled));
169
+ if (dispatch) {
170
+ this.deps.store.open({
171
+ taskId: event.taskId,
172
+ eventId: event.eventId,
173
+ executionId: event.executionId,
174
+ kind: event.type,
175
+ status: event.type === "question" ? "question" : String(event.payload.status),
176
+ userInitiated: userCancelled,
177
+ publicText,
178
+ deliveryMode: direct ? "direct" : "review",
179
+ });
180
+ }
181
+ return direct ? this.sweep().then(() => true) : Promise.resolve(true);
182
+ }
183
+ /** Deliver the public event content for every obligation that is due. */
184
+ async sweep() {
185
+ if (this.sweeping)
186
+ return;
187
+ this.sweeping = true;
188
+ try {
189
+ for (const cancel of this.deps.store.groupCancels()) {
190
+ const parent = this.deps.store.dispatch(cancel.taskId);
191
+ if (parent)
192
+ this.cancelChildren(parent, cancel.reason, cancel.byUser);
193
+ }
194
+ for (const obligation of this.deps.store.due()) {
195
+ const dispatch = this.deps.store.dispatch(obligation.taskId);
196
+ if (!dispatch?.replyTarget) {
197
+ this.deps.store.fallbackFailed(obligation.taskId, obligation.eventId, "No reply target was recorded for this task");
198
+ continue;
199
+ }
200
+ const legacyEvent = obligation.publicText === null
201
+ ? this.allEvents(obligation.executionId).find((event) => event.eventId === obligation.eventId) : undefined;
202
+ const publicText = obligation.publicText ?? (legacyEvent ? publicTaskText(legacyEvent) : null);
203
+ const text = obligation.deliveryMode === "direct" && publicText ? `${dispatch.title}\n\n${publicText}`
204
+ : obligation.deliveryMode === "review" && obligation.kind === "result"
205
+ ? t("foreman.fallback.review", this.deps.locale(), dispatch.title)
206
+ : [t(fallbackKey(obligation), this.deps.locale(), dispatch.title), publicText].filter(Boolean).join("\n\n");
207
+ let result;
208
+ try {
209
+ result = await this.deps.deliver(dispatch.replyTarget, text, dispatch.threadId);
210
+ }
211
+ catch (error) {
212
+ result = { ok: false, errorMessage: errorMessage(error) };
213
+ }
214
+ if (result.ok) {
215
+ if (obligation.deliveryMode === "direct")
216
+ this.deps.store.markDelivered(obligation.taskId, obligation.kind === "result" ? "completion" : undefined, obligation.eventId);
217
+ else
218
+ this.deps.store.fallbackSent(obligation.taskId, obligation.eventId);
219
+ logger.system(`[foreman] Sent ${obligation.deliveryMode === "direct" ? "direct delivery" : "fallback notice"} for task=${obligation.taskId} event=${obligation.eventId} (${obligation.kind})`);
220
+ continue;
221
+ }
222
+ const after = this.deps.store.fallbackFailed(obligation.taskId, obligation.eventId, result.errorMessage);
223
+ const message = `[foreman] Delivery fallback for task=${obligation.taskId} failed: ${result.errorMessage}`;
224
+ if (after?.state === "delivery_failed")
225
+ logger.err(`${message}; giving up (delivery_failed)`);
226
+ else
227
+ logger.warn(message);
228
+ }
229
+ }
230
+ catch (error) {
231
+ logger.err(`[foreman] Delivery fallback sweep failed: ${errorMessage(error)}`);
232
+ }
233
+ finally {
234
+ this.sweeping = false;
235
+ }
236
+ }
237
+ // ----------------------------------------------------------- commands
238
+ run(threadId, command) {
239
+ switch (command.type) {
240
+ case "business_read": return fail("Business reads require an admitted execution connection");
241
+ case "delegate": return this.deps.allowDelegation?.() === false ? fail("New Task delegation is disabled") : this.delegate(threadId, command);
242
+ case "delegate_batch": return this.deps.allowDelegation?.() === false ? fail("New Task delegation is disabled") : this.delegateBatch(threadId, command);
243
+ case "status": return this.status(threadId, command.args);
244
+ case "input": return this.input(threadId, command.args);
245
+ case "cancel": return this.cancel(threadId, command.args);
246
+ case "cancel_all": {
247
+ const roots = this.deps.store.allForThread(threadId).filter((task) => !task.metadata.parentTaskId && this.isOpen(task));
248
+ const results = roots.map((task) => this.cancel(threadId, { task_id: task.taskId, requested_by_user: true, reason: "User stopped this conversation's tasks" }));
249
+ return {
250
+ ok: results.every((result) => result.ok),
251
+ text: roots.length > 0 ? [
252
+ t("foreman.cancel_all", this.deps.locale(), String(roots.length)),
253
+ ...roots.slice(0, 10).map((task) => `- ${task.title}`),
254
+ ...results.filter((result) => !result.ok).map((result) => result.text),
255
+ ].join("\n") : "",
256
+ };
257
+ }
258
+ case "delivered": return this.delivered(threadId, command.taskId, command.purpose, command.eventId);
259
+ case "turn_ended": return this.turnEnded(threadId, command.events);
260
+ case "summary": return { ok: true, text: this.summary(threadId) };
261
+ }
262
+ }
263
+ envelope(threadId, commandId) {
264
+ return { commandId, caller: { kind: "general", ref: threadId }, scope: FOREMAN_SCOPE };
265
+ }
266
+ /** A task dispatched by this thread; any other thread's task is not visible. */
267
+ ownedDispatch(threadId, taskId) {
268
+ const dispatch = this.deps.store.dispatch(taskId);
269
+ return dispatch?.threadId === threadId ? dispatch : null;
270
+ }
271
+ latest(taskId) {
272
+ return this.deps.executions.latestForTask(taskId, FOREMAN_SCOPE);
273
+ }
274
+ childRecords(parent) {
275
+ return (parent.metadata.children ?? []).map((taskId) => ({ taskId, record: this.latest(taskId) }));
276
+ }
277
+ parentState(parent) {
278
+ const children = this.childRecords(parent);
279
+ if (children.every((child) => child.record === null))
280
+ return "not_started";
281
+ if (children.some((child) => child.record === null || !isTerminalExecutionState(child.record.state))) {
282
+ if (children.some((child) => child.record?.state === "running"))
283
+ return "running";
284
+ if (children.some((child) => child.record?.state === "cancel_requested"))
285
+ return "cancel_requested";
286
+ if (children.some((child) => child.record?.state === "waiting_input"))
287
+ return "waiting_input";
288
+ return "queued";
289
+ }
290
+ if (children.every((child) => child.record?.state === "succeeded"))
291
+ return "succeeded";
292
+ if (children.every((child) => child.record?.state === "cancelled") || this.deps.store.groupCancels().some((item) => item.taskId === parent.taskId))
293
+ return "cancelled";
294
+ if (children.some((child) => child.record?.state === "succeeded"))
295
+ return "partial";
296
+ return "failed";
297
+ }
298
+ deliverParentResult(threadId, taskId) {
299
+ const parent = this.ownedDispatch(threadId, taskId);
300
+ if (!parent?.metadata.children?.length)
301
+ return Promise.resolve(false);
302
+ const children = this.childRecords(parent);
303
+ if (children.some((child) => !child.record || !isTerminalExecutionState(child.record.state)))
304
+ return Promise.resolve(true);
305
+ const records = children.map((child) => child.record).filter((record) => record !== null);
306
+ // The set of attempts is the result version. A late event from an old
307
+ // attempt cannot create a second delivery for the current result version.
308
+ const eventId = `evt-task-${identity(taskId, ...records.map((record) => record.executionId))}`;
309
+ const status = this.parentState(parent);
310
+ const summary = records.map((record) => {
311
+ const childId = record.taskId;
312
+ const title = this.deps.store.dispatch(childId)?.title ?? childId;
313
+ return `${childId} "${title}": ${record.state}. ${clip(record.result?.summary ?? record.result?.error?.message ?? "", 400)}`;
314
+ }).join("\n");
315
+ const event = {
316
+ protocolVersion: 1, eventId, seq: 0, taskId, executionId: taskId, type: "result",
317
+ createdAt: records.map((record) => record.updatedAt).sort().at(-1) ?? this.now().toISOString(),
318
+ payload: {
319
+ status, summary,
320
+ data: { userMessage: children.map(({ taskId: childId, record }) => {
321
+ const message = publicTaskText({ type: "result", payload: { data: record?.result?.data } });
322
+ const title = this.deps.store.dispatch(childId)?.title ?? childId;
323
+ return message ? `${title}\n${message}` : null;
324
+ }).filter(Boolean).join("\n\n") },
325
+ artifactRefs: records.flatMap((record) => record.result?.artifactRefs ?? []).slice(0, 30),
326
+ userInitiated: status === "cancelled" && this.deps.store.isUserCancelled(taskId),
327
+ },
328
+ };
329
+ return this.deliverEvent(event, { kind: "general", ref: threadId });
330
+ }
331
+ cancelChildren(parent, reason, byUser) {
332
+ for (const { taskId, record } of this.childRecords(parent)) {
333
+ if (!record || isTerminalExecutionState(record.state))
334
+ continue;
335
+ if (byUser)
336
+ this.deps.store.markUserCancelled(taskId);
337
+ this.deps.store.withdraw(taskId);
338
+ this.deps.executions.cancel(this.envelope(parent.threadId, `cancel-${identity(parent.taskId, record.executionId, reason, String(byUser))}`), {
339
+ executionId: record.executionId, reason, userInitiated: byUser,
340
+ });
341
+ }
342
+ }
343
+ delegateBatch(threadId, command) {
344
+ const parsed = DelegateBatchArgsSchema.safeParse(command.args);
345
+ if (!parsed.success)
346
+ return Promise.resolve(invalid(parsed.error));
347
+ const key = identity(threadId, command.dispatchKey, parsed.data.title);
348
+ const specification = JSON.stringify(parsed.data);
349
+ const pending = this.pendingBatches.get(key);
350
+ if (pending)
351
+ return pending.specification === specification ? pending.result : Promise.resolve(fail("Batch identity is already being prepared with different content"));
352
+ const result = this.prepareBatch(threadId, command);
353
+ this.pendingBatches.set(key, { specification, result });
354
+ void result.finally(() => { this.pendingBatches.delete(key); }).catch(() => { });
355
+ return result;
356
+ }
357
+ async prepareBatch(threadId, command) {
358
+ const parsed = DelegateBatchArgsSchema.safeParse(command.args);
359
+ if (!parsed.success)
360
+ return invalid(parsed.error);
361
+ const args = parsed.data;
362
+ if (new Set(args.tasks.map((task) => task.key)).size !== args.tasks.length)
363
+ return fail("Task keys must be unique within a batch");
364
+ const atomic = this.deps.executions.atomic;
365
+ if (!atomic)
366
+ return fail("This execution host does not support atomic task batches");
367
+ const rootId = taskIdFor(threadId, command.dispatchKey, `batch:${args.title}`);
368
+ const specification = identity(JSON.stringify(args));
369
+ const previous = this.ownedDispatch(threadId, rootId);
370
+ if (previous && previous.metadata.specification !== specification)
371
+ return fail("This batch key was already used with different tasks; use a distinct title for new work");
372
+ const ids = args.tasks.map((task) => taskIdFor(threadId, command.dispatchKey, `${rootId}:${task.key}`));
373
+ const commits = new Map();
374
+ // Filesystem preparation happens before the synchronous acceptance transaction.
375
+ // No worker starts until every Task and command has been committed.
376
+ const workspaces = [];
377
+ const contexts = [];
378
+ for (const [index, task] of args.tasks.entries()) {
379
+ const existing = this.ownedDispatch(threadId, ids[index]);
380
+ if (existing?.metadata.specification && existing.metadata.specification !== identity(specification, task.key))
381
+ throw new Error(`Task key ${task.key} was already used with different content`);
382
+ const source = task.context_task_id ? this.ownedDispatch(threadId, task.context_task_id) : null;
383
+ const sourceRecord = source ? (existing?.metadata.contextExecutionId
384
+ ? this.deps.executions.get(existing.metadata.contextExecutionId) : this.latest(source.taskId)) : null;
385
+ if (task.context_task_id && (!sourceRecord || sourceRecord.taskId !== source?.taskId || !isTerminalExecutionState(sourceRecord.state))) {
386
+ return fail("context_task_id must name a completed execution Task from this conversation");
387
+ }
388
+ contexts.push(sourceRecord?.executionId);
389
+ const directory = task.project_dir ?? sourceRecord?.payload.workspaceRef ?? source?.workspace;
390
+ workspaces.push(existing?.workspace ?? (directory
391
+ ? await prepareTaskWorkspace(directory, ids[index], task.workspace_mode, commits)
392
+ : undefined));
393
+ }
394
+ const parent = args.result_mode === "combined" && args.tasks.length > 1;
395
+ const foreground = this.ownedDispatch(threadId, ids[0])?.metadata.foregroundCalls
396
+ ?? Math.max(0, Math.trunc(command.foreground.toolCallsUsed));
397
+ const budget = DEFAULT_LAB_RUN_BUDGET;
398
+ return atomic(() => this.deps.store.atomic(() => {
399
+ if (parent)
400
+ this.deps.store.recordDispatch({
401
+ taskId: rootId, threadId, title: args.title, replyTarget: isDeliveryTarget(command.replyTarget)
402
+ ? command.replyTarget : threadId === DEFAULT_THREAD_ID ? this.deps.ownerTarget() : null,
403
+ sender: command.sender ?? "", metadata: { children: ids, specification, sourceKey: command.dispatchKey },
404
+ });
405
+ for (const [index, task] of args.tasks.entries()) {
406
+ const result = this.delegate(threadId, {
407
+ type: "delegate", args: { ...task, project_dir: workspaces[index], workspace_mode: task.workspace_mode === "isolated" ? "shared" : task.workspace_mode },
408
+ foreground: { taskId: ids[index], toolCallsUsed: index === 0 ? foreground : 0 },
409
+ ...(parent ? { parentTaskId: rootId } : {}),
410
+ preparedWorkspace: Boolean(workspaces[index] && task.workspace_mode === "isolated"),
411
+ contextExecutionId: contexts[index],
412
+ specification: identity(specification, task.key),
413
+ sourceKey: command.dispatchKey,
414
+ budget, replyTarget: command.replyTarget, sender: command.sender,
415
+ });
416
+ if (!result.ok)
417
+ throw new Error(result.text);
418
+ }
419
+ return {
420
+ ok: true, acceptedTaskId: parent ? rootId : ids[0], acceptedTaskIds: ids,
421
+ text: `Accepted ${ids.length} Task(s)${parent ? ` under parent ${rootId} for one combined delivery` : " with independent delivery"}: ${args.tasks.map((task, i) => `${task.key}=${ids[i]}`).join(", ")}. Each Task has its own budget and runs when slots are available. You may delegate more independent work, coordinate existing Tasks, or reply. Do not repeat work assigned to these Tasks.`,
422
+ };
423
+ }));
424
+ }
425
+ delegate(threadId, command) {
426
+ if (command.args && typeof command.args === "object" && "worker" in command.args && command.args.worker !== null && command.args.worker !== undefined) {
427
+ return fail("Worker aliases are no longer used. Select a completed context_task_id, or send input to the existing Task.");
428
+ }
429
+ const parsed = DelegateArgsSchema.safeParse(command.args);
430
+ if (!parsed.success)
431
+ return invalid(parsed.error);
432
+ const args = parsed.data;
433
+ let retryOf = null;
434
+ const retryDispatchId = args.retry_task_id && command.dispatchKey ? identity(threadId, command.dispatchKey) : undefined;
435
+ const retrySpecification = retryDispatchId ? identity(JSON.stringify(args)) : undefined;
436
+ if (args.retry_task_id) {
437
+ if (!this.ownedDispatch(threadId, args.retry_task_id))
438
+ return fail(`task ${args.retry_task_id} was not delegated from this conversation`);
439
+ const replay = retryDispatchId ? this.deps.executions.attemptsForTask?.(args.retry_task_id, FOREMAN_SCOPE)
440
+ .find((attempt) => attempt.payload.retryDispatchId === retryDispatchId) : undefined;
441
+ if (replay)
442
+ return replay.payload.retrySpecification === retrySpecification
443
+ ? { ok: true, acceptedTaskId: replay.taskId, text: `Retry already accepted for task ${replay.taskId}, execution ${replay.executionId}: ${describeState(replay)}.` }
444
+ : fail("This retry was already accepted with different content; use background_task_input or a new user instruction");
445
+ retryOf = this.latest(args.retry_task_id);
446
+ if (!retryOf)
447
+ return fail(`task ${args.retry_task_id} has no earlier run to retry`);
448
+ if (!isTerminalExecutionState(retryOf.state)) {
449
+ return fail(`task ${args.retry_task_id} is still ${describeState(retryOf)}; send changes with background_task_input instead`);
450
+ }
451
+ }
452
+ const taskId = args.retry_task_id ?? (command.dispatchKey
453
+ ? taskIdFor(threadId, command.dispatchKey, args.key ?? identity(JSON.stringify(args)))
454
+ : command.foreground.taskId);
455
+ const oldDispatch = this.ownedDispatch(threadId, taskId);
456
+ if (command.parentTaskId && !this.ownedDispatch(threadId, command.parentTaskId)?.metadata.children?.includes(taskId)) {
457
+ return fail("The parent Task did not admit this child");
458
+ }
459
+ const previous = args.context_task_id ? this.ownedDispatch(threadId, args.context_task_id) : null;
460
+ const contextExecutionId = command.contextExecutionId ?? oldDispatch?.metadata.contextExecutionId;
461
+ const previousRecord = previous ? (contextExecutionId
462
+ ? this.deps.executions.get(contextExecutionId) : this.latest(previous.taskId)) : null;
463
+ if (args.context_task_id) {
464
+ if (args.retry_task_id)
465
+ return fail("Use either context_task_id for new work or retry_task_id for another attempt, not both");
466
+ if (!previousRecord || previousRecord.taskId !== previous?.taskId)
467
+ return fail("context_task_id must name an execution Task from this conversation");
468
+ if (!isTerminalExecutionState(previousRecord.state))
469
+ return fail("That Task is unfinished; use background_task_input to continue it");
470
+ }
471
+ let workspace;
472
+ if (retryOf && args.workspace_mode && (retryOf.payload.workspaceMode === "read_only") !== (args.workspace_mode === "read_only")) {
473
+ return fail("A retry keeps its workspace access. Delegate a new Task to change read-only versus writable access");
474
+ }
475
+ const workspaceMode = retryOf?.payload.workspaceMode ?? (command.preparedWorkspace ? "isolated" : args.workspace_mode ?? "shared");
476
+ const requestedDir = args.project_dir ?? retryOf?.payload.workspaceRef ?? oldDispatch?.workspace ?? previousRecord?.payload.workspaceRef ?? previous?.workspace ?? undefined;
477
+ if (requestedDir) {
478
+ try {
479
+ workspace = resolveWorkspaceCwd(requestedDir, { writable: workspaceMode !== "read_only" });
480
+ }
481
+ catch (error) {
482
+ return fail(`project_dir: ${errorMessage(error)}. Nothing was delegated.`);
483
+ }
484
+ }
485
+ // The reply target comes from the authenticated inbound message, never
486
+ // from the model. A replayed or retried dispatch keeps the first one.
487
+ const replyTarget = isDeliveryTarget(command.replyTarget)
488
+ ? command.replyTarget
489
+ : threadId === DEFAULT_THREAD_ID ? this.deps.ownerTarget() : null;
490
+ const dispatch = this.deps.store.recordDispatch({
491
+ taskId,
492
+ threadId,
493
+ title: args.title,
494
+ replyTarget,
495
+ sender: typeof command.sender === "string" ? command.sender : "",
496
+ workspace: workspace ?? null,
497
+ metadata: {
498
+ sourceKey: command.sourceKey ?? command.dispatchKey ?? (command.parentTaskId ? this.deps.store.dispatch(command.parentTaskId)?.metadata.sourceKey : undefined),
499
+ contextTaskId: previous?.taskId,
500
+ contextExecutionId: previousRecord?.executionId,
501
+ businessRead: this.deps.canReadBusiness?.(threadId, replyTarget) ?? false,
502
+ ...(command.parentTaskId ? { parentTaskId: command.parentTaskId } : {}),
503
+ budget: command.budget ?? DEFAULT_LAB_RUN_BUDGET,
504
+ foregroundCalls: command.foreground.toolCallsUsed,
505
+ preparedWorkspace: command.preparedWorkspace,
506
+ specification: command.specification,
507
+ },
508
+ });
509
+ const context = [
510
+ args.context,
511
+ retryOf?.result?.summary ? `Previous attempt result: ${clip(retryOf.result.summary, 4_000)}` : undefined,
512
+ retryOf?.payload.contextSummary,
513
+ dispatch.sender ? `Requested by: ${dispatch.sender}` : undefined,
514
+ previous && previous.taskId !== taskId ? this.previousWork(previous, previousRecord) : undefined,
515
+ ].filter((part) => typeof part === "string" && part.length > 0);
516
+ const toolCallsUsed = retryOf ? command.foreground.toolCallsUsed : dispatch.metadata.foregroundCalls ?? command.foreground.toolCallsUsed;
517
+ const payload = {
518
+ goal: args.goal,
519
+ requirements: [...new Set([...(retryOf?.payload.requirements ?? []), ...args.requirements])],
520
+ ...((args.expected_output ?? retryOf?.payload.expectedOutput) ? { expectedOutput: args.expected_output ?? retryOf?.payload.expectedOutput } : {}),
521
+ ...(context.length > 0 ? { contextSummary: clip(context.join("\n\n"), 20_000) } : {}),
522
+ checkpoint: {
523
+ done: args.done,
524
+ remaining: args.remaining,
525
+ decisions: [],
526
+ blockers: [],
527
+ artifactRefs: [...new Set([...(previousRecord?.result?.artifactRefs ?? []), ...args.artifacts])],
528
+ uncertainOperations: args.uncertain_operations,
529
+ },
530
+ title: dispatch.title,
531
+ businessRead: dispatch.metadata.businessRead === true,
532
+ foregroundToolCallsUsed: Number.isInteger(toolCallsUsed) && toolCallsUsed >= 0 ? toolCallsUsed : 0,
533
+ ...(workspace ? { workspaceRef: workspace } : {}),
534
+ workspaceMode,
535
+ ...(dispatch.metadata.parentTaskId ? { parentTaskId: dispatch.metadata.parentTaskId } : {}),
536
+ ...((command.preparedWorkspace || (dispatch.metadata.preparedWorkspace && workspace === dispatch.workspace)
537
+ || (previousRecord?.payload.preparedWorkspace && workspace === previousRecord.payload.workspaceRef)
538
+ || (retryOf?.payload.preparedWorkspace && workspace === retryOf.payload.workspaceRef)) ? { preparedWorkspace: true } : {}),
539
+ ...(retryOf ? { priorExecutionId: retryOf.executionId } : {}),
540
+ ...(retryDispatchId ? { retryDispatchId, retrySpecification } : {}),
541
+ };
542
+ // One command per handoff (or per retry of one run): a repeated call
543
+ // returns the original acceptance instead of starting a second worker.
544
+ const commandId = retryOf ? `retry-${taskId}-${retryOf.executionId}` : `delegate-${taskId}`;
545
+ const budget = remainingBudget(dispatch.metadata.budget ?? DEFAULT_LAB_RUN_BUDGET, retryOf ? this.deps.executions.attemptsForTask?.(taskId, FOREMAN_SCOPE) ?? [retryOf] : []);
546
+ if (budget.maxModelAttempts < 1 || budget.maxToolCalls <= toolCallsUsed || budget.maxDurationMs < 1)
547
+ return fail(`Task ${taskId} has exhausted its cumulative budget`);
548
+ let outcome;
549
+ try {
550
+ outcome = this.deps.executions.submit(this.envelope(threadId, commandId), {
551
+ taskId,
552
+ ...(retryOf ? { retryOf: retryOf.executionId } : {}),
553
+ payload,
554
+ budget,
555
+ });
556
+ }
557
+ catch (error) {
558
+ if (error instanceof ExecutionCommandConflictError) {
559
+ return fail(`task ${taskId} was already delegated with different content. Send changes with background_task_input, or check it with background_task_status`);
560
+ }
561
+ throw error;
562
+ }
563
+ if (outcome.kind === "stale") {
564
+ return outcome.reason === "task_has_live_execution" && outcome.record
565
+ ? fail(`task ${taskId} is already ${describeState(outcome.record)} in the background; send further requirements with background_task_input`)
566
+ : fail(`nothing was delegated (${outcome.reason})`);
567
+ }
568
+ if (retryOf) {
569
+ // The previous result is superseded: whatever it still owed the user moves to the new run.
570
+ this.deps.store.withdraw(taskId);
571
+ this.deps.store.clearUserCancelled(taskId);
572
+ if (dispatch.metadata.parentTaskId) {
573
+ this.deps.store.withdraw(dispatch.metadata.parentTaskId);
574
+ this.deps.store.clearUserCancelled(dispatch.metadata.parentTaskId);
575
+ this.deps.store.clearGroupCancel(dispatch.metadata.parentTaskId);
576
+ }
577
+ }
578
+ logger.system(`[foreman] Delegated task=${taskId} execution=${outcome.value.executionId} from thread=${threadId}${retryOf ? ` (retry of ${retryOf.executionId})` : ""}`);
579
+ return {
580
+ ok: true,
581
+ acceptedTaskId: taskId,
582
+ text: [
583
+ `Delegated as background task ${taskId} ("${dispatch.title}"), execution ${outcome.value.executionId}, ${outcome.value.state === "queued" ? "queued: it starts when a worker slot is free" : outcome.value.state}.`,
584
+ "Do not repeat this Task's work here. You may delegate more independent Tasks, coordinate existing work, or reply to the user.",
585
+ "Its question or result arrives later as a [Background task update] message.",
586
+ ].join(" "),
587
+ };
588
+ }
589
+ /** Explicit Task context survives process replacement without a worker identity. */
590
+ previousWork(previous, record) {
591
+ const lines = [`Referenced Task: ${previous.taskId} "${previous.title}" (${record ? describeState(record) : "not started"})`];
592
+ if (record?.result?.summary)
593
+ lines.push(`Its result: ${clip(record.result.summary, 4_000)}`);
594
+ if (record?.result?.artifactRefs?.length)
595
+ lines.push(`Its artifacts: ${record.result.artifactRefs.slice(0, 20).join(", ")}`);
596
+ return lines.join("\n");
597
+ }
598
+ status(threadId, args) {
599
+ const parsed = StatusArgsSchema.safeParse(args ?? {});
600
+ if (!parsed.success)
601
+ return invalid(parsed.error);
602
+ if (parsed.data.task_id) {
603
+ const dispatch = this.ownedDispatch(threadId, parsed.data.task_id);
604
+ return dispatch
605
+ ? { ok: true, text: this.describeTask(dispatch, true) }
606
+ : fail(`no background task ${parsed.data.task_id} in this conversation`);
607
+ }
608
+ const roots = this.deps.store.allForThread(threadId).filter((dispatch) => !dispatch.metadata.parentTaskId);
609
+ const active = roots.filter((dispatch) => this.isOpen(dispatch));
610
+ const dispatches = [...active, ...roots.filter((dispatch) => !this.isOpen(dispatch)).slice(0, Math.max(0, 20 - active.length))];
611
+ if (dispatches.length === 0)
612
+ return { ok: true, text: "No background tasks in this conversation." };
613
+ return { ok: true, text: dispatches.map((dispatch) => this.describeTask(dispatch, false)).join("\n") };
614
+ }
615
+ input(threadId, args) {
616
+ const parsed = InputArgsSchema.safeParse(args);
617
+ if (!parsed.success)
618
+ return invalid(parsed.error);
619
+ const { task_id: taskId, text } = parsed.data;
620
+ const dispatch = this.ownedDispatch(threadId, taskId);
621
+ if (!dispatch)
622
+ return fail(`no background task ${taskId} in this conversation`);
623
+ if (dispatch.metadata.children?.length)
624
+ return fail(`Send the answer or requirement to a child Task: ${dispatch.metadata.children.join(", ")}. Each child owns its input cursor.`);
625
+ const record = this.latest(taskId);
626
+ if (!record)
627
+ return fail(`task ${taskId} has not started`);
628
+ if (isTerminalExecutionState(record.state)) {
629
+ return fail(`task ${taskId} already ${record.state}. To change its result, run it again with delegate_task(retry_task_id="${taskId}") and put the change in requirements`);
630
+ }
631
+ if (record.state === "cancel_requested")
632
+ return fail(`task ${taskId} is stopping and takes no more input`);
633
+ const questionId = parsed.data.question_id
634
+ ?? (record.state === "waiting_input" ? record.openQuestionId ?? undefined : undefined);
635
+ const outcome = this.deps.executions.sendInput(this.envelope(threadId, `input-${randomUUID()}`), {
636
+ executionId: record.executionId,
637
+ text,
638
+ ...(questionId ? { questionId } : {}),
639
+ });
640
+ if (outcome.kind === "stale") {
641
+ return fail(outcome.reason === "question_not_open"
642
+ ? `question ${questionId ?? ""} of task ${taskId} is no longer open`
643
+ : `the input was not accepted (${outcome.reason})`);
644
+ }
645
+ // General handled what the task asked; the next result carries the obligation.
646
+ this.deps.store.withdraw(taskId);
647
+ return {
648
+ ok: true,
649
+ text: questionId
650
+ ? `Answer stored for task ${taskId} (input ${outcome.value.inputSeq}); the task resumes with it.`
651
+ : `Input stored for task ${taskId} (input ${outcome.value.inputSeq}); the worker reads it at its next step. It is not applied yet.`,
652
+ };
653
+ }
654
+ cancel(threadId, args) {
655
+ const parsed = CancelArgsSchema.safeParse(args);
656
+ if (!parsed.success)
657
+ return invalid(parsed.error);
658
+ const { task_id: taskId, requested_by_user: byUser, reason } = parsed.data;
659
+ const dispatch = this.ownedDispatch(threadId, taskId);
660
+ if (!dispatch)
661
+ return fail(`no background task ${taskId} in this conversation`);
662
+ if (dispatch.metadata.children?.length) {
663
+ if (!this.isOpen(dispatch))
664
+ return { ok: true, text: `Task ${taskId} has already finished (${this.parentState(dispatch)}).` };
665
+ if (byUser)
666
+ this.deps.store.markUserCancelled(taskId);
667
+ this.deps.store.withdraw(taskId);
668
+ this.deps.store.requestGroupCancel(taskId, reason ?? "Parent task cancelled", byUser);
669
+ this.cancelChildren(dispatch, reason ?? "Parent task cancelled", byUser);
670
+ return { ok: true, text: `Cancel requested for all unfinished children of ${taskId}. Running children must confirm their stop before the parent is cancelled.` };
671
+ }
672
+ const record = this.latest(taskId);
673
+ if (!record || isTerminalExecutionState(record.state)) {
674
+ if (byUser) {
675
+ this.deps.store.markUserCancelled(taskId);
676
+ this.deps.store.withdraw(taskId);
677
+ }
678
+ return { ok: true, text: `Task ${taskId} is not running (${record ? record.state : "never started"}); nothing to cancel.` };
679
+ }
680
+ // Recorded first: the cancelled result that follows owes the user nothing.
681
+ if (byUser)
682
+ this.deps.store.markUserCancelled(taskId);
683
+ this.deps.store.withdraw(taskId);
684
+ const outcome = this.deps.executions.cancel(this.envelope(threadId, `cancel-${randomUUID()}`), {
685
+ executionId: record.executionId,
686
+ ...(reason ? { reason } : {}),
687
+ userInitiated: byUser,
688
+ });
689
+ if (outcome.kind === "stale")
690
+ return fail(`the cancel was not accepted (${outcome.reason})`);
691
+ return {
692
+ ok: true,
693
+ text: outcome.value.state === "cancelled"
694
+ ? `Task ${taskId} was cancelled before a worker started it.`
695
+ : `Cancel requested for task ${taskId}. The worker has not confirmed it stopped yet; do not tell the user it has stopped. The task is reported cancelled only after it does.`,
696
+ };
697
+ }
698
+ delivered(threadId, taskId, purpose, eventId) {
699
+ const dispatch = this.ownedDispatch(threadId, taskId);
700
+ if (!dispatch)
701
+ return { ok: false, text: `no background task ${taskId} in this conversation` };
702
+ if (dispatch.metadata.children?.length && !eventId)
703
+ return fail("A parent Task delivery must identify its result event/version");
704
+ const satisfied = this.deps.store.markDelivered(taskId, purpose === "completion" ? "completion" : undefined, eventId);
705
+ return { ok: true, text: String(satisfied) };
706
+ }
707
+ /** General's turn that handled these events ended: whatever it did not deliver is due now. */
708
+ turnEnded(threadId, events) {
709
+ let due = 0;
710
+ for (const event of events) {
711
+ if (typeof event.taskId !== "string" || typeof event.eventId !== "string")
712
+ continue;
713
+ if (!this.ownedDispatch(threadId, event.taskId))
714
+ continue;
715
+ this.deps.store.makeDueNow(event.taskId, [event.eventId]);
716
+ due += 1;
717
+ }
718
+ if (due > 0)
719
+ void this.sweep();
720
+ return { ok: true, text: "" };
721
+ }
722
+ /** Open tasks of this conversation, for General's wake context. Empty when none. */
723
+ summary(threadId) {
724
+ const roots = this.deps.store.allForThread(threadId).filter((dispatch) => !dispatch.metadata.parentTaskId);
725
+ const open = roots.filter((dispatch) => this.isOpen(dispatch));
726
+ const recent = roots.filter((dispatch) => {
727
+ const delivery = this.deps.store.latest(dispatch.taskId);
728
+ return !this.isOpen(dispatch) && delivery?.deliveryMode === "direct" && delivery.state === "delivered";
729
+ }).slice(0, 5);
730
+ if (open.length === 0 && recent.length === 0)
731
+ return "";
732
+ let text = [
733
+ "## Background Tasks",
734
+ "Tasks this conversation delegated that are still open. Answer status questions from this list or background_task_status; do not guess. Send new requirements for a listed task with background_task_input instead of working on it here.",
735
+ ].join("\n");
736
+ for (const dispatch of open) {
737
+ const entry = this.describeTask(dispatch, false);
738
+ if (text.length + entry.length + 1 > SUMMARY_CHAR_BUDGET) {
739
+ text += "\n- More tasks: see background_task_status.";
740
+ break;
741
+ }
742
+ text += `\n${entry}`;
743
+ }
744
+ for (const dispatch of recent) {
745
+ const entry = `\n- Already delivered directly: ${dispatch.taskId} "${dispatch.title}": ${clip(this.deps.store.latest(dispatch.taskId)?.publicText ?? "", 1_000)}. Do not send it again.`;
746
+ if (text.length + entry.length <= SUMMARY_CHAR_BUDGET)
747
+ text += entry;
748
+ }
749
+ return text;
750
+ }
751
+ describeTask(dispatch, detail) {
752
+ if (dispatch.metadata.children?.length) {
753
+ const children = dispatch.metadata.children.map((id) => this.deps.store.dispatch(id))
754
+ .filter((item) => item !== null);
755
+ const delivery = this.deps.store.latest(dispatch.taskId);
756
+ return [
757
+ `- ${dispatch.taskId} "${dispatch.title}": ${this.parentState(dispatch)} (${children.length} child Tasks)`,
758
+ ...children.map((child) => ` ${this.describeTask(child, detail)}`),
759
+ ...(delivery ? [` parent delivery: ${delivery.state} (event ${delivery.eventId})`] : []),
760
+ ].join("\n");
761
+ }
762
+ const record = this.latest(dispatch.taskId);
763
+ const lines = [`- ${dispatch.taskId} "${dispatch.title}": ${record ? describeState(record) : "not started"}`];
764
+ if (dispatch.metadata.contextTaskId)
765
+ lines.push(` context from Task: ${dispatch.metadata.contextTaskId}`);
766
+ if (record) {
767
+ const events = detail || record.state === "waiting_input" ? this.allEvents(record.executionId) : [];
768
+ const newestFirst = [...events].reverse();
769
+ if (record.state === "waiting_input" && record.openQuestionId) {
770
+ const question = newestFirst.find((event) => event.type === "question" && event.payload.questionId === record.openQuestionId);
771
+ lines.push(` open question ${record.openQuestionId}: ${clip(textOf(question?.payload.text, "(text unavailable)"), 1_000)}`);
772
+ }
773
+ if (detail) {
774
+ lines.push(` execution ${record.executionId}: queued ${record.queuedAt}`
775
+ + (record.startedAt ? `, started ${record.startedAt}` : "")
776
+ + (record.stoppedAt ? `, stopped ${record.stoppedAt}` : "")
777
+ + (record.autoResumeCount > 0 ? `, resumed ${record.autoResumeCount} time(s) after interruptions` : ""));
778
+ const progress = newestFirst.find((event) => event.type === "progress" && typeof event.payload.summary === "string");
779
+ if (progress)
780
+ lines.push(` latest progress (${progress.createdAt}): ${clip(String(progress.payload.summary), 1_000)}`);
781
+ else if (!record.result)
782
+ lines.push(" no progress reported yet");
783
+ }
784
+ const result = record.result;
785
+ if (result?.summary)
786
+ lines.push(` result: ${clip(result.summary, detail ? 4_000 : 400)}`);
787
+ const prepared = detail ? publicTaskText({ type: "result", payload: { data: result?.data } }) : null;
788
+ if (prepared)
789
+ lines.push(` prepared answer: ${prepared}`);
790
+ if (result?.error)
791
+ lines.push(` error: ${result.error.code}: ${clip(result.error.message, 1_000)}`);
792
+ if (result?.artifactRefs?.length)
793
+ lines.push(` artifacts: ${result.artifactRefs.slice(0, 20).join(", ")}`);
794
+ }
795
+ const delivery = this.deps.store.latest(dispatch.taskId);
796
+ if (delivery && delivery.state !== "delivered" && delivery.state !== "withdrawn") {
797
+ lines.push(` delivery to the user: ${delivery.state}${delivery.lastError ? ` (${delivery.lastError})` : ""} (event ${delivery.eventId})`);
798
+ if (delivery.deliveryMode === "review" && delivery.state === "fallback_sent")
799
+ lines.push(" Only a status notice was sent; the result still requires General to review and deliver it.");
800
+ }
801
+ return lines.join("\n");
802
+ }
803
+ isOpen(dispatch) {
804
+ const record = this.latest(dispatch.taskId);
805
+ const workOpen = dispatch.metadata.children?.length
806
+ ? this.childRecords(dispatch).some((child) => child.record !== null && !isTerminalExecutionState(child.record.state))
807
+ : record !== null && !isTerminalExecutionState(record.state);
808
+ const delivery = this.deps.store.latest(dispatch.taskId);
809
+ return workOpen || delivery?.state === "pending" || delivery?.state === "delivery_failed"
810
+ || (delivery?.state === "fallback_sent" && delivery.deliveryMode === "review" && delivery.kind === "result");
811
+ }
812
+ allEvents(executionId) {
813
+ const events = [];
814
+ let after = 0;
815
+ for (;;) {
816
+ const page = this.deps.executions.eventsAfter(executionId, after);
817
+ const last = page.at(-1)?.seq ?? after;
818
+ if (last <= after)
819
+ break;
820
+ events.push(...page);
821
+ after = last;
822
+ if (page.length < 500)
823
+ break;
824
+ }
825
+ return events;
826
+ }
827
+ /** The system message that brings a question or result to the delegating thread. */
828
+ eventMessage(event, threadId, dispatch, obligationOpens) {
829
+ const payload = event.payload;
830
+ const title = dispatch?.title ?? event.taskId;
831
+ const taskQuote = `task_id="${event.taskId}"`;
832
+ const quote = `${taskQuote}, event_id="${event.eventId}"`;
833
+ const lines = [`[Background task update] ${event.type === "question" ? "Question from" : "Result of"} background task ${event.taskId} "${title}"`];
834
+ if (dispatch?.sender)
835
+ lines.push(`Requested by: ${dispatch.sender}`);
836
+ if (event.type === "question") {
837
+ lines.push(`Question ${textOf(payload.questionId)}: ${clip(textOf(payload.text), 4_000)}`);
838
+ if (Array.isArray(payload.choices) && payload.choices.length > 0) {
839
+ lines.push(`Choices: ${payload.choices.map(String).join(" / ")}`);
840
+ }
841
+ lines.push(`If you can answer from what you know, answer with background_task_input(${taskQuote}, question_id="${textOf(payload.questionId)}"). Otherwise ask the requester with notify_user(${quote}) and pass their answer on. Do not do the task's work here.`);
842
+ }
843
+ else {
844
+ const status = String(payload.status);
845
+ lines.push(`Status: ${status}`);
846
+ if (typeof payload.summary === "string")
847
+ lines.push(`Summary: ${clip(payload.summary, 8_000)}`);
848
+ const preparedReply = publicTaskText(event);
849
+ if (preparedReply)
850
+ lines.push(`Prepared reply for the requester (review before sending): ${preparedReply}`);
851
+ const error = payload.error;
852
+ if (error && typeof error === "object") {
853
+ const { code, message } = error;
854
+ lines.push(`Error: ${String(code)}: ${clip(String(message), 2_000)}`);
855
+ }
856
+ if (Array.isArray(payload.artifactRefs) && payload.artifactRefs.length > 0) {
857
+ lines.push(`Artifacts: ${payload.artifactRefs.slice(0, 30).map(String).join(", ")}`);
858
+ }
859
+ if (status === "cancelled" && !obligationOpens) {
860
+ lines.push("The requester asked to stop this task; nothing more is needed unless they asked to hear back.");
861
+ }
862
+ else if (dispatch?.metadata.children?.length) {
863
+ lines.push(`Review the child results together. Deliver the overall result with notify_user(${quote}, purpose="completion"). Retry only children that need further work using their child task_id; do not repeat successful children. The parent does not occupy a worker.`);
864
+ }
865
+ else if (status === "succeeded") {
866
+ lines.push(`Check the result against the request before you deliver it. If it holds up, tell the requester with notify_user(${quote}, purpose="completion") in your own words, and send any drafts it contains with the right tool. If it falls short, run it again with delegate_task(retry_task_id="${event.taskId}") and say what must change.`);
867
+ }
868
+ else {
869
+ lines.push(`Tell the requester what happened with notify_user(${quote}, purpose="completion"), or run it again with delegate_task(retry_task_id="${event.taskId}") if a retry can succeed.`);
870
+ }
871
+ }
872
+ if (obligationOpens)
873
+ lines.push("If this turn ends without that notify_user, the requester gets a short automatic notice instead.");
874
+ const routing = taskContinuationRouting(dispatch?.replyTarget
875
+ ? { sender: dispatch.sender, delivery: dispatch.replyTarget }
876
+ : undefined);
877
+ return {
878
+ id: `foreman-${event.eventId}`,
879
+ channel: "system",
880
+ // An empty recorded sender (no inbound message) also falls back.
881
+ sender: dispatch && dispatch.sender.length > 0 ? dispatch.sender : "background-task",
882
+ text: lines.join("\n"),
883
+ attachments: [],
884
+ timestamp: event.createdAt,
885
+ ...routing,
886
+ meta: { ...routing.meta, [FOREMAN_EVENT_META_KEY]: { taskId: event.taskId, eventId: event.eventId } },
887
+ ...(threadId !== DEFAULT_THREAD_ID ? { threadId } : {}),
888
+ };
889
+ }
890
+ }
891
+ //# sourceMappingURL=foreman-host.js.map