@langchain/langgraph-sdk 1.9.19 → 1.9.21

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.
Files changed (110) hide show
  1. package/dist/client/index.cjs +2 -1
  2. package/dist/client/index.cjs.map +1 -1
  3. package/dist/client/index.d.cts +1 -0
  4. package/dist/client/index.d.cts.map +1 -1
  5. package/dist/client/index.d.ts +1 -0
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +2 -1
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/client/stream/error.cjs +21 -0
  10. package/dist/client/stream/error.cjs.map +1 -1
  11. package/dist/client/stream/error.js +21 -1
  12. package/dist/client/stream/error.js.map +1 -1
  13. package/dist/client/stream/index.cjs +24 -1
  14. package/dist/client/stream/index.cjs.map +1 -1
  15. package/dist/client/stream/index.d.cts.map +1 -1
  16. package/dist/client/stream/index.d.ts.map +1 -1
  17. package/dist/client/stream/index.js +24 -1
  18. package/dist/client/stream/index.js.map +1 -1
  19. package/dist/client/stream/resolve-client-api-url.cjs +20 -0
  20. package/dist/client/stream/resolve-client-api-url.cjs.map +1 -0
  21. package/dist/client/stream/resolve-client-api-url.d.cts +17 -0
  22. package/dist/client/stream/resolve-client-api-url.d.cts.map +1 -0
  23. package/dist/client/stream/resolve-client-api-url.d.ts +17 -0
  24. package/dist/client/stream/resolve-client-api-url.d.ts.map +1 -0
  25. package/dist/client/stream/resolve-client-api-url.js +20 -0
  26. package/dist/client/stream/resolve-client-api-url.js.map +1 -0
  27. package/dist/client/stream/transport/agent-server.cjs +13 -1
  28. package/dist/client/stream/transport/agent-server.cjs.map +1 -1
  29. package/dist/client/stream/transport/agent-server.d.cts +13 -0
  30. package/dist/client/stream/transport/agent-server.d.cts.map +1 -1
  31. package/dist/client/stream/transport/agent-server.d.ts +13 -0
  32. package/dist/client/stream/transport/agent-server.d.ts.map +1 -1
  33. package/dist/client/stream/transport/agent-server.js +13 -1
  34. package/dist/client/stream/transport/agent-server.js.map +1 -1
  35. package/dist/client/stream/transport/http.cjs +74 -10
  36. package/dist/client/stream/transport/http.cjs.map +1 -1
  37. package/dist/client/stream/transport/http.d.cts +25 -1
  38. package/dist/client/stream/transport/http.d.cts.map +1 -1
  39. package/dist/client/stream/transport/http.d.ts +25 -1
  40. package/dist/client/stream/transport/http.d.ts.map +1 -1
  41. package/dist/client/stream/transport/http.js +74 -10
  42. package/dist/client/stream/transport/http.js.map +1 -1
  43. package/dist/client/stream/transport/index.cjs +2 -1
  44. package/dist/client/stream/transport/index.js +2 -1
  45. package/dist/client/stream/transport/types.d.cts +46 -0
  46. package/dist/client/stream/transport/types.d.cts.map +1 -1
  47. package/dist/client/stream/transport/types.d.ts +46 -0
  48. package/dist/client/stream/transport/types.d.ts.map +1 -1
  49. package/dist/client/stream/transport/websocket.cjs +105 -16
  50. package/dist/client/stream/transport/websocket.cjs.map +1 -1
  51. package/dist/client/stream/transport/websocket.d.cts +19 -0
  52. package/dist/client/stream/transport/websocket.d.cts.map +1 -1
  53. package/dist/client/stream/transport/websocket.d.ts +19 -0
  54. package/dist/client/stream/transport/websocket.d.ts.map +1 -1
  55. package/dist/client/stream/transport/websocket.js +105 -17
  56. package/dist/client/stream/transport/websocket.js.map +1 -1
  57. package/dist/client/stream/transport.d.cts +10 -4
  58. package/dist/client/stream/transport.d.cts.map +1 -1
  59. package/dist/client/stream/transport.d.ts +10 -4
  60. package/dist/client/stream/transport.d.ts.map +1 -1
  61. package/dist/client/stream/types.d.cts +17 -0
  62. package/dist/client/stream/types.d.cts.map +1 -1
  63. package/dist/client/stream/types.d.ts +17 -0
  64. package/dist/client/stream/types.d.ts.map +1 -1
  65. package/dist/client/threads/index.cjs +30 -14
  66. package/dist/client/threads/index.cjs.map +1 -1
  67. package/dist/client/threads/index.js +30 -14
  68. package/dist/client/threads/index.js.map +1 -1
  69. package/dist/client.cjs +3 -1
  70. package/dist/client.d.cts +2 -1
  71. package/dist/client.d.ts +2 -1
  72. package/dist/client.js +3 -2
  73. package/dist/index.cjs +1 -1
  74. package/dist/index.js +1 -1
  75. package/dist/stream/controller.cjs +31 -2
  76. package/dist/stream/controller.cjs.map +1 -1
  77. package/dist/stream/controller.d.cts.map +1 -1
  78. package/dist/stream/controller.d.ts.map +1 -1
  79. package/dist/stream/controller.js +31 -2
  80. package/dist/stream/controller.js.map +1 -1
  81. package/dist/stream/index.cjs +2 -0
  82. package/dist/stream/index.d.cts +2 -1
  83. package/dist/stream/index.d.ts +2 -1
  84. package/dist/stream/index.js +2 -1
  85. package/dist/stream/projections/channel-effect.cjs +52 -0
  86. package/dist/stream/projections/channel-effect.cjs.map +1 -0
  87. package/dist/stream/projections/channel-effect.d.cts +35 -0
  88. package/dist/stream/projections/channel-effect.d.cts.map +1 -0
  89. package/dist/stream/projections/channel-effect.d.ts +35 -0
  90. package/dist/stream/projections/channel-effect.d.ts.map +1 -0
  91. package/dist/stream/projections/channel-effect.js +52 -0
  92. package/dist/stream/projections/channel-effect.js.map +1 -0
  93. package/dist/stream/projections/index.cjs +1 -0
  94. package/dist/stream/projections/index.d.ts +1 -0
  95. package/dist/stream/projections/index.js +1 -0
  96. package/dist/stream/root-message-projection.cjs +55 -0
  97. package/dist/stream/root-message-projection.cjs.map +1 -1
  98. package/dist/stream/root-message-projection.js +55 -0
  99. package/dist/stream/root-message-projection.js.map +1 -1
  100. package/dist/ui/branching.d.cts +1 -1
  101. package/dist/ui/branching.d.ts +1 -1
  102. package/dist/ui/orchestrator.d.cts +1 -1
  103. package/dist/ui/orchestrator.d.cts.map +1 -1
  104. package/dist/ui/orchestrator.d.ts +1 -1
  105. package/dist/ui/orchestrator.d.ts.map +1 -1
  106. package/dist/utils/stream.d.cts +1 -1
  107. package/dist/utils/stream.d.cts.map +1 -1
  108. package/dist/utils/stream.d.ts +1 -1
  109. package/dist/utils/stream.d.ts.map +1 -1
  110. package/package.json +5 -2
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.cjs","names":["AsyncQueue","toWebSocketUrl","toAbsoluteUrl","hasHeaders","toError","isRecord"],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport {\n toAbsoluteUrl,\n toWebSocketUrl,\n isRecord,\n hasHeaders,\n toError,\n} from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n }\n\n async open(): Promise<void> {\n if (this.socket != null) return;\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n toAbsoluteUrl(this.apiUrl, this.streamUrl).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.closed = false;\n this.intentionalClose = false;\n\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WebSocket.CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WebSocket.OPEN ||\n socket.readyState === WebSocket.CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n const socket = this.socket;\n if (socket == null || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n const error = new Error(\"Protocol WebSocket closed unexpectedly.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n const error = new Error(\"Protocol WebSocket encountered an error.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"websocket.cjs","names":["AsyncQueue","#detachSocket","toWebSocketUrl","toAbsoluteUrl","#attachSocket","hasHeaders","toError","isRecord","#handleUnexpectedDisconnect","#scheduleReconnect","#runReconnectLoop","MaxWebSocketReconnectAttemptsError"],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport {\n toAbsoluteUrl,\n toWebSocketUrl,\n isRecord,\n hasHeaders,\n toError,\n} from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\nimport { MaxWebSocketReconnectAttemptsError } from \"../error.js\";\n\nconst WEB_SOCKET_CONNECTING = 0;\nconst WEB_SOCKET_OPEN = 1;\nconst WEB_SOCKET_CLOSED = 3;\n\n/**\n * Reconnect tuning for {@link ProtocolWebSocketTransportAdapter}. A subset\n * of {@link ProtocolWebSocketTransportOptions}.\n */\nexport interface WebSocketReconnectOptions {\n /**\n * Maximum reconnection attempts after an unexpected disconnect.\n * Defaults to 5.\n */\n maxReconnectAttempts?: number;\n\n /**\n * Invoked before each reconnect attempt (after backoff).\n */\n onReconnect?: (options: { attempt: number; cause: unknown }) => void;\n}\n\n/**\n * Exponential backoff with jitter for WebSocket reconnect. Mirrors\n * {@link streamWithRetry} in `utils/stream.ts` (capped at 5s + 1s jitter).\n */\nexport function webSocketReconnectDelayMs(attempt: number): number {\n const baseDelay = Math.min(1000 * 2 ** (attempt - 1), 5000);\n const jitter = Math.random() * 1000;\n return baseDelay + jitter;\n}\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n *\n * On unexpected disconnect the adapter reconnects with exponential\n * backoff (see {@link ProtocolWebSocketTransportOptions.maxReconnectAttempts}).\n * The server replays buffered events on the new socket; the SDK\n * deduplicates by `event_id`. {@link ProtocolWebSocketTransportOptions.onReconnected}\n * runs after each successful reconnect so `ThreadStream` can re-issue\n * `subscription.subscribe` commands.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly maxReconnectAttempts: number;\n\n private readonly onReconnect?: ProtocolWebSocketTransportOptions[\"onReconnect\"];\n\n private readonly reconnectDelayMs: (attempt: number) => number;\n\n private onReconnected?: () => void | Promise<void>;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n private reconnectInFlight: Promise<void> | null = null;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\n this.onReconnect = options.onReconnect;\n this.onReconnected = options.onReconnected;\n this.reconnectDelayMs =\n options.reconnectDelayMs ?? webSocketReconnectDelayMs;\n }\n\n /**\n * Register a callback invoked after each successful reconnect. Used\n * by {@link ThreadStream} to re-send active `subscription.subscribe`\n * commands.\n */\n setOnReconnected(handler: () => void | Promise<void>): void {\n this.onReconnected = handler;\n }\n\n async open(): Promise<void> {\n if (this.closed) {\n throw new Error(\"Protocol WebSocket transport is closed.\");\n }\n if (this.socket?.readyState === WEB_SOCKET_OPEN) {\n return;\n }\n if (this.socket != null) {\n this.#detachSocket(this.socket);\n this.socket = null;\n }\n\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n toAbsoluteUrl(this.apiUrl, this.streamUrl).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.intentionalClose = false;\n\n this.#attachSocket(socket);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n this.#detachSocket(socket);\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WEB_SOCKET_CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WEB_SOCKET_OPEN ||\n socket.readyState === WEB_SOCKET_CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n // Wait for an in-flight reconnect only when the socket is not usable.\n // After `open()` succeeds, `#runReconnectLoop` may still be awaiting\n // `onReconnected` (e.g. ThreadStream resubscribe). Those callbacks call\n // `sendCommand` and must not await the same `reconnectInFlight` promise.\n let socket = this.socket;\n if (\n this.reconnectInFlight != null &&\n (socket == null || socket.readyState !== WEB_SOCKET_OPEN)\n ) {\n await this.reconnectInFlight.catch(() => undefined);\n socket = this.socket;\n }\n\n if (socket == null || socket.readyState !== WEB_SOCKET_OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n #attachSocket(socket: WebSocket): void {\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n }\n\n #detachSocket(socket: WebSocket): void {\n socket.removeEventListener(\"message\", this.handleMessage);\n socket.removeEventListener(\"close\", this.handleClose);\n socket.removeEventListener(\"error\", this.handleSocketError);\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n const socket = this.socket;\n if (socket != null) {\n this.#detachSocket(socket);\n }\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n this.#handleUnexpectedDisconnect(\n new Error(\"Protocol WebSocket closed unexpectedly.\")\n );\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n this.#handleUnexpectedDisconnect(\n new Error(\"Protocol WebSocket encountered an error.\")\n );\n };\n\n #handleUnexpectedDisconnect(cause: unknown): void {\n const error = toError(cause);\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n\n if (this.maxReconnectAttempts <= 0) {\n this.queue.close(error);\n return;\n }\n\n this.#scheduleReconnect(cause);\n }\n\n #scheduleReconnect(cause: unknown): void {\n if (this.closed || this.intentionalClose) {\n return;\n }\n if (this.reconnectInFlight != null) {\n return;\n }\n\n this.reconnectInFlight = this.#runReconnectLoop(cause).finally(() => {\n this.reconnectInFlight = null;\n });\n }\n\n async #runReconnectLoop(initialCause: unknown): Promise<void> {\n let lastError: unknown = initialCause;\n\n for (let attempt = 1; attempt <= this.maxReconnectAttempts; attempt += 1) {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n this.onReconnect?.({ attempt, cause: lastError });\n\n const delay = this.reconnectDelayMs(attempt);\n if (delay > 0) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n try {\n await this.open();\n if (this.onReconnected) {\n await this.onReconnected();\n }\n return;\n } catch (error) {\n lastError = error;\n }\n }\n\n this.queue.close(\n new MaxWebSocketReconnectAttemptsError(\n this.maxReconnectAttempts,\n lastError\n )\n );\n }\n}\n"],"mappings":"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"}
@@ -7,8 +7,16 @@ import { Command, CommandResponse, ErrorResponse, Message } from "@langchain/pro
7
7
  * Transport adapter that speaks the thread-centric protocol over a
8
8
  * bidirectional WebSocket. Bound to a specific `threadId` — the socket
9
9
  * connects to `ws://.../threads/:thread_id/stream/events`.
10
+ *
11
+ * On unexpected disconnect the adapter reconnects with exponential
12
+ * backoff (see {@link ProtocolWebSocketTransportOptions.maxReconnectAttempts}).
13
+ * The server replays buffered events on the new socket; the SDK
14
+ * deduplicates by `event_id`. {@link ProtocolWebSocketTransportOptions.onReconnected}
15
+ * runs after each successful reconnect so `ThreadStream` can re-issue
16
+ * `subscription.subscribe` commands.
10
17
  */
11
18
  declare class ProtocolWebSocketTransportAdapter implements TransportAdapter {
19
+ #private;
12
20
  readonly threadId: string;
13
21
  private readonly queue;
14
22
  private readonly apiUrl;
@@ -16,11 +24,22 @@ declare class ProtocolWebSocketTransportAdapter implements TransportAdapter {
16
24
  private readonly onRequest?;
17
25
  private readonly webSocketFactory;
18
26
  private readonly streamUrl;
27
+ private readonly maxReconnectAttempts;
28
+ private readonly onReconnect?;
29
+ private readonly reconnectDelayMs;
30
+ private onReconnected?;
19
31
  private readonly pending;
20
32
  private socket;
21
33
  private closed;
22
34
  private intentionalClose;
35
+ private reconnectInFlight;
23
36
  constructor(options: ProtocolWebSocketTransportOptions);
37
+ /**
38
+ * Register a callback invoked after each successful reconnect. Used
39
+ * by {@link ThreadStream} to re-send active `subscription.subscribe`
40
+ * commands.
41
+ */
42
+ setOnReconnected(handler: () => void | Promise<void>): void;
24
43
  open(): Promise<void>;
25
44
  send(command: Command): Promise<CommandResponse | ErrorResponse | void>;
26
45
  events(): AsyncIterable<Message>;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.d.cts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;AA4BA;;;cAAa,iCAAA,YAA6C,gBAAA;EAAA,SAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAWf,IAAA,CAAA,GAAQ,OAAA;EAkCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA2CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBAsBG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAgBA,iBAAA;AAAA"}
1
+ {"version":3,"file":"websocket.d.cts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;;;;;;;;;;;cAmEa,iCAAA,YAA6C,gBAAA;EAAA;WAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,oBAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,gBAAA;EAAA,QAET,aAAA;EAAA,iBAES,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAAA,QAEA,iBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAqFN;;;;;EAhEf,gBAAA,CAAiB,OAAA,eAAsB,OAAA;EAIjC,IAAA,CAAA,GAAQ,OAAA;EAyCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA6CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBA8CG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAiBA,iBAAA;AAAA"}
@@ -7,8 +7,16 @@ import { Command, CommandResponse, ErrorResponse, Message } from "@langchain/pro
7
7
  * Transport adapter that speaks the thread-centric protocol over a
8
8
  * bidirectional WebSocket. Bound to a specific `threadId` — the socket
9
9
  * connects to `ws://.../threads/:thread_id/stream/events`.
10
+ *
11
+ * On unexpected disconnect the adapter reconnects with exponential
12
+ * backoff (see {@link ProtocolWebSocketTransportOptions.maxReconnectAttempts}).
13
+ * The server replays buffered events on the new socket; the SDK
14
+ * deduplicates by `event_id`. {@link ProtocolWebSocketTransportOptions.onReconnected}
15
+ * runs after each successful reconnect so `ThreadStream` can re-issue
16
+ * `subscription.subscribe` commands.
10
17
  */
11
18
  declare class ProtocolWebSocketTransportAdapter implements TransportAdapter {
19
+ #private;
12
20
  readonly threadId: string;
13
21
  private readonly queue;
14
22
  private readonly apiUrl;
@@ -16,11 +24,22 @@ declare class ProtocolWebSocketTransportAdapter implements TransportAdapter {
16
24
  private readonly onRequest?;
17
25
  private readonly webSocketFactory;
18
26
  private readonly streamUrl;
27
+ private readonly maxReconnectAttempts;
28
+ private readonly onReconnect?;
29
+ private readonly reconnectDelayMs;
30
+ private onReconnected?;
19
31
  private readonly pending;
20
32
  private socket;
21
33
  private closed;
22
34
  private intentionalClose;
35
+ private reconnectInFlight;
23
36
  constructor(options: ProtocolWebSocketTransportOptions);
37
+ /**
38
+ * Register a callback invoked after each successful reconnect. Used
39
+ * by {@link ThreadStream} to re-send active `subscription.subscribe`
40
+ * commands.
41
+ */
42
+ setOnReconnected(handler: () => void | Promise<void>): void;
24
43
  open(): Promise<void>;
25
44
  send(command: Command): Promise<CommandResponse | ErrorResponse | void>;
26
45
  events(): AsyncIterable<Message>;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.d.ts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;AA4BA;;;cAAa,iCAAA,YAA6C,gBAAA;EAAA,SAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAWf,IAAA,CAAA,GAAQ,OAAA;EAkCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA2CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBAsBG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAgBA,iBAAA;AAAA"}
1
+ {"version":3,"file":"websocket.d.ts","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"mappings":";;;;;;;;;;;;;;;;;cAmEa,iCAAA,YAA6C,gBAAA;EAAA;WAC/C,QAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,MAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,oBAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,gBAAA;EAAA,QAET,aAAA;EAAA,iBAES,OAAA;EAAA,QAET,MAAA;EAAA,QAEA,MAAA;EAAA,QAEA,gBAAA;EAAA,QAEA,iBAAA;EAER,WAAA,CAAY,OAAA,EAAS,iCAAA;EAqFN;;;;;EAhEf,gBAAA,CAAiB,OAAA,eAAsB,OAAA;EAIjC,IAAA,CAAA,GAAQ,OAAA;EAyCR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAI7B,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAalB,KAAA,CAAA,GAAS,OAAA;EAAA,QA6CP,gCAAA;EAAA,QAQM,WAAA;EAAA,iBA8CG,aAAA;EAAA,iBA0BA,WAAA;EAAA,iBAiBA,iBAAA;AAAA"}
@@ -1,10 +1,28 @@
1
+ import { MaxWebSocketReconnectAttemptsError } from "../error.js";
1
2
  import { AsyncQueue } from "./queue.js";
2
3
  import { hasHeaders, isRecord, toAbsoluteUrl, toError, toWebSocketUrl } from "./utils.js";
3
4
  //#region src/client/stream/transport/websocket.ts
5
+ const WEB_SOCKET_CONNECTING = 0;
6
+ const WEB_SOCKET_OPEN = 1;
7
+ const WEB_SOCKET_CLOSED = 3;
8
+ /**
9
+ * Exponential backoff with jitter for WebSocket reconnect. Mirrors
10
+ * {@link streamWithRetry} in `utils/stream.ts` (capped at 5s + 1s jitter).
11
+ */
12
+ function webSocketReconnectDelayMs(attempt) {
13
+ return Math.min(1e3 * 2 ** (attempt - 1), 5e3) + Math.random() * 1e3;
14
+ }
4
15
  /**
5
16
  * Transport adapter that speaks the thread-centric protocol over a
6
17
  * bidirectional WebSocket. Bound to a specific `threadId` — the socket
7
18
  * connects to `ws://.../threads/:thread_id/stream/events`.
19
+ *
20
+ * On unexpected disconnect the adapter reconnects with exponential
21
+ * backoff (see {@link ProtocolWebSocketTransportOptions.maxReconnectAttempts}).
22
+ * The server replays buffered events on the new socket; the SDK
23
+ * deduplicates by `event_id`. {@link ProtocolWebSocketTransportOptions.onReconnected}
24
+ * runs after each successful reconnect so `ThreadStream` can re-issue
25
+ * `subscription.subscribe` commands.
8
26
  */
9
27
  var ProtocolWebSocketTransportAdapter = class {
10
28
  threadId;
@@ -14,10 +32,15 @@ var ProtocolWebSocketTransportAdapter = class {
14
32
  onRequest;
15
33
  webSocketFactory;
16
34
  streamUrl;
35
+ maxReconnectAttempts;
36
+ onReconnect;
37
+ reconnectDelayMs;
38
+ onReconnected;
17
39
  pending = /* @__PURE__ */ new Map();
18
40
  socket = null;
19
41
  closed = false;
20
42
  intentionalClose = false;
43
+ reconnectInFlight = null;
21
44
  constructor(options) {
22
45
  this.apiUrl = options.apiUrl;
23
46
  this.threadId = options.threadId;
@@ -25,18 +48,32 @@ var ProtocolWebSocketTransportAdapter = class {
25
48
  this.onRequest = options.onRequest;
26
49
  this.webSocketFactory = options.webSocketFactory ?? ((url) => new WebSocket(url));
27
50
  this.streamUrl = options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;
51
+ this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
52
+ this.onReconnect = options.onReconnect;
53
+ this.onReconnected = options.onReconnected;
54
+ this.reconnectDelayMs = options.reconnectDelayMs ?? webSocketReconnectDelayMs;
55
+ }
56
+ /**
57
+ * Register a callback invoked after each successful reconnect. Used
58
+ * by {@link ThreadStream} to re-send active `subscription.subscribe`
59
+ * commands.
60
+ */
61
+ setOnReconnected(handler) {
62
+ this.onReconnected = handler;
28
63
  }
29
64
  async open() {
30
- if (this.socket != null) return;
65
+ if (this.closed) throw new Error("Protocol WebSocket transport is closed.");
66
+ if (this.socket?.readyState === WEB_SOCKET_OPEN) return;
67
+ if (this.socket != null) {
68
+ this.#detachSocket(this.socket);
69
+ this.socket = null;
70
+ }
31
71
  this.assertBrowserSafeTransportConfig();
32
72
  const wsUrl = toWebSocketUrl(toAbsoluteUrl(this.apiUrl, this.streamUrl).toString());
33
73
  const socket = this.webSocketFactory(wsUrl);
34
74
  this.socket = socket;
35
- this.closed = false;
36
75
  this.intentionalClose = false;
37
- socket.addEventListener("message", this.handleMessage);
38
- socket.addEventListener("close", this.handleClose);
39
- socket.addEventListener("error", this.handleSocketError);
76
+ this.#attachSocket(socket);
40
77
  await new Promise((resolve, reject) => {
41
78
  const onOpen = () => {
42
79
  cleanup();
@@ -80,8 +117,9 @@ var ProtocolWebSocketTransportAdapter = class {
80
117
  const socket = this.socket;
81
118
  this.socket = null;
82
119
  if (!socket) return;
120
+ this.#detachSocket(socket);
83
121
  await new Promise((resolve) => {
84
- if (socket.readyState === WebSocket.CLOSED) {
122
+ if (socket.readyState === WEB_SOCKET_CLOSED) {
85
123
  resolve();
86
124
  return;
87
125
  }
@@ -90,7 +128,7 @@ var ProtocolWebSocketTransportAdapter = class {
90
128
  resolve();
91
129
  };
92
130
  socket.addEventListener("close", onClose, { once: true });
93
- if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) socket.close();
131
+ if (socket.readyState === WEB_SOCKET_OPEN || socket.readyState === WEB_SOCKET_CONNECTING) socket.close();
94
132
  else resolve();
95
133
  });
96
134
  }
@@ -98,8 +136,12 @@ var ProtocolWebSocketTransportAdapter = class {
98
136
  if (hasHeaders(this.defaultHeaders) || this.onRequest != null) throw new Error("Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.");
99
137
  }
100
138
  async sendCommand(command) {
101
- const socket = this.socket;
102
- if (socket == null || socket.readyState !== WebSocket.OPEN) throw new Error("Protocol WebSocket is not open.");
139
+ let socket = this.socket;
140
+ if (this.reconnectInFlight != null && (socket == null || socket.readyState !== WEB_SOCKET_OPEN)) {
141
+ await this.reconnectInFlight.catch(() => void 0);
142
+ socket = this.socket;
143
+ }
144
+ if (socket == null || socket.readyState !== WEB_SOCKET_OPEN) throw new Error("Protocol WebSocket is not open.");
103
145
  return await new Promise((resolve, reject) => {
104
146
  this.pending.set(command.id, {
105
147
  resolve,
@@ -113,6 +155,16 @@ var ProtocolWebSocketTransportAdapter = class {
113
155
  }
114
156
  });
115
157
  }
158
+ #attachSocket(socket) {
159
+ socket.addEventListener("message", this.handleMessage);
160
+ socket.addEventListener("close", this.handleClose);
161
+ socket.addEventListener("error", this.handleSocketError);
162
+ }
163
+ #detachSocket(socket) {
164
+ socket.removeEventListener("message", this.handleMessage);
165
+ socket.removeEventListener("close", this.handleClose);
166
+ socket.removeEventListener("error", this.handleSocketError);
167
+ }
116
168
  handleMessage = (event) => {
117
169
  let payload;
118
170
  try {
@@ -131,25 +183,61 @@ var ProtocolWebSocketTransportAdapter = class {
131
183
  if (isRecord(payload) && payload.type === "event") this.queue.push(payload);
132
184
  };
133
185
  handleClose = () => {
186
+ const socket = this.socket;
187
+ if (socket != null) this.#detachSocket(socket);
134
188
  this.socket = null;
135
189
  if (this.intentionalClose || this.closed) {
136
190
  this.queue.close();
137
191
  return;
138
192
  }
139
- const error = /* @__PURE__ */ new Error("Protocol WebSocket closed unexpectedly.");
140
- for (const { reject } of this.pending.values()) reject(error);
141
- this.pending.clear();
142
- this.queue.close(error);
193
+ this.#handleUnexpectedDisconnect(/* @__PURE__ */ new Error("Protocol WebSocket closed unexpectedly."));
143
194
  };
144
195
  handleSocketError = () => {
145
196
  if (this.closed || this.intentionalClose) return;
146
- const error = /* @__PURE__ */ new Error("Protocol WebSocket encountered an error.");
197
+ this.#handleUnexpectedDisconnect(/* @__PURE__ */ new Error("Protocol WebSocket encountered an error."));
198
+ };
199
+ #handleUnexpectedDisconnect(cause) {
200
+ const error = toError(cause);
147
201
  for (const { reject } of this.pending.values()) reject(error);
148
202
  this.pending.clear();
149
- this.queue.close(error);
150
- };
203
+ if (this.maxReconnectAttempts <= 0) {
204
+ this.queue.close(error);
205
+ return;
206
+ }
207
+ this.#scheduleReconnect(cause);
208
+ }
209
+ #scheduleReconnect(cause) {
210
+ if (this.closed || this.intentionalClose) return;
211
+ if (this.reconnectInFlight != null) return;
212
+ this.reconnectInFlight = this.#runReconnectLoop(cause).finally(() => {
213
+ this.reconnectInFlight = null;
214
+ });
215
+ }
216
+ async #runReconnectLoop(initialCause) {
217
+ let lastError = initialCause;
218
+ for (let attempt = 1; attempt <= this.maxReconnectAttempts; attempt += 1) {
219
+ if (this.closed || this.intentionalClose) return;
220
+ this.onReconnect?.({
221
+ attempt,
222
+ cause: lastError
223
+ });
224
+ const delay = this.reconnectDelayMs(attempt);
225
+ if (delay > 0) await new Promise((resolve) => {
226
+ setTimeout(resolve, delay);
227
+ });
228
+ if (this.closed || this.intentionalClose) return;
229
+ try {
230
+ await this.open();
231
+ if (this.onReconnected) await this.onReconnected();
232
+ return;
233
+ } catch (error) {
234
+ lastError = error;
235
+ }
236
+ }
237
+ this.queue.close(new MaxWebSocketReconnectAttemptsError(this.maxReconnectAttempts, lastError));
238
+ }
151
239
  };
152
240
  //#endregion
153
- export { ProtocolWebSocketTransportAdapter };
241
+ export { ProtocolWebSocketTransportAdapter, webSocketReconnectDelayMs };
154
242
 
155
243
  //# sourceMappingURL=websocket.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.js","names":[],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport {\n toAbsoluteUrl,\n toWebSocketUrl,\n isRecord,\n hasHeaders,\n toError,\n} from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n }\n\n async open(): Promise<void> {\n if (this.socket != null) return;\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n toAbsoluteUrl(this.apiUrl, this.streamUrl).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.closed = false;\n this.intentionalClose = false;\n\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WebSocket.CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WebSocket.OPEN ||\n socket.readyState === WebSocket.CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n const socket = this.socket;\n if (socket == null || socket.readyState !== WebSocket.OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n const error = new Error(\"Protocol WebSocket closed unexpectedly.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n const error = new Error(\"Protocol WebSocket encountered an error.\");\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n this.queue.close(error);\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"websocket.js","names":["#detachSocket","#attachSocket","#handleUnexpectedDisconnect","#scheduleReconnect","#runReconnectLoop"],"sources":["../../../../src/client/stream/transport/websocket.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport type {\n Message,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport {\n toAbsoluteUrl,\n toWebSocketUrl,\n isRecord,\n hasHeaders,\n toError,\n} from \"./utils.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n PendingResponse,\n ProtocolWebSocketTransportOptions,\n} from \"./types.js\";\nimport type { TransportAdapter } from \"../transport.js\";\nimport { MaxWebSocketReconnectAttemptsError } from \"../error.js\";\n\nconst WEB_SOCKET_CONNECTING = 0;\nconst WEB_SOCKET_OPEN = 1;\nconst WEB_SOCKET_CLOSED = 3;\n\n/**\n * Reconnect tuning for {@link ProtocolWebSocketTransportAdapter}. A subset\n * of {@link ProtocolWebSocketTransportOptions}.\n */\nexport interface WebSocketReconnectOptions {\n /**\n * Maximum reconnection attempts after an unexpected disconnect.\n * Defaults to 5.\n */\n maxReconnectAttempts?: number;\n\n /**\n * Invoked before each reconnect attempt (after backoff).\n */\n onReconnect?: (options: { attempt: number; cause: unknown }) => void;\n}\n\n/**\n * Exponential backoff with jitter for WebSocket reconnect. Mirrors\n * {@link streamWithRetry} in `utils/stream.ts` (capped at 5s + 1s jitter).\n */\nexport function webSocketReconnectDelayMs(attempt: number): number {\n const baseDelay = Math.min(1000 * 2 ** (attempt - 1), 5000);\n const jitter = Math.random() * 1000;\n return baseDelay + jitter;\n}\n\n/**\n * Transport adapter that speaks the thread-centric protocol over a\n * bidirectional WebSocket. Bound to a specific `threadId` — the socket\n * connects to `ws://.../threads/:thread_id/stream/events`.\n *\n * On unexpected disconnect the adapter reconnects with exponential\n * backoff (see {@link ProtocolWebSocketTransportOptions.maxReconnectAttempts}).\n * The server replays buffered events on the new socket; the SDK\n * deduplicates by `event_id`. {@link ProtocolWebSocketTransportOptions.onReconnected}\n * runs after each successful reconnect so `ThreadStream` can re-issue\n * `subscription.subscribe` commands.\n */\nexport class ProtocolWebSocketTransportAdapter implements TransportAdapter {\n readonly threadId: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly apiUrl: string;\n\n private readonly defaultHeaders?: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly webSocketFactory: (url: string) => WebSocket;\n\n private readonly streamUrl: string;\n\n private readonly maxReconnectAttempts: number;\n\n private readonly onReconnect?: ProtocolWebSocketTransportOptions[\"onReconnect\"];\n\n private readonly reconnectDelayMs: (attempt: number) => number;\n\n private onReconnected?: () => void | Promise<void>;\n\n private readonly pending = new Map<number, PendingResponse>();\n\n private socket: WebSocket | null = null;\n\n private closed = false;\n\n private intentionalClose = false;\n\n private reconnectInFlight: Promise<void> | null = null;\n\n constructor(options: ProtocolWebSocketTransportOptions) {\n this.apiUrl = options.apiUrl;\n this.threadId = options.threadId;\n this.defaultHeaders = options.defaultHeaders;\n this.onRequest = options.onRequest;\n this.webSocketFactory =\n options.webSocketFactory ?? ((url) => new WebSocket(url));\n this.streamUrl =\n options.paths?.stream ?? `/threads/${this.threadId}/stream/events`;\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;\n this.onReconnect = options.onReconnect;\n this.onReconnected = options.onReconnected;\n this.reconnectDelayMs =\n options.reconnectDelayMs ?? webSocketReconnectDelayMs;\n }\n\n /**\n * Register a callback invoked after each successful reconnect. Used\n * by {@link ThreadStream} to re-send active `subscription.subscribe`\n * commands.\n */\n setOnReconnected(handler: () => void | Promise<void>): void {\n this.onReconnected = handler;\n }\n\n async open(): Promise<void> {\n if (this.closed) {\n throw new Error(\"Protocol WebSocket transport is closed.\");\n }\n if (this.socket?.readyState === WEB_SOCKET_OPEN) {\n return;\n }\n if (this.socket != null) {\n this.#detachSocket(this.socket);\n this.socket = null;\n }\n\n this.assertBrowserSafeTransportConfig();\n\n const wsUrl = toWebSocketUrl(\n toAbsoluteUrl(this.apiUrl, this.streamUrl).toString()\n );\n const socket = this.webSocketFactory(wsUrl);\n this.socket = socket;\n this.intentionalClose = false;\n\n this.#attachSocket(socket);\n\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(\"Failed to open protocol WebSocket.\"));\n };\n const cleanup = () => {\n socket.removeEventListener(\"open\", onOpen);\n socket.removeEventListener(\"error\", onError);\n };\n socket.addEventListener(\"open\", onOpen, { once: true });\n socket.addEventListener(\"error\", onError, { once: true });\n });\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n return await this.sendCommand(command);\n }\n\n events(): AsyncIterable<Message> {\n const queue = this.queue;\n return {\n [Symbol.asyncIterator]: () => ({\n next: async () => await queue.shift(),\n return: async () => {\n queue.close();\n return { done: true, value: undefined };\n },\n }),\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.intentionalClose = true;\n\n for (const { reject } of this.pending.values()) {\n reject(new Error(\"Protocol WebSocket connection closed.\"));\n }\n this.pending.clear();\n this.queue.close();\n\n const socket = this.socket;\n this.socket = null;\n if (!socket) {\n return;\n }\n\n this.#detachSocket(socket);\n\n await new Promise<void>((resolve) => {\n if (socket.readyState === WEB_SOCKET_CLOSED) {\n resolve();\n return;\n }\n\n const onClose = () => {\n socket.removeEventListener(\"close\", onClose);\n resolve();\n };\n\n socket.addEventListener(\"close\", onClose, { once: true });\n if (\n socket.readyState === WEB_SOCKET_OPEN ||\n socket.readyState === WEB_SOCKET_CONNECTING\n ) {\n socket.close();\n } else {\n resolve();\n }\n });\n }\n\n private assertBrowserSafeTransportConfig(): void {\n if (hasHeaders(this.defaultHeaders) || this.onRequest != null) {\n throw new Error(\n \"Browser WebSocket protocol transport does not support defaultHeaders or onRequest hooks. Supply a custom protocolWebSocketFactory if you need custom WebSocket setup.\"\n );\n }\n }\n\n private async sendCommand(\n command: Command\n ): Promise<CommandResponse | ErrorResponse> {\n // Wait for an in-flight reconnect only when the socket is not usable.\n // After `open()` succeeds, `#runReconnectLoop` may still be awaiting\n // `onReconnected` (e.g. ThreadStream resubscribe). Those callbacks call\n // `sendCommand` and must not await the same `reconnectInFlight` promise.\n let socket = this.socket;\n if (\n this.reconnectInFlight != null &&\n (socket == null || socket.readyState !== WEB_SOCKET_OPEN)\n ) {\n await this.reconnectInFlight.catch(() => undefined);\n socket = this.socket;\n }\n\n if (socket == null || socket.readyState !== WEB_SOCKET_OPEN) {\n throw new Error(\"Protocol WebSocket is not open.\");\n }\n\n return await new Promise<CommandResponse | ErrorResponse>(\n (resolve, reject) => {\n this.pending.set(command.id, { resolve, reject });\n\n try {\n socket.send(JSON.stringify(command));\n } catch (error) {\n this.pending.delete(command.id);\n reject(toError(error));\n }\n }\n );\n }\n\n #attachSocket(socket: WebSocket): void {\n socket.addEventListener(\"message\", this.handleMessage);\n socket.addEventListener(\"close\", this.handleClose);\n socket.addEventListener(\"error\", this.handleSocketError);\n }\n\n #detachSocket(socket: WebSocket): void {\n socket.removeEventListener(\"message\", this.handleMessage);\n socket.removeEventListener(\"close\", this.handleClose);\n socket.removeEventListener(\"error\", this.handleSocketError);\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n let payload: unknown;\n try {\n payload = JSON.parse(String(event.data));\n } catch {\n return;\n }\n\n if (\n isRecord(payload) &&\n typeof payload.id === \"number\" &&\n (payload.type === \"success\" || payload.type === \"error\")\n ) {\n const pending = this.pending.get(payload.id);\n if (pending) {\n this.pending.delete(payload.id);\n pending.resolve(payload as CommandResponse | ErrorResponse);\n }\n return;\n }\n\n if (isRecord(payload) && payload.type === \"event\") {\n this.queue.push(payload as Message);\n }\n };\n\n private readonly handleClose = (): void => {\n const socket = this.socket;\n if (socket != null) {\n this.#detachSocket(socket);\n }\n this.socket = null;\n\n if (this.intentionalClose || this.closed) {\n this.queue.close();\n return;\n }\n\n this.#handleUnexpectedDisconnect(\n new Error(\"Protocol WebSocket closed unexpectedly.\")\n );\n };\n\n private readonly handleSocketError = (): void => {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n this.#handleUnexpectedDisconnect(\n new Error(\"Protocol WebSocket encountered an error.\")\n );\n };\n\n #handleUnexpectedDisconnect(cause: unknown): void {\n const error = toError(cause);\n for (const { reject } of this.pending.values()) {\n reject(error);\n }\n this.pending.clear();\n\n if (this.maxReconnectAttempts <= 0) {\n this.queue.close(error);\n return;\n }\n\n this.#scheduleReconnect(cause);\n }\n\n #scheduleReconnect(cause: unknown): void {\n if (this.closed || this.intentionalClose) {\n return;\n }\n if (this.reconnectInFlight != null) {\n return;\n }\n\n this.reconnectInFlight = this.#runReconnectLoop(cause).finally(() => {\n this.reconnectInFlight = null;\n });\n }\n\n async #runReconnectLoop(initialCause: unknown): Promise<void> {\n let lastError: unknown = initialCause;\n\n for (let attempt = 1; attempt <= this.maxReconnectAttempts; attempt += 1) {\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n this.onReconnect?.({ attempt, cause: lastError });\n\n const delay = this.reconnectDelayMs(attempt);\n if (delay > 0) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n\n if (this.closed || this.intentionalClose) {\n return;\n }\n\n try {\n await this.open();\n if (this.onReconnected) {\n await this.onReconnected();\n }\n return;\n } catch (error) {\n lastError = error;\n }\n }\n\n this.queue.close(\n new MaxWebSocketReconnectAttemptsError(\n this.maxReconnectAttempts,\n lastError\n )\n );\n }\n}\n"],"mappings":";;;;AAwBA,MAAM,wBAAwB;AAC9B,MAAM,kBAAkB;AACxB,MAAM,oBAAoB;;;;;AAuB1B,SAAgB,0BAA0B,SAAyB;AAGjE,QAFkB,KAAK,IAAI,MAAO,MAAM,UAAU,IAAI,IAAK,GAC5C,KAAK,QAAQ,GAAG;;;;;;;;;;;;;;AAgBjC,IAAa,oCAAb,MAA2E;CACzE;CAEA,QAAyB,IAAI,YAAqB;CAElD;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA,0BAA2B,IAAI,KAA8B;CAE7D,SAAmC;CAEnC,SAAiB;CAEjB,mBAA2B;CAE3B,oBAAkD;CAElD,YAAY,SAA4C;AACtD,OAAK,SAAS,QAAQ;AACtB,OAAK,WAAW,QAAQ;AACxB,OAAK,iBAAiB,QAAQ;AAC9B,OAAK,YAAY,QAAQ;AACzB,OAAK,mBACH,QAAQ,sBAAsB,QAAQ,IAAI,UAAU,IAAI;AAC1D,OAAK,YACH,QAAQ,OAAO,UAAU,YAAY,KAAK,SAAS;AACrD,OAAK,uBAAuB,QAAQ,wBAAwB;AAC5D,OAAK,cAAc,QAAQ;AAC3B,OAAK,gBAAgB,QAAQ;AAC7B,OAAK,mBACH,QAAQ,oBAAoB;;;;;;;CAQhC,iBAAiB,SAA2C;AAC1D,OAAK,gBAAgB;;CAGvB,MAAM,OAAsB;AAC1B,MAAI,KAAK,OACP,OAAM,IAAI,MAAM,0CAA0C;AAE5D,MAAI,KAAK,QAAQ,eAAe,gBAC9B;AAEF,MAAI,KAAK,UAAU,MAAM;AACvB,SAAA,aAAmB,KAAK,OAAO;AAC/B,QAAK,SAAS;;AAGhB,OAAK,kCAAkC;EAEvC,MAAM,QAAQ,eACZ,cAAc,KAAK,QAAQ,KAAK,UAAU,CAAC,UAAU,CACtD;EACD,MAAM,SAAS,KAAK,iBAAiB,MAAM;AAC3C,OAAK,SAAS;AACd,OAAK,mBAAmB;AAExB,QAAA,aAAmB,OAAO;AAE1B,QAAM,IAAI,SAAe,SAAS,WAAW;GAC3C,MAAM,eAAe;AACnB,aAAS;AACT,aAAS;;GAEX,MAAM,gBAAgB;AACpB,aAAS;AACT,2BAAO,IAAI,MAAM,qCAAqC,CAAC;;GAEzD,MAAM,gBAAgB;AACpB,WAAO,oBAAoB,QAAQ,OAAO;AAC1C,WAAO,oBAAoB,SAAS,QAAQ;;AAE9C,UAAO,iBAAiB,QAAQ,QAAQ,EAAE,MAAM,MAAM,CAAC;AACvD,UAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;IACzD;;CAGJ,MAAM,KACJ,SACiD;AACjD,SAAO,MAAM,KAAK,YAAY,QAAQ;;CAGxC,SAAiC;EAC/B,MAAM,QAAQ,KAAK;AACnB,SAAO,GACJ,OAAO,uBAAuB;GAC7B,MAAM,YAAY,MAAM,MAAM,OAAO;GACrC,QAAQ,YAAY;AAClB,UAAM,OAAO;AACb,WAAO;KAAE,MAAM;KAAM,OAAO,KAAA;KAAW;;GAE1C,GACF;;CAGH,MAAM,QAAuB;AAC3B,MAAI,KAAK,OACP;AAGF,OAAK,SAAS;AACd,OAAK,mBAAmB;AAExB,OAAK,MAAM,EAAE,YAAY,KAAK,QAAQ,QAAQ,CAC5C,wBAAO,IAAI,MAAM,wCAAwC,CAAC;AAE5D,OAAK,QAAQ,OAAO;AACpB,OAAK,MAAM,OAAO;EAElB,MAAM,SAAS,KAAK;AACpB,OAAK,SAAS;AACd,MAAI,CAAC,OACH;AAGF,QAAA,aAAmB,OAAO;AAE1B,QAAM,IAAI,SAAe,YAAY;AACnC,OAAI,OAAO,eAAe,mBAAmB;AAC3C,aAAS;AACT;;GAGF,MAAM,gBAAgB;AACpB,WAAO,oBAAoB,SAAS,QAAQ;AAC5C,aAAS;;AAGX,UAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AACzD,OACE,OAAO,eAAe,mBACtB,OAAO,eAAe,sBAEtB,QAAO,OAAO;OAEd,UAAS;IAEX;;CAGJ,mCAAiD;AAC/C,MAAI,WAAW,KAAK,eAAe,IAAI,KAAK,aAAa,KACvD,OAAM,IAAI,MACR,wKACD;;CAIL,MAAc,YACZ,SAC0C;EAK1C,IAAI,SAAS,KAAK;AAClB,MACE,KAAK,qBAAqB,SACzB,UAAU,QAAQ,OAAO,eAAe,kBACzC;AACA,SAAM,KAAK,kBAAkB,YAAY,KAAA,EAAU;AACnD,YAAS,KAAK;;AAGhB,MAAI,UAAU,QAAQ,OAAO,eAAe,gBAC1C,OAAM,IAAI,MAAM,kCAAkC;AAGpD,SAAO,MAAM,IAAI,SACd,SAAS,WAAW;AACnB,QAAK,QAAQ,IAAI,QAAQ,IAAI;IAAE;IAAS;IAAQ,CAAC;AAEjD,OAAI;AACF,WAAO,KAAK,KAAK,UAAU,QAAQ,CAAC;YAC7B,OAAO;AACd,SAAK,QAAQ,OAAO,QAAQ,GAAG;AAC/B,WAAO,QAAQ,MAAM,CAAC;;IAG3B;;CAGH,cAAc,QAAyB;AACrC,SAAO,iBAAiB,WAAW,KAAK,cAAc;AACtD,SAAO,iBAAiB,SAAS,KAAK,YAAY;AAClD,SAAO,iBAAiB,SAAS,KAAK,kBAAkB;;CAG1D,cAAc,QAAyB;AACrC,SAAO,oBAAoB,WAAW,KAAK,cAAc;AACzD,SAAO,oBAAoB,SAAS,KAAK,YAAY;AACrD,SAAO,oBAAoB,SAAS,KAAK,kBAAkB;;CAG7D,iBAAkC,UAA8B;EAC9D,IAAI;AACJ,MAAI;AACF,aAAU,KAAK,MAAM,OAAO,MAAM,KAAK,CAAC;UAClC;AACN;;AAGF,MACE,SAAS,QAAQ,IACjB,OAAO,QAAQ,OAAO,aACrB,QAAQ,SAAS,aAAa,QAAQ,SAAS,UAChD;GACA,MAAM,UAAU,KAAK,QAAQ,IAAI,QAAQ,GAAG;AAC5C,OAAI,SAAS;AACX,SAAK,QAAQ,OAAO,QAAQ,GAAG;AAC/B,YAAQ,QAAQ,QAA2C;;AAE7D;;AAGF,MAAI,SAAS,QAAQ,IAAI,QAAQ,SAAS,QACxC,MAAK,MAAM,KAAK,QAAmB;;CAIvC,oBAA2C;EACzC,MAAM,SAAS,KAAK;AACpB,MAAI,UAAU,KACZ,OAAA,aAAmB,OAAO;AAE5B,OAAK,SAAS;AAEd,MAAI,KAAK,oBAAoB,KAAK,QAAQ;AACxC,QAAK,MAAM,OAAO;AAClB;;AAGF,QAAA,2CACE,IAAI,MAAM,0CAA0C,CACrD;;CAGH,0BAAiD;AAC/C,MAAI,KAAK,UAAU,KAAK,iBACtB;AAGF,QAAA,2CACE,IAAI,MAAM,2CAA2C,CACtD;;CAGH,4BAA4B,OAAsB;EAChD,MAAM,QAAQ,QAAQ,MAAM;AAC5B,OAAK,MAAM,EAAE,YAAY,KAAK,QAAQ,QAAQ,CAC5C,QAAO,MAAM;AAEf,OAAK,QAAQ,OAAO;AAEpB,MAAI,KAAK,wBAAwB,GAAG;AAClC,QAAK,MAAM,MAAM,MAAM;AACvB;;AAGF,QAAA,kBAAwB,MAAM;;CAGhC,mBAAmB,OAAsB;AACvC,MAAI,KAAK,UAAU,KAAK,iBACtB;AAEF,MAAI,KAAK,qBAAqB,KAC5B;AAGF,OAAK,oBAAoB,MAAA,iBAAuB,MAAM,CAAC,cAAc;AACnE,QAAK,oBAAoB;IACzB;;CAGJ,OAAA,iBAAwB,cAAsC;EAC5D,IAAI,YAAqB;AAEzB,OAAK,IAAI,UAAU,GAAG,WAAW,KAAK,sBAAsB,WAAW,GAAG;AACxE,OAAI,KAAK,UAAU,KAAK,iBACtB;AAGF,QAAK,cAAc;IAAE;IAAS,OAAO;IAAW,CAAC;GAEjD,MAAM,QAAQ,KAAK,iBAAiB,QAAQ;AAC5C,OAAI,QAAQ,EACV,OAAM,IAAI,SAAe,YAAY;AACnC,eAAW,SAAS,MAAM;KAC1B;AAGJ,OAAI,KAAK,UAAU,KAAK,iBACtB;AAGF,OAAI;AACF,UAAM,KAAK,MAAM;AACjB,QAAI,KAAK,cACP,OAAM,KAAK,eAAe;AAE5B;YACO,OAAO;AACd,gBAAY;;;AAIhB,OAAK,MAAM,MACT,IAAI,mCACF,KAAK,sBACL,UACD,CACF"}
@@ -86,16 +86,22 @@ interface TransportAdapter {
86
86
  */
87
87
  interface AgentServerAdapter extends TransportAdapter {
88
88
  /**
89
- * Fetch the latest checkpointed state for the bound thread. When
90
- * the adapter doesn't expose state (e.g. a purely event-replay
91
- * backend), leave this undefined the framework will skip
92
- * hydration.
89
+ * Fetch the latest checkpointed state for the bound thread via
90
+ * `GET /threads/:threadId/state` (or an adapter-specific override).
91
+ * When omitted, {@link StreamController.hydrate} falls back to
92
+ * `client.threads.getState()`.
93
93
  */
94
94
  getState?<StateType = unknown>(): Promise<{
95
95
  values: StateType;
96
+ next?: unknown;
97
+ tasks?: unknown;
98
+ metadata?: unknown;
96
99
  checkpoint?: {
97
100
  checkpoint_id?: string;
98
101
  } | null;
102
+ parent_checkpoint?: {
103
+ checkpoint_id?: string;
104
+ } | null;
99
105
  } | null>;
100
106
  /**
101
107
  * Fetch a slice of checkpoint history for the bound thread. Used
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.cts","names":[],"sources":["../../../src/client/stream/transport.ts"],"mappings":";;;;;AAgBA;;;;;;UAAiB,iBAAA;EACf,MAAA,EAAQ,aAAA,CAAc,OAAA;EACtB,KAAA,EAAO,OAAA;EACP,KAAA;AAAA;;;;;;AAUF;;UAAiB,gBAAA;EASP;;;EAAA,SALC,QAAA;EAWe;;;;EANxB,IAAA,IAAQ,OAAA;EAmCC;;;;;EA7BT,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAAlD;;;;EAKA,MAAA,IAAU,aAAA,CAAc,OAAA;EAL0B;;;;;;;;;;;;AAiDpD;;;;;;;EAxBE,eAAA,EAAiB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EAwBD;;;EApB1C,KAAA,IAAS,OAAA;AAAA;;;;;;;;;;;;;;;;;;UAoBM,kBAAA,SAA2B,gBAAA;;;;;;;EAO1C,QAAA,0BAAkC,OAAA;IAChC,MAAA,EAAQ,SAAA;IACR,UAAA;MAAe,aAAA;IAAA;EAAA;;;;;;EAOjB,UAAA,uBAAiC,OAAA;IAC/B,KAAA;EAAA,IACE,OAAA,CACF,KAAA;IACE,MAAA,EAAQ,SAAA;IACR,UAAA;MAAe,aAAA;IAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"transport.d.cts","names":[],"sources":["../../../src/client/stream/transport.ts"],"mappings":";;;;;AAgBA;;;;;;UAAiB,iBAAA;EACf,MAAA,EAAQ,aAAA,CAAc,OAAA;EACtB,KAAA,EAAO,OAAA;EACP,KAAA;AAAA;;;;;;AAUF;;UAAiB,gBAAA;EASP;;;EAAA,SALC,QAAA;EAWe;;;;EANxB,IAAA,IAAQ,OAAA;EAmCC;;;;;EA7BT,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAAlD;;;;EAKA,MAAA,IAAU,aAAA,CAAc,OAAA;EAL0B;;;;;;;;;;;;AAiDpD;;;;;;;EAxBE,eAAA,EAAiB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EAwBD;;;EApB1C,KAAA,IAAS,OAAA;AAAA;;;;;;;;;;;;;;;;;;UAoBM,kBAAA,SAA2B,gBAAA;EAwB9B;;;;;;EAjBZ,QAAA,0BAAkC,OAAA;IAChC,MAAA,EAAQ,SAAA;IACR,IAAA;IACA,KAAA;IACA,QAAA;IACA,UAAA;MAAe,aAAA;IAAA;IACf,iBAAA;MAAsB,aAAA;IAAA;EAAA;;;;;;EAOxB,UAAA,uBAAiC,OAAA;IAC/B,KAAA;EAAA,IACE,OAAA,CACF,KAAA;IACE,MAAA,EAAQ,SAAA;IACR,UAAA;MAAe,aAAA;IAAA;EAAA;AAAA"}
@@ -86,16 +86,22 @@ interface TransportAdapter {
86
86
  */
87
87
  interface AgentServerAdapter extends TransportAdapter {
88
88
  /**
89
- * Fetch the latest checkpointed state for the bound thread. When
90
- * the adapter doesn't expose state (e.g. a purely event-replay
91
- * backend), leave this undefined the framework will skip
92
- * hydration.
89
+ * Fetch the latest checkpointed state for the bound thread via
90
+ * `GET /threads/:threadId/state` (or an adapter-specific override).
91
+ * When omitted, {@link StreamController.hydrate} falls back to
92
+ * `client.threads.getState()`.
93
93
  */
94
94
  getState?<StateType = unknown>(): Promise<{
95
95
  values: StateType;
96
+ next?: unknown;
97
+ tasks?: unknown;
98
+ metadata?: unknown;
96
99
  checkpoint?: {
97
100
  checkpoint_id?: string;
98
101
  } | null;
102
+ parent_checkpoint?: {
103
+ checkpoint_id?: string;
104
+ } | null;
99
105
  } | null>;
100
106
  /**
101
107
  * Fetch a slice of checkpoint history for the bound thread. Used
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","names":[],"sources":["../../../src/client/stream/transport.ts"],"mappings":";;;;;AAgBA;;;;;;UAAiB,iBAAA;EACf,MAAA,EAAQ,aAAA,CAAc,OAAA;EACtB,KAAA,EAAO,OAAA;EACP,KAAA;AAAA;;;;;;AAUF;;UAAiB,gBAAA;EASP;;;EAAA,SALC,QAAA;EAWe;;;;EANxB,IAAA,IAAQ,OAAA;EAmCC;;;;;EA7BT,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAAlD;;;;EAKA,MAAA,IAAU,aAAA,CAAc,OAAA;EAL0B;;;;;;;;;;;;AAiDpD;;;;;;;EAxBE,eAAA,EAAiB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EAwBD;;;EApB1C,KAAA,IAAS,OAAA;AAAA;;;;;;;;;;;;;;;;;;UAoBM,kBAAA,SAA2B,gBAAA;;;;;;;EAO1C,QAAA,0BAAkC,OAAA;IAChC,MAAA,EAAQ,SAAA;IACR,UAAA;MAAe,aAAA;IAAA;EAAA;;;;;;EAOjB,UAAA,uBAAiC,OAAA;IAC/B,KAAA;EAAA,IACE,OAAA,CACF,KAAA;IACE,MAAA,EAAQ,SAAA;IACR,UAAA;MAAe,aAAA;IAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"transport.d.ts","names":[],"sources":["../../../src/client/stream/transport.ts"],"mappings":";;;;;AAgBA;;;;;;UAAiB,iBAAA;EACf,MAAA,EAAQ,aAAA,CAAc,OAAA;EACtB,KAAA,EAAO,OAAA;EACP,KAAA;AAAA;;;;;;AAUF;;UAAiB,gBAAA;EASP;;;EAAA,SALC,QAAA;EAWe;;;;EANxB,IAAA,IAAQ,OAAA;EAmCC;;;;;EA7BT,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,eAAA,GAAkB,aAAA;EAAlD;;;;EAKA,MAAA,IAAU,aAAA,CAAc,OAAA;EAL0B;;;;;;;;;;;;AAiDpD;;;;;;;EAxBE,eAAA,EAAiB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EAwBD;;;EApB1C,KAAA,IAAS,OAAA;AAAA;;;;;;;;;;;;;;;;;;UAoBM,kBAAA,SAA2B,gBAAA;EAwB9B;;;;;;EAjBZ,QAAA,0BAAkC,OAAA;IAChC,MAAA,EAAQ,SAAA;IACR,IAAA;IACA,KAAA;IACA,QAAA;IACA,UAAA;MAAe,aAAA;IAAA;IACf,iBAAA;MAAsB,aAAA;IAAA;EAAA;;;;;;EAOxB,UAAA,uBAAiC,OAAA;IAC/B,KAAA;EAAA,IACE,OAAA,CACF,KAAA;IACE,MAAA,EAAQ,SAAA;IACR,UAAA;MAAe,aAAA;IAAA;EAAA;AAAA"}
@@ -96,6 +96,23 @@ interface ThreadStreamOptions {
96
96
  * for the SSE transport and for custom {@link AgentServerAdapter}s.
97
97
  */
98
98
  webSocketFactory?: (url: string) => WebSocket;
99
+ /**
100
+ * Built-in `"sse"` / `"websocket"` transports only: max reconnect
101
+ * attempts after an unexpected disconnect. Defaults to 5.
102
+ */
103
+ maxReconnectAttempts?: number;
104
+ /**
105
+ * Built-in transports only: delay before each reconnect attempt.
106
+ * Defaults to exponential backoff with jitter.
107
+ */
108
+ reconnectDelayMs?: (attempt: number) => number;
109
+ /**
110
+ * Built-in transports only: invoked before each reconnect attempt.
111
+ */
112
+ onReconnect?: (options: {
113
+ attempt: number;
114
+ cause: unknown;
115
+ }) => void;
99
116
  }
100
117
  interface SessionOrderingState {
101
118
  lastSeenSeq?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":";;;;;UAoBiB,sBAAA,SAA+B,cAAA;EAC9C,QAAA;EACA,iBAAA;AAAA;AAAA,KAGU,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KAExB,oBAAA;EACV,MAAA;EACA,OAAA;EACA,QAAA;EACA,KAAA;EACA,MAAA;EACA,SAAA;EACA,KAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,KAGU,eAAA,kBAAiC,OAAA,IAC3C,QAAA,eAAuB,oBAAA,GACnB,OAAA,CAAQ,KAAA;EAAS,MAAA,EAAQ,oBAAA,CAAqB,QAAA;AAAA,KAC9C,QAAA,8BACE,OAAA,CAAQ,KAAA;EAAS,MAAA;AAAA;AAAA,KAGb,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;;;;;;KAQN,eAAA,kBAAiC,OAAA,IAC3C,QAAA,wCAAgD,eAAA,CAAgB,QAAA;AAAA,KAEtD,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;AApBlB;;;;;;;KA8BY,yBAAA;;;;;;;;;KAUA,qBAAA,GACR,yBAAA,GACA,kBAAA;;;;UAKa,mBAAA;EA7CM;;;;;EAmDrB,WAAA;EAjDuB;;;AAGzB;;;;;;;;;;EA4DE,SAAA,GAAY,qBAAA;EA3DI;;;AAQlB;EAwDE,iBAAA;EAxDyB;;;;;;;EAgEzB,KAAA,UAAe,KAAA;EAhE4B;;;;;;EAuE3C,gBAAA,IAAoB,GAAA,aAAgB,SAAA;AAAA;AAAA,UAGrB,oBAAA;EACf,WAAA;EACA,qBAAA;EACA,WAAA;AAAA;AAAA,UAGe,iBAAA,UACN,KAAA,UACD,aAAA,CAAc,MAAA;EAAA,SACb,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,mBAAA,SAA4B,aAAA,CAAc,gBAAA;EAAA,SAChD,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,WAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,kBAAA,GAAqB,OAAA;EACrC,MAAA,CAAO,MAAA,EAAQ,iBAAA,GAAoB,OAAA;AAAA;AAAA,UAGpB,WAAA;EACf,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,cAAA;EACrC,eAAA,CACE,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;EACX,IAAA,CAAK,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,eAAA;AAAA;;;;UAMxB,aAAA;EACf,GAAA;IAxC6C;;;;;IA8C3C,KAAA,CACE,MAAA,EAAQ,IAAA,CAAK,sBAAA,oBACZ,OAAA,CAAQ,SAAA;EAAA;EAEb,KAAA;IACE,OAAA,CAAQ,MAAA;MAAW,MAAA;IAAA,IAAoB,OAAA,CAAQ,WAAA;EAAA;EAEjD,KAAA,EAAO,WAAA;EACP,KAAA,EAAO,WAAA;AAAA;;;;;;;AA7CT;;;;UA0DiB,gBAAA;EACf,WAAA;EACA,OAAA,EAAS,QAAA;EA1DD;EA4DR,SAAA;AAAA;;;;;;;;;;;;AAtDF;;;;;;;UA2EiB,eAAA,sBACP,aAAA,CAAc,CAAA,GAAI,WAAA,CAAY,CAAA;;;;;;;;;;AAtExC;;;;KAqFY,eAAA,MACV,CAAA,SAAU,WAAA,YAAuB,CAAA,GAAI,CAAA,SAAU,aAAA,YAAyB,CAAA,GAAI,CAAA;;;;;;;;;;;;;;;AAjF9E;KAkGY,gBAAA,qBACU,MAAA,oBAA0B,MAAA,4CAEzB,WAAA,GAAc,eAAA,CACjC,eAAA,CAAgB,WAAA,CAAY,CAAA;EAAA,UAGpB,IAAA,WAAe,eAAA;AAAA"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":"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"}
@@ -96,6 +96,23 @@ interface ThreadStreamOptions {
96
96
  * for the SSE transport and for custom {@link AgentServerAdapter}s.
97
97
  */
98
98
  webSocketFactory?: (url: string) => WebSocket;
99
+ /**
100
+ * Built-in `"sse"` / `"websocket"` transports only: max reconnect
101
+ * attempts after an unexpected disconnect. Defaults to 5.
102
+ */
103
+ maxReconnectAttempts?: number;
104
+ /**
105
+ * Built-in transports only: delay before each reconnect attempt.
106
+ * Defaults to exponential backoff with jitter.
107
+ */
108
+ reconnectDelayMs?: (attempt: number) => number;
109
+ /**
110
+ * Built-in transports only: invoked before each reconnect attempt.
111
+ */
112
+ onReconnect?: (options: {
113
+ attempt: number;
114
+ cause: unknown;
115
+ }) => void;
99
116
  }
100
117
  interface SessionOrderingState {
101
118
  lastSeenSeq?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":";;;;;UAoBiB,sBAAA,SAA+B,cAAA;EAC9C,QAAA;EACA,iBAAA;AAAA;AAAA,KAGU,gBAAA,GAAmB,IAAA,CAAK,eAAA;AAAA,KAExB,oBAAA;EACV,MAAA;EACA,OAAA;EACA,QAAA;EACA,KAAA;EACA,MAAA;EACA,SAAA;EACA,KAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA;AAAA;AAAA,KAGU,eAAA,kBAAiC,OAAA,IAC3C,QAAA,eAAuB,oBAAA,GACnB,OAAA,CAAQ,KAAA;EAAS,MAAA,EAAQ,oBAAA,CAAqB,QAAA;AAAA,KAC9C,QAAA,8BACE,OAAA,CAAQ,KAAA;EAAS,MAAA;AAAA;AAAA,KAGb,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;;;;;;KAQN,eAAA,kBAAiC,OAAA,IAC3C,QAAA,wCAAgD,eAAA,CAAgB,QAAA;AAAA,KAEtD,gBAAA,4BAA4C,OAAA,MACtD,eAAA,CAAgB,SAAA;;AApBlB;;;;;;;KA8BY,yBAAA;;;;;;;;;KAUA,qBAAA,GACR,yBAAA,GACA,kBAAA;;;;UAKa,mBAAA;EA7CM;;;;;EAmDrB,WAAA;EAjDuB;;;AAGzB;;;;;;;;;;EA4DE,SAAA,GAAY,qBAAA;EA3DI;;;AAQlB;EAwDE,iBAAA;EAxDyB;;;;;;;EAgEzB,KAAA,UAAe,KAAA;EAhE4B;;;;;;EAuE3C,gBAAA,IAAoB,GAAA,aAAgB,SAAA;AAAA;AAAA,UAGrB,oBAAA;EACf,WAAA;EACA,qBAAA;EACA,WAAA;AAAA;AAAA,UAGe,iBAAA,UACN,KAAA,UACD,aAAA,CAAc,MAAA;EAAA,SACb,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,mBAAA,SAA4B,aAAA,CAAc,gBAAA;EAAA,SAChD,cAAA;EAAA,SACA,MAAA,EAAQ,eAAA;EACjB,WAAA,IAAe,OAAA;AAAA;AAAA,UAGA,WAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,kBAAA,GAAqB,OAAA;EACrC,MAAA,CAAO,MAAA,EAAQ,iBAAA,GAAoB,OAAA;AAAA;AAAA,UAGpB,WAAA;EACf,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,cAAA;EACrC,eAAA,CACE,MAAA,EAAQ,qBAAA,GACP,OAAA,CAAQ,qBAAA;EACX,IAAA,CAAK,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,eAAA;AAAA;;;;UAMxB,aAAA;EACf,GAAA;IAxC6C;;;;;IA8C3C,KAAA,CACE,MAAA,EAAQ,IAAA,CAAK,sBAAA,oBACZ,OAAA,CAAQ,SAAA;EAAA;EAEb,KAAA;IACE,OAAA,CAAQ,MAAA;MAAW,MAAA;IAAA,IAAoB,OAAA,CAAQ,WAAA;EAAA;EAEjD,KAAA,EAAO,WAAA;EACP,KAAA,EAAO,WAAA;AAAA;;;;;;;AA7CT;;;;UA0DiB,gBAAA;EACf,WAAA;EACA,OAAA,EAAS,QAAA;EA1DD;EA4DR,SAAA;AAAA;;;;;;;;;;;;AAtDF;;;;;;;UA2EiB,eAAA,sBACP,aAAA,CAAc,CAAA,GAAI,WAAA,CAAY,CAAA;;;;;;;;;;AAtExC;;;;KAqFY,eAAA,MACV,CAAA,SAAU,WAAA,YAAuB,CAAA,GAAI,CAAA,SAAU,aAAA,YAAyB,CAAA,GAAI,CAAA;;;;;;;;;;;;;;;AAjF9E;KAkGY,gBAAA,qBACU,MAAA,oBAA0B,MAAA,4CAEzB,WAAA,GAAc,eAAA,CACjC,eAAA,CAAgB,WAAA,CAAY,CAAA;EAAA,UAGpB,IAAA,WAAe,eAAA;AAAA"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/client/stream/types.ts"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  require("../../_virtual/_rolldown/runtime.cjs");
2
2
  const require_base = require("../base.cjs");
3
3
  const require_index = require("../stream/index.cjs");
4
- const require_http = require("../stream/transport/http.cjs");
5
4
  const require_websocket = require("../stream/transport/websocket.cjs");
5
+ const require_http = require("../stream/transport/http.cjs");
6
6
  let uuid = require("uuid");
7
7
  //#region src/client/threads/index.ts
8
8
  var ThreadsClient = class extends require_base.BaseClient {
@@ -249,22 +249,38 @@ var ThreadsClient = class extends require_base.BaseClient {
249
249
  threadId: (0, uuid.v7)(),
250
250
  options: threadIdOrOptions
251
251
  };
252
+ const userFetch = options.fetch;
253
+ const protocolFetch = userFetch ?? this.asyncCaller.fetch.bind(this.asyncCaller);
252
254
  let transport;
253
255
  if (options.transport != null && typeof options.transport !== "string") transport = options.transport;
254
- else transport = (options.transport ?? (this.streamProtocol === "v2-websocket" ? "websocket" : "sse")) === "websocket" ? new require_websocket.ProtocolWebSocketTransportAdapter({
255
- apiUrl: this.apiUrl,
256
- threadId,
257
- defaultHeaders: this.defaultHeaders,
258
- onRequest: this.onRequest,
259
- webSocketFactory: options.webSocketFactory
260
- }) : new require_http.ProtocolSseTransportAdapter({
261
- apiUrl: this.apiUrl,
262
- threadId,
263
- defaultHeaders: this.defaultHeaders,
264
- onRequest: this.onRequest,
265
- fetch: options.fetch
256
+ else {
257
+ const transportKind = options.transport ?? (this.streamProtocol === "v2-websocket" ? "websocket" : "sse");
258
+ const maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
259
+ /**
260
+ * Common options for both transports.
261
+ */
262
+ const commonOpts = {
263
+ apiUrl: this.apiUrl,
264
+ threadId,
265
+ defaultHeaders: this.defaultHeaders,
266
+ onRequest: this.onRequest,
267
+ maxReconnectAttempts,
268
+ reconnectDelayMs: options.reconnectDelayMs,
269
+ onReconnect: options.onReconnect
270
+ };
271
+ transport = transportKind === "websocket" ? new require_websocket.ProtocolWebSocketTransportAdapter({
272
+ ...commonOpts,
273
+ webSocketFactory: options.webSocketFactory
274
+ }) : new require_http.ProtocolSseTransportAdapter({
275
+ ...commonOpts,
276
+ fetch: userFetch,
277
+ asyncCaller: userFetch ? void 0 : this.asyncCaller
278
+ });
279
+ }
280
+ return new require_index.ThreadStream(transport, {
281
+ ...options,
282
+ fetch: protocolFetch
266
283
  });
267
- return new require_index.ThreadStream(transport, options);
268
284
  }
269
285
  };
270
286
  //#endregion