@kynetic-ai/spec 0.11.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 (501) 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 +119 -10
  7. package/dist/agent-runtime/dispatch.d.ts.map +1 -1
  8. package/dist/agent-runtime/dispatch.js +1154 -219
  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 +171 -59
  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/plan-import.d.ts +3 -3
  70. package/dist/cli/commands/plan-import.d.ts.map +1 -1
  71. package/dist/cli/commands/plan-import.js +213 -528
  72. package/dist/cli/commands/plan-import.js.map +1 -1
  73. package/dist/cli/commands/plan.d.ts.map +1 -1
  74. package/dist/cli/commands/plan.js +533 -83
  75. package/dist/cli/commands/plan.js.map +1 -1
  76. package/dist/cli/commands/review.d.ts +14 -0
  77. package/dist/cli/commands/review.d.ts.map +1 -0
  78. package/dist/cli/commands/review.js +1142 -0
  79. package/dist/cli/commands/review.js.map +1 -0
  80. package/dist/cli/commands/serve.d.ts +1 -0
  81. package/dist/cli/commands/serve.d.ts.map +1 -1
  82. package/dist/cli/commands/serve.js +33 -10
  83. package/dist/cli/commands/serve.js.map +1 -1
  84. package/dist/cli/commands/session/checkpoint.d.ts +2 -4
  85. package/dist/cli/commands/session/checkpoint.d.ts.map +1 -1
  86. package/dist/cli/commands/session/checkpoint.js +6 -107
  87. package/dist/cli/commands/session/checkpoint.js.map +1 -1
  88. package/dist/cli/commands/session/commands.d.ts.map +1 -1
  89. package/dist/cli/commands/session/commands.js +33 -23
  90. package/dist/cli/commands/session/commands.js.map +1 -1
  91. package/dist/cli/commands/session/compact.js +4 -4
  92. package/dist/cli/commands/session/compact.js.map +1 -1
  93. package/dist/cli/commands/session/create.js +2 -2
  94. package/dist/cli/commands/session/create.js.map +1 -1
  95. package/dist/cli/commands/session/format.d.ts.map +1 -1
  96. package/dist/cli/commands/session/format.js +1 -6
  97. package/dist/cli/commands/session/format.js.map +1 -1
  98. package/dist/cli/commands/session/log.d.ts +32 -7
  99. package/dist/cli/commands/session/log.d.ts.map +1 -1
  100. package/dist/cli/commands/session/log.js +166 -60
  101. package/dist/cli/commands/session/log.js.map +1 -1
  102. package/dist/cli/commands/session/migrate.d.ts +9 -0
  103. package/dist/cli/commands/session/migrate.d.ts.map +1 -0
  104. package/dist/cli/commands/session/migrate.js +46 -0
  105. package/dist/cli/commands/session/migrate.js.map +1 -0
  106. package/dist/cli/commands/session/stale-close.d.ts.map +1 -1
  107. package/dist/cli/commands/session/stale-close.js +5 -8
  108. package/dist/cli/commands/session/stale-close.js.map +1 -1
  109. package/dist/cli/commands/session/types.d.ts +1 -1
  110. package/dist/cli/commands/session/types.d.ts.map +1 -1
  111. package/dist/cli/commands/setup.d.ts +2 -2
  112. package/dist/cli/commands/setup.d.ts.map +1 -1
  113. package/dist/cli/commands/setup.js +287 -257
  114. package/dist/cli/commands/setup.js.map +1 -1
  115. package/dist/cli/commands/shadow.d.ts.map +1 -1
  116. package/dist/cli/commands/shadow.js +147 -31
  117. package/dist/cli/commands/shadow.js.map +1 -1
  118. package/dist/cli/commands/skill-crud.d.ts +7 -0
  119. package/dist/cli/commands/skill-crud.d.ts.map +1 -1
  120. package/dist/cli/commands/skill-crud.js +41 -18
  121. package/dist/cli/commands/skill-crud.js.map +1 -1
  122. package/dist/cli/commands/skill-diff.d.ts.map +1 -1
  123. package/dist/cli/commands/skill-diff.js +29 -3
  124. package/dist/cli/commands/skill-diff.js.map +1 -1
  125. package/dist/cli/commands/skill-install.d.ts.map +1 -1
  126. package/dist/cli/commands/skill-install.js +5 -4
  127. package/dist/cli/commands/skill-install.js.map +1 -1
  128. package/dist/cli/commands/task.d.ts.map +1 -1
  129. package/dist/cli/commands/task.js +359 -49
  130. package/dist/cli/commands/task.js.map +1 -1
  131. package/dist/cli/commands/trait.d.ts.map +1 -1
  132. package/dist/cli/commands/trait.js +5 -27
  133. package/dist/cli/commands/trait.js.map +1 -1
  134. package/dist/cli/commands/validate.d.ts.map +1 -1
  135. package/dist/cli/commands/validate.js +113 -52
  136. package/dist/cli/commands/validate.js.map +1 -1
  137. package/dist/cli/index.d.ts.map +1 -1
  138. package/dist/cli/index.js +69 -2
  139. package/dist/cli/index.js.map +1 -1
  140. package/dist/cli/output.d.ts +26 -0
  141. package/dist/cli/output.d.ts.map +1 -1
  142. package/dist/cli/output.js +108 -1
  143. package/dist/cli/output.js.map +1 -1
  144. package/dist/cli/sync-mode.d.ts +44 -0
  145. package/dist/cli/sync-mode.d.ts.map +1 -0
  146. package/dist/cli/sync-mode.js +64 -0
  147. package/dist/cli/sync-mode.js.map +1 -0
  148. package/dist/daemon/middleware/project-context.ts +25 -7
  149. package/dist/daemon/project-context.ts +18 -0
  150. package/dist/daemon/routes/agent-dispatch.ts +99 -22
  151. package/dist/daemon/routes/aggregation.ts +184 -0
  152. package/dist/daemon/routes/inbox.ts +5 -0
  153. package/dist/daemon/routes/items.ts +145 -0
  154. package/dist/daemon/routes/meta.ts +1 -1
  155. package/dist/daemon/routes/projects.ts +28 -6
  156. package/dist/daemon/routes/ref-resolution.ts +119 -0
  157. package/dist/daemon/routes/refs.ts +42 -0
  158. package/dist/daemon/routes/session-related.ts +140 -0
  159. package/dist/daemon/routes/sessions.ts +420 -19
  160. package/dist/daemon/routes/tasks.ts +62 -5
  161. package/dist/daemon/routes/triage.ts +40 -1
  162. package/dist/daemon/server.ts +143 -49
  163. package/dist/daemon/session-sync.ts +11 -0
  164. package/dist/daemon/shadow-sync.ts +11 -0
  165. package/dist/daemon/watcher.ts +56 -5
  166. package/dist/daemon/websocket/project-resolution.ts +77 -0
  167. package/dist/export/json.d.ts.map +1 -1
  168. package/dist/export/json.js +104 -1
  169. package/dist/export/json.js.map +1 -1
  170. package/dist/export/types.d.ts +52 -1
  171. package/dist/export/types.d.ts.map +1 -1
  172. package/dist/index.d.ts +1 -0
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +1 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/parser/agent-detection.d.ts +1 -1
  177. package/dist/parser/agent-detection.d.ts.map +1 -1
  178. package/dist/parser/agent-detection.js +10 -0
  179. package/dist/parser/agent-detection.js.map +1 -1
  180. package/dist/parser/config.d.ts +397 -2
  181. package/dist/parser/config.d.ts.map +1 -1
  182. package/dist/parser/config.js +125 -3
  183. package/dist/parser/config.js.map +1 -1
  184. package/dist/parser/dispatch-workspaces.d.ts +18 -0
  185. package/dist/parser/dispatch-workspaces.d.ts.map +1 -0
  186. package/dist/parser/dispatch-workspaces.js +209 -0
  187. package/dist/parser/dispatch-workspaces.js.map +1 -0
  188. package/dist/parser/doctor.d.ts.map +1 -1
  189. package/dist/parser/doctor.js +27 -8
  190. package/dist/parser/doctor.js.map +1 -1
  191. package/dist/parser/file-lock.d.ts.map +1 -1
  192. package/dist/parser/file-lock.js +9 -2
  193. package/dist/parser/file-lock.js.map +1 -1
  194. package/dist/parser/index.d.ts +6 -0
  195. package/dist/parser/index.d.ts.map +1 -1
  196. package/dist/parser/index.js +6 -0
  197. package/dist/parser/index.js.map +1 -1
  198. package/dist/parser/plans.d.ts.map +1 -1
  199. package/dist/parser/plans.js +1 -0
  200. package/dist/parser/plans.js.map +1 -1
  201. package/dist/parser/refs.d.ts +8 -1
  202. package/dist/parser/refs.d.ts.map +1 -1
  203. package/dist/parser/refs.js +27 -1
  204. package/dist/parser/refs.js.map +1 -1
  205. package/dist/parser/review-operations.d.ts +72 -0
  206. package/dist/parser/review-operations.d.ts.map +1 -0
  207. package/dist/parser/review-operations.js +185 -0
  208. package/dist/parser/review-operations.js.map +1 -0
  209. package/dist/parser/review-task-integration.d.ts +78 -0
  210. package/dist/parser/review-task-integration.d.ts.map +1 -0
  211. package/dist/parser/review-task-integration.js +173 -0
  212. package/dist/parser/review-task-integration.js.map +1 -0
  213. package/dist/parser/review-threads.d.ts +101 -0
  214. package/dist/parser/review-threads.d.ts.map +1 -0
  215. package/dist/parser/review-threads.js +222 -0
  216. package/dist/parser/review-threads.js.map +1 -0
  217. package/dist/parser/review-validation.d.ts +69 -0
  218. package/dist/parser/review-validation.d.ts.map +1 -0
  219. package/dist/parser/review-validation.js +207 -0
  220. package/dist/parser/review-validation.js.map +1 -0
  221. package/dist/parser/reviews.d.ts +58 -0
  222. package/dist/parser/reviews.d.ts.map +1 -0
  223. package/dist/parser/reviews.js +230 -0
  224. package/dist/parser/reviews.js.map +1 -0
  225. package/dist/parser/session-branch.d.ts +91 -0
  226. package/dist/parser/session-branch.d.ts.map +1 -0
  227. package/dist/parser/session-branch.js +565 -0
  228. package/dist/parser/session-branch.js.map +1 -0
  229. package/dist/parser/session-sync-scheduler.d.ts +53 -0
  230. package/dist/parser/session-sync-scheduler.d.ts.map +1 -0
  231. package/dist/parser/session-sync-scheduler.js +100 -0
  232. package/dist/parser/session-sync-scheduler.js.map +1 -0
  233. package/dist/parser/setup-status.d.ts +7 -1
  234. package/dist/parser/setup-status.d.ts.map +1 -1
  235. package/dist/parser/setup-status.js +104 -39
  236. package/dist/parser/setup-status.js.map +1 -1
  237. package/dist/parser/shadow-sync-scheduler.d.ts +71 -0
  238. package/dist/parser/shadow-sync-scheduler.d.ts.map +1 -0
  239. package/dist/parser/shadow-sync-scheduler.js +139 -0
  240. package/dist/parser/shadow-sync-scheduler.js.map +1 -0
  241. package/dist/parser/shadow.d.ts +121 -14
  242. package/dist/parser/shadow.d.ts.map +1 -1
  243. package/dist/parser/shadow.js +752 -27
  244. package/dist/parser/shadow.js.map +1 -1
  245. package/dist/parser/skill-render.d.ts +24 -0
  246. package/dist/parser/skill-render.d.ts.map +1 -1
  247. package/dist/parser/skill-render.js +98 -26
  248. package/dist/parser/skill-render.js.map +1 -1
  249. package/dist/parser/validate.d.ts +43 -3
  250. package/dist/parser/validate.d.ts.map +1 -1
  251. package/dist/parser/validate.js +204 -30
  252. package/dist/parser/validate.js.map +1 -1
  253. package/dist/parser/yaml.d.ts +47 -11
  254. package/dist/parser/yaml.d.ts.map +1 -1
  255. package/dist/parser/yaml.js +329 -149
  256. package/dist/parser/yaml.js.map +1 -1
  257. package/dist/review/checks.d.ts +97 -0
  258. package/dist/review/checks.d.ts.map +1 -0
  259. package/dist/review/checks.js +175 -0
  260. package/dist/review/checks.js.map +1 -0
  261. package/dist/review/index.d.ts +3 -0
  262. package/dist/review/index.d.ts.map +1 -0
  263. package/dist/review/index.js +3 -0
  264. package/dist/review/index.js.map +1 -0
  265. package/dist/review/subject-bindings.d.ts +83 -0
  266. package/dist/review/subject-bindings.d.ts.map +1 -0
  267. package/dist/review/subject-bindings.js +175 -0
  268. package/dist/review/subject-bindings.js.map +1 -0
  269. package/dist/schema/common.d.ts +26 -0
  270. package/dist/schema/common.d.ts.map +1 -1
  271. package/dist/schema/common.js +13 -0
  272. package/dist/schema/common.js.map +1 -1
  273. package/dist/schema/dispatch-workspace.d.ts +2643 -0
  274. package/dist/schema/dispatch-workspace.d.ts.map +1 -0
  275. package/dist/schema/dispatch-workspace.js +187 -0
  276. package/dist/schema/dispatch-workspace.js.map +1 -0
  277. package/dist/schema/inbox.d.ts +8 -8
  278. package/dist/schema/index.d.ts +2 -0
  279. package/dist/schema/index.d.ts.map +1 -1
  280. package/dist/schema/index.js +2 -0
  281. package/dist/schema/index.js.map +1 -1
  282. package/dist/schema/meta.d.ts +648 -116
  283. package/dist/schema/meta.d.ts.map +1 -1
  284. package/dist/schema/meta.js +27 -0
  285. package/dist/schema/meta.js.map +1 -1
  286. package/dist/schema/plan.d.ts +30 -19
  287. package/dist/schema/plan.d.ts.map +1 -1
  288. package/dist/schema/plan.js +3 -1
  289. package/dist/schema/plan.js.map +1 -1
  290. package/dist/schema/review-records.d.ts +2676 -0
  291. package/dist/schema/review-records.d.ts.map +1 -0
  292. package/dist/schema/review-records.js +232 -0
  293. package/dist/schema/review-records.js.map +1 -0
  294. package/dist/schema/spec.d.ts +32 -14
  295. package/dist/schema/spec.d.ts.map +1 -1
  296. package/dist/schema/spec.js +5 -0
  297. package/dist/schema/spec.js.map +1 -1
  298. package/dist/schema/task.d.ts +187 -29
  299. package/dist/schema/task.d.ts.map +1 -1
  300. package/dist/schema/task.js +12 -2
  301. package/dist/schema/task.js.map +1 -1
  302. package/dist/schema/triage.d.ts +22 -22
  303. package/dist/sessions/cache.d.ts +119 -0
  304. package/dist/sessions/cache.d.ts.map +1 -0
  305. package/dist/sessions/cache.js +284 -0
  306. package/dist/sessions/cache.js.map +1 -0
  307. package/dist/sessions/index.d.ts +1 -0
  308. package/dist/sessions/index.d.ts.map +1 -1
  309. package/dist/sessions/index.js +2 -0
  310. package/dist/sessions/index.js.map +1 -1
  311. package/dist/sessions/legacy.d.ts +77 -0
  312. package/dist/sessions/legacy.d.ts.map +1 -0
  313. package/dist/sessions/legacy.js +146 -0
  314. package/dist/sessions/legacy.js.map +1 -0
  315. package/dist/sessions/store.d.ts +103 -73
  316. package/dist/sessions/store.d.ts.map +1 -1
  317. package/dist/sessions/store.js +335 -186
  318. package/dist/sessions/store.js.map +1 -1
  319. package/dist/sessions/types.d.ts +44 -16
  320. package/dist/sessions/types.d.ts.map +1 -1
  321. package/dist/sessions/types.js +11 -2
  322. package/dist/sessions/types.js.map +1 -1
  323. package/dist/strings/errors.d.ts +32 -0
  324. package/dist/strings/errors.d.ts.map +1 -1
  325. package/dist/strings/errors.js +17 -0
  326. package/dist/strings/errors.js.map +1 -1
  327. package/dist/strings/labels.d.ts +1 -0
  328. package/dist/strings/labels.d.ts.map +1 -1
  329. package/dist/strings/labels.js +1 -0
  330. package/dist/strings/labels.js.map +1 -1
  331. package/dist/utils/activity.d.ts +101 -0
  332. package/dist/utils/activity.d.ts.map +1 -0
  333. package/dist/utils/activity.js +408 -0
  334. package/dist/utils/activity.js.map +1 -0
  335. package/dist/utils/git.d.ts +31 -0
  336. package/dist/utils/git.d.ts.map +1 -1
  337. package/dist/utils/git.js +87 -0
  338. package/dist/utils/git.js.map +1 -1
  339. package/dist/utils/index.d.ts +2 -0
  340. package/dist/utils/index.d.ts.map +1 -1
  341. package/dist/utils/index.js +1 -0
  342. package/dist/utils/index.js.map +1 -1
  343. package/dist/web-ui/_app/immutable/assets/0.tmlwn-Ih.css +1 -0
  344. package/dist/web-ui/_app/immutable/assets/9.BwwJybWx.css +1 -0
  345. package/dist/web-ui/_app/immutable/chunks/2KqE8gtn.js +1 -0
  346. package/dist/web-ui/_app/immutable/chunks/70-t_QvE.js +1 -0
  347. package/dist/web-ui/_app/immutable/chunks/AiWQj974.js +1 -0
  348. package/dist/web-ui/_app/immutable/chunks/{CPPfDSei.js → B25nWFyA.js} +4 -4
  349. package/dist/web-ui/_app/immutable/chunks/{DBYE9jOd.js → B2bcA_Q_.js} +1 -1
  350. package/dist/web-ui/_app/immutable/chunks/B5e5HYyB.js +1 -0
  351. package/dist/web-ui/_app/immutable/chunks/B7-5z6eA.js +1 -0
  352. package/dist/web-ui/_app/immutable/chunks/B7bGmhK0.js +1 -0
  353. package/dist/web-ui/_app/immutable/chunks/{DzO4hlg9.js → B8tYZKAE.js} +1 -1
  354. package/dist/web-ui/_app/immutable/chunks/{B5LJFxqa.js → BFGAyJjD.js} +1 -1
  355. package/dist/web-ui/_app/immutable/chunks/BG0850zf.js +1 -0
  356. package/dist/web-ui/_app/immutable/chunks/{DAMmvwn4.js → BG8eSzAd.js} +1 -1
  357. package/dist/web-ui/_app/immutable/chunks/BIMxXS8I.js +1 -0
  358. package/dist/web-ui/_app/immutable/chunks/BSzL1fpU.js +1 -0
  359. package/dist/web-ui/_app/immutable/chunks/BYtjHfeq.js +1 -0
  360. package/dist/web-ui/_app/immutable/chunks/{DxCk-KHc.js → Bp5pFYXL.js} +1 -1
  361. package/dist/web-ui/_app/immutable/chunks/{B8a0xDxR.js → BsJFsuAT.js} +1 -1
  362. package/dist/web-ui/_app/immutable/chunks/BvpNHcD6.js +1 -0
  363. package/dist/web-ui/_app/immutable/chunks/BypqA25-.js +1 -0
  364. package/dist/web-ui/_app/immutable/chunks/{BVA9Exy-.js → C0w6WDm5.js} +1 -1
  365. package/dist/web-ui/_app/immutable/chunks/C5_PAZ0y.js +1 -0
  366. package/dist/web-ui/_app/immutable/chunks/CDRO15Iv.js +1 -0
  367. package/dist/web-ui/_app/immutable/chunks/CF1CoqD5.js +1 -0
  368. package/dist/web-ui/_app/immutable/chunks/CS2sa4_m.js +1 -0
  369. package/dist/web-ui/_app/immutable/chunks/{BJ0JX3ea.js → CWUQwB9H.js} +1 -1
  370. package/dist/web-ui/_app/immutable/chunks/CY5FDdSU.js +1 -0
  371. package/dist/web-ui/_app/immutable/chunks/C_7MTDoj.js +1 -0
  372. package/dist/web-ui/_app/immutable/chunks/{D3vxvonu.js → CaAJD3dl.js} +1 -1
  373. package/dist/web-ui/_app/immutable/chunks/{BP352uRn.js → ChB5iyEL.js} +1 -1
  374. package/dist/web-ui/_app/immutable/chunks/{pE6cYWlS.js → ChQD-6N8.js} +1 -1
  375. package/dist/web-ui/_app/immutable/chunks/{Eo4gF7ih.js → CqbsoCwA.js} +1 -1
  376. package/dist/web-ui/_app/immutable/chunks/DCeJW50p.js +1 -0
  377. package/dist/web-ui/_app/immutable/chunks/{Cncwi6fQ.js → DJtZNgcs.js} +1 -1
  378. package/dist/web-ui/_app/immutable/chunks/DKIeaprD.js +1 -0
  379. package/dist/web-ui/_app/immutable/chunks/DLd2uVIA.js +1 -0
  380. package/dist/web-ui/_app/immutable/chunks/{DjcCz-PU.js → DW_subyT.js} +2 -2
  381. package/dist/web-ui/_app/immutable/chunks/DbU6lVn0.js +1 -0
  382. package/dist/web-ui/_app/immutable/chunks/Dc7ZCC5m.js +1 -0
  383. package/dist/web-ui/_app/immutable/chunks/Dd5umPsk.js +2 -0
  384. package/dist/web-ui/_app/immutable/chunks/{BysXJlZb.js → Dg_zDpDS.js} +1 -1
  385. package/dist/web-ui/_app/immutable/chunks/Dgqu8Yuc.js +1 -0
  386. package/dist/web-ui/_app/immutable/chunks/DmxsPZTB.js +1 -0
  387. package/dist/web-ui/_app/immutable/chunks/DphTaFUB.js +1 -0
  388. package/dist/web-ui/_app/immutable/chunks/DqK4iHp0.js +1 -0
  389. package/dist/web-ui/_app/immutable/chunks/{D9QNBZM2.js → DqT6OH_u.js} +2 -2
  390. package/dist/web-ui/_app/immutable/chunks/Ds9I9wQb.js +1 -0
  391. package/dist/web-ui/_app/immutable/chunks/Du5ng3u4.js +1 -0
  392. package/dist/web-ui/_app/immutable/chunks/DxJw79Wi.js +1 -0
  393. package/dist/web-ui/_app/immutable/chunks/GFTX8GgV.js +1 -0
  394. package/dist/web-ui/_app/immutable/chunks/{C076q4JN.js → HNjs76Zz.js} +1 -1
  395. package/dist/web-ui/_app/immutable/chunks/HVMjDi4_.js +1 -0
  396. package/dist/web-ui/_app/immutable/chunks/{BkOJ8DkV.js → P0A_fJvS.js} +1 -1
  397. package/dist/web-ui/_app/immutable/chunks/T3vGWjIL.js +1 -0
  398. package/dist/web-ui/_app/immutable/chunks/VTmrX9Qu.js +1 -0
  399. package/dist/web-ui/_app/immutable/chunks/{k_Qegko0.js → Xvwhx_F1.js} +1 -1
  400. package/dist/web-ui/_app/immutable/chunks/Yyz1XMQA.js +1 -0
  401. package/dist/web-ui/_app/immutable/chunks/{62JVKtnb.js → dh5HeqUr.js} +1 -1
  402. package/dist/web-ui/_app/immutable/chunks/fZMteyca.js +62 -0
  403. package/dist/web-ui/_app/immutable/chunks/{D82RulSH.js → gPrj-hqC.js} +1 -1
  404. package/dist/web-ui/_app/immutable/chunks/htcWMiYN.js +1 -0
  405. package/dist/web-ui/_app/immutable/chunks/{CwELQvbx.js → oTsvd9y4.js} +1 -1
  406. package/dist/web-ui/_app/immutable/chunks/qJfLUwU4.js +1 -0
  407. package/dist/web-ui/_app/immutable/chunks/xCtiO_JE.js +1 -0
  408. package/dist/web-ui/_app/immutable/chunks/{DvA-KON-.js → y4GeEH6k.js} +1 -1
  409. package/dist/web-ui/_app/immutable/entry/app.C4h_eOn6.js +2 -0
  410. package/dist/web-ui/_app/immutable/entry/start.CQFTf9ep.js +1 -0
  411. package/dist/web-ui/_app/immutable/nodes/0.Dh1xO970.js +1 -0
  412. package/dist/web-ui/_app/immutable/nodes/1.l75D3Opx.js +1 -0
  413. package/dist/web-ui/_app/immutable/nodes/10.DBidBPc-.js +1 -0
  414. package/dist/web-ui/_app/immutable/nodes/11.Ab0gUKWe.js +1 -0
  415. package/dist/web-ui/_app/immutable/nodes/12.CMsnoxfs.js +1 -0
  416. package/dist/web-ui/_app/immutable/nodes/13.D8YKuknB.js +1 -0
  417. package/dist/web-ui/_app/immutable/nodes/14.DZ0aan7y.js +1 -0
  418. package/dist/web-ui/_app/immutable/nodes/15.CUIKreDL.js +2 -0
  419. package/dist/web-ui/_app/immutable/nodes/16.BWc8--BO.js +1 -0
  420. package/dist/web-ui/_app/immutable/nodes/2.CDUonbuh.js +1 -0
  421. package/dist/web-ui/_app/immutable/nodes/3.Ctg3M00i.js +1 -0
  422. package/dist/web-ui/_app/immutable/nodes/4.Ci-JDwbA.js +2 -0
  423. package/dist/web-ui/_app/immutable/nodes/5.CTyEDAq0.js +1 -0
  424. package/dist/web-ui/_app/immutable/nodes/6.BTZZqsAb.js +1 -0
  425. package/dist/web-ui/_app/immutable/nodes/7.BI52g_Jo.js +137 -0
  426. package/dist/web-ui/_app/immutable/nodes/8.3hZPaB9x.js +1 -0
  427. package/dist/web-ui/_app/immutable/nodes/9.DS49kvwl.js +29 -0
  428. package/dist/web-ui/_app/version.json +1 -1
  429. package/dist/web-ui/favicon-192.png +0 -0
  430. package/dist/web-ui/favicon-32.png +0 -0
  431. package/dist/web-ui/favicon.ico +0 -0
  432. package/dist/web-ui/index.html +14 -14
  433. package/package.json +12 -6
  434. package/plugin/.claude-plugin/marketplace.json +1 -1
  435. package/plugin/.claude-plugin/plugin.json +1 -1
  436. package/plugin/plugins/kspec/skills/merge/SKILL.md +127 -0
  437. package/plugin/plugins/kspec/skills/plan/SKILL.md +55 -26
  438. package/plugin/plugins/kspec/skills/review/SKILL.md +350 -133
  439. package/plugin/plugins/kspec/skills/task-work/SKILL.md +96 -106
  440. package/templates/agents-sections/04-pr-workflow.md +15 -12
  441. package/templates/agents-sections/06-ralph-loop.md +15 -10
  442. package/templates/skills/manifest.yaml +25 -7
  443. package/templates/skills/merge/SKILL.md +120 -0
  444. package/templates/skills/plan/SKILL.md +55 -26
  445. package/templates/skills/review/SKILL.md +346 -130
  446. package/templates/skills/task-work/SKILL.md +93 -103
  447. package/dist/web-ui/_app/immutable/assets/0.BJaYkGW2.css +0 -1
  448. package/dist/web-ui/_app/immutable/assets/9.SzGLxi4x.css +0 -1
  449. package/dist/web-ui/_app/immutable/chunks/-lc0BifF.js +0 -1
  450. package/dist/web-ui/_app/immutable/chunks/8RBjHMN1.js +0 -1
  451. package/dist/web-ui/_app/immutable/chunks/B5wTVqxm.js +0 -1
  452. package/dist/web-ui/_app/immutable/chunks/B6VSmczZ.js +0 -1
  453. package/dist/web-ui/_app/immutable/chunks/BEOQc37C.js +0 -1
  454. package/dist/web-ui/_app/immutable/chunks/BHtYorjv.js +0 -1
  455. package/dist/web-ui/_app/immutable/chunks/BMuCqDX8.js +0 -1
  456. package/dist/web-ui/_app/immutable/chunks/BUZujXJ2.js +0 -1
  457. package/dist/web-ui/_app/immutable/chunks/BWET-efb.js +0 -1
  458. package/dist/web-ui/_app/immutable/chunks/BXkNecpt.js +0 -1
  459. package/dist/web-ui/_app/immutable/chunks/BYzrIfX8.js +0 -1
  460. package/dist/web-ui/_app/immutable/chunks/BpuwufMc.js +0 -1
  461. package/dist/web-ui/_app/immutable/chunks/BwMO4RrG.js +0 -1
  462. package/dist/web-ui/_app/immutable/chunks/C33JaVbg.js +0 -1
  463. package/dist/web-ui/_app/immutable/chunks/CGtqifKp.js +0 -1
  464. package/dist/web-ui/_app/immutable/chunks/CHDZZ7OG.js +0 -1
  465. package/dist/web-ui/_app/immutable/chunks/CUir3f4J.js +0 -60
  466. package/dist/web-ui/_app/immutable/chunks/CrCIbn0C.js +0 -1
  467. package/dist/web-ui/_app/immutable/chunks/D6TVmR9T.js +0 -1
  468. package/dist/web-ui/_app/immutable/chunks/D7LTux4W.js +0 -1
  469. package/dist/web-ui/_app/immutable/chunks/DAh4Wfku.js +0 -1
  470. package/dist/web-ui/_app/immutable/chunks/DAx07bEQ.js +0 -1
  471. package/dist/web-ui/_app/immutable/chunks/DOno4cA2.js +0 -1
  472. package/dist/web-ui/_app/immutable/chunks/DQA8NZIH.js +0 -2
  473. package/dist/web-ui/_app/immutable/chunks/DRfPm2bo.js +0 -1
  474. package/dist/web-ui/_app/immutable/chunks/DhQhksaB.js +0 -1
  475. package/dist/web-ui/_app/immutable/chunks/DjG7s6hm.js +0 -1
  476. package/dist/web-ui/_app/immutable/chunks/DkltRNvh.js +0 -1
  477. package/dist/web-ui/_app/immutable/chunks/DlaTnPKL.js +0 -1
  478. package/dist/web-ui/_app/immutable/chunks/ExCq5swK.js +0 -1
  479. package/dist/web-ui/_app/immutable/chunks/T3zZGv51.js +0 -1
  480. package/dist/web-ui/_app/immutable/chunks/XZumBYeP.js +0 -1
  481. package/dist/web-ui/_app/immutable/chunks/_ySfNjkF.js +0 -1
  482. package/dist/web-ui/_app/immutable/chunks/iEtR5cV6.js +0 -1
  483. package/dist/web-ui/_app/immutable/entry/app.Cgu6uKeS.js +0 -2
  484. package/dist/web-ui/_app/immutable/entry/start.9XifnLoB.js +0 -1
  485. package/dist/web-ui/_app/immutable/nodes/0.DISwcKSK.js +0 -1
  486. package/dist/web-ui/_app/immutable/nodes/1.Cx2Ufqp1.js +0 -1
  487. package/dist/web-ui/_app/immutable/nodes/10.C3z8ijXL.js +0 -1
  488. package/dist/web-ui/_app/immutable/nodes/11.DZdIjZmM.js +0 -1
  489. package/dist/web-ui/_app/immutable/nodes/12.FsIGfAOa.js +0 -1
  490. package/dist/web-ui/_app/immutable/nodes/13.DZoFwagf.js +0 -1
  491. package/dist/web-ui/_app/immutable/nodes/14.DaIzDKbQ.js +0 -1
  492. package/dist/web-ui/_app/immutable/nodes/15.BYyt4XWF.js +0 -2
  493. package/dist/web-ui/_app/immutable/nodes/16.CQkSqpOe.js +0 -1
  494. package/dist/web-ui/_app/immutable/nodes/2.Bkf_j2UJ.js +0 -1
  495. package/dist/web-ui/_app/immutable/nodes/3.kaMCurJG.js +0 -1
  496. package/dist/web-ui/_app/immutable/nodes/4.BSsFPTHG.js +0 -2
  497. package/dist/web-ui/_app/immutable/nodes/5.CpPlcCEZ.js +0 -1
  498. package/dist/web-ui/_app/immutable/nodes/6.BN4FqQmY.js +0 -1
  499. package/dist/web-ui/_app/immutable/nodes/7.9kBYIZik.js +0 -1
  500. package/dist/web-ui/_app/immutable/nodes/8.BuijtZ6B.js +0 -1
  501. package/dist/web-ui/_app/immutable/nodes/9.C-Weba8R.js +0 -1
@@ -5,56 +5,378 @@
5
5
  * - GET /api/sessions - list sessions with summaries
6
6
  * - GET /api/sessions/:id - get session metadata and detail
7
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
8
9
  *
9
10
  * AC Coverage:
10
11
  * - @ui-session-stream ac-1: Session events as structured blocks
11
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
12
20
  */
13
21
 
14
22
  import { Elysia, t } from 'elysia';
15
23
  import {
16
24
  getSession,
17
- listSessions,
18
25
  readEvents,
26
+ readEventBySeq,
19
27
  deduplicatePhasedToolCalls,
20
- getSessionLogSummary,
21
- getAllSessionLogSummaries,
22
28
  resolveSessionId,
29
+ resolveSessionBlobPointers,
23
30
  getBudget,
31
+ searchSessionEvents,
32
+ type SessionLogSummary,
24
33
  } from '../../sessions/store.js';
34
+ import {
35
+ countLegacySessions,
36
+ } from '../../sessions/legacy.js';
25
37
  import {
26
38
  initContext,
27
39
  loadAllTasks,
28
40
  loadAllItems,
29
41
  ReferenceIndex,
42
+ AlignmentIndex,
43
+ type KspecContext,
30
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
+ }
31
255
 
32
256
  export function createSessionRoutes() {
33
257
  return new Elysia({ prefix: '/api/sessions' })
34
258
 
35
- // List all sessions with summaries
36
- .get('/', async ({ projectContext }) => {
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 }) => {
37
266
  const ctx = await initContext(projectContext.path);
38
- const summaries = await getAllSessionLogSummaries(ctx.specDir);
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;
39
273
 
40
- // Sort by started_at descending (most recent first)
41
- summaries.sort((a, b) =>
42
- new Date(b.started_at).getTime() - new Date(a.started_at).getTime()
43
- );
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);
44
351
 
45
352
  return {
46
- items: summaries,
47
- total: summaries.length,
353
+ items,
354
+ total_sessions: items.length,
355
+ total_matches: totalMatches,
356
+ query: normalizedQuery,
48
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
+ }),
49
370
  })
50
371
 
51
372
  // Get single session metadata
52
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
53
375
  .get('/:id', async ({ params, error: errorResponse, projectContext }) => {
54
376
  const ctx = await initContext(projectContext.path);
55
377
 
56
378
  // Resolve session ID (supports prefix matching)
57
- const resolution = await resolveSessionId(ctx.specDir, params.id);
379
+ const resolution = await resolveSessionId(ctx.sessionsDir, params.id);
58
380
  if (!resolution.ok) {
59
381
  if (resolution.error === 'ambiguous') {
60
382
  return errorResponse(400, {
@@ -70,7 +392,8 @@ export function createSessionRoutes() {
70
392
  });
71
393
  }
72
394
 
73
- const detail = await getSessionLogSummary(ctx.specDir, resolution.id);
395
+ const sessionCache = getSessionCache(ctx.sessionsDir);
396
+ const detail = await sessionCache.get(ctx.sessionsDir, resolution.id);
74
397
  if (!detail) {
75
398
  return errorResponse(404, {
76
399
  error: 'not_found',
@@ -79,14 +402,16 @@ export function createSessionRoutes() {
79
402
  });
80
403
  }
81
404
 
82
- const metadata = await getSession(ctx.specDir, resolution.id);
405
+ const metadata = await getSession(ctx.sessionsDir, resolution.id);
83
406
 
84
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
85
409
  let spec_context: {
86
410
  spec_ref: string;
87
411
  title: string;
88
412
  acceptance_criteria: Array<{ id: string; description: string }>;
89
413
  } | null = null;
414
+ let task_title: string | null = null;
90
415
 
91
416
  if (metadata?.task_id) {
92
417
  try {
@@ -95,7 +420,8 @@ export function createSessionRoutes() {
95
420
  const index = new ReferenceIndex(tasks, items);
96
421
  const taskResult = index.resolve(metadata.task_id);
97
422
  if (taskResult.ok) {
98
- const task = taskResult.item as { spec_ref?: string };
423
+ const task = taskResult.item as { title?: string; spec_ref?: string };
424
+ task_title = task.title ?? null;
99
425
  if (task.spec_ref) {
100
426
  const specResult = index.resolve(task.spec_ref);
101
427
  if (specResult.ok) {
@@ -122,26 +448,34 @@ export function createSessionRoutes() {
122
448
  // AC: @ui-session-stream ac-4 — Include budget info
123
449
  let budget: { max_per_cycle: number; started_this_cycle: number } | null = null;
124
450
  try {
125
- budget = await getBudget(ctx.specDir, resolution.id);
451
+ budget = await getBudget(ctx.sessionsDir, resolution.id);
126
452
  } catch {
127
453
  // No budget configured — that's fine
128
454
  }
129
455
 
456
+ // Detect legacy sessions and include warning in response
457
+ const legacyCount = await countLegacySessions(ctx.specDir);
458
+
130
459
  return {
131
460
  ...detail,
132
461
  task_id: metadata?.task_id,
462
+ task_title,
133
463
  agent_id: metadata?.agent_id,
134
464
  trigger: metadata?.trigger ?? 'legacy',
135
465
  spec_context,
136
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
+ } : {}),
137
470
  };
138
471
  })
139
472
 
140
473
  // Get session events
474
+ // AC: @session-legacy-migration ac-read-fallback — detect-and-warn for legacy sessions
141
475
  .get('/:id/events', async ({ params, query, error: errorResponse, projectContext }) => {
142
476
  const ctx = await initContext(projectContext.path);
143
477
 
144
- const resolution = await resolveSessionId(ctx.specDir, params.id);
478
+ const resolution = await resolveSessionId(ctx.sessionsDir, params.id);
145
479
  if (!resolution.ok) {
146
480
  if (resolution.error === 'ambiguous') {
147
481
  return errorResponse(400, {
@@ -157,7 +491,7 @@ export function createSessionRoutes() {
157
491
  });
158
492
  }
159
493
 
160
- let events = await readEvents(ctx.specDir, resolution.id);
494
+ let events = await readEvents(ctx.sessionsDir, resolution.id);
161
495
 
162
496
  // Deduplicate phased tool calls
163
497
  events = deduplicatePhasedToolCalls(events);
@@ -168,13 +502,80 @@ export function createSessionRoutes() {
168
502
  events = events.filter(e => e.seq > sinceSeq);
169
503
  }
170
504
 
505
+ // Detect legacy sessions and include warning in response
506
+ const legacyCount = await countLegacySessions(ctx.specDir);
507
+
171
508
  return {
172
509
  events,
173
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
+ } : {}),
174
514
  };
175
515
  }, {
176
516
  query: t.Object({
177
517
  since_seq: t.Optional(t.String()),
178
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
+ };
179
580
  });
180
581
  }