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.
- package/README.md +130 -158
- package/bin/relay-pty +0 -0
- package/bin/relay-pty-darwin-arm64 +0 -0
- package/bin/relay-pty-darwin-x64 +0 -0
- package/bin/relay-pty-linux-x64 +0 -0
- package/deploy/workspace/entrypoint.sh +9 -0
- package/dist/bridge/spawner.d.ts +4 -4
- package/dist/bridge/spawner.js +58 -92
- package/dist/cli/index.d.ts +8 -6
- package/dist/cli/index.js +282 -47
- package/dist/cloud/api/daemons.js +13 -32
- package/dist/cloud/api/onboarding.js +2 -4
- package/dist/cloud/api/providers.js +6 -0
- package/dist/cloud/config.d.ts +1 -0
- package/dist/cloud/config.js +2 -0
- package/dist/cloud/db/bulk-ingest.d.ts +2 -1
- package/dist/cloud/db/drizzle.d.ts +21 -26
- package/dist/cloud/db/drizzle.js +87 -100
- package/dist/cloud/db/index.d.ts +6 -5
- package/dist/cloud/db/index.js +9 -8
- package/dist/cloud/db/schema.d.ts +1049 -1076
- package/dist/cloud/db/schema.js +59 -71
- package/dist/cloud/server.js +854 -18
- package/dist/cloud/services/persistence.d.ts +15 -15
- package/dist/cloud/services/persistence.js +14 -14
- package/dist/daemon/agent-manager.d.ts +6 -5
- package/dist/daemon/agent-manager.js +12 -8
- package/dist/daemon/channel-membership-store.d.ts +48 -0
- package/dist/daemon/channel-membership-store.js +149 -0
- package/dist/daemon/cloud-sync.d.ts +2 -0
- package/dist/daemon/cloud-sync.js +4 -0
- package/dist/daemon/connection.js +17 -9
- package/dist/daemon/router.d.ts +37 -0
- package/dist/daemon/router.js +318 -79
- package/dist/daemon/server.d.ts +15 -0
- package/dist/daemon/server.js +141 -3
- package/dist/dashboard/out/404.html +1 -0
- package/dist/dashboard/out/_next/static/IxxVRv94L1w3ReRGAiI-k/_buildManifest.js +1 -0
- package/dist/dashboard/out/_next/static/IxxVRv94L1w3ReRGAiI-k/_ssgManifest.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/116-eacf84a131b80db9.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/117-c8afed19e821a35d.js +2 -0
- package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +9 -0
- package/dist/dashboard/out/_next/static/chunks/64-87ab9cd6bcf2f737.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/648-acb2ff9f77cbfbd3.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/766-aa7c8c9900ff5f53.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/83-4f08122d4e7e79a6.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/847-f1f467060f32afff.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/891-a024fbe4b619cf6f.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-f746f29e01fffc43.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/page-ffad986adfcc8b31.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/cloud/link/page-cfeb437f08a12ed9.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-03ac6f35a6654ea6.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/history/page-240f91e8b06ba8ac.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/login/page-6ec54eee75877971.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-82938ab8fcf44694.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/page-671037943b2f2e43.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/pricing/page-0efa024c28ba4597.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-57cbd738c6a73859.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-5ab0854472b402b0.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/signup/page-18a4665665f6be11.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
- package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/main-5a40a5ae29646e1b.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/main-app-6e8e8d3ef4e0192a.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
- package/dist/dashboard/out/_next/static/css/4034f236dd1a3178.css +1 -0
- package/dist/dashboard/out/_next/static/css/8f9ed310f454e5a5.css +1 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +45 -0
- package/dist/dashboard/out/alt-logos/logo.svg +38 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo.svg +38 -0
- package/dist/dashboard/out/app/onboarding.html +1 -0
- package/dist/dashboard/out/app/onboarding.txt +7 -0
- package/dist/dashboard/out/app.html +1 -0
- package/dist/dashboard/out/app.txt +7 -0
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/cloud/link.html +1 -0
- package/dist/dashboard/out/cloud/link.txt +7 -0
- package/dist/dashboard/out/connect-repos.html +1 -0
- package/dist/dashboard/out/connect-repos.txt +7 -0
- package/dist/dashboard/out/history.html +1 -0
- package/dist/dashboard/out/history.txt +7 -0
- package/dist/dashboard/out/index.html +1 -0
- package/dist/dashboard/out/index.txt +7 -0
- package/dist/dashboard/out/login.html +5 -0
- package/dist/dashboard/out/login.txt +7 -0
- package/dist/dashboard/out/metrics.html +1 -0
- package/dist/dashboard/out/metrics.txt +7 -0
- package/dist/dashboard/out/pricing.html +13 -0
- package/dist/dashboard/out/pricing.txt +7 -0
- package/dist/dashboard/out/providers/setup/claude.html +1 -0
- package/dist/dashboard/out/providers/setup/claude.txt +8 -0
- package/dist/dashboard/out/providers/setup/codex.html +1 -0
- package/dist/dashboard/out/providers/setup/codex.txt +8 -0
- package/dist/dashboard/out/providers.html +1 -0
- package/dist/dashboard/out/providers.txt +7 -0
- package/dist/dashboard/out/signup.html +6 -0
- package/dist/dashboard/out/signup.txt +7 -0
- package/dist/dashboard-server/metrics.d.ts +105 -0
- package/dist/dashboard-server/metrics.js +193 -0
- package/dist/dashboard-server/needs-attention.d.ts +24 -0
- package/dist/dashboard-server/needs-attention.js +78 -0
- package/dist/dashboard-server/server.d.ts +15 -0
- package/dist/dashboard-server/server.js +4753 -0
- package/dist/dashboard-server/start.d.ts +6 -0
- package/dist/dashboard-server/start.js +13 -0
- package/dist/dashboard-server/user-bridge.d.ts +132 -0
- package/dist/dashboard-server/user-bridge.js +317 -0
- package/dist/protocol/channels.d.ts +14 -8
- package/dist/protocol/channels.js +1 -1
- package/dist/protocol/index.d.ts +1 -0
- package/dist/protocol/index.js +1 -0
- package/dist/protocol/relay-pty-schemas.d.ts +209 -0
- package/dist/protocol/relay-pty-schemas.js +60 -0
- package/dist/wrapper/auth-detection.js +8 -1
- package/dist/wrapper/base-wrapper.d.ts +11 -1
- package/dist/wrapper/base-wrapper.js +67 -6
- package/dist/wrapper/client.d.ts +49 -1
- package/dist/wrapper/client.js +167 -0
- package/dist/wrapper/parser.d.ts +0 -4
- package/dist/wrapper/parser.js +38 -10
- package/dist/wrapper/pty-wrapper.d.ts +12 -1
- package/dist/wrapper/pty-wrapper.js +104 -5
- package/dist/wrapper/relay-pty-orchestrator.d.ts +270 -0
- package/dist/wrapper/relay-pty-orchestrator.js +970 -0
- package/dist/wrapper/shared.d.ts +1 -1
- package/dist/wrapper/shared.js +14 -4
- package/dist/wrapper/tmux-wrapper.d.ts +13 -1
- package/dist/wrapper/tmux-wrapper.js +143 -29
- package/package.json +9 -4
- package/scripts/postinstall.js +101 -11
- package/.trajectories/active/traj_3yx9dy148mge.json +0 -42
- package/.trajectories/agent-relay-322-324.md +0 -17
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.json +0 -49
- package/.trajectories/completed/2026-01/traj_03zupyv1s7b9.md +0 -31
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.json +0 -125
- package/.trajectories/completed/2026-01/traj_0zacdjl1g4ht.md +0 -62
- package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.json +0 -65
- package/.trajectories/completed/2026-01/traj_1dviorhnkcb5.md +0 -37
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +0 -49
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +0 -31
- package/.trajectories/completed/2026-01/traj_1k5if5snst2e.json +0 -65
- package/.trajectories/completed/2026-01/traj_1k5if5snst2e.md +0 -37
- package/.trajectories/completed/2026-01/traj_1rp3rges5811.json +0 -49
- package/.trajectories/completed/2026-01/traj_1rp3rges5811.md +0 -31
- package/.trajectories/completed/2026-01/traj_22bhyulruouw.json +0 -113
- package/.trajectories/completed/2026-01/traj_22bhyulruouw.md +0 -57
- package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.json +0 -53
- package/.trajectories/completed/2026-01/traj_2dao7ddgnta0.md +0 -32
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.json +0 -49
- package/.trajectories/completed/2026-01/traj_33iuy72sezbk.md +0 -31
- package/.trajectories/completed/2026-01/traj_3t0440mjeunc.json +0 -26
- package/.trajectories/completed/2026-01/traj_3t0440mjeunc.md +0 -6
- package/.trajectories/completed/2026-01/traj_45x9494d9xnr.json +0 -47
- package/.trajectories/completed/2026-01/traj_45x9494d9xnr.md +0 -32
- package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.json +0 -53
- package/.trajectories/completed/2026-01/traj_4aa0bb77s4nh.md +0 -32
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +0 -49
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +0 -31
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.json +0 -77
- package/.trajectories/completed/2026-01/traj_5ammh5qtvklq.md +0 -42
- package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.json +0 -59
- package/.trajectories/completed/2026-01/traj_5lhmzq8rxpqv.md +0 -33
- package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.json +0 -53
- package/.trajectories/completed/2026-01/traj_5vr4e9erb1fs.md +0 -32
- package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.json +0 -48
- package/.trajectories/completed/2026-01/traj_6fgiwdoklvym.md +0 -24
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.json +0 -77
- package/.trajectories/completed/2026-01/traj_6mieijqyvaag.md +0 -42
- package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +0 -109
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.json +0 -77
- package/.trajectories/completed/2026-01/traj_78ffm31jn3uk.md +0 -42
- package/.trajectories/completed/2026-01/traj_7ludwvz45veh.json +0 -209
- package/.trajectories/completed/2026-01/traj_7ludwvz45veh.md +0 -97
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.json +0 -66
- package/.trajectories/completed/2026-01/traj_94gnp3k30goq.md +0 -36
- package/.trajectories/completed/2026-01/traj_9921cuhel0pj.json +0 -48
- package/.trajectories/completed/2026-01/traj_9921cuhel0pj.md +0 -24
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +0 -49
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +0 -31
- package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.json +0 -49
- package/.trajectories/completed/2026-01/traj_ajs7zqfux4wc.md +0 -23
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.json +0 -40
- package/.trajectories/completed/2026-01/traj_avqeghu6pz5a.md +0 -22
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +0 -66
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +0 -36
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +0 -49
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +0 -31
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +0 -65
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +0 -37
- package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.json +0 -53
- package/.trajectories/completed/2026-01/traj_cvtqhlwcq9s0.md +0 -32
- package/.trajectories/completed/2026-01/traj_cxofprm2m2en.json +0 -49
- package/.trajectories/completed/2026-01/traj_cxofprm2m2en.md +0 -31
- package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.json +0 -26
- package/.trajectories/completed/2026-01/traj_d2hhz3k0vrhn.md +0 -6
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.json +0 -121
- package/.trajectories/completed/2026-01/traj_dcsp9s8y01ra.md +0 -29
- package/.trajectories/completed/2026-01/traj_dfuvww9pege5.json +0 -59
- package/.trajectories/completed/2026-01/traj_dfuvww9pege5.md +0 -37
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +0 -36
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +0 -21
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.json +0 -53
- package/.trajectories/completed/2026-01/traj_fhx9irlckht6.md +0 -32
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.json +0 -101
- package/.trajectories/completed/2026-01/traj_fqduidx3xbtp.md +0 -52
- package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.json +0 -77
- package/.trajectories/completed/2026-01/traj_g0fisy9h51mf.md +0 -42
- package/.trajectories/completed/2026-01/traj_gjdre5voouod.json +0 -53
- package/.trajectories/completed/2026-01/traj_gjdre5voouod.md +0 -32
- package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.json +0 -25
- package/.trajectories/completed/2026-01/traj_gtlyqtta3x8l.md +0 -15
- package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.json +0 -101
- package/.trajectories/completed/2026-01/traj_h4xijiuip3w4.md +0 -44
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +0 -101
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +0 -52
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.json +0 -49
- package/.trajectories/completed/2026-01/traj_hf81ey93uz6t.md +0 -31
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.json +0 -65
- package/.trajectories/completed/2026-01/traj_hfmki2jr9d4r.md +0 -37
- package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.json +0 -22
- package/.trajectories/completed/2026-01/traj_hhxte7w4gjjx.md +0 -5
- package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.json +0 -53
- package/.trajectories/completed/2026-01/traj_hpungyhoj6v5.md +0 -32
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +0 -61
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +0 -36
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.json +0 -49
- package/.trajectories/completed/2026-01/traj_lq450ly148uw.md +0 -31
- package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.json +0 -25
- package/.trajectories/completed/2026-01/traj_m2xkjv0w2sq7.md +0 -15
- package/.trajectories/completed/2026-01/traj_multi_server_arch.md +0 -101
- package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.json +0 -53
- package/.trajectories/completed/2026-01/traj_noq5zbvnrdvz.md +0 -32
- package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.json +0 -53
- package/.trajectories/completed/2026-01/traj_ntbs6ppopf46.md +0 -32
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +0 -73
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +0 -41
- package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.json +0 -48
- package/.trajectories/completed/2026-01/traj_ozd98si6a7ns.md +0 -24
- package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.json +0 -53
- package/.trajectories/completed/2026-01/traj_prdza7a5cxp5.md +0 -32
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.json +0 -27
- package/.trajectories/completed/2026-01/traj_psd9ob0j2ru3.md +0 -14
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +0 -77
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +0 -42
- package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.json +0 -77
- package/.trajectories/completed/2026-01/traj_qb3twvvywfwi.md +0 -42
- package/.trajectories/completed/2026-01/traj_qft54mi7nfor.json +0 -53
- package/.trajectories/completed/2026-01/traj_qft54mi7nfor.md +0 -32
- package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.json +0 -83
- package/.trajectories/completed/2026-01/traj_qx9uhf8whhxo.md +0 -47
- package/.trajectories/completed/2026-01/traj_rd9toccj18a0.json +0 -59
- package/.trajectories/completed/2026-01/traj_rd9toccj18a0.md +0 -37
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +0 -109
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +0 -56
- package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.json +0 -48
- package/.trajectories/completed/2026-01/traj_rt4fiw3ecp50.md +0 -16
- package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.json +0 -59
- package/.trajectories/completed/2026-01/traj_st8j35b0hrlc.md +0 -37
- package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.json +0 -53
- package/.trajectories/completed/2026-01/traj_t1yy8m7hbuxp.md +0 -32
- package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.json +0 -84
- package/.trajectories/completed/2026-01/traj_tmux_orchestrator_analysis.md +0 -109
- package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.json +0 -53
- package/.trajectories/completed/2026-01/traj_u9n9eqasw16k.md +0 -32
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.json +0 -53
- package/.trajectories/completed/2026-01/traj_ub8csuv3lcv4.md +0 -32
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.json +0 -186
- package/.trajectories/completed/2026-01/traj_uc29tlso8i9s.md +0 -86
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.json +0 -77
- package/.trajectories/completed/2026-01/traj_ui9b4tqxoa7j.md +0 -42
- package/.trajectories/completed/2026-01/traj_v87hypnongqx.json +0 -71
- package/.trajectories/completed/2026-01/traj_v87hypnongqx.md +0 -42
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.json +0 -89
- package/.trajectories/completed/2026-01/traj_v9dkdoxylyid.md +0 -47
- package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.json +0 -53
- package/.trajectories/completed/2026-01/traj_wkp2fgzdyinb.md +0 -32
- package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.json +0 -20
- package/.trajectories/completed/2026-01/traj_x14t8w8rn7xg.md +0 -6
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +0 -113
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +0 -57
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +0 -61
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +0 -36
- package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.json +0 -175
- package/.trajectories/completed/2026-01/traj_xnwbznkvv8ua.md +0 -82
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.json +0 -65
- package/.trajectories/completed/2026-01/traj_xy9vifpqet80.md +0 -37
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.json +0 -49
- package/.trajectories/completed/2026-01/traj_y7aiwijyfmmv.md +0 -31
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +0 -49
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +0 -31
- package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.json +0 -47
- package/.trajectories/completed/2026-01/traj_ysjc8zaeqtd3.md +0 -32
- package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.json +0 -59
- package/.trajectories/completed/2026-01/traj_yvdadtvdgnz3.md +0 -37
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +0 -49
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +0 -31
- package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.json +0 -53
- package/.trajectories/completed/2026-01/traj_z0vcw1wrzide.md +0 -32
- package/.trajectories/consolidate-settings-panel.md +0 -24
- package/.trajectories/gh-cli-user-token.md +0 -26
- package/.trajectories/index.json +0 -607
package/dist/daemon/router.js
CHANGED
|
@@ -6,9 +6,11 @@ import { generateId } from '../utils/id-generator.js';
|
|
|
6
6
|
import { PROTOCOL_VERSION, } from '../protocol/types.js';
|
|
7
7
|
import { routerLog } from '../utils/logger.js';
|
|
8
8
|
import { RateLimiter, NoOpRateLimiter } from './rate-limiter.js';
|
|
9
|
+
import * as crypto from 'node:crypto';
|
|
9
10
|
import { DeliveryTracker, } from './delivery-tracker.js';
|
|
10
11
|
export class Router {
|
|
11
12
|
storage;
|
|
13
|
+
channelMembershipStore;
|
|
12
14
|
connections = new Map(); // connectionId -> Connection
|
|
13
15
|
agents = new Map(); // agentName -> Connection
|
|
14
16
|
subscriptions = new Map(); // topic -> Set<agentName>
|
|
@@ -34,6 +36,7 @@ export class Router {
|
|
|
34
36
|
rateLimiter;
|
|
35
37
|
constructor(options = {}) {
|
|
36
38
|
this.storage = options.storage;
|
|
39
|
+
this.channelMembershipStore = options.channelMembershipStore;
|
|
37
40
|
this.registry = options.registry;
|
|
38
41
|
this.onProcessingStateChange = options.onProcessingStateChange;
|
|
39
42
|
this.crossMachineHandler = options.crossMachineHandler;
|
|
@@ -47,6 +50,45 @@ export class Router {
|
|
|
47
50
|
? new NoOpRateLimiter()
|
|
48
51
|
: new RateLimiter(options.rateLimit);
|
|
49
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Restore channel memberships from persisted storage.
|
|
55
|
+
*/
|
|
56
|
+
async restoreChannelMemberships() {
|
|
57
|
+
if (!this.storage && !this.channelMembershipStore)
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
if (this.channelMembershipStore) {
|
|
61
|
+
const memberships = await this.channelMembershipStore.loadMemberships();
|
|
62
|
+
for (const membership of memberships) {
|
|
63
|
+
this.handleMembershipUpdate({
|
|
64
|
+
channel: membership.channel,
|
|
65
|
+
member: membership.member,
|
|
66
|
+
action: 'join',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (this.storage) {
|
|
71
|
+
const messages = await this.storage.getMessages({ order: 'asc' });
|
|
72
|
+
for (const msg of messages) {
|
|
73
|
+
const channel = msg.to;
|
|
74
|
+
const data = msg.data;
|
|
75
|
+
const membership = data?._channelMembership;
|
|
76
|
+
if (!channel || !membership?.member) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const action = membership.action ?? 'join';
|
|
80
|
+
this.handleMembershipUpdate({
|
|
81
|
+
channel,
|
|
82
|
+
member: membership.member,
|
|
83
|
+
action,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
routerLog.error('Failed to restore channel memberships', { error: String(err) });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
50
92
|
/**
|
|
51
93
|
* Set or update the cross-machine handler.
|
|
52
94
|
*/
|
|
@@ -96,28 +138,36 @@ export class Router {
|
|
|
96
138
|
this.connections.delete(connection.id);
|
|
97
139
|
if (connection.agentName) {
|
|
98
140
|
const isUser = connection.entityType === 'user';
|
|
141
|
+
let wasCurrentConnection = false;
|
|
99
142
|
if (isUser) {
|
|
100
143
|
const currentUser = this.users.get(connection.agentName);
|
|
101
144
|
if (currentUser?.id === connection.id) {
|
|
102
145
|
this.users.delete(connection.agentName);
|
|
146
|
+
wasCurrentConnection = true;
|
|
103
147
|
}
|
|
104
148
|
}
|
|
105
149
|
else {
|
|
106
150
|
const current = this.agents.get(connection.agentName);
|
|
107
151
|
if (current?.id === connection.id) {
|
|
108
152
|
this.agents.delete(connection.agentName);
|
|
153
|
+
wasCurrentConnection = true;
|
|
109
154
|
}
|
|
110
155
|
}
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
156
|
+
// Only clean up channel/subscription state if this was the current connection.
|
|
157
|
+
// If a new connection replaced this one, we don't want to remove channel memberships
|
|
158
|
+
// that the new connection should inherit.
|
|
159
|
+
if (wasCurrentConnection) {
|
|
160
|
+
// Remove from all subscriptions
|
|
161
|
+
for (const subscribers of this.subscriptions.values()) {
|
|
162
|
+
subscribers.delete(connection.agentName);
|
|
163
|
+
}
|
|
164
|
+
// Remove from all channels and notify remaining members
|
|
165
|
+
this.removeFromAllChannels(connection.agentName);
|
|
166
|
+
// Clean up shadow relationships
|
|
167
|
+
this.unbindShadow(connection.agentName);
|
|
168
|
+
// Clear processing state
|
|
169
|
+
this.clearProcessing(connection.agentName);
|
|
114
170
|
}
|
|
115
|
-
// Remove from all channels and notify remaining members
|
|
116
|
-
this.removeFromAllChannels(connection.agentName);
|
|
117
|
-
// Clean up shadow relationships
|
|
118
|
-
this.unbindShadow(connection.agentName);
|
|
119
|
-
// Clear processing state
|
|
120
|
-
this.clearProcessing(connection.agentName);
|
|
121
171
|
}
|
|
122
172
|
this.clearPendingForConnection(connection.id);
|
|
123
173
|
}
|
|
@@ -303,7 +353,7 @@ export class Router {
|
|
|
303
353
|
this.registry?.recordSend(senderName);
|
|
304
354
|
const to = envelope.to;
|
|
305
355
|
const topic = envelope.topic;
|
|
306
|
-
routerLog.
|
|
356
|
+
routerLog.info(`Route ${senderName} -> ${to}`, { preview: envelope.payload.body?.substring(0, 50) });
|
|
307
357
|
if (to === '*') {
|
|
308
358
|
// Broadcast to all (except sender)
|
|
309
359
|
this.broadcast(senderName, envelope, topic);
|
|
@@ -382,7 +432,7 @@ export class Router {
|
|
|
382
432
|
}
|
|
383
433
|
const deliver = this.createDeliverEnvelope(from, to, envelope, target);
|
|
384
434
|
const sent = target.send(deliver);
|
|
385
|
-
routerLog.
|
|
435
|
+
routerLog.info(`Delivered ${from} -> ${to}`, { success: sent, preview: envelope.payload.body?.substring(0, 40) });
|
|
386
436
|
this.persistDeliverEnvelope(deliver);
|
|
387
437
|
if (sent) {
|
|
388
438
|
this.trackDelivery(target, deliver);
|
|
@@ -694,14 +744,18 @@ export class Router {
|
|
|
694
744
|
/**
|
|
695
745
|
* Handle a CHANNEL_JOIN message.
|
|
696
746
|
* Adds the member to the channel and notifies existing members.
|
|
747
|
+
* If payload.member is set, adds that member (admin mode).
|
|
748
|
+
* Otherwise, adds the connection's agent name.
|
|
697
749
|
*/
|
|
698
750
|
handleChannelJoin(connection, envelope) {
|
|
699
|
-
|
|
751
|
+
// Use payload.member if provided (admin mode), otherwise use connection's name
|
|
752
|
+
const memberName = envelope.payload.member ?? connection.agentName;
|
|
700
753
|
if (!memberName) {
|
|
701
|
-
routerLog.warn('CHANNEL_JOIN from connection without name');
|
|
754
|
+
routerLog.warn('CHANNEL_JOIN from connection without name and no member specified');
|
|
702
755
|
return;
|
|
703
756
|
}
|
|
704
757
|
const channel = envelope.payload.channel;
|
|
758
|
+
const isAdminJoin = Boolean(envelope.payload.member);
|
|
705
759
|
// Get or create channel
|
|
706
760
|
let members = this.channels.get(channel);
|
|
707
761
|
if (!members) {
|
|
@@ -713,79 +767,78 @@ export class Router {
|
|
|
713
767
|
routerLog.debug(`${memberName} already in ${channel}`);
|
|
714
768
|
return;
|
|
715
769
|
}
|
|
716
|
-
//
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
770
|
+
// Only notify existing members for non-admin joins (agents joining themselves)
|
|
771
|
+
// Admin joins are silent to avoid spamming notifications when syncing
|
|
772
|
+
if (!isAdminJoin) {
|
|
773
|
+
const existingMembers = members ? Array.from(members) : [];
|
|
774
|
+
for (const existingMember of existingMembers) {
|
|
775
|
+
const memberConn = this.getConnectionByName(existingMember);
|
|
776
|
+
if (memberConn) {
|
|
777
|
+
const joinNotification = {
|
|
778
|
+
v: PROTOCOL_VERSION,
|
|
779
|
+
type: 'CHANNEL_JOIN',
|
|
780
|
+
id: generateId(),
|
|
781
|
+
ts: Date.now(),
|
|
782
|
+
from: memberName,
|
|
783
|
+
payload: envelope.payload,
|
|
784
|
+
};
|
|
785
|
+
memberConn.send(joinNotification);
|
|
786
|
+
}
|
|
729
787
|
}
|
|
730
788
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
if (!memberChannelSet) {
|
|
736
|
-
memberChannelSet = new Set();
|
|
737
|
-
this.memberChannels.set(memberName, memberChannelSet);
|
|
789
|
+
const added = this.addChannelMember(channel, memberName, { persist: true });
|
|
790
|
+
if (!added) {
|
|
791
|
+
routerLog.debug(`${memberName} already in ${channel}`);
|
|
792
|
+
return;
|
|
738
793
|
}
|
|
739
|
-
|
|
740
|
-
routerLog.info(`${memberName} joined ${channel} (${members.size} members)`);
|
|
794
|
+
routerLog.info(`${memberName} joined ${channel} (${this.channels.get(channel)?.size ?? 0} members)${isAdminJoin ? ' [admin]' : ''}`);
|
|
741
795
|
}
|
|
742
796
|
/**
|
|
743
797
|
* Handle a CHANNEL_LEAVE message.
|
|
744
798
|
* Removes the member from the channel and notifies remaining members.
|
|
799
|
+
* If payload.member is provided, removes that member instead (admin mode).
|
|
745
800
|
*/
|
|
746
801
|
handleChannelLeave(connection, envelope) {
|
|
747
|
-
|
|
802
|
+
// Use payload.member if provided (admin mode), otherwise use connection's name
|
|
803
|
+
const memberName = envelope.payload.member ?? connection.agentName;
|
|
748
804
|
if (!memberName) {
|
|
749
|
-
routerLog.warn('CHANNEL_LEAVE from connection without name');
|
|
805
|
+
routerLog.warn('CHANNEL_LEAVE from connection without name and no member specified');
|
|
750
806
|
return;
|
|
751
807
|
}
|
|
752
808
|
const channel = envelope.payload.channel;
|
|
809
|
+
const isAdminRemove = Boolean(envelope.payload.member);
|
|
753
810
|
const members = this.channels.get(channel);
|
|
754
811
|
if (!members || !members.has(memberName)) {
|
|
755
812
|
routerLog.debug(`${memberName} not in ${channel}, ignoring leave`);
|
|
756
813
|
return;
|
|
757
814
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
if (memberChannelSet) {
|
|
763
|
-
memberChannelSet.delete(channel);
|
|
764
|
-
if (memberChannelSet.size === 0) {
|
|
765
|
-
this.memberChannels.delete(memberName);
|
|
766
|
-
}
|
|
815
|
+
const removed = this.removeChannelMember(channel, memberName, { persist: true });
|
|
816
|
+
if (!removed) {
|
|
817
|
+
routerLog.debug(`${memberName} not in ${channel}, ignoring leave`);
|
|
818
|
+
return;
|
|
767
819
|
}
|
|
768
|
-
//
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
820
|
+
// Only notify remaining members for non-admin removes
|
|
821
|
+
// Admin removes are silent to avoid spamming notifications
|
|
822
|
+
if (!isAdminRemove) {
|
|
823
|
+
const remainingMembers = this.channels.get(channel);
|
|
824
|
+
if (remainingMembers) {
|
|
825
|
+
for (const remainingMember of remainingMembers) {
|
|
826
|
+
const memberConn = this.getConnectionByName(remainingMember);
|
|
827
|
+
if (memberConn) {
|
|
828
|
+
const leaveNotification = {
|
|
829
|
+
v: PROTOCOL_VERSION,
|
|
830
|
+
type: 'CHANNEL_LEAVE',
|
|
831
|
+
id: generateId(),
|
|
832
|
+
ts: Date.now(),
|
|
833
|
+
from: memberName,
|
|
834
|
+
payload: envelope.payload,
|
|
835
|
+
};
|
|
836
|
+
memberConn.send(leaveNotification);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
781
839
|
}
|
|
782
840
|
}
|
|
783
|
-
|
|
784
|
-
if (members.size === 0) {
|
|
785
|
-
this.channels.delete(channel);
|
|
786
|
-
routerLog.debug(`Channel ${channel} deleted (empty)`);
|
|
787
|
-
}
|
|
788
|
-
routerLog.info(`${memberName} left ${channel}`);
|
|
841
|
+
routerLog.info(`${memberName} left ${channel}${isAdminRemove ? ' [admin]' : ''}`);
|
|
789
842
|
}
|
|
790
843
|
/**
|
|
791
844
|
* Route a channel message to all members except the sender.
|
|
@@ -798,18 +851,33 @@ export class Router {
|
|
|
798
851
|
}
|
|
799
852
|
const channel = envelope.payload.channel;
|
|
800
853
|
const members = this.channels.get(channel);
|
|
854
|
+
routerLog.info(`routeChannelMessage: channel=${channel} sender=${senderName} members=${members ? Array.from(members).join(',') : 'NONE'}`);
|
|
801
855
|
if (!members) {
|
|
802
|
-
routerLog.warn(`Message to non-existent channel ${channel}`);
|
|
856
|
+
routerLog.warn(`Message to non-existent channel ${channel} (available channels: ${Array.from(this.channels.keys()).join(', ')})`);
|
|
803
857
|
return;
|
|
804
858
|
}
|
|
805
|
-
|
|
806
|
-
|
|
859
|
+
// Case-insensitive membership check
|
|
860
|
+
const senderMemberName = this.findMemberInSet(members, senderName);
|
|
861
|
+
if (!senderMemberName) {
|
|
862
|
+
routerLog.warn(`${senderName} not a member of ${channel} (members: ${Array.from(members).join(', ')})`);
|
|
807
863
|
return;
|
|
808
864
|
}
|
|
809
|
-
// Route to all members except sender
|
|
865
|
+
// Route to all members except the sender (no echo)
|
|
866
|
+
const allMembers = Array.from(members);
|
|
867
|
+
routerLog.info(`Routing channel message from ${senderName} to ${channel}`, {
|
|
868
|
+
totalMembers: allMembers.length,
|
|
869
|
+
members: allMembers,
|
|
870
|
+
});
|
|
871
|
+
let deliveredCount = 0;
|
|
872
|
+
const undeliveredMembers = [];
|
|
873
|
+
const connectedAgents = Array.from(this.agents.keys());
|
|
874
|
+
const connectedUsers = Array.from(this.users.keys());
|
|
875
|
+
routerLog.info(`Connected entities: agents=[${connectedAgents.join(',')}] users=[${connectedUsers.join(',')}]`);
|
|
810
876
|
for (const memberName of members) {
|
|
811
|
-
|
|
877
|
+
// Case-insensitive comparison to skip sender
|
|
878
|
+
if (this.namesMatch(memberName, senderName)) {
|
|
812
879
|
continue;
|
|
880
|
+
}
|
|
813
881
|
const memberConn = this.getConnectionByName(memberName);
|
|
814
882
|
if (memberConn) {
|
|
815
883
|
const deliverEnvelope = {
|
|
@@ -820,12 +888,29 @@ export class Router {
|
|
|
820
888
|
from: senderName,
|
|
821
889
|
payload: envelope.payload,
|
|
822
890
|
};
|
|
823
|
-
memberConn.send(deliverEnvelope);
|
|
891
|
+
const sent = memberConn.send(deliverEnvelope);
|
|
892
|
+
if (sent) {
|
|
893
|
+
deliveredCount++;
|
|
894
|
+
routerLog.info(`Delivered to ${memberName} (${memberConn.entityType || 'agent'})`);
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
routerLog.warn(`Failed to send to ${memberName}`);
|
|
898
|
+
undeliveredMembers.push(memberName);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
routerLog.warn(`Member ${memberName} is registered in channel but NOT connected to daemon - message not delivered`);
|
|
903
|
+
undeliveredMembers.push(memberName);
|
|
824
904
|
}
|
|
825
905
|
}
|
|
826
906
|
// Persist channel message
|
|
827
907
|
this.persistChannelMessage(envelope, senderName);
|
|
828
|
-
|
|
908
|
+
const recipientCount = allMembers.length - 1; // Exclude sender
|
|
909
|
+
routerLog.info(`${senderName} -> ${channel}: delivered to ${deliveredCount}/${recipientCount} members`);
|
|
910
|
+
// Log warning if some members didn't receive the message
|
|
911
|
+
if (undeliveredMembers.length > 0) {
|
|
912
|
+
routerLog.warn(`Channel message undelivered to: [${undeliveredMembers.join(', ')}] - these agents may need to reconnect to the relay daemon`);
|
|
913
|
+
}
|
|
829
914
|
}
|
|
830
915
|
/**
|
|
831
916
|
* Persist a channel message to storage.
|
|
@@ -833,6 +918,12 @@ export class Router {
|
|
|
833
918
|
persistChannelMessage(envelope, from) {
|
|
834
919
|
if (!this.storage)
|
|
835
920
|
return;
|
|
921
|
+
const payloadData = {
|
|
922
|
+
...envelope.payload.data,
|
|
923
|
+
_isChannelMessage: true,
|
|
924
|
+
_channel: envelope.payload.channel,
|
|
925
|
+
_mentions: envelope.payload.mentions,
|
|
926
|
+
};
|
|
836
927
|
this.storage.saveMessage({
|
|
837
928
|
id: envelope.id,
|
|
838
929
|
ts: envelope.ts,
|
|
@@ -841,12 +932,7 @@ export class Router {
|
|
|
841
932
|
topic: undefined,
|
|
842
933
|
kind: 'message',
|
|
843
934
|
body: envelope.payload.body,
|
|
844
|
-
data:
|
|
845
|
-
...envelope.payload.data,
|
|
846
|
-
_isChannelMessage: true,
|
|
847
|
-
_channel: envelope.payload.channel,
|
|
848
|
-
_mentions: envelope.payload.mentions,
|
|
849
|
-
},
|
|
935
|
+
data: payloadData,
|
|
850
936
|
thread: envelope.payload.thread,
|
|
851
937
|
status: 'unread',
|
|
852
938
|
is_urgent: false,
|
|
@@ -855,6 +941,44 @@ export class Router {
|
|
|
855
941
|
routerLog.error('Failed to persist channel message', { error: String(err) });
|
|
856
942
|
});
|
|
857
943
|
}
|
|
944
|
+
persistChannelMembership(channel, member, action, opts) {
|
|
945
|
+
if (this.storage) {
|
|
946
|
+
this.storage.saveMessage({
|
|
947
|
+
id: crypto.randomUUID(),
|
|
948
|
+
ts: Date.now(),
|
|
949
|
+
from: '__system__',
|
|
950
|
+
to: channel,
|
|
951
|
+
topic: undefined,
|
|
952
|
+
kind: 'state', // membership events stored as state
|
|
953
|
+
body: `${action}:${member}`,
|
|
954
|
+
data: {
|
|
955
|
+
_channelMembership: {
|
|
956
|
+
member,
|
|
957
|
+
action,
|
|
958
|
+
invitedBy: opts?.invitedBy,
|
|
959
|
+
},
|
|
960
|
+
},
|
|
961
|
+
status: 'read',
|
|
962
|
+
is_urgent: false,
|
|
963
|
+
is_broadcast: true,
|
|
964
|
+
}).catch((err) => {
|
|
965
|
+
routerLog.error('Failed to persist channel membership', { error: String(err) });
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
if (this.channelMembershipStore) {
|
|
969
|
+
const persistPromise = action === 'leave'
|
|
970
|
+
? this.channelMembershipStore.removeMember(channel, member)
|
|
971
|
+
: this.channelMembershipStore.addMember(channel, member);
|
|
972
|
+
persistPromise.catch((err) => {
|
|
973
|
+
routerLog.error('Failed to sync channel membership to cloud store', {
|
|
974
|
+
channel,
|
|
975
|
+
member,
|
|
976
|
+
action,
|
|
977
|
+
error: err instanceof Error ? err.message : String(err),
|
|
978
|
+
});
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
858
982
|
/**
|
|
859
983
|
* Get all members of a channel.
|
|
860
984
|
*/
|
|
@@ -895,9 +1019,124 @@ export class Router {
|
|
|
895
1019
|
}
|
|
896
1020
|
/**
|
|
897
1021
|
* Get a connection by name (checks both agents and users).
|
|
1022
|
+
* Uses case-insensitive lookup to handle mismatched casing.
|
|
898
1023
|
*/
|
|
899
1024
|
getConnectionByName(name) {
|
|
900
|
-
|
|
1025
|
+
// Try exact match first
|
|
1026
|
+
const exact = this.agents.get(name) ?? this.users.get(name);
|
|
1027
|
+
if (exact)
|
|
1028
|
+
return exact;
|
|
1029
|
+
// Fall back to case-insensitive search
|
|
1030
|
+
const lowerName = name.toLowerCase();
|
|
1031
|
+
for (const [key, conn] of this.agents) {
|
|
1032
|
+
if (key.toLowerCase() === lowerName)
|
|
1033
|
+
return conn;
|
|
1034
|
+
}
|
|
1035
|
+
for (const [key, conn] of this.users) {
|
|
1036
|
+
if (key.toLowerCase() === lowerName)
|
|
1037
|
+
return conn;
|
|
1038
|
+
}
|
|
1039
|
+
return undefined;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Check if a member is in a Set (case-insensitive).
|
|
1043
|
+
* Returns the actual stored name if found, undefined otherwise.
|
|
1044
|
+
*/
|
|
1045
|
+
findMemberInSet(members, name) {
|
|
1046
|
+
// Try exact match first
|
|
1047
|
+
if (members.has(name))
|
|
1048
|
+
return name;
|
|
1049
|
+
// Fall back to case-insensitive search
|
|
1050
|
+
const lowerName = name.toLowerCase();
|
|
1051
|
+
for (const member of members) {
|
|
1052
|
+
if (member.toLowerCase() === lowerName)
|
|
1053
|
+
return member;
|
|
1054
|
+
}
|
|
1055
|
+
return undefined;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Check if two names match (case-insensitive).
|
|
1059
|
+
*/
|
|
1060
|
+
namesMatch(a, b) {
|
|
1061
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Auto-join a member to a channel without notifications.
|
|
1065
|
+
* Used for default channel membership (e.g., #general).
|
|
1066
|
+
* @param memberName - The agent or user name to add
|
|
1067
|
+
* @param channel - The channel to join (e.g., '#general')
|
|
1068
|
+
*/
|
|
1069
|
+
autoJoinChannel(memberName, channel, options) {
|
|
1070
|
+
// Get or create channel
|
|
1071
|
+
let members = this.channels.get(channel);
|
|
1072
|
+
if (!members) {
|
|
1073
|
+
members = new Set();
|
|
1074
|
+
this.channels.set(channel, members);
|
|
1075
|
+
}
|
|
1076
|
+
// Check if already a member
|
|
1077
|
+
const added = this.addChannelMember(channel, memberName, { persist: options?.persist });
|
|
1078
|
+
if (added) {
|
|
1079
|
+
routerLog.debug(`Auto-joined ${memberName} to ${channel}`);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
addChannelMember(channel, memberName, options) {
|
|
1083
|
+
let members = this.channels.get(channel);
|
|
1084
|
+
if (!members) {
|
|
1085
|
+
members = new Set();
|
|
1086
|
+
this.channels.set(channel, members);
|
|
1087
|
+
}
|
|
1088
|
+
// Case-insensitive check for existing membership
|
|
1089
|
+
const existingMember = this.findMemberInSet(members, memberName);
|
|
1090
|
+
if (existingMember) {
|
|
1091
|
+
return false;
|
|
1092
|
+
}
|
|
1093
|
+
members.add(memberName);
|
|
1094
|
+
const memberChannelSet = this.memberChannels.get(memberName) ?? new Set();
|
|
1095
|
+
memberChannelSet.add(channel);
|
|
1096
|
+
this.memberChannels.set(memberName, memberChannelSet);
|
|
1097
|
+
if (options?.persist ?? true) {
|
|
1098
|
+
this.persistChannelMembership(channel, memberName, 'join');
|
|
1099
|
+
}
|
|
1100
|
+
return true;
|
|
1101
|
+
}
|
|
1102
|
+
removeChannelMember(channel, memberName, options) {
|
|
1103
|
+
const members = this.channels.get(channel);
|
|
1104
|
+
if (!members) {
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
// Case-insensitive lookup to find actual stored name
|
|
1108
|
+
const actualMemberName = this.findMemberInSet(members, memberName);
|
|
1109
|
+
if (!actualMemberName) {
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
members.delete(actualMemberName);
|
|
1113
|
+
if (members.size === 0) {
|
|
1114
|
+
this.channels.delete(channel);
|
|
1115
|
+
}
|
|
1116
|
+
// Also try case-insensitive for memberChannels cleanup
|
|
1117
|
+
const memberChannelSet = this.memberChannels.get(actualMemberName) ?? this.memberChannels.get(memberName);
|
|
1118
|
+
if (memberChannelSet) {
|
|
1119
|
+
memberChannelSet.delete(channel);
|
|
1120
|
+
if (memberChannelSet.size === 0) {
|
|
1121
|
+
this.memberChannels.delete(actualMemberName);
|
|
1122
|
+
this.memberChannels.delete(memberName); // Clean up both potential keys
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (options?.persist ?? true) {
|
|
1126
|
+
this.persistChannelMembership(channel, actualMemberName, 'leave');
|
|
1127
|
+
}
|
|
1128
|
+
return true;
|
|
1129
|
+
}
|
|
1130
|
+
handleMembershipUpdate(update) {
|
|
1131
|
+
if (!update.channel || !update.member) {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
if (update.action === 'leave') {
|
|
1135
|
+
this.removeChannelMember(update.channel, update.member, { persist: false });
|
|
1136
|
+
}
|
|
1137
|
+
else {
|
|
1138
|
+
this.addChannelMember(update.channel, update.member, { persist: false });
|
|
1139
|
+
}
|
|
901
1140
|
}
|
|
902
1141
|
}
|
|
903
1142
|
//# sourceMappingURL=router.js.map
|
package/dist/daemon/server.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export declare class Daemon {
|
|
|
37
37
|
private cloudSync?;
|
|
38
38
|
private remoteAgents;
|
|
39
39
|
private consensus?;
|
|
40
|
+
private cloudSyncDebounceTimer?;
|
|
40
41
|
/** Callback for log output from agents (used by dashboard for streaming) */
|
|
41
42
|
onLogOutput?: (agentName: string, data: string, timestamp: number) => void;
|
|
42
43
|
/** Interval for writing processing state file (500ms for responsive UI) */
|
|
@@ -51,6 +52,11 @@ export declare class Daemon {
|
|
|
51
52
|
* This file contains agents currently processing/thinking after receiving a message.
|
|
52
53
|
*/
|
|
53
54
|
private writeProcessingStateFile;
|
|
55
|
+
/**
|
|
56
|
+
* Write currently connected agents to connected-agents.json for CLI consumption.
|
|
57
|
+
* This file contains agents with active socket connections (vs agents.json which is historical).
|
|
58
|
+
*/
|
|
59
|
+
private writeConnectedAgentsFile;
|
|
54
60
|
/**
|
|
55
61
|
* Initialize storage adapter (called during start).
|
|
56
62
|
*/
|
|
@@ -85,8 +91,17 @@ export declare class Daemon {
|
|
|
85
91
|
isRemoteAgent(agentName: string): RemoteAgent | undefined;
|
|
86
92
|
/**
|
|
87
93
|
* Notify cloud sync about local agent changes.
|
|
94
|
+
* Debounced to prevent flooding the cloud API with rapid connect/disconnect events.
|
|
88
95
|
*/
|
|
89
96
|
private notifyCloudSync;
|
|
97
|
+
/**
|
|
98
|
+
* Actually perform the cloud sync (called after debounce).
|
|
99
|
+
*/
|
|
100
|
+
private doCloudSync;
|
|
101
|
+
/**
|
|
102
|
+
* Check if an agent is internal (should be hidden from cloud sync and listings).
|
|
103
|
+
*/
|
|
104
|
+
private isInternalAgent;
|
|
90
105
|
/**
|
|
91
106
|
* Stop the daemon.
|
|
92
107
|
*/
|