agent-relay 1.1.0 → 1.2.3

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 (567) hide show
  1. package/.gitattributes +3 -0
  2. package/.nvmrc +1 -0
  3. package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.json +65 -0
  4. package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.md +37 -0
  5. package/.trajectories/completed/2026-01/traj_1k5if5snst2e.json +65 -0
  6. package/.trajectories/completed/2026-01/traj_1k5if5snst2e.md +37 -0
  7. package/.trajectories/completed/2026-01/traj_1rp3rges5811.json +49 -0
  8. package/.trajectories/completed/2026-01/traj_1rp3rges5811.md +31 -0
  9. package/.trajectories/completed/2026-01/traj_22bhyulruouw.json +113 -0
  10. package/.trajectories/completed/2026-01/traj_22bhyulruouw.md +57 -0
  11. package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.json +53 -0
  12. package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.md +32 -0
  13. package/.trajectories/completed/2026-01/traj_3t0440mjeunc.json +26 -0
  14. package/.trajectories/completed/2026-01/traj_3t0440mjeunc.md +6 -0
  15. package/.trajectories/completed/2026-01/traj_45x9494d9xnr.json +47 -0
  16. package/.trajectories/completed/2026-01/traj_45x9494d9xnr.md +32 -0
  17. package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.json +53 -0
  18. package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.md +32 -0
  19. package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.json +59 -0
  20. package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.md +33 -0
  21. package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.json +53 -0
  22. package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.md +32 -0
  23. package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.json +48 -0
  24. package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.md +24 -0
  25. package/.trajectories/completed/2026-01/traj_7ludwvz45veh.json +209 -0
  26. package/.trajectories/completed/2026-01/traj_7ludwvz45veh.md +97 -0
  27. package/.trajectories/completed/2026-01/traj_9921cuhel0pj.json +48 -0
  28. package/.trajectories/completed/2026-01/traj_9921cuhel0pj.md +24 -0
  29. package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.json +49 -0
  30. package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.md +23 -0
  31. package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.json +53 -0
  32. package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.md +32 -0
  33. package/.trajectories/completed/2026-01/traj_cxofprm2m2en.json +49 -0
  34. package/.trajectories/completed/2026-01/traj_cxofprm2m2en.md +31 -0
  35. package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.json +26 -0
  36. package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.md +6 -0
  37. package/.trajectories/completed/2026-01/traj_dfuvww9pege5.json +59 -0
  38. package/.trajectories/completed/2026-01/traj_dfuvww9pege5.md +37 -0
  39. package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.json +77 -0
  40. package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.md +42 -0
  41. package/.trajectories/completed/2026-01/traj_gjdre5voouod.json +53 -0
  42. package/.trajectories/completed/2026-01/traj_gjdre5voouod.md +32 -0
  43. package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.json +25 -0
  44. package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.md +15 -0
  45. package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.json +101 -0
  46. package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.md +44 -0
  47. package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.json +22 -0
  48. package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.md +5 -0
  49. package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.json +53 -0
  50. package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.md +32 -0
  51. package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.json +25 -0
  52. package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.md +15 -0
  53. package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.json +53 -0
  54. package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.md +32 -0
  55. package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.json +53 -0
  56. package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.md +32 -0
  57. package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.json +48 -0
  58. package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.md +24 -0
  59. package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.json +53 -0
  60. package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.md +32 -0
  61. package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.json +77 -0
  62. package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.md +42 -0
  63. package/.trajectories/completed/2026-01/traj_qft54mi7nfor.json +53 -0
  64. package/.trajectories/completed/2026-01/traj_qft54mi7nfor.md +32 -0
  65. package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.json +83 -0
  66. package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.md +47 -0
  67. package/.trajectories/completed/2026-01/traj_rd9toccj18a0.json +59 -0
  68. package/.trajectories/completed/2026-01/traj_rd9toccj18a0.md +37 -0
  69. package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.json +48 -0
  70. package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.md +16 -0
  71. package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.json +59 -0
  72. package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.md +37 -0
  73. package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.json +53 -0
  74. package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.md +32 -0
  75. package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.json +84 -0
  76. package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.md +109 -0
  77. package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.json +53 -0
  78. package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.md +32 -0
  79. package/.trajectories/completed/2026-01/traj_v87hypnongqx.json +71 -0
  80. package/.trajectories/completed/2026-01/traj_v87hypnongqx.md +42 -0
  81. package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.json +53 -0
  82. package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.md +32 -0
  83. package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.json +20 -0
  84. package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.md +6 -0
  85. package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.json +175 -0
  86. package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.md +82 -0
  87. package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.json +47 -0
  88. package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.md +32 -0
  89. package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.json +59 -0
  90. package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.md +37 -0
  91. package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.json +53 -0
  92. package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.md +32 -0
  93. package/.trajectories/index.json +314 -0
  94. package/ARCHITECTURE.md +1245 -0
  95. package/README.md +1 -1
  96. package/TESTING.md +278 -0
  97. package/deploy/init-db.sql +5 -0
  98. package/deploy/scripts/setup-fly-workspaces.sh +69 -0
  99. package/deploy/scripts/setup-railway.sh +75 -0
  100. package/deploy/workspace/entrypoint-browser.sh +118 -0
  101. package/deploy/workspace/entrypoint.sh +348 -0
  102. package/deploy/workspace/git-credential-relay +111 -0
  103. package/dist/bridge/spawner.d.ts +53 -0
  104. package/dist/bridge/spawner.js +203 -19
  105. package/dist/bridge/types.d.ts +12 -0
  106. package/dist/cli/index.js +618 -5
  107. package/dist/cloud/api/auth.d.ts +3 -2
  108. package/dist/cloud/api/auth.js +10 -98
  109. package/dist/cloud/api/billing.js +30 -9
  110. package/dist/cloud/api/cli-pty-runner.d.ts +54 -0
  111. package/dist/cloud/api/cli-pty-runner.js +119 -0
  112. package/dist/cloud/api/codex-auth-helper.d.ts +15 -0
  113. package/dist/cloud/api/codex-auth-helper.js +100 -0
  114. package/dist/cloud/api/generic-webhooks.d.ts +8 -0
  115. package/dist/cloud/api/generic-webhooks.js +129 -0
  116. package/dist/cloud/api/git.d.ts +8 -0
  117. package/dist/cloud/api/git.js +152 -0
  118. package/dist/cloud/api/github-app.d.ts +11 -0
  119. package/dist/cloud/api/github-app.js +189 -0
  120. package/dist/cloud/api/middleware/planLimits.d.ts +7 -0
  121. package/dist/cloud/api/middleware/planLimits.js +39 -1
  122. package/dist/cloud/api/monitoring.d.ts +11 -0
  123. package/dist/cloud/api/monitoring.js +578 -0
  124. package/dist/cloud/api/nango-auth.d.ts +9 -0
  125. package/dist/cloud/api/nango-auth.js +377 -0
  126. package/dist/cloud/api/onboarding.d.ts +8 -1
  127. package/dist/cloud/api/onboarding.js +313 -119
  128. package/dist/cloud/api/policy.d.ts +8 -0
  129. package/dist/cloud/api/policy.js +229 -0
  130. package/dist/cloud/api/providers.js +114 -42
  131. package/dist/cloud/api/repos.d.ts +1 -0
  132. package/dist/cloud/api/repos.js +186 -0
  133. package/dist/cloud/api/test-helpers.d.ts +10 -0
  134. package/dist/cloud/api/test-helpers.js +575 -0
  135. package/dist/cloud/api/webhooks.d.ts +8 -0
  136. package/dist/cloud/api/webhooks.js +645 -0
  137. package/dist/cloud/api/workspaces.js +320 -12
  138. package/dist/cloud/billing/plans.js +32 -19
  139. package/dist/cloud/billing/types.d.ts +9 -3
  140. package/dist/cloud/config.d.ts +9 -2
  141. package/dist/cloud/config.js +13 -4
  142. package/dist/cloud/db/drizzle.d.ts +84 -1
  143. package/dist/cloud/db/drizzle.js +470 -0
  144. package/dist/cloud/db/index.d.ts +9 -4
  145. package/dist/cloud/db/index.js +11 -3
  146. package/dist/cloud/db/schema.d.ts +3283 -556
  147. package/dist/cloud/db/schema.js +314 -1
  148. package/dist/cloud/index.d.ts +1 -0
  149. package/dist/cloud/index.js +2 -0
  150. package/dist/cloud/provisioner/index.d.ts +56 -0
  151. package/dist/cloud/provisioner/index.js +676 -34
  152. package/dist/cloud/server.d.ts +1 -0
  153. package/dist/cloud/server.js +362 -13
  154. package/dist/cloud/services/auto-scaler.d.ts +152 -0
  155. package/dist/cloud/services/auto-scaler.js +439 -0
  156. package/dist/cloud/services/capacity-manager.d.ts +148 -0
  157. package/dist/cloud/services/capacity-manager.js +449 -0
  158. package/dist/cloud/services/ci-agent-spawner.d.ts +49 -0
  159. package/dist/cloud/services/ci-agent-spawner.js +373 -0
  160. package/dist/cloud/services/index.d.ts +12 -0
  161. package/dist/cloud/services/index.js +15 -0
  162. package/dist/cloud/services/mention-handler.d.ts +65 -0
  163. package/dist/cloud/services/mention-handler.js +405 -0
  164. package/dist/cloud/services/nango.d.ts +186 -0
  165. package/dist/cloud/services/nango.js +344 -0
  166. package/dist/cloud/services/persistence.d.ts +131 -0
  167. package/dist/cloud/services/persistence.js +200 -0
  168. package/dist/cloud/services/planLimits.d.ts +37 -0
  169. package/dist/cloud/services/planLimits.js +86 -5
  170. package/dist/cloud/services/scaling-orchestrator.d.ts +159 -0
  171. package/dist/cloud/services/scaling-orchestrator.js +502 -0
  172. package/dist/cloud/services/scaling-policy.d.ts +121 -0
  173. package/dist/cloud/services/scaling-policy.js +415 -0
  174. package/dist/cloud/vault/index.js +1 -1
  175. package/dist/cloud/webhooks/index.d.ts +24 -0
  176. package/dist/cloud/webhooks/index.js +29 -0
  177. package/dist/cloud/webhooks/parsers/github.d.ts +8 -0
  178. package/dist/cloud/webhooks/parsers/github.js +234 -0
  179. package/dist/cloud/webhooks/parsers/index.d.ts +23 -0
  180. package/dist/cloud/webhooks/parsers/index.js +30 -0
  181. package/dist/cloud/webhooks/parsers/linear.d.ts +9 -0
  182. package/dist/cloud/webhooks/parsers/linear.js +258 -0
  183. package/dist/cloud/webhooks/parsers/slack.d.ts +9 -0
  184. package/dist/cloud/webhooks/parsers/slack.js +214 -0
  185. package/dist/cloud/webhooks/responders/github.d.ts +8 -0
  186. package/dist/cloud/webhooks/responders/github.js +73 -0
  187. package/dist/cloud/webhooks/responders/index.d.ts +23 -0
  188. package/dist/cloud/webhooks/responders/index.js +30 -0
  189. package/dist/cloud/webhooks/responders/linear.d.ts +9 -0
  190. package/dist/cloud/webhooks/responders/linear.js +149 -0
  191. package/dist/cloud/webhooks/responders/slack.d.ts +20 -0
  192. package/dist/cloud/webhooks/responders/slack.js +178 -0
  193. package/dist/cloud/webhooks/router.d.ts +25 -0
  194. package/dist/cloud/webhooks/router.js +504 -0
  195. package/dist/cloud/webhooks/rules-engine.d.ts +24 -0
  196. package/dist/cloud/webhooks/rules-engine.js +287 -0
  197. package/dist/cloud/webhooks/types.d.ts +186 -0
  198. package/dist/cloud/webhooks/types.js +8 -0
  199. package/dist/continuity/formatter.d.ts +51 -0
  200. package/dist/continuity/formatter.js +313 -0
  201. package/dist/continuity/handoff-store.d.ts +67 -0
  202. package/dist/continuity/handoff-store.js +472 -0
  203. package/dist/continuity/index.d.ts +45 -0
  204. package/dist/continuity/index.js +48 -0
  205. package/dist/continuity/ledger-store.d.ts +110 -0
  206. package/dist/continuity/ledger-store.js +500 -0
  207. package/dist/continuity/manager.d.ts +178 -0
  208. package/dist/continuity/manager.js +562 -0
  209. package/dist/continuity/parser.d.ts +76 -0
  210. package/dist/continuity/parser.js +579 -0
  211. package/dist/continuity/types.d.ts +180 -0
  212. package/dist/continuity/types.js +9 -0
  213. package/dist/daemon/agent-manager.d.ts +27 -0
  214. package/dist/daemon/agent-manager.js +107 -6
  215. package/dist/daemon/agent-registry.d.ts +32 -0
  216. package/dist/daemon/agent-registry.js +42 -2
  217. package/dist/daemon/api.d.ts +12 -0
  218. package/dist/daemon/api.js +131 -2
  219. package/dist/daemon/cli-auth.d.ts +67 -0
  220. package/dist/daemon/cli-auth.js +537 -0
  221. package/dist/daemon/cloud-sync.js +9 -7
  222. package/dist/daemon/orchestrator.js +30 -0
  223. package/dist/daemon/router.d.ts +5 -0
  224. package/dist/daemon/router.js +78 -26
  225. package/dist/daemon/server.d.ts +5 -0
  226. package/dist/daemon/server.js +9 -1
  227. package/dist/daemon/services/browser-testing.d.ts +88 -0
  228. package/dist/daemon/services/browser-testing.js +244 -0
  229. package/dist/daemon/services/container-spawner.d.ts +135 -0
  230. package/dist/daemon/services/container-spawner.js +313 -0
  231. package/dist/daemon/types.d.ts +5 -1
  232. package/dist/dashboard/out/404.html +1 -1
  233. package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +1 -0
  234. package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
  235. package/dist/dashboard/out/_next/static/chunks/699-3b1cd6618a45d259.js +1 -0
  236. package/dist/dashboard/out/_next/static/chunks/724-2dae7627550ab88f.js +9 -0
  237. package/dist/dashboard/out/_next/static/chunks/766-1f2dd8cb7f766b0b.js +1 -0
  238. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-3fdfa60e53f2810d.js +1 -0
  239. package/dist/dashboard/out/_next/static/chunks/app/app/page-e6381e5a6e1fbcfd.js +1 -0
  240. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-3538dfe0ffe984b8.js +1 -0
  241. package/dist/dashboard/out/_next/static/chunks/app/history/{page-b6edd4dde8d08194.js → page-abb9ab2d329f56e9.js} +1 -1
  242. package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
  243. package/dist/dashboard/out/_next/static/chunks/app/login/page-c22d080201cbd9fb.js +1 -0
  244. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-67a3e98d9a43a6ed.js +1 -0
  245. package/dist/dashboard/out/_next/static/chunks/app/page-77e9c65420a06cfb.js +1 -0
  246. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-b08ed1c34d14434a.js +1 -0
  247. package/dist/dashboard/out/_next/static/chunks/app/providers/page-e88bc117ef7671c3.js +1 -0
  248. package/dist/dashboard/out/_next/static/chunks/app/signup/page-68d34f50baa8ab6b.js +1 -0
  249. package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
  250. package/dist/dashboard/out/_next/static/chunks/{main-app-5d692157a8eb1fd9.js → main-app-6e8e8d3ef4e0192a.js} +1 -1
  251. package/dist/dashboard/out/_next/static/chunks/{main-c2f423b9c9f4591b.js → main-ed4e1fb6f29c34cf.js} +1 -1
  252. package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
  253. package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +1 -0
  254. package/dist/dashboard/out/_next/static/css/7c3ae9e8617d42a5.css +1 -0
  255. package/dist/dashboard/out/app/onboarding.html +1 -0
  256. package/dist/dashboard/out/app/onboarding.txt +7 -0
  257. package/dist/dashboard/out/app.html +1 -14
  258. package/dist/dashboard/out/app.txt +2 -2
  259. package/dist/dashboard/out/connect-repos.html +1 -0
  260. package/dist/dashboard/out/connect-repos.txt +7 -0
  261. package/dist/dashboard/out/history.html +1 -1
  262. package/dist/dashboard/out/history.txt +2 -2
  263. package/dist/dashboard/out/index.html +1 -1
  264. package/dist/dashboard/out/index.txt +2 -2
  265. package/dist/dashboard/out/login.html +6 -0
  266. package/dist/dashboard/out/login.txt +7 -0
  267. package/dist/dashboard/out/metrics.html +1 -1
  268. package/dist/dashboard/out/metrics.txt +2 -2
  269. package/dist/dashboard/out/pricing.html +3 -3
  270. package/dist/dashboard/out/pricing.txt +2 -2
  271. package/dist/dashboard/out/providers.html +1 -0
  272. package/dist/dashboard/out/providers.txt +7 -0
  273. package/dist/dashboard/out/signup.html +6 -0
  274. package/dist/dashboard/out/signup.txt +7 -0
  275. package/dist/dashboard-server/server.js +1308 -8
  276. package/dist/hooks/emitter.d.ts +40 -0
  277. package/dist/hooks/emitter.js +63 -0
  278. package/dist/hooks/index.d.ts +3 -0
  279. package/dist/hooks/index.js +3 -0
  280. package/dist/hooks/registry.d.ts +173 -0
  281. package/dist/hooks/registry.js +476 -0
  282. package/dist/hooks/trajectory-hooks.d.ts +52 -0
  283. package/dist/hooks/trajectory-hooks.js +183 -0
  284. package/dist/hooks/types.d.ts +141 -0
  285. package/dist/index.d.ts +2 -0
  286. package/dist/index.js +3 -0
  287. package/dist/memory/adapters/index.d.ts +8 -0
  288. package/dist/memory/adapters/index.js +8 -0
  289. package/dist/memory/adapters/inmemory.d.ts +59 -0
  290. package/dist/memory/adapters/inmemory.js +195 -0
  291. package/dist/memory/adapters/supermemory.d.ts +71 -0
  292. package/dist/memory/adapters/supermemory.js +338 -0
  293. package/dist/memory/factory.d.ts +48 -0
  294. package/dist/memory/factory.js +143 -0
  295. package/dist/memory/index.d.ts +32 -0
  296. package/dist/memory/index.js +32 -0
  297. package/dist/memory/memory-hooks.d.ts +60 -0
  298. package/dist/memory/memory-hooks.js +313 -0
  299. package/dist/memory/service.d.ts +49 -0
  300. package/dist/memory/service.js +146 -0
  301. package/dist/memory/types.d.ts +195 -0
  302. package/dist/memory/types.js +8 -0
  303. package/dist/policy/agent-policy.d.ts +225 -0
  304. package/dist/policy/agent-policy.js +665 -0
  305. package/dist/policy/cloud-policy-fetcher.d.ts +12 -0
  306. package/dist/policy/cloud-policy-fetcher.js +64 -0
  307. package/dist/resiliency/crash-insights.d.ts +156 -0
  308. package/dist/resiliency/crash-insights.js +492 -0
  309. package/dist/resiliency/gossip-health.d.ts +137 -0
  310. package/dist/resiliency/gossip-health.js +241 -0
  311. package/dist/resiliency/index.d.ts +5 -0
  312. package/dist/resiliency/index.js +5 -0
  313. package/dist/resiliency/leader-watchdog.d.ts +109 -0
  314. package/dist/resiliency/leader-watchdog.js +189 -0
  315. package/dist/resiliency/memory-monitor.d.ts +172 -0
  316. package/dist/resiliency/memory-monitor.js +593 -0
  317. package/dist/resiliency/stateless-lead.d.ts +149 -0
  318. package/dist/resiliency/stateless-lead.js +308 -0
  319. package/dist/resiliency/supervisor.d.ts +38 -0
  320. package/dist/resiliency/supervisor.js +122 -0
  321. package/dist/shared/cli-auth-config.d.ts +91 -0
  322. package/dist/shared/cli-auth-config.js +264 -0
  323. package/dist/storage/adapter.d.ts +1 -1
  324. package/dist/trajectory/config.d.ts +84 -0
  325. package/dist/trajectory/config.js +163 -0
  326. package/dist/trajectory/index.d.ts +8 -0
  327. package/dist/trajectory/index.js +8 -0
  328. package/dist/trajectory/integration.d.ts +292 -0
  329. package/dist/trajectory/integration.js +834 -0
  330. package/dist/utils/logger.js +1 -1
  331. package/dist/utils/project-namespace.d.ts +24 -0
  332. package/dist/utils/project-namespace.js +84 -0
  333. package/dist/wrapper/parser.d.ts +10 -0
  334. package/dist/wrapper/parser.js +100 -33
  335. package/dist/wrapper/pty-wrapper.d.ts +197 -16
  336. package/dist/wrapper/pty-wrapper.js +943 -106
  337. package/dist/wrapper/shared.d.ts +165 -0
  338. package/dist/wrapper/shared.js +270 -0
  339. package/dist/wrapper/tmux-wrapper.d.ts +73 -11
  340. package/dist/wrapper/tmux-wrapper.js +541 -120
  341. package/package.json +16 -16
  342. package/scripts/postinstall.js +60 -0
  343. package/test-push.txt +1 -0
  344. package/bin/tmux +0 -0
  345. package/dist/bridge/config.d.ts.map +0 -1
  346. package/dist/bridge/config.js.map +0 -1
  347. package/dist/bridge/index.d.ts.map +0 -1
  348. package/dist/bridge/index.js.map +0 -1
  349. package/dist/bridge/multi-project-client.d.ts.map +0 -1
  350. package/dist/bridge/multi-project-client.js.map +0 -1
  351. package/dist/bridge/shadow-cli.d.ts.map +0 -1
  352. package/dist/bridge/shadow-cli.js.map +0 -1
  353. package/dist/bridge/shadow-config.d.ts.map +0 -1
  354. package/dist/bridge/shadow-config.js.map +0 -1
  355. package/dist/bridge/spawner.d.ts.map +0 -1
  356. package/dist/bridge/spawner.js.map +0 -1
  357. package/dist/bridge/teams-config.d.ts.map +0 -1
  358. package/dist/bridge/teams-config.js.map +0 -1
  359. package/dist/bridge/types.d.ts.map +0 -1
  360. package/dist/bridge/types.js.map +0 -1
  361. package/dist/bridge/utils.d.ts.map +0 -1
  362. package/dist/bridge/utils.js.map +0 -1
  363. package/dist/cli/index.d.ts.map +0 -1
  364. package/dist/cli/index.js.map +0 -1
  365. package/dist/cloud/api/auth.d.ts.map +0 -1
  366. package/dist/cloud/api/auth.js.map +0 -1
  367. package/dist/cloud/api/billing.d.ts.map +0 -1
  368. package/dist/cloud/api/billing.js.map +0 -1
  369. package/dist/cloud/api/coordinators.d.ts.map +0 -1
  370. package/dist/cloud/api/coordinators.js.map +0 -1
  371. package/dist/cloud/api/daemons.d.ts.map +0 -1
  372. package/dist/cloud/api/daemons.js.map +0 -1
  373. package/dist/cloud/api/middleware/planLimits.d.ts.map +0 -1
  374. package/dist/cloud/api/middleware/planLimits.js.map +0 -1
  375. package/dist/cloud/api/onboarding.d.ts.map +0 -1
  376. package/dist/cloud/api/onboarding.js.map +0 -1
  377. package/dist/cloud/api/providers.d.ts.map +0 -1
  378. package/dist/cloud/api/providers.js.map +0 -1
  379. package/dist/cloud/api/repos.d.ts.map +0 -1
  380. package/dist/cloud/api/repos.js.map +0 -1
  381. package/dist/cloud/api/teams.d.ts.map +0 -1
  382. package/dist/cloud/api/teams.js.map +0 -1
  383. package/dist/cloud/api/usage.d.ts.map +0 -1
  384. package/dist/cloud/api/usage.js.map +0 -1
  385. package/dist/cloud/api/workspaces.d.ts.map +0 -1
  386. package/dist/cloud/api/workspaces.js.map +0 -1
  387. package/dist/cloud/billing/index.d.ts.map +0 -1
  388. package/dist/cloud/billing/index.js.map +0 -1
  389. package/dist/cloud/billing/plans.d.ts.map +0 -1
  390. package/dist/cloud/billing/plans.js.map +0 -1
  391. package/dist/cloud/billing/service.d.ts.map +0 -1
  392. package/dist/cloud/billing/service.js.map +0 -1
  393. package/dist/cloud/billing/types.d.ts.map +0 -1
  394. package/dist/cloud/billing/types.js.map +0 -1
  395. package/dist/cloud/config.d.ts.map +0 -1
  396. package/dist/cloud/config.js.map +0 -1
  397. package/dist/cloud/db/drizzle.d.ts.map +0 -1
  398. package/dist/cloud/db/drizzle.js.map +0 -1
  399. package/dist/cloud/db/index.d.ts.map +0 -1
  400. package/dist/cloud/db/index.js.map +0 -1
  401. package/dist/cloud/db/schema.d.ts.map +0 -1
  402. package/dist/cloud/db/schema.js.map +0 -1
  403. package/dist/cloud/index.d.ts.map +0 -1
  404. package/dist/cloud/index.js.map +0 -1
  405. package/dist/cloud/provisioner/index.d.ts.map +0 -1
  406. package/dist/cloud/provisioner/index.js.map +0 -1
  407. package/dist/cloud/server.d.ts.map +0 -1
  408. package/dist/cloud/server.js.map +0 -1
  409. package/dist/cloud/services/coordinator.d.ts.map +0 -1
  410. package/dist/cloud/services/coordinator.js.map +0 -1
  411. package/dist/cloud/services/planLimits.d.ts.map +0 -1
  412. package/dist/cloud/services/planLimits.js.map +0 -1
  413. package/dist/cloud/vault/index.d.ts.map +0 -1
  414. package/dist/cloud/vault/index.js.map +0 -1
  415. package/dist/daemon/agent-manager.d.ts.map +0 -1
  416. package/dist/daemon/agent-manager.js.map +0 -1
  417. package/dist/daemon/agent-registry.d.ts.map +0 -1
  418. package/dist/daemon/agent-registry.js.map +0 -1
  419. package/dist/daemon/api.d.ts.map +0 -1
  420. package/dist/daemon/api.js.map +0 -1
  421. package/dist/daemon/auth.d.ts.map +0 -1
  422. package/dist/daemon/auth.js.map +0 -1
  423. package/dist/daemon/cloud-sync.d.ts.map +0 -1
  424. package/dist/daemon/cloud-sync.js.map +0 -1
  425. package/dist/daemon/connection.d.ts.map +0 -1
  426. package/dist/daemon/connection.js.map +0 -1
  427. package/dist/daemon/index.d.ts.map +0 -1
  428. package/dist/daemon/index.js.map +0 -1
  429. package/dist/daemon/orchestrator.d.ts.map +0 -1
  430. package/dist/daemon/orchestrator.js.map +0 -1
  431. package/dist/daemon/registry.d.ts.map +0 -1
  432. package/dist/daemon/registry.js.map +0 -1
  433. package/dist/daemon/router.d.ts.map +0 -1
  434. package/dist/daemon/router.js.map +0 -1
  435. package/dist/daemon/server.d.ts.map +0 -1
  436. package/dist/daemon/server.js.map +0 -1
  437. package/dist/daemon/types.d.ts.map +0 -1
  438. package/dist/daemon/types.js.map +0 -1
  439. package/dist/daemon/workspace-manager.d.ts.map +0 -1
  440. package/dist/daemon/workspace-manager.js.map +0 -1
  441. package/dist/dashboard/out/_next/static/chunks/693-7b3301d8f6bc5014.js +0 -1
  442. package/dist/dashboard/out/_next/static/chunks/713-f78477eb185f1f4d.js +0 -1
  443. package/dist/dashboard/out/_next/static/chunks/766-e53e1cfe39b0b5b5.js +0 -1
  444. package/dist/dashboard/out/_next/static/chunks/900-037c64bfd797fb2a.js +0 -1
  445. package/dist/dashboard/out/_next/static/chunks/app/app/page-e3d9e1f4466b9bae.js +0 -1
  446. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
  447. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-e68825a81db67ba1.js +0 -1
  448. package/dist/dashboard/out/_next/static/chunks/app/page-cc108bf68c8a657f.js +0 -1
  449. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-d80e03a5297f95b6.js +0 -1
  450. package/dist/dashboard/out/_next/static/chunks/webpack-a5acc2831d094776.js +0 -1
  451. package/dist/dashboard/out/_next/static/css/79b80143647a07d7.css +0 -1
  452. package/dist/dashboard/out/_next/static/css/8cf277370ad48cfe.css +0 -1
  453. package/dist/dashboard-server/metrics.d.ts.map +0 -1
  454. package/dist/dashboard-server/metrics.js.map +0 -1
  455. package/dist/dashboard-server/needs-attention.d.ts.map +0 -1
  456. package/dist/dashboard-server/needs-attention.js.map +0 -1
  457. package/dist/dashboard-server/server.d.ts.map +0 -1
  458. package/dist/dashboard-server/server.js.map +0 -1
  459. package/dist/dashboard-server/start.d.ts.map +0 -1
  460. package/dist/dashboard-server/start.js.map +0 -1
  461. package/dist/hooks/inbox-check/hook.d.ts.map +0 -1
  462. package/dist/hooks/inbox-check/hook.js.map +0 -1
  463. package/dist/hooks/inbox-check/index.d.ts.map +0 -1
  464. package/dist/hooks/inbox-check/index.js.map +0 -1
  465. package/dist/hooks/inbox-check/types.d.ts.map +0 -1
  466. package/dist/hooks/inbox-check/types.js.map +0 -1
  467. package/dist/hooks/inbox-check/utils.d.ts.map +0 -1
  468. package/dist/hooks/inbox-check/utils.js.map +0 -1
  469. package/dist/hooks/index.d.ts.map +0 -1
  470. package/dist/hooks/index.js.map +0 -1
  471. package/dist/hooks/types.d.ts.map +0 -1
  472. package/dist/hooks/types.js.map +0 -1
  473. package/dist/index.d.ts.map +0 -1
  474. package/dist/index.js.map +0 -1
  475. package/dist/protocol/framing.d.ts.map +0 -1
  476. package/dist/protocol/framing.js.map +0 -1
  477. package/dist/protocol/index.d.ts.map +0 -1
  478. package/dist/protocol/index.js.map +0 -1
  479. package/dist/protocol/types.d.ts.map +0 -1
  480. package/dist/protocol/types.js.map +0 -1
  481. package/dist/resiliency/context-persistence.d.ts.map +0 -1
  482. package/dist/resiliency/context-persistence.js.map +0 -1
  483. package/dist/resiliency/health-monitor.d.ts.map +0 -1
  484. package/dist/resiliency/health-monitor.js.map +0 -1
  485. package/dist/resiliency/index.d.ts.map +0 -1
  486. package/dist/resiliency/index.js.map +0 -1
  487. package/dist/resiliency/logger.d.ts.map +0 -1
  488. package/dist/resiliency/logger.js.map +0 -1
  489. package/dist/resiliency/metrics.d.ts.map +0 -1
  490. package/dist/resiliency/metrics.js.map +0 -1
  491. package/dist/resiliency/provider-context.d.ts.map +0 -1
  492. package/dist/resiliency/provider-context.js.map +0 -1
  493. package/dist/resiliency/supervisor.d.ts.map +0 -1
  494. package/dist/resiliency/supervisor.js.map +0 -1
  495. package/dist/state/agent-state.d.ts.map +0 -1
  496. package/dist/state/agent-state.js.map +0 -1
  497. package/dist/storage/adapter.d.ts.map +0 -1
  498. package/dist/storage/adapter.js.map +0 -1
  499. package/dist/storage/sqlite-adapter.d.ts.map +0 -1
  500. package/dist/storage/sqlite-adapter.js.map +0 -1
  501. package/dist/utils/agent-config.d.ts.map +0 -1
  502. package/dist/utils/agent-config.js.map +0 -1
  503. package/dist/utils/command-resolver.d.ts.map +0 -1
  504. package/dist/utils/command-resolver.js.map +0 -1
  505. package/dist/utils/index.d.ts.map +0 -1
  506. package/dist/utils/index.js.map +0 -1
  507. package/dist/utils/logger.d.ts.map +0 -1
  508. package/dist/utils/logger.js.map +0 -1
  509. package/dist/utils/name-generator.d.ts.map +0 -1
  510. package/dist/utils/name-generator.js.map +0 -1
  511. package/dist/utils/project-namespace.d.ts.map +0 -1
  512. package/dist/utils/project-namespace.js.map +0 -1
  513. package/dist/utils/tmux-resolver.d.ts.map +0 -1
  514. package/dist/utils/tmux-resolver.js.map +0 -1
  515. package/dist/utils/update-checker.d.ts.map +0 -1
  516. package/dist/utils/update-checker.js.map +0 -1
  517. package/dist/wrapper/client.d.ts.map +0 -1
  518. package/dist/wrapper/client.js.map +0 -1
  519. package/dist/wrapper/inbox.d.ts.map +0 -1
  520. package/dist/wrapper/inbox.js.map +0 -1
  521. package/dist/wrapper/index.d.ts.map +0 -1
  522. package/dist/wrapper/index.js.map +0 -1
  523. package/dist/wrapper/parser.d.ts.map +0 -1
  524. package/dist/wrapper/parser.js.map +0 -1
  525. package/dist/wrapper/pty-wrapper.d.ts.map +0 -1
  526. package/dist/wrapper/pty-wrapper.js.map +0 -1
  527. package/dist/wrapper/tmux-wrapper.d.ts.map +0 -1
  528. package/dist/wrapper/tmux-wrapper.js.map +0 -1
  529. package/docs/AGENTS.md +0 -513
  530. package/docs/ARCHITECTURE_DECISIONS.md +0 -175
  531. package/docs/CHANGELOG.md +0 -11
  532. package/docs/CLI-SIMPLIFICATION-COMPLETE.md +0 -48
  533. package/docs/CLOUD-ARCHITECTURE.md +0 -652
  534. package/docs/CLOUD-ONBOARDING-DESIGN.md +0 -1983
  535. package/docs/COMPETITIVE_ANALYSIS.md +0 -897
  536. package/docs/CONTRIBUTING.md +0 -151
  537. package/docs/DESIGN_BRIDGE_STAFFING.md +0 -878
  538. package/docs/DESIGN_V2.md +0 -1079
  539. package/docs/INTEGRATION-GUIDE.md +0 -926
  540. package/docs/MONETIZATION.md +0 -1679
  541. package/docs/PROPOSAL-trajectories.md +0 -1582
  542. package/docs/PROTOCOL.md +0 -325
  543. package/docs/SCALING_ANALYSIS.md +0 -280
  544. package/docs/TESTING_PRESENCE_FEATURES.md +0 -327
  545. package/docs/TMUX_IMPLEMENTATION_NOTES.md +0 -364
  546. package/docs/TMUX_IMPROVEMENTS.md +0 -968
  547. package/docs/agent-relay-snippet.md +0 -168
  548. package/docs/competitive-analysis-mcp-agent-mail.md +0 -389
  549. package/docs/dashboard-v2-plan.md +0 -179
  550. package/docs/guides/CLOUD.md +0 -236
  551. package/docs/guides/LOCAL.md +0 -535
  552. package/docs/guides/SELF-HOSTED.md +0 -494
  553. package/docs/proposals/shadow-as-subagent.md +0 -765
  554. package/docs/proposals/slack-bot-integration.md +0 -1457
  555. package/docs/removable-code-analysis.md +0 -24
  556. package/scripts/dev/PUBLIC_RELEASE_PLAN.md +0 -88
  557. package/scripts/dev/dev-team-setup.sh +0 -431
  558. package/scripts/e2e-test.sh +0 -119
  559. package/scripts/games/game-protocol.md +0 -79
  560. package/scripts/games/hearts-setup.sh +0 -264
  561. package/scripts/tictactoe-setup.sh +0 -181
  562. /package/dist/dashboard/out/_next/static/chunks/{117-b2cd8d6485aacf2b.js → 117-f7b8ab0809342e77.js} +0 -0
  563. /package/dist/dashboard/out/_next/static/chunks/{648-8f3f26864ce515e5.js → 648-5cc6e1921389a58a.js} +0 -0
  564. /package/dist/dashboard/out/_next/static/chunks/app/_not-found/{page-0b990dbb71d72a98.js → page-53b8a69f76db17d0.js} +0 -0
  565. /package/dist/dashboard/out/_next/static/chunks/{fd9d1056-bf46c09eb57e019c.js → fd9d1056-609918ca7b6280bb.js} +0 -0
  566. /package/dist/dashboard/out/_next/static/{6HHWb2ZmnJ4OSm0zUP7h4 → wPgKJtcOmTFLpUncDg16A}/_buildManifest.js +0 -0
  567. /package/dist/dashboard/out/_next/static/{6HHWb2ZmnJ4OSm0zUP7h4 → wPgKJtcOmTFLpUncDg16A}/_ssgManifest.js +0 -0
@@ -1,968 +0,0 @@
1
- # tmux Implementation: Technical Comparison & Analysis
2
-
3
- ## Head-to-Head: Ours vs Alternative Approach
4
-
5
- ### Architecture Overview
6
-
7
- ```
8
- ┌─────────────────────────────────────────────────────────────────────┐
9
- │ OUR APPROACH (agent-relay) │
10
- ├─────────────────────────────────────────────────────────────────────┤
11
- │ │
12
- │ User Terminal │
13
- │ │ │
14
- │ ▼ │
15
- │ ┌─────────────────────────────────────────┐ │
16
- │ │ spawn('tmux', ['attach-session', '-t'])│ ◄── stdio: 'inherit' │
17
- │ │ User sees REAL tmux session │ │
18
- │ └─────────────────────────────────────────┘ │
19
- │ │
20
- │ Background Process (same Node.js process) │
21
- │ │ │
22
- │ ├─► setInterval(200ms) │
23
- │ │ └─► execAsync('tmux capture-pane -p -J -S -') │
24
- │ │ └─► parser.parse(output) │
25
- │ │ └─► detect ->relay: patterns │
26
- │ │ └─► send to daemon │
27
- │ │ │
28
- │ └─► onMessage from daemon │
29
- │ └─► wait for idle (1.5s no output) │
30
- │ └─► execAsync('tmux send-keys -l "msg"') │
31
- │ │
32
- │ Dependencies: NONE (just tmux + node child_process) │
33
- │ │
34
- └─────────────────────────────────────────────────────────────────────┘
35
-
36
- ┌─────────────────────────────────────────────────────────────────────┐
37
- │ ALTERNATIVE APPROACH (streaming) │
38
- ├─────────────────────────────────────────────────────────────────────┤
39
- │ │
40
- │ Browser (xterm.js) │
41
- │ │ │
42
- │ │ WebSocket │
43
- │ ▼ │
44
- │ ┌─────────────────────────────────────────┐ │
45
- │ │ WebSocket Server (server.mjs) │ │
46
- │ │ │ │ │
47
- │ │ ▼ │ │
48
- │ │ node-pty.spawn('tmux', ['attach']) │ ◄── PTY pseudo-tty │
49
- │ │ │ │ │
50
- │ │ ├─► pty.onData(data) │ │
51
- │ │ │ └─► ws.send(data) │ Real-time to browser │
52
- │ │ │ │ │
53
- │ │ └─► ws.onMessage(input) │ │
54
- │ │ └─► pty.write(input) │ Real-time from user │
55
- │ └─────────────────────────────────────────┘ │
56
- │ │
57
- │ Messages: Separate system (filesystem + HTTP API) │
58
- │ └─► ~/.relay/messages/inbox/{agent}/ │
59
- │ └─► Agent polls for new files or gets tmux notification │
60
- │ │
61
- │ Dependencies: node-pty (native), ws, xterm.js │
62
- │ │
63
- └─────────────────────────────────────────────────────────────────────┘
64
- ```
65
-
66
- ---
67
-
68
- ## Technical Deep Dive
69
-
70
- ### 1. Terminal I/O Method
71
-
72
- #### Ours: Polling with `capture-pane`
73
-
74
- ```typescript
75
- // Every 200ms, capture terminal buffer
76
- private async pollForRelayCommands(): Promise<void> {
77
- const { stdout } = await execAsync(
78
- `tmux capture-pane -t ${this.sessionName} -p -J -S - 2>/dev/null`
79
- );
80
- // -p = print to stdout (not to buffer)
81
- // -J = join wrapped lines
82
- // -S - = start from beginning of scrollback
83
-
84
- const cleanContent = this.stripAnsi(stdout);
85
- const { commands } = this.parser.parse(cleanContent);
86
- // ...
87
- }
88
- ```
89
-
90
- #### Alternative: Streaming with node-pty
91
-
92
- ```javascript
93
- // Real-time event-driven
94
- const pty = spawn('tmux', ['attach-session', '-t', session]);
95
-
96
- pty.onData((data) => {
97
- // Fires immediately on ANY output
98
- ws.send(data); // Forward to browser
99
- filterAndLog(data);
100
- });
101
- ```
102
-
103
- #### Comparison
104
-
105
- | Aspect | Polling (Ours) | Streaming (Theirs) |
106
- |--------|----------------|-------------------|
107
- | **Latency** | 0-200ms (poll interval) | <10ms (event-driven) |
108
- | **CPU idle** | Constant ~1-2% (polling) | Near 0% (event-driven) |
109
- | **CPU active** | Same | Same |
110
- | **Missed output** | Possible if buffer wraps | Never (stream-based) |
111
- | **Complexity** | ~20 lines | ~50 lines + native dep |
112
- | **Build issues** | None | node-pty compilation |
113
-
114
- **Robustness verdict:** Streaming is technically more robust (no missed output), but polling is simpler and "good enough" for text-based agents that don't produce massive output bursts.
115
-
116
- ---
117
-
118
- ### 2. User Terminal Experience
119
-
120
- #### Ours: Native tmux attach
121
-
122
- ```typescript
123
- // User's terminal is directly attached to tmux
124
- this.attachProcess = spawn('tmux', ['attach-session', '-t', this.sessionName], {
125
- stdio: 'inherit', // <-- KEY: User's stdin/stdout/stderr ARE the tmux session
126
- });
127
- ```
128
-
129
- **What this means:**
130
- - User's terminal emulator renders tmux directly
131
- - All keybindings work natively (Ctrl+B, mouse, etc.)
132
- - Scrollback, copy/paste work as expected
133
- - No intermediate rendering layer
134
-
135
- #### Alternative: Browser-based xterm.js
136
-
137
- ```javascript
138
- // Terminal rendered in browser via WebGL
139
- const term = new Terminal({
140
- rendererType: 'webgl',
141
- convertEol: false, // PTY handles line endings
142
- });
143
- term.loadAddon(new FitAddon());
144
- term.loadAddon(new WebglAddon());
145
- ```
146
-
147
- **What this means:**
148
- - Terminal is *emulated* in browser
149
- - Some keybindings may differ
150
- - Scrollback limited by xterm.js buffer
151
- - Copy/paste goes through browser
152
-
153
- #### Comparison
154
-
155
- | Aspect | Native tmux (Ours) | xterm.js (Theirs) |
156
- |--------|-------------------|-------------------|
157
- | **Keybindings** | 100% native | ~95% (some edge cases) |
158
- | **Scrollback** | tmux buffer (configurable) | xterm.js buffer |
159
- | **Performance** | Native | WebGL (good, but more overhead) |
160
- | **Accessibility** | Terminal emulator's | Browser-based |
161
- | **Remote access** | SSH | Browser (Tailscale) |
162
-
163
- **Robustness verdict:** Native is more robust for power users. Browser is more accessible for teams/remote.
164
-
165
- ---
166
-
167
- ### 3. Message Injection
168
-
169
- #### Ours: Idle detection + send-keys
170
-
171
- ```typescript
172
- private async injectNextMessage(): Promise<void> {
173
- // Wait for output to settle
174
- const timeSinceOutput = Date.now() - this.lastOutputTime;
175
- if (timeSinceOutput < 1500) { // 1.5 seconds
176
- setTimeout(() => this.checkForInjectionOpportunity(), 500);
177
- return;
178
- }
179
-
180
- // Clear any partial input
181
- await this.sendKeys('Escape');
182
- await this.sleep(30);
183
- await this.sendKeys('C-u'); // Clear line
184
- await this.sleep(30);
185
-
186
- // Type the message
187
- await this.sendKeysLiteral(message);
188
- await this.sleep(50);
189
- await this.sendKeys('Enter');
190
- }
191
-
192
- private async sendKeysLiteral(text: string): Promise<void> {
193
- const escaped = text
194
- .replace(/[\r\n]+/g, ' ')
195
- .replace(/\\/g, '\\\\')
196
- .replace(/"/g, '\\"')
197
- .replace(/\$/g, '\\$')
198
- .replace(/`/g, '\\`')
199
- .replace(/!/g, '\\!');
200
- await execAsync(`tmux send-keys -t ${this.sessionName} -l "${escaped}"`);
201
- }
202
- ```
203
-
204
- #### Alternative: Multiple injection methods
205
-
206
- ```bash
207
- # Method 1: display-message (non-intrusive popup)
208
- tmux display-message -t $SESSION "Message from $FROM: $MSG"
209
-
210
- # Method 2: send-keys with echo (injects shell command)
211
- tmux send-keys -t $SESSION "echo '📬 MESSAGE: $MSG'" Enter
212
-
213
- # Method 3: send-keys literal (injects text)
214
- tmux send-keys -t $SESSION -l "Message: $MSG"
215
- ```
216
-
217
- #### Comparison
218
-
219
- | Aspect | Our Approach | Their Approach |
220
- |--------|--------------|----------------|
221
- | **Idle detection** | Time-based (1.5s) | None (fire and forget) |
222
- | **Input clearing** | Yes (Esc + Ctrl-U) | No |
223
- | **Race conditions** | Reduced | Possible |
224
- | **CLI-specific** | Yes (Gemini printf) | Partial |
225
- | **Intrusive** | Yes (types into prompt) | display-message is not |
226
-
227
- **Robustness verdict:** Our approach is more robust because of idle detection and input clearing. Their `display-message` is less intrusive but also less reliable for LLM consumption.
228
-
229
- ---
230
-
231
- ### 4. Message Detection/Parsing
232
-
233
- #### Ours: Pattern matching on terminal output
234
-
235
- ```typescript
236
- // Parser handles real-world terminal mess
237
- const INLINE_RELAY = /^(?:\s*(?:[>$%#→➜›»●•◦‣⁃\-*⏺◆◇○□■]\s*)*)?->relay:(\S+)\s+(.+)$/;
238
-
239
- // Strip ANSI codes
240
- const ANSI_PATTERN = /\x1b\[[0-9;?]*[a-zA-Z]|\x1b\].*?(?:\x07|\x1b\\)|\r/g;
241
-
242
- // Handle continuation lines (TUI wrapping)
243
- private joinContinuationLines(content: string): string {
244
- // Claude Code and TUIs insert real newlines...
245
- }
246
-
247
- // Track what we've already processed
248
- private sentMessageHashes: Set<string> = new Set();
249
- ```
250
-
251
- #### Alternative: No parsing needed
252
-
253
- ```javascript
254
- // Messages are sent via API/CLI, not terminal output
255
- send-relay-message.sh Bob "Subject" "Body"
256
-
257
- // Creates file in ~/.relay/messages/inbox/Bob/
258
- // Agent's "subconscious" polls filesystem for new files
259
- ```
260
-
261
- #### Comparison
262
-
263
- | Aspect | Pattern Parsing (Ours) | API-based (Theirs) |
264
- |--------|------------------------|-------------------|
265
- | **Agent effort** | Just output text | Call external script |
266
- | **Natural** | Yes (`->relay:Bob hi`) | No (shell command) |
267
- | **Reliable** | ~95% (edge cases) | 100% (structured) |
268
- | **Multi-line** | Complex (continuation) | Easy (JSON body) |
269
- | **ANSI codes** | Must strip | N/A |
270
-
271
- **Robustness verdict:** API-based is technically more robust (no parsing edge cases). But pattern-based is more natural for agents - they just "speak" instead of calling tools.
272
-
273
- ---
274
-
275
- ### 5. Session Management
276
-
277
- #### Ours: One wrapper = one session
278
-
279
- ```typescript
280
- // Generate unique session name
281
- this.sessionName = `relay-${config.name}-${process.pid}`;
282
-
283
- // Create session
284
- execSync(`tmux new-session -d -s ${this.sessionName} -x ${cols} -y ${rows}`);
285
-
286
- // Set environment
287
- execSync(`tmux setenv -t ${this.sessionName} AGENT_RELAY_NAME ${name}`);
288
-
289
- // When wrapper exits, session is killed
290
- stop(): void {
291
- execSync(`tmux kill-session -t ${this.sessionName} 2>/dev/null`);
292
- }
293
- ```
294
-
295
- #### Alternative: Discovery-based
296
-
297
- ```typescript
298
- // Discover existing sessions
299
- async function discoverLocalSessions(): Promise<Session[]> {
300
- const { stdout } = await execAsync('tmux list-sessions -F "#{session_name}"');
301
- return stdout.trim().split('\n').map(name => ({
302
- name,
303
- // Fetch metadata
304
- cwd: await execAsync(`tmux display-message -t ${name} -p '#{pane_current_path}'`)
305
- }));
306
- }
307
-
308
- // Sessions can exist without agents
309
- // Agents can exist without sessions
310
- // Linking is optional
311
- ```
312
-
313
- #### Comparison
314
-
315
- | Aspect | Wrapper-owns-session (Ours) | Discovery-based (Theirs) |
316
- |--------|----------------------------|-------------------------|
317
- | **Lifecycle** | Coupled (wrapper=session) | Decoupled |
318
- | **Pre-existing** | No | Yes |
319
- | **Orphan sessions** | No (killed on exit) | Possible |
320
- | **Flexibility** | Lower | Higher |
321
-
322
- **Robustness verdict:** Their approach is more flexible (can attach to existing sessions). Our approach is simpler and prevents orphan sessions.
323
-
324
- ---
325
-
326
- ## Which is More Robust?
327
-
328
- ### Our Strengths
329
-
330
- | Area | Why We're Stronger |
331
- |------|-------------------|
332
- | **Native experience** | Direct tmux attach, no emulation layer |
333
- | **Simplicity** | No native dependencies, no WebSocket complexity |
334
- | **Injection** | Idle detection prevents race conditions |
335
- | **CLI support** | Special handling for Gemini, etc. |
336
- | **Deduplication** | Won't send same message twice |
337
-
338
- ### Their Strengths
339
-
340
- | Area | Why They're Stronger |
341
- |------|---------------------|
342
- | **Real-time** | Event-driven, no polling latency |
343
- | **Visibility** | Browser dashboard shows all agents |
344
- | **Message reliability** | Filesystem-based, never lost |
345
- | **Remote access** | Browser-based, works via Tailscale |
346
- | **Agent decoupling** | Agents exist independent of sessions |
347
-
348
- ---
349
-
350
- ## Recommended Improvements for Robustness
351
-
352
- ### 1. Add Activity State Tracking (from their approach)
353
-
354
- ```typescript
355
- // Track active/idle/disconnected state
356
- private activityState: 'active' | 'idle' | 'disconnected' = 'active';
357
- private lastActivityTime = Date.now();
358
-
359
- private updateActivityState(): void {
360
- const elapsed = Date.now() - this.lastActivityTime;
361
-
362
- if (elapsed > 30_000) {
363
- this.activityState = 'idle';
364
- // Idle is the BEST time to inject
365
- this.flushMessageQueue();
366
- } else if (elapsed > 5_000) {
367
- this.activityState = 'idle';
368
- } else {
369
- this.activityState = 'active';
370
- }
371
- }
372
- ```
373
-
374
- ### 2. Add Exponential Backoff for Reconnection
375
-
376
- ```typescript
377
- private readonly RECONNECT_DELAYS = [100, 500, 1000, 2000, 5000];
378
- private reconnectAttempt = 0;
379
-
380
- private reconnect(): void {
381
- if (this.reconnectAttempt >= this.RECONNECT_DELAYS.length) {
382
- this.logStderr('Max reconnection attempts, operating offline');
383
- return;
384
- }
385
-
386
- const delay = this.RECONNECT_DELAYS[this.reconnectAttempt++];
387
- setTimeout(() => this.client.connect(), delay);
388
- }
389
- ```
390
-
391
- ### 3. Consider Hybrid: Streaming + Pattern Parsing
392
-
393
- ```typescript
394
- // Best of both worlds (optional mode)
395
- import { spawn } from 'node-pty';
396
-
397
- // Read-only PTY attach for real-time output
398
- const pty = spawn('tmux', ['attach-session', '-t', session, '-r']);
399
-
400
- pty.onData((data) => {
401
- // Real-time pattern detection
402
- const { commands } = this.parser.parse(data);
403
- for (const cmd of commands) {
404
- this.sendRelayCommand(cmd);
405
- }
406
- });
407
-
408
- // Still use send-keys for injection (works on attached session)
409
- ```
410
-
411
- ### 4. Add Bracketed Paste for Safer Injection
412
-
413
- ```typescript
414
- const PASTE_START = '\x1b[200~';
415
- const PASTE_END = '\x1b[201~';
416
-
417
- async function injectSafe(text: string): Promise<void> {
418
- // Bracketed paste prevents shell interpretation
419
- await sendKeysLiteral(PASTE_START + text + PASTE_END);
420
- await sendKeys('Enter');
421
- }
422
- ```
423
-
424
- ---
425
-
426
- ## Known Issue: `@` Symbol Conflicts with Gemini
427
-
428
- ### The Problem
429
-
430
- Gemini CLI uses `@` for file references:
431
- ```bash
432
- gemini> @src/main.ts # References a file
433
- gemini> ->relay:Bob Hi # Gemini might try to open file "relay:Bob"!
434
- ```
435
-
436
- This could explain why Gemini agents have trouble sending relay messages - the CLI intercepts `@` before it reaches the terminal output.
437
-
438
- We've standardized on `->relay:` for all CLIs to keep a single mental model. Keep the prefix configurable so Gemini users can fall back to an alternative if their CLI mangles the arrow sequence.
439
-
440
- ### Optional Alternative Prefixes
441
-
442
- | Prefix | Example | Pros | Cons |
443
- |--------|---------|------|------|
444
- | `>>` | `>>Bob: Hello` | Simple, intuitive | Could conflict with shell redirect |
445
- | `->` | `->Bob: Hello` | Clear direction | Might look like code |
446
- | `#relay:` | `#relay:Bob Hello` | Hashtag is common | Could conflict with comments |
447
- | `!relay:` | `!relay:Bob Hello` | Bang is distinct | Could trigger shell history |
448
- | `/relay` | `/relay Bob Hello` | Slash command style | Familiar pattern |
449
- | `[[relay]]` | `[[relay:Bob]] Hello` | Very distinct | Verbose |
450
- | `@>` | `@>Bob: Hello` | Keeps @ but distinct | Still has @ |
451
- | `relay::` | `relay::Bob Hello` | No special prefix char | Plain text |
452
-
453
- ### Recommended: Configurable Prefix
454
-
455
- Support multiple prefixes with a default that works everywhere:
456
-
457
- ```typescript
458
- // In config
459
- {
460
- "relayPrefix": "->relay:", // Default (works across Claude/Codex/Gemini)
461
- // Alternatives:
462
- // "relayPrefix": ">>", // For Gemini
463
- // "relayPrefix": "/relay", // Slash command style
464
- // "relayPrefix": "relay::", // Plain text
465
- }
466
-
467
- // In parser.ts
468
- const prefix = config.relayPrefix || '->relay:';
469
- const pattern = new RegExp(`^(?:\\s*)?${escapeRegex(prefix)}(\\S+)\\s+(.+)$`);
470
- ```
471
-
472
- ### Testing Gemini with Alternative Prefix
473
-
474
- ```bash
475
- # Default: unified arrow prefix
476
- agent-relay -n GeminiAgent gemini
477
-
478
- # If Gemini mangles ->relay:, try an alternative prefix
479
- agent-relay -n GeminiAgent --prefix=">>" gemini
480
-
481
- # Agent outputs:
482
- >>Bob: Can you review this code?
483
-
484
- # Instead of:
485
- ->relay:Bob Can you review this code?
486
- ```
487
-
488
- ### Implementation: CLI Flag
489
-
490
- ```typescript
491
- // In cli/index.ts
492
- .option('--prefix <pattern>', 'Relay pattern prefix (default: ->relay:)')
493
-
494
- // In wrapper config
495
- const wrapperConfig: TmuxWrapperConfig = {
496
- name: options.name,
497
- command: mainCommand,
498
- args: commandArgs,
499
- relayPrefix: options.prefix || '->relay:',
500
- // ...
501
- };
502
- ```
503
-
504
- ### Parser Update
505
-
506
- ```typescript
507
- // In parser.ts
508
- export class OutputParser {
509
- private prefix: string;
510
- private inlinePattern: RegExp;
511
-
512
- constructor(options: ParserOptions = {}) {
513
- this.prefix = options.prefix || '->relay:';
514
-
515
- // Build pattern dynamically
516
- const escaped = this.escapeRegex(this.prefix);
517
- this.inlinePattern = new RegExp(
518
- `^(?:\\s*(?:[>$%#→➜›»●•◦‣⁃\\-*⏺◆◇○□■]\\s*)*)?${escaped}(\\S+)\\s+(.+)$`
519
- );
520
- }
521
-
522
- private escapeRegex(str: string): string {
523
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
524
- }
525
- }
526
- ```
527
-
528
- ### Recommendation
529
-
530
- 1. **Short term:** Add `--prefix` flag to test with Gemini
531
- 2. **Default for all CLIs:** Keep unified `->relay:` prefix; override only when needed
532
- 3. **Long term:** Document recommended fallbacks per CLI if issues appear
533
-
534
- ```typescript
535
- // Auto-detect best prefix for CLI type
536
- function getDefaultPrefix(cliType: string): string {
537
- // Unified default; users can override via --prefix when necessary
538
- return '->relay:';
539
- }
540
- ```
541
-
542
- ---
543
-
544
- ## Verdict: Overall Robustness
545
-
546
- | Category | Winner | Reason |
547
- |----------|--------|--------|
548
- | **Message detection** | Tie | Ours is natural, theirs is reliable |
549
- | **Message delivery** | Ours | Idle detection prevents corruption |
550
- | **Terminal fidelity** | Ours | Native > emulated |
551
- | **Real-time** | Theirs | Streaming > polling |
552
- | **Simplicity** | Ours | No native deps, no browser |
553
- | **Visibility** | Theirs | Dashboard > logs |
554
- | **Multi-agent** | Theirs | Built for teams |
555
-
556
- **Overall:** For 2-3 agents, **ours is more robust** (simpler, fewer failure modes). For 5-10 agents, **theirs scales better** (visibility, discovery). The recommended improvements above would close the gap.
557
-
558
- ---
559
-
560
- ## Current Implementation Summary
561
-
562
- Our tmux wrapper uses an **attach-based polling architecture**:
563
-
564
- ```
565
- ┌─────────────────────────────────────────────────────────────┐
566
- │ User Terminal │
567
- │ └─ tmux attach-session (stdio: 'inherit') │
568
- │ └─ User sees real tmux session │
569
- │ │
570
- │ Background (every 200ms) │
571
- │ └─ tmux capture-pane -p -J -S - │
572
- │ └─ Parse for ->relay: patterns │
573
- │ └─ Send detected commands to daemon │
574
- │ │
575
- │ Message Injection │
576
- │ └─ Wait for 1.5s idle │
577
- │ └─ tmux send-keys (Escape, C-u, message, Enter) │
578
- └─────────────────────────────────────────────────────────────┘
579
- ```
580
-
581
- ---
582
-
583
- ## Alternative Approach: WebSocket + node-pty
584
-
585
- A different approach uses **real-time PTY streaming** instead of polling:
586
-
587
- ```
588
- ┌─────────────────────────────────────────────────────────────┐
589
- │ Browser/Client │
590
- │ └─ xterm.js terminal │
591
- │ └─ WebSocket connection │
592
- │ │
593
- │ Server │
594
- │ └─ node-pty spawns: tmux attach -t session │
595
- │ └─ pty.onData → ws.send (real-time streaming) │
596
- │ └─ ws.onMessage → pty.write (real-time input) │
597
- │ │
598
- │ No polling needed - events are instant │
599
- └─────────────────────────────────────────────────────────────┘
600
- ```
601
-
602
- ---
603
-
604
- ## Key Differences
605
-
606
- | Aspect | Our Approach (Polling) | Alternative (Streaming) |
607
- |--------|------------------------|-------------------------|
608
- | **Terminal location** | User's actual terminal | Browser (xterm.js) |
609
- | **Data flow** | Periodic capture-pane | Real-time PTY events |
610
- | **Latency** | 0-200ms | ~1-10ms |
611
- | **CPU usage** | Constant (polling) | Event-driven (lower) |
612
- | **Complexity** | Simple shell commands | node-pty + WebSocket |
613
- | **Dependencies** | None (just tmux) | node-pty, ws, xterm.js |
614
- | **User experience** | Native terminal feel | Browser-based |
615
-
616
- ---
617
-
618
- ## What We Do Better
619
-
620
- ### 1. Native Terminal Experience
621
-
622
- Users stay in their actual terminal. No browser, no xterm.js emulation quirks.
623
-
624
- ```bash
625
- # Our approach - user is IN the tmux
626
- agent-relay -n Alice claude
627
- # User types directly, sees real output
628
-
629
- # Alternative - user is in browser
630
- # Terminal is rendered in xterm.js WebGL
631
- # Subtle differences in keybindings, scrolling, copy/paste
632
- ```
633
-
634
- **Keep this.** The native feel is valuable.
635
-
636
- ### 2. Simpler Dependencies
637
-
638
- We only need tmux and Node.js. No native compilation (node-pty), no browser components.
639
-
640
- ```json
641
- // Our package.json - no native deps
642
- {
643
- "dependencies": {
644
- "commander": "^12.0.0",
645
- "better-sqlite3": "^9.0.0"
646
- // That's it for core functionality
647
- }
648
- }
649
-
650
- // Alternative needs
651
- {
652
- "dependencies": {
653
- "node-pty": "^1.0.0", // Native compilation required
654
- "xterm": "^5.0.0", // Browser terminal
655
- "xterm-addon-fit": "...",
656
- "xterm-addon-webgl": "...",
657
- "ws": "^8.0.0"
658
- }
659
- }
660
- ```
661
-
662
- **Keep this.** Simpler install, fewer build issues.
663
-
664
- ### 3. Pattern-Based Communication
665
-
666
- Agents just output `->relay:Name message`. No API calls, no special handling.
667
-
668
- ```
669
- # Our approach - agent outputs text naturally
670
- Claude: I'll ask Bob for help.
671
- ->relay:Bob Can you review the auth module?
672
-
673
- # Alternative - agent calls external script
674
- Claude: I'll ask Bob for help.
675
- !send-message Bob "Can you review the auth module?"
676
- ```
677
-
678
- **Keep this.** It's our killer feature.
679
-
680
- ---
681
-
682
- ## What We Can Improve
683
-
684
- ### 1. Activity Tracking
685
-
686
- The alternative tracks session activity state (active/idle/disconnected) with timestamps:
687
-
688
- ```typescript
689
- // Their approach
690
- const sessionActivity: Map<string, number> = new Map();
691
-
692
- // On any output
693
- sessionActivity.set(sessionName, Date.now());
694
-
695
- // Idle detection
696
- const IDLE_THRESHOLD = 30_000; // 30 seconds
697
- function getSessionStatus(name: string): 'active' | 'idle' | 'disconnected' {
698
- const lastActivity = sessionActivity.get(name);
699
- if (!lastActivity) return 'disconnected';
700
- return Date.now() - lastActivity > IDLE_THRESHOLD ? 'idle' : 'active';
701
- }
702
- ```
703
-
704
- **Improvement:** Add activity tracking for better injection timing:
705
-
706
- ```typescript
707
- // In tmux-wrapper.ts
708
- private lastActivityTime = Date.now();
709
- private activityState: 'active' | 'idle' = 'active';
710
-
711
- private updateActivityState(): void {
712
- const now = Date.now();
713
- const wasActive = this.activityState === 'active';
714
-
715
- if (now - this.lastActivityTime > 30_000) {
716
- this.activityState = 'idle';
717
- if (wasActive) {
718
- this.logStderr('Session went idle');
719
- // Good time to check for messages
720
- this.checkForInjectionOpportunity();
721
- }
722
- }
723
- }
724
- ```
725
-
726
- ### 2. Graceful Reconnection
727
-
728
- The alternative implements exponential backoff for WebSocket reconnection:
729
-
730
- ```typescript
731
- // Their approach
732
- const RECONNECT_DELAYS = [100, 500, 1000, 2000, 5000];
733
- let reconnectAttempt = 0;
734
-
735
- function reconnect() {
736
- if (reconnectAttempt >= RECONNECT_DELAYS.length) {
737
- console.error('Max reconnection attempts reached');
738
- return;
739
- }
740
-
741
- setTimeout(() => {
742
- connect();
743
- reconnectAttempt++;
744
- }, RECONNECT_DELAYS[reconnectAttempt]);
745
- }
746
- ```
747
-
748
- **Improvement:** Add to our RelayClient:
749
-
750
- ```typescript
751
- // In client.ts
752
- private reconnectAttempts = 0;
753
- private readonly MAX_RECONNECT_ATTEMPTS = 5;
754
- private readonly RECONNECT_DELAYS = [100, 500, 1000, 2000, 5000];
755
-
756
- private scheduleReconnect(): void {
757
- if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
758
- this.logStderr('Relay connection failed, operating offline');
759
- return;
760
- }
761
-
762
- const delay = this.RECONNECT_DELAYS[this.reconnectAttempts];
763
- this.reconnectAttempts++;
764
-
765
- setTimeout(() => {
766
- this.connect().catch(() => this.scheduleReconnect());
767
- }, delay);
768
- }
769
- ```
770
-
771
- ### 3. Agent Registry Persistence
772
-
773
- The alternative stores agent metadata in a persistent registry:
774
-
775
- ```typescript
776
- // Alternative approach - ~/.relay/agents/registry.json
777
- {
778
- "agents": {
779
- "agent-abc123": {
780
- "id": "agent-abc123",
781
- "name": "Alice",
782
- "aliases": ["alice", "dev-alice"],
783
- "workingDirectory": "/home/user/project",
784
- "cli": "claude",
785
- "createdAt": "2025-12-20T10:00:00Z",
786
- "lastSeen": "2025-12-20T14:30:00Z"
787
- }
788
- }
789
- }
790
- ```
791
-
792
- **Improvement:** Add agent registry:
793
-
794
- ```typescript
795
- // New file: src/daemon/registry.ts
796
- interface AgentRecord {
797
- id: string;
798
- name: string;
799
- cli: string;
800
- workingDirectory: string;
801
- firstSeen: string;
802
- lastSeen: string;
803
- messagesSent: number;
804
- messagesReceived: number;
805
- }
806
-
807
- class AgentRegistry {
808
- private registryPath: string;
809
- private agents: Map<string, AgentRecord> = new Map();
810
-
811
- constructor(dataDir: string) {
812
- this.registryPath = path.join(dataDir, 'agents.json');
813
- this.load();
814
- }
815
-
816
- register(name: string, cli: string, cwd: string): AgentRecord {
817
- const existing = this.agents.get(name);
818
- if (existing) {
819
- existing.lastSeen = new Date().toISOString();
820
- this.save();
821
- return existing;
822
- }
823
-
824
- const record: AgentRecord = {
825
- id: `agent-${randomId()}`,
826
- name,
827
- cli,
828
- workingDirectory: cwd,
829
- firstSeen: new Date().toISOString(),
830
- lastSeen: new Date().toISOString(),
831
- messagesSent: 0,
832
- messagesReceived: 0,
833
- };
834
-
835
- this.agents.set(name, record);
836
- this.save();
837
- return record;
838
- }
839
- }
840
- ```
841
-
842
- ### 4. Session Discovery
843
-
844
- The alternative auto-discovers tmux sessions:
845
-
846
- ```typescript
847
- // Their approach
848
- async function discoverLocalSessions(): Promise<Session[]> {
849
- const { stdout } = await execAsync('tmux list-sessions -F "#{session_name}"');
850
- const sessionNames = stdout.trim().split('\n').filter(Boolean);
851
-
852
- return Promise.all(sessionNames.map(async (name) => {
853
- const { stdout: cwd } = await execAsync(
854
- `tmux display-message -t ${name} -p '#{pane_current_path}'`
855
- );
856
- return { name, workingDirectory: cwd.trim() };
857
- }));
858
- }
859
- ```
860
-
861
- **Improvement:** Add discovery for better `agent-relay status`:
862
-
863
- ```typescript
864
- // In cli/index.ts - enhance status command
865
- async function discoverRelaySessions(): Promise<SessionInfo[]> {
866
- try {
867
- const { stdout } = await execAsync('tmux list-sessions -F "#{session_name}"');
868
- const sessions = stdout.trim().split('\n').filter(Boolean);
869
-
870
- // Filter to relay sessions only
871
- return sessions
872
- .filter(name => name.startsWith('relay-'))
873
- .map(name => {
874
- const match = name.match(/^relay-(.+)-\d+$/);
875
- return match ? { sessionName: name, agentName: match[1] } : null;
876
- })
877
- .filter(Boolean);
878
- } catch {
879
- return [];
880
- }
881
- }
882
- ```
883
-
884
- ### 5. Output Filtering
885
-
886
- The alternative filters noisy patterns from logs:
887
-
888
- ```typescript
889
- // Their approach - filter thinking indicators, escape sequences
890
- const NOISE_PATTERNS = [
891
- /\[\d+\/\d+\]/, // [1/418] thinking steps
892
- /\x1b\[[0-9;]*[mK]/, // ANSI escape sequences
893
- /^Thinking\.{1,3}$/, // "Thinking..." lines
894
- ];
895
-
896
- function filterNoise(output: string): string {
897
- return output.split('\n')
898
- .filter(line => !NOISE_PATTERNS.some(p => p.test(line)))
899
- .join('\n');
900
- }
901
- ```
902
-
903
- **Improvement:** Add optional output filtering for cleaner logs:
904
-
905
- ```typescript
906
- // In tmux-wrapper.ts
907
- private filterForLogging(output: string): string {
908
- if (!this.config.filterLogs) return output;
909
-
910
- return output
911
- .split('\n')
912
- .filter(line => {
913
- // Skip thinking indicators
914
- if (/^\[[\d/]+\]/.test(line)) return false;
915
- // Skip empty ANSI-only lines
916
- if (this.stripAnsi(line).trim() === '') return false;
917
- return true;
918
- })
919
- .join('\n');
920
- }
921
- ```
922
-
923
- ---
924
-
925
- ## Rejected Ideas
926
-
927
- ### 1. Browser-Based Terminal
928
-
929
- Moving to xterm.js would lose the native terminal feel. Users expect to use their own terminal with their own keybindings, themes, and muscle memory.
930
-
931
- **Decision:** Keep native tmux attach.
932
-
933
- ### 2. Full node-pty Integration
934
-
935
- Using node-pty for output streaming would add native dependencies and build complexity. The polling approach works well enough.
936
-
937
- **Decision:** Keep capture-pane polling. Consider optional streaming as future enhancement.
938
-
939
- ### 3. Complex Agent Lifecycle
940
-
941
- The alternative supports agents without sessions, complex metadata, and persistent memory. This adds significant complexity.
942
-
943
- **Decision:** Keep it simple. Agent = wrapper process. When wrapper exits, agent is gone.
944
-
945
- ---
946
-
947
- ## Implementation Priority
948
-
949
- | Improvement | Effort | Impact | Priority |
950
- |-------------|--------|--------|----------|
951
- | Activity tracking | Low | Medium | P1 |
952
- | Reconnection backoff | Low | Medium | P1 |
953
- | Session discovery | Low | Low | P2 |
954
- | Agent registry | Medium | Medium | P2 |
955
- | Output filtering | Low | Low | P3 |
956
-
957
- ---
958
-
959
- ## Summary
960
-
961
- Our tmux implementation is **simpler and more native** than alternatives. The key improvements to adopt:
962
-
963
- 1. **Activity state tracking** - Better injection timing
964
- 2. **Exponential backoff** - Graceful daemon reconnection
965
- 3. **Session discovery** - Better status output
966
- 4. **Agent registry** - Persistence across restarts
967
-
968
- These add minimal complexity while improving reliability. The core architecture (polling + pattern parsing + injection) remains unchanged.