agent-relay 2.0.28 → 2.0.32

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 (894) hide show
  1. package/README.md +19 -0
  2. package/dist/index.cjs +85691 -0
  3. package/dist/src/bridge/index.d.ts.map +1 -0
  4. package/dist/src/bridge/index.js.map +1 -0
  5. package/dist/src/cli/commands/doctor.d.ts +2 -0
  6. package/dist/src/cli/commands/doctor.d.ts.map +1 -0
  7. package/dist/src/cli/commands/doctor.js +451 -0
  8. package/dist/src/cli/commands/doctor.js.map +1 -0
  9. package/dist/src/cli/index.d.ts.map +1 -0
  10. package/dist/src/cli/index.js +29 -1
  11. package/dist/src/cli/index.js.map +1 -0
  12. package/dist/src/config/relay-config.d.ts.map +1 -0
  13. package/dist/src/config/relay-config.js.map +1 -0
  14. package/dist/src/continuity/index.d.ts.map +1 -0
  15. package/dist/src/continuity/index.js.map +1 -0
  16. package/dist/src/daemon/index.d.ts.map +1 -0
  17. package/dist/src/daemon/index.js.map +1 -0
  18. package/dist/src/health-worker-manager.d.ts.map +1 -0
  19. package/dist/src/health-worker-manager.js.map +1 -0
  20. package/dist/src/health-worker.d.ts.map +1 -0
  21. package/dist/src/health-worker.js.map +1 -0
  22. package/dist/src/hooks/index.d.ts.map +1 -0
  23. package/dist/src/hooks/index.js.map +1 -0
  24. package/dist/src/index.d.ts.map +1 -0
  25. package/dist/src/index.js.map +1 -0
  26. package/dist/src/memory/index.d.ts.map +1 -0
  27. package/dist/src/memory/index.js.map +1 -0
  28. package/dist/src/policy/index.d.ts.map +1 -0
  29. package/dist/src/policy/index.js.map +1 -0
  30. package/dist/src/protocol/index.d.ts.map +1 -0
  31. package/dist/src/protocol/index.js.map +1 -0
  32. package/dist/src/resiliency/index.d.ts.map +1 -0
  33. package/dist/src/resiliency/index.js.map +1 -0
  34. package/dist/src/shared/cli-auth-config.d.ts.map +1 -0
  35. package/dist/src/shared/cli-auth-config.js.map +1 -0
  36. package/dist/src/state/index.d.ts.map +1 -0
  37. package/dist/src/state/index.js.map +1 -0
  38. package/dist/src/storage/index.d.ts.map +1 -0
  39. package/dist/src/storage/index.js.map +1 -0
  40. package/dist/src/trajectory/index.d.ts.map +1 -0
  41. package/dist/src/trajectory/index.js.map +1 -0
  42. package/dist/src/utils/index.d.ts.map +1 -0
  43. package/dist/src/utils/index.js.map +1 -0
  44. package/dist/src/wrapper/index.d.ts.map +1 -0
  45. package/dist/src/wrapper/index.js.map +1 -0
  46. package/package.json +83 -20
  47. package/packages/api-types/dist/index.d.ts.map +1 -0
  48. package/packages/api-types/dist/index.js.map +1 -0
  49. package/packages/api-types/dist/schemas/agent.d.ts.map +1 -0
  50. package/packages/api-types/dist/schemas/agent.js.map +1 -0
  51. package/packages/api-types/dist/schemas/api.d.ts.map +1 -0
  52. package/packages/api-types/dist/schemas/api.js.map +1 -0
  53. package/packages/api-types/dist/schemas/decision.d.ts.map +1 -0
  54. package/packages/api-types/dist/schemas/decision.js.map +1 -0
  55. package/packages/api-types/dist/schemas/fleet.d.ts.map +1 -0
  56. package/packages/api-types/dist/schemas/fleet.js.map +1 -0
  57. package/packages/api-types/dist/schemas/history.d.ts.map +1 -0
  58. package/packages/api-types/dist/schemas/history.js.map +1 -0
  59. package/packages/api-types/dist/schemas/index.d.ts.map +1 -0
  60. package/packages/api-types/dist/schemas/index.js.map +1 -0
  61. package/packages/api-types/dist/schemas/message.d.ts.map +1 -0
  62. package/packages/api-types/dist/schemas/message.js.map +1 -0
  63. package/packages/api-types/dist/schemas/session.d.ts.map +1 -0
  64. package/packages/api-types/dist/schemas/session.js.map +1 -0
  65. package/packages/api-types/dist/schemas/task.d.ts.map +1 -0
  66. package/packages/api-types/dist/schemas/task.js.map +1 -0
  67. package/packages/api-types/package.json +1 -1
  68. package/packages/api-types/src/index.ts +22 -0
  69. package/packages/api-types/src/schemas/agent.test.ts +164 -0
  70. package/packages/api-types/src/schemas/agent.ts +110 -0
  71. package/packages/api-types/src/schemas/api.test.ts +372 -0
  72. package/packages/api-types/src/schemas/api.ts +194 -0
  73. package/packages/api-types/src/schemas/decision.test.ts +324 -0
  74. package/packages/api-types/src/schemas/decision.ts +136 -0
  75. package/packages/api-types/src/schemas/fleet.test.ts +212 -0
  76. package/packages/api-types/src/schemas/fleet.ts +83 -0
  77. package/packages/api-types/src/schemas/history.test.ts +242 -0
  78. package/packages/api-types/src/schemas/history.ts +84 -0
  79. package/packages/api-types/src/schemas/index.ts +148 -0
  80. package/packages/api-types/src/schemas/message.test.ts +192 -0
  81. package/packages/api-types/src/schemas/message.ts +98 -0
  82. package/packages/api-types/src/schemas/session.test.ts +104 -0
  83. package/packages/api-types/src/schemas/session.ts +40 -0
  84. package/packages/api-types/src/schemas/task.test.ts +192 -0
  85. package/packages/api-types/src/schemas/task.ts +78 -0
  86. package/packages/api-types/tsconfig.json +19 -0
  87. package/packages/api-types/vitest.config.ts +9 -0
  88. package/packages/benchmark/README.md +200 -0
  89. package/packages/benchmark/datasets/coding-tasks.yaml +127 -0
  90. package/packages/benchmark/datasets/coordination-tasks.yaml +122 -0
  91. package/packages/benchmark/dist/benchmark.d.ts +47 -0
  92. package/packages/benchmark/dist/benchmark.d.ts.map +1 -0
  93. package/packages/benchmark/dist/benchmark.js +224 -0
  94. package/packages/benchmark/dist/benchmark.js.map +1 -0
  95. package/packages/benchmark/dist/cli.d.ts +8 -0
  96. package/packages/benchmark/dist/cli.d.ts.map +1 -0
  97. package/packages/benchmark/dist/cli.js +185 -0
  98. package/packages/benchmark/dist/cli.js.map +1 -0
  99. package/packages/benchmark/dist/harbor.d.ts +53 -0
  100. package/packages/benchmark/dist/harbor.d.ts.map +1 -0
  101. package/packages/benchmark/dist/harbor.js +127 -0
  102. package/packages/benchmark/dist/harbor.js.map +1 -0
  103. package/packages/benchmark/dist/index.d.ts +48 -0
  104. package/packages/benchmark/dist/index.d.ts.map +1 -0
  105. package/packages/benchmark/dist/index.js +50 -0
  106. package/packages/benchmark/dist/index.js.map +1 -0
  107. package/packages/benchmark/dist/runners/base.d.ts +63 -0
  108. package/packages/benchmark/dist/runners/base.d.ts.map +1 -0
  109. package/packages/benchmark/dist/runners/base.js +155 -0
  110. package/packages/benchmark/dist/runners/base.js.map +1 -0
  111. package/packages/benchmark/dist/runners/index.d.ts +10 -0
  112. package/packages/benchmark/dist/runners/index.d.ts.map +1 -0
  113. package/packages/benchmark/dist/runners/index.js +10 -0
  114. package/packages/benchmark/dist/runners/index.js.map +1 -0
  115. package/packages/benchmark/dist/runners/single.d.ts +19 -0
  116. package/packages/benchmark/dist/runners/single.d.ts.map +1 -0
  117. package/packages/benchmark/dist/runners/single.js +111 -0
  118. package/packages/benchmark/dist/runners/single.js.map +1 -0
  119. package/packages/benchmark/dist/runners/subagent.d.ts +32 -0
  120. package/packages/benchmark/dist/runners/subagent.d.ts.map +1 -0
  121. package/packages/benchmark/dist/runners/subagent.js +212 -0
  122. package/packages/benchmark/dist/runners/subagent.js.map +1 -0
  123. package/packages/benchmark/dist/runners/swarm.d.ts +36 -0
  124. package/packages/benchmark/dist/runners/swarm.d.ts.map +1 -0
  125. package/packages/benchmark/dist/runners/swarm.js +273 -0
  126. package/packages/benchmark/dist/runners/swarm.js.map +1 -0
  127. package/packages/benchmark/dist/types.d.ts +178 -0
  128. package/packages/benchmark/dist/types.d.ts.map +1 -0
  129. package/packages/benchmark/dist/types.js +16 -0
  130. package/packages/benchmark/dist/types.js.map +1 -0
  131. package/packages/benchmark/package.json +80 -0
  132. package/packages/benchmark/src/benchmark.ts +298 -0
  133. package/packages/benchmark/src/cli.ts +240 -0
  134. package/packages/benchmark/src/harbor.ts +170 -0
  135. package/packages/benchmark/src/index.ts +73 -0
  136. package/packages/benchmark/src/runners/base.ts +204 -0
  137. package/packages/benchmark/src/runners/index.ts +10 -0
  138. package/packages/benchmark/src/runners/single.ts +121 -0
  139. package/packages/benchmark/src/runners/subagent.ts +240 -0
  140. package/packages/benchmark/src/runners/swarm.ts +326 -0
  141. package/packages/benchmark/src/types.ts +205 -0
  142. package/packages/benchmark/tsconfig.json +20 -0
  143. package/packages/bridge/dist/index.d.ts.map +1 -0
  144. package/packages/bridge/dist/index.js.map +1 -0
  145. package/packages/bridge/dist/multi-project-client.d.ts.map +1 -0
  146. package/packages/bridge/dist/multi-project-client.js.map +1 -0
  147. package/packages/bridge/dist/shadow-cli.d.ts.map +1 -0
  148. package/packages/bridge/dist/shadow-cli.js.map +1 -0
  149. package/packages/bridge/dist/spawner.d.ts.map +1 -0
  150. package/packages/bridge/dist/spawner.js +15 -2
  151. package/packages/bridge/dist/spawner.js.map +1 -0
  152. package/packages/bridge/dist/types.d.ts.map +1 -0
  153. package/packages/bridge/dist/types.js.map +1 -0
  154. package/packages/bridge/dist/utils.d.ts.map +1 -0
  155. package/packages/bridge/dist/utils.js.map +1 -0
  156. package/packages/bridge/package.json +8 -8
  157. package/packages/bridge/src/index.ts +25 -0
  158. package/packages/bridge/src/multi-project-client.test.ts +340 -0
  159. package/packages/bridge/src/multi-project-client.ts +469 -0
  160. package/packages/bridge/src/shadow-cli.ts +95 -0
  161. package/packages/bridge/src/spawner-mcp.test.ts +505 -0
  162. package/packages/bridge/src/spawner.ts +1724 -0
  163. package/packages/bridge/src/types.ts +145 -0
  164. package/packages/bridge/src/utils.test.ts +98 -0
  165. package/packages/bridge/src/utils.ts +67 -0
  166. package/packages/bridge/tsconfig.json +29 -0
  167. package/packages/bridge/vitest.config.ts +9 -0
  168. package/packages/cli-tester/dist/index.d.ts.map +1 -0
  169. package/packages/cli-tester/dist/index.js.map +1 -0
  170. package/packages/cli-tester/dist/utils/credential-check.d.ts.map +1 -0
  171. package/packages/cli-tester/dist/utils/credential-check.js.map +1 -0
  172. package/packages/cli-tester/dist/utils/socket-client.d.ts.map +1 -0
  173. package/packages/cli-tester/dist/utils/socket-client.js.map +1 -0
  174. package/packages/cli-tester/docker/Dockerfile +61 -0
  175. package/packages/cli-tester/docker/docker-compose.yml +71 -0
  176. package/packages/cli-tester/package.json +1 -1
  177. package/packages/cli-tester/src/index.ts +40 -0
  178. package/packages/cli-tester/src/utils/credential-check.ts +284 -0
  179. package/packages/cli-tester/src/utils/socket-client.ts +211 -0
  180. package/packages/cli-tester/tests/credential-check.test.ts +56 -0
  181. package/packages/cli-tester/tsconfig.json +11 -0
  182. package/packages/config/dist/agent-config.d.ts.map +1 -0
  183. package/packages/config/dist/agent-config.js.map +1 -0
  184. package/packages/config/dist/bridge-config.d.ts.map +1 -0
  185. package/packages/config/dist/bridge-config.js.map +1 -0
  186. package/packages/config/dist/bridge-utils.d.ts.map +1 -0
  187. package/packages/config/dist/bridge-utils.js.map +1 -0
  188. package/packages/config/dist/cli-auth-config.d.ts.map +1 -0
  189. package/packages/config/dist/cli-auth-config.js.map +1 -0
  190. package/packages/config/dist/cloud-config.d.ts.map +1 -0
  191. package/packages/config/dist/cloud-config.js.map +1 -0
  192. package/packages/config/dist/index.d.ts.map +1 -0
  193. package/packages/config/dist/index.js.map +1 -0
  194. package/packages/config/dist/project-namespace.d.ts.map +1 -0
  195. package/packages/config/dist/project-namespace.js.map +1 -0
  196. package/packages/config/dist/relay-config.d.ts.map +1 -0
  197. package/packages/config/dist/relay-config.js.map +1 -0
  198. package/packages/config/dist/relay-file-writer.d.ts.map +1 -0
  199. package/packages/config/dist/relay-file-writer.js.map +1 -0
  200. package/packages/config/dist/schemas.d.ts.map +1 -0
  201. package/packages/config/dist/schemas.js.map +1 -0
  202. package/packages/config/dist/shadow-config.d.ts.map +1 -0
  203. package/packages/config/dist/shadow-config.js.map +1 -0
  204. package/packages/config/dist/teams-config.d.ts.map +1 -0
  205. package/packages/config/dist/teams-config.js.map +1 -0
  206. package/packages/config/dist/trajectory-config.d.ts.map +1 -0
  207. package/packages/config/dist/trajectory-config.js.map +1 -0
  208. package/packages/config/package.json +2 -2
  209. package/packages/config/src/agent-config.test.ts +245 -0
  210. package/packages/config/src/agent-config.ts +160 -0
  211. package/packages/config/src/bridge-config.test.ts +132 -0
  212. package/packages/config/src/bridge-config.ts +189 -0
  213. package/packages/config/src/bridge-utils.ts +59 -0
  214. package/packages/config/src/cli-auth-config.ts +548 -0
  215. package/packages/config/src/cloud-config.ts +208 -0
  216. package/packages/config/src/index.ts +12 -0
  217. package/packages/config/src/project-namespace.ts +344 -0
  218. package/packages/config/src/relay-config.test.ts +51 -0
  219. package/packages/config/src/relay-config.ts +36 -0
  220. package/packages/config/src/relay-file-writer.test.ts +351 -0
  221. package/packages/config/src/relay-file-writer.ts +508 -0
  222. package/packages/config/src/schemas.test.ts +59 -0
  223. package/packages/config/src/schemas.ts +201 -0
  224. package/packages/config/src/shadow-config.ts +205 -0
  225. package/packages/config/src/teams-config.ts +135 -0
  226. package/packages/config/src/trajectory-config.ts +222 -0
  227. package/packages/config/tsconfig.json +21 -0
  228. package/packages/config/vitest.config.ts +9 -0
  229. package/packages/continuity/dist/formatter.d.ts.map +1 -0
  230. package/packages/continuity/dist/formatter.js.map +1 -0
  231. package/packages/continuity/dist/handoff-store.d.ts.map +1 -0
  232. package/packages/continuity/dist/handoff-store.js.map +1 -0
  233. package/packages/continuity/dist/index.d.ts.map +1 -0
  234. package/packages/continuity/dist/index.js.map +1 -0
  235. package/packages/continuity/dist/ledger-store.d.ts.map +1 -0
  236. package/packages/continuity/dist/ledger-store.js.map +1 -0
  237. package/packages/continuity/dist/manager.d.ts.map +1 -0
  238. package/packages/continuity/dist/manager.js.map +1 -0
  239. package/packages/continuity/dist/parser.d.ts.map +1 -0
  240. package/packages/continuity/dist/parser.js.map +1 -0
  241. package/packages/continuity/dist/types.d.ts.map +1 -0
  242. package/packages/continuity/dist/types.js.map +1 -0
  243. package/packages/continuity/package.json +1 -1
  244. package/packages/continuity/src/formatter.ts +371 -0
  245. package/packages/continuity/src/handoff-store.ts +523 -0
  246. package/packages/continuity/src/index.ts +9 -0
  247. package/packages/continuity/src/ledger-store.ts +594 -0
  248. package/packages/continuity/src/manager.test.ts +291 -0
  249. package/packages/continuity/src/manager.ts +774 -0
  250. package/packages/continuity/src/parser.test.ts +292 -0
  251. package/packages/continuity/src/parser.ts +680 -0
  252. package/packages/continuity/src/types.ts +211 -0
  253. package/packages/continuity/tsconfig.json +21 -0
  254. package/packages/continuity/vitest.config.ts +9 -0
  255. package/packages/daemon/dist/agent-manager.d.ts.map +1 -0
  256. package/packages/daemon/dist/agent-manager.js.map +1 -0
  257. package/packages/daemon/dist/agent-registry.d.ts.map +1 -0
  258. package/packages/daemon/dist/agent-registry.js.map +1 -0
  259. package/packages/daemon/dist/agent-signing.d.ts.map +1 -0
  260. package/packages/daemon/dist/agent-signing.js.map +1 -0
  261. package/packages/daemon/dist/api.d.ts.map +1 -0
  262. package/packages/daemon/dist/api.js.map +1 -0
  263. package/packages/daemon/dist/auth.d.ts.map +1 -0
  264. package/packages/daemon/dist/auth.js.map +1 -0
  265. package/packages/daemon/dist/channel-membership-store.d.ts.map +1 -0
  266. package/packages/daemon/dist/channel-membership-store.js.map +1 -0
  267. package/packages/daemon/dist/cli-auth.d.ts.map +1 -0
  268. package/packages/daemon/dist/cli-auth.js.map +1 -0
  269. package/packages/daemon/dist/cloud-sync.d.ts.map +1 -0
  270. package/packages/daemon/dist/cloud-sync.js.map +1 -0
  271. package/packages/daemon/dist/connection.d.ts.map +1 -0
  272. package/packages/daemon/dist/connection.js.map +1 -0
  273. package/packages/daemon/dist/consensus-integration.d.ts.map +1 -0
  274. package/packages/daemon/dist/consensus-integration.js.map +1 -0
  275. package/packages/daemon/dist/consensus.d.ts.map +1 -0
  276. package/packages/daemon/dist/consensus.js.map +1 -0
  277. package/packages/daemon/dist/delivery-tracker.d.ts.map +1 -0
  278. package/packages/daemon/dist/delivery-tracker.js.map +1 -0
  279. package/packages/daemon/dist/enhanced-features.d.ts.map +1 -0
  280. package/packages/daemon/dist/enhanced-features.js.map +1 -0
  281. package/packages/daemon/dist/index.d.ts.map +1 -0
  282. package/packages/daemon/dist/index.js.map +1 -0
  283. package/packages/daemon/dist/migrations/index.d.ts.map +1 -0
  284. package/packages/daemon/dist/migrations/index.js.map +1 -0
  285. package/packages/daemon/dist/orchestrator.d.ts.map +1 -0
  286. package/packages/daemon/dist/orchestrator.js.map +1 -0
  287. package/packages/daemon/dist/rate-limiter.d.ts.map +1 -0
  288. package/packages/daemon/dist/rate-limiter.js.map +1 -0
  289. package/packages/daemon/dist/registry.d.ts.map +1 -0
  290. package/packages/daemon/dist/registry.js.map +1 -0
  291. package/packages/daemon/dist/relay-ledger.d.ts.map +1 -0
  292. package/packages/daemon/dist/relay-ledger.js.map +1 -0
  293. package/packages/daemon/dist/relay-watchdog.d.ts.map +1 -0
  294. package/packages/daemon/dist/relay-watchdog.js.map +1 -0
  295. package/packages/daemon/dist/repo-manager.d.ts.map +1 -0
  296. package/packages/daemon/dist/repo-manager.js.map +1 -0
  297. package/packages/daemon/dist/router.d.ts.map +1 -0
  298. package/packages/daemon/dist/router.js.map +1 -0
  299. package/packages/daemon/dist/server.d.ts +1 -0
  300. package/packages/daemon/dist/server.d.ts.map +1 -0
  301. package/packages/daemon/dist/server.js +46 -16
  302. package/packages/daemon/dist/server.js.map +1 -0
  303. package/packages/daemon/dist/spawn-manager.d.ts.map +1 -0
  304. package/packages/daemon/dist/spawn-manager.js.map +1 -0
  305. package/packages/daemon/dist/sync-queue.d.ts.map +1 -0
  306. package/packages/daemon/dist/sync-queue.js.map +1 -0
  307. package/packages/daemon/dist/types.d.ts.map +1 -0
  308. package/packages/daemon/dist/types.js.map +1 -0
  309. package/packages/daemon/dist/workspace-manager.d.ts.map +1 -0
  310. package/packages/daemon/dist/workspace-manager.js.map +1 -0
  311. package/packages/daemon/package.json +12 -12
  312. package/packages/daemon/src/agent-manager.ts +679 -0
  313. package/packages/daemon/src/agent-registry.ts +284 -0
  314. package/packages/daemon/src/agent-signing.ts +707 -0
  315. package/packages/daemon/src/api.ts +1012 -0
  316. package/packages/daemon/src/auth.ts +276 -0
  317. package/packages/daemon/src/channel-membership-store.ts +217 -0
  318. package/packages/daemon/src/cli-auth.ts +906 -0
  319. package/packages/daemon/src/cloud-sync.ts +902 -0
  320. package/packages/daemon/src/connection.ts +534 -0
  321. package/packages/daemon/src/consensus-integration.ts +510 -0
  322. package/packages/daemon/src/consensus.ts +848 -0
  323. package/packages/daemon/src/delivery-tracker.ts +145 -0
  324. package/packages/daemon/src/enhanced-features.ts +390 -0
  325. package/packages/daemon/src/index.ts +52 -0
  326. package/packages/daemon/src/migrations/0001_initial.sql +72 -0
  327. package/packages/daemon/src/migrations/index.test.ts +195 -0
  328. package/packages/daemon/src/migrations/index.ts +286 -0
  329. package/packages/daemon/src/orchestrator.test.ts +231 -0
  330. package/packages/daemon/src/orchestrator.ts +1376 -0
  331. package/packages/daemon/src/rate-limiter.ts +172 -0
  332. package/packages/daemon/src/registry.ts +8 -0
  333. package/packages/daemon/src/relay-ledger.test.ts +358 -0
  334. package/packages/daemon/src/relay-ledger.ts +713 -0
  335. package/packages/daemon/src/relay-watchdog.test.ts +881 -0
  336. package/packages/daemon/src/relay-watchdog.ts +785 -0
  337. package/packages/daemon/src/repo-manager.ts +468 -0
  338. package/packages/daemon/src/router.test.ts +149 -0
  339. package/packages/daemon/src/router.ts +1885 -0
  340. package/packages/daemon/src/server.ts +1871 -0
  341. package/packages/daemon/src/spawn-manager.ts +275 -0
  342. package/packages/daemon/src/sync-queue.ts +477 -0
  343. package/packages/daemon/src/types.ts +158 -0
  344. package/packages/daemon/src/workspace-manager.ts +371 -0
  345. package/packages/daemon/tsconfig.json +21 -0
  346. package/packages/hooks/dist/browser.d.ts.map +1 -0
  347. package/packages/hooks/dist/browser.js.map +1 -0
  348. package/packages/hooks/dist/emitter.d.ts.map +1 -0
  349. package/packages/hooks/dist/emitter.js.map +1 -0
  350. package/packages/hooks/dist/inbox-check/hook.d.ts.map +1 -0
  351. package/packages/hooks/dist/inbox-check/hook.js.map +1 -0
  352. package/packages/hooks/dist/inbox-check/index.d.ts.map +1 -0
  353. package/packages/hooks/dist/inbox-check/index.js.map +1 -0
  354. package/packages/hooks/dist/inbox-check/types.d.ts.map +1 -0
  355. package/packages/hooks/dist/inbox-check/types.js.map +1 -0
  356. package/packages/hooks/dist/inbox-check/utils.d.ts.map +1 -0
  357. package/packages/hooks/dist/inbox-check/utils.js.map +1 -0
  358. package/packages/hooks/dist/index.d.ts.map +1 -0
  359. package/packages/hooks/dist/index.js.map +1 -0
  360. package/packages/hooks/dist/registry.d.ts.map +1 -0
  361. package/packages/hooks/dist/registry.js.map +1 -0
  362. package/packages/hooks/dist/trajectory-hooks.d.ts.map +1 -0
  363. package/packages/hooks/dist/trajectory-hooks.js.map +1 -0
  364. package/packages/hooks/dist/types.d.ts.map +1 -0
  365. package/packages/hooks/dist/types.js.map +1 -0
  366. package/packages/hooks/package.json +4 -4
  367. package/packages/hooks/src/browser.ts +2 -0
  368. package/packages/hooks/src/emitter.ts +84 -0
  369. package/packages/hooks/src/inbox-check/hook.ts +114 -0
  370. package/packages/hooks/src/inbox-check/index.ts +8 -0
  371. package/packages/hooks/src/inbox-check/types.ts +39 -0
  372. package/packages/hooks/src/inbox-check/utils.test.ts +287 -0
  373. package/packages/hooks/src/inbox-check/utils.ts +125 -0
  374. package/packages/hooks/src/index.ts +11 -0
  375. package/packages/hooks/src/registry.ts +614 -0
  376. package/packages/hooks/src/shims.d.ts +3 -0
  377. package/packages/hooks/src/trajectory-hooks.ts +251 -0
  378. package/packages/hooks/src/types.ts +342 -0
  379. package/packages/hooks/tsconfig.json +21 -0
  380. package/packages/hooks/vitest.config.ts +9 -0
  381. package/packages/mcp/dist/bin.d.ts.map +1 -0
  382. package/packages/mcp/dist/bin.js.map +1 -0
  383. package/packages/mcp/dist/client.d.ts +9 -15
  384. package/packages/mcp/dist/client.d.ts.map +1 -0
  385. package/packages/mcp/dist/client.js +42 -74
  386. package/packages/mcp/dist/client.js.map +1 -0
  387. package/packages/mcp/dist/cloud.d.ts.map +1 -0
  388. package/packages/mcp/dist/cloud.js.map +1 -0
  389. package/packages/mcp/dist/errors.d.ts.map +1 -0
  390. package/packages/mcp/dist/errors.js.map +1 -0
  391. package/packages/mcp/dist/file-transport.d.ts.map +1 -0
  392. package/packages/mcp/dist/file-transport.js.map +1 -0
  393. package/packages/mcp/dist/hybrid-client.d.ts.map +1 -0
  394. package/packages/mcp/dist/hybrid-client.js.map +1 -0
  395. package/packages/mcp/dist/index.d.ts.map +1 -0
  396. package/packages/mcp/dist/index.js.map +1 -0
  397. package/packages/mcp/dist/install-cli.d.ts.map +1 -0
  398. package/packages/mcp/dist/install-cli.js.map +1 -0
  399. package/packages/mcp/dist/install.d.ts.map +1 -0
  400. package/packages/mcp/dist/install.js.map +1 -0
  401. package/packages/mcp/dist/prompts/index.d.ts.map +1 -0
  402. package/packages/mcp/dist/prompts/index.js.map +1 -0
  403. package/packages/mcp/dist/prompts/protocol.d.ts.map +1 -0
  404. package/packages/mcp/dist/prompts/protocol.js.map +1 -0
  405. package/packages/mcp/dist/resources/agents.d.ts.map +1 -0
  406. package/packages/mcp/dist/resources/agents.js.map +1 -0
  407. package/packages/mcp/dist/resources/inbox.d.ts.map +1 -0
  408. package/packages/mcp/dist/resources/inbox.js.map +1 -0
  409. package/packages/mcp/dist/resources/index.d.ts.map +1 -0
  410. package/packages/mcp/dist/resources/index.js.map +1 -0
  411. package/packages/mcp/dist/resources/project.d.ts.map +1 -0
  412. package/packages/mcp/dist/resources/project.js.map +1 -0
  413. package/packages/mcp/dist/server.d.ts.map +1 -0
  414. package/packages/mcp/dist/server.js.map +1 -0
  415. package/packages/mcp/dist/simple.d.ts +2 -5
  416. package/packages/mcp/dist/simple.d.ts.map +1 -0
  417. package/packages/mcp/dist/simple.js.map +1 -0
  418. package/packages/mcp/dist/tools/index.d.ts.map +1 -0
  419. package/packages/mcp/dist/tools/index.js.map +1 -0
  420. package/packages/mcp/dist/tools/relay-broadcast.d.ts.map +1 -0
  421. package/packages/mcp/dist/tools/relay-broadcast.js.map +1 -0
  422. package/packages/mcp/dist/tools/relay-channel.d.ts.map +1 -0
  423. package/packages/mcp/dist/tools/relay-channel.js.map +1 -0
  424. package/packages/mcp/dist/tools/relay-connected.d.ts.map +1 -0
  425. package/packages/mcp/dist/tools/relay-connected.js.map +1 -0
  426. package/packages/mcp/dist/tools/relay-consensus.d.ts.map +1 -0
  427. package/packages/mcp/dist/tools/relay-consensus.js.map +1 -0
  428. package/packages/mcp/dist/tools/relay-continuity.d.ts.map +1 -0
  429. package/packages/mcp/dist/tools/relay-continuity.js.map +1 -0
  430. package/packages/mcp/dist/tools/relay-health.d.ts.map +1 -0
  431. package/packages/mcp/dist/tools/relay-health.js.map +1 -0
  432. package/packages/mcp/dist/tools/relay-inbox.d.ts.map +1 -0
  433. package/packages/mcp/dist/tools/relay-inbox.js.map +1 -0
  434. package/packages/mcp/dist/tools/relay-logs.d.ts.map +1 -0
  435. package/packages/mcp/dist/tools/relay-logs.js.map +1 -0
  436. package/packages/mcp/dist/tools/relay-metrics.d.ts.map +1 -0
  437. package/packages/mcp/dist/tools/relay-metrics.js.map +1 -0
  438. package/packages/mcp/dist/tools/relay-release.d.ts.map +1 -0
  439. package/packages/mcp/dist/tools/relay-release.js.map +1 -0
  440. package/packages/mcp/dist/tools/relay-remove-agent.d.ts.map +1 -0
  441. package/packages/mcp/dist/tools/relay-remove-agent.js.map +1 -0
  442. package/packages/mcp/dist/tools/relay-send.d.ts.map +1 -0
  443. package/packages/mcp/dist/tools/relay-send.js +4 -2
  444. package/packages/mcp/dist/tools/relay-send.js.map +1 -0
  445. package/packages/mcp/dist/tools/relay-shadow.d.ts.map +1 -0
  446. package/packages/mcp/dist/tools/relay-shadow.js.map +1 -0
  447. package/packages/mcp/dist/tools/relay-spawn.d.ts.map +1 -0
  448. package/packages/mcp/dist/tools/relay-spawn.js.map +1 -0
  449. package/packages/mcp/dist/tools/relay-status.d.ts.map +1 -0
  450. package/packages/mcp/dist/tools/relay-status.js.map +1 -0
  451. package/packages/mcp/dist/tools/relay-subscribe.d.ts.map +1 -0
  452. package/packages/mcp/dist/tools/relay-subscribe.js.map +1 -0
  453. package/packages/mcp/dist/tools/relay-who.d.ts.map +1 -0
  454. package/packages/mcp/dist/tools/relay-who.js.map +1 -0
  455. package/packages/mcp/package.json +3 -3
  456. package/packages/mcp/src/bin.ts +149 -0
  457. package/packages/mcp/src/client.ts +400 -0
  458. package/packages/mcp/src/cloud.ts +523 -0
  459. package/packages/mcp/src/errors.ts +54 -0
  460. package/packages/mcp/src/file-transport.ts +268 -0
  461. package/packages/mcp/src/hybrid-client.ts +209 -0
  462. package/packages/mcp/src/index.ts +122 -0
  463. package/packages/mcp/src/install-cli.ts +210 -0
  464. package/packages/mcp/src/install.ts +745 -0
  465. package/packages/mcp/src/prompts/index.ts +1 -0
  466. package/packages/mcp/src/prompts/protocol.ts +164 -0
  467. package/packages/mcp/src/resources/agents.ts +21 -0
  468. package/packages/mcp/src/resources/inbox.ts +21 -0
  469. package/packages/mcp/src/resources/index.ts +3 -0
  470. package/packages/mcp/src/resources/project.ts +29 -0
  471. package/packages/mcp/src/server.ts +431 -0
  472. package/packages/mcp/src/simple.ts +214 -0
  473. package/packages/mcp/src/tools/index.ts +133 -0
  474. package/packages/mcp/src/tools/relay-broadcast.ts +32 -0
  475. package/packages/mcp/src/tools/relay-channel.ts +93 -0
  476. package/packages/mcp/src/tools/relay-connected.ts +52 -0
  477. package/packages/mcp/src/tools/relay-consensus.ts +92 -0
  478. package/packages/mcp/src/tools/relay-continuity.ts +127 -0
  479. package/packages/mcp/src/tools/relay-health.ts +148 -0
  480. package/packages/mcp/src/tools/relay-inbox.ts +70 -0
  481. package/packages/mcp/src/tools/relay-logs.ts +106 -0
  482. package/packages/mcp/src/tools/relay-metrics.ts +140 -0
  483. package/packages/mcp/src/tools/relay-release.ts +54 -0
  484. package/packages/mcp/src/tools/relay-remove-agent.ts +58 -0
  485. package/packages/mcp/src/tools/relay-send.ts +84 -0
  486. package/packages/mcp/src/tools/relay-shadow.ts +67 -0
  487. package/packages/mcp/src/tools/relay-spawn.ts +87 -0
  488. package/packages/mcp/src/tools/relay-status.ts +57 -0
  489. package/packages/mcp/src/tools/relay-subscribe.ts +61 -0
  490. package/packages/mcp/src/tools/relay-who.ts +59 -0
  491. package/packages/mcp/tests/client.test.ts +476 -0
  492. package/packages/mcp/tests/discover.test.ts +195 -0
  493. package/packages/mcp/tests/install.test.ts +123 -0
  494. package/packages/mcp/tests/prompts.test.ts +12 -0
  495. package/packages/mcp/tests/resources.test.ts +53 -0
  496. package/packages/mcp/tests/tools.test.ts +1242 -0
  497. package/packages/mcp/tsconfig.json +22 -0
  498. package/packages/mcp/vitest.config.ts +9 -0
  499. package/packages/memory/dist/adapters/index.d.ts.map +1 -0
  500. package/packages/memory/dist/adapters/index.js.map +1 -0
  501. package/packages/memory/dist/adapters/inmemory.d.ts.map +1 -0
  502. package/packages/memory/dist/adapters/inmemory.js.map +1 -0
  503. package/packages/memory/dist/adapters/supermemory.d.ts.map +1 -0
  504. package/packages/memory/dist/adapters/supermemory.js.map +1 -0
  505. package/packages/memory/dist/context-compaction.d.ts.map +1 -0
  506. package/packages/memory/dist/context-compaction.js.map +1 -0
  507. package/packages/memory/dist/factory.d.ts.map +1 -0
  508. package/packages/memory/dist/factory.js.map +1 -0
  509. package/packages/memory/dist/index.d.ts.map +1 -0
  510. package/packages/memory/dist/index.js.map +1 -0
  511. package/packages/memory/dist/memory-hooks.d.ts.map +1 -0
  512. package/packages/memory/dist/memory-hooks.js.map +1 -0
  513. package/packages/memory/dist/service.d.ts.map +1 -0
  514. package/packages/memory/dist/service.js.map +1 -0
  515. package/packages/memory/dist/types.d.ts.map +1 -0
  516. package/packages/memory/dist/types.js.map +1 -0
  517. package/packages/memory/package.json +2 -2
  518. package/packages/memory/src/adapters/index.ts +8 -0
  519. package/packages/memory/src/adapters/inmemory.ts +265 -0
  520. package/packages/memory/src/adapters/supermemory.ts +449 -0
  521. package/packages/memory/src/context-compaction.test.ts +660 -0
  522. package/packages/memory/src/context-compaction.ts +612 -0
  523. package/packages/memory/src/factory.ts +170 -0
  524. package/packages/memory/src/index.ts +33 -0
  525. package/packages/memory/src/memory-hooks.ts +410 -0
  526. package/packages/memory/src/service.ts +194 -0
  527. package/packages/memory/src/types.ts +211 -0
  528. package/packages/memory/tsconfig.json +21 -0
  529. package/packages/memory/vitest.config.ts +9 -0
  530. package/packages/policy/dist/agent-policy.d.ts.map +1 -0
  531. package/packages/policy/dist/agent-policy.js.map +1 -0
  532. package/packages/policy/dist/cloud-policy-fetcher.d.ts.map +1 -0
  533. package/packages/policy/dist/cloud-policy-fetcher.js.map +1 -0
  534. package/packages/policy/dist/index.d.ts.map +1 -0
  535. package/packages/policy/dist/index.js.map +1 -0
  536. package/packages/policy/package.json +2 -2
  537. package/packages/policy/src/agent-policy.ts +866 -0
  538. package/packages/policy/src/cloud-policy-fetcher.ts +78 -0
  539. package/packages/policy/src/index.ts +21 -0
  540. package/packages/policy/tsconfig.json +21 -0
  541. package/packages/policy/vitest.config.ts +9 -0
  542. package/packages/protocol/dist/channels.d.ts.map +1 -0
  543. package/packages/protocol/dist/channels.js.map +1 -0
  544. package/packages/protocol/dist/framing.d.ts.map +1 -0
  545. package/packages/protocol/dist/framing.js.map +1 -0
  546. package/packages/protocol/dist/id-generator.d.ts.map +1 -0
  547. package/packages/protocol/dist/id-generator.js.map +1 -0
  548. package/packages/protocol/dist/index.d.ts.map +1 -0
  549. package/packages/protocol/dist/index.js.map +1 -0
  550. package/packages/protocol/dist/relay-pty-schemas.d.ts +70 -2
  551. package/packages/protocol/dist/relay-pty-schemas.d.ts.map +1 -0
  552. package/packages/protocol/dist/relay-pty-schemas.js.map +1 -0
  553. package/packages/protocol/dist/types.d.ts +8 -0
  554. package/packages/protocol/dist/types.d.ts.map +1 -0
  555. package/packages/protocol/dist/types.js.map +1 -0
  556. package/packages/protocol/package.json +1 -1
  557. package/packages/protocol/src/channels.test.ts +330 -0
  558. package/packages/protocol/src/channels.ts +270 -0
  559. package/packages/protocol/src/framing.test.ts +164 -0
  560. package/packages/protocol/src/framing.ts +242 -0
  561. package/packages/protocol/src/id-generator.ts +69 -0
  562. package/packages/protocol/src/index.ts +4 -0
  563. package/packages/protocol/src/relay-pty-schemas.ts +400 -0
  564. package/packages/protocol/src/types.test.ts +271 -0
  565. package/packages/protocol/src/types.ts +846 -0
  566. package/packages/protocol/tsconfig.json +21 -0
  567. package/packages/protocol/vitest.config.ts +9 -0
  568. package/packages/resiliency/dist/cgroup-manager.d.ts.map +1 -0
  569. package/packages/resiliency/dist/cgroup-manager.js.map +1 -0
  570. package/packages/resiliency/dist/context-persistence.d.ts.map +1 -0
  571. package/packages/resiliency/dist/context-persistence.js.map +1 -0
  572. package/packages/resiliency/dist/crash-insights.d.ts.map +1 -0
  573. package/packages/resiliency/dist/crash-insights.js.map +1 -0
  574. package/packages/resiliency/dist/gossip-health.d.ts.map +1 -0
  575. package/packages/resiliency/dist/gossip-health.js.map +1 -0
  576. package/packages/resiliency/dist/health-monitor.d.ts.map +1 -0
  577. package/packages/resiliency/dist/health-monitor.js.map +1 -0
  578. package/packages/resiliency/dist/index.d.ts.map +1 -0
  579. package/packages/resiliency/dist/index.js.map +1 -0
  580. package/packages/resiliency/dist/leader-watchdog.d.ts.map +1 -0
  581. package/packages/resiliency/dist/leader-watchdog.js.map +1 -0
  582. package/packages/resiliency/dist/logger.d.ts.map +1 -0
  583. package/packages/resiliency/dist/logger.js.map +1 -0
  584. package/packages/resiliency/dist/memory-monitor.d.ts.map +1 -0
  585. package/packages/resiliency/dist/memory-monitor.js.map +1 -0
  586. package/packages/resiliency/dist/metrics.d.ts.map +1 -0
  587. package/packages/resiliency/dist/metrics.js.map +1 -0
  588. package/packages/resiliency/dist/provider-context.d.ts.map +1 -0
  589. package/packages/resiliency/dist/provider-context.js.map +1 -0
  590. package/packages/resiliency/dist/stateless-lead.d.ts.map +1 -0
  591. package/packages/resiliency/dist/stateless-lead.js.map +1 -0
  592. package/packages/resiliency/dist/supervisor.d.ts.map +1 -0
  593. package/packages/resiliency/dist/supervisor.js.map +1 -0
  594. package/packages/resiliency/package.json +1 -1
  595. package/packages/resiliency/src/cgroup-manager.ts +468 -0
  596. package/packages/resiliency/src/context-persistence.ts +538 -0
  597. package/packages/resiliency/src/crash-insights.test.ts +620 -0
  598. package/packages/resiliency/src/crash-insights.ts +660 -0
  599. package/packages/resiliency/src/gossip-health.ts +333 -0
  600. package/packages/resiliency/src/health-monitor.ts +371 -0
  601. package/packages/resiliency/src/index.ts +157 -0
  602. package/packages/resiliency/src/leader-watchdog.ts +260 -0
  603. package/packages/resiliency/src/logger.ts +320 -0
  604. package/packages/resiliency/src/memory-monitor.test.ts +637 -0
  605. package/packages/resiliency/src/memory-monitor.ts +740 -0
  606. package/packages/resiliency/src/metrics.ts +311 -0
  607. package/packages/resiliency/src/provider-context.ts +452 -0
  608. package/packages/resiliency/src/stateless-lead.ts +408 -0
  609. package/packages/resiliency/src/supervisor.ts +578 -0
  610. package/packages/resiliency/tsconfig.json +21 -0
  611. package/packages/resiliency/vitest.config.ts +9 -0
  612. package/packages/sdk/dist/client.d.ts.map +1 -0
  613. package/packages/sdk/dist/client.js.map +1 -0
  614. package/packages/sdk/dist/index.d.ts.map +1 -0
  615. package/packages/sdk/dist/index.js.map +1 -0
  616. package/packages/sdk/dist/logs.d.ts.map +1 -0
  617. package/packages/sdk/dist/logs.js.map +1 -0
  618. package/packages/sdk/dist/protocol/index.d.ts.map +1 -0
  619. package/packages/sdk/dist/protocol/index.js.map +1 -0
  620. package/packages/sdk/dist/standalone.d.ts.map +1 -0
  621. package/packages/sdk/dist/standalone.js.map +1 -0
  622. package/packages/sdk/examples/SWARM_CAPABILITIES.md +498 -0
  623. package/packages/sdk/examples/SWARM_PATTERNS.md +541 -0
  624. package/packages/sdk/package.json +2 -2
  625. package/packages/sdk/src/client.test.ts +568 -0
  626. package/packages/sdk/src/client.ts +1418 -0
  627. package/packages/sdk/src/index.ts +103 -0
  628. package/packages/sdk/src/logs.test.ts +98 -0
  629. package/packages/sdk/src/logs.ts +126 -0
  630. package/packages/sdk/src/protocol/framing.test.ts +164 -0
  631. package/packages/sdk/src/protocol/index.ts +8 -0
  632. package/packages/sdk/src/standalone.ts +176 -0
  633. package/packages/sdk/tsconfig.json +22 -0
  634. package/packages/sdk/vitest.config.ts +9 -0
  635. package/packages/spawner/.trajectories/index.json +5 -0
  636. package/packages/spawner/dist/index.d.ts.map +1 -0
  637. package/packages/spawner/dist/index.js.map +1 -0
  638. package/packages/spawner/dist/types.d.ts.map +1 -0
  639. package/packages/spawner/dist/types.js.map +1 -0
  640. package/packages/spawner/package.json +1 -1
  641. package/packages/spawner/src/index.ts +8 -0
  642. package/packages/spawner/src/types.test.ts +385 -0
  643. package/packages/spawner/src/types.ts +228 -0
  644. package/packages/spawner/tsconfig.json +19 -0
  645. package/packages/spawner/vitest.config.ts +9 -0
  646. package/packages/state/dist/agent-state.d.ts.map +1 -0
  647. package/packages/state/dist/agent-state.js.map +1 -0
  648. package/packages/state/dist/index.d.ts.map +1 -0
  649. package/packages/state/dist/index.js.map +1 -0
  650. package/packages/state/package.json +1 -1
  651. package/packages/state/src/agent-state.test.ts +335 -0
  652. package/packages/state/src/agent-state.ts +153 -0
  653. package/packages/state/src/index.ts +12 -0
  654. package/packages/state/tsconfig.json +21 -0
  655. package/packages/state/vitest.config.ts +9 -0
  656. package/packages/storage/dist/adapter.d.ts +28 -1
  657. package/packages/storage/dist/adapter.d.ts.map +1 -0
  658. package/packages/storage/dist/adapter.js +104 -10
  659. package/packages/storage/dist/adapter.js.map +1 -0
  660. package/packages/storage/dist/batched-sqlite-adapter.d.ts.map +1 -0
  661. package/packages/storage/dist/batched-sqlite-adapter.js.map +1 -0
  662. package/packages/storage/dist/dead-letter-queue.d.ts.map +1 -0
  663. package/packages/storage/dist/dead-letter-queue.js.map +1 -0
  664. package/packages/storage/dist/dlq-adapter.d.ts.map +1 -0
  665. package/packages/storage/dist/dlq-adapter.js.map +1 -0
  666. package/packages/storage/dist/index.d.ts +1 -0
  667. package/packages/storage/dist/index.d.ts.map +1 -0
  668. package/packages/storage/dist/index.js +1 -0
  669. package/packages/storage/dist/index.js.map +1 -0
  670. package/packages/storage/dist/jsonl-adapter.d.ts +77 -0
  671. package/packages/storage/dist/jsonl-adapter.d.ts.map +1 -0
  672. package/packages/storage/dist/jsonl-adapter.js +505 -0
  673. package/packages/storage/dist/jsonl-adapter.js.map +1 -0
  674. package/packages/storage/dist/sqlite-adapter.d.ts +6 -1
  675. package/packages/storage/dist/sqlite-adapter.d.ts.map +1 -0
  676. package/packages/storage/dist/sqlite-adapter.js +47 -0
  677. package/packages/storage/dist/sqlite-adapter.js.map +1 -0
  678. package/packages/storage/package.json +2 -2
  679. package/packages/storage/src/adapter.ts +438 -0
  680. package/packages/storage/src/batched-sqlite-adapter.test.ts +240 -0
  681. package/packages/storage/src/batched-sqlite-adapter.ts +239 -0
  682. package/packages/storage/src/dead-letter-queue.ts +643 -0
  683. package/packages/storage/src/dlq-adapter.test.ts +492 -0
  684. package/packages/storage/src/dlq-adapter.ts +954 -0
  685. package/packages/storage/src/index.ts +6 -0
  686. package/packages/storage/src/jsonl-adapter.test.ts +200 -0
  687. package/packages/storage/src/jsonl-adapter.ts +618 -0
  688. package/packages/storage/src/memory-adapter.test.ts +36 -0
  689. package/packages/storage/src/sqlite-adapter.test.ts +562 -0
  690. package/packages/storage/src/sqlite-adapter.ts +1058 -0
  691. package/packages/storage/tsconfig.json +21 -0
  692. package/packages/storage/vitest.config.ts +9 -0
  693. package/packages/telemetry/dist/client.d.ts.map +1 -0
  694. package/packages/telemetry/dist/client.js.map +1 -0
  695. package/packages/telemetry/dist/config.d.ts.map +1 -0
  696. package/packages/telemetry/dist/config.js.map +1 -0
  697. package/packages/telemetry/dist/events.d.ts.map +1 -0
  698. package/packages/telemetry/dist/events.js.map +1 -0
  699. package/packages/telemetry/dist/index.d.ts.map +1 -0
  700. package/packages/telemetry/dist/index.js.map +1 -0
  701. package/packages/telemetry/dist/machine-id.d.ts.map +1 -0
  702. package/packages/telemetry/dist/machine-id.js.map +1 -0
  703. package/packages/telemetry/dist/posthog-config.d.ts.map +1 -0
  704. package/packages/telemetry/dist/posthog-config.js.map +1 -0
  705. package/packages/telemetry/package.json +1 -1
  706. package/packages/telemetry/src/client.ts +158 -0
  707. package/packages/telemetry/src/config.ts +110 -0
  708. package/packages/telemetry/src/events.ts +137 -0
  709. package/packages/telemetry/src/index.ts +46 -0
  710. package/packages/telemetry/src/machine-id.ts +63 -0
  711. package/packages/telemetry/src/posthog-config.ts +39 -0
  712. package/packages/telemetry/tsconfig.json +21 -0
  713. package/packages/trajectory/dist/index.d.ts.map +1 -0
  714. package/packages/trajectory/dist/index.js.map +1 -0
  715. package/packages/trajectory/dist/integration.d.ts.map +1 -0
  716. package/packages/trajectory/dist/integration.js.map +1 -0
  717. package/packages/trajectory/package.json +2 -2
  718. package/packages/trajectory/src/index.ts +1 -0
  719. package/packages/trajectory/src/integration.ts +1268 -0
  720. package/packages/trajectory/tsconfig.json +21 -0
  721. package/packages/trajectory/vitest.config.ts +9 -0
  722. package/packages/user-directory/dist/index.d.ts.map +1 -0
  723. package/packages/user-directory/dist/index.js.map +1 -0
  724. package/packages/user-directory/dist/user-directory.d.ts.map +1 -0
  725. package/packages/user-directory/dist/user-directory.js.map +1 -0
  726. package/packages/user-directory/package.json +2 -2
  727. package/packages/user-directory/src/index.ts +12 -0
  728. package/packages/user-directory/src/user-directory.ts +393 -0
  729. package/packages/user-directory/tsconfig.json +21 -0
  730. package/packages/user-directory/vitest.config.ts +9 -0
  731. package/packages/utils/dist/cjs/client-helpers.js +127 -0
  732. package/packages/utils/dist/cjs/command-resolver.js +89 -0
  733. package/packages/utils/dist/cjs/error-tracking.js +106 -0
  734. package/packages/utils/dist/cjs/git-remote.js +120 -0
  735. package/packages/utils/dist/cjs/index.js +40 -0
  736. package/packages/utils/dist/cjs/logger.js +105 -0
  737. package/packages/utils/dist/cjs/model-mapping.js +54 -0
  738. package/packages/utils/dist/cjs/name-generator.js +179 -0
  739. package/packages/utils/dist/cjs/package.json +3 -0
  740. package/packages/utils/dist/cjs/precompiled-patterns.js +271 -0
  741. package/packages/utils/dist/cjs/relay-pty-path.js +143 -0
  742. package/packages/utils/dist/cjs/update-checker.js +185 -0
  743. package/packages/utils/dist/client-helpers.d.ts +73 -0
  744. package/packages/utils/dist/client-helpers.d.ts.map +1 -0
  745. package/packages/utils/dist/client-helpers.js +130 -0
  746. package/packages/utils/dist/client-helpers.js.map +1 -0
  747. package/packages/utils/dist/command-resolver.d.ts.map +1 -0
  748. package/packages/utils/dist/command-resolver.js.map +1 -0
  749. package/packages/utils/dist/error-tracking.d.ts.map +1 -0
  750. package/packages/utils/dist/error-tracking.js.map +1 -0
  751. package/packages/utils/dist/git-remote.d.ts.map +1 -0
  752. package/packages/utils/dist/git-remote.js.map +1 -0
  753. package/packages/utils/dist/index.d.ts +1 -0
  754. package/packages/utils/dist/index.d.ts.map +1 -0
  755. package/packages/utils/dist/index.js +1 -0
  756. package/packages/utils/dist/index.js.map +1 -0
  757. package/packages/utils/dist/logger.d.ts.map +1 -0
  758. package/packages/utils/dist/logger.js.map +1 -0
  759. package/packages/utils/dist/model-mapping.d.ts.map +1 -0
  760. package/packages/utils/dist/model-mapping.js.map +1 -0
  761. package/packages/utils/dist/name-generator.d.ts.map +1 -0
  762. package/packages/utils/dist/name-generator.js.map +1 -0
  763. package/packages/utils/dist/precompiled-patterns.d.ts.map +1 -0
  764. package/packages/utils/dist/precompiled-patterns.js.map +1 -0
  765. package/packages/utils/dist/relay-pty-path.d.ts +11 -5
  766. package/packages/utils/dist/relay-pty-path.d.ts.map +1 -0
  767. package/packages/utils/dist/relay-pty-path.js +60 -5
  768. package/packages/utils/dist/relay-pty-path.js.map +1 -0
  769. package/packages/utils/dist/update-checker.d.ts.map +1 -0
  770. package/packages/utils/dist/update-checker.js.map +1 -0
  771. package/packages/utils/package.json +37 -14
  772. package/packages/utils/scripts/build-cjs.mjs +24 -0
  773. package/packages/utils/src/client-helpers.ts +221 -0
  774. package/packages/utils/src/command-resolver.ts +82 -0
  775. package/packages/utils/src/error-tracking.ts +189 -0
  776. package/packages/utils/src/git-remote.ts +143 -0
  777. package/packages/utils/src/index.ts +10 -0
  778. package/packages/utils/src/logger.ts +107 -0
  779. package/packages/utils/src/model-mapping.test.ts +122 -0
  780. package/packages/utils/src/model-mapping.ts +58 -0
  781. package/packages/utils/src/name-generator.test.ts +259 -0
  782. package/packages/utils/src/name-generator.ts +56 -0
  783. package/packages/utils/src/precompiled-patterns.test.ts +452 -0
  784. package/packages/utils/src/precompiled-patterns.ts +395 -0
  785. package/packages/utils/src/relay-pty-path.ts +196 -0
  786. package/packages/utils/src/update-checker.test.ts +260 -0
  787. package/packages/utils/src/update-checker.ts +211 -0
  788. package/packages/utils/tsconfig.json +21 -0
  789. package/packages/utils/vitest.config.ts +9 -0
  790. package/packages/wrapper/dist/__fixtures__/claude-outputs.d.ts.map +1 -0
  791. package/packages/wrapper/dist/__fixtures__/claude-outputs.js.map +1 -0
  792. package/packages/wrapper/dist/__fixtures__/codex-outputs.d.ts.map +1 -0
  793. package/packages/wrapper/dist/__fixtures__/codex-outputs.js.map +1 -0
  794. package/packages/wrapper/dist/__fixtures__/gemini-outputs.d.ts.map +1 -0
  795. package/packages/wrapper/dist/__fixtures__/gemini-outputs.js.map +1 -0
  796. package/packages/wrapper/dist/__fixtures__/index.d.ts.map +1 -0
  797. package/packages/wrapper/dist/__fixtures__/index.js.map +1 -0
  798. package/packages/wrapper/dist/auth-detection.d.ts.map +1 -0
  799. package/packages/wrapper/dist/auth-detection.js.map +1 -0
  800. package/packages/wrapper/dist/base-wrapper.d.ts.map +1 -0
  801. package/packages/wrapper/dist/base-wrapper.js.map +1 -0
  802. package/packages/wrapper/dist/client.d.ts.map +1 -0
  803. package/packages/wrapper/dist/client.js.map +1 -0
  804. package/packages/wrapper/dist/id-generator.d.ts.map +1 -0
  805. package/packages/wrapper/dist/id-generator.js.map +1 -0
  806. package/packages/wrapper/dist/idle-detector.d.ts.map +1 -0
  807. package/packages/wrapper/dist/idle-detector.js.map +1 -0
  808. package/packages/wrapper/dist/inbox.d.ts.map +1 -0
  809. package/packages/wrapper/dist/inbox.js.map +1 -0
  810. package/packages/wrapper/dist/index.d.ts.map +1 -0
  811. package/packages/wrapper/dist/index.js.map +1 -0
  812. package/packages/wrapper/dist/parser.d.ts.map +1 -0
  813. package/packages/wrapper/dist/parser.js.map +1 -0
  814. package/packages/wrapper/dist/prompt-composer.d.ts.map +1 -0
  815. package/packages/wrapper/dist/prompt-composer.js.map +1 -0
  816. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +10 -0
  817. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts.map +1 -0
  818. package/packages/wrapper/dist/relay-pty-orchestrator.js +69 -0
  819. package/packages/wrapper/dist/relay-pty-orchestrator.js.map +1 -0
  820. package/packages/wrapper/dist/shared.d.ts.map +1 -0
  821. package/packages/wrapper/dist/shared.js.map +1 -0
  822. package/packages/wrapper/dist/stuck-detector.d.ts.map +1 -0
  823. package/packages/wrapper/dist/stuck-detector.js.map +1 -0
  824. package/packages/wrapper/dist/tmux-resolver.d.ts.map +1 -0
  825. package/packages/wrapper/dist/tmux-resolver.js.map +1 -0
  826. package/packages/wrapper/dist/tmux-wrapper.d.ts.map +1 -0
  827. package/packages/wrapper/dist/tmux-wrapper.js.map +1 -0
  828. package/packages/wrapper/dist/trajectory-integration.d.ts.map +1 -0
  829. package/packages/wrapper/dist/trajectory-integration.js.map +1 -0
  830. package/packages/wrapper/dist/wrapper-types.d.ts.map +1 -0
  831. package/packages/wrapper/dist/wrapper-types.js.map +1 -0
  832. package/packages/wrapper/package.json +6 -9
  833. package/packages/wrapper/src/__fixtures__/claude-outputs.ts +471 -0
  834. package/packages/wrapper/src/__fixtures__/codex-outputs.ts +99 -0
  835. package/packages/wrapper/src/__fixtures__/gemini-outputs.ts +151 -0
  836. package/packages/wrapper/src/__fixtures__/index.ts +47 -0
  837. package/packages/wrapper/src/auth-detection.ts +244 -0
  838. package/packages/wrapper/src/base-wrapper.test.ts +589 -0
  839. package/packages/wrapper/src/base-wrapper.ts +810 -0
  840. package/packages/wrapper/src/client.test.ts +262 -0
  841. package/packages/wrapper/src/client.ts +984 -0
  842. package/packages/wrapper/src/id-generator.test.ts +71 -0
  843. package/packages/wrapper/src/id-generator.ts +69 -0
  844. package/packages/wrapper/src/idle-detector.test.ts +418 -0
  845. package/packages/wrapper/src/idle-detector.ts +384 -0
  846. package/packages/wrapper/src/inbox.test.ts +233 -0
  847. package/packages/wrapper/src/inbox.ts +89 -0
  848. package/packages/wrapper/src/index.ts +170 -0
  849. package/packages/wrapper/src/parser.regression.test.ts +251 -0
  850. package/packages/wrapper/src/parser.test.ts +1359 -0
  851. package/packages/wrapper/src/parser.ts +1477 -0
  852. package/packages/wrapper/src/prompt-composer.test.ts +219 -0
  853. package/packages/wrapper/src/prompt-composer.ts +231 -0
  854. package/packages/wrapper/src/relay-pty-orchestrator.test.ts +1204 -0
  855. package/packages/wrapper/src/relay-pty-orchestrator.ts +2626 -0
  856. package/packages/wrapper/src/shared.test.ts +322 -0
  857. package/packages/wrapper/src/shared.ts +495 -0
  858. package/packages/wrapper/src/stuck-detector.test.ts +303 -0
  859. package/packages/wrapper/src/stuck-detector.ts +511 -0
  860. package/packages/wrapper/src/tmux-resolver.test.ts +104 -0
  861. package/packages/wrapper/src/tmux-resolver.ts +207 -0
  862. package/packages/wrapper/src/tmux-wrapper.test.ts +316 -0
  863. package/packages/wrapper/src/tmux-wrapper.ts +2095 -0
  864. package/packages/wrapper/src/trajectory-detection.test.ts +151 -0
  865. package/packages/wrapper/src/trajectory-integration.ts +1261 -0
  866. package/packages/wrapper/src/wrapper-types.ts +45 -0
  867. package/packages/wrapper/tsconfig.json +19 -0
  868. package/packages/wrapper/vitest.config.ts +9 -0
  869. package/scripts/build-cjs.mjs +23 -0
  870. package/scripts/postinstall.js +132 -0
  871. package/.cursor/mcp.json +0 -11
  872. package/.gitattributes +0 -3
  873. package/.gitleaks.toml +0 -26
  874. package/.mcp.json +0 -11
  875. package/.nvmrc +0 -1
  876. package/ARCHITECTURE.md +0 -1245
  877. package/CHANGELOG.md +0 -231
  878. package/TESTING.md +0 -278
  879. package/TRAIL_GIT_AUTH_FIX.md +0 -113
  880. package/scripts/demos/README.md +0 -79
  881. package/scripts/demos/server-capacity.sh +0 -69
  882. package/scripts/demos/sprint-planning.sh +0 -73
  883. package/scripts/hooks/install.sh +0 -16
  884. package/scripts/hooks/pre-commit +0 -60
  885. package/scripts/post-publish-verify/README.md +0 -80
  886. package/scripts/post-publish-verify/run-verify.sh +0 -127
  887. package/scripts/post-publish-verify/verify-install.sh +0 -249
  888. package/scripts/stress-test-orchestrator-integration.mts +0 -1366
  889. package/scripts/stress-test-orchestrator.mjs +0 -584
  890. package/scripts/stress-test-relay-pty.sh +0 -452
  891. package/scripts/test-interactive-terminal.sh +0 -248
  892. package/specs/PRIMITIVES_ROADMAP.md +0 -2154
  893. package/tests/benchmarks/protocol.bench.ts +0 -310
  894. package/turbo.json +0 -37
@@ -0,0 +1,1871 @@
1
+ /**
2
+ * Agent Relay Daemon Server
3
+ * Main entry point for the relay daemon.
4
+ */
5
+
6
+ import net from 'node:net';
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import os from 'node:os';
10
+ import { createRequire } from 'node:module';
11
+ import { Connection, type ConnectionConfig, DEFAULT_CONFIG } from './connection.js';
12
+ import { Router } from './router.js';
13
+ import {
14
+ PROTOCOL_VERSION,
15
+ type Envelope,
16
+ type ShadowBindPayload,
17
+ type ShadowUnbindPayload,
18
+ type LogPayload,
19
+ type SendEnvelope,
20
+ type AckPayload,
21
+ type ErrorPayload,
22
+ type SpawnPayload,
23
+ type ReleasePayload,
24
+ type StatusResponsePayload,
25
+ type InboxPayload,
26
+ type InboxResponsePayload,
27
+ type MessagesQueryPayload,
28
+ type MessagesResponsePayload,
29
+ type ListAgentsPayload,
30
+ type ListAgentsResponsePayload,
31
+ type ListConnectedAgentsPayload,
32
+ type ListConnectedAgentsResponsePayload,
33
+ type RemoveAgentPayload,
34
+ type RemoveAgentResponsePayload,
35
+ type HealthPayload,
36
+ type HealthResponsePayload,
37
+ type MetricsPayload,
38
+ type MetricsResponsePayload,
39
+ } from '@agent-relay/protocol/types';
40
+ import type { ChannelJoinPayload, ChannelLeavePayload, ChannelMessagePayload } from '@agent-relay/protocol/channels';
41
+ import { SpawnManager, type SpawnManagerConfig } from './spawn-manager.js';
42
+ import { createStorageAdapter, type StorageAdapter, type StorageConfig, type StorageHealth } from '@agent-relay/storage/adapter';
43
+ import { getProjectPaths } from '@agent-relay/config';
44
+ import { AgentRegistry } from './agent-registry.js';
45
+ import { daemonLog as log } from '@agent-relay/utils/logger';
46
+ import { getCloudSync, type CloudSyncService, type RemoteAgent, type CrossMachineMessage, type AgentMetricsProvider } from './cloud-sync.js';
47
+ import { getMemoryMonitor } from '@agent-relay/resiliency';
48
+ import { generateId } from '@agent-relay/wrapper';
49
+ import {
50
+ ConsensusIntegration,
51
+ createConsensusIntegration,
52
+ type ConsensusIntegrationConfig,
53
+ } from './consensus-integration.js';
54
+ import type { ChannelMembershipStore } from './channel-membership-store.js';
55
+ import {
56
+ initTelemetry,
57
+ track,
58
+ shutdown as shutdownTelemetry,
59
+ } from '@agent-relay/telemetry';
60
+ import { RelayWatchdog, type ProcessedFile } from './relay-watchdog.js';
61
+ import type { RelayPaths } from '@agent-relay/config/relay-file-writer';
62
+
63
+ // Get version from package.json
64
+ const require = createRequire(import.meta.url);
65
+ const packageJson = require('../package.json');
66
+ const DAEMON_VERSION: string = packageJson.version;
67
+
68
+ export interface DaemonConfig extends ConnectionConfig {
69
+ socketPath: string;
70
+ pidFilePath: string;
71
+ storagePath?: string;
72
+ storageAdapter?: StorageAdapter;
73
+ /** Storage configuration (type, path, url) */
74
+ storageConfig?: StorageConfig;
75
+ /** Directory for team data (agents.json, etc.) */
76
+ teamDir?: string;
77
+ /** Enable cloud sync for cross-machine agent communication */
78
+ cloudSync?: boolean;
79
+ /** Cloud API URL (defaults to https://agent-relay.com) */
80
+ cloudUrl?: string;
81
+ /** Consensus mechanism for multi-agent decisions (enabled by default, set to false to disable) */
82
+ consensus?: boolean | Partial<ConsensusIntegrationConfig>;
83
+ /** Enable protocol-based spawning via SPAWN/RELEASE messages */
84
+ spawnManager?: boolean | Partial<SpawnManagerConfig>;
85
+ }
86
+
87
+ export const DEFAULT_SOCKET_PATH = '/tmp/agent-relay.sock';
88
+
89
+ export const DEFAULT_DAEMON_CONFIG: DaemonConfig = {
90
+ ...DEFAULT_CONFIG,
91
+ socketPath: DEFAULT_SOCKET_PATH,
92
+ pidFilePath: `${DEFAULT_SOCKET_PATH}.pid`,
93
+ };
94
+
95
+ interface PendingAck {
96
+ correlationId: string;
97
+ connectionId: string;
98
+ connection: Connection;
99
+ timeoutHandle: NodeJS.Timeout;
100
+ }
101
+
102
+ export class Daemon {
103
+ private server: net.Server;
104
+ private router!: Router;
105
+ private config: DaemonConfig;
106
+ private running = false;
107
+ private connections: Set<Connection> = new Set();
108
+ private pendingAcks: Map<string, PendingAck> = new Map();
109
+ private storage?: StorageAdapter;
110
+ private storageInitialized = false;
111
+ private registry?: AgentRegistry;
112
+ private processingStateInterval?: NodeJS.Timeout;
113
+ private cloudSync?: CloudSyncService;
114
+ private remoteAgents: RemoteAgent[] = [];
115
+ private remoteUsers: RemoteAgent[] = [];
116
+ private consensus?: ConsensusIntegration;
117
+ private cloudSyncDebounceTimer?: NodeJS.Timeout;
118
+ private spawnManager?: SpawnManager;
119
+ private shuttingDown = false;
120
+ private relayWatchdog?: RelayWatchdog;
121
+ private storageHealth?: StorageHealth;
122
+
123
+ /** Telemetry tracking */
124
+ private startTime?: number;
125
+ private agentSpawnCount = 0;
126
+
127
+ /** Callback for log output from agents (used by dashboard for streaming) */
128
+ onLogOutput?: (agentName: string, data: string, timestamp: number) => void;
129
+
130
+ /** Interval for writing processing state file (500ms for responsive UI) */
131
+ private static readonly PROCESSING_STATE_INTERVAL_MS = 500;
132
+ private static readonly DEFAULT_SYNC_TIMEOUT_MS = 30000;
133
+
134
+ constructor(config: Partial<DaemonConfig> = {}) {
135
+ this.config = { ...DEFAULT_DAEMON_CONFIG, ...config };
136
+ if (config.socketPath && !config.pidFilePath) {
137
+ this.config.pidFilePath = `${config.socketPath}.pid`;
138
+ }
139
+ // Default teamDir to same directory as socket
140
+ if (!this.config.teamDir) {
141
+ this.config.teamDir = path.dirname(this.config.socketPath);
142
+ }
143
+ if (this.config.teamDir) {
144
+ this.registry = new AgentRegistry(this.config.teamDir);
145
+ }
146
+ // SpawnManager is initialized in start() after router is created
147
+ // so we can wire up onMarkSpawning/onClearSpawning callbacks
148
+ // Storage is initialized lazily in start() to support async createStorageAdapter
149
+ this.server = net.createServer(this.handleConnection.bind(this));
150
+ }
151
+
152
+ /**
153
+ * Write current agents to agents.json for dashboard consumption.
154
+ */
155
+ private writeAgentsFile(): void {
156
+ if (!this.registry) return;
157
+ // The registry persists on every update; this is a no-op helper for symmetry.
158
+ const agents = this.registry.getAgents();
159
+ try {
160
+ const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
161
+ const targetPath = path.join(targetDir, 'agents.json');
162
+ // Ensure directory exists (defensive - may have been deleted)
163
+ if (!fs.existsSync(targetDir)) {
164
+ fs.mkdirSync(targetDir, { recursive: true });
165
+ }
166
+ const data = JSON.stringify({ agents }, null, 2);
167
+ // Write atomically: write to temp file first, then rename
168
+ // This prevents race conditions where readers see partial/empty data
169
+ const tempPath = `${targetPath}.tmp`;
170
+ fs.writeFileSync(tempPath, data, 'utf-8');
171
+ fs.renameSync(tempPath, targetPath);
172
+ } catch (err) {
173
+ log.error('Failed to write agents.json', { error: String(err) });
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Write processing state to processing-state.json for dashboard consumption.
179
+ * This file contains agents currently processing/thinking after receiving a message.
180
+ */
181
+ private writeProcessingStateFile(): void {
182
+ // Skip writes during shutdown to avoid race conditions with directory cleanup
183
+ if (this.shuttingDown) return;
184
+
185
+ try {
186
+ const processingAgents = this.router.getProcessingAgents();
187
+ const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
188
+ const targetPath = path.join(targetDir, 'processing-state.json');
189
+ // Ensure directory exists (defensive - may have been deleted)
190
+ if (!fs.existsSync(targetDir)) {
191
+ fs.mkdirSync(targetDir, { recursive: true });
192
+ }
193
+ const data = JSON.stringify({ processingAgents, updatedAt: Date.now() }, null, 2);
194
+ const tempPath = `${targetPath}.tmp`;
195
+ fs.writeFileSync(tempPath, data, 'utf-8');
196
+ fs.renameSync(tempPath, targetPath);
197
+ } catch (err) {
198
+ // Suppress ENOENT errors during shutdown race conditions
199
+ if (!this.shuttingDown) {
200
+ log.error('Failed to write processing-state.json', { error: String(err) });
201
+ }
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Write currently connected agents to connected-agents.json for CLI consumption.
207
+ * This file contains agents with active socket connections (vs agents.json which is historical).
208
+ */
209
+ private writeConnectedAgentsFile(): void {
210
+ try {
211
+ const connectedAgents = this.router.getAgents();
212
+ const connectedUsers = this.router.getUsers();
213
+ const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
214
+ const targetPath = path.join(targetDir, 'connected-agents.json');
215
+
216
+ // Debug: log what we're writing
217
+ log.info('Writing connected-agents.json', {
218
+ agents: connectedAgents.join(','),
219
+ path: targetPath,
220
+ teamDir: this.config.teamDir,
221
+ });
222
+
223
+ // Ensure directory exists (defensive - may have been deleted)
224
+ if (!fs.existsSync(targetDir)) {
225
+ fs.mkdirSync(targetDir, { recursive: true });
226
+ }
227
+ const data = JSON.stringify({
228
+ agents: connectedAgents,
229
+ users: connectedUsers,
230
+ updatedAt: Date.now(),
231
+ }, null, 2);
232
+ const tempPath = `${targetPath}.tmp`;
233
+ fs.writeFileSync(tempPath, data, 'utf-8');
234
+ fs.renameSync(tempPath, targetPath);
235
+ } catch (err) {
236
+ log.error('Failed to write connected-agents.json', { error: String(err) });
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Mark an agent as spawning (before HELLO completes).
242
+ * Messages sent to this agent will be queued for delivery after registration.
243
+ * Call this before starting the agent's PTY process.
244
+ */
245
+ markSpawning(agentName: string): void {
246
+ this.router.markSpawning(agentName);
247
+ }
248
+
249
+ /**
250
+ * Clear the spawning flag for an agent.
251
+ * Called when spawn fails or is cancelled (successful registration clears automatically).
252
+ */
253
+ clearSpawning(agentName: string): void {
254
+ this.router.clearSpawning(agentName);
255
+ }
256
+
257
+ /**
258
+ * Remove a stale agent from the router (used when process dies without clean disconnect).
259
+ * This is called by the orchestrator's health monitoring when a PID is detected as dead.
260
+ */
261
+ removeStaleAgent(agentName: string): boolean {
262
+ const removed = this.router.forceRemoveAgent(agentName);
263
+ if (removed) {
264
+ // Notify cloud sync about agent removal
265
+ this.notifyCloudSync();
266
+ // Update connected-agents.json to reflect the removal
267
+ this.writeConnectedAgentsFile();
268
+ log.info('Removed stale agent from router', { agentName });
269
+ }
270
+ return removed;
271
+ }
272
+
273
+ /**
274
+ * Initialize storage adapter (called during start).
275
+ */
276
+ private async initStorage(): Promise<void> {
277
+ if (this.storageInitialized) return;
278
+
279
+ if (this.config.storageAdapter) {
280
+ // Use explicitly provided adapter
281
+ this.storage = this.config.storageAdapter;
282
+ } else {
283
+ // Create adapter based on config/env
284
+ const storagePath = this.config.storagePath ??
285
+ path.join(path.dirname(this.config.socketPath), 'agent-relay.sqlite');
286
+ this.storage = await createStorageAdapter(storagePath, this.config.storageConfig);
287
+ }
288
+
289
+ let channelMembershipStore: ChannelMembershipStore | undefined;
290
+ const workspaceId = process.env.RELAY_WORKSPACE_ID
291
+ || process.env.AGENT_RELAY_WORKSPACE_ID
292
+ || process.env.WORKSPACE_ID;
293
+ const databaseUrl = process.env.CLOUD_DATABASE_URL
294
+ || process.env.DATABASE_URL
295
+ || process.env.AGENT_RELAY_STORAGE_URL;
296
+ const isPostgresUrl = databaseUrl?.startsWith('postgres://') || databaseUrl?.startsWith('postgresql://');
297
+
298
+ if (workspaceId && isPostgresUrl && databaseUrl) {
299
+ try {
300
+ const { CloudChannelMembershipStore } = await import('./channel-membership-store.js');
301
+ channelMembershipStore = new CloudChannelMembershipStore({ workspaceId, databaseUrl });
302
+ log.info('Channel membership store enabled (cloud DB)', { workspaceId });
303
+ } catch (err) {
304
+ log.error('Failed to initialize channel membership store', { error: String(err) });
305
+ }
306
+ } else {
307
+ log.debug('Channel membership store disabled (missing workspaceId or Postgres database URL)');
308
+ }
309
+
310
+ this.router = new Router({
311
+ storage: this.storage,
312
+ registry: this.registry,
313
+ onProcessingStateChange: () => this.writeProcessingStateFile(),
314
+ crossMachineHandler: {
315
+ sendCrossMachineMessage: this.sendCrossMachineMessage.bind(this),
316
+ isRemoteAgent: this.isRemoteAgent.bind(this),
317
+ isRemoteUser: this.isRemoteUser.bind(this),
318
+ },
319
+ channelMembershipStore,
320
+ });
321
+
322
+ // Initialize SpawnManager if enabled (after router, so we can wire callbacks)
323
+ if (this.config.spawnManager) {
324
+ const spawnConfig = typeof this.config.spawnManager === 'object'
325
+ ? this.config.spawnManager
326
+ : {};
327
+ // Derive projectRoot from teamDir (teamDir is typically {projectRoot}/.agent-relay/)
328
+ const projectRoot = spawnConfig.projectRoot || path.dirname(this.config.teamDir || this.config.socketPath);
329
+ this.spawnManager = new SpawnManager({
330
+ projectRoot,
331
+ socketPath: this.config.socketPath,
332
+ ...spawnConfig,
333
+ // Track spawn count for telemetry
334
+ onAgentSpawn: () => {
335
+ this.agentSpawnCount++;
336
+ },
337
+ // Wire spawn tracking to router so messages are queued during spawn
338
+ onMarkSpawning: (name: string) => this.router.markSpawning(name),
339
+ onClearSpawning: (name: string) => this.router.clearSpawning(name),
340
+ });
341
+ log.info('SpawnManager initialized with spawn tracking callbacks');
342
+ }
343
+
344
+ // Initialize consensus (enabled by default, can be disabled with consensus: false)
345
+ if (this.config.consensus !== false) {
346
+ const consensusConfig = typeof this.config.consensus === 'object'
347
+ ? this.config.consensus
348
+ : {};
349
+
350
+ this.consensus = createConsensusIntegration(this.router, consensusConfig);
351
+ log.info('Consensus mechanism enabled');
352
+ }
353
+
354
+ this.storageInitialized = true;
355
+ }
356
+
357
+ /**
358
+ * Start the daemon.
359
+ */
360
+ async start(): Promise<void> {
361
+ if (this.running) return;
362
+
363
+ // Initialize telemetry (don't show notice - CLI handles that)
364
+ initTelemetry({ showNotice: false });
365
+ this.startTime = Date.now();
366
+ this.agentSpawnCount = 0;
367
+
368
+ // Initialize storage
369
+ await this.initStorage();
370
+
371
+ // Storage health check: warn if non-persistent (e.g., in-memory fallback)
372
+ try {
373
+ if (this.storage?.healthCheck) {
374
+ this.storageHealth = await this.storage.healthCheck();
375
+ if (!this.storageHealth.persistent) {
376
+ console.warn('[daemon] ⚠️ Running in non-persistent mode!');
377
+ console.warn('[daemon] Messages will be lost on restart.');
378
+ }
379
+ }
380
+ } catch (err) {
381
+ log.warn('Storage health check failed', { error: String(err) });
382
+ }
383
+
384
+ // Restore channel memberships from persisted storage (cloud DB or SQLite)
385
+ await this.router.restoreChannelMemberships();
386
+
387
+ // Initialize cloud sync if configured
388
+ await this.initCloudSync();
389
+
390
+ // Clean up stale socket (only if it's actually a socket)
391
+ if (fs.existsSync(this.config.socketPath)) {
392
+ const stat = fs.lstatSync(this.config.socketPath);
393
+ if (!stat.isSocket()) {
394
+ throw new Error(
395
+ `Refusing to unlink non-socket at ${this.config.socketPath}`
396
+ );
397
+ }
398
+ fs.unlinkSync(this.config.socketPath);
399
+ }
400
+
401
+ // Ensure socket directory exists
402
+ const socketDir = path.dirname(this.config.socketPath);
403
+ if (!fs.existsSync(socketDir)) {
404
+ fs.mkdirSync(socketDir, { recursive: true });
405
+ }
406
+
407
+ // Ensure team directory exists for state files (agents.json, processing-state.json, etc.)
408
+ // Always check and create, even if same as socketDir, to handle edge cases
409
+ const teamDir = this.config.teamDir ?? socketDir;
410
+ if (!fs.existsSync(teamDir)) {
411
+ fs.mkdirSync(teamDir, { recursive: true });
412
+ }
413
+
414
+ // Set up inbox symlink for workspace namespacing
415
+ // Daemon delivers to legacy path (/tmp/relay-inbox), symlink points to workspace path
416
+ // This allows agents to use simple instructions while maintaining workspace isolation
417
+ const workspaceId = process.env.RELAY_WORKSPACE_ID
418
+ || process.env.AGENT_RELAY_WORKSPACE_ID
419
+ || process.env.WORKSPACE_ID;
420
+
421
+ const legacyInboxPath = '/tmp/relay-inbox';
422
+ let inboxPath = legacyInboxPath;
423
+
424
+ if (workspaceId) {
425
+ // Workspace-namespaced inbox directory
426
+ inboxPath = `/tmp/relay/${workspaceId}/inbox`;
427
+
428
+ try {
429
+ // Ensure workspace inbox directory exists
430
+ const inboxDir = path.dirname(inboxPath);
431
+ if (!fs.existsSync(inboxDir)) {
432
+ fs.mkdirSync(inboxDir, { recursive: true });
433
+ }
434
+ if (!fs.existsSync(inboxPath)) {
435
+ fs.mkdirSync(inboxPath, { recursive: true });
436
+ }
437
+
438
+ // Ensure legacy inbox parent directory exists
439
+ const legacyInboxParent = path.dirname(legacyInboxPath);
440
+ if (!fs.existsSync(legacyInboxParent)) {
441
+ fs.mkdirSync(legacyInboxParent, { recursive: true });
442
+ }
443
+
444
+ // Create symlink from legacy path to workspace path
445
+ // If legacy path exists as a regular directory, remove it first
446
+ if (fs.existsSync(legacyInboxPath)) {
447
+ try {
448
+ const stats = fs.lstatSync(legacyInboxPath);
449
+ if (stats.isSymbolicLink()) {
450
+ // Already a symlink - remove and recreate to ensure correct target
451
+ fs.unlinkSync(legacyInboxPath);
452
+ } else if (stats.isDirectory()) {
453
+ // Regular directory - remove it (may have stale files from previous run)
454
+ fs.rmSync(legacyInboxPath, { recursive: true, force: true });
455
+ }
456
+ } catch {
457
+ // Ignore errors during cleanup
458
+ }
459
+ }
460
+
461
+ // Create the symlink: legacy path -> workspace path
462
+ fs.symlinkSync(inboxPath, legacyInboxPath);
463
+ log.info('Created inbox symlink', { from: legacyInboxPath, to: inboxPath });
464
+ } catch (err: any) {
465
+ log.error('Failed to set up inbox symlink', { error: err.message });
466
+ // Fall back to creating legacy directory directly
467
+ try {
468
+ if (!fs.existsSync(legacyInboxPath)) {
469
+ fs.mkdirSync(legacyInboxPath, { recursive: true });
470
+ }
471
+ } catch {
472
+ // Ignore
473
+ }
474
+ }
475
+ } else {
476
+ // No workspace ID - just ensure legacy inbox directory exists
477
+ try {
478
+ if (!fs.existsSync(legacyInboxPath)) {
479
+ fs.mkdirSync(legacyInboxPath, { recursive: true });
480
+ }
481
+ } catch (err: any) {
482
+ log.error('Failed to create inbox directory', { error: err.message });
483
+ }
484
+ }
485
+
486
+ return new Promise((resolve, reject) => {
487
+ this.server.on('error', reject);
488
+ this.server.listen(this.config.socketPath, () => {
489
+ this.running = true;
490
+ // Set restrictive permissions
491
+ fs.chmodSync(this.config.socketPath, 0o600);
492
+ fs.writeFileSync(this.config.pidFilePath, `${process.pid}\n`, 'utf-8');
493
+
494
+ // Start periodic processing state updates for dashboard
495
+ this.processingStateInterval = setInterval(() => {
496
+ this.writeProcessingStateFile();
497
+ }, Daemon.PROCESSING_STATE_INTERVAL_MS);
498
+
499
+ // Start RelayWatchdog for MCP file-based messages (ledger-based for durability)
500
+ // Feature gated: set RELAY_MCP_AUTO_INSTALL=1 to enable file-based MCP messaging
501
+ const mcpAutoInstallEnabled = process.env.RELAY_MCP_AUTO_INSTALL === '1';
502
+ if (mcpAutoInstallEnabled) {
503
+ // Use parent of teamDir since teamDir is .agent-relay/team/ but outbox is at .agent-relay/outbox/
504
+ const relayRoot = path.dirname(teamDir);
505
+ this.initRelayWatchdog(relayRoot).catch(err => {
506
+ log.error('Failed to start RelayWatchdog', { error: String(err) });
507
+ });
508
+ }
509
+
510
+ // Track daemon start
511
+ track('daemon_start', {});
512
+
513
+ log.info('Listening', { socketPath: this.config.socketPath });
514
+ resolve();
515
+ });
516
+ });
517
+ }
518
+
519
+ /**
520
+ * Initialize RelayWatchdog for MCP and file-based agents.
521
+ * Uses the ledger-based watchdog for durable file processing.
522
+ */
523
+ private async initRelayWatchdog(relayDir: string): Promise<void> {
524
+ // Create project-local relay paths
525
+ const relayPaths: RelayPaths = {
526
+ rootDir: relayDir,
527
+ outboxDir: path.join(relayDir, 'outbox'),
528
+ attachmentsDir: path.join(relayDir, 'attachments'),
529
+ metaDir: path.join(relayDir, 'meta'),
530
+ legacyOutboxDir: path.join(relayDir, 'outbox'), // Same as outboxDir for project-local
531
+ };
532
+
533
+ this.relayWatchdog = new RelayWatchdog({
534
+ relayPaths,
535
+ ledgerPath: path.join(relayDir, 'meta', 'file-ledger.sqlite'),
536
+ });
537
+
538
+ // Handle delivered files from file-based agents (MCP, etc.)
539
+ this.relayWatchdog.on('file:delivered', (file: ProcessedFile) => {
540
+ const { agentName, messageType, headers, body } = file;
541
+ log.debug('File delivered', { agentName, messageType, headers });
542
+
543
+ // Determine message type from headers or filename
544
+ const kind = headers['KIND']?.toLowerCase() || messageType;
545
+
546
+ if (kind === 'spawn') {
547
+ // Handle spawn request
548
+ if (this.spawnManager) {
549
+ const envelope: Envelope<SpawnPayload> = {
550
+ v: PROTOCOL_VERSION,
551
+ type: 'SPAWN',
552
+ id: generateId(),
553
+ ts: Date.now(),
554
+ payload: {
555
+ name: headers['NAME'] || '',
556
+ cli: headers['CLI'] || 'claude',
557
+ task: body,
558
+ cwd: headers['CWD'],
559
+ model: headers['MODEL'],
560
+ spawnerName: agentName,
561
+ },
562
+ };
563
+ const virtualConnection = { agentName, send: () => true } as any;
564
+ this.spawnManager.handleSpawn(virtualConnection, envelope);
565
+ } else {
566
+ log.warn('Spawn request ignored - SpawnManager not enabled');
567
+ }
568
+ } else if (kind === 'release') {
569
+ // Handle release request
570
+ if (this.spawnManager) {
571
+ const envelope: Envelope<ReleasePayload> = {
572
+ v: PROTOCOL_VERSION,
573
+ type: 'RELEASE',
574
+ id: generateId(),
575
+ ts: Date.now(),
576
+ payload: {
577
+ name: headers['NAME'] || '',
578
+ reason: body || undefined,
579
+ },
580
+ };
581
+ const virtualConnection = { agentName, send: () => true } as any;
582
+ this.spawnManager.handleRelease(virtualConnection, envelope);
583
+ } else {
584
+ log.warn('Release request ignored - SpawnManager not enabled');
585
+ }
586
+ } else {
587
+ // Default: treat as message
588
+ const to = headers['TO'];
589
+ if (!to) {
590
+ log.warn('Message missing TO header', { agentName, headers });
591
+ return;
592
+ }
593
+
594
+ const envelope: SendEnvelope = {
595
+ v: PROTOCOL_VERSION,
596
+ type: 'SEND',
597
+ id: generateId(),
598
+ ts: Date.now(),
599
+ from: agentName,
600
+ to,
601
+ payload: {
602
+ kind: 'message',
603
+ body,
604
+ thread: headers['THREAD'],
605
+ },
606
+ };
607
+ const virtualConnection = { agentName } as any;
608
+ this.router.route(virtualConnection, envelope);
609
+ }
610
+ });
611
+
612
+ // Log errors
613
+ this.relayWatchdog.on('error', (error: Error) => {
614
+ log.error('RelayWatchdog error', { error: error.message });
615
+ });
616
+
617
+ this.relayWatchdog.on('file:failed', (record, error) => {
618
+ log.error('File processing failed', { fileId: record.fileId, error: error.message });
619
+ });
620
+
621
+ await this.relayWatchdog.start();
622
+ log.info('RelayWatchdog started', { relayDir });
623
+ }
624
+
625
+ /**
626
+ * Initialize cloud sync service for cross-machine agent communication.
627
+ */
628
+ private async initCloudSync(): Promise<void> {
629
+ // Check for cloud config file OR environment variables
630
+ const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
631
+ path.join(os.homedir(), '.local', 'share', 'agent-relay');
632
+ const configPath = path.join(dataDir, 'cloud-config.json');
633
+
634
+ const hasConfigFile = fs.existsSync(configPath);
635
+ const hasEnvApiKey = !!process.env.AGENT_RELAY_API_KEY;
636
+
637
+ // Allow cloud sync if config file exists OR API key is set via env var
638
+ // This enables cloud-hosted workspaces (Fly.io) to sync messages without a config file
639
+ if (!hasConfigFile && !hasEnvApiKey) {
640
+ log.info('Cloud sync disabled (not linked to cloud)');
641
+ return;
642
+ }
643
+
644
+ try {
645
+ let apiKey: string | undefined;
646
+ let cloudUrl: string | undefined;
647
+
648
+ if (hasConfigFile) {
649
+ // Use config file (local daemons linked via CLI)
650
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
651
+ apiKey = config.apiKey;
652
+ cloudUrl = config.cloudUrl;
653
+ } else {
654
+ // Use env vars (cloud-hosted workspaces like Fly.io)
655
+ apiKey = process.env.AGENT_RELAY_API_KEY;
656
+ // CLOUD_API_URL is set by Fly.io provisioner, AGENT_RELAY_CLOUD_URL is the standard
657
+ cloudUrl = process.env.AGENT_RELAY_CLOUD_URL || process.env.CLOUD_API_URL;
658
+ log.info('Using environment variables for cloud sync', { hasApiKey: !!apiKey, hasCloudUrl: !!cloudUrl });
659
+ }
660
+
661
+ // Get project root for workspace detection via git remote
662
+ const projectPaths = getProjectPaths();
663
+
664
+ this.cloudSync = getCloudSync({
665
+ apiKey,
666
+ cloudUrl: cloudUrl || this.config.cloudUrl,
667
+ enabled: this.config.cloudSync !== false,
668
+ projectDirectory: projectPaths.projectRoot,
669
+ });
670
+
671
+ // Listen for remote agent updates
672
+ this.cloudSync.on('remote-agents-updated', (agents: RemoteAgent[]) => {
673
+ this.remoteAgents = agents;
674
+ log.info('Remote agents updated', { count: agents.length });
675
+ this.writeRemoteAgentsFile();
676
+ });
677
+
678
+ // Listen for remote user updates (humans connected via cloud dashboard)
679
+ this.cloudSync.on('remote-users-updated', (users: RemoteAgent[]) => {
680
+ this.remoteUsers = users;
681
+ log.info('Remote users updated', { count: users.length });
682
+ this.writeRemoteUsersFile();
683
+ });
684
+
685
+ // Listen for cross-machine messages
686
+ this.cloudSync.on('cross-machine-message', (msg: CrossMachineMessage) => {
687
+ this.handleCrossMachineMessage(msg);
688
+ });
689
+
690
+ // Listen for cloud commands (e.g., credential refresh)
691
+ this.cloudSync.on('command', (cmd: { type: string; payload: unknown }) => {
692
+ log.info('Cloud command received', { type: cmd.type });
693
+ // Handle commands like credential updates, config changes, etc.
694
+ });
695
+
696
+ await this.cloudSync.start();
697
+
698
+ // Set storage adapter for message sync to cloud
699
+ if (this.storage) {
700
+ this.cloudSync.setStorage(this.storage);
701
+ }
702
+
703
+ // Set metrics provider for agent metrics sync to cloud
704
+ // Uses the singleton memory monitor from @agent-relay/resiliency
705
+ const memoryMonitor = getMemoryMonitor();
706
+ const metricsProvider: AgentMetricsProvider = {
707
+ getAll: () => {
708
+ return memoryMonitor.getAll().map(m => ({
709
+ name: m.name,
710
+ pid: m.pid,
711
+ status: m.alertLevel === 'normal' ? 'running' : m.alertLevel,
712
+ rssBytes: m.current.rssBytes,
713
+ heapUsedBytes: m.current.heapUsedBytes,
714
+ heapTotalBytes: m.current.heapTotalBytes,
715
+ cpuPercent: m.current.cpuPercent,
716
+ trend: m.trend,
717
+ trendRatePerMinute: m.trendRatePerMinute,
718
+ alertLevel: m.alertLevel,
719
+ highWatermark: m.highWatermark,
720
+ averageRss: m.averageRss,
721
+ uptimeMs: m.uptimeMs,
722
+ startedAt: m.startedAt,
723
+ }));
724
+ },
725
+ };
726
+ this.cloudSync.setMetricsProvider(metricsProvider);
727
+
728
+ log.info('Cloud sync enabled');
729
+ } catch (err) {
730
+ log.error('Failed to initialize cloud sync', { error: String(err) });
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Write remote agents to file for dashboard consumption.
736
+ */
737
+ private writeRemoteAgentsFile(): void {
738
+ try {
739
+ const targetPath = path.join(
740
+ this.config.teamDir ?? path.dirname(this.config.socketPath),
741
+ 'remote-agents.json'
742
+ );
743
+ const data = JSON.stringify({
744
+ agents: this.remoteAgents,
745
+ updatedAt: Date.now(),
746
+ }, null, 2);
747
+ const tempPath = `${targetPath}.tmp`;
748
+ fs.writeFileSync(tempPath, data, 'utf-8');
749
+ fs.renameSync(tempPath, targetPath);
750
+ } catch (err) {
751
+ log.error('Failed to write remote-agents.json', { error: String(err) });
752
+ }
753
+ }
754
+
755
+ /**
756
+ * Write remote users to file for dashboard consumption.
757
+ * Remote users are humans connected via the cloud dashboard.
758
+ */
759
+ private writeRemoteUsersFile(): void {
760
+ try {
761
+ const targetPath = path.join(
762
+ this.config.teamDir ?? path.dirname(this.config.socketPath),
763
+ 'remote-users.json'
764
+ );
765
+ const data = JSON.stringify({
766
+ users: this.remoteUsers,
767
+ updatedAt: Date.now(),
768
+ }, null, 2);
769
+ const tempPath = `${targetPath}.tmp`;
770
+ fs.writeFileSync(tempPath, data, 'utf-8');
771
+ fs.renameSync(tempPath, targetPath);
772
+ } catch (err) {
773
+ log.error('Failed to write remote-users.json', { error: String(err) });
774
+ }
775
+ }
776
+
777
+ /**
778
+ * Handle incoming message from another machine via cloud.
779
+ */
780
+ private handleCrossMachineMessage(msg: CrossMachineMessage): void {
781
+ log.info('Cross-machine message received', {
782
+ from: `${msg.from.daemonName}:${msg.from.agent}`,
783
+ to: msg.to,
784
+ });
785
+
786
+ // Find local agent
787
+ const targetConnection = Array.from(this.connections).find(
788
+ c => c.agentName === msg.to
789
+ );
790
+
791
+ if (!targetConnection) {
792
+ log.warn('Target agent not found locally', { agent: msg.to });
793
+ return;
794
+ }
795
+
796
+ // Inject message to local agent
797
+ const envelope: SendEnvelope = {
798
+ v: 1,
799
+ type: 'SEND',
800
+ id: generateId(),
801
+ ts: Date.now(),
802
+ from: `${msg.from.daemonName}:${msg.from.agent}`,
803
+ to: msg.to,
804
+ payload: {
805
+ kind: 'message',
806
+ body: msg.content,
807
+ data: {
808
+ _crossMachine: true,
809
+ _fromDaemon: msg.from.daemonId,
810
+ _fromDaemonName: msg.from.daemonName,
811
+ ...msg.metadata,
812
+ },
813
+ },
814
+ };
815
+
816
+ this.router.route(targetConnection, envelope);
817
+ }
818
+
819
+ /**
820
+ * Send message to agent on another machine via cloud.
821
+ */
822
+ async sendCrossMachineMessage(
823
+ targetDaemonId: string,
824
+ targetAgent: string,
825
+ fromAgent: string,
826
+ content: string,
827
+ metadata?: Record<string, unknown>
828
+ ): Promise<boolean> {
829
+ if (!this.cloudSync?.isConnected()) {
830
+ log.warn('Cannot send cross-machine message: not connected to cloud');
831
+ return false;
832
+ }
833
+
834
+ try {
835
+ await this.cloudSync.sendCrossMachineMessage(
836
+ targetDaemonId,
837
+ targetAgent,
838
+ fromAgent,
839
+ content,
840
+ metadata
841
+ );
842
+ return true;
843
+ } catch (err) {
844
+ log.error('Failed to send cross-machine message', { error: String(err) });
845
+ return false;
846
+ }
847
+ }
848
+
849
+ /**
850
+ * Get list of remote agents (from other machines).
851
+ */
852
+ getRemoteAgents(): RemoteAgent[] {
853
+ return this.remoteAgents;
854
+ }
855
+
856
+ /**
857
+ * Check if an agent is on a remote machine.
858
+ */
859
+ isRemoteAgent(agentName: string): RemoteAgent | undefined {
860
+ return this.remoteAgents.find(a => a.name === agentName);
861
+ }
862
+
863
+ /**
864
+ * Check if a user is on a remote machine (connected via cloud dashboard).
865
+ */
866
+ isRemoteUser(userName: string): RemoteAgent | undefined {
867
+ return this.remoteUsers.find(u => u.name === userName);
868
+ }
869
+
870
+ /**
871
+ * Notify cloud sync about local agent changes.
872
+ * Debounced to prevent flooding the cloud API with rapid connect/disconnect events.
873
+ */
874
+ private notifyCloudSync(): void {
875
+ if (!this.cloudSync?.isConnected()) return;
876
+
877
+ // Debounce: clear any pending sync and schedule a new one
878
+ if (this.cloudSyncDebounceTimer) {
879
+ clearTimeout(this.cloudSyncDebounceTimer);
880
+ }
881
+
882
+ this.cloudSyncDebounceTimer = setTimeout(() => {
883
+ this.cloudSyncDebounceTimer = undefined;
884
+ this.doCloudSync();
885
+ }, 1000); // 1 second debounce
886
+ }
887
+
888
+ /**
889
+ * Actually perform the cloud sync (called after debounce).
890
+ */
891
+ private doCloudSync(): void {
892
+ if (!this.cloudSync?.isConnected()) return;
893
+
894
+ // Get AI agents (exclude internal ones like Dashboard)
895
+ const aiAgents = Array.from(this.connections)
896
+ .filter(c => {
897
+ if (!c.agentName) return false;
898
+ if (c.entityType === 'user') return false;
899
+ if (this.isInternalAgent(c.agentName)) return false;
900
+ return true;
901
+ })
902
+ .map(c => ({
903
+ name: c.agentName!,
904
+ status: 'online',
905
+ isHuman: false,
906
+ }));
907
+
908
+ // Get human users (entityType === 'user', exclude Dashboard)
909
+ const humanUsers = Array.from(this.connections)
910
+ .filter(c => {
911
+ if (!c.agentName) return false;
912
+ if (c.entityType !== 'user') return false;
913
+ if (this.isInternalAgent(c.agentName)) return false;
914
+ return true;
915
+ })
916
+ .map(c => ({
917
+ name: c.agentName!,
918
+ status: 'online',
919
+ isHuman: true,
920
+ avatarUrl: c.avatarUrl,
921
+ }));
922
+
923
+ this.cloudSync.updateAgents([...aiAgents, ...humanUsers]);
924
+ }
925
+
926
+ /**
927
+ * Check if an agent is internal (should be hidden from cloud sync and listings).
928
+ */
929
+ private isInternalAgent(name: string): boolean {
930
+ if (name.startsWith('__')) return true;
931
+ // Dashboard, _DashboardUI, and cli are internal system agents
932
+ return name === 'Dashboard' || name === '_DashboardUI' || name === 'cli';
933
+ }
934
+
935
+ /**
936
+ * Stop the daemon.
937
+ */
938
+ async stop(): Promise<void> {
939
+ if (!this.running) return;
940
+
941
+ // Mark as shutting down to prevent race conditions with state file writes
942
+ this.shuttingDown = true;
943
+
944
+ // Track daemon stop
945
+ const uptimeSeconds = this.startTime
946
+ ? Math.floor((Date.now() - this.startTime) / 1000)
947
+ : 0;
948
+ track('daemon_stop', {
949
+ uptime_seconds: uptimeSeconds,
950
+ agent_spawn_count: this.agentSpawnCount,
951
+ });
952
+
953
+ // Shutdown telemetry (flush pending events)
954
+ await shutdownTelemetry();
955
+
956
+ // Stop cloud sync
957
+ if (this.cloudSync) {
958
+ this.cloudSync.stop();
959
+ this.cloudSync = undefined;
960
+ }
961
+
962
+ // Clear cloud sync debounce timer
963
+ if (this.cloudSyncDebounceTimer) {
964
+ clearTimeout(this.cloudSyncDebounceTimer);
965
+ this.cloudSyncDebounceTimer = undefined;
966
+ }
967
+
968
+ // Stop processing state updates
969
+ if (this.processingStateInterval) {
970
+ clearInterval(this.processingStateInterval);
971
+ this.processingStateInterval = undefined;
972
+ }
973
+
974
+ // Stop RelayWatchdog
975
+ if (this.relayWatchdog) {
976
+ await this.relayWatchdog.stop();
977
+ this.relayWatchdog = undefined;
978
+ }
979
+
980
+ // Close all active connections
981
+ for (const connection of this.connections) {
982
+ connection.close();
983
+ }
984
+ this.connections.clear();
985
+
986
+ return new Promise((resolve) => {
987
+ this.server.close(() => {
988
+ this.running = false;
989
+ // Clean up socket file
990
+ if (fs.existsSync(this.config.socketPath)) {
991
+ fs.unlinkSync(this.config.socketPath);
992
+ }
993
+ // Clean up pid file
994
+ if (fs.existsSync(this.config.pidFilePath)) {
995
+ fs.unlinkSync(this.config.pidFilePath);
996
+ }
997
+ if (this.storage?.close) {
998
+ this.storage.close().catch((err) => {
999
+ log.error('Failed to close storage', { error: String(err) });
1000
+ });
1001
+ }
1002
+ log.info('Stopped');
1003
+ resolve();
1004
+ });
1005
+ });
1006
+ }
1007
+
1008
+ /**
1009
+ * Handle new connection.
1010
+ */
1011
+ private handleConnection(socket: net.Socket): void {
1012
+ log.debug('New connection');
1013
+
1014
+ const resumeHandler = this.storage?.getSessionByResumeToken
1015
+ ? async ({ agent, resumeToken }: { agent: string; resumeToken: string }) => {
1016
+ const session = await this.storage!.getSessionByResumeToken!(resumeToken);
1017
+ if (!session || session.agentName !== agent) return null;
1018
+
1019
+ let seedSequences: Array<{ topic?: string; peer: string; seq: number }> | undefined;
1020
+ if (this.storage?.getMaxSeqByStream) {
1021
+ const streams = await this.storage.getMaxSeqByStream(agent, session.id);
1022
+ seedSequences = streams.map(s => ({
1023
+ topic: s.topic ?? 'default',
1024
+ peer: s.peer,
1025
+ seq: s.maxSeq,
1026
+ }));
1027
+ }
1028
+
1029
+ return {
1030
+ sessionId: session.id,
1031
+ resumeToken: session.resumeToken ?? resumeToken,
1032
+ seedSequences,
1033
+ };
1034
+ }
1035
+ : undefined;
1036
+
1037
+ // Provide processing state callback for heartbeat exemption
1038
+ const isProcessing = (agentName: string) => this.router.isAgentProcessing(agentName);
1039
+
1040
+ const connection = new Connection(socket, { ...this.config, resumeHandler, isProcessing });
1041
+ this.connections.add(connection);
1042
+
1043
+ connection.onMessage = (envelope: Envelope) => {
1044
+ this.handleMessage(connection, envelope);
1045
+ };
1046
+
1047
+ connection.onAck = (envelope) => {
1048
+ this.handleAck(connection, envelope);
1049
+ };
1050
+
1051
+ // Update lastSeen on successful heartbeat to keep agent status fresh
1052
+ connection.onPong = () => {
1053
+ if (connection.agentName) {
1054
+ this.registry?.touch(connection.agentName);
1055
+ }
1056
+ };
1057
+
1058
+ // Register agent when connection becomes active (after successful handshake)
1059
+ connection.onActive = () => {
1060
+ if (connection.agentName) {
1061
+ this.router.register(connection);
1062
+ log.info('Agent registered', { agent: connection.agentName });
1063
+ // Registry handles persistence internally via save()
1064
+ this.registry?.registerOrUpdate({
1065
+ name: connection.agentName,
1066
+ cli: connection.cli,
1067
+ program: connection.program,
1068
+ model: connection.model,
1069
+ task: connection.task,
1070
+ workingDirectory: connection.workingDirectory,
1071
+ });
1072
+
1073
+ // Auto-join all agents to #general channel
1074
+ this.router.autoJoinChannel(connection.agentName, '#general');
1075
+
1076
+ // Record session start
1077
+ if (this.storage?.startSession) {
1078
+ const projectPaths = getProjectPaths();
1079
+ const storage = this.storage;
1080
+ const persistSession = async (): Promise<void> => {
1081
+ let startedAt = Date.now();
1082
+ if (connection.isResumed && storage.getSessionByResumeToken) {
1083
+ const existing = await storage.getSessionByResumeToken(connection.resumeToken);
1084
+ if (existing?.startedAt) {
1085
+ startedAt = existing.startedAt;
1086
+ }
1087
+ }
1088
+
1089
+ await storage.startSession!({
1090
+ id: connection.sessionId,
1091
+ agentName: connection.agentName!,
1092
+ cli: connection.cli,
1093
+ projectId: projectPaths.projectId,
1094
+ projectRoot: projectPaths.projectRoot,
1095
+ startedAt,
1096
+ resumeToken: connection.resumeToken,
1097
+ });
1098
+ };
1099
+
1100
+ persistSession().catch(err => log.error('Failed to record session start', { error: String(err) }));
1101
+ }
1102
+ }
1103
+
1104
+ // Replay pending deliveries for resumed sessions (unacked messages from previous session)
1105
+ if (connection.isResumed) {
1106
+ this.router.replayPending(connection).catch(err => {
1107
+ log.error('Failed to replay pending messages', { error: String(err) });
1108
+ });
1109
+ }
1110
+
1111
+ // Deliver any messages that were sent while this agent was offline
1112
+ // This handles messages sent during spawn timing gaps or brief disconnections
1113
+ this.router.deliverPendingMessages(connection).catch(err => {
1114
+ log.error('Failed to deliver pending messages', { error: String(err) });
1115
+ });
1116
+
1117
+ // Auto-rejoin channels that the agent was a member of before daemon restart
1118
+ // This restores channel memberships from persisted storage (cloud DB or SQLite)
1119
+ if (connection.agentName) {
1120
+ this.router.autoRejoinChannelsForAgent(connection.agentName).catch(err => {
1121
+ log.error('Failed to auto-rejoin channels', { error: String(err) });
1122
+ });
1123
+ }
1124
+
1125
+ // Notify cloud sync about agent changes
1126
+ this.notifyCloudSync();
1127
+
1128
+ // Update connected agents file for CLI
1129
+ this.writeConnectedAgentsFile();
1130
+ };
1131
+
1132
+ connection.onClose = () => {
1133
+ log.debug('Connection closed', { agent: connection.agentName ?? connection.id });
1134
+ this.connections.delete(connection);
1135
+ this.clearPendingAcksForConnection(connection.id);
1136
+ this.router.unregister(connection);
1137
+ // Registry handles persistence internally via touch() -> save()
1138
+ if (connection.agentName) {
1139
+ this.registry?.touch(connection.agentName);
1140
+ }
1141
+
1142
+ // Record session end (disconnect - agent may still mark it closed explicitly)
1143
+ if (this.storage?.endSession) {
1144
+ this.storage.endSession(connection.sessionId, { closedBy: 'disconnect' })
1145
+ .catch(err => log.error('Failed to record session end', { error: String(err) }));
1146
+ }
1147
+
1148
+ // Notify cloud sync about agent changes
1149
+ this.notifyCloudSync();
1150
+
1151
+ // Update connected agents file for CLI
1152
+ this.writeConnectedAgentsFile();
1153
+ };
1154
+
1155
+ connection.onError = (error: Error) => {
1156
+ log.error('Connection error', { error: error.message });
1157
+ this.connections.delete(connection);
1158
+ this.clearPendingAcksForConnection(connection.id);
1159
+ this.router.unregister(connection);
1160
+ // Registry handles persistence internally via touch() -> save()
1161
+ if (connection.agentName) {
1162
+ this.registry?.touch(connection.agentName);
1163
+ }
1164
+
1165
+ // Record session end on error
1166
+ if (this.storage?.endSession) {
1167
+ this.storage.endSession(connection.sessionId, { closedBy: 'error' })
1168
+ .catch(err => log.error('Failed to record session end', { error: String(err) }));
1169
+ }
1170
+
1171
+ // Update connected agents file for CLI
1172
+ this.writeConnectedAgentsFile();
1173
+ };
1174
+ }
1175
+
1176
+ /**
1177
+ * Handle incoming message from a connection.
1178
+ */
1179
+ private handleMessage(connection: Connection, envelope: Envelope): void {
1180
+ switch (envelope.type) {
1181
+ case 'SEND': {
1182
+ const sendEnvelope = envelope as SendEnvelope;
1183
+
1184
+ const membershipUpdate = (sendEnvelope.payload.data as { _channelMembershipUpdate?: { channel?: string; member?: string; action?: 'join' | 'leave' | 'invite' } })?._channelMembershipUpdate;
1185
+ if (membershipUpdate && sendEnvelope.to === '_router') {
1186
+ this.router.handleMembershipUpdate({
1187
+ channel: membershipUpdate.channel ?? '',
1188
+ member: membershipUpdate.member ?? '',
1189
+ action: membershipUpdate.action ?? 'join',
1190
+ });
1191
+ return;
1192
+ }
1193
+
1194
+ // Check for consensus commands (messages to _consensus)
1195
+ if (this.consensus?.enabled && sendEnvelope.to === '_consensus') {
1196
+ const from = connection.agentName ?? 'unknown';
1197
+ const result = this.consensus.processIncomingMessage(from, sendEnvelope.payload.body);
1198
+
1199
+ if (result.isConsensusCommand) {
1200
+ log.info(`Consensus ${result.type} from ${from}`, {
1201
+ success: result.result?.success,
1202
+ proposalId: result.result?.proposal?.id,
1203
+ });
1204
+ // Don't route consensus commands to the router
1205
+ return;
1206
+ }
1207
+ }
1208
+
1209
+ const syncMeta = sendEnvelope.payload_meta?.sync;
1210
+ if (syncMeta?.blocking) {
1211
+ if (!syncMeta.correlationId) {
1212
+ this.sendErrorEnvelope(connection, 'Missing sync correlationId for blocking SEND');
1213
+ return;
1214
+ }
1215
+ const registered = this.registerPendingAck(connection, syncMeta.correlationId, syncMeta.timeoutMs);
1216
+ if (!registered) {
1217
+ return;
1218
+ }
1219
+ }
1220
+
1221
+ this.router.route(connection, sendEnvelope);
1222
+ break;
1223
+ }
1224
+
1225
+ case 'SUBSCRIBE':
1226
+ if (connection.agentName && envelope.topic) {
1227
+ this.router.subscribe(connection.agentName, envelope.topic);
1228
+ }
1229
+ break;
1230
+
1231
+ case 'UNSUBSCRIBE':
1232
+ if (connection.agentName && envelope.topic) {
1233
+ this.router.unsubscribe(connection.agentName, envelope.topic);
1234
+ }
1235
+ break;
1236
+
1237
+ case 'SHADOW_BIND':
1238
+ if (connection.agentName) {
1239
+ const payload = envelope.payload as ShadowBindPayload;
1240
+ this.router.bindShadow(connection.agentName, payload.primaryAgent, {
1241
+ speakOn: payload.speakOn,
1242
+ receiveIncoming: payload.receiveIncoming,
1243
+ receiveOutgoing: payload.receiveOutgoing,
1244
+ });
1245
+ }
1246
+ break;
1247
+
1248
+ case 'SHADOW_UNBIND':
1249
+ if (connection.agentName) {
1250
+ const payload = envelope.payload as ShadowUnbindPayload;
1251
+ // Verify the shadow is actually bound to the specified primary
1252
+ const currentPrimary = this.router.getPrimaryForShadow(connection.agentName);
1253
+ if (currentPrimary === payload.primaryAgent) {
1254
+ this.router.unbindShadow(connection.agentName);
1255
+ }
1256
+ }
1257
+ break;
1258
+
1259
+ case 'LOG':
1260
+ // Handle log output from daemon-connected agents
1261
+ if (connection.agentName) {
1262
+ const payload = envelope.payload as LogPayload;
1263
+ const timestamp = payload.timestamp ?? envelope.ts;
1264
+ // Forward to dashboard via callback
1265
+ if (this.onLogOutput) {
1266
+ this.onLogOutput(connection.agentName, payload.data, timestamp);
1267
+ }
1268
+ }
1269
+ break;
1270
+
1271
+ // Channel messaging handlers
1272
+ case 'CHANNEL_JOIN': {
1273
+ const channelEnvelope = envelope as Envelope<ChannelJoinPayload>;
1274
+ log.info(`Channel join: ${connection.agentName} -> ${channelEnvelope.payload.channel}`);
1275
+ this.router.handleChannelJoin(connection, channelEnvelope);
1276
+ break;
1277
+ }
1278
+
1279
+ case 'CHANNEL_LEAVE': {
1280
+ const channelEnvelope = envelope as Envelope<ChannelLeavePayload>;
1281
+ log.info(`Channel leave: ${connection.agentName} <- ${channelEnvelope.payload.channel}`);
1282
+ this.router.handleChannelLeave(connection, channelEnvelope);
1283
+ break;
1284
+ }
1285
+
1286
+ case 'CHANNEL_MESSAGE': {
1287
+ const channelEnvelope = envelope as Envelope<ChannelMessagePayload>;
1288
+ log.info(`CHANNEL_MESSAGE received: from=${connection.agentName} channel=${channelEnvelope.payload.channel}`);
1289
+ this.router.routeChannelMessage(connection, channelEnvelope);
1290
+ break;
1291
+ }
1292
+
1293
+ // Spawn/release handlers (protocol-based agent spawning)
1294
+ case 'SPAWN': {
1295
+ if (!this.spawnManager) {
1296
+ this.sendErrorEnvelope(connection, 'SpawnManager not enabled. Configure spawnManager: true in daemon config.');
1297
+ break;
1298
+ }
1299
+ const spawnEnvelope = envelope as Envelope<SpawnPayload>;
1300
+ log.info(`SPAWN request: from=${connection.agentName} agent=${spawnEnvelope.payload.name} cli=${spawnEnvelope.payload.cli}`);
1301
+ this.spawnManager.handleSpawn(connection, spawnEnvelope);
1302
+ break;
1303
+ }
1304
+
1305
+ case 'RELEASE': {
1306
+ if (!this.spawnManager) {
1307
+ this.sendErrorEnvelope(connection, 'SpawnManager not enabled. Configure spawnManager: true in daemon config.');
1308
+ break;
1309
+ }
1310
+ const releaseEnvelope = envelope as Envelope<ReleasePayload>;
1311
+ log.info(`RELEASE request: from=${connection.agentName} agent=${releaseEnvelope.payload.name}`);
1312
+ this.spawnManager.handleRelease(connection, releaseEnvelope);
1313
+ break;
1314
+ }
1315
+
1316
+ // Query handlers (MCP/client requests)
1317
+ case 'STATUS': {
1318
+ const uptimeMs = this.startTime ? Date.now() - this.startTime : 0;
1319
+ const sendStatus = async (): Promise<void> => {
1320
+ let storageHealth: StorageHealth | undefined;
1321
+ if (this.storage?.healthCheck) {
1322
+ try {
1323
+ storageHealth = await this.storage.healthCheck();
1324
+ this.storageHealth = storageHealth;
1325
+ } catch (err) {
1326
+ log.warn('STATUS: storage health check failed', { error: String(err) });
1327
+ storageHealth = this.storageHealth;
1328
+ }
1329
+ }
1330
+
1331
+ const response: Envelope<StatusResponsePayload> = {
1332
+ v: PROTOCOL_VERSION,
1333
+ type: 'STATUS_RESPONSE',
1334
+ id: envelope.id,
1335
+ ts: Date.now(),
1336
+ payload: {
1337
+ version: DAEMON_VERSION,
1338
+ uptime: uptimeMs,
1339
+ cloudConnected: this.cloudSync?.isConnected() ?? false,
1340
+ agentCount: this.router.connectionCount,
1341
+ storage: storageHealth,
1342
+ },
1343
+ };
1344
+ connection.send(response);
1345
+ };
1346
+
1347
+ sendStatus().catch(err => {
1348
+ log.error('Failed to send STATUS response', { error: String(err) });
1349
+ });
1350
+ break;
1351
+ }
1352
+
1353
+ case 'INBOX': {
1354
+ const inboxPayload = envelope.payload as InboxPayload;
1355
+ const agentName = inboxPayload.agent || connection.agentName;
1356
+
1357
+ // Get messages from storage
1358
+ const getInboxMessages = async () => {
1359
+ if (!this.storage?.getMessages) {
1360
+ return [];
1361
+ }
1362
+ try {
1363
+ // If channel is specified, get channel messages; otherwise get DMs to agent
1364
+ const toFilter = inboxPayload.channel || agentName;
1365
+ const messages = await this.storage.getMessages({
1366
+ to: toFilter,
1367
+ from: inboxPayload.from,
1368
+ limit: inboxPayload.limit || 50,
1369
+ unreadOnly: inboxPayload.unreadOnly,
1370
+ });
1371
+ return messages.map(m => ({
1372
+ id: m.id,
1373
+ from: m.from,
1374
+ body: m.body,
1375
+ channel: (m.data as { channel?: string })?.channel,
1376
+ thread: m.thread,
1377
+ timestamp: m.ts,
1378
+ }));
1379
+ } catch {
1380
+ return [];
1381
+ }
1382
+ };
1383
+
1384
+ getInboxMessages().then(messages => {
1385
+ const response: Envelope<InboxResponsePayload> = {
1386
+ v: PROTOCOL_VERSION,
1387
+ type: 'INBOX_RESPONSE',
1388
+ id: envelope.id,
1389
+ ts: Date.now(),
1390
+ payload: { messages },
1391
+ };
1392
+ connection.send(response);
1393
+ }).catch(err => {
1394
+ this.sendErrorEnvelope(connection, `Failed to get inbox: ${err.message}`);
1395
+ });
1396
+ break;
1397
+ }
1398
+
1399
+ case 'MESSAGES_QUERY': {
1400
+ // Query all messages (used by dashboard) - not filtered by recipient
1401
+ const queryPayload = envelope.payload as MessagesQueryPayload;
1402
+
1403
+ const getMessages = async () => {
1404
+ if (!this.storage?.getMessages) {
1405
+ return [];
1406
+ }
1407
+ try {
1408
+ const messages = await this.storage.getMessages({
1409
+ limit: queryPayload.limit || 100,
1410
+ sinceTs: queryPayload.sinceTs,
1411
+ from: queryPayload.from,
1412
+ to: queryPayload.to,
1413
+ thread: queryPayload.thread,
1414
+ order: queryPayload.order || 'desc',
1415
+ });
1416
+ return messages.map(m => ({
1417
+ id: m.id,
1418
+ from: m.from,
1419
+ to: m.to,
1420
+ body: m.body,
1421
+ channel: (m.data as { channel?: string })?.channel,
1422
+ thread: m.thread,
1423
+ timestamp: m.ts,
1424
+ status: m.status,
1425
+ isBroadcast: m.is_broadcast,
1426
+ replyCount: m.replyCount,
1427
+ data: m.data,
1428
+ }));
1429
+ } catch {
1430
+ return [];
1431
+ }
1432
+ };
1433
+
1434
+ getMessages().then(messages => {
1435
+ const response: Envelope<MessagesResponsePayload> = {
1436
+ v: PROTOCOL_VERSION,
1437
+ type: 'MESSAGES_RESPONSE',
1438
+ id: envelope.id,
1439
+ ts: Date.now(),
1440
+ payload: { messages },
1441
+ };
1442
+ connection.send(response);
1443
+ }).catch(err => {
1444
+ this.sendErrorEnvelope(connection, `Failed to get messages: ${err.message}`);
1445
+ });
1446
+ break;
1447
+ }
1448
+
1449
+ case 'LIST_AGENTS': {
1450
+ const listPayload = envelope.payload as ListAgentsPayload;
1451
+
1452
+ // Get connected agents from router
1453
+ const connectedAgents = this.router.getAgents();
1454
+
1455
+ // Get all agents from registry for metadata lookup
1456
+ const registryAgents = this.registry?.getAgents() ?? [];
1457
+ const registryMap = new Map(registryAgents.map(a => [a.name, a]));
1458
+
1459
+ // Build agent list from connected agents
1460
+ const agents = connectedAgents
1461
+ .filter(name => !this.isInternalAgent(name))
1462
+ .map(name => {
1463
+ const registryAgent = registryMap.get(name);
1464
+ return {
1465
+ name,
1466
+ cli: registryAgent?.cli,
1467
+ idle: false, // Connected agents are not idle
1468
+ // TODO: Add proper parent tracking via spawner relationship
1469
+ parent: undefined,
1470
+ };
1471
+ });
1472
+
1473
+ // Optionally include idle agents from registry
1474
+ if (listPayload.includeIdle && this.registry) {
1475
+ for (const agent of registryAgents) {
1476
+ if (!connectedAgents.includes(agent.name) && !this.isInternalAgent(agent.name)) {
1477
+ agents.push({
1478
+ name: agent.name,
1479
+ cli: agent.cli,
1480
+ idle: true,
1481
+ parent: undefined,
1482
+ });
1483
+ }
1484
+ }
1485
+ }
1486
+
1487
+ const response: Envelope<ListAgentsResponsePayload> = {
1488
+ v: PROTOCOL_VERSION,
1489
+ type: 'LIST_AGENTS_RESPONSE',
1490
+ id: envelope.id,
1491
+ ts: Date.now(),
1492
+ payload: { agents },
1493
+ };
1494
+ connection.send(response);
1495
+ break;
1496
+ }
1497
+
1498
+ case 'LIST_CONNECTED_AGENTS': {
1499
+ // Returns only currently connected agents (not historical/registered agents)
1500
+ const connectedAgents = this.router.getAgents();
1501
+ const registryAgents = this.registry?.getAgents() ?? [];
1502
+ const registryMap = new Map(registryAgents.map(a => [a.name, a]));
1503
+
1504
+ const agents = connectedAgents
1505
+ .filter(name => !this.isInternalAgent(name))
1506
+ .map(name => {
1507
+ const registryAgent = registryMap.get(name);
1508
+ return {
1509
+ name,
1510
+ cli: registryAgent?.cli,
1511
+ idle: false,
1512
+ // TODO: Add proper parent tracking via spawner relationship
1513
+ parent: undefined,
1514
+ };
1515
+ });
1516
+
1517
+ const connectedResponse: Envelope<ListConnectedAgentsResponsePayload> = {
1518
+ v: PROTOCOL_VERSION,
1519
+ type: 'LIST_CONNECTED_AGENTS_RESPONSE',
1520
+ id: envelope.id,
1521
+ ts: Date.now(),
1522
+ payload: { agents },
1523
+ };
1524
+ connection.send(connectedResponse);
1525
+ break;
1526
+ }
1527
+
1528
+ case 'REMOVE_AGENT': {
1529
+ const removePayload = envelope.payload as RemoveAgentPayload;
1530
+ const agentName = removePayload.name;
1531
+
1532
+ // Validate agent name
1533
+ if (!agentName || typeof agentName !== 'string' || agentName.length === 0) {
1534
+ const errorResponse: Envelope<RemoveAgentResponsePayload> = {
1535
+ v: PROTOCOL_VERSION,
1536
+ type: 'REMOVE_AGENT_RESPONSE',
1537
+ id: envelope.id,
1538
+ ts: Date.now(),
1539
+ payload: { success: false, removed: false, message: 'Invalid agent name: name is required' },
1540
+ };
1541
+ connection.send(errorResponse);
1542
+ break;
1543
+ }
1544
+
1545
+ if (agentName.length > 128) {
1546
+ const errorResponse: Envelope<RemoveAgentResponsePayload> = {
1547
+ v: PROTOCOL_VERSION,
1548
+ type: 'REMOVE_AGENT_RESPONSE',
1549
+ id: envelope.id,
1550
+ ts: Date.now(),
1551
+ payload: { success: false, removed: false, message: 'Invalid agent name: exceeds 128 characters' },
1552
+ };
1553
+ connection.send(errorResponse);
1554
+ break;
1555
+ }
1556
+
1557
+ const doRemove = async (): Promise<{ removed: boolean; message: string }> => {
1558
+ let removed = false;
1559
+ let message = '';
1560
+
1561
+ // Remove from registry (agents.json)
1562
+ if (this.registry) {
1563
+ const wasInRegistry = this.registry.getAgents().some(a => a.name === agentName);
1564
+ if (wasInRegistry) {
1565
+ this.registry.remove(agentName);
1566
+ removed = true;
1567
+ message = `Removed ${agentName} from registry`;
1568
+ }
1569
+ }
1570
+
1571
+ // Remove from storage (sessions table) if storage is available
1572
+ if (this.storage?.removeAgent) {
1573
+ await this.storage.removeAgent(agentName);
1574
+ if (!removed) {
1575
+ removed = true;
1576
+ message = `Removed ${agentName} from storage`;
1577
+ } else {
1578
+ message += ' and storage';
1579
+ }
1580
+ }
1581
+
1582
+ // Optionally remove messages
1583
+ if (removePayload.removeMessages && this.storage?.removeMessagesForAgent) {
1584
+ await this.storage.removeMessagesForAgent(agentName);
1585
+ message += ' (including messages)';
1586
+ }
1587
+
1588
+ // Force remove from router if still connected (shouldn't be, but just in case)
1589
+ if (this.router.forceRemoveAgent(agentName)) {
1590
+ message += ', disconnected from router';
1591
+ // Notify cloud sync and update connected-agents.json
1592
+ this.notifyCloudSync();
1593
+ this.writeConnectedAgentsFile();
1594
+ }
1595
+
1596
+ if (!removed) {
1597
+ message = `Agent ${agentName} not found in registry or storage`;
1598
+ }
1599
+
1600
+ return { removed, message };
1601
+ };
1602
+
1603
+ doRemove().then(({ removed, message }) => {
1604
+ const removeResponse: Envelope<RemoveAgentResponsePayload> = {
1605
+ v: PROTOCOL_VERSION,
1606
+ type: 'REMOVE_AGENT_RESPONSE',
1607
+ id: envelope.id,
1608
+ ts: Date.now(),
1609
+ payload: { success: removed, removed, message },
1610
+ };
1611
+ connection.send(removeResponse);
1612
+ }).catch(err => {
1613
+ const removeResponse: Envelope<RemoveAgentResponsePayload> = {
1614
+ v: PROTOCOL_VERSION,
1615
+ type: 'REMOVE_AGENT_RESPONSE',
1616
+ id: envelope.id,
1617
+ ts: Date.now(),
1618
+ payload: { success: false, removed: false, message: `Error: ${(err as Error).message}` },
1619
+ };
1620
+ connection.send(removeResponse);
1621
+ });
1622
+ break;
1623
+ }
1624
+
1625
+ case 'HEALTH': {
1626
+ const healthPayload = envelope.payload as HealthPayload;
1627
+
1628
+ // Compute health based on available data
1629
+ const connectedAgents = this.router.getAgents();
1630
+ const registryAgents = this.registry?.getAgents() ?? [];
1631
+ const agentCount = connectedAgents.filter(n => !this.isInternalAgent(n)).length;
1632
+
1633
+ // Basic health computation
1634
+ const issues: Array<{ severity: string; message: string }> = [];
1635
+ const recommendations: string[] = [];
1636
+ let healthScore = 100;
1637
+
1638
+ // Check for memory issues via memory monitor
1639
+ const memoryMonitor = getMemoryMonitor();
1640
+ const memoryMetrics = memoryMonitor.getAll();
1641
+ const criticalAgents = memoryMetrics.filter(m => m.alertLevel === 'critical');
1642
+ const warningAgents = memoryMetrics.filter(m => m.alertLevel === 'warning');
1643
+
1644
+ if (criticalAgents.length > 0) {
1645
+ healthScore -= 30;
1646
+ for (const agent of criticalAgents) {
1647
+ issues.push({ severity: 'critical', message: `${agent.name} has critical memory usage` });
1648
+ }
1649
+ recommendations.push('Consider releasing some agents to free memory');
1650
+ }
1651
+
1652
+ if (warningAgents.length > 0) {
1653
+ healthScore -= 10;
1654
+ for (const agent of warningAgents) {
1655
+ issues.push({ severity: 'warning', message: `${agent.name} has high memory usage` });
1656
+ }
1657
+ }
1658
+
1659
+ // Check cloud sync status
1660
+ if (!this.cloudSync?.isConnected()) {
1661
+ issues.push({ severity: 'info', message: 'Cloud sync not connected' });
1662
+ }
1663
+
1664
+ const summary = healthScore >= 80 ? 'System is healthy' :
1665
+ healthScore >= 50 ? 'System has some issues' :
1666
+ 'System needs attention';
1667
+
1668
+ const healthResponse: Envelope<HealthResponsePayload> = {
1669
+ v: PROTOCOL_VERSION,
1670
+ type: 'HEALTH_RESPONSE',
1671
+ id: envelope.id,
1672
+ ts: Date.now(),
1673
+ payload: {
1674
+ healthScore: Math.max(0, healthScore),
1675
+ summary,
1676
+ issues,
1677
+ recommendations,
1678
+ crashes: [], // Would need crash tracking implementation
1679
+ alerts: [], // Would need alert tracking implementation
1680
+ stats: {
1681
+ totalCrashes24h: 0,
1682
+ totalAlerts24h: 0,
1683
+ agentCount,
1684
+ },
1685
+ },
1686
+ };
1687
+ connection.send(healthResponse);
1688
+ break;
1689
+ }
1690
+
1691
+ case 'METRICS': {
1692
+ const metricsPayload = envelope.payload as MetricsPayload;
1693
+
1694
+ // Get metrics from memory monitor
1695
+ const memoryMonitor = getMemoryMonitor();
1696
+ let metrics = memoryMonitor.getAll();
1697
+
1698
+ // Filter to specific agent if requested
1699
+ if (metricsPayload.agent) {
1700
+ metrics = metrics.filter(m => m.name === metricsPayload.agent);
1701
+ }
1702
+
1703
+ // Convert to response format
1704
+ const agents = metrics.map(m => ({
1705
+ name: m.name,
1706
+ pid: m.pid,
1707
+ status: m.alertLevel === 'normal' ? 'running' : m.alertLevel,
1708
+ rssBytes: m.current.rssBytes,
1709
+ cpuPercent: m.current.cpuPercent,
1710
+ trend: m.trend,
1711
+ alertLevel: m.alertLevel,
1712
+ highWatermark: m.highWatermark,
1713
+ uptimeMs: m.uptimeMs,
1714
+ }));
1715
+
1716
+ // System metrics
1717
+ const system = {
1718
+ totalMemory: os.totalmem(),
1719
+ freeMemory: os.freemem(),
1720
+ heapUsed: process.memoryUsage().heapUsed,
1721
+ };
1722
+
1723
+ const metricsResponse: Envelope<MetricsResponsePayload> = {
1724
+ v: PROTOCOL_VERSION,
1725
+ type: 'METRICS_RESPONSE',
1726
+ id: envelope.id,
1727
+ ts: Date.now(),
1728
+ payload: { agents, system },
1729
+ };
1730
+ connection.send(metricsResponse);
1731
+ break;
1732
+ }
1733
+ }
1734
+ }
1735
+
1736
+ private handleAck(connection: Connection, envelope: Envelope<AckPayload>): void {
1737
+ this.router.handleAck(connection, envelope);
1738
+
1739
+ const correlationId = envelope.payload.correlationId;
1740
+ if (!correlationId) return;
1741
+
1742
+ const pending = this.pendingAcks.get(correlationId);
1743
+ if (!pending) return;
1744
+
1745
+ clearTimeout(pending.timeoutHandle);
1746
+ this.pendingAcks.delete(correlationId);
1747
+
1748
+ const forwardAck: Envelope<AckPayload> = {
1749
+ v: envelope.v,
1750
+ type: 'ACK',
1751
+ id: generateId(),
1752
+ ts: Date.now(),
1753
+ from: connection.agentName,
1754
+ to: pending.connection.agentName,
1755
+ payload: envelope.payload,
1756
+ };
1757
+
1758
+ pending.connection.send(forwardAck);
1759
+ }
1760
+
1761
+ private registerPendingAck(connection: Connection, correlationId: string, timeoutMs?: number): boolean {
1762
+ if (this.pendingAcks.has(correlationId)) {
1763
+ this.sendErrorEnvelope(connection, `Duplicate correlationId: ${correlationId}`);
1764
+ return false;
1765
+ }
1766
+
1767
+ const timeout = timeoutMs ?? Daemon.DEFAULT_SYNC_TIMEOUT_MS;
1768
+ const timeoutHandle = setTimeout(() => {
1769
+ this.pendingAcks.delete(correlationId);
1770
+ this.sendErrorEnvelope(connection, `ACK timeout after ${timeout}ms`);
1771
+ }, timeout);
1772
+
1773
+ this.pendingAcks.set(correlationId, {
1774
+ correlationId,
1775
+ connectionId: connection.id,
1776
+ connection,
1777
+ timeoutHandle,
1778
+ });
1779
+
1780
+ return true;
1781
+ }
1782
+
1783
+ private clearPendingAcksForConnection(connectionId: string): void {
1784
+ for (const [correlationId, pending] of this.pendingAcks.entries()) {
1785
+ if (pending.connectionId !== connectionId) continue;
1786
+ clearTimeout(pending.timeoutHandle);
1787
+ this.pendingAcks.delete(correlationId);
1788
+ }
1789
+ }
1790
+
1791
+ private sendErrorEnvelope(connection: Connection, message: string): void {
1792
+ const errorEnvelope: Envelope<ErrorPayload> = {
1793
+ v: PROTOCOL_VERSION,
1794
+ type: 'ERROR',
1795
+ id: generateId(),
1796
+ ts: Date.now(),
1797
+ payload: {
1798
+ code: 'INTERNAL',
1799
+ message,
1800
+ fatal: false,
1801
+ },
1802
+ };
1803
+ connection.send(errorEnvelope);
1804
+ }
1805
+
1806
+ /**
1807
+ * Get list of connected agents.
1808
+ */
1809
+ getAgents(): string[] {
1810
+ return this.router.getAgents();
1811
+ }
1812
+
1813
+ /**
1814
+ * Broadcast a system message to all connected agents.
1815
+ * Used for system notifications like agent death announcements.
1816
+ */
1817
+ broadcastSystemMessage(message: string, data?: Record<string, unknown>): void {
1818
+ this.router.broadcastSystemMessage(message, data);
1819
+ }
1820
+
1821
+ /**
1822
+ * Get connection count.
1823
+ */
1824
+ get connectionCount(): number {
1825
+ return this.router.connectionCount;
1826
+ }
1827
+
1828
+ /**
1829
+ * Check if daemon is running.
1830
+ */
1831
+ get isRunning(): boolean {
1832
+ return this.running;
1833
+ }
1834
+
1835
+ /**
1836
+ * Check if consensus is enabled.
1837
+ */
1838
+ get consensusEnabled(): boolean {
1839
+ return this.consensus?.enabled ?? false;
1840
+ }
1841
+
1842
+ /**
1843
+ * Get the consensus integration (for API access).
1844
+ */
1845
+ getConsensus(): ConsensusIntegration | undefined {
1846
+ return this.consensus;
1847
+ }
1848
+ }
1849
+
1850
+ // Run as standalone if executed directly
1851
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
1852
+ if (isMainModule) {
1853
+ const daemon = new Daemon();
1854
+
1855
+ process.on('SIGINT', async () => {
1856
+ log.info('Shutting down (SIGINT)');
1857
+ await daemon.stop();
1858
+ process.exit(0);
1859
+ });
1860
+
1861
+ process.on('SIGTERM', async () => {
1862
+ log.info('Shutting down (SIGTERM)');
1863
+ await daemon.stop();
1864
+ process.exit(0);
1865
+ });
1866
+
1867
+ daemon.start().catch((err) => {
1868
+ log.error('Failed to start', { error: String(err) });
1869
+ process.exit(1);
1870
+ });
1871
+ }