@namzu/sdk 0.1.7 → 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 (532) hide show
  1. package/CHANGELOG.md +23 -5
  2. package/README.md +14 -9
  3. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  4. package/dist/agents/ReactiveAgent.js +5 -3
  5. package/dist/agents/ReactiveAgent.js.map +1 -1
  6. package/dist/agents/RouterAgent.d.ts.map +1 -1
  7. package/dist/agents/RouterAgent.js +3 -0
  8. package/dist/agents/RouterAgent.js.map +1 -1
  9. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  10. package/dist/agents/SupervisorAgent.js +18 -5
  11. package/dist/agents/SupervisorAgent.js.map +1 -1
  12. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  13. package/dist/bridge/a2a/mapper.js +6 -0
  14. package/dist/bridge/a2a/mapper.js.map +1 -1
  15. package/dist/bridge/a2a/task.d.ts +2 -2
  16. package/dist/bridge/a2a/task.d.ts.map +1 -1
  17. package/dist/bridge/a2a/task.js.map +1 -1
  18. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  19. package/dist/bridge/sse/mapper.js +6 -0
  20. package/dist/bridge/sse/mapper.js.map +1 -1
  21. package/dist/constants/a2a/index.d.ts +2 -2
  22. package/dist/constants/a2a/index.d.ts.map +1 -1
  23. package/dist/constants/a2a/index.js.map +1 -1
  24. package/dist/contracts/api.d.ts +22 -3
  25. package/dist/contracts/api.d.ts.map +1 -1
  26. package/dist/contracts/index.d.ts +3 -1
  27. package/dist/contracts/index.d.ts.map +1 -1
  28. package/dist/contracts/index.js.map +1 -1
  29. package/dist/gateway/local.d.ts.map +1 -1
  30. package/dist/gateway/local.js +6 -0
  31. package/dist/gateway/local.js.map +1 -1
  32. package/dist/index.d.ts +4 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +4 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/manager/agent/__tests__/lifecycle.test.d.ts +2 -0
  37. package/dist/manager/agent/__tests__/lifecycle.test.d.ts.map +1 -0
  38. package/dist/manager/agent/__tests__/lifecycle.test.js +302 -0
  39. package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -0
  40. package/dist/manager/agent/lifecycle.d.ts +58 -3
  41. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  42. package/dist/manager/agent/lifecycle.js +311 -12
  43. package/dist/manager/agent/lifecycle.js.map +1 -1
  44. package/dist/manager/run/persistence.d.ts +8 -1
  45. package/dist/manager/run/persistence.d.ts.map +1 -1
  46. package/dist/manager/run/persistence.js +15 -0
  47. package/dist/manager/run/persistence.js.map +1 -1
  48. package/dist/run/reporter.d.ts.map +1 -1
  49. package/dist/run/reporter.js +25 -0
  50. package/dist/run/reporter.js.map +1 -1
  51. package/dist/runtime/query/__tests__/context.test.d.ts +2 -0
  52. package/dist/runtime/query/__tests__/context.test.d.ts.map +1 -0
  53. package/dist/runtime/query/__tests__/context.test.js +84 -0
  54. package/dist/runtime/query/__tests__/context.test.js.map +1 -0
  55. package/dist/runtime/query/context.d.ts +55 -2
  56. package/dist/runtime/query/context.d.ts.map +1 -1
  57. package/dist/runtime/query/context.js +48 -8
  58. package/dist/runtime/query/context.js.map +1 -1
  59. package/dist/runtime/query/events.d.ts.map +1 -1
  60. package/dist/runtime/query/events.js +8 -0
  61. package/dist/runtime/query/events.js.map +1 -1
  62. package/dist/runtime/query/index.d.ts +25 -2
  63. package/dist/runtime/query/index.d.ts.map +1 -1
  64. package/dist/runtime/query/index.js +11 -1
  65. package/dist/runtime/query/index.js.map +1 -1
  66. package/dist/session/__tests__/integration/_fixtures.d.ts +115 -0
  67. package/dist/session/__tests__/integration/_fixtures.d.ts.map +1 -0
  68. package/dist/session/__tests__/integration/_fixtures.js +198 -0
  69. package/dist/session/__tests__/integration/_fixtures.js.map +1 -0
  70. package/dist/session/__tests__/integration/capacity-caps.test.d.ts +13 -0
  71. package/dist/session/__tests__/integration/capacity-caps.test.d.ts.map +1 -0
  72. package/dist/session/__tests__/integration/capacity-caps.test.js +116 -0
  73. package/dist/session/__tests__/integration/capacity-caps.test.js.map +1 -0
  74. package/dist/session/__tests__/integration/e2e-spawn.test.d.ts +18 -0
  75. package/dist/session/__tests__/integration/e2e-spawn.test.d.ts.map +1 -0
  76. package/dist/session/__tests__/integration/e2e-spawn.test.js +226 -0
  77. package/dist/session/__tests__/integration/e2e-spawn.test.js.map +1 -0
  78. package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts +15 -0
  79. package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts.map +1 -0
  80. package/dist/session/__tests__/integration/event-stream-ordering.test.js +323 -0
  81. package/dist/session/__tests__/integration/event-stream-ordering.test.js.map +1 -0
  82. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts +12 -0
  83. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts.map +1 -0
  84. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js +170 -0
  85. package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js.map +1 -0
  86. package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts +18 -0
  87. package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts.map +1 -0
  88. package/dist/session/__tests__/integration/handoff-illegal-transition.test.js +146 -0
  89. package/dist/session/__tests__/integration/handoff-illegal-transition.test.js.map +1 -0
  90. package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts +15 -0
  91. package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts.map +1 -0
  92. package/dist/session/__tests__/integration/handoff-single-e2e.test.js +163 -0
  93. package/dist/session/__tests__/integration/handoff-single-e2e.test.js.map +1 -0
  94. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts +12 -0
  95. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts.map +1 -0
  96. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js +157 -0
  97. package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js.map +1 -0
  98. package/dist/session/__tests__/integration/migration-filesystem.test.d.ts +11 -0
  99. package/dist/session/__tests__/integration/migration-filesystem.test.d.ts.map +1 -0
  100. package/dist/session/__tests__/integration/migration-filesystem.test.js +140 -0
  101. package/dist/session/__tests__/integration/migration-filesystem.test.js.map +1 -0
  102. package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts +13 -0
  103. package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts.map +1 -0
  104. package/dist/session/__tests__/integration/migration-id-prefix.test.js +84 -0
  105. package/dist/session/__tests__/integration/migration-id-prefix.test.js.map +1 -0
  106. package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts +14 -0
  107. package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts.map +1 -0
  108. package/dist/session/__tests__/integration/prev-artifact-dag.test.js +241 -0
  109. package/dist/session/__tests__/integration/prev-artifact-dag.test.js.map +1 -0
  110. package/dist/session/__tests__/integration/retention-archive.test.d.ts +12 -0
  111. package/dist/session/__tests__/integration/retention-archive.test.d.ts.map +1 -0
  112. package/dist/session/__tests__/integration/retention-archive.test.js +186 -0
  113. package/dist/session/__tests__/integration/retention-archive.test.js.map +1 -0
  114. package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts +18 -0
  115. package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts.map +1 -0
  116. package/dist/session/__tests__/integration/summary-materialization-e2e.test.js +200 -0
  117. package/dist/session/__tests__/integration/summary-materialization-e2e.test.js.map +1 -0
  118. package/dist/session/__tests__/integration/tenant-isolation.test.d.ts +14 -0
  119. package/dist/session/__tests__/integration/tenant-isolation.test.d.ts.map +1 -0
  120. package/dist/session/__tests__/integration/tenant-isolation.test.js +180 -0
  121. package/dist/session/__tests__/integration/tenant-isolation.test.js.map +1 -0
  122. package/dist/session/errors.d.ts +60 -0
  123. package/dist/session/errors.d.ts.map +1 -0
  124. package/dist/session/errors.js +50 -0
  125. package/dist/session/errors.js.map +1 -0
  126. package/dist/session/events/index.d.ts +4 -0
  127. package/dist/session/events/index.d.ts.map +1 -0
  128. package/dist/session/events/index.js +8 -0
  129. package/dist/session/events/index.js.map +1 -0
  130. package/dist/session/events/schema-version.d.ts +13 -0
  131. package/dist/session/events/schema-version.d.ts.map +1 -0
  132. package/dist/session/events/schema-version.js +12 -0
  133. package/dist/session/events/schema-version.js.map +1 -0
  134. package/dist/session/events/types.d.ts +64 -0
  135. package/dist/session/events/types.d.ts.map +1 -0
  136. package/dist/session/events/types.js +2 -0
  137. package/dist/session/events/types.js.map +1 -0
  138. package/dist/session/handoff/__tests__/broadcast.test.d.ts +2 -0
  139. package/dist/session/handoff/__tests__/broadcast.test.d.ts.map +1 -0
  140. package/dist/session/handoff/__tests__/broadcast.test.js +243 -0
  141. package/dist/session/handoff/__tests__/broadcast.test.js.map +1 -0
  142. package/dist/session/handoff/__tests__/capacity.test.d.ts +2 -0
  143. package/dist/session/handoff/__tests__/capacity.test.d.ts.map +1 -0
  144. package/dist/session/handoff/__tests__/capacity.test.js +100 -0
  145. package/dist/session/handoff/__tests__/capacity.test.js.map +1 -0
  146. package/dist/session/handoff/__tests__/single.test.d.ts +2 -0
  147. package/dist/session/handoff/__tests__/single.test.d.ts.map +1 -0
  148. package/dist/session/handoff/__tests__/single.test.js +230 -0
  149. package/dist/session/handoff/__tests__/single.test.js.map +1 -0
  150. package/dist/session/handoff/assignment.d.ts +59 -0
  151. package/dist/session/handoff/assignment.d.ts.map +1 -0
  152. package/dist/session/handoff/assignment.js +11 -0
  153. package/dist/session/handoff/assignment.js.map +1 -0
  154. package/dist/session/handoff/broadcast.d.ts +47 -0
  155. package/dist/session/handoff/broadcast.d.ts.map +1 -0
  156. package/dist/session/handoff/broadcast.js +296 -0
  157. package/dist/session/handoff/broadcast.js.map +1 -0
  158. package/dist/session/handoff/capacity.d.ts +66 -0
  159. package/dist/session/handoff/capacity.d.ts.map +1 -0
  160. package/dist/session/handoff/capacity.js +60 -0
  161. package/dist/session/handoff/capacity.js.map +1 -0
  162. package/dist/session/handoff/events.d.ts +66 -0
  163. package/dist/session/handoff/events.d.ts.map +1 -0
  164. package/dist/session/handoff/events.js +13 -0
  165. package/dist/session/handoff/events.js.map +1 -0
  166. package/dist/session/handoff/index.d.ts +12 -0
  167. package/dist/session/handoff/index.d.ts.map +1 -0
  168. package/dist/session/handoff/index.js +9 -0
  169. package/dist/session/handoff/index.js.map +1 -0
  170. package/dist/session/handoff/single.d.ts +62 -0
  171. package/dist/session/handoff/single.d.ts.map +1 -0
  172. package/dist/session/handoff/single.js +217 -0
  173. package/dist/session/handoff/single.js.map +1 -0
  174. package/dist/session/handoff/version.d.ts +52 -0
  175. package/dist/session/handoff/version.d.ts.map +1 -0
  176. package/dist/session/handoff/version.js +36 -0
  177. package/dist/session/handoff/version.js.map +1 -0
  178. package/dist/session/hierarchy/__tests__/session.test.d.ts +2 -0
  179. package/dist/session/hierarchy/__tests__/session.test.d.ts.map +1 -0
  180. package/dist/session/hierarchy/__tests__/session.test.js +67 -0
  181. package/dist/session/hierarchy/__tests__/session.test.js.map +1 -0
  182. package/dist/session/hierarchy/actor.d.ts +26 -0
  183. package/dist/session/hierarchy/actor.d.ts.map +1 -0
  184. package/dist/session/hierarchy/actor.js +2 -0
  185. package/dist/session/hierarchy/actor.js.map +1 -0
  186. package/dist/session/hierarchy/index.d.ts +8 -0
  187. package/dist/session/hierarchy/index.d.ts.map +1 -0
  188. package/dist/session/hierarchy/index.js +4 -0
  189. package/dist/session/hierarchy/index.js.map +1 -0
  190. package/dist/session/hierarchy/lineage.d.ts +15 -0
  191. package/dist/session/hierarchy/lineage.d.ts.map +1 -0
  192. package/dist/session/hierarchy/lineage.js +2 -0
  193. package/dist/session/hierarchy/lineage.js.map +1 -0
  194. package/dist/session/hierarchy/project.d.ts +40 -0
  195. package/dist/session/hierarchy/project.d.ts.map +1 -0
  196. package/dist/session/hierarchy/project.js +2 -0
  197. package/dist/session/hierarchy/project.js.map +1 -0
  198. package/dist/session/hierarchy/session.d.ts +59 -0
  199. package/dist/session/hierarchy/session.d.ts.map +1 -0
  200. package/dist/session/hierarchy/session.js +51 -0
  201. package/dist/session/hierarchy/session.js.map +1 -0
  202. package/dist/session/hierarchy/sub-session.d.ts +76 -0
  203. package/dist/session/hierarchy/sub-session.d.ts.map +1 -0
  204. package/dist/session/hierarchy/sub-session.js +2 -0
  205. package/dist/session/hierarchy/sub-session.js.map +1 -0
  206. package/dist/session/hierarchy/tenant.d.ts +13 -0
  207. package/dist/session/hierarchy/tenant.d.ts.map +1 -0
  208. package/dist/session/hierarchy/tenant.js +2 -0
  209. package/dist/session/hierarchy/tenant.js.map +1 -0
  210. package/dist/session/index.d.ts +10 -0
  211. package/dist/session/index.d.ts.map +1 -0
  212. package/dist/session/index.js +15 -0
  213. package/dist/session/index.js.map +1 -0
  214. package/dist/session/intervention/__tests__/prev-artifact.test.d.ts +2 -0
  215. package/dist/session/intervention/__tests__/prev-artifact.test.d.ts.map +1 -0
  216. package/dist/session/intervention/__tests__/prev-artifact.test.js +179 -0
  217. package/dist/session/intervention/__tests__/prev-artifact.test.js.map +1 -0
  218. package/dist/session/intervention/index.d.ts +3 -0
  219. package/dist/session/intervention/index.d.ts.map +1 -0
  220. package/dist/session/intervention/index.js +8 -0
  221. package/dist/session/intervention/index.js.map +1 -0
  222. package/dist/session/intervention/prev-artifact.d.ts +103 -0
  223. package/dist/session/intervention/prev-artifact.d.ts.map +1 -0
  224. package/dist/session/intervention/prev-artifact.js +112 -0
  225. package/dist/session/intervention/prev-artifact.js.map +1 -0
  226. package/dist/session/migration/__tests__/filesystem.test.d.ts +2 -0
  227. package/dist/session/migration/__tests__/filesystem.test.d.ts.map +1 -0
  228. package/dist/session/migration/__tests__/filesystem.test.js +188 -0
  229. package/dist/session/migration/__tests__/filesystem.test.js.map +1 -0
  230. package/dist/session/migration/__tests__/id-prefix.test.d.ts +2 -0
  231. package/dist/session/migration/__tests__/id-prefix.test.d.ts.map +1 -0
  232. package/dist/session/migration/__tests__/id-prefix.test.js +83 -0
  233. package/dist/session/migration/__tests__/id-prefix.test.js.map +1 -0
  234. package/dist/session/migration/__tests__/marker.test.d.ts +2 -0
  235. package/dist/session/migration/__tests__/marker.test.d.ts.map +1 -0
  236. package/dist/session/migration/__tests__/marker.test.js +75 -0
  237. package/dist/session/migration/__tests__/marker.test.js.map +1 -0
  238. package/dist/session/migration/errors.d.ts +26 -0
  239. package/dist/session/migration/errors.d.ts.map +1 -0
  240. package/dist/session/migration/errors.js +22 -0
  241. package/dist/session/migration/errors.js.map +1 -0
  242. package/dist/session/migration/filesystem.d.ts +94 -0
  243. package/dist/session/migration/filesystem.d.ts.map +1 -0
  244. package/dist/session/migration/filesystem.js +319 -0
  245. package/dist/session/migration/filesystem.js.map +1 -0
  246. package/dist/session/migration/id-prefix.d.ts +98 -0
  247. package/dist/session/migration/id-prefix.d.ts.map +1 -0
  248. package/dist/session/migration/id-prefix.js +116 -0
  249. package/dist/session/migration/id-prefix.js.map +1 -0
  250. package/dist/session/migration/index.d.ts +8 -0
  251. package/dist/session/migration/index.d.ts.map +1 -0
  252. package/dist/session/migration/index.js +8 -0
  253. package/dist/session/migration/index.js.map +1 -0
  254. package/dist/session/migration/marker.d.ts +57 -0
  255. package/dist/session/migration/marker.d.ts.map +1 -0
  256. package/dist/session/migration/marker.js +111 -0
  257. package/dist/session/migration/marker.js.map +1 -0
  258. package/dist/session/retention/__tests__/archive.test.d.ts +2 -0
  259. package/dist/session/retention/__tests__/archive.test.d.ts.map +1 -0
  260. package/dist/session/retention/__tests__/archive.test.js +252 -0
  261. package/dist/session/retention/__tests__/archive.test.js.map +1 -0
  262. package/dist/session/retention/__tests__/disk-backend.test.d.ts +2 -0
  263. package/dist/session/retention/__tests__/disk-backend.test.d.ts.map +1 -0
  264. package/dist/session/retention/__tests__/disk-backend.test.js +154 -0
  265. package/dist/session/retention/__tests__/disk-backend.test.js.map +1 -0
  266. package/dist/session/retention/archive-backend-ref.d.ts +18 -0
  267. package/dist/session/retention/archive-backend-ref.d.ts.map +1 -0
  268. package/dist/session/retention/archive-backend-ref.js +2 -0
  269. package/dist/session/retention/archive-backend-ref.js.map +1 -0
  270. package/dist/session/retention/archive.d.ts +130 -0
  271. package/dist/session/retention/archive.d.ts.map +1 -0
  272. package/dist/session/retention/archive.js +203 -0
  273. package/dist/session/retention/archive.js.map +1 -0
  274. package/dist/session/retention/backend.d.ts +101 -0
  275. package/dist/session/retention/backend.d.ts.map +1 -0
  276. package/dist/session/retention/backend.js +15 -0
  277. package/dist/session/retention/backend.js.map +1 -0
  278. package/dist/session/retention/disk-backend.d.ts +59 -0
  279. package/dist/session/retention/disk-backend.d.ts.map +1 -0
  280. package/dist/session/retention/disk-backend.js +236 -0
  281. package/dist/session/retention/disk-backend.js.map +1 -0
  282. package/dist/session/retention/index.d.ts +9 -0
  283. package/dist/session/retention/index.d.ts.map +1 -0
  284. package/dist/session/retention/index.js +6 -0
  285. package/dist/session/retention/index.js.map +1 -0
  286. package/dist/session/retention/policy.d.ts +49 -0
  287. package/dist/session/retention/policy.d.ts.map +1 -0
  288. package/dist/session/retention/policy.js +21 -0
  289. package/dist/session/retention/policy.js.map +1 -0
  290. package/dist/session/summary/__tests__/materialize.test.d.ts +2 -0
  291. package/dist/session/summary/__tests__/materialize.test.d.ts.map +1 -0
  292. package/dist/session/summary/__tests__/materialize.test.js +269 -0
  293. package/dist/session/summary/__tests__/materialize.test.js.map +1 -0
  294. package/dist/session/summary/deliverable.d.ts +74 -0
  295. package/dist/session/summary/deliverable.d.ts.map +1 -0
  296. package/dist/session/summary/deliverable.js +20 -0
  297. package/dist/session/summary/deliverable.js.map +1 -0
  298. package/dist/session/summary/index.d.ts +6 -0
  299. package/dist/session/summary/index.d.ts.map +1 -0
  300. package/dist/session/summary/index.js +9 -0
  301. package/dist/session/summary/index.js.map +1 -0
  302. package/dist/session/summary/materialize.d.ts +82 -0
  303. package/dist/session/summary/materialize.d.ts.map +1 -0
  304. package/dist/session/summary/materialize.js +117 -0
  305. package/dist/session/summary/materialize.js.map +1 -0
  306. package/dist/session/summary/ref.d.ts +91 -0
  307. package/dist/session/summary/ref.d.ts.map +1 -0
  308. package/dist/session/summary/ref.js +51 -0
  309. package/dist/session/summary/ref.js.map +1 -0
  310. package/dist/session/workspace/__tests__/git-worktree.test.d.ts +2 -0
  311. package/dist/session/workspace/__tests__/git-worktree.test.d.ts.map +1 -0
  312. package/dist/session/workspace/__tests__/git-worktree.test.js +244 -0
  313. package/dist/session/workspace/__tests__/git-worktree.test.js.map +1 -0
  314. package/dist/session/workspace/__tests__/path-builder.test.d.ts +2 -0
  315. package/dist/session/workspace/__tests__/path-builder.test.d.ts.map +1 -0
  316. package/dist/session/workspace/__tests__/path-builder.test.js +37 -0
  317. package/dist/session/workspace/__tests__/path-builder.test.js.map +1 -0
  318. package/dist/session/workspace/driver.d.ts +55 -0
  319. package/dist/session/workspace/driver.d.ts.map +1 -0
  320. package/dist/session/workspace/driver.js +12 -0
  321. package/dist/session/workspace/driver.js.map +1 -0
  322. package/dist/session/workspace/git-worktree.d.ts +65 -0
  323. package/dist/session/workspace/git-worktree.d.ts.map +1 -0
  324. package/dist/session/workspace/git-worktree.js +156 -0
  325. package/dist/session/workspace/git-worktree.js.map +1 -0
  326. package/dist/session/workspace/index.d.ts +8 -0
  327. package/dist/session/workspace/index.d.ts.map +1 -0
  328. package/dist/session/workspace/index.js +7 -0
  329. package/dist/session/workspace/index.js.map +1 -0
  330. package/dist/session/workspace/path-builder.d.ts +50 -0
  331. package/dist/session/workspace/path-builder.d.ts.map +1 -0
  332. package/dist/session/workspace/path-builder.js +50 -0
  333. package/dist/session/workspace/path-builder.js.map +1 -0
  334. package/dist/session/workspace/ref.d.ts +46 -0
  335. package/dist/session/workspace/ref.d.ts.map +1 -0
  336. package/dist/session/workspace/ref.js +11 -0
  337. package/dist/session/workspace/ref.js.map +1 -0
  338. package/dist/session/workspace/registry.d.ts +26 -0
  339. package/dist/session/workspace/registry.d.ts.map +1 -0
  340. package/dist/session/workspace/registry.js +35 -0
  341. package/dist/session/workspace/registry.js.map +1 -0
  342. package/dist/store/conversation/memory.d.ts +22 -0
  343. package/dist/store/conversation/memory.d.ts.map +1 -1
  344. package/dist/store/conversation/memory.js +22 -0
  345. package/dist/store/conversation/memory.js.map +1 -1
  346. package/dist/store/session/__tests__/disk.test.d.ts +2 -0
  347. package/dist/store/session/__tests__/disk.test.d.ts.map +1 -0
  348. package/dist/store/session/__tests__/disk.test.js +240 -0
  349. package/dist/store/session/__tests__/disk.test.js.map +1 -0
  350. package/dist/store/session/__tests__/memory.test.d.ts +2 -0
  351. package/dist/store/session/__tests__/memory.test.d.ts.map +1 -0
  352. package/dist/store/session/__tests__/memory.test.js +217 -0
  353. package/dist/store/session/__tests__/memory.test.js.map +1 -0
  354. package/dist/store/session/disk.d.ts +85 -0
  355. package/dist/store/session/disk.d.ts.map +1 -0
  356. package/dist/store/session/disk.js +757 -0
  357. package/dist/store/session/disk.js.map +1 -0
  358. package/dist/store/session/index.d.ts +7 -0
  359. package/dist/store/session/index.d.ts.map +1 -0
  360. package/dist/store/session/index.js +11 -0
  361. package/dist/store/session/index.js.map +1 -0
  362. package/dist/store/session/linkage.d.ts +38 -0
  363. package/dist/store/session/linkage.d.ts.map +1 -0
  364. package/dist/store/session/linkage.js +64 -0
  365. package/dist/store/session/linkage.js.map +1 -0
  366. package/dist/store/session/memory.d.ts +48 -0
  367. package/dist/store/session/memory.d.ts.map +1 -0
  368. package/dist/store/session/memory.js +322 -0
  369. package/dist/store/session/memory.js.map +1 -0
  370. package/dist/store/session/messages.d.ts +20 -0
  371. package/dist/store/session/messages.d.ts.map +1 -0
  372. package/dist/store/session/messages.js +12 -0
  373. package/dist/store/session/messages.js.map +1 -0
  374. package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts +1 -1
  375. package/dist/types/agent/base.d.ts +28 -1
  376. package/dist/types/agent/base.d.ts.map +1 -1
  377. package/dist/types/agent/task.d.ts +50 -2
  378. package/dist/types/agent/task.d.ts.map +1 -1
  379. package/dist/types/agent/task.js.map +1 -1
  380. package/dist/types/conversation/index.d.ts +7 -0
  381. package/dist/types/conversation/index.d.ts.map +1 -1
  382. package/dist/types/ids/index.d.ts +26 -3
  383. package/dist/types/ids/index.d.ts.map +1 -1
  384. package/dist/types/ids/index.js +8 -1
  385. package/dist/types/ids/index.js.map +1 -1
  386. package/dist/types/invocation/__tests__/state.test.js +36 -29
  387. package/dist/types/invocation/__tests__/state.test.js.map +1 -1
  388. package/dist/types/invocation/index.d.ts +20 -4
  389. package/dist/types/invocation/index.d.ts.map +1 -1
  390. package/dist/types/invocation/index.js +10 -7
  391. package/dist/types/invocation/index.js.map +1 -1
  392. package/dist/types/run/config.d.ts +11 -1
  393. package/dist/types/run/config.d.ts.map +1 -1
  394. package/dist/types/run/events.d.ts +26 -1
  395. package/dist/types/run/events.d.ts.map +1 -1
  396. package/dist/types/run/index.d.ts.map +1 -1
  397. package/dist/types/run/index.js +8 -0
  398. package/dist/types/run/index.js.map +1 -1
  399. package/dist/types/run/metadata.d.ts +24 -1
  400. package/dist/types/run/metadata.d.ts.map +1 -1
  401. package/dist/types/run/status.d.ts +26 -0
  402. package/dist/types/run/status.d.ts.map +1 -0
  403. package/dist/types/run/status.js +2 -0
  404. package/dist/types/run/status.js.map +1 -0
  405. package/dist/types/session/ids.d.ts +18 -0
  406. package/dist/types/session/ids.d.ts.map +1 -0
  407. package/dist/types/session/ids.js +12 -0
  408. package/dist/types/session/ids.js.map +1 -0
  409. package/dist/types/session/index.d.ts +3 -0
  410. package/dist/types/session/index.d.ts.map +1 -0
  411. package/dist/types/session/index.js +5 -0
  412. package/dist/types/session/index.js.map +1 -0
  413. package/dist/types/session/store.d.ts +188 -0
  414. package/dist/types/session/store.d.ts.map +1 -0
  415. package/dist/types/session/store.js +14 -0
  416. package/dist/types/session/store.js.map +1 -0
  417. package/dist/utils/id.d.ts +18 -1
  418. package/dist/utils/id.d.ts.map +1 -1
  419. package/dist/utils/id.js +42 -4
  420. package/dist/utils/id.js.map +1 -1
  421. package/package.json +1 -1
  422. package/src/agents/ReactiveAgent.ts +7 -3
  423. package/src/agents/RouterAgent.ts +5 -0
  424. package/src/agents/SupervisorAgent.ts +26 -6
  425. package/src/bridge/a2a/mapper.ts +7 -0
  426. package/src/bridge/a2a/task.ts +2 -2
  427. package/src/bridge/sse/mapper.ts +8 -1
  428. package/src/constants/a2a/index.ts +2 -2
  429. package/src/contracts/api.ts +23 -3
  430. package/src/contracts/index.ts +2 -0
  431. package/src/gateway/local.ts +6 -0
  432. package/src/index.ts +14 -0
  433. package/src/manager/agent/__tests__/lifecycle.test.ts +452 -0
  434. package/src/manager/agent/lifecycle.ts +434 -19
  435. package/src/manager/run/persistence.ts +20 -1
  436. package/src/run/reporter.ts +28 -0
  437. package/src/runtime/query/__tests__/context.test.ts +101 -0
  438. package/src/runtime/query/context.ts +106 -10
  439. package/src/runtime/query/events.ts +8 -0
  440. package/src/runtime/query/index.ts +41 -3
  441. package/src/session/__tests__/integration/_fixtures.ts +282 -0
  442. package/src/session/__tests__/integration/capacity-caps.test.ts +164 -0
  443. package/src/session/__tests__/integration/e2e-spawn.test.ts +278 -0
  444. package/src/session/__tests__/integration/event-stream-ordering.test.ts +403 -0
  445. package/src/session/__tests__/integration/handoff-broadcast-e2e.test.ts +245 -0
  446. package/src/session/__tests__/integration/handoff-illegal-transition.test.ts +179 -0
  447. package/src/session/__tests__/integration/handoff-single-e2e.test.ts +220 -0
  448. package/src/session/__tests__/integration/hierarchy-lifecycle.test.ts +237 -0
  449. package/src/session/__tests__/integration/migration-filesystem.test.ts +209 -0
  450. package/src/session/__tests__/integration/migration-id-prefix.test.ts +101 -0
  451. package/src/session/__tests__/integration/prev-artifact-dag.test.ts +318 -0
  452. package/src/session/__tests__/integration/retention-archive.test.ts +231 -0
  453. package/src/session/__tests__/integration/summary-materialization-e2e.test.ts +237 -0
  454. package/src/session/__tests__/integration/tenant-isolation.test.ts +282 -0
  455. package/src/session/errors.ts +70 -0
  456. package/src/session/events/index.ts +16 -0
  457. package/src/session/events/schema-version.ts +13 -0
  458. package/src/session/events/types.ts +71 -0
  459. package/src/session/handoff/__tests__/broadcast.test.ts +350 -0
  460. package/src/session/handoff/__tests__/capacity.test.ts +123 -0
  461. package/src/session/handoff/__tests__/single.test.ts +316 -0
  462. package/src/session/handoff/assignment.ts +62 -0
  463. package/src/session/handoff/broadcast.ts +381 -0
  464. package/src/session/handoff/capacity.ts +121 -0
  465. package/src/session/handoff/events.ts +72 -0
  466. package/src/session/handoff/index.ts +29 -0
  467. package/src/session/handoff/single.ts +288 -0
  468. package/src/session/handoff/version.ts +59 -0
  469. package/src/session/hierarchy/__tests__/session.test.ts +92 -0
  470. package/src/session/hierarchy/actor.ts +17 -0
  471. package/src/session/hierarchy/index.ts +17 -0
  472. package/src/session/hierarchy/lineage.ts +15 -0
  473. package/src/session/hierarchy/project.ts +41 -0
  474. package/src/session/hierarchy/session.ts +97 -0
  475. package/src/session/hierarchy/sub-session.ts +92 -0
  476. package/src/session/hierarchy/tenant.ts +13 -0
  477. package/src/session/index.ts +15 -0
  478. package/src/session/intervention/__tests__/prev-artifact.test.ts +234 -0
  479. package/src/session/intervention/index.ts +16 -0
  480. package/src/session/intervention/prev-artifact.ts +180 -0
  481. package/src/session/migration/__tests__/filesystem.test.ts +263 -0
  482. package/src/session/migration/__tests__/id-prefix.test.ts +101 -0
  483. package/src/session/migration/__tests__/marker.test.ts +84 -0
  484. package/src/session/migration/errors.ts +23 -0
  485. package/src/session/migration/filesystem.ts +401 -0
  486. package/src/session/migration/id-prefix.ts +146 -0
  487. package/src/session/migration/index.ts +38 -0
  488. package/src/session/migration/marker.ts +131 -0
  489. package/src/session/retention/__tests__/archive.test.ts +316 -0
  490. package/src/session/retention/__tests__/disk-backend.test.ts +180 -0
  491. package/src/session/retention/archive-backend-ref.ts +17 -0
  492. package/src/session/retention/archive.ts +281 -0
  493. package/src/session/retention/backend.ts +107 -0
  494. package/src/session/retention/disk-backend.ts +304 -0
  495. package/src/session/retention/index.ts +16 -0
  496. package/src/session/retention/policy.ts +53 -0
  497. package/src/session/summary/__tests__/materialize.test.ts +341 -0
  498. package/src/session/summary/deliverable.ts +84 -0
  499. package/src/session/summary/index.ts +31 -0
  500. package/src/session/summary/materialize.ts +169 -0
  501. package/src/session/summary/ref.ts +104 -0
  502. package/src/session/workspace/__tests__/git-worktree.test.ts +258 -0
  503. package/src/session/workspace/__tests__/path-builder.test.ts +51 -0
  504. package/src/session/workspace/driver.ts +60 -0
  505. package/src/session/workspace/git-worktree.ts +209 -0
  506. package/src/session/workspace/index.ts +25 -0
  507. package/src/session/workspace/path-builder.ts +71 -0
  508. package/src/session/workspace/ref.ts +50 -0
  509. package/src/session/workspace/registry.ts +42 -0
  510. package/src/store/conversation/memory.ts +23 -0
  511. package/src/store/session/__tests__/disk.test.ts +346 -0
  512. package/src/store/session/__tests__/memory.test.ts +327 -0
  513. package/src/store/session/disk.ts +920 -0
  514. package/src/store/session/index.ts +14 -0
  515. package/src/store/session/linkage.ts +80 -0
  516. package/src/store/session/memory.ts +400 -0
  517. package/src/store/session/messages.ts +21 -0
  518. package/src/types/agent/base.ts +31 -1
  519. package/src/types/agent/task.ts +58 -2
  520. package/src/types/conversation/index.ts +7 -0
  521. package/src/types/ids/index.ts +41 -3
  522. package/src/types/invocation/__tests__/state.test.ts +37 -29
  523. package/src/types/invocation/index.ts +26 -10
  524. package/src/types/run/config.ts +12 -1
  525. package/src/types/run/events.ts +36 -1
  526. package/src/types/run/index.ts +8 -0
  527. package/src/types/run/metadata.ts +24 -1
  528. package/src/types/run/status.ts +33 -0
  529. package/src/types/session/ids.ts +34 -0
  530. package/src/types/session/index.ts +28 -0
  531. package/src/types/session/store.ts +229 -0
  532. package/src/utils/id.ts +55 -4
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Integration — `active → locked` illegal-transition rejection matrix per
3
+ * pattern doc §5.1.
4
+ *
5
+ * A handoff request against a session whose current Run is `running`,
6
+ * `awaiting_hitl`, `awaiting_hitl_resolution`, or `awaiting_subsession`
7
+ * rejects with typed {@link HandoffLockRejected}. The reason enum maps:
8
+ * - running → 'active_run'
9
+ * - awaiting_hitl → 'pending_hitl'
10
+ * - awaiting_hitl_resol. → 'pending_hitl'
11
+ * - awaiting_subsession → 'pending_subsession'
12
+ *
13
+ * This integration test wires the `RunStatusResolver` seam into a full
14
+ * single-handoff flow so the rejection triggers from the run-fan-in check
15
+ * (§5.1), not just from a status precondition.
16
+ */
17
+
18
+ import { describe, expect, it } from 'vitest'
19
+ import { InMemorySessionStore } from '../../../store/session/memory.js'
20
+ import { generateHandoffId } from '../../../utils/id.js'
21
+ import type { HandoffAssignment } from '../../handoff/assignment.js'
22
+ import { DefaultCapacityValidator } from '../../handoff/capacity.js'
23
+ import type { HandoffEventSink } from '../../handoff/events.js'
24
+ import {
25
+ type RunStatusResolver,
26
+ type SingleHandoffDeps,
27
+ executeSingleHandoff,
28
+ } from '../../handoff/single.js'
29
+ import { HandoffLockRejected } from '../../handoff/version.js'
30
+ import { GitWorktreeDriver } from '../../workspace/git-worktree.js'
31
+ import { WorkspaceBackendRegistry } from '../../workspace/registry.js'
32
+ import { DEFAULT_TENANT, okExec, stubLogger, userActor } from './_fixtures.js'
33
+
34
+ function buildDeps(store: InMemorySessionStore, runStatus?: RunStatusResolver): SingleHandoffDeps {
35
+ const driver = new GitWorktreeDriver({
36
+ repoRoot: '/repo',
37
+ logger: stubLogger(),
38
+ execFile: async () => okExec(),
39
+ })
40
+ const workspaceRegistry = new WorkspaceBackendRegistry()
41
+ workspaceRegistry.register(driver)
42
+
43
+ const events: HandoffEventSink = {}
44
+
45
+ return {
46
+ store,
47
+ workspaceRegistry,
48
+ capacity: new DefaultCapacityValidator(store),
49
+ events,
50
+ ...(runStatus !== undefined && { runStatus }),
51
+ }
52
+ }
53
+
54
+ async function seedIdleSession(store: InMemorySessionStore) {
55
+ const project = await store.createProject(
56
+ { tenantId: DEFAULT_TENANT, name: 'illegal' },
57
+ DEFAULT_TENANT,
58
+ )
59
+ const session = await store.createSession(
60
+ { projectId: project.id, currentActor: userActor('usr_source') },
61
+ DEFAULT_TENANT,
62
+ )
63
+ return { project, session }
64
+ }
65
+
66
+ function buildAssignment(
67
+ sourceSessionId: Awaited<ReturnType<InMemorySessionStore['createSession']>>['id'],
68
+ projectId: Awaited<ReturnType<InMemorySessionStore['createProject']>>['id'],
69
+ ): HandoffAssignment {
70
+ return {
71
+ id: generateHandoffId(),
72
+ mode: 'single',
73
+ sourceSessionId,
74
+ tenantId: DEFAULT_TENANT,
75
+ projectId,
76
+ sourceActor: userActor('usr_source'),
77
+ recipientActor: userActor('usr_target'),
78
+ expectedOwnerVersion: 0,
79
+ createdAt: new Date('2026-04-17'),
80
+ }
81
+ }
82
+
83
+ describe('Integration — illegal handoff transitions (§5.1)', () => {
84
+ it('running Run → HandoffLockRejected { reason: active_run }', async () => {
85
+ const store = new InMemorySessionStore()
86
+ const { project, session } = await seedIdleSession(store)
87
+ const deps = buildDeps(store, {
88
+ async blockingRun() {
89
+ return { reason: 'active_run' }
90
+ },
91
+ })
92
+ const assignment = buildAssignment(session.id, project.id)
93
+
94
+ try {
95
+ await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
96
+ expect.fail('expected HandoffLockRejected')
97
+ } catch (err) {
98
+ expect(err).toBeInstanceOf(HandoffLockRejected)
99
+ expect((err as HandoffLockRejected).details.reason).toBe('active_run')
100
+ }
101
+
102
+ // Source never locked — still idle with original version.
103
+ const reloaded = await store.getSession(session.id, DEFAULT_TENANT)
104
+ expect(reloaded?.status).toBe('idle')
105
+ expect(reloaded?.ownerVersion).toBe(0)
106
+ })
107
+
108
+ it('awaiting_hitl → HandoffLockRejected { reason: pending_hitl }', async () => {
109
+ const store = new InMemorySessionStore()
110
+ const { project, session } = await seedIdleSession(store)
111
+ const deps = buildDeps(store, {
112
+ async blockingRun() {
113
+ return { reason: 'pending_hitl' }
114
+ },
115
+ })
116
+
117
+ try {
118
+ await executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT)
119
+ expect.fail('expected HandoffLockRejected')
120
+ } catch (err) {
121
+ expect(err).toBeInstanceOf(HandoffLockRejected)
122
+ expect((err as HandoffLockRejected).details.reason).toBe('pending_hitl')
123
+ }
124
+ })
125
+
126
+ it('awaiting_hitl_resolution collapses into pending_hitl reason (timeout variant)', async () => {
127
+ // §5.2 treats `awaiting_hitl_resolution` as the same non-terminal class
128
+ // that forces the source into `awaiting_hitl` — the resolver surfaces
129
+ // `pending_hitl` for both. This keeps the lock-rejection enum
130
+ // conservative (no new reason variant for a sub-state).
131
+ const store = new InMemorySessionStore()
132
+ const { project, session } = await seedIdleSession(store)
133
+ const deps = buildDeps(store, {
134
+ async blockingRun() {
135
+ return { reason: 'pending_hitl' }
136
+ },
137
+ })
138
+
139
+ await expect(
140
+ executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT),
141
+ ).rejects.toBeInstanceOf(HandoffLockRejected)
142
+ })
143
+
144
+ it('awaiting_subsession → HandoffLockRejected { reason: pending_subsession }', async () => {
145
+ const store = new InMemorySessionStore()
146
+ const { project, session } = await seedIdleSession(store)
147
+ const deps = buildDeps(store, {
148
+ async blockingRun() {
149
+ return { reason: 'pending_subsession' }
150
+ },
151
+ })
152
+
153
+ try {
154
+ await executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT)
155
+ expect.fail('expected HandoffLockRejected')
156
+ } catch (err) {
157
+ expect(err).toBeInstanceOf(HandoffLockRejected)
158
+ expect((err as HandoffLockRejected).details.reason).toBe('pending_subsession')
159
+ }
160
+ })
161
+
162
+ it('session status precondition: active-status session rejects before resolver fires', async () => {
163
+ // When the session itself is already non-idle (e.g. `active`), the lock
164
+ // rejection fires from the status check — no RunStatusResolver invoked.
165
+ const store = new InMemorySessionStore()
166
+ const { project, session } = await seedIdleSession(store)
167
+ await store.updateSession({ ...session, status: 'active' }, DEFAULT_TENANT)
168
+
169
+ const deps = buildDeps(store, {
170
+ async blockingRun() {
171
+ return null // resolver would allow, but status guard trips first
172
+ },
173
+ })
174
+
175
+ await expect(
176
+ executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT),
177
+ ).rejects.toBeInstanceOf(HandoffLockRejected)
178
+ })
179
+ })
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Integration — single-recipient handoff wired through the full stack.
3
+ *
4
+ * Orthogonal to `handoff/__tests__/single.test.ts` (unit-level): this
5
+ * exercises the real `InMemorySessionStore` + real `GitWorktreeDriver` (with
6
+ * stubbed `execFile`) + real {@link DefaultCapacityValidator} so the commit
7
+ * ordering + workspace provisioning side-effects are verifiable from the
8
+ * outside.
9
+ *
10
+ * Covers roadmap §5 invariants: §4.8 (handoff has no accepted field — commit
11
+ * is atomic), §6.1 (single-recipient flow: lock → commit), §5.1 (source
12
+ * transitions back to idle with bumped ownerVersion + appended previousActors).
13
+ */
14
+
15
+ import { describe, expect, it, vi } from 'vitest'
16
+ import { InMemorySessionStore } from '../../../store/session/memory.js'
17
+ import type { TenantId } from '../../../types/ids/index.js'
18
+ import { generateHandoffId } from '../../../utils/id.js'
19
+ import { TenantIsolationError } from '../../errors.js'
20
+ import type { HandoffAssignment } from '../../handoff/assignment.js'
21
+ import { DefaultCapacityValidator } from '../../handoff/capacity.js'
22
+ import type { HandoffEventSink } from '../../handoff/events.js'
23
+ import { type SingleHandoffDeps, executeSingleHandoff } from '../../handoff/single.js'
24
+ import type { Session } from '../../hierarchy/session.js'
25
+ import { GitWorktreeDriver } from '../../workspace/git-worktree.js'
26
+ import { WorkspaceBackendRegistry } from '../../workspace/registry.js'
27
+ import { DEFAULT_TENANT, OTHER_TENANT, okExec, stubLogger, userActor } from './_fixtures.js'
28
+
29
+ function buildHandoffDeps(store: InMemorySessionStore): {
30
+ deps: SingleHandoffDeps
31
+ updateCalls: Array<{ status?: string; ownerVersion?: number }>
32
+ } {
33
+ const driver = new GitWorktreeDriver({
34
+ repoRoot: '/repo',
35
+ logger: stubLogger(),
36
+ execFile: async () => okExec(),
37
+ })
38
+ const workspaceRegistry = new WorkspaceBackendRegistry()
39
+ workspaceRegistry.register(driver)
40
+
41
+ const sink: HandoffEventSink = {
42
+ onLocked: vi.fn(),
43
+ onUnlocked: vi.fn(),
44
+ onCommitted: vi.fn(),
45
+ onBroadcastRollback: vi.fn(),
46
+ }
47
+
48
+ const updateCalls: Array<{ status?: string; ownerVersion?: number }> = []
49
+ const originalUpdate = store.updateSession.bind(store)
50
+ store.updateSession = async (session: Session, tenantId) => {
51
+ updateCalls.push({ status: session.status, ownerVersion: session.ownerVersion })
52
+ return originalUpdate(session, tenantId)
53
+ }
54
+
55
+ return {
56
+ deps: {
57
+ store,
58
+ workspaceRegistry,
59
+ capacity: new DefaultCapacityValidator(store),
60
+ events: sink,
61
+ },
62
+ updateCalls,
63
+ }
64
+ }
65
+
66
+ describe('Integration — single-recipient handoff E2E', () => {
67
+ it('idle → locked → commit: source previousActors grew + ownerVersion bumped atomically', async () => {
68
+ const store = new InMemorySessionStore()
69
+ const project = await store.createProject(
70
+ { tenantId: DEFAULT_TENANT, name: 'ho' },
71
+ DEFAULT_TENANT,
72
+ )
73
+ const sourceActor = userActor('usr_source')
74
+ const recipientActor = userActor('usr_target')
75
+ const session = await store.createSession(
76
+ { projectId: project.id, currentActor: sourceActor },
77
+ DEFAULT_TENANT,
78
+ )
79
+
80
+ const { deps, updateCalls } = buildHandoffDeps(store)
81
+ const assignment: HandoffAssignment = {
82
+ id: generateHandoffId(),
83
+ mode: 'single',
84
+ sourceSessionId: session.id,
85
+ tenantId: DEFAULT_TENANT,
86
+ projectId: project.id,
87
+ sourceActor,
88
+ recipientActor,
89
+ expectedOwnerVersion: 0,
90
+ createdAt: new Date('2026-04-17'),
91
+ }
92
+
93
+ const outcome = await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
94
+
95
+ expect(outcome.committedOwnerVersion).toBe(1)
96
+
97
+ const reloaded = await store.getSession(session.id, DEFAULT_TENANT)
98
+ expect(reloaded?.status).toBe('idle')
99
+ expect(reloaded?.ownerVersion).toBe(1)
100
+ expect(reloaded?.currentActor).toEqual(recipientActor)
101
+ expect(reloaded?.previousActors).toHaveLength(1)
102
+ expect(reloaded?.previousActors[0]).toEqual(sourceActor)
103
+
104
+ // Wired assertion: updateSession was invoked at least with the lock
105
+ // transition + the final commit. That sequence is what makes the
106
+ // handoff "atomic at the store layer".
107
+ // Expect at minimum locked (v0) followed by committed (v1).
108
+ expect(updateCalls).toEqual(
109
+ expect.arrayContaining([
110
+ { status: 'locked', ownerVersion: 0 },
111
+ { status: 'idle', ownerVersion: 1 },
112
+ ]),
113
+ )
114
+ })
115
+
116
+ it('cross-tenant assignment rejects at entry (TenantIsolationError)', async () => {
117
+ const store = new InMemorySessionStore()
118
+ const project = await store.createProject(
119
+ { tenantId: DEFAULT_TENANT, name: 'ct' },
120
+ DEFAULT_TENANT,
121
+ )
122
+ const session = await store.createSession(
123
+ { projectId: project.id, currentActor: userActor('usr_source') },
124
+ DEFAULT_TENANT,
125
+ )
126
+
127
+ const { deps } = buildHandoffDeps(store)
128
+ const assignment: HandoffAssignment = {
129
+ id: generateHandoffId(),
130
+ mode: 'single',
131
+ sourceSessionId: session.id,
132
+ tenantId: OTHER_TENANT,
133
+ projectId: project.id,
134
+ sourceActor: userActor('usr_source', OTHER_TENANT),
135
+ recipientActor: userActor('usr_target', OTHER_TENANT),
136
+ expectedOwnerVersion: 0,
137
+ createdAt: new Date('2026-04-17'),
138
+ }
139
+
140
+ await expect(executeSingleHandoff(deps, assignment, OTHER_TENANT)).rejects.toBeInstanceOf(
141
+ TenantIsolationError,
142
+ )
143
+ })
144
+
145
+ it('source-owned workspace provisioned for recipient', async () => {
146
+ const store = new InMemorySessionStore()
147
+ const project = await store.createProject(
148
+ { tenantId: DEFAULT_TENANT, name: 'wsp' },
149
+ DEFAULT_TENANT,
150
+ )
151
+ const source = await store.createSession(
152
+ { projectId: project.id, currentActor: userActor('usr_source') },
153
+ DEFAULT_TENANT,
154
+ )
155
+
156
+ const { deps } = buildHandoffDeps(store)
157
+ const assignment: HandoffAssignment = {
158
+ id: generateHandoffId(),
159
+ mode: 'single',
160
+ sourceSessionId: source.id,
161
+ tenantId: DEFAULT_TENANT,
162
+ projectId: project.id,
163
+ sourceActor: userActor('usr_source'),
164
+ recipientActor: userActor('usr_target'),
165
+ expectedOwnerVersion: 0,
166
+ createdAt: new Date('2026-04-17'),
167
+ }
168
+
169
+ const outcome = await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
170
+
171
+ expect(outcome.workspaceId.startsWith('wsp_')).toBe(true)
172
+ expect(outcome.newSessionId.startsWith('ses_')).toBe(true)
173
+
174
+ // Recipient session exists under the same tenant/project.
175
+ const recipient = await store.getSession(outcome.newSessionId, DEFAULT_TENANT)
176
+ expect(recipient).not.toBeNull()
177
+ expect(recipient?.projectId).toBe(project.id)
178
+ expect(recipient?.tenantId).toBe(DEFAULT_TENANT)
179
+ expect(recipient?.currentActor).toEqual(userActor('usr_target'))
180
+
181
+ // A sub-session edge links the source to the recipient.
182
+ const children = await store.getChildren(source.id, DEFAULT_TENANT)
183
+ expect(children).toHaveLength(1)
184
+ expect(children[0]?.kind).toBe('user_handoff')
185
+ expect(children[0]?.childSessionId).toBe(outcome.newSessionId)
186
+ })
187
+
188
+ // Tenant-denormalization-on-record assertion — every persisted entity
189
+ // carries the tenantId explicitly, which matters for §12 isolation.
190
+ it('denormalized tenantId stamped on Session + SubSession records', async () => {
191
+ const _tenantType: TenantId = DEFAULT_TENANT
192
+ const store = new InMemorySessionStore()
193
+ const project = await store.createProject(
194
+ { tenantId: DEFAULT_TENANT, name: 'denorm' },
195
+ DEFAULT_TENANT,
196
+ )
197
+ const source = await store.createSession(
198
+ { projectId: project.id, currentActor: userActor('usr_source') },
199
+ DEFAULT_TENANT,
200
+ )
201
+ const { deps } = buildHandoffDeps(store)
202
+
203
+ const assignment: HandoffAssignment = {
204
+ id: generateHandoffId(),
205
+ mode: 'single',
206
+ sourceSessionId: source.id,
207
+ tenantId: DEFAULT_TENANT,
208
+ projectId: project.id,
209
+ sourceActor: userActor('usr_source'),
210
+ recipientActor: userActor('usr_target'),
211
+ expectedOwnerVersion: 0,
212
+ createdAt: new Date(),
213
+ }
214
+ const outcome = await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
215
+
216
+ // Recipient's Session stores tenantId explicitly (not inferred).
217
+ const recipient = await store.getSession(outcome.newSessionId, DEFAULT_TENANT)
218
+ expect(recipient?.tenantId).toBe(_tenantType)
219
+ })
220
+ })
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Integration — Tenant → Project → Session → SubSession → Run hierarchy
3
+ * lifecycle against a real {@link InMemorySessionStore}.
4
+ *
5
+ * Covers roadmap §5 invariants §4 (branded IDs), §4.3 (currentActor
6
+ * immutability), §4.4 (sub-session status fan-in to drill), plus the
7
+ * `drill()` navigation primitive (§14.3). Orthogonal to `e2e-spawn.test.ts`
8
+ * (which exercises the full AgentManager spawn path); this file asserts the
9
+ * raw store contract under direct construction.
10
+ */
11
+
12
+ import { describe, expect, it } from 'vitest'
13
+ import { TenantIsolationError } from '../../errors.js'
14
+ import { DEFAULT_TENANT, agentActor, buildHarness, userActor } from './_fixtures.js'
15
+
16
+ describe('Integration — hierarchy lifecycle', () => {
17
+ it('creates Tenant → Project → Session → SubSession with properly branded IDs', async () => {
18
+ const { store } = buildHarness()
19
+ const tenant = DEFAULT_TENANT
20
+
21
+ const project = await store.createProject({ tenantId: tenant, name: 'p1' }, tenant)
22
+ expect(project.id.startsWith('prj_')).toBe(true)
23
+ expect(project.tenantId.startsWith('tnt_')).toBe(true)
24
+
25
+ const session = await store.createSession(
26
+ { projectId: project.id, currentActor: userActor('usr_a') },
27
+ tenant,
28
+ )
29
+ expect(session.id.startsWith('ses_')).toBe(true)
30
+ expect(session.projectId).toBe(project.id)
31
+ expect(session.tenantId).toBe(tenant)
32
+ expect(session.status).toBe('idle')
33
+ expect(session.ownerVersion).toBe(0)
34
+ expect(session.previousActors).toEqual([])
35
+
36
+ const childSession = await store.createSession(
37
+ { projectId: project.id, currentActor: agentActor('agt_worker') },
38
+ tenant,
39
+ )
40
+ const subSession = await store.createSubSession(
41
+ {
42
+ parentSessionId: session.id,
43
+ childSessionId: childSession.id,
44
+ kind: 'agent_spawn',
45
+ spawnedBy: userActor('usr_a'),
46
+ },
47
+ tenant,
48
+ )
49
+ expect(subSession.id.startsWith('sub_')).toBe(true)
50
+ expect(subSession.parentSessionId).toBe(session.id)
51
+ expect(subSession.childSessionId).toBe(childSession.id)
52
+ expect(subSession.kind).toBe('agent_spawn')
53
+ expect(subSession.status).toBe('pending')
54
+ })
55
+
56
+ it('drill(parentSessionId) returns a SessionView with children[] and ancestry[]', async () => {
57
+ const { store } = buildHarness()
58
+ const tenant = DEFAULT_TENANT
59
+
60
+ const project = await store.createProject({ tenantId: tenant, name: 'drill' }, tenant)
61
+ const parent = await store.createSession(
62
+ { projectId: project.id, currentActor: userActor('usr_root') },
63
+ tenant,
64
+ )
65
+ const childA = await store.createSession(
66
+ { projectId: project.id, currentActor: agentActor('agt_a') },
67
+ tenant,
68
+ )
69
+ const childB = await store.createSession(
70
+ { projectId: project.id, currentActor: agentActor('agt_b') },
71
+ tenant,
72
+ )
73
+ await store.createSubSession(
74
+ {
75
+ parentSessionId: parent.id,
76
+ childSessionId: childA.id,
77
+ kind: 'agent_spawn',
78
+ spawnedBy: userActor('usr_root'),
79
+ },
80
+ tenant,
81
+ )
82
+ await store.createSubSession(
83
+ {
84
+ parentSessionId: parent.id,
85
+ childSessionId: childB.id,
86
+ kind: 'agent_spawn',
87
+ spawnedBy: userActor('usr_root'),
88
+ },
89
+ tenant,
90
+ )
91
+
92
+ const parentView = await store.drill(parent.id, tenant)
93
+ expect(parentView).not.toBeNull()
94
+ expect(parentView?.session.id).toBe(parent.id)
95
+ expect(parentView?.children).toHaveLength(2)
96
+ expect(parentView?.ancestry).toEqual([parent.id])
97
+
98
+ const childAView = await store.drill(childA.id, tenant)
99
+ expect(childAView).not.toBeNull()
100
+ expect(childAView?.ancestry).toEqual([parent.id, childA.id])
101
+ expect(childAView?.children).toHaveLength(0)
102
+ })
103
+
104
+ it('drill returns null for unknown session (deny-by-default)', async () => {
105
+ const { store } = buildHarness()
106
+ const view = await store.drill(
107
+ 'ses_missing' as Parameters<typeof store.drill>[0],
108
+ DEFAULT_TENANT,
109
+ )
110
+ expect(view).toBeNull()
111
+ })
112
+
113
+ it('§4.3 currentActor immutable previousActors — append-only on handoff', async () => {
114
+ const { store } = buildHarness()
115
+ const tenant = DEFAULT_TENANT
116
+
117
+ const project = await store.createProject({ tenantId: tenant, name: 'actors' }, tenant)
118
+ const userA = userActor('usr_a')
119
+ const userB = userActor('usr_b')
120
+ const userC = userActor('usr_c')
121
+
122
+ const session = await store.createSession(
123
+ { projectId: project.id, currentActor: userA },
124
+ tenant,
125
+ )
126
+
127
+ // Simulate two successive handoff commits — each pushes the old actor
128
+ // onto previousActors and increments ownerVersion.
129
+ const firstHandoff = {
130
+ ...session,
131
+ currentActor: userB,
132
+ previousActors: [userA],
133
+ ownerVersion: 1,
134
+ }
135
+ await store.updateSession(firstHandoff, tenant)
136
+
137
+ const secondHandoff = {
138
+ ...firstHandoff,
139
+ currentActor: userC,
140
+ previousActors: [...firstHandoff.previousActors, userB],
141
+ ownerVersion: 2,
142
+ }
143
+ await store.updateSession(secondHandoff, tenant)
144
+
145
+ const reloaded = await store.getSession(session.id, tenant)
146
+ expect(reloaded?.currentActor).toEqual(userC)
147
+ expect(reloaded?.previousActors).toEqual([userA, userB])
148
+ expect(reloaded?.ownerVersion).toBe(2)
149
+ })
150
+
151
+ it('cycle guard via AncestryCycleError: ancestry walk detects corrupted parent linkage', async () => {
152
+ const { store } = buildHarness()
153
+ const tenant = DEFAULT_TENANT
154
+
155
+ const project = await store.createProject({ tenantId: tenant, name: 'cycle' }, tenant)
156
+ const sA = await store.createSession(
157
+ { projectId: project.id, currentActor: userActor('usr_a') },
158
+ tenant,
159
+ )
160
+ const sB = await store.createSession(
161
+ { projectId: project.id, currentActor: userActor('usr_b') },
162
+ tenant,
163
+ )
164
+
165
+ // Valid edge sA → sB.
166
+ await store.createSubSession(
167
+ {
168
+ parentSessionId: sA.id,
169
+ childSessionId: sB.id,
170
+ kind: 'agent_spawn',
171
+ spawnedBy: userActor('usr_a'),
172
+ },
173
+ tenant,
174
+ )
175
+ // Corrupting edge sB → sA closes the cycle. The store layer does not
176
+ // pre-check parent direction (the pattern doc §4.5 discusses
177
+ // intervention DAG cycles; ancestry cycles are a store-corruption
178
+ // detection path per session/errors.ts#AncestryCycleError).
179
+ await store.createSubSession(
180
+ {
181
+ parentSessionId: sB.id,
182
+ childSessionId: sA.id,
183
+ kind: 'agent_spawn',
184
+ spawnedBy: userActor('usr_b'),
185
+ },
186
+ tenant,
187
+ )
188
+
189
+ await expect(store.getAncestry(sB.id, tenant)).rejects.toThrow(/cycle/i)
190
+ })
191
+
192
+ it('SubSession pending → active → idle lifecycle', async () => {
193
+ const { store } = buildHarness()
194
+ const tenant = DEFAULT_TENANT
195
+
196
+ const project = await store.createProject({ tenantId: tenant, name: 'lifecycle' }, tenant)
197
+ const parent = await store.createSession(
198
+ { projectId: project.id, currentActor: userActor('usr_a') },
199
+ tenant,
200
+ )
201
+ const child = await store.createSession(
202
+ { projectId: project.id, currentActor: agentActor('agt_a') },
203
+ tenant,
204
+ )
205
+ const sub = await store.createSubSession(
206
+ {
207
+ parentSessionId: parent.id,
208
+ childSessionId: child.id,
209
+ kind: 'agent_spawn',
210
+ spawnedBy: userActor('usr_a'),
211
+ },
212
+ tenant,
213
+ )
214
+ expect(sub.status).toBe('pending')
215
+
216
+ // pending → active.
217
+ await store.updateSubSession({ ...sub, status: 'active' }, tenant)
218
+ const active = await store.getSubSession(sub.id, tenant)
219
+ expect(active?.status).toBe('active')
220
+
221
+ // active → idle (§5.3: no 'closed' state — sub-sessions terminate on idle).
222
+ await store.updateSubSession({ ...sub, status: 'idle' }, tenant)
223
+ const idle = await store.getSubSession(sub.id, tenant)
224
+ expect(idle?.status).toBe('idle')
225
+ })
226
+
227
+ it('cross-tenant hierarchy access rejects via TenantIsolationError', async () => {
228
+ const { store } = buildHarness()
229
+ const projectA = await store.createProject(
230
+ { tenantId: DEFAULT_TENANT, name: 'a' },
231
+ DEFAULT_TENANT,
232
+ )
233
+ await expect(
234
+ store.getProject(projectA.id, 'tnt_other' as typeof DEFAULT_TENANT),
235
+ ).rejects.toBeInstanceOf(TenantIsolationError)
236
+ })
237
+ })