agent-relay 1.3.1 → 1.3.3
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/README.md +23 -9
- 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 +58 -76
- package/dist/bridge/types.d.ts +2 -0
- package/dist/cli/index.d.ts +8 -6
- package/dist/cli/index.js +297 -30
- 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 +51 -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 +113 -22
- 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 +57 -113
- 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/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/chunks/116-de2a4ac06e5000dc.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/847-f1f467060f32afff.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/919-87d604a5d76c1fbd.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/{page-c617745b81344f4f.js → page-7f64824ae7d06707.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/app/cloud/link/page-3f559d393902aad2.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/login/page-16d1715ddaa874ee.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/{page-dc786c183425c2ac.js → page-814efc4d77b4191d.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/{main-2ee6beb2ae96d210.js → main-5a40a5ae29646e1b.js} +1 -1
- package/dist/dashboard/out/_next/static/css/44d2b52637b511bc.css +1 -0
- package/dist/dashboard/out/app/onboarding.html +1 -1
- package/dist/dashboard/out/app/onboarding.txt +1 -1
- package/dist/dashboard/out/app.html +1 -1
- package/dist/dashboard/out/app.txt +2 -2
- package/dist/dashboard/out/cloud/link.html +1 -0
- package/dist/dashboard/out/cloud/link.txt +7 -0
- package/dist/dashboard/out/connect-repos.html +1 -1
- package/dist/dashboard/out/connect-repos.txt +1 -1
- package/dist/dashboard/out/history.html +1 -1
- package/dist/dashboard/out/history.txt +2 -2
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +2 -2
- package/dist/dashboard/out/login.html +2 -3
- package/dist/dashboard/out/login.txt +2 -2
- package/dist/dashboard/out/metrics.html +1 -1
- package/dist/dashboard/out/metrics.txt +2 -2
- package/dist/dashboard/out/pricing.html +2 -2
- package/dist/dashboard/out/pricing.txt +1 -1
- package/dist/dashboard/out/providers/setup/claude.html +1 -1
- package/dist/dashboard/out/providers/setup/claude.txt +1 -1
- package/dist/dashboard/out/providers/setup/codex.html +1 -1
- package/dist/dashboard/out/providers/setup/codex.txt +1 -1
- package/dist/dashboard/out/providers.html +1 -1
- package/dist/dashboard/out/providers.txt +1 -1
- package/dist/dashboard/out/signup.html +2 -2
- package/dist/dashboard/out/signup.txt +1 -1
- package/dist/dashboard-server/server.js +244 -28
- 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 +40 -0
- package/dist/wrapper/base-wrapper.js +60 -6
- package/dist/wrapper/client.d.ts +14 -4
- package/dist/wrapper/client.js +89 -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 +14 -2
- package/dist/wrapper/pty-wrapper.js +132 -32
- package/dist/wrapper/shared.d.ts +1 -1
- package/dist/wrapper/shared.js +1 -1
- package/dist/wrapper/tmux-wrapper.d.ts +20 -2
- package/dist/wrapper/tmux-wrapper.js +163 -40
- package/package.json +3 -1
- 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/_next/static/chunks/116-2502180def231162.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/899-fc02ed79e3de4302.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/login/page-c22d080201cbd9fb.js +0 -1
- package/dist/dashboard/out/_next/static/css/48a8fbe3e659080e.css +0 -1
- /package/dist/dashboard/out/_next/static/{sDcbGRTYLcpPvyTs_rsNb → R-uQOUcOLINtsp6ACeZa9}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{sDcbGRTYLcpPvyTs_rsNb → R-uQOUcOLINtsp6ACeZa9}/_ssgManifest.js +0 -0
|
@@ -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,33 @@ 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
|
-
if (this._state === 'CLOSED' || this._state === 'ERROR') {
|
|
303
|
+
if (this._state === 'CLOSED' || this._state === 'ERROR' || this._state === 'CLOSING') {
|
|
304
|
+
console.log(`[connection] Send to ${this._agentName} blocked - state: ${this._state}`);
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
const maxQueueSize = this.config.maxWriteQueueSize ?? 2000;
|
|
308
|
+
const highWaterMark = this.config.writeQueueHighWaterMark ?? 1500;
|
|
309
|
+
// Check queue capacity
|
|
310
|
+
if (this.writeQueue.length >= maxQueueSize) {
|
|
311
|
+
// Queue full - this is a serious condition, log it
|
|
312
|
+
console.warn(`[connection] Write queue full for ${this._agentName ?? this.id}, dropping message`);
|
|
287
313
|
return false;
|
|
288
314
|
}
|
|
289
315
|
try {
|
|
290
316
|
const frame = encodeFrame(envelope);
|
|
291
|
-
this.
|
|
317
|
+
this.writeQueue.push(frame);
|
|
318
|
+
// Check if we should signal backpressure
|
|
319
|
+
if (!this._backpressured && this.writeQueue.length >= highWaterMark) {
|
|
320
|
+
this._backpressured = true;
|
|
321
|
+
this.onBackpressure?.(true);
|
|
322
|
+
}
|
|
323
|
+
// Schedule drain if not already draining
|
|
324
|
+
this.scheduleDrain();
|
|
292
325
|
return true;
|
|
293
326
|
}
|
|
294
327
|
catch (err) {
|
|
@@ -296,11 +329,53 @@ export class Connection {
|
|
|
296
329
|
return false;
|
|
297
330
|
}
|
|
298
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Schedule the drain loop to run on next tick if not already running.
|
|
334
|
+
*/
|
|
335
|
+
scheduleDrain() {
|
|
336
|
+
if (this.draining)
|
|
337
|
+
return;
|
|
338
|
+
this.draining = true;
|
|
339
|
+
setImmediate(() => this.drain());
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Drain the write queue to the socket.
|
|
343
|
+
* Respects socket backpressure by waiting for 'drain' events.
|
|
344
|
+
*/
|
|
345
|
+
drain() {
|
|
346
|
+
while (this.writeQueue.length > 0) {
|
|
347
|
+
if (this._state === 'CLOSED' || this._state === 'ERROR' || this._state === 'CLOSING') {
|
|
348
|
+
this.draining = false;
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const frame = this.writeQueue[0];
|
|
352
|
+
const canWrite = this.socket.write(frame);
|
|
353
|
+
this.writeQueue.shift();
|
|
354
|
+
if (!canWrite) {
|
|
355
|
+
// Socket buffer full - wait for drain event
|
|
356
|
+
if (!this.socketDrainHandler) {
|
|
357
|
+
this.socketDrainHandler = () => {
|
|
358
|
+
this.socketDrainHandler = undefined;
|
|
359
|
+
this.drain();
|
|
360
|
+
};
|
|
361
|
+
this.socket.once('drain', this.socketDrainHandler);
|
|
362
|
+
}
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
this.draining = false;
|
|
367
|
+
// Check if we should release backpressure
|
|
368
|
+
const lowWaterMark = this.config.writeQueueLowWaterMark ?? 500;
|
|
369
|
+
if (this._backpressured && this.writeQueue.length <= lowWaterMark) {
|
|
370
|
+
this._backpressured = false;
|
|
371
|
+
this.onBackpressure?.(false);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
299
374
|
sendError(code, message, fatal) {
|
|
300
375
|
const error = {
|
|
301
376
|
v: PROTOCOL_VERSION,
|
|
302
377
|
type: 'ERROR',
|
|
303
|
-
id:
|
|
378
|
+
id: generateId(),
|
|
304
379
|
ts: Date.now(),
|
|
305
380
|
payload: {
|
|
306
381
|
code: code,
|
|
@@ -329,18 +404,34 @@ export class Connection {
|
|
|
329
404
|
}
|
|
330
405
|
cleanup() {
|
|
331
406
|
this.parser.reset();
|
|
407
|
+
// Clear write queue
|
|
408
|
+
this.writeQueue = [];
|
|
409
|
+
this.draining = false;
|
|
410
|
+
this._backpressured = false;
|
|
411
|
+
// Remove drain handler if registered
|
|
412
|
+
if (this.socketDrainHandler) {
|
|
413
|
+
this.socket.removeListener('drain', this.socketDrainHandler);
|
|
414
|
+
this.socketDrainHandler = undefined;
|
|
415
|
+
}
|
|
332
416
|
}
|
|
333
417
|
close() {
|
|
334
418
|
if (this._state === 'CLOSED' || this._state === 'CLOSING')
|
|
335
419
|
return;
|
|
336
420
|
this._state = 'CLOSING';
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
421
|
+
// Write BYE message directly (not via queue) to avoid race with socket.end()
|
|
422
|
+
try {
|
|
423
|
+
const byeFrame = encodeFrame({
|
|
424
|
+
v: PROTOCOL_VERSION,
|
|
425
|
+
type: 'BYE',
|
|
426
|
+
id: generateId(),
|
|
427
|
+
ts: Date.now(),
|
|
428
|
+
payload: {},
|
|
429
|
+
});
|
|
430
|
+
this.socket.write(byeFrame);
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
// Ignore write errors during close
|
|
434
|
+
}
|
|
344
435
|
this.socket.end();
|
|
345
436
|
}
|
|
346
437
|
}
|
|
@@ -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
|