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.
- package/.trajectories/active/traj_3yx9dy148mge.json +42 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.json +49 -0
- package/.trajectories/completed/2026-01/traj_1g7yx6qtg4ai.md +31 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.json +49 -0
- package/.trajectories/completed/2026-01/traj_4qwd4zmhfwp4.md +31 -0
- package/.trajectories/completed/2026-01/traj_6unwwmgyj5sq.json +109 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.json +49 -0
- package/.trajectories/completed/2026-01/traj_a0tqx8biw9c4.md +31 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.json +66 -0
- package/.trajectories/completed/2026-01/traj_ax8uungxz2qh.md +36 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.json +49 -0
- package/.trajectories/completed/2026-01/traj_c9izbh2snpzf.md +31 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.json +65 -0
- package/.trajectories/completed/2026-01/traj_cpn70dw066nt.md +37 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.json +36 -0
- package/.trajectories/completed/2026-01/traj_erglv2f8t9eh.md +21 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.json +101 -0
- package/.trajectories/completed/2026-01/traj_he75f24d1xfm.md +52 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.json +61 -0
- package/.trajectories/completed/2026-01/traj_lgtodco7dp1n.md +36 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.json +73 -0
- package/.trajectories/completed/2026-01/traj_oszg9flv74pk.md +41 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.json +77 -0
- package/.trajectories/completed/2026-01/traj_pulomd3y8cvj.md +42 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.json +109 -0
- package/.trajectories/completed/2026-01/traj_rsavt0jipi3c.md +56 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.json +113 -0
- package/.trajectories/completed/2026-01/traj_x721m1j9rzup.md +57 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.json +61 -0
- package/.trajectories/completed/2026-01/traj_xjqvmep5ed3h.md +36 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.json +49 -0
- package/.trajectories/completed/2026-01/traj_y7n6hfbf7dmg.md +31 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.json +49 -0
- package/.trajectories/completed/2026-01/traj_yvfkwnkdiso2.md +31 -0
- package/.trajectories/index.json +140 -1
- package/TRAIL_GIT_AUTH_FIX.md +113 -0
- package/deploy/workspace/codex.config.toml +1 -1
- package/deploy/workspace/entrypoint.sh +20 -79
- package/deploy/workspace/gh-relay +156 -0
- package/deploy/workspace/git-credential-relay +5 -1
- package/dist/bridge/multi-project-client.js +13 -10
- package/dist/bridge/spawner.d.ts +2 -0
- package/dist/bridge/spawner.js +19 -1
- package/dist/bridge/types.d.ts +2 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +115 -69
- package/dist/cloud/api/admin.js +16 -3
- package/dist/cloud/api/codex-auth-helper.js +28 -8
- package/dist/cloud/api/consensus.d.ts +13 -0
- package/dist/cloud/api/consensus.js +259 -0
- package/dist/cloud/api/daemons.js +205 -1
- package/dist/cloud/api/git.js +37 -7
- package/dist/cloud/api/onboarding.js +4 -1
- package/dist/cloud/api/provider-env.d.ts +5 -0
- package/dist/cloud/api/provider-env.js +27 -0
- package/dist/cloud/api/providers.js +2 -0
- package/dist/cloud/api/test-helpers.js +130 -0
- package/dist/cloud/api/workspaces.js +38 -3
- package/dist/cloud/db/bulk-ingest.d.ts +88 -0
- package/dist/cloud/db/bulk-ingest.js +268 -0
- package/dist/cloud/db/drizzle.d.ts +33 -0
- package/dist/cloud/db/drizzle.js +174 -2
- package/dist/cloud/db/index.d.ts +24 -5
- package/dist/cloud/db/index.js +19 -4
- package/dist/cloud/db/schema.d.ts +397 -3
- package/dist/cloud/db/schema.js +75 -1
- package/dist/cloud/provisioner/index.d.ts +8 -0
- package/dist/cloud/provisioner/index.js +256 -50
- package/dist/cloud/server.js +47 -3
- package/dist/cloud/services/index.d.ts +1 -0
- package/dist/cloud/services/index.js +2 -0
- package/dist/cloud/services/nango.d.ts +3 -4
- package/dist/cloud/services/nango.js +11 -33
- package/dist/cloud/services/workspace-keepalive.d.ts +76 -0
- package/dist/cloud/services/workspace-keepalive.js +234 -0
- package/dist/config/relay-config.d.ts +23 -0
- package/dist/config/relay-config.js +23 -0
- package/dist/daemon/agent-manager.d.ts +20 -1
- package/dist/daemon/agent-manager.js +47 -0
- package/dist/daemon/agent-registry.js +4 -4
- package/dist/daemon/agent-signing.d.ts +158 -0
- package/dist/daemon/agent-signing.js +523 -0
- package/dist/daemon/api.js +18 -1
- package/dist/daemon/cli-auth.d.ts +4 -1
- package/dist/daemon/cli-auth.js +55 -11
- package/dist/daemon/cloud-sync.d.ts +47 -1
- package/dist/daemon/cloud-sync.js +152 -3
- package/dist/daemon/connection.d.ts +28 -0
- package/dist/daemon/connection.js +98 -15
- package/dist/daemon/consensus-integration.d.ts +167 -0
- package/dist/daemon/consensus-integration.js +371 -0
- package/dist/daemon/consensus.d.ts +271 -0
- package/dist/daemon/consensus.js +632 -0
- package/dist/daemon/delivery-tracker.d.ts +34 -0
- package/dist/daemon/delivery-tracker.js +104 -0
- package/dist/daemon/enhanced-features.d.ts +118 -0
- package/dist/daemon/enhanced-features.js +178 -0
- package/dist/daemon/index.d.ts +4 -0
- package/dist/daemon/index.js +5 -0
- package/dist/daemon/rate-limiter.d.ts +68 -0
- package/dist/daemon/rate-limiter.js +130 -0
- package/dist/daemon/router.d.ts +18 -11
- package/dist/daemon/router.js +55 -111
- package/dist/daemon/server.d.ts +13 -1
- package/dist/daemon/server.js +71 -9
- package/dist/daemon/sync-queue.d.ts +116 -0
- package/dist/daemon/sync-queue.js +361 -0
- package/dist/health-worker-manager.d.ts +62 -0
- package/dist/health-worker-manager.js +144 -0
- package/dist/health-worker.d.ts +9 -0
- package/dist/health-worker.js +79 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/memory/context-compaction.d.ts +156 -0
- package/dist/memory/context-compaction.js +453 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/protocol/channels.js +4 -4
- package/dist/protocol/framing.d.ts +72 -10
- package/dist/protocol/framing.js +194 -25
- package/dist/storage/adapter.d.ts +8 -1
- package/dist/storage/adapter.js +11 -0
- package/dist/storage/batched-sqlite-adapter.d.ts +71 -0
- package/dist/storage/batched-sqlite-adapter.js +183 -0
- package/dist/storage/dead-letter-queue.d.ts +196 -0
- package/dist/storage/dead-letter-queue.js +427 -0
- package/dist/storage/dlq-adapter.d.ts +195 -0
- package/dist/storage/dlq-adapter.js +664 -0
- package/dist/trajectory/config.d.ts +32 -14
- package/dist/trajectory/config.js +38 -16
- package/dist/trajectory/integration.js +217 -64
- package/dist/utils/git-remote.d.ts +47 -0
- package/dist/utils/git-remote.js +125 -0
- package/dist/utils/id-generator.d.ts +35 -0
- package/dist/utils/id-generator.js +60 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/precompiled-patterns.d.ts +110 -0
- package/dist/utils/precompiled-patterns.js +322 -0
- package/dist/wrapper/auth-detection.js +1 -1
- package/dist/wrapper/base-wrapper.d.ts +36 -0
- package/dist/wrapper/base-wrapper.js +48 -2
- package/dist/wrapper/client.d.ts +14 -4
- package/dist/wrapper/client.js +84 -31
- package/dist/wrapper/idle-detector.d.ts +102 -0
- package/dist/wrapper/idle-detector.js +279 -0
- package/dist/wrapper/parser.d.ts +4 -0
- package/dist/wrapper/parser.js +19 -1
- package/dist/wrapper/pty-wrapper.d.ts +7 -1
- package/dist/wrapper/pty-wrapper.js +51 -27
- package/dist/wrapper/tmux-wrapper.d.ts +12 -1
- package/dist/wrapper/tmux-wrapper.js +65 -17
- package/package.json +5 -5
- package/scripts/run-migrations.js +43 -0
- package/scripts/verify-schema.js +134 -0
- package/tests/benchmarks/protocol.bench.ts +310 -0
- package/dist/dashboard/out/404.html +0 -1
- package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_buildManifest.js +0 -1
- package/dist/dashboard/out/_next/static/T1tgCqVWHFIkV7ClEtzD7/_ssgManifest.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/117-f7b8ab0809342e77.js +0 -2
- package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/532-bace199897eeab37.js +0 -9
- package/dist/dashboard/out/_next/static/chunks/648-5cc6e1921389a58a.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/766-b54f0853794b78c3.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/83-b51836037078006c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/891-6cd50de1224f70bb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/899-bb19a9b3d9b39ea6.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/_not-found/page-53b8a69f76db17d0.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/onboarding/page-8939b0fc700f7eca.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-5af1b6b439858aa6.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-f45ecbc3e06134fc.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/history/page-8c8bed33beb2bf1c.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/login/page-16f3b49e55b1e0ed.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-ac39dc0cc3c26fa7.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-4a5938c18a11a654.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/pricing/page-982a7000fee44014.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-ac3a6ac433fd6001.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-09f9caae98a18c09.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/signup/page-547dd0ca55ecd0ba.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +0 -18
- package/dist/dashboard/out/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/framework-f66176bb897dc684.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-2ee6beb2ae96d210.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-app-5d692157a8eb1fd9.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_app-72b849fbd24ac258.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/pages/_error-7ba65e1336b92748.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +0 -1
- package/dist/dashboard/out/_next/static/css/85d2af9c7ac74d62.css +0 -1
- package/dist/dashboard/out/_next/static/css/fe4b28883eeff359.css +0 -1
- package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +0 -45
- package/dist/dashboard/out/alt-logos/logo.svg +0 -38
- package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo.svg +0 -38
- package/dist/dashboard/out/app/onboarding.html +0 -1
- package/dist/dashboard/out/app/onboarding.txt +0 -7
- package/dist/dashboard/out/app.html +0 -1
- package/dist/dashboard/out/app.txt +0 -7
- package/dist/dashboard/out/apple-icon.png +0 -0
- package/dist/dashboard/out/connect-repos.html +0 -1
- package/dist/dashboard/out/connect-repos.txt +0 -7
- package/dist/dashboard/out/history.html +0 -1
- package/dist/dashboard/out/history.txt +0 -7
- package/dist/dashboard/out/index.html +0 -1
- package/dist/dashboard/out/index.txt +0 -7
- package/dist/dashboard/out/login.html +0 -6
- package/dist/dashboard/out/login.txt +0 -7
- package/dist/dashboard/out/metrics.html +0 -1
- package/dist/dashboard/out/metrics.txt +0 -7
- package/dist/dashboard/out/pricing.html +0 -13
- package/dist/dashboard/out/pricing.txt +0 -7
- package/dist/dashboard/out/providers/setup/claude.html +0 -1
- package/dist/dashboard/out/providers/setup/claude.txt +0 -8
- package/dist/dashboard/out/providers/setup/codex.html +0 -1
- package/dist/dashboard/out/providers/setup/codex.txt +0 -8
- package/dist/dashboard/out/providers.html +0 -1
- package/dist/dashboard/out/providers.txt +0 -7
- package/dist/dashboard/out/signup.html +0 -6
- package/dist/dashboard/out/signup.txt +0 -7
- package/dist/dashboard-server/metrics.d.ts +0 -105
- package/dist/dashboard-server/metrics.js +0 -193
- package/dist/dashboard-server/needs-attention.d.ts +0 -24
- package/dist/dashboard-server/needs-attention.js +0 -78
- package/dist/dashboard-server/server.d.ts +0 -15
- package/dist/dashboard-server/server.js +0 -3776
- package/dist/dashboard-server/start.d.ts +0 -6
- package/dist/dashboard-server/start.js +0 -13
- package/dist/dashboard-server/user-bridge.d.ts +0 -103
- 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 {
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
54
|
-
this.
|
|
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:
|
|
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 =
|
|
261
|
+
const nonce = generateId();
|
|
248
262
|
this.send({
|
|
249
263
|
v: PROTOCOL_VERSION,
|
|
250
264
|
type: 'PING',
|
|
251
|
-
id:
|
|
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.
|
|
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:
|
|
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:
|
|
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
|