@nhtio/adk 0.1.0-master-f0aa531d

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 (297) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +3 -0
  3. package/batteries/index.d.ts +28 -0
  4. package/batteries/llm/index.d.ts +11 -0
  5. package/batteries/llm/openai_chat_completions/adapter.cjs +916 -0
  6. package/batteries/llm/openai_chat_completions/adapter.cjs.map +1 -0
  7. package/batteries/llm/openai_chat_completions/adapter.d.ts +101 -0
  8. package/batteries/llm/openai_chat_completions/adapter.mjs +914 -0
  9. package/batteries/llm/openai_chat_completions/adapter.mjs.map +1 -0
  10. package/batteries/llm/openai_chat_completions/exceptions.cjs +89 -0
  11. package/batteries/llm/openai_chat_completions/exceptions.cjs.map +1 -0
  12. package/batteries/llm/openai_chat_completions/exceptions.d.ts +97 -0
  13. package/batteries/llm/openai_chat_completions/exceptions.mjs +81 -0
  14. package/batteries/llm/openai_chat_completions/exceptions.mjs.map +1 -0
  15. package/batteries/llm/openai_chat_completions/helpers.cjs +819 -0
  16. package/batteries/llm/openai_chat_completions/helpers.cjs.map +1 -0
  17. package/batteries/llm/openai_chat_completions/helpers.d.ts +233 -0
  18. package/batteries/llm/openai_chat_completions/helpers.mjs +783 -0
  19. package/batteries/llm/openai_chat_completions/helpers.mjs.map +1 -0
  20. package/batteries/llm/openai_chat_completions/index.d.ts +27 -0
  21. package/batteries/llm/openai_chat_completions/types.cjs +1 -0
  22. package/batteries/llm/openai_chat_completions/types.d.ts +524 -0
  23. package/batteries/llm/openai_chat_completions/types.mjs +0 -0
  24. package/batteries/llm/openai_chat_completions/validation.cjs +190 -0
  25. package/batteries/llm/openai_chat_completions/validation.cjs.map +1 -0
  26. package/batteries/llm/openai_chat_completions/validation.d.ts +31 -0
  27. package/batteries/llm/openai_chat_completions/validation.mjs +187 -0
  28. package/batteries/llm/openai_chat_completions/validation.mjs.map +1 -0
  29. package/batteries/llm/openai_chat_completions.cjs +51 -0
  30. package/batteries/llm/openai_chat_completions.mjs +5 -0
  31. package/batteries/llm/webllm_chat_completions/adapter.cjs +658 -0
  32. package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -0
  33. package/batteries/llm/webllm_chat_completions/adapter.d.ts +103 -0
  34. package/batteries/llm/webllm_chat_completions/adapter.mjs +656 -0
  35. package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -0
  36. package/batteries/llm/webllm_chat_completions/exceptions.cjs +70 -0
  37. package/batteries/llm/webllm_chat_completions/exceptions.cjs.map +1 -0
  38. package/batteries/llm/webllm_chat_completions/exceptions.d.ts +74 -0
  39. package/batteries/llm/webllm_chat_completions/exceptions.mjs +65 -0
  40. package/batteries/llm/webllm_chat_completions/exceptions.mjs.map +1 -0
  41. package/batteries/llm/webllm_chat_completions/helpers.cjs +38 -0
  42. package/batteries/llm/webllm_chat_completions/helpers.d.ts +6 -0
  43. package/batteries/llm/webllm_chat_completions/helpers.mjs +2 -0
  44. package/batteries/llm/webllm_chat_completions/index.d.ts +25 -0
  45. package/batteries/llm/webllm_chat_completions/types.d.ts +31 -0
  46. package/batteries/llm/webllm_chat_completions/validation.cjs +115 -0
  47. package/batteries/llm/webllm_chat_completions/validation.cjs.map +1 -0
  48. package/batteries/llm/webllm_chat_completions/validation.d.ts +8 -0
  49. package/batteries/llm/webllm_chat_completions/validation.mjs +112 -0
  50. package/batteries/llm/webllm_chat_completions/validation.mjs.map +1 -0
  51. package/batteries/llm/webllm_chat_completions.cjs +50 -0
  52. package/batteries/llm/webllm_chat_completions.mjs +6 -0
  53. package/batteries/llm.cjs +63 -0
  54. package/batteries/llm.mjs +10 -0
  55. package/batteries/storage/flydrive/index.d.ts +167 -0
  56. package/batteries/storage/flydrive.cjs +249 -0
  57. package/batteries/storage/flydrive.cjs.map +1 -0
  58. package/batteries/storage/flydrive.mjs +249 -0
  59. package/batteries/storage/flydrive.mjs.map +1 -0
  60. package/batteries/storage/in_memory/index.d.ts +106 -0
  61. package/batteries/storage/in_memory.cjs +121 -0
  62. package/batteries/storage/in_memory.cjs.map +1 -0
  63. package/batteries/storage/in_memory.mjs +119 -0
  64. package/batteries/storage/in_memory.mjs.map +1 -0
  65. package/batteries/storage/index.d.ts +18 -0
  66. package/batteries/storage/opfs/index.d.ts +299 -0
  67. package/batteries/storage/opfs.cjs +368 -0
  68. package/batteries/storage/opfs.cjs.map +1 -0
  69. package/batteries/storage/opfs.mjs +366 -0
  70. package/batteries/storage/opfs.mjs.map +1 -0
  71. package/batteries/storage.cjs +4 -0
  72. package/batteries/storage.mjs +2 -0
  73. package/batteries/tools/color/index.d.ts +37 -0
  74. package/batteries/tools/color.cjs +659 -0
  75. package/batteries/tools/color.cjs.map +1 -0
  76. package/batteries/tools/color.mjs +655 -0
  77. package/batteries/tools/color.mjs.map +1 -0
  78. package/batteries/tools/comparison/index.d.ts +29 -0
  79. package/batteries/tools/comparison.cjs +171 -0
  80. package/batteries/tools/comparison.cjs.map +1 -0
  81. package/batteries/tools/comparison.mjs +168 -0
  82. package/batteries/tools/comparison.mjs.map +1 -0
  83. package/batteries/tools/data_structure/index.d.ts +30 -0
  84. package/batteries/tools/data_structure.cjs +270 -0
  85. package/batteries/tools/data_structure.cjs.map +1 -0
  86. package/batteries/tools/data_structure.mjs +267 -0
  87. package/batteries/tools/data_structure.mjs.map +1 -0
  88. package/batteries/tools/datetime_extended/index.d.ts +51 -0
  89. package/batteries/tools/datetime_extended.cjs +309 -0
  90. package/batteries/tools/datetime_extended.cjs.map +1 -0
  91. package/batteries/tools/datetime_extended.mjs +302 -0
  92. package/batteries/tools/datetime_extended.mjs.map +1 -0
  93. package/batteries/tools/datetime_math/index.d.ts +36 -0
  94. package/batteries/tools/datetime_math.cjs +175 -0
  95. package/batteries/tools/datetime_math.cjs.map +1 -0
  96. package/batteries/tools/datetime_math.mjs +171 -0
  97. package/batteries/tools/datetime_math.mjs.map +1 -0
  98. package/batteries/tools/encoding/index.d.ts +36 -0
  99. package/batteries/tools/encoding.cjs +156 -0
  100. package/batteries/tools/encoding.cjs.map +1 -0
  101. package/batteries/tools/encoding.mjs +152 -0
  102. package/batteries/tools/encoding.mjs.map +1 -0
  103. package/batteries/tools/formatting/index.d.ts +28 -0
  104. package/batteries/tools/formatting.cjs +120 -0
  105. package/batteries/tools/formatting.cjs.map +1 -0
  106. package/batteries/tools/formatting.mjs +117 -0
  107. package/batteries/tools/formatting.mjs.map +1 -0
  108. package/batteries/tools/geo_basics/index.d.ts +33 -0
  109. package/batteries/tools/geo_basics.cjs +136 -0
  110. package/batteries/tools/geo_basics.cjs.map +1 -0
  111. package/batteries/tools/geo_basics.mjs +132 -0
  112. package/batteries/tools/geo_basics.mjs.map +1 -0
  113. package/batteries/tools/index.d.ts +32 -0
  114. package/batteries/tools/math/index.d.ts +37 -0
  115. package/batteries/tools/math.cjs +136 -0
  116. package/batteries/tools/math.cjs.map +1 -0
  117. package/batteries/tools/math.mjs +133 -0
  118. package/batteries/tools/math.mjs.map +1 -0
  119. package/batteries/tools/memory/index.d.ts +73 -0
  120. package/batteries/tools/memory.cjs +193 -0
  121. package/batteries/tools/memory.cjs.map +1 -0
  122. package/batteries/tools/memory.mjs +187 -0
  123. package/batteries/tools/memory.mjs.map +1 -0
  124. package/batteries/tools/parsing/index.d.ts +47 -0
  125. package/batteries/tools/parsing.cjs +191 -0
  126. package/batteries/tools/parsing.cjs.map +1 -0
  127. package/batteries/tools/parsing.mjs +185 -0
  128. package/batteries/tools/parsing.mjs.map +1 -0
  129. package/batteries/tools/retrievables/index.d.ts +81 -0
  130. package/batteries/tools/retrievables.cjs +215 -0
  131. package/batteries/tools/retrievables.cjs.map +1 -0
  132. package/batteries/tools/retrievables.mjs +209 -0
  133. package/batteries/tools/retrievables.mjs.map +1 -0
  134. package/batteries/tools/standing_instructions/index.d.ts +64 -0
  135. package/batteries/tools/standing_instructions.cjs +126 -0
  136. package/batteries/tools/standing_instructions.cjs.map +1 -0
  137. package/batteries/tools/standing_instructions.mjs +121 -0
  138. package/batteries/tools/standing_instructions.mjs.map +1 -0
  139. package/batteries/tools/statistics/index.d.ts +46 -0
  140. package/batteries/tools/statistics.cjs +253 -0
  141. package/batteries/tools/statistics.cjs.map +1 -0
  142. package/batteries/tools/statistics.mjs +248 -0
  143. package/batteries/tools/statistics.mjs.map +1 -0
  144. package/batteries/tools/string_processing/index.d.ts +29 -0
  145. package/batteries/tools/string_processing.cjs +154 -0
  146. package/batteries/tools/string_processing.cjs.map +1 -0
  147. package/batteries/tools/string_processing.mjs +151 -0
  148. package/batteries/tools/string_processing.mjs.map +1 -0
  149. package/batteries/tools/structured_data/index.d.ts +34 -0
  150. package/batteries/tools/structured_data.cjs +189 -0
  151. package/batteries/tools/structured_data.cjs.map +1 -0
  152. package/batteries/tools/structured_data.mjs +185 -0
  153. package/batteries/tools/structured_data.mjs.map +1 -0
  154. package/batteries/tools/text_analysis/index.d.ts +31 -0
  155. package/batteries/tools/text_analysis.cjs +120 -0
  156. package/batteries/tools/text_analysis.cjs.map +1 -0
  157. package/batteries/tools/text_analysis.mjs +117 -0
  158. package/batteries/tools/text_analysis.mjs.map +1 -0
  159. package/batteries/tools/text_comparison/index.d.ts +28 -0
  160. package/batteries/tools/text_comparison.cjs +96 -0
  161. package/batteries/tools/text_comparison.cjs.map +1 -0
  162. package/batteries/tools/text_comparison.mjs +93 -0
  163. package/batteries/tools/text_comparison.mjs.map +1 -0
  164. package/batteries/tools/time/index.d.ts +27 -0
  165. package/batteries/tools/time.cjs +63 -0
  166. package/batteries/tools/time.cjs.map +1 -0
  167. package/batteries/tools/time.mjs +60 -0
  168. package/batteries/tools/time.mjs.map +1 -0
  169. package/batteries/tools/unit_conversion/index.d.ts +19 -0
  170. package/batteries/tools/unit_conversion.cjs +452 -0
  171. package/batteries/tools/unit_conversion.cjs.map +1 -0
  172. package/batteries/tools/unit_conversion.mjs +450 -0
  173. package/batteries/tools/unit_conversion.mjs.map +1 -0
  174. package/batteries/tools.cjs +80 -0
  175. package/batteries/tools.mjs +21 -0
  176. package/batteries.cjs +142 -0
  177. package/batteries.mjs +30 -0
  178. package/chunk-KmRHZBOW.js +35 -0
  179. package/common-DeZaonK1.mjs +208 -0
  180. package/common-DeZaonK1.mjs.map +1 -0
  181. package/common-Od8edUXU.js +232 -0
  182. package/common-Od8edUXU.js.map +1 -0
  183. package/common.cjs +31 -0
  184. package/common.d.ts +108 -0
  185. package/common.mjs +8 -0
  186. package/dispatch_runner-9j6bXHL3.mjs +1609 -0
  187. package/dispatch_runner-9j6bXHL3.mjs.map +1 -0
  188. package/dispatch_runner-CsoH0nld.js +1627 -0
  189. package/dispatch_runner-CsoH0nld.js.map +1 -0
  190. package/dispatch_runner.cjs +3 -0
  191. package/dispatch_runner.d.ts +17 -0
  192. package/dispatch_runner.mjs +2 -0
  193. package/exceptions-D5YrO9Vm.js +280 -0
  194. package/exceptions-D5YrO9Vm.js.map +1 -0
  195. package/exceptions-NrzIHw_R.mjs +244 -0
  196. package/exceptions-NrzIHw_R.mjs.map +1 -0
  197. package/exceptions.cjs +33 -0
  198. package/exceptions.d.ts +52 -0
  199. package/exceptions.mjs +3 -0
  200. package/factories.cjs +4 -0
  201. package/factories.d.ts +39 -0
  202. package/factories.mjs +2 -0
  203. package/forge.cjs +9 -0
  204. package/forge.d.ts +49 -0
  205. package/forge.mjs +5 -0
  206. package/guards.cjs +96 -0
  207. package/guards.cjs.map +1 -0
  208. package/guards.d.ts +83 -0
  209. package/guards.mjs +72 -0
  210. package/guards.mjs.map +1 -0
  211. package/index.cjs +107 -0
  212. package/index.cjs.map +1 -0
  213. package/index.d.ts +18 -0
  214. package/index.mjs +31 -0
  215. package/index.mjs.map +1 -0
  216. package/lib/classes/artifact_tool.d.ts +129 -0
  217. package/lib/classes/base_exception.d.ts +83 -0
  218. package/lib/classes/identity.d.ts +71 -0
  219. package/lib/classes/media.d.ts +326 -0
  220. package/lib/classes/memory.d.ts +72 -0
  221. package/lib/classes/message.d.ts +137 -0
  222. package/lib/classes/registry.d.ts +79 -0
  223. package/lib/classes/retrievable.d.ts +100 -0
  224. package/lib/classes/spooled_artifact.d.ts +296 -0
  225. package/lib/classes/spooled_json_artifact.d.ts +158 -0
  226. package/lib/classes/spooled_markdown_artifact.d.ts +202 -0
  227. package/lib/classes/thought.d.ts +142 -0
  228. package/lib/classes/tokenizable.d.ts +124 -0
  229. package/lib/classes/tool.d.ts +228 -0
  230. package/lib/classes/tool_call.d.ts +190 -0
  231. package/lib/classes/tool_registry.d.ts +159 -0
  232. package/lib/classes/turn_gate.d.ts +109 -0
  233. package/lib/contracts/dispatch_context.d.ts +345 -0
  234. package/lib/contracts/media_reader.d.ts +60 -0
  235. package/lib/contracts/spool_reader.d.ts +80 -0
  236. package/lib/contracts/spooled_artifact_constructor.d.ts +38 -0
  237. package/lib/contracts/turn_runner_config.d.ts +101 -0
  238. package/lib/contracts/turn_runner_context.d.ts +267 -0
  239. package/lib/dispatch_runner.d.ts +98 -0
  240. package/lib/exceptions/runtime.d.ts +370 -0
  241. package/lib/helpers/media_readers.d.ts +39 -0
  242. package/lib/turn_runner.d.ts +144 -0
  243. package/lib/types/dispatch_context.d.ts +233 -0
  244. package/lib/types/dispatch_runner.d.ts +387 -0
  245. package/lib/types/turn_runner.d.ts +322 -0
  246. package/lib/utils/canonical_json.d.ts +18 -0
  247. package/lib/utils/exceptions.d.ts +78 -0
  248. package/lib/utils/guards.d.ts +32 -0
  249. package/lib/utils/validation.d.ts +77 -0
  250. package/package.json +334 -0
  251. package/runtime-BJVkrGQe.js +519 -0
  252. package/runtime-BJVkrGQe.js.map +1 -0
  253. package/runtime-CrEPIFgr.mjs +346 -0
  254. package/runtime-CrEPIFgr.mjs.map +1 -0
  255. package/skills/adk-assembly/SKILL.md +109 -0
  256. package/skills/adk-assembly/references/assembly-contract.md +66 -0
  257. package/skills/adk-assembly/references/executors-tools-pipelines-events.md +113 -0
  258. package/skills/adk-assembly/references/first-integration.md +93 -0
  259. package/skills/adk-assembly/references/storage-and-context.md +102 -0
  260. package/spooled_artifact-C5ZtGxuJ.mjs +544 -0
  261. package/spooled_artifact-C5ZtGxuJ.mjs.map +1 -0
  262. package/spooled_artifact-Cm9Te22K.js +568 -0
  263. package/spooled_artifact-Cm9Te22K.js.map +1 -0
  264. package/spooled_artifact.cjs +7 -0
  265. package/spooled_artifact.d.ts +40 -0
  266. package/spooled_artifact.mjs +3 -0
  267. package/spooled_markdown_artifact-BpUJol0W.mjs +771 -0
  268. package/spooled_markdown_artifact-BpUJol0W.mjs.map +1 -0
  269. package/spooled_markdown_artifact-RRB113sy.js +786 -0
  270. package/spooled_markdown_artifact-RRB113sy.js.map +1 -0
  271. package/thought-CDb457b4.mjs +470 -0
  272. package/thought-CDb457b4.mjs.map +1 -0
  273. package/thought-DuN2PgdO.js +494 -0
  274. package/thought-DuN2PgdO.js.map +1 -0
  275. package/tool-COSeH8I6.js +302 -0
  276. package/tool-COSeH8I6.js.map +1 -0
  277. package/tool-D2WB1EA1.mjs +296 -0
  278. package/tool-D2WB1EA1.mjs.map +1 -0
  279. package/tool_call-BKyyxGaZ.mjs +578 -0
  280. package/tool_call-BKyyxGaZ.mjs.map +1 -0
  281. package/tool_call-DFgzcVcU.js +608 -0
  282. package/tool_call-DFgzcVcU.js.map +1 -0
  283. package/tool_registry-Dkfprsck.js +641 -0
  284. package/tool_registry-Dkfprsck.js.map +1 -0
  285. package/tool_registry-DqLOyGyG.mjs +592 -0
  286. package/tool_registry-DqLOyGyG.mjs.map +1 -0
  287. package/turn_runner-CMm2BHdX.js +615 -0
  288. package/turn_runner-CMm2BHdX.js.map +1 -0
  289. package/turn_runner-y7eyEcJH.mjs +603 -0
  290. package/turn_runner-y7eyEcJH.mjs.map +1 -0
  291. package/turn_runner.cjs +3 -0
  292. package/turn_runner.d.ts +21 -0
  293. package/turn_runner.mjs +2 -0
  294. package/types.cjs +1 -0
  295. package/types.d.ts +56 -0
  296. package/types.mjs +0 -0
  297. package/vite-env.d.ts +23 -0
@@ -0,0 +1,914 @@
1
+ import { a as Tokenizable, c as isObject, n as canonicalStringify, o as isError, s as isInstanceOf, t as ToolRegistry } from "../../../tool_registry-DqLOyGyG.mjs";
2
+ import { n as Message, t as Thought } from "../../../thought-CDb457b4.mjs";
3
+ import "../../../common-DeZaonK1.mjs";
4
+ import { n as Media, t as ToolCall } from "../../../tool_call-BKyyxGaZ.mjs";
5
+ import { i as ArtifactTool, t as SpooledArtifact } from "../../../spooled_artifact-C5ZtGxuJ.mjs";
6
+ import "../../../guards.mjs";
7
+ import { E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS, E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW, E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR, E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS, E_OPENAI_CHAT_COMPLETIONS_REQUEST_TIMEOUT, E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR, E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED } from "./exceptions.mjs";
8
+ import { validateOptions } from "./validation.mjs";
9
+ import { InMemorySpoolStore } from "../../storage/in_memory.mjs";
10
+ import { defaultBuildChatCompletionsHistory, defaultCreateChatCompletionsToolCallDeltaAccumulator, defaultDescriptionToChatCompletionsJsonSchema, defaultFilterThoughts, defaultRenderChatCompletionsSystemPrompt, defaultRenderChatCompletionsToolCallResult, defaultRenderFirstPartyRetrievables, defaultRenderMemories, defaultRenderRetrievableSafetyDirective, defaultRenderRetrievables, defaultRenderStandingInstructions, defaultRenderThirdPartyPrivateRetrievables, defaultRenderThirdPartyPublicRetrievables, defaultRenderThought, defaultRenderTimelineMessage, defaultRenderTrustedContent, defaultRenderUntrustedContent, defaultToolsToChatCompletionsTools } from "./helpers.mjs";
11
+ import { v6 } from "uuid";
12
+ import { DateTime } from "luxon";
13
+ import { sha256 } from "js-sha256";
14
+ //#region src/batteries/llm/openai_chat_completions/adapter.ts
15
+ /**
16
+ * Cross-environment executor adapter for OpenAI Chat Completions compatible endpoints.
17
+ *
18
+ * @module @nhtio/adk/batteries/llm/openai_chat_completions/adapter
19
+ *
20
+ * @remarks
21
+ * Cross-environment LLM adapter for the OpenAI Chat Completions wire shape. Chat Completions was
22
+ * chosen as the ADK's reference adapter because it is the de-facto interchange format for the
23
+ * majority of OpenAI-compatible gateways (vLLM, Together, Groq, Fireworks, Ollama, Azure OpenAI,
24
+ * OpenRouter, DeepSeek, Mistral La Plateforme, and most self-hosted deployments). Its tool-call
25
+ * synthetic-history shape (`role: 'assistant', tool_calls: [...]` followed by `role: 'tool'` with
26
+ * `tool_call_id`) is the lowest-common-denominator that every conformant gateway accepts.
27
+ *
28
+ * The adapter is built around three pluggable layers:
29
+ *
30
+ * 1. **Translation helpers** — the thirteen swappable functions exported from `./helpers` turn
31
+ * ADK primitives ({@link @nhtio/adk!Tokenizable}, {@link @nhtio/adk!Memory}, {@link @nhtio/adk!Message}, {@link @nhtio/adk!Thought},
32
+ * {@link @nhtio/adk!ToolCall}, {@link @nhtio/adk!Tool}, {@link @nhtio/adk!ArtifactTool}, {@link @nhtio/adk!SpooledArtifact}) into Chat
33
+ * Completions wire shapes. Consumers override individual helpers via `options.helpers.*` to
34
+ * customise envelope formats, bucket ordering, thought surfacing, or JSON Schema generation
35
+ * without forking the adapter.
36
+ * 2. **Three-layer options merging** — constructor baseline, per-`executor()` overrides, and
37
+ * per-iteration `ctx.stash.openaiChatCompletions` overrides combine with key-by-key
38
+ * precedence for `headers`/`helpers`/`retry` and wholesale replacement for everything else.
39
+ * The merged shape is re-validated on every iteration so a malformed stash override
40
+ * fails loud, not silently.
41
+ * 3. **Cross-env transport** — uses `globalThis.fetch`, `TextDecoder`, `AbortController`, and
42
+ * `AbortSignal.any` only. No Node-specific primitives; works unchanged in browser, edge, and
43
+ * server runtimes. SSE framing is hand-parsed against `\n\n` separators so the streaming path
44
+ * has no dependency on a particular SSE library.
45
+ *
46
+ * Per-iteration flow (steps 1–9 of the plan):
47
+ * 1. Merge constructor / executor / stash options and re-validate.
48
+ * 2. Resolve helpers, falling back to bundled `default*` for each unset field.
49
+ * 3. Forge artifact-query tools by walking `ctx.turnToolCalls`, collecting unique
50
+ * `SpooledArtifact` constructors, calling `<Ctor>.forgeTools(ctx)` on each, and merging the
51
+ * results with `ctx.tools`.
52
+ * 4. Pre-render every persisted tool-call result into the prompt-ready string the timeline will
53
+ * use, cached by `tc.id`.
54
+ * 5. When `tokenEncoding !== null`, sum the token weight of every persisted bucket and throw
55
+ * {@link @nhtio/adk/batteries!E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW} when the total exceeds `contextWindow`.
56
+ * 6. Build the request body via `buildChatCompletionsHistory`; carry vendor-opaque reasoning
57
+ * blocks through the `_adk_reasoning_payloads` side-channel.
58
+ * 7. POST with a linked `AbortSignal`, retry loop respecting `retry.*`, and a request-timeout
59
+ * watchdog that arms before fetch and clears once response headers arrive.
60
+ * 8. Streaming path: SSE parse via `response.body.getReader()` + `TextDecoder`; surface deltas
61
+ * through `helpers.reportMessage` / `reportThought` / `reportToolCall`; assemble tool-call
62
+ * deltas via the accumulator; persist `Message` / `Thought` / `ToolCall` records on `[DONE]`.
63
+ * 9. Non-streaming path: single `response.json()`; same persistence + tool-execution loop.
64
+ */
65
+ var ADK_CONTROL_KEYS = new Set([
66
+ "apiKey",
67
+ "baseURL",
68
+ "headers",
69
+ "fetch",
70
+ "stream",
71
+ "bucketOrder",
72
+ "contextWindow",
73
+ "selfIdentity",
74
+ "thoughtSurfacing",
75
+ "tokenEncoding",
76
+ "replayCompatibility",
77
+ "helpers",
78
+ "streamIdleTimeoutMs",
79
+ "requestTimeoutMs",
80
+ "retry",
81
+ "strictToolChoice",
82
+ "autoAck",
83
+ "unsupportedMediaPolicy"
84
+ ]);
85
+ var mergeRetry = (layers) => {
86
+ let merged;
87
+ for (const layer of layers) {
88
+ if (!layer) continue;
89
+ merged = {
90
+ ...merged ?? {},
91
+ ...layer
92
+ };
93
+ }
94
+ return merged;
95
+ };
96
+ var mergeHeaders = (layers) => {
97
+ let merged;
98
+ for (const layer of layers) {
99
+ if (!layer) continue;
100
+ merged = {
101
+ ...merged ?? {},
102
+ ...layer
103
+ };
104
+ }
105
+ return merged;
106
+ };
107
+ var mergeHelpers = (layers) => {
108
+ let merged;
109
+ for (const layer of layers) {
110
+ if (!layer) continue;
111
+ merged = {
112
+ ...merged ?? {},
113
+ ...layer
114
+ };
115
+ }
116
+ return merged;
117
+ };
118
+ var mergeOptions = (baseline, exec, stash) => {
119
+ const layers = [
120
+ baseline,
121
+ exec ?? {},
122
+ stash ?? {}
123
+ ];
124
+ const out = {};
125
+ for (const layer of layers) for (const [k, v] of Object.entries(layer)) {
126
+ if (v === void 0) continue;
127
+ if (k === "headers" || k === "helpers" || k === "retry") continue;
128
+ out[k] = v;
129
+ }
130
+ const headers = mergeHeaders(layers.map((l) => l.headers));
131
+ if (headers !== void 0) out.headers = headers;
132
+ const helpers = mergeHelpers(layers.map((l) => l.helpers));
133
+ if (helpers !== void 0) out.helpers = helpers;
134
+ const retry = mergeRetry(layers.map((l) => l.retry));
135
+ if (retry !== void 0) out.retry = retry;
136
+ return out;
137
+ };
138
+ var resolveHelpers = (overrides) => {
139
+ const src = overrides ?? {};
140
+ return {
141
+ descriptionToChatCompletionsJsonSchema: src.descriptionToChatCompletionsJsonSchema ?? defaultDescriptionToChatCompletionsJsonSchema,
142
+ renderUntrustedContent: src.renderUntrustedContent ?? defaultRenderUntrustedContent,
143
+ renderTrustedContent: src.renderTrustedContent ?? defaultRenderTrustedContent,
144
+ renderStandingInstructions: src.renderStandingInstructions ?? defaultRenderStandingInstructions,
145
+ renderMemories: src.renderMemories ?? defaultRenderMemories,
146
+ renderRetrievables: src.renderRetrievables ?? defaultRenderRetrievables,
147
+ renderRetrievableSafetyDirective: src.renderRetrievableSafetyDirective ?? defaultRenderRetrievableSafetyDirective,
148
+ renderFirstPartyRetrievables: src.renderFirstPartyRetrievables ?? defaultRenderFirstPartyRetrievables,
149
+ renderThirdPartyPublicRetrievables: src.renderThirdPartyPublicRetrievables ?? defaultRenderThirdPartyPublicRetrievables,
150
+ renderThirdPartyPrivateRetrievables: src.renderThirdPartyPrivateRetrievables ?? defaultRenderThirdPartyPrivateRetrievables,
151
+ renderTimelineMessage: src.renderTimelineMessage ?? defaultRenderTimelineMessage,
152
+ renderThought: src.renderThought ?? defaultRenderThought,
153
+ filterThoughts: src.filterThoughts ?? defaultFilterThoughts,
154
+ toolsToChatCompletionsTools: src.toolsToChatCompletionsTools ?? defaultToolsToChatCompletionsTools,
155
+ renderChatCompletionsSystemPrompt: src.renderChatCompletionsSystemPrompt ?? defaultRenderChatCompletionsSystemPrompt,
156
+ renderChatCompletionsToolCallResult: src.renderChatCompletionsToolCallResult ?? defaultRenderChatCompletionsToolCallResult,
157
+ buildChatCompletionsHistory: src.buildChatCompletionsHistory ?? defaultBuildChatCompletionsHistory,
158
+ createChatCompletionsToolCallDeltaAccumulator: src.createChatCompletionsToolCallDeltaAccumulator ?? defaultCreateChatCompletionsToolCallDeltaAccumulator
159
+ };
160
+ };
161
+ var computeBackoff = (attempt, cfg) => {
162
+ const base = cfg.baseDelayMs ?? 500;
163
+ const max = cfg.maxDelayMs ?? 3e4;
164
+ return Math.min(base * Math.pow(2, attempt - 1), max);
165
+ };
166
+ var sleepWithJitter = (ms, signal) => {
167
+ const jittered = ms * (.9 + Math.random() * .2);
168
+ return new Promise((resolve) => {
169
+ if (signal?.aborted) {
170
+ resolve();
171
+ return;
172
+ }
173
+ let onAbort;
174
+ const timer = setTimeout(() => {
175
+ if (onAbort && signal) signal.removeEventListener("abort", onAbort);
176
+ resolve();
177
+ }, jittered);
178
+ if (signal) {
179
+ onAbort = () => {
180
+ clearTimeout(timer);
181
+ resolve();
182
+ };
183
+ signal.addEventListener("abort", onAbort, { once: true });
184
+ }
185
+ });
186
+ };
187
+ var parseRetryAfter = (raw) => {
188
+ const asNum = Number(raw);
189
+ if (Number.isFinite(asNum)) return asNum * 1e3;
190
+ const asDate = Date.parse(raw);
191
+ if (Number.isFinite(asDate)) return Math.max(0, asDate - Date.now());
192
+ return 0;
193
+ };
194
+ var linkAbortSignals = (signals) => {
195
+ const anyFn = AbortSignal.any;
196
+ if (typeof anyFn === "function") return {
197
+ signal: anyFn(signals),
198
+ dispose: () => {}
199
+ };
200
+ const ctrl = new AbortController();
201
+ const links = [];
202
+ for (const sig of signals) {
203
+ if (sig.aborted) {
204
+ ctrl.abort();
205
+ break;
206
+ }
207
+ const handler = () => ctrl.abort();
208
+ sig.addEventListener("abort", handler, { once: true });
209
+ links.push({
210
+ sig,
211
+ handler
212
+ });
213
+ }
214
+ return {
215
+ signal: ctrl.signal,
216
+ dispose: () => {
217
+ for (const { sig, handler } of links) sig.removeEventListener("abort", handler);
218
+ links.length = 0;
219
+ }
220
+ };
221
+ };
222
+ var computeChecksum = (tool, args) => sha256(canonicalStringify({
223
+ tool,
224
+ args
225
+ }));
226
+ var nowIso = () => DateTime.now().toISO() ?? (/* @__PURE__ */ new Date()).toISOString();
227
+ var estimateTokensOf = async (value, encoding) => {
228
+ return Promise.resolve(value.estimateTokens(encoding));
229
+ };
230
+ /**
231
+ * Opinionated cross-environment LLM adapter for the OpenAI Chat Completions wire shape.
232
+ *
233
+ * @remarks
234
+ * Construction validates options eagerly via {@link @nhtio/adk/batteries!validateOptions} and throws
235
+ * {@link @nhtio/adk/batteries!E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS} on failure — config bugs fail loud, not at
236
+ * dispatch time. The returned instance is reusable: call {@link OpenAIChatCompletionsAdapter.executor}
237
+ * once per `DispatchRunner` configuration to obtain an {@link @nhtio/adk!DispatchExecutorFn} bound to the
238
+ * baseline plus optional executor-scope overrides.
239
+ *
240
+ * Per-iteration overrides live on the active {@link @nhtio/adk!DispatchContext}'s
241
+ * `stash.openaiChatCompletions` slot and take highest precedence — they merge into the
242
+ * executor-scope shape on every iteration. `headers`, `helpers`, and `retry` merge key-by-key
243
+ * across all three layers; every other field is replaced wholesale at the highest layer that
244
+ * sets it.
245
+ */
246
+ var OpenAIChatCompletionsAdapter = class OpenAIChatCompletionsAdapter {
247
+ /**
248
+ * Customary key for per-iteration overrides on `ctx.stash`. The adapter reads
249
+ * `ctx.stash.get(OpenAIChatCompletionsAdapter.STASH_KEY, {})` at the start of every
250
+ * iteration and merges the value into the resolved options shape.
251
+ */
252
+ static STASH_KEY = "openaiChatCompletions";
253
+ #baseline;
254
+ /**
255
+ * @param options - Constructor-baseline options. Re-validated on every iteration after
256
+ * per-dispatch and per-iteration overrides are layered in.
257
+ * @throws {@link @nhtio/adk/batteries!E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS} when `options` does not satisfy
258
+ * {@link @nhtio/adk/batteries!openAIChatCompletionsOptionsSchema}.
259
+ */
260
+ constructor(options) {
261
+ this.#baseline = validateOptions(options);
262
+ }
263
+ /**
264
+ * Returns an {@link @nhtio/adk!DispatchExecutorFn} bound to this adapter's baseline plus optional
265
+ * executor-scope overrides. The returned function is reusable across iterations — every
266
+ * iteration re-merges with `ctx.stash[STASH_KEY]` and re-validates the result.
267
+ *
268
+ * @param overrides - Optional executor-scope overrides. Higher precedence than the baseline,
269
+ * lower precedence than `ctx.stash[STASH_KEY]`.
270
+ * @returns An {@link @nhtio/adk!DispatchExecutorFn} suitable for `DispatchRunner`.
271
+ */
272
+ executor(overrides) {
273
+ const baseline = this.#baseline;
274
+ const adapterClass = OpenAIChatCompletionsAdapter;
275
+ return async (ctx, helpers) => {
276
+ const localWarn = (msg) => {
277
+ helpers.log.warn({
278
+ kind: "helper-warning",
279
+ message: msg
280
+ });
281
+ };
282
+ const stashRaw = ctx.stash.get(adapterClass.STASH_KEY, {});
283
+ const merged = validateOptions(mergeOptions(baseline, overrides, stashRaw && typeof stashRaw === "object" ? stashRaw : {}));
284
+ if (merged.tokenEncoding !== null && merged.contextWindow === void 0) throw new E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS(["tokenEncoding is non-null but contextWindow is undefined"]);
285
+ const resolvedHelpers = resolveHelpers(merged.helpers);
286
+ const uniqueCtors = /* @__PURE__ */ new Set();
287
+ for (const tc of ctx.turnToolCalls) {
288
+ const ctor = tc.results?.constructor;
289
+ if (ctor && SpooledArtifact.isSpooledArtifactConstructor(ctor)) uniqueCtors.add(ctor);
290
+ }
291
+ const forgedRegistries = [];
292
+ for (const ctor of uniqueCtors) {
293
+ const forgeFn = ctor.forgeTools;
294
+ if (typeof forgeFn === "function") forgedRegistries.push(forgeFn.call(ctor, ctx));
295
+ }
296
+ const mergedRegistry = ToolRegistry.merge([ctx.tools, ...forgedRegistries], { onCollision: "replace" });
297
+ mergedRegistry.bindContext(ctx);
298
+ const renderedToolCallResults = /* @__PURE__ */ new Map();
299
+ for (const tc of ctx.turnToolCalls) {
300
+ const rendered = await resolvedHelpers.renderChatCompletionsToolCallResult({
301
+ toolCall: tc,
302
+ results: tc.results,
303
+ tool: mergedRegistry.get(tc.tool),
304
+ renderUntrustedContent: resolvedHelpers.renderUntrustedContent,
305
+ renderTrustedContent: resolvedHelpers.renderTrustedContent,
306
+ unsupportedMediaPolicy: merged.unsupportedMediaPolicy ?? "throw",
307
+ warn: localWarn
308
+ });
309
+ renderedToolCallResults.set(tc.id, rendered);
310
+ }
311
+ if (merged.tokenEncoding !== null && merged.contextWindow !== void 0) {
312
+ const encoding = merged.tokenEncoding;
313
+ let spTokens = await estimateTokensOf(ctx.systemPrompt, encoding);
314
+ let siTokens = 0;
315
+ for (const si of ctx.standingInstructions) siTokens += await estimateTokensOf(si, encoding);
316
+ let memTokens = 0;
317
+ for (const mem of ctx.turnMemories) memTokens += await estimateTokensOf(mem.content, encoding);
318
+ let retTokens = 0;
319
+ for (const r of ctx.turnRetrievables) retTokens += await estimateTokensOf(r.content, encoding);
320
+ let tlTokens = 0;
321
+ for (const msg of ctx.turnMessages) if (msg.content !== void 0) tlTokens += await estimateTokensOf(msg.content, encoding);
322
+ for (const th of ctx.turnThoughts) tlTokens += await estimateTokensOf(th.content, encoding);
323
+ for (const rendered of renderedToolCallResults.values()) {
324
+ const tk = new Tokenizable(typeof rendered === "string" ? rendered : rendered.filter((b) => b.type === "text").map((b) => b.text).join("\n"));
325
+ tlTokens += await estimateTokensOf(tk, encoding);
326
+ }
327
+ const total = spTokens + siTokens + memTokens + retTokens + tlTokens;
328
+ const perBucketObj = {
329
+ systemPrompt: spTokens,
330
+ standingInstructions: siTokens,
331
+ memories: memTokens,
332
+ retrievables: retTokens,
333
+ timeline: tlTokens
334
+ };
335
+ helpers.log.debug({
336
+ kind: "context-window-usage",
337
+ message: `Context window usage: ${total}/${merged.contextWindow} tokens`,
338
+ payload: {
339
+ total,
340
+ limit: merged.contextWindow,
341
+ encoding,
342
+ perBucket: perBucketObj
343
+ }
344
+ });
345
+ if (total > merged.contextWindow) {
346
+ const perBucket = JSON.stringify(perBucketObj);
347
+ throw new E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW([
348
+ total,
349
+ merged.contextWindow,
350
+ encoding,
351
+ perBucket
352
+ ]);
353
+ }
354
+ }
355
+ const forcedToolNames = [];
356
+ const toolChoice = merged.tool_choice;
357
+ let toolChoiceVariant = "function";
358
+ if (toolChoice && typeof toolChoice === "object") {
359
+ if ("function" in toolChoice && toolChoice.type === "function") forcedToolNames.push(toolChoice.function.name);
360
+ else if ("custom" in toolChoice && toolChoice.type === "custom") forcedToolNames.push(toolChoice.custom.name);
361
+ else if (toolChoice.type === "allowed_tools") {
362
+ toolChoiceVariant = "allowed_tools";
363
+ for (const entry of toolChoice.allowed_tools.tools) if ("function" in entry) forcedToolNames.push(entry.function.name);
364
+ else if ("custom" in entry) forcedToolNames.push(entry.custom.name);
365
+ }
366
+ }
367
+ const forcedForgedHits = [];
368
+ for (const name of forcedToolNames) if (mergedRegistry.get(name)?.ephemeral === true) forcedForgedHits.push({ toolName: name });
369
+ if (forcedForgedHits.length > 0) {
370
+ if (merged.strictToolChoice === true) throw new E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS([`tool_choice forces forged ephemeral artifact-query tool(s): ${forcedForgedHits.map((h) => h.toolName).join(", ")} — these may not exist on the next iteration. Remove the override or unset strictToolChoice.`]);
371
+ helpers.log.warn({
372
+ kind: "tool-choice-forged-artifact",
373
+ message: `tool_choice forces ${forcedForgedHits.length} forged ephemeral artifact-query tool(s); this is almost always a misconfiguration`,
374
+ payload: {
375
+ toolNames: forcedForgedHits.map((h) => h.toolName),
376
+ variant: toolChoiceVariant
377
+ }
378
+ });
379
+ }
380
+ const { messages: wireMessages, reasoningPayloads } = await resolvedHelpers.buildChatCompletionsHistory({
381
+ systemPrompt: ctx.systemPrompt,
382
+ standingInstructions: ctx.standingInstructions,
383
+ memories: ctx.turnMemories,
384
+ retrievables: ctx.turnRetrievables,
385
+ messages: ctx.turnMessages,
386
+ thoughts: ctx.turnThoughts,
387
+ toolCalls: ctx.turnToolCalls,
388
+ tools: mergedRegistry,
389
+ renderedToolCallResults,
390
+ bucketOrder: merged.bucketOrder ?? [
391
+ "standingInstructions",
392
+ "memories",
393
+ "retrievables",
394
+ "timeline"
395
+ ],
396
+ selfIdentity: merged.selfIdentity ?? "assistant",
397
+ thoughtSurfacing: merged.thoughtSurfacing ?? "all-self",
398
+ replayCompatibility: merged.replayCompatibility ?? [],
399
+ renderChatCompletionsToolCallResult: resolvedHelpers.renderChatCompletionsToolCallResult,
400
+ renderChatCompletionsSystemPrompt: resolvedHelpers.renderChatCompletionsSystemPrompt,
401
+ renderStandingInstructions: resolvedHelpers.renderStandingInstructions,
402
+ renderMemories: resolvedHelpers.renderMemories,
403
+ renderRetrievables: resolvedHelpers.renderRetrievables,
404
+ renderRetrievableSafetyDirective: resolvedHelpers.renderRetrievableSafetyDirective,
405
+ renderFirstPartyRetrievables: resolvedHelpers.renderFirstPartyRetrievables,
406
+ renderThirdPartyPublicRetrievables: resolvedHelpers.renderThirdPartyPublicRetrievables,
407
+ renderThirdPartyPrivateRetrievables: resolvedHelpers.renderThirdPartyPrivateRetrievables,
408
+ renderTimelineMessage: resolvedHelpers.renderTimelineMessage,
409
+ renderThought: resolvedHelpers.renderThought,
410
+ filterThoughts: resolvedHelpers.filterThoughts,
411
+ renderUntrustedContent: resolvedHelpers.renderUntrustedContent,
412
+ renderTrustedContent: resolvedHelpers.renderTrustedContent,
413
+ unsupportedMediaPolicy: merged.unsupportedMediaPolicy ?? "throw",
414
+ warn: localWarn
415
+ });
416
+ const stream = merged.stream ?? true;
417
+ const body = {
418
+ model: merged.model,
419
+ messages: wireMessages,
420
+ stream
421
+ };
422
+ for (const [k, v] of Object.entries(merged)) {
423
+ if (ADK_CONTROL_KEYS.has(k)) continue;
424
+ if (k === "model" || k === "messages" || k === "stream") continue;
425
+ if (v === void 0) continue;
426
+ body[k] = v;
427
+ }
428
+ const toolsArr = mergedRegistry.all();
429
+ if (toolsArr.length > 0) body.tools = resolvedHelpers.toolsToChatCompletionsTools(toolsArr, { descriptionToChatCompletionsJsonSchema: resolvedHelpers.descriptionToChatCompletionsJsonSchema });
430
+ if (reasoningPayloads.length > 0) body._adk_reasoning_payloads = reasoningPayloads;
431
+ const rawBase = merged.baseURL ?? "https://api.openai.com/v1";
432
+ const url = `${rawBase.endsWith("/") ? rawBase.slice(0, -1) : rawBase}/chat/completions`;
433
+ const headers = { "Content-Type": "application/json" };
434
+ if (stream) headers["Accept"] = "text/event-stream";
435
+ if (merged.apiKey) headers["Authorization"] = `Bearer ${merged.apiKey}`;
436
+ if (merged.headers) Object.assign(headers, merged.headers);
437
+ const retryCfg = {
438
+ maxAttempts: merged.retry?.maxAttempts ?? 1,
439
+ baseDelayMs: merged.retry?.baseDelayMs ?? 500,
440
+ maxDelayMs: merged.retry?.maxDelayMs ?? 3e4,
441
+ retriableStatuses: merged.retry?.retriableStatuses ?? [
442
+ 429,
443
+ 502,
444
+ 503,
445
+ 504
446
+ ],
447
+ honorRetryAfter: merged.retry?.honorRetryAfter ?? true
448
+ };
449
+ const fetchFn = merged.fetch ?? globalThis.fetch;
450
+ const maxAttempts = retryCfg.maxAttempts ?? 1;
451
+ let response;
452
+ let attempt = 1;
453
+ let disposeLink = () => {};
454
+ while (attempt <= maxAttempts) {
455
+ if (ctx.abortSignal.aborted) return;
456
+ const internalController = new AbortController();
457
+ let timeoutHandle;
458
+ const requestTimeoutMs = merged.requestTimeoutMs ?? 0;
459
+ if (requestTimeoutMs > 0) timeoutHandle = setTimeout(() => internalController.abort(), requestTimeoutMs);
460
+ disposeLink();
461
+ const { signal: linkedSignal, dispose: disposeCurrentLink } = linkAbortSignals([ctx.abortSignal, internalController.signal]);
462
+ disposeLink = disposeCurrentLink;
463
+ try {
464
+ response = await fetchFn(url, {
465
+ method: "POST",
466
+ headers,
467
+ body: JSON.stringify(body),
468
+ signal: linkedSignal
469
+ });
470
+ } catch (err) {
471
+ if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
472
+ if (ctx.abortSignal.aborted) return;
473
+ if (internalController.signal.aborted) {
474
+ helpers.log.warn({
475
+ kind: "request-timeout",
476
+ message: `Request timed out after ${requestTimeoutMs}ms on attempt ${attempt}/${maxAttempts}`,
477
+ payload: {
478
+ requestTimeoutMs,
479
+ attempt,
480
+ maxAttempts
481
+ }
482
+ });
483
+ if (attempt < maxAttempts) {
484
+ const delay = computeBackoff(attempt, retryCfg);
485
+ helpers.log.debug({
486
+ kind: "retry-attempt",
487
+ message: `Retrying after request timeout in ~${delay}ms (attempt ${attempt + 1}/${maxAttempts})`,
488
+ payload: {
489
+ reason: "request-timeout",
490
+ delayMs: delay,
491
+ attempt: attempt + 1,
492
+ maxAttempts
493
+ }
494
+ });
495
+ await sleepWithJitter(delay, ctx.abortSignal);
496
+ attempt += 1;
497
+ continue;
498
+ }
499
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_REQUEST_TIMEOUT([requestTimeoutMs]));
500
+ return;
501
+ }
502
+ helpers.log.error({
503
+ kind: "transport-error",
504
+ message: `Transport failure on attempt ${attempt}/${maxAttempts}: ${isError(err) ? err.message : String(err)}`,
505
+ payload: {
506
+ attempt,
507
+ maxAttempts,
508
+ detail: isError(err) ? err.message : String(err)
509
+ }
510
+ });
511
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR([0, isError(err) ? err.message : String(err)]));
512
+ return;
513
+ }
514
+ if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
515
+ if (!response.ok) {
516
+ const status = response.status;
517
+ const retriable = (retryCfg.retriableStatuses ?? [
518
+ 429,
519
+ 502,
520
+ 503,
521
+ 504
522
+ ]).includes(status);
523
+ if (retriable && attempt < maxAttempts) {
524
+ let delay = computeBackoff(attempt, retryCfg);
525
+ let retryAfterMs;
526
+ if (retryCfg.honorRetryAfter !== false) {
527
+ const ra = response.headers.get("Retry-After");
528
+ if (ra) {
529
+ const raMs = parseRetryAfter(ra);
530
+ if (raMs > 0) {
531
+ retryAfterMs = raMs;
532
+ delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs ?? 3e4);
533
+ }
534
+ }
535
+ }
536
+ helpers.log.warn({
537
+ kind: "retry-attempt",
538
+ message: `HTTP ${status} on attempt ${attempt}/${maxAttempts}; retrying in ~${delay}ms`,
539
+ payload: {
540
+ reason: "http-status",
541
+ status,
542
+ delayMs: delay,
543
+ ...retryAfterMs !== void 0 ? { retryAfterMs } : {},
544
+ attempt: attempt + 1,
545
+ maxAttempts
546
+ }
547
+ });
548
+ await sleepWithJitter(delay, ctx.abortSignal);
549
+ attempt += 1;
550
+ continue;
551
+ }
552
+ const errBody = await response.text().catch(() => "");
553
+ helpers.log.error({
554
+ kind: "http-error",
555
+ message: `HTTP ${status} (terminal): ${errBody.slice(0, 256)}`,
556
+ payload: {
557
+ status,
558
+ body: errBody,
559
+ attempt,
560
+ maxAttempts,
561
+ retriable
562
+ }
563
+ });
564
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR([status, errBody]));
565
+ return;
566
+ }
567
+ break;
568
+ }
569
+ if (!response) return;
570
+ const spoolStore = new InMemorySpoolStore();
571
+ const executeAndPersistToolCall = async (call) => {
572
+ const tool = mergedRegistry.get(call.name);
573
+ let args = {};
574
+ let parseError;
575
+ if (call.args && call.args.length > 0) try {
576
+ const parsed = JSON.parse(call.args);
577
+ if (isObject(parsed)) args = parsed;
578
+ else parseError = new E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS([`must be a JSON object; received ${Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed}`, call.args]);
579
+ } catch {
580
+ parseError = new E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS(["are not valid JSON", call.args]);
581
+ }
582
+ const completedAt = nowIso();
583
+ if (parseError !== void 0) {
584
+ const results = new Tokenizable(parseError.message);
585
+ helpers.reportToolCall(call.id, {
586
+ tool: call.name,
587
+ args
588
+ });
589
+ helpers.reportToolCall(call.id, {
590
+ results,
591
+ isError: true,
592
+ isComplete: true
593
+ });
594
+ const checksum = computeChecksum(call.name, args);
595
+ await ctx.storeToolCall(new ToolCall({
596
+ id: call.id,
597
+ tool: call.name,
598
+ args,
599
+ checksum,
600
+ isComplete: true,
601
+ isError: true,
602
+ results,
603
+ createdAt: completedAt,
604
+ updatedAt: completedAt,
605
+ completedAt
606
+ }));
607
+ return;
608
+ }
609
+ if (!tool) {
610
+ const results = new Tokenizable(`Tool not found: ${call.name}`);
611
+ helpers.reportToolCall(call.id, {
612
+ tool: call.name,
613
+ args
614
+ });
615
+ helpers.reportToolCall(call.id, {
616
+ results,
617
+ isError: true,
618
+ isComplete: true
619
+ });
620
+ const checksum = computeChecksum(call.name, args);
621
+ await ctx.storeToolCall(new ToolCall({
622
+ id: call.id,
623
+ tool: call.name,
624
+ args,
625
+ checksum,
626
+ isComplete: true,
627
+ isError: true,
628
+ results,
629
+ createdAt: completedAt,
630
+ updatedAt: completedAt,
631
+ completedAt
632
+ }));
633
+ return;
634
+ }
635
+ helpers.reportToolCall(call.id, {
636
+ tool: tool.name,
637
+ args
638
+ });
639
+ const isArtifactTool = ArtifactTool.isArtifactTool(tool);
640
+ let results = new Tokenizable("");
641
+ let toolHadError = false;
642
+ try {
643
+ const raw = await tool.executor(ctx)(args);
644
+ if (isArtifactTool) if (Tokenizable.isTokenizable(raw)) results = raw;
645
+ else if (typeof raw === "string") results = new Tokenizable(raw);
646
+ else throw new Error(`ArtifactTool "${tool.name}" returned a non-string/non-Tokenizable value`);
647
+ else if (Media.isMedia(raw)) results = raw;
648
+ else if (Array.isArray(raw) && raw.length > 0 && raw.every((m) => Media.isMedia(m))) results = raw;
649
+ else if (typeof raw === "string" || isInstanceOf(raw, "Uint8Array", Uint8Array)) {
650
+ const reader = spoolStore.write(call.id, raw);
651
+ results = new ((tool.artifactConstructor?.()) ?? SpooledArtifact)(reader);
652
+ } else {
653
+ const reader = spoolStore.write(call.id, String(raw));
654
+ results = new ((tool.artifactConstructor?.()) ?? SpooledArtifact)(reader);
655
+ }
656
+ } catch (err) {
657
+ toolHadError = true;
658
+ let detailMsg = isError(err) ? err.message : String(err);
659
+ if (isError(err) && isError(err.cause) && err.cause.message && err.cause.message !== err.message) detailMsg = `${detailMsg} ${err.cause.message}`;
660
+ results = new Tokenizable(detailMsg);
661
+ }
662
+ helpers.reportToolCall(call.id, {
663
+ results,
664
+ isError: toolHadError,
665
+ isComplete: true
666
+ });
667
+ const checksum = computeChecksum(tool.name, args);
668
+ const completedAt2 = nowIso();
669
+ await ctx.storeToolCall(new ToolCall({
670
+ id: call.id,
671
+ tool: tool.name,
672
+ args,
673
+ checksum,
674
+ isComplete: true,
675
+ isError: toolHadError,
676
+ results,
677
+ fromArtifactTool: isArtifactTool,
678
+ createdAt: completedAt2,
679
+ updatedAt: completedAt2,
680
+ completedAt: completedAt2
681
+ }));
682
+ };
683
+ const selfIdentity = merged.selfIdentity ?? "assistant";
684
+ if (stream) {
685
+ if (!response.body) {
686
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR(["response has no body"]));
687
+ return;
688
+ }
689
+ const reader = response.body.getReader();
690
+ const decoder = new TextDecoder();
691
+ const accumulator = resolvedHelpers.createChatCompletionsToolCallDeltaAccumulator();
692
+ const streamId = v6();
693
+ let buffer = "";
694
+ let idleTimer;
695
+ let stalled = false;
696
+ let partialMessageContent = "";
697
+ let partialThoughtContent = "";
698
+ let sawMessageDelta = false;
699
+ let sawThoughtDelta = false;
700
+ let doneSentinelSeen = false;
701
+ const idleMs = merged.streamIdleTimeoutMs ?? 0;
702
+ const armIdleTimer = () => {
703
+ if (idleMs <= 0) return;
704
+ if (idleTimer) clearTimeout(idleTimer);
705
+ idleTimer = setTimeout(() => {
706
+ stalled = true;
707
+ helpers.log.warn({
708
+ kind: "stream-idle-timeout",
709
+ message: `SSE stream went idle for ${idleMs}ms; cancelling`,
710
+ payload: { idleMs }
711
+ });
712
+ reader.cancel().catch(() => {});
713
+ }, idleMs);
714
+ };
715
+ const clearIdleTimer = () => {
716
+ if (idleTimer) {
717
+ clearTimeout(idleTimer);
718
+ idleTimer = void 0;
719
+ }
720
+ };
721
+ const drainAndPersist = async () => {
722
+ if (sawMessageDelta) {
723
+ helpers.reportMessage(streamId, "", { isComplete: true });
724
+ await ctx.storeMessage(new Message({
725
+ id: streamId,
726
+ role: "assistant",
727
+ content: partialMessageContent,
728
+ identity: selfIdentity,
729
+ createdAt: nowIso(),
730
+ updatedAt: nowIso()
731
+ }));
732
+ }
733
+ if (sawThoughtDelta) {
734
+ helpers.reportThought(streamId, "", { isComplete: true });
735
+ await ctx.storeThought(new Thought({
736
+ id: streamId,
737
+ content: partialThoughtContent,
738
+ identity: selfIdentity,
739
+ createdAt: nowIso(),
740
+ updatedAt: nowIso()
741
+ }));
742
+ }
743
+ const calls = accumulator.drain();
744
+ helpers.log.debug({
745
+ kind: "accumulator-finalised",
746
+ message: `Stream finalised: ${calls.length} tool call(s), message=${sawMessageDelta}, thought=${sawThoughtDelta}`,
747
+ payload: {
748
+ toolCallCount: calls.length,
749
+ sawMessageDelta,
750
+ sawThoughtDelta,
751
+ doneSentinelSeen
752
+ }
753
+ });
754
+ if (calls.length === 0) {
755
+ if (merged.autoAck) ctx.ack();
756
+ return;
757
+ }
758
+ for (const call of calls) {
759
+ if (ctx.abortSignal.aborted) return;
760
+ await executeAndPersistToolCall(call);
761
+ }
762
+ };
763
+ try {
764
+ armIdleTimer();
765
+ while (true) {
766
+ const { done, value } = await reader.read();
767
+ if (done) break;
768
+ armIdleTimer();
769
+ if (ctx.abortSignal.aborted) {
770
+ clearIdleTimer();
771
+ return;
772
+ }
773
+ buffer += decoder.decode(value, { stream: true });
774
+ let sepIdx = buffer.indexOf("\n\n");
775
+ while (sepIdx !== -1) {
776
+ const frame = buffer.slice(0, sepIdx);
777
+ buffer = buffer.slice(sepIdx + 2);
778
+ for (const line of frame.split("\n")) {
779
+ if (!line.startsWith("data:")) continue;
780
+ const data = line.slice(5).trim();
781
+ if (data.length === 0) continue;
782
+ if (data === "[DONE]") {
783
+ doneSentinelSeen = true;
784
+ clearIdleTimer();
785
+ await drainAndPersist();
786
+ return;
787
+ }
788
+ let chunk;
789
+ try {
790
+ chunk = JSON.parse(data);
791
+ } catch {
792
+ helpers.log.trace({
793
+ kind: "sse-parse-failure",
794
+ message: "Failed to parse SSE chunk as JSON; skipping",
795
+ payload: { dataPreview: data.slice(0, 256) }
796
+ });
797
+ continue;
798
+ }
799
+ const delta = chunk.choices?.[0]?.delta;
800
+ if (!delta) continue;
801
+ if (typeof delta.content === "string" && delta.content.length > 0) {
802
+ sawMessageDelta = true;
803
+ partialMessageContent += delta.content;
804
+ helpers.reportMessage(streamId, delta.content);
805
+ }
806
+ const reasoning = delta.reasoning_content;
807
+ if (typeof reasoning === "string" && reasoning.length > 0) {
808
+ sawThoughtDelta = true;
809
+ partialThoughtContent += reasoning;
810
+ helpers.reportThought(streamId, reasoning);
811
+ }
812
+ if (Array.isArray(delta.tool_calls)) for (const d of delta.tool_calls) accumulator.feed(d);
813
+ }
814
+ sepIdx = buffer.indexOf("\n\n");
815
+ }
816
+ }
817
+ clearIdleTimer();
818
+ if (stalled) {
819
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED([idleMs]));
820
+ return;
821
+ }
822
+ if (!doneSentinelSeen) {
823
+ helpers.log.warn({
824
+ kind: "sse-eof-without-done",
825
+ message: "SSE stream ended without [DONE] sentinel; draining accumulated state"
826
+ });
827
+ await drainAndPersist();
828
+ }
829
+ } catch (err) {
830
+ clearIdleTimer();
831
+ if (ctx.abortSignal.aborted) return;
832
+ if (stalled) {
833
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED([idleMs]));
834
+ return;
835
+ }
836
+ helpers.log.error({
837
+ kind: "stream-error",
838
+ message: `SSE stream failed: ${isError(err) ? err.message : String(err)}`,
839
+ payload: { detail: isError(err) ? err.message : String(err) }
840
+ });
841
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR([isError(err) ? err.message : String(err)]));
842
+ return;
843
+ }
844
+ return;
845
+ }
846
+ let parsed;
847
+ try {
848
+ parsed = await response.json();
849
+ } catch (err) {
850
+ ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR([isError(err) ? err.message : String(err)]));
851
+ return;
852
+ }
853
+ const choice = parsed.choices?.[0];
854
+ if (!choice) {
855
+ if (merged.autoAck) ctx.ack();
856
+ return;
857
+ }
858
+ const msg = choice.message;
859
+ const responseId = parsed.id ?? v6();
860
+ if (msg && typeof msg.content === "string" && msg.content.length > 0) {
861
+ const messageId = `${responseId}:message`;
862
+ helpers.reportMessage(messageId, msg.content, { isComplete: true });
863
+ await ctx.storeMessage(new Message({
864
+ id: messageId,
865
+ role: "assistant",
866
+ content: msg.content,
867
+ identity: selfIdentity,
868
+ createdAt: nowIso(),
869
+ updatedAt: nowIso()
870
+ }));
871
+ }
872
+ const reasoning = msg?.reasoning_content;
873
+ if (typeof reasoning === "string" && reasoning.length > 0) {
874
+ const thoughtId = `${responseId}:thought`;
875
+ helpers.reportThought(thoughtId, reasoning, { isComplete: true });
876
+ await ctx.storeThought(new Thought({
877
+ id: thoughtId,
878
+ content: reasoning,
879
+ identity: selfIdentity,
880
+ createdAt: nowIso(),
881
+ updatedAt: nowIso()
882
+ }));
883
+ }
884
+ const rawCalls = msg?.tool_calls ?? [];
885
+ if (rawCalls.length === 0) {
886
+ if (merged.autoAck) ctx.ack();
887
+ return;
888
+ }
889
+ const calls = rawCalls.map((tc) => ({
890
+ id: tc.id,
891
+ type: tc.type ?? "function",
892
+ name: tc.function?.name ?? "",
893
+ args: tc.function?.arguments ?? ""
894
+ }));
895
+ for (const call of calls) {
896
+ if (ctx.abortSignal.aborted) return;
897
+ await executeAndPersistToolCall(call);
898
+ }
899
+ };
900
+ }
901
+ /**
902
+ * Returns `true` when `value` is an {@link OpenAIChatCompletionsAdapter} instance.
903
+ *
904
+ * @param value - The value to test.
905
+ * @returns `true` when `value` is an `OpenAIChatCompletionsAdapter` instance.
906
+ */
907
+ static isOpenAIChatCompletionsAdapter(value) {
908
+ return isInstanceOf(value, "OpenAIChatCompletionsAdapter", OpenAIChatCompletionsAdapter);
909
+ }
910
+ };
911
+ //#endregion
912
+ export { OpenAIChatCompletionsAdapter };
913
+
914
+ //# sourceMappingURL=adapter.mjs.map