vibepulse 0.2.2 → 0.3.0-beta.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 (253) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/cache/.previewinfo +1 -1
  5. package/.next/cache/.rscinfo +1 -1
  6. package/.next/cache/.tsbuildinfo +1 -1
  7. package/.next/cache/config.json +3 -3
  8. package/.next/fallback-build-manifest.json +2 -2
  9. package/.next/prerender-manifest.json +3 -3
  10. package/.next/routes-manifest.json +8 -0
  11. package/.next/server/app/_global-error/page.js +1 -1
  12. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  13. package/.next/server/app/_global-error.html +2 -2
  14. package/.next/server/app/_global-error.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  17. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/_not-found/page.js +1 -1
  21. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/_not-found.html +1 -1
  24. package/.next/server/app/_not-found.rsc +2 -2
  25. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  26. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  27. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  28. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  29. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  30. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
  32. package/.next/server/app/api/node/sessions/route.js +5 -3
  33. package/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
  34. package/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
  35. package/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
  36. package/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
  37. package/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
  38. package/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
  39. package/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
  40. package/.next/server/app/api/profiles/route.js.nft.json +1 -1
  41. package/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
  42. package/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  43. package/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
  44. package/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
  45. package/.next/server/app/api/sessions/[id]/open-editor/route.js +1 -1
  46. package/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
  47. package/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
  48. package/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
  49. package/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
  50. package/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
  51. package/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
  52. package/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
  53. package/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
  54. package/.next/server/app/api/sessions/route.js +4 -2
  55. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  56. package/.next/server/app/index.html +1 -1
  57. package/.next/server/app/index.rsc +3 -3
  58. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  59. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  60. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  61. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  62. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/page_client-reference-manifest.js +1 -1
  64. package/.next/server/app-paths-manifest.json +1 -0
  65. package/.next/server/chunks/[root-of-the-server]__31d19c5c._.js +3 -0
  66. package/.next/server/chunks/[root-of-the-server]__31d19c5c._.js.map +1 -0
  67. package/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
  68. package/.next/server/chunks/[root-of-the-server]__56f5f249._.js.map +1 -1
  69. package/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js +3 -0
  70. package/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js.map +1 -0
  71. package/.next/server/chunks/[root-of-the-server]__98073dd6._.js +3 -0
  72. package/.next/server/chunks/[root-of-the-server]__98073dd6._.js.map +1 -0
  73. package/.next/server/chunks/[root-of-the-server]__b7b717eb._.js +3 -0
  74. package/.next/server/chunks/[root-of-the-server]__b7b717eb._.js.map +1 -0
  75. package/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
  76. package/.next/server/chunks/[root-of-the-server]__d8e61048._.js.map +1 -1
  77. package/.next/server/chunks/[root-of-the-server]__f441109e._.js +3 -0
  78. package/.next/server/chunks/[root-of-the-server]__f441109e._.js.map +1 -0
  79. package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
  80. package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js.map +1 -0
  81. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js +3 -0
  82. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js.map +1 -0
  83. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js +3 -0
  84. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js.map +1 -0
  85. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
  86. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js.map +1 -1
  87. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
  88. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js.map +1 -1
  89. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js +3 -0
  90. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js.map +1 -0
  91. package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
  92. package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js.map +1 -0
  93. package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
  94. package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js.map → [root-of-the-server]__c91a8380._.js.map} +1 -1
  95. package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
  96. package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js.map +1 -1
  97. package/.next/server/pages/404.html +1 -1
  98. package/.next/server/pages/500.html +2 -2
  99. package/.next/server/server-reference-manifest.js +1 -1
  100. package/.next/server/server-reference-manifest.json +1 -1
  101. package/.next/standalone/.next/BUILD_ID +1 -1
  102. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  103. package/.next/standalone/.next/build-manifest.json +2 -2
  104. package/.next/standalone/.next/prerender-manifest.json +3 -3
  105. package/.next/standalone/.next/routes-manifest.json +8 -0
  106. package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
  107. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  108. package/.next/standalone/.next/server/app/_global-error.html +2 -2
  109. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  110. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  111. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  112. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  113. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  114. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  115. package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  116. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  117. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  118. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  119. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  120. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  121. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  122. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  123. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  124. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  125. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  126. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
  127. package/.next/standalone/.next/server/app/api/node/sessions/route.js +5 -3
  128. package/.next/standalone/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
  129. package/.next/standalone/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
  130. package/.next/standalone/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
  131. package/.next/standalone/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
  132. package/.next/standalone/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
  133. package/.next/standalone/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
  134. package/.next/standalone/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
  135. package/.next/standalone/.next/server/app/api/profiles/route.js.nft.json +1 -1
  136. package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
  137. package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  138. package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
  139. package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
  140. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js +1 -1
  141. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
  143. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
  144. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
  145. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
  146. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
  147. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
  148. package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
  149. package/.next/standalone/.next/server/app/api/sessions/route.js +4 -2
  150. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  151. package/.next/standalone/.next/server/app/index.html +1 -1
  152. package/.next/standalone/.next/server/app/index.rsc +3 -3
  153. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  154. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  155. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  156. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  157. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  158. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  159. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  160. package/.next/standalone/.next/server/chunks/[root-of-the-server]__31d19c5c._.js +3 -0
  161. package/.next/standalone/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
  162. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js +3 -0
  163. package/.next/standalone/.next/server/chunks/[root-of-the-server]__98073dd6._.js +3 -0
  164. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b7b717eb._.js +3 -0
  165. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
  166. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f441109e._.js +3 -0
  167. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
  168. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js +3 -0
  169. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js +3 -0
  170. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
  171. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
  172. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js +3 -0
  173. package/.next/standalone/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
  174. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
  175. package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
  176. package/.next/standalone/.next/server/pages/404.html +1 -1
  177. package/.next/standalone/.next/server/pages/500.html +2 -2
  178. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  179. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  180. package/.next/standalone/.next/static/chunks/b3bc362202331708.css +3 -0
  181. package/.next/standalone/.next/static/chunks/{65d5354ba0add961.js → c1294e057d8d4681.js} +3 -3
  182. package/.next/standalone/README.md +29 -5
  183. package/.next/standalone/docs/session-status-detection.md +36 -0
  184. package/.next/standalone/docs/superpowers/specs/2026-04-09-claude-capability-alignment-design.md +39 -0
  185. package/.next/standalone/package-lock.json +2 -2
  186. package/.next/standalone/package.json +1 -1
  187. package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.test.ts +60 -1
  188. package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.ts +77 -22
  189. package/.next/standalone/src/app/api/node/sessions/route.test.ts +282 -0
  190. package/.next/standalone/src/app/api/node/sessions/route.ts +141 -17
  191. package/.next/standalone/src/app/api/opencode-events/route.test.ts +3 -1
  192. package/.next/standalone/src/app/api/sessions/[id]/archive/route.test.ts +101 -0
  193. package/.next/standalone/src/app/api/sessions/[id]/archive/route.ts +47 -12
  194. package/.next/standalone/src/app/api/sessions/[id]/delete/route.test.ts +92 -0
  195. package/.next/standalone/src/app/api/sessions/[id]/delete/route.ts +45 -10
  196. package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.test.ts +74 -0
  197. package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.ts +22 -2
  198. package/.next/standalone/src/app/api/sessions/[id]/restore/route.test.ts +186 -0
  199. package/.next/standalone/src/app/api/sessions/[id]/restore/route.ts +184 -0
  200. package/.next/standalone/src/app/api/sessions/route.test.ts +1889 -107
  201. package/.next/standalone/src/app/api/sessions/route.ts +365 -981
  202. package/.next/standalone/src/components/KanbanBoard.test.tsx +307 -1
  203. package/.next/standalone/src/components/KanbanBoard.tsx +105 -18
  204. package/.next/standalone/src/components/ProjectCard.test.tsx +416 -2
  205. package/.next/standalone/src/components/ProjectCard.tsx +238 -86
  206. package/.next/standalone/src/components/SessionCard.test.tsx +253 -2
  207. package/.next/standalone/src/components/SessionCard.tsx +182 -76
  208. package/.next/standalone/src/hooks/useOpencodeSync.test.ts +321 -1
  209. package/.next/standalone/src/hooks/useOpencodeSync.ts +16 -12
  210. package/.next/standalone/src/lib/claudeSessionOverrides.test.ts +75 -0
  211. package/.next/standalone/src/lib/claudeSessionOverrides.ts +169 -0
  212. package/.next/standalone/src/lib/session-providers/claudeCode.test.ts +2288 -0
  213. package/.next/standalone/src/lib/session-providers/claudeCode.ts +1083 -0
  214. package/.next/standalone/src/lib/session-providers/localAggregator.test.ts +322 -0
  215. package/.next/standalone/src/lib/session-providers/localAggregator.ts +302 -0
  216. package/.next/standalone/src/lib/session-providers/opencodeProvider.ts +723 -0
  217. package/.next/standalone/src/lib/session-providers/providerIds.test.ts +337 -0
  218. package/.next/standalone/src/lib/session-providers/providerIds.ts +176 -0
  219. package/.next/standalone/src/lib/session-providers/types.ts +131 -0
  220. package/.next/standalone/src/lib/transform.test.ts +253 -0
  221. package/.next/standalone/src/lib/transform.ts +96 -37
  222. package/.next/standalone/src/types/index.ts +23 -17
  223. package/.next/static/chunks/b3bc362202331708.css +3 -0
  224. package/.next/static/chunks/{65d5354ba0add961.js → c1294e057d8d4681.js} +3 -3
  225. package/.next/trace +1 -1
  226. package/.next/trace-build +1 -1
  227. package/.next/types/routes.d.ts +2 -1
  228. package/.next/types/validator.ts +9 -0
  229. package/README.md +29 -5
  230. package/package.json +1 -1
  231. package/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
  232. package/.next/server/chunks/[root-of-the-server]__2f981540._.js.map +0 -1
  233. package/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
  234. package/.next/server/chunks/[root-of-the-server]__3745b314._.js.map +0 -1
  235. package/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
  236. package/.next/server/chunks/[root-of-the-server]__6c428a24._.js.map +0 -1
  237. package/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
  238. package/.next/server/chunks/[root-of-the-server]__73a00b88._.js.map +0 -1
  239. package/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
  240. package/.next/server/chunks/[root-of-the-server]__db285678._.js.map +0 -1
  241. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
  242. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
  243. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
  244. package/.next/standalone/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
  245. package/.next/standalone/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
  246. package/.next/standalone/.next/static/chunks/f42202943f6742e5.css +0 -3
  247. package/.next/static/chunks/f42202943f6742e5.css +0 -3
  248. /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_buildManifest.js +0 -0
  249. /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_clientMiddlewareManifest.json +0 -0
  250. /package/.next/standalone/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_ssgManifest.js +0 -0
  251. /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_buildManifest.js +0 -0
  252. /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_clientMiddlewareManifest.json +0 -0
  253. /package/.next/static/{5kq9DtuBFVxu4jsgmL5Q- → bsWNvgDS7Zp38Yt9q0DUg}/_ssgManifest.js +0 -0
@@ -33,6 +33,8 @@ function makeSession(overrides: SessionOverrides = {}): OpencodeSession {
33
33
  rawSessionId: overrides.rawSessionId,
34
34
  sourceSessionKey: overrides.sourceSessionKey,
35
35
  readOnly: overrides.readOnly,
36
+ provider: overrides.provider,
37
+ providerRawId: overrides.providerRawId,
36
38
  };
37
39
  }
38
40
 
@@ -119,3 +121,254 @@ describe('transformSession archive precedence', () => {
119
121
  expect(card.readOnly).toBe(false);
120
122
  });
121
123
  });
124
+
125
+ describe('transformSession provider propagation', () => {
126
+ it('defaults provider to opencode when not specified', () => {
127
+ const session = makeSession();
128
+
129
+ const card = transformSession(session);
130
+
131
+ expect(card.provider).toBe('opencode');
132
+ });
133
+
134
+ it('defaults readOnly to false for OpenCode sessions', () => {
135
+ const session = makeSession();
136
+
137
+ const card = transformSession(session);
138
+
139
+ expect(card.readOnly).toBe(false);
140
+ expect(card.capabilities).toEqual({
141
+ openProject: true,
142
+ openEditor: true,
143
+ archive: true,
144
+ delete: true,
145
+ });
146
+ });
147
+
148
+ it('preserves explicit claude-code provider', () => {
149
+ const session = makeSession({
150
+ provider: 'claude-code',
151
+ providerRawId: '550e8400-e29b-41d4-a716-446655440000',
152
+ });
153
+
154
+ const card = transformSession(session);
155
+
156
+ expect(card.provider).toBe('claude-code');
157
+ expect(card.providerRawId).toBe('550e8400-e29b-41d4-a716-446655440000');
158
+ });
159
+
160
+ it('preserves explicit readOnly true', () => {
161
+ const session = makeSession({ readOnly: true });
162
+
163
+ const card = transformSession(session);
164
+
165
+ expect(card.readOnly).toBe(true);
166
+ });
167
+
168
+ it('falls back to rawSessionId when providerRawId not specified', () => {
169
+ const session = makeSession({ rawSessionId: 'ses_123' });
170
+
171
+ const card = transformSession(session);
172
+
173
+ expect(card.providerRawId).toBe('ses_123');
174
+ });
175
+
176
+ it('uses providerRawId over rawSessionId when both specified', () => {
177
+ const session = makeSession({
178
+ rawSessionId: 'ses_123',
179
+ providerRawId: 'claude~550e8400-e29b-41d4-a716-446655440000',
180
+ });
181
+
182
+ const card = transformSession(session);
183
+
184
+ expect(card.providerRawId).toBe('claude~550e8400-e29b-41d4-a716-446655440000');
185
+ });
186
+
187
+ it('correctly handles Claude-backed sessions from provider properties', () => {
188
+ const session = makeSession({
189
+ provider: 'claude-code',
190
+ readOnly: true,
191
+ providerRawId: '550e8400-e29b-41d4-a716-446655440000',
192
+ });
193
+
194
+ const card = transformSession(session);
195
+
196
+ expect(card.provider).toBe('claude-code');
197
+ expect(card.readOnly).toBe(true);
198
+ expect(card.capabilities).toEqual({
199
+ openProject: true,
200
+ openEditor: false,
201
+ archive: true,
202
+ delete: true,
203
+ });
204
+ expect(card.providerRawId).toBe('550e8400-e29b-41d4-a716-446655440000');
205
+ });
206
+ });
207
+
208
+ describe('transformSession child roll-up semantics', () => {
209
+ it('pulls a Claude parent into busy when a verified Claude child is active', () => {
210
+ const now = Date.now();
211
+ const child = makeSession({
212
+ id: 'claude-child-1',
213
+ provider: 'claude-code',
214
+ parentID: 'claude-parent-1',
215
+ realTimeStatus: 'busy',
216
+ time: {
217
+ created: now - 10_000,
218
+ updated: now - 2_000,
219
+ },
220
+ });
221
+
222
+ const parent = makeSession({
223
+ id: 'claude-parent-1',
224
+ provider: 'claude-code',
225
+ realTimeStatus: 'idle',
226
+ children: [child],
227
+ });
228
+
229
+ const card = transformSession(parent);
230
+
231
+ expect(card.status).toBe('busy');
232
+ expect(card.opencodeStatus).toBe('busy');
233
+ expect(card.waitingForUser).toBe(false);
234
+ expect(card.debugReason).toBe('child_recent_activity');
235
+ });
236
+
237
+ it('pulls a Claude parent into review when a verified Claude child is idle but waiting for user', () => {
238
+ const now = Date.now();
239
+ const child = makeSession({
240
+ id: 'claude-child-2',
241
+ provider: 'claude-code',
242
+ parentID: 'claude-parent-2',
243
+ realTimeStatus: 'idle',
244
+ waitingForUser: true,
245
+ time: {
246
+ created: now - 12_000,
247
+ updated: now - 3_000,
248
+ },
249
+ });
250
+
251
+ const parent = makeSession({
252
+ id: 'claude-parent-2',
253
+ provider: 'claude-code',
254
+ realTimeStatus: 'idle',
255
+ children: [child],
256
+ });
257
+
258
+ const card = transformSession(parent);
259
+ const firstChild = card.children?.[0];
260
+
261
+ expect(card.status).toBe('review');
262
+ expect(card.opencodeStatus).toBe('busy');
263
+ expect(card.waitingForUser).toBe(true);
264
+ expect(card.debugReason).toBe('waiting_for_user');
265
+ expect(firstChild?.waitingForUser).toBe(true);
266
+ });
267
+
268
+ it('ignores malformed Claude child rows so unrelated parents stay idle', () => {
269
+ const now = Date.now();
270
+ const childWithoutParent = makeSession({
271
+ id: 'claude-child-missing-parent',
272
+ provider: 'claude-code',
273
+ realTimeStatus: 'busy',
274
+ waitingForUser: true,
275
+ time: {
276
+ created: now - 10_000,
277
+ updated: now - 1_000,
278
+ },
279
+ });
280
+ const childWithWrongParent = makeSession({
281
+ id: 'claude-child-wrong-parent',
282
+ provider: 'claude-code',
283
+ parentID: 'different-parent',
284
+ realTimeStatus: 'busy',
285
+ time: {
286
+ created: now - 10_000,
287
+ updated: now - 1_000,
288
+ },
289
+ });
290
+ const childWithoutTime = {
291
+ ...makeSession({
292
+ id: 'claude-child-without-time',
293
+ provider: 'claude-code',
294
+ parentID: 'claude-parent-3',
295
+ realTimeStatus: 'busy',
296
+ waitingForUser: true,
297
+ }),
298
+ time: undefined,
299
+ } as unknown as OpencodeSession;
300
+
301
+ const parent = makeSession({
302
+ id: 'claude-parent-3',
303
+ provider: 'claude-code',
304
+ realTimeStatus: 'idle',
305
+ children: [childWithoutParent, childWithWrongParent, childWithoutTime],
306
+ });
307
+
308
+ const card = transformSession(parent);
309
+
310
+ expect(card.status).toBe('idle');
311
+ expect(card.opencodeStatus).toBe('idle');
312
+ expect(card.waitingForUser).toBe(false);
313
+ expect(card.debugReason).toBeUndefined();
314
+ expect(card.children).toEqual([]);
315
+ });
316
+
317
+ it('keeps existing OpenCode child roll-up behavior intact', () => {
318
+ const now = Date.now();
319
+ const child = makeSession({
320
+ id: 'opencode-child-1',
321
+ realTimeStatus: 'busy',
322
+ time: {
323
+ created: now - 10_000,
324
+ updated: now - 2_000,
325
+ },
326
+ });
327
+
328
+ const parent = makeSession({
329
+ id: 'opencode-parent-1',
330
+ realTimeStatus: 'idle',
331
+ children: [child],
332
+ });
333
+
334
+ const card = transformSession(parent);
335
+
336
+ expect(card.status).toBe('busy');
337
+ expect(card.opencodeStatus).toBe('busy');
338
+ expect(card.debugReason).toBe('child_recent_activity');
339
+ expect(card.children).toEqual([
340
+ expect.objectContaining({
341
+ id: 'opencode-child-1',
342
+ }),
343
+ ]);
344
+ });
345
+
346
+ it('does not promote an OpenCode parent when a child is idle but waiting', () => {
347
+ const now = Date.now();
348
+ const child = makeSession({
349
+ id: 'opencode-child-idle-waiting',
350
+ realTimeStatus: 'idle',
351
+ waitingForUser: true,
352
+ time: {
353
+ created: now - 10_000,
354
+ updated: now - 2_000,
355
+ },
356
+ });
357
+
358
+ const parent = makeSession({
359
+ id: 'opencode-parent-idle-waiting',
360
+ realTimeStatus: 'idle',
361
+ children: [child],
362
+ });
363
+
364
+ const card = transformSession(parent);
365
+ const firstChild = card.children?.[0];
366
+
367
+ expect(card.status).toBe('idle');
368
+ expect(card.opencodeStatus).toBe('idle');
369
+ expect(card.waitingForUser).toBe(false);
370
+ expect(card.debugReason).toBeUndefined();
371
+ expect(firstChild?.waitingForUser).toBe(false);
372
+ expect(firstChild?.debugReason).toBeUndefined();
373
+ });
374
+ });
@@ -1,4 +1,5 @@
1
- import { KanbanCard, OpencodeSession, KanbanColumn, SessionDebugReason } from '@/types';
1
+ import { KanbanCard, OpencodeSession, KanbanColumn, SessionDebugReason, SessionProvider } from '@/types';
2
+ import { DEFAULT_PROVIDER_CONTEXT, getDefaultProviderContext } from './session-providers/providerIds';
2
3
 
3
4
  interface EnrichedSession extends OpencodeSession {
4
5
  realTimeStatus?: 'idle' | 'busy' | 'retry';
@@ -10,22 +11,80 @@ interface EnrichedSession extends OpencodeSession {
10
11
  type EnrichedChild = NonNullable<EnrichedSession['children']>[number];
11
12
 
12
13
  const RECENT_ACTIVITY_FALLBACK_MS = 5 * 60 * 1000;
14
+ const CHILD_BLOCKER_STALENESS_MS = 10 * 60 * 1000;
15
+
16
+ function getChildActivityTimestamp(child: EnrichedChild | undefined): number | undefined {
17
+ const childUpdatedAt = child?.time?.updated || child?.time?.created;
18
+ return typeof childUpdatedAt === 'number' && childUpdatedAt > 0 ? childUpdatedAt : undefined;
19
+ }
20
+
21
+ function isVerifiedClaudeChild(parent: EnrichedSession, child: EnrichedChild | undefined): boolean {
22
+ if (!child || child.provider !== 'claude-code') {
23
+ return true;
24
+ }
25
+
26
+ const parentProvider = parent.provider ?? DEFAULT_PROVIDER_CONTEXT.provider;
27
+ if (parentProvider !== 'claude-code') {
28
+ return false;
29
+ }
30
+
31
+ return (
32
+ typeof child.id === 'string' &&
33
+ child.id.length > 0 &&
34
+ child.id !== parent.id &&
35
+ child.parentID === parent.id &&
36
+ getChildActivityTimestamp(child) !== undefined
37
+ );
38
+ }
13
39
 
14
40
  function isRecentlyUpdated(updatedAt: number | undefined, now: number): boolean {
15
41
  return typeof updatedAt === 'number' && updatedAt > 0 && now - updatedAt <= RECENT_ACTIVITY_FALLBACK_MS;
16
42
  }
17
43
 
18
- function deriveChildDebugReason(child: EnrichedChild | undefined, now: number): SessionDebugReason | undefined {
44
+ function getChildProvider(parent: EnrichedSession, child: EnrichedChild | undefined): SessionProvider {
45
+ return child?.provider ?? parent.provider ?? DEFAULT_PROVIDER_CONTEXT.provider;
46
+ }
47
+
48
+ function shouldMarkChildWaitingForUser(parent: EnrichedSession, child: EnrichedChild | undefined): boolean {
49
+ if (!child) return false;
50
+
51
+ const childStatus = child.realTimeStatus || 'idle';
52
+ const childProvider = getChildProvider(parent, child);
53
+
54
+ return (
55
+ childStatus === 'retry' ||
56
+ (childProvider === 'claude-code'
57
+ ? !!child.waitingForUser
58
+ : childStatus === 'busy' && !!child.waitingForUser)
59
+ );
60
+ }
61
+
62
+ function isFreshWaitingChildBlocker(parent: EnrichedSession, child: EnrichedChild | undefined, now: number): boolean {
63
+ if (!child) return false;
64
+
65
+ if (!shouldMarkChildWaitingForUser(parent, child)) {
66
+ return false;
67
+ }
68
+
69
+ const childUpdated = getChildActivityTimestamp(child);
70
+ return childUpdated !== undefined && (now - childUpdated) < CHILD_BLOCKER_STALENESS_MS;
71
+ }
72
+
73
+ function deriveChildDebugReason(
74
+ parent: EnrichedSession,
75
+ child: EnrichedChild | undefined,
76
+ now: number
77
+ ): SessionDebugReason | undefined {
19
78
  if (!child) return undefined;
20
79
  if (child.debugReason) return child.debugReason;
21
80
 
22
81
  const childStatus = child.realTimeStatus || 'idle';
23
- if (child.waitingForUser || childStatus === 'retry') {
82
+ if (shouldMarkChildWaitingForUser(parent, child)) {
24
83
  return 'waiting_for_user';
25
84
  }
26
85
 
27
86
  if (childStatus === 'busy') {
28
- const childUpdatedAt = child.time?.updated || child.time?.created;
87
+ const childUpdatedAt = getChildActivityTimestamp(child);
29
88
  return isRecentlyUpdated(childUpdatedAt, now) ? 'child_recent_activity' : 'child_unknown_fallback';
30
89
  }
31
90
 
@@ -53,8 +112,8 @@ function deriveSessionDebugReason({
53
112
 
54
113
  if (waitingForUser) {
55
114
  return (
56
- deriveChildDebugReason(firstWaitingChild, now) ||
57
- deriveChildDebugReason(firstActiveChild, now) ||
115
+ deriveChildDebugReason(session, firstWaitingChild, now) ||
116
+ deriveChildDebugReason(session, firstActiveChild, now) ||
58
117
  'waiting_for_user'
59
118
  );
60
119
  }
@@ -79,43 +138,35 @@ function deriveSessionDebugReason({
79
138
  export function transformSession(session: EnrichedSession): KanbanCard {
80
139
  let status: KanbanColumn;
81
140
  const children = session.children || [];
141
+ const rollupChildren = children.filter((child) => isVerifiedClaudeChild(session, child));
82
142
  const sessionSlug = typeof session.slug === 'string' ? session.slug : '';
83
143
 
84
- // Staleness window: child blockers older than this don't keep parent in review
85
- const CHILD_BLOCKER_STALENESS_MS = 10 * 60 * 1000; // 10 minutes
86
144
  const now = Date.now();
87
145
 
88
146
  const realTimeStatus = session.realTimeStatus || 'idle';
89
- const hasActiveChildren = children.some((child) => {
147
+ const hasBusyChildren = rollupChildren.some((child) => {
90
148
  const childStatus = child.realTimeStatus || 'idle';
91
149
  return childStatus === 'busy' || childStatus === 'retry';
92
150
  });
151
+ const hasWaitingChildren = rollupChildren.some((child) => isFreshWaitingChildBlocker(session, child, now));
152
+ const hasActiveChildren = hasBusyChildren || hasWaitingChildren;
93
153
  const effectiveStatus =
94
154
  realTimeStatus === 'retry'
95
155
  ? 'retry'
96
156
  : (realTimeStatus === 'busy' || hasActiveChildren)
97
157
  ? 'busy'
98
158
  : 'idle';
99
- const hasWaitingChildren = children.some((child) => {
100
- const childStatus = child.realTimeStatus || 'idle';
101
- const isBlocker = childStatus === 'retry' || (childStatus !== 'idle' && !!child.waitingForUser);
102
- if (!isBlocker) return false;
103
- // Only consider fresh blockers (within staleness window)
104
- const childUpdated = child.time?.updated || now;
105
- return (now - childUpdated) < CHILD_BLOCKER_STALENESS_MS;
106
- });
107
159
  const parentWaiting = !!session.waitingForUser;
108
160
  const waitingForUser =
109
161
  effectiveStatus === 'retry' ||
110
162
  parentWaiting ||
111
- (effectiveStatus === 'busy' && hasWaitingChildren);
112
- const firstActiveChild = children.find((child) => {
163
+ hasWaitingChildren;
164
+ const firstActiveChild = rollupChildren.find((child) => {
113
165
  const childStatus = child.realTimeStatus || 'idle';
114
166
  return childStatus === 'busy' || childStatus === 'retry';
115
167
  });
116
- const firstWaitingChild = children.find((child) => {
117
- const childStatus = child.realTimeStatus || 'idle';
118
- return childStatus === 'retry' || (childStatus !== 'idle' && !!child.waitingForUser);
168
+ const firstWaitingChild = rollupChildren.find((child) => {
169
+ return isFreshWaitingChildBlocker(session, child, now);
119
170
  });
120
171
  const debugReason = deriveSessionDebugReason({
121
172
  session,
@@ -136,8 +187,10 @@ export function transformSession(session: EnrichedSession): KanbanCard {
136
187
  status = 'idle';
137
188
  }
138
189
 
139
- return {
140
- id: session.id,
190
+ const providerDefaults = getDefaultProviderContext(session.provider ?? DEFAULT_PROVIDER_CONTEXT.provider);
191
+
192
+ return {
193
+ id: session.id,
141
194
  sessionSlug,
142
195
  title: session.title || 'Untitled Session',
143
196
  directory: session.directory,
@@ -161,19 +214,25 @@ export function transformSession(session: EnrichedSession): KanbanCard {
161
214
  hostBaseUrl: session.hostBaseUrl,
162
215
  rawSessionId: session.rawSessionId,
163
216
  sourceSessionKey: session.sourceSessionKey,
164
- readOnly: session.readOnly,
165
- children: children.map(c => ({
166
- id: c.id,
167
- title: c.title,
168
- realTimeStatus: c.realTimeStatus || 'idle',
169
- waitingForUser:
170
- (c.realTimeStatus || 'idle') === 'retry' ||
171
- ((c.realTimeStatus || 'idle') === 'busy' && !!c.waitingForUser),
172
- debugReason: deriveChildDebugReason(c, now),
173
- createdAt: c.time?.created || 0,
174
- updatedAt: c.time?.updated || 0,
175
- })),
176
- };
217
+ readOnly: session.readOnly ?? providerDefaults.readOnly,
218
+ capabilities: session.capabilities ?? providerDefaults.capabilities,
219
+ provider: session.provider ?? providerDefaults.provider,
220
+ providerRawId: session.providerRawId ?? session.rawSessionId,
221
+ children: rollupChildren.map((c) => {
222
+ const childStatus = c.realTimeStatus || 'idle';
223
+ const childWaitingForUser = shouldMarkChildWaitingForUser(session, c);
224
+
225
+ return {
226
+ id: c.id,
227
+ title: c.title,
228
+ realTimeStatus: childStatus,
229
+ waitingForUser: childWaitingForUser,
230
+ debugReason: deriveChildDebugReason(session, c, now),
231
+ createdAt: c.time?.created || 0,
232
+ updatedAt: c.time?.updated || 0,
233
+ };
234
+ }),
235
+ };
177
236
  }
178
237
 
179
238
  function extractAgents(slug?: string): string[] {
@@ -9,8 +9,17 @@ export type SessionDebugReason =
9
9
  | 'unknown_fallback'
10
10
  | 'waiting_for_user';
11
11
 
12
+ export type SessionProvider = 'opencode' | 'claude-code';
13
+
14
+ export interface SessionCapabilities {
15
+ openProject: boolean;
16
+ openEditor: boolean;
17
+ archive: boolean;
18
+ delete: boolean;
19
+ }
20
+
12
21
  export interface KanbanCard {
13
- id: string; // composite key: hostId:sessionId
22
+ id: string;
14
23
  sessionSlug: string;
15
24
  title: string;
16
25
  directory: string;
@@ -28,14 +37,16 @@ export interface KanbanCard {
28
37
  updatedAt: number;
29
38
  archivedAt?: number;
30
39
  sortOrder: number;
31
- // Host-aware fields (direct, not nested)
32
40
  hostId?: string;
33
41
  hostLabel?: string;
34
42
  hostKind?: HostSourceKind;
35
43
  hostBaseUrl?: string;
36
- rawSessionId?: string; // original session ID without host prefix
37
- sourceSessionKey?: string; // alias for id, kept for compatibility
44
+ rawSessionId?: string;
45
+ sourceSessionKey?: string;
38
46
  readOnly?: boolean;
47
+ capabilities?: SessionCapabilities;
48
+ provider?: SessionProvider;
49
+ providerRawId?: string;
39
50
  children?: {
40
51
  id: string;
41
52
  title?: string;
@@ -47,15 +58,14 @@ export interface KanbanCard {
47
58
  }[];
48
59
  }
49
60
 
50
- // OpenCode event types
51
61
  export interface OpencodeSession {
52
- id: string; // composite key: hostId:sessionId
62
+ id: string;
53
63
  slug: string;
54
64
  title?: string;
55
65
  directory: string;
56
66
  projectName?: string;
57
67
  branch?: string;
58
- parentID?: string; // Used to filter subagents
68
+ parentID?: string;
59
69
  time: {
60
70
  created: number;
61
71
  updated: number;
@@ -64,18 +74,20 @@ export interface OpencodeSession {
64
74
  messageCount?: number;
65
75
  hasTodos?: boolean;
66
76
  hasTranscript?: boolean;
67
- realTimeStatus?: 'idle' | 'busy' | 'retry'; // Real-time status
77
+ realTimeStatus?: 'idle' | 'busy' | 'retry';
68
78
  waitingForUser?: boolean;
69
79
  debugReason?: SessionDebugReason;
70
80
  children?: OpencodeSession[];
71
- // Host-aware fields (direct, not nested)
72
81
  hostId?: string;
73
82
  hostLabel?: string;
74
83
  hostKind?: HostSourceKind;
75
84
  hostBaseUrl?: string;
76
- rawSessionId?: string; // original session ID without host prefix
77
- sourceSessionKey?: string; // alias for id, kept for compatibility
85
+ rawSessionId?: string;
86
+ sourceSessionKey?: string;
78
87
  readOnly?: boolean;
88
+ capabilities?: SessionCapabilities;
89
+ provider?: SessionProvider;
90
+ providerRawId?: string;
79
91
  }
80
92
 
81
93
  export type OpencodeEventType =
@@ -105,10 +117,8 @@ export interface OpencodeEvent {
105
117
  timestamp: number;
106
118
  }
107
119
 
108
- // Status mapping
109
120
  export type OpencodeStatus = 'idle' | 'busy' | 'retry';
110
121
 
111
- // Host source types
112
122
  export type HostSourceKind = 'local' | 'remote';
113
123
 
114
124
  export interface BuiltInHostSource {
@@ -139,10 +149,6 @@ export interface HostStatus {
139
149
  baseUrl?: string;
140
150
  }
141
151
 
142
- // Host-aware fields that appear directly on sessions/cards
143
- // (not nested in a wrapper object)
144
-
145
- // Type guards
146
152
  export function isKanbanColumn(value: string): value is KanbanColumn {
147
153
  return ['idle', 'busy', 'review', 'done'].includes(value);
148
154
  }