agent-relay 1.6.0 → 2.0.4

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 (1074) hide show
  1. package/.cursor/mcp.json +11 -0
  2. package/.gitleaks.toml +26 -0
  3. package/.mcp.json +11 -0
  4. package/.nvmrc +1 -1
  5. package/ARCHITECTURE.md +10 -10
  6. package/CHANGELOG.md +38 -0
  7. package/LICENSE +185 -17
  8. package/README.md +43 -5
  9. package/SESSION_HANDOFF.md +67 -0
  10. package/bin/relay-pty-darwin-arm64 +0 -0
  11. package/bin/relay-pty-darwin-x64 +0 -0
  12. package/bin/relay-pty-linux-x64 +0 -0
  13. package/deploy/workspace/entrypoint.sh +83 -11
  14. package/deploy/workspace/git-credential-relay +152 -27
  15. package/deploy/workspace/git-credential-relay.test.sh +230 -0
  16. package/dist/dashboard/out/404.html +1 -1
  17. package/dist/dashboard/out/_next/static/chunks/116-a883fca163f3a5bc.js +1 -0
  18. package/dist/dashboard/out/_next/static/chunks/320-900169c942e31422.js +1 -0
  19. package/dist/dashboard/out/_next/static/chunks/631-af51bad94027527a.js +1 -0
  20. package/dist/dashboard/out/_next/static/chunks/766-2aea80818f7eb0d8.js +1 -0
  21. package/dist/dashboard/out/_next/static/chunks/891-5cb1513eeb97a891.js +1 -0
  22. package/dist/dashboard/out/_next/static/chunks/app/app/page-2e525b1dcc790967.js +1 -0
  23. package/dist/dashboard/out/_next/static/chunks/app/page-4e64923d73c35bc9.js +1 -0
  24. package/dist/dashboard/out/_next/static/chunks/app/providers/page-e65a0010da6ea5be.js +1 -0
  25. package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-84161c802b020a1f.js +1 -0
  26. package/dist/dashboard/out/_next/static/css/99c2552394077586.css +1 -0
  27. package/dist/dashboard/out/app/onboarding.html +1 -1
  28. package/dist/dashboard/out/app/onboarding.txt +1 -1
  29. package/dist/dashboard/out/app.html +1 -1
  30. package/dist/dashboard/out/app.txt +2 -2
  31. package/dist/dashboard/out/cloud/link.html +1 -1
  32. package/dist/dashboard/out/cloud/link.txt +2 -2
  33. package/dist/dashboard/out/connect-repos.html +1 -1
  34. package/dist/dashboard/out/connect-repos.txt +1 -1
  35. package/dist/dashboard/out/history.html +1 -1
  36. package/dist/dashboard/out/history.txt +2 -2
  37. package/dist/dashboard/out/index.html +1 -1
  38. package/dist/dashboard/out/index.txt +2 -2
  39. package/dist/dashboard/out/login.html +2 -2
  40. package/dist/dashboard/out/login.txt +1 -1
  41. package/dist/dashboard/out/metrics.html +1 -1
  42. package/dist/dashboard/out/metrics.txt +2 -2
  43. package/dist/dashboard/out/pricing.html +3 -3
  44. package/dist/dashboard/out/pricing.txt +2 -2
  45. package/dist/dashboard/out/providers/setup/claude.html +1 -1
  46. package/dist/dashboard/out/providers/setup/claude.txt +2 -2
  47. package/dist/dashboard/out/providers/setup/codex.html +1 -1
  48. package/dist/dashboard/out/providers/setup/codex.txt +2 -2
  49. package/dist/dashboard/out/providers/setup/cursor.html +1 -0
  50. package/dist/dashboard/out/providers/setup/cursor.txt +8 -0
  51. package/dist/dashboard/out/providers.html +1 -1
  52. package/dist/dashboard/out/providers.txt +2 -2
  53. package/dist/dashboard/out/signup.html +2 -2
  54. package/dist/dashboard/out/signup.txt +1 -1
  55. package/dist/src/bridge/index.d.ts +8 -0
  56. package/dist/src/bridge/index.js +8 -0
  57. package/dist/src/cli/index.js +3091 -0
  58. package/dist/src/cloud/index.d.ts +8 -0
  59. package/dist/src/cloud/index.js +8 -0
  60. package/dist/src/config/relay-config.d.ts +5 -0
  61. package/dist/src/config/relay-config.js +5 -0
  62. package/dist/src/continuity/index.d.ts +5 -0
  63. package/dist/src/continuity/index.js +5 -0
  64. package/dist/src/daemon/index.d.ts +8 -0
  65. package/dist/src/daemon/index.js +9 -0
  66. package/dist/src/dashboard-server/index.d.ts +8 -0
  67. package/dist/src/dashboard-server/index.js +8 -0
  68. package/dist/src/hooks/index.d.ts +10 -0
  69. package/dist/src/hooks/index.js +10 -0
  70. package/dist/src/index.d.ts +13 -0
  71. package/dist/src/index.js +16 -0
  72. package/dist/src/memory/index.d.ts +5 -0
  73. package/dist/src/memory/index.js +5 -0
  74. package/dist/src/policy/index.d.ts +5 -0
  75. package/dist/src/policy/index.js +5 -0
  76. package/dist/src/protocol/index.d.ts +8 -0
  77. package/dist/src/protocol/index.js +8 -0
  78. package/dist/src/resiliency/index.d.ts +5 -0
  79. package/dist/src/resiliency/index.js +5 -0
  80. package/dist/src/shared/cli-auth-config.d.ts +5 -0
  81. package/dist/src/shared/cli-auth-config.js +5 -0
  82. package/dist/src/state/index.d.ts +5 -0
  83. package/dist/src/state/index.js +5 -0
  84. package/dist/src/storage/index.d.ts +8 -0
  85. package/dist/src/storage/index.js +8 -0
  86. package/dist/src/trajectory/index.d.ts +5 -0
  87. package/dist/src/trajectory/index.js +5 -0
  88. package/dist/src/utils/index.d.ts +5 -0
  89. package/dist/src/utils/index.js +5 -0
  90. package/dist/src/wrapper/index.d.ts +8 -0
  91. package/dist/src/wrapper/index.js +11 -0
  92. package/package.json +64 -19
  93. package/packages/api-types/dist/index.d.ts +21 -0
  94. package/packages/api-types/dist/index.js +22 -0
  95. package/packages/api-types/dist/schemas/agent.d.ts +259 -0
  96. package/packages/api-types/dist/schemas/agent.js +102 -0
  97. package/packages/api-types/dist/schemas/api.d.ts +290 -0
  98. package/packages/api-types/dist/schemas/api.js +162 -0
  99. package/packages/api-types/dist/schemas/decision.d.ts +230 -0
  100. package/packages/api-types/dist/schemas/decision.js +104 -0
  101. package/packages/api-types/dist/schemas/fleet.d.ts +615 -0
  102. package/packages/api-types/dist/schemas/fleet.js +71 -0
  103. package/packages/api-types/dist/schemas/history.d.ts +180 -0
  104. package/packages/api-types/dist/schemas/history.js +72 -0
  105. package/packages/api-types/dist/schemas/index.d.ts +14 -0
  106. package/packages/api-types/dist/schemas/index.js +22 -0
  107. package/packages/api-types/dist/schemas/message.d.ts +456 -0
  108. package/packages/api-types/dist/schemas/message.js +88 -0
  109. package/packages/api-types/dist/schemas/session.d.ts +60 -0
  110. package/packages/api-types/dist/schemas/session.js +36 -0
  111. package/packages/api-types/dist/schemas/task.d.ts +111 -0
  112. package/packages/api-types/dist/schemas/task.js +64 -0
  113. package/packages/api-types/package.json +61 -0
  114. package/packages/api-types/scripts/generate-openapi.ts +106 -0
  115. package/packages/bridge/dist/index.d.ts +8 -0
  116. package/packages/bridge/dist/index.js +9 -0
  117. package/packages/bridge/dist/multi-project-client.d.ts +99 -0
  118. package/packages/bridge/dist/multi-project-client.js +389 -0
  119. package/packages/bridge/dist/shadow-cli.js +75 -0
  120. package/packages/bridge/dist/spawner.d.ts +210 -0
  121. package/packages/bridge/dist/spawner.js +1278 -0
  122. package/packages/bridge/dist/types.d.ts +131 -0
  123. package/packages/bridge/dist/utils.d.ts +15 -0
  124. package/packages/bridge/dist/utils.js +60 -0
  125. package/packages/bridge/package.json +45 -0
  126. package/packages/cloud/dist/api/admin.js +225 -0
  127. package/packages/cloud/dist/api/billing.js +564 -0
  128. package/packages/cloud/dist/api/cli-pty-runner.d.ts +53 -0
  129. package/packages/cloud/dist/api/cli-pty-runner.js +193 -0
  130. package/packages/cloud/dist/api/codex-auth-helper.js +327 -0
  131. package/packages/cloud/dist/api/consensus.js +261 -0
  132. package/packages/cloud/dist/api/coordinators.js +750 -0
  133. package/packages/cloud/dist/api/daemons.js +535 -0
  134. package/packages/cloud/dist/api/generic-webhooks.js +129 -0
  135. package/packages/cloud/dist/api/github-app.js +223 -0
  136. package/packages/cloud/dist/api/monitoring.js +578 -0
  137. package/packages/cloud/dist/api/nango-auth.js +674 -0
  138. package/packages/cloud/dist/api/onboarding.d.ts +15 -0
  139. package/packages/cloud/dist/api/onboarding.js +679 -0
  140. package/packages/cloud/dist/api/policy.js +229 -0
  141. package/packages/cloud/dist/api/provider-env.d.ts +14 -0
  142. package/packages/cloud/dist/api/provider-env.js +75 -0
  143. package/packages/cloud/dist/api/providers.js +564 -0
  144. package/packages/cloud/dist/api/repos.js +577 -0
  145. package/packages/cloud/dist/api/sessions.d.ts +11 -0
  146. package/packages/cloud/dist/api/sessions.js +302 -0
  147. package/packages/cloud/dist/api/teams.js +281 -0
  148. package/packages/cloud/dist/api/test-helpers.js +745 -0
  149. package/packages/cloud/dist/api/workspaces.js +1799 -0
  150. package/packages/cloud/dist/billing/plans.js +245 -0
  151. package/packages/cloud/dist/config.d.ts +5 -0
  152. package/packages/cloud/dist/config.js +5 -0
  153. package/packages/cloud/dist/db/drizzle.d.ts +256 -0
  154. package/packages/cloud/dist/db/drizzle.js +1286 -0
  155. package/packages/cloud/dist/db/schema.d.ts +4873 -0
  156. package/packages/cloud/dist/db/schema.js +620 -0
  157. package/packages/cloud/dist/index.d.ts +11 -0
  158. package/packages/cloud/dist/index.js +38 -0
  159. package/packages/cloud/dist/provisioner/index.d.ts +207 -0
  160. package/packages/cloud/dist/provisioner/index.js +2114 -0
  161. package/packages/cloud/dist/server.js +1924 -0
  162. package/packages/cloud/dist/services/index.d.ts +17 -0
  163. package/packages/cloud/dist/services/index.js +25 -0
  164. package/packages/cloud/dist/services/intro-expiration.d.ts +60 -0
  165. package/packages/cloud/dist/services/intro-expiration.js +252 -0
  166. package/packages/cloud/dist/services/nango.d.ts +201 -0
  167. package/packages/cloud/dist/services/nango.js +392 -0
  168. package/packages/cloud/dist/services/persistence.d.ts +131 -0
  169. package/packages/cloud/dist/shims/consensus.d.ts +23 -0
  170. package/packages/cloud/dist/shims/consensus.js +5 -0
  171. package/packages/cloud/package.json +60 -0
  172. package/packages/config/dist/bridge-config.d.ts +52 -0
  173. package/packages/config/dist/bridge-config.js +143 -0
  174. package/packages/config/dist/bridge-utils.d.ts +30 -0
  175. package/packages/config/dist/bridge-utils.js +54 -0
  176. package/packages/config/dist/cli-auth-config.js +391 -0
  177. package/packages/config/dist/cloud-config.d.ts +75 -0
  178. package/packages/config/dist/cloud-config.js +109 -0
  179. package/packages/config/dist/index.d.ts +13 -0
  180. package/packages/config/dist/index.js +13 -0
  181. package/packages/config/dist/project-namespace.d.ts +77 -0
  182. package/packages/config/dist/project-namespace.js +288 -0
  183. package/packages/config/dist/relay-config.d.ts +33 -0
  184. package/packages/config/dist/relay-config.js +33 -0
  185. package/packages/config/dist/relay-file-writer.d.ts +200 -0
  186. package/packages/config/dist/relay-file-writer.js +407 -0
  187. package/packages/config/dist/schemas.d.ts +672 -0
  188. package/packages/config/dist/schemas.js +180 -0
  189. package/packages/config/dist/shadow-config.d.ts +87 -0
  190. package/packages/config/dist/trajectory-config.d.ts +102 -0
  191. package/packages/config/dist/trajectory-config.js +185 -0
  192. package/packages/config/package.json +103 -0
  193. package/packages/continuity/dist/index.d.ts +9 -0
  194. package/packages/continuity/dist/index.js +9 -0
  195. package/packages/continuity/dist/types.d.ts +180 -0
  196. package/packages/continuity/dist/types.js +2 -0
  197. package/packages/continuity/package.json +37 -0
  198. package/packages/daemon/dist/agent-manager.d.ts +134 -0
  199. package/packages/daemon/dist/agent-manager.js +578 -0
  200. package/packages/daemon/dist/agent-registry.js +213 -0
  201. package/packages/daemon/dist/api.d.ts +106 -0
  202. package/packages/daemon/dist/api.js +876 -0
  203. package/packages/daemon/dist/channel-membership-store.d.ts +55 -0
  204. package/packages/daemon/dist/channel-membership-store.js +176 -0
  205. package/packages/daemon/dist/cli-auth.d.ts +89 -0
  206. package/packages/daemon/dist/cli-auth.js +792 -0
  207. package/packages/daemon/dist/cloud-sync.d.ts +150 -0
  208. package/packages/daemon/dist/cloud-sync.js +446 -0
  209. package/packages/daemon/dist/connection.d.ts +130 -0
  210. package/packages/daemon/dist/connection.js +438 -0
  211. package/packages/daemon/dist/consensus-integration.js +371 -0
  212. package/packages/daemon/dist/delivery-tracker.d.ts +34 -0
  213. package/packages/daemon/dist/delivery-tracker.js +104 -0
  214. package/packages/daemon/dist/enhanced-features.d.ts +118 -0
  215. package/packages/daemon/dist/enhanced-features.js +176 -0
  216. package/packages/daemon/dist/index.d.ts +31 -0
  217. package/packages/daemon/dist/index.js +37 -0
  218. package/packages/daemon/dist/migrations/index.d.ts +73 -0
  219. package/packages/daemon/dist/migrations/index.js +241 -0
  220. package/packages/daemon/dist/orchestrator.d.ts +217 -0
  221. package/packages/daemon/dist/orchestrator.js +1143 -0
  222. package/packages/daemon/dist/relay-ledger.d.ts +261 -0
  223. package/packages/daemon/dist/relay-ledger.js +532 -0
  224. package/packages/daemon/dist/relay-watchdog.d.ts +125 -0
  225. package/packages/daemon/dist/relay-watchdog.js +611 -0
  226. package/packages/daemon/dist/repo-manager.js +384 -0
  227. package/packages/daemon/dist/router.d.ts +370 -0
  228. package/packages/daemon/dist/router.js +1437 -0
  229. package/packages/daemon/dist/server.d.ts +174 -0
  230. package/packages/daemon/dist/server.js +1001 -0
  231. package/packages/daemon/dist/spawn-manager.d.ts +78 -0
  232. package/packages/daemon/dist/spawn-manager.js +165 -0
  233. package/packages/daemon/dist/sync-queue.d.ts +116 -0
  234. package/packages/daemon/dist/sync-queue.js +361 -0
  235. package/packages/daemon/dist/types.d.ts +133 -0
  236. package/packages/daemon/dist/workspace-manager.js +314 -0
  237. package/packages/daemon/package.json +57 -0
  238. package/packages/dashboard/README.md +48 -0
  239. package/packages/dashboard/dist/health-worker-manager.d.ts +62 -0
  240. package/packages/dashboard/dist/health-worker-manager.js +144 -0
  241. package/packages/dashboard/dist/health-worker.d.ts +9 -0
  242. package/packages/dashboard/dist/health-worker.js +79 -0
  243. package/packages/dashboard/dist/index.d.ts +20 -0
  244. package/packages/dashboard/dist/index.js +19 -0
  245. package/packages/dashboard/dist/metrics.d.ts +105 -0
  246. package/packages/dashboard/dist/metrics.js +193 -0
  247. package/packages/dashboard/dist/needs-attention.d.ts +24 -0
  248. package/packages/dashboard/dist/needs-attention.js +78 -0
  249. package/packages/dashboard/dist/server.d.ts +25 -0
  250. package/packages/dashboard/dist/server.js +5179 -0
  251. package/packages/dashboard/dist/start.d.ts +6 -0
  252. package/packages/dashboard/dist/start.js +13 -0
  253. package/packages/dashboard/dist/types/threading.d.ts +8 -0
  254. package/packages/dashboard/dist/types/threading.js +2 -0
  255. package/packages/dashboard/dist/user-bridge.d.ts +154 -0
  256. package/packages/dashboard/dist/user-bridge.js +372 -0
  257. package/packages/dashboard/package.json +64 -0
  258. package/packages/dashboard/ui/app/app/onboarding/page.tsx +394 -0
  259. package/packages/dashboard/ui/app/app/page.tsx +667 -0
  260. package/packages/dashboard/ui/app/apple-icon.png +0 -0
  261. package/packages/dashboard/ui/app/cloud/link/page.tsx +464 -0
  262. package/packages/dashboard/ui/app/connect-repos/page.tsx +410 -0
  263. package/packages/dashboard/ui/app/favicon.png +0 -0
  264. package/packages/dashboard/ui/app/globals.css +59 -0
  265. package/packages/dashboard/ui/app/history/page.tsx +658 -0
  266. package/packages/dashboard/ui/app/layout.tsx +25 -0
  267. package/packages/dashboard/ui/app/login/page.tsx +280 -0
  268. package/packages/dashboard/ui/app/metrics/page.tsx +751 -0
  269. package/packages/dashboard/ui/app/page.tsx +59 -0
  270. package/packages/dashboard/ui/app/pricing/page.tsx +7 -0
  271. package/packages/dashboard/ui/app/providers/page.tsx +193 -0
  272. package/packages/dashboard/ui/app/providers/setup/[provider]/ProviderSetupClient.tsx +148 -0
  273. package/packages/dashboard/ui/app/providers/setup/[provider]/constants.ts +35 -0
  274. package/packages/dashboard/ui/app/providers/setup/[provider]/page.tsx +42 -0
  275. package/packages/dashboard/ui/app/signup/page.tsx +343 -0
  276. package/packages/dashboard/ui/index.ts +49 -0
  277. package/packages/dashboard/ui/landing/LandingPage.tsx +713 -0
  278. package/packages/dashboard/ui/landing/PricingPage.tsx +559 -0
  279. package/packages/dashboard/ui/landing/index.ts +6 -0
  280. package/packages/dashboard/ui/landing/styles.css +2850 -0
  281. package/packages/dashboard/ui/lib/agent-merge.ts +35 -0
  282. package/packages/dashboard/ui/lib/api.ts +1155 -0
  283. package/packages/dashboard/ui/lib/cloudApi.ts +876 -0
  284. package/packages/dashboard/ui/lib/colors.ts +218 -0
  285. package/packages/dashboard/ui/lib/hierarchy.ts +242 -0
  286. package/packages/dashboard/ui/lib/stuckDetection.ts +142 -0
  287. package/packages/dashboard/ui/next-env.d.ts +5 -0
  288. package/packages/dashboard/ui/next.config.js +41 -0
  289. package/packages/dashboard/ui/package-lock.json +2882 -0
  290. package/packages/dashboard/ui/package.json +33 -0
  291. package/packages/dashboard/ui/postcss.config.js +5 -0
  292. package/packages/dashboard/ui/react-components/ActivityFeed.tsx +216 -0
  293. package/packages/dashboard/ui/react-components/AddWorkspaceModal.tsx +170 -0
  294. package/packages/dashboard/ui/react-components/AgentCard.tsx +587 -0
  295. package/packages/dashboard/ui/react-components/AgentList.tsx +411 -0
  296. package/packages/dashboard/ui/react-components/AgentProfilePanel.tsx +564 -0
  297. package/packages/dashboard/ui/react-components/App.tsx +3447 -0
  298. package/packages/dashboard/ui/react-components/BillingPanel.tsx +922 -0
  299. package/packages/dashboard/ui/react-components/BillingResult.tsx +447 -0
  300. package/packages/dashboard/ui/react-components/BroadcastComposer.tsx +690 -0
  301. package/packages/dashboard/ui/react-components/ChannelAdminPanel.tsx +773 -0
  302. package/packages/dashboard/ui/react-components/ChannelBrowser.tsx +385 -0
  303. package/packages/dashboard/ui/react-components/ChannelChat.tsx +307 -0
  304. package/packages/dashboard/ui/react-components/ChannelSidebar.tsx +399 -0
  305. package/packages/dashboard/ui/react-components/CloudSessionProvider.tsx +130 -0
  306. package/packages/dashboard/ui/react-components/CommandPalette.tsx +815 -0
  307. package/packages/dashboard/ui/react-components/ConfirmationDialog.tsx +133 -0
  308. package/packages/dashboard/ui/react-components/ConversationHistory.tsx +518 -0
  309. package/packages/dashboard/ui/react-components/CoordinatorPanel.tsx +944 -0
  310. package/packages/dashboard/ui/react-components/DecisionQueue.tsx +717 -0
  311. package/packages/dashboard/ui/react-components/DirectMessageView.tsx +164 -0
  312. package/packages/dashboard/ui/react-components/FileAutocomplete.tsx +368 -0
  313. package/packages/dashboard/ui/react-components/FleetOverview.tsx +278 -0
  314. package/packages/dashboard/ui/react-components/LogViewer.tsx +310 -0
  315. package/packages/dashboard/ui/react-components/LogViewerPanel.tsx +482 -0
  316. package/packages/dashboard/ui/react-components/Logo.tsx +284 -0
  317. package/packages/dashboard/ui/react-components/MentionAutocomplete.tsx +384 -0
  318. package/packages/dashboard/ui/react-components/MessageList.tsx +649 -0
  319. package/packages/dashboard/ui/react-components/MessageSenderName.tsx +91 -0
  320. package/packages/dashboard/ui/react-components/MessageStatusIndicator.tsx +142 -0
  321. package/packages/dashboard/ui/react-components/NewConversationModal.tsx +400 -0
  322. package/packages/dashboard/ui/react-components/NotificationToast.tsx +488 -0
  323. package/packages/dashboard/ui/react-components/OnlineUsersIndicator.tsx +164 -0
  324. package/packages/dashboard/ui/react-components/Pagination.tsx +124 -0
  325. package/packages/dashboard/ui/react-components/PricingPlans.tsx +386 -0
  326. package/packages/dashboard/ui/react-components/ProjectList.tsx +625 -0
  327. package/packages/dashboard/ui/react-components/ProviderAuthFlow.tsx +843 -0
  328. package/packages/dashboard/ui/react-components/ProviderConnectionList.tsx +363 -0
  329. package/packages/dashboard/ui/react-components/ProvisioningProgress.tsx +730 -0
  330. package/packages/dashboard/ui/react-components/RepoAccessPanel.tsx +392 -0
  331. package/packages/dashboard/ui/react-components/ServerCard.tsx +202 -0
  332. package/packages/dashboard/ui/react-components/SessionExpiredModal.tsx +128 -0
  333. package/packages/dashboard/ui/react-components/SpawnModal.tsx +794 -0
  334. package/packages/dashboard/ui/react-components/TaskAssignmentUI.tsx +375 -0
  335. package/packages/dashboard/ui/react-components/TerminalProviderSetup.tsx +608 -0
  336. package/packages/dashboard/ui/react-components/ThemeProvider.tsx +325 -0
  337. package/packages/dashboard/ui/react-components/ThinkingIndicator.tsx +231 -0
  338. package/packages/dashboard/ui/react-components/ThreadList.tsx +198 -0
  339. package/packages/dashboard/ui/react-components/ThreadPanel.tsx +346 -0
  340. package/packages/dashboard/ui/react-components/TrajectoryViewer.tsx +698 -0
  341. package/packages/dashboard/ui/react-components/TypingIndicator.tsx +69 -0
  342. package/packages/dashboard/ui/react-components/UsageBanner.tsx +231 -0
  343. package/packages/dashboard/ui/react-components/UserProfilePanel.tsx +233 -0
  344. package/packages/dashboard/ui/react-components/WorkspaceContext.tsx +107 -0
  345. package/packages/dashboard/ui/react-components/WorkspaceSelector.tsx +234 -0
  346. package/packages/dashboard/ui/react-components/WorkspaceStatusIndicator.tsx +370 -0
  347. package/packages/dashboard/ui/react-components/XTermInteractive.tsx +510 -0
  348. package/packages/dashboard/ui/react-components/XTermLogViewer.tsx +719 -0
  349. package/packages/dashboard/ui/react-components/channels/ChannelDialogs.tsx +1411 -0
  350. package/packages/dashboard/ui/react-components/channels/ChannelHeader.tsx +317 -0
  351. package/packages/dashboard/ui/react-components/channels/ChannelMessageList.tsx +463 -0
  352. package/packages/dashboard/ui/react-components/channels/ChannelViewV1.tsx +146 -0
  353. package/packages/dashboard/ui/react-components/channels/MessageInput.tsx +288 -0
  354. package/packages/dashboard/ui/react-components/channels/SearchInput.tsx +172 -0
  355. package/packages/dashboard/ui/react-components/channels/SearchResults.tsx +336 -0
  356. package/packages/dashboard/ui/react-components/channels/api.ts +697 -0
  357. package/packages/dashboard/ui/react-components/channels/index.ts +76 -0
  358. package/packages/dashboard/ui/react-components/channels/mockApi.ts +344 -0
  359. package/packages/dashboard/ui/react-components/channels/types.ts +566 -0
  360. package/packages/dashboard/ui/react-components/hooks/index.ts +57 -0
  361. package/packages/dashboard/ui/react-components/hooks/useAgentLogs.ts +394 -0
  362. package/packages/dashboard/ui/react-components/hooks/useAgents.ts +127 -0
  363. package/packages/dashboard/ui/react-components/hooks/useBroadcastDedup.ts +86 -0
  364. package/packages/dashboard/ui/react-components/hooks/useChannelAdmin.ts +329 -0
  365. package/packages/dashboard/ui/react-components/hooks/useChannelBrowser.ts +239 -0
  366. package/packages/dashboard/ui/react-components/hooks/useChannelCommands.ts +138 -0
  367. package/packages/dashboard/ui/react-components/hooks/useChannels.ts +328 -0
  368. package/packages/dashboard/ui/react-components/hooks/useDebounce.ts +29 -0
  369. package/packages/dashboard/ui/react-components/hooks/useDirectMessage.ts +141 -0
  370. package/packages/dashboard/ui/react-components/hooks/useMessages.ts +309 -0
  371. package/packages/dashboard/ui/react-components/hooks/useOrchestrator.ts +364 -0
  372. package/packages/dashboard/ui/react-components/hooks/usePinnedAgents.ts +140 -0
  373. package/packages/dashboard/ui/react-components/hooks/usePresence.ts +340 -0
  374. package/packages/dashboard/ui/react-components/hooks/useRecentRepos.ts +130 -0
  375. package/packages/dashboard/ui/react-components/hooks/useSession.ts +209 -0
  376. package/packages/dashboard/ui/react-components/hooks/useTrajectory.ts +265 -0
  377. package/packages/dashboard/ui/react-components/hooks/useWebSocket.ts +169 -0
  378. package/packages/dashboard/ui/react-components/hooks/useWorkspaceMembers.ts +120 -0
  379. package/packages/dashboard/ui/react-components/hooks/useWorkspaceRepos.ts +73 -0
  380. package/packages/dashboard/ui/react-components/hooks/useWorkspaceStatus.ts +237 -0
  381. package/packages/dashboard/ui/react-components/index.ts +81 -0
  382. package/packages/dashboard/ui/react-components/layout/Header.tsx +355 -0
  383. package/packages/dashboard/ui/react-components/layout/RepoContextHeader.tsx +361 -0
  384. package/packages/dashboard/ui/react-components/layout/Sidebar.archive.test.tsx +126 -0
  385. package/packages/dashboard/ui/react-components/layout/Sidebar.test.tsx +691 -0
  386. package/packages/dashboard/ui/react-components/layout/Sidebar.tsx +930 -0
  387. package/packages/dashboard/ui/react-components/layout/index.ts +7 -0
  388. package/packages/dashboard/ui/react-components/settings/BillingSettingsPanel.tsx +564 -0
  389. package/packages/dashboard/ui/react-components/settings/SettingsPage.tsx +544 -0
  390. package/packages/dashboard/ui/react-components/settings/TeamSettingsPanel.tsx +560 -0
  391. package/packages/dashboard/ui/react-components/settings/WorkspaceSettingsPanel.tsx +1329 -0
  392. package/packages/dashboard/ui/react-components/settings/index.ts +11 -0
  393. package/packages/dashboard/ui/react-components/settings/types.ts +53 -0
  394. package/packages/dashboard/ui/react-components/utils/messageFormatting.tsx +370 -0
  395. package/packages/dashboard/ui/tailwind.config.js +148 -0
  396. package/packages/dashboard/ui/types/index.ts +304 -0
  397. package/packages/dashboard/ui/types/threading.ts +7 -0
  398. package/packages/dashboard/ui-dist/404.html +1 -0
  399. package/packages/dashboard/ui-dist/_next/static/0AsOfRemPXJmtynCKT-rx/_buildManifest.js +1 -0
  400. package/packages/dashboard/ui-dist/_next/static/0AsOfRemPXJmtynCKT-rx/_ssgManifest.js +1 -0
  401. package/packages/dashboard/ui-dist/_next/static/72btMIJ64BCAB4UgVkpaq/_buildManifest.js +1 -0
  402. package/packages/dashboard/ui-dist/_next/static/72btMIJ64BCAB4UgVkpaq/_ssgManifest.js +1 -0
  403. package/packages/dashboard/ui-dist/_next/static/chunks/116-a883fca163f3a5bc.js +1 -0
  404. package/packages/dashboard/ui-dist/_next/static/chunks/117-c8afed19e821a35d.js +2 -0
  405. package/packages/dashboard/ui-dist/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
  406. package/packages/dashboard/ui-dist/_next/static/chunks/320-900169c942e31422.js +1 -0
  407. package/packages/dashboard/ui-dist/_next/static/chunks/532-bace199897eeab37.js +9 -0
  408. package/packages/dashboard/ui-dist/_next/static/chunks/631-af51bad94027527a.js +1 -0
  409. package/packages/dashboard/ui-dist/_next/static/chunks/648-acb2ff9f77cbfbd3.js +1 -0
  410. package/packages/dashboard/ui-dist/_next/static/chunks/677-7323947c23b35979.js +1 -0
  411. package/packages/dashboard/ui-dist/_next/static/chunks/766-2aea80818f7eb0d8.js +1 -0
  412. package/packages/dashboard/ui-dist/_next/static/chunks/83-4f08122d4e7e79a6.js +1 -0
  413. package/packages/dashboard/ui-dist/_next/static/chunks/847-f1f467060f32afff.js +1 -0
  414. package/packages/dashboard/ui-dist/_next/static/chunks/891-5cb1513eeb97a891.js +1 -0
  415. package/packages/dashboard/ui-dist/_next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +1 -0
  416. package/packages/dashboard/ui-dist/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js +1 -0
  417. package/packages/dashboard/ui-dist/_next/static/chunks/app/app/onboarding/page-f746f29e01fffc43.js +1 -0
  418. package/packages/dashboard/ui-dist/_next/static/chunks/app/app/page-2e525b1dcc790967.js +1 -0
  419. package/packages/dashboard/ui-dist/_next/static/chunks/app/app/page-44813aa26ad19681.js +1 -0
  420. package/packages/dashboard/ui-dist/_next/static/chunks/app/cloud/link/page-5011ae044b90449d.js +1 -0
  421. package/packages/dashboard/ui-dist/_next/static/chunks/app/cloud/link/page-fa1d5842aa90e8a6.js +1 -0
  422. package/packages/dashboard/ui-dist/_next/static/chunks/app/connect-repos/page-03ac6f35a6654ea6.js +1 -0
  423. package/packages/dashboard/ui-dist/_next/static/chunks/app/connect-repos/page-113060009ef35bc2.js +1 -0
  424. package/packages/dashboard/ui-dist/_next/static/chunks/app/history/page-9965d2483011b846.js +1 -0
  425. package/packages/dashboard/ui-dist/_next/static/chunks/app/history/page-b2ce7c96ed0931da.js +1 -0
  426. package/packages/dashboard/ui-dist/_next/static/chunks/app/layout-6b91e33784c20610.js +1 -0
  427. package/packages/dashboard/ui-dist/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
  428. package/packages/dashboard/ui-dist/_next/static/chunks/app/login/page-6ec54eee75877971.js +1 -0
  429. package/packages/dashboard/ui-dist/_next/static/chunks/app/login/page-a0ca6f7ca6a100b8.js +1 -0
  430. package/packages/dashboard/ui-dist/_next/static/chunks/app/metrics/page-1e37ef8e73940b40.js +1 -0
  431. package/packages/dashboard/ui-dist/_next/static/chunks/app/metrics/page-bf2cb1e5915bc92d.js +1 -0
  432. package/packages/dashboard/ui-dist/_next/static/chunks/app/page-4e64923d73c35bc9.js +1 -0
  433. package/packages/dashboard/ui-dist/_next/static/chunks/app/page-7993778218818ace.js +1 -0
  434. package/packages/dashboard/ui-dist/_next/static/chunks/app/pricing/page-0efa024c28ba4597.js +1 -0
  435. package/packages/dashboard/ui-dist/_next/static/chunks/app/pricing/page-9db3ebdfa567a7c9.js +1 -0
  436. package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/page-bcf46064ac4474ce.js +1 -0
  437. package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/page-e65a0010da6ea5be.js +1 -0
  438. package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/setup/[provider]/page-4dbe33f0f7691b7c.js +1 -0
  439. package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/setup/[provider]/page-84161c802b020a1f.js +1 -0
  440. package/packages/dashboard/ui-dist/_next/static/chunks/app/signup/page-18a4665665f6be11.js +1 -0
  441. package/packages/dashboard/ui-dist/_next/static/chunks/app/signup/page-1ede2205b58649ca.js +1 -0
  442. package/packages/dashboard/ui-dist/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
  443. package/packages/dashboard/ui-dist/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +1 -0
  444. package/packages/dashboard/ui-dist/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
  445. package/packages/dashboard/ui-dist/_next/static/chunks/main-5a40a5ae29646e1b.js +1 -0
  446. package/packages/dashboard/ui-dist/_next/static/chunks/main-app-6e8e8d3ef4e0192a.js +1 -0
  447. package/packages/dashboard/ui-dist/_next/static/chunks/main-app-fdbeb09028f57c9f.js +1 -0
  448. package/packages/dashboard/ui-dist/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
  449. package/packages/dashboard/ui-dist/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
  450. package/packages/dashboard/ui-dist/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  451. package/packages/dashboard/ui-dist/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
  452. package/packages/dashboard/ui-dist/_next/static/clUN2n0bz9HCjKI0qlOxU/_buildManifest.js +1 -0
  453. package/packages/dashboard/ui-dist/_next/static/clUN2n0bz9HCjKI0qlOxU/_ssgManifest.js +1 -0
  454. package/packages/dashboard/ui-dist/_next/static/css/4034f236dd1a3178.css +1 -0
  455. package/packages/dashboard/ui-dist/_next/static/css/99c2552394077586.css +1 -0
  456. package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-128.png +0 -0
  457. package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-256.png +0 -0
  458. package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-32.png +0 -0
  459. package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-512.png +0 -0
  460. package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-64.png +0 -0
  461. package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo.svg +45 -0
  462. package/packages/dashboard/ui-dist/alt-logos/logo.svg +38 -0
  463. package/packages/dashboard/ui-dist/alt-logos/monogram-logo-128.png +0 -0
  464. package/packages/dashboard/ui-dist/alt-logos/monogram-logo-256.png +0 -0
  465. package/packages/dashboard/ui-dist/alt-logos/monogram-logo-32.png +0 -0
  466. package/packages/dashboard/ui-dist/alt-logos/monogram-logo-512.png +0 -0
  467. package/packages/dashboard/ui-dist/alt-logos/monogram-logo-64.png +0 -0
  468. package/packages/dashboard/ui-dist/alt-logos/monogram-logo.svg +38 -0
  469. package/packages/dashboard/ui-dist/app/onboarding.html +1 -0
  470. package/packages/dashboard/ui-dist/app/onboarding.txt +7 -0
  471. package/packages/dashboard/ui-dist/app.html +1 -0
  472. package/packages/dashboard/ui-dist/app.txt +7 -0
  473. package/packages/dashboard/ui-dist/apple-icon.png +0 -0
  474. package/packages/dashboard/ui-dist/cloud/link.html +1 -0
  475. package/packages/dashboard/ui-dist/cloud/link.txt +7 -0
  476. package/packages/dashboard/ui-dist/connect-repos.html +1 -0
  477. package/packages/dashboard/ui-dist/connect-repos.txt +7 -0
  478. package/packages/dashboard/ui-dist/history.html +1 -0
  479. package/packages/dashboard/ui-dist/history.txt +7 -0
  480. package/packages/dashboard/ui-dist/index.html +1 -0
  481. package/packages/dashboard/ui-dist/index.txt +7 -0
  482. package/packages/dashboard/ui-dist/login.html +5 -0
  483. package/packages/dashboard/ui-dist/login.txt +7 -0
  484. package/packages/dashboard/ui-dist/metrics.html +1 -0
  485. package/packages/dashboard/ui-dist/metrics.txt +7 -0
  486. package/packages/dashboard/ui-dist/pricing.html +13 -0
  487. package/packages/dashboard/ui-dist/pricing.txt +7 -0
  488. package/packages/dashboard/ui-dist/providers/setup/claude.html +1 -0
  489. package/packages/dashboard/ui-dist/providers/setup/claude.txt +8 -0
  490. package/packages/dashboard/ui-dist/providers/setup/codex.html +1 -0
  491. package/packages/dashboard/ui-dist/providers/setup/codex.txt +8 -0
  492. package/packages/dashboard/ui-dist/providers/setup/cursor.html +1 -0
  493. package/packages/dashboard/ui-dist/providers/setup/cursor.txt +8 -0
  494. package/packages/dashboard/ui-dist/providers.html +1 -0
  495. package/packages/dashboard/ui-dist/providers.txt +7 -0
  496. package/packages/dashboard/ui-dist/signup.html +6 -0
  497. package/packages/dashboard/ui-dist/signup.txt +7 -0
  498. package/packages/dashboard-server/dist/health-worker-manager.d.ts +62 -0
  499. package/packages/dashboard-server/dist/health-worker-manager.js +144 -0
  500. package/packages/dashboard-server/dist/health-worker.d.ts +9 -0
  501. package/packages/dashboard-server/dist/health-worker.js +79 -0
  502. package/packages/dashboard-server/dist/index.d.ts +18 -0
  503. package/packages/dashboard-server/dist/index.js +17 -0
  504. package/packages/dashboard-server/dist/server.js +5099 -0
  505. package/packages/dashboard-server/dist/start.js +13 -0
  506. package/packages/dashboard-server/dist/types/threading.d.ts +8 -0
  507. package/packages/dashboard-server/dist/types/threading.js +2 -0
  508. package/packages/dashboard-server/dist/user-bridge.d.ts +154 -0
  509. package/packages/dashboard-server/dist/user-bridge.js +372 -0
  510. package/packages/dashboard-server/package.json +55 -0
  511. package/packages/hooks/dist/browser.d.ts +2 -0
  512. package/packages/hooks/dist/browser.js +3 -0
  513. package/packages/hooks/dist/index.d.ts +11 -0
  514. package/packages/hooks/dist/index.js +11 -0
  515. package/packages/hooks/dist/registry.js +476 -0
  516. package/packages/hooks/dist/trajectory-hooks.js +183 -0
  517. package/packages/hooks/dist/types.d.ts +285 -0
  518. package/packages/hooks/dist/types.js +10 -0
  519. package/packages/hooks/package.json +57 -0
  520. package/packages/mcp/LICENSE +190 -0
  521. package/packages/mcp/README.md +330 -0
  522. package/packages/mcp/SPEC.md +1922 -0
  523. package/packages/mcp/STAFFING_PLAN.md +294 -0
  524. package/packages/mcp/dist/bin.d.ts +12 -0
  525. package/packages/mcp/dist/bin.js +127 -0
  526. package/packages/mcp/dist/client.d.ts +68 -0
  527. package/packages/mcp/dist/client.js +115 -0
  528. package/packages/mcp/dist/cloud.d.ts +108 -0
  529. package/packages/mcp/dist/cloud.js +328 -0
  530. package/packages/mcp/dist/errors.d.ts +27 -0
  531. package/packages/mcp/dist/errors.js +48 -0
  532. package/packages/mcp/dist/index.d.ts +10 -0
  533. package/packages/mcp/dist/index.js +16 -0
  534. package/packages/mcp/dist/install-cli.d.ts +35 -0
  535. package/packages/mcp/dist/install-cli.js +157 -0
  536. package/packages/mcp/dist/install.d.ts +101 -0
  537. package/packages/mcp/dist/install.js +398 -0
  538. package/packages/mcp/dist/prompts/index.d.ts +2 -0
  539. package/packages/mcp/dist/prompts/index.js +2 -0
  540. package/packages/mcp/dist/prompts/protocol.d.ts +11 -0
  541. package/packages/mcp/dist/prompts/protocol.js +168 -0
  542. package/packages/mcp/dist/resources/agents.d.ts +11 -0
  543. package/packages/mcp/dist/resources/agents.js +17 -0
  544. package/packages/mcp/dist/resources/inbox.d.ts +11 -0
  545. package/packages/mcp/dist/resources/inbox.js +17 -0
  546. package/packages/mcp/dist/resources/index.d.ts +4 -0
  547. package/packages/mcp/dist/resources/index.js +4 -0
  548. package/packages/mcp/dist/resources/project.d.ts +11 -0
  549. package/packages/mcp/dist/resources/project.js +21 -0
  550. package/packages/mcp/dist/server.d.ts +19 -0
  551. package/packages/mcp/dist/server.js +215 -0
  552. package/packages/mcp/dist/simple.d.ts +173 -0
  553. package/packages/mcp/dist/simple.js +120 -0
  554. package/packages/mcp/dist/tools/index.d.ts +10 -0
  555. package/packages/mcp/dist/tools/index.js +10 -0
  556. package/packages/mcp/dist/tools/relay-health.d.ts +23 -0
  557. package/packages/mcp/dist/tools/relay-health.js +138 -0
  558. package/packages/mcp/dist/tools/relay-inbox.d.ts +26 -0
  559. package/packages/mcp/dist/tools/relay-inbox.js +58 -0
  560. package/packages/mcp/dist/tools/relay-logs.d.ts +20 -0
  561. package/packages/mcp/dist/tools/relay-logs.js +88 -0
  562. package/packages/mcp/dist/tools/relay-metrics.d.ts +20 -0
  563. package/packages/mcp/dist/tools/relay-metrics.js +135 -0
  564. package/packages/mcp/dist/tools/relay-release.d.ts +20 -0
  565. package/packages/mcp/dist/tools/relay-release.js +44 -0
  566. package/packages/mcp/dist/tools/relay-send.d.ts +29 -0
  567. package/packages/mcp/dist/tools/relay-send.js +71 -0
  568. package/packages/mcp/dist/tools/relay-spawn.d.ts +36 -0
  569. package/packages/mcp/dist/tools/relay-spawn.js +73 -0
  570. package/packages/mcp/dist/tools/relay-status.d.ts +11 -0
  571. package/packages/mcp/dist/tools/relay-status.js +43 -0
  572. package/packages/mcp/dist/tools/relay-who.d.ts +20 -0
  573. package/packages/mcp/dist/tools/relay-who.js +47 -0
  574. package/packages/mcp/package.json +69 -0
  575. package/packages/memory/dist/memory-hooks.d.ts +60 -0
  576. package/packages/memory/package.json +40 -0
  577. package/packages/policy/dist/agent-policy.js +665 -0
  578. package/packages/policy/dist/index.d.ts +12 -0
  579. package/packages/policy/dist/index.js +12 -0
  580. package/packages/policy/package.json +40 -0
  581. package/packages/protocol/dist/channels.d.ts +137 -0
  582. package/packages/protocol/dist/channels.js +154 -0
  583. package/packages/protocol/dist/framing.d.ts +80 -0
  584. package/packages/protocol/dist/framing.js +206 -0
  585. package/packages/protocol/dist/index.d.ts +5 -0
  586. package/packages/protocol/dist/index.js +5 -0
  587. package/packages/protocol/dist/relay-pty-schemas.d.ts +258 -0
  588. package/packages/protocol/dist/types.d.ts +341 -0
  589. package/packages/protocol/dist/types.js +8 -0
  590. package/packages/protocol/package.json +61 -0
  591. package/packages/resiliency/dist/memory-monitor.js +599 -0
  592. package/packages/resiliency/dist/provider-context.d.ts +100 -0
  593. package/packages/resiliency/package.json +38 -0
  594. package/packages/sdk/README.md +288 -0
  595. package/packages/sdk/dist/client.d.ts +277 -0
  596. package/packages/sdk/dist/client.js +802 -0
  597. package/packages/sdk/dist/discovery.d.ts +29 -0
  598. package/packages/sdk/dist/discovery.js +126 -0
  599. package/packages/sdk/dist/index.d.ts +33 -0
  600. package/packages/sdk/dist/index.js +38 -0
  601. package/packages/sdk/dist/protocol/framing.d.ts +80 -0
  602. package/packages/sdk/dist/protocol/framing.js +206 -0
  603. package/packages/sdk/dist/protocol/index.d.ts +6 -0
  604. package/packages/sdk/dist/protocol/index.js +6 -0
  605. package/packages/sdk/dist/protocol/types.d.ts +341 -0
  606. package/packages/sdk/dist/protocol/types.js +8 -0
  607. package/packages/sdk/dist/standalone.d.ts +87 -0
  608. package/packages/sdk/dist/standalone.js +127 -0
  609. package/packages/sdk/package.json +80 -0
  610. package/packages/spawner/API.md +256 -0
  611. package/packages/spawner/dist/index.d.ts +8 -0
  612. package/packages/spawner/dist/index.js +8 -0
  613. package/packages/spawner/dist/types.d.ts +552 -0
  614. package/packages/spawner/dist/types.js +193 -0
  615. package/packages/spawner/package.json +47 -0
  616. package/packages/state/dist/agent-state.js +120 -0
  617. package/packages/state/dist/index.d.ts +8 -0
  618. package/packages/state/dist/index.js +8 -0
  619. package/packages/state/package.json +37 -0
  620. package/packages/storage/dist/adapter.d.ts +156 -0
  621. package/packages/storage/dist/batched-sqlite-adapter.d.ts +75 -0
  622. package/packages/storage/dist/batched-sqlite-adapter.js +189 -0
  623. package/packages/storage/dist/index.d.ts +5 -0
  624. package/packages/storage/dist/index.js +6 -0
  625. package/packages/storage/dist/sqlite-adapter.d.ts +113 -0
  626. package/packages/storage/dist/sqlite-adapter.js +752 -0
  627. package/packages/storage/package.json +74 -0
  628. package/packages/trajectory/dist/index.d.ts +2 -0
  629. package/packages/trajectory/dist/index.js +2 -0
  630. package/packages/trajectory/dist/integration.js +987 -0
  631. package/packages/trajectory/package.json +40 -0
  632. package/packages/user-directory/dist/index.d.ts +7 -0
  633. package/packages/user-directory/dist/index.js +7 -0
  634. package/packages/user-directory/dist/user-directory.d.ts +121 -0
  635. package/packages/user-directory/dist/user-directory.js +267 -0
  636. package/packages/user-directory/package.json +40 -0
  637. package/packages/utils/dist/command-resolver.js +80 -0
  638. package/packages/utils/dist/error-tracking.d.ts +103 -0
  639. package/packages/utils/dist/error-tracking.js +149 -0
  640. package/packages/utils/dist/index.d.ts +9 -0
  641. package/packages/utils/dist/index.js +9 -0
  642. package/packages/utils/dist/model-mapping.d.ts +28 -0
  643. package/packages/utils/dist/model-mapping.js +55 -0
  644. package/packages/utils/package.json +80 -0
  645. package/packages/wrapper/dist/__fixtures__/claude-outputs.d.ts +49 -0
  646. package/packages/wrapper/dist/__fixtures__/claude-outputs.js +443 -0
  647. package/packages/wrapper/dist/__fixtures__/codex-outputs.d.ts +9 -0
  648. package/packages/wrapper/dist/__fixtures__/codex-outputs.js +94 -0
  649. package/packages/wrapper/dist/__fixtures__/gemini-outputs.d.ts +19 -0
  650. package/packages/wrapper/dist/__fixtures__/gemini-outputs.js +144 -0
  651. package/packages/wrapper/dist/__fixtures__/index.d.ts +68 -0
  652. package/packages/wrapper/dist/__fixtures__/index.js +44 -0
  653. package/packages/wrapper/dist/base-wrapper.d.ts +249 -0
  654. package/packages/wrapper/dist/base-wrapper.js +614 -0
  655. package/packages/wrapper/dist/client.d.ts +254 -0
  656. package/packages/wrapper/dist/client.js +801 -0
  657. package/packages/wrapper/dist/id-generator.d.ts +35 -0
  658. package/packages/wrapper/dist/id-generator.js +60 -0
  659. package/packages/wrapper/dist/idle-detector.d.ts +110 -0
  660. package/packages/wrapper/dist/idle-detector.js +304 -0
  661. package/packages/wrapper/dist/index.d.ts +37 -0
  662. package/packages/wrapper/dist/index.js +47 -0
  663. package/packages/wrapper/dist/parser.d.ts +236 -0
  664. package/packages/wrapper/dist/parser.js +1238 -0
  665. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +409 -0
  666. package/packages/wrapper/dist/relay-pty-orchestrator.js +1930 -0
  667. package/packages/wrapper/dist/shared.d.ts +226 -0
  668. package/packages/wrapper/dist/shared.js +381 -0
  669. package/packages/wrapper/dist/stuck-detector.d.ts +161 -0
  670. package/packages/wrapper/dist/stuck-detector.js +402 -0
  671. package/packages/wrapper/dist/tmux-wrapper.d.ts +352 -0
  672. package/packages/wrapper/dist/tmux-wrapper.js +1800 -0
  673. package/packages/wrapper/dist/trajectory-integration.d.ts +292 -0
  674. package/packages/wrapper/dist/trajectory-integration.js +979 -0
  675. package/packages/wrapper/dist/wrapper-types.d.ts +41 -0
  676. package/packages/wrapper/dist/wrapper-types.js +7 -0
  677. package/packages/wrapper/package.json +63 -0
  678. package/scripts/setup-stripe-products.ts +312 -0
  679. package/scripts/stress-test-orchestrator-integration.mts +1366 -0
  680. package/scripts/stress-test-orchestrator.mjs +584 -0
  681. package/scripts/stress-test-relay-pty.sh +452 -0
  682. package/scripts/verify-schema.js +1 -1
  683. package/turbo.json +37 -0
  684. package/bin/relay-pty +0 -0
  685. package/dist/bridge/config.d.ts +0 -41
  686. package/dist/bridge/config.js +0 -143
  687. package/dist/bridge/index.d.ts +0 -10
  688. package/dist/bridge/index.js +0 -10
  689. package/dist/bridge/multi-project-client.d.ts +0 -99
  690. package/dist/bridge/multi-project-client.js +0 -389
  691. package/dist/bridge/shadow-cli.js +0 -75
  692. package/dist/bridge/shadow-config.d.ts +0 -87
  693. package/dist/bridge/spawner.d.ts +0 -186
  694. package/dist/bridge/spawner.js +0 -920
  695. package/dist/bridge/types.d.ts +0 -129
  696. package/dist/bridge/utils.d.ts +0 -30
  697. package/dist/bridge/utils.js +0 -54
  698. package/dist/cli/index.js +0 -2784
  699. package/dist/cloud/api/admin.js +0 -225
  700. package/dist/cloud/api/billing.js +0 -564
  701. package/dist/cloud/api/cli-pty-runner.d.ts +0 -54
  702. package/dist/cloud/api/cli-pty-runner.js +0 -119
  703. package/dist/cloud/api/codex-auth-helper.js +0 -327
  704. package/dist/cloud/api/consensus.js +0 -259
  705. package/dist/cloud/api/coordinators.js +0 -749
  706. package/dist/cloud/api/daemons.js +0 -535
  707. package/dist/cloud/api/generic-webhooks.js +0 -129
  708. package/dist/cloud/api/github-app.js +0 -223
  709. package/dist/cloud/api/monitoring.js +0 -578
  710. package/dist/cloud/api/nango-auth.js +0 -658
  711. package/dist/cloud/api/onboarding.d.ts +0 -15
  712. package/dist/cloud/api/onboarding.js +0 -666
  713. package/dist/cloud/api/policy.js +0 -229
  714. package/dist/cloud/api/provider-env.d.ts +0 -5
  715. package/dist/cloud/api/provider-env.js +0 -27
  716. package/dist/cloud/api/providers.js +0 -511
  717. package/dist/cloud/api/repos.js +0 -576
  718. package/dist/cloud/api/teams.js +0 -279
  719. package/dist/cloud/api/test-helpers.js +0 -745
  720. package/dist/cloud/api/workspaces.js +0 -1783
  721. package/dist/cloud/billing/plans.js +0 -245
  722. package/dist/cloud/config.d.ts +0 -75
  723. package/dist/cloud/config.js +0 -109
  724. package/dist/cloud/db/drizzle.d.ts +0 -246
  725. package/dist/cloud/db/drizzle.js +0 -1249
  726. package/dist/cloud/db/schema.d.ts +0 -4854
  727. package/dist/cloud/db/schema.js +0 -610
  728. package/dist/cloud/index.d.ts +0 -11
  729. package/dist/cloud/index.js +0 -38
  730. package/dist/cloud/provisioner/index.d.ts +0 -207
  731. package/dist/cloud/provisioner/index.js +0 -2069
  732. package/dist/cloud/server.js +0 -1599
  733. package/dist/cloud/services/index.d.ts +0 -17
  734. package/dist/cloud/services/index.js +0 -25
  735. package/dist/cloud/services/intro-expiration.d.ts +0 -55
  736. package/dist/cloud/services/intro-expiration.js +0 -211
  737. package/dist/cloud/services/nango.d.ts +0 -199
  738. package/dist/cloud/services/nango.js +0 -382
  739. package/dist/cloud/services/persistence.d.ts +0 -131
  740. package/dist/config/relay-config.d.ts +0 -23
  741. package/dist/config/relay-config.js +0 -23
  742. package/dist/continuity/index.d.ts +0 -45
  743. package/dist/continuity/index.js +0 -48
  744. package/dist/continuity/types.d.ts +0 -180
  745. package/dist/continuity/types.js +0 -9
  746. package/dist/daemon/agent-manager.d.ts +0 -134
  747. package/dist/daemon/agent-manager.js +0 -564
  748. package/dist/daemon/agent-registry.js +0 -213
  749. package/dist/daemon/api.d.ts +0 -83
  750. package/dist/daemon/api.js +0 -780
  751. package/dist/daemon/channel-membership-store.d.ts +0 -48
  752. package/dist/daemon/channel-membership-store.js +0 -149
  753. package/dist/daemon/cli-auth.d.ts +0 -82
  754. package/dist/daemon/cli-auth.js +0 -700
  755. package/dist/daemon/cloud-sync.d.ts +0 -150
  756. package/dist/daemon/cloud-sync.js +0 -424
  757. package/dist/daemon/connection.d.ts +0 -130
  758. package/dist/daemon/connection.js +0 -438
  759. package/dist/daemon/consensus-integration.js +0 -371
  760. package/dist/daemon/delivery-tracker.d.ts +0 -34
  761. package/dist/daemon/delivery-tracker.js +0 -104
  762. package/dist/daemon/enhanced-features.d.ts +0 -118
  763. package/dist/daemon/enhanced-features.js +0 -178
  764. package/dist/daemon/index.d.ts +0 -14
  765. package/dist/daemon/index.js +0 -17
  766. package/dist/daemon/orchestrator.d.ts +0 -157
  767. package/dist/daemon/orchestrator.js +0 -792
  768. package/dist/daemon/repo-manager.js +0 -384
  769. package/dist/daemon/router.d.ts +0 -358
  770. package/dist/daemon/router.js +0 -1333
  771. package/dist/daemon/server.d.ts +0 -159
  772. package/dist/daemon/server.js +0 -788
  773. package/dist/daemon/services/browser-testing.d.ts +0 -88
  774. package/dist/daemon/services/browser-testing.js +0 -244
  775. package/dist/daemon/services/container-spawner.d.ts +0 -135
  776. package/dist/daemon/services/container-spawner.js +0 -313
  777. package/dist/daemon/sync-queue.d.ts +0 -116
  778. package/dist/daemon/sync-queue.js +0 -361
  779. package/dist/daemon/types.d.ts +0 -131
  780. package/dist/daemon/user-directory.d.ts +0 -111
  781. package/dist/daemon/user-directory.js +0 -233
  782. package/dist/daemon/workspace-manager.js +0 -314
  783. package/dist/dashboard/out/_next/static/chunks/116-eacf84a131b80db9.js +0 -1
  784. package/dist/dashboard/out/_next/static/chunks/64-f4268c2ac6f4d7d4.js +0 -1
  785. package/dist/dashboard/out/_next/static/chunks/766-aa7c8c9900ff5f53.js +0 -1
  786. package/dist/dashboard/out/_next/static/chunks/891-a024fbe4b619cf6f.js +0 -1
  787. package/dist/dashboard/out/_next/static/chunks/app/app/page-ffad986adfcc8b31.js +0 -1
  788. package/dist/dashboard/out/_next/static/chunks/app/page-671037943b2f2e43.js +0 -1
  789. package/dist/dashboard/out/_next/static/chunks/app/providers/page-57cbd738c6a73859.js +0 -1
  790. package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-5ab0854472b402b0.js +0 -1
  791. package/dist/dashboard/out/_next/static/css/8f9ed310f454e5a5.css +0 -1
  792. package/dist/dashboard-server/server.js +0 -4806
  793. package/dist/dashboard-server/start.js +0 -13
  794. package/dist/dashboard-server/user-bridge.d.ts +0 -138
  795. package/dist/dashboard-server/user-bridge.js +0 -348
  796. package/dist/hooks/index.d.ts +0 -10
  797. package/dist/hooks/index.js +0 -10
  798. package/dist/hooks/registry.js +0 -476
  799. package/dist/hooks/trajectory-hooks.js +0 -183
  800. package/dist/hooks/types.d.ts +0 -284
  801. package/dist/hooks/types.js +0 -8
  802. package/dist/index.d.ts +0 -13
  803. package/dist/index.js +0 -16
  804. package/dist/memory/memory-hooks.d.ts +0 -60
  805. package/dist/policy/agent-policy.js +0 -665
  806. package/dist/protocol/channels.d.ts +0 -211
  807. package/dist/protocol/channels.js +0 -154
  808. package/dist/protocol/framing.d.ts +0 -94
  809. package/dist/protocol/framing.js +0 -240
  810. package/dist/protocol/index.d.ts +0 -4
  811. package/dist/protocol/index.js +0 -4
  812. package/dist/protocol/relay-pty-schemas.d.ts +0 -209
  813. package/dist/protocol/types.d.ts +0 -168
  814. package/dist/protocol/types.js +0 -6
  815. package/dist/resiliency/memory-monitor.js +0 -593
  816. package/dist/resiliency/provider-context.d.ts +0 -100
  817. package/dist/shared/cli-auth-config.js +0 -320
  818. package/dist/state/agent-state.js +0 -120
  819. package/dist/storage/adapter.d.ts +0 -154
  820. package/dist/storage/batched-sqlite-adapter.d.ts +0 -71
  821. package/dist/storage/batched-sqlite-adapter.js +0 -183
  822. package/dist/storage/sqlite-adapter.d.ts +0 -107
  823. package/dist/storage/sqlite-adapter.js +0 -717
  824. package/dist/trajectory/config.d.ts +0 -102
  825. package/dist/trajectory/config.js +0 -185
  826. package/dist/trajectory/index.d.ts +0 -8
  827. package/dist/trajectory/index.js +0 -8
  828. package/dist/trajectory/integration.js +0 -987
  829. package/dist/utils/command-resolver.js +0 -76
  830. package/dist/utils/index.d.ts +0 -4
  831. package/dist/utils/index.js +0 -4
  832. package/dist/utils/project-namespace.d.ts +0 -70
  833. package/dist/utils/project-namespace.js +0 -216
  834. package/dist/wrapper/base-wrapper.d.ts +0 -217
  835. package/dist/wrapper/base-wrapper.js +0 -538
  836. package/dist/wrapper/client.d.ts +0 -199
  837. package/dist/wrapper/client.js +0 -677
  838. package/dist/wrapper/idle-detector.d.ts +0 -102
  839. package/dist/wrapper/idle-detector.js +0 -279
  840. package/dist/wrapper/index.d.ts +0 -4
  841. package/dist/wrapper/index.js +0 -7
  842. package/dist/wrapper/parser.d.ts +0 -230
  843. package/dist/wrapper/parser.js +0 -1178
  844. package/dist/wrapper/pty-wrapper.d.ts +0 -343
  845. package/dist/wrapper/pty-wrapper.js +0 -1593
  846. package/dist/wrapper/relay-pty-orchestrator.d.ts +0 -296
  847. package/dist/wrapper/relay-pty-orchestrator.js +0 -1088
  848. package/dist/wrapper/shared.d.ts +0 -168
  849. package/dist/wrapper/shared.js +0 -291
  850. package/dist/wrapper/stuck-detector.d.ts +0 -101
  851. package/dist/wrapper/stuck-detector.js +0 -228
  852. package/dist/wrapper/tmux-wrapper.d.ts +0 -344
  853. package/dist/wrapper/tmux-wrapper.js +0 -1711
  854. /package/dist/dashboard/out/_next/static/{BffXAqxm-_rUlj2mAnK26 → 72btMIJ64BCAB4UgVkpaq}/_buildManifest.js +0 -0
  855. /package/dist/dashboard/out/_next/static/{BffXAqxm-_rUlj2mAnK26 → 72btMIJ64BCAB4UgVkpaq}/_ssgManifest.js +0 -0
  856. /package/dist/dashboard/out/_next/static/chunks/app/cloud/link/{page-cfeb437f08a12ed9.js → page-5011ae044b90449d.js} +0 -0
  857. /package/dist/dashboard/out/_next/static/chunks/app/history/{page-240f91e8b06ba8ac.js → page-b2ce7c96ed0931da.js} +0 -0
  858. /package/dist/dashboard/out/_next/static/chunks/app/metrics/{page-82938ab8fcf44694.js → page-bf2cb1e5915bc92d.js} +0 -0
  859. /package/dist/{cli → src/cli}/index.d.ts +0 -0
  860. /package/dist/{health-worker-manager.d.ts → src/health-worker-manager.d.ts} +0 -0
  861. /package/dist/{health-worker-manager.js → src/health-worker-manager.js} +0 -0
  862. /package/dist/{health-worker.d.ts → src/health-worker.d.ts} +0 -0
  863. /package/dist/{health-worker.js → src/health-worker.js} +0 -0
  864. /package/{dist/bridge → packages/bridge/dist}/shadow-cli.d.ts +0 -0
  865. /package/{dist/bridge → packages/bridge/dist}/types.js +0 -0
  866. /package/{dist/cloud → packages/cloud/dist}/api/admin.d.ts +0 -0
  867. /package/{dist/cloud → packages/cloud/dist}/api/auth.d.ts +0 -0
  868. /package/{dist/cloud → packages/cloud/dist}/api/auth.js +0 -0
  869. /package/{dist/cloud → packages/cloud/dist}/api/billing.d.ts +0 -0
  870. /package/{dist/cloud → packages/cloud/dist}/api/codex-auth-helper.d.ts +0 -0
  871. /package/{dist/cloud → packages/cloud/dist}/api/consensus.d.ts +0 -0
  872. /package/{dist/cloud → packages/cloud/dist}/api/coordinators.d.ts +0 -0
  873. /package/{dist/cloud → packages/cloud/dist}/api/daemons.d.ts +0 -0
  874. /package/{dist/cloud → packages/cloud/dist}/api/generic-webhooks.d.ts +0 -0
  875. /package/{dist/cloud → packages/cloud/dist}/api/git.d.ts +0 -0
  876. /package/{dist/cloud → packages/cloud/dist}/api/git.js +0 -0
  877. /package/{dist/cloud → packages/cloud/dist}/api/github-app.d.ts +0 -0
  878. /package/{dist/cloud → packages/cloud/dist}/api/middleware/planLimits.d.ts +0 -0
  879. /package/{dist/cloud → packages/cloud/dist}/api/middleware/planLimits.js +0 -0
  880. /package/{dist/cloud → packages/cloud/dist}/api/monitoring.d.ts +0 -0
  881. /package/{dist/cloud → packages/cloud/dist}/api/nango-auth.d.ts +0 -0
  882. /package/{dist/cloud → packages/cloud/dist}/api/policy.d.ts +0 -0
  883. /package/{dist/cloud → packages/cloud/dist}/api/providers.d.ts +0 -0
  884. /package/{dist/cloud → packages/cloud/dist}/api/repos.d.ts +0 -0
  885. /package/{dist/cloud → packages/cloud/dist}/api/teams.d.ts +0 -0
  886. /package/{dist/cloud → packages/cloud/dist}/api/test-helpers.d.ts +0 -0
  887. /package/{dist/cloud → packages/cloud/dist}/api/usage.d.ts +0 -0
  888. /package/{dist/cloud → packages/cloud/dist}/api/usage.js +0 -0
  889. /package/{dist/cloud → packages/cloud/dist}/api/webhooks.d.ts +0 -0
  890. /package/{dist/cloud → packages/cloud/dist}/api/webhooks.js +0 -0
  891. /package/{dist/cloud → packages/cloud/dist}/api/workspaces.d.ts +0 -0
  892. /package/{dist/cloud → packages/cloud/dist}/billing/index.d.ts +0 -0
  893. /package/{dist/cloud → packages/cloud/dist}/billing/index.js +0 -0
  894. /package/{dist/cloud → packages/cloud/dist}/billing/plans.d.ts +0 -0
  895. /package/{dist/cloud → packages/cloud/dist}/billing/service.d.ts +0 -0
  896. /package/{dist/cloud → packages/cloud/dist}/billing/service.js +0 -0
  897. /package/{dist/cloud → packages/cloud/dist}/billing/types.d.ts +0 -0
  898. /package/{dist/cloud → packages/cloud/dist}/billing/types.js +0 -0
  899. /package/{dist/cloud → packages/cloud/dist}/db/bulk-ingest.d.ts +0 -0
  900. /package/{dist/cloud → packages/cloud/dist}/db/bulk-ingest.js +0 -0
  901. /package/{dist/cloud → packages/cloud/dist}/db/index.d.ts +0 -0
  902. /package/{dist/cloud → packages/cloud/dist}/db/index.js +0 -0
  903. /package/{dist/cloud → packages/cloud/dist}/server.d.ts +0 -0
  904. /package/{dist/cloud → packages/cloud/dist}/services/auto-scaler.d.ts +0 -0
  905. /package/{dist/cloud → packages/cloud/dist}/services/auto-scaler.js +0 -0
  906. /package/{dist/cloud → packages/cloud/dist}/services/capacity-manager.d.ts +0 -0
  907. /package/{dist/cloud → packages/cloud/dist}/services/capacity-manager.js +0 -0
  908. /package/{dist/cloud → packages/cloud/dist}/services/ci-agent-spawner.d.ts +0 -0
  909. /package/{dist/cloud → packages/cloud/dist}/services/ci-agent-spawner.js +0 -0
  910. /package/{dist/cloud → packages/cloud/dist}/services/cloud-message-bus.d.ts +0 -0
  911. /package/{dist/cloud → packages/cloud/dist}/services/cloud-message-bus.js +0 -0
  912. /package/{dist/cloud → packages/cloud/dist}/services/compute-enforcement.d.ts +0 -0
  913. /package/{dist/cloud → packages/cloud/dist}/services/compute-enforcement.js +0 -0
  914. /package/{dist/cloud → packages/cloud/dist}/services/coordinator.d.ts +0 -0
  915. /package/{dist/cloud → packages/cloud/dist}/services/coordinator.js +0 -0
  916. /package/{dist/cloud → packages/cloud/dist}/services/mention-handler.d.ts +0 -0
  917. /package/{dist/cloud → packages/cloud/dist}/services/mention-handler.js +0 -0
  918. /package/{dist/cloud → packages/cloud/dist}/services/persistence.js +0 -0
  919. /package/{dist/cloud → packages/cloud/dist}/services/planLimits.d.ts +0 -0
  920. /package/{dist/cloud → packages/cloud/dist}/services/planLimits.js +0 -0
  921. /package/{dist/cloud → packages/cloud/dist}/services/presence-registry.d.ts +0 -0
  922. /package/{dist/cloud → packages/cloud/dist}/services/presence-registry.js +0 -0
  923. /package/{dist/cloud → packages/cloud/dist}/services/scaling-orchestrator.d.ts +0 -0
  924. /package/{dist/cloud → packages/cloud/dist}/services/scaling-orchestrator.js +0 -0
  925. /package/{dist/cloud → packages/cloud/dist}/services/scaling-policy.d.ts +0 -0
  926. /package/{dist/cloud → packages/cloud/dist}/services/scaling-policy.js +0 -0
  927. /package/{dist/cloud → packages/cloud/dist}/services/ssh-security.d.ts +0 -0
  928. /package/{dist/cloud → packages/cloud/dist}/services/ssh-security.js +0 -0
  929. /package/{dist/cloud → packages/cloud/dist}/services/workspace-keepalive.d.ts +0 -0
  930. /package/{dist/cloud → packages/cloud/dist}/services/workspace-keepalive.js +0 -0
  931. /package/{dist/cloud → packages/cloud/dist}/webhooks/index.d.ts +0 -0
  932. /package/{dist/cloud → packages/cloud/dist}/webhooks/index.js +0 -0
  933. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/github.d.ts +0 -0
  934. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/github.js +0 -0
  935. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/index.d.ts +0 -0
  936. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/index.js +0 -0
  937. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/linear.d.ts +0 -0
  938. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/linear.js +0 -0
  939. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/slack.d.ts +0 -0
  940. /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/slack.js +0 -0
  941. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/github.d.ts +0 -0
  942. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/github.js +0 -0
  943. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/index.d.ts +0 -0
  944. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/index.js +0 -0
  945. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/linear.d.ts +0 -0
  946. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/linear.js +0 -0
  947. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/slack.d.ts +0 -0
  948. /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/slack.js +0 -0
  949. /package/{dist/cloud → packages/cloud/dist}/webhooks/router.d.ts +0 -0
  950. /package/{dist/cloud → packages/cloud/dist}/webhooks/router.js +0 -0
  951. /package/{dist/cloud → packages/cloud/dist}/webhooks/rules-engine.d.ts +0 -0
  952. /package/{dist/cloud → packages/cloud/dist}/webhooks/rules-engine.js +0 -0
  953. /package/{dist/cloud → packages/cloud/dist}/webhooks/types.d.ts +0 -0
  954. /package/{dist/cloud → packages/cloud/dist}/webhooks/types.js +0 -0
  955. /package/{dist/utils → packages/config/dist}/agent-config.d.ts +0 -0
  956. /package/{dist/utils → packages/config/dist}/agent-config.js +0 -0
  957. /package/{dist/shared → packages/config/dist}/cli-auth-config.d.ts +0 -0
  958. /package/{dist/bridge → packages/config/dist}/shadow-config.js +0 -0
  959. /package/{dist/bridge → packages/config/dist}/teams-config.d.ts +0 -0
  960. /package/{dist/bridge → packages/config/dist}/teams-config.js +0 -0
  961. /package/{dist/continuity → packages/continuity/dist}/formatter.d.ts +0 -0
  962. /package/{dist/continuity → packages/continuity/dist}/formatter.js +0 -0
  963. /package/{dist/continuity → packages/continuity/dist}/handoff-store.d.ts +0 -0
  964. /package/{dist/continuity → packages/continuity/dist}/handoff-store.js +0 -0
  965. /package/{dist/continuity → packages/continuity/dist}/ledger-store.d.ts +0 -0
  966. /package/{dist/continuity → packages/continuity/dist}/ledger-store.js +0 -0
  967. /package/{dist/continuity → packages/continuity/dist}/manager.d.ts +0 -0
  968. /package/{dist/continuity → packages/continuity/dist}/manager.js +0 -0
  969. /package/{dist/continuity → packages/continuity/dist}/parser.d.ts +0 -0
  970. /package/{dist/continuity → packages/continuity/dist}/parser.js +0 -0
  971. /package/{dist/daemon → packages/daemon/dist}/agent-registry.d.ts +0 -0
  972. /package/{dist/daemon → packages/daemon/dist}/agent-signing.d.ts +0 -0
  973. /package/{dist/daemon → packages/daemon/dist}/agent-signing.js +0 -0
  974. /package/{dist/daemon → packages/daemon/dist}/auth.d.ts +0 -0
  975. /package/{dist/daemon → packages/daemon/dist}/auth.js +0 -0
  976. /package/{dist/daemon → packages/daemon/dist}/consensus-integration.d.ts +0 -0
  977. /package/{dist/daemon → packages/daemon/dist}/consensus.d.ts +0 -0
  978. /package/{dist/daemon → packages/daemon/dist}/consensus.js +0 -0
  979. /package/{dist/daemon → packages/daemon/dist}/rate-limiter.d.ts +0 -0
  980. /package/{dist/daemon → packages/daemon/dist}/rate-limiter.js +0 -0
  981. /package/{dist/daemon → packages/daemon/dist}/registry.d.ts +0 -0
  982. /package/{dist/daemon → packages/daemon/dist}/registry.js +0 -0
  983. /package/{dist/daemon → packages/daemon/dist}/repo-manager.d.ts +0 -0
  984. /package/{dist/daemon → packages/daemon/dist}/types.js +0 -0
  985. /package/{dist/daemon → packages/daemon/dist}/workspace-manager.d.ts +0 -0
  986. /package/{dist/dashboard-server → packages/dashboard-server/dist}/metrics.d.ts +0 -0
  987. /package/{dist/dashboard-server → packages/dashboard-server/dist}/metrics.js +0 -0
  988. /package/{dist/dashboard-server → packages/dashboard-server/dist}/needs-attention.d.ts +0 -0
  989. /package/{dist/dashboard-server → packages/dashboard-server/dist}/needs-attention.js +0 -0
  990. /package/{dist/dashboard-server → packages/dashboard-server/dist}/server.d.ts +0 -0
  991. /package/{dist/dashboard-server → packages/dashboard-server/dist}/start.d.ts +0 -0
  992. /package/{dist/hooks → packages/hooks/dist}/emitter.d.ts +0 -0
  993. /package/{dist/hooks → packages/hooks/dist}/emitter.js +0 -0
  994. /package/{dist/hooks → packages/hooks/dist}/inbox-check/hook.d.ts +0 -0
  995. /package/{dist/hooks → packages/hooks/dist}/inbox-check/hook.js +0 -0
  996. /package/{dist/hooks → packages/hooks/dist}/inbox-check/index.d.ts +0 -0
  997. /package/{dist/hooks → packages/hooks/dist}/inbox-check/index.js +0 -0
  998. /package/{dist/hooks → packages/hooks/dist}/inbox-check/types.d.ts +0 -0
  999. /package/{dist/hooks → packages/hooks/dist}/inbox-check/types.js +0 -0
  1000. /package/{dist/hooks → packages/hooks/dist}/inbox-check/utils.d.ts +0 -0
  1001. /package/{dist/hooks → packages/hooks/dist}/inbox-check/utils.js +0 -0
  1002. /package/{dist/hooks → packages/hooks/dist}/registry.d.ts +0 -0
  1003. /package/{dist/hooks → packages/hooks/dist}/trajectory-hooks.d.ts +0 -0
  1004. /package/{dist/memory → packages/memory/dist}/adapters/index.d.ts +0 -0
  1005. /package/{dist/memory → packages/memory/dist}/adapters/index.js +0 -0
  1006. /package/{dist/memory → packages/memory/dist}/adapters/inmemory.d.ts +0 -0
  1007. /package/{dist/memory → packages/memory/dist}/adapters/inmemory.js +0 -0
  1008. /package/{dist/memory → packages/memory/dist}/adapters/supermemory.d.ts +0 -0
  1009. /package/{dist/memory → packages/memory/dist}/adapters/supermemory.js +0 -0
  1010. /package/{dist/memory → packages/memory/dist}/context-compaction.d.ts +0 -0
  1011. /package/{dist/memory → packages/memory/dist}/context-compaction.js +0 -0
  1012. /package/{dist/memory → packages/memory/dist}/factory.d.ts +0 -0
  1013. /package/{dist/memory → packages/memory/dist}/factory.js +0 -0
  1014. /package/{dist/memory → packages/memory/dist}/index.d.ts +0 -0
  1015. /package/{dist/memory → packages/memory/dist}/index.js +0 -0
  1016. /package/{dist/memory → packages/memory/dist}/memory-hooks.js +0 -0
  1017. /package/{dist/memory → packages/memory/dist}/service.d.ts +0 -0
  1018. /package/{dist/memory → packages/memory/dist}/service.js +0 -0
  1019. /package/{dist/memory → packages/memory/dist}/types.d.ts +0 -0
  1020. /package/{dist/memory → packages/memory/dist}/types.js +0 -0
  1021. /package/{dist/policy → packages/policy/dist}/agent-policy.d.ts +0 -0
  1022. /package/{dist/policy → packages/policy/dist}/cloud-policy-fetcher.d.ts +0 -0
  1023. /package/{dist/policy → packages/policy/dist}/cloud-policy-fetcher.js +0 -0
  1024. /package/{dist/utils → packages/protocol/dist}/id-generator.d.ts +0 -0
  1025. /package/{dist/utils → packages/protocol/dist}/id-generator.js +0 -0
  1026. /package/{dist/protocol → packages/protocol/dist}/relay-pty-schemas.js +0 -0
  1027. /package/{dist/resiliency → packages/resiliency/dist}/context-persistence.d.ts +0 -0
  1028. /package/{dist/resiliency → packages/resiliency/dist}/context-persistence.js +0 -0
  1029. /package/{dist/resiliency → packages/resiliency/dist}/crash-insights.d.ts +0 -0
  1030. /package/{dist/resiliency → packages/resiliency/dist}/crash-insights.js +0 -0
  1031. /package/{dist/resiliency → packages/resiliency/dist}/gossip-health.d.ts +0 -0
  1032. /package/{dist/resiliency → packages/resiliency/dist}/gossip-health.js +0 -0
  1033. /package/{dist/resiliency → packages/resiliency/dist}/health-monitor.d.ts +0 -0
  1034. /package/{dist/resiliency → packages/resiliency/dist}/health-monitor.js +0 -0
  1035. /package/{dist/resiliency → packages/resiliency/dist}/index.d.ts +0 -0
  1036. /package/{dist/resiliency → packages/resiliency/dist}/index.js +0 -0
  1037. /package/{dist/resiliency → packages/resiliency/dist}/leader-watchdog.d.ts +0 -0
  1038. /package/{dist/resiliency → packages/resiliency/dist}/leader-watchdog.js +0 -0
  1039. /package/{dist/resiliency → packages/resiliency/dist}/logger.d.ts +0 -0
  1040. /package/{dist/resiliency → packages/resiliency/dist}/logger.js +0 -0
  1041. /package/{dist/resiliency → packages/resiliency/dist}/memory-monitor.d.ts +0 -0
  1042. /package/{dist/resiliency → packages/resiliency/dist}/metrics.d.ts +0 -0
  1043. /package/{dist/resiliency → packages/resiliency/dist}/metrics.js +0 -0
  1044. /package/{dist/resiliency → packages/resiliency/dist}/provider-context.js +0 -0
  1045. /package/{dist/resiliency → packages/resiliency/dist}/stateless-lead.d.ts +0 -0
  1046. /package/{dist/resiliency → packages/resiliency/dist}/stateless-lead.js +0 -0
  1047. /package/{dist/resiliency → packages/resiliency/dist}/supervisor.d.ts +0 -0
  1048. /package/{dist/resiliency → packages/resiliency/dist}/supervisor.js +0 -0
  1049. /package/{dist/state → packages/state/dist}/agent-state.d.ts +0 -0
  1050. /package/{dist/storage → packages/storage/dist}/adapter.js +0 -0
  1051. /package/{dist/storage → packages/storage/dist}/dead-letter-queue.d.ts +0 -0
  1052. /package/{dist/storage → packages/storage/dist}/dead-letter-queue.js +0 -0
  1053. /package/{dist/storage → packages/storage/dist}/dlq-adapter.d.ts +0 -0
  1054. /package/{dist/storage → packages/storage/dist}/dlq-adapter.js +0 -0
  1055. /package/{dist/trajectory → packages/trajectory/dist}/integration.d.ts +0 -0
  1056. /package/{dist/utils → packages/utils/dist}/command-resolver.d.ts +0 -0
  1057. /package/{dist/utils → packages/utils/dist}/git-remote.d.ts +0 -0
  1058. /package/{dist/utils → packages/utils/dist}/git-remote.js +0 -0
  1059. /package/{dist/utils → packages/utils/dist}/logger.d.ts +0 -0
  1060. /package/{dist/utils → packages/utils/dist}/logger.js +0 -0
  1061. /package/{dist/utils → packages/utils/dist}/name-generator.d.ts +0 -0
  1062. /package/{dist/utils → packages/utils/dist}/name-generator.js +0 -0
  1063. /package/{dist/utils → packages/utils/dist}/precompiled-patterns.d.ts +0 -0
  1064. /package/{dist/utils → packages/utils/dist}/precompiled-patterns.js +0 -0
  1065. /package/{dist/utils → packages/utils/dist}/update-checker.d.ts +0 -0
  1066. /package/{dist/utils → packages/utils/dist}/update-checker.js +0 -0
  1067. /package/{dist/wrapper → packages/wrapper/dist}/auth-detection.d.ts +0 -0
  1068. /package/{dist/wrapper → packages/wrapper/dist}/auth-detection.js +0 -0
  1069. /package/{dist/wrapper → packages/wrapper/dist}/inbox.d.ts +0 -0
  1070. /package/{dist/wrapper → packages/wrapper/dist}/inbox.js +0 -0
  1071. /package/{dist/wrapper → packages/wrapper/dist}/prompt-composer.d.ts +0 -0
  1072. /package/{dist/wrapper → packages/wrapper/dist}/prompt-composer.js +0 -0
  1073. /package/{dist/utils → packages/wrapper/dist}/tmux-resolver.d.ts +0 -0
  1074. /package/{dist/utils → packages/wrapper/dist}/tmux-resolver.js +0 -0
@@ -0,0 +1,1930 @@
1
+ /**
2
+ * RelayPtyOrchestrator - Orchestrates the relay-pty Rust binary
3
+ *
4
+ * This wrapper spawns the relay-pty binary and communicates via Unix socket.
5
+ * It provides the same interface as PtyWrapper but with improved latency
6
+ * (~550ms vs ~1700ms) by using direct PTY writes instead of tmux send-keys.
7
+ *
8
+ * Architecture:
9
+ * 1. Spawn relay-pty --name {agentName} -- {command} as child process
10
+ * 2. Connect to socket for injection:
11
+ * - With WORKSPACE_ID: /tmp/relay/{workspaceId}/sockets/{agentName}.sock
12
+ * - Without: /tmp/relay-pty-{agentName}.sock (legacy)
13
+ * 3. Parse stdout for relay commands (relay-pty echoes all output)
14
+ * 4. Translate SEND envelopes → inject messages via socket
15
+ *
16
+ * @see docs/RUST_WRAPPER_DESIGN.md for protocol details
17
+ */
18
+ import { spawn } from 'node:child_process';
19
+ import { createConnection } from 'node:net';
20
+ import { createHash } from 'node:crypto';
21
+ import { join, dirname } from 'node:path';
22
+ import { existsSync, unlinkSync, mkdirSync, symlinkSync, lstatSync, rmSync, watch, readdirSync } from 'node:fs';
23
+ import { getProjectPaths } from '@agent-relay/config/project-namespace';
24
+ import { fileURLToPath } from 'node:url';
25
+ // Get the directory where this module is located
26
+ const __filename = fileURLToPath(import.meta.url);
27
+ const __dirname = dirname(__filename);
28
+ import { BaseWrapper } from './base-wrapper.js';
29
+ import { parseSummaryWithDetails, parseSessionEndFromOutput } from './parser.js';
30
+ import { stripAnsi, sleep, buildInjectionString, verifyInjection, INJECTION_CONSTANTS, AdaptiveThrottle, } from './shared.js';
31
+ import { getMemoryMonitor, formatBytes, } from '@agent-relay/resiliency';
32
+ // ============================================================================
33
+ // Types for relay-pty socket protocol
34
+ // ============================================================================
35
+ const MAX_SOCKET_PATH_LENGTH = 107;
36
+ function hashWorkspaceId(workspaceId) {
37
+ return createHash('sha256').update(workspaceId).digest('hex').slice(0, 12);
38
+ }
39
+ /**
40
+ * Orchestrator for relay-pty Rust binary
41
+ *
42
+ * Extends BaseWrapper to provide the same interface as PtyWrapper
43
+ * but uses the relay-pty binary for improved injection reliability.
44
+ */
45
+ export class RelayPtyOrchestrator extends BaseWrapper {
46
+ config;
47
+ // Process management
48
+ relayPtyProcess;
49
+ socketPath;
50
+ _logPath;
51
+ _outboxPath;
52
+ _legacyOutboxPath; // Legacy /tmp/relay-outbox path for backwards compat
53
+ _canonicalOutboxPath; // Canonical ~/.agent-relay/outbox path (agents write here)
54
+ _workspaceId; // For symlink setup
55
+ socket;
56
+ socketConnected = false;
57
+ // Output buffering
58
+ outputBuffer = '';
59
+ rawBuffer = '';
60
+ lastParsedLength = 0;
61
+ // Interactive mode (show output to terminal)
62
+ isInteractive = false;
63
+ // Injection state
64
+ pendingInjections = new Map();
65
+ backpressureActive = false;
66
+ readyForMessages = false;
67
+ // Adaptive throttle for message queue - adjusts delay based on success/failure
68
+ throttle = new AdaptiveThrottle();
69
+ // Unread message indicator state
70
+ lastUnreadIndicatorTime = 0;
71
+ UNREAD_INDICATOR_COOLDOWN_MS = 5000; // Don't spam indicators
72
+ // Track whether any output has been received from the CLI
73
+ hasReceivedOutput = false;
74
+ // Queue monitor for stuck message detection
75
+ queueMonitorTimer;
76
+ QUEUE_MONITOR_INTERVAL_MS = 30000; // Check every 30 seconds
77
+ injectionStartTime = 0; // Track when isInjecting was set to true
78
+ MAX_INJECTION_STUCK_MS = 60000; // Force reset after 60 seconds
79
+ // Protocol monitor for detecting agent mistakes (e.g., empty AGENT_RELAY_NAME)
80
+ protocolWatcher;
81
+ protocolReminderCooldown = 0; // Prevent spam
82
+ PROTOCOL_REMINDER_COOLDOWN_MS = 30000; // 30 second cooldown between reminders
83
+ // Periodic protocol reminder for long sessions (agents sometimes forget the protocol)
84
+ periodicReminderTimer;
85
+ PERIODIC_REMINDER_INTERVAL_MS = 45 * 60 * 1000; // 45 minutes
86
+ sessionStartTime = 0;
87
+ // Track if agent is being gracefully stopped (vs crashed)
88
+ isGracefulStop = false;
89
+ // Memory/CPU monitoring
90
+ memoryMonitor;
91
+ memoryAlertHandler = null;
92
+ // Note: sessionEndProcessed and lastSummaryRawContent are inherited from BaseWrapper
93
+ constructor(config) {
94
+ super(config);
95
+ this.config = config;
96
+ // Get project paths (used for logs and local mode)
97
+ const projectPaths = getProjectPaths(config.cwd);
98
+ // Canonical outbox path - agents ALWAYS write here (transparent symlink in workspace mode)
99
+ // Uses ~/.agent-relay/outbox/{agentName}/ so agents don't need to know about workspace IDs
100
+ this._canonicalOutboxPath = join(projectPaths.dataDir, 'outbox', config.name);
101
+ // Check for workspace namespacing (for multi-tenant cloud deployment)
102
+ // WORKSPACE_ID can be in process.env or passed via config.env
103
+ const workspaceId = config.env?.WORKSPACE_ID || process.env.WORKSPACE_ID;
104
+ this._workspaceId = workspaceId;
105
+ if (workspaceId) {
106
+ // Workspace mode: relay-pty watches the actual workspace path
107
+ // Canonical path (~/.agent-relay/outbox/) will be symlinked to workspace path
108
+ const getWorkspacePaths = (id) => {
109
+ const workspaceDir = `/tmp/relay/${id}`;
110
+ return {
111
+ workspaceDir,
112
+ socketPath: `${workspaceDir}/sockets/${config.name}.sock`,
113
+ outboxPath: `${workspaceDir}/outbox/${config.name}`,
114
+ };
115
+ };
116
+ let paths = getWorkspacePaths(workspaceId);
117
+ if (paths.socketPath.length > MAX_SOCKET_PATH_LENGTH) {
118
+ const hashedWorkspaceId = hashWorkspaceId(workspaceId);
119
+ const hashedPaths = getWorkspacePaths(hashedWorkspaceId);
120
+ console.warn(`[relay-pty-orchestrator:${config.name}] Socket path too long (${paths.socketPath.length} chars); using hashed workspace id ${hashedWorkspaceId}`);
121
+ paths = hashedPaths;
122
+ }
123
+ if (paths.socketPath.length > MAX_SOCKET_PATH_LENGTH) {
124
+ throw new Error(`Socket path exceeds ${MAX_SOCKET_PATH_LENGTH} chars: ${paths.socketPath.length}`);
125
+ }
126
+ this.socketPath = paths.socketPath;
127
+ // relay-pty watches the actual workspace path
128
+ this._outboxPath = paths.outboxPath;
129
+ // Legacy path for backwards compat (older agents might still use /tmp/relay-outbox)
130
+ this._legacyOutboxPath = `/tmp/relay-outbox/${config.name}`;
131
+ }
132
+ else {
133
+ // Local mode: use ~/.agent-relay paths directly (no symlinks needed)
134
+ this._outboxPath = this._canonicalOutboxPath;
135
+ // Socket at {projectRoot}/.agent-relay/sockets/{agentName}.sock
136
+ let localSocketPath = join(projectPaths.dataDir, 'sockets', `${config.name}.sock`);
137
+ // If socket path is too long, fall back to /tmp/relay-local/{projectId}/sockets/
138
+ if (localSocketPath.length > MAX_SOCKET_PATH_LENGTH) {
139
+ const tmpSocketPath = `/tmp/relay-local/${projectPaths.projectId}/sockets/${config.name}.sock`;
140
+ console.warn(`[relay-pty-orchestrator:${config.name}] Socket path too long (${localSocketPath.length} chars); using /tmp fallback`);
141
+ localSocketPath = tmpSocketPath;
142
+ }
143
+ this.socketPath = localSocketPath;
144
+ // No legacy path needed for local mode
145
+ this._legacyOutboxPath = this._outboxPath;
146
+ }
147
+ if (this.socketPath.length > MAX_SOCKET_PATH_LENGTH) {
148
+ throw new Error(`Socket path exceeds ${MAX_SOCKET_PATH_LENGTH} chars: ${this.socketPath.length}`);
149
+ }
150
+ // Generate log path using project paths
151
+ this._logPath = join(projectPaths.teamDir, 'worker-logs', `${config.name}.log`);
152
+ // Check if we're running interactively (stdin is a TTY)
153
+ // If headless mode is forced via config, always use pipes
154
+ this.isInteractive = config.headless ? false : (process.stdin.isTTY === true);
155
+ // Initialize memory monitor (shared singleton, 10s polling interval)
156
+ this.memoryMonitor = getMemoryMonitor({ checkIntervalMs: 10_000 });
157
+ }
158
+ /**
159
+ * Debug log - only outputs when debug is enabled
160
+ */
161
+ log(message) {
162
+ if (this.config.debug) {
163
+ console.log(`[relay-pty-orchestrator:${this.config.name}] ${message}`);
164
+ }
165
+ }
166
+ /**
167
+ * Error log - always outputs (errors are important)
168
+ */
169
+ logError(message) {
170
+ if (this.config.debug) {
171
+ console.error(`[relay-pty-orchestrator:${this.config.name}] ERROR: ${message}`);
172
+ }
173
+ }
174
+ /**
175
+ * Get the outbox path for this agent (for documentation purposes)
176
+ */
177
+ get outboxPath() {
178
+ return this._outboxPath;
179
+ }
180
+ // =========================================================================
181
+ // Abstract method implementations (required by BaseWrapper)
182
+ // =========================================================================
183
+ /**
184
+ * Start the relay-pty process and connect to socket
185
+ */
186
+ async start() {
187
+ if (this.running)
188
+ return;
189
+ this.log(` Starting...`);
190
+ // Ensure socket directory exists (for workspace-namespaced paths)
191
+ const socketDir = dirname(this.socketPath);
192
+ try {
193
+ if (!existsSync(socketDir)) {
194
+ mkdirSync(socketDir, { recursive: true });
195
+ this.log(` Created socket directory: ${socketDir}`);
196
+ }
197
+ }
198
+ catch (err) {
199
+ this.logError(` Failed to create socket directory: ${err.message}`);
200
+ }
201
+ // Clean up any stale socket from previous crashed process
202
+ try {
203
+ if (existsSync(this.socketPath)) {
204
+ this.log(` Removing stale socket: ${this.socketPath}`);
205
+ unlinkSync(this.socketPath);
206
+ }
207
+ }
208
+ catch (err) {
209
+ this.logError(` Failed to clean up socket: ${err.message}`);
210
+ }
211
+ // Set up outbox directory structure
212
+ // - Workspace mode:
213
+ // 1. Create actual workspace path /tmp/relay/{workspaceId}/outbox/{name}
214
+ // 2. Symlink canonical ~/.agent-relay/outbox/{name} -> workspace path
215
+ // 3. Optional: symlink /tmp/relay-outbox/{name} -> workspace path (backwards compat)
216
+ // - Local mode: just create ~/.agent-relay/{projectId}/outbox/{name} directly
217
+ try {
218
+ // Ensure the actual outbox directory exists (where relay-pty watches)
219
+ const outboxDir = dirname(this._outboxPath);
220
+ if (!existsSync(outboxDir)) {
221
+ mkdirSync(outboxDir, { recursive: true });
222
+ }
223
+ if (!existsSync(this._outboxPath)) {
224
+ mkdirSync(this._outboxPath, { recursive: true });
225
+ }
226
+ this.log(` Created outbox directory: ${this._outboxPath}`);
227
+ // In workspace mode, create symlinks so agents can use canonical path
228
+ if (this._workspaceId) {
229
+ // Helper to create a symlink, cleaning up existing path first
230
+ const createSymlinkSafe = (linkPath, targetPath) => {
231
+ const linkParent = dirname(linkPath);
232
+ if (!existsSync(linkParent)) {
233
+ mkdirSync(linkParent, { recursive: true });
234
+ }
235
+ if (existsSync(linkPath)) {
236
+ try {
237
+ const stats = lstatSync(linkPath);
238
+ if (stats.isSymbolicLink() || stats.isFile()) {
239
+ unlinkSync(linkPath);
240
+ }
241
+ else if (stats.isDirectory()) {
242
+ rmSync(linkPath, { recursive: true, force: true });
243
+ }
244
+ }
245
+ catch {
246
+ // Ignore cleanup errors
247
+ }
248
+ }
249
+ symlinkSync(targetPath, linkPath);
250
+ this.log(` Created symlink: ${linkPath} -> ${targetPath}`);
251
+ };
252
+ // Symlink canonical path (~/.agent-relay/outbox/{name}) -> workspace path
253
+ // This is the PRIMARY symlink - agents write to canonical path, relay-pty watches workspace path
254
+ if (this._canonicalOutboxPath !== this._outboxPath) {
255
+ createSymlinkSafe(this._canonicalOutboxPath, this._outboxPath);
256
+ }
257
+ // Also create legacy /tmp/relay-outbox symlink for backwards compat with older agents
258
+ if (this._legacyOutboxPath !== this._outboxPath && this._legacyOutboxPath !== this._canonicalOutboxPath) {
259
+ createSymlinkSafe(this._legacyOutboxPath, this._outboxPath);
260
+ }
261
+ }
262
+ }
263
+ catch (err) {
264
+ this.logError(` Failed to set up outbox: ${err.message}`);
265
+ }
266
+ // Find relay-pty binary
267
+ const binaryPath = this.findRelayPtyBinary();
268
+ if (!binaryPath) {
269
+ throw new Error('relay-pty binary not found. Build with: cd relay-pty && cargo build --release');
270
+ }
271
+ this.log(` Using binary: ${binaryPath}`);
272
+ // Connect to relay daemon first
273
+ try {
274
+ await this.client.connect();
275
+ this.log(` Relay daemon connected`);
276
+ }
277
+ catch (err) {
278
+ this.logError(` Relay connect failed: ${err.message}`);
279
+ }
280
+ // Spawn relay-pty process
281
+ await this.spawnRelayPty(binaryPath);
282
+ // Wait for socket to become available and connect
283
+ await this.connectToSocket();
284
+ this.running = true;
285
+ this.readyForMessages = true;
286
+ this.startStuckDetection();
287
+ this.startQueueMonitor();
288
+ this.startProtocolMonitor();
289
+ this.startPeriodicReminder();
290
+ this.log(` Ready for messages`);
291
+ this.log(` Socket connected: ${this.socketConnected}`);
292
+ this.log(` Relay client state: ${this.client.state}`);
293
+ // Process any queued messages
294
+ this.processMessageQueue();
295
+ }
296
+ /**
297
+ * Stop the relay-pty process gracefully
298
+ */
299
+ async stop() {
300
+ if (!this.running)
301
+ return;
302
+ this.isGracefulStop = true; // Mark as graceful to prevent crash broadcast
303
+ this.running = false;
304
+ this.stopStuckDetection();
305
+ this.stopQueueMonitor();
306
+ this.stopProtocolMonitor();
307
+ this.stopPeriodicReminder();
308
+ // Unregister from memory monitor
309
+ this.memoryMonitor.unregister(this.config.name);
310
+ if (this.memoryAlertHandler) {
311
+ this.memoryMonitor.off('alert', this.memoryAlertHandler);
312
+ this.memoryAlertHandler = null;
313
+ }
314
+ this.log(` Stopping...`);
315
+ // Send shutdown command via socket
316
+ if (this.socket && this.socketConnected) {
317
+ try {
318
+ await this.sendSocketRequest({ type: 'shutdown' });
319
+ }
320
+ catch {
321
+ // Ignore errors during shutdown
322
+ }
323
+ }
324
+ // Close socket
325
+ this.disconnectSocket();
326
+ // Kill process if still running
327
+ if (this.relayPtyProcess && !this.relayPtyProcess.killed) {
328
+ this.relayPtyProcess.kill('SIGTERM');
329
+ // Force kill after timeout
330
+ await Promise.race([
331
+ new Promise((resolve) => {
332
+ this.relayPtyProcess?.on('exit', () => resolve());
333
+ }),
334
+ sleep(5000).then(() => {
335
+ if (this.relayPtyProcess && !this.relayPtyProcess.killed) {
336
+ this.relayPtyProcess.kill('SIGKILL');
337
+ }
338
+ }),
339
+ ]);
340
+ }
341
+ // Cleanup relay client
342
+ this.destroyClient();
343
+ // Clean up socket file
344
+ try {
345
+ if (existsSync(this.socketPath)) {
346
+ unlinkSync(this.socketPath);
347
+ this.log(` Cleaned up socket: ${this.socketPath}`);
348
+ }
349
+ }
350
+ catch (err) {
351
+ this.logError(` Failed to clean up socket: ${err.message}`);
352
+ }
353
+ this.log(` Stopped`);
354
+ }
355
+ /**
356
+ * Inject content into the agent via socket
357
+ */
358
+ async performInjection(_content) {
359
+ // This is called by BaseWrapper but we handle injection differently
360
+ // via the socket protocol in processMessageQueue
361
+ throw new Error('Use injectMessage() instead of performInjection()');
362
+ }
363
+ /**
364
+ * Get cleaned output for parsing
365
+ */
366
+ getCleanOutput() {
367
+ return stripAnsi(this.rawBuffer);
368
+ }
369
+ // =========================================================================
370
+ // Process management
371
+ // =========================================================================
372
+ /**
373
+ * Find the relay-pty binary
374
+ */
375
+ findRelayPtyBinary() {
376
+ // Check config path first
377
+ if (this.config.relayPtyPath && existsSync(this.config.relayPtyPath)) {
378
+ return this.config.relayPtyPath;
379
+ }
380
+ // Get the project root (three levels up from packages/wrapper/dist/)
381
+ // packages/wrapper/dist/ -> packages/wrapper -> packages -> project root
382
+ const projectRoot = join(__dirname, '..', '..', '..');
383
+ // Check common locations (ordered by priority)
384
+ const candidates = [
385
+ // Primary: installed by postinstall from platform-specific binary
386
+ join(projectRoot, 'bin', 'relay-pty'),
387
+ // Development: local Rust build
388
+ join(projectRoot, 'relay-pty', 'target', 'release', 'relay-pty'),
389
+ join(projectRoot, 'relay-pty', 'target', 'debug', 'relay-pty'),
390
+ // Local build in cwd (for development)
391
+ join(process.cwd(), 'relay-pty', 'target', 'release', 'relay-pty'),
392
+ join(process.cwd(), 'relay-pty', 'target', 'debug', 'relay-pty'),
393
+ // Installed globally
394
+ '/usr/local/bin/relay-pty',
395
+ // In node_modules (when installed as dependency)
396
+ join(process.cwd(), 'node_modules', 'agent-relay', 'bin', 'relay-pty'),
397
+ join(process.cwd(), 'node_modules', '.bin', 'relay-pty'),
398
+ ];
399
+ for (const candidate of candidates) {
400
+ if (existsSync(candidate)) {
401
+ return candidate;
402
+ }
403
+ }
404
+ return null;
405
+ }
406
+ /**
407
+ * Spawn the relay-pty process
408
+ */
409
+ async spawnRelayPty(binaryPath) {
410
+ // Get terminal dimensions for proper rendering
411
+ const rows = process.stdout.rows || 24;
412
+ const cols = process.stdout.columns || 80;
413
+ const args = [
414
+ '--name', this.config.name,
415
+ '--socket', this.socketPath,
416
+ '--idle-timeout', String(this.config.idleBeforeInjectMs ?? 500),
417
+ '--json-output', // Enable Rust parsing output
418
+ '--rows', String(rows),
419
+ '--cols', String(cols),
420
+ '--log-level', 'warn', // Only show warnings and errors
421
+ '--log-file', this._logPath, // Enable output logging
422
+ '--outbox', this._outboxPath, // Enable file-based relay messages
423
+ '--', this.config.command,
424
+ ...(this.config.args ?? []),
425
+ ];
426
+ this.log(` Spawning: ${binaryPath} ${args.join(' ')}`);
427
+ // For interactive mode, let Rust directly inherit stdin/stdout from the terminal
428
+ // This is more robust than manual forwarding through pipes
429
+ // We still pipe stderr to capture JSON parsed commands
430
+ const stdio = this.isInteractive
431
+ ? ['inherit', 'inherit', 'pipe'] // Rust handles terminal directly
432
+ : ['pipe', 'pipe', 'pipe']; // Headless mode - we handle I/O
433
+ const proc = spawn(binaryPath, args, {
434
+ cwd: this.config.cwd ?? process.cwd(),
435
+ env: {
436
+ ...process.env,
437
+ ...this.config.env,
438
+ AGENT_RELAY_NAME: this.config.name,
439
+ AGENT_RELAY_OUTBOX: this._canonicalOutboxPath, // Agents use this for outbox path
440
+ TERM: 'xterm-256color',
441
+ },
442
+ stdio,
443
+ });
444
+ this.relayPtyProcess = proc;
445
+ // Handle stdout (agent output) - only in headless mode
446
+ if (!this.isInteractive && proc.stdout) {
447
+ proc.stdout.on('data', (data) => {
448
+ const text = data.toString();
449
+ this.handleOutput(text);
450
+ });
451
+ }
452
+ // Handle stderr (relay-pty logs and JSON output) - always needed
453
+ if (proc.stderr) {
454
+ proc.stderr.on('data', (data) => {
455
+ const text = data.toString();
456
+ this.handleStderr(text);
457
+ });
458
+ }
459
+ // Handle exit
460
+ proc.on('exit', (code, signal) => {
461
+ const exitCode = code ?? (signal === 'SIGKILL' ? 137 : 1);
462
+ this.log(` Process exited: code=${exitCode} signal=${signal}`);
463
+ this.running = false;
464
+ // Get crash context before unregistering from memory monitor
465
+ const crashContext = this.memoryMonitor.getCrashContext(this.config.name);
466
+ // Unregister from memory monitor
467
+ this.memoryMonitor.unregister(this.config.name);
468
+ if (this.memoryAlertHandler) {
469
+ this.memoryMonitor.off('alert', this.memoryAlertHandler);
470
+ this.memoryAlertHandler = null;
471
+ }
472
+ // Broadcast crash notification if not a graceful stop
473
+ if (!this.isGracefulStop && this.client.state === 'READY') {
474
+ const canBroadcast = typeof this.client.broadcast === 'function';
475
+ const isNormalExit = exitCode === 0;
476
+ const wasKilled = signal === 'SIGKILL' || signal === 'SIGTERM' || exitCode === 137;
477
+ if (!isNormalExit) {
478
+ const reason = wasKilled
479
+ ? `killed by signal ${signal || 'SIGKILL'}`
480
+ : `exit code ${exitCode}`;
481
+ // Include crash context analysis if available
482
+ const contextInfo = crashContext.likelyCause !== 'unknown'
483
+ ? ` Likely cause: ${crashContext.likelyCause}. ${crashContext.analysisNotes.slice(0, 2).join('. ')}`
484
+ : '';
485
+ const message = `AGENT CRASHED: "${this.config.name}" has died unexpectedly (${reason}).${contextInfo}`;
486
+ this.log(` Broadcasting crash notification: ${message}`);
487
+ if (canBroadcast) {
488
+ this.client.broadcast(message, 'message', {
489
+ isSystemMessage: true,
490
+ agentName: this.config.name,
491
+ exitCode,
492
+ signal: signal || undefined,
493
+ crashType: 'unexpected_exit',
494
+ crashContext: {
495
+ likelyCause: crashContext.likelyCause,
496
+ peakMemory: crashContext.peakMemory,
497
+ averageMemory: crashContext.averageMemory,
498
+ memoryTrend: crashContext.memoryTrend,
499
+ },
500
+ });
501
+ }
502
+ else {
503
+ this.log(' broadcast skipped: client.broadcast not available');
504
+ }
505
+ }
506
+ }
507
+ this.emit('exit', exitCode);
508
+ this.config.onExit?.(exitCode);
509
+ });
510
+ // Handle error
511
+ proc.on('error', (err) => {
512
+ this.logError(` Process error: ${err.message}`);
513
+ this.emit('error', err);
514
+ });
515
+ // Wait for process to start
516
+ await sleep(500);
517
+ if (proc.exitCode !== null) {
518
+ throw new Error(`relay-pty exited immediately with code ${proc.exitCode}`);
519
+ }
520
+ // Register for memory/CPU monitoring
521
+ if (proc.pid) {
522
+ this.memoryMonitor.register(this.config.name, proc.pid);
523
+ this.memoryMonitor.start(); // Idempotent - starts if not already running
524
+ // Set up alert handler to send resource alerts to dashboard only (not other agents)
525
+ this.memoryAlertHandler = (alert) => {
526
+ if (alert.agentName !== this.config.name)
527
+ return;
528
+ if (this.client.state !== 'READY')
529
+ return;
530
+ const message = alert.type === 'recovered'
531
+ ? `AGENT RECOVERED: "${this.config.name}" memory usage returned to normal.`
532
+ : `AGENT RESOURCE ALERT: "${this.config.name}" - ${alert.message} (${formatBytes(alert.currentRss)})`;
533
+ this.log(` Sending resource alert to users: ${message}`);
534
+ // Send to all human users - agents don't need to know about each other's resource usage
535
+ this.client.sendMessage('@users', message, 'message', {
536
+ isSystemMessage: true,
537
+ agentName: this.config.name,
538
+ alertType: alert.type,
539
+ currentMemory: alert.currentRss,
540
+ threshold: alert.threshold,
541
+ recommendation: alert.recommendation,
542
+ });
543
+ };
544
+ this.memoryMonitor.on('alert', this.memoryAlertHandler);
545
+ }
546
+ }
547
+ /**
548
+ * Handle output from relay-pty stdout (headless mode only)
549
+ * In interactive mode, stdout goes directly to terminal via inherited stdio
550
+ */
551
+ handleOutput(data) {
552
+ // Skip processing if agent is no longer running (prevents ghost messages after release)
553
+ if (!this.running) {
554
+ return;
555
+ }
556
+ this.rawBuffer += data;
557
+ this.outputBuffer += data;
558
+ this.hasReceivedOutput = true;
559
+ // Feed to idle detector
560
+ this.feedIdleDetectorOutput(data);
561
+ // Check for unread messages and append indicator if needed
562
+ const indicator = this.formatUnreadIndicator();
563
+ const outputWithIndicator = indicator ? data + indicator : data;
564
+ // Emit output event (with indicator if present)
565
+ this.emit('output', outputWithIndicator);
566
+ // Stream to daemon if configured
567
+ if (this.config.streamLogs !== false && this.client.state === 'READY') {
568
+ this.client.sendLog(outputWithIndicator);
569
+ }
570
+ // Parse for relay commands
571
+ this.parseRelayCommands();
572
+ // Check for summary and session end
573
+ const cleanContent = stripAnsi(this.rawBuffer);
574
+ this.checkForSummary(cleanContent);
575
+ this.checkForSessionEnd(cleanContent);
576
+ }
577
+ /**
578
+ * Format an unread message indicator if there are pending messages.
579
+ * Returns empty string if no pending messages or within cooldown period.
580
+ *
581
+ * Example output:
582
+ * ───────────────────────────
583
+ * 📬 2 unread messages (from: Alice, Bob)
584
+ */
585
+ formatUnreadIndicator() {
586
+ const queueLength = this.messageQueue.length;
587
+ if (queueLength === 0) {
588
+ return '';
589
+ }
590
+ // Check cooldown to avoid spamming
591
+ const now = Date.now();
592
+ if (now - this.lastUnreadIndicatorTime < this.UNREAD_INDICATOR_COOLDOWN_MS) {
593
+ return '';
594
+ }
595
+ this.lastUnreadIndicatorTime = now;
596
+ // Collect unique sender names
597
+ const senders = [...new Set(this.messageQueue.map(m => m.from))];
598
+ const senderList = senders.slice(0, 3).join(', ');
599
+ const moreCount = senders.length > 3 ? ` +${senders.length - 3} more` : '';
600
+ const line = '─'.repeat(27);
601
+ const messageWord = queueLength === 1 ? 'message' : 'messages';
602
+ return `\n${line}\n📬 ${queueLength} unread ${messageWord} (from: ${senderList}${moreCount})\n`;
603
+ }
604
+ /**
605
+ * Handle stderr from relay-pty (logs and JSON parsed commands)
606
+ */
607
+ handleStderr(data) {
608
+ // Skip processing if agent is no longer running (prevents ghost messages after release)
609
+ if (!this.running) {
610
+ return;
611
+ }
612
+ // relay-pty outputs JSON parsed commands to stderr with --json-output
613
+ const lines = data.split('\n').filter(l => l.trim());
614
+ for (const line of lines) {
615
+ if (line.startsWith('{')) {
616
+ // JSON output - parsed relay command from Rust
617
+ try {
618
+ const parsed = JSON.parse(line);
619
+ if (parsed.type === 'relay_command' && parsed.kind) {
620
+ // Log parsed commands (only in debug mode to avoid TUI pollution)
621
+ if (parsed.kind === 'spawn' || parsed.kind === 'release') {
622
+ this.log(`Rust parsed [${parsed.kind}]: ${JSON.stringify({
623
+ spawn_name: parsed.spawn_name,
624
+ spawn_cli: parsed.spawn_cli,
625
+ spawn_task: parsed.spawn_task?.substring(0, 50),
626
+ release_name: parsed.release_name,
627
+ })}`);
628
+ }
629
+ else {
630
+ this.log(`Rust parsed [${parsed.kind}]: ${parsed.from} -> ${parsed.to}`);
631
+ }
632
+ this.handleRustParsedCommand(parsed);
633
+ }
634
+ }
635
+ catch (e) {
636
+ // Not JSON, just log (only in debug mode)
637
+ if (this.config.debug) {
638
+ console.error(`[relay-pty:${this.config.name}] ${line}`);
639
+ }
640
+ }
641
+ }
642
+ else {
643
+ // Non-JSON stderr - only show in debug mode (logs, info messages)
644
+ if (this.config.debug) {
645
+ console.error(`[relay-pty:${this.config.name}] ${line}`);
646
+ }
647
+ }
648
+ }
649
+ }
650
+ /**
651
+ * Handle a parsed command from Rust relay-pty
652
+ * Rust outputs structured JSON with 'kind' field: "message", "spawn", "release"
653
+ */
654
+ handleRustParsedCommand(parsed) {
655
+ switch (parsed.kind) {
656
+ case 'spawn':
657
+ if (parsed.spawn_name && parsed.spawn_cli) {
658
+ this.log(` Spawn detected: ${parsed.spawn_name} (${parsed.spawn_cli})`);
659
+ this.handleSpawnCommand(parsed.spawn_name, parsed.spawn_cli, parsed.spawn_task || '');
660
+ }
661
+ break;
662
+ case 'release':
663
+ if (parsed.release_name) {
664
+ this.log(`Release: ${parsed.release_name}`);
665
+ this.handleReleaseCommand(parsed.release_name);
666
+ }
667
+ else {
668
+ this.logError(`Missing release_name in parsed command: ${JSON.stringify(parsed)}`);
669
+ }
670
+ break;
671
+ case 'message':
672
+ default:
673
+ this.sendRelayCommand({
674
+ to: parsed.to,
675
+ kind: 'message',
676
+ body: parsed.body,
677
+ thread: parsed.thread,
678
+ raw: parsed.raw,
679
+ });
680
+ break;
681
+ }
682
+ }
683
+ /**
684
+ * Handle spawn command (from Rust stderr JSON parsing)
685
+ *
686
+ * Note: We do NOT send the initial task message here because the spawner
687
+ * now handles it after waitUntilCliReady(). Sending it here would cause
688
+ * duplicate task delivery.
689
+ */
690
+ handleSpawnCommand(name, cli, task) {
691
+ const key = `spawn:${name}:${cli}`;
692
+ if (this.processedSpawnCommands.has(key)) {
693
+ this.log(`Spawn already processed: ${key}`);
694
+ return;
695
+ }
696
+ this.processedSpawnCommands.add(key);
697
+ // Log spawn attempts (only in debug mode to avoid TUI pollution)
698
+ this.log(`SPAWN REQUEST: ${name} (${cli})`);
699
+ this.log(` dashboardPort=${this.config.dashboardPort}, onSpawn=${!!this.config.onSpawn}`);
700
+ // Try dashboard API first, fall back to callback
701
+ // The spawner will send the task after waitUntilCliReady()
702
+ if (this.config.dashboardPort) {
703
+ this.log(`Calling dashboard API at port ${this.config.dashboardPort}`);
704
+ this.spawnViaDashboardApi(name, cli, task)
705
+ .then(() => {
706
+ this.log(`SPAWN SUCCESS: ${name} via dashboard API`);
707
+ })
708
+ .catch(err => {
709
+ this.logError(`SPAWN FAILED: ${name} - ${err.message}`);
710
+ if (this.config.onSpawn) {
711
+ this.log(`Falling back to onSpawn callback`);
712
+ Promise.resolve(this.config.onSpawn(name, cli, task))
713
+ .catch(e => this.logError(`SPAWN CALLBACK FAILED: ${e.message}`));
714
+ }
715
+ });
716
+ }
717
+ else if (this.config.onSpawn) {
718
+ this.log(`Using onSpawn callback directly`);
719
+ Promise.resolve(this.config.onSpawn(name, cli, task))
720
+ .catch(e => this.logError(`SPAWN CALLBACK FAILED: ${e.message}`));
721
+ }
722
+ else {
723
+ this.logError(`SPAWN FAILED: No spawn mechanism available! (dashboardPort=${this.config.dashboardPort}, onSpawn=${!!this.config.onSpawn})`);
724
+ }
725
+ }
726
+ /**
727
+ * Handle release command
728
+ */
729
+ handleReleaseCommand(name) {
730
+ const key = `release:${name}`;
731
+ if (this.processedReleaseCommands.has(key)) {
732
+ return;
733
+ }
734
+ this.processedReleaseCommands.add(key);
735
+ this.log(` Release: ${name}`);
736
+ // Try dashboard API first, fall back to callback
737
+ if (this.config.dashboardPort) {
738
+ this.releaseViaDashboardApi(name).catch(err => {
739
+ this.logError(` Dashboard release failed: ${err.message}`);
740
+ this.config.onRelease?.(name);
741
+ });
742
+ }
743
+ else if (this.config.onRelease) {
744
+ this.config.onRelease(name);
745
+ }
746
+ }
747
+ /**
748
+ * Spawn agent via dashboard API
749
+ */
750
+ async spawnViaDashboardApi(name, cli, task) {
751
+ const url = `http://localhost:${this.config.dashboardPort}/api/spawn`;
752
+ const body = {
753
+ name,
754
+ cli,
755
+ task,
756
+ spawnerName: this.config.name, // Include spawner name so task appears from correct agent
757
+ };
758
+ try {
759
+ const response = await fetch(url, {
760
+ method: 'POST',
761
+ headers: { 'Content-Type': 'application/json' },
762
+ body: JSON.stringify(body),
763
+ });
764
+ if (!response.ok) {
765
+ const errorBody = await response.text().catch(() => 'unknown');
766
+ throw new Error(`HTTP ${response.status}: ${errorBody}`);
767
+ }
768
+ const result = await response.json().catch(() => ({}));
769
+ if (result.success === false) {
770
+ throw new Error(result.error || 'Spawn failed without specific error');
771
+ }
772
+ }
773
+ catch (err) {
774
+ // Enhance error with context
775
+ if (err.code === 'ECONNREFUSED') {
776
+ throw new Error(`Dashboard not reachable at ${url} (connection refused)`);
777
+ }
778
+ throw err;
779
+ }
780
+ }
781
+ /**
782
+ * Release agent via dashboard API
783
+ */
784
+ async releaseViaDashboardApi(name) {
785
+ const response = await fetch(`http://localhost:${this.config.dashboardPort}/api/spawned/${encodeURIComponent(name)}`, {
786
+ method: 'DELETE',
787
+ });
788
+ if (!response.ok) {
789
+ const body = await response.json().catch(() => ({ error: 'Unknown' }));
790
+ throw new Error(`HTTP ${response.status}: ${body.error || 'Unknown error'}`);
791
+ }
792
+ this.log(`Released ${name} via dashboard API`);
793
+ }
794
+ // =========================================================================
795
+ // Socket communication
796
+ // =========================================================================
797
+ /**
798
+ * Connect to the relay-pty socket
799
+ */
800
+ async connectToSocket() {
801
+ const timeout = this.config.socketConnectTimeoutMs ?? 5000;
802
+ const maxAttempts = this.config.socketReconnectAttempts ?? 3;
803
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
804
+ try {
805
+ await this.attemptSocketConnection(timeout);
806
+ this.log(` Socket connected`);
807
+ return;
808
+ }
809
+ catch (err) {
810
+ this.logError(` Socket connect attempt ${attempt}/${maxAttempts} failed: ${err.message}`);
811
+ if (attempt < maxAttempts) {
812
+ await sleep(1000 * attempt); // Exponential backoff
813
+ }
814
+ }
815
+ }
816
+ throw new Error(`Failed to connect to socket after ${maxAttempts} attempts`);
817
+ }
818
+ /**
819
+ * Attempt a single socket connection
820
+ */
821
+ attemptSocketConnection(timeout) {
822
+ return new Promise((resolve, reject) => {
823
+ const timer = setTimeout(() => {
824
+ reject(new Error('Socket connection timeout'));
825
+ }, timeout);
826
+ this.socket = createConnection(this.socketPath, () => {
827
+ clearTimeout(timer);
828
+ this.socketConnected = true;
829
+ resolve();
830
+ });
831
+ this.socket.on('error', (err) => {
832
+ clearTimeout(timer);
833
+ this.socketConnected = false;
834
+ reject(err);
835
+ });
836
+ this.socket.on('close', () => {
837
+ this.socketConnected = false;
838
+ this.log(` Socket closed`);
839
+ });
840
+ // Handle incoming data (responses)
841
+ let buffer = '';
842
+ this.socket.on('data', (data) => {
843
+ buffer += data.toString();
844
+ // Process complete lines
845
+ const lines = buffer.split('\n');
846
+ buffer = lines.pop() ?? ''; // Keep incomplete line in buffer
847
+ for (const line of lines) {
848
+ if (line.trim()) {
849
+ this.handleSocketResponse(line);
850
+ }
851
+ }
852
+ });
853
+ });
854
+ }
855
+ /**
856
+ * Disconnect from socket
857
+ */
858
+ disconnectSocket() {
859
+ if (this.socket) {
860
+ this.socket.destroy();
861
+ this.socket = undefined;
862
+ this.socketConnected = false;
863
+ }
864
+ // Reject all pending injections
865
+ for (const [_id, pending] of this.pendingInjections) {
866
+ clearTimeout(pending.timeout);
867
+ pending.reject(new Error('Socket disconnected'));
868
+ }
869
+ this.pendingInjections.clear();
870
+ }
871
+ /**
872
+ * Send a request to the socket and optionally wait for response
873
+ */
874
+ sendSocketRequest(request) {
875
+ return new Promise((resolve, reject) => {
876
+ if (!this.socket || !this.socketConnected) {
877
+ reject(new Error('Socket not connected'));
878
+ return;
879
+ }
880
+ const json = JSON.stringify(request) + '\n';
881
+ this.socket.write(json, (err) => {
882
+ if (err) {
883
+ reject(err);
884
+ }
885
+ else {
886
+ resolve();
887
+ }
888
+ });
889
+ });
890
+ }
891
+ /**
892
+ * Handle a response from the socket
893
+ */
894
+ handleSocketResponse(line) {
895
+ try {
896
+ const response = JSON.parse(line);
897
+ switch (response.type) {
898
+ case 'inject_result':
899
+ // handleInjectResult is async (does verification), but we don't await here
900
+ // Errors are handled internally by the method
901
+ this.handleInjectResult(response).catch((err) => {
902
+ this.logError(` Error handling inject result: ${err.message}`);
903
+ });
904
+ break;
905
+ case 'status':
906
+ // Status responses are typically requested explicitly
907
+ this.log(` Status: idle=${response.agent_idle} queue=${response.queue_length}`);
908
+ break;
909
+ case 'backpressure':
910
+ this.handleBackpressure(response);
911
+ break;
912
+ case 'error':
913
+ this.logError(` Socket error: ${response.message}`);
914
+ break;
915
+ case 'shutdown_ack':
916
+ this.log(` Shutdown acknowledged`);
917
+ break;
918
+ }
919
+ }
920
+ catch (err) {
921
+ this.logError(` Failed to parse socket response: ${err.message}`);
922
+ }
923
+ }
924
+ /**
925
+ * Handle injection result response
926
+ * After Rust reports 'delivered', verifies the message appeared in output.
927
+ * If verification fails, retries up to MAX_RETRIES times.
928
+ */
929
+ async handleInjectResult(response) {
930
+ this.log(` handleInjectResult: id=${response.id.substring(0, 8)} status=${response.status}`);
931
+ const pending = this.pendingInjections.get(response.id);
932
+ if (!pending) {
933
+ // Response for unknown message - might be from a previous session
934
+ this.log(` No pending injection found for ${response.id.substring(0, 8)}`);
935
+ return;
936
+ }
937
+ if (response.status === 'delivered') {
938
+ // Rust says it sent the message + Enter key
939
+ // Now verify the message actually appeared in the terminal output
940
+ this.log(` Message ${pending.shortId} marked delivered by Rust, verifying in output...`);
941
+ // In interactive mode, we can't verify because stdout goes directly to terminal
942
+ // Trust Rust's "delivered" status in this case
943
+ if (this.isInteractive) {
944
+ this.log(` Interactive mode - trusting Rust delivery status`);
945
+ clearTimeout(pending.timeout);
946
+ this.pendingInjections.delete(response.id);
947
+ if (pending.retryCount === 0) {
948
+ this.injectionMetrics.successFirstTry++;
949
+ }
950
+ else {
951
+ this.injectionMetrics.successWithRetry++;
952
+ }
953
+ this.injectionMetrics.total++;
954
+ pending.resolve(true);
955
+ this.log(` Message ${pending.shortId} delivered (interactive mode) ✓`);
956
+ return;
957
+ }
958
+ // Skip verification if queue is backing up - trust Rust's delivery status
959
+ // relay-pty writes directly to PTY which is more reliable than tmux
960
+ const queueBackingUp = this.messageQueue.length >= 2;
961
+ if (queueBackingUp) {
962
+ this.log(` Queue backing up (${this.messageQueue.length} pending), skipping verification for ${pending.shortId}`);
963
+ clearTimeout(pending.timeout);
964
+ this.pendingInjections.delete(response.id);
965
+ if (pending.retryCount === 0) {
966
+ this.injectionMetrics.successFirstTry++;
967
+ }
968
+ else {
969
+ this.injectionMetrics.successWithRetry++;
970
+ }
971
+ this.injectionMetrics.total++;
972
+ pending.resolve(true);
973
+ return;
974
+ }
975
+ // Give a brief moment for output to be captured
976
+ await sleep(100);
977
+ // Verify the message pattern appears in captured output
978
+ const verified = await verifyInjection(pending.shortId, pending.from, async () => this.getCleanOutput());
979
+ if (verified) {
980
+ clearTimeout(pending.timeout);
981
+ this.pendingInjections.delete(response.id);
982
+ // Update metrics based on retry count (0 = first try)
983
+ if (pending.retryCount === 0) {
984
+ this.injectionMetrics.successFirstTry++;
985
+ }
986
+ else {
987
+ this.injectionMetrics.successWithRetry++;
988
+ this.log(` Message ${pending.shortId} succeeded on attempt ${pending.retryCount + 1}`);
989
+ }
990
+ this.injectionMetrics.total++;
991
+ pending.resolve(true);
992
+ this.log(` Message ${pending.shortId} verified in output ✓`);
993
+ }
994
+ else {
995
+ // Message was "delivered" but not found in output
996
+ // This is the bug case - Enter key may not have been processed
997
+ this.log(` Message ${pending.shortId} NOT found in output after delivery`);
998
+ // Check if we should retry
999
+ if (pending.retryCount < INJECTION_CONSTANTS.MAX_RETRIES - 1) {
1000
+ this.log(` Retrying injection (attempt ${pending.retryCount + 2}/${INJECTION_CONSTANTS.MAX_RETRIES})`);
1001
+ clearTimeout(pending.timeout);
1002
+ this.pendingInjections.delete(response.id);
1003
+ // Wait before retry with backoff
1004
+ await sleep(INJECTION_CONSTANTS.RETRY_BACKOFF_MS * (pending.retryCount + 1));
1005
+ // IMPORTANT: Check again if message appeared (late verification / race condition fix)
1006
+ // The previous injection may have succeeded but verification timed out
1007
+ const lateVerified = await verifyInjection(pending.shortId, pending.from, async () => this.getCleanOutput());
1008
+ if (lateVerified) {
1009
+ this.log(` Message ${pending.shortId} found on late verification, skipping retry`);
1010
+ if (pending.retryCount === 0) {
1011
+ this.injectionMetrics.successFirstTry++;
1012
+ }
1013
+ else {
1014
+ this.injectionMetrics.successWithRetry++;
1015
+ }
1016
+ this.injectionMetrics.total++;
1017
+ pending.resolve(true);
1018
+ return;
1019
+ }
1020
+ // Re-inject by sending another socket request
1021
+ // The original promise will be resolved when this retry completes
1022
+ // Prepend [RETRY] to help agent notice this is a retry
1023
+ const retryBody = pending.originalBody.startsWith('[RETRY]')
1024
+ ? pending.originalBody
1025
+ : `[RETRY] ${pending.originalBody}`;
1026
+ const retryRequest = {
1027
+ type: 'inject',
1028
+ id: response.id,
1029
+ from: pending.from,
1030
+ body: retryBody,
1031
+ priority: 1, // Higher priority for retries
1032
+ };
1033
+ // Create new pending entry with incremented retry count
1034
+ const newTimeout = setTimeout(() => {
1035
+ this.logError(` Retry timeout for ${pending.shortId}`);
1036
+ this.pendingInjections.delete(response.id);
1037
+ pending.resolve(false);
1038
+ }, 30000);
1039
+ this.pendingInjections.set(response.id, {
1040
+ ...pending,
1041
+ timeout: newTimeout,
1042
+ retryCount: pending.retryCount + 1,
1043
+ originalBody: retryBody, // Use retry body for subsequent retries
1044
+ });
1045
+ this.sendSocketRequest(retryRequest).catch((err) => {
1046
+ this.logError(` Retry request failed: ${err.message}`);
1047
+ clearTimeout(newTimeout);
1048
+ this.pendingInjections.delete(response.id);
1049
+ pending.resolve(false);
1050
+ });
1051
+ }
1052
+ else {
1053
+ // Max retries exceeded
1054
+ this.logError(` Message ${pending.shortId} failed after ${INJECTION_CONSTANTS.MAX_RETRIES} attempts - NOT found in output`);
1055
+ clearTimeout(pending.timeout);
1056
+ this.pendingInjections.delete(response.id);
1057
+ this.injectionMetrics.failed++;
1058
+ this.injectionMetrics.total++;
1059
+ pending.resolve(false);
1060
+ this.emit('injection-failed', {
1061
+ messageId: response.id,
1062
+ from: pending.from,
1063
+ error: 'Message delivered but not verified in output after max retries',
1064
+ });
1065
+ }
1066
+ }
1067
+ }
1068
+ else if (response.status === 'failed') {
1069
+ clearTimeout(pending.timeout);
1070
+ this.pendingInjections.delete(response.id);
1071
+ this.injectionMetrics.failed++;
1072
+ this.injectionMetrics.total++;
1073
+ pending.resolve(false);
1074
+ this.logError(` Message ${pending.shortId} failed: ${response.error}`);
1075
+ this.emit('injection-failed', {
1076
+ messageId: response.id,
1077
+ from: pending.from,
1078
+ error: response.error ?? 'Unknown error',
1079
+ });
1080
+ }
1081
+ // queued/injecting are intermediate states - wait for final status
1082
+ }
1083
+ /**
1084
+ * Handle backpressure notification
1085
+ */
1086
+ handleBackpressure(response) {
1087
+ const wasActive = this.backpressureActive;
1088
+ this.backpressureActive = !response.accept;
1089
+ if (this.backpressureActive !== wasActive) {
1090
+ this.log(` Backpressure: ${this.backpressureActive ? 'ACTIVE' : 'cleared'} (queue=${response.queue_length})`);
1091
+ this.emit('backpressure', { queueLength: response.queue_length, accept: response.accept });
1092
+ // Resume processing if backpressure cleared
1093
+ if (!this.backpressureActive) {
1094
+ this.processMessageQueue();
1095
+ }
1096
+ }
1097
+ }
1098
+ // =========================================================================
1099
+ // Message handling
1100
+ // =========================================================================
1101
+ /**
1102
+ * Inject a message into the agent via socket
1103
+ */
1104
+ async injectMessage(msg, retryCount = 0) {
1105
+ const shortId = msg.messageId.substring(0, 8);
1106
+ this.log(` === INJECT START: ${shortId} from ${msg.from} (attempt ${retryCount + 1}) ===`);
1107
+ if (!this.socket || !this.socketConnected) {
1108
+ this.logError(` Cannot inject - socket not connected`);
1109
+ return false;
1110
+ }
1111
+ // Build injection content
1112
+ const content = buildInjectionString(msg);
1113
+ this.log(` Injection content (${content.length} bytes): ${content.substring(0, 100)}...`);
1114
+ // Create request
1115
+ const request = {
1116
+ type: 'inject',
1117
+ id: msg.messageId,
1118
+ from: msg.from,
1119
+ body: content,
1120
+ priority: msg.importance ?? 0,
1121
+ };
1122
+ this.log(` Sending inject request to socket...`);
1123
+ // Create promise for result
1124
+ return new Promise((resolve, reject) => {
1125
+ const timeout = setTimeout(() => {
1126
+ this.logError(` Inject timeout for ${shortId} after 30s`);
1127
+ this.pendingInjections.delete(msg.messageId);
1128
+ resolve(false); // Timeout = failure
1129
+ }, 30000); // 30 second timeout for injection
1130
+ this.pendingInjections.set(msg.messageId, {
1131
+ resolve,
1132
+ reject,
1133
+ timeout,
1134
+ from: msg.from,
1135
+ shortId,
1136
+ retryCount,
1137
+ originalBody: content,
1138
+ });
1139
+ // Send request
1140
+ this.sendSocketRequest(request)
1141
+ .then(() => {
1142
+ this.log(` Socket request sent for ${shortId}`);
1143
+ })
1144
+ .catch((err) => {
1145
+ this.logError(` Socket request failed for ${shortId}: ${err.message}`);
1146
+ clearTimeout(timeout);
1147
+ this.pendingInjections.delete(msg.messageId);
1148
+ resolve(false);
1149
+ });
1150
+ });
1151
+ }
1152
+ /** Maximum retries for failed injections before giving up */
1153
+ static MAX_INJECTION_RETRIES = 5;
1154
+ /** Backoff delay multiplier (ms) for retries: delay = BASE * 2^retryCount */
1155
+ static INJECTION_RETRY_BASE_MS = 2000;
1156
+ /**
1157
+ * Process queued messages
1158
+ */
1159
+ async processMessageQueue() {
1160
+ if (!this.readyForMessages || this.backpressureActive || this.isInjecting) {
1161
+ return;
1162
+ }
1163
+ if (this.messageQueue.length === 0) {
1164
+ return;
1165
+ }
1166
+ // Check if agent is in editor mode - delay injection if so
1167
+ const idleResult = this.idleDetector.checkIdle();
1168
+ if (idleResult.inEditorMode) {
1169
+ this.log(` Agent in editor mode, delaying injection (queue: ${this.messageQueue.length})`);
1170
+ // Check again in 2 seconds
1171
+ setTimeout(() => this.processMessageQueue(), 2000);
1172
+ return;
1173
+ }
1174
+ this.isInjecting = true;
1175
+ this.injectionStartTime = Date.now();
1176
+ const msg = this.messageQueue.shift();
1177
+ const retryCount = msg._retryCount ?? 0;
1178
+ const bodyPreview = msg.body.substring(0, 50).replace(/\n/g, '\\n');
1179
+ this.log(` Processing message from ${msg.from}: "${bodyPreview}..." (remaining=${this.messageQueue.length}, retry=${retryCount})`);
1180
+ try {
1181
+ const success = await this.injectMessage(msg);
1182
+ // Metrics are now tracked in handleInjectResult which knows about retries
1183
+ if (!success) {
1184
+ // Record failure for adaptive throttling
1185
+ this.throttle.recordFailure();
1186
+ // Re-queue with backoff if under retry limit
1187
+ if (retryCount < RelayPtyOrchestrator.MAX_INJECTION_RETRIES) {
1188
+ const backoffMs = RelayPtyOrchestrator.INJECTION_RETRY_BASE_MS * Math.pow(2, retryCount);
1189
+ this.log(` Re-queuing message ${msg.messageId.substring(0, 8)} for retry ${retryCount + 1} in ${backoffMs}ms`);
1190
+ msg._retryCount = retryCount + 1;
1191
+ // Add to front of queue for priority
1192
+ this.messageQueue.unshift(msg);
1193
+ // Wait before retrying
1194
+ this.isInjecting = false;
1195
+ setTimeout(() => this.processMessageQueue(), backoffMs);
1196
+ return;
1197
+ }
1198
+ this.logError(` Injection failed for message ${msg.messageId.substring(0, 8)} after ${retryCount} retries`);
1199
+ this.config.onInjectionFailed?.(msg.messageId, 'Injection failed after max retries');
1200
+ this.sendSyncAck(msg.messageId, msg.sync, 'ERROR', { error: 'injection_failed_max_retries' });
1201
+ }
1202
+ else {
1203
+ // Record success for adaptive throttling
1204
+ this.throttle.recordSuccess();
1205
+ this.sendSyncAck(msg.messageId, msg.sync, 'OK');
1206
+ }
1207
+ }
1208
+ catch (err) {
1209
+ this.logError(` Injection error: ${err.message}`);
1210
+ // Track metrics for exceptions (not handled by handleInjectResult)
1211
+ this.injectionMetrics.failed++;
1212
+ this.injectionMetrics.total++;
1213
+ // Record failure for adaptive throttling
1214
+ this.throttle.recordFailure();
1215
+ this.sendSyncAck(msg.messageId, msg.sync, 'ERROR', { error: err.message });
1216
+ }
1217
+ finally {
1218
+ this.isInjecting = false;
1219
+ this.injectionStartTime = 0;
1220
+ // Process next message after adaptive delay (faster when healthy, slower under stress)
1221
+ if (this.messageQueue.length > 0 && !this.backpressureActive) {
1222
+ const delay = this.throttle.getDelay();
1223
+ setTimeout(() => this.processMessageQueue(), delay);
1224
+ }
1225
+ }
1226
+ }
1227
+ /**
1228
+ * Override handleIncomingMessage to trigger queue processing
1229
+ */
1230
+ handleIncomingMessage(from, payload, messageId, meta, originalTo) {
1231
+ this.log(` === MESSAGE RECEIVED: ${messageId.substring(0, 8)} from ${from} ===`);
1232
+ this.log(` Body preview: ${payload.body?.substring(0, 100) ?? '(no body)'}...`);
1233
+ super.handleIncomingMessage(from, payload, messageId, meta, originalTo);
1234
+ this.log(` Queue length after add: ${this.messageQueue.length}`);
1235
+ this.processMessageQueue();
1236
+ }
1237
+ // =========================================================================
1238
+ // Queue monitor - Detect and process stuck messages
1239
+ // =========================================================================
1240
+ /**
1241
+ * Start the queue monitor to periodically check for stuck messages.
1242
+ * This ensures messages don't get orphaned in the queue when the agent is idle.
1243
+ */
1244
+ startQueueMonitor() {
1245
+ if (this.queueMonitorTimer) {
1246
+ return; // Already started
1247
+ }
1248
+ this.log(` Starting queue monitor (interval: ${this.QUEUE_MONITOR_INTERVAL_MS}ms)`);
1249
+ this.queueMonitorTimer = setInterval(() => {
1250
+ this.checkForStuckQueue();
1251
+ }, this.QUEUE_MONITOR_INTERVAL_MS);
1252
+ // Don't keep process alive just for queue monitoring
1253
+ this.queueMonitorTimer.unref?.();
1254
+ }
1255
+ /**
1256
+ * Stop the queue monitor.
1257
+ */
1258
+ stopQueueMonitor() {
1259
+ if (this.queueMonitorTimer) {
1260
+ clearInterval(this.queueMonitorTimer);
1261
+ this.queueMonitorTimer = undefined;
1262
+ this.log(` Queue monitor stopped`);
1263
+ }
1264
+ }
1265
+ // =========================================================================
1266
+ // Protocol monitoring (detect agent mistakes like empty AGENT_RELAY_NAME)
1267
+ // =========================================================================
1268
+ /**
1269
+ * Start watching for protocol issues in the outbox directory.
1270
+ * Detects common mistakes like:
1271
+ * - Empty AGENT_RELAY_NAME causing files at outbox//
1272
+ * - Files created directly in outbox/ instead of agent subdirectory
1273
+ */
1274
+ startProtocolMonitor() {
1275
+ // Get the outbox parent directory (one level up from agent's outbox)
1276
+ const parentDir = dirname(this._canonicalOutboxPath);
1277
+ // Ensure parent directory exists
1278
+ try {
1279
+ if (!existsSync(parentDir)) {
1280
+ mkdirSync(parentDir, { recursive: true });
1281
+ }
1282
+ }
1283
+ catch {
1284
+ // Ignore - directory may already exist
1285
+ }
1286
+ try {
1287
+ this.protocolWatcher = watch(parentDir, (eventType, filename) => {
1288
+ if (eventType === 'rename' && filename) {
1289
+ // Check for files directly in parent (not in agent subdirectory)
1290
+ // This happens when $AGENT_RELAY_NAME is empty
1291
+ const fullPath = join(parentDir, filename);
1292
+ try {
1293
+ // If it's a file (not directory) directly in the parent, that's an issue
1294
+ if (existsSync(fullPath) && !lstatSync(fullPath).isDirectory()) {
1295
+ this.handleProtocolIssue('file_in_root', filename);
1296
+ }
1297
+ // Check for empty-named directory (double slash symptom)
1298
+ if (filename === '' || filename.startsWith('/')) {
1299
+ this.handleProtocolIssue('empty_agent_name', filename);
1300
+ }
1301
+ }
1302
+ catch {
1303
+ // Ignore stat errors
1304
+ }
1305
+ }
1306
+ });
1307
+ // Don't keep process alive just for protocol monitoring
1308
+ this.protocolWatcher.unref?.();
1309
+ this.log(` Protocol monitor started on ${parentDir}`);
1310
+ }
1311
+ catch (err) {
1312
+ // Don't fail start() if protocol monitoring fails
1313
+ this.logError(` Failed to start protocol monitor: ${err.message}`);
1314
+ }
1315
+ // Also do an initial scan for existing issues
1316
+ this.scanForProtocolIssues();
1317
+ }
1318
+ /**
1319
+ * Stop the protocol monitor.
1320
+ */
1321
+ stopProtocolMonitor() {
1322
+ if (this.protocolWatcher) {
1323
+ this.protocolWatcher.close();
1324
+ this.protocolWatcher = undefined;
1325
+ this.log(` Protocol monitor stopped`);
1326
+ }
1327
+ }
1328
+ /**
1329
+ * Scan for existing protocol issues (called once at startup).
1330
+ */
1331
+ scanForProtocolIssues() {
1332
+ const parentDir = dirname(this._canonicalOutboxPath);
1333
+ try {
1334
+ if (!existsSync(parentDir))
1335
+ return;
1336
+ const entries = readdirSync(parentDir);
1337
+ for (const entry of entries) {
1338
+ const fullPath = join(parentDir, entry);
1339
+ try {
1340
+ // Check for files directly in parent (should only be directories)
1341
+ if (!lstatSync(fullPath).isDirectory()) {
1342
+ this.handleProtocolIssue('file_in_root', entry);
1343
+ break; // Only report once
1344
+ }
1345
+ }
1346
+ catch {
1347
+ // Ignore stat errors
1348
+ }
1349
+ }
1350
+ }
1351
+ catch {
1352
+ // Ignore scan errors
1353
+ }
1354
+ }
1355
+ /**
1356
+ * Handle a detected protocol issue by injecting a helpful reminder.
1357
+ */
1358
+ handleProtocolIssue(issue, filename) {
1359
+ const now = Date.now();
1360
+ // Respect cooldown to avoid spamming
1361
+ if (now - this.protocolReminderCooldown < this.PROTOCOL_REMINDER_COOLDOWN_MS) {
1362
+ return;
1363
+ }
1364
+ this.protocolReminderCooldown = now;
1365
+ this.log(` Protocol issue detected: ${issue} (${filename})`);
1366
+ const reminders = {
1367
+ empty_agent_name: `⚠️ **Protocol Issue Detected**
1368
+
1369
+ Your \`$AGENT_RELAY_NAME\` environment variable appears to be empty or unset.
1370
+ Your agent name is: **${this.config.name}**
1371
+
1372
+ Correct outbox path: \`$AGENT_RELAY_OUTBOX\`
1373
+
1374
+ When writing relay files, use:
1375
+ \`\`\`bash
1376
+ cat > $AGENT_RELAY_OUTBOX/msg << 'EOF'
1377
+ TO: TargetAgent
1378
+
1379
+ Your message here
1380
+ EOF
1381
+ \`\`\`
1382
+ Then output: \`->relay-file:msg\``,
1383
+ file_in_root: `⚠️ **Protocol Issue Detected**
1384
+
1385
+ Found file "${filename}" directly in the outbox root instead of using the proper path.
1386
+ Your agent name is: **${this.config.name}**
1387
+
1388
+ The \`$AGENT_RELAY_OUTBOX\` path already points to your agent's directory.
1389
+ Write files directly inside it:
1390
+
1391
+ \`\`\`bash
1392
+ cat > $AGENT_RELAY_OUTBOX/msg << 'EOF'
1393
+ TO: TargetAgent
1394
+
1395
+ Your message here
1396
+ EOF
1397
+ \`\`\`
1398
+ Then output: \`->relay-file:msg\``,
1399
+ };
1400
+ const reminder = reminders[issue];
1401
+ if (reminder) {
1402
+ this.injectProtocolReminder(reminder);
1403
+ }
1404
+ }
1405
+ /**
1406
+ * Inject a protocol reminder message to the agent.
1407
+ */
1408
+ injectProtocolReminder(message) {
1409
+ const queuedMsg = {
1410
+ from: 'system',
1411
+ body: message,
1412
+ messageId: `protocol-reminder-${Date.now()}`,
1413
+ importance: 2, // Higher priority
1414
+ };
1415
+ this.messageQueue.unshift(queuedMsg); // Add to front of queue
1416
+ this.log(` Queued protocol reminder (queue size: ${this.messageQueue.length})`);
1417
+ // Trigger processing if not already in progress
1418
+ if (!this.isInjecting && this.readyForMessages) {
1419
+ this.processMessageQueue();
1420
+ }
1421
+ }
1422
+ // =========================================================================
1423
+ // Periodic protocol reminders (for long sessions where agents forget protocol)
1424
+ // =========================================================================
1425
+ /**
1426
+ * Start sending periodic protocol reminders.
1427
+ * Agents in long sessions sometimes forget the relay protocol - these
1428
+ * reminders help them stay on track without user intervention.
1429
+ */
1430
+ startPeriodicReminder() {
1431
+ this.sessionStartTime = Date.now();
1432
+ this.periodicReminderTimer = setInterval(() => {
1433
+ this.sendPeriodicProtocolReminder();
1434
+ }, this.PERIODIC_REMINDER_INTERVAL_MS);
1435
+ // Don't keep process alive just for reminders
1436
+ this.periodicReminderTimer.unref?.();
1437
+ const intervalMinutes = Math.round(this.PERIODIC_REMINDER_INTERVAL_MS / 60000);
1438
+ this.log(` Periodic protocol reminder started (interval: ${intervalMinutes} minutes)`);
1439
+ }
1440
+ /**
1441
+ * Stop periodic protocol reminders.
1442
+ */
1443
+ stopPeriodicReminder() {
1444
+ if (this.periodicReminderTimer) {
1445
+ clearInterval(this.periodicReminderTimer);
1446
+ this.periodicReminderTimer = undefined;
1447
+ this.log(` Periodic protocol reminder stopped`);
1448
+ }
1449
+ }
1450
+ /**
1451
+ * Send a periodic protocol reminder to the agent.
1452
+ * This reminds agents about proper relay communication format after long sessions.
1453
+ */
1454
+ sendPeriodicProtocolReminder() {
1455
+ // Don't send if not ready
1456
+ if (!this.running || !this.readyForMessages) {
1457
+ return;
1458
+ }
1459
+ const sessionDurationMinutes = Math.round((Date.now() - this.sessionStartTime) / 60000);
1460
+ const reminder = `📋 **Protocol Reminder** (Session: ${sessionDurationMinutes} minutes)
1461
+
1462
+ You are **${this.config.name}** in a multi-agent relay system. Here's how to communicate:
1463
+
1464
+ **Sending Messages:**
1465
+ \`\`\`bash
1466
+ cat > $AGENT_RELAY_OUTBOX/msg << 'EOF'
1467
+ TO: *
1468
+
1469
+ Your message here
1470
+ EOF
1471
+ \`\`\`
1472
+ Then output: \`->relay-file:msg\`
1473
+
1474
+ Use \`TO: *\` to broadcast to all agents, or \`TO: AgentName\` for a specific agent.
1475
+
1476
+ **Spawning Agents:**
1477
+ \`\`\`bash
1478
+ cat > $AGENT_RELAY_OUTBOX/spawn << 'EOF'
1479
+ KIND: spawn
1480
+ NAME: WorkerName
1481
+ CLI: claude
1482
+
1483
+ Task description here
1484
+ EOF
1485
+ \`\`\`
1486
+ Then output: \`->relay-file:spawn\`
1487
+
1488
+ **Protocol Tips:**
1489
+ - Always ACK when you receive a task: "ACK: Brief description"
1490
+ - Send DONE when complete: "DONE: What was accomplished"
1491
+ - Keep your lead informed of progress
1492
+
1493
+ 📖 See **AGENTS.md** in the project root for full protocol documentation.`;
1494
+ this.log(` Sending periodic protocol reminder (session: ${sessionDurationMinutes}m)`);
1495
+ this.injectProtocolReminder(reminder);
1496
+ }
1497
+ /**
1498
+ * Check for messages stuck in the queue and process them if the agent is idle.
1499
+ *
1500
+ * This handles cases where:
1501
+ * 1. Messages arrived while the agent was busy and the retry mechanism failed
1502
+ * 2. Socket disconnection/reconnection left messages orphaned
1503
+ * 3. Injection timeouts occurred without proper queue resumption
1504
+ */
1505
+ checkForStuckQueue() {
1506
+ // Skip if not ready for messages
1507
+ if (!this.readyForMessages || !this.running) {
1508
+ return;
1509
+ }
1510
+ // Skip if queue is empty
1511
+ if (this.messageQueue.length === 0) {
1512
+ return;
1513
+ }
1514
+ // Check if currently injecting
1515
+ if (this.isInjecting) {
1516
+ // Check if injection has been stuck for too long
1517
+ const stuckDuration = Date.now() - this.injectionStartTime;
1518
+ if (stuckDuration > this.MAX_INJECTION_STUCK_MS) {
1519
+ this.logError(` ⚠️ Injection stuck for ${Math.round(stuckDuration / 1000)}s - force resetting`);
1520
+ this.isInjecting = false;
1521
+ this.injectionStartTime = 0;
1522
+ // Clear any pending injections that might be stuck
1523
+ for (const [id, pending] of this.pendingInjections) {
1524
+ clearTimeout(pending.timeout);
1525
+ this.logError(` Clearing stuck pending injection: ${id.substring(0, 8)}`);
1526
+ }
1527
+ this.pendingInjections.clear();
1528
+ // Continue to process the queue below
1529
+ }
1530
+ else {
1531
+ return; // Still within normal injection time
1532
+ }
1533
+ }
1534
+ // Skip if backpressure is active
1535
+ if (this.backpressureActive) {
1536
+ return;
1537
+ }
1538
+ // Check if the agent is idle (high confidence)
1539
+ const idleResult = this.idleDetector.checkIdle({ minSilenceMs: 2000 });
1540
+ if (!idleResult.isIdle) {
1541
+ // Agent is still working, let it finish
1542
+ return;
1543
+ }
1544
+ // We have messages in the queue, agent is idle, not currently injecting
1545
+ // This is a stuck queue situation - trigger processing
1546
+ const senders = [...new Set(this.messageQueue.map(m => m.from))];
1547
+ this.log(` ⚠️ Queue monitor: Found ${this.messageQueue.length} stuck message(s) from [${senders.join(', ')}]`);
1548
+ this.log(` ⚠️ Agent is idle (confidence: ${(idleResult.confidence * 100).toFixed(0)}%), triggering queue processing`);
1549
+ // Process the queue
1550
+ this.processMessageQueue();
1551
+ }
1552
+ // =========================================================================
1553
+ // Output parsing
1554
+ // =========================================================================
1555
+ /**
1556
+ * Parse relay commands from output
1557
+ */
1558
+ parseRelayCommands() {
1559
+ const cleanContent = stripAnsi(this.rawBuffer);
1560
+ if (cleanContent.length <= this.lastParsedLength) {
1561
+ return;
1562
+ }
1563
+ // Parse new content with lookback for fenced messages
1564
+ const lookbackStart = Math.max(0, this.lastParsedLength - 500);
1565
+ const contentToParse = cleanContent.substring(lookbackStart);
1566
+ // Parse fenced messages
1567
+ this.parseFencedMessages(contentToParse);
1568
+ // Parse single-line messages
1569
+ this.parseSingleLineMessages(contentToParse);
1570
+ // Parse spawn/release commands
1571
+ this.parseSpawnReleaseCommands(contentToParse);
1572
+ this.lastParsedLength = cleanContent.length;
1573
+ }
1574
+ /**
1575
+ * Parse fenced multi-line messages
1576
+ */
1577
+ parseFencedMessages(content) {
1578
+ const escapedPrefix = this.relayPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1579
+ const fencePattern = new RegExp(`${escapedPrefix}(\\S+)(?:\\s+\\[thread:([\\w-]+)\\])?\\s*<<<([\\s\\S]*?)>>>`, 'g');
1580
+ let match;
1581
+ while ((match = fencePattern.exec(content)) !== null) {
1582
+ const target = match[1];
1583
+ const thread = match[2];
1584
+ const body = match[3].trim();
1585
+ if (!body || target === 'spawn' || target === 'release') {
1586
+ continue;
1587
+ }
1588
+ this.sendRelayCommand({
1589
+ to: target,
1590
+ kind: 'message',
1591
+ body,
1592
+ thread,
1593
+ raw: match[0],
1594
+ });
1595
+ }
1596
+ }
1597
+ /**
1598
+ * Parse single-line messages
1599
+ */
1600
+ parseSingleLineMessages(content) {
1601
+ const lines = content.split('\n');
1602
+ const escapedPrefix = this.relayPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1603
+ const pattern = new RegExp(`${escapedPrefix}(\\S+)(?:\\s+\\[thread:([\\w-]+)\\])?\\s+(.+)$`);
1604
+ for (const line of lines) {
1605
+ // Skip fenced messages
1606
+ if (line.includes('<<<') || line.includes('>>>')) {
1607
+ continue;
1608
+ }
1609
+ const match = line.match(pattern);
1610
+ if (!match) {
1611
+ continue;
1612
+ }
1613
+ const target = match[1];
1614
+ const thread = match[2];
1615
+ const body = match[3].trim();
1616
+ if (!body || target === 'spawn' || target === 'release') {
1617
+ continue;
1618
+ }
1619
+ this.sendRelayCommand({
1620
+ to: target,
1621
+ kind: 'message',
1622
+ body,
1623
+ thread,
1624
+ raw: line,
1625
+ });
1626
+ }
1627
+ }
1628
+ // =========================================================================
1629
+ // Summary and session end detection
1630
+ // =========================================================================
1631
+ /**
1632
+ * Check for [[SUMMARY]] blocks
1633
+ */
1634
+ checkForSummary(content) {
1635
+ const result = parseSummaryWithDetails(content);
1636
+ if (!result.found || !result.valid) {
1637
+ return;
1638
+ }
1639
+ if (result.rawContent === this.lastSummaryRawContent) {
1640
+ return;
1641
+ }
1642
+ this.lastSummaryRawContent = result.rawContent ?? '';
1643
+ this.emit('summary', {
1644
+ agentName: this.config.name,
1645
+ summary: result.summary,
1646
+ });
1647
+ }
1648
+ /**
1649
+ * Check for [[SESSION_END]] blocks
1650
+ */
1651
+ checkForSessionEnd(content) {
1652
+ if (this.sessionEndProcessed) {
1653
+ return;
1654
+ }
1655
+ const sessionEnd = parseSessionEndFromOutput(content);
1656
+ if (!sessionEnd) {
1657
+ return;
1658
+ }
1659
+ this.sessionEndProcessed = true;
1660
+ this.emit('session-end', {
1661
+ agentName: this.config.name,
1662
+ marker: sessionEnd,
1663
+ });
1664
+ }
1665
+ // =========================================================================
1666
+ // Public API
1667
+ // =========================================================================
1668
+ /**
1669
+ * Query status from relay-pty
1670
+ */
1671
+ async queryStatus() {
1672
+ if (!this.socket || !this.socketConnected) {
1673
+ return null;
1674
+ }
1675
+ try {
1676
+ await this.sendSocketRequest({ type: 'status' });
1677
+ // Response will come asynchronously via handleSocketResponse
1678
+ // For now, return null - could implement request/response matching
1679
+ return null;
1680
+ }
1681
+ catch {
1682
+ return null;
1683
+ }
1684
+ }
1685
+ /**
1686
+ * Wait for the CLI to be ready to receive messages.
1687
+ * This waits for:
1688
+ * 1. The CLI to produce at least one output (it has started)
1689
+ * 2. The CLI to become idle (it's ready for input)
1690
+ *
1691
+ * This is more reliable than a random sleep because it waits for
1692
+ * actual signals from the CLI rather than guessing how long it takes to start.
1693
+ *
1694
+ * @param timeoutMs Maximum time to wait (default: 30s)
1695
+ * @param pollMs Polling interval (default: 100ms)
1696
+ * @returns true if CLI is ready, false if timeout
1697
+ */
1698
+ async waitUntilCliReady(timeoutMs = 30000, pollMs = 100) {
1699
+ const startTime = Date.now();
1700
+ this.log(` Waiting for CLI to be ready (timeout: ${timeoutMs}ms)`);
1701
+ // In interactive mode, stdout is inherited (not captured), so hasReceivedOutput
1702
+ // will never be set. Trust that the process is ready if it's running.
1703
+ if (this.isInteractive) {
1704
+ this.log(` Interactive mode - trusting process is ready`);
1705
+ // Give a brief moment for the CLI to initialize its TUI.
1706
+ // 500ms is a conservative estimate based on typical CLI startup times:
1707
+ // - Claude CLI: ~200-300ms to show initial prompt
1708
+ // - Codex/Gemini: ~300-400ms
1709
+ // This delay is only used in interactive mode where we can't detect output.
1710
+ // In non-interactive mode, we poll for actual output instead.
1711
+ await sleep(500);
1712
+ return this.running;
1713
+ }
1714
+ // Phase 1: Wait for first output (CLI has started)
1715
+ while (Date.now() - startTime < timeoutMs) {
1716
+ if (this.hasReceivedOutput) {
1717
+ this.log(` CLI has started producing output`);
1718
+ break;
1719
+ }
1720
+ await sleep(pollMs);
1721
+ }
1722
+ if (!this.hasReceivedOutput) {
1723
+ this.log(` Timeout waiting for CLI to produce output`);
1724
+ return false;
1725
+ }
1726
+ // Phase 2: Wait for idle state (CLI is ready for input)
1727
+ const remainingTime = timeoutMs - (Date.now() - startTime);
1728
+ if (remainingTime <= 0) {
1729
+ return false;
1730
+ }
1731
+ const idleResult = await this.waitForIdleState(remainingTime, pollMs);
1732
+ if (idleResult.isIdle) {
1733
+ this.log(` CLI is idle and ready (confidence: ${idleResult.confidence.toFixed(2)})`);
1734
+ return true;
1735
+ }
1736
+ this.log(` Timeout waiting for CLI to become idle`);
1737
+ return false;
1738
+ }
1739
+ /**
1740
+ * Check if the CLI has produced any output yet.
1741
+ * Useful for checking if the CLI has started without blocking.
1742
+ * In interactive mode, returns true if process is running (output isn't captured).
1743
+ */
1744
+ hasCliStarted() {
1745
+ // In interactive mode, stdout isn't captured so hasReceivedOutput is never set
1746
+ if (this.isInteractive) {
1747
+ return this.running;
1748
+ }
1749
+ return this.hasReceivedOutput;
1750
+ }
1751
+ /**
1752
+ * Check if the orchestrator is ready to receive and inject messages.
1753
+ * This requires:
1754
+ * 1. relay-pty process spawned
1755
+ * 2. Socket connected to relay-pty
1756
+ * 3. running flag set
1757
+ *
1758
+ * Use this to verify the agent can actually receive injected messages,
1759
+ * not just that the CLI is running.
1760
+ */
1761
+ isReadyForMessages() {
1762
+ return this.readyForMessages && this.running && this.socketConnected;
1763
+ }
1764
+ /**
1765
+ * Wait until the orchestrator is ready to receive and inject messages.
1766
+ * This is more comprehensive than waitUntilCliReady because it ensures:
1767
+ * 1. CLI is ready (has output and is idle)
1768
+ * 2. Orchestrator is ready (socket connected, can inject)
1769
+ *
1770
+ * @param timeoutMs Maximum time to wait (default: 30s)
1771
+ * @param pollMs Polling interval (default: 100ms)
1772
+ * @returns true if ready, false if timeout
1773
+ */
1774
+ async waitUntilReadyForMessages(timeoutMs = 30000, pollMs = 100) {
1775
+ const startTime = Date.now();
1776
+ this.log(` Waiting for orchestrator to be ready for messages (timeout: ${timeoutMs}ms)`);
1777
+ // First wait for CLI to be ready (output + idle)
1778
+ const cliReady = await this.waitUntilCliReady(timeoutMs, pollMs);
1779
+ if (!cliReady) {
1780
+ this.log(` CLI not ready within timeout`);
1781
+ return false;
1782
+ }
1783
+ // Then wait for readyForMessages flag
1784
+ const remainingTime = timeoutMs - (Date.now() - startTime);
1785
+ if (remainingTime <= 0) {
1786
+ this.log(` No time remaining to wait for readyForMessages`);
1787
+ return this.isReadyForMessages();
1788
+ }
1789
+ while (Date.now() - startTime < timeoutMs) {
1790
+ if (this.isReadyForMessages()) {
1791
+ this.log(` Orchestrator is ready for messages`);
1792
+ return true;
1793
+ }
1794
+ await sleep(pollMs);
1795
+ }
1796
+ this.log(` Timeout waiting for orchestrator to be ready for messages`);
1797
+ return false;
1798
+ }
1799
+ /**
1800
+ * Get raw output buffer
1801
+ */
1802
+ getRawOutput() {
1803
+ return this.rawBuffer;
1804
+ }
1805
+ /**
1806
+ * Check if backpressure is active
1807
+ */
1808
+ isBackpressureActive() {
1809
+ return this.backpressureActive;
1810
+ }
1811
+ /**
1812
+ * Get the socket path
1813
+ */
1814
+ getSocketPath() {
1815
+ return this.socketPath;
1816
+ }
1817
+ /**
1818
+ * Get the relay-pty process PID
1819
+ */
1820
+ get pid() {
1821
+ return this.relayPtyProcess?.pid;
1822
+ }
1823
+ /**
1824
+ * Get the log file path (not used by relay-pty, returns undefined)
1825
+ */
1826
+ get logPath() {
1827
+ return this._logPath;
1828
+ }
1829
+ /**
1830
+ * Kill the process forcefully
1831
+ */
1832
+ async kill() {
1833
+ this.isGracefulStop = true; // Mark as intentional to prevent crash broadcast
1834
+ if (this.relayPtyProcess && !this.relayPtyProcess.killed) {
1835
+ this.relayPtyProcess.kill('SIGKILL');
1836
+ }
1837
+ this.running = false;
1838
+ this.disconnectSocket();
1839
+ this.destroyClient();
1840
+ }
1841
+ /**
1842
+ * Get output lines (for compatibility with PtyWrapper)
1843
+ * @param limit Maximum number of lines to return
1844
+ */
1845
+ getOutput(limit) {
1846
+ const lines = this.rawBuffer.split('\n');
1847
+ if (limit && limit > 0) {
1848
+ return lines.slice(-limit);
1849
+ }
1850
+ return lines;
1851
+ }
1852
+ /**
1853
+ * Write data directly to the process stdin
1854
+ * @param data Data to write
1855
+ */
1856
+ async write(data) {
1857
+ if (!this.relayPtyProcess || !this.relayPtyProcess.stdin) {
1858
+ throw new Error('Process not running');
1859
+ }
1860
+ const buffer = typeof data === 'string' ? Buffer.from(data) : data;
1861
+ this.relayPtyProcess.stdin.write(buffer);
1862
+ }
1863
+ /**
1864
+ * Inject a task using the socket-based injection system with verification.
1865
+ * This is the preferred method for spawned agent task delivery.
1866
+ *
1867
+ * @param task The task text to inject
1868
+ * @param from The sender name (default: "spawner")
1869
+ * @returns Promise resolving to true if injection succeeded, false otherwise
1870
+ */
1871
+ async injectTask(task, from = 'spawner') {
1872
+ if (!this.socket || !this.socketConnected) {
1873
+ this.log(` Socket not connected for task injection, falling back to stdin write`);
1874
+ // Fallback to direct write if socket not available
1875
+ try {
1876
+ await this.write(task + '\n');
1877
+ return true;
1878
+ }
1879
+ catch (err) {
1880
+ this.logError(` Stdin write fallback failed: ${err.message}`);
1881
+ return false;
1882
+ }
1883
+ }
1884
+ const messageId = `task-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
1885
+ const shortId = messageId.substring(0, 8);
1886
+ this.log(` Injecting task via socket: ${shortId}`);
1887
+ // Create request
1888
+ const request = {
1889
+ type: 'inject',
1890
+ id: messageId,
1891
+ from,
1892
+ body: task,
1893
+ priority: 0, // High priority for initial task
1894
+ };
1895
+ // Send with timeout and verification
1896
+ return new Promise((resolve) => {
1897
+ const timeout = setTimeout(() => {
1898
+ this.logError(` Task inject timeout for ${shortId} after 30s`);
1899
+ this.pendingInjections.delete(messageId);
1900
+ resolve(false);
1901
+ }, 30000);
1902
+ this.pendingInjections.set(messageId, {
1903
+ resolve,
1904
+ reject: () => resolve(false),
1905
+ timeout,
1906
+ from,
1907
+ shortId,
1908
+ retryCount: 0,
1909
+ originalBody: task,
1910
+ });
1911
+ this.sendSocketRequest(request)
1912
+ .then(() => {
1913
+ this.log(` Task inject request sent: ${shortId}`);
1914
+ })
1915
+ .catch((err) => {
1916
+ this.logError(` Task inject socket request failed: ${err.message}`);
1917
+ clearTimeout(timeout);
1918
+ this.pendingInjections.delete(messageId);
1919
+ resolve(false);
1920
+ });
1921
+ });
1922
+ }
1923
+ /**
1924
+ * Get the agent ID (from continuity if available)
1925
+ */
1926
+ getAgentId() {
1927
+ return this.agentId;
1928
+ }
1929
+ }
1930
+ //# sourceMappingURL=relay-pty-orchestrator.js.map