dsh-interconnect 0.13.0 → 0.15.0
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/README.md +39 -10
- package/dsh.plugin.json +1 -1
- package/lib/index.js +58 -18
- package/lib/interconnect/index.js +58 -18
- package/lib/interconnect/index.js.map +2 -2
- package/lib/tool-interconnect/index.js +58 -18
- package/lib/tool-interconnect/index.js.map +2 -2
- package/lib/types/interconnect/index.d.ts +13 -1
- package/lib/types/interconnect/index.d.ts.map +1 -1
- package/lib/types/interconnect/types.d.ts +17 -6
- package/lib/types/interconnect/types.d.ts.map +1 -1
- package/package.json +1 -7
- package/lib/interconnect/invariant.js +0 -25
- package/lib/interconnect/invariant.js.map +0 -7
- package/lib/skill-interconnect/invariant.js +0 -13
- package/lib/skill-interconnect/invariant.js.map +0 -7
- package/lib/tool-interconnect/invariant.js +0 -13
- package/lib/tool-interconnect/invariant.js.map +0 -7
- package/lib/types/interconnect/invariant.d.ts +0 -16
- package/lib/types/interconnect/invariant.d.ts.map +0 -1
- package/lib/types/skill-interconnect/invariant.d.ts +0 -16
- package/lib/types/skill-interconnect/invariant.d.ts.map +0 -1
- package/lib/types/tool-interconnect/invariant.d.ts +0 -16
- package/lib/types/tool-interconnect/invariant.d.ts.map +0 -1
|
@@ -823,12 +823,8 @@ defineMethod("transform", [
|
|
|
823
823
|
|
|
824
824
|
// src/interconnect/index.ts
|
|
825
825
|
import WebSocket, { WebSocketServer } from "ws";
|
|
826
|
-
function isSessionOwnedBySubagent(
|
|
827
|
-
|
|
828
|
-
const parentId = session.header.parentSession;
|
|
829
|
-
if (parentId === void 0 || agent === void 0) return false;
|
|
830
|
-
const parent = ctx.agents.get(parentId);
|
|
831
|
-
return parent !== void 0 && ctx.agents.isOwnedBy(agent.id, parent);
|
|
826
|
+
function isSessionOwnedBySubagent(session) {
|
|
827
|
+
return session.header.origin === "subagent";
|
|
832
828
|
}
|
|
833
829
|
function assertNever(value) {
|
|
834
830
|
throw new Error(`interconnect: unhandled variant ${JSON.stringify(value)}`);
|
|
@@ -839,6 +835,7 @@ var DEFAULT_INSTANCE_ID = "dsh";
|
|
|
839
835
|
var DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
|
|
840
836
|
var LINK_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
841
837
|
var LINK_CHANNEL = "/interconnect/link";
|
|
838
|
+
var SELF_PEER_ORIGIN = "self";
|
|
842
839
|
var MAX_INSTANCE_ID_CHARS = 256;
|
|
843
840
|
var MAX_REQUEST_ID_CHARS = 256;
|
|
844
841
|
var MAX_LISTED_SESSIONS = 100;
|
|
@@ -966,11 +963,19 @@ var InterconnectService = class extends Service {
|
|
|
966
963
|
this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
967
964
|
this.delivery = config.delivery ?? "followup";
|
|
968
965
|
this.allowResume = config.allowResume ?? true;
|
|
969
|
-
for (const origin of Object.
|
|
966
|
+
for (const [peerInstanceId, origin] of Object.entries(config.peers ?? {})) {
|
|
967
|
+
if (origin === SELF_PEER_ORIGIN) {
|
|
968
|
+
if (peerInstanceId !== this.instanceId) {
|
|
969
|
+
throw new Error(
|
|
970
|
+
`interconnect: peer "${peerInstanceId}" is configured as self, but this instance announces "${this.instanceId}"; key a self peer by the instanceId`
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
970
975
|
linkUrl(trimBase(origin));
|
|
971
976
|
}
|
|
972
977
|
for (const [peerInstanceId, origin] of Object.entries(config.peers ?? {})) {
|
|
973
|
-
this.link(peerInstanceId, origin);
|
|
978
|
+
this.link(peerInstanceId, origin === SELF_PEER_ORIGIN ? () => this.selfOrigin() : origin);
|
|
974
979
|
}
|
|
975
980
|
const upgrade = {
|
|
976
981
|
path: LINK_CHANNEL,
|
|
@@ -1213,7 +1218,8 @@ var InterconnectService = class extends Service {
|
|
|
1213
1218
|
* `Config.peers` entry; the resulting state owns reconnection for the life of
|
|
1214
1219
|
* the service fiber.
|
|
1215
1220
|
* @param instanceId - the peer's `instanceId`.
|
|
1216
|
-
* @param origin - receiver origin this instance dials
|
|
1221
|
+
* @param origin - receiver origin this instance dials (e.g.
|
|
1222
|
+
* `http://127.0.0.1:13080`), or a resolver for a `self` route.
|
|
1217
1223
|
*/
|
|
1218
1224
|
link(instanceId, origin) {
|
|
1219
1225
|
const state = new LinkState(
|
|
@@ -1223,11 +1229,28 @@ var InterconnectService = class extends Service {
|
|
|
1223
1229
|
this.ctx.logger,
|
|
1224
1230
|
() => this.resolveToken(),
|
|
1225
1231
|
instanceId,
|
|
1226
|
-
trimBase(origin)
|
|
1232
|
+
typeof origin === "string" ? trimBase(origin) : origin
|
|
1227
1233
|
);
|
|
1228
1234
|
this.linkStates.set(instanceId, state);
|
|
1229
1235
|
state.dial();
|
|
1230
1236
|
}
|
|
1237
|
+
/**
|
|
1238
|
+
* The origin of this instance's own host webserver, or undefined while no
|
|
1239
|
+
* listening port exists. Read on every dial rather than cached: the
|
|
1240
|
+
* webserver may activate after this service, its port is assigned only in
|
|
1241
|
+
* its listen callback (`--port 0` picks a fresh one per start), and a
|
|
1242
|
+
* replacement webserver would move it again. Loopback is used regardless of
|
|
1243
|
+
* the bind host: a server bound to `0.0.0.0` also accepts `127.0.0.1`, while
|
|
1244
|
+
* `0.0.0.0` itself is a bind address, not a dialable origin.
|
|
1245
|
+
* @returns `http://127.0.0.1:<port>`, or undefined without a listening server.
|
|
1246
|
+
*/
|
|
1247
|
+
selfOrigin() {
|
|
1248
|
+
const server = this.ctx.get("webServer");
|
|
1249
|
+
if (server === void 0) return void 0;
|
|
1250
|
+
const port = server.port;
|
|
1251
|
+
if (typeof port !== "number" || port <= 0) return void 0;
|
|
1252
|
+
return `http://127.0.0.1:${String(port)}`;
|
|
1253
|
+
}
|
|
1231
1254
|
/** Push one serialized lifecycle fact out to every linked peer over WS. */
|
|
1232
1255
|
fanout(notification) {
|
|
1233
1256
|
this.broadcast(notification);
|
|
@@ -1362,7 +1385,7 @@ var InterconnectService = class extends Service {
|
|
|
1362
1385
|
* @returns the bounded listing.
|
|
1363
1386
|
*/
|
|
1364
1387
|
listSessions(rowBudgetBytes) {
|
|
1365
|
-
const reachable = this.ctx.agents.list().filter((agent) => !isSessionOwnedBySubagent(
|
|
1388
|
+
const reachable = this.ctx.agents.list().filter((agent) => !isSessionOwnedBySubagent(agent.session));
|
|
1366
1389
|
const sessions = [];
|
|
1367
1390
|
let used = 0;
|
|
1368
1391
|
for (const agent of reachable.slice(0, MAX_LISTED_SESSIONS)) {
|
|
@@ -1389,7 +1412,8 @@ var InterconnectService = class extends Service {
|
|
|
1389
1412
|
let title;
|
|
1390
1413
|
try {
|
|
1391
1414
|
const snapshot = this.ctx.get("sessionProjections")?.snapshot(agent.session);
|
|
1392
|
-
const
|
|
1415
|
+
const values = snapshot?.values;
|
|
1416
|
+
const value = values?.title;
|
|
1393
1417
|
if (typeof value === "string" && value !== "") title = value;
|
|
1394
1418
|
} catch {
|
|
1395
1419
|
title = void 0;
|
|
@@ -1447,7 +1471,7 @@ var InterconnectService = class extends Service {
|
|
|
1447
1471
|
}
|
|
1448
1472
|
agent = woken.agent;
|
|
1449
1473
|
}
|
|
1450
|
-
if (isSessionOwnedBySubagent(
|
|
1474
|
+
if (isSessionOwnedBySubagent(agent.session)) {
|
|
1451
1475
|
return { delivered: false, instance: this.instanceId, reason: "session-owned-by-subagent" };
|
|
1452
1476
|
}
|
|
1453
1477
|
const sender = (() => {
|
|
@@ -1656,24 +1680,40 @@ var LinkState = class {
|
|
|
1656
1680
|
this.logger = logger;
|
|
1657
1681
|
this.resolveToken = resolveToken;
|
|
1658
1682
|
this.instanceId = instanceId;
|
|
1659
|
-
this.
|
|
1683
|
+
this.origin = origin;
|
|
1660
1684
|
}
|
|
1661
1685
|
dialed;
|
|
1662
1686
|
closed = false;
|
|
1663
1687
|
retry = 0;
|
|
1664
1688
|
reconnectTimer;
|
|
1665
1689
|
warnedNoToken = false;
|
|
1666
|
-
|
|
1667
|
-
peer;
|
|
1690
|
+
warnedNoOrigin = false;
|
|
1668
1691
|
instanceId;
|
|
1692
|
+
origin;
|
|
1693
|
+
/** The origin this link dials now; for an unresolved `self` route, the sentinel is shown. */
|
|
1694
|
+
get peer() {
|
|
1695
|
+
return typeof this.origin === "string" ? this.origin : this.origin() ?? SELF_PEER_ORIGIN;
|
|
1696
|
+
}
|
|
1669
1697
|
/** Open the socket; reconnect is scheduled by the close handler. */
|
|
1670
1698
|
dial() {
|
|
1671
1699
|
if (this.closed) return;
|
|
1700
|
+
const origin = typeof this.origin === "string" ? this.origin : this.origin();
|
|
1701
|
+
if (origin === void 0) {
|
|
1702
|
+
if (this.retry >= 1 && !this.warnedNoOrigin) {
|
|
1703
|
+
this.warnedNoOrigin = true;
|
|
1704
|
+
this.logger.warn(
|
|
1705
|
+
`interconnect: peer ${this.instanceId} is configured as self but this instance has no listening webServer port yet; the link stays down and retries`
|
|
1706
|
+
);
|
|
1707
|
+
}
|
|
1708
|
+
this.scheduleReconnect();
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
this.warnedNoOrigin = false;
|
|
1672
1712
|
let url;
|
|
1673
1713
|
try {
|
|
1674
|
-
url = linkUrl(
|
|
1714
|
+
url = linkUrl(origin);
|
|
1675
1715
|
} catch {
|
|
1676
|
-
this.logger.warn(`interconnect: invalid peer origin ${
|
|
1716
|
+
this.logger.warn(`interconnect: invalid peer origin ${origin}; link to ${this.instanceId} stays down`);
|
|
1677
1717
|
return;
|
|
1678
1718
|
}
|
|
1679
1719
|
void this.resolveToken().then((token) => {
|