dsh-interconnect 0.11.24 → 0.13.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 +141 -24
- package/assets/dsh-interconnect.md +60 -24
- package/dsh.plugin.json +1 -1
- package/lib/index.js +27 -10
- package/lib/interconnect/index.js +27 -10
- package/lib/interconnect/index.js.map +3 -3
- package/lib/tool-interconnect/index.js +105 -24
- package/lib/tool-interconnect/index.js.map +3 -3
- package/lib/types/interconnect/index.d.ts +16 -6
- package/lib/types/interconnect/index.d.ts.map +1 -1
- package/lib/types/interconnect/types.d.ts +38 -9
- package/lib/types/interconnect/types.d.ts.map +1 -1
- package/lib/types/tool-interconnect/index.d.ts +1 -1
- package/lib/types/tool-interconnect/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
|
8
8
|
import { remoteErrorOf } from "@deepseek-ai/dsh-typert-protocol";
|
|
9
9
|
import { boundContextSummary, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// ../dsh-wt-ic-merge/vendor/cosmokit/src/misc.ts
|
|
12
12
|
function isNullable(value) {
|
|
13
13
|
return value === null || value === void 0;
|
|
14
14
|
}
|
|
@@ -30,7 +30,7 @@ function pick(source, keys, forced) {
|
|
|
30
30
|
return result;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// ../dsh-wt-ic-merge/vendor/cosmokit/src/types.ts
|
|
34
34
|
function is(type, value) {
|
|
35
35
|
if (arguments.length === 1) return (value2) => is(type, value2);
|
|
36
36
|
return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
|
|
@@ -139,7 +139,7 @@ function deepEqual(a, b, strict) {
|
|
|
139
139
|
}) ?? Object.keys({ ...a, ...b }).every((key) => deepEqual(a[key], b[key], strict));
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
//
|
|
142
|
+
// ../dsh-wt-ic-merge/vendor/cosmokit/src/time.ts
|
|
143
143
|
var Time;
|
|
144
144
|
((Time2) => {
|
|
145
145
|
Time2.millisecond = 1;
|
|
@@ -219,7 +219,7 @@ var Time;
|
|
|
219
219
|
Time2.template = template;
|
|
220
220
|
})(Time || (Time = {}));
|
|
221
221
|
|
|
222
|
-
//
|
|
222
|
+
// ../dsh-wt-ic-merge/vendor/schemastery/lib/index.mjs
|
|
223
223
|
var kSchema = Symbol.for("schemastery");
|
|
224
224
|
var kValidationError = Symbol.for("ValidationError");
|
|
225
225
|
globalThis.__schemastery_index__ ??= 0;
|
|
@@ -1111,7 +1111,7 @@ var InterconnectService = class extends Service {
|
|
|
1111
1111
|
* Returns the peer identity when reachable, or undefined when the link is
|
|
1112
1112
|
* not up or no answer arrives.
|
|
1113
1113
|
* @param instanceId - the peer's `instanceId` as configured under {@link Config.peers}.
|
|
1114
|
-
* @returns the peer's identity, or undefined when
|
|
1114
|
+
* @returns the peer's identity, or undefined when there is no usable answer.
|
|
1115
1115
|
*/
|
|
1116
1116
|
async ping(instanceId) {
|
|
1117
1117
|
return this.queryRequest(instanceId, { kind: "ping" });
|
|
@@ -1125,16 +1125,33 @@ var InterconnectService = class extends Service {
|
|
|
1125
1125
|
async list(instanceId) {
|
|
1126
1126
|
return this.queryRequest(instanceId, { kind: "list" });
|
|
1127
1127
|
}
|
|
1128
|
+
/**
|
|
1129
|
+
* The peers this instance configures, each with the current state of the link
|
|
1130
|
+
* it dials, in configuration order. This is how a caller tells an id this
|
|
1131
|
+
* instance cannot reach at all (`peer-not-configured`) from one whose link is
|
|
1132
|
+
* merely down (`unreachable`), which the bare `undefined` of `ping`/`list`
|
|
1133
|
+
* cannot express. A configured peer whose link is `down` is still a valid
|
|
1134
|
+
* target: its reconnect runs on its own backoff, so it may answer later.
|
|
1135
|
+
* @returns one row per configured peer.
|
|
1136
|
+
*/
|
|
1137
|
+
peers() {
|
|
1138
|
+
return [...this.linkStates].map(([instanceId, state]) => ({
|
|
1139
|
+
instanceId,
|
|
1140
|
+
origin: state.peer,
|
|
1141
|
+
link: state.writable() ? "up" : "down"
|
|
1142
|
+
}));
|
|
1143
|
+
}
|
|
1128
1144
|
/**
|
|
1129
1145
|
* Send a `msg` frame over the live link to a peer and resolve with its
|
|
1130
|
-
* correlated `msg-result`.
|
|
1131
|
-
*
|
|
1132
|
-
*
|
|
1133
|
-
*
|
|
1146
|
+
* correlated `msg-result`. A `peer-not-configured` result when this instance
|
|
1147
|
+
* configures no route for that id, undefined when the route exists but its
|
|
1148
|
+
* link is down, and a `message-too-large` result when the encoded frame would
|
|
1149
|
+
* not fit the link.
|
|
1134
1150
|
*/
|
|
1135
1151
|
async msgRequest(instanceId, kind, payload) {
|
|
1136
1152
|
const state = this.linkStates.get(instanceId);
|
|
1137
|
-
if (state === void 0
|
|
1153
|
+
if (state === void 0) return { delivered: false, instance: instanceId, reason: "peer-not-configured" };
|
|
1154
|
+
if (!state.writable()) return void 0;
|
|
1138
1155
|
const reqId = `m${++this.reqIdCounter}-${randomUUID()}`;
|
|
1139
1156
|
const frame = {
|
|
1140
1157
|
type: "msg",
|
|
@@ -1788,11 +1805,13 @@ var deliveryOutcome = (result) => ({
|
|
|
1788
1805
|
...result.delivery === void 0 ? {} : { delivery: result.delivery },
|
|
1789
1806
|
...result.reason === void 0 ? {} : { reason: result.reason }
|
|
1790
1807
|
});
|
|
1808
|
+
var configures = (interconnect, instanceId) => interconnect.peers().some((peer) => peer.instanceId === instanceId);
|
|
1809
|
+
var unansweredText = (instanceId, configured) => configured === false ? `not configured: this instance has no peer "${instanceId}" \u2014 add it to the interconnect peers mapping (interconnect_peers lists what is configured)` : `unreachable: peer "${instanceId}" gave no usable answer (its link is down, or the shared token differs); retrying may succeed`;
|
|
1791
1810
|
function apply(ctx) {
|
|
1792
1811
|
const interconnect = ctx.interconnect;
|
|
1793
1812
|
ctx.tools.register(defineTool({
|
|
1794
1813
|
name: "interconnect_send",
|
|
1795
|
-
description: 'Deliver one text message to a
|
|
1814
|
+
description: 'Deliver one text message to a session on another DSH instance (same machine, another machine, or another session). Returns whether the peer instance received it and which instance answered. A live session receives directly; a persisted session with no live agent is woken by default (resume), and when delivery fails the result reports the reason (for example "session-not-live" or "resume-refused"). The sending instance and session ids are attached automatically, so the receiver can reply with interconnect_reply; do not pass a sender parameter.',
|
|
1796
1815
|
parameters: {
|
|
1797
1816
|
instanceId: {
|
|
1798
1817
|
type: "string",
|
|
@@ -1816,7 +1835,7 @@ function apply(ctx) {
|
|
|
1816
1835
|
},
|
|
1817
1836
|
resume: {
|
|
1818
1837
|
type: "boolean",
|
|
1819
|
-
description: `Wake the target session if it is persisted but not live.
|
|
1838
|
+
description: `Wake the target session if it is persisted but not live. Defaults to true: waking starts a real agent turn \u2014 a billed model call with that session's full toolset \u2014 on the receiver, which is what makes an offline target reachable. Set false to leave it asleep and report reason "session-not-live". The receiver may still refuse, answering reason "resume-refused".`
|
|
1820
1839
|
}
|
|
1821
1840
|
},
|
|
1822
1841
|
output: {
|
|
@@ -1830,6 +1849,8 @@ function apply(ctx) {
|
|
|
1830
1849
|
}
|
|
1831
1850
|
const text = (() => {
|
|
1832
1851
|
switch (value.reason) {
|
|
1852
|
+
case "peer-not-configured":
|
|
1853
|
+
return `not delivered: this instance configures no peer "${value.instance}" \u2014 add it to the interconnect peers mapping (interconnect_peers lists what is configured)`;
|
|
1833
1854
|
case "unreachable":
|
|
1834
1855
|
return `not delivered: ${value.instance} did not answer (unreachable); retrying may succeed`;
|
|
1835
1856
|
case "resume-refused":
|
|
@@ -1841,7 +1862,7 @@ function apply(ctx) {
|
|
|
1841
1862
|
case "message-too-large":
|
|
1842
1863
|
return "not delivered: the message is too large for the peer link (limit 1 MiB); shorten the text and try again";
|
|
1843
1864
|
default:
|
|
1844
|
-
return `not delivered: no live session "${_args.sessionId}" on ${value.instance}` + (_args.resume ===
|
|
1865
|
+
return `not delivered: no live session "${_args.sessionId}" on ${value.instance}` + (_args.resume === false ? " \u2014 waking was disabled (resume: false); the message was not delivered to any other session" : " \u2014 it could not be woken there (the receiver has no resuming agent lookup); the message was not delivered to any other session");
|
|
1845
1866
|
}
|
|
1846
1867
|
})();
|
|
1847
1868
|
return [{ type: "text", text }];
|
|
@@ -1859,7 +1880,9 @@ function apply(ctx) {
|
|
|
1859
1880
|
// as a reply target.
|
|
1860
1881
|
...sessionId === void 0 ? {} : { sender: interconnect.selfSender(String(sessionId)) },
|
|
1861
1882
|
...args.delivery === void 0 ? {} : { delivery: args.delivery },
|
|
1862
|
-
|
|
1883
|
+
// Waking is the default: a persisted target with no live agent is
|
|
1884
|
+
// otherwise unreachable, and a caller that wants it left asleep passes false.
|
|
1885
|
+
resume: args.resume ?? true
|
|
1863
1886
|
}));
|
|
1864
1887
|
return deliveryOutcome(result);
|
|
1865
1888
|
}
|
|
@@ -1880,17 +1903,20 @@ function apply(ctx) {
|
|
|
1880
1903
|
additionalProperties: false,
|
|
1881
1904
|
properties: {
|
|
1882
1905
|
reachable: { type: "boolean", required: true },
|
|
1883
|
-
instance: { type: "string" }
|
|
1906
|
+
instance: { type: "string" },
|
|
1907
|
+
// Whether this instance configures that peer at all: absent/false means
|
|
1908
|
+
// the id cannot be reached by any retry, true means the link is down.
|
|
1909
|
+
configured: { type: "boolean" }
|
|
1884
1910
|
}
|
|
1885
1911
|
},
|
|
1886
|
-
render: (
|
|
1912
|
+
render: (args, value) => [{
|
|
1887
1913
|
type: "text",
|
|
1888
|
-
text: value.reachable ? `reachable: ${value.instance ?? "(unknown instance)"}` :
|
|
1914
|
+
text: value.reachable ? `reachable: ${value.instance ?? "(unknown instance)"}` : unansweredText(args.instanceId, value.configured)
|
|
1889
1915
|
}]
|
|
1890
1916
|
},
|
|
1891
1917
|
async execute(args, exec) {
|
|
1892
1918
|
const result = await raceSignal(exec.signal, interconnect.ping(args.instanceId));
|
|
1893
|
-
if (result === void 0) return { reachable: false };
|
|
1919
|
+
if (result === void 0) return { reachable: false, configured: configures(interconnect, args.instanceId) };
|
|
1894
1920
|
return { reachable: true, instance: result.instance };
|
|
1895
1921
|
}
|
|
1896
1922
|
}));
|
|
@@ -1911,6 +1937,8 @@ function apply(ctx) {
|
|
|
1911
1937
|
properties: {
|
|
1912
1938
|
reachable: { type: "boolean", required: true },
|
|
1913
1939
|
instance: { type: "string" },
|
|
1940
|
+
// Whether this instance configures that peer at all, as in interconnect_ping.
|
|
1941
|
+
configured: { type: "boolean" },
|
|
1914
1942
|
sessions: {
|
|
1915
1943
|
type: "array",
|
|
1916
1944
|
items: {
|
|
@@ -1925,8 +1953,8 @@ function apply(ctx) {
|
|
|
1925
1953
|
}
|
|
1926
1954
|
}
|
|
1927
1955
|
},
|
|
1928
|
-
render: (
|
|
1929
|
-
if (!value.reachable) return [{ type: "text", text:
|
|
1956
|
+
render: (args, value) => {
|
|
1957
|
+
if (!value.reachable) return [{ type: "text", text: unansweredText(args.instanceId, value.configured) }];
|
|
1930
1958
|
const sessions = value.sessions ?? [];
|
|
1931
1959
|
if (sessions.length === 0) {
|
|
1932
1960
|
return [{ type: "text", text: `no live sessions on ${value.instance ?? "(unknown instance)"}` }];
|
|
@@ -1944,7 +1972,7 @@ function apply(ctx) {
|
|
|
1944
1972
|
},
|
|
1945
1973
|
async execute(args, exec) {
|
|
1946
1974
|
const result = await raceSignal(exec.signal, interconnect.list(args.instanceId));
|
|
1947
|
-
if (result === void 0) return { reachable: false };
|
|
1975
|
+
if (result === void 0) return { reachable: false, configured: configures(interconnect, args.instanceId) };
|
|
1948
1976
|
return {
|
|
1949
1977
|
reachable: true,
|
|
1950
1978
|
instance: result.instance,
|
|
@@ -1958,6 +1986,55 @@ function apply(ctx) {
|
|
|
1958
1986
|
};
|
|
1959
1987
|
}
|
|
1960
1988
|
}));
|
|
1989
|
+
ctx.tools.register(defineTool({
|
|
1990
|
+
name: "interconnect_peers",
|
|
1991
|
+
description: `List the peer DSH instances this instance is configured to reach, with each peer's dialed link state. Call this first when you do not already know the peer ids available here: every listed instanceId is a valid target for interconnect_send, interconnect_ping, and interconnect_list. A peer listed with link "down" is configured but currently cannot carry frames, so it answers nothing until its reconnect succeeds; an id that is not listed at all cannot be reached and reports reason "peer-not-configured". A message that arrives from a peer this instance does not configure can be received but not replied to.`,
|
|
1992
|
+
parameters: {},
|
|
1993
|
+
output: {
|
|
1994
|
+
schema: {
|
|
1995
|
+
type: "object",
|
|
1996
|
+
additionalProperties: false,
|
|
1997
|
+
properties: {
|
|
1998
|
+
peers: {
|
|
1999
|
+
type: "array",
|
|
2000
|
+
items: {
|
|
2001
|
+
type: "object",
|
|
2002
|
+
additionalProperties: false,
|
|
2003
|
+
properties: {
|
|
2004
|
+
instanceId: { type: "string", required: true },
|
|
2005
|
+
origin: { type: "string", required: true },
|
|
2006
|
+
link: { type: "string", required: true }
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
},
|
|
2012
|
+
render: (_args, value) => {
|
|
2013
|
+
const peers = value.peers ?? [];
|
|
2014
|
+
if (peers.length === 0) {
|
|
2015
|
+
return [{
|
|
2016
|
+
type: "text",
|
|
2017
|
+
text: "no peers configured: this instance can receive messages but cannot send or reply. Add entries to the interconnect row's peers mapping (a peer id keyed to the origin this instance dials); the change applies when that row's config is re-activated."
|
|
2018
|
+
}];
|
|
2019
|
+
}
|
|
2020
|
+
return [{
|
|
2021
|
+
type: "text",
|
|
2022
|
+
text: peers.map((peer) => `${peer.instanceId} ${peer.origin} [${peer.link}]`).join("\n")
|
|
2023
|
+
}];
|
|
2024
|
+
}
|
|
2025
|
+
},
|
|
2026
|
+
execute(_args, _exec) {
|
|
2027
|
+
return Promise.resolve({
|
|
2028
|
+
peers: interconnect.peers().map((peer) => ({
|
|
2029
|
+
instanceId: peer.instanceId,
|
|
2030
|
+
// The origin is this instance's own dial target, read locally: it is
|
|
2031
|
+
// what a caller needs to diagnose a peer whose link will not come up.
|
|
2032
|
+
origin: peer.origin,
|
|
2033
|
+
link: peer.link
|
|
2034
|
+
}))
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
}));
|
|
1961
2038
|
ctx.tools.register(defineTool({
|
|
1962
2039
|
name: "interconnect_reply",
|
|
1963
2040
|
description: `Deliver one text message back to the peer that this session last received an interconnect message from. Only the reply text is needed: the replying session is this agent's own, and the outbound target (the sending instance id and session id) is recalled from the message this session received, so you do not ask for an address twice. The recalled target is the most recent sender only: a later message from another peer replaces it, and restarting this instance forgets it until a new message arrives, so name the intended recipient in the reply text when more than one peer may message this session. A session that never received a message through interconnect \u2014 or received one without a sender identity \u2014 reports reason "no-sender-known".`,
|
|
@@ -1974,7 +2051,7 @@ function apply(ctx) {
|
|
|
1974
2051
|
},
|
|
1975
2052
|
resume: {
|
|
1976
2053
|
type: "boolean",
|
|
1977
|
-
description:
|
|
2054
|
+
description: `Wake the recorded sender's session if it is persisted but not live. Defaults to true: a reply completes an interchange the sender started, so the session is woken by a real billed agent turn on the receiver. Set false to leave it asleep and report reason "session-not-live". The receiver may still refuse, answering "resume-refused".`
|
|
1978
2055
|
}
|
|
1979
2056
|
},
|
|
1980
2057
|
output: {
|
|
@@ -1996,6 +2073,9 @@ function apply(ctx) {
|
|
|
1996
2073
|
if (value.reason === "session-owned-by-subagent") {
|
|
1997
2074
|
return "not delivered: the recorded sender's session belongs to a subagent and its parent agent owns delivery";
|
|
1998
2075
|
}
|
|
2076
|
+
if (value.reason === "peer-not-configured") {
|
|
2077
|
+
return `not delivered: this instance configures no peer "${value.instance}" \u2014 the sender can be messaged, but this instance cannot dial it back until it is added to the interconnect peers mapping`;
|
|
2078
|
+
}
|
|
1999
2079
|
if (value.reason === "unreachable") {
|
|
2000
2080
|
return "not delivered: the recorded sender did not answer (unreachable); retrying may succeed";
|
|
2001
2081
|
}
|
|
@@ -2005,7 +2085,7 @@ function apply(ctx) {
|
|
|
2005
2085
|
if (value.reason === "resume-failed") {
|
|
2006
2086
|
return "not delivered: could not wake the recorded sender's session";
|
|
2007
2087
|
}
|
|
2008
|
-
return "not delivered: the recorded sender's session is not live";
|
|
2088
|
+
return "not delivered: the recorded sender's session is not live" + (_args.resume === false ? " \u2014 waking was disabled (resume: false); the reply was not sent to any other session" : " \u2014 it could not be woken there (the receiver has no resuming agent lookup); the reply was not sent to any other session");
|
|
2009
2089
|
})();
|
|
2010
2090
|
return [{ type: "text", text }];
|
|
2011
2091
|
}
|
|
@@ -2021,7 +2101,8 @@ function apply(ctx) {
|
|
|
2021
2101
|
sessionId: String(exec.agent.session.id),
|
|
2022
2102
|
text: args.text,
|
|
2023
2103
|
...args.delivery === void 0 ? {} : { delivery: args.delivery },
|
|
2024
|
-
|
|
2104
|
+
// Replying to a sender that has gone offline should still reach it.
|
|
2105
|
+
resume: args.resume ?? true
|
|
2025
2106
|
}));
|
|
2026
2107
|
return deliveryOutcome(result);
|
|
2027
2108
|
}
|