dsh-interconnect 0.11.16 → 0.11.18
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/dsh.plugin.json +1 -1
- package/lib/index.js +34 -30
- package/lib/interconnect/index.js +34 -30
- package/lib/interconnect/index.js.map +2 -2
- package/lib/tool-interconnect/index.js +34 -30
- package/lib/tool-interconnect/index.js.map +2 -2
- package/lib/types/interconnect/index.d.ts +12 -8
- package/lib/types/interconnect/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -838,6 +838,7 @@ var PLUGIN_SOURCE = "dsh-interconnect";
|
|
|
838
838
|
var MAX_LINK_FRAME_BYTES = 1024 * 1024;
|
|
839
839
|
var LINK_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
840
840
|
var LINK_CHANNEL = "/interconnect/link";
|
|
841
|
+
var MAX_INSTANCE_ID_CHARS = 256;
|
|
841
842
|
var MAX_REQUEST_ID_CHARS = 256;
|
|
842
843
|
var MAX_LISTED_SESSIONS = 100;
|
|
843
844
|
var frameFitsLink = (frame) => Buffer.byteLength(JSON.stringify(frame), "utf8") <= MAX_LINK_FRAME_BYTES;
|
|
@@ -931,7 +932,7 @@ var linkFrameSchema = Schema.union([
|
|
|
931
932
|
var InterconnectService = class extends Service {
|
|
932
933
|
static inject = ["agents", "credentials"];
|
|
933
934
|
static Config = Schema.object({
|
|
934
|
-
instanceId: Schema.string().default("dsh"),
|
|
935
|
+
instanceId: Schema.string().max(MAX_INSTANCE_ID_CHARS).default("dsh"),
|
|
935
936
|
requestTimeoutMs: Schema.natural().max(6e4).default(1e4),
|
|
936
937
|
peers: Schema.dict(Schema.string()).default({}),
|
|
937
938
|
delivery: Schema.union([Schema.const("followup"), Schema.const("steer"), Schema.const("inject")]).default("followup"),
|
|
@@ -941,7 +942,6 @@ var InterconnectService = class extends Service {
|
|
|
941
942
|
requestTimeoutMs;
|
|
942
943
|
delivery;
|
|
943
944
|
allowResume;
|
|
944
|
-
subscriptions = [];
|
|
945
945
|
server = new WebSocketServer({ noServer: true, maxPayload: MAX_LINK_FRAME_BYTES });
|
|
946
946
|
sockets = /* @__PURE__ */ new Set();
|
|
947
947
|
/** Outbound peer links keyed by the peer's `instanceId`. */
|
|
@@ -951,6 +951,8 @@ var InterconnectService = class extends Service {
|
|
|
951
951
|
peerOf = /* @__PURE__ */ new WeakMap();
|
|
952
952
|
/** Configured peer an outbound dial owns a socket for; absent on inbound sockets. */
|
|
953
953
|
dialedPeerOf = /* @__PURE__ */ new WeakMap();
|
|
954
|
+
/** Dialed sockets already reported as announcing an id other than their configured key. */
|
|
955
|
+
mismatchWarned = /* @__PURE__ */ new WeakSet();
|
|
954
956
|
/** Sender each local session last received a send from, keyed by local session id. */
|
|
955
957
|
senders = /* @__PURE__ */ new Map();
|
|
956
958
|
/** In-flight frames sent over a peer link, keyed by `reqId`, awaiting a correlated result. */
|
|
@@ -998,28 +1000,28 @@ var InterconnectService = class extends Service {
|
|
|
998
1000
|
socket.ping();
|
|
999
1001
|
}
|
|
1000
1002
|
}, 3e4);
|
|
1001
|
-
|
|
1003
|
+
ctx.on("agent/status", ({ agent, status }) => {
|
|
1002
1004
|
this.fanout({ kind: "agent/status", sessionId: String(agent.session.id), status });
|
|
1003
|
-
})
|
|
1004
|
-
|
|
1005
|
+
});
|
|
1006
|
+
ctx.on("agent/created", ({ agent }) => {
|
|
1005
1007
|
this.fanout({ kind: "agent/created", sessionId: String(agent.session.id) });
|
|
1006
|
-
})
|
|
1007
|
-
|
|
1008
|
+
});
|
|
1009
|
+
ctx.on("agent/disposed", ({ agent }) => {
|
|
1008
1010
|
this.fanout({ kind: "agent/disposed", sessionId: String(agent.session.id) });
|
|
1009
|
-
})
|
|
1010
|
-
|
|
1011
|
+
});
|
|
1012
|
+
ctx.on("session/created", (session) => {
|
|
1011
1013
|
const parentSessionId = session.header.parentSession === void 0 ? void 0 : String(session.header.parentSession);
|
|
1012
1014
|
this.fanout({
|
|
1013
1015
|
kind: "session/created",
|
|
1014
1016
|
sessionId: String(session.id),
|
|
1015
1017
|
...parentSessionId === void 0 ? {} : { parentSessionId }
|
|
1016
1018
|
});
|
|
1017
|
-
})
|
|
1018
|
-
|
|
1019
|
+
});
|
|
1020
|
+
ctx.on("session/disposed", (session) => {
|
|
1019
1021
|
this.senders.delete(String(session.id));
|
|
1020
1022
|
this.fanout({ kind: "session/disposed", sessionId: String(session.id) });
|
|
1021
|
-
})
|
|
1022
|
-
|
|
1023
|
+
});
|
|
1024
|
+
ctx.on("subagent/end", (info) => {
|
|
1023
1025
|
if (!info.local) return;
|
|
1024
1026
|
this.fanout({
|
|
1025
1027
|
kind: "subagent/end",
|
|
@@ -1027,10 +1029,7 @@ var InterconnectService = class extends Service {
|
|
|
1027
1029
|
childSessionId: String(info.id),
|
|
1028
1030
|
stopReason: info.stopReason
|
|
1029
1031
|
});
|
|
1030
|
-
})
|
|
1031
|
-
ctx.effect(() => () => {
|
|
1032
|
-
for (const dispose of this.subscriptions.splice(0)) dispose();
|
|
1033
|
-
}, "interconnect: event subscriptions");
|
|
1032
|
+
});
|
|
1034
1033
|
ctx.effect(() => () => {
|
|
1035
1034
|
if (this.heartbeatTimer !== void 0) clearInterval(this.heartbeatTimer);
|
|
1036
1035
|
for (const state of this.linkStates.values()) state.close();
|
|
@@ -1220,10 +1219,12 @@ var InterconnectService = class extends Service {
|
|
|
1220
1219
|
* dialed link plus the peer's inbound one), so one event must leave over one
|
|
1221
1220
|
* of them. Which one is decided by local ownership, not by the peer: a dialed
|
|
1222
1221
|
* socket is a configured peer's link and always sends. An inbound socket is
|
|
1223
|
-
* skipped only when
|
|
1224
|
-
*
|
|
1225
|
-
*
|
|
1226
|
-
* peer
|
|
1222
|
+
* skipped only when this instance has an open dialed link configured under
|
|
1223
|
+
* the id that socket itself announced, so a self-reported announcement can
|
|
1224
|
+
* never silence another peer's link; with no such dialed link every inbound
|
|
1225
|
+
* socket receives the event. A peer listed under a key other than the id it
|
|
1226
|
+
* announces is warned about once per link, because then its own inbound
|
|
1227
|
+
* socket is not attributable and the peer receives each event twice.
|
|
1227
1228
|
*
|
|
1228
1229
|
* That holds once a peer's announcement has been seen. Between this
|
|
1229
1230
|
* instance's dialed link opening and the peer's `hello` arriving on its
|
|
@@ -1274,12 +1275,7 @@ var InterconnectService = class extends Service {
|
|
|
1274
1275
|
if (query.kind === "ping") {
|
|
1275
1276
|
result = { pong: true, instance: this.instanceId };
|
|
1276
1277
|
} else if (query.kind === "list") {
|
|
1277
|
-
|
|
1278
|
-
if (rowBudget < 0) {
|
|
1279
|
-
this.ctx.logger.warn("interconnect: list answer cannot fit the link frame cap; not answering");
|
|
1280
|
-
return;
|
|
1281
|
-
}
|
|
1282
|
-
result = this.listSessions(rowBudget);
|
|
1278
|
+
result = this.listSessions(this.listRowsBudgetBytes(reqId));
|
|
1283
1279
|
} else {
|
|
1284
1280
|
const payload = {
|
|
1285
1281
|
sender: this.peerOf.get(socket) ?? "unknown-peer",
|
|
@@ -1311,10 +1307,11 @@ var InterconnectService = class extends Service {
|
|
|
1311
1307
|
* Bytes one `list` answer may spend on its `sessions` rows: the frame cap
|
|
1312
1308
|
* minus the serialized envelope that answer will carry. The envelope holds
|
|
1313
1309
|
* the peer-supplied `reqId` and this instance's own id, so it is measured per
|
|
1314
|
-
* answer instead of reserved with a fixed constant.
|
|
1310
|
+
* answer instead of reserved with a fixed constant. Both ids are bounded
|
|
1311
|
+
* ({@link MAX_REQUEST_ID_CHARS} and {@link MAX_INSTANCE_ID_CHARS} characters),
|
|
1312
|
+
* so the envelope always fits and the budget is always positive.
|
|
1315
1313
|
* @param reqId - the request id the answer has to echo.
|
|
1316
|
-
* @returns the byte budget for the rows
|
|
1317
|
-
* exceeds the cap and so cannot be sent at all.
|
|
1314
|
+
* @returns the byte budget for the rows.
|
|
1318
1315
|
*/
|
|
1319
1316
|
listRowsBudgetBytes(reqId) {
|
|
1320
1317
|
const envelope = {
|
|
@@ -1579,6 +1576,13 @@ var InterconnectService = class extends Service {
|
|
|
1579
1576
|
const frame = parsed;
|
|
1580
1577
|
if (frame.type === "hello") {
|
|
1581
1578
|
this.peerOf.set(socket, frame.sender);
|
|
1579
|
+
const dialedPeer = this.dialedPeerOf.get(socket);
|
|
1580
|
+
if (dialedPeer !== void 0 && dialedPeer !== frame.sender && !this.mismatchWarned.has(socket)) {
|
|
1581
|
+
this.mismatchWarned.add(socket);
|
|
1582
|
+
this.ctx.logger.warn(
|
|
1583
|
+
`interconnect: peer configured as ${dialedPeer} announces ${frame.sender}; list a peer under the instanceId it announces, or the duplicate inbound link is not recognised`
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1582
1586
|
return;
|
|
1583
1587
|
}
|
|
1584
1588
|
if (frame.type === "event") {
|