@langchain/langgraph-sdk 1.6.5 → 1.7.1

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 (223) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +7 -13
  2. package/dist/auth/error.cjs +1 -2
  3. package/dist/auth/error.cjs.map +1 -1
  4. package/dist/auth/error.js +1 -1
  5. package/dist/auth/index.cjs +3 -4
  6. package/dist/auth/index.cjs.map +1 -1
  7. package/dist/auth/index.js +1 -2
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/client.cjs +9 -9
  10. package/dist/client.cjs.map +1 -1
  11. package/dist/client.d.cts.map +1 -1
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/client.js +3 -3
  14. package/dist/client.js.map +1 -1
  15. package/dist/index.cjs +5 -6
  16. package/dist/index.d.cts +4 -4
  17. package/dist/index.d.ts +4 -4
  18. package/dist/index.js +1 -2
  19. package/dist/logging/index.cjs +2 -3
  20. package/dist/logging/index.cjs.map +1 -1
  21. package/dist/logging/index.js +1 -1
  22. package/dist/react/index.cjs +46 -11
  23. package/dist/react/index.cjs.map +1 -0
  24. package/dist/react/index.d.cts +14 -11
  25. package/dist/react/index.d.cts.map +1 -0
  26. package/dist/react/index.d.ts +14 -11
  27. package/dist/react/index.d.ts.map +1 -0
  28. package/dist/react/index.js +13 -4
  29. package/dist/react/index.js.map +1 -0
  30. package/dist/react-ui/index.cjs +39 -15
  31. package/dist/react-ui/index.d.cts +1 -2
  32. package/dist/react-ui/index.d.ts +1 -2
  33. package/dist/react-ui/index.js +1 -8
  34. package/dist/react-ui/server/index.cjs +15 -6
  35. package/dist/react-ui/server/index.d.cts +1 -2
  36. package/dist/react-ui/server/index.d.ts +1 -2
  37. package/dist/react-ui/server/index.js +2 -4
  38. package/dist/singletons/fetch.cjs +1 -2
  39. package/dist/singletons/fetch.cjs.map +1 -1
  40. package/dist/singletons/fetch.js +1 -1
  41. package/dist/singletons/fetch.js.map +1 -1
  42. package/dist/types.messages.d.cts +5 -3
  43. package/dist/types.messages.d.cts.map +1 -1
  44. package/dist/types.messages.d.ts +5 -3
  45. package/dist/types.messages.d.ts.map +1 -1
  46. package/dist/ui/branching.cjs +25 -2
  47. package/dist/ui/branching.cjs.map +1 -1
  48. package/dist/ui/branching.d.cts +26 -1
  49. package/dist/ui/branching.d.cts.map +1 -1
  50. package/dist/ui/branching.d.ts +26 -1
  51. package/dist/ui/branching.d.ts.map +1 -1
  52. package/dist/ui/branching.js +25 -2
  53. package/dist/ui/branching.js.map +1 -1
  54. package/dist/ui/errors.cjs +1 -2
  55. package/dist/ui/errors.cjs.map +1 -1
  56. package/dist/ui/errors.js +1 -1
  57. package/dist/ui/index.cjs +30 -0
  58. package/dist/ui/index.d.cts +15 -0
  59. package/dist/ui/index.d.ts +15 -0
  60. package/dist/ui/index.js +10 -0
  61. package/dist/ui/interrupts.cjs +20 -0
  62. package/dist/ui/interrupts.cjs.map +1 -0
  63. package/dist/ui/interrupts.d.cts +11 -0
  64. package/dist/ui/interrupts.d.cts.map +1 -0
  65. package/dist/ui/interrupts.d.ts +11 -0
  66. package/dist/ui/interrupts.d.ts.map +1 -0
  67. package/dist/ui/interrupts.js +20 -0
  68. package/dist/ui/interrupts.js.map +1 -0
  69. package/dist/ui/manager.cjs +13 -15
  70. package/dist/ui/manager.cjs.map +1 -1
  71. package/dist/ui/manager.d.cts +224 -0
  72. package/dist/ui/manager.d.cts.map +1 -0
  73. package/dist/ui/manager.d.ts +224 -0
  74. package/dist/ui/manager.d.ts.map +1 -0
  75. package/dist/ui/manager.js +10 -12
  76. package/dist/ui/manager.js.map +1 -1
  77. package/dist/ui/messages.cjs +40 -3
  78. package/dist/ui/messages.cjs.map +1 -1
  79. package/dist/ui/messages.d.cts +56 -0
  80. package/dist/ui/messages.d.cts.map +1 -0
  81. package/dist/ui/messages.d.ts +56 -0
  82. package/dist/ui/messages.d.ts.map +1 -0
  83. package/dist/ui/messages.js +37 -3
  84. package/dist/ui/messages.js.map +1 -1
  85. package/dist/ui/queue.cjs +74 -0
  86. package/dist/ui/queue.cjs.map +1 -0
  87. package/dist/ui/queue.d.cts +72 -0
  88. package/dist/ui/queue.d.cts.map +1 -0
  89. package/dist/ui/queue.d.ts +72 -0
  90. package/dist/ui/queue.d.ts.map +1 -0
  91. package/dist/ui/queue.js +74 -0
  92. package/dist/ui/queue.js.map +1 -0
  93. package/dist/ui/stream/base.d.cts +11 -0
  94. package/dist/ui/stream/base.d.cts.map +1 -1
  95. package/dist/ui/stream/base.d.ts +11 -0
  96. package/dist/ui/stream/base.d.ts.map +1 -1
  97. package/dist/ui/stream/index.d.cts +4 -4
  98. package/dist/ui/stream/index.d.cts.map +1 -1
  99. package/dist/ui/stream/index.d.ts +4 -4
  100. package/dist/ui/stream/index.d.ts.map +1 -1
  101. package/dist/ui/subagents.cjs +7 -5
  102. package/dist/ui/subagents.cjs.map +1 -1
  103. package/dist/ui/subagents.d.cts +7 -0
  104. package/dist/ui/subagents.d.cts.map +1 -1
  105. package/dist/ui/subagents.d.ts +7 -0
  106. package/dist/ui/subagents.d.ts.map +1 -1
  107. package/dist/ui/subagents.js +5 -3
  108. package/dist/ui/subagents.js.map +1 -1
  109. package/dist/ui/transport.cjs +30 -0
  110. package/dist/ui/transport.cjs.map +1 -0
  111. package/dist/{react/stream.custom.d.cts → ui/transport.d.cts} +3 -3
  112. package/dist/ui/transport.d.cts.map +1 -0
  113. package/dist/{react/stream.custom.d.ts → ui/transport.d.ts} +3 -3
  114. package/dist/ui/transport.d.ts.map +1 -0
  115. package/dist/ui/transport.js +30 -0
  116. package/dist/ui/transport.js.map +1 -0
  117. package/dist/ui/types.d.cts +125 -16
  118. package/dist/ui/types.d.cts.map +1 -1
  119. package/dist/ui/types.d.ts +125 -16
  120. package/dist/ui/types.d.ts.map +1 -1
  121. package/dist/ui/utils.cjs +1 -2
  122. package/dist/ui/utils.cjs.map +1 -1
  123. package/dist/ui/utils.d.cts +7 -0
  124. package/dist/ui/utils.d.cts.map +1 -0
  125. package/dist/ui/utils.d.ts +7 -0
  126. package/dist/ui/utils.d.ts.map +1 -0
  127. package/dist/ui/utils.js +1 -1
  128. package/dist/utils/async_caller.cjs +3 -4
  129. package/dist/utils/async_caller.cjs.map +1 -1
  130. package/dist/utils/async_caller.js +1 -2
  131. package/dist/utils/async_caller.js.map +1 -1
  132. package/dist/utils/env.cjs +1 -2
  133. package/dist/utils/env.cjs.map +1 -1
  134. package/dist/utils/env.js +1 -1
  135. package/dist/utils/env.js.map +1 -1
  136. package/dist/utils/error.cjs +1 -2
  137. package/dist/utils/error.cjs.map +1 -1
  138. package/dist/utils/error.js +1 -1
  139. package/dist/utils/index.cjs +8 -0
  140. package/dist/utils/index.d.cts +4 -0
  141. package/dist/utils/index.d.ts +4 -0
  142. package/dist/utils/index.js +4 -0
  143. package/dist/utils/signals.cjs +1 -2
  144. package/dist/utils/signals.cjs.map +1 -1
  145. package/dist/utils/signals.js +1 -1
  146. package/dist/utils/signals.js.map +1 -1
  147. package/dist/utils/sse.cjs +1 -2
  148. package/dist/utils/sse.cjs.map +1 -1
  149. package/dist/utils/sse.d.cts +11 -0
  150. package/dist/utils/sse.d.cts.map +1 -0
  151. package/dist/utils/sse.d.ts +11 -0
  152. package/dist/utils/sse.d.ts.map +1 -0
  153. package/dist/utils/sse.js +1 -1
  154. package/dist/utils/sse.js.map +1 -1
  155. package/dist/utils/stream.cjs +2 -3
  156. package/dist/utils/stream.cjs.map +1 -1
  157. package/dist/utils/stream.d.cts +19 -0
  158. package/dist/utils/stream.d.cts.map +1 -0
  159. package/dist/utils/stream.d.ts +19 -0
  160. package/dist/utils/stream.d.ts.map +1 -0
  161. package/dist/utils/stream.js +1 -2
  162. package/dist/utils/stream.js.map +1 -1
  163. package/dist/utils/tools.cjs +27 -19
  164. package/dist/utils/tools.cjs.map +1 -1
  165. package/dist/utils/tools.d.cts +7 -0
  166. package/dist/utils/tools.d.cts.map +1 -0
  167. package/dist/utils/tools.d.ts +7 -0
  168. package/dist/utils/tools.d.ts.map +1 -0
  169. package/dist/utils/tools.js +27 -18
  170. package/dist/utils/tools.js.map +1 -1
  171. package/package.json +41 -12
  172. package/dist/react/stream.cjs +0 -18
  173. package/dist/react/stream.cjs.map +0 -1
  174. package/dist/react/stream.custom.cjs +0 -164
  175. package/dist/react/stream.custom.cjs.map +0 -1
  176. package/dist/react/stream.custom.d.cts.map +0 -1
  177. package/dist/react/stream.custom.d.ts.map +0 -1
  178. package/dist/react/stream.custom.js +0 -162
  179. package/dist/react/stream.custom.js.map +0 -1
  180. package/dist/react/stream.d.cts +0 -174
  181. package/dist/react/stream.d.cts.map +0 -1
  182. package/dist/react/stream.d.ts +0 -174
  183. package/dist/react/stream.d.ts.map +0 -1
  184. package/dist/react/stream.js +0 -17
  185. package/dist/react/stream.js.map +0 -1
  186. package/dist/react/stream.lgp.cjs +0 -544
  187. package/dist/react/stream.lgp.cjs.map +0 -1
  188. package/dist/react/stream.lgp.js +0 -543
  189. package/dist/react/stream.lgp.js.map +0 -1
  190. package/dist/react/thread.cjs +0 -21
  191. package/dist/react/thread.cjs.map +0 -1
  192. package/dist/react/thread.js +0 -20
  193. package/dist/react/thread.js.map +0 -1
  194. package/dist/react/types.d.cts +0 -79
  195. package/dist/react/types.d.cts.map +0 -1
  196. package/dist/react/types.d.ts +0 -79
  197. package/dist/react/types.d.ts.map +0 -1
  198. package/dist/react-ui/client.cjs +0 -138
  199. package/dist/react-ui/client.cjs.map +0 -1
  200. package/dist/react-ui/client.d.cts +0 -76
  201. package/dist/react-ui/client.d.cts.map +0 -1
  202. package/dist/react-ui/client.d.ts +0 -76
  203. package/dist/react-ui/client.d.ts.map +0 -1
  204. package/dist/react-ui/client.js +0 -132
  205. package/dist/react-ui/client.js.map +0 -1
  206. package/dist/react-ui/index.cjs.map +0 -1
  207. package/dist/react-ui/index.js.map +0 -1
  208. package/dist/react-ui/server/server.cjs +0 -57
  209. package/dist/react-ui/server/server.cjs.map +0 -1
  210. package/dist/react-ui/server/server.d.cts +0 -54
  211. package/dist/react-ui/server/server.d.cts.map +0 -1
  212. package/dist/react-ui/server/server.d.ts +0 -54
  213. package/dist/react-ui/server/server.d.ts.map +0 -1
  214. package/dist/react-ui/server/server.js +0 -56
  215. package/dist/react-ui/server/server.js.map +0 -1
  216. package/dist/react-ui/types.cjs +0 -38
  217. package/dist/react-ui/types.cjs.map +0 -1
  218. package/dist/react-ui/types.d.cts +0 -25
  219. package/dist/react-ui/types.d.cts.map +0 -1
  220. package/dist/react-ui/types.d.ts +0 -25
  221. package/dist/react-ui/types.d.ts.map +0 -1
  222. package/dist/react-ui/types.js +0 -35
  223. package/dist/react-ui/types.js.map +0 -1
@@ -0,0 +1,4 @@
1
+ import { getToolCallsWithResults } from "./tools.cjs";
2
+ import { BytesLineDecoder, SSEDecoder } from "./sse.cjs";
3
+ import { IterableReadableStream } from "./stream.cjs";
4
+ export { BytesLineDecoder, IterableReadableStream, SSEDecoder, getToolCallsWithResults };
@@ -0,0 +1,4 @@
1
+ import { getToolCallsWithResults } from "./tools.js";
2
+ import { BytesLineDecoder, SSEDecoder } from "./sse.js";
3
+ import { IterableReadableStream } from "./stream.js";
4
+ export { BytesLineDecoder, IterableReadableStream, SSEDecoder, getToolCallsWithResults };
@@ -0,0 +1,4 @@
1
+ import { BytesLineDecoder, SSEDecoder } from "./sse.js";
2
+ import { IterableReadableStream } from "./stream.js";
3
+ import { getToolCallsWithResults } from "./tools.js";
4
+ export { BytesLineDecoder, IterableReadableStream, SSEDecoder, getToolCallsWithResults };
@@ -1,4 +1,3 @@
1
-
2
1
  //#region src/utils/signals.ts
3
2
  function mergeSignals(...signals) {
4
3
  const nonZeroSignals = signals.filter((signal) => signal != null);
@@ -14,7 +13,7 @@ function mergeSignals(...signals) {
14
13
  }
15
14
  return controller.signal;
16
15
  }
17
-
18
16
  //#endregion
19
17
  exports.mergeSignals = mergeSignals;
18
+
20
19
  //# sourceMappingURL=signals.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"signals.cjs","names":[],"sources":["../../src/utils/signals.ts"],"sourcesContent":["export function mergeSignals(...signals: (AbortSignal | null | undefined)[]) {\n const nonZeroSignals = signals.filter(\n (signal): signal is AbortSignal => signal != null\n );\n\n if (nonZeroSignals.length === 0) return undefined;\n if (nonZeroSignals.length === 1) return nonZeroSignals[0];\n\n const controller = new AbortController();\n for (const signal of signals) {\n if (signal?.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n signal?.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n}\n"],"mappings":";;AAAA,SAAgB,aAAa,GAAG,SAA6C;CAC3E,MAAM,iBAAiB,QAAQ,QAC5B,WAAkC,UAAU,KAC9C;AAED,KAAI,eAAe,WAAW,EAAG,QAAO;AACxC,KAAI,eAAe,WAAW,EAAG,QAAO,eAAe;CAEvD,MAAM,aAAa,IAAI,iBAAiB;AACxC,MAAK,MAAM,UAAU,SAAS;AAC5B,MAAI,QAAQ,SAAS;AACnB,cAAW,MAAM,OAAO,OAAO;AAC/B,UAAO,WAAW;;AAGpB,UAAQ,iBAAiB,eAAe,WAAW,MAAM,OAAO,OAAO,EAAE,EACvE,MAAM,MACP,CAAC;;AAGJ,QAAO,WAAW"}
1
+ {"version":3,"file":"signals.cjs","names":[],"sources":["../../src/utils/signals.ts"],"sourcesContent":["export function mergeSignals(...signals: (AbortSignal | null | undefined)[]) {\n const nonZeroSignals = signals.filter(\n (signal): signal is AbortSignal => signal != null\n );\n\n if (nonZeroSignals.length === 0) return undefined;\n if (nonZeroSignals.length === 1) return nonZeroSignals[0];\n\n const controller = new AbortController();\n for (const signal of signals) {\n if (signal?.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n signal?.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n}\n"],"mappings":";AAAA,SAAgB,aAAa,GAAG,SAA6C;CAC3E,MAAM,iBAAiB,QAAQ,QAC5B,WAAkC,UAAU,KAC9C;AAED,KAAI,eAAe,WAAW,EAAG,QAAO,KAAA;AACxC,KAAI,eAAe,WAAW,EAAG,QAAO,eAAe;CAEvD,MAAM,aAAa,IAAI,iBAAiB;AACxC,MAAK,MAAM,UAAU,SAAS;AAC5B,MAAI,QAAQ,SAAS;AACnB,cAAW,MAAM,OAAO,OAAO;AAC/B,UAAO,WAAW;;AAGpB,UAAQ,iBAAiB,eAAe,WAAW,MAAM,OAAO,OAAO,EAAE,EACvE,MAAM,MACP,CAAC;;AAGJ,QAAO,WAAW"}
@@ -13,7 +13,7 @@ function mergeSignals(...signals) {
13
13
  }
14
14
  return controller.signal;
15
15
  }
16
-
17
16
  //#endregion
18
17
  export { mergeSignals };
18
+
19
19
  //# sourceMappingURL=signals.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signals.js","names":[],"sources":["../../src/utils/signals.ts"],"sourcesContent":["export function mergeSignals(...signals: (AbortSignal | null | undefined)[]) {\n const nonZeroSignals = signals.filter(\n (signal): signal is AbortSignal => signal != null\n );\n\n if (nonZeroSignals.length === 0) return undefined;\n if (nonZeroSignals.length === 1) return nonZeroSignals[0];\n\n const controller = new AbortController();\n for (const signal of signals) {\n if (signal?.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n signal?.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n}\n"],"mappings":";AAAA,SAAgB,aAAa,GAAG,SAA6C;CAC3E,MAAM,iBAAiB,QAAQ,QAC5B,WAAkC,UAAU,KAC9C;AAED,KAAI,eAAe,WAAW,EAAG,QAAO;AACxC,KAAI,eAAe,WAAW,EAAG,QAAO,eAAe;CAEvD,MAAM,aAAa,IAAI,iBAAiB;AACxC,MAAK,MAAM,UAAU,SAAS;AAC5B,MAAI,QAAQ,SAAS;AACnB,cAAW,MAAM,OAAO,OAAO;AAC/B,UAAO,WAAW;;AAGpB,UAAQ,iBAAiB,eAAe,WAAW,MAAM,OAAO,OAAO,EAAE,EACvE,MAAM,MACP,CAAC;;AAGJ,QAAO,WAAW"}
1
+ {"version":3,"file":"signals.js","names":[],"sources":["../../src/utils/signals.ts"],"sourcesContent":["export function mergeSignals(...signals: (AbortSignal | null | undefined)[]) {\n const nonZeroSignals = signals.filter(\n (signal): signal is AbortSignal => signal != null\n );\n\n if (nonZeroSignals.length === 0) return undefined;\n if (nonZeroSignals.length === 1) return nonZeroSignals[0];\n\n const controller = new AbortController();\n for (const signal of signals) {\n if (signal?.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n signal?.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n}\n"],"mappings":";AAAA,SAAgB,aAAa,GAAG,SAA6C;CAC3E,MAAM,iBAAiB,QAAQ,QAC5B,WAAkC,UAAU,KAC9C;AAED,KAAI,eAAe,WAAW,EAAG,QAAO,KAAA;AACxC,KAAI,eAAe,WAAW,EAAG,QAAO,eAAe;CAEvD,MAAM,aAAa,IAAI,iBAAiB;AACxC,MAAK,MAAM,UAAU,SAAS;AAC5B,MAAI,QAAQ,SAAS;AACnB,cAAW,MAAM,OAAO,OAAO;AAC/B,UAAO,WAAW;;AAGpB,UAAQ,iBAAiB,eAAe,WAAW,MAAM,OAAO,OAAO,EAAE,EACvE,MAAM,MACP,CAAC;;AAGJ,QAAO,WAAW"}
@@ -1,4 +1,3 @@
1
-
2
1
  //#region src/utils/sse.ts
3
2
  const CR = "\r".charCodeAt(0);
4
3
  const LF = "\n".charCodeAt(0);
@@ -117,8 +116,8 @@ function joinArrays(data) {
117
116
  function decodeArraysToJson(decoder, data) {
118
117
  return JSON.parse(decoder.decode(joinArrays(data)));
119
118
  }
120
-
121
119
  //#endregion
122
120
  exports.BytesLineDecoder = BytesLineDecoder;
123
121
  exports.SSEDecoder = SSEDecoder;
122
+
124
123
  //# sourceMappingURL=sse.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sse.cjs","names":[],"sources":["../../src/utils/sse.ts"],"sourcesContent":["const CR = \"\\r\".charCodeAt(0);\nconst LF = \"\\n\".charCodeAt(0);\nconst NULL = \"\\0\".charCodeAt(0);\nconst COLON = \":\".charCodeAt(0);\nconst SPACE = \" \".charCodeAt(0);\n\nconst TRAILING_NEWLINE = [CR, LF];\n\nexport function BytesLineDecoder() {\n let buffer: Uint8Array[] = [];\n let trailingCr = false;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n start() {\n buffer = [];\n trailingCr = false;\n },\n\n transform(chunk, controller) {\n // See https://docs.python.org/3/glossary.html#term-universal-newlines\n let text = chunk;\n\n // Handle trailing CR from previous chunk\n if (trailingCr) {\n text = joinArrays([[CR], text]);\n trailingCr = false;\n }\n\n // Check for trailing CR in current chunk\n if (text.length > 0 && text.at(-1) === CR) {\n trailingCr = true;\n text = text.subarray(0, -1);\n }\n\n if (!text.length) return;\n const trailingNewline = TRAILING_NEWLINE.includes(text.at(-1)!);\n\n const lastIdx = text.length - 1;\n const { lines } = text.reduce<{ lines: Uint8Array[]; from: number }>(\n (acc, cur, idx) => {\n if (acc.from > idx) return acc;\n\n if (cur === CR || cur === LF) {\n acc.lines.push(text.subarray(acc.from, idx));\n if (cur === CR && text[idx + 1] === LF) {\n acc.from = idx + 2;\n } else {\n acc.from = idx + 1;\n }\n }\n\n if (idx === lastIdx && acc.from <= lastIdx) {\n acc.lines.push(text.subarray(acc.from));\n }\n\n return acc;\n },\n { lines: [], from: 0 }\n );\n\n if (lines.length === 1 && !trailingNewline) {\n buffer.push(lines[0]);\n return;\n }\n\n if (buffer.length) {\n // Include existing buffer in first line\n buffer.push(lines[0]);\n lines[0] = joinArrays(buffer);\n buffer = [];\n }\n\n if (!trailingNewline) {\n // If the last segment is not newline terminated,\n // buffer it for the next chunk\n if (lines.length) buffer = [lines.pop()!];\n }\n\n // Enqueue complete lines\n for (const line of lines) {\n controller.enqueue(line);\n }\n },\n\n flush(controller) {\n if (buffer.length) {\n controller.enqueue(joinArrays(buffer));\n }\n },\n });\n}\n\ninterface StreamPart {\n id: string | undefined;\n event: string;\n data: unknown;\n}\n\nexport function SSEDecoder() {\n let event = \"\";\n let data: Uint8Array[] = [];\n let lastEventId = \"\";\n let retry: number | null = null;\n\n const decoder = new TextDecoder();\n\n return new TransformStream<Uint8Array, StreamPart>({\n transform(chunk, controller) {\n // Handle empty line case\n if (!chunk.length) {\n if (!event && !data.length && !lastEventId && retry == null) return;\n\n const sse = {\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n };\n\n // NOTE: as per the SSE spec, do not reset lastEventId\n event = \"\";\n data = [];\n retry = null;\n\n controller.enqueue(sse);\n return;\n }\n\n // Ignore comments\n if (chunk[0] === COLON) return;\n\n const sepIdx = chunk.indexOf(COLON);\n if (sepIdx === -1) return;\n\n const fieldName = decoder.decode(chunk.subarray(0, sepIdx));\n let value = chunk.subarray(sepIdx + 1);\n if (value[0] === SPACE) value = value.subarray(1);\n\n if (fieldName === \"event\") {\n event = decoder.decode(value);\n } else if (fieldName === \"data\") {\n data.push(value);\n } else if (fieldName === \"id\") {\n if (value.indexOf(NULL) === -1) lastEventId = decoder.decode(value);\n } else if (fieldName === \"retry\") {\n const retryNum = Number.parseInt(decoder.decode(value), 10);\n if (!Number.isNaN(retryNum)) retry = retryNum;\n }\n },\n\n flush(controller) {\n if (event) {\n controller.enqueue({\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n });\n }\n },\n });\n}\n\nfunction joinArrays(data: ArrayLike<number>[]) {\n const totalLength = data.reduce((acc, curr) => acc + curr.length, 0);\n const merged = new Uint8Array(totalLength);\n let offset = 0;\n for (const c of data) {\n merged.set(c, offset);\n offset += c.length;\n }\n return merged;\n}\n\nfunction decodeArraysToJson(decoder: TextDecoder, data: ArrayLike<number>[]) {\n return JSON.parse(decoder.decode(joinArrays(data)));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sse.cjs","names":[],"sources":["../../src/utils/sse.ts"],"sourcesContent":["const CR = \"\\r\".charCodeAt(0);\nconst LF = \"\\n\".charCodeAt(0);\nconst NULL = \"\\0\".charCodeAt(0);\nconst COLON = \":\".charCodeAt(0);\nconst SPACE = \" \".charCodeAt(0);\n\nconst TRAILING_NEWLINE = [CR, LF];\n\nexport function BytesLineDecoder() {\n let buffer: Uint8Array[] = [];\n let trailingCr = false;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n start() {\n buffer = [];\n trailingCr = false;\n },\n\n transform(chunk, controller) {\n // See https://docs.python.org/3/glossary.html#term-universal-newlines\n let text = chunk;\n\n // Handle trailing CR from previous chunk\n if (trailingCr) {\n text = joinArrays([[CR], text]);\n trailingCr = false;\n }\n\n // Check for trailing CR in current chunk\n if (text.length > 0 && text.at(-1) === CR) {\n trailingCr = true;\n text = text.subarray(0, -1);\n }\n\n if (!text.length) return;\n const trailingNewline = TRAILING_NEWLINE.includes(text.at(-1)!);\n\n const lastIdx = text.length - 1;\n const { lines } = text.reduce<{ lines: Uint8Array[]; from: number }>(\n (acc, cur, idx) => {\n if (acc.from > idx) return acc;\n\n if (cur === CR || cur === LF) {\n acc.lines.push(text.subarray(acc.from, idx));\n if (cur === CR && text[idx + 1] === LF) {\n acc.from = idx + 2;\n } else {\n acc.from = idx + 1;\n }\n }\n\n if (idx === lastIdx && acc.from <= lastIdx) {\n acc.lines.push(text.subarray(acc.from));\n }\n\n return acc;\n },\n { lines: [], from: 0 }\n );\n\n if (lines.length === 1 && !trailingNewline) {\n buffer.push(lines[0]);\n return;\n }\n\n if (buffer.length) {\n // Include existing buffer in first line\n buffer.push(lines[0]);\n lines[0] = joinArrays(buffer);\n buffer = [];\n }\n\n if (!trailingNewline) {\n // If the last segment is not newline terminated,\n // buffer it for the next chunk\n if (lines.length) buffer = [lines.pop()!];\n }\n\n // Enqueue complete lines\n for (const line of lines) {\n controller.enqueue(line);\n }\n },\n\n flush(controller) {\n if (buffer.length) {\n controller.enqueue(joinArrays(buffer));\n }\n },\n });\n}\n\ninterface StreamPart {\n id: string | undefined;\n event: string;\n data: unknown;\n}\n\nexport function SSEDecoder() {\n let event = \"\";\n let data: Uint8Array[] = [];\n let lastEventId = \"\";\n let retry: number | null = null;\n\n const decoder = new TextDecoder();\n\n return new TransformStream<Uint8Array, StreamPart>({\n transform(chunk, controller) {\n // Handle empty line case\n if (!chunk.length) {\n if (!event && !data.length && !lastEventId && retry == null) return;\n\n const sse = {\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n };\n\n // NOTE: as per the SSE spec, do not reset lastEventId\n event = \"\";\n data = [];\n retry = null;\n\n controller.enqueue(sse);\n return;\n }\n\n // Ignore comments\n if (chunk[0] === COLON) return;\n\n const sepIdx = chunk.indexOf(COLON);\n if (sepIdx === -1) return;\n\n const fieldName = decoder.decode(chunk.subarray(0, sepIdx));\n let value = chunk.subarray(sepIdx + 1);\n if (value[0] === SPACE) value = value.subarray(1);\n\n if (fieldName === \"event\") {\n event = decoder.decode(value);\n } else if (fieldName === \"data\") {\n data.push(value);\n } else if (fieldName === \"id\") {\n if (value.indexOf(NULL) === -1) lastEventId = decoder.decode(value);\n } else if (fieldName === \"retry\") {\n const retryNum = Number.parseInt(decoder.decode(value), 10);\n if (!Number.isNaN(retryNum)) retry = retryNum;\n }\n },\n\n flush(controller) {\n if (event) {\n controller.enqueue({\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n });\n }\n },\n });\n}\n\nfunction joinArrays(data: ArrayLike<number>[]) {\n const totalLength = data.reduce((acc, curr) => acc + curr.length, 0);\n const merged = new Uint8Array(totalLength);\n let offset = 0;\n for (const c of data) {\n merged.set(c, offset);\n offset += c.length;\n }\n return merged;\n}\n\nfunction decodeArraysToJson(decoder: TextDecoder, data: ArrayLike<number>[]) {\n return JSON.parse(decoder.decode(joinArrays(data)));\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ //#region src/utils/sse.d.ts
2
+ declare function BytesLineDecoder(): TransformStream<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>>;
3
+ interface StreamPart {
4
+ id: string | undefined;
5
+ event: string;
6
+ data: unknown;
7
+ }
8
+ declare function SSEDecoder(): TransformStream<Uint8Array<ArrayBufferLike>, StreamPart>;
9
+ //#endregion
10
+ export { BytesLineDecoder, SSEDecoder };
11
+ //# sourceMappingURL=sse.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.cts","names":[],"sources":["../../src/utils/sse.ts"],"mappings":";iBAQgB,gBAAA,CAAA,GAAgB,eAAA,CAAA,UAAA,CAAA,eAAA,GAAA,UAAA,CAAA,eAAA;AAAA,UAoFtB,UAAA;EACR,EAAA;EACA,KAAA;EACA,IAAA;AAAA;AAAA,iBAGc,UAAA,CAAA,GAAU,eAAA,CAAA,UAAA,CAAA,eAAA,GAAA,UAAA"}
@@ -0,0 +1,11 @@
1
+ //#region src/utils/sse.d.ts
2
+ declare function BytesLineDecoder(): TransformStream<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>>;
3
+ interface StreamPart {
4
+ id: string | undefined;
5
+ event: string;
6
+ data: unknown;
7
+ }
8
+ declare function SSEDecoder(): TransformStream<Uint8Array<ArrayBufferLike>, StreamPart>;
9
+ //#endregion
10
+ export { BytesLineDecoder, SSEDecoder };
11
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","names":[],"sources":["../../src/utils/sse.ts"],"mappings":";iBAQgB,gBAAA,CAAA,GAAgB,eAAA,CAAA,UAAA,CAAA,eAAA,GAAA,UAAA,CAAA,eAAA;AAAA,UAoFtB,UAAA;EACR,EAAA;EACA,KAAA;EACA,IAAA;AAAA;AAAA,iBAGc,UAAA,CAAA,GAAU,eAAA,CAAA,UAAA,CAAA,eAAA,GAAA,UAAA"}
package/dist/utils/sse.js CHANGED
@@ -116,7 +116,7 @@ function joinArrays(data) {
116
116
  function decodeArraysToJson(decoder, data) {
117
117
  return JSON.parse(decoder.decode(joinArrays(data)));
118
118
  }
119
-
120
119
  //#endregion
121
120
  export { BytesLineDecoder, SSEDecoder };
121
+
122
122
  //# sourceMappingURL=sse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sse.js","names":[],"sources":["../../src/utils/sse.ts"],"sourcesContent":["const CR = \"\\r\".charCodeAt(0);\nconst LF = \"\\n\".charCodeAt(0);\nconst NULL = \"\\0\".charCodeAt(0);\nconst COLON = \":\".charCodeAt(0);\nconst SPACE = \" \".charCodeAt(0);\n\nconst TRAILING_NEWLINE = [CR, LF];\n\nexport function BytesLineDecoder() {\n let buffer: Uint8Array[] = [];\n let trailingCr = false;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n start() {\n buffer = [];\n trailingCr = false;\n },\n\n transform(chunk, controller) {\n // See https://docs.python.org/3/glossary.html#term-universal-newlines\n let text = chunk;\n\n // Handle trailing CR from previous chunk\n if (trailingCr) {\n text = joinArrays([[CR], text]);\n trailingCr = false;\n }\n\n // Check for trailing CR in current chunk\n if (text.length > 0 && text.at(-1) === CR) {\n trailingCr = true;\n text = text.subarray(0, -1);\n }\n\n if (!text.length) return;\n const trailingNewline = TRAILING_NEWLINE.includes(text.at(-1)!);\n\n const lastIdx = text.length - 1;\n const { lines } = text.reduce<{ lines: Uint8Array[]; from: number }>(\n (acc, cur, idx) => {\n if (acc.from > idx) return acc;\n\n if (cur === CR || cur === LF) {\n acc.lines.push(text.subarray(acc.from, idx));\n if (cur === CR && text[idx + 1] === LF) {\n acc.from = idx + 2;\n } else {\n acc.from = idx + 1;\n }\n }\n\n if (idx === lastIdx && acc.from <= lastIdx) {\n acc.lines.push(text.subarray(acc.from));\n }\n\n return acc;\n },\n { lines: [], from: 0 }\n );\n\n if (lines.length === 1 && !trailingNewline) {\n buffer.push(lines[0]);\n return;\n }\n\n if (buffer.length) {\n // Include existing buffer in first line\n buffer.push(lines[0]);\n lines[0] = joinArrays(buffer);\n buffer = [];\n }\n\n if (!trailingNewline) {\n // If the last segment is not newline terminated,\n // buffer it for the next chunk\n if (lines.length) buffer = [lines.pop()!];\n }\n\n // Enqueue complete lines\n for (const line of lines) {\n controller.enqueue(line);\n }\n },\n\n flush(controller) {\n if (buffer.length) {\n controller.enqueue(joinArrays(buffer));\n }\n },\n });\n}\n\ninterface StreamPart {\n id: string | undefined;\n event: string;\n data: unknown;\n}\n\nexport function SSEDecoder() {\n let event = \"\";\n let data: Uint8Array[] = [];\n let lastEventId = \"\";\n let retry: number | null = null;\n\n const decoder = new TextDecoder();\n\n return new TransformStream<Uint8Array, StreamPart>({\n transform(chunk, controller) {\n // Handle empty line case\n if (!chunk.length) {\n if (!event && !data.length && !lastEventId && retry == null) return;\n\n const sse = {\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n };\n\n // NOTE: as per the SSE spec, do not reset lastEventId\n event = \"\";\n data = [];\n retry = null;\n\n controller.enqueue(sse);\n return;\n }\n\n // Ignore comments\n if (chunk[0] === COLON) return;\n\n const sepIdx = chunk.indexOf(COLON);\n if (sepIdx === -1) return;\n\n const fieldName = decoder.decode(chunk.subarray(0, sepIdx));\n let value = chunk.subarray(sepIdx + 1);\n if (value[0] === SPACE) value = value.subarray(1);\n\n if (fieldName === \"event\") {\n event = decoder.decode(value);\n } else if (fieldName === \"data\") {\n data.push(value);\n } else if (fieldName === \"id\") {\n if (value.indexOf(NULL) === -1) lastEventId = decoder.decode(value);\n } else if (fieldName === \"retry\") {\n const retryNum = Number.parseInt(decoder.decode(value), 10);\n if (!Number.isNaN(retryNum)) retry = retryNum;\n }\n },\n\n flush(controller) {\n if (event) {\n controller.enqueue({\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n });\n }\n },\n });\n}\n\nfunction joinArrays(data: ArrayLike<number>[]) {\n const totalLength = data.reduce((acc, curr) => acc + curr.length, 0);\n const merged = new Uint8Array(totalLength);\n let offset = 0;\n for (const c of data) {\n merged.set(c, offset);\n offset += c.length;\n }\n return merged;\n}\n\nfunction decodeArraysToJson(decoder: TextDecoder, data: ArrayLike<number>[]) {\n return JSON.parse(decoder.decode(joinArrays(data)));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sse.js","names":[],"sources":["../../src/utils/sse.ts"],"sourcesContent":["const CR = \"\\r\".charCodeAt(0);\nconst LF = \"\\n\".charCodeAt(0);\nconst NULL = \"\\0\".charCodeAt(0);\nconst COLON = \":\".charCodeAt(0);\nconst SPACE = \" \".charCodeAt(0);\n\nconst TRAILING_NEWLINE = [CR, LF];\n\nexport function BytesLineDecoder() {\n let buffer: Uint8Array[] = [];\n let trailingCr = false;\n\n return new TransformStream<Uint8Array, Uint8Array>({\n start() {\n buffer = [];\n trailingCr = false;\n },\n\n transform(chunk, controller) {\n // See https://docs.python.org/3/glossary.html#term-universal-newlines\n let text = chunk;\n\n // Handle trailing CR from previous chunk\n if (trailingCr) {\n text = joinArrays([[CR], text]);\n trailingCr = false;\n }\n\n // Check for trailing CR in current chunk\n if (text.length > 0 && text.at(-1) === CR) {\n trailingCr = true;\n text = text.subarray(0, -1);\n }\n\n if (!text.length) return;\n const trailingNewline = TRAILING_NEWLINE.includes(text.at(-1)!);\n\n const lastIdx = text.length - 1;\n const { lines } = text.reduce<{ lines: Uint8Array[]; from: number }>(\n (acc, cur, idx) => {\n if (acc.from > idx) return acc;\n\n if (cur === CR || cur === LF) {\n acc.lines.push(text.subarray(acc.from, idx));\n if (cur === CR && text[idx + 1] === LF) {\n acc.from = idx + 2;\n } else {\n acc.from = idx + 1;\n }\n }\n\n if (idx === lastIdx && acc.from <= lastIdx) {\n acc.lines.push(text.subarray(acc.from));\n }\n\n return acc;\n },\n { lines: [], from: 0 }\n );\n\n if (lines.length === 1 && !trailingNewline) {\n buffer.push(lines[0]);\n return;\n }\n\n if (buffer.length) {\n // Include existing buffer in first line\n buffer.push(lines[0]);\n lines[0] = joinArrays(buffer);\n buffer = [];\n }\n\n if (!trailingNewline) {\n // If the last segment is not newline terminated,\n // buffer it for the next chunk\n if (lines.length) buffer = [lines.pop()!];\n }\n\n // Enqueue complete lines\n for (const line of lines) {\n controller.enqueue(line);\n }\n },\n\n flush(controller) {\n if (buffer.length) {\n controller.enqueue(joinArrays(buffer));\n }\n },\n });\n}\n\ninterface StreamPart {\n id: string | undefined;\n event: string;\n data: unknown;\n}\n\nexport function SSEDecoder() {\n let event = \"\";\n let data: Uint8Array[] = [];\n let lastEventId = \"\";\n let retry: number | null = null;\n\n const decoder = new TextDecoder();\n\n return new TransformStream<Uint8Array, StreamPart>({\n transform(chunk, controller) {\n // Handle empty line case\n if (!chunk.length) {\n if (!event && !data.length && !lastEventId && retry == null) return;\n\n const sse = {\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n };\n\n // NOTE: as per the SSE spec, do not reset lastEventId\n event = \"\";\n data = [];\n retry = null;\n\n controller.enqueue(sse);\n return;\n }\n\n // Ignore comments\n if (chunk[0] === COLON) return;\n\n const sepIdx = chunk.indexOf(COLON);\n if (sepIdx === -1) return;\n\n const fieldName = decoder.decode(chunk.subarray(0, sepIdx));\n let value = chunk.subarray(sepIdx + 1);\n if (value[0] === SPACE) value = value.subarray(1);\n\n if (fieldName === \"event\") {\n event = decoder.decode(value);\n } else if (fieldName === \"data\") {\n data.push(value);\n } else if (fieldName === \"id\") {\n if (value.indexOf(NULL) === -1) lastEventId = decoder.decode(value);\n } else if (fieldName === \"retry\") {\n const retryNum = Number.parseInt(decoder.decode(value), 10);\n if (!Number.isNaN(retryNum)) retry = retryNum;\n }\n },\n\n flush(controller) {\n if (event) {\n controller.enqueue({\n id: lastEventId || undefined,\n event,\n data: data.length ? decodeArraysToJson(decoder, data) : null,\n });\n }\n },\n });\n}\n\nfunction joinArrays(data: ArrayLike<number>[]) {\n const totalLength = data.reduce((acc, curr) => acc + curr.length, 0);\n const merged = new Uint8Array(totalLength);\n let offset = 0;\n for (const c of data) {\n merged.set(c, offset);\n offset += c.length;\n }\n return merged;\n}\n\nfunction decodeArraysToJson(decoder: TextDecoder, data: ArrayLike<number>[]) {\n return JSON.parse(decoder.decode(joinArrays(data)));\n}\n"],"mappings":"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"}
@@ -1,5 +1,4 @@
1
- const require_error = require('./error.cjs');
2
-
1
+ const require_error = require("./error.cjs");
3
2
  //#region src/utils/stream.ts
4
3
  /**
5
4
  * Error thrown when maximum reconnection attempts are exceeded.
@@ -167,8 +166,8 @@ var IterableReadableStream = class IterableReadableStream extends ReadableStream
167
166
  });
168
167
  }
169
168
  };
170
-
171
169
  //#endregion
172
170
  exports.IterableReadableStream = IterableReadableStream;
173
171
  exports.streamWithRetry = streamWithRetry;
172
+
174
173
  //# sourceMappingURL=stream.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream.cjs","names":["isNetworkError"],"sources":["../../src/utils/stream.ts"],"sourcesContent":["import { isNetworkError } from \"./error.js\";\n\n// in this case don't quite match.\ntype IterableReadableStreamInterface<T> = ReadableStream<T> & AsyncIterable<T>;\n\n/**\n * Options for streaming with automatic retry logic.\n */\nexport interface StreamWithRetryOptions {\n /**\n * Maximum number of reconnection attempts. Default is 5.\n */\n maxRetries?: number;\n\n /**\n * AbortSignal to cancel the stream.\n */\n signal?: AbortSignal;\n\n /**\n * Callback invoked when a reconnection attempt is made.\n */\n onReconnect?: (options: {\n attempt: number;\n lastEventId?: string;\n cause: unknown;\n }) => void;\n}\n\n/**\n * Parameters for making a stream request\n */\nexport interface StreamRequestParams {\n /**\n * Last event ID to resume from, if available\n */\n lastEventId?: string;\n\n /**\n * Optional reconnection path from the Location header\n */\n reconnectPath?: string;\n}\n\n/**\n * Error thrown when maximum reconnection attempts are exceeded.\n */\nexport class MaxReconnectAttemptsError extends Error {\n constructor(maxAttempts: number, cause: unknown) {\n super(`Exceeded maximum SSE reconnection attempts (${maxAttempts})`);\n this.name = \"MaxReconnectAttemptsError\";\n this.cause = cause;\n }\n}\n\n/**\n * Stream with automatic retry logic for SSE connections.\n * Implements reconnection behavior similar to the Python SDK.\n *\n * @param makeRequest Function to make requests. When `params` is undefined/empty, it's the initial request.\n * When `params.reconnectPath` is provided, it's a reconnection request.\n * @param options Configuration options\n * @returns AsyncGenerator yielding stream events\n */\nexport async function* streamWithRetry<T extends { id?: string }>(\n makeRequest: (params?: StreamRequestParams) => Promise<{\n response: Response;\n stream: ReadableStream<T>;\n }>,\n options: StreamWithRetryOptions = {}\n): AsyncGenerator<T> {\n const maxRetries = options.maxRetries ?? 5;\n let attempt = 0;\n let lastEventId: string | undefined;\n let reconnectPath: string | undefined;\n\n while (true) {\n let shouldRetry = false;\n let lastError: unknown;\n let reader: ReadableStreamDefaultReader<T> | undefined;\n\n try {\n // Check if aborted before making request\n if (options.signal?.aborted) return;\n\n // Make request - initial if no reconnect path, reconnect otherwise\n const { response, stream } = await makeRequest(\n reconnectPath ? { lastEventId, reconnectPath } : undefined\n );\n\n // Check for Location header (server-provided reconnection path)\n const locationHeader = response.headers.get(\"location\");\n if (locationHeader) {\n reconnectPath = locationHeader;\n }\n\n // Verify content type\n const contentType = response.headers.get(\"content-type\")?.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 reader = stream.getReader();\n\n try {\n while (true) {\n // Check abort signal before each read\n if (options.signal?.aborted) {\n await reader.cancel();\n return;\n }\n\n const { done, value } = await reader.read();\n\n if (done) {\n // Stream completed successfully\n break;\n }\n\n // Track last event ID for reconnection\n if (value.id) {\n lastEventId = value.id;\n }\n\n yield value;\n }\n\n // Stream completed successfully, exit retry loop\n break;\n } catch (error) {\n // Error during streaming - attempt reconnect if we have a location header\n if (reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore errors when releasing lock\n }\n }\n }\n } catch (error) {\n lastError = error;\n\n // Only retry if we have reconnection capability and it's a network error\n if (isNetworkError(error) && reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n }\n\n if (shouldRetry) {\n attempt += 1;\n if (attempt > maxRetries) {\n throw new MaxReconnectAttemptsError(maxRetries, lastError);\n }\n\n // Notify about reconnection attempt\n options.onReconnect?.({ attempt, lastEventId, cause: lastError });\n\n // Exponential backoff with jitter: min(1000 * 2^attempt, 5000) + random jitter\n const baseDelay = Math.min(1000 * 2 ** (attempt - 1), 5000);\n const jitter = Math.random() * 1000;\n const delay = baseDelay + jitter;\n\n await new Promise((resolve) => {\n setTimeout(resolve, delay);\n });\n\n continue;\n }\n\n // Successfully completed\n break;\n }\n}\n\n/*\n * Support async iterator syntax for ReadableStreams in all environments.\n * Source: https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490\n */\nexport class IterableReadableStream<T>\n extends ReadableStream<T>\n implements IterableReadableStreamInterface<T>\n{\n public reader: ReadableStreamDefaultReader<T>;\n\n ensureReader() {\n if (!this.reader) {\n this.reader = this.getReader();\n }\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.ensureReader();\n try {\n const result = await this.reader.read();\n if (result.done) {\n this.reader.releaseLock(); // release lock when stream becomes closed\n return {\n done: true,\n value: undefined,\n };\n } else {\n return {\n done: false,\n value: result.value,\n };\n }\n } catch (e) {\n this.reader.releaseLock(); // release lock when stream becomes errored\n throw e;\n }\n }\n\n async return(): Promise<IteratorResult<T>> {\n this.ensureReader();\n // If wrapped in a Node stream, cancel is already called.\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n return { done: true, value: undefined };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async throw(e: any): Promise<IteratorResult<T>> {\n this.ensureReader();\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n throw e;\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not present in Node 18 types, required in latest Node 22\n async [Symbol.asyncDispose]() {\n await this.return();\n }\n\n [Symbol.asyncIterator]() {\n return this;\n }\n\n static fromReadableStream<T>(stream: ReadableStream<T>) {\n // From https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#reading_the_stream\n const reader = stream.getReader();\n return new IterableReadableStream<T>({\n start(controller) {\n return pump();\n function pump(): Promise<T | undefined> {\n return reader.read().then(({ done, value }) => {\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n return;\n }\n // Enqueue the next data chunk into our target stream\n controller.enqueue(value);\n return pump();\n });\n }\n },\n cancel() {\n reader.releaseLock();\n },\n });\n }\n\n static fromAsyncGenerator<T>(generator: AsyncGenerator<T>) {\n return new IterableReadableStream<T>({\n async pull(controller) {\n const { value, done } = await generator.next();\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n }\n // Fix: `else if (value)` will hang the streaming when nullish value (e.g. empty string) is pulled\n controller.enqueue(value);\n },\n async cancel(reason) {\n await generator.return(reason);\n },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stream.cjs","names":["isNetworkError"],"sources":["../../src/utils/stream.ts"],"sourcesContent":["import { isNetworkError } from \"./error.js\";\n\n// in this case don't quite match.\ntype IterableReadableStreamInterface<T> = ReadableStream<T> & AsyncIterable<T>;\n\n/**\n * Options for streaming with automatic retry logic.\n */\nexport interface StreamWithRetryOptions {\n /**\n * Maximum number of reconnection attempts. Default is 5.\n */\n maxRetries?: number;\n\n /**\n * AbortSignal to cancel the stream.\n */\n signal?: AbortSignal;\n\n /**\n * Callback invoked when a reconnection attempt is made.\n */\n onReconnect?: (options: {\n attempt: number;\n lastEventId?: string;\n cause: unknown;\n }) => void;\n}\n\n/**\n * Parameters for making a stream request\n */\nexport interface StreamRequestParams {\n /**\n * Last event ID to resume from, if available\n */\n lastEventId?: string;\n\n /**\n * Optional reconnection path from the Location header\n */\n reconnectPath?: string;\n}\n\n/**\n * Error thrown when maximum reconnection attempts are exceeded.\n */\nexport class MaxReconnectAttemptsError extends Error {\n constructor(maxAttempts: number, cause: unknown) {\n super(`Exceeded maximum SSE reconnection attempts (${maxAttempts})`);\n this.name = \"MaxReconnectAttemptsError\";\n this.cause = cause;\n }\n}\n\n/**\n * Stream with automatic retry logic for SSE connections.\n * Implements reconnection behavior similar to the Python SDK.\n *\n * @param makeRequest Function to make requests. When `params` is undefined/empty, it's the initial request.\n * When `params.reconnectPath` is provided, it's a reconnection request.\n * @param options Configuration options\n * @returns AsyncGenerator yielding stream events\n */\nexport async function* streamWithRetry<T extends { id?: string }>(\n makeRequest: (params?: StreamRequestParams) => Promise<{\n response: Response;\n stream: ReadableStream<T>;\n }>,\n options: StreamWithRetryOptions = {}\n): AsyncGenerator<T> {\n const maxRetries = options.maxRetries ?? 5;\n let attempt = 0;\n let lastEventId: string | undefined;\n let reconnectPath: string | undefined;\n\n while (true) {\n let shouldRetry = false;\n let lastError: unknown;\n let reader: ReadableStreamDefaultReader<T> | undefined;\n\n try {\n // Check if aborted before making request\n if (options.signal?.aborted) return;\n\n // Make request - initial if no reconnect path, reconnect otherwise\n const { response, stream } = await makeRequest(\n reconnectPath ? { lastEventId, reconnectPath } : undefined\n );\n\n // Check for Location header (server-provided reconnection path)\n const locationHeader = response.headers.get(\"location\");\n if (locationHeader) {\n reconnectPath = locationHeader;\n }\n\n // Verify content type\n const contentType = response.headers.get(\"content-type\")?.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 reader = stream.getReader();\n\n try {\n while (true) {\n // Check abort signal before each read\n if (options.signal?.aborted) {\n await reader.cancel();\n return;\n }\n\n const { done, value } = await reader.read();\n\n if (done) {\n // Stream completed successfully\n break;\n }\n\n // Track last event ID for reconnection\n if (value.id) {\n lastEventId = value.id;\n }\n\n yield value;\n }\n\n // Stream completed successfully, exit retry loop\n break;\n } catch (error) {\n // Error during streaming - attempt reconnect if we have a location header\n if (reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore errors when releasing lock\n }\n }\n }\n } catch (error) {\n lastError = error;\n\n // Only retry if we have reconnection capability and it's a network error\n if (isNetworkError(error) && reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n }\n\n if (shouldRetry) {\n attempt += 1;\n if (attempt > maxRetries) {\n throw new MaxReconnectAttemptsError(maxRetries, lastError);\n }\n\n // Notify about reconnection attempt\n options.onReconnect?.({ attempt, lastEventId, cause: lastError });\n\n // Exponential backoff with jitter: min(1000 * 2^attempt, 5000) + random jitter\n const baseDelay = Math.min(1000 * 2 ** (attempt - 1), 5000);\n const jitter = Math.random() * 1000;\n const delay = baseDelay + jitter;\n\n await new Promise((resolve) => {\n setTimeout(resolve, delay);\n });\n\n continue;\n }\n\n // Successfully completed\n break;\n }\n}\n\n/*\n * Support async iterator syntax for ReadableStreams in all environments.\n * Source: https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490\n */\nexport class IterableReadableStream<T>\n extends ReadableStream<T>\n implements IterableReadableStreamInterface<T>\n{\n public reader: ReadableStreamDefaultReader<T>;\n\n ensureReader() {\n if (!this.reader) {\n this.reader = this.getReader();\n }\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.ensureReader();\n try {\n const result = await this.reader.read();\n if (result.done) {\n this.reader.releaseLock(); // release lock when stream becomes closed\n return {\n done: true,\n value: undefined,\n };\n } else {\n return {\n done: false,\n value: result.value,\n };\n }\n } catch (e) {\n this.reader.releaseLock(); // release lock when stream becomes errored\n throw e;\n }\n }\n\n async return(): Promise<IteratorResult<T>> {\n this.ensureReader();\n // If wrapped in a Node stream, cancel is already called.\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n return { done: true, value: undefined };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async throw(e: any): Promise<IteratorResult<T>> {\n this.ensureReader();\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n throw e;\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not present in Node 18 types, required in latest Node 22\n async [Symbol.asyncDispose]() {\n await this.return();\n }\n\n [Symbol.asyncIterator]() {\n return this;\n }\n\n static fromReadableStream<T>(stream: ReadableStream<T>) {\n // From https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#reading_the_stream\n const reader = stream.getReader();\n return new IterableReadableStream<T>({\n start(controller) {\n return pump();\n function pump(): Promise<T | undefined> {\n return reader.read().then(({ done, value }) => {\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n return;\n }\n // Enqueue the next data chunk into our target stream\n controller.enqueue(value);\n return pump();\n });\n }\n },\n cancel() {\n reader.releaseLock();\n },\n });\n }\n\n static fromAsyncGenerator<T>(generator: AsyncGenerator<T>) {\n return new IterableReadableStream<T>({\n async pull(controller) {\n const { value, done } = await generator.next();\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n }\n // Fix: `else if (value)` will hang the streaming when nullish value (e.g. empty string) is pulled\n controller.enqueue(value);\n },\n async cancel(reason) {\n await generator.return(reason);\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ //#region src/utils/stream.d.ts
2
+ type IterableReadableStreamInterface<T> = ReadableStream<T> & AsyncIterable<T>;
3
+ /**
4
+ * Options for streaming with automatic retry logic.
5
+ */
6
+ declare class IterableReadableStream<T> extends ReadableStream<T> implements IterableReadableStreamInterface<T> {
7
+ reader: ReadableStreamDefaultReader<T>;
8
+ ensureReader(): void;
9
+ next(): Promise<IteratorResult<T>>;
10
+ return(): Promise<IteratorResult<T>>;
11
+ throw(e: any): Promise<IteratorResult<T>>;
12
+ [Symbol.asyncDispose](): Promise<void>;
13
+ [Symbol.asyncIterator](): this;
14
+ static fromReadableStream<T>(stream: ReadableStream<T>): IterableReadableStream<T>;
15
+ static fromAsyncGenerator<T>(generator: AsyncGenerator<T>): IterableReadableStream<T>;
16
+ }
17
+ //#endregion
18
+ export { IterableReadableStream };
19
+ //# sourceMappingURL=stream.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.cts","names":[],"sources":["../../src/utils/stream.ts"],"mappings":";KAGK,+BAAA,MAAqC,cAAA,CAAe,CAAA,IAAK,aAAA,CAAc,CAAA;;;;cAyL/D,sBAAA,YACH,cAAA,CAAe,CAAA,aACZ,+BAAA,CAAgC,CAAA;EAEpC,MAAA,EAAQ,2BAAA,CAA4B,CAAA;EAE3C,YAAA,CAAA;EAMM,IAAA,CAAA,GAAQ,OAAA,CAAQ,cAAA,CAAe,CAAA;EAsB/B,MAAA,CAAA,GAAU,OAAA,CAAQ,cAAA,CAAe,CAAA;EAYjC,KAAA,CAAM,CAAA,QAAS,OAAA,CAAQ,cAAA,CAAe,CAAA;EAAA,CAYrC,MAAA,CAAO,YAAA,KAAa,OAAA;EAAA,CAI1B,MAAA,CAAO,aAAA;EAAA,OAID,kBAAA,GAAA,CAAsB,MAAA,EAAQ,cAAA,CAAe,CAAA,IAAE,sBAAA,CAAA,CAAA;EAAA,OAyB/C,kBAAA,GAAA,CAAsB,SAAA,EAAW,cAAA,CAAe,CAAA,IAAE,sBAAA,CAAA,CAAA;AAAA"}
@@ -0,0 +1,19 @@
1
+ //#region src/utils/stream.d.ts
2
+ type IterableReadableStreamInterface<T> = ReadableStream<T> & AsyncIterable<T>;
3
+ /**
4
+ * Options for streaming with automatic retry logic.
5
+ */
6
+ declare class IterableReadableStream<T> extends ReadableStream<T> implements IterableReadableStreamInterface<T> {
7
+ reader: ReadableStreamDefaultReader<T>;
8
+ ensureReader(): void;
9
+ next(): Promise<IteratorResult<T>>;
10
+ return(): Promise<IteratorResult<T>>;
11
+ throw(e: any): Promise<IteratorResult<T>>;
12
+ [Symbol.asyncDispose](): Promise<void>;
13
+ [Symbol.asyncIterator](): this;
14
+ static fromReadableStream<T>(stream: ReadableStream<T>): IterableReadableStream<T>;
15
+ static fromAsyncGenerator<T>(generator: AsyncGenerator<T>): IterableReadableStream<T>;
16
+ }
17
+ //#endregion
18
+ export { IterableReadableStream };
19
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","names":[],"sources":["../../src/utils/stream.ts"],"mappings":";KAGK,+BAAA,MAAqC,cAAA,CAAe,CAAA,IAAK,aAAA,CAAc,CAAA;;;;cAyL/D,sBAAA,YACH,cAAA,CAAe,CAAA,aACZ,+BAAA,CAAgC,CAAA;EAEpC,MAAA,EAAQ,2BAAA,CAA4B,CAAA;EAE3C,YAAA,CAAA;EAMM,IAAA,CAAA,GAAQ,OAAA,CAAQ,cAAA,CAAe,CAAA;EAsB/B,MAAA,CAAA,GAAU,OAAA,CAAQ,cAAA,CAAe,CAAA;EAYjC,KAAA,CAAM,CAAA,QAAS,OAAA,CAAQ,cAAA,CAAe,CAAA;EAAA,CAYrC,MAAA,CAAO,YAAA,KAAa,OAAA;EAAA,CAI1B,MAAA,CAAO,aAAA;EAAA,OAID,kBAAA,GAAA,CAAsB,MAAA,EAAQ,cAAA,CAAe,CAAA,IAAE,sBAAA,CAAA,CAAA;EAAA,OAyB/C,kBAAA,GAAA,CAAsB,SAAA,EAAW,cAAA,CAAe,CAAA,IAAE,sBAAA,CAAA,CAAA;AAAA"}
@@ -1,5 +1,4 @@
1
1
  import { isNetworkError } from "./error.js";
2
-
3
2
  //#region src/utils/stream.ts
4
3
  /**
5
4
  * Error thrown when maximum reconnection attempts are exceeded.
@@ -167,7 +166,7 @@ var IterableReadableStream = class IterableReadableStream extends ReadableStream
167
166
  });
168
167
  }
169
168
  };
170
-
171
169
  //#endregion
172
170
  export { IterableReadableStream, streamWithRetry };
171
+
173
172
  //# sourceMappingURL=stream.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","names":[],"sources":["../../src/utils/stream.ts"],"sourcesContent":["import { isNetworkError } from \"./error.js\";\n\n// in this case don't quite match.\ntype IterableReadableStreamInterface<T> = ReadableStream<T> & AsyncIterable<T>;\n\n/**\n * Options for streaming with automatic retry logic.\n */\nexport interface StreamWithRetryOptions {\n /**\n * Maximum number of reconnection attempts. Default is 5.\n */\n maxRetries?: number;\n\n /**\n * AbortSignal to cancel the stream.\n */\n signal?: AbortSignal;\n\n /**\n * Callback invoked when a reconnection attempt is made.\n */\n onReconnect?: (options: {\n attempt: number;\n lastEventId?: string;\n cause: unknown;\n }) => void;\n}\n\n/**\n * Parameters for making a stream request\n */\nexport interface StreamRequestParams {\n /**\n * Last event ID to resume from, if available\n */\n lastEventId?: string;\n\n /**\n * Optional reconnection path from the Location header\n */\n reconnectPath?: string;\n}\n\n/**\n * Error thrown when maximum reconnection attempts are exceeded.\n */\nexport class MaxReconnectAttemptsError extends Error {\n constructor(maxAttempts: number, cause: unknown) {\n super(`Exceeded maximum SSE reconnection attempts (${maxAttempts})`);\n this.name = \"MaxReconnectAttemptsError\";\n this.cause = cause;\n }\n}\n\n/**\n * Stream with automatic retry logic for SSE connections.\n * Implements reconnection behavior similar to the Python SDK.\n *\n * @param makeRequest Function to make requests. When `params` is undefined/empty, it's the initial request.\n * When `params.reconnectPath` is provided, it's a reconnection request.\n * @param options Configuration options\n * @returns AsyncGenerator yielding stream events\n */\nexport async function* streamWithRetry<T extends { id?: string }>(\n makeRequest: (params?: StreamRequestParams) => Promise<{\n response: Response;\n stream: ReadableStream<T>;\n }>,\n options: StreamWithRetryOptions = {}\n): AsyncGenerator<T> {\n const maxRetries = options.maxRetries ?? 5;\n let attempt = 0;\n let lastEventId: string | undefined;\n let reconnectPath: string | undefined;\n\n while (true) {\n let shouldRetry = false;\n let lastError: unknown;\n let reader: ReadableStreamDefaultReader<T> | undefined;\n\n try {\n // Check if aborted before making request\n if (options.signal?.aborted) return;\n\n // Make request - initial if no reconnect path, reconnect otherwise\n const { response, stream } = await makeRequest(\n reconnectPath ? { lastEventId, reconnectPath } : undefined\n );\n\n // Check for Location header (server-provided reconnection path)\n const locationHeader = response.headers.get(\"location\");\n if (locationHeader) {\n reconnectPath = locationHeader;\n }\n\n // Verify content type\n const contentType = response.headers.get(\"content-type\")?.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 reader = stream.getReader();\n\n try {\n while (true) {\n // Check abort signal before each read\n if (options.signal?.aborted) {\n await reader.cancel();\n return;\n }\n\n const { done, value } = await reader.read();\n\n if (done) {\n // Stream completed successfully\n break;\n }\n\n // Track last event ID for reconnection\n if (value.id) {\n lastEventId = value.id;\n }\n\n yield value;\n }\n\n // Stream completed successfully, exit retry loop\n break;\n } catch (error) {\n // Error during streaming - attempt reconnect if we have a location header\n if (reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore errors when releasing lock\n }\n }\n }\n } catch (error) {\n lastError = error;\n\n // Only retry if we have reconnection capability and it's a network error\n if (isNetworkError(error) && reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n }\n\n if (shouldRetry) {\n attempt += 1;\n if (attempt > maxRetries) {\n throw new MaxReconnectAttemptsError(maxRetries, lastError);\n }\n\n // Notify about reconnection attempt\n options.onReconnect?.({ attempt, lastEventId, cause: lastError });\n\n // Exponential backoff with jitter: min(1000 * 2^attempt, 5000) + random jitter\n const baseDelay = Math.min(1000 * 2 ** (attempt - 1), 5000);\n const jitter = Math.random() * 1000;\n const delay = baseDelay + jitter;\n\n await new Promise((resolve) => {\n setTimeout(resolve, delay);\n });\n\n continue;\n }\n\n // Successfully completed\n break;\n }\n}\n\n/*\n * Support async iterator syntax for ReadableStreams in all environments.\n * Source: https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490\n */\nexport class IterableReadableStream<T>\n extends ReadableStream<T>\n implements IterableReadableStreamInterface<T>\n{\n public reader: ReadableStreamDefaultReader<T>;\n\n ensureReader() {\n if (!this.reader) {\n this.reader = this.getReader();\n }\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.ensureReader();\n try {\n const result = await this.reader.read();\n if (result.done) {\n this.reader.releaseLock(); // release lock when stream becomes closed\n return {\n done: true,\n value: undefined,\n };\n } else {\n return {\n done: false,\n value: result.value,\n };\n }\n } catch (e) {\n this.reader.releaseLock(); // release lock when stream becomes errored\n throw e;\n }\n }\n\n async return(): Promise<IteratorResult<T>> {\n this.ensureReader();\n // If wrapped in a Node stream, cancel is already called.\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n return { done: true, value: undefined };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async throw(e: any): Promise<IteratorResult<T>> {\n this.ensureReader();\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n throw e;\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not present in Node 18 types, required in latest Node 22\n async [Symbol.asyncDispose]() {\n await this.return();\n }\n\n [Symbol.asyncIterator]() {\n return this;\n }\n\n static fromReadableStream<T>(stream: ReadableStream<T>) {\n // From https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#reading_the_stream\n const reader = stream.getReader();\n return new IterableReadableStream<T>({\n start(controller) {\n return pump();\n function pump(): Promise<T | undefined> {\n return reader.read().then(({ done, value }) => {\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n return;\n }\n // Enqueue the next data chunk into our target stream\n controller.enqueue(value);\n return pump();\n });\n }\n },\n cancel() {\n reader.releaseLock();\n },\n });\n }\n\n static fromAsyncGenerator<T>(generator: AsyncGenerator<T>) {\n return new IterableReadableStream<T>({\n async pull(controller) {\n const { value, done } = await generator.next();\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n }\n // Fix: `else if (value)` will hang the streaming when nullish value (e.g. empty string) is pulled\n controller.enqueue(value);\n },\n async cancel(reason) {\n await generator.return(reason);\n },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stream.js","names":[],"sources":["../../src/utils/stream.ts"],"sourcesContent":["import { isNetworkError } from \"./error.js\";\n\n// in this case don't quite match.\ntype IterableReadableStreamInterface<T> = ReadableStream<T> & AsyncIterable<T>;\n\n/**\n * Options for streaming with automatic retry logic.\n */\nexport interface StreamWithRetryOptions {\n /**\n * Maximum number of reconnection attempts. Default is 5.\n */\n maxRetries?: number;\n\n /**\n * AbortSignal to cancel the stream.\n */\n signal?: AbortSignal;\n\n /**\n * Callback invoked when a reconnection attempt is made.\n */\n onReconnect?: (options: {\n attempt: number;\n lastEventId?: string;\n cause: unknown;\n }) => void;\n}\n\n/**\n * Parameters for making a stream request\n */\nexport interface StreamRequestParams {\n /**\n * Last event ID to resume from, if available\n */\n lastEventId?: string;\n\n /**\n * Optional reconnection path from the Location header\n */\n reconnectPath?: string;\n}\n\n/**\n * Error thrown when maximum reconnection attempts are exceeded.\n */\nexport class MaxReconnectAttemptsError extends Error {\n constructor(maxAttempts: number, cause: unknown) {\n super(`Exceeded maximum SSE reconnection attempts (${maxAttempts})`);\n this.name = \"MaxReconnectAttemptsError\";\n this.cause = cause;\n }\n}\n\n/**\n * Stream with automatic retry logic for SSE connections.\n * Implements reconnection behavior similar to the Python SDK.\n *\n * @param makeRequest Function to make requests. When `params` is undefined/empty, it's the initial request.\n * When `params.reconnectPath` is provided, it's a reconnection request.\n * @param options Configuration options\n * @returns AsyncGenerator yielding stream events\n */\nexport async function* streamWithRetry<T extends { id?: string }>(\n makeRequest: (params?: StreamRequestParams) => Promise<{\n response: Response;\n stream: ReadableStream<T>;\n }>,\n options: StreamWithRetryOptions = {}\n): AsyncGenerator<T> {\n const maxRetries = options.maxRetries ?? 5;\n let attempt = 0;\n let lastEventId: string | undefined;\n let reconnectPath: string | undefined;\n\n while (true) {\n let shouldRetry = false;\n let lastError: unknown;\n let reader: ReadableStreamDefaultReader<T> | undefined;\n\n try {\n // Check if aborted before making request\n if (options.signal?.aborted) return;\n\n // Make request - initial if no reconnect path, reconnect otherwise\n const { response, stream } = await makeRequest(\n reconnectPath ? { lastEventId, reconnectPath } : undefined\n );\n\n // Check for Location header (server-provided reconnection path)\n const locationHeader = response.headers.get(\"location\");\n if (locationHeader) {\n reconnectPath = locationHeader;\n }\n\n // Verify content type\n const contentType = response.headers.get(\"content-type\")?.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 reader = stream.getReader();\n\n try {\n while (true) {\n // Check abort signal before each read\n if (options.signal?.aborted) {\n await reader.cancel();\n return;\n }\n\n const { done, value } = await reader.read();\n\n if (done) {\n // Stream completed successfully\n break;\n }\n\n // Track last event ID for reconnection\n if (value.id) {\n lastEventId = value.id;\n }\n\n yield value;\n }\n\n // Stream completed successfully, exit retry loop\n break;\n } catch (error) {\n // Error during streaming - attempt reconnect if we have a location header\n if (reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n } finally {\n if (reader) {\n try {\n reader.releaseLock();\n } catch {\n // Ignore errors when releasing lock\n }\n }\n }\n } catch (error) {\n lastError = error;\n\n // Only retry if we have reconnection capability and it's a network error\n if (isNetworkError(error) && reconnectPath && !options.signal?.aborted) {\n shouldRetry = true;\n } else {\n throw error;\n }\n }\n\n if (shouldRetry) {\n attempt += 1;\n if (attempt > maxRetries) {\n throw new MaxReconnectAttemptsError(maxRetries, lastError);\n }\n\n // Notify about reconnection attempt\n options.onReconnect?.({ attempt, lastEventId, cause: lastError });\n\n // Exponential backoff with jitter: min(1000 * 2^attempt, 5000) + random jitter\n const baseDelay = Math.min(1000 * 2 ** (attempt - 1), 5000);\n const jitter = Math.random() * 1000;\n const delay = baseDelay + jitter;\n\n await new Promise((resolve) => {\n setTimeout(resolve, delay);\n });\n\n continue;\n }\n\n // Successfully completed\n break;\n }\n}\n\n/*\n * Support async iterator syntax for ReadableStreams in all environments.\n * Source: https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490\n */\nexport class IterableReadableStream<T>\n extends ReadableStream<T>\n implements IterableReadableStreamInterface<T>\n{\n public reader: ReadableStreamDefaultReader<T>;\n\n ensureReader() {\n if (!this.reader) {\n this.reader = this.getReader();\n }\n }\n\n async next(): Promise<IteratorResult<T>> {\n this.ensureReader();\n try {\n const result = await this.reader.read();\n if (result.done) {\n this.reader.releaseLock(); // release lock when stream becomes closed\n return {\n done: true,\n value: undefined,\n };\n } else {\n return {\n done: false,\n value: result.value,\n };\n }\n } catch (e) {\n this.reader.releaseLock(); // release lock when stream becomes errored\n throw e;\n }\n }\n\n async return(): Promise<IteratorResult<T>> {\n this.ensureReader();\n // If wrapped in a Node stream, cancel is already called.\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n return { done: true, value: undefined };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async throw(e: any): Promise<IteratorResult<T>> {\n this.ensureReader();\n if (this.locked) {\n const cancelPromise = this.reader.cancel(); // cancel first, but don't await yet\n this.reader.releaseLock(); // release lock first\n await cancelPromise; // now await it\n }\n throw e;\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore Not present in Node 18 types, required in latest Node 22\n async [Symbol.asyncDispose]() {\n await this.return();\n }\n\n [Symbol.asyncIterator]() {\n return this;\n }\n\n static fromReadableStream<T>(stream: ReadableStream<T>) {\n // From https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams#reading_the_stream\n const reader = stream.getReader();\n return new IterableReadableStream<T>({\n start(controller) {\n return pump();\n function pump(): Promise<T | undefined> {\n return reader.read().then(({ done, value }) => {\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n return;\n }\n // Enqueue the next data chunk into our target stream\n controller.enqueue(value);\n return pump();\n });\n }\n },\n cancel() {\n reader.releaseLock();\n },\n });\n }\n\n static fromAsyncGenerator<T>(generator: AsyncGenerator<T>) {\n return new IterableReadableStream<T>({\n async pull(controller) {\n const { value, done } = await generator.next();\n // When no more data needs to be consumed, close the stream\n if (done) {\n controller.close();\n }\n // Fix: `else if (value)` will hang the streaming when nullish value (e.g. empty string) is pulled\n controller.enqueue(value);\n },\n async cancel(reason) {\n await generator.return(reason);\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,3 @@
1
-
2
1
  //#region src/utils/tools.ts
3
2
  /**
4
3
  * Extracts tool calls with their results from a list of messages.
@@ -20,33 +19,42 @@
20
19
  /**
21
20
  * Computes the lifecycle state of a tool call based on its result.
22
21
  */
23
- function computeToolCallState(result) {
24
- if (!result) return "pending";
25
- return result.status === "error" ? "error" : "completed";
22
+ function computeToolCallState(result, impliedCompleted) {
23
+ if (result) return result.status === "error" ? "error" : "completed";
24
+ if (impliedCompleted) return "completed";
25
+ return "pending";
26
26
  }
27
27
  function getToolCallsWithResults(messages) {
28
28
  const results = [];
29
29
  const toolResultsById = /* @__PURE__ */ new Map();
30
30
  for (const msg of messages) if (msg.type === "tool") toolResultsById.set(msg.tool_call_id, msg);
31
- for (const msg of messages) if (msg.type === "ai" && msg.tool_calls && msg.tool_calls.length > 0) {
32
- const aiMessage = msg;
33
- for (let i = 0; i < aiMessage.tool_calls.length; i += 1) {
34
- const call = aiMessage.tool_calls[i];
35
- const callId = call.id;
36
- const result = callId ? toolResultsById.get(callId) : void 0;
37
- results.push({
38
- id: callId ?? `${aiMessage.id ?? "unknown"}-${i}`,
39
- call,
40
- result,
41
- aiMessage,
42
- index: i,
43
- state: computeToolCallState(result)
44
- });
31
+ for (let msgIdx = 0; msgIdx < messages.length; msgIdx += 1) {
32
+ const msg = messages[msgIdx];
33
+ if (msg.type === "ai" && msg.tool_calls && msg.tool_calls.length > 0) {
34
+ const aiMessage = msg;
35
+ let impliedCompleted = false;
36
+ for (let j = msgIdx + 1; j < messages.length; j += 1) if (messages[j].type === "ai") {
37
+ impliedCompleted = true;
38
+ break;
39
+ }
40
+ for (let i = 0; i < aiMessage.tool_calls.length; i += 1) {
41
+ const call = aiMessage.tool_calls[i];
42
+ const callId = call.id;
43
+ const result = callId ? toolResultsById.get(callId) : void 0;
44
+ results.push({
45
+ id: callId ?? `${aiMessage.id ?? "unknown"}-${i}`,
46
+ call,
47
+ result,
48
+ aiMessage,
49
+ index: i,
50
+ state: computeToolCallState(result, impliedCompleted)
51
+ });
52
+ }
45
53
  }
46
54
  }
47
55
  return results;
48
56
  }
49
-
50
57
  //#endregion
51
58
  exports.getToolCallsWithResults = getToolCallsWithResults;
59
+
52
60
  //# sourceMappingURL=tools.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.cjs","names":[],"sources":["../../src/utils/tools.ts"],"sourcesContent":["import type {\n Message,\n AIMessage,\n ToolMessage,\n ToolCallState,\n ToolCallWithResult,\n DefaultToolCall,\n} from \"../types.messages.js\";\n\n/**\n * Extracts tool calls with their results from a list of messages.\n *\n * @template ToolCall The type of tool calls.\n * @param messages The list of messages to extract tool calls from.\n * @returns An array of ToolCallWithResult objects.\n *\n * @example\n * ```ts\n * const toolCalls = getToolCallsWithResults(messages);\n * for (const { call, result } of toolCalls) {\n * if (call.name === \"get_weather\") {\n * console.log(`Weather for ${call.args.location}:`, result?.content);\n * }\n * }\n * ```\n */\n/**\n * Computes the lifecycle state of a tool call based on its result.\n */\nfunction computeToolCallState(result: ToolMessage | undefined): ToolCallState {\n if (!result) return \"pending\";\n return result.status === \"error\" ? \"error\" : \"completed\";\n}\n\nexport function getToolCallsWithResults<ToolCall = DefaultToolCall>(\n messages: Message<ToolCall>[]\n): ToolCallWithResult<ToolCall>[] {\n const results: ToolCallWithResult<ToolCall>[] = [];\n\n // Create a map of tool_call_id to ToolMessage for quick lookup\n const toolResultsById = new Map<string, ToolMessage>();\n for (const msg of messages) {\n if (msg.type === \"tool\") {\n toolResultsById.set(msg.tool_call_id, msg);\n }\n }\n\n // Find all AI messages with tool calls and pair them with results\n for (const msg of messages) {\n if (msg.type === \"ai\" && msg.tool_calls && msg.tool_calls.length > 0) {\n const aiMessage = msg as AIMessage<ToolCall>;\n for (let i = 0; i < aiMessage.tool_calls!.length; i += 1) {\n const call = aiMessage.tool_calls![i] as ToolCall & { id?: string };\n const callId = call.id as string | undefined;\n const result = callId ? toolResultsById.get(callId) : undefined;\n\n results.push({\n id: callId ?? `${aiMessage.id ?? \"unknown\"}-${i}`,\n call,\n result,\n aiMessage,\n index: i,\n state: computeToolCallState(result),\n });\n }\n }\n }\n\n return results;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,qBAAqB,QAAgD;AAC5E,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO,OAAO,WAAW,UAAU,UAAU;;AAG/C,SAAgB,wBACd,UACgC;CAChC,MAAM,UAA0C,EAAE;CAGlD,MAAM,kCAAkB,IAAI,KAA0B;AACtD,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,OACf,iBAAgB,IAAI,IAAI,cAAc,IAAI;AAK9C,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,QAAQ,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;EACpE,MAAM,YAAY;AAClB,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,WAAY,QAAQ,KAAK,GAAG;GACxD,MAAM,OAAO,UAAU,WAAY;GACnC,MAAM,SAAS,KAAK;GACpB,MAAM,SAAS,SAAS,gBAAgB,IAAI,OAAO,GAAG;AAEtD,WAAQ,KAAK;IACX,IAAI,UAAU,GAAG,UAAU,MAAM,UAAU,GAAG;IAC9C;IACA;IACA;IACA,OAAO;IACP,OAAO,qBAAqB,OAAO;IACpC,CAAC;;;AAKR,QAAO"}
1
+ {"version":3,"file":"tools.cjs","names":[],"sources":["../../src/utils/tools.ts"],"sourcesContent":["import type {\n Message,\n AIMessage,\n ToolMessage,\n ToolCallState,\n ToolCallWithResult,\n DefaultToolCall,\n} from \"../types.messages.js\";\n\n/**\n * Extracts tool calls with their results from a list of messages.\n *\n * @template ToolCall The type of tool calls.\n * @param messages The list of messages to extract tool calls from.\n * @returns An array of ToolCallWithResult objects.\n *\n * @example\n * ```ts\n * const toolCalls = getToolCallsWithResults(messages);\n * for (const { call, result } of toolCalls) {\n * if (call.name === \"get_weather\") {\n * console.log(`Weather for ${call.args.location}:`, result?.content);\n * }\n * }\n * ```\n */\n/**\n * Computes the lifecycle state of a tool call based on its result.\n */\nfunction computeToolCallState(\n result: ToolMessage | undefined,\n impliedCompleted: boolean\n): ToolCallState {\n if (result) return result.status === \"error\" ? \"error\" : \"completed\";\n if (impliedCompleted) return \"completed\";\n return \"pending\";\n}\n\nexport function getToolCallsWithResults<ToolCall = DefaultToolCall>(\n messages: Message<ToolCall>[]\n): ToolCallWithResult<ToolCall>[] {\n const results: ToolCallWithResult<ToolCall>[] = [];\n\n // Create a map of tool_call_id to ToolMessage for quick lookup\n const toolResultsById = new Map<string, ToolMessage>();\n for (const msg of messages) {\n if (msg.type === \"tool\") {\n toolResultsById.set(msg.tool_call_id, msg);\n }\n }\n\n // Find all AI messages with tool calls and pair them with results.\n // For each, independently check if there's a subsequent AI message,\n // which implies the tools completed (handles tools returning Commands\n // where ToolMessages are embedded in the state update rather than streamed).\n for (let msgIdx = 0; msgIdx < messages.length; msgIdx += 1) {\n const msg = messages[msgIdx];\n if (msg.type === \"ai\" && msg.tool_calls && msg.tool_calls.length > 0) {\n const aiMessage = msg as AIMessage<ToolCall>;\n\n let impliedCompleted = false;\n for (let j = msgIdx + 1; j < messages.length; j += 1) {\n if (messages[j].type === \"ai\") {\n impliedCompleted = true;\n break;\n }\n }\n\n for (let i = 0; i < aiMessage.tool_calls!.length; i += 1) {\n const call = aiMessage.tool_calls![i] as ToolCall & { id?: string };\n const callId = call.id as string | undefined;\n const result = callId ? toolResultsById.get(callId) : undefined;\n\n results.push({\n id: callId ?? `${aiMessage.id ?? \"unknown\"}-${i}`,\n call,\n result,\n aiMessage,\n index: i,\n state: computeToolCallState(result, impliedCompleted),\n });\n }\n }\n }\n\n return results;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,qBACP,QACA,kBACe;AACf,KAAI,OAAQ,QAAO,OAAO,WAAW,UAAU,UAAU;AACzD,KAAI,iBAAkB,QAAO;AAC7B,QAAO;;AAGT,SAAgB,wBACd,UACgC;CAChC,MAAM,UAA0C,EAAE;CAGlD,MAAM,kCAAkB,IAAI,KAA0B;AACtD,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,OACf,iBAAgB,IAAI,IAAI,cAAc,IAAI;AAQ9C,MAAK,IAAI,SAAS,GAAG,SAAS,SAAS,QAAQ,UAAU,GAAG;EAC1D,MAAM,MAAM,SAAS;AACrB,MAAI,IAAI,SAAS,QAAQ,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;GACpE,MAAM,YAAY;GAElB,IAAI,mBAAmB;AACvB,QAAK,IAAI,IAAI,SAAS,GAAG,IAAI,SAAS,QAAQ,KAAK,EACjD,KAAI,SAAS,GAAG,SAAS,MAAM;AAC7B,uBAAmB;AACnB;;AAIJ,QAAK,IAAI,IAAI,GAAG,IAAI,UAAU,WAAY,QAAQ,KAAK,GAAG;IACxD,MAAM,OAAO,UAAU,WAAY;IACnC,MAAM,SAAS,KAAK;IACpB,MAAM,SAAS,SAAS,gBAAgB,IAAI,OAAO,GAAG,KAAA;AAEtD,YAAQ,KAAK;KACX,IAAI,UAAU,GAAG,UAAU,MAAM,UAAU,GAAG;KAC9C;KACA;KACA;KACA,OAAO;KACP,OAAO,qBAAqB,QAAQ,iBAAiB;KACtD,CAAC;;;;AAKR,QAAO"}
@@ -0,0 +1,7 @@
1
+ import { DefaultToolCall, Message, ToolCallWithResult } from "../types.messages.cjs";
2
+
3
+ //#region src/utils/tools.d.ts
4
+ declare function getToolCallsWithResults<ToolCall = DefaultToolCall>(messages: Message<ToolCall>[]): ToolCallWithResult<ToolCall>[];
5
+ //#endregion
6
+ export { getToolCallsWithResults };
7
+ //# sourceMappingURL=tools.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.cts","names":[],"sources":["../../src/utils/tools.ts"],"mappings":";;;iBAsCgB,uBAAA,YAAmC,eAAA,CAAA,CACjD,QAAA,EAAU,OAAA,CAAQ,QAAA,MACjB,kBAAA,CAAmB,QAAA"}
@@ -0,0 +1,7 @@
1
+ import { DefaultToolCall, Message, ToolCallWithResult } from "../types.messages.js";
2
+
3
+ //#region src/utils/tools.d.ts
4
+ declare function getToolCallsWithResults<ToolCall = DefaultToolCall>(messages: Message<ToolCall>[]): ToolCallWithResult<ToolCall>[];
5
+ //#endregion
6
+ export { getToolCallsWithResults };
7
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","names":[],"sources":["../../src/utils/tools.ts"],"mappings":";;;iBAsCgB,uBAAA,YAAmC,eAAA,CAAA,CACjD,QAAA,EAAU,OAAA,CAAQ,QAAA,MACjB,kBAAA,CAAmB,QAAA"}
@@ -19,33 +19,42 @@
19
19
  /**
20
20
  * Computes the lifecycle state of a tool call based on its result.
21
21
  */
22
- function computeToolCallState(result) {
23
- if (!result) return "pending";
24
- return result.status === "error" ? "error" : "completed";
22
+ function computeToolCallState(result, impliedCompleted) {
23
+ if (result) return result.status === "error" ? "error" : "completed";
24
+ if (impliedCompleted) return "completed";
25
+ return "pending";
25
26
  }
26
27
  function getToolCallsWithResults(messages) {
27
28
  const results = [];
28
29
  const toolResultsById = /* @__PURE__ */ new Map();
29
30
  for (const msg of messages) if (msg.type === "tool") toolResultsById.set(msg.tool_call_id, msg);
30
- for (const msg of messages) if (msg.type === "ai" && msg.tool_calls && msg.tool_calls.length > 0) {
31
- const aiMessage = msg;
32
- for (let i = 0; i < aiMessage.tool_calls.length; i += 1) {
33
- const call = aiMessage.tool_calls[i];
34
- const callId = call.id;
35
- const result = callId ? toolResultsById.get(callId) : void 0;
36
- results.push({
37
- id: callId ?? `${aiMessage.id ?? "unknown"}-${i}`,
38
- call,
39
- result,
40
- aiMessage,
41
- index: i,
42
- state: computeToolCallState(result)
43
- });
31
+ for (let msgIdx = 0; msgIdx < messages.length; msgIdx += 1) {
32
+ const msg = messages[msgIdx];
33
+ if (msg.type === "ai" && msg.tool_calls && msg.tool_calls.length > 0) {
34
+ const aiMessage = msg;
35
+ let impliedCompleted = false;
36
+ for (let j = msgIdx + 1; j < messages.length; j += 1) if (messages[j].type === "ai") {
37
+ impliedCompleted = true;
38
+ break;
39
+ }
40
+ for (let i = 0; i < aiMessage.tool_calls.length; i += 1) {
41
+ const call = aiMessage.tool_calls[i];
42
+ const callId = call.id;
43
+ const result = callId ? toolResultsById.get(callId) : void 0;
44
+ results.push({
45
+ id: callId ?? `${aiMessage.id ?? "unknown"}-${i}`,
46
+ call,
47
+ result,
48
+ aiMessage,
49
+ index: i,
50
+ state: computeToolCallState(result, impliedCompleted)
51
+ });
52
+ }
44
53
  }
45
54
  }
46
55
  return results;
47
56
  }
48
-
49
57
  //#endregion
50
58
  export { getToolCallsWithResults };
59
+
51
60
  //# sourceMappingURL=tools.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","names":[],"sources":["../../src/utils/tools.ts"],"sourcesContent":["import type {\n Message,\n AIMessage,\n ToolMessage,\n ToolCallState,\n ToolCallWithResult,\n DefaultToolCall,\n} from \"../types.messages.js\";\n\n/**\n * Extracts tool calls with their results from a list of messages.\n *\n * @template ToolCall The type of tool calls.\n * @param messages The list of messages to extract tool calls from.\n * @returns An array of ToolCallWithResult objects.\n *\n * @example\n * ```ts\n * const toolCalls = getToolCallsWithResults(messages);\n * for (const { call, result } of toolCalls) {\n * if (call.name === \"get_weather\") {\n * console.log(`Weather for ${call.args.location}:`, result?.content);\n * }\n * }\n * ```\n */\n/**\n * Computes the lifecycle state of a tool call based on its result.\n */\nfunction computeToolCallState(result: ToolMessage | undefined): ToolCallState {\n if (!result) return \"pending\";\n return result.status === \"error\" ? \"error\" : \"completed\";\n}\n\nexport function getToolCallsWithResults<ToolCall = DefaultToolCall>(\n messages: Message<ToolCall>[]\n): ToolCallWithResult<ToolCall>[] {\n const results: ToolCallWithResult<ToolCall>[] = [];\n\n // Create a map of tool_call_id to ToolMessage for quick lookup\n const toolResultsById = new Map<string, ToolMessage>();\n for (const msg of messages) {\n if (msg.type === \"tool\") {\n toolResultsById.set(msg.tool_call_id, msg);\n }\n }\n\n // Find all AI messages with tool calls and pair them with results\n for (const msg of messages) {\n if (msg.type === \"ai\" && msg.tool_calls && msg.tool_calls.length > 0) {\n const aiMessage = msg as AIMessage<ToolCall>;\n for (let i = 0; i < aiMessage.tool_calls!.length; i += 1) {\n const call = aiMessage.tool_calls![i] as ToolCall & { id?: string };\n const callId = call.id as string | undefined;\n const result = callId ? toolResultsById.get(callId) : undefined;\n\n results.push({\n id: callId ?? `${aiMessage.id ?? \"unknown\"}-${i}`,\n call,\n result,\n aiMessage,\n index: i,\n state: computeToolCallState(result),\n });\n }\n }\n }\n\n return results;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,qBAAqB,QAAgD;AAC5E,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO,OAAO,WAAW,UAAU,UAAU;;AAG/C,SAAgB,wBACd,UACgC;CAChC,MAAM,UAA0C,EAAE;CAGlD,MAAM,kCAAkB,IAAI,KAA0B;AACtD,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,OACf,iBAAgB,IAAI,IAAI,cAAc,IAAI;AAK9C,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,QAAQ,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;EACpE,MAAM,YAAY;AAClB,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,WAAY,QAAQ,KAAK,GAAG;GACxD,MAAM,OAAO,UAAU,WAAY;GACnC,MAAM,SAAS,KAAK;GACpB,MAAM,SAAS,SAAS,gBAAgB,IAAI,OAAO,GAAG;AAEtD,WAAQ,KAAK;IACX,IAAI,UAAU,GAAG,UAAU,MAAM,UAAU,GAAG;IAC9C;IACA;IACA;IACA,OAAO;IACP,OAAO,qBAAqB,OAAO;IACpC,CAAC;;;AAKR,QAAO"}
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../../src/utils/tools.ts"],"sourcesContent":["import type {\n Message,\n AIMessage,\n ToolMessage,\n ToolCallState,\n ToolCallWithResult,\n DefaultToolCall,\n} from \"../types.messages.js\";\n\n/**\n * Extracts tool calls with their results from a list of messages.\n *\n * @template ToolCall The type of tool calls.\n * @param messages The list of messages to extract tool calls from.\n * @returns An array of ToolCallWithResult objects.\n *\n * @example\n * ```ts\n * const toolCalls = getToolCallsWithResults(messages);\n * for (const { call, result } of toolCalls) {\n * if (call.name === \"get_weather\") {\n * console.log(`Weather for ${call.args.location}:`, result?.content);\n * }\n * }\n * ```\n */\n/**\n * Computes the lifecycle state of a tool call based on its result.\n */\nfunction computeToolCallState(\n result: ToolMessage | undefined,\n impliedCompleted: boolean\n): ToolCallState {\n if (result) return result.status === \"error\" ? \"error\" : \"completed\";\n if (impliedCompleted) return \"completed\";\n return \"pending\";\n}\n\nexport function getToolCallsWithResults<ToolCall = DefaultToolCall>(\n messages: Message<ToolCall>[]\n): ToolCallWithResult<ToolCall>[] {\n const results: ToolCallWithResult<ToolCall>[] = [];\n\n // Create a map of tool_call_id to ToolMessage for quick lookup\n const toolResultsById = new Map<string, ToolMessage>();\n for (const msg of messages) {\n if (msg.type === \"tool\") {\n toolResultsById.set(msg.tool_call_id, msg);\n }\n }\n\n // Find all AI messages with tool calls and pair them with results.\n // For each, independently check if there's a subsequent AI message,\n // which implies the tools completed (handles tools returning Commands\n // where ToolMessages are embedded in the state update rather than streamed).\n for (let msgIdx = 0; msgIdx < messages.length; msgIdx += 1) {\n const msg = messages[msgIdx];\n if (msg.type === \"ai\" && msg.tool_calls && msg.tool_calls.length > 0) {\n const aiMessage = msg as AIMessage<ToolCall>;\n\n let impliedCompleted = false;\n for (let j = msgIdx + 1; j < messages.length; j += 1) {\n if (messages[j].type === \"ai\") {\n impliedCompleted = true;\n break;\n }\n }\n\n for (let i = 0; i < aiMessage.tool_calls!.length; i += 1) {\n const call = aiMessage.tool_calls![i] as ToolCall & { id?: string };\n const callId = call.id as string | undefined;\n const result = callId ? toolResultsById.get(callId) : undefined;\n\n results.push({\n id: callId ?? `${aiMessage.id ?? \"unknown\"}-${i}`,\n call,\n result,\n aiMessage,\n index: i,\n state: computeToolCallState(result, impliedCompleted),\n });\n }\n }\n }\n\n return results;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,qBACP,QACA,kBACe;AACf,KAAI,OAAQ,QAAO,OAAO,WAAW,UAAU,UAAU;AACzD,KAAI,iBAAkB,QAAO;AAC7B,QAAO;;AAGT,SAAgB,wBACd,UACgC;CAChC,MAAM,UAA0C,EAAE;CAGlD,MAAM,kCAAkB,IAAI,KAA0B;AACtD,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,OACf,iBAAgB,IAAI,IAAI,cAAc,IAAI;AAQ9C,MAAK,IAAI,SAAS,GAAG,SAAS,SAAS,QAAQ,UAAU,GAAG;EAC1D,MAAM,MAAM,SAAS;AACrB,MAAI,IAAI,SAAS,QAAQ,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;GACpE,MAAM,YAAY;GAElB,IAAI,mBAAmB;AACvB,QAAK,IAAI,IAAI,SAAS,GAAG,IAAI,SAAS,QAAQ,KAAK,EACjD,KAAI,SAAS,GAAG,SAAS,MAAM;AAC7B,uBAAmB;AACnB;;AAIJ,QAAK,IAAI,IAAI,GAAG,IAAI,UAAU,WAAY,QAAQ,KAAK,GAAG;IACxD,MAAM,OAAO,UAAU,WAAY;IACnC,MAAM,SAAS,KAAK;IACpB,MAAM,SAAS,SAAS,gBAAgB,IAAI,OAAO,GAAG,KAAA;AAEtD,YAAQ,KAAK;KACX,IAAI,UAAU,GAAG,UAAU,MAAM,UAAU,GAAG;KAC9C;KACA;KACA;KACA,OAAO;KACP,OAAO,qBAAqB,QAAQ,iBAAiB;KACtD,CAAC;;;;AAKR,QAAO"}