@kynetic-ai/spec 0.10.0 → 0.12.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 (487) hide show
  1. package/README.md +55 -455
  2. package/dist/agent-runtime/bootstrap.d.ts +31 -0
  3. package/dist/agent-runtime/bootstrap.d.ts.map +1 -0
  4. package/dist/agent-runtime/bootstrap.js +302 -0
  5. package/dist/agent-runtime/bootstrap.js.map +1 -0
  6. package/dist/agent-runtime/dispatch.d.ts +150 -10
  7. package/dist/agent-runtime/dispatch.d.ts.map +1 -1
  8. package/dist/agent-runtime/dispatch.js +1248 -244
  9. package/dist/agent-runtime/dispatch.js.map +1 -1
  10. package/dist/agent-runtime/invocation.d.ts +28 -1
  11. package/dist/agent-runtime/invocation.d.ts.map +1 -1
  12. package/dist/agent-runtime/invocation.js +172 -60
  13. package/dist/agent-runtime/invocation.js.map +1 -1
  14. package/dist/agent-runtime/prompts.d.ts +9 -0
  15. package/dist/agent-runtime/prompts.d.ts.map +1 -1
  16. package/dist/agent-runtime/prompts.js +42 -7
  17. package/dist/agent-runtime/prompts.js.map +1 -1
  18. package/dist/agent-runtime/session-event-accumulator.d.ts +83 -0
  19. package/dist/agent-runtime/session-event-accumulator.d.ts.map +1 -0
  20. package/dist/agent-runtime/session-event-accumulator.js +203 -0
  21. package/dist/agent-runtime/session-event-accumulator.js.map +1 -0
  22. package/dist/agent-runtime/session-event-types.d.ts +67 -0
  23. package/dist/agent-runtime/session-event-types.d.ts.map +1 -0
  24. package/dist/agent-runtime/session-event-types.js +13 -0
  25. package/dist/agent-runtime/session-event-types.js.map +1 -0
  26. package/dist/agent-runtime/workspace.d.ts +244 -0
  27. package/dist/agent-runtime/workspace.d.ts.map +1 -0
  28. package/dist/agent-runtime/workspace.js +2025 -0
  29. package/dist/agent-runtime/workspace.js.map +1 -0
  30. package/dist/agents/adapters.d.ts.map +1 -1
  31. package/dist/agents/adapters.js +58 -13
  32. package/dist/agents/adapters.js.map +1 -1
  33. package/dist/agents/spawner.d.ts +8 -0
  34. package/dist/agents/spawner.d.ts.map +1 -1
  35. package/dist/agents/spawner.js +25 -3
  36. package/dist/agents/spawner.js.map +1 -1
  37. package/dist/cli/batch-exec.js +1 -1
  38. package/dist/cli/batch-exec.js.map +1 -1
  39. package/dist/cli/command-annotations.d.ts +15 -3
  40. package/dist/cli/command-annotations.d.ts.map +1 -1
  41. package/dist/cli/command-annotations.js +23 -3
  42. package/dist/cli/command-annotations.js.map +1 -1
  43. package/dist/cli/commands/agent.d.ts +2 -0
  44. package/dist/cli/commands/agent.d.ts.map +1 -1
  45. package/dist/cli/commands/agent.js +144 -27
  46. package/dist/cli/commands/agent.js.map +1 -1
  47. package/dist/cli/commands/agents.d.ts.map +1 -1
  48. package/dist/cli/commands/agents.js +5 -5
  49. package/dist/cli/commands/agents.js.map +1 -1
  50. package/dist/cli/commands/derive.d.ts.map +1 -1
  51. package/dist/cli/commands/derive.js +118 -3
  52. package/dist/cli/commands/derive.js.map +1 -1
  53. package/dist/cli/commands/guard.d.ts.map +1 -1
  54. package/dist/cli/commands/guard.js +8 -6
  55. package/dist/cli/commands/guard.js.map +1 -1
  56. package/dist/cli/commands/index.d.ts +1 -0
  57. package/dist/cli/commands/index.d.ts.map +1 -1
  58. package/dist/cli/commands/index.js +1 -0
  59. package/dist/cli/commands/index.js.map +1 -1
  60. package/dist/cli/commands/init.d.ts.map +1 -1
  61. package/dist/cli/commands/init.js +20 -0
  62. package/dist/cli/commands/init.js.map +1 -1
  63. package/dist/cli/commands/item.d.ts.map +1 -1
  64. package/dist/cli/commands/item.js +205 -47
  65. package/dist/cli/commands/item.js.map +1 -1
  66. package/dist/cli/commands/log.d.ts.map +1 -1
  67. package/dist/cli/commands/log.js +24 -10
  68. package/dist/cli/commands/log.js.map +1 -1
  69. package/dist/cli/commands/meta.d.ts.map +1 -1
  70. package/dist/cli/commands/meta.js +10 -1
  71. package/dist/cli/commands/meta.js.map +1 -1
  72. package/dist/cli/commands/plan-import.d.ts +3 -3
  73. package/dist/cli/commands/plan-import.d.ts.map +1 -1
  74. package/dist/cli/commands/plan-import.js +213 -528
  75. package/dist/cli/commands/plan-import.js.map +1 -1
  76. package/dist/cli/commands/plan.d.ts.map +1 -1
  77. package/dist/cli/commands/plan.js +533 -83
  78. package/dist/cli/commands/plan.js.map +1 -1
  79. package/dist/cli/commands/review.d.ts +14 -0
  80. package/dist/cli/commands/review.d.ts.map +1 -0
  81. package/dist/cli/commands/review.js +1142 -0
  82. package/dist/cli/commands/review.js.map +1 -0
  83. package/dist/cli/commands/serve.d.ts +1 -0
  84. package/dist/cli/commands/serve.d.ts.map +1 -1
  85. package/dist/cli/commands/serve.js +33 -10
  86. package/dist/cli/commands/serve.js.map +1 -1
  87. package/dist/cli/commands/session/checkpoint.d.ts +2 -4
  88. package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
  89. package/dist/cli/commands/session/checkpoint.js +6 -107
  90. package/dist/cli/commands/session/checkpoint.js.map +1 -1
  91. package/dist/cli/commands/session/commands.d.ts.map +1 -1
  92. package/dist/cli/commands/session/commands.js +33 -23
  93. package/dist/cli/commands/session/commands.js.map +1 -1
  94. package/dist/cli/commands/session/compact.js +4 -4
  95. package/dist/cli/commands/session/compact.js.map +1 -1
  96. package/dist/cli/commands/session/create.js +2 -2
  97. package/dist/cli/commands/session/create.js.map +1 -1
  98. package/dist/cli/commands/session/format.d.ts.map +1 -1
  99. package/dist/cli/commands/session/format.js +1 -6
  100. package/dist/cli/commands/session/format.js.map +1 -1
  101. package/dist/cli/commands/session/log.d.ts +32 -7
  102. package/dist/cli/commands/session/log.d.ts.map +1 -1
  103. package/dist/cli/commands/session/log.js +166 -60
  104. package/dist/cli/commands/session/log.js.map +1 -1
  105. package/dist/cli/commands/session/migrate.d.ts +9 -0
  106. package/dist/cli/commands/session/migrate.d.ts.map +1 -0
  107. package/dist/cli/commands/session/migrate.js +46 -0
  108. package/dist/cli/commands/session/migrate.js.map +1 -0
  109. package/dist/cli/commands/session/stale-close.d.ts.map +1 -1
  110. package/dist/cli/commands/session/stale-close.js +5 -8
  111. package/dist/cli/commands/session/stale-close.js.map +1 -1
  112. package/dist/cli/commands/session/types.d.ts +1 -1
  113. package/dist/cli/commands/session/types.d.ts.map +1 -1
  114. package/dist/cli/commands/setup.d.ts +2 -2
  115. package/dist/cli/commands/setup.d.ts.map +1 -1
  116. package/dist/cli/commands/setup.js +287 -257
  117. package/dist/cli/commands/setup.js.map +1 -1
  118. package/dist/cli/commands/shadow.d.ts.map +1 -1
  119. package/dist/cli/commands/shadow.js +147 -31
  120. package/dist/cli/commands/shadow.js.map +1 -1
  121. package/dist/cli/commands/skill-crud.d.ts +7 -0
  122. package/dist/cli/commands/skill-crud.d.ts.map +1 -1
  123. package/dist/cli/commands/skill-crud.js +41 -18
  124. package/dist/cli/commands/skill-crud.js.map +1 -1
  125. package/dist/cli/commands/skill-diff.d.ts.map +1 -1
  126. package/dist/cli/commands/skill-diff.js +29 -3
  127. package/dist/cli/commands/skill-diff.js.map +1 -1
  128. package/dist/cli/commands/skill-install.d.ts.map +1 -1
  129. package/dist/cli/commands/skill-install.js +5 -4
  130. package/dist/cli/commands/skill-install.js.map +1 -1
  131. package/dist/cli/commands/task.d.ts.map +1 -1
  132. package/dist/cli/commands/task.js +359 -49
  133. package/dist/cli/commands/task.js.map +1 -1
  134. package/dist/cli/commands/trait.d.ts.map +1 -1
  135. package/dist/cli/commands/trait.js +5 -27
  136. package/dist/cli/commands/trait.js.map +1 -1
  137. package/dist/cli/commands/validate.d.ts.map +1 -1
  138. package/dist/cli/commands/validate.js +113 -52
  139. package/dist/cli/commands/validate.js.map +1 -1
  140. package/dist/cli/index.d.ts.map +1 -1
  141. package/dist/cli/index.js +69 -2
  142. package/dist/cli/index.js.map +1 -1
  143. package/dist/cli/output.d.ts +26 -0
  144. package/dist/cli/output.d.ts.map +1 -1
  145. package/dist/cli/output.js +108 -1
  146. package/dist/cli/output.js.map +1 -1
  147. package/dist/cli/sync-mode.d.ts +44 -0
  148. package/dist/cli/sync-mode.d.ts.map +1 -0
  149. package/dist/cli/sync-mode.js +64 -0
  150. package/dist/cli/sync-mode.js.map +1 -0
  151. package/dist/daemon/middleware/project-context.ts +25 -7
  152. package/dist/daemon/project-context.ts +18 -0
  153. package/dist/daemon/routes/agent-dispatch.ts +107 -23
  154. package/dist/daemon/routes/aggregation.ts +184 -0
  155. package/dist/daemon/routes/inbox.ts +5 -0
  156. package/dist/daemon/routes/items.ts +167 -0
  157. package/dist/daemon/routes/meta.ts +141 -1
  158. package/dist/daemon/routes/plans.ts +147 -0
  159. package/dist/daemon/routes/projects.ts +28 -6
  160. package/dist/daemon/routes/ref-resolution.ts +119 -0
  161. package/dist/daemon/routes/refs.ts +42 -0
  162. package/dist/daemon/routes/session-related.ts +140 -0
  163. package/dist/daemon/routes/sessions.ts +581 -0
  164. package/dist/daemon/routes/tasks.ts +257 -2
  165. package/dist/daemon/routes/triage.ts +40 -1
  166. package/dist/daemon/routes/validation.ts +1 -1
  167. package/dist/daemon/server.ts +165 -50
  168. package/dist/daemon/session-sync.ts +11 -0
  169. package/dist/daemon/shadow-sync.ts +11 -0
  170. package/dist/daemon/watcher.ts +56 -5
  171. package/dist/daemon/websocket/project-resolution.ts +77 -0
  172. package/dist/export/json.d.ts.map +1 -1
  173. package/dist/export/json.js +104 -1
  174. package/dist/export/json.js.map +1 -1
  175. package/dist/export/types.d.ts +52 -1
  176. package/dist/export/types.d.ts.map +1 -1
  177. package/dist/index.d.ts +1 -0
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +1 -0
  180. package/dist/index.js.map +1 -1
  181. package/dist/parser/agent-detection.d.ts +1 -1
  182. package/dist/parser/agent-detection.d.ts.map +1 -1
  183. package/dist/parser/agent-detection.js +10 -0
  184. package/dist/parser/agent-detection.js.map +1 -1
  185. package/dist/parser/alignment.d.ts.map +1 -1
  186. package/dist/parser/alignment.js +4 -2
  187. package/dist/parser/alignment.js.map +1 -1
  188. package/dist/parser/config.d.ts +397 -2
  189. package/dist/parser/config.d.ts.map +1 -1
  190. package/dist/parser/config.js +125 -3
  191. package/dist/parser/config.js.map +1 -1
  192. package/dist/parser/dispatch-workspaces.d.ts +18 -0
  193. package/dist/parser/dispatch-workspaces.d.ts.map +1 -0
  194. package/dist/parser/dispatch-workspaces.js +209 -0
  195. package/dist/parser/dispatch-workspaces.js.map +1 -0
  196. package/dist/parser/doctor.d.ts.map +1 -1
  197. package/dist/parser/doctor.js +27 -8
  198. package/dist/parser/doctor.js.map +1 -1
  199. package/dist/parser/file-lock.d.ts.map +1 -1
  200. package/dist/parser/file-lock.js +9 -2
  201. package/dist/parser/file-lock.js.map +1 -1
  202. package/dist/parser/index.d.ts +6 -0
  203. package/dist/parser/index.d.ts.map +1 -1
  204. package/dist/parser/index.js +6 -0
  205. package/dist/parser/index.js.map +1 -1
  206. package/dist/parser/plans.d.ts.map +1 -1
  207. package/dist/parser/plans.js +1 -0
  208. package/dist/parser/plans.js.map +1 -1
  209. package/dist/parser/refs.d.ts +8 -1
  210. package/dist/parser/refs.d.ts.map +1 -1
  211. package/dist/parser/refs.js +27 -1
  212. package/dist/parser/refs.js.map +1 -1
  213. package/dist/parser/review-operations.d.ts +72 -0
  214. package/dist/parser/review-operations.d.ts.map +1 -0
  215. package/dist/parser/review-operations.js +185 -0
  216. package/dist/parser/review-operations.js.map +1 -0
  217. package/dist/parser/review-task-integration.d.ts +78 -0
  218. package/dist/parser/review-task-integration.d.ts.map +1 -0
  219. package/dist/parser/review-task-integration.js +173 -0
  220. package/dist/parser/review-task-integration.js.map +1 -0
  221. package/dist/parser/review-threads.d.ts +101 -0
  222. package/dist/parser/review-threads.d.ts.map +1 -0
  223. package/dist/parser/review-threads.js +222 -0
  224. package/dist/parser/review-threads.js.map +1 -0
  225. package/dist/parser/review-validation.d.ts +69 -0
  226. package/dist/parser/review-validation.d.ts.map +1 -0
  227. package/dist/parser/review-validation.js +207 -0
  228. package/dist/parser/review-validation.js.map +1 -0
  229. package/dist/parser/reviews.d.ts +58 -0
  230. package/dist/parser/reviews.d.ts.map +1 -0
  231. package/dist/parser/reviews.js +230 -0
  232. package/dist/parser/reviews.js.map +1 -0
  233. package/dist/parser/session-branch.d.ts +91 -0
  234. package/dist/parser/session-branch.d.ts.map +1 -0
  235. package/dist/parser/session-branch.js +565 -0
  236. package/dist/parser/session-branch.js.map +1 -0
  237. package/dist/parser/session-sync-scheduler.d.ts +53 -0
  238. package/dist/parser/session-sync-scheduler.d.ts.map +1 -0
  239. package/dist/parser/session-sync-scheduler.js +100 -0
  240. package/dist/parser/session-sync-scheduler.js.map +1 -0
  241. package/dist/parser/setup-status.d.ts +7 -1
  242. package/dist/parser/setup-status.d.ts.map +1 -1
  243. package/dist/parser/setup-status.js +104 -39
  244. package/dist/parser/setup-status.js.map +1 -1
  245. package/dist/parser/shadow-sync-scheduler.d.ts +71 -0
  246. package/dist/parser/shadow-sync-scheduler.d.ts.map +1 -0
  247. package/dist/parser/shadow-sync-scheduler.js +139 -0
  248. package/dist/parser/shadow-sync-scheduler.js.map +1 -0
  249. package/dist/parser/shadow.d.ts +121 -14
  250. package/dist/parser/shadow.d.ts.map +1 -1
  251. package/dist/parser/shadow.js +752 -27
  252. package/dist/parser/shadow.js.map +1 -1
  253. package/dist/parser/skill-render.d.ts +24 -0
  254. package/dist/parser/skill-render.d.ts.map +1 -1
  255. package/dist/parser/skill-render.js +98 -26
  256. package/dist/parser/skill-render.js.map +1 -1
  257. package/dist/parser/validate.d.ts +43 -3
  258. package/dist/parser/validate.d.ts.map +1 -1
  259. package/dist/parser/validate.js +204 -30
  260. package/dist/parser/validate.js.map +1 -1
  261. package/dist/parser/yaml.d.ts +47 -11
  262. package/dist/parser/yaml.d.ts.map +1 -1
  263. package/dist/parser/yaml.js +329 -149
  264. package/dist/parser/yaml.js.map +1 -1
  265. package/dist/review/checks.d.ts +97 -0
  266. package/dist/review/checks.d.ts.map +1 -0
  267. package/dist/review/checks.js +175 -0
  268. package/dist/review/checks.js.map +1 -0
  269. package/dist/review/index.d.ts +3 -0
  270. package/dist/review/index.d.ts.map +1 -0
  271. package/dist/review/index.js +3 -0
  272. package/dist/review/index.js.map +1 -0
  273. package/dist/review/subject-bindings.d.ts +83 -0
  274. package/dist/review/subject-bindings.d.ts.map +1 -0
  275. package/dist/review/subject-bindings.js +175 -0
  276. package/dist/review/subject-bindings.js.map +1 -0
  277. package/dist/schema/common.d.ts +26 -0
  278. package/dist/schema/common.d.ts.map +1 -1
  279. package/dist/schema/common.js +13 -0
  280. package/dist/schema/common.js.map +1 -1
  281. package/dist/schema/dispatch-workspace.d.ts +2643 -0
  282. package/dist/schema/dispatch-workspace.d.ts.map +1 -0
  283. package/dist/schema/dispatch-workspace.js +187 -0
  284. package/dist/schema/dispatch-workspace.js.map +1 -0
  285. package/dist/schema/inbox.d.ts +8 -8
  286. package/dist/schema/index.d.ts +2 -0
  287. package/dist/schema/index.d.ts.map +1 -1
  288. package/dist/schema/index.js +2 -0
  289. package/dist/schema/index.js.map +1 -1
  290. package/dist/schema/meta.d.ts +663 -116
  291. package/dist/schema/meta.d.ts.map +1 -1
  292. package/dist/schema/meta.js +28 -0
  293. package/dist/schema/meta.js.map +1 -1
  294. package/dist/schema/plan.d.ts +30 -19
  295. package/dist/schema/plan.d.ts.map +1 -1
  296. package/dist/schema/plan.js +3 -1
  297. package/dist/schema/plan.js.map +1 -1
  298. package/dist/schema/review-records.d.ts +2676 -0
  299. package/dist/schema/review-records.d.ts.map +1 -0
  300. package/dist/schema/review-records.js +232 -0
  301. package/dist/schema/review-records.js.map +1 -0
  302. package/dist/schema/spec.d.ts +32 -14
  303. package/dist/schema/spec.d.ts.map +1 -1
  304. package/dist/schema/spec.js +5 -0
  305. package/dist/schema/spec.js.map +1 -1
  306. package/dist/schema/task.d.ts +187 -29
  307. package/dist/schema/task.d.ts.map +1 -1
  308. package/dist/schema/task.js +12 -2
  309. package/dist/schema/task.js.map +1 -1
  310. package/dist/schema/triage.d.ts +22 -22
  311. package/dist/sessions/cache.d.ts +119 -0
  312. package/dist/sessions/cache.d.ts.map +1 -0
  313. package/dist/sessions/cache.js +284 -0
  314. package/dist/sessions/cache.js.map +1 -0
  315. package/dist/sessions/index.d.ts +1 -0
  316. package/dist/sessions/index.d.ts.map +1 -1
  317. package/dist/sessions/index.js +2 -0
  318. package/dist/sessions/index.js.map +1 -1
  319. package/dist/sessions/legacy.d.ts +77 -0
  320. package/dist/sessions/legacy.d.ts.map +1 -0
  321. package/dist/sessions/legacy.js +146 -0
  322. package/dist/sessions/legacy.js.map +1 -0
  323. package/dist/sessions/store.d.ts +115 -71
  324. package/dist/sessions/store.d.ts.map +1 -1
  325. package/dist/sessions/store.js +357 -182
  326. package/dist/sessions/store.js.map +1 -1
  327. package/dist/sessions/types.d.ts +44 -16
  328. package/dist/sessions/types.d.ts.map +1 -1
  329. package/dist/sessions/types.js +11 -2
  330. package/dist/sessions/types.js.map +1 -1
  331. package/dist/strings/errors.d.ts +32 -0
  332. package/dist/strings/errors.d.ts.map +1 -1
  333. package/dist/strings/errors.js +17 -0
  334. package/dist/strings/errors.js.map +1 -1
  335. package/dist/strings/labels.d.ts +1 -0
  336. package/dist/strings/labels.d.ts.map +1 -1
  337. package/dist/strings/labels.js +1 -0
  338. package/dist/strings/labels.js.map +1 -1
  339. package/dist/utils/activity.d.ts +101 -0
  340. package/dist/utils/activity.d.ts.map +1 -0
  341. package/dist/utils/activity.js +408 -0
  342. package/dist/utils/activity.js.map +1 -0
  343. package/dist/utils/git.d.ts +31 -0
  344. package/dist/utils/git.d.ts.map +1 -1
  345. package/dist/utils/git.js +87 -0
  346. package/dist/utils/git.js.map +1 -1
  347. package/dist/utils/index.d.ts +2 -0
  348. package/dist/utils/index.d.ts.map +1 -1
  349. package/dist/utils/index.js +1 -0
  350. package/dist/utils/index.js.map +1 -1
  351. package/dist/web-ui/_app/immutable/assets/0.tmlwn-Ih.css +1 -0
  352. package/dist/web-ui/_app/immutable/assets/9.BwwJybWx.css +1 -0
  353. package/dist/web-ui/_app/immutable/chunks/2KqE8gtn.js +1 -0
  354. package/dist/web-ui/_app/immutable/chunks/70-t_QvE.js +1 -0
  355. package/dist/web-ui/_app/immutable/chunks/AiWQj974.js +1 -0
  356. package/dist/web-ui/_app/immutable/chunks/B25nWFyA.js +5 -0
  357. package/dist/web-ui/_app/immutable/chunks/B2bcA_Q_.js +1 -0
  358. package/dist/web-ui/_app/immutable/chunks/B5e5HYyB.js +1 -0
  359. package/dist/web-ui/_app/immutable/chunks/B7-5z6eA.js +1 -0
  360. package/dist/web-ui/_app/immutable/chunks/B7bGmhK0.js +1 -0
  361. package/dist/web-ui/_app/immutable/chunks/B8tYZKAE.js +1 -0
  362. package/dist/web-ui/_app/immutable/chunks/BFGAyJjD.js +1 -0
  363. package/dist/web-ui/_app/immutable/chunks/BG0850zf.js +1 -0
  364. package/dist/web-ui/_app/immutable/chunks/BG8eSzAd.js +1 -0
  365. package/dist/web-ui/_app/immutable/chunks/BIMxXS8I.js +1 -0
  366. package/dist/web-ui/_app/immutable/chunks/BSzL1fpU.js +1 -0
  367. package/dist/web-ui/_app/immutable/chunks/BYtjHfeq.js +1 -0
  368. package/dist/web-ui/_app/immutable/chunks/{D1ArdqNb.js → Bp5pFYXL.js} +1 -1
  369. package/dist/web-ui/_app/immutable/chunks/BsJFsuAT.js +1 -0
  370. package/dist/web-ui/_app/immutable/chunks/BvpNHcD6.js +1 -0
  371. package/dist/web-ui/_app/immutable/chunks/BypqA25-.js +1 -0
  372. package/dist/web-ui/_app/immutable/chunks/C0w6WDm5.js +1 -0
  373. package/dist/web-ui/_app/immutable/chunks/C5_PAZ0y.js +1 -0
  374. package/dist/web-ui/_app/immutable/chunks/CDRO15Iv.js +1 -0
  375. package/dist/web-ui/_app/immutable/chunks/CF1CoqD5.js +1 -0
  376. package/dist/web-ui/_app/immutable/chunks/CS2sa4_m.js +1 -0
  377. package/dist/web-ui/_app/immutable/chunks/CWUQwB9H.js +1 -0
  378. package/dist/web-ui/_app/immutable/chunks/CY5FDdSU.js +1 -0
  379. package/dist/web-ui/_app/immutable/chunks/C_7MTDoj.js +1 -0
  380. package/dist/web-ui/_app/immutable/chunks/CaAJD3dl.js +1 -0
  381. package/dist/web-ui/_app/immutable/chunks/{i-XnOIX0.js → ChB5iyEL.js} +1 -1
  382. package/dist/web-ui/_app/immutable/chunks/ChQD-6N8.js +1 -0
  383. package/dist/web-ui/_app/immutable/chunks/{BCkp8Hs8.js → CqbsoCwA.js} +1 -1
  384. package/dist/web-ui/_app/immutable/chunks/DCeJW50p.js +1 -0
  385. package/dist/web-ui/_app/immutable/chunks/DJtZNgcs.js +1 -0
  386. package/dist/web-ui/_app/immutable/chunks/DKIeaprD.js +1 -0
  387. package/dist/web-ui/_app/immutable/chunks/DLd2uVIA.js +1 -0
  388. package/dist/web-ui/_app/immutable/chunks/DW_subyT.js +2 -0
  389. package/dist/web-ui/_app/immutable/chunks/DbU6lVn0.js +1 -0
  390. package/dist/web-ui/_app/immutable/chunks/Dc7ZCC5m.js +1 -0
  391. package/dist/web-ui/_app/immutable/chunks/Dd5umPsk.js +2 -0
  392. package/dist/web-ui/_app/immutable/chunks/Dg_zDpDS.js +1 -0
  393. package/dist/web-ui/_app/immutable/chunks/Dgqu8Yuc.js +1 -0
  394. package/dist/web-ui/_app/immutable/chunks/DmxsPZTB.js +1 -0
  395. package/dist/web-ui/_app/immutable/chunks/DphTaFUB.js +1 -0
  396. package/dist/web-ui/_app/immutable/chunks/DqK4iHp0.js +1 -0
  397. package/dist/web-ui/_app/immutable/chunks/DqT6OH_u.js +2 -0
  398. package/dist/web-ui/_app/immutable/chunks/Ds9I9wQb.js +1 -0
  399. package/dist/web-ui/_app/immutable/chunks/Du5ng3u4.js +1 -0
  400. package/dist/web-ui/_app/immutable/chunks/DxJw79Wi.js +1 -0
  401. package/dist/web-ui/_app/immutable/chunks/GFTX8GgV.js +1 -0
  402. package/dist/web-ui/_app/immutable/chunks/HNjs76Zz.js +1 -0
  403. package/dist/web-ui/_app/immutable/chunks/HVMjDi4_.js +1 -0
  404. package/dist/web-ui/_app/immutable/chunks/P0A_fJvS.js +1 -0
  405. package/dist/web-ui/_app/immutable/chunks/T3vGWjIL.js +1 -0
  406. package/dist/web-ui/_app/immutable/chunks/VTmrX9Qu.js +1 -0
  407. package/dist/web-ui/_app/immutable/chunks/Xvwhx_F1.js +1 -0
  408. package/dist/web-ui/_app/immutable/chunks/Yyz1XMQA.js +1 -0
  409. package/dist/web-ui/_app/immutable/chunks/dh5HeqUr.js +1 -0
  410. package/dist/web-ui/_app/immutable/chunks/fZMteyca.js +62 -0
  411. package/dist/web-ui/_app/immutable/chunks/{D28BF5MJ.js → gPrj-hqC.js} +1 -1
  412. package/dist/web-ui/_app/immutable/chunks/htcWMiYN.js +1 -0
  413. package/dist/web-ui/_app/immutable/chunks/oTsvd9y4.js +1 -0
  414. package/dist/web-ui/_app/immutable/chunks/qJfLUwU4.js +1 -0
  415. package/dist/web-ui/_app/immutable/chunks/xCtiO_JE.js +1 -0
  416. package/dist/web-ui/_app/immutable/chunks/y4GeEH6k.js +1 -0
  417. package/dist/web-ui/_app/immutable/entry/app.C4h_eOn6.js +2 -0
  418. package/dist/web-ui/_app/immutable/entry/start.CQFTf9ep.js +1 -0
  419. package/dist/web-ui/_app/immutable/nodes/0.Dh1xO970.js +1 -0
  420. package/dist/web-ui/_app/immutable/nodes/1.l75D3Opx.js +1 -0
  421. package/dist/web-ui/_app/immutable/nodes/10.DBidBPc-.js +1 -0
  422. package/dist/web-ui/_app/immutable/nodes/11.Ab0gUKWe.js +1 -0
  423. package/dist/web-ui/_app/immutable/nodes/12.CMsnoxfs.js +1 -0
  424. package/dist/web-ui/_app/immutable/nodes/13.D8YKuknB.js +1 -0
  425. package/dist/web-ui/_app/immutable/nodes/14.DZ0aan7y.js +1 -0
  426. package/dist/web-ui/_app/immutable/nodes/15.CUIKreDL.js +2 -0
  427. package/dist/web-ui/_app/immutable/nodes/16.BWc8--BO.js +1 -0
  428. package/dist/web-ui/_app/immutable/nodes/2.CDUonbuh.js +1 -0
  429. package/dist/web-ui/_app/immutable/nodes/3.Ctg3M00i.js +1 -0
  430. package/dist/web-ui/_app/immutable/nodes/4.Ci-JDwbA.js +2 -0
  431. package/dist/web-ui/_app/immutable/nodes/5.CTyEDAq0.js +1 -0
  432. package/dist/web-ui/_app/immutable/nodes/6.BTZZqsAb.js +1 -0
  433. package/dist/web-ui/_app/immutable/nodes/7.BI52g_Jo.js +137 -0
  434. package/dist/web-ui/_app/immutable/nodes/8.3hZPaB9x.js +1 -0
  435. package/dist/web-ui/_app/immutable/nodes/9.DS49kvwl.js +29 -0
  436. package/dist/web-ui/_app/version.json +1 -1
  437. package/dist/web-ui/favicon-192.png +0 -0
  438. package/dist/web-ui/favicon-32.png +0 -0
  439. package/dist/web-ui/favicon.ico +0 -0
  440. package/dist/web-ui/index.html +14 -11
  441. package/package.json +14 -7
  442. package/plugin/.claude-plugin/marketplace.json +1 -1
  443. package/plugin/.claude-plugin/plugin.json +1 -1
  444. package/plugin/plugins/kspec/skills/merge/SKILL.md +127 -0
  445. package/plugin/plugins/kspec/skills/plan/SKILL.md +55 -26
  446. package/plugin/plugins/kspec/skills/review/SKILL.md +350 -133
  447. package/plugin/plugins/kspec/skills/task-work/SKILL.md +96 -106
  448. package/templates/agents-sections/04-pr-workflow.md +15 -12
  449. package/templates/agents-sections/06-ralph-loop.md +15 -10
  450. package/templates/skills/manifest.yaml +25 -7
  451. package/templates/skills/merge/SKILL.md +120 -0
  452. package/templates/skills/plan/SKILL.md +55 -26
  453. package/templates/skills/review/SKILL.md +346 -130
  454. package/templates/skills/task-work/SKILL.md +93 -103
  455. package/dist/web-ui/_app/immutable/assets/0.BxCxvrZR.css +0 -1
  456. package/dist/web-ui/_app/immutable/chunks/B-CZR0q8.js +0 -1
  457. package/dist/web-ui/_app/immutable/chunks/B1IR5Su5.js +0 -1
  458. package/dist/web-ui/_app/immutable/chunks/B_Cvvtc4.js +0 -1
  459. package/dist/web-ui/_app/immutable/chunks/BtFaGGII.js +0 -1
  460. package/dist/web-ui/_app/immutable/chunks/Bu8JVsCH.js +0 -1
  461. package/dist/web-ui/_app/immutable/chunks/C87u-CNA.js +0 -1
  462. package/dist/web-ui/_app/immutable/chunks/CrFkBTYp.js +0 -1
  463. package/dist/web-ui/_app/immutable/chunks/D6RtLpzL.js +0 -1
  464. package/dist/web-ui/_app/immutable/chunks/D7FHSgx2.js +0 -1
  465. package/dist/web-ui/_app/immutable/chunks/DBXrsxZQ.js +0 -2
  466. package/dist/web-ui/_app/immutable/chunks/Da_hHMuA.js +0 -1
  467. package/dist/web-ui/_app/immutable/chunks/Do6LchSF.js +0 -1
  468. package/dist/web-ui/_app/immutable/chunks/DoNPtcAw.js +0 -1
  469. package/dist/web-ui/_app/immutable/chunks/DtUbXRZz.js +0 -1
  470. package/dist/web-ui/_app/immutable/chunks/DyFPRlLl.js +0 -1
  471. package/dist/web-ui/_app/immutable/chunks/DzAP8lRM.js +0 -1
  472. package/dist/web-ui/_app/immutable/chunks/DzVXElzN.js +0 -2
  473. package/dist/web-ui/_app/immutable/chunks/aoPBFken.js +0 -1
  474. package/dist/web-ui/_app/immutable/chunks/laxtrUO3.js +0 -1
  475. package/dist/web-ui/_app/immutable/chunks/q1nIWgqB.js +0 -1
  476. package/dist/web-ui/_app/immutable/chunks/sTLbk5Nm.js +0 -1
  477. package/dist/web-ui/_app/immutable/chunks/vwKgQu5P.js +0 -5
  478. package/dist/web-ui/_app/immutable/entry/app.BCwMcqnT.js +0 -2
  479. package/dist/web-ui/_app/immutable/entry/start.wKCQH-tt.js +0 -1
  480. package/dist/web-ui/_app/immutable/nodes/0.CjGVMG74.js +0 -1
  481. package/dist/web-ui/_app/immutable/nodes/1.B6_AIPan.js +0 -1
  482. package/dist/web-ui/_app/immutable/nodes/2.q4oCS7Ws.js +0 -1
  483. package/dist/web-ui/_app/immutable/nodes/3.rTKZf9o2.js +0 -1
  484. package/dist/web-ui/_app/immutable/nodes/4.DVIDRu1d.js +0 -1
  485. package/dist/web-ui/_app/immutable/nodes/5.8PtPXIOd.js +0 -1
  486. package/dist/web-ui/_app/immutable/nodes/6.ZZrTemy_.js +0 -1
  487. package/dist/web-ui/_app/immutable/nodes/7.IP-gxCxi.js +0 -1
@@ -0,0 +1,581 @@
1
+ /**
2
+ * Session API Routes
3
+ *
4
+ * REST endpoints for session data:
5
+ * - GET /api/sessions - list sessions with summaries
6
+ * - GET /api/sessions/:id - get session metadata and detail
7
+ * - GET /api/sessions/:id/events - get session events from events.jsonl
8
+ * - GET /api/sessions/:id/events/:seq - get single event with blob resolution
9
+ *
10
+ * AC Coverage:
11
+ * - @ui-session-stream ac-1: Session events as structured blocks
12
+ * - @ui-session-stream ac-4: Session metadata, spec context, budget for context panel
13
+ * - @session-legacy-migration ac-read-fallback: Detect-and-warn on all session read endpoints
14
+ * - @session-summary-cache ac-cache-build: Cache built on first list request
15
+ * - @session-summary-cache ac-cache-invalidate: Cache refreshed via directory listing diff
16
+ * - @session-summary-cache ac-active-refresh: Active sessions recomputed on each request
17
+ * - @session-event-detail-endpoint ac-single-event-fetch: Single event by seq with blob resolution
18
+ * - @session-event-detail-endpoint ac-blob-resolution: Blob pointers resolved to full content
19
+ * - @session-event-detail-endpoint ac-not-found: 404 for missing session or seq
20
+ */
21
+
22
+ import { Elysia, t } from 'elysia';
23
+ import {
24
+ getSession,
25
+ readEvents,
26
+ readEventBySeq,
27
+ deduplicatePhasedToolCalls,
28
+ resolveSessionId,
29
+ resolveSessionBlobPointers,
30
+ getBudget,
31
+ searchSessionEvents,
32
+ type SessionLogSummary,
33
+ } from '../../sessions/store.js';
34
+ import {
35
+ countLegacySessions,
36
+ } from '../../sessions/legacy.js';
37
+ import {
38
+ initContext,
39
+ loadAllTasks,
40
+ loadAllItems,
41
+ ReferenceIndex,
42
+ AlignmentIndex,
43
+ type KspecContext,
44
+ } from '../../parser/index.js';
45
+ import { resolveRefTitle } from './ref-resolution.js';
46
+ import { getSessionCache } from '../../sessions/cache.js';
47
+ import { SessionStatusSchema } from '../../sessions/types.js';
48
+ import { parseTimeSpec } from '../../utils/time.js';
49
+
50
+ type SessionListQuery = {
51
+ status?: string | string[];
52
+ agent_type?: string | string[];
53
+ agent_id?: string | string[];
54
+ trigger?: string | string[];
55
+ task_id?: string;
56
+ spec_ref?: string;
57
+ since?: string;
58
+ };
59
+
60
+ function normalizeValues(value?: string | string[]): string[] {
61
+ if (!value) return [];
62
+ return Array.isArray(value) ? value : [value];
63
+ }
64
+
65
+ function sortSessionSummaries(summaries: SessionLogSummary[]): SessionLogSummary[] {
66
+ return [...summaries].sort(
67
+ (a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime(),
68
+ );
69
+ }
70
+
71
+ function buildTaskRefSet(task: { _ulid: string; slugs: string[] }): Set<string> {
72
+ const refs = new Set<string>([task._ulid, `@${task._ulid}`]);
73
+ for (const slug of task.slugs) {
74
+ refs.add(slug);
75
+ refs.add(`@${slug}`);
76
+ }
77
+ return refs;
78
+ }
79
+
80
+ function filterSessionsByTaskRefs(
81
+ summaries: SessionLogSummary[],
82
+ refs: Set<string>,
83
+ ): SessionLogSummary[] {
84
+ return summaries.filter((summary) => {
85
+ if (!summary.task_id) return false;
86
+ const normalized = summary.task_id.startsWith('@')
87
+ ? summary.task_id.slice(1)
88
+ : summary.task_id;
89
+ return refs.has(summary.task_id) || refs.has(normalized);
90
+ });
91
+ }
92
+
93
+ async function filterSessionSummaries(
94
+ ctx: KspecContext,
95
+ query: SessionListQuery,
96
+ ): Promise<
97
+ | { summaries: SessionLogSummary[]; unfilteredTotal: number }
98
+ | {
99
+ error: {
100
+ status: number;
101
+ body: {
102
+ error: string;
103
+ message?: string;
104
+ suggestion?: string;
105
+ details?: Array<{ field: string; message: string }>;
106
+ };
107
+ };
108
+ }
109
+ > {
110
+ const validStatuses = SessionStatusSchema.options;
111
+ const statusValues = normalizeValues(query.status);
112
+ const invalidStatuses = statusValues.filter(
113
+ (status) => !validStatuses.includes(status as typeof validStatuses[number]),
114
+ );
115
+ if (invalidStatuses.length > 0) {
116
+ return {
117
+ error: {
118
+ status: 400,
119
+ body: {
120
+ error: 'invalid_filter',
121
+ details: [
122
+ {
123
+ field: 'status',
124
+ message: `Invalid status value(s): ${invalidStatuses.join(', ')}. Valid values: ${validStatuses.join(', ')}`,
125
+ },
126
+ ],
127
+ },
128
+ },
129
+ };
130
+ }
131
+
132
+ const sessionCache = getSessionCache(ctx.sessionsDir);
133
+ let filtered = sortSessionSummaries(await sessionCache.getAll(ctx.sessionsDir));
134
+ // AC: @session-filter-controls ac-filter-counts — Capture unfiltered total before applying filters
135
+ const unfilteredTotal = filtered.length;
136
+
137
+ if (statusValues.length > 0) {
138
+ filtered = filtered.filter((summary) => statusValues.includes(summary.status));
139
+ }
140
+
141
+ const agentTypeValues = normalizeValues(query.agent_type);
142
+ if (agentTypeValues.length > 0) {
143
+ filtered = filtered.filter((summary) => agentTypeValues.includes(summary.agent_type));
144
+ }
145
+
146
+ const agentIdValues = normalizeValues(query.agent_id);
147
+ if (agentIdValues.length > 0) {
148
+ filtered = filtered.filter(
149
+ (summary) => summary.agent_id != null && agentIdValues.includes(summary.agent_id),
150
+ );
151
+ }
152
+
153
+ const triggerValues = normalizeValues(query.trigger);
154
+ if (triggerValues.length > 0) {
155
+ filtered = filtered.filter((summary) => {
156
+ if (!summary.trigger) return false;
157
+ return triggerValues.some((value) => {
158
+ if (value === 'dispatched') return summary.trigger!.startsWith('task.');
159
+ return summary.trigger === value;
160
+ });
161
+ });
162
+ }
163
+
164
+ let tasks: Awaited<ReturnType<typeof loadAllTasks>> | null = null;
165
+ let items: Awaited<ReturnType<typeof loadAllItems>> | null = null;
166
+ const ensureAlignmentContext = async () => {
167
+ if (!tasks) tasks = await loadAllTasks(ctx);
168
+ if (!items) items = await loadAllItems(ctx);
169
+ return { tasks, items };
170
+ };
171
+
172
+ if (query.task_id) {
173
+ const { tasks: loadedTasks, items: loadedItems } = await ensureAlignmentContext();
174
+ const refIndex = new ReferenceIndex(loadedTasks, loadedItems);
175
+ const resolved = refIndex.resolve(
176
+ query.task_id.startsWith('@') ? query.task_id.slice(1) : query.task_id,
177
+ );
178
+ if (!resolved.ok) {
179
+ return {
180
+ error: {
181
+ status: 404,
182
+ body: {
183
+ error: 'not_found',
184
+ message: `Task reference "${query.task_id}" not found`,
185
+ suggestion: 'Use GET /api/tasks or kspec task list to find valid task references',
186
+ },
187
+ },
188
+ };
189
+ }
190
+
191
+ const matchTask = loadedTasks.find((task) => task._ulid === resolved.ulid);
192
+ if (matchTask) {
193
+ filtered = filterSessionsByTaskRefs(filtered, buildTaskRefSet(matchTask));
194
+ } else {
195
+ filtered = [];
196
+ }
197
+ }
198
+
199
+ if (query.spec_ref) {
200
+ const { tasks: loadedTasks, items: loadedItems } = await ensureAlignmentContext();
201
+ const refIndex = new ReferenceIndex(loadedTasks, loadedItems);
202
+ const alignmentIndex = new AlignmentIndex(loadedTasks, loadedItems);
203
+ alignmentIndex.buildLinks(refIndex);
204
+
205
+ const resolved = refIndex.resolve(
206
+ query.spec_ref.startsWith('@') ? query.spec_ref.slice(1) : query.spec_ref,
207
+ );
208
+ if (!resolved.ok) {
209
+ return {
210
+ error: {
211
+ status: 404,
212
+ body: {
213
+ error: 'not_found',
214
+ message: `Spec reference "${query.spec_ref}" not found`,
215
+ suggestion: 'Use GET /api/items or kspec item list to find valid spec references',
216
+ },
217
+ },
218
+ };
219
+ }
220
+
221
+ const taskRefs = new Set<string>();
222
+ for (const task of alignmentIndex.getTasksForSpec(resolved.ulid)) {
223
+ for (const ref of buildTaskRefSet(task)) {
224
+ taskRefs.add(ref);
225
+ }
226
+ }
227
+ filtered = filterSessionsByTaskRefs(filtered, taskRefs);
228
+ }
229
+
230
+ if (query.since) {
231
+ const sinceDate = parseTimeSpec(query.since);
232
+ if (!sinceDate) {
233
+ return {
234
+ error: {
235
+ status: 400,
236
+ body: {
237
+ error: 'invalid_filter',
238
+ details: [
239
+ {
240
+ field: 'since',
241
+ message: `Invalid time value: "${query.since}". Use ISO 8601 format or a relative value like 7d, 24h, 2w, or 1m.`,
242
+ },
243
+ ],
244
+ },
245
+ },
246
+ };
247
+ }
248
+ filtered = filtered.filter(
249
+ (summary) => new Date(summary.started_at).getTime() >= sinceDate.getTime(),
250
+ );
251
+ }
252
+
253
+ return { summaries: filtered, unfilteredTotal };
254
+ }
255
+
256
+ export function createSessionRoutes() {
257
+ return new Elysia({ prefix: '/api/sessions' })
258
+
259
+ // List all sessions with summaries, pagination, and filtering
260
+ // AC: @session-legacy-migration ac-read-fallback ac-list-merge — detect-and-warn for legacy sessions
261
+ // AC: @session-summary-cache ac-cache-build — Uses cached summaries instead of re-reading all files
262
+ // AC: @session-list-pagination-api ac-pagination — offset/limit pagination with total
263
+ // AC: @session-list-pagination-api ac-metadata-only — Only reads session.yaml, uses cache
264
+ // AC: @ui-api-ref-resolution ac-1 — Include task_title resolved server-side
265
+ .get('/', async ({ query, error: errorResponse, projectContext }) => {
266
+ const ctx = await initContext(projectContext.path);
267
+ const filteredResult = await filterSessionSummaries(ctx, query);
268
+ if ('error' in filteredResult) {
269
+ return errorResponse(filteredResult.error.status, filteredResult.error.body);
270
+ }
271
+ const filtered = filteredResult.summaries;
272
+ const { unfilteredTotal } = filteredResult;
273
+
274
+ // AC: @session-list-pagination-api ac-pagination — Apply pagination after filtering
275
+ // AC: @trait-api-endpoint ac-4 — {items, total, offset, limit} wrapper
276
+ const total = filtered.length;
277
+ const offset = Number(query.offset) || 0;
278
+ const limit = Number(query.limit) || total;
279
+ const paginated = filtered.slice(offset, offset + limit);
280
+
281
+ // AC: @ui-api-ref-resolution ac-1 — Resolve task_title for session summaries
282
+ let refIndex: ReferenceIndex | null = null;
283
+ const taskIdsPresent = paginated.some((s) => s.task_id);
284
+ if (taskIdsPresent) {
285
+ try {
286
+ const tasks = await loadAllTasks(ctx);
287
+ const items = await loadAllItems(ctx);
288
+ refIndex = new ReferenceIndex(tasks, items);
289
+ } catch {
290
+ // Non-critical — task_title will be null
291
+ }
292
+ }
293
+ const enriched = paginated.map((s) => ({
294
+ ...s,
295
+ task_title: s.task_id && refIndex ? resolveRefTitle(refIndex, s.task_id) : null,
296
+ }));
297
+
298
+ // Detect legacy sessions and include warning in response
299
+ const legacyCount = await countLegacySessions(ctx.specDir);
300
+
301
+ // AC: @session-filter-controls ac-filter-counts — Include unfiltered_total in response
302
+ return {
303
+ items: enriched,
304
+ total,
305
+ unfiltered_total: unfilteredTotal,
306
+ offset,
307
+ limit,
308
+ ...(legacyCount > 0 ? {
309
+ warning: `${legacyCount} legacy session(s) found in .kspec/sessions/. Run \`kspec session migrate\` to move them to .kspec-sessions/.`,
310
+ } : {}),
311
+ };
312
+ }, {
313
+ query: t.Object({
314
+ status: t.Optional(t.Union([t.String(), t.Array(t.String())])),
315
+ agent_type: t.Optional(t.Union([t.String(), t.Array(t.String())])),
316
+ agent_id: t.Optional(t.Union([t.String(), t.Array(t.String())])),
317
+ trigger: t.Optional(t.Union([t.String(), t.Array(t.String())])),
318
+ task_id: t.Optional(t.String()),
319
+ spec_ref: t.Optional(t.String()),
320
+ since: t.Optional(t.String()),
321
+ limit: t.Optional(t.String()),
322
+ offset: t.Optional(t.String()),
323
+ }),
324
+ })
325
+
326
+ // AC: @session-text-search ac-api-search
327
+ // AC: @session-text-search ac-scope-narrowing — metadata filters narrow the scanned sessions first
328
+ .get('/search', async ({ query, error: errorResponse, projectContext }) => {
329
+ const ctx = await initContext(projectContext.path);
330
+ const normalizedQuery = query.q.trim();
331
+ if (normalizedQuery.length === 0) {
332
+ return {
333
+ items: [],
334
+ total_sessions: 0,
335
+ total_matches: 0,
336
+ query: '',
337
+ };
338
+ }
339
+
340
+ const filteredResult = await filterSessionSummaries(ctx, query);
341
+ if ('error' in filteredResult) {
342
+ return errorResponse(filteredResult.error.status, filteredResult.error.body);
343
+ }
344
+
345
+ const limit = Number(query.limit) || 50;
346
+ const items = await searchSessionEvents(ctx.sessionsDir, normalizedQuery, {
347
+ sessionSummaries: filteredResult.summaries,
348
+ limit,
349
+ });
350
+ const totalMatches = items.reduce((sum, session) => sum + session.matches.length, 0);
351
+
352
+ return {
353
+ items,
354
+ total_sessions: items.length,
355
+ total_matches: totalMatches,
356
+ query: normalizedQuery,
357
+ };
358
+ }, {
359
+ query: t.Object({
360
+ q: t.String(),
361
+ status: t.Optional(t.Union([t.String(), t.Array(t.String())])),
362
+ agent_type: t.Optional(t.Union([t.String(), t.Array(t.String())])),
363
+ agent_id: t.Optional(t.Union([t.String(), t.Array(t.String())])),
364
+ trigger: t.Optional(t.Union([t.String(), t.Array(t.String())])),
365
+ task_id: t.Optional(t.String()),
366
+ spec_ref: t.Optional(t.String()),
367
+ since: t.Optional(t.String()),
368
+ limit: t.Optional(t.String()),
369
+ }),
370
+ })
371
+
372
+ // Get single session metadata
373
+ // AC: @ui-session-stream ac-4 — Includes spec context, budget, and task info
374
+ // AC: @session-legacy-migration ac-read-fallback — detect-and-warn for legacy sessions
375
+ .get('/:id', async ({ params, error: errorResponse, projectContext }) => {
376
+ const ctx = await initContext(projectContext.path);
377
+
378
+ // Resolve session ID (supports prefix matching)
379
+ const resolution = await resolveSessionId(ctx.sessionsDir, params.id);
380
+ if (!resolution.ok) {
381
+ if (resolution.error === 'ambiguous') {
382
+ return errorResponse(400, {
383
+ error: 'ambiguous_id',
384
+ message: `Ambiguous session ID: ${params.id} matches ${resolution.matches.length} sessions`,
385
+ suggestion: 'Provide a longer prefix to uniquely identify the session',
386
+ });
387
+ }
388
+ return errorResponse(404, {
389
+ error: 'not_found',
390
+ message: `Session not found: ${params.id}`,
391
+ suggestion: 'Use GET /api/sessions to list available sessions',
392
+ });
393
+ }
394
+
395
+ const sessionCache = getSessionCache(ctx.sessionsDir);
396
+ const detail = await sessionCache.get(ctx.sessionsDir, resolution.id);
397
+ if (!detail) {
398
+ return errorResponse(404, {
399
+ error: 'not_found',
400
+ message: `Session not found: ${params.id}`,
401
+ suggestion: 'Use GET /api/sessions to list available sessions',
402
+ });
403
+ }
404
+
405
+ const metadata = await getSession(ctx.sessionsDir, resolution.id);
406
+
407
+ // AC: @ui-session-stream ac-4 — Resolve spec context from task's spec_ref
408
+ // AC: @ui-api-ref-resolution ac-1 — Resolve task_title
409
+ let spec_context: {
410
+ spec_ref: string;
411
+ title: string;
412
+ acceptance_criteria: Array<{ id: string; description: string }>;
413
+ } | null = null;
414
+ let task_title: string | null = null;
415
+
416
+ if (metadata?.task_id) {
417
+ try {
418
+ const tasks = await loadAllTasks(ctx);
419
+ const items = await loadAllItems(ctx);
420
+ const index = new ReferenceIndex(tasks, items);
421
+ const taskResult = index.resolve(metadata.task_id);
422
+ if (taskResult.ok) {
423
+ const task = taskResult.item as { title?: string; spec_ref?: string };
424
+ task_title = task.title ?? null;
425
+ if (task.spec_ref) {
426
+ const specResult = index.resolve(task.spec_ref);
427
+ if (specResult.ok) {
428
+ const specItem = specResult.item as {
429
+ title: string;
430
+ acceptance_criteria?: Array<{ description?: string; given?: string }>;
431
+ };
432
+ spec_context = {
433
+ spec_ref: task.spec_ref,
434
+ title: specItem.title,
435
+ acceptance_criteria: (specItem.acceptance_criteria ?? []).map((ac, i) => ({
436
+ id: `ac-${i + 1}`,
437
+ description: ac.description ?? ac.given ?? '',
438
+ })),
439
+ };
440
+ }
441
+ }
442
+ }
443
+ } catch {
444
+ // Non-critical — spec context is optional
445
+ }
446
+ }
447
+
448
+ // AC: @ui-session-stream ac-4 — Include budget info
449
+ let budget: { max_per_cycle: number; started_this_cycle: number } | null = null;
450
+ try {
451
+ budget = await getBudget(ctx.sessionsDir, resolution.id);
452
+ } catch {
453
+ // No budget configured — that's fine
454
+ }
455
+
456
+ // Detect legacy sessions and include warning in response
457
+ const legacyCount = await countLegacySessions(ctx.specDir);
458
+
459
+ return {
460
+ ...detail,
461
+ task_id: metadata?.task_id,
462
+ task_title,
463
+ agent_id: metadata?.agent_id,
464
+ trigger: metadata?.trigger ?? 'legacy',
465
+ spec_context,
466
+ budget,
467
+ ...(legacyCount > 0 ? {
468
+ warning: `${legacyCount} legacy session(s) found in .kspec/sessions/. Run \`kspec session migrate\` to move them to .kspec-sessions/.`,
469
+ } : {}),
470
+ };
471
+ })
472
+
473
+ // Get session events
474
+ // AC: @session-legacy-migration ac-read-fallback — detect-and-warn for legacy sessions
475
+ .get('/:id/events', async ({ params, query, error: errorResponse, projectContext }) => {
476
+ const ctx = await initContext(projectContext.path);
477
+
478
+ const resolution = await resolveSessionId(ctx.sessionsDir, params.id);
479
+ if (!resolution.ok) {
480
+ if (resolution.error === 'ambiguous') {
481
+ return errorResponse(400, {
482
+ error: 'ambiguous_id',
483
+ message: `Ambiguous session ID: ${params.id} matches ${resolution.matches.length} sessions`,
484
+ suggestion: 'Provide a longer prefix to uniquely identify the session',
485
+ });
486
+ }
487
+ return errorResponse(404, {
488
+ error: 'not_found',
489
+ message: `Session not found: ${params.id}`,
490
+ suggestion: 'Use GET /api/sessions to list available sessions',
491
+ });
492
+ }
493
+
494
+ let events = await readEvents(ctx.sessionsDir, resolution.id);
495
+
496
+ // Deduplicate phased tool calls
497
+ events = deduplicatePhasedToolCalls(events);
498
+
499
+ // Filter by since_seq if provided (for incremental loading)
500
+ const sinceSeq = query.since_seq !== undefined ? parseInt(query.since_seq, 10) : undefined;
501
+ if (sinceSeq !== undefined && !isNaN(sinceSeq)) {
502
+ events = events.filter(e => e.seq > sinceSeq);
503
+ }
504
+
505
+ // Detect legacy sessions and include warning in response
506
+ const legacyCount = await countLegacySessions(ctx.specDir);
507
+
508
+ return {
509
+ events,
510
+ total: events.length,
511
+ ...(legacyCount > 0 ? {
512
+ warning: `${legacyCount} legacy session(s) found in .kspec/sessions/. Run \`kspec session migrate\` to move them to .kspec-sessions/.`,
513
+ } : {}),
514
+ };
515
+ }, {
516
+ query: t.Object({
517
+ since_seq: t.Optional(t.String()),
518
+ }),
519
+ })
520
+
521
+ // Get single session event by sequence number with blob resolution
522
+ // AC: @session-event-detail-endpoint ac-single-event-fetch — Returns full event for seq
523
+ // AC: @session-event-detail-endpoint ac-blob-resolution — Blob pointers resolved to full content
524
+ // AC: @session-event-detail-endpoint ac-not-found — 404 for missing session or seq
525
+ // AC: @trait-api-endpoint ac-1 — Returns 2xx with JSON body on success
526
+ // AC: @trait-api-endpoint ac-2 — Returns 404 for invalid session or seq ref
527
+ .get('/:id/events/:seq', async ({ params, error: errorResponse, projectContext }) => {
528
+ const ctx = await initContext(projectContext.path);
529
+
530
+ // Resolve session ID (supports prefix matching)
531
+ const resolution = await resolveSessionId(ctx.sessionsDir, params.id);
532
+ if (!resolution.ok) {
533
+ if (resolution.error === 'ambiguous') {
534
+ return errorResponse(400, {
535
+ error: 'ambiguous_id',
536
+ message: `Ambiguous session ID: ${params.id} matches ${resolution.matches.length} sessions`,
537
+ suggestion: 'Provide a longer prefix to uniquely identify the session',
538
+ });
539
+ }
540
+ return errorResponse(404, {
541
+ error: 'not_found',
542
+ message: `Session not found: ${params.id}`,
543
+ suggestion: 'Use GET /api/sessions to list available sessions',
544
+ });
545
+ }
546
+
547
+ // Parse and validate seq parameter
548
+ const seq = parseInt(params.seq, 10);
549
+ if (isNaN(seq) || seq < 0) {
550
+ return errorResponse(400, {
551
+ error: 'invalid_parameter',
552
+ details: [{
553
+ field: 'seq',
554
+ message: `Invalid sequence number: "${params.seq}". Must be a non-negative integer.`,
555
+ }],
556
+ });
557
+ }
558
+
559
+ // Targeted single-event read
560
+ const event = await readEventBySeq(ctx.sessionsDir, resolution.id, seq);
561
+ if (!event) {
562
+ return errorResponse(404, {
563
+ error: 'not_found',
564
+ message: `Event with seq ${seq} not found in session ${params.id}`,
565
+ suggestion: 'Use GET /api/sessions/:id/events to list available events',
566
+ });
567
+ }
568
+
569
+ // Resolve blob pointers in event data
570
+ const resolvedData = await resolveSessionBlobPointers(
571
+ ctx.sessionsDir,
572
+ resolution.id,
573
+ event.data,
574
+ );
575
+
576
+ return {
577
+ ...event,
578
+ data: resolvedData,
579
+ };
580
+ });
581
+ }