@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,131 @@
1
+ /**
2
+ * Migration marker file I/O — ensures the boot-time v0.2.0 re-layout is
3
+ * idempotent and race-safe.
4
+ *
5
+ * Layout:
6
+ * {rootDir}/.migration/v0.2.0 — completion marker (JSON body)
7
+ * {rootDir}/.migration/v0.2.0.tmp — in-flight lock (O_EXCL / `wx` flag)
8
+ *
9
+ * Atomicity contract (Convention #8):
10
+ * - `writeMarker` goes through write-tmp-rename so readers never see a
11
+ * partially serialized body.
12
+ * - `acquireMigrationLock` uses the `wx` flag (O_CREAT | O_EXCL) so
13
+ * concurrent boots detect each other rather than overwriting. Loser
14
+ * cooperates (see filesystem.ts — waits and re-checks the main marker).
15
+ * - `readMarker` tolerates missing / corrupt files: missing → null,
16
+ * corrupt JSON → null (not throw). The migrator treats either as "run
17
+ * again"; a corrupt marker is safer to retry than to honor as valid.
18
+ */
19
+
20
+ import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'
21
+ import { dirname } from 'node:path'
22
+ import type { ProjectId } from '../../types/session/ids.js'
23
+
24
+ /**
25
+ * Marker payload. `migratedThreads` preserves the legacy → new mapping so
26
+ * later tooling (e.g. `namzu sdk migrate-ids`) can cross-reference.
27
+ */
28
+ export interface MigrationMarker {
29
+ version: string
30
+ at: Date
31
+ migratedThreads: readonly { legacyThreadId: string; newProjectId: ProjectId }[]
32
+ }
33
+
34
+ interface PersistedMarker {
35
+ version: string
36
+ at: string
37
+ migratedThreads: readonly { legacyThreadId: string; newProjectId: string }[]
38
+ }
39
+
40
+ /**
41
+ * Read a marker file. Returns `null` when the file is absent OR when the
42
+ * contents fail JSON.parse — corruption is treated as "migration did not
43
+ * complete cleanly", so the caller re-runs rather than honoring stale data.
44
+ */
45
+ export async function readMarker(path: string): Promise<MigrationMarker | null> {
46
+ let raw: string
47
+ try {
48
+ raw = await readFile(path, 'utf-8')
49
+ } catch (err) {
50
+ const code = (err as NodeJS.ErrnoException).code
51
+ if (code === 'ENOENT') return null
52
+ throw err
53
+ }
54
+
55
+ let parsed: PersistedMarker
56
+ try {
57
+ parsed = JSON.parse(raw) as PersistedMarker
58
+ } catch {
59
+ return null
60
+ }
61
+
62
+ if (
63
+ typeof parsed !== 'object' ||
64
+ parsed === null ||
65
+ typeof parsed.version !== 'string' ||
66
+ typeof parsed.at !== 'string' ||
67
+ !Array.isArray(parsed.migratedThreads)
68
+ ) {
69
+ return null
70
+ }
71
+
72
+ return {
73
+ version: parsed.version,
74
+ at: new Date(parsed.at),
75
+ migratedThreads: parsed.migratedThreads.map((m) => ({
76
+ legacyThreadId: m.legacyThreadId,
77
+ newProjectId: m.newProjectId as ProjectId,
78
+ })),
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Write a marker atomically via write-tmp-rename. Parent directory is
84
+ * created on demand so callers do not have to mkdir separately.
85
+ */
86
+ export async function writeMarker(path: string, marker: MigrationMarker): Promise<void> {
87
+ await mkdir(dirname(path), { recursive: true })
88
+ const serialized: PersistedMarker = {
89
+ version: marker.version,
90
+ at: marker.at.toISOString(),
91
+ migratedThreads: marker.migratedThreads.map((m) => ({
92
+ legacyThreadId: m.legacyThreadId,
93
+ newProjectId: m.newProjectId as string,
94
+ })),
95
+ }
96
+ const tmp = `${path}.write.tmp`
97
+ try {
98
+ await writeFile(tmp, JSON.stringify(serialized, null, 2), 'utf-8')
99
+ await rename(tmp, path)
100
+ } catch (err) {
101
+ await unlink(tmp).catch(() => undefined)
102
+ throw err
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Acquire an exclusive lock by creating `tmpPath` with the `wx` flag.
108
+ * EEXIST means another process is mid-migration — callers must treat that
109
+ * as "wait and re-check the main marker" rather than overwriting.
110
+ *
111
+ * Returns on success; throws the raw `NodeJS.ErrnoException` on EEXIST or
112
+ * any other FS failure so the caller can branch on `code`.
113
+ */
114
+ export async function acquireMigrationLock(tmpPath: string): Promise<void> {
115
+ await mkdir(dirname(tmpPath), { recursive: true })
116
+ await writeFile(tmpPath, JSON.stringify({ at: new Date().toISOString() }), { flag: 'wx' })
117
+ }
118
+
119
+ /**
120
+ * Release the lock. Missing file is tolerated — lock release is idempotent
121
+ * by design so crashed mid-migrations do not wedge subsequent boots.
122
+ */
123
+ export async function releaseMigrationLock(tmpPath: string): Promise<void> {
124
+ try {
125
+ await unlink(tmpPath)
126
+ } catch (err) {
127
+ const code = (err as NodeJS.ErrnoException).code
128
+ if (code === 'ENOENT') return
129
+ throw err
130
+ }
131
+ }
@@ -0,0 +1,316 @@
1
+ import { mkdtempSync, rmSync } from 'node:fs'
2
+ import { tmpdir } from 'node:os'
3
+ import { join } from 'node:path'
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
5
+ import type { ActorRef } from '../../../session/hierarchy/actor.js'
6
+ import type { SubSession } from '../../../session/hierarchy/sub-session.js'
7
+ import { type ExecFile, GitWorktreeDriver } from '../../../session/workspace/git-worktree.js'
8
+ import type { WorkspaceRef } from '../../../session/workspace/ref.js'
9
+ import { WorkspaceBackendRegistry } from '../../../session/workspace/registry.js'
10
+ import { InMemorySessionStore } from '../../../store/session/memory.js'
11
+ import type { AgentId, TenantId, UserId } from '../../../types/ids/index.js'
12
+ import { createUserMessage } from '../../../types/message/index.js'
13
+ import type { WorkspaceId } from '../../../types/session/ids.js'
14
+ import {
15
+ ArchivalManager,
16
+ ArchiveNotConfiguredError,
17
+ SubSessionNotArchivableError,
18
+ } from '../archive.js'
19
+ import { DiskArchiveBackend } from '../disk-backend.js'
20
+
21
+ const tenantA = 'tnt_alpha' as TenantId
22
+
23
+ function stubLogger() {
24
+ return {
25
+ debug: vi.fn(),
26
+ info: vi.fn(),
27
+ warn: vi.fn(),
28
+ error: vi.fn(),
29
+ child() {
30
+ return stubLogger()
31
+ },
32
+ }
33
+ }
34
+
35
+ function userActor(tenantId: TenantId): ActorRef {
36
+ return { kind: 'user', userId: 'usr_a' as UserId, tenantId }
37
+ }
38
+
39
+ function agentActor(tenantId: TenantId): ActorRef {
40
+ return { kind: 'agent', agentId: 'agt_a' as AgentId, tenantId }
41
+ }
42
+
43
+ async function seedIdleSubSession(store: InMemorySessionStore) {
44
+ const project = await store.createProject({ tenantId: tenantA, name: 'p' }, tenantA)
45
+ const parent = await store.createSession(
46
+ { projectId: project.id, currentActor: userActor(tenantA) },
47
+ tenantA,
48
+ )
49
+ const child = await store.createSession(
50
+ { projectId: project.id, currentActor: agentActor(tenantA) },
51
+ tenantA,
52
+ )
53
+ const sub = await store.createSubSession(
54
+ {
55
+ parentSessionId: parent.id,
56
+ childSessionId: child.id,
57
+ kind: 'agent_spawn',
58
+ spawnedBy: userActor(tenantA),
59
+ },
60
+ tenantA,
61
+ )
62
+ // Flip the newly-created sub-session from 'pending' → 'idle' so it's
63
+ // eligible for archival.
64
+ await store.updateSubSession({ ...sub, status: 'idle' }, tenantA)
65
+ return { project, parent, child, sub }
66
+ }
67
+
68
+ function buildRegistry(): WorkspaceBackendRegistry {
69
+ const exec: ExecFile = async () => ({ stdout: '', stderr: '' })
70
+ const driver = new GitWorktreeDriver({
71
+ repoRoot: '/repo',
72
+ logger: stubLogger(),
73
+ execFile: exec,
74
+ })
75
+ const registry = new WorkspaceBackendRegistry()
76
+ registry.register(driver)
77
+ return registry
78
+ }
79
+
80
+ describe('ArchivalManager', () => {
81
+ let rootDir: string
82
+ let store: InMemorySessionStore
83
+ let backend: DiskArchiveBackend
84
+
85
+ beforeEach(() => {
86
+ rootDir = mkdtempSync(join(tmpdir(), 'namzu-retention-archive-'))
87
+ store = new InMemorySessionStore()
88
+ backend = new DiskArchiveBackend({ rootDir })
89
+ })
90
+
91
+ afterEach(() => {
92
+ rmSync(rootDir, { recursive: true, force: true })
93
+ })
94
+
95
+ it('archive happy path: idle sub-session → tombstone attached, backend called once', async () => {
96
+ const { sub, child } = await seedIdleSubSession(store)
97
+ await store.appendMessage(child.id, createUserMessage('hi'), tenantA)
98
+
99
+ const storeSpy = vi.spyOn(backend, 'store')
100
+ const manager = new ArchivalManager({
101
+ sessionStore: store,
102
+ workspaceRegistry: buildRegistry(),
103
+ archiveBackend: backend,
104
+ })
105
+
106
+ const tombstone = await manager.archive(sub.id, tenantA)
107
+
108
+ expect(tombstone.subSessionId).toBe(sub.id)
109
+ expect(tombstone.archiveRef).toMatch(/^arc_/)
110
+ expect(tombstone.archivedAt).toBeInstanceOf(Date)
111
+ expect(storeSpy).toHaveBeenCalledTimes(1)
112
+
113
+ const after = await store.getSubSession(sub.id, tenantA)
114
+ expect(after?.status).toBe('archived')
115
+ expect(after?.archiveRef).toBe(tombstone.archiveRef)
116
+ expect(after?.archivedAt).toBeInstanceOf(Date)
117
+ })
118
+
119
+ it('rejects non-archivable statuses (running/active → not_idle)', async () => {
120
+ const { sub } = await seedIdleSubSession(store)
121
+ await store.updateSubSession({ ...sub, status: 'active' }, tenantA)
122
+
123
+ const manager = new ArchivalManager({
124
+ sessionStore: store,
125
+ workspaceRegistry: buildRegistry(),
126
+ archiveBackend: backend,
127
+ })
128
+
129
+ await expect(manager.archive(sub.id, tenantA)).rejects.toBeInstanceOf(
130
+ SubSessionNotArchivableError,
131
+ )
132
+ })
133
+
134
+ it('rejects already-archived sub-sessions', async () => {
135
+ const { sub } = await seedIdleSubSession(store)
136
+ const manager = new ArchivalManager({
137
+ sessionStore: store,
138
+ workspaceRegistry: buildRegistry(),
139
+ archiveBackend: backend,
140
+ })
141
+ await manager.archive(sub.id, tenantA)
142
+
143
+ await expect(manager.archive(sub.id, tenantA)).rejects.toMatchObject({
144
+ name: 'SubSessionNotArchivableError',
145
+ details: { reason: 'already_archived' },
146
+ })
147
+ })
148
+
149
+ it('rejects missing sub-sessions', async () => {
150
+ const manager = new ArchivalManager({
151
+ sessionStore: store,
152
+ workspaceRegistry: buildRegistry(),
153
+ archiveBackend: backend,
154
+ })
155
+ await expect(manager.archive('sub_missing' as SubSession['id'], tenantA)).rejects.toMatchObject(
156
+ { name: 'SubSessionNotArchivableError', details: { reason: 'missing' } },
157
+ )
158
+ })
159
+
160
+ it('deny-by-default: backend absent → ArchiveNotConfiguredError', async () => {
161
+ const { sub } = await seedIdleSubSession(store)
162
+ const manager = new ArchivalManager({
163
+ sessionStore: store,
164
+ workspaceRegistry: buildRegistry(),
165
+ // archiveBackend intentionally omitted.
166
+ })
167
+ await expect(manager.archive(sub.id, tenantA)).rejects.toBeInstanceOf(ArchiveNotConfiguredError)
168
+ })
169
+
170
+ it('restore reads tombstone, invokes backend.restore, flips status back to idle', async () => {
171
+ const { sub } = await seedIdleSubSession(store)
172
+ const manager = new ArchivalManager({
173
+ sessionStore: store,
174
+ workspaceRegistry: buildRegistry(),
175
+ archiveBackend: backend,
176
+ })
177
+ await manager.archive(sub.id, tenantA)
178
+
179
+ const restoreSpy = vi.spyOn(backend, 'restore')
180
+ await manager.restore(sub.id, tenantA)
181
+
182
+ expect(restoreSpy).toHaveBeenCalledTimes(1)
183
+ const after = await store.getSubSession(sub.id, tenantA)
184
+ expect(after?.status).toBe('idle')
185
+ expect(after?.archiveRef).toBeUndefined()
186
+ expect(after?.archivedAt).toBeUndefined()
187
+ })
188
+
189
+ it('tombstone is navigable via drill post-archive with status=archived + archiveRef', async () => {
190
+ const { parent, sub } = await seedIdleSubSession(store)
191
+ const manager = new ArchivalManager({
192
+ sessionStore: store,
193
+ workspaceRegistry: buildRegistry(),
194
+ archiveBackend: backend,
195
+ })
196
+ const tombstone = await manager.archive(sub.id, tenantA)
197
+
198
+ const view = await store.drill(parent.id, tenantA)
199
+ expect(view).not.toBeNull()
200
+ expect(view?.children).toHaveLength(1)
201
+ const child = view?.children[0]
202
+ expect(child?.id).toBe(sub.id)
203
+ expect(child?.status).toBe('archived')
204
+ expect(child?.archiveRef).toBe(tombstone.archiveRef)
205
+ expect(child?.archivedAt).toBeInstanceOf(Date)
206
+ })
207
+
208
+ it('workspace disposal failures do not unwind the committed archive', async () => {
209
+ const { sub } = await seedIdleSubSession(store)
210
+
211
+ // Registry with a driver whose `dispose` always throws.
212
+ const registry = new WorkspaceBackendRegistry()
213
+ registry.register({
214
+ kind: 'git-worktree',
215
+ async create() {
216
+ return {
217
+ id: 'wsp_x' as WorkspaceId,
218
+ meta: {
219
+ backend: 'git-worktree',
220
+ repoRoot: '/r',
221
+ branch: 'main',
222
+ worktreePath: '/r/x',
223
+ },
224
+ createdAt: new Date(),
225
+ }
226
+ },
227
+ async branch(ref) {
228
+ return ref
229
+ },
230
+ async dispose() {
231
+ throw new Error('dispose boom')
232
+ },
233
+ async inspect() {
234
+ return { exists: true, currentRef: 'HEAD', isDirty: false }
235
+ },
236
+ })
237
+
238
+ const workspaceRef: WorkspaceRef = {
239
+ id: 'wsp_y' as WorkspaceId,
240
+ meta: { backend: 'git-worktree', repoRoot: '/r', branch: 'main', worktreePath: '/r/y' },
241
+ createdAt: new Date(),
242
+ }
243
+ // Seed the sub-session with a workspaceId and a resolver that returns
244
+ // the live ref.
245
+ await store.updateSubSession({ ...sub, status: 'idle', workspaceId: workspaceRef.id }, tenantA)
246
+
247
+ const manager = new ArchivalManager({
248
+ sessionStore: store,
249
+ workspaceRegistry: registry,
250
+ archiveBackend: backend,
251
+ workspaceResolver: async () => workspaceRef,
252
+ })
253
+
254
+ // Archive must complete even though dispose throws internally.
255
+ const tombstone = await manager.archive(sub.id, tenantA)
256
+ expect(tombstone.archiveRef).toMatch(/^arc_/)
257
+
258
+ const after = await store.getSubSession(sub.id, tenantA)
259
+ expect(after?.status).toBe('archived')
260
+ })
261
+
262
+ it('SessionMessage round-trip: archive preserves original MessageId + timestamps', async () => {
263
+ // Phase 9 Known Delta #7: ArchivalManager now uses
264
+ // SessionStore.loadSessionMessages for full-fidelity archival (no more
265
+ // synthetic `msg_restored_N` IDs).
266
+ const { sub, child } = await seedIdleSubSession(store)
267
+ const id1 = await store.appendMessage(child.id, createUserMessage('m1'), tenantA)
268
+ const id2 = await store.appendMessage(child.id, createUserMessage('m2'), tenantA)
269
+
270
+ const captured: unknown[] = []
271
+ const capturingBackend: DiskArchiveBackend = Object.assign(
272
+ new DiskArchiveBackend({ rootDir }),
273
+ {
274
+ store: vi.fn(async (bundle: unknown) => {
275
+ captured.push(bundle)
276
+ return {
277
+ archiveRef: 'arc_test_ref',
278
+ archivedAt: new Date(),
279
+ }
280
+ }),
281
+ },
282
+ ) as unknown as DiskArchiveBackend
283
+
284
+ const manager = new ArchivalManager({
285
+ sessionStore: store,
286
+ workspaceRegistry: buildRegistry(),
287
+ archiveBackend: capturingBackend,
288
+ })
289
+ await manager.archive(sub.id, tenantA)
290
+
291
+ expect(captured).toHaveLength(1)
292
+ const bundle = captured[0] as {
293
+ messages: Array<{ id: string; at: Date; message: unknown }>
294
+ }
295
+ expect(bundle.messages).toHaveLength(2)
296
+ expect(bundle.messages[0]?.id).toBe(id1)
297
+ expect(bundle.messages[1]?.id).toBe(id2)
298
+ expect(bundle.messages[0]?.id).not.toMatch(/^msg_restored_/)
299
+ expect(bundle.messages[0]?.at).toBeInstanceOf(Date)
300
+ })
301
+
302
+ it('onArchived callback fires exactly once on success with the tombstone', async () => {
303
+ const { sub } = await seedIdleSubSession(store)
304
+ const onArchived = vi.fn()
305
+ const manager = new ArchivalManager({
306
+ sessionStore: store,
307
+ workspaceRegistry: buildRegistry(),
308
+ archiveBackend: backend,
309
+ onArchived,
310
+ })
311
+ const tombstone = await manager.archive(sub.id, tenantA)
312
+
313
+ expect(onArchived).toHaveBeenCalledTimes(1)
314
+ expect(onArchived).toHaveBeenCalledWith(tombstone)
315
+ })
316
+ })
@@ -0,0 +1,180 @@
1
+ import { mkdtempSync, rmSync } from 'node:fs'
2
+ import { readFile, readdir } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
6
+ import type { SessionMessage } from '../../../store/session/messages.js'
7
+ import type { MessageId, SessionId, TenantId } from '../../../types/ids/index.js'
8
+ import { createUserMessage } from '../../../types/message/index.js'
9
+ import type { SubSessionId, SummaryId, WorkspaceId } from '../../../types/session/ids.js'
10
+ import type { SessionSummaryRef } from '../../summary/ref.js'
11
+ import type { WorkspaceRef } from '../../workspace/ref.js'
12
+ import type { ArchiveBackendRef } from '../archive-backend-ref.js'
13
+ import { DiskArchiveBackend } from '../disk-backend.js'
14
+
15
+ const tenantA = 'tnt_alpha' as TenantId
16
+
17
+ function fakeSessionId(): SessionId {
18
+ return 'ses_fake_1' as SessionId
19
+ }
20
+
21
+ function fakeSubSessionId(): SubSessionId {
22
+ return 'sub_fake_1' as SubSessionId
23
+ }
24
+
25
+ function buildMessages(sessionId: SessionId): SessionMessage[] {
26
+ return [
27
+ {
28
+ id: 'msg_1' as MessageId,
29
+ sessionId,
30
+ tenantId: tenantA,
31
+ message: createUserMessage('hello'),
32
+ at: new Date('2026-04-01T00:00:00Z'),
33
+ },
34
+ {
35
+ id: 'msg_2' as MessageId,
36
+ sessionId,
37
+ tenantId: tenantA,
38
+ message: createUserMessage('world'),
39
+ at: new Date('2026-04-01T00:01:00Z'),
40
+ },
41
+ ]
42
+ }
43
+
44
+ function buildSummary(sessionId: SessionId): SessionSummaryRef {
45
+ return {
46
+ id: 'sum_disk_archive_1' as SummaryId,
47
+ sessionRef: sessionId,
48
+ tenantId: tenantA,
49
+ outcome: { status: 'succeeded' },
50
+ deliverables: [],
51
+ agentSummary: 'done',
52
+ keyDecisions: [{ at: new Date('2026-04-01T00:00:30Z'), summary: 'decided x' }],
53
+ at: new Date('2026-04-01T00:02:00Z'),
54
+ materializedBy: 'kernel',
55
+ }
56
+ }
57
+
58
+ function buildWorkspace(): WorkspaceRef {
59
+ return {
60
+ id: 'wsp_archive_1' as WorkspaceId,
61
+ meta: {
62
+ backend: 'git-worktree',
63
+ repoRoot: '/repo',
64
+ branch: 'feature/x',
65
+ worktreePath: '/repo/worktrees/x',
66
+ },
67
+ createdAt: new Date('2026-04-01T00:00:00Z'),
68
+ }
69
+ }
70
+
71
+ describe('DiskArchiveBackend', () => {
72
+ let rootDir: string
73
+ let backend: DiskArchiveBackend
74
+
75
+ beforeEach(() => {
76
+ rootDir = mkdtempSync(join(tmpdir(), 'namzu-archive-disk-'))
77
+ backend = new DiskArchiveBackend({ rootDir })
78
+ })
79
+
80
+ afterEach(() => {
81
+ rmSync(rootDir, { recursive: true, force: true })
82
+ })
83
+
84
+ it('store + restore round-trip preserves summary, messages, and subsession metadata', async () => {
85
+ const sessionId = fakeSessionId()
86
+ const subSessionId = fakeSubSessionId()
87
+ const messages = buildMessages(sessionId)
88
+ const summaryRef = buildSummary(sessionId)
89
+ const workspace = buildWorkspace()
90
+
91
+ const out = await backend.store({
92
+ subSessionId,
93
+ sessionId,
94
+ tenantId: tenantA,
95
+ workspace,
96
+ summaryRef,
97
+ messages,
98
+ })
99
+
100
+ expect(out.archiveRef).toMatch(/^arc_/)
101
+ expect(out.archivedAt).toBeInstanceOf(Date)
102
+
103
+ const restored = await backend.restore(out.archiveRef)
104
+ expect(restored.subSessionId).toBe(subSessionId)
105
+ expect(restored.sessionId).toBe(sessionId)
106
+ expect(restored.tenantId).toBe(tenantA)
107
+ expect(restored.workspace?.id).toBe(workspace.id)
108
+ expect(restored.summaryRef?.id).toBe(summaryRef.id)
109
+ expect(restored.summaryRef?.at).toBeInstanceOf(Date)
110
+ expect(restored.messages).toHaveLength(2)
111
+ expect(restored.messages[0]?.at).toBeInstanceOf(Date)
112
+ })
113
+
114
+ it('archive location is under {rootDir}/archive/{arc_*}/', async () => {
115
+ const out = await backend.store({
116
+ subSessionId: fakeSubSessionId(),
117
+ sessionId: fakeSessionId(),
118
+ tenantId: tenantA,
119
+ messages: [],
120
+ })
121
+
122
+ const archivesRoot = join(rootDir, 'archive')
123
+ const entries = await readdir(archivesRoot)
124
+ expect(entries).toContain(out.archiveRef)
125
+ })
126
+
127
+ it('writes the archive.json marker last (atomic via tmp-rename; no stray .tmp after)', async () => {
128
+ const out = await backend.store({
129
+ subSessionId: fakeSubSessionId(),
130
+ sessionId: fakeSessionId(),
131
+ tenantId: tenantA,
132
+ messages: buildMessages(fakeSessionId()),
133
+ })
134
+
135
+ const dir = join(rootDir, 'archive', out.archiveRef)
136
+ const entries = await readdir(dir)
137
+ expect(entries).toContain('archive.json')
138
+ expect(entries).toContain('subsession.json')
139
+ expect(entries).toContain('messages.jsonl')
140
+ // No lingering .tmp files from the write-tmp-rename sequence.
141
+ expect(entries.some((e) => e.endsWith('.tmp'))).toBe(false)
142
+
143
+ const marker = JSON.parse(await readFile(join(dir, 'archive.json'), 'utf-8'))
144
+ expect(marker.archiveRef).toBe(out.archiveRef)
145
+ })
146
+
147
+ it('restore on missing archive throws ArchiveNotFoundError (reason: missing)', async () => {
148
+ await expect(backend.restore('arc_nonexistent' as ArchiveBackendRef)).rejects.toMatchObject({
149
+ name: 'ArchiveNotFoundError',
150
+ details: { reason: 'missing' },
151
+ })
152
+ })
153
+
154
+ it('multiple archives receive unique refs (no collisions)', async () => {
155
+ const refs = new Set<string>()
156
+ for (let i = 0; i < 5; i++) {
157
+ const out = await backend.store({
158
+ subSessionId: fakeSubSessionId(),
159
+ sessionId: fakeSessionId(),
160
+ tenantId: tenantA,
161
+ messages: [],
162
+ })
163
+ refs.add(out.archiveRef)
164
+ }
165
+ expect(refs.size).toBe(5)
166
+ })
167
+
168
+ it('store without optional fields (no workspace, no summary, no messages) still round-trips', async () => {
169
+ const out = await backend.store({
170
+ subSessionId: fakeSubSessionId(),
171
+ sessionId: fakeSessionId(),
172
+ tenantId: tenantA,
173
+ messages: [],
174
+ })
175
+ const restored = await backend.restore(out.archiveRef)
176
+ expect(restored.workspace).toBeUndefined()
177
+ expect(restored.summaryRef).toBeUndefined()
178
+ expect(restored.messages).toHaveLength(0)
179
+ })
180
+ })
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Branded lookup identifier for an archived sub-session in a pluggable
3
+ * {@link ArchiveBackend}. Produced by
4
+ * {@link ArchiveBackend.store} on a successful archival write and consumed by
5
+ * {@link ArchiveBackend.restore} to re-hydrate the bundle.
6
+ *
7
+ * Follows Convention #2 (`<prefix>_<opaque>` template literal brand). Prefix
8
+ * chosen as `arc_` to match the `archive` domain — short, unambiguous, and
9
+ * distinct from every existing prefix (`prj_`, `ses_`, `sub_`, `sum_`, …).
10
+ *
11
+ * The string after the prefix is opaque — backend-specific. A disk backend
12
+ * embeds it in a directory path; an object-store backend may encode a key;
13
+ * callers MUST NOT parse or pattern-match beyond the brand check.
14
+ *
15
+ * See session-hierarchy.md §12.3 Retention and Archival.
16
+ */
17
+ export type ArchiveBackendRef = `arc_${string}`