agent-comms 9.1.1 → 9.1.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/bridges/user/web/request-handling.d.ts +26 -0
- package/dist/bridges/user/web/request-handling.d.ts.map +1 -0
- package/dist/bridges/user/web/request-handling.js +92 -0
- package/dist/bridges/user/web/request-handling.js.map +1 -0
- package/dist/bridges/user/web/server.d.ts +4 -0
- package/dist/bridges/user/web/server.d.ts.map +1 -1
- package/dist/bridges/user/web/server.js +88 -53
- package/dist/bridges/user/web/server.js.map +1 -1
- package/dist/bridges/user/web/web-access.d.ts.map +1 -1
- package/dist/bridges/user/web/web-access.js +14 -1
- package/dist/bridges/user/web/web-access.js.map +1 -1
- package/dist/bridges/user/web/web-assets.d.ts +2 -1
- package/dist/bridges/user/web/web-assets.d.ts.map +1 -1
- package/dist/bridges/user/web/web-assets.js +2 -2
- package/dist/bridges/user/web/web-assets.js.map +1 -1
- package/dist/bridges/user/web/web-console-static.d.ts.map +1 -1
- package/dist/bridges/user/web/web-console-static.js +34 -22
- package/dist/bridges/user/web/web-console-static.js.map +1 -1
- package/dist/core/detached-work.d.ts +14 -0
- package/dist/core/detached-work.d.ts.map +1 -0
- package/dist/core/detached-work.js +40 -0
- package/dist/core/detached-work.js.map +1 -0
- package/dist/core/election-sessions.d.ts.map +1 -1
- package/dist/core/election-sessions.js +6 -6
- package/dist/core/election-sessions.js.map +1 -1
- package/dist/core/hub-session.d.ts +10 -1
- package/dist/core/hub-session.d.ts.map +1 -1
- package/dist/core/hub-session.js +34 -32
- package/dist/core/hub-session.js.map +1 -1
- package/dist/core/message-limits.d.ts +14 -0
- package/dist/core/message-limits.d.ts.map +1 -0
- package/dist/core/message-limits.js +22 -0
- package/dist/core/message-limits.js.map +1 -0
- package/dist/core/peer-lifecycle.d.ts +2 -0
- package/dist/core/peer-lifecycle.d.ts.map +1 -1
- package/dist/core/peer-lifecycle.js +9 -4
- package/dist/core/peer-lifecycle.js.map +1 -1
- package/dist/core/pending-connection.d.ts +20 -1
- package/dist/core/pending-connection.d.ts.map +1 -1
- package/dist/core/pending-connection.js +27 -0
- package/dist/core/pending-connection.js.map +1 -1
- package/dist/core/relay-server.d.ts +15 -1
- package/dist/core/relay-server.d.ts.map +1 -1
- package/dist/core/relay-server.js +70 -16
- package/dist/core/relay-server.js.map +1 -1
- package/dist/core/room-router.d.ts.map +1 -1
- package/dist/core/room-router.js +31 -10
- package/dist/core/room-router.js.map +1 -1
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +3 -0
- package/dist/core/tool.js.map +1 -1
- package/dist/core/transport-events.d.ts.map +1 -1
- package/dist/core/transport-events.js +9 -5
- package/dist/core/transport-events.js.map +1 -1
- package/dist/core/wire-mesh-transport.d.ts +2 -2
- package/dist/core/wire-mesh-transport.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.js +96 -115
- package/dist/core/wire-mesh-transport.js.map +1 -1
- package/package.json +1 -1
- package/src/bridges/user/web/request-handling.ts +118 -0
- package/src/bridges/user/web/server.ts +109 -54
- package/src/bridges/user/web/web-access.ts +14 -1
- package/src/bridges/user/web/web-assets.ts +7 -3
- package/src/bridges/user/web/web-console-static.ts +34 -26
- package/src/core/detached-work.ts +55 -0
- package/src/core/election-sessions.ts +8 -4
- package/src/core/hub-session.ts +63 -41
- package/src/core/message-limits.ts +29 -0
- package/src/core/peer-lifecycle.ts +10 -4
- package/src/core/pending-connection.ts +48 -1
- package/src/core/relay-server.ts +110 -23
- package/src/core/room-router.ts +36 -9
- package/src/core/tool.ts +3 -0
- package/src/core/transport-events.ts +9 -5
- package/src/core/wire-mesh-transport.ts +58 -63
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* connect_request's accept/reject flow maps onto sendManageRequest's own request/response round trip directly, rather than a separate pair of connect_accepted/connect_rejected messages: manage-request-frame already tolerates arbitrary latency between a request and its response, so "waiting for a human to approve" is simply a manage-response that hasn't been sent yet, not a protocol gap needing its own mechanism. connect_request therefore holds its own IncomingManageRequest open (rather than responding immediately) until acceptConnection/rejectConnection is actually called.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { consumeEach, reportRejection } from "./detached-work.js";
|
|
11
12
|
import { createTlsTransport } from "wire-mesh-core/adapters/tls-transport";
|
|
12
13
|
import {
|
|
13
14
|
ROOM_JOIN_APPROVAL_TIMEOUT_MS,
|
|
@@ -44,8 +45,7 @@ import { RelayRole } from "./relay-role.js";
|
|
|
44
45
|
import { ALL_INTERFACES_HOST } from "./relay-server.js";
|
|
45
46
|
import {
|
|
46
47
|
DEFAULT_PENDING_CONNECTION_TIMEOUT_MS,
|
|
47
|
-
|
|
48
|
-
type ConnectionDecision,
|
|
48
|
+
holdConnectRequest,
|
|
49
49
|
type PendingConnection,
|
|
50
50
|
} from "./pending-connection.js";
|
|
51
51
|
import {
|
|
@@ -435,18 +435,20 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
435
435
|
): void {
|
|
436
436
|
// Watched from the start, so any claim this session gossips before it introduces itself is read and dropped rather than held back and replayed once it is trusted.
|
|
437
437
|
this.electionSessions.watch(session, handle);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
438
|
+
let approved = false;
|
|
439
|
+
// A request that cannot be handled is reported and the next is still read; the loop ends only where the session itself is being ended, and says so with "stop".
|
|
440
|
+
void consumeEach(
|
|
441
|
+
session.incomingManageRequests,
|
|
442
|
+
async (request) => {
|
|
443
|
+
if (this.shutDown) return "stop";
|
|
442
444
|
if (approved) {
|
|
443
445
|
await this.roomRouter.handleLocalRequest(request, handle);
|
|
444
|
-
continue;
|
|
446
|
+
return "continue";
|
|
445
447
|
}
|
|
446
448
|
const message = extractMessage(request.command);
|
|
447
449
|
if (message === undefined) {
|
|
448
450
|
await request.respond({ result: "ok" }).catch(() => undefined);
|
|
449
|
-
continue;
|
|
451
|
+
return "continue";
|
|
450
452
|
}
|
|
451
453
|
if (message.method === "introduce") {
|
|
452
454
|
this.trackSession(handle.id, session);
|
|
@@ -458,48 +460,36 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
458
460
|
});
|
|
459
461
|
await request.respond({ result: "ok" }).catch(() => undefined);
|
|
460
462
|
approved = true;
|
|
461
|
-
continue;
|
|
463
|
+
return "continue";
|
|
462
464
|
}
|
|
463
465
|
if (message.method === "connect_request") {
|
|
464
|
-
// Held open deliberately (see this file's own header comment).
|
|
465
|
-
const decision = await
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
resolve,
|
|
474
|
-
// unref()'d so a stray pending connection (this timer failing to be cleared through some path not yet covered) can never by itself keep the process alive for up to pendingConnectionTimeoutMs after everything else is done -- confirmed necessary directly: shutdown() originally cleared every pendingConnections entry without clearing its timer, and the whole test process hung for the full default timeout before exiting.
|
|
475
|
-
timeoutHandle: setTimeout(() => {
|
|
476
|
-
expirePendingConnection(this.pendingConnections, handle.id);
|
|
477
|
-
}, this.pendingConnectionTimeoutMs).unref(),
|
|
478
|
-
});
|
|
479
|
-
this.events.onConnectionRequest(handle, {
|
|
480
|
-
peerId: handle.id,
|
|
481
|
-
dataPort: message.dataPort,
|
|
482
|
-
name: message.name,
|
|
483
|
-
fingerprint: message.fingerprint,
|
|
484
|
-
});
|
|
485
|
-
});
|
|
466
|
+
// Held open deliberately (see this file's own header comment).
|
|
467
|
+
const decision = await holdConnectRequest(
|
|
468
|
+
{
|
|
469
|
+
pendingConnections: this.pendingConnections,
|
|
470
|
+
timeoutMs: this.pendingConnectionTimeoutMs,
|
|
471
|
+
events: this.events,
|
|
472
|
+
},
|
|
473
|
+
{ request, message, handle, session },
|
|
474
|
+
);
|
|
486
475
|
if (decision === "reject") {
|
|
487
476
|
// Rejection can arrive via rejectConnection (session still open) or via shutdown/disconnect (session already closing) -- catch rather than assume which.
|
|
488
477
|
await session.close().catch(() => undefined);
|
|
489
|
-
return;
|
|
478
|
+
return "stop";
|
|
490
479
|
}
|
|
491
480
|
// acceptConnection has already called trackSession synchronously, before firing onIntroduction -- nothing left to do here beyond trusting subsequent requests on this same session.
|
|
492
481
|
approved = true;
|
|
493
|
-
continue;
|
|
482
|
+
return "continue";
|
|
494
483
|
}
|
|
495
484
|
// Any other message before introduce/connect_request is a protocol violation from an unapproved peer -- refuse and terminate rather than route it.
|
|
496
485
|
await request
|
|
497
486
|
.respond({ result: "error", code: "not_approved" })
|
|
498
487
|
.catch(() => undefined);
|
|
499
488
|
await session.close().catch(() => undefined);
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
|
|
489
|
+
return "stop";
|
|
490
|
+
},
|
|
491
|
+
() => this.events.onError,
|
|
492
|
+
);
|
|
503
493
|
}
|
|
504
494
|
|
|
505
495
|
/** Opens a mesh session over an already-established connection, wired to this transport's own frame dispatch and advertised addresses. Every session this transport owns is built exactly this way, whether the connection was accepted by a listener or dialled at a peer, at the coordinator, or at a remote gateway, so the wiring lives here rather than being restated at each call site. Resolves undefined, with the session closed, when shutdown() has begun by the time the session is open, whether it began before the connection arrived or while the session was opening: shutdown() closes only the sessions it can see, so one opened after it started would stay open, and its peer would never learn that this side had gone (agent-comms#372). */
|
|
@@ -527,18 +517,28 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
527
517
|
machineLocal: boolean,
|
|
528
518
|
): void {
|
|
529
519
|
this.roomRouter.drainSession(session, handle);
|
|
530
|
-
this.
|
|
520
|
+
// Reads every revocation-entry this session's peer announces, for as long as the session lives, handing each one to MeshStore via onRevocationAnnounce, one call per entry, matching revocationAnnouncements' own per-entry flattening of a revocation-announce frame's entries array. A gossiped revocation is not a manage-request and has no verb for roomRouter to dispatch, so it has its own independent drain.
|
|
521
|
+
// A revocation that cannot be handled is reported and the next one is still read: one bad entry must not end revocation intake on this session.
|
|
522
|
+
void consumeEach(
|
|
523
|
+
session.revocationAnnouncements,
|
|
524
|
+
(entry) => {
|
|
525
|
+
this.events.onRevocationAnnounce(entry);
|
|
526
|
+
return "continue";
|
|
527
|
+
},
|
|
528
|
+
() => this.events.onError,
|
|
529
|
+
);
|
|
531
530
|
this.electionSessions.watch(session, handle);
|
|
532
531
|
if (machineLocal) this.electionSessions.enrol(session, handle);
|
|
533
532
|
}
|
|
534
533
|
|
|
535
|
-
/**
|
|
536
|
-
private
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
this.
|
|
540
|
-
|
|
541
|
-
|
|
534
|
+
/** The callback a listener calls with each connection it accepts, handling it without awaiting it: a failure goes to the transport's error handler, or stderr, and is never left as an unhandled rejection that would end the process. */
|
|
535
|
+
private accepting(accept: Readonly<AcceptOptions>) {
|
|
536
|
+
return (connection: Readonly<Connection>): void => {
|
|
537
|
+
reportRejection(
|
|
538
|
+
this.handleAcceptedConnection(connection, accept),
|
|
539
|
+
() => this.events.onError,
|
|
540
|
+
);
|
|
541
|
+
};
|
|
542
542
|
}
|
|
543
543
|
|
|
544
544
|
private watchForDisconnect(
|
|
@@ -546,10 +546,12 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
546
546
|
handle: Readonly<ConnectionHandle>,
|
|
547
547
|
deviceIdHex: string,
|
|
548
548
|
): void {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
549
|
+
// The presence this session last reported. Every session event carries the whole directory, including events this side causes by sending a frame and the final closed event, so the peer's advert repeats long after the peer sent it. Only a change is news: re-reporting a stale advert would mark a departed peer's agent running again after the coordinator has set it offline, and nothing would ever correct that.
|
|
550
|
+
let reportedPresence: AgentStatus | undefined;
|
|
551
|
+
// A session event that cannot be handled is reported and the next is still read, so the closed event that ends this session's bookkeeping is never missed.
|
|
552
|
+
void consumeEach(
|
|
553
|
+
session.events,
|
|
554
|
+
(event) => {
|
|
553
555
|
this.knownDevices.mergeLocal(event.directory);
|
|
554
556
|
const presence = findPresenceAdvert(deviceIdHex, event.directory);
|
|
555
557
|
if (presence !== undefined && presence !== reportedPresence) {
|
|
@@ -576,10 +578,12 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
576
578
|
if (wasTracked && !this.shutDown) {
|
|
577
579
|
this.events.onPeerDisconnected(handle);
|
|
578
580
|
}
|
|
579
|
-
return;
|
|
581
|
+
return "stop";
|
|
580
582
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
+
return "continue";
|
|
584
|
+
},
|
|
585
|
+
() => this.events.onError,
|
|
586
|
+
);
|
|
583
587
|
}
|
|
584
588
|
|
|
585
589
|
// -----------------------------------------------------------------------
|
|
@@ -589,9 +593,7 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
589
593
|
async startDataServer(): Promise<void> {
|
|
590
594
|
this.dataListener = await this.wireTransport.listen(
|
|
591
595
|
`${COORDINATOR_HOST}:0`,
|
|
592
|
-
(
|
|
593
|
-
void this.handleAcceptedConnection(connection, DATA_SERVER_ACCEPT);
|
|
594
|
-
},
|
|
596
|
+
this.accepting(DATA_SERVER_ACCEPT),
|
|
595
597
|
);
|
|
596
598
|
this._dataPort = listenerPort(this.dataListener);
|
|
597
599
|
}
|
|
@@ -642,9 +644,7 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
642
644
|
coordinatorListeners: this.coordinatorListeners,
|
|
643
645
|
host,
|
|
644
646
|
port,
|
|
645
|
-
onAccepted: (
|
|
646
|
-
void this.handleAcceptedConnection(connection, DEFAULT_LISTENER_ACCEPT);
|
|
647
|
-
},
|
|
647
|
+
onAccepted: this.accepting(DEFAULT_LISTENER_ACCEPT),
|
|
648
648
|
isShuttingDown: () => this.isShuttingDown(),
|
|
649
649
|
});
|
|
650
650
|
this._isCoordinator = true;
|
|
@@ -907,12 +907,7 @@ export class WireMeshTransport implements MeshTransport {
|
|
|
907
907
|
host,
|
|
908
908
|
port,
|
|
909
909
|
policy,
|
|
910
|
-
onAccepted: (
|
|
911
|
-
void this.handleAcceptedConnection(
|
|
912
|
-
connection,
|
|
913
|
-
operatorListenerAccept(policy),
|
|
914
|
-
);
|
|
915
|
-
},
|
|
910
|
+
onAccepted: this.accepting(operatorListenerAccept(policy)),
|
|
916
911
|
});
|
|
917
912
|
}
|
|
918
913
|
|