dsh-interconnect 0.11.16 → 0.11.17

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-interconnect",
3
3
  "description": "Cross-instance message/event handoff for DeepSeek Harness (DSH): WebSocket link, model-facing tools, and companion skill.",
4
- "version": "0.11.16",
4
+ "version": "0.11.17",
5
5
  "entry": {
6
6
  "name": "dsh-interconnect/interconnect",
7
7
  "inject": ["agents", "credentials"]
package/lib/index.js CHANGED
@@ -835,6 +835,7 @@ var PLUGIN_SOURCE = "dsh-interconnect";
835
835
  var MAX_LINK_FRAME_BYTES = 1024 * 1024;
836
836
  var LINK_HANDSHAKE_TIMEOUT_MS = 1e4;
837
837
  var LINK_CHANNEL = "/interconnect/link";
838
+ var MAX_INSTANCE_ID_CHARS = 256;
838
839
  var MAX_REQUEST_ID_CHARS = 256;
839
840
  var MAX_LISTED_SESSIONS = 100;
840
841
  var frameFitsLink = (frame) => Buffer.byteLength(JSON.stringify(frame), "utf8") <= MAX_LINK_FRAME_BYTES;
@@ -928,7 +929,7 @@ var linkFrameSchema = Schema.union([
928
929
  var InterconnectService = class extends Service {
929
930
  static inject = ["agents", "credentials"];
930
931
  static Config = Schema.object({
931
- instanceId: Schema.string().default("dsh"),
932
+ instanceId: Schema.string().max(MAX_INSTANCE_ID_CHARS).default("dsh"),
932
933
  requestTimeoutMs: Schema.natural().max(6e4).default(1e4),
933
934
  peers: Schema.dict(Schema.string()).default({}),
934
935
  delivery: Schema.union([Schema.const("followup"), Schema.const("steer"), Schema.const("inject")]).default("followup"),
@@ -938,7 +939,6 @@ var InterconnectService = class extends Service {
938
939
  requestTimeoutMs;
939
940
  delivery;
940
941
  allowResume;
941
- subscriptions = [];
942
942
  server = new WebSocketServer({ noServer: true, maxPayload: MAX_LINK_FRAME_BYTES });
943
943
  sockets = /* @__PURE__ */ new Set();
944
944
  /** Outbound peer links keyed by the peer's `instanceId`. */
@@ -995,28 +995,28 @@ var InterconnectService = class extends Service {
995
995
  socket.ping();
996
996
  }
997
997
  }, 3e4);
998
- this.subscriptions.push(ctx.on("agent/status", ({ agent, status }) => {
998
+ ctx.on("agent/status", ({ agent, status }) => {
999
999
  this.fanout({ kind: "agent/status", sessionId: String(agent.session.id), status });
1000
- }));
1001
- this.subscriptions.push(ctx.on("agent/created", ({ agent }) => {
1000
+ });
1001
+ ctx.on("agent/created", ({ agent }) => {
1002
1002
  this.fanout({ kind: "agent/created", sessionId: String(agent.session.id) });
1003
- }));
1004
- this.subscriptions.push(ctx.on("agent/disposed", ({ agent }) => {
1003
+ });
1004
+ ctx.on("agent/disposed", ({ agent }) => {
1005
1005
  this.fanout({ kind: "agent/disposed", sessionId: String(agent.session.id) });
1006
- }));
1007
- this.subscriptions.push(ctx.on("session/created", (session) => {
1006
+ });
1007
+ ctx.on("session/created", (session) => {
1008
1008
  const parentSessionId = session.header.parentSession === void 0 ? void 0 : String(session.header.parentSession);
1009
1009
  this.fanout({
1010
1010
  kind: "session/created",
1011
1011
  sessionId: String(session.id),
1012
1012
  ...parentSessionId === void 0 ? {} : { parentSessionId }
1013
1013
  });
1014
- }));
1015
- this.subscriptions.push(ctx.on("session/disposed", (session) => {
1014
+ });
1015
+ ctx.on("session/disposed", (session) => {
1016
1016
  this.senders.delete(String(session.id));
1017
1017
  this.fanout({ kind: "session/disposed", sessionId: String(session.id) });
1018
- }));
1019
- this.subscriptions.push(ctx.on("subagent/end", (info) => {
1018
+ });
1019
+ ctx.on("subagent/end", (info) => {
1020
1020
  if (!info.local) return;
1021
1021
  this.fanout({
1022
1022
  kind: "subagent/end",
@@ -1024,10 +1024,7 @@ var InterconnectService = class extends Service {
1024
1024
  childSessionId: String(info.id),
1025
1025
  stopReason: info.stopReason
1026
1026
  });
1027
- }));
1028
- ctx.effect(() => () => {
1029
- for (const dispose of this.subscriptions.splice(0)) dispose();
1030
- }, "interconnect: event subscriptions");
1027
+ });
1031
1028
  ctx.effect(() => () => {
1032
1029
  if (this.heartbeatTimer !== void 0) clearInterval(this.heartbeatTimer);
1033
1030
  for (const state of this.linkStates.values()) state.close();
@@ -1217,10 +1214,12 @@ var InterconnectService = class extends Service {
1217
1214
  * dialed link plus the peer's inbound one), so one event must leave over one
1218
1215
  * of them. Which one is decided by local ownership, not by the peer: a dialed
1219
1216
  * socket is a configured peer's link and always sends. An inbound socket is
1220
- * skipped only when a dialed link to the id that socket itself announced is
1221
- * open, so an announcement can suppress nothing but a duplicate the
1222
- * controlled link already carries — an impostor cannot drop an event for a
1223
- * peer, and without a dialed link every inbound socket receives it.
1217
+ * skipped only when an open dialed link covers the id that socket itself
1218
+ * announced — either because `Config.peers` lists that peer under the id, or
1219
+ * because the peer announced the id over that dialed link — so an
1220
+ * announcement can suppress nothing but a duplicate the controlled link
1221
+ * already carries. An impostor cannot drop an event for a peer, and without a
1222
+ * dialed link every inbound socket receives it.
1224
1223
  *
1225
1224
  * That holds once a peer's announcement has been seen. Between this
1226
1225
  * instance's dialed link opening and the peer's `hello` arriving on its
@@ -1239,7 +1238,10 @@ var InterconnectService = class extends Service {
1239
1238
  continue;
1240
1239
  }
1241
1240
  const dialedPeer = this.dialedPeerOf.get(socket);
1242
- if (dialedPeer !== void 0) covered.add(dialedPeer);
1241
+ if (dialedPeer === void 0) continue;
1242
+ covered.add(dialedPeer);
1243
+ const announced = this.peerOf.get(socket);
1244
+ if (announced !== void 0) covered.add(announced);
1243
1245
  }
1244
1246
  for (const socket of this.sockets) {
1245
1247
  if (this.dialedPeerOf.get(socket) === void 0) {
@@ -1271,12 +1273,7 @@ var InterconnectService = class extends Service {
1271
1273
  if (query.kind === "ping") {
1272
1274
  result = { pong: true, instance: this.instanceId };
1273
1275
  } else if (query.kind === "list") {
1274
- const rowBudget = this.listRowsBudgetBytes(reqId);
1275
- if (rowBudget < 0) {
1276
- this.ctx.logger.warn("interconnect: list answer cannot fit the link frame cap; not answering");
1277
- return;
1278
- }
1279
- result = this.listSessions(rowBudget);
1276
+ result = this.listSessions(this.listRowsBudgetBytes(reqId));
1280
1277
  } else {
1281
1278
  const payload = {
1282
1279
  sender: this.peerOf.get(socket) ?? "unknown-peer",
@@ -1308,10 +1305,11 @@ var InterconnectService = class extends Service {
1308
1305
  * Bytes one `list` answer may spend on its `sessions` rows: the frame cap
1309
1306
  * minus the serialized envelope that answer will carry. The envelope holds
1310
1307
  * the peer-supplied `reqId` and this instance's own id, so it is measured per
1311
- * answer instead of reserved with a fixed constant.
1308
+ * answer instead of reserved with a fixed constant. Both ids are bounded
1309
+ * ({@link MAX_REQUEST_ID_CHARS} and {@link MAX_INSTANCE_ID_CHARS} characters),
1310
+ * so the envelope always fits and the budget is always positive.
1312
1311
  * @param reqId - the request id the answer has to echo.
1313
- * @returns the byte budget for the rows, negative when even an empty answer
1314
- * exceeds the cap and so cannot be sent at all.
1312
+ * @returns the byte budget for the rows.
1315
1313
  */
1316
1314
  listRowsBudgetBytes(reqId) {
1317
1315
  const envelope = {
@@ -835,6 +835,7 @@ var PLUGIN_SOURCE = "dsh-interconnect";
835
835
  var MAX_LINK_FRAME_BYTES = 1024 * 1024;
836
836
  var LINK_HANDSHAKE_TIMEOUT_MS = 1e4;
837
837
  var LINK_CHANNEL = "/interconnect/link";
838
+ var MAX_INSTANCE_ID_CHARS = 256;
838
839
  var MAX_REQUEST_ID_CHARS = 256;
839
840
  var MAX_LISTED_SESSIONS = 100;
840
841
  var frameFitsLink = (frame) => Buffer.byteLength(JSON.stringify(frame), "utf8") <= MAX_LINK_FRAME_BYTES;
@@ -928,7 +929,7 @@ var linkFrameSchema = Schema.union([
928
929
  var InterconnectService = class extends Service {
929
930
  static inject = ["agents", "credentials"];
930
931
  static Config = Schema.object({
931
- instanceId: Schema.string().default("dsh"),
932
+ instanceId: Schema.string().max(MAX_INSTANCE_ID_CHARS).default("dsh"),
932
933
  requestTimeoutMs: Schema.natural().max(6e4).default(1e4),
933
934
  peers: Schema.dict(Schema.string()).default({}),
934
935
  delivery: Schema.union([Schema.const("followup"), Schema.const("steer"), Schema.const("inject")]).default("followup"),
@@ -938,7 +939,6 @@ var InterconnectService = class extends Service {
938
939
  requestTimeoutMs;
939
940
  delivery;
940
941
  allowResume;
941
- subscriptions = [];
942
942
  server = new WebSocketServer({ noServer: true, maxPayload: MAX_LINK_FRAME_BYTES });
943
943
  sockets = /* @__PURE__ */ new Set();
944
944
  /** Outbound peer links keyed by the peer's `instanceId`. */
@@ -995,28 +995,28 @@ var InterconnectService = class extends Service {
995
995
  socket.ping();
996
996
  }
997
997
  }, 3e4);
998
- this.subscriptions.push(ctx.on("agent/status", ({ agent, status }) => {
998
+ ctx.on("agent/status", ({ agent, status }) => {
999
999
  this.fanout({ kind: "agent/status", sessionId: String(agent.session.id), status });
1000
- }));
1001
- this.subscriptions.push(ctx.on("agent/created", ({ agent }) => {
1000
+ });
1001
+ ctx.on("agent/created", ({ agent }) => {
1002
1002
  this.fanout({ kind: "agent/created", sessionId: String(agent.session.id) });
1003
- }));
1004
- this.subscriptions.push(ctx.on("agent/disposed", ({ agent }) => {
1003
+ });
1004
+ ctx.on("agent/disposed", ({ agent }) => {
1005
1005
  this.fanout({ kind: "agent/disposed", sessionId: String(agent.session.id) });
1006
- }));
1007
- this.subscriptions.push(ctx.on("session/created", (session) => {
1006
+ });
1007
+ ctx.on("session/created", (session) => {
1008
1008
  const parentSessionId = session.header.parentSession === void 0 ? void 0 : String(session.header.parentSession);
1009
1009
  this.fanout({
1010
1010
  kind: "session/created",
1011
1011
  sessionId: String(session.id),
1012
1012
  ...parentSessionId === void 0 ? {} : { parentSessionId }
1013
1013
  });
1014
- }));
1015
- this.subscriptions.push(ctx.on("session/disposed", (session) => {
1014
+ });
1015
+ ctx.on("session/disposed", (session) => {
1016
1016
  this.senders.delete(String(session.id));
1017
1017
  this.fanout({ kind: "session/disposed", sessionId: String(session.id) });
1018
- }));
1019
- this.subscriptions.push(ctx.on("subagent/end", (info) => {
1018
+ });
1019
+ ctx.on("subagent/end", (info) => {
1020
1020
  if (!info.local) return;
1021
1021
  this.fanout({
1022
1022
  kind: "subagent/end",
@@ -1024,10 +1024,7 @@ var InterconnectService = class extends Service {
1024
1024
  childSessionId: String(info.id),
1025
1025
  stopReason: info.stopReason
1026
1026
  });
1027
- }));
1028
- ctx.effect(() => () => {
1029
- for (const dispose of this.subscriptions.splice(0)) dispose();
1030
- }, "interconnect: event subscriptions");
1027
+ });
1031
1028
  ctx.effect(() => () => {
1032
1029
  if (this.heartbeatTimer !== void 0) clearInterval(this.heartbeatTimer);
1033
1030
  for (const state of this.linkStates.values()) state.close();
@@ -1217,10 +1214,12 @@ var InterconnectService = class extends Service {
1217
1214
  * dialed link plus the peer's inbound one), so one event must leave over one
1218
1215
  * of them. Which one is decided by local ownership, not by the peer: a dialed
1219
1216
  * socket is a configured peer's link and always sends. An inbound socket is
1220
- * skipped only when a dialed link to the id that socket itself announced is
1221
- * open, so an announcement can suppress nothing but a duplicate the
1222
- * controlled link already carries — an impostor cannot drop an event for a
1223
- * peer, and without a dialed link every inbound socket receives it.
1217
+ * skipped only when an open dialed link covers the id that socket itself
1218
+ * announced — either because `Config.peers` lists that peer under the id, or
1219
+ * because the peer announced the id over that dialed link — so an
1220
+ * announcement can suppress nothing but a duplicate the controlled link
1221
+ * already carries. An impostor cannot drop an event for a peer, and without a
1222
+ * dialed link every inbound socket receives it.
1224
1223
  *
1225
1224
  * That holds once a peer's announcement has been seen. Between this
1226
1225
  * instance's dialed link opening and the peer's `hello` arriving on its
@@ -1239,7 +1238,10 @@ var InterconnectService = class extends Service {
1239
1238
  continue;
1240
1239
  }
1241
1240
  const dialedPeer = this.dialedPeerOf.get(socket);
1242
- if (dialedPeer !== void 0) covered.add(dialedPeer);
1241
+ if (dialedPeer === void 0) continue;
1242
+ covered.add(dialedPeer);
1243
+ const announced = this.peerOf.get(socket);
1244
+ if (announced !== void 0) covered.add(announced);
1243
1245
  }
1244
1246
  for (const socket of this.sockets) {
1245
1247
  if (this.dialedPeerOf.get(socket) === void 0) {
@@ -1271,12 +1273,7 @@ var InterconnectService = class extends Service {
1271
1273
  if (query.kind === "ping") {
1272
1274
  result = { pong: true, instance: this.instanceId };
1273
1275
  } else if (query.kind === "list") {
1274
- const rowBudget = this.listRowsBudgetBytes(reqId);
1275
- if (rowBudget < 0) {
1276
- this.ctx.logger.warn("interconnect: list answer cannot fit the link frame cap; not answering");
1277
- return;
1278
- }
1279
- result = this.listSessions(rowBudget);
1276
+ result = this.listSessions(this.listRowsBudgetBytes(reqId));
1280
1277
  } else {
1281
1278
  const payload = {
1282
1279
  sender: this.peerOf.get(socket) ?? "unknown-peer",
@@ -1308,10 +1305,11 @@ var InterconnectService = class extends Service {
1308
1305
  * Bytes one `list` answer may spend on its `sessions` rows: the frame cap
1309
1306
  * minus the serialized envelope that answer will carry. The envelope holds
1310
1307
  * the peer-supplied `reqId` and this instance's own id, so it is measured per
1311
- * answer instead of reserved with a fixed constant.
1308
+ * answer instead of reserved with a fixed constant. Both ids are bounded
1309
+ * ({@link MAX_REQUEST_ID_CHARS} and {@link MAX_INSTANCE_ID_CHARS} characters),
1310
+ * so the envelope always fits and the budget is always positive.
1312
1311
  * @param reqId - the request id the answer has to echo.
1313
- * @returns the byte budget for the rows, negative when even an empty answer
1314
- * exceeds the cap and so cannot be sent at all.
1312
+ * @returns the byte budget for the rows.
1315
1313
  */
1316
1314
  listRowsBudgetBytes(reqId) {
1317
1315
  const envelope = {