agent-relay 1.3.2 → 1.5.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 (318) hide show
  1. package/README.md +130 -158
  2. package/bin/relay-pty +0 -0
  3. package/bin/relay-pty-darwin-arm64 +0 -0
  4. package/bin/relay-pty-darwin-x64 +0 -0
  5. package/bin/relay-pty-linux-x64 +0 -0
  6. package/deploy/workspace/entrypoint.sh +9 -0
  7. package/dist/bridge/spawner.d.ts +4 -4
  8. package/dist/bridge/spawner.js +58 -92
  9. package/dist/cli/index.d.ts +8 -6
  10. package/dist/cli/index.js +282 -47
  11. package/dist/cloud/api/daemons.js +13 -32
  12. package/dist/cloud/api/onboarding.js +2 -4
  13. package/dist/cloud/api/providers.js +6 -0
  14. package/dist/cloud/config.d.ts +1 -0
  15. package/dist/cloud/config.js +2 -0
  16. package/dist/cloud/db/bulk-ingest.d.ts +2 -1
  17. package/dist/cloud/db/drizzle.d.ts +21 -26
  18. package/dist/cloud/db/drizzle.js +87 -100
  19. package/dist/cloud/db/index.d.ts +6 -5
  20. package/dist/cloud/db/index.js +9 -8
  21. package/dist/cloud/db/schema.d.ts +1049 -1076
  22. package/dist/cloud/db/schema.js +59 -71
  23. package/dist/cloud/server.js +854 -18
  24. package/dist/cloud/services/persistence.d.ts +15 -15
  25. package/dist/cloud/services/persistence.js +14 -14
  26. package/dist/daemon/agent-manager.d.ts +6 -5
  27. package/dist/daemon/agent-manager.js +12 -8
  28. package/dist/daemon/channel-membership-store.d.ts +48 -0
  29. package/dist/daemon/channel-membership-store.js +149 -0
  30. package/dist/daemon/cloud-sync.d.ts +2 -0
  31. package/dist/daemon/cloud-sync.js +4 -0
  32. package/dist/daemon/connection.js +17 -9
  33. package/dist/daemon/router.d.ts +37 -0
  34. package/dist/daemon/router.js +318 -79
  35. package/dist/daemon/server.d.ts +15 -0
  36. package/dist/daemon/server.js +141 -3
  37. package/dist/dashboard/out/404.html +1 -0
  38. package/dist/dashboard/out/_next/static/IxxVRv94L1w3ReRGAiI-k/_buildManifest.js +1 -0
  39. package/dist/dashboard/out/_next/static/IxxVRv94L1w3ReRGAiI-k/_ssgManifest.js +1 -0
  40. package/dist/dashboard/out/_next/static/chunks/116-eacf84a131b80db9.js +1 -0
  41. package/dist/dashboard/out/_next/static/chunks/117-c8afed19e821a35d.js +2 -0
  42. package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
  43. package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +9 -0
  44. package/dist/dashboard/out/_next/static/chunks/64-87ab9cd6bcf2f737.js +1 -0
  45. package/dist/dashboard/out/_next/static/chunks/648-acb2ff9f77cbfbd3.js +1 -0
  46. package/dist/dashboard/out/_next/static/chunks/766-aa7c8c9900ff5f53.js +1 -0
  47. package/dist/dashboard/out/_next/static/chunks/83-4f08122d4e7e79a6.js +1 -0
  48. package/dist/dashboard/out/_next/static/chunks/847-f1f467060f32afff.js +1 -0
  49. package/dist/dashboard/out/_next/static/chunks/891-a024fbe4b619cf6f.js +1 -0
  50. package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +1 -0
  51. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-f746f29e01fffc43.js +1 -0
  52. package/dist/dashboard/out/_next/static/chunks/app/app/page-ffad986adfcc8b31.js +1 -0
  53. package/dist/dashboard/out/_next/static/chunks/app/cloud/link/page-cfeb437f08a12ed9.js +1 -0
  54. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-03ac6f35a6654ea6.js +1 -0
  55. package/dist/dashboard/out/_next/static/chunks/app/history/page-240f91e8b06ba8ac.js +1 -0
  56. package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
  57. package/dist/dashboard/out/_next/static/chunks/app/login/page-6ec54eee75877971.js +1 -0
  58. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-82938ab8fcf44694.js +1 -0
  59. package/dist/dashboard/out/_next/static/chunks/app/page-671037943b2f2e43.js +1 -0
  60. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-0efa024c28ba4597.js +1 -0
  61. package/dist/dashboard/out/_next/static/chunks/app/providers/page-57cbd738c6a73859.js +1 -0
  62. package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-5ab0854472b402b0.js +1 -0
  63. package/dist/dashboard/out/_next/static/chunks/app/signup/page-18a4665665f6be11.js +1 -0
  64. package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
  65. package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +1 -0
  66. package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
  67. package/dist/dashboard/out/_next/static/chunks/main-5a40a5ae29646e1b.js +1 -0
  68. package/dist/dashboard/out/_next/static/chunks/main-app-6e8e8d3ef4e0192a.js +1 -0
  69. package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
  70. package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
  71. package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  72. package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
  73. package/dist/dashboard/out/_next/static/css/4034f236dd1a3178.css +1 -0
  74. package/dist/dashboard/out/_next/static/css/8f9ed310f454e5a5.css +1 -0
  75. package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
  76. package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
  77. package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
  78. package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
  79. package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
  80. package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +45 -0
  81. package/dist/dashboard/out/alt-logos/logo.svg +38 -0
  82. package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
  83. package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
  84. package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
  85. package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
  86. package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
  87. package/dist/dashboard/out/alt-logos/monogram-logo.svg +38 -0
  88. package/dist/dashboard/out/app/onboarding.html +1 -0
  89. package/dist/dashboard/out/app/onboarding.txt +7 -0
  90. package/dist/dashboard/out/app.html +1 -0
  91. package/dist/dashboard/out/app.txt +7 -0
  92. package/dist/dashboard/out/apple-icon.png +0 -0
  93. package/dist/dashboard/out/cloud/link.html +1 -0
  94. package/dist/dashboard/out/cloud/link.txt +7 -0
  95. package/dist/dashboard/out/connect-repos.html +1 -0
  96. package/dist/dashboard/out/connect-repos.txt +7 -0
  97. package/dist/dashboard/out/history.html +1 -0
  98. package/dist/dashboard/out/history.txt +7 -0
  99. package/dist/dashboard/out/index.html +1 -0
  100. package/dist/dashboard/out/index.txt +7 -0
  101. package/dist/dashboard/out/login.html +5 -0
  102. package/dist/dashboard/out/login.txt +7 -0
  103. package/dist/dashboard/out/metrics.html +1 -0
  104. package/dist/dashboard/out/metrics.txt +7 -0
  105. package/dist/dashboard/out/pricing.html +13 -0
  106. package/dist/dashboard/out/pricing.txt +7 -0
  107. package/dist/dashboard/out/providers/setup/claude.html +1 -0
  108. package/dist/dashboard/out/providers/setup/claude.txt +8 -0
  109. package/dist/dashboard/out/providers/setup/codex.html +1 -0
  110. package/dist/dashboard/out/providers/setup/codex.txt +8 -0
  111. package/dist/dashboard/out/providers.html +1 -0
  112. package/dist/dashboard/out/providers.txt +7 -0
  113. package/dist/dashboard/out/signup.html +6 -0
  114. package/dist/dashboard/out/signup.txt +7 -0
  115. package/dist/dashboard-server/metrics.d.ts +105 -0
  116. package/dist/dashboard-server/metrics.js +193 -0
  117. package/dist/dashboard-server/needs-attention.d.ts +24 -0
  118. package/dist/dashboard-server/needs-attention.js +78 -0
  119. package/dist/dashboard-server/server.d.ts +15 -0
  120. package/dist/dashboard-server/server.js +4753 -0
  121. package/dist/dashboard-server/start.d.ts +6 -0
  122. package/dist/dashboard-server/start.js +13 -0
  123. package/dist/dashboard-server/user-bridge.d.ts +132 -0
  124. package/dist/dashboard-server/user-bridge.js +317 -0
  125. package/dist/protocol/channels.d.ts +14 -8
  126. package/dist/protocol/channels.js +1 -1
  127. package/dist/protocol/index.d.ts +1 -0
  128. package/dist/protocol/index.js +1 -0
  129. package/dist/protocol/relay-pty-schemas.d.ts +209 -0
  130. package/dist/protocol/relay-pty-schemas.js +60 -0
  131. package/dist/wrapper/auth-detection.js +8 -1
  132. package/dist/wrapper/base-wrapper.d.ts +11 -1
  133. package/dist/wrapper/base-wrapper.js +67 -6
  134. package/dist/wrapper/client.d.ts +49 -1
  135. package/dist/wrapper/client.js +167 -0
  136. package/dist/wrapper/parser.d.ts +0 -4
  137. package/dist/wrapper/parser.js +38 -10
  138. package/dist/wrapper/pty-wrapper.d.ts +12 -1
  139. package/dist/wrapper/pty-wrapper.js +104 -5
  140. package/dist/wrapper/relay-pty-orchestrator.d.ts +270 -0
  141. package/dist/wrapper/relay-pty-orchestrator.js +970 -0
  142. package/dist/wrapper/shared.d.ts +1 -1
  143. package/dist/wrapper/shared.js +14 -4
  144. package/dist/wrapper/tmux-wrapper.d.ts +13 -1
  145. package/dist/wrapper/tmux-wrapper.js +143 -29
  146. package/package.json +9 -4
  147. package/scripts/postinstall.js +101 -11
  148. package/.trajectories/active/traj_3yx9dy148mge.json +0 -42
  149. package/.trajectories/agent-relay-322-324.md +0 -17
  150. package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.json +0 -49
  151. package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.md +0 -31
  152. package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.json +0 -125
  153. package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.md +0 -62
  154. package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.json +0 -65
  155. package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.md +0 -37
  156. package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +0 -49
  157. package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +0 -31
  158. package/.trajectories/completed/2026-01/traj_1k5if5snst2e.json +0 -65
  159. package/.trajectories/completed/2026-01/traj_1k5if5snst2e.md +0 -37
  160. package/.trajectories/completed/2026-01/traj_1rp3rges5811.json +0 -49
  161. package/.trajectories/completed/2026-01/traj_1rp3rges5811.md +0 -31
  162. package/.trajectories/completed/2026-01/traj_22bhyulruouw.json +0 -113
  163. package/.trajectories/completed/2026-01/traj_22bhyulruouw.md +0 -57
  164. package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.json +0 -53
  165. package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.md +0 -32
  166. package/.trajectories/completed/2026-01/traj_33iuy72sezbk.json +0 -49
  167. package/.trajectories/completed/2026-01/traj_33iuy72sezbk.md +0 -31
  168. package/.trajectories/completed/2026-01/traj_3t0440mjeunc.json +0 -26
  169. package/.trajectories/completed/2026-01/traj_3t0440mjeunc.md +0 -6
  170. package/.trajectories/completed/2026-01/traj_45x9494d9xnr.json +0 -47
  171. package/.trajectories/completed/2026-01/traj_45x9494d9xnr.md +0 -32
  172. package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.json +0 -53
  173. package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.md +0 -32
  174. package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +0 -49
  175. package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +0 -31
  176. package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.json +0 -77
  177. package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.md +0 -42
  178. package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.json +0 -59
  179. package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.md +0 -33
  180. package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.json +0 -53
  181. package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.md +0 -32
  182. package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.json +0 -48
  183. package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.md +0 -24
  184. package/.trajectories/completed/2026-01/traj_6mieijqyvaag.json +0 -77
  185. package/.trajectories/completed/2026-01/traj_6mieijqyvaag.md +0 -42
  186. package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +0 -109
  187. package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.json +0 -77
  188. package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.md +0 -42
  189. package/.trajectories/completed/2026-01/traj_7ludwvz45veh.json +0 -209
  190. package/.trajectories/completed/2026-01/traj_7ludwvz45veh.md +0 -97
  191. package/.trajectories/completed/2026-01/traj_94gnp3k30goq.json +0 -66
  192. package/.trajectories/completed/2026-01/traj_94gnp3k30goq.md +0 -36
  193. package/.trajectories/completed/2026-01/traj_9921cuhel0pj.json +0 -48
  194. package/.trajectories/completed/2026-01/traj_9921cuhel0pj.md +0 -24
  195. package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +0 -49
  196. package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +0 -31
  197. package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.json +0 -49
  198. package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.md +0 -23
  199. package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.json +0 -40
  200. package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.md +0 -22
  201. package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +0 -66
  202. package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +0 -36
  203. package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +0 -49
  204. package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +0 -31
  205. package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +0 -65
  206. package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +0 -37
  207. package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.json +0 -53
  208. package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.md +0 -32
  209. package/.trajectories/completed/2026-01/traj_cxofprm2m2en.json +0 -49
  210. package/.trajectories/completed/2026-01/traj_cxofprm2m2en.md +0 -31
  211. package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.json +0 -26
  212. package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.md +0 -6
  213. package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.json +0 -121
  214. package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.md +0 -29
  215. package/.trajectories/completed/2026-01/traj_dfuvww9pege5.json +0 -59
  216. package/.trajectories/completed/2026-01/traj_dfuvww9pege5.md +0 -37
  217. package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +0 -36
  218. package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +0 -21
  219. package/.trajectories/completed/2026-01/traj_fhx9irlckht6.json +0 -53
  220. package/.trajectories/completed/2026-01/traj_fhx9irlckht6.md +0 -32
  221. package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.json +0 -101
  222. package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.md +0 -52
  223. package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.json +0 -77
  224. package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.md +0 -42
  225. package/.trajectories/completed/2026-01/traj_gjdre5voouod.json +0 -53
  226. package/.trajectories/completed/2026-01/traj_gjdre5voouod.md +0 -32
  227. package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.json +0 -25
  228. package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.md +0 -15
  229. package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.json +0 -101
  230. package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.md +0 -44
  231. package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +0 -101
  232. package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +0 -52
  233. package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.json +0 -49
  234. package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.md +0 -31
  235. package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.json +0 -65
  236. package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.md +0 -37
  237. package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.json +0 -22
  238. package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.md +0 -5
  239. package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.json +0 -53
  240. package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.md +0 -32
  241. package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +0 -61
  242. package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +0 -36
  243. package/.trajectories/completed/2026-01/traj_lq450ly148uw.json +0 -49
  244. package/.trajectories/completed/2026-01/traj_lq450ly148uw.md +0 -31
  245. package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.json +0 -25
  246. package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.md +0 -15
  247. package/.trajectories/completed/2026-01/traj_multi_server_arch.md +0 -101
  248. package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.json +0 -53
  249. package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.md +0 -32
  250. package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.json +0 -53
  251. package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.md +0 -32
  252. package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +0 -73
  253. package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +0 -41
  254. package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.json +0 -48
  255. package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.md +0 -24
  256. package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.json +0 -53
  257. package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.md +0 -32
  258. package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.json +0 -27
  259. package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.md +0 -14
  260. package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +0 -77
  261. package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +0 -42
  262. package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.json +0 -77
  263. package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.md +0 -42
  264. package/.trajectories/completed/2026-01/traj_qft54mi7nfor.json +0 -53
  265. package/.trajectories/completed/2026-01/traj_qft54mi7nfor.md +0 -32
  266. package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.json +0 -83
  267. package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.md +0 -47
  268. package/.trajectories/completed/2026-01/traj_rd9toccj18a0.json +0 -59
  269. package/.trajectories/completed/2026-01/traj_rd9toccj18a0.md +0 -37
  270. package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +0 -109
  271. package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +0 -56
  272. package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.json +0 -48
  273. package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.md +0 -16
  274. package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.json +0 -59
  275. package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.md +0 -37
  276. package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.json +0 -53
  277. package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.md +0 -32
  278. package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.json +0 -84
  279. package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.md +0 -109
  280. package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.json +0 -53
  281. package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.md +0 -32
  282. package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.json +0 -53
  283. package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.md +0 -32
  284. package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.json +0 -186
  285. package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.md +0 -86
  286. package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json +0 -77
  287. package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.md +0 -42
  288. package/.trajectories/completed/2026-01/traj_v87hypnongqx.json +0 -71
  289. package/.trajectories/completed/2026-01/traj_v87hypnongqx.md +0 -42
  290. package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.json +0 -89
  291. package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.md +0 -47
  292. package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.json +0 -53
  293. package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.md +0 -32
  294. package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.json +0 -20
  295. package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.md +0 -6
  296. package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +0 -113
  297. package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +0 -57
  298. package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +0 -61
  299. package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +0 -36
  300. package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.json +0 -175
  301. package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.md +0 -82
  302. package/.trajectories/completed/2026-01/traj_xy9vifpqet80.json +0 -65
  303. package/.trajectories/completed/2026-01/traj_xy9vifpqet80.md +0 -37
  304. package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.json +0 -49
  305. package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.md +0 -31
  306. package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +0 -49
  307. package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +0 -31
  308. package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.json +0 -47
  309. package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.md +0 -32
  310. package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.json +0 -59
  311. package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.md +0 -37
  312. package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +0 -49
  313. package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +0 -31
  314. package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.json +0 -53
  315. package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.md +0 -32
  316. package/.trajectories/consolidate-settings-panel.md +0 -24
  317. package/.trajectories/gh-cli-user-token.md +0 -26
  318. package/.trajectories/index.json +0 -607
@@ -2,16 +2,16 @@
2
2
  * Cloud Persistence Service
3
3
  *
4
4
  * Handles durable persistence of agent session data for cloud deployments.
5
- * Subscribes to PtyWrapper events ('summary', 'session-end') and persists
5
+ * Subscribes to wrapper events ('summary', 'session-end') and persists
6
6
  * to PostgreSQL via Drizzle ORM.
7
7
  *
8
- * This decouples PtyWrapper from storage concerns - the wrapper emits events,
8
+ * This decouples wrappers from storage concerns - the wrapper emits events,
9
9
  * this service handles persistence. Different storage backends can be swapped
10
10
  * by implementing alternative persistence services.
11
11
  *
12
- * @see PtyWrapperEvents in src/wrapper/pty-wrapper.ts for event definitions
12
+ * @see RelayPtyOrchestratorEvents in src/wrapper/relay-pty-orchestrator.ts for event definitions
13
13
  */
14
- import type { PtyWrapper } from '../../wrapper/pty-wrapper.js';
14
+ import type { RelayPtyOrchestrator } from '../../wrapper/relay-pty-orchestrator.js';
15
15
  /**
16
16
  * Configuration for CloudPersistenceService
17
17
  */
@@ -32,12 +32,12 @@ export interface CloudPersistenceConfig {
32
32
  * workspaceId: 'workspace-123',
33
33
  * });
34
34
  *
35
- * // Bind to a PtyWrapper instance
36
- * const pty = new PtyWrapper(config);
37
- * const sessionId = await persistence.bindToPtyWrapper(pty);
35
+ * // Bind to a RelayPtyOrchestrator instance
36
+ * const pty = new RelayPtyOrchestrator(config);
37
+ * const sessionId = await persistence.bindToWrapper(pty);
38
38
  *
39
39
  * // When done, unbind to clean up listeners
40
- * persistence.unbindFromPtyWrapper(pty);
40
+ * persistence.unbindFromWrapper(pty);
41
41
  * ```
42
42
  */
43
43
  export declare class CloudPersistenceService {
@@ -45,19 +45,19 @@ export declare class CloudPersistenceService {
45
45
  private boundWrappers;
46
46
  constructor(config: CloudPersistenceConfig);
47
47
  /**
48
- * Bind to a PtyWrapper instance and start persisting its events.
48
+ * Bind to a RelayPtyOrchestrator instance and start persisting its events.
49
49
  * Creates a new agent session record and returns the session ID.
50
50
  *
51
- * @param wrapper The PtyWrapper to bind to
51
+ * @param wrapper The RelayPtyOrchestrator to bind to
52
52
  * @returns The session ID for this agent session
53
53
  */
54
- bindToPtyWrapper(wrapper: PtyWrapper): Promise<string>;
54
+ bindToRelayPtyOrchestrator(wrapper: RelayPtyOrchestrator): Promise<string>;
55
55
  /**
56
- * Unbind from a PtyWrapper and clean up event listeners.
56
+ * Unbind from a RelayPtyOrchestrator and clean up event listeners.
57
57
  *
58
- * @param wrapper The PtyWrapper to unbind from
58
+ * @param wrapper The RelayPtyOrchestrator to unbind from
59
59
  */
60
- unbindFromPtyWrapper(wrapper: PtyWrapper): void;
60
+ unbindFromRelayPtyOrchestrator(wrapper: RelayPtyOrchestrator): void;
61
61
  /**
62
62
  * Handle a summary event - persist to agent_summaries table.
63
63
  */
@@ -69,7 +69,7 @@ export declare class CloudPersistenceService {
69
69
  /**
70
70
  * Get the session ID for a bound wrapper.
71
71
  */
72
- getSessionId(wrapper: PtyWrapper): string | undefined;
72
+ getSessionId(wrapper: RelayPtyOrchestrator): string | undefined;
73
73
  /**
74
74
  * Get all summaries for a session.
75
75
  */
@@ -2,14 +2,14 @@
2
2
  * Cloud Persistence Service
3
3
  *
4
4
  * Handles durable persistence of agent session data for cloud deployments.
5
- * Subscribes to PtyWrapper events ('summary', 'session-end') and persists
5
+ * Subscribes to wrapper events ('summary', 'session-end') and persists
6
6
  * to PostgreSQL via Drizzle ORM.
7
7
  *
8
- * This decouples PtyWrapper from storage concerns - the wrapper emits events,
8
+ * This decouples wrappers from storage concerns - the wrapper emits events,
9
9
  * this service handles persistence. Different storage backends can be swapped
10
10
  * by implementing alternative persistence services.
11
11
  *
12
- * @see PtyWrapperEvents in src/wrapper/pty-wrapper.ts for event definitions
12
+ * @see RelayPtyOrchestratorEvents in src/wrapper/relay-pty-orchestrator.ts for event definitions
13
13
  */
14
14
  import { eq, and, desc } from 'drizzle-orm';
15
15
  import { getDb } from '../db/drizzle.js';
@@ -23,12 +23,12 @@ import { agentSessions, agentSummaries } from '../db/schema.js';
23
23
  * workspaceId: 'workspace-123',
24
24
  * });
25
25
  *
26
- * // Bind to a PtyWrapper instance
27
- * const pty = new PtyWrapper(config);
28
- * const sessionId = await persistence.bindToPtyWrapper(pty);
26
+ * // Bind to a RelayPtyOrchestrator instance
27
+ * const pty = new RelayPtyOrchestrator(config);
28
+ * const sessionId = await persistence.bindToWrapper(pty);
29
29
  *
30
30
  * // When done, unbind to clean up listeners
31
- * persistence.unbindFromPtyWrapper(pty);
31
+ * persistence.unbindFromWrapper(pty);
32
32
  * ```
33
33
  */
34
34
  export class CloudPersistenceService {
@@ -38,13 +38,13 @@ export class CloudPersistenceService {
38
38
  this.config = config;
39
39
  }
40
40
  /**
41
- * Bind to a PtyWrapper instance and start persisting its events.
41
+ * Bind to a RelayPtyOrchestrator instance and start persisting its events.
42
42
  * Creates a new agent session record and returns the session ID.
43
43
  *
44
- * @param wrapper The PtyWrapper to bind to
44
+ * @param wrapper The RelayPtyOrchestrator to bind to
45
45
  * @returns The session ID for this agent session
46
46
  */
47
- async bindToPtyWrapper(wrapper) {
47
+ async bindToRelayPtyOrchestrator(wrapper) {
48
48
  const db = getDb();
49
49
  const agentName = wrapper.name;
50
50
  // Create session record
@@ -79,11 +79,11 @@ export class CloudPersistenceService {
79
79
  return sessionId;
80
80
  }
81
81
  /**
82
- * Unbind from a PtyWrapper and clean up event listeners.
82
+ * Unbind from a RelayPtyOrchestrator and clean up event listeners.
83
83
  *
84
- * @param wrapper The PtyWrapper to unbind from
84
+ * @param wrapper The RelayPtyOrchestrator to unbind from
85
85
  */
86
- unbindFromPtyWrapper(wrapper) {
86
+ unbindFromRelayPtyOrchestrator(wrapper) {
87
87
  const binding = this.boundWrappers.get(wrapper);
88
88
  if (!binding)
89
89
  return;
@@ -187,7 +187,7 @@ export class CloudPersistenceService {
187
187
  */
188
188
  destroy() {
189
189
  for (const wrapper of this.boundWrappers.keys()) {
190
- this.unbindFromPtyWrapper(wrapper);
190
+ this.unbindFromRelayPtyOrchestrator(wrapper);
191
191
  }
192
192
  }
193
193
  }
@@ -3,11 +3,12 @@
3
3
  * Manages agents across workspaces with integrated resiliency.
4
4
  */
5
5
  import { EventEmitter } from 'events';
6
- import { PtyWrapper, type SummaryEvent, type SessionEndEvent } from '../wrapper/pty-wrapper.js';
6
+ import { RelayPtyOrchestrator } from '../wrapper/relay-pty-orchestrator.js';
7
+ import type { SummaryEvent, SessionEndEvent } from '../wrapper/pty-wrapper.js';
7
8
  import type { Agent, SpawnAgentRequest } from './types.js';
8
9
  /**
9
10
  * Optional cloud persistence handler.
10
- * When set, agent-manager forwards PtyWrapper events to this handler.
11
+ * When set, agent-manager forwards wrapper events to this handler.
11
12
  */
12
13
  export interface CloudPersistenceHandler {
13
14
  onSummary: (agentId: string, event: SummaryEvent) => Promise<void>;
@@ -16,7 +17,7 @@ export interface CloudPersistenceHandler {
16
17
  destroy?: () => void;
17
18
  }
18
19
  interface ManagedAgent extends Agent {
19
- pty?: PtyWrapper;
20
+ pty?: RelayPtyOrchestrator;
20
21
  }
21
22
  export declare class AgentManager extends EventEmitter {
22
23
  private agents;
@@ -26,7 +27,7 @@ export declare class AgentManager extends EventEmitter {
26
27
  private cloudPersistence?;
27
28
  constructor(dataDir: string);
28
29
  /**
29
- * Set cloud persistence handler for forwarding PtyWrapper events.
30
+ * Set cloud persistence handler for forwarding RelayPtyOrchestrator events.
30
31
  * When set, 'summary' and 'session-end' events from agents are forwarded
31
32
  * to the handler for cloud persistence (PostgreSQL/Redis).
32
33
  */
@@ -99,7 +100,7 @@ export declare class AgentManager extends EventEmitter {
99
100
  */
100
101
  private handleAgentExit;
101
102
  /**
102
- * Bind PtyWrapper events to cloud persistence and daemon events.
103
+ * Bind RelayPtyOrchestrator events to cloud persistence and daemon events.
103
104
  *
104
105
  * Events bound:
105
106
  * - 'summary': Agent output a [[SUMMARY]] block
@@ -8,7 +8,7 @@ import { EventEmitter } from 'events';
8
8
  import { createLogger } from '../resiliency/logger.js';
9
9
  import { getSupervisor } from '../resiliency/supervisor.js';
10
10
  import { detectProvider } from '../resiliency/provider-context.js';
11
- import { PtyWrapper } from '../wrapper/pty-wrapper.js';
11
+ import { RelayPtyOrchestrator } from '../wrapper/relay-pty-orchestrator.js';
12
12
  import { resolveCommand } from '../utils/command-resolver.js';
13
13
  const logger = createLogger('agent-manager');
14
14
  function generateId() {
@@ -42,7 +42,7 @@ export class AgentManager extends EventEmitter {
42
42
  logger.info('Agent manager initialized');
43
43
  }
44
44
  /**
45
- * Set cloud persistence handler for forwarding PtyWrapper events.
45
+ * Set cloud persistence handler for forwarding RelayPtyOrchestrator events.
46
46
  * When set, 'summary' and 'session-end' events from agents are forwarded
47
47
  * to the handler for cloud persistence (PostgreSQL/Redis).
48
48
  */
@@ -96,7 +96,9 @@ export class AgentManager extends EventEmitter {
96
96
  command,
97
97
  args,
98
98
  cwd: workspacePath,
99
- logsDir: this.logsDir,
99
+ streamLogs: true,
100
+ // Skip continuity for cloud-managed agents
101
+ skipContinuity: true,
100
102
  env: {
101
103
  CLOUD_API_URL: process.env.CLOUD_API_URL || '',
102
104
  WORKSPACE_TOKEN: process.env.WORKSPACE_TOKEN || '',
@@ -109,11 +111,11 @@ export class AgentManager extends EventEmitter {
109
111
  },
110
112
  };
111
113
  // Create and start PTY
112
- const pty = new PtyWrapper(ptyConfig);
114
+ const pty = new RelayPtyOrchestrator(ptyConfig);
113
115
  await pty.start();
114
116
  agent.pid = pty.pid;
115
117
  agent.pty = pty;
116
- // Subscribe to PtyWrapper events for cloud persistence
118
+ // Subscribe to RelayPtyOrchestrator events for cloud persistence
117
119
  this.bindPtyEvents(agent.id, pty);
118
120
  // Inject initial task
119
121
  if (task && task.trim()) {
@@ -340,7 +342,9 @@ export class AgentManager extends EventEmitter {
340
342
  command,
341
343
  args,
342
344
  cwd: workspacePath,
343
- logsDir: this.logsDir,
345
+ streamLogs: true,
346
+ // Skip continuity for cloud-managed agents
347
+ skipContinuity: true,
344
348
  env: {
345
349
  CLOUD_API_URL: process.env.CLOUD_API_URL || '',
346
350
  WORKSPACE_TOKEN: process.env.WORKSPACE_TOKEN || '',
@@ -351,7 +355,7 @@ export class AgentManager extends EventEmitter {
351
355
  this.handleAgentExit(agent.id, code);
352
356
  },
353
357
  };
354
- const pty = new PtyWrapper(ptyConfig);
358
+ const pty = new RelayPtyOrchestrator(ptyConfig);
355
359
  await pty.start();
356
360
  agent.pid = pty.pid;
357
361
  agent.pty = pty;
@@ -403,7 +407,7 @@ export class AgentManager extends EventEmitter {
403
407
  }
404
408
  }
405
409
  /**
406
- * Bind PtyWrapper events to cloud persistence and daemon events.
410
+ * Bind RelayPtyOrchestrator events to cloud persistence and daemon events.
407
411
  *
408
412
  * Events bound:
409
413
  * - 'summary': Agent output a [[SUMMARY]] block
@@ -0,0 +1,48 @@
1
+ export interface ChannelMembershipRecord {
2
+ channel: string;
3
+ member: string;
4
+ }
5
+ export interface ChannelMembershipStore {
6
+ loadMemberships(): Promise<ChannelMembershipRecord[]>;
7
+ addMember(channel: string, member: string): Promise<void>;
8
+ removeMember(channel: string, member: string): Promise<void>;
9
+ }
10
+ export interface CloudChannelMembershipStoreOptions {
11
+ workspaceId: string;
12
+ databaseUrl: string;
13
+ }
14
+ /**
15
+ * Cloud-backed membership store that uses the channel_members table in Postgres.
16
+ * This is used by the daemon when running in a cloud workspace so membership
17
+ * survives restarts and is shared across processes.
18
+ */
19
+ export declare class CloudChannelMembershipStore implements ChannelMembershipStore {
20
+ private workspaceId;
21
+ private pool;
22
+ constructor(options: CloudChannelMembershipStoreOptions);
23
+ /**
24
+ * Load all memberships for the workspace from Postgres.
25
+ */
26
+ loadMemberships(): Promise<ChannelMembershipRecord[]>;
27
+ /**
28
+ * Add a member to a channel in Postgres (no-op if already present).
29
+ */
30
+ addMember(channel: string, member: string): Promise<void>;
31
+ /**
32
+ * Remove a member from a channel in Postgres.
33
+ */
34
+ removeMember(channel: string, member: string): Promise<void>;
35
+ /**
36
+ * Normalize channel name for DB lookups (strip leading '#' and ignore DMs).
37
+ */
38
+ private normalizeChannelId;
39
+ /**
40
+ * Convert DB channel_id to router/channel format (prepend '#').
41
+ */
42
+ private formatChannelId;
43
+ /**
44
+ * Look up the channel row ID for a workspace/channel_id combination.
45
+ */
46
+ private getChannelRowId;
47
+ }
48
+ //# sourceMappingURL=channel-membership-store.d.ts.map
@@ -0,0 +1,149 @@
1
+ import { Pool } from 'pg';
2
+ import { createLogger } from '../utils/logger.js';
3
+ const log = createLogger('channel-membership-store');
4
+ /**
5
+ * Cloud-backed membership store that uses the channel_members table in Postgres.
6
+ * This is used by the daemon when running in a cloud workspace so membership
7
+ * survives restarts and is shared across processes.
8
+ */
9
+ export class CloudChannelMembershipStore {
10
+ workspaceId;
11
+ pool;
12
+ constructor(options) {
13
+ this.workspaceId = options.workspaceId;
14
+ this.pool = new Pool({
15
+ connectionString: options.databaseUrl,
16
+ ssl: options.databaseUrl.includes('sslmode=require')
17
+ ? { rejectUnauthorized: false }
18
+ : undefined,
19
+ max: 5,
20
+ idleTimeoutMillis: 30_000,
21
+ connectionTimeoutMillis: 10_000,
22
+ });
23
+ }
24
+ /**
25
+ * Load all memberships for the workspace from Postgres.
26
+ */
27
+ async loadMemberships() {
28
+ try {
29
+ const result = await this.pool.query(`
30
+ SELECT c.channel_id AS channel_id, cm.member_id AS member_id
31
+ FROM channel_members cm
32
+ INNER JOIN channels c ON cm.channel_id = c.id
33
+ WHERE c.workspace_id = $1 AND c.status != 'archived'
34
+ `, [this.workspaceId]);
35
+ return result.rows
36
+ .map((row) => ({
37
+ channel: this.formatChannelId(row.channel_id),
38
+ member: row.member_id,
39
+ }))
40
+ .filter((row) => Boolean(row.channel && row.member));
41
+ }
42
+ catch (err) {
43
+ log.error('Failed to load channel memberships from cloud DB', {
44
+ error: err instanceof Error ? err.message : String(err),
45
+ });
46
+ return [];
47
+ }
48
+ }
49
+ /**
50
+ * Add a member to a channel in Postgres (no-op if already present).
51
+ */
52
+ async addMember(channel, member) {
53
+ const normalized = this.normalizeChannelId(channel);
54
+ if (!normalized) {
55
+ return;
56
+ }
57
+ const channelRowId = await this.getChannelRowId(normalized);
58
+ if (!channelRowId) {
59
+ return;
60
+ }
61
+ try {
62
+ await this.pool.query(`
63
+ INSERT INTO channel_members (channel_id, member_id, member_type, role)
64
+ VALUES ($1, $2, 'agent', 'member')
65
+ ON CONFLICT (channel_id, member_id) DO NOTHING
66
+ `, [channelRowId, member]);
67
+ }
68
+ catch (err) {
69
+ log.error('Failed to add channel member in cloud DB', {
70
+ channel: normalized,
71
+ member,
72
+ error: err instanceof Error ? err.message : String(err),
73
+ });
74
+ }
75
+ }
76
+ /**
77
+ * Remove a member from a channel in Postgres.
78
+ */
79
+ async removeMember(channel, member) {
80
+ const normalized = this.normalizeChannelId(channel);
81
+ if (!normalized) {
82
+ return;
83
+ }
84
+ const channelRowId = await this.getChannelRowId(normalized);
85
+ if (!channelRowId) {
86
+ return;
87
+ }
88
+ try {
89
+ await this.pool.query('DELETE FROM channel_members WHERE channel_id = $1 AND member_id = $2', [channelRowId, member]);
90
+ }
91
+ catch (err) {
92
+ log.error('Failed to remove channel member in cloud DB', {
93
+ channel: normalized,
94
+ member,
95
+ error: err instanceof Error ? err.message : String(err),
96
+ });
97
+ }
98
+ }
99
+ /**
100
+ * Normalize channel name for DB lookups (strip leading '#' and ignore DMs).
101
+ */
102
+ normalizeChannelId(channel) {
103
+ if (!channel)
104
+ return null;
105
+ if (channel.startsWith('dm:')) {
106
+ return null; // DM channels are not stored in channel_members
107
+ }
108
+ return channel.startsWith('#') ? channel.slice(1) : channel;
109
+ }
110
+ /**
111
+ * Convert DB channel_id to router/channel format (prepend '#').
112
+ */
113
+ formatChannelId(channelId) {
114
+ if (!channelId)
115
+ return '';
116
+ if (channelId.startsWith('#') || channelId.startsWith('dm:')) {
117
+ return channelId;
118
+ }
119
+ return `#${channelId}`;
120
+ }
121
+ /**
122
+ * Look up the channel row ID for a workspace/channel_id combination.
123
+ */
124
+ async getChannelRowId(channelId) {
125
+ try {
126
+ const result = await this.pool.query(`
127
+ SELECT id FROM channels
128
+ WHERE workspace_id = $1 AND channel_id = $2
129
+ LIMIT 1
130
+ `, [this.workspaceId, channelId]);
131
+ if (!result.rows[0]?.id) {
132
+ log.warn('Channel not found in cloud DB for membership update', {
133
+ workspaceId: this.workspaceId,
134
+ channelId,
135
+ });
136
+ return null;
137
+ }
138
+ return result.rows[0].id;
139
+ }
140
+ catch (err) {
141
+ log.error('Failed to look up channel row in cloud DB', {
142
+ channelId,
143
+ error: err instanceof Error ? err.message : String(err),
144
+ });
145
+ return null;
146
+ }
147
+ }
148
+ }
149
+ //# sourceMappingURL=channel-membership-store.js.map
@@ -76,6 +76,8 @@ export declare class CloudSyncService extends EventEmitter {
76
76
  updateAgents(agents: Array<{
77
77
  name: string;
78
78
  status: string;
79
+ isHuman?: boolean;
80
+ avatarUrl?: string;
79
81
  }>): void;
80
82
  /**
81
83
  * Get all remote agents (from other machines)
@@ -175,6 +175,8 @@ export class CloudSyncService extends EventEmitter {
175
175
  const agents = Array.from(this.localAgents.entries()).map(([name, info]) => ({
176
176
  name,
177
177
  status: info.status,
178
+ isHuman: info.isHuman,
179
+ avatarUrl: info.avatarUrl,
178
180
  }));
179
181
  const response = await fetch(`${this.config.cloudUrl}/api/daemons/heartbeat`, {
180
182
  method: 'POST',
@@ -224,6 +226,8 @@ export class CloudSyncService extends EventEmitter {
224
226
  const agents = Array.from(this.localAgents.entries()).map(([name, info]) => ({
225
227
  name,
226
228
  status: info.status,
229
+ isHuman: info.isHuman,
230
+ avatarUrl: info.avatarUrl,
227
231
  }));
228
232
  const response = await fetch(`${this.config.cloudUrl}/api/daemons/agents`, {
229
233
  method: 'POST',
@@ -300,7 +300,8 @@ export class Connection {
300
300
  * Returns false if the connection is closed or the queue is full.
301
301
  */
302
302
  send(envelope) {
303
- if (this._state === 'CLOSED' || this._state === 'ERROR') {
303
+ if (this._state === 'CLOSED' || this._state === 'ERROR' || this._state === 'CLOSING') {
304
+ console.log(`[connection] Send to ${this._agentName} blocked - state: ${this._state}`);
304
305
  return false;
305
306
  }
306
307
  const maxQueueSize = this.config.maxWriteQueueSize ?? 2000;
@@ -343,7 +344,7 @@ export class Connection {
343
344
  */
344
345
  drain() {
345
346
  while (this.writeQueue.length > 0) {
346
- if (this._state === 'CLOSED' || this._state === 'ERROR') {
347
+ if (this._state === 'CLOSED' || this._state === 'ERROR' || this._state === 'CLOSING') {
347
348
  this.draining = false;
348
349
  return;
349
350
  }
@@ -417,13 +418,20 @@ export class Connection {
417
418
  if (this._state === 'CLOSED' || this._state === 'CLOSING')
418
419
  return;
419
420
  this._state = 'CLOSING';
420
- this.send({
421
- v: PROTOCOL_VERSION,
422
- type: 'BYE',
423
- id: generateId(),
424
- ts: Date.now(),
425
- payload: {},
426
- });
421
+ // Write BYE message directly (not via queue) to avoid race with socket.end()
422
+ try {
423
+ const byeFrame = encodeFrame({
424
+ v: PROTOCOL_VERSION,
425
+ type: 'BYE',
426
+ id: generateId(),
427
+ ts: Date.now(),
428
+ payload: {},
429
+ });
430
+ this.socket.write(byeFrame);
431
+ }
432
+ catch {
433
+ // Ignore write errors during close
434
+ }
427
435
  this.socket.end();
428
436
  }
429
437
  }
@@ -8,6 +8,7 @@ import type { StorageAdapter } from '../storage/adapter.js';
8
8
  import type { AgentRegistry } from './agent-registry.js';
9
9
  import { type RateLimitConfig } from './rate-limiter.js';
10
10
  import { type DeliveryReliabilityOptions } from './delivery-tracker.js';
11
+ import type { ChannelMembershipStore } from './channel-membership-store.js';
11
12
  export interface RoutableConnection {
12
13
  id: string;
13
14
  agentName?: string;
@@ -40,6 +41,7 @@ interface ShadowRelationship extends ShadowConfig {
40
41
  }
41
42
  export declare class Router {
42
43
  private storage?;
44
+ private channelMembershipStore?;
43
45
  private connections;
44
46
  private agents;
45
47
  private subscriptions;
@@ -71,7 +73,12 @@ export declare class Router {
71
73
  crossMachineHandler?: CrossMachineHandler;
72
74
  /** Rate limit configuration. Set to null to disable rate limiting. */
73
75
  rateLimit?: Partial<RateLimitConfig> | null;
76
+ channelMembershipStore?: ChannelMembershipStore;
74
77
  });
78
+ /**
79
+ * Restore channel memberships from persisted storage.
80
+ */
81
+ restoreChannelMemberships(): Promise<void>;
75
82
  /**
76
83
  * Set or update the cross-machine handler.
77
84
  */
@@ -229,11 +236,14 @@ export declare class Router {
229
236
  /**
230
237
  * Handle a CHANNEL_JOIN message.
231
238
  * Adds the member to the channel and notifies existing members.
239
+ * If payload.member is set, adds that member (admin mode).
240
+ * Otherwise, adds the connection's agent name.
232
241
  */
233
242
  handleChannelJoin(connection: RoutableConnection, envelope: Envelope<ChannelJoinPayload>): void;
234
243
  /**
235
244
  * Handle a CHANNEL_LEAVE message.
236
245
  * Removes the member from the channel and notifies remaining members.
246
+ * If payload.member is provided, removes that member instead (admin mode).
237
247
  */
238
248
  handleChannelLeave(connection: RoutableConnection, envelope: Envelope<ChannelLeavePayload>): void;
239
249
  /**
@@ -244,6 +254,7 @@ export declare class Router {
244
254
  * Persist a channel message to storage.
245
255
  */
246
256
  private persistChannelMessage;
257
+ private persistChannelMembership;
247
258
  /**
248
259
  * Get all members of a channel.
249
260
  */
@@ -270,8 +281,34 @@ export declare class Router {
270
281
  getUsers(): string[];
271
282
  /**
272
283
  * Get a connection by name (checks both agents and users).
284
+ * Uses case-insensitive lookup to handle mismatched casing.
273
285
  */
274
286
  private getConnectionByName;
287
+ /**
288
+ * Check if a member is in a Set (case-insensitive).
289
+ * Returns the actual stored name if found, undefined otherwise.
290
+ */
291
+ private findMemberInSet;
292
+ /**
293
+ * Check if two names match (case-insensitive).
294
+ */
295
+ private namesMatch;
296
+ /**
297
+ * Auto-join a member to a channel without notifications.
298
+ * Used for default channel membership (e.g., #general).
299
+ * @param memberName - The agent or user name to add
300
+ * @param channel - The channel to join (e.g., '#general')
301
+ */
302
+ autoJoinChannel(memberName: string, channel: string, options?: {
303
+ persist?: boolean;
304
+ }): void;
305
+ private addChannelMember;
306
+ private removeChannelMember;
307
+ handleMembershipUpdate(update: {
308
+ channel: string;
309
+ member: string;
310
+ action: 'join' | 'leave' | 'invite';
311
+ }): void;
275
312
  }
276
313
  export {};
277
314
  //# sourceMappingURL=router.d.ts.map