@namzu/sdk 0.1.8 → 0.2.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 (531) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  3. package/dist/agents/ReactiveAgent.js +5 -3
  4. package/dist/agents/ReactiveAgent.js.map +1 -1
  5. package/dist/agents/RouterAgent.d.ts.map +1 -1
  6. package/dist/agents/RouterAgent.js +3 -0
  7. package/dist/agents/RouterAgent.js.map +1 -1
  8. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  9. package/dist/agents/SupervisorAgent.js +18 -5
  10. package/dist/agents/SupervisorAgent.js.map +1 -1
  11. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  12. package/dist/bridge/a2a/mapper.js +6 -0
  13. package/dist/bridge/a2a/mapper.js.map +1 -1
  14. package/dist/bridge/a2a/task.d.ts +2 -2
  15. package/dist/bridge/a2a/task.d.ts.map +1 -1
  16. package/dist/bridge/a2a/task.js.map +1 -1
  17. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  18. package/dist/bridge/sse/mapper.js +6 -0
  19. package/dist/bridge/sse/mapper.js.map +1 -1
  20. package/dist/constants/a2a/index.d.ts +2 -2
  21. package/dist/constants/a2a/index.d.ts.map +1 -1
  22. package/dist/constants/a2a/index.js.map +1 -1
  23. package/dist/contracts/api.d.ts +22 -3
  24. package/dist/contracts/api.d.ts.map +1 -1
  25. package/dist/contracts/index.d.ts +3 -1
  26. package/dist/contracts/index.d.ts.map +1 -1
  27. package/dist/contracts/index.js.map +1 -1
  28. package/dist/gateway/local.d.ts.map +1 -1
  29. package/dist/gateway/local.js +6 -0
  30. package/dist/gateway/local.js.map +1 -1
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +4 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/manager/agent/__tests__/lifecycle.test.d.ts +2 -0
  36. package/dist/manager/agent/__tests__/lifecycle.test.d.ts.map +1 -0
  37. package/dist/manager/agent/__tests__/lifecycle.test.js +302 -0
  38. package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -0
  39. package/dist/manager/agent/lifecycle.d.ts +58 -3
  40. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  41. package/dist/manager/agent/lifecycle.js +311 -12
  42. package/dist/manager/agent/lifecycle.js.map +1 -1
  43. package/dist/manager/run/persistence.d.ts +8 -1
  44. package/dist/manager/run/persistence.d.ts.map +1 -1
  45. package/dist/manager/run/persistence.js +15 -0
  46. package/dist/manager/run/persistence.js.map +1 -1
  47. package/dist/run/reporter.d.ts.map +1 -1
  48. package/dist/run/reporter.js +25 -0
  49. package/dist/run/reporter.js.map +1 -1
  50. package/dist/runtime/query/__tests__/context.test.d.ts +2 -0
  51. package/dist/runtime/query/__tests__/context.test.d.ts.map +1 -0
  52. package/dist/runtime/query/__tests__/context.test.js +84 -0
  53. package/dist/runtime/query/__tests__/context.test.js.map +1 -0
  54. package/dist/runtime/query/context.d.ts +55 -2
  55. package/dist/runtime/query/context.d.ts.map +1 -1
  56. package/dist/runtime/query/context.js +48 -8
  57. package/dist/runtime/query/context.js.map +1 -1
  58. package/dist/runtime/query/events.d.ts.map +1 -1
  59. package/dist/runtime/query/events.js +8 -0
  60. package/dist/runtime/query/events.js.map +1 -1
  61. package/dist/runtime/query/index.d.ts +25 -2
  62. package/dist/runtime/query/index.d.ts.map +1 -1
  63. package/dist/runtime/query/index.js +11 -1
  64. package/dist/runtime/query/index.js.map +1 -1
  65. package/dist/session/__tests__/integration/_fixtures.d.ts +115 -0
  66. package/dist/session/__tests__/integration/_fixtures.d.ts.map +1 -0
  67. package/dist/session/__tests__/integration/_fixtures.js +198 -0
  68. package/dist/session/__tests__/integration/_fixtures.js.map +1 -0
  69. package/dist/session/__tests__/integration/capacity-caps.test.d.ts +13 -0
  70. package/dist/session/__tests__/integration/capacity-caps.test.d.ts.map +1 -0
  71. package/dist/session/__tests__/integration/capacity-caps.test.js +116 -0
  72. package/dist/session/__tests__/integration/capacity-caps.test.js.map +1 -0
  73. package/dist/session/__tests__/integration/e2e-spawn.test.d.ts +18 -0
  74. package/dist/session/__tests__/integration/e2e-spawn.test.d.ts.map +1 -0
  75. package/dist/session/__tests__/integration/e2e-spawn.test.js +226 -0
  76. package/dist/session/__tests__/integration/e2e-spawn.test.js.map +1 -0
  77. package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts +15 -0
  78. package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts.map +1 -0
  79. package/dist/session/__tests__/integration/event-stream-ordering.test.js +323 -0
  80. package/dist/session/__tests__/integration/event-stream-ordering.test.js.map +1 -0
  81. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts +12 -0
  82. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts.map +1 -0
  83. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js +170 -0
  84. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js.map +1 -0
  85. package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts +18 -0
  86. package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts.map +1 -0
  87. package/dist/session/__tests__/integration/handoff-illegal-transition.test.js +146 -0
  88. package/dist/session/__tests__/integration/handoff-illegal-transition.test.js.map +1 -0
  89. package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts +15 -0
  90. package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts.map +1 -0
  91. package/dist/session/__tests__/integration/handoff-single-e2e.test.js +163 -0
  92. package/dist/session/__tests__/integration/handoff-single-e2e.test.js.map +1 -0
  93. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts +12 -0
  94. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts.map +1 -0
  95. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js +157 -0
  96. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js.map +1 -0
  97. package/dist/session/__tests__/integration/migration-filesystem.test.d.ts +11 -0
  98. package/dist/session/__tests__/integration/migration-filesystem.test.d.ts.map +1 -0
  99. package/dist/session/__tests__/integration/migration-filesystem.test.js +140 -0
  100. package/dist/session/__tests__/integration/migration-filesystem.test.js.map +1 -0
  101. package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts +13 -0
  102. package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts.map +1 -0
  103. package/dist/session/__tests__/integration/migration-id-prefix.test.js +84 -0
  104. package/dist/session/__tests__/integration/migration-id-prefix.test.js.map +1 -0
  105. package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts +14 -0
  106. package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts.map +1 -0
  107. package/dist/session/__tests__/integration/prev-artifact-dag.test.js +241 -0
  108. package/dist/session/__tests__/integration/prev-artifact-dag.test.js.map +1 -0
  109. package/dist/session/__tests__/integration/retention-archive.test.d.ts +12 -0
  110. package/dist/session/__tests__/integration/retention-archive.test.d.ts.map +1 -0
  111. package/dist/session/__tests__/integration/retention-archive.test.js +186 -0
  112. package/dist/session/__tests__/integration/retention-archive.test.js.map +1 -0
  113. package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts +18 -0
  114. package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts.map +1 -0
  115. package/dist/session/__tests__/integration/summary-materialization-e2e.test.js +200 -0
  116. package/dist/session/__tests__/integration/summary-materialization-e2e.test.js.map +1 -0
  117. package/dist/session/__tests__/integration/tenant-isolation.test.d.ts +14 -0
  118. package/dist/session/__tests__/integration/tenant-isolation.test.d.ts.map +1 -0
  119. package/dist/session/__tests__/integration/tenant-isolation.test.js +180 -0
  120. package/dist/session/__tests__/integration/tenant-isolation.test.js.map +1 -0
  121. package/dist/session/errors.d.ts +60 -0
  122. package/dist/session/errors.d.ts.map +1 -0
  123. package/dist/session/errors.js +50 -0
  124. package/dist/session/errors.js.map +1 -0
  125. package/dist/session/events/index.d.ts +4 -0
  126. package/dist/session/events/index.d.ts.map +1 -0
  127. package/dist/session/events/index.js +8 -0
  128. package/dist/session/events/index.js.map +1 -0
  129. package/dist/session/events/schema-version.d.ts +13 -0
  130. package/dist/session/events/schema-version.d.ts.map +1 -0
  131. package/dist/session/events/schema-version.js +12 -0
  132. package/dist/session/events/schema-version.js.map +1 -0
  133. package/dist/session/events/types.d.ts +64 -0
  134. package/dist/session/events/types.d.ts.map +1 -0
  135. package/dist/session/events/types.js +2 -0
  136. package/dist/session/events/types.js.map +1 -0
  137. package/dist/session/handoff/__tests__/broadcast.test.d.ts +2 -0
  138. package/dist/session/handoff/__tests__/broadcast.test.d.ts.map +1 -0
  139. package/dist/session/handoff/__tests__/broadcast.test.js +243 -0
  140. package/dist/session/handoff/__tests__/broadcast.test.js.map +1 -0
  141. package/dist/session/handoff/__tests__/capacity.test.d.ts +2 -0
  142. package/dist/session/handoff/__tests__/capacity.test.d.ts.map +1 -0
  143. package/dist/session/handoff/__tests__/capacity.test.js +100 -0
  144. package/dist/session/handoff/__tests__/capacity.test.js.map +1 -0
  145. package/dist/session/handoff/__tests__/single.test.d.ts +2 -0
  146. package/dist/session/handoff/__tests__/single.test.d.ts.map +1 -0
  147. package/dist/session/handoff/__tests__/single.test.js +230 -0
  148. package/dist/session/handoff/__tests__/single.test.js.map +1 -0
  149. package/dist/session/handoff/assignment.d.ts +59 -0
  150. package/dist/session/handoff/assignment.d.ts.map +1 -0
  151. package/dist/session/handoff/assignment.js +11 -0
  152. package/dist/session/handoff/assignment.js.map +1 -0
  153. package/dist/session/handoff/broadcast.d.ts +47 -0
  154. package/dist/session/handoff/broadcast.d.ts.map +1 -0
  155. package/dist/session/handoff/broadcast.js +296 -0
  156. package/dist/session/handoff/broadcast.js.map +1 -0
  157. package/dist/session/handoff/capacity.d.ts +66 -0
  158. package/dist/session/handoff/capacity.d.ts.map +1 -0
  159. package/dist/session/handoff/capacity.js +60 -0
  160. package/dist/session/handoff/capacity.js.map +1 -0
  161. package/dist/session/handoff/events.d.ts +66 -0
  162. package/dist/session/handoff/events.d.ts.map +1 -0
  163. package/dist/session/handoff/events.js +13 -0
  164. package/dist/session/handoff/events.js.map +1 -0
  165. package/dist/session/handoff/index.d.ts +12 -0
  166. package/dist/session/handoff/index.d.ts.map +1 -0
  167. package/dist/session/handoff/index.js +9 -0
  168. package/dist/session/handoff/index.js.map +1 -0
  169. package/dist/session/handoff/single.d.ts +62 -0
  170. package/dist/session/handoff/single.d.ts.map +1 -0
  171. package/dist/session/handoff/single.js +217 -0
  172. package/dist/session/handoff/single.js.map +1 -0
  173. package/dist/session/handoff/version.d.ts +52 -0
  174. package/dist/session/handoff/version.d.ts.map +1 -0
  175. package/dist/session/handoff/version.js +36 -0
  176. package/dist/session/handoff/version.js.map +1 -0
  177. package/dist/session/hierarchy/__tests__/session.test.d.ts +2 -0
  178. package/dist/session/hierarchy/__tests__/session.test.d.ts.map +1 -0
  179. package/dist/session/hierarchy/__tests__/session.test.js +67 -0
  180. package/dist/session/hierarchy/__tests__/session.test.js.map +1 -0
  181. package/dist/session/hierarchy/actor.d.ts +26 -0
  182. package/dist/session/hierarchy/actor.d.ts.map +1 -0
  183. package/dist/session/hierarchy/actor.js +2 -0
  184. package/dist/session/hierarchy/actor.js.map +1 -0
  185. package/dist/session/hierarchy/index.d.ts +8 -0
  186. package/dist/session/hierarchy/index.d.ts.map +1 -0
  187. package/dist/session/hierarchy/index.js +4 -0
  188. package/dist/session/hierarchy/index.js.map +1 -0
  189. package/dist/session/hierarchy/lineage.d.ts +15 -0
  190. package/dist/session/hierarchy/lineage.d.ts.map +1 -0
  191. package/dist/session/hierarchy/lineage.js +2 -0
  192. package/dist/session/hierarchy/lineage.js.map +1 -0
  193. package/dist/session/hierarchy/project.d.ts +40 -0
  194. package/dist/session/hierarchy/project.d.ts.map +1 -0
  195. package/dist/session/hierarchy/project.js +2 -0
  196. package/dist/session/hierarchy/project.js.map +1 -0
  197. package/dist/session/hierarchy/session.d.ts +59 -0
  198. package/dist/session/hierarchy/session.d.ts.map +1 -0
  199. package/dist/session/hierarchy/session.js +51 -0
  200. package/dist/session/hierarchy/session.js.map +1 -0
  201. package/dist/session/hierarchy/sub-session.d.ts +76 -0
  202. package/dist/session/hierarchy/sub-session.d.ts.map +1 -0
  203. package/dist/session/hierarchy/sub-session.js +2 -0
  204. package/dist/session/hierarchy/sub-session.js.map +1 -0
  205. package/dist/session/hierarchy/tenant.d.ts +13 -0
  206. package/dist/session/hierarchy/tenant.d.ts.map +1 -0
  207. package/dist/session/hierarchy/tenant.js +2 -0
  208. package/dist/session/hierarchy/tenant.js.map +1 -0
  209. package/dist/session/index.d.ts +10 -0
  210. package/dist/session/index.d.ts.map +1 -0
  211. package/dist/session/index.js +15 -0
  212. package/dist/session/index.js.map +1 -0
  213. package/dist/session/intervention/__tests__/prev-artifact.test.d.ts +2 -0
  214. package/dist/session/intervention/__tests__/prev-artifact.test.d.ts.map +1 -0
  215. package/dist/session/intervention/__tests__/prev-artifact.test.js +179 -0
  216. package/dist/session/intervention/__tests__/prev-artifact.test.js.map +1 -0
  217. package/dist/session/intervention/index.d.ts +3 -0
  218. package/dist/session/intervention/index.d.ts.map +1 -0
  219. package/dist/session/intervention/index.js +8 -0
  220. package/dist/session/intervention/index.js.map +1 -0
  221. package/dist/session/intervention/prev-artifact.d.ts +103 -0
  222. package/dist/session/intervention/prev-artifact.d.ts.map +1 -0
  223. package/dist/session/intervention/prev-artifact.js +112 -0
  224. package/dist/session/intervention/prev-artifact.js.map +1 -0
  225. package/dist/session/migration/__tests__/filesystem.test.d.ts +2 -0
  226. package/dist/session/migration/__tests__/filesystem.test.d.ts.map +1 -0
  227. package/dist/session/migration/__tests__/filesystem.test.js +188 -0
  228. package/dist/session/migration/__tests__/filesystem.test.js.map +1 -0
  229. package/dist/session/migration/__tests__/id-prefix.test.d.ts +2 -0
  230. package/dist/session/migration/__tests__/id-prefix.test.d.ts.map +1 -0
  231. package/dist/session/migration/__tests__/id-prefix.test.js +83 -0
  232. package/dist/session/migration/__tests__/id-prefix.test.js.map +1 -0
  233. package/dist/session/migration/__tests__/marker.test.d.ts +2 -0
  234. package/dist/session/migration/__tests__/marker.test.d.ts.map +1 -0
  235. package/dist/session/migration/__tests__/marker.test.js +75 -0
  236. package/dist/session/migration/__tests__/marker.test.js.map +1 -0
  237. package/dist/session/migration/errors.d.ts +26 -0
  238. package/dist/session/migration/errors.d.ts.map +1 -0
  239. package/dist/session/migration/errors.js +22 -0
  240. package/dist/session/migration/errors.js.map +1 -0
  241. package/dist/session/migration/filesystem.d.ts +94 -0
  242. package/dist/session/migration/filesystem.d.ts.map +1 -0
  243. package/dist/session/migration/filesystem.js +319 -0
  244. package/dist/session/migration/filesystem.js.map +1 -0
  245. package/dist/session/migration/id-prefix.d.ts +98 -0
  246. package/dist/session/migration/id-prefix.d.ts.map +1 -0
  247. package/dist/session/migration/id-prefix.js +116 -0
  248. package/dist/session/migration/id-prefix.js.map +1 -0
  249. package/dist/session/migration/index.d.ts +8 -0
  250. package/dist/session/migration/index.d.ts.map +1 -0
  251. package/dist/session/migration/index.js +8 -0
  252. package/dist/session/migration/index.js.map +1 -0
  253. package/dist/session/migration/marker.d.ts +57 -0
  254. package/dist/session/migration/marker.d.ts.map +1 -0
  255. package/dist/session/migration/marker.js +111 -0
  256. package/dist/session/migration/marker.js.map +1 -0
  257. package/dist/session/retention/__tests__/archive.test.d.ts +2 -0
  258. package/dist/session/retention/__tests__/archive.test.d.ts.map +1 -0
  259. package/dist/session/retention/__tests__/archive.test.js +252 -0
  260. package/dist/session/retention/__tests__/archive.test.js.map +1 -0
  261. package/dist/session/retention/__tests__/disk-backend.test.d.ts +2 -0
  262. package/dist/session/retention/__tests__/disk-backend.test.d.ts.map +1 -0
  263. package/dist/session/retention/__tests__/disk-backend.test.js +154 -0
  264. package/dist/session/retention/__tests__/disk-backend.test.js.map +1 -0
  265. package/dist/session/retention/archive-backend-ref.d.ts +18 -0
  266. package/dist/session/retention/archive-backend-ref.d.ts.map +1 -0
  267. package/dist/session/retention/archive-backend-ref.js +2 -0
  268. package/dist/session/retention/archive-backend-ref.js.map +1 -0
  269. package/dist/session/retention/archive.d.ts +130 -0
  270. package/dist/session/retention/archive.d.ts.map +1 -0
  271. package/dist/session/retention/archive.js +203 -0
  272. package/dist/session/retention/archive.js.map +1 -0
  273. package/dist/session/retention/backend.d.ts +101 -0
  274. package/dist/session/retention/backend.d.ts.map +1 -0
  275. package/dist/session/retention/backend.js +15 -0
  276. package/dist/session/retention/backend.js.map +1 -0
  277. package/dist/session/retention/disk-backend.d.ts +59 -0
  278. package/dist/session/retention/disk-backend.d.ts.map +1 -0
  279. package/dist/session/retention/disk-backend.js +236 -0
  280. package/dist/session/retention/disk-backend.js.map +1 -0
  281. package/dist/session/retention/index.d.ts +9 -0
  282. package/dist/session/retention/index.d.ts.map +1 -0
  283. package/dist/session/retention/index.js +6 -0
  284. package/dist/session/retention/index.js.map +1 -0
  285. package/dist/session/retention/policy.d.ts +49 -0
  286. package/dist/session/retention/policy.d.ts.map +1 -0
  287. package/dist/session/retention/policy.js +21 -0
  288. package/dist/session/retention/policy.js.map +1 -0
  289. package/dist/session/summary/__tests__/materialize.test.d.ts +2 -0
  290. package/dist/session/summary/__tests__/materialize.test.d.ts.map +1 -0
  291. package/dist/session/summary/__tests__/materialize.test.js +269 -0
  292. package/dist/session/summary/__tests__/materialize.test.js.map +1 -0
  293. package/dist/session/summary/deliverable.d.ts +74 -0
  294. package/dist/session/summary/deliverable.d.ts.map +1 -0
  295. package/dist/session/summary/deliverable.js +20 -0
  296. package/dist/session/summary/deliverable.js.map +1 -0
  297. package/dist/session/summary/index.d.ts +6 -0
  298. package/dist/session/summary/index.d.ts.map +1 -0
  299. package/dist/session/summary/index.js +9 -0
  300. package/dist/session/summary/index.js.map +1 -0
  301. package/dist/session/summary/materialize.d.ts +82 -0
  302. package/dist/session/summary/materialize.d.ts.map +1 -0
  303. package/dist/session/summary/materialize.js +117 -0
  304. package/dist/session/summary/materialize.js.map +1 -0
  305. package/dist/session/summary/ref.d.ts +91 -0
  306. package/dist/session/summary/ref.d.ts.map +1 -0
  307. package/dist/session/summary/ref.js +51 -0
  308. package/dist/session/summary/ref.js.map +1 -0
  309. package/dist/session/workspace/__tests__/git-worktree.test.d.ts +2 -0
  310. package/dist/session/workspace/__tests__/git-worktree.test.d.ts.map +1 -0
  311. package/dist/session/workspace/__tests__/git-worktree.test.js +244 -0
  312. package/dist/session/workspace/__tests__/git-worktree.test.js.map +1 -0
  313. package/dist/session/workspace/__tests__/path-builder.test.d.ts +2 -0
  314. package/dist/session/workspace/__tests__/path-builder.test.d.ts.map +1 -0
  315. package/dist/session/workspace/__tests__/path-builder.test.js +37 -0
  316. package/dist/session/workspace/__tests__/path-builder.test.js.map +1 -0
  317. package/dist/session/workspace/driver.d.ts +55 -0
  318. package/dist/session/workspace/driver.d.ts.map +1 -0
  319. package/dist/session/workspace/driver.js +12 -0
  320. package/dist/session/workspace/driver.js.map +1 -0
  321. package/dist/session/workspace/git-worktree.d.ts +65 -0
  322. package/dist/session/workspace/git-worktree.d.ts.map +1 -0
  323. package/dist/session/workspace/git-worktree.js +156 -0
  324. package/dist/session/workspace/git-worktree.js.map +1 -0
  325. package/dist/session/workspace/index.d.ts +8 -0
  326. package/dist/session/workspace/index.d.ts.map +1 -0
  327. package/dist/session/workspace/index.js +7 -0
  328. package/dist/session/workspace/index.js.map +1 -0
  329. package/dist/session/workspace/path-builder.d.ts +50 -0
  330. package/dist/session/workspace/path-builder.d.ts.map +1 -0
  331. package/dist/session/workspace/path-builder.js +50 -0
  332. package/dist/session/workspace/path-builder.js.map +1 -0
  333. package/dist/session/workspace/ref.d.ts +46 -0
  334. package/dist/session/workspace/ref.d.ts.map +1 -0
  335. package/dist/session/workspace/ref.js +11 -0
  336. package/dist/session/workspace/ref.js.map +1 -0
  337. package/dist/session/workspace/registry.d.ts +26 -0
  338. package/dist/session/workspace/registry.d.ts.map +1 -0
  339. package/dist/session/workspace/registry.js +35 -0
  340. package/dist/session/workspace/registry.js.map +1 -0
  341. package/dist/store/conversation/memory.d.ts +22 -0
  342. package/dist/store/conversation/memory.d.ts.map +1 -1
  343. package/dist/store/conversation/memory.js +22 -0
  344. package/dist/store/conversation/memory.js.map +1 -1
  345. package/dist/store/session/__tests__/disk.test.d.ts +2 -0
  346. package/dist/store/session/__tests__/disk.test.d.ts.map +1 -0
  347. package/dist/store/session/__tests__/disk.test.js +240 -0
  348. package/dist/store/session/__tests__/disk.test.js.map +1 -0
  349. package/dist/store/session/__tests__/memory.test.d.ts +2 -0
  350. package/dist/store/session/__tests__/memory.test.d.ts.map +1 -0
  351. package/dist/store/session/__tests__/memory.test.js +217 -0
  352. package/dist/store/session/__tests__/memory.test.js.map +1 -0
  353. package/dist/store/session/disk.d.ts +85 -0
  354. package/dist/store/session/disk.d.ts.map +1 -0
  355. package/dist/store/session/disk.js +757 -0
  356. package/dist/store/session/disk.js.map +1 -0
  357. package/dist/store/session/index.d.ts +7 -0
  358. package/dist/store/session/index.d.ts.map +1 -0
  359. package/dist/store/session/index.js +11 -0
  360. package/dist/store/session/index.js.map +1 -0
  361. package/dist/store/session/linkage.d.ts +38 -0
  362. package/dist/store/session/linkage.d.ts.map +1 -0
  363. package/dist/store/session/linkage.js +64 -0
  364. package/dist/store/session/linkage.js.map +1 -0
  365. package/dist/store/session/memory.d.ts +48 -0
  366. package/dist/store/session/memory.d.ts.map +1 -0
  367. package/dist/store/session/memory.js +322 -0
  368. package/dist/store/session/memory.js.map +1 -0
  369. package/dist/store/session/messages.d.ts +20 -0
  370. package/dist/store/session/messages.d.ts.map +1 -0
  371. package/dist/store/session/messages.js +12 -0
  372. package/dist/store/session/messages.js.map +1 -0
  373. package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts +1 -1
  374. package/dist/types/agent/base.d.ts +28 -1
  375. package/dist/types/agent/base.d.ts.map +1 -1
  376. package/dist/types/agent/task.d.ts +50 -2
  377. package/dist/types/agent/task.d.ts.map +1 -1
  378. package/dist/types/agent/task.js.map +1 -1
  379. package/dist/types/conversation/index.d.ts +7 -0
  380. package/dist/types/conversation/index.d.ts.map +1 -1
  381. package/dist/types/ids/index.d.ts +26 -3
  382. package/dist/types/ids/index.d.ts.map +1 -1
  383. package/dist/types/ids/index.js +8 -1
  384. package/dist/types/ids/index.js.map +1 -1
  385. package/dist/types/invocation/__tests__/state.test.js +36 -29
  386. package/dist/types/invocation/__tests__/state.test.js.map +1 -1
  387. package/dist/types/invocation/index.d.ts +20 -4
  388. package/dist/types/invocation/index.d.ts.map +1 -1
  389. package/dist/types/invocation/index.js +10 -7
  390. package/dist/types/invocation/index.js.map +1 -1
  391. package/dist/types/run/config.d.ts +11 -1
  392. package/dist/types/run/config.d.ts.map +1 -1
  393. package/dist/types/run/events.d.ts +26 -1
  394. package/dist/types/run/events.d.ts.map +1 -1
  395. package/dist/types/run/index.d.ts.map +1 -1
  396. package/dist/types/run/index.js +8 -0
  397. package/dist/types/run/index.js.map +1 -1
  398. package/dist/types/run/metadata.d.ts +24 -1
  399. package/dist/types/run/metadata.d.ts.map +1 -1
  400. package/dist/types/run/status.d.ts +26 -0
  401. package/dist/types/run/status.d.ts.map +1 -0
  402. package/dist/types/run/status.js +2 -0
  403. package/dist/types/run/status.js.map +1 -0
  404. package/dist/types/session/ids.d.ts +18 -0
  405. package/dist/types/session/ids.d.ts.map +1 -0
  406. package/dist/types/session/ids.js +12 -0
  407. package/dist/types/session/ids.js.map +1 -0
  408. package/dist/types/session/index.d.ts +3 -0
  409. package/dist/types/session/index.d.ts.map +1 -0
  410. package/dist/types/session/index.js +5 -0
  411. package/dist/types/session/index.js.map +1 -0
  412. package/dist/types/session/store.d.ts +188 -0
  413. package/dist/types/session/store.d.ts.map +1 -0
  414. package/dist/types/session/store.js +14 -0
  415. package/dist/types/session/store.js.map +1 -0
  416. package/dist/utils/id.d.ts +18 -1
  417. package/dist/utils/id.d.ts.map +1 -1
  418. package/dist/utils/id.js +42 -4
  419. package/dist/utils/id.js.map +1 -1
  420. package/package.json +1 -1
  421. package/src/agents/ReactiveAgent.ts +7 -3
  422. package/src/agents/RouterAgent.ts +5 -0
  423. package/src/agents/SupervisorAgent.ts +26 -6
  424. package/src/bridge/a2a/mapper.ts +7 -0
  425. package/src/bridge/a2a/task.ts +2 -2
  426. package/src/bridge/sse/mapper.ts +8 -1
  427. package/src/constants/a2a/index.ts +2 -2
  428. package/src/contracts/api.ts +23 -3
  429. package/src/contracts/index.ts +2 -0
  430. package/src/gateway/local.ts +6 -0
  431. package/src/index.ts +14 -0
  432. package/src/manager/agent/__tests__/lifecycle.test.ts +452 -0
  433. package/src/manager/agent/lifecycle.ts +434 -19
  434. package/src/manager/run/persistence.ts +20 -1
  435. package/src/run/reporter.ts +28 -0
  436. package/src/runtime/query/__tests__/context.test.ts +101 -0
  437. package/src/runtime/query/context.ts +106 -10
  438. package/src/runtime/query/events.ts +8 -0
  439. package/src/runtime/query/index.ts +41 -3
  440. package/src/session/__tests__/integration/_fixtures.ts +282 -0
  441. package/src/session/__tests__/integration/capacity-caps.test.ts +164 -0
  442. package/src/session/__tests__/integration/e2e-spawn.test.ts +278 -0
  443. package/src/session/__tests__/integration/event-stream-ordering.test.ts +403 -0
  444. package/src/session/__tests__/integration/handoff-broadcast-e2e.test.ts +245 -0
  445. package/src/session/__tests__/integration/handoff-illegal-transition.test.ts +179 -0
  446. package/src/session/__tests__/integration/handoff-single-e2e.test.ts +220 -0
  447. package/src/session/__tests__/integration/hierarchy-lifecycle.test.ts +237 -0
  448. package/src/session/__tests__/integration/migration-filesystem.test.ts +209 -0
  449. package/src/session/__tests__/integration/migration-id-prefix.test.ts +101 -0
  450. package/src/session/__tests__/integration/prev-artifact-dag.test.ts +318 -0
  451. package/src/session/__tests__/integration/retention-archive.test.ts +231 -0
  452. package/src/session/__tests__/integration/summary-materialization-e2e.test.ts +237 -0
  453. package/src/session/__tests__/integration/tenant-isolation.test.ts +282 -0
  454. package/src/session/errors.ts +70 -0
  455. package/src/session/events/index.ts +16 -0
  456. package/src/session/events/schema-version.ts +13 -0
  457. package/src/session/events/types.ts +71 -0
  458. package/src/session/handoff/__tests__/broadcast.test.ts +350 -0
  459. package/src/session/handoff/__tests__/capacity.test.ts +123 -0
  460. package/src/session/handoff/__tests__/single.test.ts +316 -0
  461. package/src/session/handoff/assignment.ts +62 -0
  462. package/src/session/handoff/broadcast.ts +381 -0
  463. package/src/session/handoff/capacity.ts +121 -0
  464. package/src/session/handoff/events.ts +72 -0
  465. package/src/session/handoff/index.ts +29 -0
  466. package/src/session/handoff/single.ts +288 -0
  467. package/src/session/handoff/version.ts +59 -0
  468. package/src/session/hierarchy/__tests__/session.test.ts +92 -0
  469. package/src/session/hierarchy/actor.ts +17 -0
  470. package/src/session/hierarchy/index.ts +17 -0
  471. package/src/session/hierarchy/lineage.ts +15 -0
  472. package/src/session/hierarchy/project.ts +41 -0
  473. package/src/session/hierarchy/session.ts +97 -0
  474. package/src/session/hierarchy/sub-session.ts +92 -0
  475. package/src/session/hierarchy/tenant.ts +13 -0
  476. package/src/session/index.ts +15 -0
  477. package/src/session/intervention/__tests__/prev-artifact.test.ts +234 -0
  478. package/src/session/intervention/index.ts +16 -0
  479. package/src/session/intervention/prev-artifact.ts +180 -0
  480. package/src/session/migration/__tests__/filesystem.test.ts +263 -0
  481. package/src/session/migration/__tests__/id-prefix.test.ts +101 -0
  482. package/src/session/migration/__tests__/marker.test.ts +84 -0
  483. package/src/session/migration/errors.ts +23 -0
  484. package/src/session/migration/filesystem.ts +401 -0
  485. package/src/session/migration/id-prefix.ts +146 -0
  486. package/src/session/migration/index.ts +38 -0
  487. package/src/session/migration/marker.ts +131 -0
  488. package/src/session/retention/__tests__/archive.test.ts +316 -0
  489. package/src/session/retention/__tests__/disk-backend.test.ts +180 -0
  490. package/src/session/retention/archive-backend-ref.ts +17 -0
  491. package/src/session/retention/archive.ts +281 -0
  492. package/src/session/retention/backend.ts +107 -0
  493. package/src/session/retention/disk-backend.ts +304 -0
  494. package/src/session/retention/index.ts +16 -0
  495. package/src/session/retention/policy.ts +53 -0
  496. package/src/session/summary/__tests__/materialize.test.ts +341 -0
  497. package/src/session/summary/deliverable.ts +84 -0
  498. package/src/session/summary/index.ts +31 -0
  499. package/src/session/summary/materialize.ts +169 -0
  500. package/src/session/summary/ref.ts +104 -0
  501. package/src/session/workspace/__tests__/git-worktree.test.ts +258 -0
  502. package/src/session/workspace/__tests__/path-builder.test.ts +51 -0
  503. package/src/session/workspace/driver.ts +60 -0
  504. package/src/session/workspace/git-worktree.ts +209 -0
  505. package/src/session/workspace/index.ts +25 -0
  506. package/src/session/workspace/path-builder.ts +71 -0
  507. package/src/session/workspace/ref.ts +50 -0
  508. package/src/session/workspace/registry.ts +42 -0
  509. package/src/store/conversation/memory.ts +23 -0
  510. package/src/store/session/__tests__/disk.test.ts +346 -0
  511. package/src/store/session/__tests__/memory.test.ts +327 -0
  512. package/src/store/session/disk.ts +920 -0
  513. package/src/store/session/index.ts +14 -0
  514. package/src/store/session/linkage.ts +80 -0
  515. package/src/store/session/memory.ts +400 -0
  516. package/src/store/session/messages.ts +21 -0
  517. package/src/types/agent/base.ts +31 -1
  518. package/src/types/agent/task.ts +58 -2
  519. package/src/types/conversation/index.ts +7 -0
  520. package/src/types/ids/index.ts +41 -3
  521. package/src/types/invocation/__tests__/state.test.ts +37 -29
  522. package/src/types/invocation/index.ts +26 -10
  523. package/src/types/run/config.ts +12 -1
  524. package/src/types/run/events.ts +36 -1
  525. package/src/types/run/index.ts +8 -0
  526. package/src/types/run/metadata.ts +24 -1
  527. package/src/types/run/status.ts +33 -0
  528. package/src/types/session/ids.ts +34 -0
  529. package/src/types/session/index.ts +28 -0
  530. package/src/types/session/store.ts +229 -0
  531. package/src/utils/id.ts +55 -4
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Multi-recipient (broadcast) handoff — atomic fan-out with compensating
3
+ * rollback.
4
+ *
5
+ * See session-hierarchy.md §6.2 (Multi-recipient broadcast flow — atomic),
6
+ * §5.4 (broadcast source-session post-fan-out), §6.5 (capacity enforcement).
7
+ *
8
+ * **Atomicity contract** — one transaction boundary: either the full fan-out
9
+ * commits (source CAS-lock + N assignments + N sub-sessions + N worktrees),
10
+ * or none of it is externally observable. On any step failure the kernel
11
+ * executes a compensating rollback and emits `onBroadcastRollback` with
12
+ * accurate `partialState` counts. The rollback itself is idempotent per
13
+ * roadmap Risk #3.
14
+ *
15
+ * Commit / rollback sequence (pattern doc §6.2):
16
+ * ```
17
+ * COMMIT: idle → locked(CAS) → N assignments → N sub-sessions → N worktrees → awaiting_merge
18
+ * ROLLBACK: ( ← tear down partial worktrees ← delete partial sub-sessions ←
19
+ * delete partial assignments ← release CAS ) emit broadcast.rollback;
20
+ * source → idle
21
+ * ```
22
+ */
23
+
24
+ import type { SessionId, TenantId } from '../../types/ids/index.js'
25
+ import type { SubSessionId } from '../../types/session/ids.js'
26
+ import type { SessionStore } from '../../types/session/store.js'
27
+ import { TenantIsolationError } from '../errors.js'
28
+ import type { Session } from '../hierarchy/session.js'
29
+ import type { WorkspaceBackendDriver } from '../workspace/driver.js'
30
+ import type { WorkspaceRef } from '../workspace/ref.js'
31
+ import type { WorkspaceBackendRegistry } from '../workspace/registry.js'
32
+ import type { HandoffAssignment, HandoffOutcome } from './assignment.js'
33
+ import type { CapacityValidator } from './capacity.js'
34
+ import type { HandoffEventSink } from './events.js'
35
+ import { NOOP_RUN_STATUS_RESOLVER, type RunStatusResolver } from './single.js'
36
+ import { HandoffLockRejected, HandoffVersionConflict } from './version.js'
37
+
38
+ export interface BroadcastHandoffDeps {
39
+ store: SessionStore
40
+ workspaceRegistry: WorkspaceBackendRegistry
41
+ capacity: CapacityValidator
42
+ events: HandoffEventSink
43
+ runStatus?: RunStatusResolver
44
+ }
45
+
46
+ /**
47
+ * Per-recipient partial state tracked during fan-out so rollback can
48
+ * compensate exactly what was created. `subSessionId` is populated at the
49
+ * same step that creates the record so rollback can delete it in reverse
50
+ * order alongside the child session.
51
+ */
52
+ interface RecipientPartial {
53
+ assignmentId: HandoffAssignment['id']
54
+ recipientActor: HandoffAssignment['recipientActor']
55
+ workspace: WorkspaceRef | null
56
+ createdSessionId: SessionId | null
57
+ createdSubSessionId: SubSessionId | null
58
+ }
59
+
60
+ /**
61
+ * Executes a broadcast fan-out. All assignments MUST share the same
62
+ * `sourceSessionId`, `expectedOwnerVersion`, `broadcastId`, and `projectId`.
63
+ * Duplicate recipients (same actor kind + id) are rejected before any write.
64
+ *
65
+ * Returns one {@link HandoffOutcome} per recipient on success. On failure the
66
+ * source session is restored, every provisioned resource is torn down, and
67
+ * the underlying error propagates after `onBroadcastRollback` is emitted.
68
+ */
69
+ export async function executeBroadcastHandoff(
70
+ deps: BroadcastHandoffDeps,
71
+ assignments: readonly HandoffAssignment[],
72
+ tenantId: TenantId,
73
+ ): Promise<readonly HandoffOutcome[]> {
74
+ // 1. Shape validation -----------------------------------------------------
75
+
76
+ if (assignments.length === 0) {
77
+ throw new Error('executeBroadcastHandoff: assignments must not be empty')
78
+ }
79
+ if (assignments.length === 1) {
80
+ throw new Error(
81
+ 'executeBroadcastHandoff: single-recipient handoffs must use executeSingleHandoff',
82
+ )
83
+ }
84
+
85
+ const first = assignments[0]
86
+ if (!first) throw new Error('executeBroadcastHandoff: assignments[0] undefined')
87
+
88
+ if (first.mode !== 'broadcast') {
89
+ throw new Error(`executeBroadcastHandoff: mode must be 'broadcast' (got '${first.mode}')`)
90
+ }
91
+ if (!first.broadcastId) {
92
+ throw new Error('executeBroadcastHandoff: broadcastId required on every assignment')
93
+ }
94
+
95
+ for (const a of assignments) {
96
+ if (a.tenantId !== tenantId) {
97
+ throw new TenantIsolationError({
98
+ requested: tenantId,
99
+ resource: `handoff-assignment(${a.id})`,
100
+ })
101
+ }
102
+ if (a.sourceSessionId !== first.sourceSessionId) {
103
+ throw new Error('executeBroadcastHandoff: all assignments must share sourceSessionId')
104
+ }
105
+ if (a.broadcastId !== first.broadcastId) {
106
+ throw new Error('executeBroadcastHandoff: all assignments must share broadcastId')
107
+ }
108
+ if (a.expectedOwnerVersion !== first.expectedOwnerVersion) {
109
+ throw new Error('executeBroadcastHandoff: all assignments must share expectedOwnerVersion')
110
+ }
111
+ if (a.projectId !== first.projectId) {
112
+ throw new Error('executeBroadcastHandoff: all assignments must share projectId')
113
+ }
114
+ if (a.mode !== 'broadcast') {
115
+ throw new Error('executeBroadcastHandoff: every assignment must have mode="broadcast"')
116
+ }
117
+ }
118
+
119
+ // 2. Dedupe recipients — same concern as the collab doc's Codex R: two rows
120
+ // targeting the same actor would produce duplicate sub-sessions + racy
121
+ // worktree paths. Reject before any side effect.
122
+ const seen = new Set<string>()
123
+ for (const a of assignments) {
124
+ const key = recipientKey(a.recipientActor)
125
+ if (seen.has(key)) {
126
+ throw new Error(
127
+ `executeBroadcastHandoff: duplicate recipient detected (${key}). Dedupe before submitting.`,
128
+ )
129
+ }
130
+ seen.add(key)
131
+ }
132
+
133
+ // 3. Load source + tenant check.
134
+ const source = await deps.store.getSession(first.sourceSessionId, tenantId)
135
+ if (!source) {
136
+ throw new Error(`Source session ${first.sourceSessionId} not found`)
137
+ }
138
+ if (source.tenantId !== tenantId) {
139
+ throw new TenantIsolationError({
140
+ requested: tenantId,
141
+ resource: `session(${source.id})`,
142
+ })
143
+ }
144
+ if (source.projectId !== first.projectId) {
145
+ throw new Error(
146
+ `Assignment projectId ${first.projectId} does not match source projectId ${source.projectId}`,
147
+ )
148
+ }
149
+
150
+ // 4. Status check.
151
+ if (source.status !== 'idle') {
152
+ throw new HandoffLockRejected({
153
+ sessionId: source.id,
154
+ reason:
155
+ source.status === 'active'
156
+ ? 'active_run'
157
+ : source.status === 'awaiting_hitl'
158
+ ? 'pending_hitl'
159
+ : 'pending_subsession',
160
+ })
161
+ }
162
+
163
+ // 5. Non-terminal Run fan-in (§5.1).
164
+ const runResolver = deps.runStatus ?? NOOP_RUN_STATUS_RESOLVER
165
+ const blocking = await runResolver.blockingRun(source.id, tenantId)
166
+ if (blocking) {
167
+ throw new HandoffLockRejected({ sessionId: source.id, reason: blocking.reason })
168
+ }
169
+
170
+ // 6. Capacity — width + depth. Width covers N new children in one shot
171
+ // (§6.5); depth covers `source.depth + 1 ≤ maxDepth`.
172
+ const project = await deps.store.getProject(source.projectId, tenantId)
173
+ if (!project) {
174
+ throw new Error(`Project ${source.projectId} not found`)
175
+ }
176
+ await deps.capacity.validateWidth(
177
+ source.id,
178
+ assignments.length,
179
+ project.config.maxDelegationWidth,
180
+ tenantId,
181
+ )
182
+ await deps.capacity.validateDepth(source.id, project.config.maxDelegationDepth, tenantId)
183
+
184
+ // 7. CAS → source `idle → locked`.
185
+ if (source.ownerVersion !== first.expectedOwnerVersion) {
186
+ throw new HandoffVersionConflict({
187
+ sessionId: source.id,
188
+ expected: first.expectedOwnerVersion,
189
+ actual: source.ownerVersion,
190
+ })
191
+ }
192
+
193
+ const locked: Session = { ...source, status: 'locked' }
194
+ await deps.store.updateSession(locked, tenantId)
195
+ emit(deps.events.onLocked, { sessionId: source.id, at: new Date() })
196
+
197
+ // 8. Fan-out provisioning. Track per-recipient partial state so rollback
198
+ // can compensate precisely.
199
+ const partials: RecipientPartial[] = []
200
+ let assignmentsWritten = 0
201
+ let subsessionsCreated = 0
202
+ let worktreesProvisioned = 0
203
+
204
+ try {
205
+ const driver: WorkspaceBackendDriver = deps.workspaceRegistry.get('git-worktree')
206
+
207
+ for (const assignment of assignments) {
208
+ // Per-recipient: new isolated workspace → new child session → new
209
+ // sub-session edge. Each sub-op advances the partial-state counters.
210
+ const partial: RecipientPartial = {
211
+ assignmentId: assignment.id,
212
+ recipientActor: assignment.recipientActor,
213
+ workspace: null,
214
+ createdSessionId: null,
215
+ createdSubSessionId: null,
216
+ }
217
+ partials.push(partial)
218
+
219
+ partial.workspace = await driver.create({ label: `broadcast-${assignment.id}` })
220
+ worktreesProvisioned += 1
221
+
222
+ const childSession = await deps.store.createSession(
223
+ { projectId: source.projectId, currentActor: assignment.recipientActor },
224
+ tenantId,
225
+ )
226
+ partial.createdSessionId = childSession.id
227
+
228
+ const subSession = await deps.store.createSubSession(
229
+ {
230
+ parentSessionId: source.id,
231
+ childSessionId: childSession.id,
232
+ kind: 'user_handoff',
233
+ spawnedBy: assignment.sourceActor,
234
+ },
235
+ tenantId,
236
+ )
237
+ partial.createdSubSessionId = subSession.id
238
+ subsessionsCreated += 1
239
+
240
+ // "Assignments written" is the last per-recipient step — counts any
241
+ // recipient whose full row reached store-durable state.
242
+ assignmentsWritten += 1
243
+ }
244
+
245
+ // 9. Commit source: `locked → awaiting_merge` (§5.4 — broadcast source is
246
+ // not `idle` until all recipients terminalize; coordinator role).
247
+ const committed: Session = {
248
+ ...source,
249
+ status: 'awaiting_merge',
250
+ ownerVersion: source.ownerVersion + 1,
251
+ }
252
+ await deps.store.updateSession(committed, tenantId)
253
+
254
+ emit(deps.events.onCommitted, {
255
+ sessionId: source.id,
256
+ newVersion: committed.ownerVersion,
257
+ handoffIds: assignments.map((a) => a.id),
258
+ at: new Date(),
259
+ })
260
+
261
+ return partials.map<HandoffOutcome>((p) => {
262
+ if (!p.workspace || !p.createdSessionId) {
263
+ // Unreachable — success path populates both fields on every partial.
264
+ throw new Error(`Broadcast partial for ${p.assignmentId} missing post-commit fields`)
265
+ }
266
+ return {
267
+ assignmentId: p.assignmentId,
268
+ newSessionId: p.createdSessionId,
269
+ workspaceId: p.workspace.id,
270
+ committedOwnerVersion: committed.ownerVersion,
271
+ }
272
+ })
273
+ } catch (failure) {
274
+ await rollbackBroadcast(deps, source, partials, tenantId, {
275
+ broadcastId: first.broadcastId,
276
+ reason: failure instanceof Error ? failure.message : String(failure),
277
+ assignmentsWritten,
278
+ subsessionsCreated,
279
+ worktreesProvisioned,
280
+ })
281
+ throw failure
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Compensating rollback — idempotent per Risk #3.
287
+ *
288
+ * Order (reverse of commit): tear down worktrees → delete partially created
289
+ * sub-sessions → delete partially created child sessions → release source
290
+ * CAS lock → emit `onBroadcastRollback`. Every sub-op swallows its own
291
+ * secondary error so the primary failure remains the surfaced cause.
292
+ *
293
+ * Phase 8 closed the Phase 4 Known Delta (INTERP #2): previous implementation
294
+ * flipped partial recipient sessions to `status: 'archived'` as a stopgap
295
+ * because the store had no `deleteSession` primitive. The store now exposes
296
+ * `deleteSession` + `deleteSubSession` (idempotent), so rollback is total —
297
+ * no orphan records remain.
298
+ */
299
+ async function rollbackBroadcast(
300
+ deps: BroadcastHandoffDeps,
301
+ source: Session,
302
+ partials: readonly RecipientPartial[],
303
+ tenantId: TenantId,
304
+ meta: {
305
+ broadcastId: string
306
+ reason: string
307
+ assignmentsWritten: number
308
+ subsessionsCreated: number
309
+ worktreesProvisioned: number
310
+ },
311
+ ): Promise<void> {
312
+ // a. Dispose any provisioned worktrees (idempotent by driver contract).
313
+ for (const partial of partials) {
314
+ if (!partial.workspace) continue
315
+ try {
316
+ const driver = deps.workspaceRegistry.get('git-worktree')
317
+ await driver.dispose(partial.workspace)
318
+ } catch {
319
+ // Idempotent — secondary failure must not mask the primary one.
320
+ }
321
+ }
322
+
323
+ // b. Delete any partially created sub-sessions (reverse order of creation
324
+ // so child-side constraints release before the session below them).
325
+ for (const partial of partials) {
326
+ if (!partial.createdSubSessionId) continue
327
+ try {
328
+ await deps.store.deleteSubSession(partial.createdSubSessionId, tenantId)
329
+ } catch {
330
+ // Idempotent.
331
+ }
332
+ }
333
+
334
+ // c. Delete partially created recipient sessions (total cleanup — pattern
335
+ // doc §6.2: fan-out is atomic, partial state is externally
336
+ // unobservable).
337
+ for (const partial of partials) {
338
+ if (!partial.createdSessionId) continue
339
+ try {
340
+ await deps.store.deleteSession(partial.createdSessionId, tenantId)
341
+ } catch {
342
+ // Idempotent.
343
+ }
344
+ }
345
+
346
+ // d. Release source CAS — `locked → idle`, preserving ownerVersion.
347
+ try {
348
+ const reverted: Session = { ...source, status: 'idle' }
349
+ await deps.store.updateSession(reverted, tenantId)
350
+ } catch {
351
+ // Idempotent — the write-tmp-rename primitive is itself idempotent
352
+ // against the same payload.
353
+ }
354
+
355
+ emit(deps.events.onBroadcastRollback, {
356
+ sessionId: source.id,
357
+ broadcastId: meta.broadcastId,
358
+ reason: meta.reason,
359
+ partialState: {
360
+ assignmentsWritten: meta.assignmentsWritten,
361
+ subsessionsCreated: meta.subsessionsCreated,
362
+ worktreesProvisioned: meta.worktreesProvisioned,
363
+ },
364
+ at: new Date(),
365
+ })
366
+ }
367
+
368
+ function recipientKey(actor: HandoffAssignment['recipientActor']): string {
369
+ switch (actor.kind) {
370
+ case 'user':
371
+ return `user:${actor.userId}`
372
+ case 'agent':
373
+ return `agent:${actor.agentId}`
374
+ case 'system':
375
+ return `system:${actor.role}`
376
+ }
377
+ }
378
+
379
+ function emit<T>(handler: ((ev: T) => void) | undefined, event: T): void {
380
+ if (handler) handler(event)
381
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Delegation capacity validation — depth + width caps applied at the kernel
3
+ * boundary before any write (session-hierarchy.md §6.5).
4
+ *
5
+ * Both `spawnSubSession()` and `broadcastHandoff()` call this module before
6
+ * committing; violations abort with typed {@link DelegationCapacityExceeded}
7
+ * and no partial writes occur (Convention #0: no workarounds; Convention #5:
8
+ * deny-by-default).
9
+ */
10
+
11
+ import type { SessionId, TenantId } from '../../types/ids/index.js'
12
+ import type { SessionStore } from '../../types/session/store.js'
13
+
14
+ /** Capacity dimension under validation. */
15
+ export type CapacityDimension = 'depth' | 'width'
16
+
17
+ /**
18
+ * Raised when a spawn / broadcast would exceed the project's configured
19
+ * capacity caps. The check is a precondition — no partial writes occur on
20
+ * violation (session-hierarchy.md §6.5).
21
+ */
22
+ export class DelegationCapacityExceeded extends Error {
23
+ readonly details: {
24
+ dimension: CapacityDimension
25
+ current: number
26
+ limit: number
27
+ sessionId: SessionId
28
+ }
29
+
30
+ constructor(details: {
31
+ dimension: CapacityDimension
32
+ current: number
33
+ limit: number
34
+ sessionId: SessionId
35
+ }) {
36
+ super(
37
+ `Delegation capacity exceeded: ${details.dimension} ${details.current}/${details.limit} on ${details.sessionId}`,
38
+ )
39
+ this.name = 'DelegationCapacityExceeded'
40
+ this.details = details
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Capacity validator abstraction. Lets the handoff flows inject a validator
46
+ * without carrying the full {@link SessionStore} surface area into their
47
+ * dependency envelope (Convention #9: function-based flow keeps deps narrow).
48
+ *
49
+ * Both methods throw {@link DelegationCapacityExceeded} on violation and
50
+ * return void on success.
51
+ */
52
+ export interface CapacityValidator {
53
+ /**
54
+ * Asserts `parentSession.depth + 1 ≤ projectMaxDepth`. Depth is computed by
55
+ * walking the ancestry via {@link SessionStore.getAncestry} — root-to-self
56
+ * length minus one equals the session's depth in the delegation tree.
57
+ */
58
+ validateDepth(
59
+ parentSessionId: SessionId,
60
+ projectMaxDepth: number,
61
+ tenantId: TenantId,
62
+ ): Promise<void>
63
+
64
+ /**
65
+ * Asserts `existingDirectChildren + pendingNewChildren ≤ projectMaxWidth`
66
+ * under `parentSessionId`. The width cap applies to a single spawn call —
67
+ * a broadcast of N recipients passes `pendingNewChildren = N`.
68
+ */
69
+ validateWidth(
70
+ parentSessionId: SessionId,
71
+ pendingNewChildren: number,
72
+ projectMaxWidth: number,
73
+ tenantId: TenantId,
74
+ ): Promise<void>
75
+ }
76
+
77
+ /**
78
+ * Default validator backed by {@link SessionStore}. Uses `getAncestry` for
79
+ * depth (root-to-self chain length) and `getChildren` for width (count of
80
+ * existing direct sub-sessions).
81
+ */
82
+ export class DefaultCapacityValidator implements CapacityValidator {
83
+ constructor(private readonly store: SessionStore) {}
84
+
85
+ async validateDepth(
86
+ parentSessionId: SessionId,
87
+ projectMaxDepth: number,
88
+ tenantId: TenantId,
89
+ ): Promise<void> {
90
+ const ancestry = await this.store.getAncestry(parentSessionId, tenantId)
91
+ // depth is 0-indexed — root session has depth 0. New child depth =
92
+ // ancestry length (root-to-parent inclusive).
93
+ const newDepth = ancestry.length
94
+ if (newDepth > projectMaxDepth) {
95
+ throw new DelegationCapacityExceeded({
96
+ dimension: 'depth',
97
+ current: newDepth,
98
+ limit: projectMaxDepth,
99
+ sessionId: parentSessionId,
100
+ })
101
+ }
102
+ }
103
+
104
+ async validateWidth(
105
+ parentSessionId: SessionId,
106
+ pendingNewChildren: number,
107
+ projectMaxWidth: number,
108
+ tenantId: TenantId,
109
+ ): Promise<void> {
110
+ const existing = await this.store.getChildren(parentSessionId, tenantId)
111
+ const total = existing.length + pendingNewChildren
112
+ if (total > projectMaxWidth) {
113
+ throw new DelegationCapacityExceeded({
114
+ dimension: 'width',
115
+ current: total,
116
+ limit: projectMaxWidth,
117
+ sessionId: parentSessionId,
118
+ })
119
+ }
120
+ }
121
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * HandoffEventSink — pluggable event sink for Phase 4.
3
+ *
4
+ * Phase 4 does NOT extend `RunEvent` with handoff variants (they land in a
5
+ * later phase that wires the full `SessionHierarchyEvent` union referenced
6
+ * by roadmap §1). In the meantime the flow functions invoke this sink so
7
+ * consumers + tests can observe state transitions without a concrete event
8
+ * bus. Every callback is optional; {@link NOOP_HANDOFF_SINK} is the
9
+ * deny-by-default instance to pass when observation is not needed.
10
+ */
11
+
12
+ import type { SessionId } from '../../types/ids/index.js'
13
+ import type { HandoffId } from '../../types/session/ids.js'
14
+
15
+ /** Fired when a source session transitions `idle → locked` for handoff. */
16
+ export interface HandoffLockedEvent {
17
+ sessionId: SessionId
18
+ at: Date
19
+ }
20
+
21
+ /**
22
+ * Fired when a compensating rollback returns a source session
23
+ * `locked → idle` (either CAS failure or downstream provisioning failure).
24
+ */
25
+ export interface HandoffUnlockedEvent {
26
+ sessionId: SessionId
27
+ at: Date
28
+ }
29
+
30
+ /**
31
+ * Fired once per handoff commit. For `single` the `handoffIds` array has one
32
+ * entry; for `broadcast` it contains every recipient's assignment id.
33
+ */
34
+ export interface HandoffCommittedEvent {
35
+ sessionId: SessionId
36
+ newVersion: number
37
+ handoffIds: readonly HandoffId[]
38
+ at: Date
39
+ }
40
+
41
+ /**
42
+ * Fired when a broadcast fan-out mid-flight rolls back. `partialState` carries
43
+ * the per-stage counts of resources that were provisioned before the failure
44
+ * was detected — consumers can correlate with their own observability signals
45
+ * to verify the rollback is complete.
46
+ */
47
+ export interface HandoffBroadcastRollbackEvent {
48
+ sessionId: SessionId
49
+ broadcastId: string
50
+ reason: string
51
+ partialState: {
52
+ assignmentsWritten: number
53
+ subsessionsCreated: number
54
+ worktreesProvisioned: number
55
+ }
56
+ at: Date
57
+ }
58
+
59
+ /**
60
+ * Pluggable observer interface. All callbacks optional; consumers implement
61
+ * only what they need (Convention #5 deny-by-default applies to event
62
+ * consumers too — they must opt in explicitly).
63
+ */
64
+ export interface HandoffEventSink {
65
+ onLocked?(ev: HandoffLockedEvent): void
66
+ onUnlocked?(ev: HandoffUnlockedEvent): void
67
+ onCommitted?(ev: HandoffCommittedEvent): void
68
+ onBroadcastRollback?(ev: HandoffBroadcastRollbackEvent): void
69
+ }
70
+
71
+ /** No-op sink — inject when the caller does not need handoff observability. */
72
+ export const NOOP_HANDOFF_SINK: HandoffEventSink = Object.freeze({})
@@ -0,0 +1,29 @@
1
+ // Sub-barrel for the handoff state machine (Convention #4).
2
+ // Concrete flow functions + types live in sibling files; re-export the public
3
+ // surface here. See session-hierarchy.md §6.
4
+
5
+ export type { HandoffAssignment, HandoffMode, HandoffOutcome } from './assignment.js'
6
+
7
+ export {
8
+ DefaultCapacityValidator,
9
+ DelegationCapacityExceeded,
10
+ } from './capacity.js'
11
+ export type { CapacityDimension, CapacityValidator } from './capacity.js'
12
+
13
+ export { HandoffLockRejected, HandoffVersionConflict } from './version.js'
14
+ export type { HandoffLockRejectedReason } from './version.js'
15
+
16
+ export { NOOP_HANDOFF_SINK } from './events.js'
17
+ export type {
18
+ HandoffBroadcastRollbackEvent,
19
+ HandoffCommittedEvent,
20
+ HandoffEventSink,
21
+ HandoffLockedEvent,
22
+ HandoffUnlockedEvent,
23
+ } from './events.js'
24
+
25
+ export { executeSingleHandoff, NOOP_RUN_STATUS_RESOLVER } from './single.js'
26
+ export type { RunStatusResolver, SingleHandoffDeps } from './single.js'
27
+
28
+ export { executeBroadcastHandoff } from './broadcast.js'
29
+ export type { BroadcastHandoffDeps } from './broadcast.js'