@excitedjs/dreamux 0.26.0-beta.265 → 0.26.0

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 (494) hide show
  1. package/CHANGELOG.json +393 -0
  2. package/CHANGELOG.md +71 -1
  3. package/README.md +47 -34
  4. package/dist/admin/client.js +18 -7
  5. package/dist/admin/client.js.map +1 -1
  6. package/dist/admin/socket.js +15 -104
  7. package/dist/admin/socket.js.map +1 -1
  8. package/dist/agent-runtime/capabilities.js +4 -17
  9. package/dist/agent-runtime/capabilities.js.map +1 -1
  10. package/dist/agent-runtime/catalog.js.map +1 -1
  11. package/dist/agent-runtime/external-provider.js.map +1 -1
  12. package/dist/agent-runtime/host-context.js +1 -12
  13. package/dist/agent-runtime/host-context.js.map +1 -1
  14. package/dist/agent-runtime/host-paths.js +1 -1
  15. package/dist/agent-runtime/host-paths.js.map +1 -1
  16. package/dist/agent-runtime/index.js +7 -0
  17. package/dist/agent-runtime/index.js.map +1 -1
  18. package/dist/agent-runtime/skill-sources.js +23 -6
  19. package/dist/agent-runtime/skill-sources.js.map +1 -1
  20. package/dist/channel/catalog.js.map +1 -1
  21. package/dist/channel/external-channel-provider.js.map +1 -1
  22. package/dist/cli/commands/daemon.js +5 -8
  23. package/dist/cli/commands/daemon.js.map +1 -1
  24. package/dist/cli/commands/dispatcher.js +11 -15
  25. package/dist/cli/commands/dispatcher.js.map +1 -1
  26. package/dist/cli/commands/index.js +4 -4
  27. package/dist/cli/commands/index.js.map +1 -1
  28. package/dist/cli/commands/mcp.js +2 -4
  29. package/dist/cli/commands/mcp.js.map +1 -1
  30. package/dist/cli/commands/onboard.js +0 -4
  31. package/dist/cli/commands/onboard.js.map +1 -1
  32. package/dist/cli/commands/parse.js +1 -1
  33. package/dist/cli/commands/parse.js.map +1 -1
  34. package/dist/cli/commands/serve.js +3 -2
  35. package/dist/cli/commands/serve.js.map +1 -1
  36. package/dist/cli/commands/status.js +3 -3
  37. package/dist/cli/commands/status.js.map +1 -1
  38. package/dist/cli/commands/types.js +25 -3
  39. package/dist/cli/commands/types.js.map +1 -1
  40. package/dist/cli/commands/uninstall.js +2 -8
  41. package/dist/cli/commands/uninstall.js.map +1 -1
  42. package/dist/cli/doctor-plugins.js +44 -0
  43. package/dist/cli/doctor-plugins.js.map +1 -0
  44. package/dist/cli/doctor-types.js +2 -0
  45. package/dist/cli/doctor-types.js.map +1 -0
  46. package/dist/cli/doctor.js +48 -125
  47. package/dist/cli/doctor.js.map +1 -1
  48. package/dist/cli/dreamux.js +2 -30
  49. package/dist/cli/dreamux.js.map +1 -1
  50. package/dist/cli/server.js +55 -70
  51. package/dist/cli/server.js.map +1 -1
  52. package/dist/command/errors.js +43 -2
  53. package/dist/command/errors.js.map +1 -1
  54. package/dist/command/host.js +10 -17
  55. package/dist/command/host.js.map +1 -1
  56. package/dist/command/payload.js +0 -9
  57. package/dist/command/payload.js.map +1 -1
  58. package/dist/command/port.js +0 -4
  59. package/dist/command/port.js.map +1 -1
  60. package/dist/command/registry.js +0 -4
  61. package/dist/command/registry.js.map +1 -1
  62. package/dist/command/schema.js.map +1 -1
  63. package/dist/command/types.js.map +1 -0
  64. package/dist/command/validate.js +22 -41
  65. package/dist/command/validate.js.map +1 -1
  66. package/dist/config/commands.js +46 -0
  67. package/dist/config/commands.js.map +1 -0
  68. package/dist/config/config.js +155 -169
  69. package/dist/config/config.js.map +1 -1
  70. package/dist/config/load.js +138 -0
  71. package/dist/config/load.js.map +1 -0
  72. package/dist/config/service.js +196 -0
  73. package/dist/config/service.js.map +1 -0
  74. package/dist/daemon/{service-control.js → control.js} +15 -16
  75. package/dist/daemon/control.js.map +1 -0
  76. package/dist/daemon/environment.js +353 -0
  77. package/dist/daemon/environment.js.map +1 -0
  78. package/dist/daemon/host.js +16 -0
  79. package/dist/daemon/host.js.map +1 -0
  80. package/dist/daemon/install.js +167 -69
  81. package/dist/daemon/install.js.map +1 -1
  82. package/dist/daemon/status.js +87 -0
  83. package/dist/daemon/status.js.map +1 -0
  84. package/dist/{cli/service-status-parse.js → daemon/unit.js} +90 -12
  85. package/dist/daemon/unit.js.map +1 -0
  86. package/dist/{service/mcp/descriptor.js → mcp/launch.js} +3 -7
  87. package/dist/mcp/launch.js.map +1 -0
  88. package/dist/mcp/server.js +9 -5
  89. package/dist/mcp/server.js.map +1 -1
  90. package/dist/mcp/shim.js +18 -16
  91. package/dist/mcp/shim.js.map +1 -1
  92. package/dist/onboard/config-files.js +12 -26
  93. package/dist/onboard/config-files.js.map +1 -1
  94. package/dist/onboard/run.js +47 -66
  95. package/dist/onboard/run.js.map +1 -1
  96. package/dist/onboard/uninstall.js +59 -42
  97. package/dist/onboard/uninstall.js.map +1 -1
  98. package/dist/onboard/wizard.js +33 -18
  99. package/dist/onboard/wizard.js.map +1 -1
  100. package/dist/{onboard/commands.js → platform/command-runner.js} +7 -3
  101. package/dist/platform/command-runner.js.map +1 -0
  102. package/dist/platform/deduplicate.js +32 -0
  103. package/dist/platform/deduplicate.js.map +1 -0
  104. package/dist/{state → platform}/dispatcher-id.js +1 -1
  105. package/dist/platform/dispatcher-id.js.map +1 -0
  106. package/dist/platform/errors.js +23 -23
  107. package/dist/platform/errors.js.map +1 -1
  108. package/dist/{onboard/ledger.js → platform/file-ledger.js} +13 -38
  109. package/dist/platform/file-ledger.js.map +1 -0
  110. package/dist/platform/history-page.js +62 -0
  111. package/dist/platform/history-page.js.map +1 -0
  112. package/dist/platform/in-flight-work.js.map +1 -0
  113. package/dist/platform/instance-lock.js +95 -0
  114. package/dist/platform/instance-lock.js.map +1 -0
  115. package/dist/platform/json-value.js +26 -15
  116. package/dist/platform/json-value.js.map +1 -1
  117. package/dist/platform/jsonl.js.map +1 -1
  118. package/dist/platform/logger.js +5 -5
  119. package/dist/platform/logger.js.map +1 -1
  120. package/dist/platform/package-bin.js +3 -1
  121. package/dist/platform/package-bin.js.map +1 -1
  122. package/dist/platform/paths.js +42 -69
  123. package/dist/platform/paths.js.map +1 -1
  124. package/dist/platform/runtime-sockets.js +7 -2
  125. package/dist/platform/runtime-sockets.js.map +1 -1
  126. package/dist/platform/serial-queue.js +22 -0
  127. package/dist/platform/serial-queue.js.map +1 -0
  128. package/dist/platform/shutdown-errors.js.map +1 -0
  129. package/dist/platform/work-fence.js +30 -0
  130. package/dist/platform/work-fence.js.map +1 -0
  131. package/dist/plugin/hooks.js +407 -0
  132. package/dist/plugin/hooks.js.map +1 -0
  133. package/dist/plugin/host.js +97 -0
  134. package/dist/plugin/host.js.map +1 -0
  135. package/dist/plugin/loader.js +250 -0
  136. package/dist/plugin/loader.js.map +1 -0
  137. package/dist/provider-diagnostics.js +12 -8
  138. package/dist/provider-diagnostics.js.map +1 -1
  139. package/dist/registry/builtins.js +49 -45
  140. package/dist/registry/builtins.js.map +1 -1
  141. package/dist/registry/index.js +10 -2
  142. package/dist/registry/index.js.map +1 -1
  143. package/dist/registry/provider-loader.js +41 -61
  144. package/dist/registry/provider-loader.js.map +1 -1
  145. package/dist/registry/provider-ref.js.map +1 -1
  146. package/dist/registry/registry.js +14 -22
  147. package/dist/registry/registry.js.map +1 -1
  148. package/dist/{command/catalog.js → server/command-catalog.js} +14 -5
  149. package/dist/server/command-catalog.js.map +1 -0
  150. package/dist/server/command-host.js +2 -0
  151. package/dist/server/command-host.js.map +1 -0
  152. package/dist/server-commands.js +1 -1
  153. package/dist/server-commands.js.map +1 -1
  154. package/dist/server.js +70 -129
  155. package/dist/server.js.map +1 -1
  156. package/dist/service/{agent-entity/activity-reader.js → agent/activity.js} +86 -34
  157. package/dist/service/agent/activity.js.map +1 -0
  158. package/dist/service/{teammate-service/admission-ledger.js → agent/admission.js} +66 -1
  159. package/dist/service/agent/admission.js.map +1 -0
  160. package/dist/service/{channel-submission.js → agent/channel-submission.js} +5 -5
  161. package/dist/service/agent/channel-submission.js.map +1 -0
  162. package/dist/service/{teammate-collection → agent}/commands.js +38 -50
  163. package/dist/service/agent/commands.js.map +1 -0
  164. package/dist/service/{teammate-service → agent}/completion-renderer.js +30 -5
  165. package/dist/service/agent/completion-renderer.js.map +1 -0
  166. package/dist/service/agent/errors.js.map +1 -0
  167. package/dist/service/agent/factory.js +78 -0
  168. package/dist/service/agent/factory.js.map +1 -0
  169. package/dist/service/{agent-entity/types.js → agent/identity.js} +28 -34
  170. package/dist/service/agent/identity.js.map +1 -0
  171. package/dist/service/agent/index.js +769 -0
  172. package/dist/service/agent/index.js.map +1 -0
  173. package/dist/service/agent/mcp.js +420 -0
  174. package/dist/service/agent/mcp.js.map +1 -0
  175. package/dist/service/agent/records.js +74 -0
  176. package/dist/service/agent/records.js.map +1 -0
  177. package/dist/service/agent/requests.js +172 -0
  178. package/dist/service/agent/requests.js.map +1 -0
  179. package/dist/service/{teammate-service/runtime-owner.js → agent/runtime-generation.js} +81 -79
  180. package/dist/service/agent/runtime-generation.js.map +1 -0
  181. package/dist/service/agent/runtime-id.js +22 -0
  182. package/dist/service/agent/runtime-id.js.map +1 -0
  183. package/dist/service/{agent-entity → agent}/runtime-state.js +33 -40
  184. package/dist/service/agent/runtime-state.js.map +1 -0
  185. package/dist/service/agent/service-types.js +28 -0
  186. package/dist/service/agent/service-types.js.map +1 -0
  187. package/dist/service/{teammate-service/index.js → agent/service.js} +236 -102
  188. package/dist/service/agent/service.js.map +1 -0
  189. package/dist/service/{agent-entity/identity-store.js → agent/store.js} +175 -96
  190. package/dist/service/agent/store.js.map +1 -0
  191. package/dist/service/agent/submission.js.map +1 -0
  192. package/dist/service/{teammate-collection → agent}/system-prompt.js +7 -3
  193. package/dist/service/agent/system-prompt.js.map +1 -0
  194. package/dist/service/agent/turn.js +89 -0
  195. package/dist/service/agent/turn.js.map +1 -0
  196. package/dist/service/agent/types.js.map +1 -0
  197. package/dist/service/channel-service/commands.js +7 -3
  198. package/dist/service/channel-service/commands.js.map +1 -1
  199. package/dist/{channel → service/channel-service}/core-port.js +6 -5
  200. package/dist/service/channel-service/core-port.js.map +1 -0
  201. package/dist/service/channel-service/index.js +211 -93
  202. package/dist/service/channel-service/index.js.map +1 -1
  203. package/dist/service/channel-service/mcp-delegate.js +8 -9
  204. package/dist/service/channel-service/mcp-delegate.js.map +1 -1
  205. package/dist/service/completion-router/index.js +27 -27
  206. package/dist/service/completion-router/index.js.map +1 -1
  207. package/dist/{channel → service/dispatcher-core-events}/conversation-projection.js +9 -7
  208. package/dist/service/dispatcher-core-events/conversation-projection.js.map +1 -0
  209. package/dist/service/dispatcher-core-events/index.js +76 -68
  210. package/dist/service/dispatcher-core-events/index.js.map +1 -1
  211. package/dist/service/dispatcher-core-events/seal.js +1 -24
  212. package/dist/service/dispatcher-core-events/seal.js.map +1 -1
  213. package/dist/service/dispatcher-service/agent.js +267 -45
  214. package/dist/service/dispatcher-service/agent.js.map +1 -1
  215. package/dist/service/dispatcher-service/base-prompt.js +2 -2
  216. package/dist/service/dispatcher-service/base-prompt.js.map +1 -1
  217. package/dist/service/dispatcher-service/index.js +139 -378
  218. package/dist/service/dispatcher-service/index.js.map +1 -1
  219. package/dist/service/dispatcher-service/lifecycle.js +230 -0
  220. package/dist/service/dispatcher-service/lifecycle.js.map +1 -0
  221. package/dist/{daemon → service/dispatcher-service}/restart-intent.js +17 -38
  222. package/dist/service/dispatcher-service/restart-intent.js.map +1 -0
  223. package/dist/service/dispatcher-workspace.js +10 -23
  224. package/dist/service/dispatcher-workspace.js.map +1 -1
  225. package/dist/service/dispatchers/commands.js +30 -31
  226. package/dist/service/dispatchers/commands.js.map +1 -1
  227. package/dist/service/dispatchers/index.js +92 -64
  228. package/dist/service/dispatchers/index.js.map +1 -1
  229. package/dist/service/index.js +2 -2
  230. package/dist/service/index.js.map +1 -1
  231. package/dist/{mcp → service/mcp}/catalog.js +11 -48
  232. package/dist/service/mcp/catalog.js.map +1 -0
  233. package/dist/service/mcp/commands.js +3 -3
  234. package/dist/service/mcp/commands.js.map +1 -1
  235. package/dist/service/mcp/dispatch-reminders.js +1 -1
  236. package/dist/service/mcp/dispatch-reminders.js.map +1 -1
  237. package/dist/service/mcp/identity-version.js +14 -0
  238. package/dist/service/mcp/identity-version.js.map +1 -1
  239. package/dist/service/mcp/leases.js +12 -33
  240. package/dist/service/mcp/leases.js.map +1 -1
  241. package/dist/service/mcp/tool-metadata.js +15 -76
  242. package/dist/service/mcp/tool-metadata.js.map +1 -1
  243. package/dist/service/name-allocator.js +2 -3
  244. package/dist/service/name-allocator.js.map +1 -1
  245. package/dist/service/scheduler/commands.js +7 -19
  246. package/dist/service/scheduler/commands.js.map +1 -1
  247. package/dist/service/scheduler/errors.js +1 -1
  248. package/dist/service/scheduler/errors.js.map +1 -1
  249. package/dist/service/scheduler/{service.js → index.js} +175 -142
  250. package/dist/service/scheduler/index.js.map +1 -0
  251. package/dist/service/scheduler/{mcp-delegate.js → mcp.js} +61 -58
  252. package/dist/service/scheduler/mcp.js.map +1 -0
  253. package/dist/service/scheduler/requests.js +60 -0
  254. package/dist/service/scheduler/requests.js.map +1 -0
  255. package/dist/service/scheduler/store.js +183 -151
  256. package/dist/service/scheduler/store.js.map +1 -1
  257. package/dist/service/scheduler/types.js +1 -33
  258. package/dist/service/scheduler/types.js.map +1 -1
  259. package/dist/service/{team-collection → team}/commands.js +45 -59
  260. package/dist/service/team/commands.js.map +1 -0
  261. package/dist/service/{team-collection → team}/create-request.js +8 -20
  262. package/dist/service/team/create-request.js.map +1 -0
  263. package/dist/service/{team-collection → team}/errors.js +7 -10
  264. package/dist/service/team/errors.js.map +1 -0
  265. package/dist/service/team/index.js +817 -0
  266. package/dist/service/team/index.js.map +1 -0
  267. package/dist/service/team/leader-mcp.js +42 -0
  268. package/dist/service/team/leader-mcp.js.map +1 -0
  269. package/dist/service/team/leader.js +157 -0
  270. package/dist/service/team/leader.js.map +1 -0
  271. package/dist/service/team/mcp.js +328 -0
  272. package/dist/service/team/mcp.js.map +1 -0
  273. package/dist/service/{team-collection/types.js → team/requests.js} +64 -15
  274. package/dist/service/team/requests.js.map +1 -0
  275. package/dist/service/team/service.js +903 -0
  276. package/dist/service/team/service.js.map +1 -0
  277. package/dist/service/team/store.js +373 -0
  278. package/dist/service/team/store.js.map +1 -0
  279. package/dist/service/team/team-summary.js.map +1 -0
  280. package/dist/service/team/teams-port.js +2 -0
  281. package/dist/service/team/teams-port.js.map +1 -0
  282. package/dist/service/team/types.js +22 -0
  283. package/dist/service/team/types.js.map +1 -0
  284. package/dist/service/workflow-service/commands.js +5 -6
  285. package/dist/service/workflow-service/commands.js.map +1 -1
  286. package/dist/service/workflow-service/errors.js +2 -0
  287. package/dist/service/workflow-service/errors.js.map +1 -1
  288. package/dist/service/workflow-service/index.js +80 -112
  289. package/dist/service/workflow-service/index.js.map +1 -1
  290. package/dist/service/workflow-service/journal.js +90 -6
  291. package/dist/service/workflow-service/journal.js.map +1 -1
  292. package/dist/service/workflow-service/limits.js +20 -9
  293. package/dist/service/workflow-service/limits.js.map +1 -1
  294. package/dist/service/workflow-service/mcp.js +171 -0
  295. package/dist/service/workflow-service/mcp.js.map +1 -0
  296. package/dist/service/workflow-service/protocol.js +72 -1
  297. package/dist/service/workflow-service/protocol.js.map +1 -1
  298. package/dist/service/workflow-service/requests.js +70 -0
  299. package/dist/service/workflow-service/requests.js.map +1 -0
  300. package/dist/service/workflow-service/run.js +258 -145
  301. package/dist/service/workflow-service/run.js.map +1 -1
  302. package/dist/service/workflow-service/runner-process.js +0 -19
  303. package/dist/service/workflow-service/runner-process.js.map +1 -1
  304. package/dist/service/workflow-service/runner.js +6 -30
  305. package/dist/service/workflow-service/runner.js.map +1 -1
  306. package/dist/service/workflow-service/script-compiler.js +7 -9
  307. package/dist/service/workflow-service/script-compiler.js.map +1 -1
  308. package/dist/service/workflow-service/{run-support.js → semaphore.js} +1 -30
  309. package/dist/service/workflow-service/semaphore.js.map +1 -0
  310. package/dist/service/workflow-service/store.js +105 -25
  311. package/dist/service/workflow-service/store.js.map +1 -1
  312. package/dist/service/workflow-service/types.js +1 -77
  313. package/dist/service/workflow-service/types.js.map +1 -1
  314. package/dist/service/worktree/manager.js +29 -62
  315. package/dist/service/worktree/manager.js.map +1 -1
  316. package/dist/service/worktree/paths.js +4 -1
  317. package/dist/service/worktree/paths.js.map +1 -1
  318. package/dist/service/worktree/repo-request.js +38 -14
  319. package/dist/service/worktree/repo-request.js.map +1 -1
  320. package/dist/service/worktree/types.js +2 -0
  321. package/dist/service/worktree/types.js.map +1 -0
  322. package/dist/service/worktree/workspaces.js +27 -34
  323. package/dist/service/worktree/workspaces.js.map +1 -1
  324. package/package.json +13 -8
  325. package/skills/dispatcher/dreamux-maintenance/SKILL.md +5 -4
  326. package/skills/dispatcher/dreamux-maintenance/references/builtin-bootstrap.md +50 -0
  327. package/skills/dispatcher/dreamux-maintenance/references/builtin-codex.md +4 -5
  328. package/skills/dispatcher/dreamux-maintenance/references/builtin-feishu.md +69 -11
  329. package/skills/dispatcher/dreamux-maintenance/references/config-envelope.md +77 -12
  330. package/skills/dispatcher/dreamux-maintenance/references/feishu-access-v3.md +39 -24
  331. package/skills/dispatcher/dreamux-maintenance/references/service-lifecycle.md +105 -37
  332. package/dist/channel/conversation-projection.js.map +0 -1
  333. package/dist/channel/core-port.js.map +0 -1
  334. package/dist/cli/server-ctl.js +0 -163
  335. package/dist/cli/server-ctl.js.map +0 -1
  336. package/dist/cli/service-status-parse.js.map +0 -1
  337. package/dist/command/catalog.js.map +0 -1
  338. package/dist/config/config-helpers.js +0 -100
  339. package/dist/config/config-helpers.js.map +0 -1
  340. package/dist/daemon/restart-intent.js.map +0 -1
  341. package/dist/daemon/service-control.js.map +0 -1
  342. package/dist/mcp/catalog.js.map +0 -1
  343. package/dist/mcp/failure-text.js +0 -46
  344. package/dist/mcp/failure-text.js.map +0 -1
  345. package/dist/onboard/commands.js.map +0 -1
  346. package/dist/onboard/ledger.js.map +0 -1
  347. package/dist/onboard/service-node.js +0 -183
  348. package/dist/onboard/service-node.js.map +0 -1
  349. package/dist/onboard/service.js +0 -310
  350. package/dist/onboard/service.js.map +0 -1
  351. package/dist/platform/atomic-write.js +0 -63
  352. package/dist/platform/atomic-write.js.map +0 -1
  353. package/dist/platform/error-info.js +0 -11
  354. package/dist/platform/error-info.js.map +0 -1
  355. package/dist/platform/json-document-store.js +0 -54
  356. package/dist/platform/json-document-store.js.map +0 -1
  357. package/dist/service/agent-entity/activity-errors.js +0 -71
  358. package/dist/service/agent-entity/activity-errors.js.map +0 -1
  359. package/dist/service/agent-entity/activity-reader.js.map +0 -1
  360. package/dist/service/agent-entity/agent-config.js +0 -48
  361. package/dist/service/agent-entity/agent-config.js.map +0 -1
  362. package/dist/service/agent-entity/history-query.js +0 -60
  363. package/dist/service/agent-entity/history-query.js.map +0 -1
  364. package/dist/service/agent-entity/identity-store.js.map +0 -1
  365. package/dist/service/agent-entity/read-helpers.js +0 -172
  366. package/dist/service/agent-entity/read-helpers.js.map +0 -1
  367. package/dist/service/agent-entity/runtime-profile.js +0 -53
  368. package/dist/service/agent-entity/runtime-profile.js.map +0 -1
  369. package/dist/service/agent-entity/runtime-state.js.map +0 -1
  370. package/dist/service/agent-entity/types.js.map +0 -1
  371. package/dist/service/channel-service/mcp-delegates.js +0 -21
  372. package/dist/service/channel-service/mcp-delegates.js.map +0 -1
  373. package/dist/service/channel-service/types.js.map +0 -1
  374. package/dist/service/channel-submission.js.map +0 -1
  375. package/dist/service/closed-fact.js +0 -49
  376. package/dist/service/closed-fact.js.map +0 -1
  377. package/dist/service/deduplicate.js +0 -42
  378. package/dist/service/deduplicate.js.map +0 -1
  379. package/dist/service/dispatcher-core-events/scoped-source.js +0 -59
  380. package/dist/service/dispatcher-core-events/scoped-source.js.map +0 -1
  381. package/dist/service/dispatcher-service/dispatcher-workflows.js +0 -50
  382. package/dist/service/dispatcher-service/dispatcher-workflows.js.map +0 -1
  383. package/dist/service/dispatcher-service/identity.js +0 -97
  384. package/dist/service/dispatcher-service/identity.js.map +0 -1
  385. package/dist/service/dispatcher-service/inbound-task-drain.js +0 -37
  386. package/dist/service/dispatcher-service/inbound-task-drain.js.map +0 -1
  387. package/dist/service/dispatcher-service/input-source-lifecycle.js +0 -312
  388. package/dist/service/dispatcher-service/input-source-lifecycle.js.map +0 -1
  389. package/dist/service/dispatcher-service/input-source-start-rollback.js +0 -53
  390. package/dist/service/dispatcher-service/input-source-start-rollback.js.map +0 -1
  391. package/dist/service/dispatcher-service/mcp-delegates.js +0 -67
  392. package/dist/service/dispatcher-service/mcp-delegates.js.map +0 -1
  393. package/dist/service/dispatcher-service/restart-notice.js +0 -27
  394. package/dist/service/dispatcher-service/restart-notice.js.map +0 -1
  395. package/dist/service/dispatcher-service/runnable-channel.js +0 -13
  396. package/dist/service/dispatcher-service/runnable-channel.js.map +0 -1
  397. package/dist/service/dispatcher-service/runtime-helpers.js +0 -11
  398. package/dist/service/dispatcher-service/runtime-helpers.js.map +0 -1
  399. package/dist/service/dispatcher-service/runtime-status.js +0 -35
  400. package/dist/service/dispatcher-service/runtime-status.js.map +0 -1
  401. package/dist/service/dispatcher-service/team-leader-handle.js +0 -29
  402. package/dist/service/dispatcher-service/team-leader-handle.js.map +0 -1
  403. package/dist/service/dispatcher-service/team-runtime-stop.js +0 -13
  404. package/dist/service/dispatcher-service/team-runtime-stop.js.map +0 -1
  405. package/dist/service/dispatcher-service/teammate-ops.js +0 -14
  406. package/dist/service/dispatcher-service/teammate-ops.js.map +0 -1
  407. package/dist/service/frozen-snapshot.js +0 -16
  408. package/dist/service/frozen-snapshot.js.map +0 -1
  409. package/dist/service/in-flight-work.js.map +0 -1
  410. package/dist/service/legacy-state.js +0 -119
  411. package/dist/service/legacy-state.js.map +0 -1
  412. package/dist/service/mcp/descriptor.js.map +0 -1
  413. package/dist/service/scheduler/mcp-delegate.js.map +0 -1
  414. package/dist/service/scheduler/service.js.map +0 -1
  415. package/dist/service/serial-queue.js +0 -64
  416. package/dist/service/serial-queue.js.map +0 -1
  417. package/dist/service/shutdown-errors.js.map +0 -1
  418. package/dist/service/team-collection/commands.js.map +0 -1
  419. package/dist/service/team-collection/create-request.js.map +0 -1
  420. package/dist/service/team-collection/errors.js.map +0 -1
  421. package/dist/service/team-collection/index.js +0 -304
  422. package/dist/service/team-collection/index.js.map +0 -1
  423. package/dist/service/team-collection/mcp-delegate.js +0 -389
  424. package/dist/service/team-collection/mcp-delegate.js.map +0 -1
  425. package/dist/service/team-collection/read-helpers.js +0 -66
  426. package/dist/service/team-collection/read-helpers.js.map +0 -1
  427. package/dist/service/team-collection/read-model.js +0 -120
  428. package/dist/service/team-collection/read-model.js.map +0 -1
  429. package/dist/service/team-collection/roster-reader.js +0 -52
  430. package/dist/service/team-collection/roster-reader.js.map +0 -1
  431. package/dist/service/team-collection/runtime-registry.js +0 -369
  432. package/dist/service/team-collection/runtime-registry.js.map +0 -1
  433. package/dist/service/team-collection/store.js +0 -320
  434. package/dist/service/team-collection/store.js.map +0 -1
  435. package/dist/service/team-collection/types.js.map +0 -1
  436. package/dist/service/team-collection/worktree-cleanup.js +0 -45
  437. package/dist/service/team-collection/worktree-cleanup.js.map +0 -1
  438. package/dist/service/team-service/closing.js +0 -279
  439. package/dist/service/team-service/closing.js.map +0 -1
  440. package/dist/service/team-service/collaborators.js +0 -82
  441. package/dist/service/team-service/collaborators.js.map +0 -1
  442. package/dist/service/team-service/completion-targets.js +0 -72
  443. package/dist/service/team-service/completion-targets.js.map +0 -1
  444. package/dist/service/team-service/index.js +0 -559
  445. package/dist/service/team-service/index.js.map +0 -1
  446. package/dist/service/team-service/leader-agent.js +0 -170
  447. package/dist/service/team-service/leader-agent.js.map +0 -1
  448. package/dist/service/team-service/roster-projection.js +0 -69
  449. package/dist/service/team-service/roster-projection.js.map +0 -1
  450. package/dist/service/team-service/team-summary.js.map +0 -1
  451. package/dist/service/team-service/types.js +0 -61
  452. package/dist/service/team-service/types.js.map +0 -1
  453. package/dist/service/teammate-collection/commands.js.map +0 -1
  454. package/dist/service/teammate-collection/dissolve-members.js +0 -44
  455. package/dist/service/teammate-collection/dissolve-members.js.map +0 -1
  456. package/dist/service/teammate-collection/errors.js.map +0 -1
  457. package/dist/service/teammate-collection/index.js +0 -497
  458. package/dist/service/teammate-collection/index.js.map +0 -1
  459. package/dist/service/teammate-collection/mcp-delegate.js +0 -210
  460. package/dist/service/teammate-collection/mcp-delegate.js.map +0 -1
  461. package/dist/service/teammate-collection/mcp-tool-descriptors.js +0 -421
  462. package/dist/service/teammate-collection/mcp-tool-descriptors.js.map +0 -1
  463. package/dist/service/teammate-collection/system-prompt.js.map +0 -1
  464. package/dist/service/teammate-collection/types.js.map +0 -1
  465. package/dist/service/teammate-service/admission-ledger.js.map +0 -1
  466. package/dist/service/teammate-service/completion-renderer.js.map +0 -1
  467. package/dist/service/teammate-service/factory.js +0 -5
  468. package/dist/service/teammate-service/factory.js.map +0 -1
  469. package/dist/service/teammate-service/index.js.map +0 -1
  470. package/dist/service/teammate-service/runtime-owner.js.map +0 -1
  471. package/dist/service/teammate-service/submission.js.map +0 -1
  472. package/dist/service/teammate-service/turn-coordinator.js +0 -93
  473. package/dist/service/teammate-service/turn-coordinator.js.map +0 -1
  474. package/dist/service/teammate-service/turn-recording.js +0 -178
  475. package/dist/service/teammate-service/turn-recording.js.map +0 -1
  476. package/dist/service/teammate-service/types.js +0 -12
  477. package/dist/service/teammate-service/types.js.map +0 -1
  478. package/dist/service/workflow-service/agent-policy.js +0 -6
  479. package/dist/service/workflow-service/agent-policy.js.map +0 -1
  480. package/dist/service/workflow-service/json-args.js +0 -72
  481. package/dist/service/workflow-service/json-args.js.map +0 -1
  482. package/dist/service/workflow-service/run-support.js.map +0 -1
  483. package/dist/service/workflow-service/run-terminal.js +0 -111
  484. package/dist/service/workflow-service/run-terminal.js.map +0 -1
  485. package/dist/state/dispatcher-id.js.map +0 -1
  486. package/dist/state/dispatcher-store.js +0 -73
  487. package/dist/state/dispatcher-store.js.map +0 -1
  488. /package/dist/{service/channel-service → command}/types.js +0 -0
  489. /package/dist/{service → platform}/in-flight-work.js +0 -0
  490. /package/dist/{service → platform}/shutdown-errors.js +0 -0
  491. /package/dist/service/{teammate-collection → agent}/errors.js +0 -0
  492. /package/dist/service/{teammate-service → agent}/submission.js +0 -0
  493. /package/dist/service/{teammate-collection → agent}/types.js +0 -0
  494. /package/dist/service/{team-service → team}/team-summary.js +0 -0
@@ -0,0 +1,817 @@
1
+ import { normalizeSkillSources, parseAgentRuntimeSkillSources, } from '../../agent-runtime/skill-sources.js';
2
+ import { defaultWorkspaceEnabled } from '../../config/config.js';
3
+ import { ServerShuttingDownError } from '../../platform/errors.js';
4
+ import { clampHistoryLimit, decodeCursor, encodeCursor, matchesGrepText, previewText, } from '../../platform/history-page.js';
5
+ import { teamMateCollectionDir } from '../../platform/paths.js';
6
+ import { KeyedAsyncQueue } from '../../platform/serial-queue.js';
7
+ import { throwSettledFailures } from '../../platform/shutdown-errors.js';
8
+ import { requireLifecycleText, } from '../agent/identity.js';
9
+ import { toStatus } from '../agent/records.js';
10
+ import { agentCollectionMemberCount, readAgentIdentity, } from '../agent/store.js';
11
+ import { ensureDispatcherWorkspace } from '../dispatcher-workspace.js';
12
+ import { allocateConcreteNameAsync } from '../name-allocator.js';
13
+ import { repoWorktree } from '../worktree/repo-request.js';
14
+ import { IdempotencyConflictError, TeamClosedError, teamErrorInfo, TeamNotFoundError, } from './errors.js';
15
+ import { TEAM_LEADER_REQUIRED_SKILL_SOURCES } from './leader.js';
16
+ import { settleTeamWorktreeCleanup, TeamService, } from './service.js';
17
+ import { TeamStore } from './store.js';
18
+ import { teamSummary } from './team-summary.js';
19
+ import { validateTeamId, } from './types.js';
20
+ /**
21
+ * The dispatcher's team collection (issue #233): one per dispatcher, owned by
22
+ * `DispatcherService`. It stores, creates, finds, lists, materializes, and
23
+ * evicts Teams, and nothing else — a Team's own lifecycle, including dissolve,
24
+ * belongs to the {@link TeamService} that is that Team.
25
+ *
26
+ * `get` (private) is a get-or-rebuild factory (like `Dispatchers.get` /
27
+ * `TeammateCollection.entityFor`): cached live service if any, else rebuilt
28
+ * from the persisted {@link TeamRecord} and cached. Each `TeamService` OWNS its
29
+ * per-team `TeammateCollection`, bound to the Team ID and workspace at
30
+ * construction from the shared dependencies forwarded here.
31
+ *
32
+ * Implements {@link TeamsPort} directly: every dispatcher-facing per-Team
33
+ * operation gates itself on the injected the dispatcher fence internally, so a
34
+ * caller reaches a Team through exactly one surface instead of an
35
+ * open/admit/read triple it must fence itself.
36
+ */
37
+ export class TeamCollection {
38
+ opts;
39
+ dispatcherId;
40
+ store;
41
+ worktrees;
42
+ /**
43
+ * One materialized Team per id: the whole materialization cache.
44
+ * {@link track} sets an entry only once it has also arranged eviction
45
+ * (`service.closed.then(...)`), so an entry here is by definition a Team
46
+ * this collection is both caching and watching for its close.
47
+ */
48
+ live = new Map();
49
+ constructing = new Map();
50
+ /**
51
+ * Created Teams whose initial turn is still being admitted. Not in `live`,
52
+ * because a Team that is not yet running must not be served; held here,
53
+ * because that turn starts the leader's runtime and a host stop has to reach
54
+ * it.
55
+ */
56
+ starting = new Set();
57
+ /** Serializes the whole lookup/create sequence per request id. */
58
+ createRequestLifecycle = new KeyedAsyncQueue();
59
+ constructor(opts) {
60
+ this.opts = opts;
61
+ this.dispatcherId = opts.dispatcherId;
62
+ this.worktrees = opts.worktrees;
63
+ this.store = new TeamStore({
64
+ root: opts.root,
65
+ dispatcherId: this.dispatcherId,
66
+ });
67
+ }
68
+ /**
69
+ * Create one Team under a `team.create` request identity.
70
+ *
71
+ * The Team record is the whole protocol. Publishing through its canonical
72
+ * serialized owner is the acceptance point: it makes the Team exist, takes the
73
+ * concrete name, and durably records the request id and canonical
74
+ * payload hash a later replay is decided against.
75
+ *
76
+ * Before publication no Team or accepted request exists on disk, so a lost
77
+ * process, a rejected candidate, or a plain retry may pick another name.
78
+ * While a valid Team record carries that acceptance, the same id with the
79
+ * same payload resolves back to that Team, including once it is closed;
80
+ * the same id with a different payload is an idempotency conflict.
81
+ *
82
+ * Gated by the dispatcher fence like every other `TeamsPort` method: a create
83
+ * admitted right as the dispatcher starts closing still crosses the fence
84
+ * before doing any work, rather than building a workspace and a leader only
85
+ * to self-close in `track()` the moment it registers.
86
+ *
87
+ * The caller's `command` is the wire-shaped request, untranslated: only a
88
+ * request that is not a replay of an already-accepted one runs through the
89
+ * owning Dispatcher's `createTeam` hook (replay identity is decided above,
90
+ * against `payloadHash` alone, so a plugin never sees a replayed request),
91
+ * and only after that hook does this method do the repo/skill-source
92
+ * translation `commands.ts` and `mcp.ts` used to each do for themselves —
93
+ * so a plugin's own `leader.skill_sources`/`repo` value gets the identical
94
+ * mandatory-root injection and repo→worktree mapping an admin-supplied one
95
+ * gets, rather than skipping it by running before the hook.
96
+ */
97
+ async createFromRequest(input) {
98
+ return this.opts.fence.admit(() => this.createRequestLifecycle.run(input.requestId, async () => {
99
+ const accepted = await this.acceptedRequest(input.requestId);
100
+ if (accepted !== null) {
101
+ if (accepted.create_payload_hash !== input.payloadHash) {
102
+ throw new IdempotencyConflictError(`request_id ${JSON.stringify(input.requestId)} was already accepted with a ` +
103
+ 'different team.create payload; use a new request_id for a new Team');
104
+ }
105
+ // Read the accepted Team without materializing it or resubmitting work.
106
+ return this.summaryFromRecord(accepted);
107
+ }
108
+ // `request_id` is excluded from what a tap sees: replay identity is
109
+ // decided above, against `payloadHash` alone, before this hook ever
110
+ // runs.
111
+ const { name_prefix, intent, leader, repo: requestedRepo, } = input.command;
112
+ const resolved = await this.opts.dispatcherHooks.createTeam.promise({
113
+ name_prefix,
114
+ intent,
115
+ leader,
116
+ ...(requestedRepo !== undefined ? { repo: requestedRepo } : {}),
117
+ });
118
+ // A `createTeam` tap can return any `leader.skill_sources` value the
119
+ // type system cannot check at runtime (a plugin need not even be
120
+ // written in TypeScript). The pre-hook value already passed this same
121
+ // structural check once (`optionalParsedSkillSources`, at parse time);
122
+ // after the hook, and only after it, repeat it so a malformed value
123
+ // fails as a clean RuleViolation naming the field instead of a raw
124
+ // filesystem error deep inside skill-root canonicalization.
125
+ const parsedSkillSources = resolved.leader.skill_sources !== undefined
126
+ ? parseAgentRuntimeSkillSources(resolved.leader.skill_sources, 'plugin "createTeam" hook output\'s leader.skill_sources')
127
+ : null;
128
+ const skillSources = await normalizeSkillSources(parsedSkillSources, {
129
+ requiredSources: TEAM_LEADER_REQUIRED_SKILL_SOURCES,
130
+ });
131
+ const repo = repoWorktree(resolved.repo ?? null);
132
+ // A named repository request with no explicit path passes no
133
+ // `repoCwd` at all: `prepareWorkspace()` already falls back to the
134
+ // dispatcher's own default workspace when it sees none.
135
+ const repoCwd = repo?.cwd ?? null;
136
+ const options = {
137
+ intent: resolved.intent,
138
+ leaderAgentRuntime: resolved.leader.agent_runtime,
139
+ ...(repoCwd !== null ? { repoCwd } : {}),
140
+ ...(repo !== null ? { worktree: repo.worktree } : {}),
141
+ prompt: resolved.leader.prompt,
142
+ identity: resolved.leader.identity,
143
+ ...(skillSources !== null ? { skillSources } : {}),
144
+ };
145
+ const outcome = { created: null };
146
+ const teamName = await allocateConcreteNameAsync({
147
+ kind: 'team',
148
+ base: resolved.name_prefix,
149
+ accept: async (candidate) => {
150
+ // No probe here: `create`'s own early-out against the same
151
+ // record is the authoritative answer, so a candidate already
152
+ // owned by another Team is refused there instead of twice.
153
+ outcome.created = await this.createAtCandidate({
154
+ ...options,
155
+ name: candidate,
156
+ createRequest: {
157
+ requestId: input.requestId,
158
+ payloadHash: input.payloadHash,
159
+ },
160
+ deliverCompletionToDispatcher: input.deliverCompletionToDispatcher,
161
+ });
162
+ return outcome.created !== null;
163
+ },
164
+ });
165
+ const created = outcome.created;
166
+ if (created === null) {
167
+ throw new Error(`team.create request ${JSON.stringify(input.requestId)} accepted the name ` +
168
+ `${JSON.stringify(teamName)} without publishing a Team record`);
169
+ }
170
+ return created.status();
171
+ }));
172
+ }
173
+ /**
174
+ * The Team this request id already produced, read from the records.
175
+ *
176
+ * The records are the only ledger: a creation that failed after publishing
177
+ * one still accepted the request, and a process that died before returning
178
+ * left the same proof behind. While that valid record remains, scanning it
179
+ * makes a replay answer the same way across restarts.
180
+ */
181
+ async acceptedRequest(requestId) {
182
+ for (const team of await this.store.list()) {
183
+ if (team.create_request_id === requestId)
184
+ return team;
185
+ }
186
+ return null;
187
+ }
188
+ /**
189
+ * Create one Team at a candidate name, or report the candidate as taken.
190
+ *
191
+ * `null` means a valid Team record occupies that name — the candidate is
192
+ * unavailable and the allocator should offer another.
193
+ */
194
+ async createAtCandidate(input) {
195
+ return this.create(input, validateTeamId(input.name));
196
+ }
197
+ /**
198
+ * Compact rows from records alone; a list never consults a live runtime.
199
+ *
200
+ * Gated like every other per-Team operational read: an inventory read
201
+ * this narrow still names a Team, so it is fenced the same as `history` and
202
+ * `summary` rather than left observable through a dispatcher stop.
203
+ */
204
+ async list() {
205
+ return this.opts.fence.admit(() => this.listRows());
206
+ }
207
+ /** Gated because a read is still per-Team operational access. */
208
+ async history(input) {
209
+ return this.opts.fence.admit(() => this.historyResult(input));
210
+ }
211
+ /**
212
+ * Get-or-rebuild the team's service; a cold-cache miss is deduped (#233).
213
+ *
214
+ * Live Teams only: a closed record has no service, so this reports it as
215
+ * closed rather than building one. Callers outside reach a Team through
216
+ * {@link open} or a lease, which say the same thing in their own words.
217
+ *
218
+ * Creation publishes a Team's record before its object graph is finished, so
219
+ * a read that saw only the record would build a second, competing owner of
220
+ * the same Team. Every path that can produce the object registers through
221
+ * `constructing` instead. `null` from a joined construction means that
222
+ * construction did not produce this Team — a create whose candidate turned
223
+ * out to be taken — so the decision is made again against what is now
224
+ * durable rather than reported as this caller's answer.
225
+ */
226
+ async get(teamId) {
227
+ const id = validateTeamId(teamId);
228
+ for (;;) {
229
+ const cached = this.live.get(id);
230
+ if (cached !== undefined)
231
+ return cached;
232
+ const joined = this.constructing.get(id);
233
+ if (joined === undefined) {
234
+ const construction = this.rebuild(id);
235
+ this.publishConstruction(id, construction);
236
+ return construction;
237
+ }
238
+ const service = await joined;
239
+ if (service !== null)
240
+ return service;
241
+ }
242
+ }
243
+ /**
244
+ * One Team's status.
245
+ *
246
+ * An open Team this process already holds answers for itself, because its
247
+ * live state is the more current version of the same shape. Any other Team is
248
+ * read from its records: whether it is closed or simply not materialized
249
+ * here, a read must not build an entity — and a Team with no runtime in this
250
+ * process has no runtime state for a projection to be missing.
251
+ *
252
+ * Gated the same as every other per-Team operational read; the wrap
253
+ * used to sit on the caller (`DispatcherService.getTeamStatus()`) and now
254
+ * sits here instead, on the method itself.
255
+ */
256
+ async summary(teamId) {
257
+ return this.opts.fence.admit(async () => {
258
+ const record = await this.mustTeam(validateTeamId(teamId));
259
+ return this.summaryFromRecord(record);
260
+ });
261
+ }
262
+ /**
263
+ * One source selection for status and accepted-request replay.
264
+ *
265
+ * The record just read from the store is authoritative for lifecycle, and it
266
+ * decides whether there is an entity to ask at all: a closed Team is a
267
+ * record, so it answers from that record and never from a cached service.
268
+ * Only an open Team this process holds answers for itself.
269
+ */
270
+ async summaryFromRecord(record) {
271
+ if (record.status === 'closed')
272
+ return this.recordSummary(record);
273
+ const live = this.live.get(record.team_id) ?? null;
274
+ return live === null ? this.recordSummary(record) : live.status();
275
+ }
276
+ /**
277
+ * Finish the physical reclamation a previous run left pending.
278
+ *
279
+ * The Team's own worktree cleanup fact is the entire recovery authority: a
280
+ * `closed` Team still marked `cleanup-pending` has a managed checkout the
281
+ * operator authorized destroying, and nothing else about a dissolve outlives
282
+ * the process that ran it. Nothing is materialized to do it — a closed Team
283
+ * is a record, and the record is all this work reads and writes.
284
+ *
285
+ * Each reclaim is launched, not awaited: it is the same background work that
286
+ * ran behind the durable close, so a slow Git must not hold up dispatcher
287
+ * start. A failure leaves the pending fact standing for the next start.
288
+ */
289
+ async recoverWorktreeCleanup() {
290
+ for (const record of await this.store.list()) {
291
+ if (record.status !== 'closed')
292
+ continue;
293
+ if (record.worktree.cleanup_state !== 'cleanup-pending')
294
+ continue;
295
+ void this.reclaimTeamWorktree(record.team_id);
296
+ }
297
+ }
298
+ async reclaimTeamWorktree(teamId) {
299
+ try {
300
+ const record = await this.store.acquire(teamId);
301
+ try {
302
+ await settleTeamWorktreeCleanup(record, this.worktrees);
303
+ }
304
+ finally {
305
+ await record.release();
306
+ }
307
+ }
308
+ catch (error) {
309
+ this.opts.log.error({
310
+ dispatcher_id: this.dispatcherId,
311
+ team_id: teamId,
312
+ err: teamErrorInfo(error),
313
+ }, 'Team managed worktree cleanup recovery failed');
314
+ }
315
+ }
316
+ /**
317
+ * Get one Team that can take new work.
318
+ *
319
+ * The collection's whole part in a per-Team operation: find the entity, or
320
+ * say why there is none to reach — {@link TeamNotFoundError} for a Team that
321
+ * does not exist, {@link TeamClosedError} for one that is over. What happens
322
+ * next is the Team's own.
323
+ */
324
+ async open(teamId) {
325
+ const id = validateTeamId(teamId);
326
+ const record = await this.mustTeam(id);
327
+ if (record.status === 'closed') {
328
+ throw new TeamClosedError(`Team ${JSON.stringify(id)} is closed`);
329
+ }
330
+ return this.get(id);
331
+ }
332
+ async mustTeam(teamId) {
333
+ const team = await this.store.get(validateTeamId(teamId));
334
+ if (team === null) {
335
+ throw new TeamNotFoundError(`Team ${JSON.stringify(teamId)} does not exist`);
336
+ }
337
+ return team;
338
+ }
339
+ /**
340
+ * Submit one turn to a Team's TeamLeader.
341
+ *
342
+ * `deliverCompletionToDispatcher` is resolved to a `completionRecipient` here, inside
343
+ * the fence, rather than accepted as one directly: a caller outside `team/`
344
+ * knows only whether a Core-side initiator is waiting for the leader's
345
+ * completion, never the dispatcher Agent itself. A Channel-originated turn
346
+ * passes `false`, because the leader answers on its own Channel.
347
+ */
348
+ submitToLeader(teamId, input) {
349
+ return this.opts.fence.admit(async () => {
350
+ const { deliverCompletionToDispatcher, ...submission } = input;
351
+ const completionRecipient = deliverCompletionToDispatcher
352
+ ? this.opts.completionOwner.completionRecipient()
353
+ : null;
354
+ return (await this.open(teamId)).submitInput({
355
+ ...submission,
356
+ ...(completionRecipient !== null ? { completionRecipient } : {}),
357
+ });
358
+ });
359
+ }
360
+ /** Interrupt one Team's leader. */
361
+ interruptLeader(teamId) {
362
+ return this.opts.fence.admit(async () => (await this.open(teamId)).interruptLeader());
363
+ }
364
+ /**
365
+ * Submit one Team's dissolve.
366
+ *
367
+ * Never waits for the outcome: once the Team owns the operation the caller
368
+ * has its receipt, and a second submission joins the first instead of
369
+ * dismantling the Team twice.
370
+ */
371
+ dissolve(teamId, input) {
372
+ return this.opts.fence.admit(async () => (await this.open(teamId)).dissolve(input));
373
+ }
374
+ scheduler(teamId) {
375
+ return this.opts.fence.admit(async () => (await this.open(teamId)).scheduler);
376
+ }
377
+ /**
378
+ * Materialize every non-closed Team. Rebuilding a Team reconciles the
379
+ * Workflow records its previous process left running, so a failure here
380
+ * fails the dispatcher's start before any channel opens.
381
+ */
382
+ async recover() {
383
+ for (const team of await this.store.list()) {
384
+ if (team.status === 'closed')
385
+ continue;
386
+ await this.get(team.team_id);
387
+ }
388
+ }
389
+ /**
390
+ * Open every non-closed Team's admissions (Workflow admission and its
391
+ * scheduler) through {@link TeamService.startAdmissions}. One Team's failure
392
+ * is logged and does not keep the others closed.
393
+ */
394
+ async startAdmissions() {
395
+ for (const team of await this.store.list()) {
396
+ if (team.status === 'closed')
397
+ continue;
398
+ try {
399
+ const service = await this.get(team.team_id);
400
+ await service.startAdmissions();
401
+ }
402
+ catch (error) {
403
+ this.opts.log.error({
404
+ dispatcher_id: this.dispatcherId,
405
+ team_id: team.team_id,
406
+ err: teamErrorInfo(error),
407
+ }, 'Team admissions start failed');
408
+ }
409
+ }
410
+ }
411
+ /** Close every live Team's admissions (Workflow stop-all and its scheduler). */
412
+ stopAdmissions() {
413
+ for (const service of this.live.values())
414
+ service.stopAdmissions();
415
+ }
416
+ /**
417
+ * Release the runtime authority this process took over its Teams.
418
+ *
419
+ * Only Teams this process materialized are swept, because only they hold a
420
+ * runtime to release. The discovery pass this replaced listed every durable
421
+ * non-closed record and materialized it first, which on a `starting` record
422
+ * creates that Team's TeamLeader identity — durable work on the stop path,
423
+ * done to entities the run never started.
424
+ */
425
+ async stopForHost() {
426
+ const services = [...this.live.values(), ...this.starting];
427
+ // Nothing is evicted: a stopped Team is still this collection's until it
428
+ // closes, so the dispatcher's second sweep reaches it again if a pre-fence
429
+ // use restarted its leader in between.
430
+ const results = await Promise.allSettled(
431
+ // The containment root publishes its aggregate admission fence before
432
+ // this sweep. Do not hold a Team route lock while releasing members:
433
+ // their captured completion delivery may resolve the same TeamLeader
434
+ // through that route before the leader itself is released.
435
+ services.map((service) => service.stopForHost()));
436
+ throwSettledFailures(results, 'multiple Team runtimes failed to stop');
437
+ }
438
+ /**
439
+ * Create one Team at this candidate name, or report the name as taken.
440
+ *
441
+ * `null` means the candidate is not this creation's to use: a valid Team
442
+ * record already occupies it, or a construction already owns it. The caller
443
+ * allocates another one, and nothing this attempt made survives it.
444
+ */
445
+ async create(input, teamId) {
446
+ requireLifecycleText(input.intent, 'Team create intent');
447
+ // Whoever registers first owns this id synchronously, so a second
448
+ // create at the same candidate steps aside rather than racing it.
449
+ if (this.constructing.has(teamId))
450
+ return null;
451
+ const construction = this.createTeam(input, teamId);
452
+ this.publishConstruction(teamId, construction);
453
+ return construction;
454
+ }
455
+ /**
456
+ * Prepare the workspace, publish the record, and take ownership of the
457
+ * result — the whole of what creating a Team means here.
458
+ *
459
+ * It is one operation because it has one side effect to answer for. The
460
+ * checkout is prepared before any record exists, so an ending that never
461
+ * publishes one has to undo it; after publication the record owns the
462
+ * checkout, and undoing it here would reach into a Team that exists.
463
+ */
464
+ async createTeam(input, teamId) {
465
+ const record = await this.store.acquire(teamId);
466
+ let transferred = false;
467
+ try {
468
+ // Keep the candidate's captured owner from this cheap early-out through
469
+ // workspace preparation and publication in the same serialized queue.
470
+ if (record.current !== null)
471
+ return null;
472
+ const workspace = await this.prepareWorkspace(input, teamId);
473
+ const serviceInput = {
474
+ teamId,
475
+ name: input.name,
476
+ createRequest: input.createRequest,
477
+ prompt: input.prompt,
478
+ deliverCompletionToDispatcher: input.deliverCompletionToDispatcher,
479
+ leaderAgentRuntime: input.leaderAgentRuntime,
480
+ intent: input.intent,
481
+ identity: input.identity,
482
+ skillSources: input.skillSources,
483
+ workspace,
484
+ };
485
+ let created;
486
+ try {
487
+ created = await TeamService.createNew(this.depsBase(teamId, record), serviceInput);
488
+ }
489
+ catch (error) {
490
+ await this.discardUnclaimedCheckout(teamId, workspace);
491
+ throw error;
492
+ }
493
+ if (created === null) {
494
+ await this.discardUnclaimedCheckout(teamId, workspace);
495
+ return null;
496
+ }
497
+ this.starting.add(created);
498
+ try {
499
+ this.refuseIfClosing(created);
500
+ await created.startCreated(serviceInput);
501
+ }
502
+ finally {
503
+ this.starting.delete(created);
504
+ }
505
+ transferred = true;
506
+ this.track(created, record);
507
+ return created;
508
+ }
509
+ finally {
510
+ if (!transferred)
511
+ await record.release();
512
+ }
513
+ }
514
+ async prepareWorkspace(input, teamId) {
515
+ const workspaceRoot = await ensureDispatcherWorkspace(this.opts.config.current(), this.dispatcherId);
516
+ return input.worktree === undefined && input.repoCwd === undefined
517
+ ? this.worktrees.prepareDefaultWorkspace({
518
+ dispatcherWorkspace: workspaceRoot,
519
+ slug: teamId,
520
+ workspaceEnabled: defaultWorkspaceEnabled(this.opts.config.current(), this.dispatcherId),
521
+ })
522
+ : this.worktrees.prepare({
523
+ dispatcherId: this.dispatcherId,
524
+ teammateName: `team-${teamId}`,
525
+ cwd: input.repoCwd ?? workspaceRoot,
526
+ dispatcherWorkspace: workspaceRoot,
527
+ request: input.worktree,
528
+ });
529
+ }
530
+ /**
531
+ * Undo the checkout this failed attempt made, while it is still nobody's.
532
+ *
533
+ * A Team record is the only owner a managed checkout can have, so its absence
534
+ * is the proof that this preparation is unclaimed. Once a record exists — the
535
+ * closed one an abandoned creation leaves, or the Team that took the name —
536
+ * that record carries the cleanup and this must not touch the directory.
537
+ * Removal honors the requested policy, so a `keep` checkout is kept exactly
538
+ * as a dissolve would keep it, and a reused directory is never reached at all.
539
+ *
540
+ * Nothing here can be retried later: without a record there is no owner to
541
+ * carry a pending reclaim, so a removal that reports a refusal rather than
542
+ * raising one is stated here or nowhere.
543
+ */
544
+ async discardUnclaimedCheckout(teamId, workspace) {
545
+ if (!workspace.createdCheckout)
546
+ return;
547
+ try {
548
+ if ((await this.store.get(teamId)) !== null)
549
+ return;
550
+ const cleaned = await this.worktrees.cleanup({
551
+ source_cwd: workspace.sourceCwd,
552
+ source_repo: workspace.sourceRepo,
553
+ worktree: workspace.worktree,
554
+ });
555
+ // Removed or deliberately kept is the end of it. Anything else is a
556
+ // directory this attempt made and nobody now owns, so it is reported the
557
+ // same way a raised failure is — once, with where it is and why it stayed.
558
+ if (cleaned.cleanup_state !== 'deleted' &&
559
+ cleaned.cleanup_state !== 'kept') {
560
+ this.opts.log.warn({
561
+ dispatcher_id: this.dispatcherId,
562
+ team_id: teamId,
563
+ path: cleaned.path,
564
+ cleanup_state: cleaned.cleanup_state,
565
+ cleanup_error: cleaned.cleanup_error,
566
+ }, 'prepared Team worktree was left behind');
567
+ }
568
+ }
569
+ catch (error) {
570
+ this.opts.log.warn({
571
+ dispatcher_id: this.dispatcherId,
572
+ team_id: teamId,
573
+ path: workspace.worktree.path,
574
+ err: teamErrorInfo(error),
575
+ }, 'prepared Team worktree was left behind');
576
+ }
577
+ }
578
+ /**
579
+ * Rebuild one Team from its record.
580
+ *
581
+ * A closed record is not a Team, it is that Team's history: nothing here
582
+ * constructs one, so a status read, a startup sweep, or a leftover physical
583
+ * cleanup answers from the record instead. That is what keeps this
584
+ * collection bounded by the Teams that are alive rather than by every Team
585
+ * that ever existed.
586
+ */
587
+ async rebuild(teamId) {
588
+ const handle = await this.store.acquire(teamId);
589
+ let transferred = false;
590
+ try {
591
+ const record = handle.current;
592
+ if (record === null) {
593
+ throw new TeamNotFoundError(`Team ${JSON.stringify(teamId)} not found`);
594
+ }
595
+ if (record.status === 'closed') {
596
+ throw new TeamClosedError(`Team ${JSON.stringify(teamId)} is closed`);
597
+ }
598
+ const service = await TeamService.rebuild(this.depsBase(teamId, handle), record);
599
+ transferred = true;
600
+ this.track(service, handle);
601
+ return service;
602
+ }
603
+ finally {
604
+ if (!transferred)
605
+ await handle.release();
606
+ }
607
+ }
608
+ /**
609
+ * Register the one construction of this Team while it runs.
610
+ *
611
+ * It is removed as soon as it settles: the cache holds what it produced, and
612
+ * a construction that produced nothing leaves no trace to join.
613
+ */
614
+ publishConstruction(teamId, construction) {
615
+ const tracked = construction.finally(() => {
616
+ this.constructing.delete(teamId);
617
+ });
618
+ this.constructing.set(teamId, tracked);
619
+ // Whoever started it reports its failure; a construction nobody joined must
620
+ // not also surface as an unhandled rejection.
621
+ void tracked.catch(() => undefined);
622
+ }
623
+ /**
624
+ * Take ownership of one materialized Team: cache it and listen for its end.
625
+ *
626
+ * This collection is what holds a Team, so this is where it starts holding
627
+ * one: the factory hands back a finished service and the owner tracks it,
628
+ * rather than being called back into from inside the construction it asked
629
+ * for.
630
+ */
631
+ track(service, record) {
632
+ this.live.set(service.id, service);
633
+ // The exact instance that ended is the exact instance dropped; a Team
634
+ // rebuilt at the same id afterwards is a different object and stays.
635
+ void service.closed.then(async () => {
636
+ this.evict(service.id, service);
637
+ await record.release();
638
+ });
639
+ this.refuseIfClosing(service);
640
+ }
641
+ /**
642
+ * Stop a just-held Team and refuse to go on, when the dispatcher is already
643
+ * closing.
644
+ *
645
+ * `create`/`rebuild` cross the dispatcher fence before `close()` publishes its
646
+ * fence but finish constructing afterward, after the dispatcher's first
647
+ * sweep may already have passed. `stopForHost()` gives back runtime
648
+ * authority without closing anything, so a caller that went on would simply
649
+ * start the runtime again; throwing is what stops the continuation — for a
650
+ * created Team, before its initial turn starts the leader's runtime.
651
+ */
652
+ refuseIfClosing(service) {
653
+ if (!this.opts.fence.isClosing())
654
+ return;
655
+ service.stopForHost().catch(() => undefined);
656
+ throw new ServerShuttingDownError(`dispatcher '${this.dispatcherId}' is shutting down`);
657
+ }
658
+ evict(teamId, expectedService) {
659
+ if (this.live.get(teamId) !== expectedService)
660
+ return;
661
+ this.live.delete(teamId);
662
+ }
663
+ depsBase(teamId, record) {
664
+ return {
665
+ ...this.opts,
666
+ // Each Team gets its own already-resolved root; nothing below rebuilds it.
667
+ teamRoot: this.store.teamRoot(teamId),
668
+ // Bound to this Team's own id: the service can publish and merge its
669
+ // own record but can never address another Team's by id.
670
+ record,
671
+ };
672
+ }
673
+ // Store-only Team list/summary/history projection; never materializes a
674
+ // runtime.
675
+ async listRows() {
676
+ const out = [];
677
+ for (const team of await this.store.list()) {
678
+ out.push(await this.listRow(team));
679
+ }
680
+ return out;
681
+ }
682
+ async historyResult(input) {
683
+ const rows = [];
684
+ for (const team of await this.store.list()) {
685
+ const row = await this.historyRow(team);
686
+ if (matchesTeamHistoryQuery(row, input))
687
+ rows.push(row);
688
+ }
689
+ rows.sort((a, b) => b.updated_at - a.updated_at ||
690
+ b.created_at - a.created_at ||
691
+ a.team_name.localeCompare(b.team_name));
692
+ const start = input.cursor !== undefined ? decodeCursor(input.cursor) : 0;
693
+ const limit = clampHistoryLimit(input.limit);
694
+ const items = rows.slice(start, start + limit);
695
+ const next = start + items.length;
696
+ return {
697
+ items,
698
+ next_cursor: next < rows.length ? encodeCursor(next) : null,
699
+ };
700
+ }
701
+ /**
702
+ * One Team's status, read from its records alone.
703
+ *
704
+ * How a closed Team is reported: it has no runtime left to ask, and
705
+ * constructing one to answer a read would resurrect an entity that is over.
706
+ * The leader's runtime state is `null` because nothing is running, not
707
+ * because nothing is known.
708
+ */
709
+ async recordSummary(team) {
710
+ const leader = await this.leaderIdentity(team);
711
+ return teamSummary(team, leader === null ? null : toStatus(leader, null), await this.memberCount(team));
712
+ }
713
+ async listRow(team) {
714
+ return {
715
+ team_name: team.team_id,
716
+ status: team.status,
717
+ intent: team.intent,
718
+ source_repo: team.source_repo,
719
+ leader_name: team.leader_name,
720
+ leader_agent_runtime: team.leader_agent_runtime,
721
+ leader_state: await this.leaderState(team),
722
+ member_count: await this.memberCount(team),
723
+ created_at: team.created_at,
724
+ updated_at: team.updated_at,
725
+ closed_at: team.closed_at,
726
+ worktree_cleanup: team.worktree.cleanup_state,
727
+ };
728
+ }
729
+ async historyRow(team) {
730
+ return {
731
+ team_name: team.team_id,
732
+ status: team.status,
733
+ intent: team.intent,
734
+ source_repo: team.source_repo,
735
+ leader_name: team.leader_name,
736
+ leader_agent_runtime: team.leader_agent_runtime,
737
+ leader_state: await this.leaderState(team),
738
+ member_count: await this.memberCount(team),
739
+ created_at: team.created_at,
740
+ updated_at: team.updated_at,
741
+ closed_at: team.closed_at,
742
+ close_note: team.close_note,
743
+ close_note_preview: team.close_note === null ? null : previewText(team.close_note),
744
+ worktree_cleanup: team.worktree.cleanup_state,
745
+ };
746
+ }
747
+ /**
748
+ * The leader's durable status, read from this Team's root and accepted only
749
+ * when the record names the leader the Team record names. No probing: the
750
+ * leader has exactly one location and this is it.
751
+ *
752
+ * A Team this process already holds live answers from that live entity's
753
+ * own in-memory identity — the more current copy, and one that needs no
754
+ * file read — rather than from a second, independently-cached read over
755
+ * the same `identity.json` a live owner already committed through.
756
+ */
757
+ async leaderState(team) {
758
+ const live = this.live.get(team.team_id) ?? null;
759
+ if (live !== null)
760
+ return live.leaderIdentityStatus();
761
+ return (await this.leaderIdentity(team))?.status ?? null;
762
+ }
763
+ /**
764
+ * The store decides what an unreadable leader means, and this read accepts
765
+ * that decision unchanged: a missing or corrupt record is the `null` the
766
+ * store already logged, while a record this version refuses to interpret —
767
+ * old state — is raised. Catching here would turn "this file says something
768
+ * Dreamux no longer accepts" into "there is no leader", which is the one
769
+ * answer that is never true.
770
+ */
771
+ async leaderIdentity(team) {
772
+ const leader = await readAgentIdentity({
773
+ dir: this.store.teamRoot(team.team_id),
774
+ dispatcherId: this.dispatcherId,
775
+ expectedName: null,
776
+ log: this.opts.log,
777
+ });
778
+ return leader !== null && leader.name === team.leader_name ? leader : null;
779
+ }
780
+ /** Directory occupancy is the roster fact; an unreadable member still counts. */
781
+ async memberCount(team) {
782
+ return agentCollectionMemberCount(teamMateCollectionDir(this.store.teamRoot(team.team_id)));
783
+ }
784
+ }
785
+ function matchesTeamHistoryQuery(row, input) {
786
+ if (input.name !== undefined &&
787
+ row.team_name !== validateTeamId(input.name)) {
788
+ return false;
789
+ }
790
+ if (input.status !== undefined && row.status !== input.status)
791
+ return false;
792
+ if (input.repo !== undefined) {
793
+ const needle = input.repo.toLowerCase();
794
+ const hit = row.source_repo !== null &&
795
+ row.source_repo.toLowerCase().includes(needle);
796
+ if (!hit)
797
+ return false;
798
+ }
799
+ if (input.grep !== undefined && !teamRowMatchesText(row, input.grep)) {
800
+ return false;
801
+ }
802
+ if (input.since !== undefined && row.updated_at < input.since)
803
+ return false;
804
+ if (input.until !== undefined && row.updated_at > input.until)
805
+ return false;
806
+ return true;
807
+ }
808
+ function teamRowMatchesText(row, grep) {
809
+ return matchesGrepText([
810
+ row.team_name,
811
+ row.intent,
812
+ row.source_repo,
813
+ row.leader_name,
814
+ row.close_note,
815
+ ], grep);
816
+ }
817
+ //# sourceMappingURL=index.js.map