agent-relay 1.3.0 → 1.3.2

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 (240) hide show
  1. package/.trajectories/active/traj_3yx9dy148mge.json +42 -0
  2. package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +49 -0
  3. package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +31 -0
  4. package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +49 -0
  5. package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +31 -0
  6. package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +109 -0
  7. package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +49 -0
  8. package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +31 -0
  9. package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +66 -0
  10. package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +36 -0
  11. package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +49 -0
  12. package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +31 -0
  13. package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +65 -0
  14. package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +37 -0
  15. package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +36 -0
  16. package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +21 -0
  17. package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +101 -0
  18. package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +52 -0
  19. package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +61 -0
  20. package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +36 -0
  21. package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +73 -0
  22. package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +41 -0
  23. package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +77 -0
  24. package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +42 -0
  25. package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +109 -0
  26. package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +56 -0
  27. package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +113 -0
  28. package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +57 -0
  29. package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +61 -0
  30. package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +36 -0
  31. package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +49 -0
  32. package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +31 -0
  33. package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +49 -0
  34. package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +31 -0
  35. package/.trajectories/index.json +140 -1
  36. package/TRAIL_GIT_AUTH_FIX.md +113 -0
  37. package/deploy/workspace/codex.config.toml +1 -1
  38. package/deploy/workspace/entrypoint.sh +20 -79
  39. package/deploy/workspace/gh-relay +156 -0
  40. package/deploy/workspace/git-credential-relay +5 -1
  41. package/dist/bridge/multi-project-client.js +13 -10
  42. package/dist/bridge/spawner.d.ts +2 -0
  43. package/dist/bridge/spawner.js +19 -1
  44. package/dist/bridge/types.d.ts +2 -0
  45. package/dist/cli/index.d.ts +1 -1
  46. package/dist/cli/index.js +115 -69
  47. package/dist/cloud/api/admin.js +16 -3
  48. package/dist/cloud/api/codex-auth-helper.js +28 -8
  49. package/dist/cloud/api/consensus.d.ts +13 -0
  50. package/dist/cloud/api/consensus.js +259 -0
  51. package/dist/cloud/api/daemons.js +205 -1
  52. package/dist/cloud/api/git.js +37 -7
  53. package/dist/cloud/api/onboarding.js +4 -1
  54. package/dist/cloud/api/provider-env.d.ts +5 -0
  55. package/dist/cloud/api/provider-env.js +27 -0
  56. package/dist/cloud/api/providers.js +2 -0
  57. package/dist/cloud/api/test-helpers.js +130 -0
  58. package/dist/cloud/api/workspaces.js +38 -3
  59. package/dist/cloud/db/bulk-ingest.d.ts +88 -0
  60. package/dist/cloud/db/bulk-ingest.js +268 -0
  61. package/dist/cloud/db/drizzle.d.ts +33 -0
  62. package/dist/cloud/db/drizzle.js +174 -2
  63. package/dist/cloud/db/index.d.ts +24 -5
  64. package/dist/cloud/db/index.js +19 -4
  65. package/dist/cloud/db/schema.d.ts +397 -3
  66. package/dist/cloud/db/schema.js +75 -1
  67. package/dist/cloud/provisioner/index.d.ts +8 -0
  68. package/dist/cloud/provisioner/index.js +256 -50
  69. package/dist/cloud/server.js +47 -3
  70. package/dist/cloud/services/index.d.ts +1 -0
  71. package/dist/cloud/services/index.js +2 -0
  72. package/dist/cloud/services/nango.d.ts +3 -4
  73. package/dist/cloud/services/nango.js +11 -33
  74. package/dist/cloud/services/workspace-keepalive.d.ts +76 -0
  75. package/dist/cloud/services/workspace-keepalive.js +234 -0
  76. package/dist/config/relay-config.d.ts +23 -0
  77. package/dist/config/relay-config.js +23 -0
  78. package/dist/daemon/agent-manager.d.ts +20 -1
  79. package/dist/daemon/agent-manager.js +47 -0
  80. package/dist/daemon/agent-registry.js +4 -4
  81. package/dist/daemon/agent-signing.d.ts +158 -0
  82. package/dist/daemon/agent-signing.js +523 -0
  83. package/dist/daemon/api.js +18 -1
  84. package/dist/daemon/cli-auth.d.ts +4 -1
  85. package/dist/daemon/cli-auth.js +55 -11
  86. package/dist/daemon/cloud-sync.d.ts +47 -1
  87. package/dist/daemon/cloud-sync.js +152 -3
  88. package/dist/daemon/connection.d.ts +28 -0
  89. package/dist/daemon/connection.js +98 -15
  90. package/dist/daemon/consensus-integration.d.ts +167 -0
  91. package/dist/daemon/consensus-integration.js +371 -0
  92. package/dist/daemon/consensus.d.ts +271 -0
  93. package/dist/daemon/consensus.js +632 -0
  94. package/dist/daemon/delivery-tracker.d.ts +34 -0
  95. package/dist/daemon/delivery-tracker.js +104 -0
  96. package/dist/daemon/enhanced-features.d.ts +118 -0
  97. package/dist/daemon/enhanced-features.js +178 -0
  98. package/dist/daemon/index.d.ts +4 -0
  99. package/dist/daemon/index.js +5 -0
  100. package/dist/daemon/rate-limiter.d.ts +68 -0
  101. package/dist/daemon/rate-limiter.js +130 -0
  102. package/dist/daemon/router.d.ts +18 -11
  103. package/dist/daemon/router.js +55 -111
  104. package/dist/daemon/server.d.ts +13 -1
  105. package/dist/daemon/server.js +71 -9
  106. package/dist/daemon/sync-queue.d.ts +116 -0
  107. package/dist/daemon/sync-queue.js +361 -0
  108. package/dist/health-worker-manager.d.ts +62 -0
  109. package/dist/health-worker-manager.js +144 -0
  110. package/dist/health-worker.d.ts +9 -0
  111. package/dist/health-worker.js +79 -0
  112. package/dist/index.d.ts +2 -1
  113. package/dist/index.js +5 -1
  114. package/dist/memory/context-compaction.d.ts +156 -0
  115. package/dist/memory/context-compaction.js +453 -0
  116. package/dist/memory/index.d.ts +1 -0
  117. package/dist/memory/index.js +1 -0
  118. package/dist/protocol/channels.js +4 -4
  119. package/dist/protocol/framing.d.ts +72 -10
  120. package/dist/protocol/framing.js +194 -25
  121. package/dist/storage/adapter.d.ts +8 -1
  122. package/dist/storage/adapter.js +11 -0
  123. package/dist/storage/batched-sqlite-adapter.d.ts +71 -0
  124. package/dist/storage/batched-sqlite-adapter.js +183 -0
  125. package/dist/storage/dead-letter-queue.d.ts +196 -0
  126. package/dist/storage/dead-letter-queue.js +427 -0
  127. package/dist/storage/dlq-adapter.d.ts +195 -0
  128. package/dist/storage/dlq-adapter.js +664 -0
  129. package/dist/trajectory/config.d.ts +32 -14
  130. package/dist/trajectory/config.js +38 -16
  131. package/dist/trajectory/integration.js +217 -64
  132. package/dist/utils/git-remote.d.ts +47 -0
  133. package/dist/utils/git-remote.js +125 -0
  134. package/dist/utils/id-generator.d.ts +35 -0
  135. package/dist/utils/id-generator.js +60 -0
  136. package/dist/utils/index.d.ts +1 -0
  137. package/dist/utils/index.js +1 -0
  138. package/dist/utils/precompiled-patterns.d.ts +110 -0
  139. package/dist/utils/precompiled-patterns.js +322 -0
  140. package/dist/wrapper/auth-detection.js +1 -1
  141. package/dist/wrapper/base-wrapper.d.ts +36 -0
  142. package/dist/wrapper/base-wrapper.js +48 -2
  143. package/dist/wrapper/client.d.ts +14 -4
  144. package/dist/wrapper/client.js +84 -31
  145. package/dist/wrapper/idle-detector.d.ts +102 -0
  146. package/dist/wrapper/idle-detector.js +279 -0
  147. package/dist/wrapper/parser.d.ts +4 -0
  148. package/dist/wrapper/parser.js +19 -1
  149. package/dist/wrapper/pty-wrapper.d.ts +7 -1
  150. package/dist/wrapper/pty-wrapper.js +51 -27
  151. package/dist/wrapper/tmux-wrapper.d.ts +12 -1
  152. package/dist/wrapper/tmux-wrapper.js +65 -17
  153. package/package.json +5 -5
  154. package/scripts/run-migrations.js +43 -0
  155. package/scripts/verify-schema.js +134 -0
  156. package/tests/benchmarks/protocol.bench.ts +310 -0
  157. package/dist/dashboard/out/404.html +0 -1
  158. package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_buildManifest.js +0 -1
  159. package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +0 -1
  160. package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +0 -1
  161. package/dist/dashboard/out/_next/static/chunks/117-f7b8ab0809342e77.js +0 -2
  162. package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +0 -1
  163. package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +0 -9
  164. package/dist/dashboard/out/_next/static/chunks/648-5cc6e1921389a58a.js +0 -1
  165. package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +0 -1
  166. package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +0 -1
  167. package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +0 -1
  168. package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +0 -1
  169. package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-53b8a69f76db17d0.js +0 -1
  170. package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +0 -1
  171. package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +0 -1
  172. package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-f45ecbc3e06134fc.js +0 -1
  173. package/dist/dashboard/out/_next/static/chunks/app/history/page-8c8bed33beb2bf1c.js +0 -1
  174. package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
  175. package/dist/dashboard/out/_next/static/chunks/app/login/page-16f3b49e55b1e0ed.js +0 -1
  176. package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +0 -1
  177. package/dist/dashboard/out/_next/static/chunks/app/page-4a5938c18a11a654.js +0 -1
  178. package/dist/dashboard/out/_next/static/chunks/app/pricing/page-982a7000fee44014.js +0 -1
  179. package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +0 -1
  180. package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +0 -1
  181. package/dist/dashboard/out/_next/static/chunks/app/signup/page-547dd0ca55ecd0ba.js +0 -1
  182. package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +0 -18
  183. package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +0 -1
  184. package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
  185. package/dist/dashboard/out/_next/static/chunks/main-2ee6beb2ae96d210.js +0 -1
  186. package/dist/dashboard/out/_next/static/chunks/main-app-5d692157a8eb1fd9.js +0 -1
  187. package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
  188. package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
  189. package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  190. package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +0 -1
  191. package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +0 -1
  192. package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +0 -1
  193. package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
  194. package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
  195. package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
  196. package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
  197. package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
  198. package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +0 -45
  199. package/dist/dashboard/out/alt-logos/logo.svg +0 -38
  200. package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
  201. package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
  202. package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
  203. package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
  204. package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
  205. package/dist/dashboard/out/alt-logos/monogram-logo.svg +0 -38
  206. package/dist/dashboard/out/app/onboarding.html +0 -1
  207. package/dist/dashboard/out/app/onboarding.txt +0 -7
  208. package/dist/dashboard/out/app.html +0 -1
  209. package/dist/dashboard/out/app.txt +0 -7
  210. package/dist/dashboard/out/apple-icon.png +0 -0
  211. package/dist/dashboard/out/connect-repos.html +0 -1
  212. package/dist/dashboard/out/connect-repos.txt +0 -7
  213. package/dist/dashboard/out/history.html +0 -1
  214. package/dist/dashboard/out/history.txt +0 -7
  215. package/dist/dashboard/out/index.html +0 -1
  216. package/dist/dashboard/out/index.txt +0 -7
  217. package/dist/dashboard/out/login.html +0 -6
  218. package/dist/dashboard/out/login.txt +0 -7
  219. package/dist/dashboard/out/metrics.html +0 -1
  220. package/dist/dashboard/out/metrics.txt +0 -7
  221. package/dist/dashboard/out/pricing.html +0 -13
  222. package/dist/dashboard/out/pricing.txt +0 -7
  223. package/dist/dashboard/out/providers/setup/claude.html +0 -1
  224. package/dist/dashboard/out/providers/setup/claude.txt +0 -8
  225. package/dist/dashboard/out/providers/setup/codex.html +0 -1
  226. package/dist/dashboard/out/providers/setup/codex.txt +0 -8
  227. package/dist/dashboard/out/providers.html +0 -1
  228. package/dist/dashboard/out/providers.txt +0 -7
  229. package/dist/dashboard/out/signup.html +0 -6
  230. package/dist/dashboard/out/signup.txt +0 -7
  231. package/dist/dashboard-server/metrics.d.ts +0 -105
  232. package/dist/dashboard-server/metrics.js +0 -193
  233. package/dist/dashboard-server/needs-attention.d.ts +0 -24
  234. package/dist/dashboard-server/needs-attention.js +0 -78
  235. package/dist/dashboard-server/server.d.ts +0 -15
  236. package/dist/dashboard-server/server.js +0 -3776
  237. package/dist/dashboard-server/start.d.ts +0 -6
  238. package/dist/dashboard-server/start.js +0 -13
  239. package/dist/dashboard-server/user-bridge.d.ts +0 -103
  240. package/dist/dashboard-server/user-bridge.js +0 -189
@@ -7,14 +7,12 @@
7
7
  * v v
8
8
  * ERROR -------> CLOSED
9
9
  */
10
- import { v4 as uuid } from 'uuid';
10
+ import { generateId } from '../utils/id-generator.js';
11
11
  import { PROTOCOL_VERSION, } from '../protocol/types.js';
12
- import { encodeFrame, FrameParser } from '../protocol/framing.js';
12
+ import { encodeFrameLegacy as encodeFrame, FrameParser } from '../protocol/framing.js';
13
+ import { DEFAULT_CONNECTION_CONFIG } from '../config/relay-config.js';
13
14
  export const DEFAULT_CONFIG = {
14
- maxFrameBytes: 1024 * 1024,
15
- heartbeatMs: 5000,
16
- // 6x multiplier = 30 second timeout, more tolerant for AI agents processing long responses
17
- heartbeatTimeoutMultiplier: 6,
15
+ ...DEFAULT_CONNECTION_CONFIG,
18
16
  };
19
17
  export class Connection {
20
18
  id;
@@ -38,6 +36,11 @@ export class Connection {
38
36
  lastPongReceived;
39
37
  // Sequence numbers per (topic, peer) stream
40
38
  sequences = new Map();
39
+ // Write queue for backpressure handling
40
+ writeQueue = [];
41
+ draining = false;
42
+ _backpressured = false;
43
+ socketDrainHandler;
41
44
  // Event handlers
42
45
  onMessage;
43
46
  onClose;
@@ -45,13 +48,16 @@ export class Connection {
45
48
  onActive; // Fires when connection transitions to ACTIVE state
46
49
  onAck;
47
50
  onPong; // Fires on successful heartbeat response
51
+ /** Fires when write queue crosses high/low water marks */
52
+ onBackpressure;
48
53
  constructor(socket, config = {}) {
49
- this.id = uuid();
54
+ this.id = generateId();
50
55
  this.socket = socket;
51
56
  this.config = { ...DEFAULT_CONFIG, ...config };
52
57
  this.parser = new FrameParser(this.config.maxFrameBytes);
53
- this._sessionId = uuid();
54
- this._resumeToken = uuid();
58
+ this.parser.setLegacyMode(true); // Use 4-byte header for backwards compatibility
59
+ this._sessionId = generateId();
60
+ this._resumeToken = generateId();
55
61
  this.setupSocketHandlers();
56
62
  this._state = 'HANDSHAKING';
57
63
  }
@@ -94,6 +100,14 @@ export class Connection {
94
100
  get isResumed() {
95
101
  return this._isResumed;
96
102
  }
103
+ /** Whether this connection is currently backpressured (write queue above high water mark) */
104
+ get backpressured() {
105
+ return this._backpressured;
106
+ }
107
+ /** Current number of messages queued for writing */
108
+ get writeQueueLength() {
109
+ return this.writeQueue.length;
110
+ }
97
111
  setupSocketHandlers() {
98
112
  this.socket.on('data', (data) => this.handleData(data));
99
113
  this.socket.on('close', () => this.handleClose());
@@ -187,7 +201,7 @@ export class Connection {
187
201
  const welcome = {
188
202
  v: PROTOCOL_VERSION,
189
203
  type: 'WELCOME',
190
- id: uuid(),
204
+ id: generateId(),
191
205
  ts: Date.now(),
192
206
  payload: {
193
207
  session_id: this._sessionId,
@@ -244,11 +258,11 @@ export class Connection {
244
258
  }
245
259
  }
246
260
  // Send ping
247
- const nonce = uuid();
261
+ const nonce = generateId();
248
262
  this.send({
249
263
  v: PROTOCOL_VERSION,
250
264
  type: 'PING',
251
- id: uuid(),
265
+ id: generateId(),
252
266
  ts: now,
253
267
  payload: { nonce },
254
268
  });
@@ -281,14 +295,32 @@ export class Connection {
281
295
  }
282
296
  /**
283
297
  * Send an envelope to this connection.
298
+ *
299
+ * Uses a write queue to prevent blocking on slow consumers.
300
+ * Returns false if the connection is closed or the queue is full.
284
301
  */
285
302
  send(envelope) {
286
303
  if (this._state === 'CLOSED' || this._state === 'ERROR') {
287
304
  return false;
288
305
  }
306
+ const maxQueueSize = this.config.maxWriteQueueSize ?? 2000;
307
+ const highWaterMark = this.config.writeQueueHighWaterMark ?? 1500;
308
+ // Check queue capacity
309
+ if (this.writeQueue.length >= maxQueueSize) {
310
+ // Queue full - this is a serious condition, log it
311
+ console.warn(`[connection] Write queue full for ${this._agentName ?? this.id}, dropping message`);
312
+ return false;
313
+ }
289
314
  try {
290
315
  const frame = encodeFrame(envelope);
291
- this.socket.write(frame);
316
+ this.writeQueue.push(frame);
317
+ // Check if we should signal backpressure
318
+ if (!this._backpressured && this.writeQueue.length >= highWaterMark) {
319
+ this._backpressured = true;
320
+ this.onBackpressure?.(true);
321
+ }
322
+ // Schedule drain if not already draining
323
+ this.scheduleDrain();
292
324
  return true;
293
325
  }
294
326
  catch (err) {
@@ -296,11 +328,53 @@ export class Connection {
296
328
  return false;
297
329
  }
298
330
  }
331
+ /**
332
+ * Schedule the drain loop to run on next tick if not already running.
333
+ */
334
+ scheduleDrain() {
335
+ if (this.draining)
336
+ return;
337
+ this.draining = true;
338
+ setImmediate(() => this.drain());
339
+ }
340
+ /**
341
+ * Drain the write queue to the socket.
342
+ * Respects socket backpressure by waiting for 'drain' events.
343
+ */
344
+ drain() {
345
+ while (this.writeQueue.length > 0) {
346
+ if (this._state === 'CLOSED' || this._state === 'ERROR') {
347
+ this.draining = false;
348
+ return;
349
+ }
350
+ const frame = this.writeQueue[0];
351
+ const canWrite = this.socket.write(frame);
352
+ this.writeQueue.shift();
353
+ if (!canWrite) {
354
+ // Socket buffer full - wait for drain event
355
+ if (!this.socketDrainHandler) {
356
+ this.socketDrainHandler = () => {
357
+ this.socketDrainHandler = undefined;
358
+ this.drain();
359
+ };
360
+ this.socket.once('drain', this.socketDrainHandler);
361
+ }
362
+ return;
363
+ }
364
+ }
365
+ this.draining = false;
366
+ // Check if we should release backpressure
367
+ const lowWaterMark = this.config.writeQueueLowWaterMark ?? 500;
368
+ if (this._backpressured && this.writeQueue.length <= lowWaterMark) {
369
+ this._backpressured = false;
370
+ this.onBackpressure?.(false);
371
+ }
372
+ }
299
373
  sendError(code, message, fatal) {
300
374
  const error = {
301
375
  v: PROTOCOL_VERSION,
302
376
  type: 'ERROR',
303
- id: uuid(),
377
+ id: generateId(),
304
378
  ts: Date.now(),
305
379
  payload: {
306
380
  code: code,
@@ -329,6 +403,15 @@ export class Connection {
329
403
  }
330
404
  cleanup() {
331
405
  this.parser.reset();
406
+ // Clear write queue
407
+ this.writeQueue = [];
408
+ this.draining = false;
409
+ this._backpressured = false;
410
+ // Remove drain handler if registered
411
+ if (this.socketDrainHandler) {
412
+ this.socket.removeListener('drain', this.socketDrainHandler);
413
+ this.socketDrainHandler = undefined;
414
+ }
332
415
  }
333
416
  close() {
334
417
  if (this._state === 'CLOSED' || this._state === 'CLOSING')
@@ -337,7 +420,7 @@ export class Connection {
337
420
  this.send({
338
421
  v: PROTOCOL_VERSION,
339
422
  type: 'BYE',
340
- id: uuid(),
423
+ id: generateId(),
341
424
  ts: Date.now(),
342
425
  payload: {},
343
426
  });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Consensus Integration for Agent Relay
3
+ *
4
+ * Integrates the consensus mechanism with the router/daemon.
5
+ * This is an optional feature that can be enabled to allow agents
6
+ * to participate in distributed decision-making.
7
+ *
8
+ * Usage:
9
+ * 1. Create a ConsensusIntegration with the router and optional config
10
+ * 2. Call processIncomingMessage() on each received message to detect votes
11
+ * 3. Use createProposal() to start a new consensus vote
12
+ *
13
+ * Example:
14
+ * ```typescript
15
+ * const consensus = new ConsensusIntegration(router, { enabled: true });
16
+ *
17
+ * // Create a proposal
18
+ * consensus.createProposal({
19
+ * title: 'Approve API design',
20
+ * description: 'Should we proceed with the REST API design?',
21
+ * proposer: 'Architect',
22
+ * participants: ['Developer', 'Reviewer', 'Lead'],
23
+ * consensusType: 'majority',
24
+ * });
25
+ *
26
+ * // Process incoming messages to detect votes
27
+ * consensus.processIncomingMessage(from, body);
28
+ * ```
29
+ */
30
+ import { ConsensusEngine, createConsensusEngine, formatProposalMessage, formatResultMessage, parseVoteCommand, parseProposalCommand, isConsensusCommand, type Proposal, type ConsensusResult, type ConsensusConfig, type VoteValue, type ConsensusType, type ParsedProposalCommand } from './consensus.js';
31
+ import type { Router } from './router.js';
32
+ export interface CloudSyncConfig {
33
+ /** Cloud API base URL (defaults to AGENT_RELAY_CLOUD_URL or https://agent-relay.com) */
34
+ url?: string;
35
+ /** Daemon API key for authentication (defaults to AGENT_RELAY_API_KEY) */
36
+ apiKey?: string;
37
+ /** Workspace ID for self-hosted setups (optional - cloud can derive from API key) */
38
+ workspaceId?: string;
39
+ }
40
+ export interface ConsensusIntegrationConfig {
41
+ /** Enable consensus feature (default: true) */
42
+ enabled: boolean;
43
+ /** Consensus engine configuration */
44
+ consensus?: Partial<ConsensusConfig>;
45
+ /** Auto-broadcast proposals to participants (default: true) */
46
+ autoBroadcast?: boolean;
47
+ /** Auto-broadcast results when resolved (default: true) */
48
+ autoResultBroadcast?: boolean;
49
+ /** Log consensus events (default: true) */
50
+ logEvents?: boolean;
51
+ /** Cloud sync configuration (optional) */
52
+ cloudSync?: CloudSyncConfig;
53
+ }
54
+ export interface ProposalOptions {
55
+ title: string;
56
+ description: string;
57
+ proposer: string;
58
+ participants: string[];
59
+ consensusType?: ConsensusType;
60
+ timeoutMs?: number;
61
+ quorum?: number;
62
+ threshold?: number;
63
+ metadata?: Record<string, unknown>;
64
+ }
65
+ /**
66
+ * Integrates consensus mechanism with the relay router.
67
+ * Provides automatic proposal broadcasting and vote detection.
68
+ */
69
+ export declare class ConsensusIntegration {
70
+ private config;
71
+ private engine;
72
+ private router;
73
+ private log;
74
+ constructor(router: Router, config?: Partial<ConsensusIntegrationConfig>);
75
+ /**
76
+ * Check if consensus is enabled.
77
+ */
78
+ get enabled(): boolean;
79
+ /**
80
+ * Get the underlying consensus engine.
81
+ */
82
+ getEngine(): ConsensusEngine;
83
+ /**
84
+ * Create a new proposal and optionally broadcast to participants.
85
+ */
86
+ createProposal(options: ProposalOptions): Proposal;
87
+ /**
88
+ * Process an incoming message to detect and handle consensus commands.
89
+ * Handles both PROPOSE and VOTE commands.
90
+ */
91
+ processIncomingMessage(from: string, body: string): {
92
+ isConsensusCommand: boolean;
93
+ type?: 'propose' | 'vote';
94
+ result?: {
95
+ success: boolean;
96
+ error?: string;
97
+ proposal?: Proposal;
98
+ };
99
+ };
100
+ /**
101
+ * Check if a message is a consensus command without processing it.
102
+ */
103
+ isConsensusMessage(body: string): boolean;
104
+ /**
105
+ * Get pending proposals for an agent.
106
+ */
107
+ getPendingVotes(agentName: string): Proposal[];
108
+ /**
109
+ * Get all proposals for an agent.
110
+ */
111
+ getProposals(agentName: string): Proposal[];
112
+ /**
113
+ * Get a specific proposal by ID.
114
+ */
115
+ getProposal(proposalId: string): Proposal | null;
116
+ /**
117
+ * Cancel a proposal.
118
+ */
119
+ cancelProposal(proposalId: string, agentName: string): {
120
+ success: boolean;
121
+ error?: string;
122
+ };
123
+ /**
124
+ * Get consensus statistics.
125
+ */
126
+ getStats(): {
127
+ total: number;
128
+ pending: number;
129
+ approved: number;
130
+ rejected: number;
131
+ expired: number;
132
+ cancelled: number;
133
+ avgParticipation: number;
134
+ };
135
+ /**
136
+ * Cleanup resources.
137
+ */
138
+ cleanup(): void;
139
+ /**
140
+ * Sync a proposal to the cloud dashboard.
141
+ *
142
+ * Auto-detects cloud settings from workspace env vars:
143
+ * - CLOUD_API_URL / AGENT_RELAY_CLOUD_URL - cloud URL
144
+ * - WORKSPACE_ID / AGENT_RELAY_WORKSPACE_ID - workspace ID
145
+ * - WORKSPACE_TOKEN / AGENT_RELAY_API_KEY - auth token
146
+ */
147
+ private syncToCloud;
148
+ private setupEventHandlers;
149
+ /**
150
+ * Broadcast a proposal to all participants via the router.
151
+ */
152
+ private broadcastProposal;
153
+ /**
154
+ * Broadcast the result of a proposal to all participants.
155
+ */
156
+ private broadcastResult;
157
+ /**
158
+ * Send a message to an agent via the router.
159
+ */
160
+ private sendToAgent;
161
+ }
162
+ /**
163
+ * Create a consensus integration instance.
164
+ */
165
+ export declare function createConsensusIntegration(router: Router, config?: Partial<ConsensusIntegrationConfig>): ConsensusIntegration;
166
+ export { ConsensusEngine, createConsensusEngine, formatProposalMessage, formatResultMessage, parseVoteCommand, parseProposalCommand, isConsensusCommand, type Proposal, type ConsensusResult, type ConsensusConfig, type VoteValue, type ConsensusType, type ParsedProposalCommand, };
167
+ //# sourceMappingURL=consensus-integration.d.ts.map