@hypercli.com/sdk 2026.3.16 → 2026.3.18
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 +16 -3
- package/dist/agent.d.ts +1 -10
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +29 -33
- package/dist/agent.js.map +1 -1
- package/dist/agents.d.ts +102 -4
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +446 -14
- package/dist/agents.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -3
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +13 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +70 -13
- package/dist/config.js.map +1 -1
- package/dist/gateway.d.ts +23 -5
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +363 -86
- package/dist/gateway.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/gateway.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Protocol: OpenClaw Gateway v3
|
|
8
8
|
*/
|
|
9
9
|
import { getPublicKeyAsync, signAsync, utils as edUtils } from "@noble/ed25519";
|
|
10
|
+
import NodeWebSocket from "ws";
|
|
10
11
|
class GatewayRequestError extends Error {
|
|
11
12
|
gatewayCode;
|
|
12
13
|
details;
|
|
@@ -21,8 +22,8 @@ const PROTOCOL_VERSION = 3;
|
|
|
21
22
|
const DEFAULT_TIMEOUT = 15_000;
|
|
22
23
|
const CHAT_TIMEOUT = 120_000;
|
|
23
24
|
const RECONNECT_CLOSE_CODE = 4008;
|
|
24
|
-
const DEFAULT_CLIENT_ID = "
|
|
25
|
-
const DEFAULT_CLIENT_MODE = "
|
|
25
|
+
const DEFAULT_CLIENT_ID = "cli";
|
|
26
|
+
const DEFAULT_CLIENT_MODE = "cli";
|
|
26
27
|
const DEFAULT_CLIENT_VERSION = "@hypercli/sdk";
|
|
27
28
|
const DEFAULT_CAPS = ["tool-events"];
|
|
28
29
|
const CONNECT_TIMER_MS = 750;
|
|
@@ -72,6 +73,122 @@ function asRecord(value) {
|
|
|
72
73
|
? value
|
|
73
74
|
: null;
|
|
74
75
|
}
|
|
76
|
+
function extractMessageText(message) {
|
|
77
|
+
if (typeof message === "string") {
|
|
78
|
+
return message;
|
|
79
|
+
}
|
|
80
|
+
const record = asRecord(message);
|
|
81
|
+
if (!record) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const content = record.content;
|
|
85
|
+
if (typeof content === "string") {
|
|
86
|
+
return content;
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(content)) {
|
|
89
|
+
const parts = content
|
|
90
|
+
.map((entry) => {
|
|
91
|
+
const item = asRecord(entry);
|
|
92
|
+
if (!item || item.type !== "text" || typeof item.text !== "string" || !item.text) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return item.text;
|
|
96
|
+
})
|
|
97
|
+
.filter((value) => typeof value === "string");
|
|
98
|
+
if (parts.length > 0) {
|
|
99
|
+
return parts.join("\n");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return typeof record.text === "string" ? record.text : null;
|
|
103
|
+
}
|
|
104
|
+
function extractMessageRunId(message) {
|
|
105
|
+
const record = asRecord(message);
|
|
106
|
+
if (!record) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const directRunId = typeof record.runId === "string" ? record.runId.trim() : "";
|
|
110
|
+
if (directRunId) {
|
|
111
|
+
return directRunId;
|
|
112
|
+
}
|
|
113
|
+
const agentRunId = typeof record.agentRunId === "string" ? record.agentRunId.trim() : "";
|
|
114
|
+
if (agentRunId) {
|
|
115
|
+
return agentRunId;
|
|
116
|
+
}
|
|
117
|
+
const meta = asRecord(record.meta);
|
|
118
|
+
const metaRunId = typeof meta?.runId === "string" ? meta.runId.trim() : "";
|
|
119
|
+
return metaRunId || null;
|
|
120
|
+
}
|
|
121
|
+
function latestHistoryAssistantText(messages, acceptedRunIds) {
|
|
122
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
123
|
+
const message = asRecord(messages[index]);
|
|
124
|
+
if (!message) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const role = typeof message.role === "string" ? message.role.trim().toLowerCase() : "";
|
|
128
|
+
if (role !== "assistant") {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const messageRunId = extractMessageRunId(message);
|
|
132
|
+
if (messageRunId && acceptedRunIds.size > 0 && !acceptedRunIds.has(messageRunId)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const text = extractMessageText(message)?.trim();
|
|
136
|
+
if (text) {
|
|
137
|
+
return text;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
function streamDelta(previousText, nextText) {
|
|
143
|
+
if (!nextText) {
|
|
144
|
+
return { delta: "", nextText: previousText };
|
|
145
|
+
}
|
|
146
|
+
if (previousText && nextText.startsWith(previousText)) {
|
|
147
|
+
return { delta: nextText.slice(previousText.length), nextText };
|
|
148
|
+
}
|
|
149
|
+
if (nextText === previousText) {
|
|
150
|
+
return { delta: "", nextText: previousText };
|
|
151
|
+
}
|
|
152
|
+
return { delta: nextText, nextText };
|
|
153
|
+
}
|
|
154
|
+
function parseAgentSessionKey(sessionKey) {
|
|
155
|
+
const normalized = (sessionKey ?? "").trim().toLowerCase();
|
|
156
|
+
if (!normalized) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const parts = normalized.split(":").filter(Boolean);
|
|
160
|
+
if (parts.length < 3 || parts[0] !== "agent") {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const agentId = parts[1]?.trim();
|
|
164
|
+
const rest = parts.slice(2).join(":").trim();
|
|
165
|
+
if (!agentId || !rest) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return { agentId, rest };
|
|
169
|
+
}
|
|
170
|
+
function sameSessionKey(left, right) {
|
|
171
|
+
const normalizedLeft = (left ?? "").trim().toLowerCase();
|
|
172
|
+
const normalizedRight = (right ?? "").trim().toLowerCase();
|
|
173
|
+
if (!normalizedLeft || !normalizedRight) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (normalizedLeft === normalizedRight) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
const parsedLeft = parseAgentSessionKey(normalizedLeft);
|
|
180
|
+
const parsedRight = parseAgentSessionKey(normalizedRight);
|
|
181
|
+
if (parsedLeft && parsedRight) {
|
|
182
|
+
return parsedLeft.agentId === parsedRight.agentId && parsedLeft.rest === parsedRight.rest;
|
|
183
|
+
}
|
|
184
|
+
if (parsedLeft) {
|
|
185
|
+
return parsedLeft.rest === normalizedRight;
|
|
186
|
+
}
|
|
187
|
+
if (parsedRight) {
|
|
188
|
+
return normalizedLeft === parsedRight.rest;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
75
192
|
function splitConfigPath(path) {
|
|
76
193
|
return path
|
|
77
194
|
.split(".")
|
|
@@ -641,7 +758,12 @@ export class GatewayClient {
|
|
|
641
758
|
this.caps = Array.isArray(options.caps)
|
|
642
759
|
? options.caps.map((cap) => cap.trim()).filter(Boolean)
|
|
643
760
|
: [...DEFAULT_CAPS];
|
|
644
|
-
|
|
761
|
+
// Non-browser SDK clients should not send Origin by default. OpenClaw
|
|
762
|
+
// treats any Origin header as browser-originated and applies browser
|
|
763
|
+
// origin checks to the connection.
|
|
764
|
+
this.origin = typeof options.origin === "string" && options.origin.trim()
|
|
765
|
+
? options.origin.trim()
|
|
766
|
+
: undefined;
|
|
645
767
|
this.defaultTimeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
646
768
|
this.onHello = options.onHello;
|
|
647
769
|
this.onClose = options.onClose;
|
|
@@ -766,7 +888,8 @@ export class GatewayClient {
|
|
|
766
888
|
openSocket() {
|
|
767
889
|
if (this.closed || this.ws)
|
|
768
890
|
return;
|
|
769
|
-
|
|
891
|
+
const useBrowserSocket = "localStorage" in globalThis && typeof WebSocket !== "undefined";
|
|
892
|
+
if (!useBrowserSocket && typeof NodeWebSocket === "undefined") {
|
|
770
893
|
throw new Error("WebSocket is not available in this environment");
|
|
771
894
|
}
|
|
772
895
|
if (this.reconnectTimer) {
|
|
@@ -776,20 +899,38 @@ export class GatewayClient {
|
|
|
776
899
|
const wsUrl = this.token
|
|
777
900
|
? `${this.url}${this.url.includes("?") ? "&" : "?"}token=${encodeURIComponent(this.token)}`
|
|
778
901
|
: this.url;
|
|
779
|
-
const ws =
|
|
902
|
+
const ws = useBrowserSocket
|
|
903
|
+
? new WebSocket(wsUrl)
|
|
904
|
+
: new NodeWebSocket(wsUrl, this.origin ? { headers: { Origin: this.origin } } : undefined);
|
|
780
905
|
this.ws = ws;
|
|
781
|
-
|
|
906
|
+
if (useBrowserSocket) {
|
|
907
|
+
ws.onopen = () => {
|
|
908
|
+
this.queueConnect();
|
|
909
|
+
};
|
|
910
|
+
ws.onmessage = (event) => {
|
|
911
|
+
this.handleMessage(String(event.data ?? ""));
|
|
912
|
+
};
|
|
913
|
+
ws.onerror = () => {
|
|
914
|
+
// Close handling covers retries and surfaced errors.
|
|
915
|
+
};
|
|
916
|
+
ws.onclose = (event) => {
|
|
917
|
+
this.handleClose(ws, event.code ?? 1006, String(event.reason ?? ""));
|
|
918
|
+
};
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const nodeWs = ws;
|
|
922
|
+
nodeWs.on("open", () => {
|
|
782
923
|
this.queueConnect();
|
|
783
|
-
};
|
|
784
|
-
|
|
785
|
-
this.handleMessage(
|
|
786
|
-
};
|
|
787
|
-
|
|
924
|
+
});
|
|
925
|
+
nodeWs.on("message", (data) => {
|
|
926
|
+
this.handleMessage(typeof data === "string" ? data : data.toString());
|
|
927
|
+
});
|
|
928
|
+
nodeWs.on("error", () => {
|
|
788
929
|
// Close handling covers retries and surfaced errors.
|
|
789
|
-
};
|
|
790
|
-
|
|
791
|
-
this.handleClose(ws,
|
|
792
|
-
};
|
|
930
|
+
});
|
|
931
|
+
nodeWs.on("close", (code, reason) => {
|
|
932
|
+
this.handleClose(ws, code ?? 1006, reason?.toString() ?? "");
|
|
933
|
+
});
|
|
793
934
|
}
|
|
794
935
|
queueConnect() {
|
|
795
936
|
this.connectNonce = null;
|
|
@@ -1112,15 +1253,89 @@ export class GatewayClient {
|
|
|
1112
1253
|
return normalizeOpenClawConfigSchema(res) ?? { schema: {}, uiHints: {} };
|
|
1113
1254
|
}
|
|
1114
1255
|
async configPatch(patch) {
|
|
1115
|
-
await this.rpc("config.
|
|
1256
|
+
const { hash, baseHash } = await this.rpc("config.get");
|
|
1257
|
+
await this.rpc("config.patch", {
|
|
1258
|
+
raw: JSON.stringify(patch),
|
|
1259
|
+
baseHash: hash ?? baseHash ?? "",
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
async configApply(config) {
|
|
1263
|
+
const { hash, baseHash } = await this.rpc("config.get");
|
|
1264
|
+
await this.rpc("config.apply", {
|
|
1265
|
+
raw: JSON.stringify(config),
|
|
1266
|
+
baseHash: hash ?? baseHash ?? "",
|
|
1267
|
+
});
|
|
1116
1268
|
}
|
|
1117
1269
|
async configSet(config) {
|
|
1118
|
-
await this.rpc("config.
|
|
1270
|
+
const { hash, baseHash } = await this.rpc("config.get");
|
|
1271
|
+
await this.rpc("config.set", {
|
|
1272
|
+
raw: JSON.stringify(config),
|
|
1273
|
+
baseHash: hash ?? baseHash ?? "",
|
|
1274
|
+
});
|
|
1119
1275
|
}
|
|
1120
1276
|
async modelsList() {
|
|
1121
1277
|
const res = await this.rpc("models.list");
|
|
1122
1278
|
return res?.models ?? res ?? [];
|
|
1123
1279
|
}
|
|
1280
|
+
async waitReady(timeoutMs = 300_000, options = {}) {
|
|
1281
|
+
const retryIntervalMs = options.retryIntervalMs ?? 5_000;
|
|
1282
|
+
const probe = options.probe ?? "config";
|
|
1283
|
+
const deadline = Date.now() + timeoutMs;
|
|
1284
|
+
let lastError = null;
|
|
1285
|
+
while (!this.closed) {
|
|
1286
|
+
try {
|
|
1287
|
+
if (!this.connected) {
|
|
1288
|
+
await this.connect();
|
|
1289
|
+
}
|
|
1290
|
+
if (probe === "status") {
|
|
1291
|
+
return await this.status();
|
|
1292
|
+
}
|
|
1293
|
+
return await this.configGet();
|
|
1294
|
+
}
|
|
1295
|
+
catch (error) {
|
|
1296
|
+
lastError = error;
|
|
1297
|
+
this.close();
|
|
1298
|
+
if (Date.now() >= deadline) {
|
|
1299
|
+
break;
|
|
1300
|
+
}
|
|
1301
|
+
await new Promise((resolve) => setTimeout(resolve, retryIntervalMs));
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
const detail = lastError instanceof Error ? `: ${lastError.message}` : "";
|
|
1305
|
+
throw new Error(`Gateway readiness probe timed out after ${timeoutMs}ms${detail}`);
|
|
1306
|
+
}
|
|
1307
|
+
async channelsStatus(probe = false, timeoutMs) {
|
|
1308
|
+
const params = { probe };
|
|
1309
|
+
if (timeoutMs !== undefined)
|
|
1310
|
+
params.timeoutMs = timeoutMs;
|
|
1311
|
+
return await this.rpc("channels.status", params);
|
|
1312
|
+
}
|
|
1313
|
+
async channelsLogout(channel, accountId) {
|
|
1314
|
+
const params = { channel };
|
|
1315
|
+
if (accountId)
|
|
1316
|
+
params.accountId = accountId;
|
|
1317
|
+
return await this.rpc("channels.logout", params);
|
|
1318
|
+
}
|
|
1319
|
+
async webLoginStart(options = {}) {
|
|
1320
|
+
const params = {};
|
|
1321
|
+
if (options.force)
|
|
1322
|
+
params.force = true;
|
|
1323
|
+
if (options.timeoutMs !== undefined)
|
|
1324
|
+
params.timeoutMs = options.timeoutMs;
|
|
1325
|
+
if (options.verbose)
|
|
1326
|
+
params.verbose = true;
|
|
1327
|
+
if (options.accountId)
|
|
1328
|
+
params.accountId = options.accountId;
|
|
1329
|
+
return await this.rpc("web.login.start", params, 30_000);
|
|
1330
|
+
}
|
|
1331
|
+
async webLoginWait(options = {}) {
|
|
1332
|
+
const params = {};
|
|
1333
|
+
if (options.timeoutMs !== undefined)
|
|
1334
|
+
params.timeoutMs = options.timeoutMs;
|
|
1335
|
+
if (options.accountId)
|
|
1336
|
+
params.accountId = options.accountId;
|
|
1337
|
+
return await this.rpc("web.login.wait", params, CHAT_TIMEOUT);
|
|
1338
|
+
}
|
|
1124
1339
|
// ---------------------------------------------------------------------------
|
|
1125
1340
|
// Sessions
|
|
1126
1341
|
// ---------------------------------------------------------------------------
|
|
@@ -1148,6 +1363,7 @@ export class GatewayClient {
|
|
|
1148
1363
|
async sendChat(message, sessionKey = "main", agentId, attachments) {
|
|
1149
1364
|
const params = {
|
|
1150
1365
|
message,
|
|
1366
|
+
deliver: false,
|
|
1151
1367
|
sessionKey,
|
|
1152
1368
|
idempotencyKey: makeId(),
|
|
1153
1369
|
};
|
|
@@ -1167,90 +1383,151 @@ export class GatewayClient {
|
|
|
1167
1383
|
if (!this.connected || !this.ws) {
|
|
1168
1384
|
throw new Error("Not connected");
|
|
1169
1385
|
}
|
|
1170
|
-
const
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
id,
|
|
1174
|
-
method: "chat.send",
|
|
1175
|
-
params: {
|
|
1176
|
-
message,
|
|
1177
|
-
sessionKey,
|
|
1178
|
-
idempotencyKey: makeId(),
|
|
1179
|
-
},
|
|
1180
|
-
};
|
|
1181
|
-
const events = [];
|
|
1386
|
+
const idempotencyKey = makeId();
|
|
1387
|
+
const acceptedRunIds = new Set([idempotencyKey]);
|
|
1388
|
+
const queuedEvents = [];
|
|
1182
1389
|
let resolveWait = null;
|
|
1183
|
-
let
|
|
1184
|
-
let
|
|
1185
|
-
// Temporary event handler for chat events
|
|
1390
|
+
let streamedDisplayText = false;
|
|
1391
|
+
let lastLegacyText = "";
|
|
1186
1392
|
const handler = (evt) => {
|
|
1187
|
-
if (evt.event?.startsWith("chat.")) {
|
|
1188
|
-
|
|
1393
|
+
if (evt.event === "chat" || evt.event?.startsWith("chat.")) {
|
|
1394
|
+
queuedEvents.push(evt);
|
|
1395
|
+
const waiter = resolveWait;
|
|
1396
|
+
resolveWait = null;
|
|
1397
|
+
waiter?.();
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
this.eventHandlers.add(handler);
|
|
1401
|
+
try {
|
|
1402
|
+
const ack = await this.rpc("chat.send", {
|
|
1403
|
+
message,
|
|
1404
|
+
deliver: false,
|
|
1405
|
+
sessionKey,
|
|
1406
|
+
idempotencyKey,
|
|
1407
|
+
}, CHAT_TIMEOUT);
|
|
1408
|
+
const serverRunId = typeof ack?.runId === "string" ? ack.runId.trim() : "";
|
|
1409
|
+
if (serverRunId) {
|
|
1410
|
+
acceptedRunIds.add(serverRunId);
|
|
1411
|
+
}
|
|
1412
|
+
let deadline = Date.now() + CHAT_TIMEOUT;
|
|
1413
|
+
while (Date.now() < deadline) {
|
|
1414
|
+
if (queuedEvents.length === 0) {
|
|
1415
|
+
const remainingMs = Math.max(100, Math.min(1000, deadline - Date.now()));
|
|
1416
|
+
await new Promise((resolve) => {
|
|
1417
|
+
const timer = setTimeout(() => {
|
|
1418
|
+
if (resolveWait === release) {
|
|
1419
|
+
resolveWait = null;
|
|
1420
|
+
}
|
|
1421
|
+
resolve();
|
|
1422
|
+
}, remainingMs);
|
|
1423
|
+
const release = () => {
|
|
1424
|
+
clearTimeout(timer);
|
|
1425
|
+
resolve();
|
|
1426
|
+
};
|
|
1427
|
+
resolveWait = release;
|
|
1428
|
+
});
|
|
1429
|
+
continue;
|
|
1430
|
+
}
|
|
1431
|
+
const evt = queuedEvents.shift();
|
|
1432
|
+
const payload = asRecord(evt.payload) ?? {};
|
|
1433
|
+
const payloadRunId = typeof payload.runId === "string" ? payload.runId.trim() : "";
|
|
1434
|
+
const payloadSessionKey = typeof payload.sessionKey === "string" ? payload.sessionKey.trim() : "";
|
|
1435
|
+
if (payloadRunId && !acceptedRunIds.has(payloadRunId)) {
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
if (payloadSessionKey && !sameSessionKey(payloadSessionKey, sessionKey)) {
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1441
|
+
deadline = Date.now() + CHAT_TIMEOUT;
|
|
1189
1442
|
if (evt.event === "chat.content") {
|
|
1190
|
-
|
|
1443
|
+
const text = typeof payload.text === "string" ? payload.text : "";
|
|
1444
|
+
if (text) {
|
|
1445
|
+
streamedDisplayText = true;
|
|
1446
|
+
yield { type: "content", text };
|
|
1447
|
+
}
|
|
1448
|
+
continue;
|
|
1191
1449
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1450
|
+
if (evt.event === "chat.thinking") {
|
|
1451
|
+
yield { type: "thinking", text: typeof payload.text === "string" ? payload.text : "" };
|
|
1452
|
+
continue;
|
|
1194
1453
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1454
|
+
if (evt.event === "chat.tool_call") {
|
|
1455
|
+
yield { type: "tool_call", data: payload };
|
|
1456
|
+
continue;
|
|
1197
1457
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1458
|
+
if (evt.event === "chat.tool_result") {
|
|
1459
|
+
yield { type: "tool_result", data: payload };
|
|
1460
|
+
continue;
|
|
1200
1461
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1462
|
+
if (evt.event === "chat.done") {
|
|
1463
|
+
yield { type: "done", data: payload };
|
|
1464
|
+
return;
|
|
1204
1465
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1466
|
+
if (evt.event === "chat.error") {
|
|
1467
|
+
yield {
|
|
1468
|
+
type: "error",
|
|
1469
|
+
text: typeof payload.message === "string" ? payload.message : "Unknown error",
|
|
1470
|
+
data: payload,
|
|
1471
|
+
};
|
|
1472
|
+
return;
|
|
1208
1473
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
};
|
|
1212
|
-
this.eventHandlers.add(handler);
|
|
1213
|
-
// Also listen for the RPC response
|
|
1214
|
-
const timer = setTimeout(() => {
|
|
1215
|
-
this.pending.delete(id);
|
|
1216
|
-
error = new Error("Chat timeout");
|
|
1217
|
-
done = true;
|
|
1218
|
-
resolveWait?.();
|
|
1219
|
-
}, CHAT_TIMEOUT);
|
|
1220
|
-
this.pending.set(id, {
|
|
1221
|
-
resolve: () => {
|
|
1222
|
-
clearTimeout(timer);
|
|
1223
|
-
done = true;
|
|
1224
|
-
resolveWait?.();
|
|
1225
|
-
},
|
|
1226
|
-
reject: (rejection) => {
|
|
1227
|
-
clearTimeout(timer);
|
|
1228
|
-
error = rejection instanceof Error ? rejection : new Error(String(rejection));
|
|
1229
|
-
done = true;
|
|
1230
|
-
resolveWait?.();
|
|
1231
|
-
},
|
|
1232
|
-
timer,
|
|
1233
|
-
});
|
|
1234
|
-
this.ws.send(JSON.stringify(req));
|
|
1235
|
-
try {
|
|
1236
|
-
while (!done || events.length > 0) {
|
|
1237
|
-
if (events.length > 0) {
|
|
1238
|
-
yield events.shift();
|
|
1474
|
+
if (evt.event !== "chat") {
|
|
1475
|
+
continue;
|
|
1239
1476
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1477
|
+
const state = typeof payload.state === "string" ? payload.state.trim().toLowerCase() : "";
|
|
1478
|
+
const currentText = extractMessageText(payload.message) ?? "";
|
|
1479
|
+
if (state === "delta") {
|
|
1480
|
+
const streamed = streamDelta(lastLegacyText, currentText);
|
|
1481
|
+
lastLegacyText = streamed.nextText;
|
|
1482
|
+
if (streamed.delta) {
|
|
1483
|
+
streamedDisplayText = true;
|
|
1484
|
+
yield { type: "content", text: streamed.delta, data: payload };
|
|
1485
|
+
}
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
if (state === "final") {
|
|
1489
|
+
if (currentText) {
|
|
1490
|
+
const streamed = streamDelta(lastLegacyText, currentText);
|
|
1491
|
+
lastLegacyText = streamed.nextText;
|
|
1492
|
+
if (streamed.delta) {
|
|
1493
|
+
streamedDisplayText = true;
|
|
1494
|
+
yield { type: "content", text: streamed.delta, data: payload };
|
|
1495
|
+
}
|
|
1496
|
+
yield { type: "done", data: payload };
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
if (streamedDisplayText || lastLegacyText) {
|
|
1500
|
+
yield { type: "done", data: payload };
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
const historyText = latestHistoryAssistantText(await this.chatHistory(sessionKey, 20), acceptedRunIds);
|
|
1504
|
+
if (historyText) {
|
|
1505
|
+
yield { type: "content", text: historyText, data: payload };
|
|
1506
|
+
}
|
|
1507
|
+
yield { type: "done", data: payload };
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
if (state === "error" || state === "aborted") {
|
|
1511
|
+
if (currentText) {
|
|
1512
|
+
const streamed = streamDelta(lastLegacyText, currentText);
|
|
1513
|
+
lastLegacyText = streamed.nextText;
|
|
1514
|
+
if (streamed.delta) {
|
|
1515
|
+
streamedDisplayText = true;
|
|
1516
|
+
yield { type: "content", text: streamed.delta, data: payload };
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
yield {
|
|
1520
|
+
type: "error",
|
|
1521
|
+
text: typeof payload.errorMessage === "string" ? payload.errorMessage : state,
|
|
1522
|
+
data: payload,
|
|
1523
|
+
};
|
|
1524
|
+
return;
|
|
1245
1525
|
}
|
|
1246
1526
|
}
|
|
1247
|
-
|
|
1248
|
-
throw error;
|
|
1527
|
+
throw new Error("Streaming chat.send timed out");
|
|
1249
1528
|
}
|
|
1250
1529
|
finally {
|
|
1251
|
-
clearTimeout(timer);
|
|
1252
1530
|
this.eventHandlers.delete(handler);
|
|
1253
|
-
this.pending.delete(id);
|
|
1254
1531
|
}
|
|
1255
1532
|
}
|
|
1256
1533
|
// ---------------------------------------------------------------------------
|