dsh-interconnect 0.11.3 → 0.11.5

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.
@@ -20,9 +20,9 @@ job is only to pick the right tool and the right target.
20
20
  instance/session identity is attached automatically by the tool, so you must
21
21
  NOT pass a sender parameter.
22
22
  - `interconnect_reply` — send a message back to the peer that this session last
23
- received an interconnect message from. Pass only the reply text: the replying
23
+ received an interconnect message from. Pass only the reply text. The replying
24
24
  session is this agent's own session, and the remote target is recalled
25
- automatically, so do not try to look up or pass the sender address again.
25
+ automatically; do not try to look up or pass an address again.
26
26
 
27
27
  ## Sender identity is automatic
28
28
 
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.3",
4
+ "version": "0.11.5",
5
5
  "entry": {
6
6
  "name": "dsh-interconnect/interconnect",
7
7
  "inject": ["agents", "credentials"]
package/lib/index.js CHANGED
@@ -859,14 +859,31 @@ var withoutNullFields = (value) => {
859
859
  return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== null).map(([key, field]) => [key, withoutNullFields(field)]));
860
860
  };
861
861
  var projectMsgResult = (result) => result.delivered ? { delivered: true, instance: result.instance, ...result.delivery === void 0 ? {} : { delivery: result.delivery } } : { delivered: false, instance: result.instance, reason: result.reason };
862
- var acceptsQueryResult = (schema, result) => {
862
+ var parseQueryResult = (schema, result) => {
863
863
  try {
864
864
  const [parsed] = Schema.resolve(result, schema, {});
865
- return parsed !== void 0 && parsed !== null;
865
+ return parsed;
866
866
  } catch {
867
- return false;
867
+ return void 0;
868
868
  }
869
869
  };
870
+ var projectPingResult = (result) => {
871
+ const parsed = parseQueryResult(pingResultSchema, result);
872
+ return parsed === void 0 ? void 0 : { pong: true, instance: parsed.instance };
873
+ };
874
+ var projectListResult = (result) => {
875
+ const parsed = parseQueryResult(listResultSchema, result);
876
+ if (parsed === void 0) return void 0;
877
+ const rows = parsed.sessions;
878
+ return {
879
+ instance: parsed.instance,
880
+ sessions: rows.map((row) => ({
881
+ sessionId: row.sessionId,
882
+ ...row.title === void 0 || row.title === null ? {} : { title: row.title },
883
+ ...row.status === void 0 || row.status === null ? {} : { status: row.status }
884
+ }))
885
+ };
886
+ };
870
887
  var queryResultSchema = Schema.union([
871
888
  pingResultSchema,
872
889
  listResultSchema,
@@ -1125,7 +1142,7 @@ var InterconnectService = class extends Service {
1125
1142
  const state = this.linkStates.get(instanceId);
1126
1143
  if (state === void 0 || !state.writable()) return void 0;
1127
1144
  const reqId = `q${++this.reqIdCounter}-${randomUUID()}`;
1128
- const accept = query.kind === "ping" ? (result) => acceptsQueryResult(pingResultSchema, result) : (result) => acceptsQueryResult(listResultSchema, result);
1145
+ const accept = query.kind === "ping" ? projectPingResult : projectListResult;
1129
1146
  try {
1130
1147
  return await this.waitForResult(reqId, "query", (failure) => {
1131
1148
  const wrote = state.sendFrame({ type: "query", reqId, query });
@@ -1142,16 +1159,12 @@ var InterconnectService = class extends Service {
1142
1159
  this.pendingMessages.delete(reqId);
1143
1160
  reject(new Error(`interconnect: no result for ${reqId} within ${this.requestTimeoutMs}ms`));
1144
1161
  }, this.requestTimeoutMs);
1145
- this.pendingMessages.set(reqId, {
1146
- timer,
1147
- kind,
1148
- ...accept === void 0 ? {} : { accept },
1149
- resolve: (result) => {
1150
- clearTimeout(timer);
1151
- this.pendingMessages.delete(reqId);
1152
- resolve2(result);
1153
- }
1154
- });
1162
+ const settle = (result) => {
1163
+ clearTimeout(timer);
1164
+ this.pendingMessages.delete(reqId);
1165
+ resolve2(result);
1166
+ };
1167
+ this.pendingMessages.set(reqId, kind === "query" ? { timer, kind, accept, resolve: settle } : { timer, kind, resolve: settle });
1155
1168
  send((error) => {
1156
1169
  clearTimeout(timer);
1157
1170
  this.pendingMessages.delete(reqId);
@@ -1395,6 +1408,19 @@ var InterconnectService = class extends Service {
1395
1408
  const credential = await this.ctx.credentials.resolve(credentialRef(INTERCONNECT_TOKEN_REF));
1396
1409
  return credential === void 0 || credential.value.length === 0 ? void 0 : credential.value;
1397
1410
  }
1411
+ /**
1412
+ * Answer one refused upgrade and close its socket. `socket.end()` alone only
1413
+ * half-closes the connection: a client that keeps its own writing side open
1414
+ * (a socket created with `allowHalfOpen`) leaves the connection live, and a
1415
+ * refused socket never enters the link pool, so nothing else would reap it.
1416
+ * @param socket - the refused upgrade socket.
1417
+ * @param response - the complete HTTP response sent to the client.
1418
+ */
1419
+ refuseUpgrade(socket, response) {
1420
+ socket.end(response, () => {
1421
+ socket.destroy();
1422
+ });
1423
+ }
1398
1424
  /** Inbound WebSocket upgrade: authenticate the bearer header, then accept. */
1399
1425
  async handleUpgrade(req, socket, head) {
1400
1426
  let token;
@@ -1402,21 +1428,21 @@ var InterconnectService = class extends Service {
1402
1428
  token = await this.resolveToken();
1403
1429
  } catch (error) {
1404
1430
  this.ctx.logger.warn(`interconnect: upgrade token read failed: ${error instanceof Error ? error.message : String(error)}`);
1405
- socket.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1431
+ this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1406
1432
  return;
1407
1433
  }
1408
1434
  if (token === void 0) {
1409
- socket.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1435
+ this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1410
1436
  return;
1411
1437
  }
1412
1438
  if (this.disposed) {
1413
- socket.end("HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
1439
+ this.refuseUpgrade(socket, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
1414
1440
  return;
1415
1441
  }
1416
1442
  const expected = `Bearer ${token}`;
1417
1443
  const header = req.headers.authorization;
1418
1444
  if (header === void 0 || !timingSafeEqual(header, expected)) {
1419
- socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
1445
+ this.refuseUpgrade(socket, "HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
1420
1446
  return;
1421
1447
  }
1422
1448
  this.server.handleUpgrade(req, socket, head, (websocket) => {
@@ -1497,9 +1523,9 @@ var InterconnectService = class extends Service {
1497
1523
  }
1498
1524
  if (frame.type === "query-result") {
1499
1525
  const pending = this.pendingMessages.get(frame.reqId);
1500
- if (pending !== void 0 && pending.kind === "query" && (pending.accept === void 0 || pending.accept(frame.result))) {
1501
- ;
1502
- pending.resolve?.(frame.result);
1526
+ if (pending !== void 0 && pending.kind === "query") {
1527
+ const answer = pending.accept(frame.result);
1528
+ if (answer !== void 0) pending.resolve?.(answer);
1503
1529
  }
1504
1530
  return;
1505
1531
  }
@@ -859,14 +859,31 @@ var withoutNullFields = (value) => {
859
859
  return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== null).map(([key, field]) => [key, withoutNullFields(field)]));
860
860
  };
861
861
  var projectMsgResult = (result) => result.delivered ? { delivered: true, instance: result.instance, ...result.delivery === void 0 ? {} : { delivery: result.delivery } } : { delivered: false, instance: result.instance, reason: result.reason };
862
- var acceptsQueryResult = (schema, result) => {
862
+ var parseQueryResult = (schema, result) => {
863
863
  try {
864
864
  const [parsed] = Schema.resolve(result, schema, {});
865
- return parsed !== void 0 && parsed !== null;
865
+ return parsed;
866
866
  } catch {
867
- return false;
867
+ return void 0;
868
868
  }
869
869
  };
870
+ var projectPingResult = (result) => {
871
+ const parsed = parseQueryResult(pingResultSchema, result);
872
+ return parsed === void 0 ? void 0 : { pong: true, instance: parsed.instance };
873
+ };
874
+ var projectListResult = (result) => {
875
+ const parsed = parseQueryResult(listResultSchema, result);
876
+ if (parsed === void 0) return void 0;
877
+ const rows = parsed.sessions;
878
+ return {
879
+ instance: parsed.instance,
880
+ sessions: rows.map((row) => ({
881
+ sessionId: row.sessionId,
882
+ ...row.title === void 0 || row.title === null ? {} : { title: row.title },
883
+ ...row.status === void 0 || row.status === null ? {} : { status: row.status }
884
+ }))
885
+ };
886
+ };
870
887
  var queryResultSchema = Schema.union([
871
888
  pingResultSchema,
872
889
  listResultSchema,
@@ -1125,7 +1142,7 @@ var InterconnectService = class extends Service {
1125
1142
  const state = this.linkStates.get(instanceId);
1126
1143
  if (state === void 0 || !state.writable()) return void 0;
1127
1144
  const reqId = `q${++this.reqIdCounter}-${randomUUID()}`;
1128
- const accept = query.kind === "ping" ? (result) => acceptsQueryResult(pingResultSchema, result) : (result) => acceptsQueryResult(listResultSchema, result);
1145
+ const accept = query.kind === "ping" ? projectPingResult : projectListResult;
1129
1146
  try {
1130
1147
  return await this.waitForResult(reqId, "query", (failure) => {
1131
1148
  const wrote = state.sendFrame({ type: "query", reqId, query });
@@ -1142,16 +1159,12 @@ var InterconnectService = class extends Service {
1142
1159
  this.pendingMessages.delete(reqId);
1143
1160
  reject(new Error(`interconnect: no result for ${reqId} within ${this.requestTimeoutMs}ms`));
1144
1161
  }, this.requestTimeoutMs);
1145
- this.pendingMessages.set(reqId, {
1146
- timer,
1147
- kind,
1148
- ...accept === void 0 ? {} : { accept },
1149
- resolve: (result) => {
1150
- clearTimeout(timer);
1151
- this.pendingMessages.delete(reqId);
1152
- resolve2(result);
1153
- }
1154
- });
1162
+ const settle = (result) => {
1163
+ clearTimeout(timer);
1164
+ this.pendingMessages.delete(reqId);
1165
+ resolve2(result);
1166
+ };
1167
+ this.pendingMessages.set(reqId, kind === "query" ? { timer, kind, accept, resolve: settle } : { timer, kind, resolve: settle });
1155
1168
  send((error) => {
1156
1169
  clearTimeout(timer);
1157
1170
  this.pendingMessages.delete(reqId);
@@ -1395,6 +1408,19 @@ var InterconnectService = class extends Service {
1395
1408
  const credential = await this.ctx.credentials.resolve(credentialRef(INTERCONNECT_TOKEN_REF));
1396
1409
  return credential === void 0 || credential.value.length === 0 ? void 0 : credential.value;
1397
1410
  }
1411
+ /**
1412
+ * Answer one refused upgrade and close its socket. `socket.end()` alone only
1413
+ * half-closes the connection: a client that keeps its own writing side open
1414
+ * (a socket created with `allowHalfOpen`) leaves the connection live, and a
1415
+ * refused socket never enters the link pool, so nothing else would reap it.
1416
+ * @param socket - the refused upgrade socket.
1417
+ * @param response - the complete HTTP response sent to the client.
1418
+ */
1419
+ refuseUpgrade(socket, response) {
1420
+ socket.end(response, () => {
1421
+ socket.destroy();
1422
+ });
1423
+ }
1398
1424
  /** Inbound WebSocket upgrade: authenticate the bearer header, then accept. */
1399
1425
  async handleUpgrade(req, socket, head) {
1400
1426
  let token;
@@ -1402,21 +1428,21 @@ var InterconnectService = class extends Service {
1402
1428
  token = await this.resolveToken();
1403
1429
  } catch (error) {
1404
1430
  this.ctx.logger.warn(`interconnect: upgrade token read failed: ${error instanceof Error ? error.message : String(error)}`);
1405
- socket.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1431
+ this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1406
1432
  return;
1407
1433
  }
1408
1434
  if (token === void 0) {
1409
- socket.end("HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1435
+ this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
1410
1436
  return;
1411
1437
  }
1412
1438
  if (this.disposed) {
1413
- socket.end("HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
1439
+ this.refuseUpgrade(socket, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
1414
1440
  return;
1415
1441
  }
1416
1442
  const expected = `Bearer ${token}`;
1417
1443
  const header = req.headers.authorization;
1418
1444
  if (header === void 0 || !timingSafeEqual(header, expected)) {
1419
- socket.end("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
1445
+ this.refuseUpgrade(socket, "HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
1420
1446
  return;
1421
1447
  }
1422
1448
  this.server.handleUpgrade(req, socket, head, (websocket) => {
@@ -1497,9 +1523,9 @@ var InterconnectService = class extends Service {
1497
1523
  }
1498
1524
  if (frame.type === "query-result") {
1499
1525
  const pending = this.pendingMessages.get(frame.reqId);
1500
- if (pending !== void 0 && pending.kind === "query" && (pending.accept === void 0 || pending.accept(frame.result))) {
1501
- ;
1502
- pending.resolve?.(frame.result);
1526
+ if (pending !== void 0 && pending.kind === "query") {
1527
+ const answer = pending.accept(frame.result);
1528
+ if (answer !== void 0) pending.resolve?.(answer);
1503
1529
  }
1504
1530
  return;
1505
1531
  }