@openclaw/gateway-client 2026.9.6 → 2026.9.7
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 +78 -3
- package/dist/browser.d.mts +23 -3
- package/dist/browser.mjs +51 -2
- package/dist/index.d.mts +21 -3
- package/dist/index.mjs +124 -77
- package/dist/{protocol-client-D6mJuY1_.d.mts → protocol-client-DT451Msj.d.mts} +18 -1
- package/dist/{readiness-ChxbBSMj.d.mts → readiness-Cj5C24o2.d.mts} +15 -23
- package/dist/readiness.d.mts +1 -1
- package/dist/scope-upgrade.d.mts +1 -0
- package/dist/scope-upgrade.mjs +12 -2
- package/dist/{session-subscriptions-C91CEXwY.d.mts → session-subscriptions-Bcq6_wtR.d.mts} +15 -3
- package/dist/{session-subscriptions-DUpRfWJb.mjs → session-subscriptions-LtlX9oRQ.mjs} +306 -116
- package/dist/timeouts.mjs +1 -1
- package/package.json +3 -3
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, clearGatewayConnectTimeout, resolveSafeTimeoutDelayMs, startGatewayConnectTimeout } from "./timeouts.mjs";
|
|
2
2
|
import { ConnectErrorDetailCodes, readConnectErrorDetailCode, readConnectErrorRecoveryAdvice, readPairingConnectErrorDetails } from "@openclaw/gateway-protocol/connect-error-details";
|
|
3
3
|
import { isGatewayEventFrame, isGatewayResponseFrame } from "@openclaw/gateway-protocol/frame-guards";
|
|
4
|
+
import { GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS, isGatewaySuspendUnavailableError } from "@openclaw/gateway-protocol/restart-unavailable";
|
|
4
5
|
import { GATEWAY_ASSISTANT_ERROR_FALLBACK_TEXT } from "@openclaw/gateway-protocol/gateway-error-details";
|
|
5
6
|
//#region packages/gateway-client/src/device-auth.ts
|
|
6
7
|
function normalizeDeviceMetadataForAuth(value) {
|
|
7
8
|
if (typeof value !== "string") return "";
|
|
8
|
-
|
|
9
|
-
if (!trimmed) return "";
|
|
10
|
-
return trimmed.replace(/[A-Z]/g, (char) => String.fromCharCode(char.charCodeAt(0) + 32));
|
|
9
|
+
return value.trim().replace(/[A-Z]/g, (char) => String.fromCharCode(char.charCodeAt(0) + 32));
|
|
11
10
|
}
|
|
12
11
|
function buildDeviceAuthPayload(params) {
|
|
13
12
|
const scopes = params.scopes.join(",");
|
|
@@ -44,6 +43,63 @@ function buildDeviceAuthPayloadV3(params) {
|
|
|
44
43
|
].join("|");
|
|
45
44
|
}
|
|
46
45
|
//#endregion
|
|
46
|
+
//#region packages/normalization-core/src/record-coerce.ts
|
|
47
|
+
/** Type guard for non-array object records at browser-safe boundaries. */
|
|
48
|
+
function isRecord(value) {
|
|
49
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
/** Coerces object-like values to records, falling back to an empty record. */
|
|
52
|
+
function asRecord(value) {
|
|
53
|
+
return asOptionalObjectRecord(value) ?? {};
|
|
54
|
+
}
|
|
55
|
+
/** Returns a non-array record or null. */
|
|
56
|
+
function asNullableRecord(value) {
|
|
57
|
+
return isRecord(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
/** Returns any object-backed record, including arrays, or undefined. */
|
|
60
|
+
function asOptionalObjectRecord(value) {
|
|
61
|
+
return value && typeof value === "object" ? value : void 0;
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region packages/gateway-client/src/chat-stream-message.ts
|
|
65
|
+
/** A snapshot includes its delta; append-only frames require an existing baseline. */
|
|
66
|
+
function mergeChatStreamMessage(previous, payload) {
|
|
67
|
+
if (payload.message !== void 0) return payload.message;
|
|
68
|
+
const delta = payload.deltaText;
|
|
69
|
+
if (typeof delta !== "string") return previous;
|
|
70
|
+
const replace = payload.replace === true;
|
|
71
|
+
if (!delta && !replace) return previous;
|
|
72
|
+
if (!isRecord(previous) && !replace) return;
|
|
73
|
+
const message = isRecord(previous) ? previous : { role: "assistant" };
|
|
74
|
+
if (typeof message.content === "string") return {
|
|
75
|
+
...message,
|
|
76
|
+
content: replace ? delta : message.content + delta
|
|
77
|
+
};
|
|
78
|
+
const content = Array.isArray(message.content) ? [...message.content] : [];
|
|
79
|
+
const isText = (block) => isRecord(block) && block.type === "text";
|
|
80
|
+
if (replace) return {
|
|
81
|
+
...message,
|
|
82
|
+
content: [{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: delta
|
|
85
|
+
}, ...content.filter((block) => !isText(block))]
|
|
86
|
+
};
|
|
87
|
+
const index = content.findLastIndex(isText);
|
|
88
|
+
const block = content[index];
|
|
89
|
+
if (isText(block)) content[index] = {
|
|
90
|
+
...block,
|
|
91
|
+
text: `${typeof block.text === "string" ? block.text : ""}${delta}`
|
|
92
|
+
};
|
|
93
|
+
else content.push({
|
|
94
|
+
type: "text",
|
|
95
|
+
text: delta
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
...message,
|
|
99
|
+
content
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
47
103
|
//#region packages/gateway-client/src/connect-auth.ts
|
|
48
104
|
function normalized(value) {
|
|
49
105
|
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
@@ -114,21 +170,19 @@ var GatewayBrowserDeviceAuthLifecycle = class {
|
|
|
114
170
|
deviceId: identity.deviceId,
|
|
115
171
|
role: params.role
|
|
116
172
|
}) : null;
|
|
117
|
-
const storedValue = stored?.token;
|
|
118
173
|
const selectedAuth = selectGatewayConnectAuth({
|
|
119
174
|
token: params.token,
|
|
120
175
|
bootstrapToken: params.bootstrapToken,
|
|
121
176
|
password: params.password,
|
|
122
|
-
storedToken:
|
|
177
|
+
storedToken: stored?.token,
|
|
123
178
|
storedScopes: stored?.scopes,
|
|
124
179
|
pendingDeviceTokenRetry: params.pendingDeviceTokenRetry,
|
|
125
180
|
trustedDeviceTokenRetry: params.trustedDeviceTokenRetry,
|
|
126
181
|
preferBootstrapToken: params.preferBootstrapToken
|
|
127
182
|
});
|
|
128
|
-
const { usingStoredDeviceToken } = selectedAuth;
|
|
129
183
|
const scopes = resolveGatewayConnectScopes({
|
|
130
184
|
requestedScopes: selectedAuth.authBootstrapToken ? params.bootstrapScopes ? [...params.bootstrapScopes] : void 0 : void 0,
|
|
131
|
-
usingStoredDeviceToken,
|
|
185
|
+
usingStoredDeviceToken: selectedAuth.usingStoredDeviceToken,
|
|
132
186
|
storedScopes: selectedAuth.storedScopes,
|
|
133
187
|
defaultScopes: params.defaultScopes
|
|
134
188
|
});
|
|
@@ -143,10 +197,6 @@ var GatewayBrowserDeviceAuthLifecycle = class {
|
|
|
143
197
|
const signedAtMs = params.challengeTs === void 0 ? this.deps.nowMs?.() ?? Date.now() : params.challengeTs;
|
|
144
198
|
if (typeof signedAtMs !== "number" || !Number.isSafeInteger(signedAtMs) || signedAtMs < 0) throw new Error("gateway connect challenge timestamp invalid");
|
|
145
199
|
const nonce = params.nonce ?? "";
|
|
146
|
-
const { authBootstrapToken: primary, signatureToken: signed } = selectedAuth;
|
|
147
|
-
let token = null;
|
|
148
|
-
if (primary) token = primary;
|
|
149
|
-
else if (signed) token = signed;
|
|
150
200
|
const payload = buildDeviceAuthPayloadV3({
|
|
151
201
|
deviceId: identity.deviceId,
|
|
152
202
|
clientId: params.client.id,
|
|
@@ -154,7 +204,7 @@ var GatewayBrowserDeviceAuthLifecycle = class {
|
|
|
154
204
|
role: params.role,
|
|
155
205
|
scopes,
|
|
156
206
|
signedAtMs,
|
|
157
|
-
token,
|
|
207
|
+
token: selectedAuth.authBootstrapToken || selectedAuth.signatureToken || null,
|
|
158
208
|
nonce,
|
|
159
209
|
platform: params.client.platform,
|
|
160
210
|
deviceFamily: params.client.deviceFamily
|
|
@@ -245,9 +295,12 @@ function computeBackoff(policy, attempt) {
|
|
|
245
295
|
const jitter = base * policy.jitter * Math.random();
|
|
246
296
|
return Math.min(policy.maxMs, Math.round(base + jitter));
|
|
247
297
|
}
|
|
298
|
+
function resolveSleepDelayMs(ms) {
|
|
299
|
+
return Number.isFinite(ms) && ms > 0 ? Math.min(Math.max(Math.floor(ms), 1), MAX_TIMER_TIMEOUT_MS) : 0;
|
|
300
|
+
}
|
|
248
301
|
async function sleepWithAbort(ms, abortSignal, options = {}) {
|
|
249
|
-
|
|
250
|
-
|
|
302
|
+
const delayMs = resolveSleepDelayMs(ms);
|
|
303
|
+
if (delayMs === 0) return;
|
|
251
304
|
await new Promise((resolve, reject) => {
|
|
252
305
|
let settled = false;
|
|
253
306
|
let timer = null;
|
|
@@ -330,21 +383,18 @@ const defaultSleep = async (ms) => {
|
|
|
330
383
|
} while (remainingMs > 0);
|
|
331
384
|
};
|
|
332
385
|
function clampNumber(value, fallback, min, max) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return Math.min(Math.max(next, min ?? Number.NEGATIVE_INFINITY), max ?? Number.POSITIVE_INFINITY);
|
|
386
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
387
|
+
return Math.min(Math.max(value, min ?? Number.NEGATIVE_INFINITY), max ?? Number.POSITIVE_INFINITY);
|
|
336
388
|
}
|
|
337
389
|
function resolveAttemptCount(value, fallback) {
|
|
338
|
-
return Math.max(1, Math.round(
|
|
390
|
+
return Math.max(1, Math.round(clampNumber(value, fallback)));
|
|
339
391
|
}
|
|
340
392
|
function resolveRetryDelayMs(value) {
|
|
341
|
-
|
|
342
|
-
return Math.min(Math.max(Math.round(finite), 0), MAX_TIMER_TIMEOUT_MS);
|
|
393
|
+
return Math.min(Math.max(Math.round(value === Number.POSITIVE_INFINITY ? MAX_TIMER_TIMEOUT_MS : Number.isFinite(value) ? value : 0), 0), MAX_TIMER_TIMEOUT_MS);
|
|
343
394
|
}
|
|
344
395
|
function resolveJitterConfig(value, fallback) {
|
|
345
396
|
if (value === "full") return "full";
|
|
346
|
-
|
|
347
|
-
return fraction === void 0 ? fallback : Math.min(Math.max(fraction, 0), 1);
|
|
397
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.min(Math.max(value, 0), 1) : fallback;
|
|
348
398
|
}
|
|
349
399
|
function resolveRetryConfig(defaults = DEFAULT_RETRY_CONFIG, overrides) {
|
|
350
400
|
const attempts = resolveAttemptCount(overrides?.attempts, defaults.attempts);
|
|
@@ -499,6 +549,20 @@ var GatewayPendingRequests = class {
|
|
|
499
549
|
this.pending = /* @__PURE__ */ new Map();
|
|
500
550
|
this.requestSequence = 0;
|
|
501
551
|
}
|
|
552
|
+
setSuspensionPhase(phase) {
|
|
553
|
+
if (phase === "accepting") this.resumeIdentityRequests();
|
|
554
|
+
else if (phase === "preparing" || phase === "draining" || phase === "prepared") this.pauseIdentityRequests();
|
|
555
|
+
}
|
|
556
|
+
pauseIdentityRequests(retryAfterMs = GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS) {
|
|
557
|
+
clearTimeout(this.suspensionTimer);
|
|
558
|
+
this.suspensionTimer = setTimeout(() => this.resumeIdentityRequests(), resolveSafeTimeoutDelayMs(Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? retryAfterMs : GATEWAY_SUSPEND_IDENTITY_RETRY_AFTER_MS));
|
|
559
|
+
this.suspensionTimer.unref?.();
|
|
560
|
+
}
|
|
561
|
+
resumeIdentityRequests() {
|
|
562
|
+
clearTimeout(this.suspensionTimer);
|
|
563
|
+
this.suspensionTimer = void 0;
|
|
564
|
+
for (const pending of this.pending.values()) if (pending.waitingForResume) pending.resend?.();
|
|
565
|
+
}
|
|
502
566
|
get hasPending() {
|
|
503
567
|
return this.pending.size > 0;
|
|
504
568
|
}
|
|
@@ -562,15 +626,27 @@ var GatewayPendingRequests = class {
|
|
|
562
626
|
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
563
627
|
this.pending.set(id, pending);
|
|
564
628
|
try {
|
|
565
|
-
|
|
629
|
+
const frame = JSON.stringify({
|
|
566
630
|
type: "req",
|
|
567
631
|
id,
|
|
568
632
|
method,
|
|
569
633
|
params
|
|
570
|
-
})
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
634
|
+
});
|
|
635
|
+
const send = () => {
|
|
636
|
+
if (this.pending.get(id) !== pending) return;
|
|
637
|
+
pending.waitingForResume = Boolean(pending.resend && this.suspensionTimer);
|
|
638
|
+
if (pending.waitingForResume) return;
|
|
639
|
+
try {
|
|
640
|
+
sender.send(frame);
|
|
641
|
+
if (this.pending.get(id) !== pending) return;
|
|
642
|
+
requestSent = true;
|
|
643
|
+
this.invoke("sent", () => options?.onSent?.(id));
|
|
644
|
+
} catch (error) {
|
|
645
|
+
if (retire("CLIENT_SEND_ERROR")) reject(error instanceof Error ? error : new Error(String(error)));
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
if (method === "agent.identity.get") pending.resend = send;
|
|
649
|
+
send();
|
|
574
650
|
} catch (error) {
|
|
575
651
|
if (retire("CLIENT_SEND_ERROR")) reject(error instanceof Error ? error : new Error(String(error)));
|
|
576
652
|
}
|
|
@@ -587,6 +663,11 @@ var GatewayPendingRequests = class {
|
|
|
587
663
|
}
|
|
588
664
|
return;
|
|
589
665
|
}
|
|
666
|
+
if (!frame.ok && pending.resend && !pending.acceptedNotified && frame.error?.code === "UNAVAILABLE" && frame.error.retryable === true && isGatewaySuspendUnavailableError(frame.error)) {
|
|
667
|
+
this.pauseIdentityRequests(frame.error.retryAfterMs);
|
|
668
|
+
pending.waitingForResume = true;
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
590
671
|
this.pending.delete(frame.id);
|
|
591
672
|
pending.cleanup?.();
|
|
592
673
|
if (frame.ok) {
|
|
@@ -600,6 +681,8 @@ var GatewayPendingRequests = class {
|
|
|
600
681
|
pending.reject(error);
|
|
601
682
|
}
|
|
602
683
|
flush(error) {
|
|
684
|
+
clearTimeout(this.suspensionTimer);
|
|
685
|
+
this.suspensionTimer = void 0;
|
|
603
686
|
const retired = this.pending;
|
|
604
687
|
this.pending = /* @__PURE__ */ new Map();
|
|
605
688
|
this.requestSequence = 0;
|
|
@@ -615,19 +698,15 @@ var GatewayPendingRequests = class {
|
|
|
615
698
|
}
|
|
616
699
|
finishTiming(id, pending, ok, errorCode) {
|
|
617
700
|
const endedAtMs = this.opts.nowMs();
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
});
|
|
628
|
-
} catch (error) {
|
|
629
|
-
this.opts.onCallbackError?.("request timing", error);
|
|
630
|
-
}
|
|
701
|
+
this.invoke("request timing", () => this.opts.onTiming?.({
|
|
702
|
+
id,
|
|
703
|
+
method: pending.method,
|
|
704
|
+
ok,
|
|
705
|
+
durationMs: Math.max(0, endedAtMs - pending.startedAtMs),
|
|
706
|
+
startedAtMs: pending.startedAtMs,
|
|
707
|
+
endedAtMs,
|
|
708
|
+
errorCode
|
|
709
|
+
}));
|
|
631
710
|
}
|
|
632
711
|
invoke(label, callback) {
|
|
633
712
|
try {
|
|
@@ -872,6 +951,7 @@ var GatewayProtocolClient = class {
|
|
|
872
951
|
this.request("connect", this.opts.buildConnectParams(plan)).then((hello) => {
|
|
873
952
|
if (!this.isConnectCurrent(socket, generation)) return;
|
|
874
953
|
this.helloReceived = true;
|
|
954
|
+
this.requests.setSuspensionPhase(hello.snapshot?.suspension?.phase);
|
|
875
955
|
this.clearHandshakeTimer();
|
|
876
956
|
this.connectFailure = void 0;
|
|
877
957
|
this.reconnectSupervisor.reset();
|
|
@@ -910,7 +990,7 @@ var GatewayProtocolClient = class {
|
|
|
910
990
|
});
|
|
911
991
|
}
|
|
912
992
|
handleMessage(socket, generation, raw) {
|
|
913
|
-
if (!this.isActive(socket, generation)) return;
|
|
993
|
+
if (!this.isActive(socket, generation) || this.connectionAbort?.signal.aborted) return;
|
|
914
994
|
let parsed;
|
|
915
995
|
try {
|
|
916
996
|
parsed = JSON.parse(raw);
|
|
@@ -943,26 +1023,34 @@ var GatewayProtocolClient = class {
|
|
|
943
1023
|
if (seq !== null) {
|
|
944
1024
|
if (this.lastSeq !== null && seq > this.lastSeq + 1) {
|
|
945
1025
|
const expected = this.lastSeq + 1;
|
|
1026
|
+
const state = asRecord(parsed.payload).state;
|
|
1027
|
+
if (parsed.event === "chat" && (state === "final" || state === "error" || state === "aborted")) this.dispatchEvent(socket, generation, parsed);
|
|
1028
|
+
if (!this.isActive(socket, generation) || this.connectionAbort?.signal.aborted) return;
|
|
946
1029
|
this.invoke("gap", () => this.opts.onGap?.({
|
|
947
1030
|
expected,
|
|
948
1031
|
received: seq
|
|
949
1032
|
}));
|
|
950
|
-
if (
|
|
1033
|
+
if (this.isActive(socket, generation) && !this.connectionAbort?.signal.aborted) this.closeSocket(4e3, "event sequence gap");
|
|
1034
|
+
return;
|
|
951
1035
|
}
|
|
952
1036
|
this.lastSeq = seq;
|
|
953
1037
|
}
|
|
954
|
-
|
|
955
|
-
this.invoke("event", () => this.opts.onEvent?.(parsed));
|
|
956
|
-
for (const [listener, subscription] of listeners) {
|
|
957
|
-
if (!this.isActive(socket, generation)) return;
|
|
958
|
-
if (this.listeners.isCurrent(listener, subscription)) this.invoke("event listener", () => listener(parsed));
|
|
959
|
-
}
|
|
1038
|
+
this.dispatchEvent(socket, generation, parsed);
|
|
960
1039
|
return;
|
|
961
1040
|
}
|
|
962
1041
|
if (!isGatewayResponseFrame(parsed)) return;
|
|
963
1042
|
this.opts.onActivity?.();
|
|
964
1043
|
this.requests.handleResponse(parsed);
|
|
965
1044
|
}
|
|
1045
|
+
dispatchEvent(socket, generation, event) {
|
|
1046
|
+
const listeners = this.listeners.snapshot();
|
|
1047
|
+
if (event.event === "gateway.suspension" && typeof event.payload === "object" && event.payload !== null && "phase" in event.payload) this.requests.setSuspensionPhase(event.payload.phase);
|
|
1048
|
+
this.invoke("event", () => this.opts.onEvent?.(event));
|
|
1049
|
+
for (const [listener, subscription] of listeners) {
|
|
1050
|
+
if (!this.isActive(socket, generation) || this.connectionAbort?.signal.aborted) return;
|
|
1051
|
+
if (this.listeners.isCurrent(listener, subscription)) this.invoke("event listener", () => listener(event));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
966
1054
|
handleClose(socket, generation, code, reason) {
|
|
967
1055
|
if (this.socket !== socket) {
|
|
968
1056
|
if (this.stoppedSocket?.socket === socket) {
|
|
@@ -1008,13 +1096,23 @@ var GatewayProtocolClient = class {
|
|
|
1008
1096
|
const retry = this.reconnectSupervisor.next();
|
|
1009
1097
|
if (!retry) return;
|
|
1010
1098
|
this.reconnectSignal = retry.signal;
|
|
1011
|
-
|
|
1099
|
+
let delayMs = overrideMs;
|
|
1100
|
+
if (delayMs === void 0) {
|
|
1101
|
+
const base = Math.max(retry.delayMs, minimumMs);
|
|
1102
|
+
const ceiling = Math.max(this.opts.reconnect.maxMs, Math.min(Number.MAX_VALUE, minimumMs * 1.2));
|
|
1103
|
+
const lower = Math.max(minimumMs, Math.min(base, ceiling / 1.2));
|
|
1104
|
+
const upper = Math.min(base * 1.2, ceiling);
|
|
1105
|
+
delayMs = Math.ceil(lower + Math.random() * (upper - lower));
|
|
1106
|
+
}
|
|
1107
|
+
delayMs = resolveSleepDelayMs(delayMs);
|
|
1108
|
+
sleepWithAbort(delayMs, retry.signal).then(() => {
|
|
1012
1109
|
if (this.reconnectSignal !== retry.signal) return;
|
|
1013
1110
|
this.reconnectSignal = null;
|
|
1014
1111
|
this.invoke("reconnect", () => this.connect());
|
|
1015
1112
|
}, () => {
|
|
1016
1113
|
if (this.reconnectSignal === retry.signal) this.reconnectSignal = null;
|
|
1017
1114
|
});
|
|
1115
|
+
this.invoke("reconnect scheduled", () => this.opts.onReconnectScheduled?.(delayMs, retry.signal));
|
|
1018
1116
|
}
|
|
1019
1117
|
closeContext() {
|
|
1020
1118
|
return {
|
|
@@ -1083,16 +1181,6 @@ function shouldPauseGatewayReconnect(params) {
|
|
|
1083
1181
|
return NON_RECOVERABLE_AUTH_ERRORS.has(code) || params.protocolMismatchIsTerminal === true && code === ConnectErrorDetailCodes.PROTOCOL_MISMATCH || params.clientVersionMismatchIsTerminal === true && code === ConnectErrorDetailCodes.CLIENT_VERSION_MISMATCH;
|
|
1084
1182
|
}
|
|
1085
1183
|
//#endregion
|
|
1086
|
-
//#region packages/normalization-core/src/record-coerce.ts
|
|
1087
|
-
/** Type guard for non-array object records at browser-safe boundaries. */
|
|
1088
|
-
function isRecord(value) {
|
|
1089
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1090
|
-
}
|
|
1091
|
-
/** Returns a non-array record or null. */
|
|
1092
|
-
function asNullableRecord(value) {
|
|
1093
|
-
return isRecord(value) ? value : null;
|
|
1094
|
-
}
|
|
1095
|
-
//#endregion
|
|
1096
1184
|
//#region packages/normalization-core/src/stable-stringify.ts
|
|
1097
1185
|
const preserveString = (value) => value;
|
|
1098
1186
|
/** Deterministically stringifies values, optionally normalizing strings before key ordering. */
|
|
@@ -1346,12 +1434,7 @@ function isSessionProjectionErrorMessage(message, errorMessage) {
|
|
|
1346
1434
|
//#endregion
|
|
1347
1435
|
//#region packages/gateway-client/src/session-projection-final-identity.ts
|
|
1348
1436
|
/** Terminal identity rules used to reconcile live and durable assistant projections. */
|
|
1349
|
-
/**
|
|
1350
|
-
* The class of rows the #148297 relaxation newly admits as finals: persisted
|
|
1351
|
-
* with the run's terminal tool stop reason while carrying no tool-call
|
|
1352
|
-
* content. Both adoption directions and every position rule scope to this
|
|
1353
|
-
* predicate, so pre-existing match semantics stay untouched.
|
|
1354
|
-
*/
|
|
1437
|
+
/** A run's selected text reply can inherit its terminal tool stop reason. */
|
|
1355
1438
|
function isToolUsePersistedFinalRow(message) {
|
|
1356
1439
|
return asNullableRecord(message)?.["stopReason"] === "toolUse" && !isSessionProjectionToolContinuation(message);
|
|
1357
1440
|
}
|
|
@@ -1448,22 +1531,15 @@ function findUniqueSnapshotTerminalMatch(current, matches, run, snapshot) {
|
|
|
1448
1531
|
});
|
|
1449
1532
|
snapshotIndexes = indexes;
|
|
1450
1533
|
};
|
|
1451
|
-
const
|
|
1452
|
-
const runId = current.identity?.runId;
|
|
1453
|
-
if (!runId || entry.identity?.runId !== runId) return false;
|
|
1454
|
-
ensureRunIndexes();
|
|
1455
|
-
const entryIndex = snapshotIndexes?.get(entry);
|
|
1456
|
-
return entryIndex !== void 0 && firstUserIndex >= 0 && firstUserIndex < entryIndex && lastAssistantIndex <= entryIndex;
|
|
1457
|
-
};
|
|
1458
|
-
const isLastSameRunAssistantRow = (entry) => {
|
|
1534
|
+
const hasTerminalSnapshotPosition = (entry, requireUser) => {
|
|
1459
1535
|
const runId = current.identity?.runId;
|
|
1460
1536
|
if (!runId || entry.identity?.runId !== runId) return false;
|
|
1461
1537
|
ensureRunIndexes();
|
|
1462
1538
|
const entryIndex = snapshotIndexes?.get(entry);
|
|
1463
|
-
return entryIndex !== void 0 && lastAssistantIndex >= 0 && entryIndex >=
|
|
1539
|
+
return entryIndex !== void 0 && lastAssistantIndex <= entryIndex && (requireUser ? firstUserIndex >= 0 && firstUserIndex < entryIndex : lastAssistantIndex >= 0);
|
|
1464
1540
|
};
|
|
1465
1541
|
const durableTerminalMatches = matches.filter((entry) => {
|
|
1466
|
-
return (asNullableRecord(asNullableRecord(entry.message)?.["__openclaw"])?.runTerminal === true || entry.identity?.runId === current.identity?.runId && (hasTerminalStopReason(entry.message) ||
|
|
1542
|
+
return (asNullableRecord(asNullableRecord(entry.message)?.["__openclaw"])?.runTerminal === true || entry.identity?.runId === current.identity?.runId && (hasTerminalStopReason(entry.message) || isToolUsePersistedFinalRow(entry.message) && hasTerminalSnapshotPosition(entry, false)) || hasTerminalSnapshotPosition(entry, true)) && readFinalContentIdentity(entry.message) === terminalContent;
|
|
1467
1543
|
});
|
|
1468
1544
|
const entry = durableTerminalMatches.length === 1 ? durableTerminalMatches[0] : void 0;
|
|
1469
1545
|
if (!entry) return null;
|
|
@@ -1493,22 +1569,19 @@ function retainSessionProjectionRuns(runs) {
|
|
|
1493
1569
|
}
|
|
1494
1570
|
//#endregion
|
|
1495
1571
|
//#region packages/gateway-client/src/session-projection-run-event.ts
|
|
1496
|
-
function readNonemptyString(value) {
|
|
1497
|
-
return typeof value === "string" ? value.trim() || null : null;
|
|
1498
|
-
}
|
|
1499
1572
|
/** Normalizes Gateway run envelopes once for every browser and terminal adapter. */
|
|
1500
1573
|
function reduceSessionProjectionRunEvent(projection, event, scope = {}) {
|
|
1501
|
-
const runId =
|
|
1574
|
+
const runId = readSessionProjectionString(event.runId);
|
|
1502
1575
|
if (!runId || typeof event.state !== "string" || ![
|
|
1503
1576
|
"delta",
|
|
1504
1577
|
"final",
|
|
1505
1578
|
"error",
|
|
1506
1579
|
"aborted"
|
|
1507
1580
|
].includes(event.state)) return null;
|
|
1508
|
-
const message = event.message;
|
|
1509
|
-
const messageStopReason = isRecord(message) ?
|
|
1510
|
-
const stopReason =
|
|
1511
|
-
const errorKind =
|
|
1581
|
+
const message = event.state === "delta" ? mergeChatStreamMessage(projection.runs[runId]?.message, event) : event.message;
|
|
1582
|
+
const messageStopReason = isRecord(message) ? readSessionProjectionString(message.stopReason) : null;
|
|
1583
|
+
const stopReason = readSessionProjectionString(event.stopReason) ?? messageStopReason;
|
|
1584
|
+
const errorKind = readSessionProjectionString(event.errorKind);
|
|
1512
1585
|
const base = {
|
|
1513
1586
|
runId,
|
|
1514
1587
|
seq: typeof event.seq === "number" ? event.seq : void 0,
|
|
@@ -1861,16 +1934,67 @@ function reduceSessionProjection(state, event) {
|
|
|
1861
1934
|
}
|
|
1862
1935
|
}
|
|
1863
1936
|
//#endregion
|
|
1864
|
-
//#region packages/
|
|
1865
|
-
|
|
1937
|
+
//#region packages/normalization-core/src/result.ts
|
|
1938
|
+
/** Create a successful {@link Result}. */
|
|
1939
|
+
function ok(value) {
|
|
1866
1940
|
return {
|
|
1867
|
-
|
|
1868
|
-
|
|
1941
|
+
ok: true,
|
|
1942
|
+
value
|
|
1869
1943
|
};
|
|
1870
1944
|
}
|
|
1945
|
+
/** Create a failed {@link Result}. */
|
|
1946
|
+
function err(error) {
|
|
1947
|
+
return {
|
|
1948
|
+
ok: false,
|
|
1949
|
+
error
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1952
|
+
//#endregion
|
|
1953
|
+
//#region packages/normalization-core/src/agent-id.ts
|
|
1954
|
+
const VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
|
|
1955
|
+
const INVALID_CHARS_RE = /[^a-z0-9_-]+/g;
|
|
1956
|
+
const LEADING_DASH_RE = /^-+/;
|
|
1957
|
+
const TRAILING_DASH_RE = /-+$/;
|
|
1958
|
+
/** Normalizes an explicitly supplied agent id without falling back to the default agent. */
|
|
1959
|
+
function normalizeAgentIdStrict(value) {
|
|
1960
|
+
const trimmed = (value ?? "").trim();
|
|
1961
|
+
const normalized = trimmed.toLowerCase();
|
|
1962
|
+
if (VALID_ID_RE.test(trimmed)) return ok(normalized);
|
|
1963
|
+
const agentId = normalized.replace(INVALID_CHARS_RE, "-").replace(LEADING_DASH_RE, "").replace(TRAILING_DASH_RE, "").slice(0, 64);
|
|
1964
|
+
return agentId ? ok(agentId) : err("unrepresentable");
|
|
1965
|
+
}
|
|
1966
|
+
//#endregion
|
|
1967
|
+
//#region packages/normalization-core/src/uuid.ts
|
|
1968
|
+
let warnedWeakCrypto = false;
|
|
1969
|
+
function warnWeakCryptoOnce() {
|
|
1970
|
+
if (warnedWeakCrypto) return;
|
|
1971
|
+
warnedWeakCrypto = true;
|
|
1972
|
+
console.warn("[uuid] crypto API missing; refusing insecure UUID generation");
|
|
1973
|
+
}
|
|
1974
|
+
function generateUUID(cryptoLike = globalThis.crypto) {
|
|
1975
|
+
if (cryptoLike && typeof cryptoLike.randomUUID === "function") return cryptoLike.randomUUID();
|
|
1976
|
+
if (cryptoLike && typeof cryptoLike.getRandomValues === "function") {
|
|
1977
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
1978
|
+
cryptoLike.getRandomValues(bytes);
|
|
1979
|
+
const view = new DataView(bytes.buffer);
|
|
1980
|
+
view.setUint8(6, view.getUint8(6) & 15 | 64);
|
|
1981
|
+
view.setUint8(8, view.getUint8(8) & 63 | 128);
|
|
1982
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1983
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
1984
|
+
}
|
|
1985
|
+
warnWeakCryptoOnce();
|
|
1986
|
+
throw new Error("Web Crypto is required for UUID generation");
|
|
1987
|
+
}
|
|
1988
|
+
//#endregion
|
|
1989
|
+
//#region packages/gateway-client/src/session-subscriptions.ts
|
|
1990
|
+
function normalizedAgentScope(agentId) {
|
|
1991
|
+
if (!agentId) return null;
|
|
1992
|
+
const normalized = normalizeAgentIdStrict(agentId);
|
|
1993
|
+
return normalized.ok ? normalized.value : agentId;
|
|
1994
|
+
}
|
|
1871
1995
|
/**
|
|
1872
|
-
*
|
|
1873
|
-
*
|
|
1996
|
+
* Shared leases retain one wire observer ID. The Gateway combines independently
|
|
1997
|
+
* addressed observers; approval delivery upgrades the same owner.
|
|
1874
1998
|
*/
|
|
1875
1999
|
var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
1876
2000
|
#client;
|
|
@@ -1892,17 +2016,24 @@ var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
|
1892
2016
|
const normalizedKey = key.trim();
|
|
1893
2017
|
if (!normalizedKey) throw new Error("Session message subscription requires a session key");
|
|
1894
2018
|
const agentId = options.agentId?.trim() || null;
|
|
2019
|
+
const scopeAgentId = normalizedAgentScope(agentId);
|
|
2020
|
+
const narration = options.mode === "narration";
|
|
2021
|
+
const includeApprovals = options.includeApprovals === true;
|
|
1895
2022
|
let entry;
|
|
2023
|
+
let created = false;
|
|
1896
2024
|
while (true) {
|
|
1897
2025
|
if (this.#retired) throw new Error("Session message subscription belongs to a replaced Gateway connection");
|
|
1898
|
-
const existing = [...this.#entries].find((candidate) =>
|
|
2026
|
+
const existing = [...this.#entries].find((candidate) => {
|
|
2027
|
+
return candidate.scopeAgentId === scopeAgentId && ([...candidate.requestedKeys].some((requestedKey) => this.#areKeysEquivalent(requestedKey, normalizedKey)) || candidate.key !== "global" && this.#areKeysEquivalent(candidate.key, normalizedKey));
|
|
2028
|
+
});
|
|
1899
2029
|
if (!existing) {
|
|
1900
|
-
const provisional = [...this.#entries].find((candidate) => candidate.
|
|
2030
|
+
const provisional = [...this.#entries].find((candidate) => candidate.scopeAgentId === scopeAgentId && !candidate.canonicalSettled && this.#couldShareCanonicalIdentity(candidate.key, normalizedKey));
|
|
1901
2031
|
if (provisional) {
|
|
1902
2032
|
await (provisional.plainFallback ?? provisional.ready).catch(() => void 0);
|
|
1903
2033
|
continue;
|
|
1904
2034
|
}
|
|
1905
|
-
entry = this.#createEntry(normalizedKey, agentId
|
|
2035
|
+
entry = this.#createEntry(normalizedKey, agentId);
|
|
2036
|
+
created = true;
|
|
1906
2037
|
break;
|
|
1907
2038
|
}
|
|
1908
2039
|
if (!existing.release) {
|
|
@@ -1913,13 +2044,31 @@ var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
|
1913
2044
|
await existing.release.catch(() => void 0);
|
|
1914
2045
|
}
|
|
1915
2046
|
entry.pendingOwners += 1;
|
|
2047
|
+
if (!narration) entry.pendingFullOwners += 1;
|
|
1916
2048
|
try {
|
|
1917
|
-
|
|
2049
|
+
if (created) {
|
|
2050
|
+
entry.ready = this.#requestSubscribe(entry, includeApprovals);
|
|
2051
|
+
if (includeApprovals) entry.ready = this.#trackApprovalRequest(entry, entry.ready);
|
|
2052
|
+
entry.ready.catch(() => void 0);
|
|
2053
|
+
}
|
|
2054
|
+
if (entry.refreshRequired) {
|
|
2055
|
+
entry.refreshRequired = false;
|
|
2056
|
+
entry.plainFallback = null;
|
|
2057
|
+
entry.approvalRequest = null;
|
|
2058
|
+
const retainedApprovals = [...entry.handles].some((handle) => handle.includeApprovals);
|
|
2059
|
+
entry.ready = this.#requestSubscribe(entry, retainedApprovals).catch((error) => {
|
|
2060
|
+
entry.refreshRequired = true;
|
|
2061
|
+
throw error;
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
const result = await this.#acquireCapability(entry, includeApprovals);
|
|
2065
|
+
if (!narration && entry.mode === "narration") await this.#requestSubscribe(entry, false, "full");
|
|
1918
2066
|
if (this.#retired) throw new Error("Session message subscription completed on a replaced Gateway connection");
|
|
1919
2067
|
const subscription = {
|
|
1920
2068
|
key: result.key,
|
|
1921
2069
|
agentId,
|
|
1922
|
-
...
|
|
2070
|
+
...narration ? { mode: "narration" } : {},
|
|
2071
|
+
...includeApprovals ? {
|
|
1923
2072
|
includeApprovals: true,
|
|
1924
2073
|
...result.approvalReplay !== void 0 ? { approvalReplay: result.approvalReplay } : {}
|
|
1925
2074
|
} : {}
|
|
@@ -1932,6 +2081,7 @@ var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
|
1932
2081
|
return subscription;
|
|
1933
2082
|
} finally {
|
|
1934
2083
|
entry.pendingOwners -= 1;
|
|
2084
|
+
if (!narration) entry.pendingFullOwners -= 1;
|
|
1935
2085
|
if (entry.pendingOwners === 0 && entry.handles.size === 0 && !entry.release) this.#entries.delete(entry);
|
|
1936
2086
|
}
|
|
1937
2087
|
}
|
|
@@ -1939,26 +2089,34 @@ var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
|
1939
2089
|
const owner = sessionMessageSubscriptionOwners.get(subscription);
|
|
1940
2090
|
if (!owner || owner.coordinator !== this) return Promise.resolve();
|
|
1941
2091
|
const { entry } = owner;
|
|
1942
|
-
if (
|
|
2092
|
+
if (entry.release) return entry.releasing === subscription ? entry.release : entry.release.catch(() => void 0).then(() => this.release(subscription));
|
|
2093
|
+
const releasesLastFullOwner = subscription.mode !== "narration" && [...entry.handles].every((handle) => handle === subscription || handle.mode === "narration");
|
|
2094
|
+
if (this.#retired || entry.handles.size > 1 && !releasesLastFullOwner) {
|
|
1943
2095
|
this.#finishRelease(subscription, owner);
|
|
1944
2096
|
return Promise.resolve();
|
|
1945
2097
|
}
|
|
1946
|
-
if (entry.release) return entry.release;
|
|
1947
2098
|
if (entry.pendingOwners > 0) {
|
|
1948
|
-
const
|
|
1949
|
-
|
|
2099
|
+
const tracked = Promise.allSettled([
|
|
2100
|
+
entry.ready,
|
|
2101
|
+
entry.approvalRequest,
|
|
2102
|
+
entry.wireRequest
|
|
2103
|
+
]).then(() => {
|
|
1950
2104
|
if (entry.release === tracked) entry.release = null;
|
|
1951
2105
|
return this.release(subscription);
|
|
1952
2106
|
});
|
|
1953
2107
|
entry.release = tracked;
|
|
2108
|
+
entry.releasing = subscription;
|
|
1954
2109
|
return tracked;
|
|
1955
2110
|
}
|
|
1956
|
-
const
|
|
1957
|
-
|
|
1958
|
-
|
|
2111
|
+
const removeEntry = entry.handles.size === 1;
|
|
2112
|
+
const tracked = (removeEntry ? this.#requestMessages(entry).catch((error) => {
|
|
2113
|
+
if (error instanceof GatewayProtocolRequestTimeoutError && error.requestSent) entry.refreshRequired = true;
|
|
2114
|
+
throw error;
|
|
2115
|
+
}) : this.#requestSubscribe(entry, false, "narration")).then(() => this.#finishRelease(subscription, owner, removeEntry)).finally(() => {
|
|
1959
2116
|
if (entry.release === tracked) entry.release = null;
|
|
1960
2117
|
});
|
|
1961
2118
|
entry.release = tracked;
|
|
2119
|
+
entry.releasing = subscription;
|
|
1962
2120
|
return tracked;
|
|
1963
2121
|
}
|
|
1964
2122
|
/** A reconnect retires leases without touching the next connection's observers. */
|
|
@@ -1970,22 +2128,26 @@ var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
|
1970
2128
|
}
|
|
1971
2129
|
this.#entries.clear();
|
|
1972
2130
|
}
|
|
1973
|
-
#createEntry(key, agentId
|
|
2131
|
+
#createEntry(key, agentId) {
|
|
1974
2132
|
const entry = {
|
|
2133
|
+
subscriptionId: generateUUID(),
|
|
1975
2134
|
key,
|
|
1976
2135
|
requestedKeys: /* @__PURE__ */ new Set([key]),
|
|
1977
2136
|
agentId,
|
|
2137
|
+
scopeAgentId: normalizedAgentScope(agentId),
|
|
2138
|
+
ownerAgentId: null,
|
|
1978
2139
|
ready: Promise.resolve({ key }),
|
|
1979
2140
|
approvalRequest: null,
|
|
1980
2141
|
plainFallback: null,
|
|
2142
|
+
wireRequest: null,
|
|
2143
|
+
includeApprovals: false,
|
|
1981
2144
|
canonicalSettled: false,
|
|
2145
|
+
refreshRequired: false,
|
|
1982
2146
|
handles: /* @__PURE__ */ new Set(),
|
|
1983
2147
|
pendingOwners: 0,
|
|
2148
|
+
pendingFullOwners: 0,
|
|
1984
2149
|
release: null
|
|
1985
2150
|
};
|
|
1986
|
-
entry.ready = this.#requestSubscribe(entry, includeApprovals);
|
|
1987
|
-
if (includeApprovals) entry.ready = this.#trackApprovalRequest(entry, entry.ready);
|
|
1988
|
-
entry.ready.catch(() => void 0);
|
|
1989
2151
|
this.#entries.add(entry);
|
|
1990
2152
|
return entry;
|
|
1991
2153
|
}
|
|
@@ -2016,19 +2178,43 @@ var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
|
2016
2178
|
entry.approvalRequest = tracked;
|
|
2017
2179
|
return tracked;
|
|
2018
2180
|
}
|
|
2019
|
-
|
|
2020
|
-
const
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2181
|
+
#requestSubscribe(entry, includeApprovals, mode) {
|
|
2182
|
+
const send = () => this.#sendSubscribe(entry, includeApprovals, mode);
|
|
2183
|
+
const tracked = (entry.wireRequest ? entry.wireRequest.then(send, send) : send()).finally(() => {
|
|
2184
|
+
if (entry.wireRequest === tracked) entry.wireRequest = null;
|
|
2185
|
+
});
|
|
2186
|
+
entry.wireRequest = tracked;
|
|
2187
|
+
return tracked;
|
|
2188
|
+
}
|
|
2189
|
+
#requestMessages(entry, subscription) {
|
|
2190
|
+
const agentId = entry.key === "global" ? entry.ownerAgentId ?? entry.agentId : entry.agentId;
|
|
2191
|
+
return this.#client.request(subscription ? "sessions.messages.subscribe" : "sessions.messages.unsubscribe", {
|
|
2192
|
+
subscriptionId: entry.subscriptionId,
|
|
2193
|
+
key: entry.key,
|
|
2194
|
+
...agentId ? { agentId } : {},
|
|
2195
|
+
...subscription?.mode ? { mode: subscription.mode } : {},
|
|
2196
|
+
...subscription?.includeApprovals ? { includeApprovals: true } : {}
|
|
2197
|
+
}, { timeoutMs: DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS });
|
|
2198
|
+
}
|
|
2199
|
+
async #sendSubscribe(entry, requestedApprovals, requestedMode) {
|
|
2200
|
+
if (this.#retired) throw new Error("Session message subscription belongs to a replaced Gateway connection");
|
|
2201
|
+
if (requestedMode === "full" && entry.mode !== "narration") return { key: entry.key };
|
|
2202
|
+
const includeApprovals = requestedApprovals || requestedMode !== void 0 && entry.includeApprovals;
|
|
2203
|
+
const mode = requestedMode === "narration" || requestedMode !== "full" && entry.pendingFullOwners === 0 && [...entry.handles].every((handle) => handle.mode === "narration") ? "narration" : void 0;
|
|
2204
|
+
const result = await this.#requestMessages(entry, {
|
|
2205
|
+
mode,
|
|
2206
|
+
includeApprovals
|
|
2207
|
+
}).catch(async (error) => {
|
|
2025
2208
|
if (!(error instanceof GatewayProtocolRequestTimeoutError) || !error.requestSent || this.#retired) throw error;
|
|
2026
2209
|
try {
|
|
2027
2210
|
const retainedApprovals = [...entry.handles].some((handle) => handle.includeApprovals);
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2211
|
+
const retainedMode = entry.handles.size > 0 && [...entry.handles].every((handle) => handle.mode === "narration") ? "narration" : void 0;
|
|
2212
|
+
await this.#requestMessages(entry, entry.handles.size > 0 ? {
|
|
2213
|
+
mode: retainedMode,
|
|
2214
|
+
includeApprovals: retainedApprovals
|
|
2215
|
+
} : void 0);
|
|
2216
|
+
entry.mode = retainedMode;
|
|
2217
|
+
entry.includeApprovals = retainedApprovals;
|
|
2032
2218
|
} catch (recoveryError) {
|
|
2033
2219
|
if (!this.#retired) throw new AggregateError([error, recoveryError], "session message subscription recovery failed", { cause: recoveryError });
|
|
2034
2220
|
}
|
|
@@ -2038,6 +2224,10 @@ var GatewaySessionMessageSubscriptionCoordinator = class {
|
|
|
2038
2224
|
const responseKey = response && "key" in response ? response.key : void 0;
|
|
2039
2225
|
entry.key = typeof responseKey === "string" && responseKey.trim() ? responseKey.trim() : entry.key;
|
|
2040
2226
|
entry.canonicalSettled = true;
|
|
2227
|
+
const responseAgentId = response && "agentId" in response ? response.agentId : void 0;
|
|
2228
|
+
entry.ownerAgentId = normalizedAgentScope((typeof responseAgentId === "string" ? responseAgentId : null) ?? entry.ownerAgentId ?? entry.scopeAgentId);
|
|
2229
|
+
entry.mode = mode;
|
|
2230
|
+
entry.includeApprovals = includeApprovals;
|
|
2041
2231
|
return {
|
|
2042
2232
|
key: entry.key,
|
|
2043
2233
|
...response && "approvalReplay" in response ? { approvalReplay: response.approvalReplay } : {}
|
|
@@ -2075,4 +2265,4 @@ function releaseGatewaySessionMessageSubscription(subscription) {
|
|
|
2075
2265
|
return sessionMessageSubscriptionOwners.get(subscription)?.coordinator.release(subscription) ?? Promise.resolve();
|
|
2076
2266
|
}
|
|
2077
2267
|
//#endregion
|
|
2078
|
-
export {
|
|
2268
|
+
export { selectGatewayConnectAuth as A, isGatewayProtocolResponseError as C, GatewayBrowserDeviceAuthLifecycle as D, gatewayOriginScope as E, isRecord as F, buildDeviceAuthPayload as I, buildDeviceAuthPayloadV3 as L, mergeChatStreamMessage as M, asNullableRecord as N, buildGatewayConnectAuth as O, asRecord as P, normalizeDeviceMetadataForAuth as R, GatewayProtocolRequestTimeoutError as S, gatewayCredentialScope as T, readSessionMessageIdentity as _, createSessionProjection as a, GatewayProtocolClient as b, reduceSessionProjection as c, readSessionProjectionFinalMessageIdentity as d, isSessionProjectionErrorMessage as f, readAssistantStreamSegmentIdentity as g, normalizeSessionProjectionRunId as h, resetGatewaySessionMessageSubscriptionCoordinator as i, shouldRetryGatewayWithDeviceToken as j, resolveGatewayConnectScopes as k, reduceSessionProjectionRunEvent as l, isLocallyOptimisticSessionMessage as m, getGatewaySessionMessageSubscriptionCoordinator as n, projectLiveSessionMessage as o, projectSessionTerminalReplyMessage as p, releaseGatewaySessionMessageSubscription as r, reconcileSessionProjectionSnapshot as s, GatewaySessionMessageSubscriptionCoordinator as t, hasSessionProjectionAcceptedFinal as u, readSessionMessageSequence as v, resolveModelCatalogConnect as w, GatewayProtocolRequestError as x, shouldPauseGatewayReconnect as y };
|