@nhtio/adk 1.20260603.0 → 1.20260604.0

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 (142) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/batteries/embeddings/openai/adapter.cjs +1 -1
  3. package/batteries/embeddings/openai/adapter.mjs +1 -1
  4. package/batteries/embeddings/webllm/adapter.cjs +1 -1
  5. package/batteries/embeddings/webllm/adapter.mjs +1 -1
  6. package/batteries/llm/openai_chat_completions/adapter.cjs +42 -29
  7. package/batteries/llm/openai_chat_completions/adapter.cjs.map +1 -1
  8. package/batteries/llm/openai_chat_completions/adapter.mjs +43 -30
  9. package/batteries/llm/openai_chat_completions/adapter.mjs.map +1 -1
  10. package/batteries/llm/openai_chat_completions/helpers.cjs +35 -2
  11. package/batteries/llm/openai_chat_completions/helpers.cjs.map +1 -1
  12. package/batteries/llm/openai_chat_completions/helpers.d.ts +21 -1
  13. package/batteries/llm/openai_chat_completions/helpers.mjs +35 -3
  14. package/batteries/llm/openai_chat_completions/helpers.mjs.map +1 -1
  15. package/batteries/llm/openai_chat_completions/index.d.ts +2 -2
  16. package/batteries/llm/openai_chat_completions/types.d.ts +52 -0
  17. package/batteries/llm/openai_chat_completions/validation.cjs +3 -1
  18. package/batteries/llm/openai_chat_completions/validation.cjs.map +1 -1
  19. package/batteries/llm/openai_chat_completions/validation.mjs +3 -1
  20. package/batteries/llm/openai_chat_completions/validation.mjs.map +1 -1
  21. package/batteries/llm/openai_chat_completions.cjs +1 -0
  22. package/batteries/llm/openai_chat_completions.mjs +2 -2
  23. package/batteries/llm/webllm_chat_completions/adapter.cjs +42 -29
  24. package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -1
  25. package/batteries/llm/webllm_chat_completions/adapter.mjs +43 -30
  26. package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -1
  27. package/batteries/llm/webllm_chat_completions/helpers.cjs +1 -0
  28. package/batteries/llm/webllm_chat_completions/helpers.d.ts +1 -1
  29. package/batteries/llm/webllm_chat_completions/helpers.mjs +2 -2
  30. package/batteries/llm/webllm_chat_completions/index.d.ts +2 -2
  31. package/batteries/llm/webllm_chat_completions/types.d.ts +1 -1
  32. package/batteries/llm/webllm_chat_completions/validation.cjs +3 -1
  33. package/batteries/llm/webllm_chat_completions/validation.cjs.map +1 -1
  34. package/batteries/llm/webllm_chat_completions/validation.mjs +3 -1
  35. package/batteries/llm/webllm_chat_completions/validation.mjs.map +1 -1
  36. package/batteries/llm/webllm_chat_completions.cjs +1 -0
  37. package/batteries/llm/webllm_chat_completions.mjs +2 -2
  38. package/batteries/llm.cjs +1 -0
  39. package/batteries/llm.mjs +2 -2
  40. package/batteries/storage/flydrive.cjs +1 -1
  41. package/batteries/storage/flydrive.mjs +1 -1
  42. package/batteries/storage/in_memory.cjs +1 -1
  43. package/batteries/storage/in_memory.mjs +1 -1
  44. package/batteries/storage/opfs.cjs +1 -1
  45. package/batteries/storage/opfs.mjs +1 -1
  46. package/batteries/tools/color.cjs +2 -2
  47. package/batteries/tools/color.mjs +2 -2
  48. package/batteries/tools/comparison.cjs +3 -3
  49. package/batteries/tools/comparison.mjs +3 -3
  50. package/batteries/tools/data_structure.cjs +3 -3
  51. package/batteries/tools/data_structure.mjs +3 -3
  52. package/batteries/tools/datetime_extended.cjs +3 -3
  53. package/batteries/tools/datetime_extended.mjs +3 -3
  54. package/batteries/tools/datetime_math.cjs +3 -3
  55. package/batteries/tools/datetime_math.mjs +3 -3
  56. package/batteries/tools/encoding.cjs +3 -3
  57. package/batteries/tools/encoding.mjs +3 -3
  58. package/batteries/tools/formatting.cjs +3 -3
  59. package/batteries/tools/formatting.mjs +3 -3
  60. package/batteries/tools/geo_basics.cjs +2 -2
  61. package/batteries/tools/geo_basics.mjs +2 -2
  62. package/batteries/tools/math.cjs +3 -3
  63. package/batteries/tools/math.mjs +3 -3
  64. package/batteries/tools/memory.cjs +6 -6
  65. package/batteries/tools/memory.mjs +6 -6
  66. package/batteries/tools/parsing.cjs +4 -4
  67. package/batteries/tools/parsing.mjs +4 -4
  68. package/batteries/tools/retrievables.cjs +5 -5
  69. package/batteries/tools/retrievables.mjs +5 -5
  70. package/batteries/tools/standing_instructions.cjs +4 -4
  71. package/batteries/tools/standing_instructions.mjs +4 -4
  72. package/batteries/tools/statistics.cjs +4 -4
  73. package/batteries/tools/statistics.mjs +4 -4
  74. package/batteries/tools/string_processing.cjs +3 -3
  75. package/batteries/tools/string_processing.mjs +3 -3
  76. package/batteries/tools/structured_data.cjs +3 -3
  77. package/batteries/tools/structured_data.mjs +3 -3
  78. package/batteries/tools/text_analysis.cjs +3 -3
  79. package/batteries/tools/text_analysis.mjs +3 -3
  80. package/batteries/tools/text_comparison.cjs +2 -2
  81. package/batteries/tools/text_comparison.mjs +2 -2
  82. package/batteries/tools/time.cjs +3 -3
  83. package/batteries/tools/time.mjs +3 -3
  84. package/batteries/tools/unit_conversion.cjs +2 -2
  85. package/batteries/tools/unit_conversion.mjs +2 -2
  86. package/batteries.cjs +1 -0
  87. package/batteries.mjs +2 -2
  88. package/{common-Bfqzwdpd.js → common-D_e5zYsG.js} +7 -7
  89. package/{common-Bfqzwdpd.js.map → common-D_e5zYsG.js.map} +1 -1
  90. package/{common-Bo5q3gDK.mjs → common-lMrnzoyn.mjs} +7 -7
  91. package/{common-Bo5q3gDK.mjs.map → common-lMrnzoyn.mjs.map} +1 -1
  92. package/common.cjs +7 -7
  93. package/common.mjs +7 -7
  94. package/{dispatch_runner-DNy8EuK2.mjs → dispatch_runner-CDF3X0nv.mjs} +3 -3
  95. package/{dispatch_runner-DNy8EuK2.mjs.map → dispatch_runner-CDF3X0nv.mjs.map} +1 -1
  96. package/{dispatch_runner-D_EIoEwR.js → dispatch_runner-CpuyATj1.js} +3 -3
  97. package/{dispatch_runner-D_EIoEwR.js.map → dispatch_runner-CpuyATj1.js.map} +1 -1
  98. package/dispatch_runner.cjs +1 -1
  99. package/dispatch_runner.mjs +1 -1
  100. package/forge.cjs +4 -4
  101. package/forge.mjs +4 -4
  102. package/guards.cjs +8 -8
  103. package/guards.mjs +8 -8
  104. package/index.cjs +11 -11
  105. package/index.mjs +11 -11
  106. package/mcp/adk-docs-corpus.json +1 -1
  107. package/package.json +97 -97
  108. package/skills/adk-assembly/SKILL.md +2 -2
  109. package/{spooled_artifact-Chp6PEr8.js → spooled_artifact-B8gIIn9h.js} +3 -3
  110. package/{spooled_artifact-Chp6PEr8.js.map → spooled_artifact-B8gIIn9h.js.map} +1 -1
  111. package/{spooled_artifact-xpzZicMR.mjs → spooled_artifact-CWoKUDEm.mjs} +3 -3
  112. package/{spooled_artifact-xpzZicMR.mjs.map → spooled_artifact-CWoKUDEm.mjs.map} +1 -1
  113. package/spooled_artifact.cjs +2 -2
  114. package/spooled_artifact.mjs +2 -2
  115. package/{spooled_markdown_artifact-CigJMNOl.mjs → spooled_markdown_artifact-CNle4jXN.mjs} +3 -3
  116. package/{spooled_markdown_artifact-CigJMNOl.mjs.map → spooled_markdown_artifact-CNle4jXN.mjs.map} +1 -1
  117. package/{spooled_markdown_artifact-b72JfMYo.js → spooled_markdown_artifact-DQX0RCdI.js} +3 -3
  118. package/{spooled_markdown_artifact-b72JfMYo.js.map → spooled_markdown_artifact-DQX0RCdI.js.map} +1 -1
  119. package/{thought-pxBGk4lN.js → thought-BD6AkkOr.js} +3 -3
  120. package/{thought-pxBGk4lN.js.map → thought-BD6AkkOr.js.map} +1 -1
  121. package/{thought-BhoBfz9K.mjs → thought-B_P8LiB6.mjs} +3 -3
  122. package/{thought-BhoBfz9K.mjs.map → thought-B_P8LiB6.mjs.map} +1 -1
  123. package/{tool-C381-NLO.mjs → tool-CRZSUcdP.mjs} +3 -3
  124. package/{tool-C381-NLO.mjs.map → tool-CRZSUcdP.mjs.map} +1 -1
  125. package/{tool-CxIEutdk.js → tool-CX9vNHAw.js} +3 -3
  126. package/{tool-CxIEutdk.js.map → tool-CX9vNHAw.js.map} +1 -1
  127. package/{tool_call-BPPa_KYu.js → tool_call--7ti-frB.js} +4 -4
  128. package/{tool_call-BPPa_KYu.js.map → tool_call--7ti-frB.js.map} +1 -1
  129. package/{tool_call-H02gzSIs.mjs → tool_call-BUeMuCc6.mjs} +4 -4
  130. package/{tool_call-H02gzSIs.mjs.map → tool_call-BUeMuCc6.mjs.map} +1 -1
  131. package/{tool_registry-CbtMt21B.mjs → tool_registry-BGHg6KTq.mjs} +142 -142
  132. package/tool_registry-BGHg6KTq.mjs.map +1 -0
  133. package/{tool_registry-CoM6fy7F.js → tool_registry-CtCQ4Xoz.js} +141 -141
  134. package/tool_registry-CtCQ4Xoz.js.map +1 -0
  135. package/{turn_runner-DFW9OuBj.js → turn_runner-BJTtAORU.js} +5 -5
  136. package/{turn_runner-DFW9OuBj.js.map → turn_runner-BJTtAORU.js.map} +1 -1
  137. package/{turn_runner-CJke8_wy.mjs → turn_runner-C02LZHjt.mjs} +5 -5
  138. package/{turn_runner-CJke8_wy.mjs.map → turn_runner-C02LZHjt.mjs.map} +1 -1
  139. package/turn_runner.cjs +1 -1
  140. package/turn_runner.mjs +1 -1
  141. package/tool_registry-CbtMt21B.mjs.map +0 -1
  142. package/tool_registry-CoM6fy7F.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to `@nhtio/adk` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 2026-06-04
9
+
10
+ ### Fixed
11
+
12
+ - **LLM batteries now surface reasoning from providers that use the `reasoning` field.** The OpenAI
13
+ and WebLLM Chat Completions batteries read only `reasoning_content`, so thinking output from
14
+ endpoints that emit `reasoning` (Ollama's `/v1`, post-rename vLLM, OpenRouter) produced **no**
15
+ thought events in either streaming or non-streaming mode. Reasoning is not part of OpenAI's
16
+ official Chat Completions spec, so OpenAI-compatible providers disagree on the field name; both
17
+ batteries now read `reasoning` and `reasoning_content` across both the streaming delta and
18
+ non-streaming message shapes. Verified live against a per-model matrix of real endpoints
19
+ (claude-haiku-4-5, gemini-3.5-flash, gemma4, deepseek-v4-flash, glm-5.1, gpt-oss:20b, kimi-k2.6,
20
+ and a workstation Ollama tag).
21
+
22
+ ### Added
23
+
24
+ - **`reasoningFieldPrecedence` option on the Chat Completions batteries.** An ordered, de-duplicating
25
+ control over which provider reasoning field wins. When more than one listed field is present with
26
+ identical content (or only one is present) a single thought is emitted, attributed to the
27
+ highest-precedence field; when they diverge, each surfaces as its own thought rather than silently
28
+ dropping one (in streaming mode both stream live and are de-duplicated by content at persistence).
29
+ Defaults to `['reasoning', 'reasoning_content']`. A typed `reasoning` field was added to the
30
+ `ChatCompletionsChunkDelta` and `ChatCompletionsResponseMessage` wire shapes, and the new
31
+ `ReasoningField` / `ReasoningFieldPrecedence` / `ReasoningExtract` types plus the
32
+ `extractReasoningFields` helper are exported from both batteries.
33
+
8
34
  ## 2026-06-03
9
35
 
10
36
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../../chunk-Ble4zEEl.js");
3
- const require_tool_registry = require("../../../tool_registry-CoM6fy7F.js");
3
+ const require_tool_registry = require("../../../tool_registry-CtCQ4Xoz.js");
4
4
  require("../../../guards.cjs");
5
5
  const require_lib_utils_retry = require("../../../lib/utils/retry.cjs");
6
6
  const require_batteries_embeddings_openai_exceptions = require("./exceptions.cjs");
@@ -1,4 +1,4 @@
1
- import { o as isError } from "../../../tool_registry-CbtMt21B.mjs";
1
+ import { o as isError } from "../../../tool_registry-BGHg6KTq.mjs";
2
2
  import "../../../guards.mjs";
3
3
  import { computeBackoff, parseRetryAfter, sleepWithJitter } from "../../../lib/utils/retry.mjs";
4
4
  import { E_OPENAI_EMBEDDINGS_HTTP_ERROR, E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE, E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT } from "./exceptions.mjs";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../../chunk-Ble4zEEl.js");
3
- const require_tool_registry = require("../../../tool_registry-CoM6fy7F.js");
3
+ const require_tool_registry = require("../../../tool_registry-CtCQ4Xoz.js");
4
4
  require("../../../guards.cjs");
5
5
  const require_batteries_embeddings_openai_helpers = require("../openai/helpers.cjs");
6
6
  const require_batteries_embeddings_webllm_exceptions = require("./exceptions.cjs");
@@ -1,4 +1,4 @@
1
- import { o as isError } from "../../../tool_registry-CbtMt21B.mjs";
1
+ import { o as isError } from "../../../tool_registry-BGHg6KTq.mjs";
2
2
  import "../../../guards.mjs";
3
3
  import { applyEmbeddingPrefix } from "../openai/helpers.mjs";
4
4
  import { E_INVALID_WEBLLM_EMBEDDINGS_OPTIONS, E_WEBLLM_EMBEDDINGS_ENGINE_ERROR } from "./exceptions.mjs";
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../../chunk-Ble4zEEl.js");
3
- const require_tool_registry = require("../../../tool_registry-CoM6fy7F.js");
4
- const require_thought = require("../../../thought-pxBGk4lN.js");
5
- const require_spooled_artifact = require("../../../spooled_artifact-Chp6PEr8.js");
6
- require("../../../common-Bfqzwdpd.js");
7
- const require_tool_call = require("../../../tool_call-BPPa_KYu.js");
3
+ const require_tool_registry = require("../../../tool_registry-CtCQ4Xoz.js");
4
+ const require_thought = require("../../../thought-BD6AkkOr.js");
5
+ const require_spooled_artifact = require("../../../spooled_artifact-B8gIIn9h.js");
6
+ require("../../../common-D_e5zYsG.js");
7
+ const require_tool_call = require("../../../tool_call--7ti-frB.js");
8
8
  require("../../../guards.cjs");
9
9
  const require_lib_utils_retry = require("../../../lib/utils/retry.cjs");
10
10
  const require_batteries_llm_openai_chat_completions_exceptions = require("./exceptions.cjs");
@@ -77,6 +77,7 @@ var ADK_CONTROL_KEYS = new Set([
77
77
  "thoughtSurfacing",
78
78
  "tokenEncoding",
79
79
  "replayCompatibility",
80
+ "reasoningFieldPrecedence",
80
81
  "helpers",
81
82
  "streamIdleTimeoutMs",
82
83
  "requestTimeoutMs",
@@ -624,6 +625,7 @@ var OpenAIChatCompletionsAdapter = class OpenAIChatCompletionsAdapter {
624
625
  }));
625
626
  };
626
627
  const selfIdentity = merged.selfIdentity ?? "assistant";
628
+ const reasoningPrecedence = merged.reasoningFieldPrecedence ?? ["reasoning", "reasoning_content"];
627
629
  if (stream) {
628
630
  if (!response.body) {
629
631
  ctx.nack(new require_batteries_llm_openai_chat_completions_exceptions.E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR(["response has no body"]));
@@ -637,10 +639,11 @@ var OpenAIChatCompletionsAdapter = class OpenAIChatCompletionsAdapter {
637
639
  let idleTimer;
638
640
  let stalled = false;
639
641
  let partialMessageContent = "";
640
- let partialThoughtContent = "";
641
642
  let sawMessageDelta = false;
642
- let sawThoughtDelta = false;
643
643
  let doneSentinelSeen = false;
644
+ const thoughtAccum = /* @__PURE__ */ new Map();
645
+ let primaryThoughtField;
646
+ const thoughtStreamId = (field) => field === primaryThoughtField ? streamId : `${streamId}:${field}`;
644
647
  const idleMs = merged.streamIdleTimeoutMs ?? 0;
645
648
  const armIdleTimer = () => {
646
649
  if (idleMs <= 0) return;
@@ -673,24 +676,30 @@ var OpenAIChatCompletionsAdapter = class OpenAIChatCompletionsAdapter {
673
676
  updatedAt: nowIso()
674
677
  }));
675
678
  }
676
- if (sawThoughtDelta) {
677
- helpers.reportThought(streamId, "", { isComplete: true });
678
- await ctx.storeThought(new require_thought.Thought({
679
- id: streamId,
680
- content: partialThoughtContent,
681
- identity: selfIdentity,
682
- createdAt: nowIso(),
683
- updatedAt: nowIso()
684
- }));
685
- }
679
+ for (const field of thoughtAccum.keys()) helpers.reportThought(thoughtStreamId(field), "", { isComplete: true });
680
+ const thoughtExtracts = require_batteries_llm_openai_chat_completions_helpers.extractReasoningFields(Object.fromEntries(thoughtAccum), reasoningPrecedence);
681
+ if (thoughtExtracts.length === 1) await ctx.storeThought(new require_thought.Thought({
682
+ id: streamId,
683
+ content: thoughtExtracts[0].content,
684
+ identity: selfIdentity,
685
+ createdAt: nowIso(),
686
+ updatedAt: nowIso()
687
+ }));
688
+ else for (const { field, content } of thoughtExtracts) await ctx.storeThought(new require_thought.Thought({
689
+ id: thoughtStreamId(field),
690
+ content,
691
+ identity: selfIdentity,
692
+ createdAt: nowIso(),
693
+ updatedAt: nowIso()
694
+ }));
686
695
  const calls = accumulator.drain();
687
696
  helpers.log.debug({
688
697
  kind: "accumulator-finalised",
689
- message: `Stream finalised: ${calls.length} tool call(s), message=${sawMessageDelta}, thought=${sawThoughtDelta}`,
698
+ message: `Stream finalised: ${calls.length} tool call(s), message=${sawMessageDelta}, thoughtFields=${thoughtExtracts.length}`,
690
699
  payload: {
691
700
  toolCallCount: calls.length,
692
701
  sawMessageDelta,
693
- sawThoughtDelta,
702
+ thoughtFieldCount: thoughtExtracts.length,
694
703
  doneSentinelSeen
695
704
  }
696
705
  });
@@ -746,11 +755,15 @@ var OpenAIChatCompletionsAdapter = class OpenAIChatCompletionsAdapter {
746
755
  partialMessageContent += delta.content;
747
756
  helpers.reportMessage(streamId, delta.content);
748
757
  }
749
- const reasoning = delta.reasoning_content;
750
- if (typeof reasoning === "string" && reasoning.length > 0) {
751
- sawThoughtDelta = true;
752
- partialThoughtContent += reasoning;
753
- helpers.reportThought(streamId, reasoning);
758
+ for (const field of reasoningPrecedence) {
759
+ const reasoning = delta[field];
760
+ if (typeof reasoning !== "string" || reasoning.length === 0) continue;
761
+ if (!thoughtAccum.has(field)) {
762
+ primaryThoughtField ??= field;
763
+ thoughtAccum.set(field, "");
764
+ }
765
+ thoughtAccum.set(field, thoughtAccum.get(field) + reasoning);
766
+ helpers.reportThought(thoughtStreamId(field), reasoning);
754
767
  }
755
768
  if (Array.isArray(delta.tool_calls)) for (const d of delta.tool_calls) accumulator.feed(d);
756
769
  }
@@ -812,13 +825,13 @@ var OpenAIChatCompletionsAdapter = class OpenAIChatCompletionsAdapter {
812
825
  updatedAt: nowIso()
813
826
  }));
814
827
  }
815
- const reasoning = msg?.reasoning_content;
816
- if (typeof reasoning === "string" && reasoning.length > 0) {
817
- const thoughtId = `${responseId}:thought`;
818
- helpers.reportThought(thoughtId, reasoning, { isComplete: true });
828
+ const reasoningExtracts = require_batteries_llm_openai_chat_completions_helpers.extractReasoningFields(msg, reasoningPrecedence);
829
+ for (const { field, content } of reasoningExtracts) {
830
+ const thoughtId = reasoningExtracts.length > 1 ? `${responseId}:thought:${field}` : `${responseId}:thought`;
831
+ helpers.reportThought(thoughtId, content, { isComplete: true });
819
832
  await ctx.storeThought(new require_thought.Thought({
820
833
  id: thoughtId,
821
- content: reasoning,
834
+ content,
822
835
  identity: selfIdentity,
823
836
  createdAt: nowIso(),
824
837
  updatedAt: nowIso()
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.cjs","names":["#baseline"],"sources":["../../../../src/batteries/llm/openai_chat_completions/adapter.ts"],"sourcesContent":["/**\n * Cross-environment executor adapter for OpenAI Chat Completions compatible endpoints.\n *\n * @module @nhtio/adk/batteries/llm/openai_chat_completions/adapter\n *\n * @remarks\n * Cross-environment LLM adapter for the OpenAI Chat Completions wire shape. Chat Completions was\n * chosen as the ADK's reference adapter because it is the de-facto interchange format for the\n * majority of OpenAI-compatible gateways (vLLM, Together, Groq, Fireworks, Ollama, Azure OpenAI,\n * OpenRouter, DeepSeek, Mistral La Plateforme, and most self-hosted deployments). Its tool-call\n * synthetic-history shape (`role: 'assistant', tool_calls: [...]` followed by `role: 'tool'` with\n * `tool_call_id`) is the lowest-common-denominator that every conformant gateway accepts.\n *\n * The adapter is built around three pluggable layers:\n *\n * 1. **Translation helpers** — the thirteen swappable functions exported from `./helpers` turn\n * ADK primitives ({@link @nhtio/adk!Tokenizable}, {@link @nhtio/adk!Memory}, {@link @nhtio/adk!Message}, {@link @nhtio/adk!Thought},\n * {@link @nhtio/adk!ToolCall}, {@link @nhtio/adk!Tool}, {@link @nhtio/adk!ArtifactTool}, {@link @nhtio/adk!SpooledArtifact}) into Chat\n * Completions wire shapes. Consumers override individual helpers via `options.helpers.*` to\n * customise envelope formats, bucket ordering, thought surfacing, or JSON Schema generation\n * without forking the adapter.\n * 2. **Three-layer options merging** — constructor baseline, per-`executor()` overrides, and\n * per-iteration `ctx.stash.openaiChatCompletions` overrides combine with key-by-key\n * precedence for `headers`/`helpers`/`retry` and wholesale replacement for everything else.\n * The merged shape is re-validated on every iteration so a malformed stash override\n * fails loud, not silently.\n * 3. **Cross-env transport** — uses `globalThis.fetch`, `TextDecoder`, `AbortController`, and\n * `AbortSignal.any` only. No Node-specific primitives; works unchanged in browser, edge, and\n * server runtimes. SSE framing is hand-parsed against `\\n\\n` separators so the streaming path\n * has no dependency on a particular SSE library.\n *\n * Per-iteration flow (steps 1–9 of the plan):\n * 1. Merge constructor / executor / stash options and re-validate.\n * 2. Resolve helpers, falling back to bundled `default*` for each unset field.\n * 3. Forge artifact-query tools by walking `ctx.turnToolCalls`, collecting unique\n * `SpooledArtifact` constructors, calling `<Ctor>.forgeTools(ctx)` on each, and merging the\n * results with `ctx.tools`.\n * 4. Pre-render every persisted tool-call result into the prompt-ready string the timeline will\n * use, cached by `tc.id`.\n * 5. When `tokenEncoding !== null`, sum the token weight of every persisted bucket and throw\n * {@link @nhtio/adk/batteries!E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW} when the total exceeds `contextWindow`.\n * 6. Build the request body via `buildChatCompletionsHistory`; carry vendor-opaque reasoning\n * blocks through the `_adk_reasoning_payloads` side-channel.\n * 7. POST with a linked `AbortSignal`, retry loop respecting `retry.*`, and a request-timeout\n * watchdog that arms before fetch and clears once response headers arrive.\n * 8. Streaming path: SSE parse via `response.body.getReader()` + `TextDecoder`; surface deltas\n * through `helpers.reportMessage` / `reportThought` / `reportToolCall`; assemble tool-call\n * deltas via the accumulator; persist `Message` / `Thought` / `ToolCall` records on `[DONE]`.\n * 9. Non-streaming path: single `response.json()`; same persistence + tool-execution loop.\n */\n\nimport { DateTime } from 'luxon'\nimport { sha256 } from 'js-sha256'\nimport { v6 as uuidv6 } from 'uuid'\nimport { validateOptions } from './validation'\nimport { isError, isInstanceOf, isObject } from '@nhtio/adk/guards'\nimport { canonicalStringify } from '../../../lib/utils/canonical_json'\nimport { InMemorySpoolStore } from '@nhtio/adk/batteries/storage/in_memory'\nimport {\n computeBackoff,\n sleepWithJitter,\n parseRetryAfter,\n linkAbortSignals,\n} from '../../../lib/utils/retry'\nimport {\n Tokenizable,\n ToolRegistry,\n ToolCall,\n Message,\n Thought,\n SpooledArtifact,\n Media,\n ArtifactTool,\n} from '@nhtio/adk/common'\nimport {\n E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS,\n E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW,\n E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR,\n E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR,\n E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED,\n E_OPENAI_CHAT_COMPLETIONS_REQUEST_TIMEOUT,\n E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS,\n} from './exceptions'\nimport {\n defaultDescriptionToChatCompletionsJsonSchema,\n defaultRenderUntrustedContent,\n defaultRenderTrustedContent,\n defaultRenderStandingInstructions,\n defaultRenderMemories,\n defaultRenderRetrievables,\n defaultRenderRetrievableSafetyDirective,\n defaultRenderFirstPartyRetrievables,\n defaultRenderThirdPartyPublicRetrievables,\n defaultRenderThirdPartyPrivateRetrievables,\n defaultRenderTimelineMessage,\n defaultRenderThought,\n defaultFilterThoughts,\n defaultToolsToChatCompletionsTools,\n defaultRenderChatCompletionsSystemPrompt,\n defaultRenderChatCompletionsToolCallResult,\n defaultBuildChatCompletionsHistory,\n defaultCreateChatCompletionsToolCallDeltaAccumulator,\n} from './helpers'\nimport type { DispatchContext } from '@nhtio/adk/types'\nimport type { Tool, Memory, TokenEncoding } from '@nhtio/adk/common'\nimport type { DispatchExecutorFn, DispatchExecutorHelpers } from '@nhtio/adk/dispatch_runner'\nimport type {\n OpenAIChatCompletionsAdapterOptions,\n ChatCompletionsHelpers,\n ChatCompletionsRetryConfig,\n OpenAIChatCompletionsRequestBody,\n ChatCompletionsChunk,\n ChatCompletionsResponse,\n AssembledToolCall,\n ChatCompletionsContentBlock,\n} from './types'\n\n// ─── ADK-control keys (stripped before sending the request body) ──────────\n\nconst ADK_CONTROL_KEYS: ReadonlySet<string> = new Set([\n 'apiKey',\n 'baseURL',\n 'headers',\n 'fetch',\n 'stream',\n 'bucketOrder',\n 'contextWindow',\n 'selfIdentity',\n 'thoughtSurfacing',\n 'tokenEncoding',\n 'replayCompatibility',\n 'helpers',\n 'streamIdleTimeoutMs',\n 'requestTimeoutMs',\n 'retry',\n 'strictToolChoice',\n 'autoAck',\n 'unsupportedMediaPolicy',\n])\n\n// ─── Option merging ───────────────────────────────────────────────────────────\n\nconst mergeRetry = (\n layers: ReadonlyArray<ChatCompletionsRetryConfig | undefined>\n): ChatCompletionsRetryConfig | undefined => {\n let merged: ChatCompletionsRetryConfig | undefined\n for (const layer of layers) {\n if (!layer) continue\n merged = { ...(merged ?? {}), ...layer }\n }\n return merged\n}\n\nconst mergeHeaders = (\n layers: ReadonlyArray<Record<string, string> | undefined>\n): Record<string, string> | undefined => {\n let merged: Record<string, string> | undefined\n for (const layer of layers) {\n if (!layer) continue\n merged = { ...(merged ?? {}), ...layer }\n }\n return merged\n}\n\nconst mergeHelpers = (\n layers: ReadonlyArray<Partial<ChatCompletionsHelpers> | undefined>\n): Partial<ChatCompletionsHelpers> | undefined => {\n let merged: Partial<ChatCompletionsHelpers> | undefined\n for (const layer of layers) {\n if (!layer) continue\n merged = { ...(merged ?? {}), ...layer }\n }\n return merged\n}\n\nconst mergeOptions = (\n baseline: OpenAIChatCompletionsAdapterOptions,\n exec: Partial<OpenAIChatCompletionsAdapterOptions> | undefined,\n stash: Partial<OpenAIChatCompletionsAdapterOptions> | undefined\n): Partial<OpenAIChatCompletionsAdapterOptions> => {\n const layers = [baseline as Partial<OpenAIChatCompletionsAdapterOptions>, exec ?? {}, stash ?? {}]\n const out: Record<string, unknown> = {}\n for (const layer of layers) {\n for (const [k, v] of Object.entries(layer)) {\n if (v === undefined) continue\n if (k === 'headers' || k === 'helpers' || k === 'retry') continue\n out[k] = v\n }\n }\n const headers = mergeHeaders(layers.map((l) => l.headers))\n if (headers !== undefined) out.headers = headers\n const helpers = mergeHelpers(layers.map((l) => l.helpers))\n if (helpers !== undefined) out.helpers = helpers\n const retry = mergeRetry(layers.map((l) => l.retry))\n if (retry !== undefined) out.retry = retry\n return out as Partial<OpenAIChatCompletionsAdapterOptions>\n}\n\n// ─── Helper resolution ────────────────────────────────────────────────────────\n\nconst resolveHelpers = (\n overrides: Partial<ChatCompletionsHelpers> | undefined\n): ChatCompletionsHelpers => {\n const src = overrides ?? {}\n return {\n descriptionToChatCompletionsJsonSchema:\n src.descriptionToChatCompletionsJsonSchema ?? defaultDescriptionToChatCompletionsJsonSchema,\n renderUntrustedContent: src.renderUntrustedContent ?? defaultRenderUntrustedContent,\n renderTrustedContent: src.renderTrustedContent ?? defaultRenderTrustedContent,\n renderStandingInstructions: src.renderStandingInstructions ?? defaultRenderStandingInstructions,\n renderMemories: src.renderMemories ?? defaultRenderMemories,\n renderRetrievables: src.renderRetrievables ?? defaultRenderRetrievables,\n renderRetrievableSafetyDirective:\n src.renderRetrievableSafetyDirective ?? defaultRenderRetrievableSafetyDirective,\n renderFirstPartyRetrievables:\n src.renderFirstPartyRetrievables ?? defaultRenderFirstPartyRetrievables,\n renderThirdPartyPublicRetrievables:\n src.renderThirdPartyPublicRetrievables ?? defaultRenderThirdPartyPublicRetrievables,\n renderThirdPartyPrivateRetrievables:\n src.renderThirdPartyPrivateRetrievables ?? defaultRenderThirdPartyPrivateRetrievables,\n renderTimelineMessage: src.renderTimelineMessage ?? defaultRenderTimelineMessage,\n renderThought: src.renderThought ?? defaultRenderThought,\n filterThoughts: src.filterThoughts ?? defaultFilterThoughts,\n toolsToChatCompletionsTools:\n src.toolsToChatCompletionsTools ?? defaultToolsToChatCompletionsTools,\n renderChatCompletionsSystemPrompt:\n src.renderChatCompletionsSystemPrompt ?? defaultRenderChatCompletionsSystemPrompt,\n renderChatCompletionsToolCallResult:\n src.renderChatCompletionsToolCallResult ?? defaultRenderChatCompletionsToolCallResult,\n buildChatCompletionsHistory:\n src.buildChatCompletionsHistory ?? defaultBuildChatCompletionsHistory,\n createChatCompletionsToolCallDeltaAccumulator:\n src.createChatCompletionsToolCallDeltaAccumulator ??\n defaultCreateChatCompletionsToolCallDeltaAccumulator,\n }\n}\n\n// ─── Retry / timeout helpers ──────────────────────────────────────────────────\n// Shared with the OpenAI Embeddings battery — see `../../../lib/utils/retry`. These are pure,\n// environment-neutral primitives; the import keeps retry behavior identical across batteries\n// without duplication.\n\n// ─── ID helpers ───────────────────────────────────────────────────────────────\n\n// Canonical (key-order-insensitive) checksum — MUST match the contract documented\n// on canonicalStringify and used by Tool.executor + dispatch_runner's streaming\n// helper, so ctx.toolCallCount(checksum) detects semantically-identical repeat\n// calls regardless of argument key order.\nconst computeChecksum = (tool: string, args: unknown): string =>\n sha256(canonicalStringify({ tool, args }))\n\nconst nowIso = (): string => DateTime.now().toISO() ?? new Date().toISOString()\n\n// ─── Token measurement ────────────────────────────────────────────────────────\n\nconst estimateTokensOf = async (\n value: { estimateTokens: (encoding: TokenEncoding) => number | Promise<number> },\n encoding: TokenEncoding\n): Promise<number> => {\n return Promise.resolve(value.estimateTokens(encoding))\n}\n\n// ─── Adapter class ────────────────────────────────────────────────────────────\n\n/**\n * Opinionated cross-environment LLM adapter for the OpenAI Chat Completions wire shape.\n *\n * @remarks\n * Construction validates options eagerly via {@link @nhtio/adk/batteries!validateOptions} and throws\n * {@link @nhtio/adk/batteries!E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS} on failure — config bugs fail loud, not at\n * dispatch time. The returned instance is reusable: call {@link OpenAIChatCompletionsAdapter.executor}\n * once per `DispatchRunner` configuration to obtain an {@link @nhtio/adk!DispatchExecutorFn} bound to the\n * baseline plus optional executor-scope overrides.\n *\n * Per-iteration overrides live on the active {@link @nhtio/adk!DispatchContext}'s\n * `stash.openaiChatCompletions` slot and take highest precedence — they merge into the\n * executor-scope shape on every iteration. `headers`, `helpers`, and `retry` merge key-by-key\n * across all three layers; every other field is replaced wholesale at the highest layer that\n * sets it.\n */\nexport class OpenAIChatCompletionsAdapter {\n /**\n * Customary key for per-iteration overrides on `ctx.stash`. The adapter reads\n * `ctx.stash.get(OpenAIChatCompletionsAdapter.STASH_KEY, {})` at the start of every\n * iteration and merges the value into the resolved options shape.\n */\n public static readonly STASH_KEY = 'openaiChatCompletions' as const\n\n readonly #baseline: OpenAIChatCompletionsAdapterOptions\n\n /**\n * @param options - Constructor-baseline options. Re-validated on every iteration after\n * per-dispatch and per-iteration overrides are layered in.\n * @throws {@link @nhtio/adk/batteries!E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS} when `options` does not satisfy\n * {@link @nhtio/adk/batteries!openAIChatCompletionsOptionsSchema}.\n */\n constructor(options: unknown) {\n this.#baseline = validateOptions(options)\n }\n\n /**\n * Returns an {@link @nhtio/adk!DispatchExecutorFn} bound to this adapter's baseline plus optional\n * executor-scope overrides. The returned function is reusable across iterations — every\n * iteration re-merges with `ctx.stash[STASH_KEY]` and re-validates the result.\n *\n * @param overrides - Optional executor-scope overrides. Higher precedence than the baseline,\n * lower precedence than `ctx.stash[STASH_KEY]`.\n * @returns An {@link @nhtio/adk!DispatchExecutorFn} suitable for `DispatchRunner`.\n */\n executor(overrides?: Partial<OpenAIChatCompletionsAdapterOptions>): DispatchExecutorFn {\n const baseline = this.#baseline\n const adapterClass = OpenAIChatCompletionsAdapter\n return async (ctx: DispatchContext, helpers: DispatchExecutorHelpers): Promise<void> => {\n // Bridge helpers.log → the legacy `warn: (msg) => void` slot exposed by the per-call\n // helper signatures. Helpers downstream don't need to know the structured shape — they\n // emit a single string, which we route to `helpers.log.warn` with a stable `kind` so\n // observability middleware can still filter and aggregate.\n const localWarn = (msg: string): void => {\n helpers.log.warn({ kind: 'helper-warning', message: msg })\n }\n\n // ── Step 1: merge & validate ──────────────────────────────────────────\n const stashRaw = ctx.stash.get(adapterClass.STASH_KEY, {}) as unknown\n const stashOverrides =\n stashRaw && typeof stashRaw === 'object'\n ? (stashRaw as Partial<OpenAIChatCompletionsAdapterOptions>)\n : {}\n const mergedRaw = mergeOptions(baseline, overrides, stashOverrides)\n const merged = validateOptions(mergedRaw)\n\n // Cross-field invariant: tokenEncoding non-null requires contextWindow.\n if (merged.tokenEncoding !== null && merged.contextWindow === undefined) {\n throw new E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS([\n 'tokenEncoding is non-null but contextWindow is undefined',\n ])\n }\n\n // ── Step 2: resolve helpers ───────────────────────────────────────────\n const resolvedHelpers = resolveHelpers(merged.helpers)\n\n // ── Step 3: forge artifact-query tools ────────────────────────────────\n const uniqueCtors = new Set<typeof SpooledArtifact>()\n for (const tc of ctx.turnToolCalls) {\n const results = tc.results as unknown as { constructor?: unknown }\n const ctor = results?.constructor\n if (ctor && SpooledArtifact.isSpooledArtifactConstructor(ctor)) {\n uniqueCtors.add(ctor as unknown as typeof SpooledArtifact)\n }\n }\n const forgedRegistries: ToolRegistry[] = []\n for (const ctor of uniqueCtors) {\n const forgeFn = (\n ctor as unknown as {\n forgeTools?: (c: DispatchContext) => ToolRegistry\n }\n ).forgeTools\n if (typeof forgeFn === 'function') {\n forgedRegistries.push(forgeFn.call(ctor, ctx))\n }\n }\n const mergedRegistry = ToolRegistry.merge([ctx.tools, ...forgedRegistries], {\n onCollision: 'replace',\n })\n mergedRegistry.bindContext(ctx)\n\n // ── Step 4: pre-render tool-call results ──────────────────────────────\n const renderedToolCallResults = new Map<string, string | ChatCompletionsContentBlock[]>()\n for (const tc of ctx.turnToolCalls) {\n const rendered = await resolvedHelpers.renderChatCompletionsToolCallResult({\n toolCall: tc,\n results: tc.results as\n | Tokenizable\n | SpooledArtifact\n | SpooledArtifact[]\n | Media\n | Media[],\n tool: mergedRegistry.get(tc.tool) as Tool | ArtifactTool | undefined,\n renderUntrustedContent: resolvedHelpers.renderUntrustedContent,\n renderTrustedContent: resolvedHelpers.renderTrustedContent,\n unsupportedMediaPolicy: merged.unsupportedMediaPolicy ?? 'throw',\n warn: localWarn,\n })\n renderedToolCallResults.set(tc.id, rendered)\n }\n\n // ── Step 5: context window enforcement ────────────────────────────────\n if (merged.tokenEncoding !== null && merged.contextWindow !== undefined) {\n const encoding = merged.tokenEncoding as TokenEncoding\n let spTokens = await estimateTokensOf(ctx.systemPrompt, encoding)\n let siTokens = 0\n for (const si of ctx.standingInstructions) {\n siTokens += await estimateTokensOf(si, encoding)\n }\n let memTokens = 0\n for (const mem of ctx.turnMemories as Set<Memory>) {\n memTokens += await estimateTokensOf(mem.content, encoding)\n }\n let retTokens = 0\n for (const r of ctx.turnRetrievables) {\n retTokens += await estimateTokensOf(r.content, encoding)\n }\n let tlTokens = 0\n for (const msg of ctx.turnMessages) {\n if (msg.content !== undefined) {\n tlTokens += await estimateTokensOf(msg.content, encoding)\n }\n }\n for (const th of ctx.turnThoughts) {\n tlTokens += await estimateTokensOf(th.content, encoding)\n }\n for (const rendered of renderedToolCallResults.values()) {\n const textPart =\n typeof rendered === 'string'\n ? rendered\n : rendered\n .filter((b): b is { type: 'text'; text: string } => b.type === 'text')\n .map((b) => b.text)\n .join('\\n')\n const tk = new Tokenizable(textPart)\n tlTokens += await estimateTokensOf(tk, encoding)\n }\n const total = spTokens + siTokens + memTokens + retTokens + tlTokens\n const perBucketObj = {\n systemPrompt: spTokens,\n standingInstructions: siTokens,\n memories: memTokens,\n retrievables: retTokens,\n timeline: tlTokens,\n }\n helpers.log.debug({\n kind: 'context-window-usage',\n message: `Context window usage: ${total}/${merged.contextWindow} tokens`,\n payload: {\n total,\n limit: merged.contextWindow,\n encoding,\n perBucket: perBucketObj,\n },\n })\n if (total > merged.contextWindow) {\n const perBucket = JSON.stringify(perBucketObj)\n throw new E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW([\n total,\n merged.contextWindow,\n encoding,\n perBucket,\n ])\n }\n }\n\n // ── Step 5b: tool_choice + forged artifact-tools guard ────────────────\n // When `tool_choice` (or the `allowed_tools` variant) forces the model onto a specific\n // tool name and that name resolves to an ephemeral, forged artifact-query tool, surface\n // it as a structured warning (or throw under `strictToolChoice: true`). Forging an\n // artifact-query tool by name is almost always a misconfiguration — the tool may not\n // exist in the next iteration once the artifact ages out of `ctx.turnToolCalls`.\n const forcedToolNames: string[] = []\n const toolChoice = merged.tool_choice\n let toolChoiceVariant: 'function' | 'allowed_tools' = 'function'\n if (toolChoice && typeof toolChoice === 'object') {\n if ('function' in toolChoice && toolChoice.type === 'function') {\n forcedToolNames.push(toolChoice.function.name)\n } else if ('custom' in toolChoice && toolChoice.type === 'custom') {\n forcedToolNames.push(toolChoice.custom.name)\n } else if (toolChoice.type === 'allowed_tools') {\n toolChoiceVariant = 'allowed_tools'\n for (const entry of toolChoice.allowed_tools.tools) {\n if ('function' in entry) forcedToolNames.push(entry.function.name)\n else if ('custom' in entry) forcedToolNames.push(entry.custom.name)\n }\n }\n }\n const forcedForgedHits: Array<{ toolName: string }> = []\n for (const name of forcedToolNames) {\n const t = mergedRegistry.get(name) as { ephemeral?: boolean } | undefined\n if (t?.ephemeral === true) {\n forcedForgedHits.push({ toolName: name })\n }\n }\n if (forcedForgedHits.length > 0) {\n if (merged.strictToolChoice === true) {\n throw new E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS([\n `tool_choice forces forged ephemeral artifact-query tool(s): ${forcedForgedHits\n .map((h) => h.toolName)\n .join(\n ', '\n )} — these may not exist on the next iteration. Remove the override or unset strictToolChoice.`,\n ])\n }\n helpers.log.warn({\n kind: 'tool-choice-forged-artifact',\n message: `tool_choice forces ${forcedForgedHits.length} forged ephemeral artifact-query tool(s); this is almost always a misconfiguration`,\n payload: {\n toolNames: forcedForgedHits.map((h) => h.toolName),\n variant: toolChoiceVariant,\n },\n })\n }\n\n // ── Step 6: build request body ────────────────────────────────────────\n const { messages: wireMessages, reasoningPayloads } =\n await resolvedHelpers.buildChatCompletionsHistory({\n systemPrompt: ctx.systemPrompt,\n standingInstructions: ctx.standingInstructions,\n memories: ctx.turnMemories,\n retrievables: ctx.turnRetrievables,\n messages: ctx.turnMessages,\n thoughts: ctx.turnThoughts,\n toolCalls: ctx.turnToolCalls,\n tools: mergedRegistry,\n renderedToolCallResults,\n bucketOrder: merged.bucketOrder ?? [\n 'standingInstructions',\n 'memories',\n 'retrievables',\n 'timeline',\n ],\n selfIdentity: merged.selfIdentity ?? 'assistant',\n thoughtSurfacing: merged.thoughtSurfacing ?? 'all-self',\n replayCompatibility: merged.replayCompatibility ?? [],\n renderChatCompletionsToolCallResult: resolvedHelpers.renderChatCompletionsToolCallResult,\n renderChatCompletionsSystemPrompt: resolvedHelpers.renderChatCompletionsSystemPrompt,\n renderStandingInstructions: resolvedHelpers.renderStandingInstructions,\n renderMemories: resolvedHelpers.renderMemories,\n renderRetrievables: resolvedHelpers.renderRetrievables,\n renderRetrievableSafetyDirective: resolvedHelpers.renderRetrievableSafetyDirective,\n renderFirstPartyRetrievables: resolvedHelpers.renderFirstPartyRetrievables,\n renderThirdPartyPublicRetrievables: resolvedHelpers.renderThirdPartyPublicRetrievables,\n renderThirdPartyPrivateRetrievables: resolvedHelpers.renderThirdPartyPrivateRetrievables,\n renderTimelineMessage: resolvedHelpers.renderTimelineMessage,\n renderThought: resolvedHelpers.renderThought,\n filterThoughts: resolvedHelpers.filterThoughts,\n renderUntrustedContent: resolvedHelpers.renderUntrustedContent,\n renderTrustedContent: resolvedHelpers.renderTrustedContent,\n unsupportedMediaPolicy: merged.unsupportedMediaPolicy ?? 'throw',\n warn: localWarn,\n })\n\n const stream = merged.stream ?? true\n const body: OpenAIChatCompletionsRequestBody = {\n model: merged.model,\n messages: wireMessages,\n stream,\n }\n for (const [k, v] of Object.entries(merged)) {\n if (ADK_CONTROL_KEYS.has(k)) continue\n if (k === 'model' || k === 'messages' || k === 'stream') continue\n if (v === undefined) continue\n ;(body as Record<string, unknown>)[k] = v\n }\n const toolsArr = mergedRegistry.all()\n if (toolsArr.length > 0) {\n body.tools = resolvedHelpers.toolsToChatCompletionsTools(toolsArr, {\n descriptionToChatCompletionsJsonSchema:\n resolvedHelpers.descriptionToChatCompletionsJsonSchema,\n })\n }\n if (reasoningPayloads.length > 0) {\n body._adk_reasoning_payloads = reasoningPayloads\n }\n\n // ── Step 7: POST with retry / timeout loop ────────────────────────────\n const rawBase = merged.baseURL ?? 'https://api.openai.com/v1'\n const baseURL = rawBase.endsWith('/') ? rawBase.slice(0, -1) : rawBase\n const url = `${baseURL}/chat/completions`\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (stream) headers['Accept'] = 'text/event-stream'\n if (merged.apiKey) {\n headers['Authorization'] = `Bearer ${merged.apiKey}`\n }\n // User-supplied headers override built defaults (including Authorization).\n if (merged.headers) {\n Object.assign(headers, merged.headers)\n }\n\n const retryCfg: ChatCompletionsRetryConfig = {\n maxAttempts: merged.retry?.maxAttempts ?? 1,\n baseDelayMs: merged.retry?.baseDelayMs ?? 500,\n maxDelayMs: merged.retry?.maxDelayMs ?? 30_000,\n retriableStatuses: merged.retry?.retriableStatuses ?? [429, 502, 503, 504],\n honorRetryAfter: merged.retry?.honorRetryAfter ?? true,\n }\n\n const fetchFn = merged.fetch ?? globalThis.fetch\n const maxAttempts = retryCfg.maxAttempts ?? 1\n\n let response: Response | undefined\n let attempt = 1\n // The abort-signal link attaches listeners to the long-lived ctx.abortSignal.\n // Each retry attempt re-links, so dispose the PRIOR attempt's link before\n // making a new one — otherwise N retries leave N stale listeners on\n // ctx.abortSignal for the rest of the turn. The final (successful) attempt's\n // link is bounded by the turn's lifetime, same as the native AbortSignal.any.\n let disposeLink: () => void = () => {}\n while (attempt <= maxAttempts) {\n if (ctx.abortSignal.aborted) return\n\n const internalController = new AbortController()\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined\n const requestTimeoutMs = merged.requestTimeoutMs ?? 0\n if (requestTimeoutMs > 0) {\n timeoutHandle = setTimeout(() => internalController.abort(), requestTimeoutMs)\n }\n disposeLink()\n const { signal: linkedSignal, dispose: disposeCurrentLink } = linkAbortSignals([\n ctx.abortSignal,\n internalController.signal,\n ])\n disposeLink = disposeCurrentLink\n\n try {\n response = await fetchFn(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: linkedSignal,\n })\n } catch (err) {\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n if (ctx.abortSignal.aborted) return\n if (internalController.signal.aborted) {\n // Request timed out before headers arrived — eligible for retry.\n helpers.log.warn({\n kind: 'request-timeout',\n message: `Request timed out after ${requestTimeoutMs}ms on attempt ${attempt}/${maxAttempts}`,\n payload: { requestTimeoutMs, attempt, maxAttempts },\n })\n if (attempt < maxAttempts) {\n const delay = computeBackoff(attempt, retryCfg)\n helpers.log.debug({\n kind: 'retry-attempt',\n message: `Retrying after request timeout in ~${delay}ms (attempt ${attempt + 1}/${maxAttempts})`,\n payload: {\n reason: 'request-timeout',\n delayMs: delay,\n attempt: attempt + 1,\n maxAttempts,\n },\n })\n await sleepWithJitter(delay, ctx.abortSignal)\n attempt += 1\n continue\n }\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_REQUEST_TIMEOUT([requestTimeoutMs]))\n return\n }\n // Generic transport failure — surface as HTTP_ERROR with status 0.\n helpers.log.error({\n kind: 'transport-error',\n message: `Transport failure on attempt ${attempt}/${maxAttempts}: ${isError(err) ? err.message : String(err)}`,\n payload: {\n attempt,\n maxAttempts,\n detail: isError(err) ? err.message : String(err),\n },\n })\n ctx.nack(\n new E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR([0, isError(err) ? err.message : String(err)])\n )\n return\n }\n\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n\n if (!response.ok) {\n const status = response.status\n const retriable = (retryCfg.retriableStatuses ?? [429, 502, 503, 504]).includes(status)\n if (retriable && attempt < maxAttempts) {\n let delay = computeBackoff(attempt, retryCfg)\n let retryAfterMs: number | undefined\n if (retryCfg.honorRetryAfter !== false) {\n const ra = response.headers.get('Retry-After')\n if (ra) {\n const raMs = parseRetryAfter(ra)\n if (raMs > 0) {\n retryAfterMs = raMs\n delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs ?? 30_000)\n }\n }\n }\n helpers.log.warn({\n kind: 'retry-attempt',\n message: `HTTP ${status} on attempt ${attempt}/${maxAttempts}; retrying in ~${delay}ms`,\n payload: {\n reason: 'http-status',\n status,\n delayMs: delay,\n ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),\n attempt: attempt + 1,\n maxAttempts,\n },\n })\n await sleepWithJitter(delay, ctx.abortSignal)\n attempt += 1\n continue\n }\n const errBody = await response.text().catch(() => '')\n helpers.log.error({\n kind: 'http-error',\n message: `HTTP ${status} (terminal): ${errBody.slice(0, 256)}`,\n payload: {\n status,\n body: errBody,\n attempt,\n maxAttempts,\n retriable,\n },\n })\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR([status, errBody]))\n return\n }\n\n break\n }\n\n if (!response) return\n\n // Spool store used to back string / Uint8Array tool returns. Bytes are written under the\n // call id; the resulting SpooledArtifact (or tool-configured subclass) is the model-visible\n // handle for the rest of the turn. Injectable via the `spoolStore` option so consumers can\n // back artifacts with durable storage (OPFS, Flydrive); defaults to an ephemeral per-dispatch\n // in-memory store. NOTE: an injected durable store persists across turns, so call ids must be\n // globally unique for that store (see the `spoolStore` option docs).\n const spoolStore = merged.spoolStore ?? new InMemorySpoolStore()\n\n // ── Inner helper: persist + execute one assembled tool call ───────────\n const executeAndPersistToolCall = async (call: AssembledToolCall): Promise<void> => {\n const tool = mergedRegistry.get(call.name)\n // Parse args defensively. The model may emit non-JSON or a non-object\n // JSON value (string, number, array, null); both are recoverable error\n // conditions, NOT dispatch-killers. A parse failure short-circuits to\n // a persisted error ToolCall — formatted by `E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS`\n // so consumers can match on the stable error code — letting the model\n // self-correct on the next iteration.\n let args: Record<string, unknown> = {}\n let parseError:\n | InstanceType<typeof E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS>\n | undefined\n if (call.args && call.args.length > 0) {\n try {\n const parsed: unknown = JSON.parse(call.args)\n if (isObject(parsed)) {\n args = parsed\n } else {\n const receivedKind = Array.isArray(parsed)\n ? 'array'\n : parsed === null\n ? 'null'\n : typeof parsed\n parseError = new E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS([\n `must be a JSON object; received ${receivedKind}`,\n call.args,\n ])\n }\n } catch {\n parseError = new E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS([\n 'are not valid JSON',\n call.args,\n ])\n }\n }\n const completedAt = nowIso()\n if (parseError !== undefined) {\n const results = new Tokenizable(parseError.message)\n helpers.reportToolCall(call.id, { tool: call.name, args })\n helpers.reportToolCall(call.id, {\n results,\n isError: true,\n isComplete: true,\n })\n const checksum = computeChecksum(call.name, args)\n await ctx.storeToolCall(\n new ToolCall({\n id: call.id,\n tool: call.name,\n args,\n checksum,\n isComplete: true,\n isError: true,\n results,\n createdAt: completedAt,\n updatedAt: completedAt,\n completedAt,\n })\n )\n return\n }\n if (!tool) {\n // List the tools that DO exist so the model can self-correct on the next iteration\n // instead of guessing. Without this, a single typo'd / hallucinated tool name yields\n // a dead-end \"not found\" with no path forward.\n const available = mergedRegistry\n .all()\n .map((t) => t.name)\n .sort()\n const errText =\n available.length > 0\n ? `Tool not found: ${call.name}. Available tools: ${available.join(', ')}.`\n : `Tool not found: ${call.name}. No tools are available this turn.`\n const results = new Tokenizable(errText)\n helpers.reportToolCall(call.id, { tool: call.name, args })\n helpers.reportToolCall(call.id, {\n results,\n isError: true,\n isComplete: true,\n })\n const checksum = computeChecksum(call.name, args)\n await ctx.storeToolCall(\n new ToolCall({\n id: call.id,\n tool: call.name,\n args,\n checksum,\n isComplete: true,\n isError: true,\n results,\n createdAt: completedAt,\n updatedAt: completedAt,\n completedAt,\n })\n )\n return\n }\n helpers.reportToolCall(call.id, { tool: tool.name, args })\n const isArtifactTool = ArtifactTool.isArtifactTool(tool)\n let results: Tokenizable | SpooledArtifact | SpooledArtifact[] | Media | Media[] =\n new Tokenizable('')\n let toolHadError = false\n try {\n const raw = await tool.executor(ctx)(args)\n if (isArtifactTool) {\n // ArtifactTool: handler returns a string | Tokenizable that *is* the model-visible\n // answer to a query against a prior artifact. No spool write, no SpooledArtifact\n // construction — pass through (or wrap a bare string in Tokenizable).\n if (Tokenizable.isTokenizable(raw)) {\n results = raw\n } else if (typeof raw === 'string') {\n results = new Tokenizable(raw)\n } else {\n throw new Error(\n `ArtifactTool \"${tool.name}\" returned a non-string/non-Tokenizable value`\n )\n }\n } else if (Media.isMedia(raw)) {\n results = raw\n } else if (Array.isArray(raw) && raw.length > 0 && raw.every((m) => Media.isMedia(m))) {\n results = raw as Media[]\n } else if (typeof raw === 'string' || isInstanceOf(raw, 'Uint8Array', Uint8Array)) {\n const reader = await spoolStore.write(call.id, raw as string | Uint8Array)\n const ArtifactCtor = (tool as Tool).artifactConstructor?.() ?? SpooledArtifact\n results = new ArtifactCtor(reader)\n } else {\n // Defensive fallback — wrap stringified value so the model gets *something*.\n const reader = await spoolStore.write(call.id, String(raw))\n const ArtifactCtor = (tool as Tool).artifactConstructor?.() ?? SpooledArtifact\n results = new ArtifactCtor(reader)\n }\n } catch (err) {\n toolHadError = true\n // Surface field-level validation detail from the cause chain so the\n // model can self-correct on the specific offending field. `E_INVALID_TOOL_ARGS`\n // carries the joi `ValidationException` on `cause`, whose message is the\n // joined field-level error text (e.g. `\"text\" is required`).\n let detailMsg = isError(err) ? err.message : String(err)\n if (\n isError(err) &&\n isError(err.cause) &&\n err.cause.message &&\n err.cause.message !== err.message\n ) {\n detailMsg = `${detailMsg} ${err.cause.message}`\n }\n results = new Tokenizable(detailMsg)\n }\n helpers.reportToolCall(call.id, {\n results,\n isError: toolHadError,\n isComplete: true,\n })\n const checksum = computeChecksum(tool.name, args)\n const completedAt2 = nowIso()\n await ctx.storeToolCall(\n new ToolCall({\n id: call.id,\n tool: tool.name,\n args,\n checksum,\n isComplete: true,\n isError: toolHadError,\n results,\n fromArtifactTool: isArtifactTool,\n createdAt: completedAt2,\n updatedAt: completedAt2,\n completedAt: completedAt2,\n })\n )\n }\n\n const selfIdentity = merged.selfIdentity ?? 'assistant'\n\n // ── Step 8: streaming path ────────────────────────────────────────────\n if (stream) {\n if (!response.body) {\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR(['response has no body']))\n return\n }\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n const accumulator = resolvedHelpers.createChatCompletionsToolCallDeltaAccumulator()\n const streamId = uuidv6()\n\n let buffer = ''\n let idleTimer: ReturnType<typeof setTimeout> | undefined\n let stalled = false\n let partialMessageContent = ''\n let partialThoughtContent = ''\n let sawMessageDelta = false\n let sawThoughtDelta = false\n let doneSentinelSeen = false\n\n const idleMs = merged.streamIdleTimeoutMs ?? 0\n const armIdleTimer = (): void => {\n if (idleMs <= 0) return\n if (idleTimer) clearTimeout(idleTimer)\n idleTimer = setTimeout(() => {\n stalled = true\n helpers.log.warn({\n kind: 'stream-idle-timeout',\n message: `SSE stream went idle for ${idleMs}ms; cancelling`,\n payload: { idleMs },\n })\n reader.cancel().catch(() => {\n /* swallow */\n })\n }, idleMs)\n }\n const clearIdleTimer = (): void => {\n if (idleTimer) {\n clearTimeout(idleTimer)\n idleTimer = undefined\n }\n }\n\n const drainAndPersist = async (): Promise<void> => {\n if (sawMessageDelta) {\n helpers.reportMessage(streamId, '', { isComplete: true })\n await ctx.storeMessage(\n new Message({\n id: streamId,\n role: 'assistant',\n content: partialMessageContent,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n if (sawThoughtDelta) {\n helpers.reportThought(streamId, '', { isComplete: true })\n await ctx.storeThought(\n new Thought({\n id: streamId,\n content: partialThoughtContent,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n const calls = accumulator.drain()\n helpers.log.debug({\n kind: 'accumulator-finalised',\n message: `Stream finalised: ${calls.length} tool call(s), message=${sawMessageDelta}, thought=${sawThoughtDelta}`,\n payload: {\n toolCallCount: calls.length,\n sawMessageDelta,\n sawThoughtDelta,\n doneSentinelSeen,\n },\n })\n if (calls.length === 0) {\n // No tool calls — terminal text answer. Only self-ack when opted in;\n // otherwise leave the context unsignalled so the implementor's output\n // pipeline owns turn completion (autoAck defaults to false).\n if (merged.autoAck) ctx.ack()\n return\n }\n for (const call of calls) {\n if (ctx.abortSignal.aborted) return\n await executeAndPersistToolCall(call)\n }\n // Tool calls produced — do NOT ack; the runner will iterate again.\n }\n\n try {\n armIdleTimer()\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n armIdleTimer()\n if (ctx.abortSignal.aborted) {\n clearIdleTimer()\n return\n }\n buffer += decoder.decode(value, { stream: true })\n let sepIdx = buffer.indexOf('\\n\\n')\n while (sepIdx !== -1) {\n const frame = buffer.slice(0, sepIdx)\n buffer = buffer.slice(sepIdx + 2)\n for (const line of frame.split('\\n')) {\n if (!line.startsWith('data:')) continue\n const data = line.slice(5).trim()\n if (data.length === 0) continue\n if (data === '[DONE]') {\n doneSentinelSeen = true\n clearIdleTimer()\n await drainAndPersist()\n return\n }\n let chunk: ChatCompletionsChunk\n try {\n chunk = JSON.parse(data) as ChatCompletionsChunk\n } catch {\n helpers.log.trace({\n kind: 'sse-parse-failure',\n message: 'Failed to parse SSE chunk as JSON; skipping',\n payload: { dataPreview: data.slice(0, 256) },\n })\n continue\n }\n const delta = chunk.choices?.[0]?.delta\n if (!delta) continue\n if (typeof delta.content === 'string' && delta.content.length > 0) {\n sawMessageDelta = true\n partialMessageContent += delta.content\n helpers.reportMessage(streamId, delta.content)\n }\n const reasoning = (delta as { reasoning_content?: string | null }).reasoning_content\n if (typeof reasoning === 'string' && reasoning.length > 0) {\n sawThoughtDelta = true\n partialThoughtContent += reasoning\n helpers.reportThought(streamId, reasoning)\n }\n if (Array.isArray(delta.tool_calls)) {\n for (const d of delta.tool_calls) {\n accumulator.feed(d)\n }\n }\n // finish_reason emitted before [DONE] — no special action required; the [DONE]\n // sentinel is the canonical terminator.\n }\n sepIdx = buffer.indexOf('\\n\\n')\n }\n }\n clearIdleTimer()\n if (stalled) {\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED([idleMs]))\n return\n }\n if (!doneSentinelSeen) {\n // EOF without [DONE] — still drain and persist whatever we have.\n helpers.log.warn({\n kind: 'sse-eof-without-done',\n message: 'SSE stream ended without [DONE] sentinel; draining accumulated state',\n })\n await drainAndPersist()\n }\n } catch (err) {\n clearIdleTimer()\n if (ctx.abortSignal.aborted) return\n if (stalled) {\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED([idleMs]))\n return\n }\n helpers.log.error({\n kind: 'stream-error',\n message: `SSE stream failed: ${isError(err) ? err.message : String(err)}`,\n payload: { detail: isError(err) ? err.message : String(err) },\n })\n ctx.nack(\n new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR([isError(err) ? err.message : String(err)])\n )\n return\n }\n return\n }\n\n // ── Step 9: non-streaming path ────────────────────────────────────────\n let parsed: ChatCompletionsResponse\n try {\n parsed = (await response.json()) as ChatCompletionsResponse\n } catch (err) {\n ctx.nack(\n new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR([isError(err) ? err.message : String(err)])\n )\n return\n }\n const choice = parsed.choices?.[0]\n if (!choice) {\n // Empty response, no tool calls — terminal. Self-ack only when opted in.\n if (merged.autoAck) ctx.ack()\n return\n }\n const msg = choice.message\n const responseId = parsed.id ?? uuidv6()\n\n if (msg && typeof msg.content === 'string' && msg.content.length > 0) {\n const messageId = `${responseId}:message`\n helpers.reportMessage(messageId, msg.content, { isComplete: true })\n await ctx.storeMessage(\n new Message({\n id: messageId,\n role: 'assistant',\n content: msg.content,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n const reasoning = (msg as { reasoning_content?: string | null } | undefined)\n ?.reasoning_content\n if (typeof reasoning === 'string' && reasoning.length > 0) {\n const thoughtId = `${responseId}:thought`\n helpers.reportThought(thoughtId, reasoning, { isComplete: true })\n await ctx.storeThought(\n new Thought({\n id: thoughtId,\n content: reasoning,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n\n const rawCalls = msg?.tool_calls ?? []\n if (rawCalls.length === 0) {\n // No tool calls — terminal text answer. Self-ack only when opted in;\n // otherwise the implementor's output pipeline owns completion.\n if (merged.autoAck) ctx.ack()\n return\n }\n const calls: AssembledToolCall[] = rawCalls.map((tc) => ({\n id: tc.id,\n type: tc.type ?? 'function',\n name: tc.function?.name ?? '',\n args: tc.function?.arguments ?? '',\n }))\n for (const call of calls) {\n if (ctx.abortSignal.aborted) return\n await executeAndPersistToolCall(call)\n }\n // Tool calls produced — do NOT ack; the runner will iterate again.\n }\n }\n\n /**\n * Returns `true` when `value` is an {@link OpenAIChatCompletionsAdapter} instance.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is an `OpenAIChatCompletionsAdapter` instance.\n */\n public static isOpenAIChatCompletionsAdapter(\n value: unknown\n ): value is OpenAIChatCompletionsAdapter {\n return isInstanceOf(value, 'OpenAIChatCompletionsAdapter', OpenAIChatCompletionsAdapter)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"adapter.cjs","names":["#baseline"],"sources":["../../../../src/batteries/llm/openai_chat_completions/adapter.ts"],"sourcesContent":["/**\n * Cross-environment executor adapter for OpenAI Chat Completions compatible endpoints.\n *\n * @module @nhtio/adk/batteries/llm/openai_chat_completions/adapter\n *\n * @remarks\n * Cross-environment LLM adapter for the OpenAI Chat Completions wire shape. Chat Completions was\n * chosen as the ADK's reference adapter because it is the de-facto interchange format for the\n * majority of OpenAI-compatible gateways (vLLM, Together, Groq, Fireworks, Ollama, Azure OpenAI,\n * OpenRouter, DeepSeek, Mistral La Plateforme, and most self-hosted deployments). Its tool-call\n * synthetic-history shape (`role: 'assistant', tool_calls: [...]` followed by `role: 'tool'` with\n * `tool_call_id`) is the lowest-common-denominator that every conformant gateway accepts.\n *\n * The adapter is built around three pluggable layers:\n *\n * 1. **Translation helpers** — the thirteen swappable functions exported from `./helpers` turn\n * ADK primitives ({@link @nhtio/adk!Tokenizable}, {@link @nhtio/adk!Memory}, {@link @nhtio/adk!Message}, {@link @nhtio/adk!Thought},\n * {@link @nhtio/adk!ToolCall}, {@link @nhtio/adk!Tool}, {@link @nhtio/adk!ArtifactTool}, {@link @nhtio/adk!SpooledArtifact}) into Chat\n * Completions wire shapes. Consumers override individual helpers via `options.helpers.*` to\n * customise envelope formats, bucket ordering, thought surfacing, or JSON Schema generation\n * without forking the adapter.\n * 2. **Three-layer options merging** — constructor baseline, per-`executor()` overrides, and\n * per-iteration `ctx.stash.openaiChatCompletions` overrides combine with key-by-key\n * precedence for `headers`/`helpers`/`retry` and wholesale replacement for everything else.\n * The merged shape is re-validated on every iteration so a malformed stash override\n * fails loud, not silently.\n * 3. **Cross-env transport** — uses `globalThis.fetch`, `TextDecoder`, `AbortController`, and\n * `AbortSignal.any` only. No Node-specific primitives; works unchanged in browser, edge, and\n * server runtimes. SSE framing is hand-parsed against `\\n\\n` separators so the streaming path\n * has no dependency on a particular SSE library.\n *\n * Per-iteration flow (steps 1–9 of the plan):\n * 1. Merge constructor / executor / stash options and re-validate.\n * 2. Resolve helpers, falling back to bundled `default*` for each unset field.\n * 3. Forge artifact-query tools by walking `ctx.turnToolCalls`, collecting unique\n * `SpooledArtifact` constructors, calling `<Ctor>.forgeTools(ctx)` on each, and merging the\n * results with `ctx.tools`.\n * 4. Pre-render every persisted tool-call result into the prompt-ready string the timeline will\n * use, cached by `tc.id`.\n * 5. When `tokenEncoding !== null`, sum the token weight of every persisted bucket and throw\n * {@link @nhtio/adk/batteries!E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW} when the total exceeds `contextWindow`.\n * 6. Build the request body via `buildChatCompletionsHistory`; carry vendor-opaque reasoning\n * blocks through the `_adk_reasoning_payloads` side-channel.\n * 7. POST with a linked `AbortSignal`, retry loop respecting `retry.*`, and a request-timeout\n * watchdog that arms before fetch and clears once response headers arrive.\n * 8. Streaming path: SSE parse via `response.body.getReader()` + `TextDecoder`; surface deltas\n * through `helpers.reportMessage` / `reportThought` / `reportToolCall`; assemble tool-call\n * deltas via the accumulator; persist `Message` / `Thought` / `ToolCall` records on `[DONE]`.\n * 9. Non-streaming path: single `response.json()`; same persistence + tool-execution loop.\n */\n\nimport { DateTime } from 'luxon'\nimport { sha256 } from 'js-sha256'\nimport { v6 as uuidv6 } from 'uuid'\nimport { validateOptions } from './validation'\nimport { isError, isInstanceOf, isObject } from '@nhtio/adk/guards'\nimport { canonicalStringify } from '../../../lib/utils/canonical_json'\nimport { InMemorySpoolStore } from '@nhtio/adk/batteries/storage/in_memory'\nimport {\n computeBackoff,\n sleepWithJitter,\n parseRetryAfter,\n linkAbortSignals,\n} from '../../../lib/utils/retry'\nimport {\n Tokenizable,\n ToolRegistry,\n ToolCall,\n Message,\n Thought,\n SpooledArtifact,\n Media,\n ArtifactTool,\n} from '@nhtio/adk/common'\nimport {\n E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS,\n E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW,\n E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR,\n E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR,\n E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED,\n E_OPENAI_CHAT_COMPLETIONS_REQUEST_TIMEOUT,\n E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS,\n} from './exceptions'\nimport {\n defaultDescriptionToChatCompletionsJsonSchema,\n defaultRenderUntrustedContent,\n defaultRenderTrustedContent,\n defaultRenderStandingInstructions,\n defaultRenderMemories,\n defaultRenderRetrievables,\n defaultRenderRetrievableSafetyDirective,\n defaultRenderFirstPartyRetrievables,\n defaultRenderThirdPartyPublicRetrievables,\n defaultRenderThirdPartyPrivateRetrievables,\n defaultRenderTimelineMessage,\n defaultRenderThought,\n defaultFilterThoughts,\n defaultToolsToChatCompletionsTools,\n defaultRenderChatCompletionsSystemPrompt,\n defaultRenderChatCompletionsToolCallResult,\n defaultBuildChatCompletionsHistory,\n defaultCreateChatCompletionsToolCallDeltaAccumulator,\n extractReasoningFields,\n} from './helpers'\nimport type { DispatchContext } from '@nhtio/adk/types'\nimport type { Tool, Memory, TokenEncoding } from '@nhtio/adk/common'\nimport type { DispatchExecutorFn, DispatchExecutorHelpers } from '@nhtio/adk/dispatch_runner'\nimport type {\n OpenAIChatCompletionsAdapterOptions,\n ChatCompletionsHelpers,\n ChatCompletionsRetryConfig,\n OpenAIChatCompletionsRequestBody,\n ChatCompletionsChunk,\n ChatCompletionsResponse,\n AssembledToolCall,\n ChatCompletionsContentBlock,\n ReasoningField,\n} from './types'\n\n// ─── ADK-control keys (stripped before sending the request body) ──────────\n\nconst ADK_CONTROL_KEYS: ReadonlySet<string> = new Set([\n 'apiKey',\n 'baseURL',\n 'headers',\n 'fetch',\n 'stream',\n 'bucketOrder',\n 'contextWindow',\n 'selfIdentity',\n 'thoughtSurfacing',\n 'tokenEncoding',\n 'replayCompatibility',\n 'reasoningFieldPrecedence',\n 'helpers',\n 'streamIdleTimeoutMs',\n 'requestTimeoutMs',\n 'retry',\n 'strictToolChoice',\n 'autoAck',\n 'unsupportedMediaPolicy',\n])\n\n// ─── Option merging ───────────────────────────────────────────────────────────\n\nconst mergeRetry = (\n layers: ReadonlyArray<ChatCompletionsRetryConfig | undefined>\n): ChatCompletionsRetryConfig | undefined => {\n let merged: ChatCompletionsRetryConfig | undefined\n for (const layer of layers) {\n if (!layer) continue\n merged = { ...(merged ?? {}), ...layer }\n }\n return merged\n}\n\nconst mergeHeaders = (\n layers: ReadonlyArray<Record<string, string> | undefined>\n): Record<string, string> | undefined => {\n let merged: Record<string, string> | undefined\n for (const layer of layers) {\n if (!layer) continue\n merged = { ...(merged ?? {}), ...layer }\n }\n return merged\n}\n\nconst mergeHelpers = (\n layers: ReadonlyArray<Partial<ChatCompletionsHelpers> | undefined>\n): Partial<ChatCompletionsHelpers> | undefined => {\n let merged: Partial<ChatCompletionsHelpers> | undefined\n for (const layer of layers) {\n if (!layer) continue\n merged = { ...(merged ?? {}), ...layer }\n }\n return merged\n}\n\nconst mergeOptions = (\n baseline: OpenAIChatCompletionsAdapterOptions,\n exec: Partial<OpenAIChatCompletionsAdapterOptions> | undefined,\n stash: Partial<OpenAIChatCompletionsAdapterOptions> | undefined\n): Partial<OpenAIChatCompletionsAdapterOptions> => {\n const layers = [baseline as Partial<OpenAIChatCompletionsAdapterOptions>, exec ?? {}, stash ?? {}]\n const out: Record<string, unknown> = {}\n for (const layer of layers) {\n for (const [k, v] of Object.entries(layer)) {\n if (v === undefined) continue\n if (k === 'headers' || k === 'helpers' || k === 'retry') continue\n out[k] = v\n }\n }\n const headers = mergeHeaders(layers.map((l) => l.headers))\n if (headers !== undefined) out.headers = headers\n const helpers = mergeHelpers(layers.map((l) => l.helpers))\n if (helpers !== undefined) out.helpers = helpers\n const retry = mergeRetry(layers.map((l) => l.retry))\n if (retry !== undefined) out.retry = retry\n return out as Partial<OpenAIChatCompletionsAdapterOptions>\n}\n\n// ─── Helper resolution ────────────────────────────────────────────────────────\n\nconst resolveHelpers = (\n overrides: Partial<ChatCompletionsHelpers> | undefined\n): ChatCompletionsHelpers => {\n const src = overrides ?? {}\n return {\n descriptionToChatCompletionsJsonSchema:\n src.descriptionToChatCompletionsJsonSchema ?? defaultDescriptionToChatCompletionsJsonSchema,\n renderUntrustedContent: src.renderUntrustedContent ?? defaultRenderUntrustedContent,\n renderTrustedContent: src.renderTrustedContent ?? defaultRenderTrustedContent,\n renderStandingInstructions: src.renderStandingInstructions ?? defaultRenderStandingInstructions,\n renderMemories: src.renderMemories ?? defaultRenderMemories,\n renderRetrievables: src.renderRetrievables ?? defaultRenderRetrievables,\n renderRetrievableSafetyDirective:\n src.renderRetrievableSafetyDirective ?? defaultRenderRetrievableSafetyDirective,\n renderFirstPartyRetrievables:\n src.renderFirstPartyRetrievables ?? defaultRenderFirstPartyRetrievables,\n renderThirdPartyPublicRetrievables:\n src.renderThirdPartyPublicRetrievables ?? defaultRenderThirdPartyPublicRetrievables,\n renderThirdPartyPrivateRetrievables:\n src.renderThirdPartyPrivateRetrievables ?? defaultRenderThirdPartyPrivateRetrievables,\n renderTimelineMessage: src.renderTimelineMessage ?? defaultRenderTimelineMessage,\n renderThought: src.renderThought ?? defaultRenderThought,\n filterThoughts: src.filterThoughts ?? defaultFilterThoughts,\n toolsToChatCompletionsTools:\n src.toolsToChatCompletionsTools ?? defaultToolsToChatCompletionsTools,\n renderChatCompletionsSystemPrompt:\n src.renderChatCompletionsSystemPrompt ?? defaultRenderChatCompletionsSystemPrompt,\n renderChatCompletionsToolCallResult:\n src.renderChatCompletionsToolCallResult ?? defaultRenderChatCompletionsToolCallResult,\n buildChatCompletionsHistory:\n src.buildChatCompletionsHistory ?? defaultBuildChatCompletionsHistory,\n createChatCompletionsToolCallDeltaAccumulator:\n src.createChatCompletionsToolCallDeltaAccumulator ??\n defaultCreateChatCompletionsToolCallDeltaAccumulator,\n }\n}\n\n// ─── Retry / timeout helpers ──────────────────────────────────────────────────\n// Shared with the OpenAI Embeddings battery — see `../../../lib/utils/retry`. These are pure,\n// environment-neutral primitives; the import keeps retry behavior identical across batteries\n// without duplication.\n\n// ─── ID helpers ───────────────────────────────────────────────────────────────\n\n// Canonical (key-order-insensitive) checksum — MUST match the contract documented\n// on canonicalStringify and used by Tool.executor + dispatch_runner's streaming\n// helper, so ctx.toolCallCount(checksum) detects semantically-identical repeat\n// calls regardless of argument key order.\nconst computeChecksum = (tool: string, args: unknown): string =>\n sha256(canonicalStringify({ tool, args }))\n\nconst nowIso = (): string => DateTime.now().toISO() ?? new Date().toISOString()\n\n// ─── Token measurement ────────────────────────────────────────────────────────\n\nconst estimateTokensOf = async (\n value: { estimateTokens: (encoding: TokenEncoding) => number | Promise<number> },\n encoding: TokenEncoding\n): Promise<number> => {\n return Promise.resolve(value.estimateTokens(encoding))\n}\n\n// ─── Adapter class ────────────────────────────────────────────────────────────\n\n/**\n * Opinionated cross-environment LLM adapter for the OpenAI Chat Completions wire shape.\n *\n * @remarks\n * Construction validates options eagerly via {@link @nhtio/adk/batteries!validateOptions} and throws\n * {@link @nhtio/adk/batteries!E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS} on failure — config bugs fail loud, not at\n * dispatch time. The returned instance is reusable: call {@link OpenAIChatCompletionsAdapter.executor}\n * once per `DispatchRunner` configuration to obtain an {@link @nhtio/adk!DispatchExecutorFn} bound to the\n * baseline plus optional executor-scope overrides.\n *\n * Per-iteration overrides live on the active {@link @nhtio/adk!DispatchContext}'s\n * `stash.openaiChatCompletions` slot and take highest precedence — they merge into the\n * executor-scope shape on every iteration. `headers`, `helpers`, and `retry` merge key-by-key\n * across all three layers; every other field is replaced wholesale at the highest layer that\n * sets it.\n */\nexport class OpenAIChatCompletionsAdapter {\n /**\n * Customary key for per-iteration overrides on `ctx.stash`. The adapter reads\n * `ctx.stash.get(OpenAIChatCompletionsAdapter.STASH_KEY, {})` at the start of every\n * iteration and merges the value into the resolved options shape.\n */\n public static readonly STASH_KEY = 'openaiChatCompletions' as const\n\n readonly #baseline: OpenAIChatCompletionsAdapterOptions\n\n /**\n * @param options - Constructor-baseline options. Re-validated on every iteration after\n * per-dispatch and per-iteration overrides are layered in.\n * @throws {@link @nhtio/adk/batteries!E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS} when `options` does not satisfy\n * {@link @nhtio/adk/batteries!openAIChatCompletionsOptionsSchema}.\n */\n constructor(options: unknown) {\n this.#baseline = validateOptions(options)\n }\n\n /**\n * Returns an {@link @nhtio/adk!DispatchExecutorFn} bound to this adapter's baseline plus optional\n * executor-scope overrides. The returned function is reusable across iterations — every\n * iteration re-merges with `ctx.stash[STASH_KEY]` and re-validates the result.\n *\n * @param overrides - Optional executor-scope overrides. Higher precedence than the baseline,\n * lower precedence than `ctx.stash[STASH_KEY]`.\n * @returns An {@link @nhtio/adk!DispatchExecutorFn} suitable for `DispatchRunner`.\n */\n executor(overrides?: Partial<OpenAIChatCompletionsAdapterOptions>): DispatchExecutorFn {\n const baseline = this.#baseline\n const adapterClass = OpenAIChatCompletionsAdapter\n return async (ctx: DispatchContext, helpers: DispatchExecutorHelpers): Promise<void> => {\n // Bridge helpers.log → the legacy `warn: (msg) => void` slot exposed by the per-call\n // helper signatures. Helpers downstream don't need to know the structured shape — they\n // emit a single string, which we route to `helpers.log.warn` with a stable `kind` so\n // observability middleware can still filter and aggregate.\n const localWarn = (msg: string): void => {\n helpers.log.warn({ kind: 'helper-warning', message: msg })\n }\n\n // ── Step 1: merge & validate ──────────────────────────────────────────\n const stashRaw = ctx.stash.get(adapterClass.STASH_KEY, {}) as unknown\n const stashOverrides =\n stashRaw && typeof stashRaw === 'object'\n ? (stashRaw as Partial<OpenAIChatCompletionsAdapterOptions>)\n : {}\n const mergedRaw = mergeOptions(baseline, overrides, stashOverrides)\n const merged = validateOptions(mergedRaw)\n\n // Cross-field invariant: tokenEncoding non-null requires contextWindow.\n if (merged.tokenEncoding !== null && merged.contextWindow === undefined) {\n throw new E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS([\n 'tokenEncoding is non-null but contextWindow is undefined',\n ])\n }\n\n // ── Step 2: resolve helpers ───────────────────────────────────────────\n const resolvedHelpers = resolveHelpers(merged.helpers)\n\n // ── Step 3: forge artifact-query tools ────────────────────────────────\n const uniqueCtors = new Set<typeof SpooledArtifact>()\n for (const tc of ctx.turnToolCalls) {\n const results = tc.results as unknown as { constructor?: unknown }\n const ctor = results?.constructor\n if (ctor && SpooledArtifact.isSpooledArtifactConstructor(ctor)) {\n uniqueCtors.add(ctor as unknown as typeof SpooledArtifact)\n }\n }\n const forgedRegistries: ToolRegistry[] = []\n for (const ctor of uniqueCtors) {\n const forgeFn = (\n ctor as unknown as {\n forgeTools?: (c: DispatchContext) => ToolRegistry\n }\n ).forgeTools\n if (typeof forgeFn === 'function') {\n forgedRegistries.push(forgeFn.call(ctor, ctx))\n }\n }\n const mergedRegistry = ToolRegistry.merge([ctx.tools, ...forgedRegistries], {\n onCollision: 'replace',\n })\n mergedRegistry.bindContext(ctx)\n\n // ── Step 4: pre-render tool-call results ──────────────────────────────\n const renderedToolCallResults = new Map<string, string | ChatCompletionsContentBlock[]>()\n for (const tc of ctx.turnToolCalls) {\n const rendered = await resolvedHelpers.renderChatCompletionsToolCallResult({\n toolCall: tc,\n results: tc.results as\n | Tokenizable\n | SpooledArtifact\n | SpooledArtifact[]\n | Media\n | Media[],\n tool: mergedRegistry.get(tc.tool) as Tool | ArtifactTool | undefined,\n renderUntrustedContent: resolvedHelpers.renderUntrustedContent,\n renderTrustedContent: resolvedHelpers.renderTrustedContent,\n unsupportedMediaPolicy: merged.unsupportedMediaPolicy ?? 'throw',\n warn: localWarn,\n })\n renderedToolCallResults.set(tc.id, rendered)\n }\n\n // ── Step 5: context window enforcement ────────────────────────────────\n if (merged.tokenEncoding !== null && merged.contextWindow !== undefined) {\n const encoding = merged.tokenEncoding as TokenEncoding\n let spTokens = await estimateTokensOf(ctx.systemPrompt, encoding)\n let siTokens = 0\n for (const si of ctx.standingInstructions) {\n siTokens += await estimateTokensOf(si, encoding)\n }\n let memTokens = 0\n for (const mem of ctx.turnMemories as Set<Memory>) {\n memTokens += await estimateTokensOf(mem.content, encoding)\n }\n let retTokens = 0\n for (const r of ctx.turnRetrievables) {\n retTokens += await estimateTokensOf(r.content, encoding)\n }\n let tlTokens = 0\n for (const msg of ctx.turnMessages) {\n if (msg.content !== undefined) {\n tlTokens += await estimateTokensOf(msg.content, encoding)\n }\n }\n for (const th of ctx.turnThoughts) {\n tlTokens += await estimateTokensOf(th.content, encoding)\n }\n for (const rendered of renderedToolCallResults.values()) {\n const textPart =\n typeof rendered === 'string'\n ? rendered\n : rendered\n .filter((b): b is { type: 'text'; text: string } => b.type === 'text')\n .map((b) => b.text)\n .join('\\n')\n const tk = new Tokenizable(textPart)\n tlTokens += await estimateTokensOf(tk, encoding)\n }\n const total = spTokens + siTokens + memTokens + retTokens + tlTokens\n const perBucketObj = {\n systemPrompt: spTokens,\n standingInstructions: siTokens,\n memories: memTokens,\n retrievables: retTokens,\n timeline: tlTokens,\n }\n helpers.log.debug({\n kind: 'context-window-usage',\n message: `Context window usage: ${total}/${merged.contextWindow} tokens`,\n payload: {\n total,\n limit: merged.contextWindow,\n encoding,\n perBucket: perBucketObj,\n },\n })\n if (total > merged.contextWindow) {\n const perBucket = JSON.stringify(perBucketObj)\n throw new E_OPENAI_CHAT_COMPLETIONS_CONTEXT_OVERFLOW([\n total,\n merged.contextWindow,\n encoding,\n perBucket,\n ])\n }\n }\n\n // ── Step 5b: tool_choice + forged artifact-tools guard ────────────────\n // When `tool_choice` (or the `allowed_tools` variant) forces the model onto a specific\n // tool name and that name resolves to an ephemeral, forged artifact-query tool, surface\n // it as a structured warning (or throw under `strictToolChoice: true`). Forging an\n // artifact-query tool by name is almost always a misconfiguration — the tool may not\n // exist in the next iteration once the artifact ages out of `ctx.turnToolCalls`.\n const forcedToolNames: string[] = []\n const toolChoice = merged.tool_choice\n let toolChoiceVariant: 'function' | 'allowed_tools' = 'function'\n if (toolChoice && typeof toolChoice === 'object') {\n if ('function' in toolChoice && toolChoice.type === 'function') {\n forcedToolNames.push(toolChoice.function.name)\n } else if ('custom' in toolChoice && toolChoice.type === 'custom') {\n forcedToolNames.push(toolChoice.custom.name)\n } else if (toolChoice.type === 'allowed_tools') {\n toolChoiceVariant = 'allowed_tools'\n for (const entry of toolChoice.allowed_tools.tools) {\n if ('function' in entry) forcedToolNames.push(entry.function.name)\n else if ('custom' in entry) forcedToolNames.push(entry.custom.name)\n }\n }\n }\n const forcedForgedHits: Array<{ toolName: string }> = []\n for (const name of forcedToolNames) {\n const t = mergedRegistry.get(name) as { ephemeral?: boolean } | undefined\n if (t?.ephemeral === true) {\n forcedForgedHits.push({ toolName: name })\n }\n }\n if (forcedForgedHits.length > 0) {\n if (merged.strictToolChoice === true) {\n throw new E_INVALID_OPENAI_CHAT_COMPLETIONS_OPTIONS([\n `tool_choice forces forged ephemeral artifact-query tool(s): ${forcedForgedHits\n .map((h) => h.toolName)\n .join(\n ', '\n )} — these may not exist on the next iteration. Remove the override or unset strictToolChoice.`,\n ])\n }\n helpers.log.warn({\n kind: 'tool-choice-forged-artifact',\n message: `tool_choice forces ${forcedForgedHits.length} forged ephemeral artifact-query tool(s); this is almost always a misconfiguration`,\n payload: {\n toolNames: forcedForgedHits.map((h) => h.toolName),\n variant: toolChoiceVariant,\n },\n })\n }\n\n // ── Step 6: build request body ────────────────────────────────────────\n const { messages: wireMessages, reasoningPayloads } =\n await resolvedHelpers.buildChatCompletionsHistory({\n systemPrompt: ctx.systemPrompt,\n standingInstructions: ctx.standingInstructions,\n memories: ctx.turnMemories,\n retrievables: ctx.turnRetrievables,\n messages: ctx.turnMessages,\n thoughts: ctx.turnThoughts,\n toolCalls: ctx.turnToolCalls,\n tools: mergedRegistry,\n renderedToolCallResults,\n bucketOrder: merged.bucketOrder ?? [\n 'standingInstructions',\n 'memories',\n 'retrievables',\n 'timeline',\n ],\n selfIdentity: merged.selfIdentity ?? 'assistant',\n thoughtSurfacing: merged.thoughtSurfacing ?? 'all-self',\n replayCompatibility: merged.replayCompatibility ?? [],\n renderChatCompletionsToolCallResult: resolvedHelpers.renderChatCompletionsToolCallResult,\n renderChatCompletionsSystemPrompt: resolvedHelpers.renderChatCompletionsSystemPrompt,\n renderStandingInstructions: resolvedHelpers.renderStandingInstructions,\n renderMemories: resolvedHelpers.renderMemories,\n renderRetrievables: resolvedHelpers.renderRetrievables,\n renderRetrievableSafetyDirective: resolvedHelpers.renderRetrievableSafetyDirective,\n renderFirstPartyRetrievables: resolvedHelpers.renderFirstPartyRetrievables,\n renderThirdPartyPublicRetrievables: resolvedHelpers.renderThirdPartyPublicRetrievables,\n renderThirdPartyPrivateRetrievables: resolvedHelpers.renderThirdPartyPrivateRetrievables,\n renderTimelineMessage: resolvedHelpers.renderTimelineMessage,\n renderThought: resolvedHelpers.renderThought,\n filterThoughts: resolvedHelpers.filterThoughts,\n renderUntrustedContent: resolvedHelpers.renderUntrustedContent,\n renderTrustedContent: resolvedHelpers.renderTrustedContent,\n unsupportedMediaPolicy: merged.unsupportedMediaPolicy ?? 'throw',\n warn: localWarn,\n })\n\n const stream = merged.stream ?? true\n const body: OpenAIChatCompletionsRequestBody = {\n model: merged.model,\n messages: wireMessages,\n stream,\n }\n for (const [k, v] of Object.entries(merged)) {\n if (ADK_CONTROL_KEYS.has(k)) continue\n if (k === 'model' || k === 'messages' || k === 'stream') continue\n if (v === undefined) continue\n ;(body as Record<string, unknown>)[k] = v\n }\n const toolsArr = mergedRegistry.all()\n if (toolsArr.length > 0) {\n body.tools = resolvedHelpers.toolsToChatCompletionsTools(toolsArr, {\n descriptionToChatCompletionsJsonSchema:\n resolvedHelpers.descriptionToChatCompletionsJsonSchema,\n })\n }\n if (reasoningPayloads.length > 0) {\n body._adk_reasoning_payloads = reasoningPayloads\n }\n\n // ── Step 7: POST with retry / timeout loop ────────────────────────────\n const rawBase = merged.baseURL ?? 'https://api.openai.com/v1'\n const baseURL = rawBase.endsWith('/') ? rawBase.slice(0, -1) : rawBase\n const url = `${baseURL}/chat/completions`\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (stream) headers['Accept'] = 'text/event-stream'\n if (merged.apiKey) {\n headers['Authorization'] = `Bearer ${merged.apiKey}`\n }\n // User-supplied headers override built defaults (including Authorization).\n if (merged.headers) {\n Object.assign(headers, merged.headers)\n }\n\n const retryCfg: ChatCompletionsRetryConfig = {\n maxAttempts: merged.retry?.maxAttempts ?? 1,\n baseDelayMs: merged.retry?.baseDelayMs ?? 500,\n maxDelayMs: merged.retry?.maxDelayMs ?? 30_000,\n retriableStatuses: merged.retry?.retriableStatuses ?? [429, 502, 503, 504],\n honorRetryAfter: merged.retry?.honorRetryAfter ?? true,\n }\n\n const fetchFn = merged.fetch ?? globalThis.fetch\n const maxAttempts = retryCfg.maxAttempts ?? 1\n\n let response: Response | undefined\n let attempt = 1\n // The abort-signal link attaches listeners to the long-lived ctx.abortSignal.\n // Each retry attempt re-links, so dispose the PRIOR attempt's link before\n // making a new one — otherwise N retries leave N stale listeners on\n // ctx.abortSignal for the rest of the turn. The final (successful) attempt's\n // link is bounded by the turn's lifetime, same as the native AbortSignal.any.\n let disposeLink: () => void = () => {}\n while (attempt <= maxAttempts) {\n if (ctx.abortSignal.aborted) return\n\n const internalController = new AbortController()\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined\n const requestTimeoutMs = merged.requestTimeoutMs ?? 0\n if (requestTimeoutMs > 0) {\n timeoutHandle = setTimeout(() => internalController.abort(), requestTimeoutMs)\n }\n disposeLink()\n const { signal: linkedSignal, dispose: disposeCurrentLink } = linkAbortSignals([\n ctx.abortSignal,\n internalController.signal,\n ])\n disposeLink = disposeCurrentLink\n\n try {\n response = await fetchFn(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: linkedSignal,\n })\n } catch (err) {\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n if (ctx.abortSignal.aborted) return\n if (internalController.signal.aborted) {\n // Request timed out before headers arrived — eligible for retry.\n helpers.log.warn({\n kind: 'request-timeout',\n message: `Request timed out after ${requestTimeoutMs}ms on attempt ${attempt}/${maxAttempts}`,\n payload: { requestTimeoutMs, attempt, maxAttempts },\n })\n if (attempt < maxAttempts) {\n const delay = computeBackoff(attempt, retryCfg)\n helpers.log.debug({\n kind: 'retry-attempt',\n message: `Retrying after request timeout in ~${delay}ms (attempt ${attempt + 1}/${maxAttempts})`,\n payload: {\n reason: 'request-timeout',\n delayMs: delay,\n attempt: attempt + 1,\n maxAttempts,\n },\n })\n await sleepWithJitter(delay, ctx.abortSignal)\n attempt += 1\n continue\n }\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_REQUEST_TIMEOUT([requestTimeoutMs]))\n return\n }\n // Generic transport failure — surface as HTTP_ERROR with status 0.\n helpers.log.error({\n kind: 'transport-error',\n message: `Transport failure on attempt ${attempt}/${maxAttempts}: ${isError(err) ? err.message : String(err)}`,\n payload: {\n attempt,\n maxAttempts,\n detail: isError(err) ? err.message : String(err),\n },\n })\n ctx.nack(\n new E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR([0, isError(err) ? err.message : String(err)])\n )\n return\n }\n\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n\n if (!response.ok) {\n const status = response.status\n const retriable = (retryCfg.retriableStatuses ?? [429, 502, 503, 504]).includes(status)\n if (retriable && attempt < maxAttempts) {\n let delay = computeBackoff(attempt, retryCfg)\n let retryAfterMs: number | undefined\n if (retryCfg.honorRetryAfter !== false) {\n const ra = response.headers.get('Retry-After')\n if (ra) {\n const raMs = parseRetryAfter(ra)\n if (raMs > 0) {\n retryAfterMs = raMs\n delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs ?? 30_000)\n }\n }\n }\n helpers.log.warn({\n kind: 'retry-attempt',\n message: `HTTP ${status} on attempt ${attempt}/${maxAttempts}; retrying in ~${delay}ms`,\n payload: {\n reason: 'http-status',\n status,\n delayMs: delay,\n ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),\n attempt: attempt + 1,\n maxAttempts,\n },\n })\n await sleepWithJitter(delay, ctx.abortSignal)\n attempt += 1\n continue\n }\n const errBody = await response.text().catch(() => '')\n helpers.log.error({\n kind: 'http-error',\n message: `HTTP ${status} (terminal): ${errBody.slice(0, 256)}`,\n payload: {\n status,\n body: errBody,\n attempt,\n maxAttempts,\n retriable,\n },\n })\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_HTTP_ERROR([status, errBody]))\n return\n }\n\n break\n }\n\n if (!response) return\n\n // Spool store used to back string / Uint8Array tool returns. Bytes are written under the\n // call id; the resulting SpooledArtifact (or tool-configured subclass) is the model-visible\n // handle for the rest of the turn. Injectable via the `spoolStore` option so consumers can\n // back artifacts with durable storage (OPFS, Flydrive); defaults to an ephemeral per-dispatch\n // in-memory store. NOTE: an injected durable store persists across turns, so call ids must be\n // globally unique for that store (see the `spoolStore` option docs).\n const spoolStore = merged.spoolStore ?? new InMemorySpoolStore()\n\n // ── Inner helper: persist + execute one assembled tool call ───────────\n const executeAndPersistToolCall = async (call: AssembledToolCall): Promise<void> => {\n const tool = mergedRegistry.get(call.name)\n // Parse args defensively. The model may emit non-JSON or a non-object\n // JSON value (string, number, array, null); both are recoverable error\n // conditions, NOT dispatch-killers. A parse failure short-circuits to\n // a persisted error ToolCall — formatted by `E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS`\n // so consumers can match on the stable error code — letting the model\n // self-correct on the next iteration.\n let args: Record<string, unknown> = {}\n let parseError:\n | InstanceType<typeof E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS>\n | undefined\n if (call.args && call.args.length > 0) {\n try {\n const parsed: unknown = JSON.parse(call.args)\n if (isObject(parsed)) {\n args = parsed\n } else {\n const receivedKind = Array.isArray(parsed)\n ? 'array'\n : parsed === null\n ? 'null'\n : typeof parsed\n parseError = new E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS([\n `must be a JSON object; received ${receivedKind}`,\n call.args,\n ])\n }\n } catch {\n parseError = new E_OPENAI_CHAT_COMPLETIONS_INVALID_TOOL_CALL_ARGS([\n 'are not valid JSON',\n call.args,\n ])\n }\n }\n const completedAt = nowIso()\n if (parseError !== undefined) {\n const results = new Tokenizable(parseError.message)\n helpers.reportToolCall(call.id, { tool: call.name, args })\n helpers.reportToolCall(call.id, {\n results,\n isError: true,\n isComplete: true,\n })\n const checksum = computeChecksum(call.name, args)\n await ctx.storeToolCall(\n new ToolCall({\n id: call.id,\n tool: call.name,\n args,\n checksum,\n isComplete: true,\n isError: true,\n results,\n createdAt: completedAt,\n updatedAt: completedAt,\n completedAt,\n })\n )\n return\n }\n if (!tool) {\n // List the tools that DO exist so the model can self-correct on the next iteration\n // instead of guessing. Without this, a single typo'd / hallucinated tool name yields\n // a dead-end \"not found\" with no path forward.\n const available = mergedRegistry\n .all()\n .map((t) => t.name)\n .sort()\n const errText =\n available.length > 0\n ? `Tool not found: ${call.name}. Available tools: ${available.join(', ')}.`\n : `Tool not found: ${call.name}. No tools are available this turn.`\n const results = new Tokenizable(errText)\n helpers.reportToolCall(call.id, { tool: call.name, args })\n helpers.reportToolCall(call.id, {\n results,\n isError: true,\n isComplete: true,\n })\n const checksum = computeChecksum(call.name, args)\n await ctx.storeToolCall(\n new ToolCall({\n id: call.id,\n tool: call.name,\n args,\n checksum,\n isComplete: true,\n isError: true,\n results,\n createdAt: completedAt,\n updatedAt: completedAt,\n completedAt,\n })\n )\n return\n }\n helpers.reportToolCall(call.id, { tool: tool.name, args })\n const isArtifactTool = ArtifactTool.isArtifactTool(tool)\n let results: Tokenizable | SpooledArtifact | SpooledArtifact[] | Media | Media[] =\n new Tokenizable('')\n let toolHadError = false\n try {\n const raw = await tool.executor(ctx)(args)\n if (isArtifactTool) {\n // ArtifactTool: handler returns a string | Tokenizable that *is* the model-visible\n // answer to a query against a prior artifact. No spool write, no SpooledArtifact\n // construction — pass through (or wrap a bare string in Tokenizable).\n if (Tokenizable.isTokenizable(raw)) {\n results = raw\n } else if (typeof raw === 'string') {\n results = new Tokenizable(raw)\n } else {\n throw new Error(\n `ArtifactTool \"${tool.name}\" returned a non-string/non-Tokenizable value`\n )\n }\n } else if (Media.isMedia(raw)) {\n results = raw\n } else if (Array.isArray(raw) && raw.length > 0 && raw.every((m) => Media.isMedia(m))) {\n results = raw as Media[]\n } else if (typeof raw === 'string' || isInstanceOf(raw, 'Uint8Array', Uint8Array)) {\n const reader = await spoolStore.write(call.id, raw as string | Uint8Array)\n const ArtifactCtor = (tool as Tool).artifactConstructor?.() ?? SpooledArtifact\n results = new ArtifactCtor(reader)\n } else {\n // Defensive fallback — wrap stringified value so the model gets *something*.\n const reader = await spoolStore.write(call.id, String(raw))\n const ArtifactCtor = (tool as Tool).artifactConstructor?.() ?? SpooledArtifact\n results = new ArtifactCtor(reader)\n }\n } catch (err) {\n toolHadError = true\n // Surface field-level validation detail from the cause chain so the\n // model can self-correct on the specific offending field. `E_INVALID_TOOL_ARGS`\n // carries the joi `ValidationException` on `cause`, whose message is the\n // joined field-level error text (e.g. `\"text\" is required`).\n let detailMsg = isError(err) ? err.message : String(err)\n if (\n isError(err) &&\n isError(err.cause) &&\n err.cause.message &&\n err.cause.message !== err.message\n ) {\n detailMsg = `${detailMsg} ${err.cause.message}`\n }\n results = new Tokenizable(detailMsg)\n }\n helpers.reportToolCall(call.id, {\n results,\n isError: toolHadError,\n isComplete: true,\n })\n const checksum = computeChecksum(tool.name, args)\n const completedAt2 = nowIso()\n await ctx.storeToolCall(\n new ToolCall({\n id: call.id,\n tool: tool.name,\n args,\n checksum,\n isComplete: true,\n isError: toolHadError,\n results,\n fromArtifactTool: isArtifactTool,\n createdAt: completedAt2,\n updatedAt: completedAt2,\n completedAt: completedAt2,\n })\n )\n }\n\n const selfIdentity = merged.selfIdentity ?? 'assistant'\n const reasoningPrecedence = merged.reasoningFieldPrecedence ?? [\n 'reasoning',\n 'reasoning_content',\n ]\n\n // ── Step 8: streaming path ────────────────────────────────────────────\n if (stream) {\n if (!response.body) {\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR(['response has no body']))\n return\n }\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n const accumulator = resolvedHelpers.createChatCompletionsToolCallDeltaAccumulator()\n const streamId = uuidv6()\n\n let buffer = ''\n let idleTimer: ReturnType<typeof setTimeout> | undefined\n let stalled = false\n let partialMessageContent = ''\n let sawMessageDelta = false\n let doneSentinelSeen = false\n\n // Reasoning may stream under more than one provider-specific field at once. Accumulate each\n // field's text under its own live stream id; at completion we dedup by content to decide\n // whether to persist one Thought or one per divergent field. The first field to appear keeps\n // the bare `streamId` (the common single-field case); any others get a `:field` suffix.\n const thoughtAccum = new Map<ReasoningField, string>()\n let primaryThoughtField: ReasoningField | undefined\n const thoughtStreamId = (field: ReasoningField): string =>\n field === primaryThoughtField ? streamId : `${streamId}:${field}`\n\n const idleMs = merged.streamIdleTimeoutMs ?? 0\n const armIdleTimer = (): void => {\n if (idleMs <= 0) return\n if (idleTimer) clearTimeout(idleTimer)\n idleTimer = setTimeout(() => {\n stalled = true\n helpers.log.warn({\n kind: 'stream-idle-timeout',\n message: `SSE stream went idle for ${idleMs}ms; cancelling`,\n payload: { idleMs },\n })\n reader.cancel().catch(() => {\n /* swallow */\n })\n }, idleMs)\n }\n const clearIdleTimer = (): void => {\n if (idleTimer) {\n clearTimeout(idleTimer)\n idleTimer = undefined\n }\n }\n\n const drainAndPersist = async (): Promise<void> => {\n if (sawMessageDelta) {\n helpers.reportMessage(streamId, '', { isComplete: true })\n await ctx.storeMessage(\n new Message({\n id: streamId,\n role: 'assistant',\n content: partialMessageContent,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n // Finalize every reasoning stream we opened live, then dedup by content to decide how many\n // Thoughts to persist: one (fields agreed, or only one present) vs one per divergent field.\n for (const field of thoughtAccum.keys()) {\n helpers.reportThought(thoughtStreamId(field), '', { isComplete: true })\n }\n const thoughtExtracts = extractReasoningFields(\n Object.fromEntries(thoughtAccum) as Partial<Record<ReasoningField, string>>,\n reasoningPrecedence\n )\n if (thoughtExtracts.length === 1) {\n await ctx.storeThought(\n new Thought({\n id: streamId,\n content: thoughtExtracts[0].content,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n } else {\n for (const { field, content } of thoughtExtracts) {\n await ctx.storeThought(\n new Thought({\n id: thoughtStreamId(field),\n content,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n }\n const calls = accumulator.drain()\n helpers.log.debug({\n kind: 'accumulator-finalised',\n message: `Stream finalised: ${calls.length} tool call(s), message=${sawMessageDelta}, thoughtFields=${thoughtExtracts.length}`,\n payload: {\n toolCallCount: calls.length,\n sawMessageDelta,\n thoughtFieldCount: thoughtExtracts.length,\n doneSentinelSeen,\n },\n })\n if (calls.length === 0) {\n // No tool calls — terminal text answer. Only self-ack when opted in;\n // otherwise leave the context unsignalled so the implementor's output\n // pipeline owns turn completion (autoAck defaults to false).\n if (merged.autoAck) ctx.ack()\n return\n }\n for (const call of calls) {\n if (ctx.abortSignal.aborted) return\n await executeAndPersistToolCall(call)\n }\n // Tool calls produced — do NOT ack; the runner will iterate again.\n }\n\n try {\n armIdleTimer()\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n armIdleTimer()\n if (ctx.abortSignal.aborted) {\n clearIdleTimer()\n return\n }\n buffer += decoder.decode(value, { stream: true })\n let sepIdx = buffer.indexOf('\\n\\n')\n while (sepIdx !== -1) {\n const frame = buffer.slice(0, sepIdx)\n buffer = buffer.slice(sepIdx + 2)\n for (const line of frame.split('\\n')) {\n if (!line.startsWith('data:')) continue\n const data = line.slice(5).trim()\n if (data.length === 0) continue\n if (data === '[DONE]') {\n doneSentinelSeen = true\n clearIdleTimer()\n await drainAndPersist()\n return\n }\n let chunk: ChatCompletionsChunk\n try {\n chunk = JSON.parse(data) as ChatCompletionsChunk\n } catch {\n helpers.log.trace({\n kind: 'sse-parse-failure',\n message: 'Failed to parse SSE chunk as JSON; skipping',\n payload: { dataPreview: data.slice(0, 256) },\n })\n continue\n }\n const delta = chunk.choices?.[0]?.delta\n if (!delta) continue\n if (typeof delta.content === 'string' && delta.content.length > 0) {\n sawMessageDelta = true\n partialMessageContent += delta.content\n helpers.reportMessage(streamId, delta.content)\n }\n // Stream every reasoning field present on this delta, in precedence order. No\n // mid-stream dedup — deltas are fragments; we compare full contents at completion.\n for (const field of reasoningPrecedence) {\n const reasoning = delta[field]\n if (typeof reasoning !== 'string' || reasoning.length === 0) continue\n if (!thoughtAccum.has(field)) {\n primaryThoughtField ??= field\n thoughtAccum.set(field, '')\n }\n thoughtAccum.set(field, thoughtAccum.get(field)! + reasoning)\n helpers.reportThought(thoughtStreamId(field), reasoning)\n }\n if (Array.isArray(delta.tool_calls)) {\n for (const d of delta.tool_calls) {\n accumulator.feed(d)\n }\n }\n // finish_reason emitted before [DONE] — no special action required; the [DONE]\n // sentinel is the canonical terminator.\n }\n sepIdx = buffer.indexOf('\\n\\n')\n }\n }\n clearIdleTimer()\n if (stalled) {\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED([idleMs]))\n return\n }\n if (!doneSentinelSeen) {\n // EOF without [DONE] — still drain and persist whatever we have.\n helpers.log.warn({\n kind: 'sse-eof-without-done',\n message: 'SSE stream ended without [DONE] sentinel; draining accumulated state',\n })\n await drainAndPersist()\n }\n } catch (err) {\n clearIdleTimer()\n if (ctx.abortSignal.aborted) return\n if (stalled) {\n ctx.nack(new E_OPENAI_CHAT_COMPLETIONS_STREAM_STALLED([idleMs]))\n return\n }\n helpers.log.error({\n kind: 'stream-error',\n message: `SSE stream failed: ${isError(err) ? err.message : String(err)}`,\n payload: { detail: isError(err) ? err.message : String(err) },\n })\n ctx.nack(\n new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR([isError(err) ? err.message : String(err)])\n )\n return\n }\n return\n }\n\n // ── Step 9: non-streaming path ────────────────────────────────────────\n let parsed: ChatCompletionsResponse\n try {\n parsed = (await response.json()) as ChatCompletionsResponse\n } catch (err) {\n ctx.nack(\n new E_OPENAI_CHAT_COMPLETIONS_STREAM_ERROR([isError(err) ? err.message : String(err)])\n )\n return\n }\n const choice = parsed.choices?.[0]\n if (!choice) {\n // Empty response, no tool calls — terminal. Self-ack only when opted in.\n if (merged.autoAck) ctx.ack()\n return\n }\n const msg = choice.message\n const responseId = parsed.id ?? uuidv6()\n\n if (msg && typeof msg.content === 'string' && msg.content.length > 0) {\n const messageId = `${responseId}:message`\n helpers.reportMessage(messageId, msg.content, { isComplete: true })\n await ctx.storeMessage(\n new Message({\n id: messageId,\n role: 'assistant',\n content: msg.content,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n // Reasoning may arrive under more than one provider-specific field. A single extracted\n // entry (only one field present, or several but identical) keeps the historical\n // `${responseId}:thought` id; divergent fields each surface as their own thought.\n const reasoningExtracts = extractReasoningFields(msg, reasoningPrecedence)\n for (const { field, content } of reasoningExtracts) {\n const thoughtId =\n reasoningExtracts.length > 1 ? `${responseId}:thought:${field}` : `${responseId}:thought`\n helpers.reportThought(thoughtId, content, { isComplete: true })\n await ctx.storeThought(\n new Thought({\n id: thoughtId,\n content,\n identity: selfIdentity,\n createdAt: nowIso(),\n updatedAt: nowIso(),\n })\n )\n }\n\n const rawCalls = msg?.tool_calls ?? []\n if (rawCalls.length === 0) {\n // No tool calls — terminal text answer. Self-ack only when opted in;\n // otherwise the implementor's output pipeline owns completion.\n if (merged.autoAck) ctx.ack()\n return\n }\n const calls: AssembledToolCall[] = rawCalls.map((tc) => ({\n id: tc.id,\n type: tc.type ?? 'function',\n name: tc.function?.name ?? '',\n args: tc.function?.arguments ?? '',\n }))\n for (const call of calls) {\n if (ctx.abortSignal.aborted) return\n await executeAndPersistToolCall(call)\n }\n // Tool calls produced — do NOT ack; the runner will iterate again.\n }\n }\n\n /**\n * Returns `true` when `value` is an {@link OpenAIChatCompletionsAdapter} instance.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is an `OpenAIChatCompletionsAdapter` instance.\n */\n public static isOpenAIChatCompletionsAdapter(\n value: unknown\n ): value is OpenAIChatCompletionsAdapter {\n return isInstanceOf(value, 'OpenAIChatCompletionsAdapter', OpenAIChatCompletionsAdapter)\n }\n}\n"],"mappings":"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"}