@langchain/langgraph-sdk 1.12.4 → 1.12.5

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 (67) hide show
  1. package/dist/client/stream/handles/subagents.cjs +9 -2
  2. package/dist/client/stream/handles/subagents.cjs.map +1 -1
  3. package/dist/client/stream/handles/subagents.d.cts.map +1 -1
  4. package/dist/client/stream/handles/subagents.d.ts.map +1 -1
  5. package/dist/client/stream/handles/subagents.js +10 -3
  6. package/dist/client/stream/handles/subagents.js.map +1 -1
  7. package/dist/client/stream/handles/subgraphs.cjs +24 -2
  8. package/dist/client/stream/handles/subgraphs.cjs.map +1 -1
  9. package/dist/client/stream/handles/subgraphs.d.cts.map +1 -1
  10. package/dist/client/stream/handles/subgraphs.d.ts.map +1 -1
  11. package/dist/client/stream/handles/subgraphs.js +24 -3
  12. package/dist/client/stream/handles/subgraphs.js.map +1 -1
  13. package/dist/client/stream/index.cjs +17 -7
  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 +17 -7
  18. package/dist/client/stream/index.js.map +1 -1
  19. package/dist/client/stream/messages.cjs +43 -0
  20. package/dist/client/stream/messages.cjs.map +1 -1
  21. package/dist/client/stream/messages.d.cts +8 -0
  22. package/dist/client/stream/messages.d.cts.map +1 -1
  23. package/dist/client/stream/messages.d.ts +8 -0
  24. package/dist/client/stream/messages.d.ts.map +1 -1
  25. package/dist/client/stream/messages.js +43 -0
  26. package/dist/client/stream/messages.js.map +1 -1
  27. package/dist/client/stream/transport/http.cjs +1 -12
  28. package/dist/client/stream/transport/http.cjs.map +1 -1
  29. package/dist/client/stream/transport/http.d.cts.map +1 -1
  30. package/dist/client/stream/transport/http.d.ts.map +1 -1
  31. package/dist/client/stream/transport/http.js +1 -12
  32. package/dist/client/stream/transport/http.js.map +1 -1
  33. package/dist/client/stream/transport/utils.cjs +12 -0
  34. package/dist/client/stream/transport/utils.cjs.map +1 -1
  35. package/dist/client/stream/transport/utils.js +12 -1
  36. package/dist/client/stream/transport/utils.js.map +1 -1
  37. package/dist/client/stream/transport/websocket.cjs +33 -3
  38. package/dist/client/stream/transport/websocket.cjs.map +1 -1
  39. package/dist/client/stream/transport/websocket.d.cts +4 -1
  40. package/dist/client/stream/transport/websocket.d.cts.map +1 -1
  41. package/dist/client/stream/transport/websocket.d.ts +4 -1
  42. package/dist/client/stream/transport/websocket.d.ts.map +1 -1
  43. package/dist/client/stream/transport/websocket.js +34 -4
  44. package/dist/client/stream/transport/websocket.js.map +1 -1
  45. package/dist/stream/controller.cjs +94 -3
  46. package/dist/stream/controller.cjs.map +1 -1
  47. package/dist/stream/controller.d.cts.map +1 -1
  48. package/dist/stream/controller.d.ts.map +1 -1
  49. package/dist/stream/controller.js +94 -3
  50. package/dist/stream/controller.js.map +1 -1
  51. package/dist/stream/lifecycle-loading-tracker.cjs +5 -0
  52. package/dist/stream/lifecycle-loading-tracker.cjs.map +1 -1
  53. package/dist/stream/lifecycle-loading-tracker.js +5 -0
  54. package/dist/stream/lifecycle-loading-tracker.js.map +1 -1
  55. package/dist/stream/projections/messages.cjs +45 -12
  56. package/dist/stream/projections/messages.cjs.map +1 -1
  57. package/dist/stream/projections/messages.js +45 -12
  58. package/dist/stream/projections/messages.js.map +1 -1
  59. package/dist/stream/root-message-projection.cjs +71 -8
  60. package/dist/stream/root-message-projection.cjs.map +1 -1
  61. package/dist/stream/root-message-projection.js +71 -8
  62. package/dist/stream/root-message-projection.js.map +1 -1
  63. package/dist/stream/submit-coordinator.cjs +7 -2
  64. package/dist/stream/submit-coordinator.cjs.map +1 -1
  65. package/dist/stream/submit-coordinator.js +7 -2
  66. package/dist/stream/submit-coordinator.js.map +1 -1
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","names":["#events","#textPromise","#reasoningPromise","#resolveText","#resolveReasoning","#textChunks","#textWaiters","#textDone","#reasoningChunks","#reasoningWaiters","#reasoningDone","#usageIterator","#assembleMessage","#assembler","#activeStreaming"],"sources":["../../../src/client/stream/messages.ts"],"sourcesContent":["import {\n AIMessage,\n type ContentBlock as CoreContentBlock,\n type UsageMetadata,\n} from \"@langchain/core/messages\";\nimport type {\n ChatModelStreamEvent,\n ContentBlockDelta as CoreContentBlockDelta,\n} from \"@langchain/core/language_models/event\";\nimport type {\n ContentBlock,\n FinalizedContentBlock,\n MessageMetadata,\n MessagesEvent,\n UsageInfo,\n} from \"@langchain/protocol\";\n\nimport { MultiCursorBuffer } from \"./multi-cursor-buffer.js\";\n\ntype TextContentStream = AsyncIterable<string> &\n PromiseLike<string> & { full: AsyncIterable<string> };\n\ntype UsageMetadataStream = AsyncIterable<UsageMetadata> &\n PromiseLike<UsageMetadata | undefined>;\n\ntype ToolCallsStream = AsyncIterable<CoreContentBlock.Tools.ToolCall> &\n PromiseLike<Array<CoreContentBlock.Tools.ToolCall>> & {\n full: AsyncIterable<Array<CoreContentBlock.Tools.ToolCall>>;\n };\n\nfunction applyCoreContentDelta(\n target: CoreContentBlock,\n delta: CoreContentBlock\n): CoreContentBlock {\n if (target.type !== delta.type) {\n return structuredClone(delta);\n }\n\n switch (delta.type) {\n case \"text\":\n return {\n ...target,\n ...delta,\n text: `${\"text\" in target ? target.text : \"\"}${delta.text}`,\n } as CoreContentBlock;\n case \"reasoning\":\n return {\n ...target,\n ...delta,\n reasoning: `${\"reasoning\" in target ? target.reasoning : \"\"}${delta.reasoning}`,\n } as CoreContentBlock;\n case \"tool_call_chunk\":\n case \"server_tool_call_chunk\": {\n const merged = { ...target, ...delta } as Record<string, unknown>;\n if (delta.id == null && \"id\" in target && target.id != null) {\n merged.id = target.id;\n }\n if (delta.name == null && \"name\" in target && target.name != null) {\n merged.name = target.name;\n }\n merged.args = `${(\"args\" in target ? target.args : \"\") ?? \"\"}${delta.args ?? \"\"}`;\n return merged as unknown as CoreContentBlock;\n }\n default:\n return { ...target, ...delta } as CoreContentBlock;\n }\n}\n\nfunction coreContentBlockFromDelta(\n delta: CoreContentBlockDelta,\n current?: CoreContentBlock\n): CoreContentBlock {\n switch (delta.type) {\n case \"text-delta\":\n return { type: \"text\", text: delta.text } as CoreContentBlock;\n case \"reasoning-delta\":\n return {\n type: \"reasoning\",\n reasoning: delta.reasoning,\n } as CoreContentBlock;\n case \"data-delta\": {\n const merged = { ...(current ?? {}), data: delta.data } as Record<\n string,\n unknown\n >;\n if (delta.encoding) merged.encoding = delta.encoding;\n return merged as unknown as CoreContentBlock;\n }\n case \"block-delta\":\n return delta.fields as CoreContentBlock;\n }\n}\n\nfunction applyCoreEventDelta(\n current: CoreContentBlock | undefined,\n event: Extract<ChatModelStreamEvent, { event: \"content-block-delta\" }> & {\n content?: CoreContentBlock;\n }\n): CoreContentBlock {\n if (event.content) {\n return current\n ? applyCoreContentDelta(current, event.content)\n : event.content;\n }\n\n switch (event.delta.type) {\n case \"text-delta\":\n if (current?.type === \"text\") {\n return {\n ...current,\n text: `${\"text\" in current ? current.text : \"\"}${event.delta.text}`,\n } as CoreContentBlock;\n }\n return coreContentBlockFromDelta(event.delta, current);\n case \"reasoning-delta\":\n if (current?.type === \"reasoning\") {\n return {\n ...current,\n reasoning: `${\"reasoning\" in current ? current.reasoning : \"\"}${event.delta.reasoning}`,\n } as CoreContentBlock;\n }\n return coreContentBlockFromDelta(event.delta, current);\n case \"data-delta\": {\n const merged = { ...(current ?? {}) } as Record<string, unknown>;\n merged.data = `${(merged.data as string | undefined) ?? \"\"}${event.delta.data}`;\n if (event.delta.encoding) merged.encoding = event.delta.encoding;\n return merged as unknown as CoreContentBlock;\n }\n case \"block-delta\":\n return {\n ...(current ?? {}),\n ...event.delta.fields,\n } as CoreContentBlock;\n }\n}\n\nfunction normalizeUsage(\n usage: UsageInfo | Partial<UsageMetadata> | undefined\n): UsageMetadata | undefined {\n if (!usage) return undefined;\n return {\n ...usage,\n input_tokens: usage.input_tokens ?? 0,\n output_tokens: usage.output_tokens ?? 0,\n total_tokens: usage.total_tokens ?? 0,\n };\n}\n\nfunction readRunId(data: object): string | undefined {\n const record = data as Record<string, unknown>;\n return typeof record.run_id === \"string\" ? record.run_id : undefined;\n}\n\nfunction readUsage(data: object): UsageInfo | undefined {\n const usage = (data as Record<string, unknown>).usage;\n return usage != null && typeof usage === \"object\" && !Array.isArray(usage)\n ? (usage as UsageInfo)\n : undefined;\n}\n\nfunction readFinishMetadata(data: object): Record<string, unknown> | undefined {\n const record = data as Record<string, unknown>;\n const isRecord = (value: unknown): value is Record<string, unknown> =>\n value != null && typeof value === \"object\" && !Array.isArray(value);\n const merged = {\n ...(isRecord(record.responseMetadata) ? record.responseMetadata : {}),\n ...(isRecord(record.metadata) ? record.metadata : {}),\n };\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/**\n * Mutable view of a streamed message as message and content-block events are\n * assembled into a single structure.\n */\nexport interface AssembledMessage {\n id: string;\n namespace: string[];\n blocks: ContentBlock[];\n node?: string;\n runId?: string;\n usage?: UsageInfo;\n metadata?: MessageMetadata;\n finishMetadata?: Record<string, any>;\n finishReason?: string;\n error?: {\n message: string;\n code?: string;\n };\n}\n\n/**\n * Symbol keys for assembler → StreamingMessage communication.\n * Module-private: invisible to external consumers, accessible to\n * {@link StreamingMessageAssembler} within this file.\n */\nconst PUSH_TEXT: unique symbol = Symbol(\"pushText\");\nconst PUSH_REASONING: unique symbol = Symbol(\"pushReasoning\");\nconst PUSH_EVENT: unique symbol = Symbol(\"pushEvent\");\nconst UPDATE_CONTEXT: unique symbol = Symbol(\"updateContext\");\nconst FINISH: unique symbol = Symbol(\"finish\");\nconst ERROR: unique symbol = Symbol(\"error\");\n\n/**\n * Live streaming view of a single message lifecycle, matching the\n * in-process `ChatModelStream` dual-interface pattern.\n *\n * - `text` / `reasoning`: iterate for streaming deltas, or await for\n * the full concatenated string after the message completes.\n * - `usage`: promise that resolves with token usage on message-finish.\n * - `blocks`: the assembled content blocks (updated as deltas arrive).\n *\n * Created by {@link StreamingMessageAssembler} and yielded by\n * the `session.messages` lazy getter.\n */\nexport class StreamingMessage\n implements AsyncIterable<ChatModelStreamEvent>, PromiseLike<AIMessage>\n{\n readonly id: string;\n readonly namespace: string[];\n node: string | undefined;\n readonly metadata: MessageMetadata | undefined;\n readonly assembled: AssembledMessage;\n readonly #events = new MultiCursorBuffer<ChatModelStreamEvent>();\n\n #textChunks: string[] = [];\n #reasoningChunks: string[] = [];\n #textWaiters: Array<() => void> = [];\n #reasoningWaiters: Array<() => void> = [];\n #textDone = false;\n #reasoningDone = false;\n\n #resolveText!: (v: string) => void;\n #resolveReasoning!: (v: string) => void;\n readonly #textPromise: Promise<string>;\n readonly #reasoningPromise: Promise<string>;\n\n constructor(assembled: AssembledMessage) {\n this.id = assembled.id;\n this.assembled = assembled;\n this.namespace = assembled.namespace;\n this.node = assembled.node;\n this.metadata = assembled.metadata;\n this.#textPromise = new Promise<string>((r) => {\n this.#resolveText = r;\n });\n this.#reasoningPromise = new Promise<string>((r) => {\n this.#resolveReasoning = r;\n });\n }\n\n get text(): TextContentStream {\n const chunks = this.#textChunks;\n const waiters = this.#textWaiters;\n const getDone = () => this.#textDone;\n let cursor = 0;\n return {\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<string>> {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n };\n },\n then: this.#textPromise.then.bind(this.#textPromise),\n full: {\n async *[Symbol.asyncIterator]() {\n let accumulated = \"\";\n for await (const chunk of {\n [Symbol.asyncIterator]: () =>\n ({\n next: async (): Promise<IteratorResult<string>> => {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n }) satisfies AsyncIterator<string>,\n }) {\n accumulated += chunk;\n yield accumulated;\n }\n },\n },\n };\n }\n\n get reasoning(): TextContentStream {\n const chunks = this.#reasoningChunks;\n const waiters = this.#reasoningWaiters;\n const getDone = () => this.#reasoningDone;\n let cursor = 0;\n return {\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<string>> {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n };\n },\n then: this.#reasoningPromise.then.bind(this.#reasoningPromise),\n full: {\n async *[Symbol.asyncIterator]() {\n let accumulated = \"\";\n for await (const chunk of {\n [Symbol.asyncIterator]: () =>\n ({\n next: async (): Promise<IteratorResult<string>> => {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n }) satisfies AsyncIterator<string>,\n }) {\n accumulated += chunk;\n yield accumulated;\n }\n },\n },\n };\n }\n\n get usage(): UsageMetadataStream {\n const promise = (async () => {\n let usage: UsageMetadata | undefined;\n for await (const snapshot of this.#usageIterator()) {\n usage = snapshot;\n }\n return usage;\n })();\n return {\n [Symbol.asyncIterator]: () => this.#usageIterator(),\n then: promise.then.bind(promise),\n };\n }\n\n get toolCalls(): ToolCallsStream {\n const events = this.#events;\n const iterator = async function* () {\n for await (const event of events) {\n if (\n event.event === \"content-block-finish\" &&\n event.content.type === \"tool_call\"\n ) {\n yield event.content as CoreContentBlock.Tools.ToolCall;\n }\n }\n };\n return {\n [Symbol.asyncIterator]: iterator,\n then: async (onfulfilled, onrejected) => {\n try {\n const calls: CoreContentBlock.Tools.ToolCall[] = [];\n for await (const call of iterator()) calls.push(call);\n return onfulfilled ? onfulfilled(calls) : (calls as never);\n } catch (err) {\n if (onrejected) return onrejected(err);\n throw err;\n }\n },\n full: {\n async *[Symbol.asyncIterator]() {\n const calls: CoreContentBlock.Tools.ToolCall[] = [];\n for await (const call of iterator()) {\n calls.push(call);\n yield [...calls];\n }\n },\n },\n };\n }\n\n get output(): PromiseLike<AIMessage> {\n return { then: (onf, onr) => this.#assembleMessage().then(onf, onr) };\n }\n\n get blocks(): ContentBlock[] {\n return this.assembled.blocks;\n }\n\n [Symbol.asyncIterator](): AsyncIterator<ChatModelStreamEvent> {\n return this.#events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AIMessage, TResult2 = never>(\n onfulfilled?:\n | ((value: AIMessage) => TResult1 | PromiseLike<TResult1>)\n | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): PromiseLike<TResult1 | TResult2> {\n return this.#assembleMessage().then(onfulfilled, onrejected);\n }\n\n async *#usageIterator(): AsyncGenerator<UsageMetadata> {\n for await (const event of this.#events) {\n if (event.event === \"message-start\" && event.usage) {\n yield normalizeUsage(event.usage)!;\n } else if (event.event === \"message-finish\" && event.usage) {\n yield normalizeUsage(event.usage)!;\n }\n }\n }\n\n async #assembleMessage(): Promise<AIMessage> {\n const contentBlocks: Array<CoreContentBlock | undefined> = [];\n let id: string | undefined = this.id;\n let usage = normalizeUsage(this.assembled.usage);\n let metadata: Record<string, unknown> = {\n ...(this.assembled.finishMetadata ?? {}),\n };\n let finishReason: string | undefined = this.assembled.finishReason;\n\n for await (const event of this.#events) {\n switch (event.event) {\n case \"message-start\":\n id = event.id ?? id;\n if (event.usage) usage = normalizeUsage(event.usage);\n break;\n case \"content-block-start\":\n contentBlocks[event.index] = event.content;\n break;\n case \"content-block-delta\": {\n const current = contentBlocks[event.index];\n contentBlocks[event.index] = applyCoreEventDelta(current, event);\n break;\n }\n case \"content-block-finish\":\n contentBlocks[event.index] = event.content;\n break;\n case \"message-finish\": {\n finishReason = event.reason;\n if (event.usage) usage = normalizeUsage(event.usage);\n const finishMetadata = readFinishMetadata(event);\n if (finishMetadata != null) {\n metadata = { ...metadata, ...finishMetadata };\n }\n break;\n }\n default:\n break;\n }\n }\n\n return new AIMessage({\n id,\n content: contentBlocks.filter(\n (block): block is CoreContentBlock => block != null\n ),\n usage_metadata: usage,\n additional_kwargs: {\n namespace: this.namespace,\n ...(this.node != null ? { node: this.node } : {}),\n ...(this.assembled.runId != null\n ? { run_id: this.assembled.runId }\n : {}),\n ...(this.metadata != null ? { metadata: this.metadata } : {}),\n ...(this.assembled.usage != null\n ? { usage: this.assembled.usage }\n : {}),\n },\n response_metadata: {\n ...(finishReason != null ? { finish_reason: finishReason } : {}),\n ...metadata,\n output_version: \"v1\" as const,\n },\n });\n }\n\n [PUSH_EVENT](event: ChatModelStreamEvent): void {\n this.#events.push(event);\n }\n\n [UPDATE_CONTEXT](event: MessagesEvent): void {\n this.node = event.params.node ?? this.node;\n }\n\n [PUSH_TEXT](delta: string): void {\n this.#textChunks.push(delta);\n // Wake every caught-up iterator so each one advances its own cursor.\n // Iterators re-check `chunks.length` before delivering, which keeps\n // the cursor the single source of truth for what a consumer has seen.\n const pending = this.#textWaiters.splice(0, this.#textWaiters.length);\n for (const waiter of pending) waiter();\n }\n\n [PUSH_REASONING](delta: string): void {\n this.#reasoningChunks.push(delta);\n const pending = this.#reasoningWaiters.splice(\n 0,\n this.#reasoningWaiters.length\n );\n for (const waiter of pending) waiter();\n }\n\n [FINISH](): void {\n this.#textDone = true;\n this.#reasoningDone = true;\n this.#resolveText(this.#textChunks.join(\"\"));\n this.#resolveReasoning(this.#reasoningChunks.join(\"\"));\n const textPending = this.#textWaiters.splice(0, this.#textWaiters.length);\n for (const waiter of textPending) waiter();\n const reasoningPending = this.#reasoningWaiters.splice(\n 0,\n this.#reasoningWaiters.length\n );\n for (const waiter of reasoningPending) waiter();\n this.#events.close();\n }\n\n [ERROR](): void {\n this[FINISH]();\n }\n}\n\n/**\n * Public view yielded by message projections.\n *\n * `StreamingMessage` is PromiseLike so callers can still `await` a message\n * object directly, but TypeScript applies `Awaited<T>` to values produced by\n * `for await`. Exposing a non-thenable view keeps loop variables typed as the\n * streaming handle instead of as the finalized `AIMessage`.\n */\nexport type StreamingMessageHandle = Omit<StreamingMessage, \"then\">;\n\nexport function toStreamingMessageHandle(\n message: StreamingMessage\n): StreamingMessageHandle {\n return new Proxy(message, {\n get(target, prop) {\n if (prop === \"then\") return undefined;\n const value = Reflect.get(target, prop, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n has(target, prop) {\n if (prop === \"then\") return false;\n return prop in target;\n },\n }) as StreamingMessageHandle;\n}\n\n/**\n * Emitted by `MessageAssembler.consume()` to describe how a message changed in\n * response to a single protocol event.\n */\nexport type MessageAssemblyUpdate =\n | {\n kind: \"message-start\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n }\n | {\n kind: \"content-block-start\" | \"content-block-delta\";\n key: string;\n message: AssembledMessage;\n index: number;\n block: ContentBlock;\n event: MessagesEvent;\n }\n | {\n kind: \"content-block-finish\";\n key: string;\n message: AssembledMessage;\n index: number;\n block: FinalizedContentBlock;\n event: MessagesEvent;\n }\n | {\n kind: \"usage\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n }\n | {\n kind: \"message-finish\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n }\n | {\n kind: \"message-error\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n };\n\nfunction cloneBlock<T extends ContentBlock>(block: T): T {\n return structuredClone(block);\n}\n\nfunction blockFromDelta(\n delta: CoreContentBlockDelta,\n current?: ContentBlock\n): ContentBlock {\n return coreContentBlockFromDelta(\n delta,\n current as unknown as CoreContentBlock | undefined\n ) as unknown as ContentBlock;\n}\n\nfunction applyContentDelta(\n target: ContentBlock,\n delta: ContentBlock\n): ContentBlock {\n if (target.type !== delta.type) {\n return cloneBlock(delta);\n }\n\n switch (delta.type) {\n case \"text\":\n return {\n ...target,\n ...delta,\n text: `${\"text\" in target ? target.text : \"\"}${delta.text}`,\n } as ContentBlock;\n case \"reasoning\":\n return {\n ...target,\n ...delta,\n reasoning: `${\"reasoning\" in target ? target.reasoning : \"\"}${delta.reasoning}`,\n } as ContentBlock;\n case \"tool_call_chunk\":\n case \"server_tool_call_chunk\": {\n // Spread target first, then delta — but preserve target's\n // ``id``/``name`` when the delta explicitly sets them to\n // null/undefined. Some providers (notably Anthropic via the\n // langchain-core compat bridge) only attach the tool-call\n // identifiers to the first ``content-block-start`` chunk; every\n // subsequent ``input_json_delta`` chunk carries ``id=null,\n // name=null``. A naive ``{...target, ...delta}`` spread\n // overwrites the captured identifiers with null, which in turn\n // makes downstream consumers (e.g. ``extractToolCallChunks`` in\n // ``assembled-to-message.ts``) drop the chunk on the floor until\n // the final ``content-block-finish`` event promotes it to a\n // finalized ``tool_call`` — causing tool-call cards to appear\n // all-at-once at the end of the turn instead of incrementally.\n const merged = { ...target, ...delta } as Record<string, unknown>;\n if (delta.id == null && \"id\" in target && target.id != null) {\n merged.id = target.id;\n }\n if (delta.name == null && \"name\" in target && target.name != null) {\n merged.name = target.name;\n }\n merged.args = `${(\"args\" in target ? target.args : \"\") ?? \"\"}${delta.args ?? \"\"}`;\n return merged as unknown as ContentBlock;\n }\n default:\n return {\n ...target,\n ...delta,\n } as ContentBlock;\n }\n}\n\nfunction messageKeyFor(event: MessagesEvent): string {\n const { namespace, node, data } = event.params;\n const namespaceKey = namespace.join(\"/\");\n const runId = readRunId(data);\n const messageId = data.event === \"message-start\" ? (data.id ?? \"\") : \"\";\n return `${namespaceKey}::${node ?? \"\"}::${runId ?? messageId}`;\n}\n\nfunction toChatModelStreamEvent(event: MessagesEvent): ChatModelStreamEvent {\n return event.params.data as unknown as ChatModelStreamEvent;\n}\n\n/**\n * Incrementally assembles `messages` events into complete message objects.\n */\nexport class MessageAssembler {\n private readonly activeMessages = new Map<string, AssembledMessage>();\n private readonly activeByNamespaceNode = new Map<string, string>();\n private readonly blockIndexByProtocolIndexAndType = new Map<string, number>();\n\n /**\n * Applies a single message event and returns the resulting assembly update.\n *\n * @param event - Incoming `messages` event to fold into the assembler state.\n */\n consume(event: MessagesEvent): MessageAssemblyUpdate {\n const data = event.params.data;\n const namespaceNodeKey = `${event.params.namespace.join(\"/\")}::${event.params.node ?? \"\"}`;\n const runId = readRunId(data);\n\n if (data.event === \"message-start\") {\n const key = messageKeyFor(event);\n this.activeByNamespaceNode.set(namespaceNodeKey, key);\n const message: AssembledMessage = {\n id: data.id,\n namespace: [...event.params.namespace],\n node: event.params.node,\n runId,\n usage: readUsage(data),\n metadata: data.metadata,\n blocks: [],\n };\n this.activeMessages.set(key, message);\n return { kind: \"message-start\", key, message, event };\n }\n\n const runKey = runId != null ? `${namespaceNodeKey}::${runId}` : undefined;\n const fallbackKey = this.activeByNamespaceNode.get(namespaceNodeKey);\n const fallbackMessage =\n fallbackKey != null ? this.activeMessages.get(fallbackKey) : undefined;\n const activeKey =\n (runKey != null && this.activeMessages.has(runKey)\n ? runKey\n : undefined) ??\n (runId == null || fallbackMessage?.runId == null\n ? fallbackKey\n : undefined);\n if (!activeKey) {\n // A continuation event (delta/finish/error) arrived without a\n // prior `message-start`. This can happen on late-attaching\n // subscriptions when the server has already trimmed the\n // `message-start` from its replay buffer. Synthesize a minimal\n // active message so the assembler can still fold subsequent\n // events instead of hard-failing the caller.\n const syntheticKey = runKey ?? `${namespaceNodeKey}::`;\n this.activeByNamespaceNode.set(namespaceNodeKey, syntheticKey);\n const synthetic: AssembledMessage = {\n id: data.id,\n namespace: [...event.params.namespace],\n node: event.params.node,\n runId,\n blocks: [],\n };\n this.activeMessages.set(syntheticKey, synthetic);\n return this.consume(event);\n }\n\n const message = this.activeMessages.get(activeKey);\n if (!message) {\n throw new Error(`No active message state found for key ${activeKey}`);\n }\n\n if ((data as { event?: string }).event === \"usage\") {\n message.usage = (data as { usage?: UsageInfo }).usage;\n return {\n kind: \"usage\",\n key: activeKey,\n message,\n event,\n };\n }\n\n switch (data.event) {\n case \"content-block-start\": {\n message.blocks[data.index] = cloneBlock(data.content);\n this.blockIndexByProtocolIndexAndType.set(\n blockIndexKey(activeKey, data.index, data.content.type),\n data.index\n );\n return {\n kind: \"content-block-start\",\n key: activeKey,\n message,\n index: data.index,\n block: data.content,\n event,\n };\n }\n case \"content-block-delta\": {\n const deltaEvent = data as typeof data & {\n content?: ContentBlock;\n delta?: CoreContentBlockDelta;\n };\n const deltaBlock =\n deltaEvent.content ??\n (deltaEvent.delta != null\n ? blockFromDelta(deltaEvent.delta, message.blocks[data.index])\n : undefined);\n if (deltaBlock == null) {\n throw new Error(\"Received content-block-delta without content\");\n }\n const targetIndex = this.resolveBlockIndex(\n activeKey,\n message.blocks,\n data.index,\n deltaBlock.type\n );\n const current = message.blocks[targetIndex];\n message.blocks[targetIndex] =\n deltaEvent.content != null\n ? current == null\n ? cloneBlock(deltaEvent.content)\n : applyContentDelta(current, deltaEvent.content)\n : (applyCoreEventDelta(\n current as CoreContentBlock | undefined,\n data as unknown as Extract<\n ChatModelStreamEvent,\n { event: \"content-block-delta\" }\n >\n ) as unknown as ContentBlock);\n return {\n kind: \"content-block-delta\",\n key: activeKey,\n message,\n index: targetIndex,\n block: deltaBlock,\n event,\n };\n }\n case \"content-block-finish\": {\n const targetIndex = this.resolveFinishBlockIndex(\n activeKey,\n data.index,\n data.content.type\n );\n message.blocks[targetIndex] = cloneBlock(data.content);\n return {\n kind: \"content-block-finish\",\n key: activeKey,\n message,\n index: targetIndex,\n block: data.content,\n event,\n };\n }\n case \"message-finish\": {\n message.usage = readUsage(data) ?? message.usage;\n message.finishMetadata = readFinishMetadata(data);\n message.finishReason = data.reason;\n this.clearActiveMessage(activeKey, namespaceNodeKey);\n this.clearBlockIndexAliases(activeKey);\n return {\n kind: \"message-finish\",\n key: activeKey,\n message: structuredClone(message),\n event,\n };\n }\n case \"error\": {\n message.error = { message: data.message, code: data.code };\n this.clearActiveMessage(activeKey, namespaceNodeKey);\n this.clearBlockIndexAliases(activeKey);\n return {\n kind: \"message-error\",\n key: activeKey,\n message: structuredClone(message),\n event,\n };\n }\n }\n }\n\n private clearActiveMessage(\n activeKey: string,\n namespaceNodeKey: string\n ): void {\n this.activeMessages.delete(activeKey);\n if (this.activeByNamespaceNode.get(namespaceNodeKey) === activeKey) {\n this.activeByNamespaceNode.delete(namespaceNodeKey);\n }\n }\n\n private resolveBlockIndex(\n activeKey: string,\n blocks: ContentBlock[],\n protocolIndex: number,\n blockType: string\n ): number {\n const current = blocks[protocolIndex];\n if (\n current == null ||\n current.type === blockType ||\n areCompatibleBlockTypes(current.type, blockType)\n ) {\n this.blockIndexByProtocolIndexAndType.set(\n blockIndexKey(activeKey, protocolIndex, blockType),\n protocolIndex\n );\n return protocolIndex;\n }\n\n const key = blockIndexKey(activeKey, protocolIndex, blockType);\n const existing = this.blockIndexByProtocolIndexAndType.get(key);\n if (existing != null) return existing;\n\n const nextIndex = blocks.length;\n this.blockIndexByProtocolIndexAndType.set(key, nextIndex);\n return nextIndex;\n }\n\n private resolveFinishBlockIndex(\n activeKey: string,\n protocolIndex: number,\n blockType: string\n ): number {\n const key = blockIndexKey(activeKey, protocolIndex, blockType);\n const existing = this.blockIndexByProtocolIndexAndType.get(key);\n if (existing != null) return existing;\n\n this.blockIndexByProtocolIndexAndType.set(key, protocolIndex);\n return protocolIndex;\n }\n\n private clearBlockIndexAliases(activeKey: string): void {\n const prefix = `${activeKey}::`;\n for (const key of this.blockIndexByProtocolIndexAndType.keys()) {\n if (key.startsWith(prefix))\n this.blockIndexByProtocolIndexAndType.delete(key);\n }\n }\n}\n\nfunction blockIndexKey(\n activeKey: string,\n protocolIndex: number,\n blockType: string\n): string {\n return `${activeKey}::${protocolIndex}::${blockType}`;\n}\n\nfunction areCompatibleBlockTypes(\n currentType: string,\n nextType: string\n): boolean {\n const toolCallTypes = new Set([\n \"tool_call\",\n \"tool_call_chunk\",\n \"tool_use\",\n \"input_json_delta\",\n ]);\n const serverToolCallTypes = new Set([\n \"server_tool_call\",\n \"server_tool_call_chunk\",\n ]);\n\n return (\n (toolCallTypes.has(currentType) && toolCallTypes.has(nextType)) ||\n (serverToolCallTypes.has(currentType) && serverToolCallTypes.has(nextType))\n );\n}\n\n/**\n * Assembles `messages` events into {@link StreamingMessage} instances\n * with live text/reasoning delta streams, matching the in-process\n * `ChatModelStream` dual-interface pattern.\n */\nexport class StreamingMessageAssembler {\n readonly #assembler = new MessageAssembler();\n readonly #activeStreaming = new Map<string, StreamingMessage>();\n\n /**\n * Folds a single event and returns a new {@link StreamingMessage}\n * when a `message-start` is seen, or `undefined` for continuation\n * events (deltas, finish, error).\n */\n consume(event: MessagesEvent): StreamingMessage | undefined {\n const update = this.#assembler.consume(event);\n if (update == null) return undefined;\n\n switch (update.kind) {\n case \"message-start\": {\n const streaming = new StreamingMessage(update.message);\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n this.#activeStreaming.set(update.key, streaming);\n return streaming;\n }\n case \"content-block-start\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n }\n if (\n streaming &&\n update.block.type === \"text\" &&\n \"text\" in update.block &&\n update.block.text\n ) {\n streaming[PUSH_TEXT](update.block.text);\n }\n if (\n streaming &&\n update.block.type === \"reasoning\" &&\n \"reasoning\" in update.block &&\n update.block.reasoning\n ) {\n streaming[PUSH_REASONING](update.block.reasoning);\n }\n return undefined;\n }\n case \"content-block-delta\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (!streaming) return undefined;\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n if (update.block.type === \"text\" && \"text\" in update.block) {\n streaming[PUSH_TEXT](update.block.text);\n }\n if (update.block.type === \"reasoning\" && \"reasoning\" in update.block) {\n streaming[PUSH_REASONING](update.block.reasoning);\n }\n return undefined;\n }\n case \"content-block-finish\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n }\n return undefined;\n }\n case \"usage\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n }\n return undefined;\n }\n case \"message-finish\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n streaming[FINISH]();\n this.#activeStreaming.delete(update.key);\n }\n return undefined;\n }\n case \"message-error\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n streaming[ERROR]();\n this.#activeStreaming.delete(update.key);\n }\n return undefined;\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","names":["#events","#textPromise","#reasoningPromise","#resolveText","#resolveReasoning","#textChunks","#textWaiters","#textDone","#reasoningChunks","#reasoningWaiters","#reasoningDone","#usageIterator","#assembleMessage","#assembler","#activeStreaming"],"sources":["../../../src/client/stream/messages.ts"],"sourcesContent":["import {\n AIMessage,\n type ContentBlock as CoreContentBlock,\n type UsageMetadata,\n} from \"@langchain/core/messages\";\nimport type {\n ChatModelStreamEvent,\n ContentBlockDelta as CoreContentBlockDelta,\n} from \"@langchain/core/language_models/event\";\nimport type {\n ContentBlock,\n FinalizedContentBlock,\n MessageMetadata,\n MessagesEvent,\n UsageInfo,\n} from \"@langchain/protocol\";\n\nimport { MultiCursorBuffer } from \"./multi-cursor-buffer.js\";\n\ntype TextContentStream = AsyncIterable<string> &\n PromiseLike<string> & { full: AsyncIterable<string> };\n\ntype UsageMetadataStream = AsyncIterable<UsageMetadata> &\n PromiseLike<UsageMetadata | undefined>;\n\ntype ToolCallsStream = AsyncIterable<CoreContentBlock.Tools.ToolCall> &\n PromiseLike<Array<CoreContentBlock.Tools.ToolCall>> & {\n full: AsyncIterable<Array<CoreContentBlock.Tools.ToolCall>>;\n };\n\nfunction applyCoreContentDelta(\n target: CoreContentBlock,\n delta: CoreContentBlock\n): CoreContentBlock {\n if (target.type !== delta.type) {\n return structuredClone(delta);\n }\n\n switch (delta.type) {\n case \"text\":\n return {\n ...target,\n ...delta,\n text: `${\"text\" in target ? target.text : \"\"}${delta.text}`,\n } as CoreContentBlock;\n case \"reasoning\":\n return {\n ...target,\n ...delta,\n reasoning: `${\"reasoning\" in target ? target.reasoning : \"\"}${delta.reasoning}`,\n } as CoreContentBlock;\n case \"tool_call_chunk\":\n case \"server_tool_call_chunk\": {\n const merged = { ...target, ...delta } as Record<string, unknown>;\n if (delta.id == null && \"id\" in target && target.id != null) {\n merged.id = target.id;\n }\n if (delta.name == null && \"name\" in target && target.name != null) {\n merged.name = target.name;\n }\n merged.args = `${(\"args\" in target ? target.args : \"\") ?? \"\"}${delta.args ?? \"\"}`;\n return merged as unknown as CoreContentBlock;\n }\n default:\n return { ...target, ...delta } as CoreContentBlock;\n }\n}\n\nfunction coreContentBlockFromDelta(\n delta: CoreContentBlockDelta,\n current?: CoreContentBlock\n): CoreContentBlock {\n switch (delta.type) {\n case \"text-delta\":\n return { type: \"text\", text: delta.text } as CoreContentBlock;\n case \"reasoning-delta\":\n return {\n type: \"reasoning\",\n reasoning: delta.reasoning,\n } as CoreContentBlock;\n case \"data-delta\": {\n const merged = { ...(current ?? {}), data: delta.data } as Record<\n string,\n unknown\n >;\n if (delta.encoding) merged.encoding = delta.encoding;\n return merged as unknown as CoreContentBlock;\n }\n case \"block-delta\":\n return delta.fields as CoreContentBlock;\n }\n}\n\nfunction applyCoreEventDelta(\n current: CoreContentBlock | undefined,\n event: Extract<ChatModelStreamEvent, { event: \"content-block-delta\" }> & {\n content?: CoreContentBlock;\n }\n): CoreContentBlock {\n if (event.content) {\n return current\n ? applyCoreContentDelta(current, event.content)\n : event.content;\n }\n\n switch (event.delta.type) {\n case \"text-delta\":\n if (current?.type === \"text\") {\n return {\n ...current,\n text: `${\"text\" in current ? current.text : \"\"}${event.delta.text}`,\n } as CoreContentBlock;\n }\n return coreContentBlockFromDelta(event.delta, current);\n case \"reasoning-delta\":\n if (current?.type === \"reasoning\") {\n return {\n ...current,\n reasoning: `${\"reasoning\" in current ? current.reasoning : \"\"}${event.delta.reasoning}`,\n } as CoreContentBlock;\n }\n return coreContentBlockFromDelta(event.delta, current);\n case \"data-delta\": {\n const merged = { ...(current ?? {}) } as Record<string, unknown>;\n merged.data = `${(merged.data as string | undefined) ?? \"\"}${event.delta.data}`;\n if (event.delta.encoding) merged.encoding = event.delta.encoding;\n return merged as unknown as CoreContentBlock;\n }\n case \"block-delta\":\n return {\n ...(current ?? {}),\n ...event.delta.fields,\n } as CoreContentBlock;\n }\n}\n\nfunction normalizeUsage(\n usage: UsageInfo | Partial<UsageMetadata> | undefined\n): UsageMetadata | undefined {\n if (!usage) return undefined;\n return {\n ...usage,\n input_tokens: usage.input_tokens ?? 0,\n output_tokens: usage.output_tokens ?? 0,\n total_tokens: usage.total_tokens ?? 0,\n };\n}\n\nfunction readRunId(data: object): string | undefined {\n const record = data as Record<string, unknown>;\n return typeof record.run_id === \"string\" ? record.run_id : undefined;\n}\n\nfunction readUsage(data: object): UsageInfo | undefined {\n const usage = (data as Record<string, unknown>).usage;\n return usage != null && typeof usage === \"object\" && !Array.isArray(usage)\n ? (usage as UsageInfo)\n : undefined;\n}\n\nfunction readFinishMetadata(data: object): Record<string, unknown> | undefined {\n const record = data as Record<string, unknown>;\n const isRecord = (value: unknown): value is Record<string, unknown> =>\n value != null && typeof value === \"object\" && !Array.isArray(value);\n const merged = {\n ...(isRecord(record.responseMetadata) ? record.responseMetadata : {}),\n ...(isRecord(record.metadata) ? record.metadata : {}),\n };\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/**\n * Mutable view of a streamed message as message and content-block events are\n * assembled into a single structure.\n */\nexport interface AssembledMessage {\n id: string;\n namespace: string[];\n blocks: ContentBlock[];\n node?: string;\n runId?: string;\n usage?: UsageInfo;\n metadata?: MessageMetadata;\n finishMetadata?: Record<string, any>;\n finishReason?: string;\n error?: {\n message: string;\n code?: string;\n };\n}\n\n/**\n * Symbol keys for assembler → StreamingMessage communication.\n * Module-private: invisible to external consumers, accessible to\n * {@link StreamingMessageAssembler} within this file.\n */\nconst PUSH_TEXT: unique symbol = Symbol(\"pushText\");\nconst PUSH_REASONING: unique symbol = Symbol(\"pushReasoning\");\nconst PUSH_EVENT: unique symbol = Symbol(\"pushEvent\");\nconst UPDATE_CONTEXT: unique symbol = Symbol(\"updateContext\");\nconst FINISH: unique symbol = Symbol(\"finish\");\nconst ERROR: unique symbol = Symbol(\"error\");\n\n/**\n * Live streaming view of a single message lifecycle, matching the\n * in-process `ChatModelStream` dual-interface pattern.\n *\n * - `text` / `reasoning`: iterate for streaming deltas, or await for\n * the full concatenated string after the message completes.\n * - `usage`: promise that resolves with token usage on message-finish.\n * - `blocks`: the assembled content blocks (updated as deltas arrive).\n *\n * Created by {@link StreamingMessageAssembler} and yielded by\n * the `session.messages` lazy getter.\n */\nexport class StreamingMessage\n implements AsyncIterable<ChatModelStreamEvent>, PromiseLike<AIMessage>\n{\n readonly id: string;\n readonly namespace: string[];\n node: string | undefined;\n readonly metadata: MessageMetadata | undefined;\n readonly assembled: AssembledMessage;\n readonly #events = new MultiCursorBuffer<ChatModelStreamEvent>();\n\n #textChunks: string[] = [];\n #reasoningChunks: string[] = [];\n #textWaiters: Array<() => void> = [];\n #reasoningWaiters: Array<() => void> = [];\n #textDone = false;\n #reasoningDone = false;\n\n #resolveText!: (v: string) => void;\n #resolveReasoning!: (v: string) => void;\n readonly #textPromise: Promise<string>;\n readonly #reasoningPromise: Promise<string>;\n\n constructor(assembled: AssembledMessage) {\n this.id = assembled.id;\n this.assembled = assembled;\n this.namespace = assembled.namespace;\n this.node = assembled.node;\n this.metadata = assembled.metadata;\n this.#textPromise = new Promise<string>((r) => {\n this.#resolveText = r;\n });\n this.#reasoningPromise = new Promise<string>((r) => {\n this.#resolveReasoning = r;\n });\n }\n\n get text(): TextContentStream {\n const chunks = this.#textChunks;\n const waiters = this.#textWaiters;\n const getDone = () => this.#textDone;\n let cursor = 0;\n return {\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<string>> {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n };\n },\n then: this.#textPromise.then.bind(this.#textPromise),\n full: {\n async *[Symbol.asyncIterator]() {\n let accumulated = \"\";\n for await (const chunk of {\n [Symbol.asyncIterator]: () =>\n ({\n next: async (): Promise<IteratorResult<string>> => {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n }) satisfies AsyncIterator<string>,\n }) {\n accumulated += chunk;\n yield accumulated;\n }\n },\n },\n };\n }\n\n get reasoning(): TextContentStream {\n const chunks = this.#reasoningChunks;\n const waiters = this.#reasoningWaiters;\n const getDone = () => this.#reasoningDone;\n let cursor = 0;\n return {\n [Symbol.asyncIterator]() {\n return {\n async next(): Promise<IteratorResult<string>> {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n };\n },\n then: this.#reasoningPromise.then.bind(this.#reasoningPromise),\n full: {\n async *[Symbol.asyncIterator]() {\n let accumulated = \"\";\n for await (const chunk of {\n [Symbol.asyncIterator]: () =>\n ({\n next: async (): Promise<IteratorResult<string>> => {\n while (true) {\n if (cursor < chunks.length) {\n return { done: false, value: chunks[cursor++] };\n }\n if (getDone()) {\n return { done: true, value: undefined };\n }\n await new Promise<void>((resolve) => {\n waiters.push(resolve);\n });\n }\n },\n }) satisfies AsyncIterator<string>,\n }) {\n accumulated += chunk;\n yield accumulated;\n }\n },\n },\n };\n }\n\n get usage(): UsageMetadataStream {\n const promise = (async () => {\n let usage: UsageMetadata | undefined;\n for await (const snapshot of this.#usageIterator()) {\n usage = snapshot;\n }\n return usage;\n })();\n return {\n [Symbol.asyncIterator]: () => this.#usageIterator(),\n then: promise.then.bind(promise),\n };\n }\n\n get toolCalls(): ToolCallsStream {\n const events = this.#events;\n const iterator = async function* () {\n for await (const event of events) {\n if (\n event.event === \"content-block-finish\" &&\n event.content.type === \"tool_call\"\n ) {\n yield event.content as CoreContentBlock.Tools.ToolCall;\n }\n }\n };\n return {\n [Symbol.asyncIterator]: iterator,\n then: async (onfulfilled, onrejected) => {\n try {\n const calls: CoreContentBlock.Tools.ToolCall[] = [];\n for await (const call of iterator()) calls.push(call);\n return onfulfilled ? onfulfilled(calls) : (calls as never);\n } catch (err) {\n if (onrejected) return onrejected(err);\n throw err;\n }\n },\n full: {\n async *[Symbol.asyncIterator]() {\n const calls: CoreContentBlock.Tools.ToolCall[] = [];\n for await (const call of iterator()) {\n calls.push(call);\n yield [...calls];\n }\n },\n },\n };\n }\n\n get output(): PromiseLike<AIMessage> {\n return { then: (onf, onr) => this.#assembleMessage().then(onf, onr) };\n }\n\n get blocks(): ContentBlock[] {\n return this.assembled.blocks;\n }\n\n [Symbol.asyncIterator](): AsyncIterator<ChatModelStreamEvent> {\n return this.#events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AIMessage, TResult2 = never>(\n onfulfilled?:\n | ((value: AIMessage) => TResult1 | PromiseLike<TResult1>)\n | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null\n ): PromiseLike<TResult1 | TResult2> {\n return this.#assembleMessage().then(onfulfilled, onrejected);\n }\n\n async *#usageIterator(): AsyncGenerator<UsageMetadata> {\n for await (const event of this.#events) {\n if (event.event === \"message-start\" && event.usage) {\n yield normalizeUsage(event.usage)!;\n } else if (event.event === \"message-finish\" && event.usage) {\n yield normalizeUsage(event.usage)!;\n }\n }\n }\n\n async #assembleMessage(): Promise<AIMessage> {\n const contentBlocks: Array<CoreContentBlock | undefined> = [];\n let id: string | undefined = this.id;\n let usage = normalizeUsage(this.assembled.usage);\n let metadata: Record<string, unknown> = {\n ...(this.assembled.finishMetadata ?? {}),\n };\n let finishReason: string | undefined = this.assembled.finishReason;\n\n for await (const event of this.#events) {\n switch (event.event) {\n case \"message-start\":\n id = event.id ?? id;\n if (event.usage) usage = normalizeUsage(event.usage);\n break;\n case \"content-block-start\":\n contentBlocks[event.index] = event.content;\n break;\n case \"content-block-delta\": {\n const current = contentBlocks[event.index];\n contentBlocks[event.index] = applyCoreEventDelta(current, event);\n break;\n }\n case \"content-block-finish\":\n contentBlocks[event.index] = event.content;\n break;\n case \"message-finish\": {\n finishReason = event.reason;\n if (event.usage) usage = normalizeUsage(event.usage);\n const finishMetadata = readFinishMetadata(event);\n if (finishMetadata != null) {\n metadata = { ...metadata, ...finishMetadata };\n }\n break;\n }\n default:\n break;\n }\n }\n\n return new AIMessage({\n id,\n content: contentBlocks.filter(\n (block): block is CoreContentBlock => block != null\n ),\n usage_metadata: usage,\n additional_kwargs: {\n namespace: this.namespace,\n ...(this.node != null ? { node: this.node } : {}),\n ...(this.assembled.runId != null\n ? { run_id: this.assembled.runId }\n : {}),\n ...(this.metadata != null ? { metadata: this.metadata } : {}),\n ...(this.assembled.usage != null\n ? { usage: this.assembled.usage }\n : {}),\n },\n response_metadata: {\n ...(finishReason != null ? { finish_reason: finishReason } : {}),\n ...metadata,\n output_version: \"v1\" as const,\n },\n });\n }\n\n [PUSH_EVENT](event: ChatModelStreamEvent): void {\n this.#events.push(event);\n }\n\n [UPDATE_CONTEXT](event: MessagesEvent): void {\n this.node = event.params.node ?? this.node;\n }\n\n [PUSH_TEXT](delta: string): void {\n this.#textChunks.push(delta);\n // Wake every caught-up iterator so each one advances its own cursor.\n // Iterators re-check `chunks.length` before delivering, which keeps\n // the cursor the single source of truth for what a consumer has seen.\n const pending = this.#textWaiters.splice(0, this.#textWaiters.length);\n for (const waiter of pending) waiter();\n }\n\n [PUSH_REASONING](delta: string): void {\n this.#reasoningChunks.push(delta);\n const pending = this.#reasoningWaiters.splice(\n 0,\n this.#reasoningWaiters.length\n );\n for (const waiter of pending) waiter();\n }\n\n [FINISH](): void {\n this.#textDone = true;\n this.#reasoningDone = true;\n this.#resolveText(this.#textChunks.join(\"\"));\n this.#resolveReasoning(this.#reasoningChunks.join(\"\"));\n const textPending = this.#textWaiters.splice(0, this.#textWaiters.length);\n for (const waiter of textPending) waiter();\n const reasoningPending = this.#reasoningWaiters.splice(\n 0,\n this.#reasoningWaiters.length\n );\n for (const waiter of reasoningPending) waiter();\n this.#events.close();\n }\n\n [ERROR](): void {\n this[FINISH]();\n }\n}\n\n/**\n * Public view yielded by message projections.\n *\n * `StreamingMessage` is PromiseLike so callers can still `await` a message\n * object directly, but TypeScript applies `Awaited<T>` to values produced by\n * `for await`. Exposing a non-thenable view keeps loop variables typed as the\n * streaming handle instead of as the finalized `AIMessage`.\n */\nexport type StreamingMessageHandle = Omit<StreamingMessage, \"then\">;\n\nexport function toStreamingMessageHandle(\n message: StreamingMessage\n): StreamingMessageHandle {\n return new Proxy(message, {\n get(target, prop) {\n if (prop === \"then\") return undefined;\n const value = Reflect.get(target, prop, target);\n return typeof value === \"function\" ? value.bind(target) : value;\n },\n has(target, prop) {\n if (prop === \"then\") return false;\n return prop in target;\n },\n }) as StreamingMessageHandle;\n}\n\n/**\n * Emitted by `MessageAssembler.consume()` to describe how a message changed in\n * response to a single protocol event.\n */\nexport type MessageAssemblyUpdate =\n | {\n kind: \"message-start\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n }\n | {\n kind: \"content-block-start\" | \"content-block-delta\";\n key: string;\n message: AssembledMessage;\n index: number;\n block: ContentBlock;\n event: MessagesEvent;\n }\n | {\n kind: \"content-block-finish\";\n key: string;\n message: AssembledMessage;\n index: number;\n block: FinalizedContentBlock;\n event: MessagesEvent;\n }\n | {\n kind: \"usage\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n }\n | {\n kind: \"message-finish\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n }\n | {\n kind: \"message-error\";\n key: string;\n message: AssembledMessage;\n event: MessagesEvent;\n };\n\nfunction cloneBlock<T extends ContentBlock>(block: T): T {\n return structuredClone(block);\n}\n\nfunction blockFromDelta(\n delta: CoreContentBlockDelta,\n current?: ContentBlock\n): ContentBlock {\n return coreContentBlockFromDelta(\n delta,\n current as unknown as CoreContentBlock | undefined\n ) as unknown as ContentBlock;\n}\n\nfunction applyContentDelta(\n target: ContentBlock,\n delta: ContentBlock\n): ContentBlock {\n if (target.type !== delta.type) {\n return cloneBlock(delta);\n }\n\n switch (delta.type) {\n case \"text\":\n return {\n ...target,\n ...delta,\n text: `${\"text\" in target ? target.text : \"\"}${delta.text}`,\n } as ContentBlock;\n case \"reasoning\":\n return {\n ...target,\n ...delta,\n reasoning: `${\"reasoning\" in target ? target.reasoning : \"\"}${delta.reasoning}`,\n } as ContentBlock;\n case \"tool_call_chunk\":\n case \"server_tool_call_chunk\": {\n // Spread target first, then delta — but preserve target's\n // ``id``/``name`` when the delta explicitly sets them to\n // null/undefined. Some providers (notably Anthropic via the\n // langchain-core compat bridge) only attach the tool-call\n // identifiers to the first ``content-block-start`` chunk; every\n // subsequent ``input_json_delta`` chunk carries ``id=null,\n // name=null``. A naive ``{...target, ...delta}`` spread\n // overwrites the captured identifiers with null, which in turn\n // makes downstream consumers (e.g. ``extractToolCallChunks`` in\n // ``assembled-to-message.ts``) drop the chunk on the floor until\n // the final ``content-block-finish`` event promotes it to a\n // finalized ``tool_call`` — causing tool-call cards to appear\n // all-at-once at the end of the turn instead of incrementally.\n const merged = { ...target, ...delta } as Record<string, unknown>;\n if (delta.id == null && \"id\" in target && target.id != null) {\n merged.id = target.id;\n }\n if (delta.name == null && \"name\" in target && target.name != null) {\n merged.name = target.name;\n }\n merged.args = `${(\"args\" in target ? target.args : \"\") ?? \"\"}${delta.args ?? \"\"}`;\n return merged as unknown as ContentBlock;\n }\n default:\n return {\n ...target,\n ...delta,\n } as ContentBlock;\n }\n}\n\nfunction messageKeyFor(event: MessagesEvent): string {\n const { namespace, node, data } = event.params;\n const namespaceKey = namespace.join(\"/\");\n const runId = readRunId(data);\n const messageId = data.event === \"message-start\" ? (data.id ?? \"\") : \"\";\n return `${namespaceKey}::${node ?? \"\"}::${runId ?? messageId}`;\n}\n\nfunction toChatModelStreamEvent(event: MessagesEvent): ChatModelStreamEvent {\n return event.params.data as unknown as ChatModelStreamEvent;\n}\n\n/**\n * Incrementally assembles `messages` events into complete message objects.\n */\nexport class MessageAssembler {\n private readonly activeMessages = new Map<string, AssembledMessage>();\n private readonly activeByNamespaceNode = new Map<string, string>();\n private readonly blockIndexByProtocolIndexAndType = new Map<string, number>();\n\n /**\n * Applies a single message event and returns the resulting assembly update.\n *\n * @param event - Incoming `messages` event to fold into the assembler state.\n */\n consume(event: MessagesEvent): MessageAssemblyUpdate {\n const data = event.params.data;\n const namespaceNodeKey = `${event.params.namespace.join(\"/\")}::${event.params.node ?? \"\"}`;\n const runId = readRunId(data);\n\n if (data.event === \"message-start\") {\n const key = messageKeyFor(event);\n this.activeByNamespaceNode.set(namespaceNodeKey, key);\n const message: AssembledMessage = {\n id: data.id,\n namespace: [...event.params.namespace],\n node: event.params.node,\n runId,\n usage: readUsage(data),\n metadata: data.metadata,\n blocks: [],\n };\n this.activeMessages.set(key, message);\n return { kind: \"message-start\", key, message, event };\n }\n\n const runKey = runId != null ? `${namespaceNodeKey}::${runId}` : undefined;\n const fallbackKey = this.activeByNamespaceNode.get(namespaceNodeKey);\n const fallbackMessage =\n fallbackKey != null ? this.activeMessages.get(fallbackKey) : undefined;\n const activeKey =\n (runKey != null && this.activeMessages.has(runKey)\n ? runKey\n : undefined) ??\n (runId == null || fallbackMessage?.runId == null\n ? fallbackKey\n : undefined);\n if (!activeKey) {\n // A continuation event (delta/finish/error) arrived without a\n // prior `message-start`. This can happen on late-attaching\n // subscriptions when the server has already trimmed the\n // `message-start` from its replay buffer. Synthesize a minimal\n // active message so the assembler can still fold subsequent\n // events instead of hard-failing the caller.\n const syntheticKey = runKey ?? `${namespaceNodeKey}::`;\n this.activeByNamespaceNode.set(namespaceNodeKey, syntheticKey);\n const synthetic: AssembledMessage = {\n id: data.id,\n namespace: [...event.params.namespace],\n node: event.params.node,\n runId,\n blocks: [],\n };\n this.activeMessages.set(syntheticKey, synthetic);\n return this.consume(event);\n }\n\n const message = this.activeMessages.get(activeKey);\n if (!message) {\n throw new Error(`No active message state found for key ${activeKey}`);\n }\n\n if ((data as { event?: string }).event === \"usage\") {\n message.usage = (data as { usage?: UsageInfo }).usage;\n return {\n kind: \"usage\",\n key: activeKey,\n message,\n event,\n };\n }\n\n switch (data.event) {\n case \"content-block-start\": {\n message.blocks[data.index] = cloneBlock(data.content);\n this.blockIndexByProtocolIndexAndType.set(\n blockIndexKey(activeKey, data.index, data.content.type),\n data.index\n );\n return {\n kind: \"content-block-start\",\n key: activeKey,\n message,\n index: data.index,\n block: data.content,\n event,\n };\n }\n case \"content-block-delta\": {\n const deltaEvent = data as typeof data & {\n content?: ContentBlock;\n delta?: CoreContentBlockDelta;\n };\n const deltaBlock =\n deltaEvent.content ??\n (deltaEvent.delta != null\n ? blockFromDelta(deltaEvent.delta, message.blocks[data.index])\n : undefined);\n if (deltaBlock == null) {\n throw new Error(\"Received content-block-delta without content\");\n }\n const targetIndex = this.resolveBlockIndex(\n activeKey,\n message.blocks,\n data.index,\n deltaBlock.type\n );\n const current = message.blocks[targetIndex];\n message.blocks[targetIndex] =\n deltaEvent.content != null\n ? current == null\n ? cloneBlock(deltaEvent.content)\n : applyContentDelta(current, deltaEvent.content)\n : (applyCoreEventDelta(\n current as CoreContentBlock | undefined,\n data as unknown as Extract<\n ChatModelStreamEvent,\n { event: \"content-block-delta\" }\n >\n ) as unknown as ContentBlock);\n return {\n kind: \"content-block-delta\",\n key: activeKey,\n message,\n index: targetIndex,\n block: deltaBlock,\n event,\n };\n }\n case \"content-block-finish\": {\n const targetIndex = this.resolveFinishBlockIndex(\n activeKey,\n data.index,\n data.content.type\n );\n message.blocks[targetIndex] = cloneBlock(data.content);\n return {\n kind: \"content-block-finish\",\n key: activeKey,\n message,\n index: targetIndex,\n block: data.content,\n event,\n };\n }\n case \"message-finish\": {\n message.usage = readUsage(data) ?? message.usage;\n message.finishMetadata = readFinishMetadata(data);\n message.finishReason = data.reason;\n this.clearActiveMessage(activeKey, namespaceNodeKey);\n this.clearBlockIndexAliases(activeKey);\n return {\n kind: \"message-finish\",\n key: activeKey,\n message: structuredClone(message),\n event,\n };\n }\n case \"error\": {\n message.error = { message: data.message, code: data.code };\n this.clearActiveMessage(activeKey, namespaceNodeKey);\n this.clearBlockIndexAliases(activeKey);\n return {\n kind: \"message-error\",\n key: activeKey,\n message: structuredClone(message),\n event,\n };\n }\n }\n }\n\n /**\n * Drop every message that started but never got `message-finish` or\n * `error`, e.g. because the run was cancelled mid-stream.\n *\n * @param namespace - Only abandon messages at or below this namespace.\n * @returns The abandoned messages.\n */\n abandonActive(namespace?: readonly string[]): AssembledMessage[] {\n const abandoned: AssembledMessage[] = [];\n for (const [key, message] of this.activeMessages) {\n if (\n namespace != null &&\n !hasNamespacePrefix(message.namespace, namespace)\n )\n continue;\n abandoned.push(message);\n this.activeMessages.delete(key);\n this.clearBlockIndexAliases(key);\n for (const [nsNode, activeKey] of this.activeByNamespaceNode) {\n if (activeKey === key) this.activeByNamespaceNode.delete(nsNode);\n }\n }\n return abandoned;\n }\n\n private clearActiveMessage(\n activeKey: string,\n namespaceNodeKey: string\n ): void {\n this.activeMessages.delete(activeKey);\n if (this.activeByNamespaceNode.get(namespaceNodeKey) === activeKey) {\n this.activeByNamespaceNode.delete(namespaceNodeKey);\n }\n }\n\n private resolveBlockIndex(\n activeKey: string,\n blocks: ContentBlock[],\n protocolIndex: number,\n blockType: string\n ): number {\n const current = blocks[protocolIndex];\n if (\n current == null ||\n current.type === blockType ||\n areCompatibleBlockTypes(current.type, blockType)\n ) {\n this.blockIndexByProtocolIndexAndType.set(\n blockIndexKey(activeKey, protocolIndex, blockType),\n protocolIndex\n );\n return protocolIndex;\n }\n\n const key = blockIndexKey(activeKey, protocolIndex, blockType);\n const existing = this.blockIndexByProtocolIndexAndType.get(key);\n if (existing != null) return existing;\n\n const nextIndex = blocks.length;\n this.blockIndexByProtocolIndexAndType.set(key, nextIndex);\n return nextIndex;\n }\n\n private resolveFinishBlockIndex(\n activeKey: string,\n protocolIndex: number,\n blockType: string\n ): number {\n const key = blockIndexKey(activeKey, protocolIndex, blockType);\n const existing = this.blockIndexByProtocolIndexAndType.get(key);\n if (existing != null) return existing;\n\n this.blockIndexByProtocolIndexAndType.set(key, protocolIndex);\n return protocolIndex;\n }\n\n private clearBlockIndexAliases(activeKey: string): void {\n const prefix = `${activeKey}::`;\n for (const key of this.blockIndexByProtocolIndexAndType.keys()) {\n if (key.startsWith(prefix))\n this.blockIndexByProtocolIndexAndType.delete(key);\n }\n }\n}\n\nfunction hasNamespacePrefix(\n namespace: readonly string[],\n prefix: readonly string[]\n): boolean {\n return prefix.every((segment, i) => namespace[i] === segment);\n}\n\nfunction blockIndexKey(\n activeKey: string,\n protocolIndex: number,\n blockType: string\n): string {\n return `${activeKey}::${protocolIndex}::${blockType}`;\n}\n\nfunction areCompatibleBlockTypes(\n currentType: string,\n nextType: string\n): boolean {\n const toolCallTypes = new Set([\n \"tool_call\",\n \"tool_call_chunk\",\n \"tool_use\",\n \"input_json_delta\",\n ]);\n const serverToolCallTypes = new Set([\n \"server_tool_call\",\n \"server_tool_call_chunk\",\n ]);\n\n return (\n (toolCallTypes.has(currentType) && toolCallTypes.has(nextType)) ||\n (serverToolCallTypes.has(currentType) && serverToolCallTypes.has(nextType))\n );\n}\n\n/**\n * Assembles `messages` events into {@link StreamingMessage} instances\n * with live text/reasoning delta streams, matching the in-process\n * `ChatModelStream` dual-interface pattern.\n */\nexport class StreamingMessageAssembler {\n readonly #assembler = new MessageAssembler();\n readonly #activeStreaming = new Map<string, StreamingMessage>();\n\n /**\n * Folds a single event and returns a new {@link StreamingMessage}\n * when a `message-start` is seen, or `undefined` for continuation\n * events (deltas, finish, error).\n */\n consume(event: MessagesEvent): StreamingMessage | undefined {\n const update = this.#assembler.consume(event);\n if (update == null) return undefined;\n\n switch (update.kind) {\n case \"message-start\": {\n const streaming = new StreamingMessage(update.message);\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n this.#activeStreaming.set(update.key, streaming);\n return streaming;\n }\n case \"content-block-start\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n }\n if (\n streaming &&\n update.block.type === \"text\" &&\n \"text\" in update.block &&\n update.block.text\n ) {\n streaming[PUSH_TEXT](update.block.text);\n }\n if (\n streaming &&\n update.block.type === \"reasoning\" &&\n \"reasoning\" in update.block &&\n update.block.reasoning\n ) {\n streaming[PUSH_REASONING](update.block.reasoning);\n }\n return undefined;\n }\n case \"content-block-delta\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (!streaming) return undefined;\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n if (update.block.type === \"text\" && \"text\" in update.block) {\n streaming[PUSH_TEXT](update.block.text);\n }\n if (update.block.type === \"reasoning\" && \"reasoning\" in update.block) {\n streaming[PUSH_REASONING](update.block.reasoning);\n }\n return undefined;\n }\n case \"content-block-finish\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n }\n return undefined;\n }\n case \"usage\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n }\n return undefined;\n }\n case \"message-finish\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n streaming[FINISH]();\n this.#activeStreaming.delete(update.key);\n }\n return undefined;\n }\n case \"message-error\": {\n const streaming = this.#activeStreaming.get(update.key);\n if (streaming) {\n streaming[UPDATE_CONTEXT](update.event);\n streaming[PUSH_EVENT](toChatModelStreamEvent(update.event));\n streaming[ERROR]();\n this.#activeStreaming.delete(update.key);\n }\n return undefined;\n }\n }\n }\n\n /**\n * Settle every message that never finished, marking it with an\n * `aborted` error so awaiting consumers don't hang.\n *\n * @param namespace - Only abandon messages at or below this namespace.\n * @returns The abandoned messages.\n */\n abandonActive(namespace?: readonly string[]): StreamingMessage[] {\n this.#assembler.abandonActive(namespace);\n const abandoned: StreamingMessage[] = [];\n for (const [key, streaming] of this.#activeStreaming) {\n if (\n namespace != null &&\n !hasNamespacePrefix(streaming.namespace, namespace)\n )\n continue;\n streaming.assembled.error = {\n message: \"Message stream ended before message-finish\",\n code: \"aborted\",\n };\n streaming[ERROR]();\n this.#activeStreaming.delete(key);\n abandoned.push(streaming);\n }\n return abandoned;\n }\n}\n"],"mappings":"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"}
@@ -6,17 +6,6 @@ const require_queue = require("./queue.cjs");
6
6
  const require_utils = require("./utils.cjs");
7
7
  //#region src/client/stream/transport/http.ts
8
8
  /**
9
- * True for a 4xx HTTP error other than 408 (Request Timeout) and 429
10
- * (Too Many Requests): retrying the same request cannot succeed. Errors
11
- * with no `status` (network errors, idle timeouts, server closes) stay
12
- * retryable.
13
- */
14
- function isNonRetryableHttpError(error) {
15
- if (!require_utils.isRecord(error) || typeof error.status !== "number") return false;
16
- const { status } = error;
17
- return status >= 400 && status < 500 && status !== 408 && status !== 429;
18
- }
19
- /**
20
9
  * Transport adapter that speaks the thread-centric protocol over HTTP
21
10
  * commands plus SSE event streams. Bound to a `threadId` at construction
22
11
  * or later via {@link setThreadId}; request URLs derive from the
@@ -201,7 +190,7 @@ var ProtocolSseTransportAdapter = class {
201
190
  streamQueue.close();
202
191
  return;
203
192
  }
204
- if (this.maxReconnectAttempts <= 0 || isNonRetryableHttpError(error)) {
193
+ if (this.maxReconnectAttempts <= 0 || require_utils.isNonRetryableHttpError(error)) {
205
194
  if (!readySettled) rejectReady(error);
206
195
  streamQueue.close(require_utils.toError(error));
207
196
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"http.cjs","names":["isRecord","AsyncQueue","reconnectDelayMs","resolveProtocolPath","encodePathSegment","toAbsoluteUrl","mergeHeaders","toError","isProtocolResponse","BytesLineDecoder","idleReconnectStream","SSEDecoder","IterableReadableStream"],"sources":["../../../../src/client/stream/transport/http.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport { encodePathSegment } from \"../../base.js\";\nimport type {\n Message,\n SubscribeParams,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport type { AsyncCaller } from \"../../../utils/async_caller.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n ProtocolSseTransportOptions,\n ProtocolTransportPaths,\n} from \"./types.js\";\nimport type { TransportAdapter, EventStreamHandle } from \"../transport.js\";\nimport {\n toAbsoluteUrl,\n isRecord,\n mergeHeaders,\n toError,\n isProtocolResponse,\n resolveProtocolPath,\n} from \"./utils.js\";\nimport { BytesLineDecoder, SSEDecoder } from \"../../../utils/sse.js\";\nimport {\n IterableReadableStream,\n idleReconnectStream,\n type IdleReconnectMode,\n} from \"../../../utils/stream.js\";\nimport {\n DEFAULT_MAX_RECONNECT_ATTEMPTS,\n reconnectDelayMs,\n} from \"../../../utils/reconnect.js\";\nimport { DEFAULT_IDLE_RECONNECT } from \"../../../utils/stream.js\";\n\n/**\n * True for a 4xx HTTP error other than 408 (Request Timeout) and 429\n * (Too Many Requests): retrying the same request cannot succeed. Errors\n * with no `status` (network errors, idle timeouts, server closes) stay\n * retryable.\n */\nfunction isNonRetryableHttpError(error: unknown): boolean {\n if (!isRecord(error) || typeof error.status !== \"number\") return false;\n const { status } = error;\n return status >= 400 && status < 500 && status !== 408 && status !== 429;\n}\n\n/**\n * Transport adapter that speaks the thread-centric protocol over HTTP\n * commands plus SSE event streams. Bound to a `threadId` at construction\n * or later via {@link setThreadId}; request URLs derive from the\n * currently-bound thread. Each {@link openEventStream} call opens an\n * independent filtered SSE connection via\n * `POST /threads/:thread_id/stream/events`.\n */\nexport class ProtocolSseTransportAdapter implements TransportAdapter {\n threadId: string;\n\n readonly apiUrl: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly fetchImpl: typeof fetch;\n\n private readonly defaultHeaders: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly fetchFactory?: () => typeof fetch | Promise<typeof fetch>;\n\n private readonly asyncCaller?: AsyncCaller;\n\n private readonly maxReconnectAttempts: number;\n\n private readonly idleReconnect: IdleReconnectMode | null;\n\n private readonly onReconnect?: ProtocolSseTransportOptions[\"onReconnect\"];\n\n private readonly onConnected?: ProtocolSseTransportOptions[\"onConnected\"];\n\n private readonly reconnectDelayMs: (attempt: number) => number;\n\n private readonly paths?: ProtocolTransportPaths;\n\n private readonly sessionAbortController = new AbortController();\n\n private readonly eventStreams = new Set<AbortController>();\n\n private closed = false;\n\n constructor(options: ProtocolSseTransportOptions) {\n this.fetchImpl = options.fetch ?? fetch;\n this.apiUrl = options.apiUrl;\n this.defaultHeaders = options.defaultHeaders ?? {};\n this.onRequest = options.onRequest;\n this.fetchFactory = options.fetchFactory;\n this.asyncCaller = options.asyncCaller;\n // Custom `fetch` (auth shims, proxies) must keep reconnect enabled — that is\n // the production path for tenant-aware browsers. Tests that need fail-fast\n // mocks should pass `maxReconnectAttempts: 0` (and `idleReconnect: 0`)\n // explicitly rather than relying on `fetch` presence.\n this.maxReconnectAttempts =\n options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;\n // Default to heartbeat-adaptive idle reconnect, which stays dormant\n // unless the server actually emits keep-alive heartbeats. Pass `0` to\n // disable.\n this.idleReconnect = options.idleReconnect ?? DEFAULT_IDLE_RECONNECT;\n this.onReconnect = options.onReconnect;\n this.onConnected = options.onConnected;\n this.reconnectDelayMs = options.reconnectDelayMs ?? reconnectDelayMs;\n this.threadId = options.threadId ?? \"\";\n this.paths = options.paths;\n }\n\n /** {@inheritDoc TransportAdapter.setThreadId} */\n setThreadId(threadId: string): void {\n this.threadId = threadId;\n }\n\n /**\n * Command/stream/state URLs derive from the currently-bound thread so a\n * single adapter can follow {@link setThreadId} re-binds. A fixed\n * `paths.*` string overrides the default and is used as-is.\n */\n private get commandsUrl(): string {\n return resolveProtocolPath(\n this.paths?.commands,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/commands`\n );\n }\n\n private get streamUrl(): string {\n return resolveProtocolPath(\n this.paths?.stream,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/stream/events`\n );\n }\n\n private get stateUrl(): string {\n return resolveProtocolPath(\n this.paths?.state,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/state`\n );\n }\n\n /**\n * Fetch checkpointed thread state for hydration.\n *\n * Uses `GET`, matching `client.threads.getState()` and both LangGraph\n * Platform and Agent Protocol custom backends (`POST` is reserved for\n * `updateState`).\n */\n async getState<StateType = unknown>(): Promise<{\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n } | null> {\n const url = toAbsoluteUrl(this.apiUrl, this.stateUrl);\n let requestInit: RequestInit = {\n method: \"GET\",\n headers: mergeHeaders(this.defaultHeaders, {}),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (response.status === 404) return null;\n if (!response.ok) {\n const error = toError(\n new Error(\n `Thread state request failed: ${response.status} ${response.statusText}`\n )\n ) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n return (await response.json()) as {\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n };\n }\n\n private async resolveFetch(): Promise<typeof fetch> {\n if (this.fetchFactory) {\n return await this.fetchFactory();\n }\n return this.fetchImpl;\n }\n\n /**\n * HTTP/SSE transports have no handshake — connections are made\n * per-command and per-subscription.\n */\n async open(): Promise<void> {\n // no-op\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n const response = await this.request(this.commandsUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(command),\n signal: this.sessionAbortController.signal,\n });\n\n if (response.status === 202 || response.status === 204) {\n return undefined;\n }\n\n const payload = (await response.json()) as unknown;\n if (!isProtocolResponse(payload)) {\n throw new Error(\"Protocol command did not return a valid response.\");\n }\n return payload;\n }\n\n /**\n * WebSocket-style single event stream.\n * For the SSE transport this returns a dummy iterable; real event\n * delivery happens via {@link openEventStream}.\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 openEventStream(params: SubscribeParams): EventStreamHandle {\n if (this.closed) {\n throw new Error(\"Protocol transport is closed.\");\n }\n\n const ac = new AbortController();\n this.eventStreams.add(ac);\n const streamQueue = new AsyncQueue<Message>();\n const streamUrl = this.streamUrl;\n\n let resolveReady!: () => void;\n let rejectReady!: (err: unknown) => void;\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n\n // Honor an explicit caller `since` until the stream has connected\n // once. Do not advance it from observed `seq` values — those are\n // connection-local, and carrying them onto a post-connect reconnect\n // POST filters out the full Redis replay (heartbeats only). Pre-ready\n // retries still send the caller cursor; only after a successful open\n // do reconnects omit `since` and rely on durable `event_id` dedup.\n const initialSince =\n typeof (params as SubscribeParams & { since?: unknown }).since ===\n \"number\"\n ? (params as SubscribeParams & { since: number }).since\n : undefined;\n\n let readySettled = false;\n\n const startStream = async () => {\n let attempt = 0;\n\n while (!ac.signal.aborted && !this.closed) {\n try {\n const response = await this.request(\n streamUrl,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n channels: params.channels,\n ...(params.namespaces ? { namespaces: params.namespaces } : {}),\n ...(params.depth != null ? { depth: params.depth } : {}),\n ...(!readySettled && initialSince != null\n ? { since: initialSince }\n : {}),\n }),\n signal: ac.signal,\n },\n { stream: true }\n );\n\n const contentType = response.headers\n .get(\"content-type\")\n ?.split(\";\")[0];\n if (contentType && !contentType.includes(\"text/event-stream\")) {\n throw new Error(\n `Expected response header Content-Type to contain 'text/event-stream', got '${contentType}'`\n );\n }\n\n await this.onConnected?.({\n kind: attempt === 0 ? \"initial\" : \"reconnected\",\n attempt,\n });\n // A successful (re)connect restores the full retry budget, even\n // if the stream then closes with no events (idle thread, idle\n // timeout). Reset after `onConnected` so it still reports how\n // many attempts this reconnect took.\n attempt = 0;\n\n if (!readySettled) {\n readySettled = true;\n resolveReady();\n }\n\n const readable =\n response.body ??\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n },\n });\n\n // Idle watchdog on the line stream (between byte-line and SSE\n // decoding) so it can reset on any line and recognise `:` keep-alive\n // heartbeats to drive `\"auto\"` mode. On idle it errors the stream,\n // which the catch below treats like any other disconnect and\n // re-opens the SSE without carrying a connection-local `since`.\n const enableIdle =\n this.idleReconnect === \"auto\" ||\n (typeof this.idleReconnect === \"number\" && this.idleReconnect > 0);\n const lines = readable.pipeThrough(BytesLineDecoder());\n const watched = enableIdle\n ? lines.pipeThrough(\n idleReconnectStream({ mode: this.idleReconnect! })\n )\n : lines;\n const stream = watched.pipeThrough(SSEDecoder());\n const iterable = IterableReadableStream.fromReadableStream(stream);\n\n for await (const event of iterable) {\n if (ac.signal.aborted || this.closed) {\n break;\n }\n if (isRecord(event.data)) {\n streamQueue.push(event.data as Message);\n }\n }\n if (\n ac.signal.aborted ||\n this.closed ||\n this.maxReconnectAttempts <= 0\n ) {\n streamQueue.close();\n return;\n }\n // The thread stream is open-ended: the server only ends it when\n // its own upstream consumer died or it is shutting down, so a\n // clean close is a disconnect, not the end of the thread.\n throw new Error(\"Event stream closed by the server\");\n } catch (error) {\n if (ac.signal.aborted || this.closed) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close();\n return;\n }\n // A client error (e.g. 401/403) fails the same way on every retry,\n // so end the stream without using the retry budget.\n if (\n this.maxReconnectAttempts <= 0 ||\n isNonRetryableHttpError(error)\n ) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n attempt += 1;\n if (attempt > this.maxReconnectAttempts) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n const delay = this.reconnectDelayMs(attempt);\n this.onReconnect?.({ attempt, cause: error, delayMs: delay });\n if (delay > 0) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n }\n }\n };\n\n void startStream();\n\n const cleanup = () => {\n this.eventStreams.delete(ac);\n ac.abort();\n streamQueue.close();\n };\n\n return {\n events: {\n [Symbol.asyncIterator]: () => ({\n next: async () => await streamQueue.shift(),\n return: async () => {\n cleanup();\n return { done: true, value: undefined };\n },\n }),\n },\n ready,\n close: cleanup,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.sessionAbortController.abort();\n for (const ac of this.eventStreams) ac.abort();\n this.eventStreams.clear();\n this.queue.close();\n }\n\n private async request(\n path: string,\n init: RequestInit,\n options?: { stream?: boolean }\n ): Promise<Response> {\n const url = toAbsoluteUrl(this.apiUrl, path);\n let requestInit: RequestInit = {\n ...init,\n headers: mergeHeaders(this.defaultHeaders, init.headers),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n // Long-lived SSE event streams must not run through AsyncCaller: its\n // p-queue/p-retry semantics are designed for discrete request/response\n // calls, and wrapping a streaming response stalls the call (and can\n // leak retries). Stream resilience is handled separately by the\n // reconnect loop in `openEventStream`.\n const useAsyncCaller = this.asyncCaller != null && !options?.stream;\n\n const execute = async (): Promise<Response> => {\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (!response.ok) {\n // Reject with the Response so AsyncCaller maps it to HTTPError and\n // applies STATUS_NO_RETRY / retry policy consistently with REST.\n if (useAsyncCaller) {\n throw response;\n }\n let body = \"\";\n let detail = \"\";\n try {\n body = await response.text();\n const parsed = JSON.parse(body);\n if (typeof parsed === \"object\" && parsed != null) {\n detail =\n ((parsed as Record<string, unknown>).message as string) ??\n ((parsed as Record<string, unknown>).error as string) ??\n \"\";\n }\n if (!detail) detail = body;\n } catch {\n // body unreadable or not JSON — fall through\n }\n const message = detail\n ? `Protocol request failed: ${response.status} ${response.statusText} — ${detail}`\n : `Protocol request failed: ${response.status} ${response.statusText}`;\n // Keep the status (like `getState`) so callers can tell an auth\n // failure from a network failure without parsing the message.\n throw Object.assign(new Error(message), {\n status: response.status,\n text: body,\n });\n }\n return response;\n };\n\n try {\n return useAsyncCaller\n ? await this.asyncCaller!.call(execute)\n : await execute();\n } catch (error) {\n throw toError(error);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http.cjs","names":["AsyncQueue","reconnectDelayMs","resolveProtocolPath","encodePathSegment","toAbsoluteUrl","mergeHeaders","toError","isProtocolResponse","BytesLineDecoder","idleReconnectStream","SSEDecoder","IterableReadableStream","isRecord","isNonRetryableHttpError"],"sources":["../../../../src/client/stream/transport/http.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport { encodePathSegment } from \"../../base.js\";\nimport type {\n Message,\n SubscribeParams,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport type { AsyncCaller } from \"../../../utils/async_caller.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n ProtocolSseTransportOptions,\n ProtocolTransportPaths,\n} from \"./types.js\";\nimport type { TransportAdapter, EventStreamHandle } from \"../transport.js\";\nimport {\n toAbsoluteUrl,\n isRecord,\n isNonRetryableHttpError,\n mergeHeaders,\n toError,\n isProtocolResponse,\n resolveProtocolPath,\n} from \"./utils.js\";\nimport { BytesLineDecoder, SSEDecoder } from \"../../../utils/sse.js\";\nimport {\n IterableReadableStream,\n idleReconnectStream,\n type IdleReconnectMode,\n} from \"../../../utils/stream.js\";\nimport {\n DEFAULT_MAX_RECONNECT_ATTEMPTS,\n reconnectDelayMs,\n} from \"../../../utils/reconnect.js\";\nimport { DEFAULT_IDLE_RECONNECT } from \"../../../utils/stream.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over HTTP\n * commands plus SSE event streams. Bound to a `threadId` at construction\n * or later via {@link setThreadId}; request URLs derive from the\n * currently-bound thread. Each {@link openEventStream} call opens an\n * independent filtered SSE connection via\n * `POST /threads/:thread_id/stream/events`.\n */\nexport class ProtocolSseTransportAdapter implements TransportAdapter {\n threadId: string;\n\n readonly apiUrl: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly fetchImpl: typeof fetch;\n\n private readonly defaultHeaders: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly fetchFactory?: () => typeof fetch | Promise<typeof fetch>;\n\n private readonly asyncCaller?: AsyncCaller;\n\n private readonly maxReconnectAttempts: number;\n\n private readonly idleReconnect: IdleReconnectMode | null;\n\n private readonly onReconnect?: ProtocolSseTransportOptions[\"onReconnect\"];\n\n private readonly onConnected?: ProtocolSseTransportOptions[\"onConnected\"];\n\n private readonly reconnectDelayMs: (attempt: number) => number;\n\n private readonly paths?: ProtocolTransportPaths;\n\n private readonly sessionAbortController = new AbortController();\n\n private readonly eventStreams = new Set<AbortController>();\n\n private closed = false;\n\n constructor(options: ProtocolSseTransportOptions) {\n this.fetchImpl = options.fetch ?? fetch;\n this.apiUrl = options.apiUrl;\n this.defaultHeaders = options.defaultHeaders ?? {};\n this.onRequest = options.onRequest;\n this.fetchFactory = options.fetchFactory;\n this.asyncCaller = options.asyncCaller;\n // Custom `fetch` (auth shims, proxies) must keep reconnect enabled — that is\n // the production path for tenant-aware browsers. Tests that need fail-fast\n // mocks should pass `maxReconnectAttempts: 0` (and `idleReconnect: 0`)\n // explicitly rather than relying on `fetch` presence.\n this.maxReconnectAttempts =\n options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;\n // Default to heartbeat-adaptive idle reconnect, which stays dormant\n // unless the server actually emits keep-alive heartbeats. Pass `0` to\n // disable.\n this.idleReconnect = options.idleReconnect ?? DEFAULT_IDLE_RECONNECT;\n this.onReconnect = options.onReconnect;\n this.onConnected = options.onConnected;\n this.reconnectDelayMs = options.reconnectDelayMs ?? reconnectDelayMs;\n this.threadId = options.threadId ?? \"\";\n this.paths = options.paths;\n }\n\n /** {@inheritDoc TransportAdapter.setThreadId} */\n setThreadId(threadId: string): void {\n this.threadId = threadId;\n }\n\n /**\n * Command/stream/state URLs derive from the currently-bound thread so a\n * single adapter can follow {@link setThreadId} re-binds. A fixed\n * `paths.*` string overrides the default and is used as-is.\n */\n private get commandsUrl(): string {\n return resolveProtocolPath(\n this.paths?.commands,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/commands`\n );\n }\n\n private get streamUrl(): string {\n return resolveProtocolPath(\n this.paths?.stream,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/stream/events`\n );\n }\n\n private get stateUrl(): string {\n return resolveProtocolPath(\n this.paths?.state,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/state`\n );\n }\n\n /**\n * Fetch checkpointed thread state for hydration.\n *\n * Uses `GET`, matching `client.threads.getState()` and both LangGraph\n * Platform and Agent Protocol custom backends (`POST` is reserved for\n * `updateState`).\n */\n async getState<StateType = unknown>(): Promise<{\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n } | null> {\n const url = toAbsoluteUrl(this.apiUrl, this.stateUrl);\n let requestInit: RequestInit = {\n method: \"GET\",\n headers: mergeHeaders(this.defaultHeaders, {}),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (response.status === 404) return null;\n if (!response.ok) {\n const error = toError(\n new Error(\n `Thread state request failed: ${response.status} ${response.statusText}`\n )\n ) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n return (await response.json()) as {\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n };\n }\n\n private async resolveFetch(): Promise<typeof fetch> {\n if (this.fetchFactory) {\n return await this.fetchFactory();\n }\n return this.fetchImpl;\n }\n\n /**\n * HTTP/SSE transports have no handshake — connections are made\n * per-command and per-subscription.\n */\n async open(): Promise<void> {\n // no-op\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n const response = await this.request(this.commandsUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(command),\n signal: this.sessionAbortController.signal,\n });\n\n if (response.status === 202 || response.status === 204) {\n return undefined;\n }\n\n const payload = (await response.json()) as unknown;\n if (!isProtocolResponse(payload)) {\n throw new Error(\"Protocol command did not return a valid response.\");\n }\n return payload;\n }\n\n /**\n * WebSocket-style single event stream.\n * For the SSE transport this returns a dummy iterable; real event\n * delivery happens via {@link openEventStream}.\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 openEventStream(params: SubscribeParams): EventStreamHandle {\n if (this.closed) {\n throw new Error(\"Protocol transport is closed.\");\n }\n\n const ac = new AbortController();\n this.eventStreams.add(ac);\n const streamQueue = new AsyncQueue<Message>();\n const streamUrl = this.streamUrl;\n\n let resolveReady!: () => void;\n let rejectReady!: (err: unknown) => void;\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n\n // Honor an explicit caller `since` until the stream has connected\n // once. Do not advance it from observed `seq` values — those are\n // connection-local, and carrying them onto a post-connect reconnect\n // POST filters out the full Redis replay (heartbeats only). Pre-ready\n // retries still send the caller cursor; only after a successful open\n // do reconnects omit `since` and rely on durable `event_id` dedup.\n const initialSince =\n typeof (params as SubscribeParams & { since?: unknown }).since ===\n \"number\"\n ? (params as SubscribeParams & { since: number }).since\n : undefined;\n\n let readySettled = false;\n\n const startStream = async () => {\n let attempt = 0;\n\n while (!ac.signal.aborted && !this.closed) {\n try {\n const response = await this.request(\n streamUrl,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n channels: params.channels,\n ...(params.namespaces ? { namespaces: params.namespaces } : {}),\n ...(params.depth != null ? { depth: params.depth } : {}),\n ...(!readySettled && initialSince != null\n ? { since: initialSince }\n : {}),\n }),\n signal: ac.signal,\n },\n { stream: true }\n );\n\n const contentType = response.headers\n .get(\"content-type\")\n ?.split(\";\")[0];\n if (contentType && !contentType.includes(\"text/event-stream\")) {\n throw new Error(\n `Expected response header Content-Type to contain 'text/event-stream', got '${contentType}'`\n );\n }\n\n await this.onConnected?.({\n kind: attempt === 0 ? \"initial\" : \"reconnected\",\n attempt,\n });\n // A successful (re)connect restores the full retry budget, even\n // if the stream then closes with no events (idle thread, idle\n // timeout). Reset after `onConnected` so it still reports how\n // many attempts this reconnect took.\n attempt = 0;\n\n if (!readySettled) {\n readySettled = true;\n resolveReady();\n }\n\n const readable =\n response.body ??\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n },\n });\n\n // Idle watchdog on the line stream (between byte-line and SSE\n // decoding) so it can reset on any line and recognise `:` keep-alive\n // heartbeats to drive `\"auto\"` mode. On idle it errors the stream,\n // which the catch below treats like any other disconnect and\n // re-opens the SSE without carrying a connection-local `since`.\n const enableIdle =\n this.idleReconnect === \"auto\" ||\n (typeof this.idleReconnect === \"number\" && this.idleReconnect > 0);\n const lines = readable.pipeThrough(BytesLineDecoder());\n const watched = enableIdle\n ? lines.pipeThrough(\n idleReconnectStream({ mode: this.idleReconnect! })\n )\n : lines;\n const stream = watched.pipeThrough(SSEDecoder());\n const iterable = IterableReadableStream.fromReadableStream(stream);\n\n for await (const event of iterable) {\n if (ac.signal.aborted || this.closed) {\n break;\n }\n if (isRecord(event.data)) {\n streamQueue.push(event.data as Message);\n }\n }\n if (\n ac.signal.aborted ||\n this.closed ||\n this.maxReconnectAttempts <= 0\n ) {\n streamQueue.close();\n return;\n }\n // The thread stream is open-ended: the server only ends it when\n // its own upstream consumer died or it is shutting down, so a\n // clean close is a disconnect, not the end of the thread.\n throw new Error(\"Event stream closed by the server\");\n } catch (error) {\n if (ac.signal.aborted || this.closed) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close();\n return;\n }\n // A client error (e.g. 401/403) fails the same way on every retry,\n // so end the stream without using the retry budget.\n if (\n this.maxReconnectAttempts <= 0 ||\n isNonRetryableHttpError(error)\n ) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n attempt += 1;\n if (attempt > this.maxReconnectAttempts) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n const delay = this.reconnectDelayMs(attempt);\n this.onReconnect?.({ attempt, cause: error, delayMs: delay });\n if (delay > 0) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n }\n }\n };\n\n void startStream();\n\n const cleanup = () => {\n this.eventStreams.delete(ac);\n ac.abort();\n streamQueue.close();\n };\n\n return {\n events: {\n [Symbol.asyncIterator]: () => ({\n next: async () => await streamQueue.shift(),\n return: async () => {\n cleanup();\n return { done: true, value: undefined };\n },\n }),\n },\n ready,\n close: cleanup,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.sessionAbortController.abort();\n for (const ac of this.eventStreams) ac.abort();\n this.eventStreams.clear();\n this.queue.close();\n }\n\n private async request(\n path: string,\n init: RequestInit,\n options?: { stream?: boolean }\n ): Promise<Response> {\n const url = toAbsoluteUrl(this.apiUrl, path);\n let requestInit: RequestInit = {\n ...init,\n headers: mergeHeaders(this.defaultHeaders, init.headers),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n // Long-lived SSE event streams must not run through AsyncCaller: its\n // p-queue/p-retry semantics are designed for discrete request/response\n // calls, and wrapping a streaming response stalls the call (and can\n // leak retries). Stream resilience is handled separately by the\n // reconnect loop in `openEventStream`.\n const useAsyncCaller = this.asyncCaller != null && !options?.stream;\n\n const execute = async (): Promise<Response> => {\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (!response.ok) {\n // Reject with the Response so AsyncCaller maps it to HTTPError and\n // applies STATUS_NO_RETRY / retry policy consistently with REST.\n if (useAsyncCaller) {\n throw response;\n }\n let body = \"\";\n let detail = \"\";\n try {\n body = await response.text();\n const parsed = JSON.parse(body);\n if (typeof parsed === \"object\" && parsed != null) {\n detail =\n ((parsed as Record<string, unknown>).message as string) ??\n ((parsed as Record<string, unknown>).error as string) ??\n \"\";\n }\n if (!detail) detail = body;\n } catch {\n // body unreadable or not JSON — fall through\n }\n const message = detail\n ? `Protocol request failed: ${response.status} ${response.statusText} — ${detail}`\n : `Protocol request failed: ${response.status} ${response.statusText}`;\n // Keep the status (like `getState`) so callers can tell an auth\n // failure from a network failure without parsing the message.\n throw Object.assign(new Error(message), {\n status: response.status,\n text: body,\n });\n }\n return response;\n };\n\n try {\n return useAsyncCaller\n ? await this.asyncCaller!.call(execute)\n : await execute();\n } catch (error) {\n throw toError(error);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AA+CA,IAAa,8BAAb,MAAqE;CACnE;CAEA;CAEA,QAAyB,IAAIA,cAAAA,YAAqB;CAElD;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA,yBAA0C,IAAI,iBAAiB;CAE/D,+BAAgC,IAAI,KAAsB;CAE1D,SAAiB;CAEjB,YAAY,SAAsC;AAChD,OAAK,YAAY,QAAQ,SAAS;AAClC,OAAK,SAAS,QAAQ;AACtB,OAAK,iBAAiB,QAAQ,kBAAkB,EAAE;AAClD,OAAK,YAAY,QAAQ;AACzB,OAAK,eAAe,QAAQ;AAC5B,OAAK,cAAc,QAAQ;AAK3B,OAAK,uBACH,QAAQ,wBAAA;AAIV,OAAK,gBAAgB,QAAQ,iBAAA;AAC7B,OAAK,cAAc,QAAQ;AAC3B,OAAK,cAAc,QAAQ;AAC3B,OAAK,mBAAmB,QAAQ,oBAAoBC,kBAAAA;AACpD,OAAK,WAAW,QAAQ,YAAY;AACpC,OAAK,QAAQ,QAAQ;;;CAIvB,YAAY,UAAwB;AAClC,OAAK,WAAW;;;;;;;CAQlB,IAAY,cAAsB;AAChC,SAAOC,cAAAA,oBACL,KAAK,OAAO,UACZ,KAAK,WACJ,OAAO,YAAYC,aAAAA,kBAAkB,GAAG,CAAC,WAC3C;;CAGH,IAAY,YAAoB;AAC9B,SAAOD,cAAAA,oBACL,KAAK,OAAO,QACZ,KAAK,WACJ,OAAO,YAAYC,aAAAA,kBAAkB,GAAG,CAAC,gBAC3C;;CAGH,IAAY,WAAmB;AAC7B,SAAOD,cAAAA,oBACL,KAAK,OAAO,OACZ,KAAK,WACJ,OAAO,YAAYC,aAAAA,kBAAkB,GAAG,CAAC,QAC3C;;;;;;;;;CAUH,MAAM,WAOI;EACR,MAAM,MAAMC,cAAAA,cAAc,KAAK,QAAQ,KAAK,SAAS;EACrD,IAAI,cAA2B;GAC7B,QAAQ;GACR,SAASC,cAAAA,aAAa,KAAK,gBAAgB,EAAE,CAAC;GAC/C;AAED,MAAI,KAAK,UACP,eAAc,MAAM,KAAK,UAAU,KAAK,YAAY;EAItD,MAAM,WAAW,OADC,MAAM,KAAK,cAAc,EACV,IAAI,UAAU,EAAE,YAAY;AAC7D,MAAI,SAAS,WAAW,IAAK,QAAO;AACpC,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,QAAQC,cAAAA,wBACZ,IAAI,MACF,gCAAgC,SAAS,OAAO,GAAG,SAAS,aAC7D,CACF;AACD,SAAM,SAAS,SAAS;AACxB,SAAM;;AAGR,SAAQ,MAAM,SAAS,MAAM;;CAU/B,MAAc,eAAsC;AAClD,MAAI,KAAK,aACP,QAAO,MAAM,KAAK,cAAc;AAElC,SAAO,KAAK;;;;;;CAOd,MAAM,OAAsB;CAI5B,MAAM,KACJ,SACiD;EACjD,MAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,aAAa;GACpD,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU,QAAQ;GAC7B,QAAQ,KAAK,uBAAuB;GACrC,CAAC;AAEF,MAAI,SAAS,WAAW,OAAO,SAAS,WAAW,IACjD;EAGF,MAAM,UAAW,MAAM,SAAS,MAAM;AACtC,MAAI,CAACC,cAAAA,mBAAmB,QAAQ,CAC9B,OAAM,IAAI,MAAM,oDAAoD;AAEtE,SAAO;;;;;;;CAQT,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,gBAAgB,QAA4C;AAC1D,MAAI,KAAK,OACP,OAAM,IAAI,MAAM,gCAAgC;EAGlD,MAAM,KAAK,IAAI,iBAAiB;AAChC,OAAK,aAAa,IAAI,GAAG;EACzB,MAAM,cAAc,IAAIP,cAAAA,YAAqB;EAC7C,MAAM,YAAY,KAAK;EAEvB,IAAI;EACJ,IAAI;EACJ,MAAM,QAAQ,IAAI,SAAe,SAAS,WAAW;AACnD,kBAAe;AACf,iBAAc;IACd;EAQF,MAAM,eACJ,OAAQ,OAAiD,UACzD,WACK,OAA+C,QAChD,KAAA;EAEN,IAAI,eAAe;EAEnB,MAAM,cAAc,YAAY;GAC9B,IAAI,UAAU;AAEd,UAAO,CAAC,GAAG,OAAO,WAAW,CAAC,KAAK,OACjC,KAAI;IACF,MAAM,WAAW,MAAM,KAAK,QAC1B,WACA;KACE,QAAQ;KACR,SAAS;MACP,gBAAgB;MAChB,QAAQ;MACT;KACD,MAAM,KAAK,UAAU;MACnB,UAAU,OAAO;MACjB,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,YAAY,GAAG,EAAE;MAC9D,GAAI,OAAO,SAAS,OAAO,EAAE,OAAO,OAAO,OAAO,GAAG,EAAE;MACvD,GAAI,CAAC,gBAAgB,gBAAgB,OACjC,EAAE,OAAO,cAAc,GACvB,EAAE;MACP,CAAC;KACF,QAAQ,GAAG;KACZ,EACD,EAAE,QAAQ,MAAM,CACjB;IAED,MAAM,cAAc,SAAS,QAC1B,IAAI,eAAe,EAClB,MAAM,IAAI,CAAC;AACf,QAAI,eAAe,CAAC,YAAY,SAAS,oBAAoB,CAC3D,OAAM,IAAI,MACR,8EAA8E,YAAY,GAC3F;AAGH,UAAM,KAAK,cAAc;KACvB,MAAM,YAAY,IAAI,YAAY;KAClC;KACD,CAAC;AAKF,cAAU;AAEV,QAAI,CAAC,cAAc;AACjB,oBAAe;AACf,mBAAc;;IAGhB,MAAM,WACJ,SAAS,QACT,IAAI,eAA2B,EAC7B,MAAM,YAAY;AAChB,gBAAW,OAAO;OAErB,CAAC;IAOJ,MAAM,aACJ,KAAK,kBAAkB,UACtB,OAAO,KAAK,kBAAkB,YAAY,KAAK,gBAAgB;IAClE,MAAM,QAAQ,SAAS,YAAYQ,YAAAA,kBAAkB,CAAC;IAMtD,MAAM,UALU,aACZ,MAAM,YACJC,eAAAA,oBAAoB,EAAE,MAAM,KAAK,eAAgB,CAAC,CACnD,GACD,OACmB,YAAYC,YAAAA,YAAY,CAAC;IAChD,MAAM,WAAWC,eAAAA,uBAAuB,mBAAmB,OAAO;AAElE,eAAW,MAAM,SAAS,UAAU;AAClC,SAAI,GAAG,OAAO,WAAW,KAAK,OAC5B;AAEF,SAAIC,cAAAA,SAAS,MAAM,KAAK,CACtB,aAAY,KAAK,MAAM,KAAgB;;AAG3C,QACE,GAAG,OAAO,WACV,KAAK,UACL,KAAK,wBAAwB,GAC7B;AACA,iBAAY,OAAO;AACnB;;AAKF,UAAM,IAAI,MAAM,oCAAoC;YAC7C,OAAO;AACd,QAAI,GAAG,OAAO,WAAW,KAAK,QAAQ;AACpC,SAAI,CAAC,aACH,aAAY,MAAM;AAEpB,iBAAY,OAAO;AACnB;;AAIF,QACE,KAAK,wBAAwB,KAC7BC,cAAAA,wBAAwB,MAAM,EAC9B;AACA,SAAI,CAAC,aACH,aAAY,MAAM;AAEpB,iBAAY,MAAMP,cAAAA,QAAQ,MAAM,CAAC;AACjC;;AAEF,eAAW;AACX,QAAI,UAAU,KAAK,sBAAsB;AACvC,SAAI,CAAC,aACH,aAAY,MAAM;AAEpB,iBAAY,MAAMA,cAAAA,QAAQ,MAAM,CAAC;AACjC;;IAEF,MAAM,QAAQ,KAAK,iBAAiB,QAAQ;AAC5C,SAAK,cAAc;KAAE;KAAS,OAAO;KAAO,SAAS;KAAO,CAAC;AAC7D,QAAI,QAAQ,EACV,OAAM,IAAI,SAAe,YAAY;AACnC,gBAAW,SAAS,MAAM;MAC1B;;;AAML,eAAa;EAElB,MAAM,gBAAgB;AACpB,QAAK,aAAa,OAAO,GAAG;AAC5B,MAAG,OAAO;AACV,eAAY,OAAO;;AAGrB,SAAO;GACL,QAAQ,GACL,OAAO,uBAAuB;IAC7B,MAAM,YAAY,MAAM,YAAY,OAAO;IAC3C,QAAQ,YAAY;AAClB,cAAS;AACT,YAAO;MAAE,MAAM;MAAM,OAAO,KAAA;MAAW;;IAE1C,GACF;GACD;GACA,OAAO;GACR;;CAGH,MAAM,QAAuB;AAC3B,MAAI,KAAK,OACP;AAEF,OAAK,SAAS;AACd,OAAK,uBAAuB,OAAO;AACnC,OAAK,MAAM,MAAM,KAAK,aAAc,IAAG,OAAO;AAC9C,OAAK,aAAa,OAAO;AACzB,OAAK,MAAM,OAAO;;CAGpB,MAAc,QACZ,MACA,MACA,SACmB;EACnB,MAAM,MAAMF,cAAAA,cAAc,KAAK,QAAQ,KAAK;EAC5C,IAAI,cAA2B;GAC7B,GAAG;GACH,SAASC,cAAAA,aAAa,KAAK,gBAAgB,KAAK,QAAQ;GACzD;AAED,MAAI,KAAK,UACP,eAAc,MAAM,KAAK,UAAU,KAAK,YAAY;EAQtD,MAAM,iBAAiB,KAAK,eAAe,QAAQ,CAAC,SAAS;EAE7D,MAAM,UAAU,YAA+B;GAE7C,MAAM,WAAW,OADC,MAAM,KAAK,cAAc,EACV,IAAI,UAAU,EAAE,YAAY;AAC7D,OAAI,CAAC,SAAS,IAAI;AAGhB,QAAI,eACF,OAAM;IAER,IAAI,OAAO;IACX,IAAI,SAAS;AACb,QAAI;AACF,YAAO,MAAM,SAAS,MAAM;KAC5B,MAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,SAAI,OAAO,WAAW,YAAY,UAAU,KAC1C,UACI,OAAmC,WACnC,OAAmC,SACrC;AAEJ,SAAI,CAAC,OAAQ,UAAS;YAChB;IAGR,MAAM,UAAU,SACZ,4BAA4B,SAAS,OAAO,GAAG,SAAS,WAAW,KAAK,WACxE,4BAA4B,SAAS,OAAO,GAAG,SAAS;AAG5D,UAAM,OAAO,OAAO,IAAI,MAAM,QAAQ,EAAE;KACtC,QAAQ,SAAS;KACjB,MAAM;KACP,CAAC;;AAEJ,UAAO;;AAGT,MAAI;AACF,UAAO,iBACH,MAAM,KAAK,YAAa,KAAK,QAAQ,GACrC,MAAM,SAAS;WACZ,OAAO;AACd,SAAMC,cAAAA,QAAQ,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.cts","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"mappings":";;;;;;;AA0DA;;;;;;cAAa,2BAAA,YAAuC,gBAAA;EAClD,QAAA;EAAA,SAES,MAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,YAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,oBAAA;EAAA,iBAEA,aAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,KAAA;EAAA,iBAEA,sBAAA;EAAA,iBAEA,YAAA;EAAA,QAET,MAAA;EAER,WAAA,CAAY,OAAA,EAAS,2BAAA;EAxBJ;EAiDjB,WAAA,CAAY,QAAA;EA7CK;;;;;EAAA,YAsDL,WAAA,CAAA;EAAA,YAQA,SAAA,CAAA;EAAA,YAQA,QAAA,CAAA;EAtDK;;;;;;;EAqEX,QAAA,qBAAA,CAAA,GAAiC,OAAA;IACrC,MAAA,EAAQ,SAAA;IACR,IAAA;IACA,KAAA;IACA,QAAA;IACA,UAAA;MAAe,aAAA;IAAA;IACf,iBAAA;MAAsB,aAAA;IAAA;EAAA;EAAA,QAmCV,YAAA;EAnCZ;;;;EA8CI,IAAA,CAAA,GAAQ,OAAA;EAIR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EADlB;;;;;EAyBX,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAaxB,eAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EA4LpC,KAAA,CAAA,GAAS,OAAA;EAAA,QAWD,OAAA;AAAA"}
1
+ {"version":3,"file":"http.d.cts","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"mappings":";;;;;;;AA+CA;;;;;;cAAa,2BAAA,YAAuC,gBAAA;EAClD,QAAA;EAAA,SAES,MAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,YAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,oBAAA;EAAA,iBAEA,aAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,KAAA;EAAA,iBAEA,sBAAA;EAAA,iBAEA,YAAA;EAAA,QAET,MAAA;EAER,WAAA,CAAY,OAAA,EAAS,2BAAA;EAxBJ;EAiDjB,WAAA,CAAY,QAAA;EA7CK;;;;;EAAA,YAsDL,WAAA,CAAA;EAAA,YAQA,SAAA,CAAA;EAAA,YAQA,QAAA,CAAA;EAtDK;;;;;;;EAqEX,QAAA,qBAAA,CAAA,GAAiC,OAAA;IACrC,MAAA,EAAQ,SAAA;IACR,IAAA;IACA,KAAA;IACA,QAAA;IACA,UAAA;MAAe,aAAA;IAAA;IACf,iBAAA;MAAsB,aAAA;IAAA;EAAA;EAAA,QAmCV,YAAA;EAnCZ;;;;EA8CI,IAAA,CAAA,GAAQ,OAAA;EAIR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EADlB;;;;;EAyBX,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAaxB,eAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EA4LpC,KAAA,CAAA,GAAS,OAAA;EAAA,QAWD,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"mappings":";;;;;;;AA0DA;;;;;;cAAa,2BAAA,YAAuC,gBAAA;EAClD,QAAA;EAAA,SAES,MAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,YAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,oBAAA;EAAA,iBAEA,aAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,KAAA;EAAA,iBAEA,sBAAA;EAAA,iBAEA,YAAA;EAAA,QAET,MAAA;EAER,WAAA,CAAY,OAAA,EAAS,2BAAA;EAxBJ;EAiDjB,WAAA,CAAY,QAAA;EA7CK;;;;;EAAA,YAsDL,WAAA,CAAA;EAAA,YAQA,SAAA,CAAA;EAAA,YAQA,QAAA,CAAA;EAtDK;;;;;;;EAqEX,QAAA,qBAAA,CAAA,GAAiC,OAAA;IACrC,MAAA,EAAQ,SAAA;IACR,IAAA;IACA,KAAA;IACA,QAAA;IACA,UAAA;MAAe,aAAA;IAAA;IACf,iBAAA;MAAsB,aAAA;IAAA;EAAA;EAAA,QAmCV,YAAA;EAnCZ;;;;EA8CI,IAAA,CAAA,GAAQ,OAAA;EAIR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EADlB;;;;;EAyBX,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAaxB,eAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EA4LpC,KAAA,CAAA,GAAS,OAAA;EAAA,QAWD,OAAA;AAAA"}
1
+ {"version":3,"file":"http.d.ts","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"mappings":";;;;;;;AA+CA;;;;;;cAAa,2BAAA,YAAuC,gBAAA;EAClD,QAAA;EAAA,SAES,MAAA;EAAA,iBAEQ,KAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,cAAA;EAAA,iBAEA,SAAA;EAAA,iBAEA,YAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,oBAAA;EAAA,iBAEA,aAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,WAAA;EAAA,iBAEA,gBAAA;EAAA,iBAEA,KAAA;EAAA,iBAEA,sBAAA;EAAA,iBAEA,YAAA;EAAA,QAET,MAAA;EAER,WAAA,CAAY,OAAA,EAAS,2BAAA;EAxBJ;EAiDjB,WAAA,CAAY,QAAA;EA7CK;;;;;EAAA,YAsDL,WAAA,CAAA;EAAA,YAQA,SAAA,CAAA;EAAA,YAQA,QAAA,CAAA;EAtDK;;;;;;;EAqEX,QAAA,qBAAA,CAAA,GAAiC,OAAA;IACrC,MAAA,EAAQ,SAAA;IACR,IAAA;IACA,KAAA;IACA,QAAA;IACA,UAAA;MAAe,aAAA;IAAA;IACf,iBAAA;MAAsB,aAAA;IAAA;EAAA;EAAA,QAmCV,YAAA;EAnCZ;;;;EA8CI,IAAA,CAAA,GAAQ,OAAA;EAIR,IAAA,CACJ,OAAA,EAAS,OAAA,GACR,OAAA,CAAQ,eAAA,GAAkB,aAAA;EADlB;;;;;EAyBX,MAAA,CAAA,GAAU,aAAA,CAAc,OAAA;EAaxB,eAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,iBAAA;EA4LpC,KAAA,CAAA,GAAS,OAAA;EAAA,QAWD,OAAA;AAAA"}
@@ -3,20 +3,9 @@ import { reconnectDelayMs } from "../../../utils/reconnect.js";
3
3
  import { IterableReadableStream, idleReconnectStream } from "../../../utils/stream.js";
4
4
  import { encodePathSegment } from "../../base.js";
5
5
  import { AsyncQueue } from "./queue.js";
6
- import { isProtocolResponse, isRecord, mergeHeaders, resolveProtocolPath, toAbsoluteUrl, toError } from "./utils.js";
6
+ import { isNonRetryableHttpError, isProtocolResponse, isRecord, mergeHeaders, resolveProtocolPath, toAbsoluteUrl, toError } from "./utils.js";
7
7
  //#region src/client/stream/transport/http.ts
8
8
  /**
9
- * True for a 4xx HTTP error other than 408 (Request Timeout) and 429
10
- * (Too Many Requests): retrying the same request cannot succeed. Errors
11
- * with no `status` (network errors, idle timeouts, server closes) stay
12
- * retryable.
13
- */
14
- function isNonRetryableHttpError(error) {
15
- if (!isRecord(error) || typeof error.status !== "number") return false;
16
- const { status } = error;
17
- return status >= 400 && status < 500 && status !== 408 && status !== 429;
18
- }
19
- /**
20
9
  * Transport adapter that speaks the thread-centric protocol over HTTP
21
10
  * commands plus SSE event streams. Bound to a `threadId` at construction
22
11
  * or later via {@link setThreadId}; request URLs derive from the
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport { encodePathSegment } from \"../../base.js\";\nimport type {\n Message,\n SubscribeParams,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport type { AsyncCaller } from \"../../../utils/async_caller.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n ProtocolSseTransportOptions,\n ProtocolTransportPaths,\n} from \"./types.js\";\nimport type { TransportAdapter, EventStreamHandle } from \"../transport.js\";\nimport {\n toAbsoluteUrl,\n isRecord,\n mergeHeaders,\n toError,\n isProtocolResponse,\n resolveProtocolPath,\n} from \"./utils.js\";\nimport { BytesLineDecoder, SSEDecoder } from \"../../../utils/sse.js\";\nimport {\n IterableReadableStream,\n idleReconnectStream,\n type IdleReconnectMode,\n} from \"../../../utils/stream.js\";\nimport {\n DEFAULT_MAX_RECONNECT_ATTEMPTS,\n reconnectDelayMs,\n} from \"../../../utils/reconnect.js\";\nimport { DEFAULT_IDLE_RECONNECT } from \"../../../utils/stream.js\";\n\n/**\n * True for a 4xx HTTP error other than 408 (Request Timeout) and 429\n * (Too Many Requests): retrying the same request cannot succeed. Errors\n * with no `status` (network errors, idle timeouts, server closes) stay\n * retryable.\n */\nfunction isNonRetryableHttpError(error: unknown): boolean {\n if (!isRecord(error) || typeof error.status !== \"number\") return false;\n const { status } = error;\n return status >= 400 && status < 500 && status !== 408 && status !== 429;\n}\n\n/**\n * Transport adapter that speaks the thread-centric protocol over HTTP\n * commands plus SSE event streams. Bound to a `threadId` at construction\n * or later via {@link setThreadId}; request URLs derive from the\n * currently-bound thread. Each {@link openEventStream} call opens an\n * independent filtered SSE connection via\n * `POST /threads/:thread_id/stream/events`.\n */\nexport class ProtocolSseTransportAdapter implements TransportAdapter {\n threadId: string;\n\n readonly apiUrl: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly fetchImpl: typeof fetch;\n\n private readonly defaultHeaders: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly fetchFactory?: () => typeof fetch | Promise<typeof fetch>;\n\n private readonly asyncCaller?: AsyncCaller;\n\n private readonly maxReconnectAttempts: number;\n\n private readonly idleReconnect: IdleReconnectMode | null;\n\n private readonly onReconnect?: ProtocolSseTransportOptions[\"onReconnect\"];\n\n private readonly onConnected?: ProtocolSseTransportOptions[\"onConnected\"];\n\n private readonly reconnectDelayMs: (attempt: number) => number;\n\n private readonly paths?: ProtocolTransportPaths;\n\n private readonly sessionAbortController = new AbortController();\n\n private readonly eventStreams = new Set<AbortController>();\n\n private closed = false;\n\n constructor(options: ProtocolSseTransportOptions) {\n this.fetchImpl = options.fetch ?? fetch;\n this.apiUrl = options.apiUrl;\n this.defaultHeaders = options.defaultHeaders ?? {};\n this.onRequest = options.onRequest;\n this.fetchFactory = options.fetchFactory;\n this.asyncCaller = options.asyncCaller;\n // Custom `fetch` (auth shims, proxies) must keep reconnect enabled — that is\n // the production path for tenant-aware browsers. Tests that need fail-fast\n // mocks should pass `maxReconnectAttempts: 0` (and `idleReconnect: 0`)\n // explicitly rather than relying on `fetch` presence.\n this.maxReconnectAttempts =\n options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;\n // Default to heartbeat-adaptive idle reconnect, which stays dormant\n // unless the server actually emits keep-alive heartbeats. Pass `0` to\n // disable.\n this.idleReconnect = options.idleReconnect ?? DEFAULT_IDLE_RECONNECT;\n this.onReconnect = options.onReconnect;\n this.onConnected = options.onConnected;\n this.reconnectDelayMs = options.reconnectDelayMs ?? reconnectDelayMs;\n this.threadId = options.threadId ?? \"\";\n this.paths = options.paths;\n }\n\n /** {@inheritDoc TransportAdapter.setThreadId} */\n setThreadId(threadId: string): void {\n this.threadId = threadId;\n }\n\n /**\n * Command/stream/state URLs derive from the currently-bound thread so a\n * single adapter can follow {@link setThreadId} re-binds. A fixed\n * `paths.*` string overrides the default and is used as-is.\n */\n private get commandsUrl(): string {\n return resolveProtocolPath(\n this.paths?.commands,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/commands`\n );\n }\n\n private get streamUrl(): string {\n return resolveProtocolPath(\n this.paths?.stream,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/stream/events`\n );\n }\n\n private get stateUrl(): string {\n return resolveProtocolPath(\n this.paths?.state,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/state`\n );\n }\n\n /**\n * Fetch checkpointed thread state for hydration.\n *\n * Uses `GET`, matching `client.threads.getState()` and both LangGraph\n * Platform and Agent Protocol custom backends (`POST` is reserved for\n * `updateState`).\n */\n async getState<StateType = unknown>(): Promise<{\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n } | null> {\n const url = toAbsoluteUrl(this.apiUrl, this.stateUrl);\n let requestInit: RequestInit = {\n method: \"GET\",\n headers: mergeHeaders(this.defaultHeaders, {}),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (response.status === 404) return null;\n if (!response.ok) {\n const error = toError(\n new Error(\n `Thread state request failed: ${response.status} ${response.statusText}`\n )\n ) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n return (await response.json()) as {\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n };\n }\n\n private async resolveFetch(): Promise<typeof fetch> {\n if (this.fetchFactory) {\n return await this.fetchFactory();\n }\n return this.fetchImpl;\n }\n\n /**\n * HTTP/SSE transports have no handshake — connections are made\n * per-command and per-subscription.\n */\n async open(): Promise<void> {\n // no-op\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n const response = await this.request(this.commandsUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(command),\n signal: this.sessionAbortController.signal,\n });\n\n if (response.status === 202 || response.status === 204) {\n return undefined;\n }\n\n const payload = (await response.json()) as unknown;\n if (!isProtocolResponse(payload)) {\n throw new Error(\"Protocol command did not return a valid response.\");\n }\n return payload;\n }\n\n /**\n * WebSocket-style single event stream.\n * For the SSE transport this returns a dummy iterable; real event\n * delivery happens via {@link openEventStream}.\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 openEventStream(params: SubscribeParams): EventStreamHandle {\n if (this.closed) {\n throw new Error(\"Protocol transport is closed.\");\n }\n\n const ac = new AbortController();\n this.eventStreams.add(ac);\n const streamQueue = new AsyncQueue<Message>();\n const streamUrl = this.streamUrl;\n\n let resolveReady!: () => void;\n let rejectReady!: (err: unknown) => void;\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n\n // Honor an explicit caller `since` until the stream has connected\n // once. Do not advance it from observed `seq` values — those are\n // connection-local, and carrying them onto a post-connect reconnect\n // POST filters out the full Redis replay (heartbeats only). Pre-ready\n // retries still send the caller cursor; only after a successful open\n // do reconnects omit `since` and rely on durable `event_id` dedup.\n const initialSince =\n typeof (params as SubscribeParams & { since?: unknown }).since ===\n \"number\"\n ? (params as SubscribeParams & { since: number }).since\n : undefined;\n\n let readySettled = false;\n\n const startStream = async () => {\n let attempt = 0;\n\n while (!ac.signal.aborted && !this.closed) {\n try {\n const response = await this.request(\n streamUrl,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n channels: params.channels,\n ...(params.namespaces ? { namespaces: params.namespaces } : {}),\n ...(params.depth != null ? { depth: params.depth } : {}),\n ...(!readySettled && initialSince != null\n ? { since: initialSince }\n : {}),\n }),\n signal: ac.signal,\n },\n { stream: true }\n );\n\n const contentType = response.headers\n .get(\"content-type\")\n ?.split(\";\")[0];\n if (contentType && !contentType.includes(\"text/event-stream\")) {\n throw new Error(\n `Expected response header Content-Type to contain 'text/event-stream', got '${contentType}'`\n );\n }\n\n await this.onConnected?.({\n kind: attempt === 0 ? \"initial\" : \"reconnected\",\n attempt,\n });\n // A successful (re)connect restores the full retry budget, even\n // if the stream then closes with no events (idle thread, idle\n // timeout). Reset after `onConnected` so it still reports how\n // many attempts this reconnect took.\n attempt = 0;\n\n if (!readySettled) {\n readySettled = true;\n resolveReady();\n }\n\n const readable =\n response.body ??\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n },\n });\n\n // Idle watchdog on the line stream (between byte-line and SSE\n // decoding) so it can reset on any line and recognise `:` keep-alive\n // heartbeats to drive `\"auto\"` mode. On idle it errors the stream,\n // which the catch below treats like any other disconnect and\n // re-opens the SSE without carrying a connection-local `since`.\n const enableIdle =\n this.idleReconnect === \"auto\" ||\n (typeof this.idleReconnect === \"number\" && this.idleReconnect > 0);\n const lines = readable.pipeThrough(BytesLineDecoder());\n const watched = enableIdle\n ? lines.pipeThrough(\n idleReconnectStream({ mode: this.idleReconnect! })\n )\n : lines;\n const stream = watched.pipeThrough(SSEDecoder());\n const iterable = IterableReadableStream.fromReadableStream(stream);\n\n for await (const event of iterable) {\n if (ac.signal.aborted || this.closed) {\n break;\n }\n if (isRecord(event.data)) {\n streamQueue.push(event.data as Message);\n }\n }\n if (\n ac.signal.aborted ||\n this.closed ||\n this.maxReconnectAttempts <= 0\n ) {\n streamQueue.close();\n return;\n }\n // The thread stream is open-ended: the server only ends it when\n // its own upstream consumer died or it is shutting down, so a\n // clean close is a disconnect, not the end of the thread.\n throw new Error(\"Event stream closed by the server\");\n } catch (error) {\n if (ac.signal.aborted || this.closed) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close();\n return;\n }\n // A client error (e.g. 401/403) fails the same way on every retry,\n // so end the stream without using the retry budget.\n if (\n this.maxReconnectAttempts <= 0 ||\n isNonRetryableHttpError(error)\n ) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n attempt += 1;\n if (attempt > this.maxReconnectAttempts) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n const delay = this.reconnectDelayMs(attempt);\n this.onReconnect?.({ attempt, cause: error, delayMs: delay });\n if (delay > 0) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n }\n }\n };\n\n void startStream();\n\n const cleanup = () => {\n this.eventStreams.delete(ac);\n ac.abort();\n streamQueue.close();\n };\n\n return {\n events: {\n [Symbol.asyncIterator]: () => ({\n next: async () => await streamQueue.shift(),\n return: async () => {\n cleanup();\n return { done: true, value: undefined };\n },\n }),\n },\n ready,\n close: cleanup,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.sessionAbortController.abort();\n for (const ac of this.eventStreams) ac.abort();\n this.eventStreams.clear();\n this.queue.close();\n }\n\n private async request(\n path: string,\n init: RequestInit,\n options?: { stream?: boolean }\n ): Promise<Response> {\n const url = toAbsoluteUrl(this.apiUrl, path);\n let requestInit: RequestInit = {\n ...init,\n headers: mergeHeaders(this.defaultHeaders, init.headers),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n // Long-lived SSE event streams must not run through AsyncCaller: its\n // p-queue/p-retry semantics are designed for discrete request/response\n // calls, and wrapping a streaming response stalls the call (and can\n // leak retries). Stream resilience is handled separately by the\n // reconnect loop in `openEventStream`.\n const useAsyncCaller = this.asyncCaller != null && !options?.stream;\n\n const execute = async (): Promise<Response> => {\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (!response.ok) {\n // Reject with the Response so AsyncCaller maps it to HTTPError and\n // applies STATUS_NO_RETRY / retry policy consistently with REST.\n if (useAsyncCaller) {\n throw response;\n }\n let body = \"\";\n let detail = \"\";\n try {\n body = await response.text();\n const parsed = JSON.parse(body);\n if (typeof parsed === \"object\" && parsed != null) {\n detail =\n ((parsed as Record<string, unknown>).message as string) ??\n ((parsed as Record<string, unknown>).error as string) ??\n \"\";\n }\n if (!detail) detail = body;\n } catch {\n // body unreadable or not JSON — fall through\n }\n const message = detail\n ? `Protocol request failed: ${response.status} ${response.statusText} — ${detail}`\n : `Protocol request failed: ${response.status} ${response.statusText}`;\n // Keep the status (like `getState`) so callers can tell an auth\n // failure from a network failure without parsing the message.\n throw Object.assign(new Error(message), {\n status: response.status,\n text: body,\n });\n }\n return response;\n };\n\n try {\n return useAsyncCaller\n ? await this.asyncCaller!.call(execute)\n : await execute();\n } catch (error) {\n throw toError(error);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http.js","names":[],"sources":["../../../../src/client/stream/transport/http.ts"],"sourcesContent":["import { AsyncQueue } from \"./queue.js\";\nimport { encodePathSegment } from \"../../base.js\";\nimport type {\n Message,\n SubscribeParams,\n Command,\n CommandResponse,\n ErrorResponse,\n} from \"@langchain/protocol\";\n\nimport type { AsyncCaller } from \"../../../utils/async_caller.js\";\nimport type {\n HeaderValue,\n ProtocolRequestHook,\n ProtocolSseTransportOptions,\n ProtocolTransportPaths,\n} from \"./types.js\";\nimport type { TransportAdapter, EventStreamHandle } from \"../transport.js\";\nimport {\n toAbsoluteUrl,\n isRecord,\n isNonRetryableHttpError,\n mergeHeaders,\n toError,\n isProtocolResponse,\n resolveProtocolPath,\n} from \"./utils.js\";\nimport { BytesLineDecoder, SSEDecoder } from \"../../../utils/sse.js\";\nimport {\n IterableReadableStream,\n idleReconnectStream,\n type IdleReconnectMode,\n} from \"../../../utils/stream.js\";\nimport {\n DEFAULT_MAX_RECONNECT_ATTEMPTS,\n reconnectDelayMs,\n} from \"../../../utils/reconnect.js\";\nimport { DEFAULT_IDLE_RECONNECT } from \"../../../utils/stream.js\";\n\n/**\n * Transport adapter that speaks the thread-centric protocol over HTTP\n * commands plus SSE event streams. Bound to a `threadId` at construction\n * or later via {@link setThreadId}; request URLs derive from the\n * currently-bound thread. Each {@link openEventStream} call opens an\n * independent filtered SSE connection via\n * `POST /threads/:thread_id/stream/events`.\n */\nexport class ProtocolSseTransportAdapter implements TransportAdapter {\n threadId: string;\n\n readonly apiUrl: string;\n\n private readonly queue = new AsyncQueue<Message>();\n\n private readonly fetchImpl: typeof fetch;\n\n private readonly defaultHeaders: Record<string, HeaderValue>;\n\n private readonly onRequest?: ProtocolRequestHook;\n\n private readonly fetchFactory?: () => typeof fetch | Promise<typeof fetch>;\n\n private readonly asyncCaller?: AsyncCaller;\n\n private readonly maxReconnectAttempts: number;\n\n private readonly idleReconnect: IdleReconnectMode | null;\n\n private readonly onReconnect?: ProtocolSseTransportOptions[\"onReconnect\"];\n\n private readonly onConnected?: ProtocolSseTransportOptions[\"onConnected\"];\n\n private readonly reconnectDelayMs: (attempt: number) => number;\n\n private readonly paths?: ProtocolTransportPaths;\n\n private readonly sessionAbortController = new AbortController();\n\n private readonly eventStreams = new Set<AbortController>();\n\n private closed = false;\n\n constructor(options: ProtocolSseTransportOptions) {\n this.fetchImpl = options.fetch ?? fetch;\n this.apiUrl = options.apiUrl;\n this.defaultHeaders = options.defaultHeaders ?? {};\n this.onRequest = options.onRequest;\n this.fetchFactory = options.fetchFactory;\n this.asyncCaller = options.asyncCaller;\n // Custom `fetch` (auth shims, proxies) must keep reconnect enabled — that is\n // the production path for tenant-aware browsers. Tests that need fail-fast\n // mocks should pass `maxReconnectAttempts: 0` (and `idleReconnect: 0`)\n // explicitly rather than relying on `fetch` presence.\n this.maxReconnectAttempts =\n options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;\n // Default to heartbeat-adaptive idle reconnect, which stays dormant\n // unless the server actually emits keep-alive heartbeats. Pass `0` to\n // disable.\n this.idleReconnect = options.idleReconnect ?? DEFAULT_IDLE_RECONNECT;\n this.onReconnect = options.onReconnect;\n this.onConnected = options.onConnected;\n this.reconnectDelayMs = options.reconnectDelayMs ?? reconnectDelayMs;\n this.threadId = options.threadId ?? \"\";\n this.paths = options.paths;\n }\n\n /** {@inheritDoc TransportAdapter.setThreadId} */\n setThreadId(threadId: string): void {\n this.threadId = threadId;\n }\n\n /**\n * Command/stream/state URLs derive from the currently-bound thread so a\n * single adapter can follow {@link setThreadId} re-binds. A fixed\n * `paths.*` string overrides the default and is used as-is.\n */\n private get commandsUrl(): string {\n return resolveProtocolPath(\n this.paths?.commands,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/commands`\n );\n }\n\n private get streamUrl(): string {\n return resolveProtocolPath(\n this.paths?.stream,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/stream/events`\n );\n }\n\n private get stateUrl(): string {\n return resolveProtocolPath(\n this.paths?.state,\n this.threadId,\n (id) => `/threads/${encodePathSegment(id)}/state`\n );\n }\n\n /**\n * Fetch checkpointed thread state for hydration.\n *\n * Uses `GET`, matching `client.threads.getState()` and both LangGraph\n * Platform and Agent Protocol custom backends (`POST` is reserved for\n * `updateState`).\n */\n async getState<StateType = unknown>(): Promise<{\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n } | null> {\n const url = toAbsoluteUrl(this.apiUrl, this.stateUrl);\n let requestInit: RequestInit = {\n method: \"GET\",\n headers: mergeHeaders(this.defaultHeaders, {}),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (response.status === 404) return null;\n if (!response.ok) {\n const error = toError(\n new Error(\n `Thread state request failed: ${response.status} ${response.statusText}`\n )\n ) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n return (await response.json()) as {\n values: StateType;\n next?: unknown;\n tasks?: unknown;\n metadata?: unknown;\n checkpoint?: { checkpoint_id?: string } | null;\n parent_checkpoint?: { checkpoint_id?: string } | null;\n };\n }\n\n private async resolveFetch(): Promise<typeof fetch> {\n if (this.fetchFactory) {\n return await this.fetchFactory();\n }\n return this.fetchImpl;\n }\n\n /**\n * HTTP/SSE transports have no handshake — connections are made\n * per-command and per-subscription.\n */\n async open(): Promise<void> {\n // no-op\n }\n\n async send(\n command: Command\n ): Promise<CommandResponse | ErrorResponse | void> {\n const response = await this.request(this.commandsUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(command),\n signal: this.sessionAbortController.signal,\n });\n\n if (response.status === 202 || response.status === 204) {\n return undefined;\n }\n\n const payload = (await response.json()) as unknown;\n if (!isProtocolResponse(payload)) {\n throw new Error(\"Protocol command did not return a valid response.\");\n }\n return payload;\n }\n\n /**\n * WebSocket-style single event stream.\n * For the SSE transport this returns a dummy iterable; real event\n * delivery happens via {@link openEventStream}.\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 openEventStream(params: SubscribeParams): EventStreamHandle {\n if (this.closed) {\n throw new Error(\"Protocol transport is closed.\");\n }\n\n const ac = new AbortController();\n this.eventStreams.add(ac);\n const streamQueue = new AsyncQueue<Message>();\n const streamUrl = this.streamUrl;\n\n let resolveReady!: () => void;\n let rejectReady!: (err: unknown) => void;\n const ready = new Promise<void>((resolve, reject) => {\n resolveReady = resolve;\n rejectReady = reject;\n });\n\n // Honor an explicit caller `since` until the stream has connected\n // once. Do not advance it from observed `seq` values — those are\n // connection-local, and carrying them onto a post-connect reconnect\n // POST filters out the full Redis replay (heartbeats only). Pre-ready\n // retries still send the caller cursor; only after a successful open\n // do reconnects omit `since` and rely on durable `event_id` dedup.\n const initialSince =\n typeof (params as SubscribeParams & { since?: unknown }).since ===\n \"number\"\n ? (params as SubscribeParams & { since: number }).since\n : undefined;\n\n let readySettled = false;\n\n const startStream = async () => {\n let attempt = 0;\n\n while (!ac.signal.aborted && !this.closed) {\n try {\n const response = await this.request(\n streamUrl,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n accept: \"text/event-stream\",\n },\n body: JSON.stringify({\n channels: params.channels,\n ...(params.namespaces ? { namespaces: params.namespaces } : {}),\n ...(params.depth != null ? { depth: params.depth } : {}),\n ...(!readySettled && initialSince != null\n ? { since: initialSince }\n : {}),\n }),\n signal: ac.signal,\n },\n { stream: true }\n );\n\n const contentType = response.headers\n .get(\"content-type\")\n ?.split(\";\")[0];\n if (contentType && !contentType.includes(\"text/event-stream\")) {\n throw new Error(\n `Expected response header Content-Type to contain 'text/event-stream', got '${contentType}'`\n );\n }\n\n await this.onConnected?.({\n kind: attempt === 0 ? \"initial\" : \"reconnected\",\n attempt,\n });\n // A successful (re)connect restores the full retry budget, even\n // if the stream then closes with no events (idle thread, idle\n // timeout). Reset after `onConnected` so it still reports how\n // many attempts this reconnect took.\n attempt = 0;\n\n if (!readySettled) {\n readySettled = true;\n resolveReady();\n }\n\n const readable =\n response.body ??\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.close();\n },\n });\n\n // Idle watchdog on the line stream (between byte-line and SSE\n // decoding) so it can reset on any line and recognise `:` keep-alive\n // heartbeats to drive `\"auto\"` mode. On idle it errors the stream,\n // which the catch below treats like any other disconnect and\n // re-opens the SSE without carrying a connection-local `since`.\n const enableIdle =\n this.idleReconnect === \"auto\" ||\n (typeof this.idleReconnect === \"number\" && this.idleReconnect > 0);\n const lines = readable.pipeThrough(BytesLineDecoder());\n const watched = enableIdle\n ? lines.pipeThrough(\n idleReconnectStream({ mode: this.idleReconnect! })\n )\n : lines;\n const stream = watched.pipeThrough(SSEDecoder());\n const iterable = IterableReadableStream.fromReadableStream(stream);\n\n for await (const event of iterable) {\n if (ac.signal.aborted || this.closed) {\n break;\n }\n if (isRecord(event.data)) {\n streamQueue.push(event.data as Message);\n }\n }\n if (\n ac.signal.aborted ||\n this.closed ||\n this.maxReconnectAttempts <= 0\n ) {\n streamQueue.close();\n return;\n }\n // The thread stream is open-ended: the server only ends it when\n // its own upstream consumer died or it is shutting down, so a\n // clean close is a disconnect, not the end of the thread.\n throw new Error(\"Event stream closed by the server\");\n } catch (error) {\n if (ac.signal.aborted || this.closed) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close();\n return;\n }\n // A client error (e.g. 401/403) fails the same way on every retry,\n // so end the stream without using the retry budget.\n if (\n this.maxReconnectAttempts <= 0 ||\n isNonRetryableHttpError(error)\n ) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n attempt += 1;\n if (attempt > this.maxReconnectAttempts) {\n if (!readySettled) {\n rejectReady(error);\n }\n streamQueue.close(toError(error));\n return;\n }\n const delay = this.reconnectDelayMs(attempt);\n this.onReconnect?.({ attempt, cause: error, delayMs: delay });\n if (delay > 0) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n }\n }\n };\n\n void startStream();\n\n const cleanup = () => {\n this.eventStreams.delete(ac);\n ac.abort();\n streamQueue.close();\n };\n\n return {\n events: {\n [Symbol.asyncIterator]: () => ({\n next: async () => await streamQueue.shift(),\n return: async () => {\n cleanup();\n return { done: true, value: undefined };\n },\n }),\n },\n ready,\n close: cleanup,\n };\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closed = true;\n this.sessionAbortController.abort();\n for (const ac of this.eventStreams) ac.abort();\n this.eventStreams.clear();\n this.queue.close();\n }\n\n private async request(\n path: string,\n init: RequestInit,\n options?: { stream?: boolean }\n ): Promise<Response> {\n const url = toAbsoluteUrl(this.apiUrl, path);\n let requestInit: RequestInit = {\n ...init,\n headers: mergeHeaders(this.defaultHeaders, init.headers),\n };\n\n if (this.onRequest) {\n requestInit = await this.onRequest(url, requestInit);\n }\n\n // Long-lived SSE event streams must not run through AsyncCaller: its\n // p-queue/p-retry semantics are designed for discrete request/response\n // calls, and wrapping a streaming response stalls the call (and can\n // leak retries). Stream resilience is handled separately by the\n // reconnect loop in `openEventStream`.\n const useAsyncCaller = this.asyncCaller != null && !options?.stream;\n\n const execute = async (): Promise<Response> => {\n const fetchImpl = await this.resolveFetch();\n const response = await fetchImpl(url.toString(), requestInit);\n if (!response.ok) {\n // Reject with the Response so AsyncCaller maps it to HTTPError and\n // applies STATUS_NO_RETRY / retry policy consistently with REST.\n if (useAsyncCaller) {\n throw response;\n }\n let body = \"\";\n let detail = \"\";\n try {\n body = await response.text();\n const parsed = JSON.parse(body);\n if (typeof parsed === \"object\" && parsed != null) {\n detail =\n ((parsed as Record<string, unknown>).message as string) ??\n ((parsed as Record<string, unknown>).error as string) ??\n \"\";\n }\n if (!detail) detail = body;\n } catch {\n // body unreadable or not JSON — fall through\n }\n const message = detail\n ? `Protocol request failed: ${response.status} ${response.statusText} — ${detail}`\n : `Protocol request failed: ${response.status} ${response.statusText}`;\n // Keep the status (like `getState`) so callers can tell an auth\n // failure from a network failure without parsing the message.\n throw Object.assign(new Error(message), {\n status: response.status,\n text: body,\n });\n }\n return response;\n };\n\n try {\n return useAsyncCaller\n ? await this.asyncCaller!.call(execute)\n : await execute();\n } catch (error) {\n throw toError(error);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,17 @@
1
1
  //#region src/client/stream/transport/utils.ts
2
2
  const isRecord = (value) => typeof value === "object" && value !== null;
3
3
  /**
4
+ * True for a 4xx HTTP error other than 408 (Request Timeout) and 429
5
+ * (Too Many Requests): retrying the same request cannot succeed. Errors
6
+ * with no `status` (network errors, idle timeouts, server closes) stay
7
+ * retryable.
8
+ */
9
+ function isNonRetryableHttpError(error) {
10
+ if (!isRecord(error) || typeof error.status !== "number") return false;
11
+ const { status } = error;
12
+ return status >= 400 && status < 500 && status !== 408 && status !== 429;
13
+ }
14
+ /**
4
15
  * Resolve a {@link ProtocolPath} against the transport's currently-bound
5
16
  * thread.
6
17
  *
@@ -54,6 +65,7 @@ function isProtocolResponse(value) {
54
65
  }
55
66
  //#endregion
56
67
  exports.hasHeaders = hasHeaders;
68
+ exports.isNonRetryableHttpError = isNonRetryableHttpError;
57
69
  exports.isProtocolResponse = isProtocolResponse;
58
70
  exports.isRecord = isRecord;
59
71
  exports.mergeHeaders = mergeHeaders;