dsh-interconnect 0.11.15 → 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/assets/dsh-interconnect.md +2 -0
- package/dsh.plugin.json +1 -1
- package/lib/index.js +47 -40
- package/lib/interconnect/index.js +47 -40
- package/lib/interconnect/index.js.map +2 -2
- package/lib/tool-interconnect/index.js +53 -41
- package/lib/tool-interconnect/index.js.map +2 -2
- package/lib/types/interconnect/index.d.ts +12 -9
- package/lib/types/interconnect/index.d.ts.map +1 -1
- package/lib/types/interconnect/types.d.ts +4 -1
- package/lib/types/interconnect/types.d.ts.map +1 -1
- package/lib/types/tool-interconnect/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -80,6 +80,8 @@ When a send/reply reports `delivered: false`, read `reason`:
|
|
|
80
80
|
choose a live target instead.
|
|
81
81
|
- `session-owned-by-subagent` — the session belongs to a subagent and its
|
|
82
82
|
parent owns delivery; reach it through the parent.
|
|
83
|
+
- `message-too-large` — the message is larger than the peer link can carry
|
|
84
|
+
(1 MiB once encoded); shorten the text instead of retrying it unchanged.
|
|
83
85
|
- `no-sender-known` — a `reply` was attempted for a local session that never
|
|
84
86
|
received a sender-carrying interconnect message, or whose recorded sender was
|
|
85
87
|
forgotten by a receiver restart; establish contact with `interconnect_send`
|
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.
|
|
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,8 +835,10 @@ 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;
|
|
841
|
+
var frameFitsLink = (frame) => Buffer.byteLength(JSON.stringify(frame), "utf8") <= MAX_LINK_FRAME_BYTES;
|
|
840
842
|
var rowBytes = (row) => Buffer.byteLength(JSON.stringify(row), "utf8") + 1;
|
|
841
843
|
var notificationSchema = Schema.union([
|
|
842
844
|
Schema.object({ kind: Schema.const("agent/created").required(), sessionId: Schema.string().required() }),
|
|
@@ -927,7 +929,7 @@ var linkFrameSchema = Schema.union([
|
|
|
927
929
|
var InterconnectService = class extends Service {
|
|
928
930
|
static inject = ["agents", "credentials"];
|
|
929
931
|
static Config = Schema.object({
|
|
930
|
-
instanceId: Schema.string().default("dsh"),
|
|
932
|
+
instanceId: Schema.string().max(MAX_INSTANCE_ID_CHARS).default("dsh"),
|
|
931
933
|
requestTimeoutMs: Schema.natural().max(6e4).default(1e4),
|
|
932
934
|
peers: Schema.dict(Schema.string()).default({}),
|
|
933
935
|
delivery: Schema.union([Schema.const("followup"), Schema.const("steer"), Schema.const("inject")]).default("followup"),
|
|
@@ -937,7 +939,6 @@ var InterconnectService = class extends Service {
|
|
|
937
939
|
requestTimeoutMs;
|
|
938
940
|
delivery;
|
|
939
941
|
allowResume;
|
|
940
|
-
subscriptions = [];
|
|
941
942
|
server = new WebSocketServer({ noServer: true, maxPayload: MAX_LINK_FRAME_BYTES });
|
|
942
943
|
sockets = /* @__PURE__ */ new Set();
|
|
943
944
|
/** Outbound peer links keyed by the peer's `instanceId`. */
|
|
@@ -994,28 +995,28 @@ var InterconnectService = class extends Service {
|
|
|
994
995
|
socket.ping();
|
|
995
996
|
}
|
|
996
997
|
}, 3e4);
|
|
997
|
-
|
|
998
|
+
ctx.on("agent/status", ({ agent, status }) => {
|
|
998
999
|
this.fanout({ kind: "agent/status", sessionId: String(agent.session.id), status });
|
|
999
|
-
})
|
|
1000
|
-
|
|
1000
|
+
});
|
|
1001
|
+
ctx.on("agent/created", ({ agent }) => {
|
|
1001
1002
|
this.fanout({ kind: "agent/created", sessionId: String(agent.session.id) });
|
|
1002
|
-
})
|
|
1003
|
-
|
|
1003
|
+
});
|
|
1004
|
+
ctx.on("agent/disposed", ({ agent }) => {
|
|
1004
1005
|
this.fanout({ kind: "agent/disposed", sessionId: String(agent.session.id) });
|
|
1005
|
-
})
|
|
1006
|
-
|
|
1006
|
+
});
|
|
1007
|
+
ctx.on("session/created", (session) => {
|
|
1007
1008
|
const parentSessionId = session.header.parentSession === void 0 ? void 0 : String(session.header.parentSession);
|
|
1008
1009
|
this.fanout({
|
|
1009
1010
|
kind: "session/created",
|
|
1010
1011
|
sessionId: String(session.id),
|
|
1011
1012
|
...parentSessionId === void 0 ? {} : { parentSessionId }
|
|
1012
1013
|
});
|
|
1013
|
-
})
|
|
1014
|
-
|
|
1014
|
+
});
|
|
1015
|
+
ctx.on("session/disposed", (session) => {
|
|
1015
1016
|
this.senders.delete(String(session.id));
|
|
1016
1017
|
this.fanout({ kind: "session/disposed", sessionId: String(session.id) });
|
|
1017
|
-
})
|
|
1018
|
-
|
|
1018
|
+
});
|
|
1019
|
+
ctx.on("subagent/end", (info) => {
|
|
1019
1020
|
if (!info.local) return;
|
|
1020
1021
|
this.fanout({
|
|
1021
1022
|
kind: "subagent/end",
|
|
@@ -1023,10 +1024,7 @@ var InterconnectService = class extends Service {
|
|
|
1023
1024
|
childSessionId: String(info.id),
|
|
1024
1025
|
stopReason: info.stopReason
|
|
1025
1026
|
});
|
|
1026
|
-
})
|
|
1027
|
-
ctx.effect(() => () => {
|
|
1028
|
-
for (const dispose of this.subscriptions.splice(0)) dispose();
|
|
1029
|
-
}, "interconnect: event subscriptions");
|
|
1027
|
+
});
|
|
1030
1028
|
ctx.effect(() => () => {
|
|
1031
1029
|
if (this.heartbeatTimer !== void 0) clearInterval(this.heartbeatTimer);
|
|
1032
1030
|
for (const state of this.linkStates.values()) state.close();
|
|
@@ -1123,22 +1121,30 @@ var InterconnectService = class extends Service {
|
|
|
1123
1121
|
* Send a `msg` frame over the live link to a peer and resolve with its
|
|
1124
1122
|
* correlated `msg-result`. Undefined when the peer has no live link here
|
|
1125
1123
|
* (not configured, or the link is down) — sends can only address configured+
|
|
1126
|
-
* connected peers by design
|
|
1124
|
+
* connected peers by design — and a `message-too-large` result when the
|
|
1125
|
+
* encoded frame would not fit the link.
|
|
1127
1126
|
*/
|
|
1128
1127
|
async msgRequest(instanceId, kind, payload) {
|
|
1129
1128
|
const state = this.linkStates.get(instanceId);
|
|
1130
1129
|
if (state === void 0 || !state.writable()) return void 0;
|
|
1131
1130
|
const reqId = `m${++this.reqIdCounter}-${randomUUID()}`;
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1131
|
+
const frame = {
|
|
1132
|
+
type: "msg",
|
|
1133
|
+
reqId,
|
|
1134
|
+
message: {
|
|
1135
|
+
kind,
|
|
1136
|
+
sessionId: payload.sessionId,
|
|
1137
|
+
text: payload.text,
|
|
1138
|
+
...payload.sender === void 0 ? {} : { sender: payload.sender },
|
|
1139
|
+
...payload.delivery === void 0 ? {} : { delivery: payload.delivery },
|
|
1140
|
+
...payload.resume === void 0 ? {} : { resume: payload.resume }
|
|
1141
|
+
}
|
|
1139
1142
|
};
|
|
1143
|
+
if (!frameFitsLink(frame)) {
|
|
1144
|
+
return { delivered: false, instance: instanceId, reason: "message-too-large" };
|
|
1145
|
+
}
|
|
1140
1146
|
return this.waitForResult(reqId, "msg", (failure) => {
|
|
1141
|
-
const wrote = state.sendFrame(
|
|
1147
|
+
const wrote = state.sendFrame(frame);
|
|
1142
1148
|
if (!wrote) failure(new Error(`interconnect: peer link to ${instanceId} closed while sending`));
|
|
1143
1149
|
});
|
|
1144
1150
|
}
|
|
@@ -1208,10 +1214,12 @@ var InterconnectService = class extends Service {
|
|
|
1208
1214
|
* dialed link plus the peer's inbound one), so one event must leave over one
|
|
1209
1215
|
* of them. Which one is decided by local ownership, not by the peer: a dialed
|
|
1210
1216
|
* socket is a configured peer's link and always sends. An inbound socket is
|
|
1211
|
-
* skipped only when
|
|
1212
|
-
*
|
|
1213
|
-
*
|
|
1214
|
-
*
|
|
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.
|
|
1215
1223
|
*
|
|
1216
1224
|
* That holds once a peer's announcement has been seen. Between this
|
|
1217
1225
|
* instance's dialed link opening and the peer's `hello` arriving on its
|
|
@@ -1230,7 +1238,10 @@ var InterconnectService = class extends Service {
|
|
|
1230
1238
|
continue;
|
|
1231
1239
|
}
|
|
1232
1240
|
const dialedPeer = this.dialedPeerOf.get(socket);
|
|
1233
|
-
if (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);
|
|
1234
1245
|
}
|
|
1235
1246
|
for (const socket of this.sockets) {
|
|
1236
1247
|
if (this.dialedPeerOf.get(socket) === void 0) {
|
|
@@ -1262,12 +1273,7 @@ var InterconnectService = class extends Service {
|
|
|
1262
1273
|
if (query.kind === "ping") {
|
|
1263
1274
|
result = { pong: true, instance: this.instanceId };
|
|
1264
1275
|
} else if (query.kind === "list") {
|
|
1265
|
-
|
|
1266
|
-
if (rowBudget < 0) {
|
|
1267
|
-
this.ctx.logger.warn("interconnect: list answer cannot fit the link frame cap; not answering");
|
|
1268
|
-
return;
|
|
1269
|
-
}
|
|
1270
|
-
result = this.listSessions(rowBudget);
|
|
1276
|
+
result = this.listSessions(this.listRowsBudgetBytes(reqId));
|
|
1271
1277
|
} else {
|
|
1272
1278
|
const payload = {
|
|
1273
1279
|
sender: this.peerOf.get(socket) ?? "unknown-peer",
|
|
@@ -1299,10 +1305,11 @@ var InterconnectService = class extends Service {
|
|
|
1299
1305
|
* Bytes one `list` answer may spend on its `sessions` rows: the frame cap
|
|
1300
1306
|
* minus the serialized envelope that answer will carry. The envelope holds
|
|
1301
1307
|
* the peer-supplied `reqId` and this instance's own id, so it is measured per
|
|
1302
|
-
* 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.
|
|
1303
1311
|
* @param reqId - the request id the answer has to echo.
|
|
1304
|
-
* @returns the byte budget for the rows
|
|
1305
|
-
* exceeds the cap and so cannot be sent at all.
|
|
1312
|
+
* @returns the byte budget for the rows.
|
|
1306
1313
|
*/
|
|
1307
1314
|
listRowsBudgetBytes(reqId) {
|
|
1308
1315
|
const envelope = {
|
|
@@ -835,8 +835,10 @@ 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;
|
|
841
|
+
var frameFitsLink = (frame) => Buffer.byteLength(JSON.stringify(frame), "utf8") <= MAX_LINK_FRAME_BYTES;
|
|
840
842
|
var rowBytes = (row) => Buffer.byteLength(JSON.stringify(row), "utf8") + 1;
|
|
841
843
|
var notificationSchema = Schema.union([
|
|
842
844
|
Schema.object({ kind: Schema.const("agent/created").required(), sessionId: Schema.string().required() }),
|
|
@@ -927,7 +929,7 @@ var linkFrameSchema = Schema.union([
|
|
|
927
929
|
var InterconnectService = class extends Service {
|
|
928
930
|
static inject = ["agents", "credentials"];
|
|
929
931
|
static Config = Schema.object({
|
|
930
|
-
instanceId: Schema.string().default("dsh"),
|
|
932
|
+
instanceId: Schema.string().max(MAX_INSTANCE_ID_CHARS).default("dsh"),
|
|
931
933
|
requestTimeoutMs: Schema.natural().max(6e4).default(1e4),
|
|
932
934
|
peers: Schema.dict(Schema.string()).default({}),
|
|
933
935
|
delivery: Schema.union([Schema.const("followup"), Schema.const("steer"), Schema.const("inject")]).default("followup"),
|
|
@@ -937,7 +939,6 @@ var InterconnectService = class extends Service {
|
|
|
937
939
|
requestTimeoutMs;
|
|
938
940
|
delivery;
|
|
939
941
|
allowResume;
|
|
940
|
-
subscriptions = [];
|
|
941
942
|
server = new WebSocketServer({ noServer: true, maxPayload: MAX_LINK_FRAME_BYTES });
|
|
942
943
|
sockets = /* @__PURE__ */ new Set();
|
|
943
944
|
/** Outbound peer links keyed by the peer's `instanceId`. */
|
|
@@ -994,28 +995,28 @@ var InterconnectService = class extends Service {
|
|
|
994
995
|
socket.ping();
|
|
995
996
|
}
|
|
996
997
|
}, 3e4);
|
|
997
|
-
|
|
998
|
+
ctx.on("agent/status", ({ agent, status }) => {
|
|
998
999
|
this.fanout({ kind: "agent/status", sessionId: String(agent.session.id), status });
|
|
999
|
-
})
|
|
1000
|
-
|
|
1000
|
+
});
|
|
1001
|
+
ctx.on("agent/created", ({ agent }) => {
|
|
1001
1002
|
this.fanout({ kind: "agent/created", sessionId: String(agent.session.id) });
|
|
1002
|
-
})
|
|
1003
|
-
|
|
1003
|
+
});
|
|
1004
|
+
ctx.on("agent/disposed", ({ agent }) => {
|
|
1004
1005
|
this.fanout({ kind: "agent/disposed", sessionId: String(agent.session.id) });
|
|
1005
|
-
})
|
|
1006
|
-
|
|
1006
|
+
});
|
|
1007
|
+
ctx.on("session/created", (session) => {
|
|
1007
1008
|
const parentSessionId = session.header.parentSession === void 0 ? void 0 : String(session.header.parentSession);
|
|
1008
1009
|
this.fanout({
|
|
1009
1010
|
kind: "session/created",
|
|
1010
1011
|
sessionId: String(session.id),
|
|
1011
1012
|
...parentSessionId === void 0 ? {} : { parentSessionId }
|
|
1012
1013
|
});
|
|
1013
|
-
})
|
|
1014
|
-
|
|
1014
|
+
});
|
|
1015
|
+
ctx.on("session/disposed", (session) => {
|
|
1015
1016
|
this.senders.delete(String(session.id));
|
|
1016
1017
|
this.fanout({ kind: "session/disposed", sessionId: String(session.id) });
|
|
1017
|
-
})
|
|
1018
|
-
|
|
1018
|
+
});
|
|
1019
|
+
ctx.on("subagent/end", (info) => {
|
|
1019
1020
|
if (!info.local) return;
|
|
1020
1021
|
this.fanout({
|
|
1021
1022
|
kind: "subagent/end",
|
|
@@ -1023,10 +1024,7 @@ var InterconnectService = class extends Service {
|
|
|
1023
1024
|
childSessionId: String(info.id),
|
|
1024
1025
|
stopReason: info.stopReason
|
|
1025
1026
|
});
|
|
1026
|
-
})
|
|
1027
|
-
ctx.effect(() => () => {
|
|
1028
|
-
for (const dispose of this.subscriptions.splice(0)) dispose();
|
|
1029
|
-
}, "interconnect: event subscriptions");
|
|
1027
|
+
});
|
|
1030
1028
|
ctx.effect(() => () => {
|
|
1031
1029
|
if (this.heartbeatTimer !== void 0) clearInterval(this.heartbeatTimer);
|
|
1032
1030
|
for (const state of this.linkStates.values()) state.close();
|
|
@@ -1123,22 +1121,30 @@ var InterconnectService = class extends Service {
|
|
|
1123
1121
|
* Send a `msg` frame over the live link to a peer and resolve with its
|
|
1124
1122
|
* correlated `msg-result`. Undefined when the peer has no live link here
|
|
1125
1123
|
* (not configured, or the link is down) — sends can only address configured+
|
|
1126
|
-
* connected peers by design
|
|
1124
|
+
* connected peers by design — and a `message-too-large` result when the
|
|
1125
|
+
* encoded frame would not fit the link.
|
|
1127
1126
|
*/
|
|
1128
1127
|
async msgRequest(instanceId, kind, payload) {
|
|
1129
1128
|
const state = this.linkStates.get(instanceId);
|
|
1130
1129
|
if (state === void 0 || !state.writable()) return void 0;
|
|
1131
1130
|
const reqId = `m${++this.reqIdCounter}-${randomUUID()}`;
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1131
|
+
const frame = {
|
|
1132
|
+
type: "msg",
|
|
1133
|
+
reqId,
|
|
1134
|
+
message: {
|
|
1135
|
+
kind,
|
|
1136
|
+
sessionId: payload.sessionId,
|
|
1137
|
+
text: payload.text,
|
|
1138
|
+
...payload.sender === void 0 ? {} : { sender: payload.sender },
|
|
1139
|
+
...payload.delivery === void 0 ? {} : { delivery: payload.delivery },
|
|
1140
|
+
...payload.resume === void 0 ? {} : { resume: payload.resume }
|
|
1141
|
+
}
|
|
1139
1142
|
};
|
|
1143
|
+
if (!frameFitsLink(frame)) {
|
|
1144
|
+
return { delivered: false, instance: instanceId, reason: "message-too-large" };
|
|
1145
|
+
}
|
|
1140
1146
|
return this.waitForResult(reqId, "msg", (failure) => {
|
|
1141
|
-
const wrote = state.sendFrame(
|
|
1147
|
+
const wrote = state.sendFrame(frame);
|
|
1142
1148
|
if (!wrote) failure(new Error(`interconnect: peer link to ${instanceId} closed while sending`));
|
|
1143
1149
|
});
|
|
1144
1150
|
}
|
|
@@ -1208,10 +1214,12 @@ var InterconnectService = class extends Service {
|
|
|
1208
1214
|
* dialed link plus the peer's inbound one), so one event must leave over one
|
|
1209
1215
|
* of them. Which one is decided by local ownership, not by the peer: a dialed
|
|
1210
1216
|
* socket is a configured peer's link and always sends. An inbound socket is
|
|
1211
|
-
* skipped only when
|
|
1212
|
-
*
|
|
1213
|
-
*
|
|
1214
|
-
*
|
|
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.
|
|
1215
1223
|
*
|
|
1216
1224
|
* That holds once a peer's announcement has been seen. Between this
|
|
1217
1225
|
* instance's dialed link opening and the peer's `hello` arriving on its
|
|
@@ -1230,7 +1238,10 @@ var InterconnectService = class extends Service {
|
|
|
1230
1238
|
continue;
|
|
1231
1239
|
}
|
|
1232
1240
|
const dialedPeer = this.dialedPeerOf.get(socket);
|
|
1233
|
-
if (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);
|
|
1234
1245
|
}
|
|
1235
1246
|
for (const socket of this.sockets) {
|
|
1236
1247
|
if (this.dialedPeerOf.get(socket) === void 0) {
|
|
@@ -1262,12 +1273,7 @@ var InterconnectService = class extends Service {
|
|
|
1262
1273
|
if (query.kind === "ping") {
|
|
1263
1274
|
result = { pong: true, instance: this.instanceId };
|
|
1264
1275
|
} else if (query.kind === "list") {
|
|
1265
|
-
|
|
1266
|
-
if (rowBudget < 0) {
|
|
1267
|
-
this.ctx.logger.warn("interconnect: list answer cannot fit the link frame cap; not answering");
|
|
1268
|
-
return;
|
|
1269
|
-
}
|
|
1270
|
-
result = this.listSessions(rowBudget);
|
|
1276
|
+
result = this.listSessions(this.listRowsBudgetBytes(reqId));
|
|
1271
1277
|
} else {
|
|
1272
1278
|
const payload = {
|
|
1273
1279
|
sender: this.peerOf.get(socket) ?? "unknown-peer",
|
|
@@ -1299,10 +1305,11 @@ var InterconnectService = class extends Service {
|
|
|
1299
1305
|
* Bytes one `list` answer may spend on its `sessions` rows: the frame cap
|
|
1300
1306
|
* minus the serialized envelope that answer will carry. The envelope holds
|
|
1301
1307
|
* the peer-supplied `reqId` and this instance's own id, so it is measured per
|
|
1302
|
-
* 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.
|
|
1303
1311
|
* @param reqId - the request id the answer has to echo.
|
|
1304
|
-
* @returns the byte budget for the rows
|
|
1305
|
-
* exceeds the cap and so cannot be sent at all.
|
|
1312
|
+
* @returns the byte budget for the rows.
|
|
1306
1313
|
*/
|
|
1307
1314
|
listRowsBudgetBytes(reqId) {
|
|
1308
1315
|
const envelope = {
|