vibepulse 0.2.0 → 0.2.1

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 (420) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +4 -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 +32 -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/events/route.js +1 -1
  32. package/.next/server/app/api/node/events/route.js.nft.json +1 -1
  33. package/.next/server/app/api/node/health/route.js +1 -1
  34. package/.next/server/app/api/node/health/route.js.nft.json +1 -1
  35. package/.next/server/app/api/node/sessions/[id]/archive/route/app-paths-manifest.json +3 -0
  36. package/.next/server/app/api/node/sessions/[id]/archive/route/build-manifest.json +11 -0
  37. package/.next/server/app/api/node/sessions/[id]/archive/route/server-reference-manifest.json +4 -0
  38. package/.next/server/app/api/node/sessions/[id]/archive/route.js +6 -0
  39. package/.next/server/app/api/node/sessions/[id]/archive/route.js.map +5 -0
  40. package/.next/server/app/api/node/sessions/[id]/archive/route.js.nft.json +1 -0
  41. package/.next/server/app/api/node/sessions/[id]/archive/route_client-reference-manifest.js +2 -0
  42. package/.next/server/app/api/node/sessions/[id]/delete/route/app-paths-manifest.json +3 -0
  43. package/.next/server/app/api/node/sessions/[id]/delete/route/build-manifest.json +11 -0
  44. package/.next/server/app/api/node/sessions/[id]/delete/route/server-reference-manifest.json +4 -0
  45. package/.next/server/app/api/node/sessions/[id]/delete/route.js +7 -0
  46. package/.next/server/app/api/node/sessions/[id]/delete/route.js.map +5 -0
  47. package/.next/server/app/api/node/sessions/[id]/delete/route.js.nft.json +1 -0
  48. package/.next/server/app/api/node/sessions/[id]/delete/route_client-reference-manifest.js +2 -0
  49. package/.next/server/app/api/node/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
  50. package/.next/server/app/api/node/sessions/[id]/open-editor/route/build-manifest.json +11 -0
  51. package/.next/server/app/api/node/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
  52. package/.next/server/app/api/node/sessions/[id]/open-editor/route.js +7 -0
  53. package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.map +5 -0
  54. package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -0
  55. package/.next/server/app/api/node/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
  56. package/.next/server/app/api/node/sessions/route.js +1 -1
  57. package/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
  58. package/.next/server/app/api/opencode-events/route.js +1 -1
  59. package/.next/server/app/api/opencode-events/route.js.nft.json +1 -1
  60. package/.next/server/app/api/sessions/[id]/archive/route.js +2 -1
  61. package/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  62. package/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
  63. package/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
  64. package/.next/server/app/api/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
  65. package/.next/server/app/api/sessions/[id]/open-editor/route/build-manifest.json +11 -0
  66. package/.next/server/app/api/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
  67. package/.next/server/app/api/sessions/[id]/open-editor/route.js +7 -0
  68. package/.next/server/app/api/sessions/[id]/open-editor/route.js.map +5 -0
  69. package/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -0
  70. package/.next/server/app/api/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
  71. package/.next/server/app/api/sessions/route.js +1 -1
  72. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  73. package/.next/server/app/index.html +1 -1
  74. package/.next/server/app/index.rsc +3 -3
  75. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  76. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  77. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  78. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  79. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  80. package/.next/server/app/page.js +1 -1
  81. package/.next/server/app/page.js.nft.json +1 -1
  82. package/.next/server/app/page_client-reference-manifest.js +1 -1
  83. package/.next/server/app-paths-manifest.json +4 -0
  84. package/.next/server/chunks/[root-of-the-server]__1211da38._.js +3 -0
  85. package/.next/server/chunks/[root-of-the-server]__1211da38._.js.map +1 -0
  86. package/.next/{standalone/.next/server/chunks/[root-of-the-server]__b698889b._.js → server/chunks/[root-of-the-server]__1b87ec42._.js} +2 -2
  87. package/.next/server/chunks/[root-of-the-server]__1b87ec42._.js.map +1 -0
  88. package/.next/server/chunks/[root-of-the-server]__2b526e7a._.js +3 -0
  89. package/.next/server/chunks/[root-of-the-server]__2b526e7a._.js.map +1 -0
  90. package/.next/server/chunks/[root-of-the-server]__2f981540._.js +3 -0
  91. package/.next/server/chunks/[root-of-the-server]__2f981540._.js.map +1 -0
  92. package/.next/server/chunks/[root-of-the-server]__3745b314._.js +3 -0
  93. package/.next/server/chunks/[root-of-the-server]__3745b314._.js.map +1 -0
  94. package/.next/server/chunks/[root-of-the-server]__56690af0._.js +1 -1
  95. package/.next/server/chunks/[root-of-the-server]__56690af0._.js.map +1 -1
  96. package/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
  97. package/.next/server/chunks/[root-of-the-server]__56f5f249._.js.map +1 -1
  98. package/.next/server/chunks/[root-of-the-server]__59175de4._.js +1 -1
  99. package/.next/server/chunks/[root-of-the-server]__59175de4._.js.map +1 -1
  100. package/.next/server/chunks/[root-of-the-server]__64fffc02._.js +1 -1
  101. package/.next/server/chunks/[root-of-the-server]__64fffc02._.js.map +1 -1
  102. package/.next/server/chunks/[root-of-the-server]__6c428a24._.js +3 -0
  103. package/.next/server/chunks/[root-of-the-server]__6c428a24._.js.map +1 -0
  104. package/.next/server/chunks/[root-of-the-server]__73a00b88._.js +3 -0
  105. package/.next/server/chunks/[root-of-the-server]__73a00b88._.js.map +1 -0
  106. package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +1 -1
  107. package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js.map +1 -1
  108. package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +1 -1
  109. package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js.map +1 -1
  110. package/.next/server/chunks/[root-of-the-server]__b796d06c._.js +1 -1
  111. package/.next/server/chunks/[root-of-the-server]__b796d06c._.js.map +1 -1
  112. package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +1 -1
  113. package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js.map +1 -1
  114. package/.next/server/chunks/[root-of-the-server]__d8e61048._.js +3 -0
  115. package/.next/server/chunks/[root-of-the-server]__d8e61048._.js.map +1 -0
  116. package/.next/server/chunks/[root-of-the-server]__db285678._.js +3 -0
  117. package/.next/server/chunks/[root-of-the-server]__db285678._.js.map +1 -0
  118. package/.next/{standalone/.next/server/chunks/[root-of-the-server]__16a9eb0a._.js → server/chunks/[root-of-the-server]__e00a9200._.js} +2 -2
  119. package/.next/server/chunks/{[root-of-the-server]__16a9eb0a._.js.map → [root-of-the-server]__e00a9200._.js.map} +1 -1
  120. package/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js +3 -0
  121. package/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js.map +1 -0
  122. package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_archive_route_actions_237255a5.js +3 -0
  123. package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_archive_route_actions_237255a5.js.map +1 -0
  124. package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_delete_route_actions_e5d426f6.js +3 -0
  125. package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_delete_route_actions_e5d426f6.js.map +1 -0
  126. package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_open-editor_route_actions_eaebf476.js +3 -0
  127. package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_open-editor_route_actions_eaebf476.js.map +1 -0
  128. package/.next/server/chunks/ce889_server_app_api_node_sessions_[id]_open-editor_route_actions_791cdf5b.js +3 -0
  129. package/.next/server/chunks/ce889_server_app_api_node_sessions_[id]_open-editor_route_actions_791cdf5b.js.map +1 -0
  130. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js +1 -1
  131. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js.map +1 -1
  132. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
  133. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js.map +1 -1
  134. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js +2 -2
  135. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js.map +1 -1
  136. package/.next/server/chunks/ssr/{[root-of-the-server]__efc52f08._.js → [root-of-the-server]__631e12d0._.js} +2 -2
  137. package/.next/server/chunks/ssr/[root-of-the-server]__a8cd3911._.js +3 -0
  138. package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
  139. package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js.map +1 -1
  140. package/.next/server/pages/404.html +1 -1
  141. package/.next/server/pages/500.html +2 -2
  142. package/.next/server/server-reference-manifest.js +1 -1
  143. package/.next/server/server-reference-manifest.json +1 -1
  144. package/.next/standalone/.next/BUILD_ID +1 -1
  145. package/.next/standalone/.next/app-path-routes-manifest.json +4 -0
  146. package/.next/standalone/.next/build-manifest.json +2 -2
  147. package/.next/standalone/.next/prerender-manifest.json +3 -3
  148. package/.next/standalone/.next/routes-manifest.json +32 -0
  149. package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
  150. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  151. package/.next/standalone/.next/server/app/_global-error.html +2 -2
  152. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  153. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  154. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  155. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  156. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  157. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  158. package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  159. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  160. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  161. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  162. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  163. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  164. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  165. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  166. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  167. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  168. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  169. package/.next/standalone/.next/server/app/api/node/events/route.js +1 -1
  170. package/.next/standalone/.next/server/app/api/node/events/route.js.nft.json +1 -1
  171. package/.next/standalone/.next/server/app/api/node/health/route.js +1 -1
  172. package/.next/standalone/.next/server/app/api/node/health/route.js.nft.json +1 -1
  173. package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route/app-paths-manifest.json +3 -0
  174. package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route/build-manifest.json +11 -0
  175. package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route/server-reference-manifest.json +4 -0
  176. package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route.js +6 -0
  177. package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route.js.map +5 -0
  178. package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route.js.nft.json +1 -0
  179. package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route_client-reference-manifest.js +2 -0
  180. package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route/app-paths-manifest.json +3 -0
  181. package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route/build-manifest.json +11 -0
  182. package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route/server-reference-manifest.json +4 -0
  183. package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js +7 -0
  184. package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js.map +5 -0
  185. package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js.nft.json +1 -0
  186. package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route_client-reference-manifest.js +2 -0
  187. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
  188. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route/build-manifest.json +11 -0
  189. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
  190. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js +7 -0
  191. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.map +5 -0
  192. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -0
  193. package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
  194. package/.next/standalone/.next/server/app/api/node/sessions/route.js +1 -1
  195. package/.next/standalone/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
  196. package/.next/standalone/.next/server/app/api/opencode-events/route.js +1 -1
  197. package/.next/standalone/.next/server/app/api/opencode-events/route.js.nft.json +1 -1
  198. package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js +2 -1
  199. package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  200. package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
  201. package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
  202. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
  203. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route/build-manifest.json +11 -0
  204. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
  205. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js +7 -0
  206. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.map +5 -0
  207. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -0
  208. package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
  209. package/.next/standalone/.next/server/app/api/sessions/route.js +1 -1
  210. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  211. package/.next/standalone/.next/server/app/index.html +1 -1
  212. package/.next/standalone/.next/server/app/index.rsc +3 -3
  213. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  214. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  215. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  216. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  217. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  218. package/.next/standalone/.next/server/app/page.js +1 -1
  219. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  220. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  221. package/.next/standalone/.next/server/app-paths-manifest.json +4 -0
  222. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1211da38._.js +3 -0
  223. package/.next/{server/chunks/[root-of-the-server]__b698889b._.js → standalone/.next/server/chunks/[root-of-the-server]__1b87ec42._.js} +2 -2
  224. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2b526e7a._.js +3 -0
  225. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f981540._.js +3 -0
  226. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3745b314._.js +3 -0
  227. package/.next/standalone/.next/server/chunks/[root-of-the-server]__56690af0._.js +1 -1
  228. package/.next/standalone/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
  229. package/.next/standalone/.next/server/chunks/[root-of-the-server]__59175de4._.js +1 -1
  230. package/.next/standalone/.next/server/chunks/[root-of-the-server]__64fffc02._.js +1 -1
  231. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c428a24._.js +3 -0
  232. package/.next/standalone/.next/server/chunks/[root-of-the-server]__73a00b88._.js +3 -0
  233. package/.next/standalone/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +1 -1
  234. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +1 -1
  235. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b796d06c._.js +1 -1
  236. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +1 -1
  237. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8e61048._.js +3 -0
  238. package/.next/standalone/.next/server/chunks/[root-of-the-server]__db285678._.js +3 -0
  239. package/.next/{server/chunks/[root-of-the-server]__16a9eb0a._.js → standalone/.next/server/chunks/[root-of-the-server]__e00a9200._.js} +2 -2
  240. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js +3 -0
  241. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_archive_route_actions_237255a5.js +3 -0
  242. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_delete_route_actions_e5d426f6.js +3 -0
  243. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_open-editor_route_actions_eaebf476.js +3 -0
  244. package/.next/standalone/.next/server/chunks/ce889_server_app_api_node_sessions_[id]_open-editor_route_actions_791cdf5b.js +3 -0
  245. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js +1 -1
  246. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
  247. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js +2 -2
  248. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__efc52f08._.js → [root-of-the-server]__631e12d0._.js} +2 -2
  249. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__a8cd3911._.js +3 -0
  250. package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
  251. package/.next/standalone/.next/server/pages/404.html +1 -1
  252. package/.next/standalone/.next/server/pages/500.html +2 -2
  253. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  254. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  255. package/.next/standalone/.next/static/chunks/7ac19aaef01f4a03.js +13 -0
  256. package/.next/standalone/.next/static/chunks/f42202943f6742e5.css +3 -0
  257. package/.next/standalone/AGENTS.md +85 -0
  258. package/.next/standalone/README.md +76 -0
  259. package/.next/standalone/__mocks__/child_process.ts +4 -0
  260. package/.next/standalone/bin/dev-runtime.js +58 -0
  261. package/.next/standalone/bin/vibepulse.js +87 -0
  262. package/.next/standalone/check-hsql.mjs +71 -0
  263. package/.next/standalone/docs/session-status-detection.md +258 -0
  264. package/.next/standalone/eslint.config.mjs +31 -0
  265. package/.next/standalone/next.config.ts +8 -0
  266. package/.next/standalone/package-lock.json +10312 -0
  267. package/.next/standalone/package.json +1 -1
  268. package/.next/standalone/postcss.config.mjs +7 -0
  269. package/.next/standalone/src/AGENTS.md +41 -0
  270. package/.next/standalone/src/app/api/AGENTS.md +40 -0
  271. package/.next/standalone/src/app/api/node/events/route.test.ts +196 -0
  272. package/.next/standalone/src/app/api/node/events/route.ts +259 -0
  273. package/.next/standalone/src/app/api/node/health/route.test.ts +190 -0
  274. package/.next/standalone/src/app/api/node/health/route.ts +48 -0
  275. package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.test.ts +128 -0
  276. package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.ts +97 -0
  277. package/.next/standalone/src/app/api/node/sessions/[id]/delete/route.test.ts +113 -0
  278. package/.next/standalone/src/app/api/node/sessions/[id]/delete/route.ts +81 -0
  279. package/.next/standalone/src/app/api/node/sessions/[id]/open-editor/route.test.ts +206 -0
  280. package/.next/standalone/src/app/api/node/sessions/[id]/open-editor/route.ts +123 -0
  281. package/.next/standalone/src/app/api/node/sessions/route.test.ts +408 -0
  282. package/.next/standalone/src/app/api/node/sessions/route.ts +1094 -0
  283. package/.next/standalone/src/app/api/nodes/route.test.ts +237 -0
  284. package/.next/standalone/src/app/api/nodes/route.ts +176 -0
  285. package/.next/standalone/src/app/api/opencode-config/route.test.ts +86 -0
  286. package/.next/standalone/src/app/api/opencode-config/route.ts +376 -0
  287. package/.next/standalone/src/app/api/opencode-config/status/route.ts +31 -0
  288. package/.next/standalone/src/app/api/opencode-events/route.test.ts +624 -0
  289. package/.next/standalone/src/app/api/opencode-events/route.ts +508 -0
  290. package/.next/standalone/src/app/api/opencode-models/route.test.ts +167 -0
  291. package/.next/standalone/src/app/api/opencode-models/route.ts +76 -0
  292. package/.next/standalone/src/app/api/profiles/[id]/apply/route.ts +49 -0
  293. package/.next/standalone/src/app/api/profiles/[id]/export/route.ts +31 -0
  294. package/.next/standalone/src/app/api/profiles/[id]/route.ts +160 -0
  295. package/.next/standalone/src/app/api/profiles/import/route.test.js +107 -0
  296. package/.next/standalone/src/app/api/profiles/import/route.ts +65 -0
  297. package/.next/standalone/src/app/api/profiles/route.ts +107 -0
  298. package/.next/standalone/src/app/api/sessions/[id]/archive/route.test.ts +136 -0
  299. package/.next/standalone/src/app/api/sessions/[id]/archive/route.ts +170 -0
  300. package/.next/standalone/src/app/api/sessions/[id]/delete/route.test.ts +113 -0
  301. package/.next/standalone/src/app/api/sessions/[id]/delete/route.ts +137 -0
  302. package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.test.ts +218 -0
  303. package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.ts +85 -0
  304. package/.next/standalone/src/app/api/sessions/[id]/route.test.ts +531 -0
  305. package/.next/standalone/src/app/api/sessions/[id]/route.ts +75 -0
  306. package/.next/standalone/src/app/api/sessions/route.test.ts +1298 -0
  307. package/.next/standalone/src/app/api/sessions/route.ts +1695 -0
  308. package/.next/standalone/src/app/favicon.ico +0 -0
  309. package/.next/standalone/src/app/globals.css +66 -0
  310. package/.next/standalone/src/app/layout.tsx +37 -0
  311. package/.next/standalone/src/app/page.test.tsx +134 -0
  312. package/.next/standalone/src/app/page.tsx +358 -0
  313. package/.next/standalone/src/components/AGENTS.md +42 -0
  314. package/.next/standalone/src/components/ErrorBoundary.tsx +72 -0
  315. package/.next/standalone/src/components/KanbanBoard.test.tsx +704 -0
  316. package/.next/standalone/src/components/KanbanBoard.tsx +852 -0
  317. package/.next/standalone/src/components/LoadingState.tsx +37 -0
  318. package/.next/standalone/src/components/ProjectCard.test.tsx +773 -0
  319. package/.next/standalone/src/components/ProjectCard.tsx +595 -0
  320. package/.next/standalone/src/components/QueryProvider.tsx +25 -0
  321. package/.next/standalone/src/components/SessionCard.test.tsx +566 -0
  322. package/.next/standalone/src/components/SessionCard.tsx +434 -0
  323. package/.next/standalone/src/components/SessionList.tsx +60 -0
  324. package/.next/standalone/src/components/host-config/HostManagerDialog.test.tsx +252 -0
  325. package/.next/standalone/src/components/host-config/HostManagerDialog.tsx +476 -0
  326. package/.next/standalone/src/components/opencode-config/AgentConfigForm.test.tsx +72 -0
  327. package/.next/standalone/src/components/opencode-config/AgentConfigForm.tsx +483 -0
  328. package/.next/standalone/src/components/opencode-config/AgentModelSelector.tsx +284 -0
  329. package/.next/standalone/src/components/opencode-config/AgentsConfigPanel.tsx +162 -0
  330. package/.next/standalone/src/components/opencode-config/ConfigButton.tsx +43 -0
  331. package/.next/standalone/src/components/opencode-config/ConfigPanel.tsx +91 -0
  332. package/.next/standalone/src/components/opencode-config/FullscreenConfigPanel.tsx +435 -0
  333. package/.next/standalone/src/components/opencode-config/GeneralSettingsForm.test.tsx +91 -0
  334. package/.next/standalone/src/components/opencode-config/GeneralSettingsForm.tsx +288 -0
  335. package/.next/standalone/src/components/opencode-config/categories/CategoriesList.tsx +382 -0
  336. package/.next/standalone/src/components/opencode-config/categories/CategoriesManager.test.tsx +111 -0
  337. package/.next/standalone/src/components/opencode-config/categories/CategoriesManager.tsx +174 -0
  338. package/.next/standalone/src/components/opencode-config/categories/CategoryConfigForm.tsx +453 -0
  339. package/.next/standalone/src/components/opencode-config/profiles/ProfileCard.tsx +140 -0
  340. package/.next/standalone/src/components/opencode-config/profiles/ProfileEditor.tsx +446 -0
  341. package/.next/standalone/src/components/opencode-config/profiles/ProfileList.tsx +446 -0
  342. package/.next/standalone/src/components/opencode-config/profiles/ProfileManager.test.tsx +225 -0
  343. package/.next/standalone/src/components/opencode-config/profiles/ProfileManager.tsx +405 -0
  344. package/.next/standalone/src/components/ui/Tabs.tsx +59 -0
  345. package/.next/standalone/src/hooks/useHostSources.test.ts +509 -0
  346. package/.next/standalone/src/hooks/useHostSources.ts +299 -0
  347. package/.next/standalone/src/hooks/useOpencodeSync.test.ts +387 -0
  348. package/.next/standalone/src/hooks/useOpencodeSync.ts +571 -0
  349. package/.next/standalone/src/index.ts +2 -0
  350. package/.next/standalone/src/lib/editorLauncher.server.ts +36 -0
  351. package/.next/standalone/src/lib/editorLauncher.test.ts +35 -0
  352. package/.next/standalone/src/lib/editorLauncher.ts +25 -0
  353. package/.next/standalone/src/lib/hostAccent.test.ts +58 -0
  354. package/.next/standalone/src/lib/hostAccent.ts +46 -0
  355. package/.next/standalone/src/lib/hostIdentity.test.ts +187 -0
  356. package/.next/standalone/src/lib/hostIdentity.ts +122 -0
  357. package/.next/standalone/src/lib/hostSourcesStorage.test.ts +141 -0
  358. package/.next/standalone/src/lib/hostSourcesStorage.ts +72 -0
  359. package/.next/standalone/src/lib/nodeProtocol.test.ts +159 -0
  360. package/.next/standalone/src/lib/nodeProtocol.ts +142 -0
  361. package/.next/standalone/src/lib/nodeRegistry.test.ts +173 -0
  362. package/.next/standalone/src/lib/nodeRegistry.ts +398 -0
  363. package/.next/standalone/src/lib/notificationSound.ts +292 -0
  364. package/.next/standalone/src/lib/opencodeConfig.test.ts +100 -0
  365. package/.next/standalone/src/lib/opencodeConfig.ts +76 -0
  366. package/.next/standalone/src/lib/opencodeDiscovery.ts +275 -0
  367. package/.next/standalone/src/lib/profiles/share.test.ts +91 -0
  368. package/.next/standalone/src/lib/profiles/share.ts +93 -0
  369. package/.next/standalone/src/lib/profiles/storage.test.ts +108 -0
  370. package/.next/standalone/src/lib/profiles/storage.ts +370 -0
  371. package/.next/standalone/src/lib/runtimeMode.test.ts +29 -0
  372. package/.next/standalone/src/lib/runtimeMode.ts +29 -0
  373. package/.next/standalone/src/lib/sessionActionErrors.ts +37 -0
  374. package/.next/standalone/src/lib/sessionArchiveOverrides.test.ts +43 -0
  375. package/.next/standalone/src/lib/sessionArchiveOverrides.ts +116 -0
  376. package/.next/standalone/src/lib/transform.test.ts +121 -0
  377. package/.next/standalone/src/lib/transform.ts +193 -0
  378. package/.next/standalone/src/test/setup.ts +8 -0
  379. package/.next/standalone/src/types/index.ts +152 -0
  380. package/.next/standalone/src/types/opencodeConfig.ts +149 -0
  381. package/.next/standalone/tsconfig.json +34 -0
  382. package/.next/standalone/tsconfig.lib.json +17 -0
  383. package/.next/standalone/vitest.config.ts +16 -0
  384. package/.next/static/chunks/7ac19aaef01f4a03.js +13 -0
  385. package/.next/static/chunks/f42202943f6742e5.css +3 -0
  386. package/.next/trace +1 -1
  387. package/.next/trace-build +1 -1
  388. package/.next/types/routes.d.ts +5 -1
  389. package/.next/types/validator.ts +36 -0
  390. package/package.json +1 -1
  391. package/.next/server/chunks/[root-of-the-server]__0b017945._.js +0 -3
  392. package/.next/server/chunks/[root-of-the-server]__0b017945._.js.map +0 -1
  393. package/.next/server/chunks/[root-of-the-server]__1e118bd3._.js +0 -3
  394. package/.next/server/chunks/[root-of-the-server]__1e118bd3._.js.map +0 -1
  395. package/.next/server/chunks/[root-of-the-server]__6979e732._.js +0 -3
  396. package/.next/server/chunks/[root-of-the-server]__6979e732._.js.map +0 -1
  397. package/.next/server/chunks/[root-of-the-server]__a7b4d79d._.js +0 -3
  398. package/.next/server/chunks/[root-of-the-server]__a7b4d79d._.js.map +0 -1
  399. package/.next/server/chunks/[root-of-the-server]__b698889b._.js.map +0 -1
  400. package/.next/server/chunks/[root-of-the-server]__ddc251b7._.js +0 -3
  401. package/.next/server/chunks/[root-of-the-server]__ddc251b7._.js.map +0 -1
  402. package/.next/server/chunks/ssr/[root-of-the-server]__b0788643._.js +0 -3
  403. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b017945._.js +0 -3
  404. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1e118bd3._.js +0 -3
  405. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6979e732._.js +0 -3
  406. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a7b4d79d._.js +0 -3
  407. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ddc251b7._.js +0 -3
  408. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__b0788643._.js +0 -3
  409. package/.next/standalone/.next/static/chunks/9f8c22002e7395e8.js +0 -13
  410. package/.next/standalone/.next/static/chunks/f1b55db60e7ed6f3.css +0 -3
  411. package/.next/static/chunks/9f8c22002e7395e8.js +0 -13
  412. package/.next/static/chunks/f1b55db60e7ed6f3.css +0 -3
  413. /package/.next/server/chunks/ssr/{[root-of-the-server]__efc52f08._.js.map → [root-of-the-server]__631e12d0._.js.map} +0 -0
  414. /package/.next/server/chunks/ssr/{[root-of-the-server]__b0788643._.js.map → [root-of-the-server]__a8cd3911._.js.map} +0 -0
  415. /package/.next/standalone/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_buildManifest.js +0 -0
  416. /package/.next/standalone/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_clientMiddlewareManifest.json +0 -0
  417. /package/.next/standalone/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_ssgManifest.js +0 -0
  418. /package/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_buildManifest.js +0 -0
  419. /package/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_clientMiddlewareManifest.json +0 -0
  420. /package/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_ssgManifest.js +0 -0
@@ -0,0 +1,1094 @@
1
+ import { createOpencodeClient } from '@opencode-ai/sdk';
2
+ import { execSync } from 'child_process';
3
+ import path from 'path';
4
+ import {
5
+ discoverOpencodePortsWithMeta,
6
+ discoverOpencodeProcessCwdsWithoutPortWithMeta,
7
+ } from '@/lib/opencodeDiscovery';
8
+ import { readConfig } from '@/lib/opencodeConfig';
9
+ import { composeSourceKey } from '@/lib/hostIdentity';
10
+ import {
11
+ NODE_PROTOCOL_VERSION,
12
+ createNodeFailureResponse,
13
+ guardNodeRequest,
14
+ toNodeRequestGuardResponse,
15
+ type NodeFailureReason,
16
+ } from '@/lib/nodeProtocol';
17
+ import {
18
+ clearSessionForceUnarchived,
19
+ markSessionForceUnarchived,
20
+ pruneSessionStickyStatusBlocked,
21
+ pruneSessionForceUnarchived,
22
+ shouldForceSessionUnarchived,
23
+ takeSessionStickyStatusBlocked,
24
+ } from '@/lib/sessionArchiveOverrides';
25
+
26
+ type SessionLike = {
27
+ id: string;
28
+ slug?: string;
29
+ title?: string;
30
+ directory: string;
31
+ debugReason?: string;
32
+ parentID?: string;
33
+ time?: {
34
+ created: number;
35
+ updated: number;
36
+ archived?: number;
37
+ };
38
+ };
39
+
40
+ type ProcessHint = {
41
+ pid: number;
42
+ directory: string;
43
+ projectName: string;
44
+ reason: 'process_without_api_port';
45
+ };
46
+
47
+ const CHILD_ACTIVE_WINDOW_MS = 30 * 60 * 1000;
48
+ const CHILD_UNKNOWN_STATE_BUSY_WINDOW_MS = 2 * 60 * 1000;
49
+ const CHILD_STATUS_MESSAGE_CHECK_LIMIT = 50;
50
+ const STALL_DETECTION_WINDOW_MS = 30 * 1000;
51
+ const STATUS_STICKY_RETENTION_MS = 24 * 60 * 60 * 1000;
52
+ const STATUS_STICKY_ABSENT_RETENTION_MS = 30 * 60 * 1000;
53
+ const DEFAULT_STATUS_STICKY_MAX_ENTRIES = 5000;
54
+ const GIT_COMMAND_TIMEOUT_MS = 1200;
55
+ const sessionListTimeoutMs = readPositiveTimeoutEnv('OPENCODE_SESSIONS_LIST_TIMEOUT_MS', 6000);
56
+ const sessionStatusTimeoutMs = readPositiveTimeoutEnv('OPENCODE_SESSIONS_STATUS_TIMEOUT_MS', 4000);
57
+ const sessionMessagesTimeoutMs = readPositiveTimeoutEnv('OPENCODE_SESSIONS_MESSAGES_TIMEOUT_MS', 2500);
58
+
59
+ const LOCAL_SOURCE = {
60
+ hostId: 'local',
61
+ hostLabel: 'Local',
62
+ hostKind: 'local',
63
+ } as const;
64
+
65
+ type StableRealtimeStatus = 'idle' | 'busy' | 'retry';
66
+
67
+ type StatusStickyState = {
68
+ lastBusyAt: number;
69
+ lastSeenAt: number;
70
+ };
71
+
72
+ type HostAwareFields = {
73
+ hostId?: typeof LOCAL_SOURCE.hostId;
74
+ hostLabel?: typeof LOCAL_SOURCE.hostLabel;
75
+ hostKind?: typeof LOCAL_SOURCE.hostKind;
76
+ rawSessionId?: string;
77
+ sourceSessionKey?: string;
78
+ readOnly?: boolean;
79
+ };
80
+
81
+ type ChildEntry = HostAwareFields & {
82
+ id: string;
83
+ slug?: string;
84
+ title?: string;
85
+ directory?: string;
86
+ debugReason?: string;
87
+ parentID?: string;
88
+ time?: { created: number; updated: number; archived?: number };
89
+ realTimeStatus: string;
90
+ waitingForUser: boolean;
91
+ };
92
+
93
+ type EnrichedSession = SessionLike & HostAwareFields & {
94
+ projectName: string;
95
+ branch: string | null;
96
+ realTimeStatus: StableRealtimeStatus;
97
+ waitingForUser: boolean;
98
+ children: ChildEntry[];
99
+ };
100
+
101
+ type SessionStatusStabilizationTarget = {
102
+ id: string;
103
+ time?: {
104
+ archived?: number;
105
+ };
106
+ realTimeStatus: string;
107
+ waitingForUser: boolean;
108
+ children: Array<{
109
+ id: string;
110
+ time?: {
111
+ archived?: number;
112
+ };
113
+ realTimeStatus: string;
114
+ waitingForUser: boolean;
115
+ }>;
116
+ };
117
+
118
+ type MessageStateStatus = string;
119
+
120
+ type MessagePart = {
121
+ state?: {
122
+ status?: unknown;
123
+ };
124
+ };
125
+
126
+ type LocalResultMeta = {
127
+ online: boolean;
128
+ degraded?: boolean;
129
+ reason?: string;
130
+ };
131
+
132
+ type LocalSessionsSuccessPayload = {
133
+ sessions: EnrichedSession[];
134
+ processHints: ProcessHint[];
135
+ failedPorts?: Array<{ port: number; reason: string }>;
136
+ degraded?: boolean;
137
+ };
138
+
139
+ type LocalSessionsResult =
140
+ | {
141
+ ok: true;
142
+ payload: LocalSessionsSuccessPayload;
143
+ meta: LocalResultMeta;
144
+ }
145
+ | {
146
+ ok: false;
147
+ reason: NodeFailureReason;
148
+ extras: Record<string, unknown>;
149
+ };
150
+
151
+ type LocalHostStatus = {
152
+ hostId: typeof LOCAL_SOURCE.hostId;
153
+ hostLabel: typeof LOCAL_SOURCE.hostLabel;
154
+ hostKind: typeof LOCAL_SOURCE.hostKind;
155
+ online: boolean;
156
+ degraded?: boolean;
157
+ reason?: string;
158
+ };
159
+
160
+ type NodeSessionsSuccessPayload = {
161
+ ok: true;
162
+ role: 'node';
163
+ protocolVersion: typeof NODE_PROTOCOL_VERSION;
164
+ source: typeof LOCAL_SOURCE;
165
+ upstream: {
166
+ kind: 'opencode';
167
+ reachable: true;
168
+ };
169
+ sessions: EnrichedSession[];
170
+ processHints: ProcessHint[];
171
+ hosts: [LocalHostStatus];
172
+ hostStatuses: [LocalHostStatus];
173
+ failedPorts?: Array<{ port: number; reason: string }>;
174
+ degraded?: true;
175
+ };
176
+
177
+ const statusStickyState = new Map<string, StatusStickyState>();
178
+
179
+ const WAITING_PART_STATUSES = new Set<string>([
180
+ 'awaiting-input',
181
+ 'awaiting_input',
182
+ 'input-required',
183
+ 'input_required',
184
+ 'requires-input',
185
+ 'requires_input',
186
+ 'blocked',
187
+ 'paused',
188
+ ]);
189
+
190
+ export const dynamic = 'force-dynamic';
191
+
192
+ export async function GET(request: Request) {
193
+ const guardResult = guardNodeRequest(request);
194
+ if (!guardResult.ok) {
195
+ return toNodeRequestGuardResponse(guardResult);
196
+ }
197
+
198
+ const stickyBusyDelayMs = await readStickyBusyDelayMs();
199
+ const result = await getLocalSessionsResult(stickyBusyDelayMs);
200
+
201
+ if (!result.ok) {
202
+ return createNodeFailureResponse(result.reason, result.extras);
203
+ }
204
+
205
+ const hostStatus = toLocalHostStatus(result.meta);
206
+
207
+ const payload: NodeSessionsSuccessPayload = {
208
+ ok: true,
209
+ role: 'node',
210
+ protocolVersion: NODE_PROTOCOL_VERSION,
211
+ source: LOCAL_SOURCE,
212
+ upstream: {
213
+ kind: 'opencode',
214
+ reachable: true,
215
+ },
216
+ sessions: result.payload.sessions.map((session) => addLocalHostMetadataToSession(session)),
217
+ processHints: result.payload.processHints,
218
+ hosts: [hostStatus],
219
+ hostStatuses: [hostStatus],
220
+ ...(result.payload.failedPorts ? { failedPorts: result.payload.failedPorts } : {}),
221
+ ...(result.payload.degraded ? { degraded: true as const } : {}),
222
+ };
223
+
224
+ return Response.json(payload);
225
+ }
226
+
227
+ function readPositiveTimeoutEnv(name: string, fallback: number): number {
228
+ const raw = process.env[name];
229
+ const parsed = Number(raw);
230
+ if (Number.isFinite(parsed) && parsed > 0) {
231
+ return Math.floor(parsed);
232
+ }
233
+ return fallback;
234
+ }
235
+
236
+ function withTimeout<T>(operation: (signal: AbortSignal) => Promise<T>, timeoutMs: number, label: string): Promise<T> {
237
+ const timeoutError = new Error(`${label} timed out after ${timeoutMs}ms`);
238
+ const timeoutController = new AbortController();
239
+ let timeoutHandle: NodeJS.Timeout | undefined;
240
+ const timeoutPromise = new Promise<never>((_, reject) => {
241
+ timeoutHandle = setTimeout(() => {
242
+ timeoutController.abort();
243
+ reject(timeoutError);
244
+ }, timeoutMs);
245
+ });
246
+
247
+ const operationPromise = operation(timeoutController.signal).catch((error) => {
248
+ if (timeoutController.signal.aborted) {
249
+ throw timeoutError;
250
+ }
251
+
252
+ throw error;
253
+ });
254
+
255
+ return Promise.race([operationPromise, timeoutPromise]).finally(() => {
256
+ if (timeoutHandle) {
257
+ clearTimeout(timeoutHandle);
258
+ }
259
+ });
260
+ }
261
+
262
+ function normalizePartStatus(status: string): string {
263
+ return status.trim().toLowerCase();
264
+ }
265
+
266
+ function isWaitingPartStatus(status: string): boolean {
267
+ return WAITING_PART_STATUSES.has(normalizePartStatus(status));
268
+ }
269
+
270
+ function collectPartStatuses(messages: Array<{ parts?: MessagePart[] }>): MessageStateStatus[] {
271
+ const partStatuses: MessageStateStatus[] = [];
272
+
273
+ for (const message of messages) {
274
+ for (const part of message.parts || []) {
275
+ const status = part?.state?.status;
276
+ if (typeof status === 'string') {
277
+ const normalized = normalizePartStatus(status);
278
+ if (normalized) {
279
+ partStatuses.push(normalized);
280
+ }
281
+ }
282
+ }
283
+ }
284
+
285
+ return partStatuses;
286
+ }
287
+
288
+ async function fetchPartStatuses(
289
+ client: ReturnType<typeof createOpencodeClient>,
290
+ sessionId: string,
291
+ timeoutMs: number
292
+ ): Promise<MessageStateStatus[]> {
293
+ const messagesResult = await withTimeout(
294
+ (signal) =>
295
+ client.session.messages({
296
+ path: { id: sessionId },
297
+ query: { limit: 8 },
298
+ signal,
299
+ }),
300
+ timeoutMs,
301
+ `session.messages(${sessionId})`
302
+ );
303
+ const messages = (messagesResult.data || []) as Array<{ parts?: MessagePart[] }>;
304
+ return collectPartStatuses(messages);
305
+ }
306
+
307
+ function getUpdatedAt(session: { time?: { updated?: number; created?: number } }): number {
308
+ return session.time?.updated || session.time?.created || 0;
309
+ }
310
+
311
+ function normalizeRealtimeStatus(value: string | undefined): StableRealtimeStatus {
312
+ if (value === 'busy' || value === 'retry') return value;
313
+ return 'idle';
314
+ }
315
+
316
+ function clearStickyStatusState(sessionId: string): void {
317
+ statusStickyState.delete(sessionId);
318
+ statusStickyState.delete(`child:${sessionId}`);
319
+ }
320
+
321
+ function applyStickyBusyStatus(
322
+ id: string,
323
+ status: StableRealtimeStatus,
324
+ now: number,
325
+ stickyBusyWindowMs: number
326
+ ): StableRealtimeStatus {
327
+ const existing = statusStickyState.get(id) ?? { lastBusyAt: 0, lastSeenAt: now };
328
+
329
+ if (status === 'busy') {
330
+ existing.lastBusyAt = now;
331
+ existing.lastSeenAt = now;
332
+ statusStickyState.set(id, existing);
333
+ return status;
334
+ }
335
+
336
+ if (status === 'retry') {
337
+ existing.lastSeenAt = now;
338
+ statusStickyState.set(id, existing);
339
+ return status;
340
+ }
341
+
342
+ const shouldKeepBusy = existing.lastBusyAt > 0 && now - existing.lastBusyAt <= stickyBusyWindowMs;
343
+ existing.lastSeenAt = now;
344
+ statusStickyState.set(id, existing);
345
+ return shouldKeepBusy ? 'busy' : 'idle';
346
+ }
347
+
348
+ function getStickyStateMaxEntries(): number {
349
+ const raw = Number(process.env.OPENCODE_STATUS_STICKY_MAX_ENTRIES);
350
+ if (Number.isFinite(raw) && raw > 0) {
351
+ return Math.floor(raw);
352
+ }
353
+ return DEFAULT_STATUS_STICKY_MAX_ENTRIES;
354
+ }
355
+
356
+ function pruneStickyState(now: number, activeIds: Set<string>): void {
357
+ for (const [id, state] of statusStickyState) {
358
+ const ageMs = now - state.lastSeenAt;
359
+ const isActive = activeIds.has(id);
360
+ if (ageMs > STATUS_STICKY_RETENTION_MS || (!isActive && ageMs > STATUS_STICKY_ABSENT_RETENTION_MS)) {
361
+ statusStickyState.delete(id);
362
+ }
363
+ }
364
+
365
+ const maxEntries = getStickyStateMaxEntries();
366
+ if (statusStickyState.size <= maxEntries) {
367
+ return;
368
+ }
369
+
370
+ const overflow = statusStickyState.size - maxEntries;
371
+ const sortedByLastSeen = Array.from(statusStickyState.entries()).sort((a, b) => a[1].lastSeenAt - b[1].lastSeenAt);
372
+
373
+ let removed = 0;
374
+ for (const [id] of sortedByLastSeen) {
375
+ if (removed >= overflow) break;
376
+ if (activeIds.has(id)) continue;
377
+ statusStickyState.delete(id);
378
+ removed++;
379
+ }
380
+
381
+ if (removed >= overflow) {
382
+ return;
383
+ }
384
+
385
+ for (const [id] of sortedByLastSeen) {
386
+ if (removed >= overflow) break;
387
+ if (!statusStickyState.has(id)) continue;
388
+ statusStickyState.delete(id);
389
+ removed++;
390
+ }
391
+ }
392
+
393
+ function hasRecentActivity(session: { time?: { updated?: number } }, now: number): boolean {
394
+ const updatedAt = session.time?.updated;
395
+ if (!updatedAt) return false;
396
+ return now - updatedAt <= STALL_DETECTION_WINDOW_MS;
397
+ }
398
+
399
+ function toChildEntry(
400
+ child: SessionLike,
401
+ status: StableRealtimeStatus,
402
+ waitingForUser = false
403
+ ): ChildEntry {
404
+ return {
405
+ id: child.id,
406
+ slug: child.slug,
407
+ title: child.title,
408
+ directory: child.directory,
409
+ debugReason: child.debugReason,
410
+ parentID: child.parentID,
411
+ time: child.time,
412
+ realTimeStatus: status,
413
+ waitingForUser,
414
+ };
415
+ }
416
+
417
+ function clearSessionStabilizationState(session: SessionStatusStabilizationTarget): void {
418
+ clearStickyStatusState(session.id);
419
+ clearSessionForceUnarchived(session.id);
420
+ for (const child of session.children) {
421
+ clearStickyStatusState(`child:${child.id}`);
422
+ clearSessionForceUnarchived(child.id);
423
+ }
424
+ }
425
+
426
+ function shouldSkipSessionStatusStabilization(
427
+ session: SessionStatusStabilizationTarget,
428
+ now: number
429
+ ): boolean {
430
+ if (takeSessionStickyStatusBlocked(session.id, now)) {
431
+ clearSessionStabilizationState(session);
432
+ return true;
433
+ }
434
+
435
+ if (session.time?.archived) {
436
+ clearSessionStabilizationState(session);
437
+ return true;
438
+ }
439
+
440
+ return false;
441
+ }
442
+
443
+ function applyStickyStatusStabilization(
444
+ session: SessionStatusStabilizationTarget,
445
+ stickyNow: number,
446
+ stickyBusyDelayMs: number
447
+ ): void {
448
+ for (const child of session.children) {
449
+ if (child.time?.archived) {
450
+ clearStickyStatusState(`child:${child.id}`);
451
+ clearSessionForceUnarchived(child.id);
452
+ continue;
453
+ }
454
+
455
+ const normalizedChildStatus = normalizeRealtimeStatus(child.realTimeStatus);
456
+ const childStatusForStabilization =
457
+ child.waitingForUser && normalizedChildStatus === 'idle' ? 'retry' : normalizedChildStatus;
458
+ child.realTimeStatus = applyStickyBusyStatus(
459
+ `child:${child.id}`,
460
+ childStatusForStabilization,
461
+ stickyNow,
462
+ stickyBusyDelayMs
463
+ );
464
+
465
+ if (child.realTimeStatus === 'busy' || child.realTimeStatus === 'retry' || child.waitingForUser) {
466
+ markSessionForceUnarchived(child.id, stickyNow);
467
+ }
468
+ }
469
+
470
+ const normalizedSessionStatus = normalizeRealtimeStatus(session.realTimeStatus);
471
+ const sessionStatusForStabilization =
472
+ session.waitingForUser && normalizedSessionStatus === 'idle' ? 'retry' : normalizedSessionStatus;
473
+ session.realTimeStatus = applyStickyBusyStatus(
474
+ session.id,
475
+ sessionStatusForStabilization,
476
+ stickyNow,
477
+ stickyBusyDelayMs
478
+ );
479
+
480
+ const hasActiveChildren = session.children.some(
481
+ (child) => child.realTimeStatus === 'busy' || child.realTimeStatus === 'retry' || child.waitingForUser
482
+ );
483
+ const shouldAutoUnarchive =
484
+ session.realTimeStatus === 'busy' ||
485
+ session.realTimeStatus === 'retry' ||
486
+ session.waitingForUser ||
487
+ hasActiveChildren;
488
+
489
+ if (shouldAutoUnarchive) {
490
+ markSessionForceUnarchived(session.id, stickyNow);
491
+ }
492
+ }
493
+
494
+ function getProjectName(directory: string): string {
495
+ return path.basename(directory);
496
+ }
497
+
498
+ function isGitRepo(directory: string): boolean {
499
+ try {
500
+ const result = execSync('git rev-parse --is-inside-work-tree', {
501
+ cwd: directory,
502
+ encoding: 'utf-8',
503
+ stdio: ['ignore', 'pipe', 'ignore'],
504
+ timeout: GIT_COMMAND_TIMEOUT_MS,
505
+ });
506
+ return result.trim() === 'true';
507
+ } catch {
508
+ return false;
509
+ }
510
+ }
511
+
512
+ function getGitBranch(directory: string): string | null {
513
+ if (!isGitRepo(directory)) return null;
514
+ try {
515
+ const branch = execSync('git branch --show-current', {
516
+ cwd: directory,
517
+ encoding: 'utf-8',
518
+ stdio: ['ignore', 'pipe', 'ignore'],
519
+ timeout: GIT_COMMAND_TIMEOUT_MS,
520
+ });
521
+ return branch.trim() || null;
522
+ } catch {
523
+ return null;
524
+ }
525
+ }
526
+
527
+ async function readStickyBusyDelayMs(): Promise<number> {
528
+ let stickyBusyDelayMs = 1000;
529
+ try {
530
+ const config = await readConfig();
531
+ const vibepulseRaw =
532
+ config.vibepulse && typeof config.vibepulse === 'object' && !Array.isArray(config.vibepulse)
533
+ ? config.vibepulse
534
+ : {};
535
+ const vibepulse = vibepulseRaw as Record<string, unknown>;
536
+ const stickyDelay = vibepulse['stickyBusyDelayMs'] as number | undefined;
537
+ if (typeof stickyDelay === 'number' && Number.isFinite(stickyDelay) && stickyDelay >= 0) {
538
+ stickyBusyDelayMs = stickyDelay;
539
+ }
540
+ } catch {
541
+ }
542
+
543
+ return stickyBusyDelayMs;
544
+ }
545
+
546
+ function sortChildEntries(children: ChildEntry[]): void {
547
+ children.sort((a, b) => {
548
+ const aActive = a.realTimeStatus === 'busy' || a.realTimeStatus === 'retry';
549
+ const bActive = b.realTimeStatus === 'busy' || b.realTimeStatus === 'retry';
550
+
551
+ if (aActive && !bActive) return -1;
552
+ if (!aActive && bActive) return 1;
553
+
554
+ const aTime = a.time?.updated || a.time?.created || 0;
555
+ const bTime = b.time?.updated || b.time?.created || 0;
556
+ return bTime - aTime;
557
+ });
558
+ }
559
+
560
+ function addLocalHostMetadataToChildEntry(child: ChildEntry): ChildEntry {
561
+ const rawSessionId = child.rawSessionId ?? child.id;
562
+ const sourceSessionKey = composeSourceKey(LOCAL_SOURCE.hostId, rawSessionId);
563
+
564
+ return {
565
+ ...child,
566
+ id: sourceSessionKey,
567
+ parentID: child.parentID ? composeSourceKey(LOCAL_SOURCE.hostId, child.parentID) : child.parentID,
568
+ hostId: LOCAL_SOURCE.hostId,
569
+ hostLabel: LOCAL_SOURCE.hostLabel,
570
+ hostKind: LOCAL_SOURCE.hostKind,
571
+ rawSessionId,
572
+ sourceSessionKey,
573
+ readOnly: false,
574
+ };
575
+ }
576
+
577
+ function addLocalHostMetadataToSession(session: EnrichedSession): EnrichedSession {
578
+ const rawSessionId = session.rawSessionId ?? session.id;
579
+ const sourceSessionKey = composeSourceKey(LOCAL_SOURCE.hostId, rawSessionId);
580
+
581
+ return {
582
+ ...session,
583
+ id: sourceSessionKey,
584
+ parentID: session.parentID ? composeSourceKey(LOCAL_SOURCE.hostId, session.parentID) : session.parentID,
585
+ hostId: LOCAL_SOURCE.hostId,
586
+ hostLabel: LOCAL_SOURCE.hostLabel,
587
+ hostKind: LOCAL_SOURCE.hostKind,
588
+ rawSessionId,
589
+ sourceSessionKey,
590
+ readOnly: false,
591
+ children: session.children.map((child) => addLocalHostMetadataToChildEntry(child)),
592
+ };
593
+ }
594
+
595
+ function toLocalHostStatus(meta: LocalResultMeta): LocalHostStatus {
596
+ return {
597
+ hostId: LOCAL_SOURCE.hostId,
598
+ hostLabel: LOCAL_SOURCE.hostLabel,
599
+ hostKind: LOCAL_SOURCE.hostKind,
600
+ online: meta.online,
601
+ ...(meta.degraded ? { degraded: true } : {}),
602
+ ...(meta.reason ? { reason: meta.reason } : {}),
603
+ };
604
+ }
605
+
606
+ function isTimeoutErrorMessage(value: string): boolean {
607
+ return value.toLowerCase().includes('timed out');
608
+ }
609
+
610
+ function getFailureMeta(reason: NodeFailureReason): { statusReason: string; reachable: false } {
611
+ if (reason === 'upstream_timeout') {
612
+ return {
613
+ statusReason: 'OpenCode discovery timed out',
614
+ reachable: false,
615
+ };
616
+ }
617
+
618
+ return {
619
+ statusReason: 'OpenCode server not found',
620
+ reachable: false,
621
+ };
622
+ }
623
+
624
+ function createLocalFailureResult(
625
+ reason: NodeFailureReason,
626
+ processHints: ProcessHint[],
627
+ extras: Record<string, unknown> = {}
628
+ ): Extract<LocalSessionsResult, { ok: false }> {
629
+ const failureMeta = getFailureMeta(reason);
630
+ const hostStatus = toLocalHostStatus({
631
+ online: false,
632
+ degraded: true,
633
+ reason: failureMeta.statusReason,
634
+ });
635
+
636
+ return {
637
+ ok: false,
638
+ reason,
639
+ extras: {
640
+ role: 'node',
641
+ source: LOCAL_SOURCE,
642
+ upstream: {
643
+ kind: 'opencode',
644
+ reachable: failureMeta.reachable,
645
+ },
646
+ processHints,
647
+ hosts: [hostStatus],
648
+ hostStatuses: [hostStatus],
649
+ ...extras,
650
+ },
651
+ };
652
+ }
653
+
654
+ function resolveFailureReasonFromMessages(messages: string[]): NodeFailureReason {
655
+ if (messages.length > 0 && messages.every(isTimeoutErrorMessage)) {
656
+ return 'upstream_timeout';
657
+ }
658
+
659
+ return 'upstream_unreachable';
660
+ }
661
+
662
+ async function getLocalSessionsResult(stickyBusyDelayMs: number): Promise<LocalSessionsResult> {
663
+ const { processes: rawProcessHints, timedOut: processDiscoveryTimedOut } =
664
+ discoverOpencodeProcessCwdsWithoutPortWithMeta();
665
+ const processHintsByDirectory = new Map<string, ProcessHint>();
666
+ for (const process of rawProcessHints) {
667
+ if (!process.cwd || process.cwd.startsWith('/private/tmp/opencode')) {
668
+ continue;
669
+ }
670
+ if (processHintsByDirectory.has(process.cwd)) {
671
+ continue;
672
+ }
673
+ processHintsByDirectory.set(process.cwd, {
674
+ pid: process.pid,
675
+ directory: process.cwd,
676
+ projectName: getProjectName(process.cwd),
677
+ reason: 'process_without_api_port',
678
+ });
679
+ }
680
+
681
+ const processHints = Array.from(processHintsByDirectory.values());
682
+ const { ports, timedOut: portDiscoveryTimedOut } = discoverOpencodePortsWithMeta();
683
+
684
+ if (!ports.length) {
685
+ if (portDiscoveryTimedOut || processDiscoveryTimedOut) {
686
+ return createLocalFailureResult('upstream_timeout', processHints);
687
+ }
688
+
689
+ return createLocalFailureResult('upstream_unreachable', processHints);
690
+ }
691
+
692
+ try {
693
+ const results = await Promise.allSettled(
694
+ ports.map(async (port) => {
695
+ const client = createOpencodeClient({ baseUrl: `http://localhost:${port}` });
696
+ const sessionsResult = await withTimeout(
697
+ (signal) => client.session.list({ signal }),
698
+ sessionListTimeoutMs,
699
+ `session.list(${port})`
700
+ );
701
+ const statusResult = await withTimeout(
702
+ (signal) => client.session.status({ signal }),
703
+ sessionStatusTimeoutMs,
704
+ `session.status(${port})`
705
+ ).catch(() => ({ data: {} }));
706
+ return { port, client, sessions: sessionsResult.data || [], status: statusResult.data || {} };
707
+ })
708
+ );
709
+
710
+ const allSessions: SessionLike[] = [];
711
+ const statusMap: Record<string, { type: StableRealtimeStatus }> = {};
712
+ const clientByPort: Record<number, ReturnType<typeof createOpencodeClient>> = {};
713
+ const sessionPortMap: Record<string, number> = {};
714
+ const failedPorts: Array<{ port: number; reason: string }> = [];
715
+
716
+ for (let i = 0; i < results.length; i++) {
717
+ const result = results[i];
718
+ const port = ports[i];
719
+
720
+ if (result.status !== 'fulfilled') {
721
+ failedPorts.push({
722
+ port,
723
+ reason: result.reason instanceof Error ? result.reason.message : String(result.reason),
724
+ });
725
+ continue;
726
+ }
727
+
728
+ allSessions.push(...(result.value.sessions as SessionLike[]));
729
+ Object.assign(statusMap, result.value.status);
730
+ clientByPort[result.value.port] = result.value.client;
731
+ for (const session of result.value.sessions as SessionLike[]) {
732
+ if (!(session.id in sessionPortMap)) {
733
+ sessionPortMap[session.id] = result.value.port;
734
+ }
735
+ }
736
+ }
737
+
738
+ const uniqueSessions: SessionLike[] = [];
739
+ const seen = new Set<string>();
740
+ for (const session of allSessions) {
741
+ if (seen.has(session.id)) {
742
+ continue;
743
+ }
744
+ seen.add(session.id);
745
+ uniqueSessions.push(session);
746
+ }
747
+
748
+ const parentSessions = uniqueSessions.filter((session) => !session.parentID);
749
+ const childSessions = uniqueSessions.filter((session) => !!session.parentID);
750
+
751
+ const lifecycleNow = Date.now();
752
+ pruneSessionForceUnarchived(lifecycleNow);
753
+ pruneSessionStickyStatusBlocked(lifecycleNow);
754
+
755
+ for (const session of parentSessions) {
756
+ if (session.time?.archived !== undefined && shouldForceSessionUnarchived(session.id, lifecycleNow)) {
757
+ session.time = {
758
+ ...session.time,
759
+ archived: undefined,
760
+ };
761
+ }
762
+ }
763
+
764
+ for (const child of childSessions) {
765
+ if (child.time?.archived !== undefined && shouldForceSessionUnarchived(child.id, lifecycleNow)) {
766
+ child.time = {
767
+ ...child.time,
768
+ archived: undefined,
769
+ };
770
+ }
771
+ }
772
+
773
+ if (results.length > 0 && failedPorts.length === results.length) {
774
+ pruneStickyState(Date.now(), new Set<string>());
775
+ return createLocalFailureResult(resolveFailureReasonFromMessages(failedPorts.map((entry) => entry.reason)), processHints, {
776
+ failedPorts,
777
+ });
778
+ }
779
+
780
+ const enrichedSessions: EnrichedSession[] = parentSessions.map((session) => ({
781
+ ...session,
782
+ projectName: getProjectName(session.directory),
783
+ branch: getGitBranch(session.directory),
784
+ realTimeStatus: statusMap[session.id]?.type || 'idle',
785
+ waitingForUser: false,
786
+ children: [],
787
+ }));
788
+
789
+ const parentById = new Map(enrichedSessions.map((session) => [session.id, session]));
790
+ const now = Date.now();
791
+ const unresolvedChildren: Array<{ parentId: string; child: SessionLike; childUpdatedAt: number }> = [];
792
+
793
+ for (const child of childSessions) {
794
+ let parent = child.parentID ? parentById.get(child.parentID) : undefined;
795
+
796
+ if (!parent) {
797
+ const candidates = enrichedSessions
798
+ .filter((session) => session.directory === child.directory)
799
+ .sort((a, b) => getUpdatedAt(b) - getUpdatedAt(a));
800
+
801
+ parent =
802
+ candidates.find((session) => session.realTimeStatus === 'busy' || session.realTimeStatus === 'retry') ||
803
+ candidates[0];
804
+ }
805
+
806
+ if (!parent) {
807
+ continue;
808
+ }
809
+
810
+ const statusFromMap = statusMap[child.id]?.type;
811
+ const childUpdatedAt = getUpdatedAt(child);
812
+ const isRecent = childUpdatedAt > 0 && now - childUpdatedAt <= CHILD_ACTIVE_WINDOW_MS;
813
+ const shouldSkipArchivedChild = !!child.time?.archived && !statusFromMap && !isRecent;
814
+
815
+ if (shouldSkipArchivedChild) {
816
+ continue;
817
+ }
818
+
819
+ if (statusFromMap && statusFromMap !== 'idle') {
820
+ parent.children.push(toChildEntry(child, statusFromMap));
821
+ } else if (isRecent) {
822
+ if (unresolvedChildren.length < CHILD_STATUS_MESSAGE_CHECK_LIMIT) {
823
+ unresolvedChildren.push({ parentId: parent.id, child, childUpdatedAt });
824
+ }
825
+ }
826
+ }
827
+
828
+ if (unresolvedChildren.length > 0) {
829
+ const unresolvedChecks = await Promise.allSettled(
830
+ unresolvedChildren.map(async ({ parentId, child, childUpdatedAt }) => {
831
+ const port = sessionPortMap[child.id] ?? sessionPortMap[parentId];
832
+ const client = port ? clientByPort[port] : undefined;
833
+ const assumeBusyForUnknown = childUpdatedAt > 0 && now - childUpdatedAt <= CHILD_UNKNOWN_STATE_BUSY_WINDOW_MS;
834
+ if (!client) {
835
+ return {
836
+ parentId,
837
+ child,
838
+ childStatus: assumeBusyForUnknown ? ('busy' as const) : ('idle' as const),
839
+ };
840
+ }
841
+
842
+ try {
843
+ const partStatuses = await fetchPartStatuses(client, child.id, sessionMessagesTimeoutMs);
844
+ const hasRunningState = partStatuses.some((status) => status === 'running');
845
+ const hasWaitingState = !hasRunningState && partStatuses.some(isWaitingPartStatus);
846
+ const hasActiveState = hasWaitingState || hasRunningState;
847
+ const recentlyActive = childUpdatedAt > 0 && now - childUpdatedAt <= 5 * 60 * 1000;
848
+
849
+ return {
850
+ parentId,
851
+ child,
852
+ childWaitingForUser: hasWaitingState,
853
+ childStatus: hasActiveState
854
+ ? ('busy' as const)
855
+ : recentlyActive || assumeBusyForUnknown
856
+ ? ('busy' as const)
857
+ : ('idle' as const),
858
+ };
859
+ } catch {
860
+ return {
861
+ parentId,
862
+ child,
863
+ childWaitingForUser: false,
864
+ childStatus: assumeBusyForUnknown ? ('busy' as const) : ('idle' as const),
865
+ };
866
+ }
867
+ })
868
+ );
869
+
870
+ for (const result of unresolvedChecks) {
871
+ if (result.status !== 'fulfilled') continue;
872
+ if (result.value.childStatus === 'idle') continue;
873
+ const parent = parentById.get(result.value.parentId);
874
+ if (!parent) continue;
875
+ parent.children.push(toChildEntry(result.value.child, result.value.childStatus, result.value.childWaitingForUser));
876
+ }
877
+ }
878
+
879
+ const parentStatusFallbackCandidates = enrichedSessions
880
+ .filter((session) => {
881
+ if (session.realTimeStatus !== 'idle') return false;
882
+ const updatedAt = getUpdatedAt(session);
883
+ if (updatedAt > 0 && now - updatedAt <= CHILD_ACTIVE_WINDOW_MS) return true;
884
+ return !!session.time?.archived;
885
+ })
886
+ .sort((a, b) => getUpdatedAt(b) - getUpdatedAt(a))
887
+ .slice(0, CHILD_STATUS_MESSAGE_CHECK_LIMIT);
888
+
889
+ if (parentStatusFallbackCandidates.length > 0) {
890
+ const parentFallbackChecks = await Promise.allSettled(
891
+ parentStatusFallbackCandidates.map(async (session) => {
892
+ const updatedAt = getUpdatedAt(session);
893
+ const assumeBusyForUnknown = updatedAt > 0 && now - updatedAt <= CHILD_UNKNOWN_STATE_BUSY_WINDOW_MS;
894
+ const port = sessionPortMap[session.id];
895
+ const client = port ? clientByPort[port] : undefined;
896
+
897
+ if (!client) {
898
+ return {
899
+ sessionId: session.id,
900
+ status: assumeBusyForUnknown ? ('busy' as const) : ('idle' as const),
901
+ waitingForUser: false,
902
+ };
903
+ }
904
+
905
+ try {
906
+ const partStatuses = await fetchPartStatuses(client, session.id, sessionMessagesTimeoutMs);
907
+ const hasRunningState = partStatuses.some((status) => status === 'running');
908
+ const hasWaitingState = !hasRunningState && partStatuses.some(isWaitingPartStatus);
909
+ const hasCompletedState = partStatuses.length > 0 && partStatuses.every((status) => status === 'completed');
910
+ const recentlyActive = hasRecentActivity(session, now);
911
+
912
+ return {
913
+ sessionId: session.id,
914
+ status: hasRunningState || hasWaitingState
915
+ ? ('busy' as const)
916
+ : hasCompletedState && !recentlyActive
917
+ ? ('idle' as const)
918
+ : assumeBusyForUnknown || recentlyActive
919
+ ? ('busy' as const)
920
+ : ('idle' as const),
921
+ waitingForUser: hasWaitingState,
922
+ };
923
+ } catch {
924
+ return {
925
+ sessionId: session.id,
926
+ status: assumeBusyForUnknown ? ('busy' as const) : ('idle' as const),
927
+ waitingForUser: false,
928
+ };
929
+ }
930
+ })
931
+ );
932
+
933
+ for (const result of parentFallbackChecks) {
934
+ if (result.status !== 'fulfilled') continue;
935
+ if (result.value.status === 'idle') continue;
936
+ const session = parentById.get(result.value.sessionId);
937
+ if (!session) continue;
938
+ session.realTimeStatus = result.value.status;
939
+ if (result.value.waitingForUser) {
940
+ session.waitingForUser = true;
941
+ }
942
+ }
943
+ }
944
+
945
+ for (const session of enrichedSessions) {
946
+ if (session.children.length > 0) {
947
+ sortChildEntries(session.children);
948
+ }
949
+ }
950
+
951
+ const sessionsForInteractionChecks = enrichedSessions.filter(
952
+ (session) =>
953
+ session.realTimeStatus === 'busy' ||
954
+ !!session.time?.archived ||
955
+ session.children.some((child) => child.realTimeStatus === 'busy' || child.realTimeStatus === 'retry')
956
+ );
957
+
958
+ if (sessionsForInteractionChecks.length > 0) {
959
+ const pendingChecks = await Promise.allSettled(
960
+ sessionsForInteractionChecks.map(async (session) => {
961
+ const port = sessionPortMap[session.id];
962
+ const client = port ? clientByPort[port] : undefined;
963
+ if (!client) {
964
+ return {
965
+ sessionId: session.id,
966
+ parentWaiting: false,
967
+ running: false,
968
+ waitingChildIds: new Set<string>(),
969
+ };
970
+ }
971
+
972
+ try {
973
+ const partStatuses = await fetchPartStatuses(client, session.id, sessionMessagesTimeoutMs);
974
+ const hasRunning = partStatuses.some((status) => status === 'running');
975
+ const hasInteractionWait = !hasRunning && partStatuses.some(isWaitingPartStatus);
976
+
977
+ const childStateChecks = await Promise.allSettled(
978
+ session.children
979
+ .filter((child) => child.realTimeStatus === 'busy' || child.realTimeStatus === 'retry')
980
+ .map(async (child) => {
981
+ const childPort = sessionPortMap[child.id] ?? sessionPortMap[session.id];
982
+ const childClient = childPort ? clientByPort[childPort] : undefined;
983
+ if (!childClient) {
984
+ return { childId: child.id, waiting: false };
985
+ }
986
+
987
+ try {
988
+ const childStatuses = await fetchPartStatuses(childClient, child.id, sessionMessagesTimeoutMs);
989
+ const childHasRunning = childStatuses.some((status) => status === 'running');
990
+ return {
991
+ childId: child.id,
992
+ waiting: !childHasRunning && childStatuses.some(isWaitingPartStatus),
993
+ };
994
+ } catch {
995
+ return { childId: child.id, waiting: false };
996
+ }
997
+ })
998
+ );
999
+
1000
+ const waitingChildIds = new Set(
1001
+ childStateChecks
1002
+ .filter((entry): entry is PromiseFulfilledResult<{ childId: string; waiting: boolean }> => entry.status === 'fulfilled')
1003
+ .filter((entry) => entry.value.waiting)
1004
+ .map((entry) => entry.value.childId)
1005
+ );
1006
+
1007
+ return {
1008
+ sessionId: session.id,
1009
+ parentWaiting: hasInteractionWait,
1010
+ running: hasRunning,
1011
+ waitingChildIds,
1012
+ };
1013
+ } catch {
1014
+ return {
1015
+ sessionId: session.id,
1016
+ parentWaiting: false,
1017
+ running: false,
1018
+ waitingChildIds: new Set<string>(),
1019
+ };
1020
+ }
1021
+ })
1022
+ );
1023
+
1024
+ for (const result of pendingChecks) {
1025
+ if (result.status !== 'fulfilled') continue;
1026
+ const session = enrichedSessions.find((candidate) => candidate.id === result.value.sessionId);
1027
+ if (!session) continue;
1028
+
1029
+ for (const child of session.children) {
1030
+ if (result.value.waitingChildIds.has(child.id)) {
1031
+ child.waitingForUser = true;
1032
+ }
1033
+ }
1034
+
1035
+ if (result.value.running) {
1036
+ session.realTimeStatus = 'busy';
1037
+ }
1038
+ if (result.value.parentWaiting) {
1039
+ session.waitingForUser = true;
1040
+ }
1041
+ }
1042
+ }
1043
+
1044
+ const stickyNow = Date.now();
1045
+ const activeStickyIds = new Set<string>();
1046
+ for (const session of enrichedSessions) {
1047
+ activeStickyIds.add(session.id);
1048
+ for (const child of session.children) {
1049
+ activeStickyIds.add(`child:${child.id}`);
1050
+ }
1051
+ }
1052
+
1053
+ for (const session of enrichedSessions) {
1054
+ if (shouldSkipSessionStatusStabilization(session, stickyNow)) {
1055
+ continue;
1056
+ }
1057
+
1058
+ applyStickyStatusStabilization(session, stickyNow, stickyBusyDelayMs);
1059
+ }
1060
+
1061
+ pruneStickyState(stickyNow, activeStickyIds);
1062
+
1063
+ const knownDirectories = new Set<string>();
1064
+ for (const session of uniqueSessions) {
1065
+ if (session.directory) {
1066
+ knownDirectories.add(session.directory);
1067
+ }
1068
+ }
1069
+
1070
+ const filteredProcessHints = processHints.filter((hint) => !knownDirectories.has(hint.directory));
1071
+
1072
+ return {
1073
+ ok: true,
1074
+ payload: {
1075
+ sessions: enrichedSessions,
1076
+ processHints: filteredProcessHints,
1077
+ ...(failedPorts.length > 0 ? { failedPorts, degraded: true } : {}),
1078
+ },
1079
+ meta: {
1080
+ online: true,
1081
+ ...(failedPorts.length > 0 ? { degraded: true } : {}),
1082
+ },
1083
+ };
1084
+ } catch (error) {
1085
+ console.error('Error fetching node-local sessions:', error);
1086
+ return createLocalFailureResult(
1087
+ error instanceof Error && isTimeoutErrorMessage(error.message) ? 'upstream_timeout' : 'upstream_unreachable',
1088
+ processHints,
1089
+ {
1090
+ details: error instanceof Error ? error.message : String(error),
1091
+ }
1092
+ );
1093
+ }
1094
+ }