@openclaw/gateway-client 2026.9.6 → 2026.9.8

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 CHANGED
@@ -163,9 +163,23 @@ transport timeouts return `false`. Both entries use the same implementation.
163
163
  ## Defaults and reconnect behavior
164
164
 
165
165
  The Node client starts with a 30 second request timeout, a 15 second
166
- connect-challenge timeout, and exponential reconnect delays from 1 second to 30
167
- seconds with a multiplier of 2. Server-provided startup retry hints may override
168
- the next delay.
166
+ connect-challenge timeout, and exponential reconnect backoff from 1 second to 30
167
+ seconds with a multiplier of 2. Reconnects use randomized delays: the first waits
168
+ 1–1.2 seconds, and sustained failures spread retries across 25–30 seconds.
169
+ Retryable server hints remain minimum waits and can extend beyond the normal cap,
170
+ with up to 20% additional spread. Adapter-owned startup retry hints retain their
171
+ exact next delay without advancing normal backoff.
172
+
173
+ Browser adapters can observe the shared client's `onReconnectScheduled(delayMs,
174
+ signal)` callback to display the actual retry wait, including jitter and timer
175
+ bounds. The signal aborts when the wait is canceled or superseded; adapters should
176
+ clear their countdown when creating the next socket.
177
+
178
+ A sequence gap calls `onGap` and retires the socket unless the callback already
179
+ replaced it. A gap-revealing `chat` final, aborted, or error event is delivered
180
+ first so its authoritative outcome can settle the run. Other gapped frames and
181
+ subsequent frames from that socket are not delivered. Reconnect restores a fresh live-text baseline; applications should
182
+ also refresh durable state and restore their session subscriptions.
169
183
 
170
184
  The canonical defaults table and the server policy fields that can replace
171
185
  pre-handshake values are documented in the
@@ -175,6 +189,67 @@ Use the `./timeouts` entry point when a host must align readiness or watchdog
175
189
  budgets with these defaults. Use the `./readiness` entry point when startup must
176
190
  wait for an event-loop probe before opening the socket.
177
191
 
192
+ ## Streaming chat
193
+
194
+ Event callbacks receive the wire payload unchanged. A `chat` delta's optional
195
+ `message` is an authoritative snapshot that already includes `deltaText`.
196
+ Without `message`, append `deltaText` to the run's existing text. `replace: true`
197
+ replaces the text, including an empty replacement. The first frame received for
198
+ a run and frames that change canvas or media content supply a snapshot.
199
+
200
+ Both public entries export `mergeChatStreamMessage(previousMessage, payload)`
201
+ for clients that own their run state. The helper preserves nontext content and
202
+ message metadata and never appends a snapshot's delta twice. An append without
203
+ a baseline returns `undefined`; recover the connection instead of displaying
204
+ an incomplete answer. `reduceSessionProjectionRunEvent` uses the same merge
205
+ operation for clients using the shared session projection.
206
+
207
+ The high-level `@openclaw/sdk` retains reconstructed chat and assistant-item text
208
+ in its normalized run-event replay, so late readers can recover the text after the initial wire
209
+ snapshot is evicted. Its `rawEvents()` and each normalized event's `raw` field
210
+ still expose the original wire event, including omitted `message` fields.
211
+ Normalized assistant events retain cumulative `data.text` for their current item;
212
+ `data.delta` keeps its wire meaning, and the raw event can omit `data.text`.
213
+ Active chat baselines remain protected while their connection is current. The
214
+ SDK's concrete Gateway transport reconciles outstanding owned or observed runs
215
+ after reconnect through `agent.wait`. Active chat runs can recover display text
216
+ from an exact `chat.history.inFlightRun` match; newer live text always wins over
217
+ an older history response. Assistant-item text re-baselines on its next wire
218
+ snapshot because history contains display text, not raw assistant-item text.
219
+
220
+ SDK and ACP recovery share `recoverTerminalReply`: it collects all assistant
221
+ items for the run in transcript order, preserves live item boundaries, and reads
222
+ full messages when history truncated them. Recovered text preserves leading
223
+ indentation and trailing whitespace. It scans at most ten 200-record pages;
224
+ an incomplete scan reports unavailable instead of presenting a partial reply. The
225
+ bounded `terminalReply.text` summary is never used as complete output. Local
226
+ normalized recovery events have no `raw` field. Their `data.recovery.status`
227
+ reports `rebaselined`, `recovered`, or `unavailable`; unavailable full text omits
228
+ `outputText`. A bare wait timeout is not a run terminal. Terminal observations
229
+ have finite retention, and sessionless or unavailable transcript occurrences
230
+ cannot be reconstructed; unknown outcomes remain unsettled with a recovery
231
+ notice. Automatic recovery stops after four consecutive unavailable observations
232
+ (initial probe plus three waits), releases retention protection, and stays stopped
233
+ across reconnects. Confirmed activity resets that budget; late terminal frames can
234
+ still settle the run. Queued replies use exponential backoff with the Gateway
235
+ client's positive 20% jitter and a 25–30 second cap. ACP reports unavailable full
236
+ text explicitly instead of settling with a partial answer. Closing the client or
237
+ returning a run iterator cancels its recovery work.
238
+
239
+ Reconnect retires old text baselines. Ending the transport event stream also
240
+ releases outstanding-run protection, retaining only bounded replay. Custom
241
+ `OpenClawTransport` implementations must deliver terminal outcomes or end a
242
+ retired event stream; the generic transport interface does not expose reconnect
243
+ notifications and does not receive this automatic reconciliation.
244
+ Confirmed session unsubscribe also releases that session's baseline protection;
245
+ it also releases unobserved runs accepted before their first output. The
246
+ acknowledgment cannot retire a newer acceptance or subscription's snapshot. The concrete
247
+ Gateway transport preserves acknowledgment and event order. Custom transports
248
+ must preserve that ordering or end their retired event stream.
249
+ For raw `global` or `unknown` session keys, pass `agentId` when unsubscribing
250
+ to identify an agent-owned baseline. An unscoped acknowledgment cannot retire
251
+ a baseline with a known agent owner.
252
+
178
253
  ## Bundled internals
179
254
 
180
255
  The retry supervisor and the small `@openclaw/net-policy/ip` implementation are
@@ -1,7 +1,7 @@
1
- import { A as normalizeSessionProjectionRunId, C as reduceSessionProjectionRunEvent, D as SessionMessageIdentity, E as SessionMessageEnvelope, F as gatewayOriginScope, I as buildDeviceAuthPayload, L as buildDeviceAuthPayloadV3, M as readSessionMessageIdentity, N as readSessionMessageSequence, O as SessionProjectionEntry, P as gatewayCredentialScope, R as normalizeDeviceMetadataForAuth, S as SessionProjectionRunTransition, T as readSessionProjectionFinalMessageIdentity, _ as reconcileSessionProjectionSnapshot, a as GatewaySessionMessageSubscriptionOptions, b as projectSessionTerminalReplyMessage, c as resetGatewaySessionMessageSubscriptionCoordinator, d as SessionProjectionRunStatus, f as SessionProjectionScope, g as projectLiveSessionMessage, h as createSessionProjection, i as GatewaySessionMessageSubscriptionCoordinatorOptions, j as readAssistantStreamSegmentIdentity, k as isLocallyOptimisticSessionMessage, l as SessionProjectionEvent, m as SessionProjectionState, n as GatewaySessionMessageSubscription, o as getGatewaySessionMessageSubscriptionCoordinator, p as SessionProjectionSnapshotOptions, r as GatewaySessionMessageSubscriptionCoordinator, s as releaseGatewaySessionMessageSubscription, t as GatewaySessionMessageRequestClient, u as SessionProjectionRun, v as reduceSessionProjection, w as hasSessionProjectionAcceptedFinal, x as SessionProjectionGatewayRunEvent, y as isSessionProjectionErrorMessage } from "./session-subscriptions-C91CEXwY.mjs";
1
+ import { A as normalizeSessionProjectionRunId, C as reduceSessionProjectionRunEvent, D as SessionMessageIdentity, E as SessionMessageEnvelope, F as gatewayOriginScope, I as mergeChatStreamMessage, L as buildDeviceAuthPayload, M as readSessionMessageIdentity, N as readSessionMessageSequence, O as SessionProjectionEntry, P as gatewayCredentialScope, R as buildDeviceAuthPayloadV3, S as SessionProjectionRunTransition, T as readSessionProjectionFinalMessageIdentity, _ as reconcileSessionProjectionSnapshot, a as GatewaySessionMessageSubscriptionOptions, b as projectSessionTerminalReplyMessage, c as resetGatewaySessionMessageSubscriptionCoordinator, d as SessionProjectionRunStatus, f as SessionProjectionScope, g as projectLiveSessionMessage, h as createSessionProjection, i as GatewaySessionMessageSubscriptionCoordinatorOptions, j as readAssistantStreamSegmentIdentity, k as isLocallyOptimisticSessionMessage, l as SessionProjectionEvent, m as SessionProjectionState, n as GatewaySessionMessageSubscription, o as getGatewaySessionMessageSubscriptionCoordinator, p as SessionProjectionSnapshotOptions, r as GatewaySessionMessageSubscriptionCoordinator, s as releaseGatewaySessionMessageSubscription, t as GatewaySessionMessageRequestClient, u as SessionProjectionRun, v as reduceSessionProjection, w as hasSessionProjectionAcceptedFinal, x as SessionProjectionGatewayRunEvent, y as isSessionProjectionErrorMessage, z as normalizeDeviceMetadataForAuth } from "./session-subscriptions-Bcq6_wtR.mjs";
2
2
  import { a as GatewayBrowserDeviceTokenStore, c as resolveGatewayConnectScopes, i as GatewayBrowserDeviceTokenRecord, l as selectGatewayConnectAuth, n as GatewayBrowserDeviceAuthPlan, o as GatewayConnectAuthSelection, r as GatewayBrowserDeviceIdentity, s as buildGatewayConnectAuth, t as GatewayBrowserDeviceAuthLifecycle, u as shouldRetryGatewayWithDeviceToken } from "./browser-device-auth-DoPo-sPB.mjs";
3
3
  import { i as isGatewayProtocolResponseError, n as GatewayProtocolRequestOptions, r as GatewayProtocolRequestTimeoutError, t as GatewayProtocolRequestError } from "./protocol-request-CtVhe3Bj.mjs";
4
- import { a as GatewayProtocolSocketHandlers, i as GatewayProtocolSocket, n as GatewayProtocolCloseContext, o as GatewayProtocolTiming, r as GatewayProtocolConnectAuthority, s as GatewayProtocolRequestTiming, t as GatewayProtocolClient } from "./protocol-client-D6mJuY1_.mjs";
4
+ import { a as GatewayProtocolConnectAuthority, c as GatewayProtocolTiming, i as GatewayProtocolCloseContext, l as GatewayProtocolRequestTiming, o as GatewayProtocolSocket, s as GatewayProtocolSocketHandlers, t as GatewayProtocolClient } from "./protocol-client-DT451Msj.mjs";
5
5
  import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, resolveSafeTimeoutDelayMs } from "./timeouts.mjs";
6
6
  import { ConnectParams as ConnectParams$1, GATEWAY_SERVER_CAPS } from "@openclaw/gateway-protocol/frame-guards";
7
7
  import { ConnectParams, ErrorShape, EventFrame, HelloOk } from "@openclaw/gateway-protocol";
@@ -10,6 +10,26 @@ export * from "@openclaw/gateway-protocol/gateway-error-details";
10
10
  export * from "@openclaw/gateway-protocol/client-info";
11
11
  export * from "@openclaw/gateway-protocol/startup-unavailable";
12
12
  export * from "@openclaw/gateway-protocol/version";
13
+ //#region packages/gateway-client/src/chat-stream-projection.d.ts
14
+ type ChatStreamScope = {
15
+ sessionKey: string;
16
+ agentId: unknown;
17
+ };
18
+ type ChatStreamEvent = {
19
+ event: string;
20
+ payload?: unknown;
21
+ };
22
+ /** Connection-owned chat baselines, independent of the selected local view. */
23
+ export declare class GatewayChatStreamProjection {
24
+ private readonly runs;
25
+ project<T extends ChatStreamEvent>(event: T): {
26
+ event: T;
27
+ missingBaseline: boolean;
28
+ };
29
+ retire(isRetired: (scope: ChatStreamScope) => boolean): void;
30
+ clear(): void;
31
+ }
32
+ //#endregion
13
33
  //#region packages/gateway-client/src/model-catalog-connect.d.ts
14
34
  /** A requested snapshot opts in only after the server advertises its connect field. */
15
35
  export declare function resolveModelCatalogConnect(params: {
@@ -27,4 +47,4 @@ export declare function shouldPauseGatewayReconnect(params: {
27
47
  clientVersionMismatchIsTerminal?: boolean;
28
48
  }): boolean;
29
49
  //#endregion
30
- export { type ConnectParams, DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, type ErrorShape, type EventFrame, GATEWAY_SERVER_CAPS, GatewayBrowserDeviceAuthLifecycle, GatewayBrowserDeviceAuthPlan, GatewayBrowserDeviceIdentity, GatewayBrowserDeviceTokenRecord, GatewayBrowserDeviceTokenStore, GatewayConnectAuthSelection, GatewayProtocolClient, type GatewayProtocolCloseContext, type GatewayProtocolConnectAuthority, GatewayProtocolRequestError, type GatewayProtocolRequestOptions, GatewayProtocolRequestTimeoutError, type GatewayProtocolRequestTiming, type GatewayProtocolSocket, type GatewayProtocolSocketHandlers, type GatewayProtocolTiming, GatewaySessionMessageRequestClient, GatewaySessionMessageSubscription, GatewaySessionMessageSubscriptionCoordinator, GatewaySessionMessageSubscriptionCoordinatorOptions, GatewaySessionMessageSubscriptionOptions, type HelloOk, type SessionMessageEnvelope, type SessionMessageIdentity, type SessionProjectionEntry, SessionProjectionEvent, type SessionProjectionGatewayRunEvent, SessionProjectionRun, SessionProjectionRunStatus, type SessionProjectionRunTransition, SessionProjectionScope, SessionProjectionSnapshotOptions, SessionProjectionState, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveGatewayConnectScopes, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken };
50
+ export { type ConnectParams, DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, type ErrorShape, type EventFrame, GATEWAY_SERVER_CAPS, GatewayBrowserDeviceAuthLifecycle, GatewayBrowserDeviceAuthPlan, GatewayBrowserDeviceIdentity, GatewayBrowserDeviceTokenRecord, GatewayBrowserDeviceTokenStore, GatewayConnectAuthSelection, GatewayProtocolClient, type GatewayProtocolCloseContext, type GatewayProtocolConnectAuthority, GatewayProtocolRequestError, type GatewayProtocolRequestOptions, GatewayProtocolRequestTimeoutError, type GatewayProtocolRequestTiming, type GatewayProtocolSocket, type GatewayProtocolSocketHandlers, type GatewayProtocolTiming, GatewaySessionMessageRequestClient, GatewaySessionMessageSubscription, GatewaySessionMessageSubscriptionCoordinator, GatewaySessionMessageSubscriptionCoordinatorOptions, GatewaySessionMessageSubscriptionOptions, type HelloOk, type SessionMessageEnvelope, type SessionMessageIdentity, type SessionProjectionEntry, SessionProjectionEvent, type SessionProjectionGatewayRunEvent, SessionProjectionRun, SessionProjectionRunStatus, type SessionProjectionRunTransition, SessionProjectionScope, SessionProjectionSnapshotOptions, SessionProjectionState, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, mergeChatStreamMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveGatewayConnectScopes, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken };
package/dist/browser.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as resolveGatewayConnectScopes, C as GatewayProtocolRequestTimeoutError, D as gatewayOriginScope, E as gatewayCredentialScope, F as normalizeDeviceMetadataForAuth, M as shouldRetryGatewayWithDeviceToken, N as buildDeviceAuthPayload, O as GatewayBrowserDeviceAuthLifecycle, P as buildDeviceAuthPayloadV3, S as GatewayProtocolRequestError, T as resolveModelCatalogConnect, _ as readSessionMessageIdentity, a as createSessionProjection, b as shouldPauseGatewayReconnect, c as reduceSessionProjection, d as readSessionProjectionFinalMessageIdentity, f as isSessionProjectionErrorMessage, g as readAssistantStreamSegmentIdentity, h as normalizeSessionProjectionRunId, i as resetGatewaySessionMessageSubscriptionCoordinator, j as selectGatewayConnectAuth, k as buildGatewayConnectAuth, l as reduceSessionProjectionRunEvent, m as isLocallyOptimisticSessionMessage, n as getGatewaySessionMessageSubscriptionCoordinator, o as projectLiveSessionMessage, p as projectSessionTerminalReplyMessage, r as releaseGatewaySessionMessageSubscription, s as reconcileSessionProjectionSnapshot, t as GatewaySessionMessageSubscriptionCoordinator, u as hasSessionProjectionAcceptedFinal, v as readSessionMessageSequence, w as isGatewayProtocolResponseError, x as GatewayProtocolClient } from "./session-subscriptions-DUpRfWJb.mjs";
1
+ import { A as selectGatewayConnectAuth, C as isGatewayProtocolResponseError, D as GatewayBrowserDeviceAuthLifecycle, E as gatewayOriginScope, I as buildDeviceAuthPayload, L as buildDeviceAuthPayloadV3, M as mergeChatStreamMessage, N as asNullableRecord, O as buildGatewayConnectAuth, R as normalizeDeviceMetadataForAuth, S as GatewayProtocolRequestTimeoutError, T as gatewayCredentialScope, _ as readSessionMessageIdentity, a as createSessionProjection, b as GatewayProtocolClient, c as reduceSessionProjection, d as readSessionProjectionFinalMessageIdentity, f as isSessionProjectionErrorMessage, g as readAssistantStreamSegmentIdentity, h as normalizeSessionProjectionRunId, i as resetGatewaySessionMessageSubscriptionCoordinator, j as shouldRetryGatewayWithDeviceToken, k as resolveGatewayConnectScopes, l as reduceSessionProjectionRunEvent, m as isLocallyOptimisticSessionMessage, n as getGatewaySessionMessageSubscriptionCoordinator, o as projectLiveSessionMessage, p as projectSessionTerminalReplyMessage, r as releaseGatewaySessionMessageSubscription, s as reconcileSessionProjectionSnapshot, t as GatewaySessionMessageSubscriptionCoordinator, u as hasSessionProjectionAcceptedFinal, v as readSessionMessageSequence, w as resolveModelCatalogConnect, x as GatewayProtocolRequestError, y as shouldPauseGatewayReconnect } from "./session-subscriptions-LtlX9oRQ.mjs";
2
2
  import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, resolveSafeTimeoutDelayMs } from "./timeouts.mjs";
3
3
  import { GATEWAY_SERVER_CAPS } from "@openclaw/gateway-protocol/frame-guards";
4
4
  export * from "@openclaw/gateway-protocol/connect-error-details";
@@ -6,4 +6,53 @@ export * from "@openclaw/gateway-protocol/gateway-error-details";
6
6
  export * from "@openclaw/gateway-protocol/client-info";
7
7
  export * from "@openclaw/gateway-protocol/startup-unavailable";
8
8
  export * from "@openclaw/gateway-protocol/version";
9
- export { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, GATEWAY_SERVER_CAPS, GatewayBrowserDeviceAuthLifecycle, GatewayProtocolClient, GatewayProtocolRequestError, GatewayProtocolRequestTimeoutError, GatewaySessionMessageSubscriptionCoordinator, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveGatewayConnectScopes, resolveModelCatalogConnect, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldPauseGatewayReconnect, shouldRetryGatewayWithDeviceToken };
9
+ //#region packages/gateway-client/src/chat-stream-projection.ts
10
+ /** Connection-owned chat baselines, independent of the selected local view. */
11
+ var GatewayChatStreamProjection = class {
12
+ constructor() {
13
+ this.runs = /* @__PURE__ */ new Map();
14
+ }
15
+ project(event) {
16
+ if (event.event !== "chat") return {
17
+ event,
18
+ missingBaseline: false
19
+ };
20
+ const payload = asNullableRecord(event.payload);
21
+ if (!payload || typeof payload.runId !== "string" || typeof payload.sessionKey !== "string") return {
22
+ event,
23
+ missingBaseline: false
24
+ };
25
+ if (payload.state === "delta") {
26
+ const previous = this.runs.get(payload.runId);
27
+ const message = mergeChatStreamMessage(previous?.sessionKey === payload.sessionKey && previous.agentId === payload.agentId ? previous.message : void 0, payload);
28
+ if (message !== void 0) this.runs.set(payload.runId, {
29
+ sessionKey: payload.sessionKey,
30
+ agentId: payload.agentId,
31
+ message
32
+ });
33
+ return {
34
+ event: {
35
+ ...event,
36
+ payload: {
37
+ ...payload,
38
+ message
39
+ }
40
+ },
41
+ missingBaseline: message === void 0
42
+ };
43
+ }
44
+ if (payload.state === "final" || payload.state === "error" || payload.state === "aborted") this.runs.delete(payload.runId);
45
+ return {
46
+ event,
47
+ missingBaseline: false
48
+ };
49
+ }
50
+ retire(isRetired) {
51
+ for (const [runId, scope] of this.runs) if (isRetired(scope)) this.runs.delete(runId);
52
+ }
53
+ clear() {
54
+ this.runs.clear();
55
+ }
56
+ };
57
+ //#endregion
58
+ export { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, GATEWAY_SERVER_CAPS, GatewayBrowserDeviceAuthLifecycle, GatewayChatStreamProjection, GatewayProtocolClient, GatewayProtocolRequestError, GatewayProtocolRequestTimeoutError, GatewaySessionMessageSubscriptionCoordinator, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, mergeChatStreamMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveGatewayConnectScopes, resolveModelCatalogConnect, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldPauseGatewayReconnect, shouldRetryGatewayWithDeviceToken };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,27 @@
1
- import { A as normalizeSessionProjectionRunId, C as reduceSessionProjectionRunEvent, D as SessionMessageIdentity, E as SessionMessageEnvelope, F as gatewayOriginScope, I as buildDeviceAuthPayload, L as buildDeviceAuthPayloadV3, M as readSessionMessageIdentity, N as readSessionMessageSequence, O as SessionProjectionEntry, P as gatewayCredentialScope, R as normalizeDeviceMetadataForAuth, S as SessionProjectionRunTransition, T as readSessionProjectionFinalMessageIdentity, _ as reconcileSessionProjectionSnapshot, a as GatewaySessionMessageSubscriptionOptions, b as projectSessionTerminalReplyMessage, c as resetGatewaySessionMessageSubscriptionCoordinator, d as SessionProjectionRunStatus, f as SessionProjectionScope, g as projectLiveSessionMessage, h as createSessionProjection, i as GatewaySessionMessageSubscriptionCoordinatorOptions, j as readAssistantStreamSegmentIdentity, k as isLocallyOptimisticSessionMessage, l as SessionProjectionEvent, m as SessionProjectionState, n as GatewaySessionMessageSubscription, o as getGatewaySessionMessageSubscriptionCoordinator, p as SessionProjectionSnapshotOptions, r as GatewaySessionMessageSubscriptionCoordinator, s as releaseGatewaySessionMessageSubscription, t as GatewaySessionMessageRequestClient, u as SessionProjectionRun, v as reduceSessionProjection, w as hasSessionProjectionAcceptedFinal, x as SessionProjectionGatewayRunEvent, y as isSessionProjectionErrorMessage } from "./session-subscriptions-C91CEXwY.mjs";
1
+ import { A as normalizeSessionProjectionRunId, C as reduceSessionProjectionRunEvent, D as SessionMessageIdentity, E as SessionMessageEnvelope, F as gatewayOriginScope, I as mergeChatStreamMessage, L as buildDeviceAuthPayload, M as readSessionMessageIdentity, N as readSessionMessageSequence, O as SessionProjectionEntry, P as gatewayCredentialScope, R as buildDeviceAuthPayloadV3, S as SessionProjectionRunTransition, T as readSessionProjectionFinalMessageIdentity, _ as reconcileSessionProjectionSnapshot, a as GatewaySessionMessageSubscriptionOptions, b as projectSessionTerminalReplyMessage, c as resetGatewaySessionMessageSubscriptionCoordinator, d as SessionProjectionRunStatus, f as SessionProjectionScope, g as projectLiveSessionMessage, h as createSessionProjection, i as GatewaySessionMessageSubscriptionCoordinatorOptions, j as readAssistantStreamSegmentIdentity, k as isLocallyOptimisticSessionMessage, l as SessionProjectionEvent, m as SessionProjectionState, n as GatewaySessionMessageSubscription, o as getGatewaySessionMessageSubscriptionCoordinator, p as SessionProjectionSnapshotOptions, r as GatewaySessionMessageSubscriptionCoordinator, s as releaseGatewaySessionMessageSubscription, t as GatewaySessionMessageRequestClient, u as SessionProjectionRun, v as reduceSessionProjection, w as hasSessionProjectionAcceptedFinal, x as SessionProjectionGatewayRunEvent, y as isSessionProjectionErrorMessage, z as normalizeDeviceMetadataForAuth } from "./session-subscriptions-Bcq6_wtR.mjs";
2
2
  import { a as GatewayBrowserDeviceTokenStore, c as resolveGatewayConnectScopes, i as GatewayBrowserDeviceTokenRecord, l as selectGatewayConnectAuth, n as GatewayBrowserDeviceAuthPlan, o as GatewayConnectAuthSelection, r as GatewayBrowserDeviceIdentity, s as buildGatewayConnectAuth, t as GatewayBrowserDeviceAuthLifecycle, u as shouldRetryGatewayWithDeviceToken } from "./browser-device-auth-DoPo-sPB.mjs";
3
3
  import { i as isGatewayProtocolResponseError } from "./protocol-request-CtVhe3Bj.mjs";
4
+ import { n as GatewayClientCloseInfo, r as GatewayClientConnectionMetadata } from "./protocol-client-DT451Msj.mjs";
4
5
  import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, getConnectChallengeTimeoutMsFromEnv, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, startGatewayConnectTimeout } from "./timeouts.mjs";
5
- import { _ as GatewayReconnectPausedInfo, a as startGatewayClientWithReadinessWait, b as DeviceAuthTokenRecord, c as waitForEventLoopReady, d as GatewayClientCloseInfo, f as GatewayClientConnectionMetadata, g as GatewayClientRequestTimeoutError, h as GatewayClientRequestOptions, i as startGatewayClientWhenEventLoopReady, l as DeviceIdentity, m as GatewayClientOptions, n as GatewayClientStartReadinessOptions, o as EventLoopReadyOptions, p as GatewayClientHostDeps, r as GatewayClientStartable, s as EventLoopReadyResult, t as EventLoopReadyWaiter, u as GatewayClient, v as GatewayClientRequestError, y as isGatewayConnectAssemblyError } from "./readiness-ChxbBSMj.mjs";
6
+ import { _ as isGatewayConnectAssemblyError, a as startGatewayClientWithReadinessWait, c as waitForEventLoopReady, d as GatewayClientHostDeps, f as GatewayClientOptions, g as GatewayClientRequestError, h as GatewayReconnectPausedInfo, i as startGatewayClientWhenEventLoopReady, l as DeviceIdentity, m as GatewayClientRequestTimeoutError, n as GatewayClientStartReadinessOptions, o as EventLoopReadyOptions, p as GatewayClientRequestOptions, r as GatewayClientStartable, s as EventLoopReadyResult, t as EventLoopReadyWaiter, u as GatewayClient, v as DeviceAuthTokenRecord } from "./readiness-Cj5C24o2.mjs";
6
7
  import { GatewayScopeUpgrade, ScopeUpgradeBinding, ScopeUpgradeOptions, ScopeUpgradeOutcome } from "./scope-upgrade.mjs";
8
+ //#region packages/gateway-client/src/run-recovery-text.d.ts
9
+ export type RecoveryRequest = (method: string, params: Record<string, unknown>, signal: AbortSignal) => Promise<unknown>;
10
+ export declare function recoverTerminalReply(params: {
11
+ runId: string;
12
+ scope: {
13
+ sessionKey?: string;
14
+ agentId?: string;
15
+ sessionId?: string;
16
+ };
17
+ result: unknown;
18
+ request: RecoveryRequest;
19
+ signal: AbortSignal;
20
+ }): Promise<{
21
+ outputText?: string;
22
+ unavailable?: string;
23
+ }>;
24
+ //#endregion
7
25
  //#region packages/gateway-client/src/cloudflare-access.d.ts
8
26
  export declare const CF_ACCESS_CLIENT_ID_HEADER = "CF-Access-Client-Id";
9
27
  export declare const CF_ACCESS_CLIENT_SECRET_HEADER = "CF-Access-Client-Secret";
@@ -14,4 +32,4 @@ export type CloudflareAccessCredentials = Readonly<{
14
32
  /** Build only the two headers Cloudflare Access defines for service-token auth. */
15
33
  export declare function buildCloudflareAccessHeaders(credentials: CloudflareAccessCredentials): Record<string, string>;
16
34
  //#endregion
17
- export { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, type DeviceAuthTokenRecord, DeviceIdentity, EventLoopReadyOptions, EventLoopReadyResult, EventLoopReadyWaiter, GatewayBrowserDeviceAuthLifecycle, GatewayBrowserDeviceAuthPlan, GatewayBrowserDeviceIdentity, GatewayBrowserDeviceTokenRecord, GatewayBrowserDeviceTokenStore, GatewayClient, GatewayClientCloseInfo, GatewayClientConnectionMetadata, GatewayClientHostDeps, GatewayClientOptions, GatewayClientRequestError, GatewayClientRequestOptions, GatewayClientRequestTimeoutError, GatewayClientStartReadinessOptions, GatewayClientStartable, GatewayConnectAuthSelection, GatewayReconnectPausedInfo, GatewayScopeUpgrade, GatewaySessionMessageRequestClient, GatewaySessionMessageSubscription, GatewaySessionMessageSubscriptionCoordinator, GatewaySessionMessageSubscriptionCoordinatorOptions, GatewaySessionMessageSubscriptionOptions, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, ScopeUpgradeBinding, ScopeUpgradeOptions, ScopeUpgradeOutcome, type SessionMessageEnvelope, type SessionMessageIdentity, type SessionProjectionEntry, SessionProjectionEvent, type SessionProjectionGatewayRunEvent, SessionProjectionRun, SessionProjectionRunStatus, type SessionProjectionRunTransition, SessionProjectionScope, SessionProjectionSnapshotOptions, SessionProjectionState, addSafeTimeoutDelayGraceMs, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getConnectChallengeTimeoutMsFromEnv, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayConnectAssemblyError, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolveGatewayConnectScopes, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken, startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait, startGatewayConnectTimeout, waitForEventLoopReady };
35
+ export { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, type DeviceAuthTokenRecord, DeviceIdentity, EventLoopReadyOptions, EventLoopReadyResult, EventLoopReadyWaiter, GatewayBrowserDeviceAuthLifecycle, GatewayBrowserDeviceAuthPlan, GatewayBrowserDeviceIdentity, GatewayBrowserDeviceTokenRecord, GatewayBrowserDeviceTokenStore, GatewayClient, type GatewayClientCloseInfo, type GatewayClientConnectionMetadata, GatewayClientHostDeps, GatewayClientOptions, GatewayClientRequestError, GatewayClientRequestOptions, GatewayClientRequestTimeoutError, GatewayClientStartReadinessOptions, GatewayClientStartable, GatewayConnectAuthSelection, GatewayReconnectPausedInfo, GatewayScopeUpgrade, GatewaySessionMessageRequestClient, GatewaySessionMessageSubscription, GatewaySessionMessageSubscriptionCoordinator, GatewaySessionMessageSubscriptionCoordinatorOptions, GatewaySessionMessageSubscriptionOptions, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, ScopeUpgradeBinding, ScopeUpgradeOptions, ScopeUpgradeOutcome, type SessionMessageEnvelope, type SessionMessageIdentity, type SessionProjectionEntry, SessionProjectionEvent, type SessionProjectionGatewayRunEvent, SessionProjectionRun, SessionProjectionRunStatus, type SessionProjectionRunTransition, SessionProjectionScope, SessionProjectionSnapshotOptions, SessionProjectionState, addSafeTimeoutDelayGraceMs, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getConnectChallengeTimeoutMsFromEnv, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayConnectAssemblyError, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, mergeChatStreamMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolveGatewayConnectScopes, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken, startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait, startGatewayConnectTimeout, waitForEventLoopReady };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as resolveGatewayConnectScopes, C as GatewayProtocolRequestTimeoutError, D as gatewayOriginScope, E as gatewayCredentialScope, F as normalizeDeviceMetadataForAuth, M as shouldRetryGatewayWithDeviceToken, N as buildDeviceAuthPayload, O as GatewayBrowserDeviceAuthLifecycle, P as buildDeviceAuthPayloadV3, S as GatewayProtocolRequestError, T as resolveModelCatalogConnect, _ as readSessionMessageIdentity, a as createSessionProjection, b as shouldPauseGatewayReconnect, c as reduceSessionProjection, d as readSessionProjectionFinalMessageIdentity, f as isSessionProjectionErrorMessage, g as readAssistantStreamSegmentIdentity, h as normalizeSessionProjectionRunId, i as resetGatewaySessionMessageSubscriptionCoordinator, j as selectGatewayConnectAuth, k as buildGatewayConnectAuth, l as reduceSessionProjectionRunEvent, m as isLocallyOptimisticSessionMessage, n as getGatewaySessionMessageSubscriptionCoordinator, o as projectLiveSessionMessage, p as projectSessionTerminalReplyMessage, r as releaseGatewaySessionMessageSubscription, s as reconcileSessionProjectionSnapshot, t as GatewaySessionMessageSubscriptionCoordinator, u as hasSessionProjectionAcceptedFinal, v as readSessionMessageSequence, w as isGatewayProtocolResponseError, x as GatewayProtocolClient, y as isRecord } from "./session-subscriptions-DUpRfWJb.mjs";
1
+ import { A as selectGatewayConnectAuth, C as isGatewayProtocolResponseError, D as GatewayBrowserDeviceAuthLifecycle, E as gatewayOriginScope, F as isRecord, I as buildDeviceAuthPayload, L as buildDeviceAuthPayloadV3, M as mergeChatStreamMessage, O as buildGatewayConnectAuth, P as asRecord, R as normalizeDeviceMetadataForAuth, S as GatewayProtocolRequestTimeoutError, T as gatewayCredentialScope, _ as readSessionMessageIdentity, a as createSessionProjection, b as GatewayProtocolClient, c as reduceSessionProjection, d as readSessionProjectionFinalMessageIdentity, f as isSessionProjectionErrorMessage, g as readAssistantStreamSegmentIdentity, h as normalizeSessionProjectionRunId, i as resetGatewaySessionMessageSubscriptionCoordinator, j as shouldRetryGatewayWithDeviceToken, k as resolveGatewayConnectScopes, l as reduceSessionProjectionRunEvent, m as isLocallyOptimisticSessionMessage, n as getGatewaySessionMessageSubscriptionCoordinator, o as projectLiveSessionMessage, p as projectSessionTerminalReplyMessage, r as releaseGatewaySessionMessageSubscription, s as reconcileSessionProjectionSnapshot, t as GatewaySessionMessageSubscriptionCoordinator, u as hasSessionProjectionAcceptedFinal, v as readSessionMessageSequence, w as resolveModelCatalogConnect, x as GatewayProtocolRequestError, y as shouldPauseGatewayReconnect } from "./session-subscriptions-LtlX9oRQ.mjs";
2
2
  import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, getConnectChallengeTimeoutMsFromEnv, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, startGatewayConnectTimeout } from "./timeouts.mjs";
3
3
  import { rawDataToString } from "./websocket-data.mjs";
4
4
  import { n as startGatewayClientWithReadinessWait, r as waitForEventLoopReady, t as startGatewayClientWhenEventLoopReady } from "./readiness-CrWxd4TI.mjs";
@@ -10,7 +10,8 @@ import { resolveGatewayStartupRetryAfterMs } from "@openclaw/gateway-protocol/st
10
10
  import { MIN_CLIENT_PROTOCOL_VERSION, MIN_NODE_PROTOCOL_VERSION, MIN_PROBE_PROTOCOL_VERSION, PROTOCOL_VERSION } from "@openclaw/gateway-protocol/version";
11
11
  import { randomUUID } from "node:crypto";
12
12
  import ipaddr from "ipaddr.js";
13
- import { TLSSocket } from "node:tls";
13
+ import { isIP } from "node:net";
14
+ import { TLSSocket, checkServerIdentity } from "node:tls";
14
15
  import path from "node:path";
15
16
  //#region packages/normalization-core/src/string-coerce.ts
16
17
  /** Trims string input and returns null for non-strings or empty strings. */
@@ -82,10 +83,7 @@ function normalizeUrlQueryParamName(name) {
82
83
  try {
83
84
  decoded = decodeURIComponent(current).replace(URL_QUERY_NAME_SEPARATOR_RE, "");
84
85
  } catch {
85
- return {
86
- value: normalizeLowercaseStringOrEmpty(current).replaceAll("-", "_"),
87
- unresolvedEncoding: current.includes("%")
88
- };
86
+ break;
89
87
  }
90
88
  if (decoded === current) return {
91
89
  value: normalizeLowercaseStringOrEmpty(current).replaceAll("-", "_"),
@@ -159,9 +157,7 @@ function redactQueryString(value, depth) {
159
157
  redactedEntries.push([redactedKey, redactedValue]);
160
158
  }
161
159
  if (!mutated) return value;
162
- const redactedParams = new URLSearchParams();
163
- for (const [key, entryValue] of redactedEntries) redactedParams.append(key, entryValue);
164
- return redactedParams.toString();
160
+ return new URLSearchParams(redactedEntries).toString();
165
161
  }
166
162
  function redactUrlLikeFallback(value) {
167
163
  return redactSensitiveUrlPath(redactEmbeddedUrlUserInfo(value).replace(/([?&])([^=&]+)=([^&]*)/g, (match, prefix, key) => isSensitiveUrlQueryParamName$1(key) ? `${prefix}${key}=***` : match));
@@ -172,14 +168,14 @@ function redactAuthorityUserInfo(candidate, authorityStart) {
172
168
  if (userInfoEnd < 0) return candidate;
173
169
  return `${candidate.slice(0, authorityStart)}***:***@${authority.slice(userInfoEnd + 1)}`;
174
170
  }
171
+ function skipAuthoritySeparators(candidate, start) {
172
+ let cursor = start;
173
+ while (candidate[cursor] === "/" || candidate[cursor] === "\\") cursor += 1;
174
+ return cursor;
175
+ }
175
176
  function redactEmbeddedUrlUserInfo(value) {
176
- return value.replace(SPECIAL_SCHEME_AUTHORITY_RE, (candidate) => {
177
- let authorityStart = candidate.indexOf(":") + 1;
178
- while (authorityStart < candidate.length && (candidate[authorityStart] === "/" || candidate[authorityStart] === "\\")) authorityStart += 1;
179
- return redactAuthorityUserInfo(candidate, authorityStart);
180
- }).replace(SPECIAL_SCHEME_SPILLED_USERINFO_RE, (candidate) => {
181
- let authorityStart = candidate.indexOf(":") + 1;
182
- while (authorityStart < candidate.length && (candidate[authorityStart] === "/" || candidate[authorityStart] === "\\")) authorityStart += 1;
177
+ return value.replace(SPECIAL_SCHEME_AUTHORITY_RE, (candidate) => redactAuthorityUserInfo(candidate, skipAuthoritySeparators(candidate, candidate.indexOf(":") + 1))).replace(SPECIAL_SCHEME_SPILLED_USERINFO_RE, (candidate) => {
178
+ const authorityStart = skipAuthoritySeparators(candidate, candidate.indexOf(":") + 1);
183
179
  const userInfoEnd = candidate.lastIndexOf("@");
184
180
  const firstReservedDelimiter = candidate.slice(authorityStart).search(/[\\/?#]/u);
185
181
  if (userInfoEnd < 0 || firstReservedDelimiter < 0) return candidate;
@@ -191,11 +187,7 @@ function redactEmbeddedUrlUserInfo(value) {
191
187
  const possiblePort = candidate.slice(credentialSeparator + 1, absoluteReservedDelimiter);
192
188
  if (/^\d+$/u.test(possiblePort) || /^\[[^\]]+\](?::\d+)?$/u.test(authorityPrefix)) return candidate;
193
189
  return `${candidate.slice(0, authorityStart)}***:***@${candidate.slice(userInfoEnd + 1)}`;
194
- }).replace(PROTOCOL_RELATIVE_AUTHORITY_RE, (candidate) => {
195
- let authorityStart = 0;
196
- while (authorityStart < candidate.length && (candidate[authorityStart] === "/" || candidate[authorityStart] === "\\")) authorityStart += 1;
197
- return redactAuthorityUserInfo(candidate, authorityStart);
198
- });
190
+ }).replace(PROTOCOL_RELATIVE_AUTHORITY_RE, (candidate) => redactAuthorityUserInfo(candidate, skipAuthoritySeparators(candidate, 0)));
199
191
  }
200
192
  function hasUnresolvedEmbeddedUrlUserInfo(value) {
201
193
  for (const match of value.matchAll(/(?:\b(?:https?|wss?|ftp):[\\/]{0,2}|[\\/]{2,})/giu)) {
@@ -219,16 +211,15 @@ function redactFragment(value, depth) {
219
211
  if (depth > MAX_NESTED_URL_REDACTION_DEPTH && looksLikeNestedUrlValue(value)) return "***";
220
212
  const wholeUrl = redactSensitiveUrlAtDepth(value, depth);
221
213
  if (wholeUrl.parsedWholeUrl) return redactUrlLikeFallback(wholeUrl.value);
222
- const candidate = value;
223
- const firstQueryDelimiter = candidate.search(/[?&]/u);
224
- const firstEquals = candidate.indexOf("=");
225
- if (firstEquals >= 0 && (firstQueryDelimiter < 0 || firstEquals < firstQueryDelimiter)) return redactQueryString(candidate, depth);
226
- const hashRouterQueryIndex = candidate.indexOf("?");
214
+ const firstQueryDelimiter = value.search(/[?&]/u);
215
+ const firstEquals = value.indexOf("=");
216
+ if (firstEquals >= 0 && (firstQueryDelimiter < 0 || firstEquals < firstQueryDelimiter)) return redactQueryString(value, depth);
217
+ const hashRouterQueryIndex = value.indexOf("?");
227
218
  if (hashRouterQueryIndex >= 0) {
228
- const redactedQuery = redactQueryString(candidate.slice(hashRouterQueryIndex + 1), depth);
229
- return `${redactEncodedUrlLikeString(redactUrlLikeFallback(candidate.slice(0, hashRouterQueryIndex + 1)), depth + 1)}${redactedQuery}`;
219
+ const redactedQuery = redactQueryString(value.slice(hashRouterQueryIndex + 1), depth);
220
+ return `${redactEncodedUrlLikeString(redactUrlLikeFallback(value.slice(0, hashRouterQueryIndex + 1)), depth + 1)}${redactedQuery}`;
230
221
  }
231
- const fallback = redactUrlLikeFallback(candidate);
222
+ const fallback = redactUrlLikeFallback(value);
232
223
  if (!looksLikeNestedUrlValue(fallback)) return fallback;
233
224
  let decoded;
234
225
  try {
@@ -350,9 +341,7 @@ function isIpv6Address(address) {
350
341
  return address.kind() === "ipv6";
351
342
  }
352
343
  function normalizeIpv4MappedAddress(address) {
353
- if (!isIpv6Address(address)) return address;
354
- if (!address.isIPv4MappedAddress()) return address;
355
- return address.toIPv4Address();
344
+ return isIpv6Address(address) && address.isIPv4MappedAddress() ? address.toIPv4Address() : address;
356
345
  }
357
346
  function normalizeIpParseInput(raw) {
358
347
  const trimmed = normalizeOptionalString(raw);
@@ -629,6 +618,11 @@ function resolveGatewayWebSocketTransport(params) {
629
618
  const expectedFingerprint = params.tlsFingerprint ? normalizeTlsFingerprint(params.tlsFingerprint) : void 0;
630
619
  if (params.tlsFingerprint && !expectedFingerprint) throw new GatewayWebSocketTransportConfigurationError("gateway tls fingerprint must be a SHA-256 fingerprint");
631
620
  const options = { ...params.options };
621
+ if (usesTls && params.tlsServerName) {
622
+ const peerName = params.tlsServerName;
623
+ options.servername = isIP(peerName) ? "" : peerName;
624
+ options.checkServerIdentity = (_hostname, certificate) => checkServerIdentity(peerName, certificate);
625
+ }
632
626
  if (usesTls && expectedFingerprint) applyGatewayWebSocketTlsPin(options, expectedFingerprint, normalize);
633
627
  return { options };
634
628
  }
@@ -812,6 +806,7 @@ var GatewayClient = class {
812
806
  onParseError: (error) => this.logDebug(`gateway client parse error: ${formatGatewayClientErrorForLog(error)}`),
813
807
  onEvent: (event) => this.opts.onEvent?.(event),
814
808
  onGap: (info) => this.opts.onGap?.(info),
809
+ onRequestTiming: (timing) => this.opts.onRequestTiming?.(timing),
815
810
  onActivity: () => {
816
811
  this.lastTick = Date.now();
817
812
  },
@@ -870,6 +865,7 @@ var GatewayClient = class {
870
865
  const transport = resolveGatewayWebSocketTransport({
871
866
  url,
872
867
  tlsFingerprint: this.opts.tlsFingerprint,
868
+ tlsServerName: this.opts.tlsServerName,
873
869
  env: this.opts.env,
874
870
  normalizeTlsFingerprint: this.deps.normalizeTlsFingerprint,
875
871
  options: {
@@ -1105,15 +1101,10 @@ var GatewayClient = class {
1105
1101
  if (this.opts.role !== "node" || this.opts.mode !== GATEWAY_CLIENT_MODES.NODE || this.opts.clientName !== GATEWAY_CLIENT_NAMES.NODE_HOST) return false;
1106
1102
  return (this.opts.minProtocol ?? MIN_NODE_PROTOCOL_VERSION) === MIN_NODE_PROTOCOL_VERSION && (this.opts.maxProtocol ?? PROTOCOL_VERSION) === PROTOCOL_VERSION;
1107
1103
  }
1108
- shouldRetryWithLegacyNodeProtocol(error) {
1109
- if (this.useLegacyNodeProtocolEnvelope || !this.shouldNegotiateLegacyNodeProtocol() || !(error instanceof GatewayClientRequestError)) return false;
1110
- const detailCode = readConnectErrorDetailCode(error.details);
1111
- return error.details?.expectedProtocol === MIN_NODE_PROTOCOL_VERSION && (detailCode === ConnectErrorDetailCodes.PROTOCOL_MISMATCH || normalizeGatewayErrorText(error.message).includes("protocol mismatch"));
1112
- }
1113
- shouldRetryWithCurrentNodeProtocol(error) {
1114
- if (!this.useLegacyNodeProtocolEnvelope || !this.shouldNegotiateLegacyNodeProtocol() || !(error instanceof GatewayClientRequestError)) return false;
1104
+ shouldRetryWithAlternateNodeProtocol(error) {
1105
+ if (!this.shouldNegotiateLegacyNodeProtocol() || !(error instanceof GatewayClientRequestError)) return false;
1115
1106
  const detailCode = readConnectErrorDetailCode(error.details);
1116
- return error.details?.expectedProtocol === PROTOCOL_VERSION && (detailCode === ConnectErrorDetailCodes.PROTOCOL_MISMATCH || normalizeGatewayErrorText(error.message).includes("protocol mismatch"));
1107
+ return error.details?.expectedProtocol === (this.useLegacyNodeProtocolEnvelope ? PROTOCOL_VERSION : MIN_NODE_PROTOCOL_VERSION) && (detailCode === ConnectErrorDetailCodes.PROTOCOL_MISMATCH || normalizeGatewayErrorText(error.message).includes("protocol mismatch"));
1117
1108
  }
1118
1109
  buildDeviceConnectParams(params) {
1119
1110
  if (!this.opts.deviceIdentity) return;
@@ -1207,23 +1198,12 @@ var GatewayClient = class {
1207
1198
  return cleared instanceof Promise ? cleared.then(() => decision) : decision;
1208
1199
  }
1209
1200
  resolveConnectRequestFailure(error, assembled) {
1210
- if (this.shouldRetryWithCurrentNodeProtocol(error)) {
1211
- const resetBackoff = !this.nodeProtocolTransitionPending;
1212
- this.useLegacyNodeProtocolEnvelope = false;
1213
- this.nodeProtocolTransitionPending = true;
1214
- if (resetBackoff) this.protocol.resetReconnectBackoff(250);
1215
- this.logDebug("gateway rejected protocol v3; retrying node host with protocol v4");
1216
- return {
1217
- closeCode: 1008,
1218
- closeReason: "connect retry"
1219
- };
1220
- }
1221
- if (this.shouldRetryWithLegacyNodeProtocol(error)) {
1201
+ if (this.shouldRetryWithAlternateNodeProtocol(error)) {
1222
1202
  const resetBackoff = !this.nodeProtocolTransitionPending;
1223
- this.useLegacyNodeProtocolEnvelope = true;
1203
+ this.useLegacyNodeProtocolEnvelope = !this.useLegacyNodeProtocolEnvelope;
1224
1204
  this.nodeProtocolTransitionPending = true;
1225
1205
  if (resetBackoff) this.protocol.resetReconnectBackoff(250);
1226
- this.logDebug("gateway rejected protocol v4; retrying node host with protocol v3");
1206
+ this.logDebug(this.useLegacyNodeProtocolEnvelope ? "gateway rejected protocol v4; retrying node host with protocol v3" : "gateway rejected protocol v3; retrying node host with protocol v4");
1227
1207
  return {
1228
1208
  closeCode: 1008,
1229
1209
  closeReason: "connect retry"
@@ -1252,10 +1232,7 @@ var GatewayClient = class {
1252
1232
  reconnectDelayMs: startupRetryAfterMs
1253
1233
  };
1254
1234
  }
1255
- if (this.shouldFailClosedForUnsupportedAgentRuntimeIdentity({
1256
- error,
1257
- authAgentRuntimeIdentityToken: assembled.authAgentRuntimeIdentityToken
1258
- })) {
1235
+ if (assembled.authAgentRuntimeIdentityToken && this.isUnsupportedConnectAuthField(error, "agentruntimeidentitytoken")) {
1259
1236
  const unsupportedIdentityError = /* @__PURE__ */ new Error("gateway rejected required agent runtime identity auth field; refusing to retry without it");
1260
1237
  this.stopped = true;
1261
1238
  this.notifyConnectError(unsupportedIdentityError);
@@ -1266,10 +1243,7 @@ var GatewayClient = class {
1266
1243
  stop: true
1267
1244
  };
1268
1245
  }
1269
- if (this.shouldRetryWithoutApprovalRuntimeToken({
1270
- error,
1271
- authApprovalRuntimeToken: assembled.authApprovalRuntimeToken
1272
- })) {
1246
+ if (!this.approvalRuntimeTokenRetryBudgetUsed && assembled.authApprovalRuntimeToken && this.isUnsupportedConnectAuthField(error, "approvalruntimetoken")) {
1273
1247
  this.approvalRuntimeTokenCompatibilityDisabled = true;
1274
1248
  this.approvalRuntimeTokenRetryBudgetUsed = true;
1275
1249
  this.protocol.resetReconnectBackoff(250);
@@ -1386,20 +1360,10 @@ var GatewayClient = class {
1386
1360
  this.logDebug(`gateway client reconnect paused handler error: ${formatGatewayClientErrorForLog(err)}`);
1387
1361
  }
1388
1362
  }
1389
- shouldRetryWithoutApprovalRuntimeToken(params) {
1390
- if (this.approvalRuntimeTokenRetryBudgetUsed) return false;
1391
- if (!params.authApprovalRuntimeToken) return false;
1392
- if (!(params.error instanceof GatewayClientRequestError)) return false;
1393
- if (params.error.gatewayCode !== "INVALID_REQUEST") return false;
1394
- const message = normalizeGatewayErrorText(params.error.message);
1395
- return message.includes("invalid connect params") && message.includes("approvalruntimetoken");
1396
- }
1397
- shouldFailClosedForUnsupportedAgentRuntimeIdentity(params) {
1398
- if (!params.authAgentRuntimeIdentityToken) return false;
1399
- if (!(params.error instanceof GatewayClientRequestError)) return false;
1400
- if (params.error.gatewayCode !== "INVALID_REQUEST") return false;
1401
- const message = normalizeGatewayErrorText(params.error.message);
1402
- return message.includes("invalid connect params") && message.includes("agentruntimeidentitytoken");
1363
+ isUnsupportedConnectAuthField(error, field) {
1364
+ if (!(error instanceof GatewayClientRequestError) || error.gatewayCode !== "INVALID_REQUEST") return false;
1365
+ const message = normalizeGatewayErrorText(error.message);
1366
+ return message.includes("invalid connect params") && message.includes(field);
1403
1367
  }
1404
1368
  isTrustedDeviceRetryEndpoint() {
1405
1369
  const rawUrl = this.opts.url ?? "ws://127.0.0.1:18789";
@@ -1459,6 +1423,89 @@ function createGatewayRequestAbortError(method) {
1459
1423
  return err;
1460
1424
  }
1461
1425
  //#endregion
1426
+ //#region packages/gateway-client/src/run-recovery-text.ts
1427
+ async function recoverTerminalReply(params) {
1428
+ const { runId, scope, request, signal } = params;
1429
+ const result = asRecord(params.result);
1430
+ const disposition = asRecord(result.terminalReply).disposition;
1431
+ if (disposition === "silent" || disposition === "empty") return { outputText: "" };
1432
+ if (!scope.sessionKey) return { unavailable: "session-unavailable" };
1433
+ const receipt = asRecord(result.terminalReceipt);
1434
+ if (receipt.runId !== void 0 && receipt.runId !== runId) return { unavailable: "terminal-receipt-mismatch" };
1435
+ const expectedSessionId = typeof receipt.sessionId === "string" ? receipt.sessionId : scope.sessionId;
1436
+ const idempotencyKey = typeof receipt.assistantTranscriptIdempotencyKey === "string" ? receipt.assistantTranscriptIdempotencyKey : void 0;
1437
+ const matches = (message) => {
1438
+ const metadata = asRecord(message["__openclaw"]);
1439
+ return message.role === "assistant" && asRecord(message.openclawStreamFallback).source !== "segment" && (metadata.runId === runId || idempotencyKey !== void 0 && metadata.idempotencyKey === idempotencyKey);
1440
+ };
1441
+ const target = {
1442
+ sessionKey: scope.sessionKey,
1443
+ ...scope.agentId ? { agentId: scope.agentId } : {}
1444
+ };
1445
+ let offset;
1446
+ let sessionId = expectedSessionId;
1447
+ const pages = [];
1448
+ let complete = false;
1449
+ try {
1450
+ for (let pageIndex = 0; pageIndex < 10; pageIndex += 1) {
1451
+ signal.throwIfAborted();
1452
+ const page = asRecord(await request("chat.history", {
1453
+ ...target,
1454
+ limit: 200,
1455
+ ...offset !== void 0 ? { offset } : {}
1456
+ }, signal));
1457
+ if (sessionId !== void 0 && page.sessionId !== sessionId) return { unavailable: "session-changed" };
1458
+ if (typeof page.sessionId === "string") sessionId = page.sessionId;
1459
+ const messages = Array.isArray(page.messages) ? page.messages.map(asRecord) : [];
1460
+ pages.unshift(messages.filter(matches));
1461
+ if (page.hasMore !== true) {
1462
+ complete = true;
1463
+ break;
1464
+ }
1465
+ if (typeof page.nextOffset !== "number" || !Number.isSafeInteger(page.nextOffset) || page.nextOffset <= (offset ?? 0)) break;
1466
+ offset = page.nextOffset;
1467
+ }
1468
+ if (!complete) return { unavailable: "history-limit-reached" };
1469
+ const messages = pages.flat();
1470
+ if (messages.length === 0 || idempotencyKey !== void 0 && !messages.some((message) => asRecord(message["__openclaw"]).idempotencyKey === idempotencyKey)) return { unavailable: "reply-not-found" };
1471
+ let outputText = "";
1472
+ const seen = /* @__PURE__ */ new Set();
1473
+ for (let message of messages) {
1474
+ signal.throwIfAborted();
1475
+ const metadata = asRecord(message["__openclaw"]);
1476
+ if (typeof metadata.id === "string") {
1477
+ if (seen.has(metadata.id)) continue;
1478
+ seen.add(metadata.id);
1479
+ }
1480
+ if (metadata.truncated === true) {
1481
+ if (typeof metadata.id !== "string") return { unavailable: "message-identity-unavailable" };
1482
+ const full = asRecord(await request("chat.message.get", {
1483
+ ...target,
1484
+ messageId: metadata.id
1485
+ }, signal));
1486
+ message = asRecord(full.message);
1487
+ if (full.ok !== true || asRecord(message["__openclaw"]).id !== metadata.id || !matches(message) || asRecord(message["__openclaw"]).truncated === true) return { unavailable: "full-message-unavailable" };
1488
+ }
1489
+ const content = typeof message.text === "string" ? message.text : message.content;
1490
+ if (typeof content !== "string" && !Array.isArray(content)) return { unavailable: "reply-text-unavailable" };
1491
+ const text = typeof content === "string" ? content : content.flatMap((block) => {
1492
+ const part = asRecord(block);
1493
+ return (part.type === "text" || part.type === "input_text" || part.type === "output_text") && typeof part.text === "string" ? [part.text] : [];
1494
+ }).join("\n");
1495
+ if (outputText && text) {
1496
+ const trailing = outputText.endsWith("\n\n") ? 2 : outputText.endsWith("\n") ? 1 : 0;
1497
+ const leading = text.startsWith("\n\n") ? 2 : text.startsWith("\n") ? 1 : 0;
1498
+ outputText += "\n".repeat(Math.max(0, 2 - trailing - leading));
1499
+ }
1500
+ outputText += text;
1501
+ }
1502
+ return { outputText };
1503
+ } catch {
1504
+ signal.throwIfAborted();
1505
+ return { unavailable: "history-request-failed" };
1506
+ }
1507
+ }
1508
+ //#endregion
1462
1509
  //#region packages/gateway-client/src/cloudflare-access.ts
1463
1510
  const CF_ACCESS_CLIENT_ID_HEADER = "CF-Access-Client-Id";
1464
1511
  const CF_ACCESS_CLIENT_SECRET_HEADER = "CF-Access-Client-Secret";
@@ -1470,4 +1517,4 @@ function buildCloudflareAccessHeaders(credentials) {
1470
1517
  };
1471
1518
  }
1472
1519
  //#endregion
1473
- export { CF_ACCESS_CLIENT_ID_HEADER, CF_ACCESS_CLIENT_SECRET_HEADER, DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, GatewayBrowserDeviceAuthLifecycle, GatewayClient, GatewayClientRequestError, GatewayClientRequestTimeoutError, GatewayScopeUpgrade, GatewaySessionMessageSubscriptionCoordinator, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, buildCloudflareAccessHeaders, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getConnectChallengeTimeoutMsFromEnv, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayConnectAssemblyError, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolveGatewayConnectScopes, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken, startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait, startGatewayConnectTimeout, waitForEventLoopReady };
1520
+ export { CF_ACCESS_CLIENT_ID_HEADER, CF_ACCESS_CLIENT_SECRET_HEADER, DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, GatewayBrowserDeviceAuthLifecycle, GatewayClient, GatewayClientRequestError, GatewayClientRequestTimeoutError, GatewayScopeUpgrade, GatewaySessionMessageSubscriptionCoordinator, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, buildCloudflareAccessHeaders, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getConnectChallengeTimeoutMsFromEnv, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayConnectAssemblyError, isGatewayProtocolResponseError, isLocallyOptimisticSessionMessage, isSessionProjectionErrorMessage, mergeChatStreamMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, projectSessionTerminalReplyMessage, readAssistantStreamSegmentIdentity, readSessionMessageIdentity, readSessionMessageSequence, readSessionProjectionFinalMessageIdentity, reconcileSessionProjectionSnapshot, recoverTerminalReply, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolveGatewayConnectScopes, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken, startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait, startGatewayConnectTimeout, waitForEventLoopReady };