@aoagents/ao-web 0.3.0 → 0.4.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 (281) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +179 -187
  3. package/.next/app-path-routes-manifest.json +9 -10
  4. package/.next/build-manifest.json +6 -6
  5. package/.next/next-server.js.nft.json +1 -1
  6. package/.next/prerender-manifest.json +34 -34
  7. package/.next/react-loadable-manifest.json +14 -14
  8. package/.next/required-server-files.json +3 -3
  9. package/.next/server/app/_not-found/page.js +2 -2
  10. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  11. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. package/.next/server/app/_not-found.html +1 -1
  13. package/.next/server/app/_not-found.rsc +8 -11
  14. package/.next/server/app/api/backlog/route.js +1 -1
  15. package/.next/server/app/api/backlog/route.js.nft.json +1 -1
  16. package/.next/server/app/api/backlog/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/browse-directory/route.js +1 -1
  18. package/.next/server/app/api/browse-directory/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/filesystem/browse/route.js +1 -1
  20. package/.next/server/app/api/filesystem/browse/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/issues/route.js +1 -1
  22. package/.next/server/app/api/issues/route.js.nft.json +1 -1
  23. package/.next/server/app/api/issues/route_client-reference-manifest.js +1 -1
  24. package/.next/server/app/api/observability/route.js +1 -1
  25. package/.next/server/app/api/observability/route.js.nft.json +1 -1
  26. package/.next/server/app/api/observability/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/orchestrators/route.js +1 -1
  28. package/.next/server/app/api/orchestrators/route.js.nft.json +1 -1
  29. package/.next/server/app/api/orchestrators/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/projects/[id]/route.js +1 -1
  31. package/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
  32. package/.next/server/app/api/projects/[id]/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/projects/reload/route.js +1 -1
  34. package/.next/server/app/api/projects/reload/route.js.nft.json +1 -1
  35. package/.next/server/app/api/projects/reload/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/projects/route.js +1 -1
  37. package/.next/server/app/api/projects/route.js.nft.json +1 -1
  38. package/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/prs/[id]/merge/route.js +1 -1
  40. package/.next/server/app/api/prs/[id]/merge/route.js.nft.json +1 -1
  41. package/.next/server/app/api/prs/[id]/merge/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/runtime/terminal/route.js +1 -1
  43. package/.next/server/app/api/runtime/terminal/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/sessions/[id]/kill/route.js +1 -1
  45. package/.next/server/app/api/sessions/[id]/kill/route.js.nft.json +1 -1
  46. package/.next/server/app/api/sessions/[id]/kill/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/sessions/[id]/message/route.js +1 -1
  48. package/.next/server/app/api/sessions/[id]/message/route.js.nft.json +1 -1
  49. package/.next/server/app/api/sessions/[id]/message/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/sessions/[id]/remap/route.js +1 -1
  51. package/.next/server/app/api/sessions/[id]/remap/route.js.nft.json +1 -1
  52. package/.next/server/app/api/sessions/[id]/remap/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/sessions/[id]/restore/route.js +1 -1
  54. package/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -1
  55. package/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  57. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  58. package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/sessions/[id]/send/route.js +1 -1
  60. package/.next/server/app/api/sessions/[id]/send/route.js.nft.json +1 -1
  61. package/.next/server/app/api/sessions/[id]/send/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/sessions/patches/route.js +1 -1
  63. package/.next/server/app/api/sessions/patches/route.js.nft.json +1 -1
  64. package/.next/server/app/api/sessions/patches/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/sessions/route.js +1 -1
  66. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  67. package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/setup-labels/route.js +1 -1
  69. package/.next/server/app/api/setup-labels/route.js.nft.json +1 -1
  70. package/.next/server/app/api/setup-labels/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/spawn/route.js +1 -1
  72. package/.next/server/app/api/spawn/route.js.nft.json +1 -1
  73. package/.next/server/app/api/spawn/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/verify/route.js +1 -1
  75. package/.next/server/app/api/verify/route.js.nft.json +1 -1
  76. package/.next/server/app/api/verify/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/webhooks/[...slug]/route.js +1 -1
  78. package/.next/server/app/api/webhooks/[...slug]/route.js.nft.json +1 -1
  79. package/.next/server/app/api/webhooks/[...slug]/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/apple-icon/route.js +1 -1
  81. package/.next/server/app/apple-icon/route.js.nft.json +1 -1
  82. package/.next/server/app/apple-icon/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/apple-icon.body +0 -0
  84. package/.next/server/app/dev/terminal-test/page.js +3 -3
  85. package/.next/server/app/dev/terminal-test/page.js.nft.json +1 -1
  86. package/.next/server/app/dev/terminal-test/page_client-reference-manifest.js +1 -1
  87. package/.next/server/app/dev/terminal-test.html +1 -1
  88. package/.next/server/app/dev/terminal-test.rsc +9 -12
  89. package/.next/server/app/icon/route.js +1 -1
  90. package/.next/server/app/icon/route.js.nft.json +1 -1
  91. package/.next/server/app/icon/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/icon-192/route.js +1 -1
  93. package/.next/server/app/icon-192/route.js.nft.json +1 -1
  94. package/.next/server/app/icon-192/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/icon-512/route.js +1 -1
  96. package/.next/server/app/icon-512/route.js.nft.json +1 -1
  97. package/.next/server/app/icon-512/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/icon.body +0 -0
  99. package/.next/server/app/manifest.webmanifest/route.js +2 -2
  100. package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
  101. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/manifest.webmanifest.body +1 -1
  103. package/.next/server/app/orchestrators/page.js +2 -2
  104. package/.next/server/app/orchestrators/page.js.nft.json +1 -1
  105. package/.next/server/app/orchestrators/page_client-reference-manifest.js +1 -1
  106. package/.next/server/app/page.js +2 -2
  107. package/.next/server/app/page.js.nft.json +1 -1
  108. package/.next/server/app/page_client-reference-manifest.js +1 -1
  109. package/.next/server/app/projects/[projectId]/page.js +2 -2
  110. package/.next/server/app/projects/[projectId]/page.js.nft.json +1 -1
  111. package/.next/server/app/projects/[projectId]/page_client-reference-manifest.js +1 -1
  112. package/.next/server/app/projects/[projectId]/sessions/[id]/page.js +2 -2
  113. package/.next/server/app/projects/[projectId]/sessions/[id]/page.js.nft.json +1 -1
  114. package/.next/server/app/projects/[projectId]/sessions/[id]/page_client-reference-manifest.js +1 -1
  115. package/.next/server/app/projects/[projectId]/settings/page.js +2 -2
  116. package/.next/server/app/projects/[projectId]/settings/page.js.nft.json +1 -1
  117. package/.next/server/app/projects/[projectId]/settings/page_client-reference-manifest.js +1 -1
  118. package/.next/server/app/prs/page.js +2 -2
  119. package/.next/server/app/prs/page.js.nft.json +1 -1
  120. package/.next/server/app/prs/page_client-reference-manifest.js +1 -1
  121. package/.next/server/app/sessions/[id]/page.js +2 -2
  122. package/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  123. package/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  124. package/.next/server/app/test-direct/page.js +2 -2
  125. package/.next/server/app/test-direct/page.js.nft.json +1 -1
  126. package/.next/server/app/test-direct/page_client-reference-manifest.js +1 -1
  127. package/.next/server/app/test-direct.html +1 -1
  128. package/.next/server/app/test-direct.rsc +9 -12
  129. package/.next/server/app-paths-manifest.json +9 -10
  130. package/.next/server/chunks/1172.js +1 -1
  131. package/.next/server/chunks/1271.js +1 -1
  132. package/.next/server/chunks/1876.js +9 -0
  133. package/.next/server/chunks/3714.js +1 -1
  134. package/.next/server/chunks/4520.js +1 -1
  135. package/.next/server/chunks/7167.js +1 -0
  136. package/.next/server/chunks/7173.js +9 -0
  137. package/.next/server/chunks/8539.js +3 -0
  138. package/.next/server/chunks/9223.js +440 -0
  139. package/.next/server/chunks/9381.js +658 -0
  140. package/.next/server/middleware-build-manifest.js +1 -1
  141. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  142. package/.next/server/next-font-manifest.js +1 -1
  143. package/.next/server/next-font-manifest.json +1 -1
  144. package/.next/server/pages/404.html +1 -1
  145. package/.next/server/pages/500.html +1 -1
  146. package/.next/server/server-reference-manifest.json +1 -1
  147. package/.next/static/5nmlY5IOwz-Od3p2SB2hh/_buildManifest.js +1 -0
  148. package/.next/static/chunks/1383.faf35b62c555f5a1.js +1 -0
  149. package/.next/static/chunks/3764.ba81c7b8d5b21136.js +1 -0
  150. package/.next/static/chunks/3780-e20898ff49ddc867.js +1 -0
  151. package/.next/static/chunks/4465-0c7772f1499dffa4.js +1 -0
  152. package/.next/static/chunks/5795-b96fd46c8c7344fc.js +1 -0
  153. package/.next/static/chunks/7008-6e85da85f12f9858.js +1 -0
  154. package/.next/static/chunks/app/_not-found/page-3b8a01e726e988c8.js +1 -0
  155. package/.next/static/chunks/app/api/backlog/route-3b8a01e726e988c8.js +1 -0
  156. package/.next/static/chunks/app/api/browse-directory/route-3b8a01e726e988c8.js +1 -0
  157. package/.next/static/chunks/app/api/filesystem/browse/route-3b8a01e726e988c8.js +1 -0
  158. package/.next/static/chunks/app/api/issues/route-3b8a01e726e988c8.js +1 -0
  159. package/.next/static/chunks/app/api/observability/route-3b8a01e726e988c8.js +1 -0
  160. package/.next/static/chunks/app/api/orchestrators/route-3b8a01e726e988c8.js +1 -0
  161. package/.next/static/chunks/app/api/projects/[id]/route-3b8a01e726e988c8.js +1 -0
  162. package/.next/static/chunks/app/api/projects/reload/route-3b8a01e726e988c8.js +1 -0
  163. package/.next/static/chunks/app/api/projects/route-3b8a01e726e988c8.js +1 -0
  164. package/.next/static/chunks/app/api/prs/[id]/merge/route-3b8a01e726e988c8.js +1 -0
  165. package/.next/static/chunks/app/api/runtime/terminal/route-3b8a01e726e988c8.js +1 -0
  166. package/.next/static/chunks/app/api/sessions/[id]/kill/route-3b8a01e726e988c8.js +1 -0
  167. package/.next/static/chunks/app/api/sessions/[id]/message/route-3b8a01e726e988c8.js +1 -0
  168. package/.next/static/chunks/app/api/sessions/[id]/remap/route-3b8a01e726e988c8.js +1 -0
  169. package/.next/static/chunks/app/api/sessions/[id]/restore/route-3b8a01e726e988c8.js +1 -0
  170. package/.next/static/chunks/app/api/sessions/[id]/route-3b8a01e726e988c8.js +1 -0
  171. package/.next/static/chunks/app/api/sessions/[id]/send/route-3b8a01e726e988c8.js +1 -0
  172. package/.next/static/chunks/app/api/sessions/patches/route-3b8a01e726e988c8.js +1 -0
  173. package/.next/static/chunks/app/api/sessions/route-3b8a01e726e988c8.js +1 -0
  174. package/.next/static/chunks/app/api/setup-labels/route-3b8a01e726e988c8.js +1 -0
  175. package/.next/static/chunks/app/api/spawn/route-3b8a01e726e988c8.js +1 -0
  176. package/.next/static/chunks/app/api/verify/route-3b8a01e726e988c8.js +1 -0
  177. package/.next/static/chunks/app/api/webhooks/[...slug]/route-3b8a01e726e988c8.js +1 -0
  178. package/.next/static/chunks/app/apple-icon/route-3b8a01e726e988c8.js +1 -0
  179. package/.next/static/chunks/app/dev/terminal-test/page-095511c7a110fbc9.js +1 -0
  180. package/.next/static/chunks/app/error-3ebcf3275d91d60a.js +1 -0
  181. package/.next/static/chunks/app/global-error-8f082029a4cf6af1.js +1 -0
  182. package/.next/static/chunks/app/icon/route-3b8a01e726e988c8.js +1 -0
  183. package/.next/static/chunks/app/icon-192/route-3b8a01e726e988c8.js +1 -0
  184. package/.next/static/chunks/app/icon-512/route-3b8a01e726e988c8.js +1 -0
  185. package/.next/static/chunks/app/layout-be776dec5531d47b.js +1 -0
  186. package/.next/static/chunks/app/loading-3b8a01e726e988c8.js +1 -0
  187. package/.next/static/chunks/app/manifest.webmanifest/route-3b8a01e726e988c8.js +1 -0
  188. package/.next/static/chunks/app/{not-found-824d5d3c6e296eeb.js → not-found-0eb78483b8277b8d.js} +1 -1
  189. package/.next/static/chunks/app/orchestrators/page-406e530b0d4ad46f.js +1 -0
  190. package/.next/static/chunks/app/page-a92f3646b54545b0.js +1 -0
  191. package/.next/static/chunks/app/projects/[projectId]/loading-3b8a01e726e988c8.js +1 -0
  192. package/.next/static/chunks/app/projects/[projectId]/page-d14fdcff4998d3bf.js +1 -0
  193. package/.next/static/chunks/app/projects/[projectId]/sessions/[id]/page-96fc04db76c65761.js +1 -0
  194. package/.next/static/chunks/app/projects/[projectId]/settings/page-ce2e904def1bef9c.js +1 -0
  195. package/.next/static/chunks/app/prs/page-95dbbebb4d372535.js +1 -0
  196. package/.next/static/chunks/app/sessions/[id]/error-5468aace23fd8872.js +1 -0
  197. package/.next/static/chunks/app/sessions/[id]/loading-3b8a01e726e988c8.js +1 -0
  198. package/.next/static/chunks/app/sessions/[id]/{not-found-824d5d3c6e296eeb.js → not-found-0eb78483b8277b8d.js} +1 -1
  199. package/.next/static/chunks/app/sessions/[id]/page-8ef744531757185e.js +1 -0
  200. package/.next/static/chunks/app/test-direct/page-ab98dc9b04bacaa5.js +1 -0
  201. package/.next/static/chunks/{main-app-690acf9d5d2050c9.js → main-app-4b27cdf1baf203ad.js} +1 -1
  202. package/.next/static/chunks/{webpack-d4ff5ed5e153ca3e.js → webpack-ab12779265bdb8f9.js} +1 -1
  203. package/.next/static/css/397ea3e60645d3f3.css +1 -0
  204. package/dist-server/mux-websocket.js +135 -123
  205. package/dist-server/tmux-utils.js +99 -9
  206. package/next.config.js +6 -0
  207. package/package.json +18 -16
  208. package/.next/server/app/api/events/route.js +0 -9
  209. package/.next/server/app/api/events/route.js.nft.json +0 -1
  210. package/.next/server/app/api/events/route_client-reference-manifest.js +0 -1
  211. package/.next/server/chunks/252.js +0 -11
  212. package/.next/server/chunks/2810.js +0 -1
  213. package/.next/server/chunks/3602.js +0 -382
  214. package/.next/server/chunks/3667.js +0 -277
  215. package/.next/server/chunks/6172.js +0 -9
  216. package/.next/server/chunks/8367.js +0 -3
  217. package/.next/static/chunks/3697.4d6d86c5f0caf73e.js +0 -1
  218. package/.next/static/chunks/4465-aaba60a6355de914.js +0 -1
  219. package/.next/static/chunks/6078.47ce88bee96cfaee.js +0 -1
  220. package/.next/static/chunks/6607-405ce4d15e595f4a.js +0 -1
  221. package/.next/static/chunks/7008-71ebb186f0549f41.js +0 -1
  222. package/.next/static/chunks/9331-fcdd652218ac6f68.js +0 -1
  223. package/.next/static/chunks/app/_not-found/page-e2dea9178b4af8db.js +0 -1
  224. package/.next/static/chunks/app/api/backlog/route-e2dea9178b4af8db.js +0 -1
  225. package/.next/static/chunks/app/api/browse-directory/route-e2dea9178b4af8db.js +0 -1
  226. package/.next/static/chunks/app/api/events/route-e2dea9178b4af8db.js +0 -1
  227. package/.next/static/chunks/app/api/filesystem/browse/route-e2dea9178b4af8db.js +0 -1
  228. package/.next/static/chunks/app/api/issues/route-e2dea9178b4af8db.js +0 -1
  229. package/.next/static/chunks/app/api/observability/route-e2dea9178b4af8db.js +0 -1
  230. package/.next/static/chunks/app/api/orchestrators/route-e2dea9178b4af8db.js +0 -1
  231. package/.next/static/chunks/app/api/projects/[id]/route-e2dea9178b4af8db.js +0 -1
  232. package/.next/static/chunks/app/api/projects/reload/route-e2dea9178b4af8db.js +0 -1
  233. package/.next/static/chunks/app/api/projects/route-e2dea9178b4af8db.js +0 -1
  234. package/.next/static/chunks/app/api/prs/[id]/merge/route-e2dea9178b4af8db.js +0 -1
  235. package/.next/static/chunks/app/api/runtime/terminal/route-e2dea9178b4af8db.js +0 -1
  236. package/.next/static/chunks/app/api/sessions/[id]/kill/route-e2dea9178b4af8db.js +0 -1
  237. package/.next/static/chunks/app/api/sessions/[id]/message/route-e2dea9178b4af8db.js +0 -1
  238. package/.next/static/chunks/app/api/sessions/[id]/remap/route-e2dea9178b4af8db.js +0 -1
  239. package/.next/static/chunks/app/api/sessions/[id]/restore/route-e2dea9178b4af8db.js +0 -1
  240. package/.next/static/chunks/app/api/sessions/[id]/route-e2dea9178b4af8db.js +0 -1
  241. package/.next/static/chunks/app/api/sessions/[id]/send/route-e2dea9178b4af8db.js +0 -1
  242. package/.next/static/chunks/app/api/sessions/patches/route-e2dea9178b4af8db.js +0 -1
  243. package/.next/static/chunks/app/api/sessions/route-e2dea9178b4af8db.js +0 -1
  244. package/.next/static/chunks/app/api/setup-labels/route-e2dea9178b4af8db.js +0 -1
  245. package/.next/static/chunks/app/api/spawn/route-e2dea9178b4af8db.js +0 -1
  246. package/.next/static/chunks/app/api/verify/route-e2dea9178b4af8db.js +0 -1
  247. package/.next/static/chunks/app/api/webhooks/[...slug]/route-e2dea9178b4af8db.js +0 -1
  248. package/.next/static/chunks/app/apple-icon/route-e2dea9178b4af8db.js +0 -1
  249. package/.next/static/chunks/app/dev/terminal-test/page-5765f0465db56fed.js +0 -1
  250. package/.next/static/chunks/app/error-670f1d8bf2b6859c.js +0 -1
  251. package/.next/static/chunks/app/global-error-ca06d2b1be2d4ae0.js +0 -1
  252. package/.next/static/chunks/app/icon/route-e2dea9178b4af8db.js +0 -1
  253. package/.next/static/chunks/app/icon-192/route-e2dea9178b4af8db.js +0 -1
  254. package/.next/static/chunks/app/icon-512/route-e2dea9178b4af8db.js +0 -1
  255. package/.next/static/chunks/app/layout-ddf79478d9a673bb.js +0 -1
  256. package/.next/static/chunks/app/loading-e2dea9178b4af8db.js +0 -1
  257. package/.next/static/chunks/app/manifest.webmanifest/route-e2dea9178b4af8db.js +0 -1
  258. package/.next/static/chunks/app/orchestrators/page-4c190484788aaaa3.js +0 -1
  259. package/.next/static/chunks/app/page-d3b83ad5f09b6ec7.js +0 -1
  260. package/.next/static/chunks/app/projects/[projectId]/loading-e2dea9178b4af8db.js +0 -1
  261. package/.next/static/chunks/app/projects/[projectId]/page-9f3dfbea006747cf.js +0 -1
  262. package/.next/static/chunks/app/projects/[projectId]/sessions/[id]/page-a7090a06948f9a6b.js +0 -1
  263. package/.next/static/chunks/app/projects/[projectId]/settings/page-219df6dcbc6d1107.js +0 -1
  264. package/.next/static/chunks/app/prs/page-a285e930235a23af.js +0 -1
  265. package/.next/static/chunks/app/sessions/[id]/error-eb0973907da68a37.js +0 -1
  266. package/.next/static/chunks/app/sessions/[id]/loading-e2dea9178b4af8db.js +0 -1
  267. package/.next/static/chunks/app/sessions/[id]/page-98f398b822092218.js +0 -1
  268. package/.next/static/chunks/app/test-direct/page-f9bff7d7b4dfe728.js +0 -1
  269. package/.next/static/css/46a7e8e962075e54.css +0 -1
  270. package/.next/static/css/577ea7f5ad4f0576.css +0 -1
  271. package/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  272. package/.next/static/media/558ca1a6aa3cb55e-s.p.woff2 +0 -0
  273. package/.next/static/media/64d784ea54a4acde-s.woff2 +0 -0
  274. package/.next/static/media/6d831b18ae5b01dc-s.woff2 +0 -0
  275. package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  276. package/.next/static/media/ac0e76ddaeeb7981-s.woff2 +0 -0
  277. package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  278. package/.next/static/media/edc640959b0c7826-s.woff2 +0 -0
  279. package/.next/static/media/ff71da380fbe67dd-s.woff2 +0 -0
  280. package/.next/static/q6yh3n8jgvyI9JIbexzuH/_buildManifest.js +0 -1
  281. /package/.next/static/{q6yh3n8jgvyI9JIbexzuH → 5nmlY5IOwz-Od3p2SB2hh}/_ssgManifest.js +0 -0
@@ -2,46 +2,77 @@
2
2
  * Multiplexed WebSocket server for terminal multiplexing.
3
3
  * Manages multiple terminal connections over a single persistent WebSocket.
4
4
  *
5
- * Session updates are delivered via a single shared SSE connection from this
6
- * process to Next.js /api/events, then broadcast to all subscribed clients.
7
- * This replaces per-client HTTP polling and makes session updates event-driven.
5
+ * Session updates are delivered via polling of Next.js /api/sessions/patches
6
+ * every 3s, then broadcast to all subscribed clients via WebSocket.
8
7
  */
9
8
  import { WebSocketServer, WebSocket } from "ws";
10
9
  import { homedir, userInfo } from "node:os";
11
10
  import { spawn } from "node:child_process";
12
11
  import { findTmux, resolveTmuxSession, validateSessionId } from "./tmux-utils.js";
13
12
  /**
14
- * Manages a single shared SSE connection to Next.js /api/events.
15
- * Broadcasts session patches to all subscribed callbacks.
16
- * Lazily connects on first subscriber, disconnects when the last one leaves.
13
+ * Manages polling of session patches from Next.js /api/sessions/patches.
14
+ * Broadcasts to all subscribed callbacks.
15
+ * Lazily starts polling on first subscriber, stops when the last one leaves.
17
16
  */
18
17
  export class SessionBroadcaster {
19
18
  subscribers = new Set();
20
- abortController = null;
21
- reconnectTimer = null;
19
+ errorSubscribers = new Set();
20
+ intervalId = null;
21
+ polling = false;
22
22
  baseUrl;
23
23
  constructor(nextPort) {
24
24
  this.baseUrl = `http://localhost:${nextPort}`;
25
25
  }
26
26
  /**
27
- * Subscribe to session patches. Returns an unsubscribe function.
28
- * Sends an immediate snapshot to the new subscriber, then live SSE pushes.
27
+ * Subscribe to session patches and errors. Returns an unsubscribe function.
28
+ * Sends an immediate snapshot to the new subscriber, then polling updates.
29
29
  */
30
- subscribe(callback) {
30
+ subscribe(callback, onError) {
31
31
  const wasEmpty = this.subscribers.size === 0;
32
32
  this.subscribers.add(callback);
33
+ if (onError)
34
+ this.errorSubscribers.add(onError);
33
35
  // Immediately send a one-off snapshot to just this new subscriber
34
- void this.fetchSnapshot().then((sessions) => {
35
- if (sessions && this.subscribers.has(callback)) {
36
- callback(sessions);
36
+ void this.fetchSnapshot().then((result) => {
37
+ if (result.sessions && this.subscribers.has(callback)) {
38
+ try {
39
+ callback(result.sessions);
40
+ }
41
+ catch {
42
+ // Isolate subscriber errors so one bad subscriber doesn't break others
43
+ }
44
+ }
45
+ else if (result.error && onError && this.errorSubscribers.has(onError)) {
46
+ try {
47
+ onError(result.error);
48
+ }
49
+ catch {
50
+ // Isolate subscriber errors
51
+ }
37
52
  }
38
53
  });
39
- // Start the shared SSE connection if this is the first subscriber
54
+ // Start polling if this is the first subscriber
40
55
  if (wasEmpty) {
41
- void this.connect();
56
+ this.intervalId = setInterval(() => {
57
+ if (this.polling)
58
+ return;
59
+ this.polling = true;
60
+ void this.fetchSnapshot()
61
+ .then((result) => {
62
+ if (result.sessions && this.intervalId !== null)
63
+ this.broadcast(result.sessions);
64
+ else if (result.error && this.intervalId !== null)
65
+ this.broadcastError(result.error);
66
+ })
67
+ .finally(() => {
68
+ this.polling = false;
69
+ });
70
+ }, 3000);
42
71
  }
43
72
  return () => {
44
73
  this.subscribers.delete(callback);
74
+ if (onError)
75
+ this.errorSubscribers.delete(onError);
45
76
  if (this.subscribers.size === 0) {
46
77
  this.disconnect();
47
78
  }
@@ -57,99 +88,45 @@ export class SessionBroadcaster {
57
88
  }
58
89
  }
59
90
  }
60
- /** One-shot HTTP fetch of the current session list for immediate delivery. */
61
- async fetchSnapshot() {
62
- try {
63
- const controller = new AbortController();
64
- const timeoutId = setTimeout(() => controller.abort(), 4000);
91
+ broadcastError(error) {
92
+ for (const callback of this.errorSubscribers) {
65
93
  try {
66
- const res = await fetch(`${this.baseUrl}/api/sessions/patches`, {
67
- signal: controller.signal,
68
- });
69
- clearTimeout(timeoutId);
70
- if (!res.ok)
71
- return null;
72
- const data = (await res.json());
73
- return data.sessions ?? null;
94
+ callback(error);
74
95
  }
75
- catch {
76
- clearTimeout(timeoutId);
77
- return null;
96
+ catch (err) {
97
+ console.error("[MuxServer] Session error subscriber threw:", err);
78
98
  }
79
99
  }
80
- catch {
81
- return null;
82
- }
83
100
  }
84
- /** Open a persistent SSE connection and stream events to all subscribers. */
85
- async connect() {
86
- if (this.abortController)
87
- return;
101
+ /** One-shot HTTP fetch of the current session list. */
102
+ async fetchSnapshot() {
88
103
  const controller = new AbortController();
89
- this.abortController = controller;
90
- const { signal } = controller;
104
+ const timeoutId = setTimeout(() => controller.abort(), 4000);
91
105
  try {
92
- const res = await fetch(`${this.baseUrl}/api/events`, {
93
- signal,
94
- headers: { Accept: "text/event-stream" },
106
+ const res = await fetch(`${this.baseUrl}/api/sessions/patches`, {
107
+ signal: controller.signal,
95
108
  });
96
- if (!res.ok || !res.body) {
97
- throw new Error(`SSE connect failed: ${res.status}`);
98
- }
99
- const reader = res.body.getReader();
100
- const decoder = new TextDecoder();
101
- let buffer = "";
102
- while (!signal.aborted) {
103
- const { done, value } = await reader.read();
104
- if (done)
105
- break;
106
- buffer += decoder.decode(value, { stream: true });
107
- const lines = buffer.split("\n");
108
- buffer = lines.pop() ?? "";
109
- for (const line of lines) {
110
- if (!line.startsWith("data: "))
111
- continue;
112
- try {
113
- const event = JSON.parse(line.slice(6));
114
- if (event.type === "snapshot" && event.sessions) {
115
- this.broadcast(event.sessions);
116
- }
117
- }
118
- catch {
119
- // ignore malformed events
120
- }
121
- }
109
+ clearTimeout(timeoutId);
110
+ if (!res.ok) {
111
+ const msg = `Session fetch failed: HTTP ${res.status}`;
112
+ console.warn(`[SessionBroadcaster] ${msg}`);
113
+ return { sessions: null, error: msg };
122
114
  }
115
+ const data = (await res.json());
116
+ return { sessions: data.sessions ?? null, error: null };
123
117
  }
124
118
  catch (err) {
125
- if (signal.aborted)
126
- return; // intentional disconnect, not an error
127
- console.warn("[MuxServer] SSE connection lost:", err instanceof Error ? err.message : err);
128
- }
129
- finally {
130
- // Only clear our own controller — a concurrent connect() may have already
131
- // set a new one (e.g. disconnect() → subscribe() → connect() in the same tick).
132
- if (this.abortController === controller) {
133
- this.abortController = null;
134
- }
135
- }
136
- // Reconnect with backoff if there are still subscribers
137
- if (this.subscribers.size > 0) {
138
- console.log("[MuxServer] SSE reconnecting in 5s");
139
- this.reconnectTimer = setTimeout(() => {
140
- this.reconnectTimer = null;
141
- if (this.subscribers.size > 0)
142
- void this.connect();
143
- }, 5000);
119
+ clearTimeout(timeoutId);
120
+ const msg = err instanceof Error ? err.message : String(err);
121
+ console.warn("[SessionBroadcaster] fetchSnapshot error:", msg);
122
+ return { sessions: null, error: msg };
144
123
  }
145
124
  }
146
125
  disconnect() {
147
- if (this.reconnectTimer) {
148
- clearTimeout(this.reconnectTimer);
149
- this.reconnectTimer = null;
126
+ if (this.intervalId !== null) {
127
+ clearInterval(this.intervalId);
128
+ this.intervalId = null;
150
129
  }
151
- this.abortController?.abort();
152
- this.abortController = null;
153
130
  }
154
131
  }
155
132
  let ptySpawn;
@@ -162,6 +139,7 @@ catch (err) {
162
139
  console.warn("[MuxServer] node-pty not available — mux server will be disabled.", err);
163
140
  }
164
141
  const RING_BUFFER_MAX = 50 * 1024; // 50KB max per terminal
142
+ const WS_BUFFER_HIGH_WATERMARK = 64 * 1024; // 64KB
165
143
  const MAX_REATTACH_ATTEMPTS = 3;
166
144
  /**
167
145
  * TerminalManager manages PTY processes independently of WebSocket connections.
@@ -173,21 +151,29 @@ class TerminalManager {
173
151
  constructor(tmuxPath) {
174
152
  this.TMUX = tmuxPath ?? findTmux();
175
153
  }
154
+ terminalKey(id, projectId) {
155
+ return projectId ? `${projectId}:${id}` : id;
156
+ }
176
157
  /**
177
158
  * Open/attach to a terminal. If already open, just return.
178
159
  * If has subscribers but PTY crashed, re-attach.
179
160
  */
180
- open(id) {
161
+ open(id, projectId, tmuxName) {
181
162
  // Validate and resolve
182
163
  if (!validateSessionId(id)) {
183
164
  throw new Error(`Invalid session ID: ${id}`);
184
165
  }
185
- const tmuxSessionId = resolveTmuxSession(id, this.TMUX);
166
+ // Use provided tmuxName, or reuse from existing terminal entry, or resolve
167
+ const key = this.terminalKey(id, projectId);
168
+ const existing = this.terminals.get(key);
169
+ const tmuxSessionId = tmuxName ??
170
+ existing?.tmuxSessionId ??
171
+ resolveTmuxSession(id, this.TMUX, undefined, undefined, projectId);
186
172
  if (!tmuxSessionId) {
187
173
  throw new Error(`Session not found: ${id}`);
188
174
  }
189
175
  // Get or create terminal entry
190
- let terminal = this.terminals.get(id);
176
+ let terminal = this.terminals.get(key);
191
177
  if (!terminal) {
192
178
  terminal = {
193
179
  id,
@@ -199,7 +185,7 @@ class TerminalManager {
199
185
  bufferBytes: 0,
200
186
  reattachAttempts: 0,
201
187
  };
202
- this.terminals.set(id, terminal);
188
+ this.terminals.set(key, terminal);
203
189
  }
204
190
  // If PTY is already attached, we're done
205
191
  if (terminal.pty) {
@@ -271,7 +257,7 @@ class TerminalManager {
271
257
  terminal.reattachAttempts += 1;
272
258
  console.log(`[MuxServer] Re-attaching to ${id} (attempt ${terminal.reattachAttempts}/${MAX_REATTACH_ATTEMPTS})`);
273
259
  try {
274
- this.open(id);
260
+ this.open(id, projectId);
275
261
  terminal.reattachAttempts = 0; // reset on successful attach
276
262
  return; // re-attached — don't notify exit
277
263
  }
@@ -293,8 +279,8 @@ class TerminalManager {
293
279
  /**
294
280
  * Write data to the PTY if attached
295
281
  */
296
- write(id, data) {
297
- const terminal = this.terminals.get(id);
282
+ write(id, data, projectId) {
283
+ const terminal = this.terminals.get(this.terminalKey(id, projectId));
298
284
  if (terminal?.pty) {
299
285
  terminal.pty.write(data);
300
286
  }
@@ -302,8 +288,8 @@ class TerminalManager {
302
288
  /**
303
289
  * Resize the PTY if attached
304
290
  */
305
- resize(id, cols, rows) {
306
- const terminal = this.terminals.get(id);
291
+ resize(id, cols, rows, projectId) {
292
+ const terminal = this.terminals.get(this.terminalKey(id, projectId));
307
293
  if (terminal?.pty) {
308
294
  terminal.pty.resize(cols, rows);
309
295
  }
@@ -313,10 +299,11 @@ class TerminalManager {
313
299
  * Automatically opens the terminal if needed.
314
300
  * @param onExit - called when the PTY exits and cannot be re-attached
315
301
  */
316
- subscribe(id, callback, onExit) {
302
+ subscribe(id, projectId, callback, onExit) {
317
303
  // Ensure terminal is open
318
- this.open(id);
319
- const terminal = this.terminals.get(id);
304
+ this.open(id, projectId);
305
+ const key = this.terminalKey(id, projectId);
306
+ const terminal = this.terminals.get(key);
320
307
  if (!terminal) {
321
308
  throw new Error(`Failed to open terminal: ${id}`);
322
309
  }
@@ -335,15 +322,15 @@ class TerminalManager {
335
322
  terminal.pty.kill();
336
323
  terminal.pty = null;
337
324
  }
338
- this.terminals.delete(id);
325
+ this.terminals.delete(key);
339
326
  }
340
327
  };
341
328
  }
342
329
  /**
343
330
  * Get buffered data for a terminal
344
331
  */
345
- getBuffer(id) {
346
- const terminal = this.terminals.get(id);
332
+ getBuffer(id, projectId) {
333
+ const terminal = this.terminals.get(this.terminalKey(id, projectId));
347
334
  if (!terminal)
348
335
  return "";
349
336
  return terminal.buffer.join("");
@@ -400,60 +387,75 @@ export function createMuxWebSocket(tmuxPath) {
400
387
  }
401
388
  else if (msg.ch === "terminal") {
402
389
  const { id, type } = msg;
390
+ const projectId = "projectId" in msg ? msg.projectId : undefined;
391
+ const subscriptionKey = projectId ? `${projectId}:${id}` : id;
403
392
  try {
404
393
  if (type === "open") {
405
394
  // Validate session exists
406
- terminalManager.open(id);
395
+ terminalManager.open(id, projectId, "tmuxName" in msg ? msg.tmuxName : undefined);
407
396
  // Send opened confirmation (idempotent — safe to send on re-open)
408
- const openedMsg = { ch: "terminal", id, type: "opened" };
397
+ const openedMsg = {
398
+ ch: "terminal",
399
+ id,
400
+ type: "opened",
401
+ ...(projectId && { projectId }),
402
+ };
409
403
  ws.send(JSON.stringify(openedMsg));
410
404
  // Subscribe and send history buffer only for new subscribers.
411
405
  // Skipping the buffer on re-open prevents duplicate output when
412
406
  // MuxProvider re-sends open for all terminals on reconnect.
413
- if (!subscriptions.has(id)) {
407
+ if (!subscriptions.has(subscriptionKey)) {
414
408
  // Send buffered history to catch up the new subscriber
415
- const buffer = terminalManager.getBuffer(id);
409
+ const buffer = terminalManager.getBuffer(id, projectId);
416
410
  if (buffer) {
417
411
  const bufferMsg = {
418
412
  ch: "terminal",
419
413
  id,
420
414
  type: "data",
421
415
  data: buffer,
416
+ ...(projectId && { projectId }),
422
417
  };
423
418
  ws.send(JSON.stringify(bufferMsg));
424
419
  }
425
- const unsub = terminalManager.subscribe(id, (data) => {
420
+ const unsub = terminalManager.subscribe(id, projectId, (data) => {
426
421
  const dataMsg = {
427
422
  ch: "terminal",
428
423
  id,
429
424
  type: "data",
430
425
  data,
426
+ ...(projectId && { projectId }),
431
427
  };
432
428
  if (ws.readyState === WebSocket.OPEN) {
433
429
  ws.send(JSON.stringify(dataMsg));
434
430
  }
435
431
  }, (exitCode) => {
436
- const exitedMsg = { ch: "terminal", id, type: "exited", code: exitCode };
432
+ const exitedMsg = {
433
+ ch: "terminal",
434
+ id,
435
+ type: "exited",
436
+ code: exitCode,
437
+ ...(projectId && { projectId }),
438
+ };
437
439
  if (ws.readyState === WebSocket.OPEN) {
438
440
  ws.send(JSON.stringify(exitedMsg));
439
441
  }
440
442
  });
441
- subscriptions.set(id, unsub);
443
+ subscriptions.set(subscriptionKey, unsub);
442
444
  }
443
445
  }
444
446
  else if (type === "data" && "data" in msg) {
445
- terminalManager.write(id, msg.data);
447
+ terminalManager.write(id, msg.data, projectId);
446
448
  }
447
449
  else if (type === "resize" && "cols" in msg && "rows" in msg) {
448
- terminalManager.resize(id, msg.cols, msg.rows);
450
+ terminalManager.resize(id, msg.cols, msg.rows, projectId);
449
451
  }
450
452
  else if (type === "close") {
451
453
  // Unsubscribe this client only — TerminalManager is shared across
452
454
  // all mux connections so we must not kill the PTY here.
453
- const unsub = subscriptions.get(id);
455
+ const unsub = subscriptions.get(subscriptionKey);
454
456
  if (unsub) {
455
457
  unsub();
456
- subscriptions.delete(id);
458
+ subscriptions.delete(subscriptionKey);
457
459
  }
458
460
  }
459
461
  }
@@ -464,6 +466,7 @@ export function createMuxWebSocket(tmuxPath) {
464
466
  id,
465
467
  type: "error",
466
468
  message: err instanceof Error ? err.message : String(err),
469
+ ...(projectId && { projectId }),
467
470
  };
468
471
  ws.send(JSON.stringify(errorMsg));
469
472
  }
@@ -472,10 +475,19 @@ export function createMuxWebSocket(tmuxPath) {
472
475
  else if (msg.ch === "subscribe") {
473
476
  if (msg.topics.includes("sessions") && !sessionUnsubscribe) {
474
477
  sessionUnsubscribe = broadcaster.subscribe((sessions) => {
475
- if (ws.readyState === WebSocket.OPEN) {
476
- const snapMsg = { ch: "sessions", type: "snapshot", sessions };
477
- ws.send(JSON.stringify(snapMsg));
478
+ if (ws.readyState !== WebSocket.OPEN)
479
+ return;
480
+ if (ws.bufferedAmount > WS_BUFFER_HIGH_WATERMARK) {
481
+ console.warn("[MuxServer] Skipping session snapshot — socket backpressured");
482
+ return;
478
483
  }
484
+ const snapMsg = { ch: "sessions", type: "snapshot", sessions };
485
+ ws.send(JSON.stringify(snapMsg));
486
+ }, (error) => {
487
+ if (ws.readyState !== WebSocket.OPEN)
488
+ return;
489
+ const errMsg = { ch: "sessions", type: "error", error };
490
+ ws.send(JSON.stringify(errMsg));
479
491
  });
480
492
  }
481
493
  }
@@ -5,10 +5,74 @@
5
5
  * so the logic can be properly unit tested.
6
6
  */
7
7
  import { execFileSync } from "node:child_process";
8
+ import { readdirSync, existsSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
8
11
  /** Session ID validation regex — alphanumeric, hyphens, underscores only */
9
12
  export const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
10
13
  /** Hash prefix pattern — 12-char lowercase hex, as generated by generateConfigHash */
11
14
  const HASH_PREFIX_PATTERN = /^[a-f0-9]{12}-/;
15
+ /**
16
+ * StorageKey pattern — either bare 12-hex hash or `{hash}-{projectName}`
17
+ * wrapped form. The wrapped suffix comes from `basename(projectPath)` on
18
+ * disk so it can contain any character a filesystem path component allows
19
+ * (spaces, unicode, etc.); we only require that something non-empty
20
+ * follows the `-` separator. Security: storageKey is passed to
21
+ * `execFileSync` which bypasses the shell, so arbitrary characters are
22
+ * safe in the argv.
23
+ */
24
+ const STORAGE_KEY_PATTERN = /^[a-f0-9]{12}(-.+)?$/;
25
+ const defaultFs = {
26
+ // Only return subdirectory names. `readdirSync` without withFileTypes
27
+ // includes plain files, so a stray file like `aabbccddeef0` would pass
28
+ // STORAGE_KEY_PATTERN and trigger an unnecessary existsSync probe.
29
+ readdir: (p) => readdirSync(p, { withFileTypes: true })
30
+ .filter((e) => e.isDirectory())
31
+ .map((e) => e.name),
32
+ exists: (p) => existsSync(p),
33
+ homedir,
34
+ };
35
+ /**
36
+ * Find every storageKey that owns a given sessionId by scanning the AO
37
+ * base directory for projects whose `sessions/{sessionId}` file exists.
38
+ *
39
+ * This is the authoritative disambiguation step: tmux names alone are
40
+ * ambiguous when storageKey can take either the bare-hash or wrapped
41
+ * `{hash}-{projectName}` form. The on-disk session record is unique per
42
+ * storageKey so it tells us exactly which tmux name to expect.
43
+ *
44
+ * Returns all candidates (not just the first). Multiple projects can
45
+ * share a sessionId like `app-1`, and the caller must probe each until
46
+ * it finds a live tmux session — otherwise a stale metadata dir from
47
+ * one project could shadow the live session of another.
48
+ */
49
+ function findStorageKeysForSession(sessionId, fs, projectId) {
50
+ const aoBase = join(fs.homedir(), ".agent-orchestrator");
51
+ let entries;
52
+ try {
53
+ entries = fs.readdir(aoBase);
54
+ }
55
+ catch {
56
+ return [];
57
+ }
58
+ const matches = [];
59
+ const projectMatches = [];
60
+ for (const entry of entries) {
61
+ if (!STORAGE_KEY_PATTERN.test(entry))
62
+ continue;
63
+ const sessionFile = join(aoBase, entry, "sessions", sessionId);
64
+ if (fs.exists(sessionFile)) {
65
+ const unwrappedProjectId = entry.slice(13); // Strip "{hash}-" prefix when present.
66
+ if (projectId && (entry === projectId || unwrappedProjectId === projectId)) {
67
+ projectMatches.push(entry);
68
+ }
69
+ else {
70
+ matches.push(entry);
71
+ }
72
+ }
73
+ }
74
+ return [...projectMatches, ...matches];
75
+ }
12
76
  /**
13
77
  * Validate a session ID format.
14
78
  * Prevents path traversal, shell injection, and other attacks.
@@ -45,20 +109,29 @@ export function findTmux(execFn = execFileSync) {
45
109
  /**
46
110
  * Resolve a user-facing session ID to its actual tmux session name.
47
111
  *
48
- * The hash-based architecture prefixes tmux session names with a config hash
49
- * (e.g., "8474d6f29887-ao-15" for user-facing "ao-15"). This function:
112
+ * ao-core names tmux sessions as `{storageKey}-{sessionId}`, where
113
+ * storageKey is either `{12-hex}` (bare hash) or `{12-hex}-{projectName}`
114
+ * (legacy wrapped format). This function:
50
115
  *
51
- * 1. Tries exact match first using tmux's `=` prefix syntax to prevent
116
+ * 1. Tries exact match using tmux's `=` prefix syntax to prevent
52
117
  * prefix matching (where "ao-1" would incorrectly match "ao-15").
53
- * 2. Falls back to listing all sessions and finding one with a 12-char hex
54
- * prefix followed by the exact session ID (e.g., `{12-hex}-{sessionId}`).
118
+ * 2. Looks up the storageKey owning this sessionId on disk (under
119
+ * `~/.agent-orchestrator/{storageKey}/sessions/{sessionId}`) and asks
120
+ * tmux whether the exact `{storageKey}-{sessionId}` session exists.
121
+ * The on-disk check is authoritative — it avoids ambiguous suffix
122
+ * matches where a bare session like `{hash}-my-app-1` could be
123
+ * mistaken for a lookup of `app-1`.
124
+ * 3. Falls back to listing sessions and matching a hash-prefixed name
125
+ * whose remainder equals the sessionId (bare-hash only), so behavior
126
+ * stays correct even if the on-disk session record is absent.
55
127
  *
56
128
  * @param sessionId - User-facing session ID (e.g., "ao-15")
57
129
  * @param tmuxPath - Full path to tmux binary
58
130
  * @param execFn - Injectable execFileSync for testing. Defaults to child_process.execFileSync.
131
+ * @param fs - Injectable filesystem adapter for testing.
59
132
  * @returns The actual tmux session name, or null if not found
60
133
  */
61
- export function resolveTmuxSession(sessionId, tmuxPath, execFn = execFileSync) {
134
+ export function resolveTmuxSession(sessionId, tmuxPath, execFn = execFileSync, fs = defaultFs, projectId) {
62
135
  // Try exact match first using = prefix for exact matching (e.g., "ao-orchestrator")
63
136
  // Without =, tmux uses prefix matching: "ao-1" would match "ao-15"
64
137
  try {
@@ -68,9 +141,26 @@ export function resolveTmuxSession(sessionId, tmuxPath, execFn = execFileSync) {
68
141
  catch {
69
142
  // Not an exact match
70
143
  }
71
- // Search for hash-prefixed tmux session (e.g., "8474d6f29887-ao-15" for "ao-15")
72
- // Validate the 12-char hex prefix to avoid ambiguous suffix matches where
73
- // "hash-my-app-1" could falsely match a lookup for "app-1".
144
+ // Authoritative path: find candidate storageKeys on disk, then verify
145
+ // each exact tmux session name with has-session. This is unambiguous
146
+ // even when the storageKey is wrapped (`{hash}-{projectName}`). Walk
147
+ // every candidate so a stale metadata dir in one project can't shadow
148
+ // the live session of another project with the same sessionId.
149
+ for (const storageKey of findStorageKeysForSession(sessionId, fs, projectId)) {
150
+ const tmuxName = `${storageKey}-${sessionId}`;
151
+ try {
152
+ execFn(tmuxPath, ["has-session", "-t", `=${tmuxName}`], { timeout: 5000 });
153
+ return tmuxName;
154
+ }
155
+ catch {
156
+ // Session dir exists but tmux session doesn't — try next candidate
157
+ }
158
+ }
159
+ // Fallback: list sessions and match the bare-hash form only. We
160
+ // intentionally do NOT match by trailing suffix here — that would cause
161
+ // `app-1` to falsely resolve a distinct session `{hash}-my-app-1`. If a
162
+ // wrapped-storageKey session isn't findable on disk above, it's safer
163
+ // to return null than to guess.
74
164
  try {
75
165
  const output = execFn(tmuxPath, ["list-sessions", "-F", "#{session_name}"], {
76
166
  timeout: 5000,
package/next.config.js CHANGED
@@ -1,5 +1,11 @@
1
+ import path from "path";
2
+ import { fileURLToPath } from "url";
3
+
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+
1
6
  /** @type {import('next').NextConfig} */
2
7
  const nextConfig = {
8
+ outputFileTracingRoot: path.join(__dirname, "../.."),
3
9
  transpilePackages: [
4
10
  "@aoagents/ao-core",
5
11
  "@aoagents/ao-plugin-agent-claude-code",