@tanstack/ai 0.36.0 → 0.37.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 (205) hide show
  1. package/dist/esm/activities/chat/adapter.d.ts +163 -0
  2. package/dist/esm/activities/chat/adapter.js +17 -0
  3. package/dist/esm/activities/chat/adapter.js.map +1 -0
  4. package/dist/esm/activities/chat/agent-loop-strategies.d.ts +59 -0
  5. package/dist/esm/activities/chat/agent-loop-strategies.js +23 -0
  6. package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -0
  7. package/dist/esm/activities/chat/index.d.ts +270 -0
  8. package/dist/esm/activities/chat/index.js +1724 -0
  9. package/dist/esm/activities/chat/index.js.map +1 -0
  10. package/dist/esm/activities/chat/mcp/manager.d.ts +25 -0
  11. package/dist/esm/activities/chat/mcp/manager.js +71 -0
  12. package/dist/esm/activities/chat/mcp/manager.js.map +1 -0
  13. package/dist/esm/activities/chat/mcp/types.d.ts +56 -0
  14. package/dist/esm/activities/chat/messages.d.ts +78 -0
  15. package/dist/esm/activities/chat/messages.js +374 -0
  16. package/dist/esm/activities/chat/messages.js.map +1 -0
  17. package/dist/esm/activities/chat/middleware/builder.d.ts +46 -0
  18. package/dist/esm/activities/chat/middleware/builder.js +17 -0
  19. package/dist/esm/activities/chat/middleware/builder.js.map +1 -0
  20. package/dist/esm/activities/chat/middleware/capabilities.d.ts +93 -0
  21. package/dist/esm/activities/chat/middleware/capabilities.js +45 -0
  22. package/dist/esm/activities/chat/middleware/capabilities.js.map +1 -0
  23. package/dist/esm/activities/chat/middleware/compose.d.ts +87 -0
  24. package/dist/esm/activities/chat/middleware/compose.js +510 -0
  25. package/dist/esm/activities/chat/middleware/compose.js.map +1 -0
  26. package/dist/esm/activities/chat/middleware/define.d.ts +20 -0
  27. package/dist/esm/activities/chat/middleware/define.js +7 -0
  28. package/dist/esm/activities/chat/middleware/define.js.map +1 -0
  29. package/dist/esm/activities/chat/middleware/index.d.ts +10 -0
  30. package/dist/esm/activities/chat/middleware/tool-cache-middleware.d.ts +89 -0
  31. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +76 -0
  32. package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -0
  33. package/dist/esm/activities/chat/middleware/types.d.ts +405 -0
  34. package/dist/esm/activities/chat/middleware/validate.d.ts +19 -0
  35. package/dist/esm/activities/chat/middleware/validate.js +30 -0
  36. package/dist/esm/activities/chat/middleware/validate.js.map +1 -0
  37. package/dist/esm/activities/chat/runtime-context-types.d.ts +43 -0
  38. package/dist/esm/activities/chat/stream/index.d.ts +11 -0
  39. package/dist/esm/activities/chat/stream/json-parser.d.ts +38 -0
  40. package/dist/esm/activities/chat/stream/json-parser.js +28 -0
  41. package/dist/esm/activities/chat/stream/json-parser.js.map +1 -0
  42. package/dist/esm/activities/chat/stream/message-updaters.d.ts +81 -0
  43. package/dist/esm/activities/chat/stream/message-updaters.js +253 -0
  44. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -0
  45. package/dist/esm/activities/chat/stream/processor.d.ts +439 -0
  46. package/dist/esm/activities/chat/stream/processor.js +1426 -0
  47. package/dist/esm/activities/chat/stream/processor.js.map +1 -0
  48. package/dist/esm/activities/chat/stream/strategies.d.ts +43 -0
  49. package/dist/esm/activities/chat/stream/strategies.js +54 -0
  50. package/dist/esm/activities/chat/stream/strategies.js.map +1 -0
  51. package/dist/esm/activities/chat/stream/types.d.ts +90 -0
  52. package/dist/esm/activities/chat/tools/lazy-tool-manager.d.ts +82 -0
  53. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +194 -0
  54. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -0
  55. package/dist/esm/activities/chat/tools/lazy-tools.d.ts +15 -0
  56. package/dist/esm/activities/chat/tools/lazy-tools.js +16 -0
  57. package/dist/esm/activities/chat/tools/lazy-tools.js.map +1 -0
  58. package/dist/esm/activities/chat/tools/schema-converter.d.ts +140 -0
  59. package/dist/esm/activities/chat/tools/schema-converter.js +167 -0
  60. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -0
  61. package/dist/esm/activities/chat/tools/tool-calls.d.ts +140 -0
  62. package/dist/esm/activities/chat/tools/tool-calls.js +512 -0
  63. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -0
  64. package/dist/esm/activities/chat/tools/tool-definition.d.ts +135 -0
  65. package/dist/esm/activities/chat/tools/tool-definition.js +25 -0
  66. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -0
  67. package/dist/esm/activities/error-payload.d.ts +30 -0
  68. package/dist/esm/activities/error-payload.js +54 -0
  69. package/dist/esm/activities/error-payload.js.map +1 -0
  70. package/dist/esm/activities/generateAudio/adapter.d.ts +62 -0
  71. package/dist/esm/activities/generateAudio/adapter.js +16 -0
  72. package/dist/esm/activities/generateAudio/adapter.js.map +1 -0
  73. package/dist/esm/activities/generateAudio/index.d.ts +85 -0
  74. package/dist/esm/activities/generateAudio/index.js +114 -0
  75. package/dist/esm/activities/generateAudio/index.js.map +1 -0
  76. package/dist/esm/activities/generateImage/adapter.d.ts +78 -0
  77. package/dist/esm/activities/generateImage/adapter.js +16 -0
  78. package/dist/esm/activities/generateImage/adapter.js.map +1 -0
  79. package/dist/esm/activities/generateImage/index.d.ts +134 -0
  80. package/dist/esm/activities/generateImage/index.js +121 -0
  81. package/dist/esm/activities/generateImage/index.js.map +1 -0
  82. package/dist/esm/activities/generateSpeech/adapter.d.ts +62 -0
  83. package/dist/esm/activities/generateSpeech/adapter.js +16 -0
  84. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -0
  85. package/dist/esm/activities/generateSpeech/index.d.ts +96 -0
  86. package/dist/esm/activities/generateSpeech/index.js +119 -0
  87. package/dist/esm/activities/generateSpeech/index.js.map +1 -0
  88. package/dist/esm/activities/generateTranscription/adapter.d.ts +62 -0
  89. package/dist/esm/activities/generateTranscription/adapter.js +16 -0
  90. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -0
  91. package/dist/esm/activities/generateTranscription/index.d.ts +109 -0
  92. package/dist/esm/activities/generateTranscription/index.js +109 -0
  93. package/dist/esm/activities/generateTranscription/index.js.map +1 -0
  94. package/dist/esm/activities/generateVideo/adapter.d.ts +145 -0
  95. package/dist/esm/activities/generateVideo/adapter.js +30 -0
  96. package/dist/esm/activities/generateVideo/adapter.js.map +1 -0
  97. package/dist/esm/activities/generateVideo/index.d.ts +223 -0
  98. package/dist/esm/activities/generateVideo/index.js +295 -0
  99. package/dist/esm/activities/generateVideo/index.js.map +1 -0
  100. package/dist/esm/activities/generateVideo/snap.d.ts +14 -0
  101. package/dist/esm/activities/generateVideo/snap.js +54 -0
  102. package/dist/esm/activities/generateVideo/snap.js.map +1 -0
  103. package/dist/esm/activities/index.d.ts +27 -0
  104. package/dist/esm/activities/index.js +43 -0
  105. package/dist/esm/activities/index.js.map +1 -0
  106. package/dist/esm/activities/middleware/index.d.ts +2 -0
  107. package/dist/esm/activities/middleware/run.d.ts +20 -0
  108. package/dist/esm/activities/middleware/run.js +42 -0
  109. package/dist/esm/activities/middleware/run.js.map +1 -0
  110. package/dist/esm/activities/middleware/types.d.ts +118 -0
  111. package/dist/esm/activities/stream-generation-result.d.ts +15 -0
  112. package/dist/esm/activities/stream-generation-result.js +49 -0
  113. package/dist/esm/activities/stream-generation-result.js.map +1 -0
  114. package/dist/esm/activities/summarize/adapter.d.ts +74 -0
  115. package/dist/esm/activities/summarize/adapter.js +16 -0
  116. package/dist/esm/activities/summarize/adapter.js.map +1 -0
  117. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +45 -0
  118. package/dist/esm/activities/summarize/chat-stream-summarize.js +212 -0
  119. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -0
  120. package/dist/esm/activities/summarize/index.d.ts +108 -0
  121. package/dist/esm/activities/summarize/index.js +113 -0
  122. package/dist/esm/activities/summarize/index.js.map +1 -0
  123. package/dist/esm/adapter-internals.d.ts +5 -0
  124. package/dist/esm/adapter-internals.js +10 -0
  125. package/dist/esm/adapter-internals.js.map +1 -0
  126. package/dist/esm/client.d.ts +44 -0
  127. package/dist/esm/client.js +67 -0
  128. package/dist/esm/client.js.map +1 -0
  129. package/dist/esm/extend-adapter.d.ts +152 -0
  130. package/dist/esm/extend-adapter.js +21 -0
  131. package/dist/esm/extend-adapter.js.map +1 -0
  132. package/dist/esm/index.d.ts +45 -0
  133. package/dist/esm/index.js +109 -0
  134. package/dist/esm/index.js.map +1 -0
  135. package/dist/esm/logger/console-logger.d.ts +29 -0
  136. package/dist/esm/logger/console-logger.js +83 -0
  137. package/dist/esm/logger/console-logger.js.map +1 -0
  138. package/dist/esm/logger/internal-logger.d.ts +41 -0
  139. package/dist/esm/logger/internal-logger.js +86 -0
  140. package/dist/esm/logger/internal-logger.js.map +1 -0
  141. package/dist/esm/logger/resolve.d.ts +14 -0
  142. package/dist/esm/logger/resolve.js +54 -0
  143. package/dist/esm/logger/resolve.js.map +1 -0
  144. package/dist/esm/logger/types.d.ts +75 -0
  145. package/dist/esm/middlewares/content-guard.d.ts +77 -0
  146. package/dist/esm/middlewares/content-guard.js +156 -0
  147. package/dist/esm/middlewares/content-guard.js.map +1 -0
  148. package/dist/esm/middlewares/index.d.ts +2 -0
  149. package/dist/esm/middlewares/index.js +7 -0
  150. package/dist/esm/middlewares/index.js.map +1 -0
  151. package/dist/esm/middlewares/otel.d.ts +81 -0
  152. package/dist/esm/middlewares/otel.js +745 -0
  153. package/dist/esm/middlewares/otel.js.map +1 -0
  154. package/dist/esm/middlewares/tool-cache.d.ts +1 -0
  155. package/dist/esm/middlewares/usage-attributes.d.ts +24 -0
  156. package/dist/esm/middlewares/usage-attributes.js +43 -0
  157. package/dist/esm/middlewares/usage-attributes.js.map +1 -0
  158. package/dist/esm/realtime/index.d.ts +28 -0
  159. package/dist/esm/realtime/index.js +8 -0
  160. package/dist/esm/realtime/index.js.map +1 -0
  161. package/dist/esm/realtime/types.d.ts +282 -0
  162. package/dist/esm/stream-to-response.d.ts +102 -0
  163. package/dist/esm/stream-to-response.js +121 -0
  164. package/dist/esm/stream-to-response.js.map +1 -0
  165. package/dist/esm/strip-to-spec-middleware.d.ts +18 -0
  166. package/dist/esm/strip-to-spec-middleware.js +20 -0
  167. package/dist/esm/strip-to-spec-middleware.js.map +1 -0
  168. package/dist/esm/system-prompts.d.ts +66 -0
  169. package/dist/esm/system-prompts.js +23 -0
  170. package/dist/esm/system-prompts.js.map +1 -0
  171. package/dist/esm/tool-registry.d.ts +81 -0
  172. package/dist/esm/tool-registry.js +49 -0
  173. package/dist/esm/tool-registry.js.map +1 -0
  174. package/dist/esm/tools/provider-tool.d.ts +30 -0
  175. package/dist/esm/tools/provider-tool.js +7 -0
  176. package/dist/esm/tools/provider-tool.js.map +1 -0
  177. package/dist/esm/types.d.ts +1594 -0
  178. package/dist/esm/utilities/ag-ui-wire.d.ts +44 -0
  179. package/dist/esm/utilities/ag-ui-wire.js +107 -0
  180. package/dist/esm/utilities/ag-ui-wire.js.map +1 -0
  181. package/dist/esm/utilities/chat-params.d.ts +85 -0
  182. package/dist/esm/utilities/chat-params.js +100 -0
  183. package/dist/esm/utilities/chat-params.js.map +1 -0
  184. package/dist/esm/utilities/errors.d.ts +13 -0
  185. package/dist/esm/utilities/errors.js +22 -0
  186. package/dist/esm/utilities/errors.js.map +1 -0
  187. package/dist/esm/utilities/media-prompt.d.ts +35 -0
  188. package/dist/esm/utilities/media-prompt.js +43 -0
  189. package/dist/esm/utilities/media-prompt.js.map +1 -0
  190. package/dist/esm/utilities/numbers.d.ts +8 -0
  191. package/dist/esm/utilities/numbers.js +12 -0
  192. package/dist/esm/utilities/numbers.js.map +1 -0
  193. package/dist/esm/utilities/sampling-keys.d.ts +20 -0
  194. package/dist/esm/utilities/sampling-keys.js +20 -0
  195. package/dist/esm/utilities/sampling-keys.js.map +1 -0
  196. package/dist/esm/utilities/tool-result.d.ts +21 -0
  197. package/dist/esm/utilities/tool-result.js +37 -0
  198. package/dist/esm/utilities/tool-result.js.map +1 -0
  199. package/dist/esm/utilities/usage.d.ts +31 -0
  200. package/dist/esm/utilities/usage.js +11 -0
  201. package/dist/esm/utilities/usage.js.map +1 -0
  202. package/dist/esm/utils.d.ts +17 -0
  203. package/dist/esm/utils.js +20 -0
  204. package/dist/esm/utils.js.map +1 -0
  205. package/package.json +3 -3
@@ -0,0 +1,1724 @@
1
+ import { devtoolsMiddleware } from "@tanstack/ai-event-client";
2
+ import { undoNullWidening } from "@tanstack/ai-utils";
3
+ import { stripToSpecMiddleware } from "../../strip-to-spec-middleware.js";
4
+ import { streamToText } from "../../stream-to-response.js";
5
+ import { resolveDebugOption } from "../../logger/resolve.js";
6
+ import { normalizeToolResult } from "../../utilities/tool-result.js";
7
+ import { LazyToolManager } from "./tools/lazy-tool-manager.js";
8
+ import { ToolCallManager, MiddlewareAbortError, executeToolCalls } from "./tools/tool-calls.js";
9
+ import { convertSchemaForStructuredOutput, isStandardSchema, parseWithStandardSchema, convertSchemaToJsonSchema } from "./tools/schema-converter.js";
10
+ import { maxIterations } from "./agent-loop-strategies.js";
11
+ import { convertMessagesToModelMessages, generateMessageId } from "./messages.js";
12
+ import { MiddlewareRunner } from "./middleware/compose.js";
13
+ import { CapabilityRegistry } from "./middleware/capabilities.js";
14
+ import { validateCapabilities } from "./middleware/validate.js";
15
+ import { MCPManager } from "./mcp/manager.js";
16
+ import { EventType } from "@ag-ui/core";
17
+ const kind = "text";
18
+ function createChatOptions(options) {
19
+ return options;
20
+ }
21
+ function combineAbortSignals(a, b) {
22
+ if (!a) return b;
23
+ if (!b) return a;
24
+ if (a.aborted) return a;
25
+ if (b.aborted) return b;
26
+ const controller = new AbortController();
27
+ const onAbort = (source) => () => {
28
+ controller.abort(source.reason);
29
+ };
30
+ a.addEventListener("abort", onAbort(a), { once: true });
31
+ b.addEventListener("abort", onAbort(b), { once: true });
32
+ return controller.signal;
33
+ }
34
+ class TextEngine {
35
+ adapter;
36
+ params;
37
+ systemPrompts;
38
+ tools;
39
+ loopStrategy;
40
+ toolCallManager;
41
+ lazyToolManager;
42
+ initialMessageCount;
43
+ requestId;
44
+ streamId;
45
+ effectiveRequest;
46
+ effectiveSignal;
47
+ messages;
48
+ iterationCount = 0;
49
+ lastFinishReason = null;
50
+ streamStartTime = 0;
51
+ totalChunkCount = 0;
52
+ currentMessageId = null;
53
+ accumulatedContent = "";
54
+ accumulatedThinking = [];
55
+ currentThinkingContent = "";
56
+ currentThinkingSignature = "";
57
+ eventOptions;
58
+ eventToolNames;
59
+ finishedEvent = null;
60
+ earlyTermination = false;
61
+ toolPhase = "continue";
62
+ cyclePhase = "processText";
63
+ // Client state extracted from initial messages (before conversion to ModelMessage)
64
+ initialApprovals;
65
+ initialClientToolResults;
66
+ // AG-UI protocol IDs
67
+ threadId;
68
+ runIdOverride;
69
+ parentRunIdOverride;
70
+ // Middleware support
71
+ middlewareRunner;
72
+ middlewareCtx;
73
+ deferredPromises = [];
74
+ abortReason;
75
+ middlewareAbortController;
76
+ // Combines the caller's signal with middleware abort() so running tools
77
+ // observe both cancellation sources via ctx.abortSignal.
78
+ toolAbortSignal;
79
+ terminalHookCalled = false;
80
+ logger;
81
+ // Structured-output finalization state (populated by runStructuredFinalization)
82
+ structuredOutputResult = null;
83
+ // Native combined mode: tracks whether we've already emitted the synthetic
84
+ // `structured-output.start` event before the schema-constrained final-turn
85
+ // text begins streaming. The event must precede the first
86
+ // TEXT_MESSAGE_START so the client-side StreamProcessor routes the JSON
87
+ // deltas into a StructuredOutputPart instead of a plain TextPart.
88
+ combinedStartEmitted = false;
89
+ // Native combined mode: messageId we want the synthetic
90
+ // `structured-output.start` (and any error emitted before deltas arrive)
91
+ // to carry, so the client matches it to the streaming text deltas.
92
+ combinedStructuredMessageId = null;
93
+ // Holds the validated value when `finalStructuredOutput.validate` is provided
94
+ // and succeeds. Distinct from `structuredOutputResult.data` (the normalized
95
+ // but unvalidated payload from the structured-output.complete chunk).
96
+ validatedStructuredOutput = void 0;
97
+ hasValidatedStructuredOutput = false;
98
+ finalizationError = null;
99
+ finalStructuredOutput;
100
+ constructor(config, logger) {
101
+ this.logger = logger;
102
+ this.adapter = config.adapter;
103
+ this.finalStructuredOutput = config.finalStructuredOutput;
104
+ this.params = config.params;
105
+ this.systemPrompts = config.params.systemPrompts || [];
106
+ this.loopStrategy = config.params.agentLoopStrategy || maxIterations(5);
107
+ this.initialMessageCount = config.params.messages.length;
108
+ const { approvals, clientToolResults } = this.extractClientStateFromOriginalMessages(
109
+ config.params.messages
110
+ );
111
+ this.initialApprovals = approvals;
112
+ this.initialClientToolResults = clientToolResults;
113
+ this.messages = convertMessagesToModelMessages(config.params.messages);
114
+ this.lazyToolManager = new LazyToolManager(
115
+ config.params.tools || [],
116
+ this.messages,
117
+ config.params.lazyToolsConfig
118
+ );
119
+ this.tools = this.lazyToolManager.getActiveTools();
120
+ this.toolCallManager = new ToolCallManager(this.tools);
121
+ this.requestId = this.createId("chat");
122
+ this.streamId = this.createId("stream");
123
+ this.effectiveRequest = config.params.abortController ? { signal: config.params.abortController.signal } : void 0;
124
+ this.effectiveSignal = config.params.abortController?.signal;
125
+ this.threadId = config.params.threadId || config.params.conversationId || this.createId("thread");
126
+ this.runIdOverride = config.params.runId;
127
+ this.parentRunIdOverride = config.params.parentRunId;
128
+ const allMiddleware = [
129
+ devtoolsMiddleware(),
130
+ ...config.middleware || [],
131
+ stripToSpecMiddleware()
132
+ ];
133
+ this.middlewareRunner = new MiddlewareRunner(allMiddleware, logger);
134
+ this.middlewareAbortController = new AbortController();
135
+ this.toolAbortSignal = combineAbortSignals(
136
+ this.effectiveSignal,
137
+ this.middlewareAbortController.signal
138
+ );
139
+ this.middlewareCtx = {
140
+ requestId: this.requestId,
141
+ streamId: this.streamId,
142
+ runId: this.runIdOverride ?? this.requestId,
143
+ threadId: this.threadId,
144
+ // Legacy alias kept on the ctx so middleware that reads
145
+ // `ctx.conversationId` keeps working. Always equals `threadId`.
146
+ conversationId: this.threadId,
147
+ phase: "init",
148
+ iteration: 0,
149
+ chunkIndex: 0,
150
+ signal: this.effectiveSignal,
151
+ abort: (reason) => {
152
+ this.abortReason = reason;
153
+ this.middlewareAbortController?.abort(reason);
154
+ },
155
+ context: config.context,
156
+ defer: (promise) => {
157
+ this.deferredPromises.push(promise);
158
+ },
159
+ // Provider / adapter info
160
+ activity: "chat",
161
+ provider: config.adapter.name,
162
+ model: config.params.model,
163
+ source: "server",
164
+ streaming: true,
165
+ // Config-derived (updated in beforeRun and applyMiddlewareConfig)
166
+ systemPrompts: this.systemPrompts,
167
+ toolNames: void 0,
168
+ options: void 0,
169
+ modelOptions: config.params.modelOptions,
170
+ // Computed
171
+ messageCount: this.initialMessageCount,
172
+ hasTools: this.tools.length > 0,
173
+ // Mutable per-iteration
174
+ currentMessageId: null,
175
+ accumulatedContent: "",
176
+ // References
177
+ messages: this.messages,
178
+ createId: (prefix) => this.createId(prefix),
179
+ // Capability bookkeeping for this request (populated by middleware setup)
180
+ capabilities: new CapabilityRegistry(),
181
+ // Convenience accessors that delegate to a capability handle's own
182
+ // tuple getter/provider, keyed by this context. `getX(ctx)` and
183
+ // `ctx.get(X)` are interchangeable.
184
+ get: (capability) => capability[0](this.middlewareCtx),
185
+ getOptional: (capability) => capability[0](this.middlewareCtx, { optional: true }),
186
+ provide: (capability, value) => capability[1](this.middlewareCtx, value)
187
+ };
188
+ }
189
+ /** Get the accumulated content after the chat loop completes */
190
+ getAccumulatedContent() {
191
+ return this.accumulatedContent;
192
+ }
193
+ /** Get the final messages array after the chat loop completes */
194
+ getMessages() {
195
+ return this.messages;
196
+ }
197
+ /** Returns the structured-output result if finalization ran successfully. */
198
+ getStructuredOutputResult() {
199
+ return this.structuredOutputResult;
200
+ }
201
+ /**
202
+ * Returns the validated structured-output value (the result of running
203
+ * `finalStructuredOutput.validate` against the raw structured-output data)
204
+ * wrapped in a `{ value }` object so callers can distinguish "no validation
205
+ * happened" from "validation produced undefined". Returns `null` when no
206
+ * validator was configured or validation hasn't been performed yet.
207
+ */
208
+ getValidatedStructuredOutput() {
209
+ return this.hasValidatedStructuredOutput ? { value: this.validatedStructuredOutput } : null;
210
+ }
211
+ /** Returns the recorded finalization error, if any. */
212
+ getFinalizationError() {
213
+ return this.finalizationError;
214
+ }
215
+ async *run() {
216
+ this.beforeRun();
217
+ this.logger.agentLoop("run started", {
218
+ threadId: this.middlewareCtx.threadId
219
+ });
220
+ try {
221
+ await this.middlewareRunner.runSetup(this.middlewareCtx);
222
+ this.middlewareCtx.phase = "init";
223
+ const initialConfig = this.buildMiddlewareConfig();
224
+ const transformedConfig = await this.middlewareRunner.runOnConfig(
225
+ this.middlewareCtx,
226
+ initialConfig
227
+ );
228
+ this.applyMiddlewareConfig(transformedConfig);
229
+ await this.middlewareRunner.runOnStart(this.middlewareCtx);
230
+ const pendingPhase = yield* this.checkForPendingToolCalls();
231
+ if (pendingPhase === "wait") {
232
+ return;
233
+ }
234
+ const skipAgentLoop = !!this.finalStructuredOutput && this.tools.length === 0 && this.finalStructuredOutput.nativeCombined !== true;
235
+ if (!skipAgentLoop) {
236
+ do {
237
+ if (this.earlyTermination || this.isCancelled()) {
238
+ return;
239
+ }
240
+ this.logger.agentLoop(`iteration=${this.middlewareCtx.iteration}`, {
241
+ iteration: this.middlewareCtx.iteration
242
+ });
243
+ await this.beginCycle();
244
+ if (this.cyclePhase === "processText") {
245
+ this.middlewareCtx.phase = "beforeModel";
246
+ this.middlewareCtx.iteration = this.iterationCount;
247
+ const iterConfig = this.buildMiddlewareConfig();
248
+ const iterTransformedConfig = await this.middlewareRunner.runOnConfig(
249
+ this.middlewareCtx,
250
+ iterConfig
251
+ );
252
+ this.applyMiddlewareConfig(iterTransformedConfig);
253
+ yield* this.streamModelResponse();
254
+ } else {
255
+ yield* this.processToolCalls();
256
+ }
257
+ this.endCycle();
258
+ } while (this.shouldContinue());
259
+ }
260
+ this.logger.agentLoop("run finished", {
261
+ finishReason: this.lastFinishReason
262
+ });
263
+ if (this.finalStructuredOutput && !this.isCancelled() && !this.finalizationError) {
264
+ if (this.finalStructuredOutput.nativeCombined === true) {
265
+ yield* this.harvestCombinedStructuredOutput();
266
+ } else {
267
+ yield* this.runStructuredFinalization();
268
+ }
269
+ }
270
+ if (!this.terminalHookCalled && this.toolPhase !== "wait" && !this.isCancelled()) {
271
+ if (this.finalizationError) {
272
+ this.terminalHookCalled = true;
273
+ const errForHook = new Error(
274
+ this.finalizationError.message,
275
+ this.finalizationError.cause !== void 0 ? { cause: this.finalizationError.cause } : void 0
276
+ );
277
+ if (this.finalizationError.code !== void 0) {
278
+ Object.defineProperty(errForHook, "code", {
279
+ value: this.finalizationError.code,
280
+ enumerable: true
281
+ });
282
+ }
283
+ await this.middlewareRunner.runOnError(this.middlewareCtx, {
284
+ error: errForHook,
285
+ duration: Date.now() - this.streamStartTime
286
+ });
287
+ } else {
288
+ this.terminalHookCalled = true;
289
+ await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
290
+ finishReason: this.lastFinishReason,
291
+ duration: Date.now() - this.streamStartTime,
292
+ content: this.accumulatedContent,
293
+ usage: this.finishedEvent?.usage
294
+ });
295
+ }
296
+ }
297
+ } catch (error) {
298
+ if (!this.terminalHookCalled) {
299
+ this.terminalHookCalled = true;
300
+ if (error instanceof MiddlewareAbortError) {
301
+ this.abortReason = error.message;
302
+ await this.middlewareRunner.runOnAbort(this.middlewareCtx, {
303
+ reason: error.message,
304
+ duration: Date.now() - this.streamStartTime
305
+ });
306
+ } else {
307
+ this.logger.errors("chat run failed", {
308
+ error,
309
+ threadId: this.middlewareCtx.threadId
310
+ });
311
+ await this.middlewareRunner.runOnError(this.middlewareCtx, {
312
+ error,
313
+ duration: Date.now() - this.streamStartTime
314
+ });
315
+ }
316
+ }
317
+ if (!(error instanceof MiddlewareAbortError)) {
318
+ throw error;
319
+ }
320
+ } finally {
321
+ if (!this.terminalHookCalled && this.isCancelled()) {
322
+ this.terminalHookCalled = true;
323
+ await this.middlewareRunner.runOnAbort(this.middlewareCtx, {
324
+ reason: this.abortReason,
325
+ duration: Date.now() - this.streamStartTime
326
+ });
327
+ }
328
+ if (this.deferredPromises.length > 0) {
329
+ await Promise.allSettled(this.deferredPromises);
330
+ }
331
+ }
332
+ }
333
+ beforeRun() {
334
+ this.streamStartTime = Date.now();
335
+ const { tools, metadata } = this.params;
336
+ const options = {};
337
+ if (metadata !== void 0) options.metadata = metadata;
338
+ this.eventOptions = Object.keys(options).length > 0 ? options : void 0;
339
+ this.eventToolNames = tools?.map((t) => t.name);
340
+ this.middlewareCtx.options = this.eventOptions;
341
+ this.middlewareCtx.toolNames = this.eventToolNames;
342
+ }
343
+ async beginCycle() {
344
+ if (this.cyclePhase === "processText") {
345
+ await this.beginIteration();
346
+ }
347
+ }
348
+ endCycle() {
349
+ if (this.cyclePhase === "processText") {
350
+ this.cyclePhase = "executeToolCalls";
351
+ return;
352
+ }
353
+ this.cyclePhase = "processText";
354
+ this.iterationCount++;
355
+ }
356
+ async beginIteration() {
357
+ this.currentMessageId = this.createId("msg");
358
+ this.accumulatedContent = "";
359
+ this.accumulatedThinking = [];
360
+ this.currentThinkingContent = "";
361
+ this.currentThinkingSignature = "";
362
+ this.finishedEvent = null;
363
+ this.middlewareCtx.currentMessageId = this.currentMessageId;
364
+ this.middlewareCtx.accumulatedContent = "";
365
+ await this.middlewareRunner.runOnIteration(this.middlewareCtx, {
366
+ iteration: this.iterationCount,
367
+ messageId: this.currentMessageId
368
+ });
369
+ }
370
+ async *streamModelResponse() {
371
+ const { metadata, modelOptions } = this.params;
372
+ const tools = this.tools;
373
+ const toolsWithJsonSchemas = tools.map((tool) => ({
374
+ ...tool,
375
+ inputSchema: tool.inputSchema ? convertSchemaToJsonSchema(tool.inputSchema) : void 0,
376
+ outputSchema: tool.outputSchema ? convertSchemaToJsonSchema(tool.outputSchema) : void 0
377
+ }));
378
+ this.middlewareCtx.phase = "modelStream";
379
+ const providerName = this.adapter.provider ?? this.adapter.name;
380
+ this.logger.request(
381
+ `activity=chat provider=${providerName} model=${this.params.model} messages=${this.messages.length} tools=${this.tools.length} stream=true`,
382
+ {
383
+ provider: providerName,
384
+ model: this.params.model,
385
+ messageCount: this.messages.length,
386
+ toolCount: this.tools.length
387
+ }
388
+ );
389
+ const combinedSchema = this.finalStructuredOutput?.nativeCombined === true ? this.finalStructuredOutput.jsonSchema : void 0;
390
+ for await (const chunk of this.adapter.chatStream({
391
+ model: this.params.model,
392
+ messages: this.messages,
393
+ tools: toolsWithJsonSchemas,
394
+ metadata,
395
+ request: this.effectiveRequest,
396
+ modelOptions,
397
+ systemPrompts: this.systemPrompts,
398
+ logger: this.logger,
399
+ threadId: this.threadId,
400
+ runId: this.runIdOverride,
401
+ parentRunId: this.parentRunIdOverride,
402
+ ...combinedSchema ? { outputSchema: combinedSchema } : {}
403
+ })) {
404
+ if (this.isCancelled()) {
405
+ break;
406
+ }
407
+ this.totalChunkCount++;
408
+ this.handleStreamChunk(chunk);
409
+ if (this.finalStructuredOutput?.nativeCombined === true && this.finalStructuredOutput.yieldChunks && !this.combinedStartEmitted && chunk.type === EventType.TEXT_MESSAGE_START) {
410
+ this.combinedStartEmitted = true;
411
+ const messageId = typeof chunk.messageId === "string" && chunk.messageId !== "" ? chunk.messageId : generateMessageId();
412
+ this.combinedStructuredMessageId = messageId;
413
+ const synthStart = {
414
+ type: EventType.CUSTOM,
415
+ name: "structured-output.start",
416
+ value: { messageId },
417
+ model: this.params.model,
418
+ timestamp: Date.now(),
419
+ threadId: this.threadId,
420
+ ...this.runIdOverride ? { runId: this.runIdOverride } : {}
421
+ };
422
+ const synthOutputs = await this.middlewareRunner.runOnChunk(
423
+ this.middlewareCtx,
424
+ synthStart
425
+ );
426
+ for (const outputChunk of synthOutputs) {
427
+ yield outputChunk;
428
+ this.middlewareCtx.chunkIndex++;
429
+ }
430
+ }
431
+ const outputChunks = await this.middlewareRunner.runOnChunk(
432
+ this.middlewareCtx,
433
+ chunk
434
+ );
435
+ const suppressAgentLifecycle = !!this.finalStructuredOutput && this.finalStructuredOutput.yieldChunks && this.finalStructuredOutput.nativeCombined !== true;
436
+ for (const outputChunk of outputChunks) {
437
+ if (suppressAgentLifecycle && (outputChunk.type === EventType.RUN_STARTED || outputChunk.type === EventType.RUN_FINISHED)) {
438
+ continue;
439
+ }
440
+ this.logger.output(`type=${outputChunk.type}`, { chunk: outputChunk });
441
+ yield outputChunk;
442
+ this.middlewareCtx.chunkIndex++;
443
+ }
444
+ if (chunk.type === "RUN_FINISHED" && chunk.usage) {
445
+ await this.middlewareRunner.runOnUsage(this.middlewareCtx, chunk.usage);
446
+ }
447
+ if (this.earlyTermination) {
448
+ break;
449
+ }
450
+ }
451
+ }
452
+ handleStreamChunk(chunk) {
453
+ switch (chunk.type) {
454
+ // AG-UI Events
455
+ case "TEXT_MESSAGE_CONTENT":
456
+ this.handleTextMessageContentEvent(chunk);
457
+ break;
458
+ case "TOOL_CALL_START":
459
+ this.handleToolCallStartEvent(chunk);
460
+ break;
461
+ case "TOOL_CALL_ARGS":
462
+ this.handleToolCallArgsEvent(chunk);
463
+ break;
464
+ case "TOOL_CALL_END":
465
+ this.handleToolCallEndEvent(chunk);
466
+ break;
467
+ case "RUN_FINISHED":
468
+ this.handleRunFinishedEvent(chunk);
469
+ break;
470
+ case "RUN_ERROR":
471
+ this.handleRunErrorEvent(chunk);
472
+ break;
473
+ case "STEP_STARTED":
474
+ this.handleStepStartedEvent();
475
+ break;
476
+ case "STEP_FINISHED":
477
+ this.handleStepFinishedEvent(chunk);
478
+ break;
479
+ }
480
+ }
481
+ // ===========================
482
+ // AG-UI Event Handlers
483
+ // ===========================
484
+ handleTextMessageContentEvent(chunk) {
485
+ if (chunk.content) {
486
+ this.accumulatedContent = chunk.content;
487
+ } else {
488
+ this.accumulatedContent += chunk.delta;
489
+ }
490
+ this.middlewareCtx.accumulatedContent = this.accumulatedContent;
491
+ }
492
+ handleToolCallStartEvent(chunk) {
493
+ this.toolCallManager.addToolCallStartEvent(chunk);
494
+ }
495
+ handleToolCallArgsEvent(chunk) {
496
+ this.toolCallManager.addToolCallArgsEvent(chunk);
497
+ }
498
+ handleToolCallEndEvent(chunk) {
499
+ this.toolCallManager.completeToolCall(chunk);
500
+ }
501
+ handleRunFinishedEvent(chunk) {
502
+ this.finishedEvent = chunk;
503
+ this.lastFinishReason = chunk.finishReason ?? null;
504
+ }
505
+ handleRunErrorEvent(_chunk) {
506
+ this.earlyTermination = true;
507
+ }
508
+ finalizeCurrentThinkingStep() {
509
+ if (this.currentThinkingContent) {
510
+ this.accumulatedThinking.push({
511
+ content: this.currentThinkingContent,
512
+ ...this.currentThinkingSignature && {
513
+ signature: this.currentThinkingSignature
514
+ }
515
+ });
516
+ this.currentThinkingContent = "";
517
+ this.currentThinkingSignature = "";
518
+ }
519
+ }
520
+ handleStepStartedEvent() {
521
+ this.finalizeCurrentThinkingStep();
522
+ }
523
+ handleStepFinishedEvent(chunk) {
524
+ if (chunk.delta) {
525
+ this.currentThinkingContent += chunk.delta;
526
+ }
527
+ if (chunk.signature) {
528
+ this.currentThinkingSignature = chunk.signature;
529
+ }
530
+ }
531
+ /**
532
+ * Tools available for execution this turn. The discovery tool is dropped
533
+ * from the advertised set (`this.tools`) once every lazy tool is discovered,
534
+ * but a model may still re-request discovery; this widens execution lookup
535
+ * to include it so such calls don't fail with "Unknown tool". Centralised so
536
+ * both execution sites (`processToolCalls` and `checkForPendingToolCalls`)
537
+ * stay in sync.
538
+ */
539
+ resolveExecutableTools(toolCalls) {
540
+ return this.lazyToolManager.getExecutableTools(
541
+ this.tools,
542
+ toolCalls.map((tc) => tc.function.name)
543
+ );
544
+ }
545
+ async *checkForPendingToolCalls() {
546
+ const pendingToolCalls = this.getPendingToolCallsFromMessages();
547
+ if (pendingToolCalls.length === 0) {
548
+ return "continue";
549
+ }
550
+ const finishEvent = this.createSyntheticFinishedEvent();
551
+ const undiscoveredLazyResults = [];
552
+ const executablePendingCalls = pendingToolCalls.filter((tc) => {
553
+ if (this.lazyToolManager.isUndiscoveredLazyTool(tc.function.name)) {
554
+ undiscoveredLazyResults.push({
555
+ toolCallId: tc.id,
556
+ toolName: tc.function.name,
557
+ result: {
558
+ error: this.lazyToolManager.getUndiscoveredToolError(
559
+ tc.function.name
560
+ )
561
+ },
562
+ state: "output-error"
563
+ });
564
+ return false;
565
+ }
566
+ return true;
567
+ });
568
+ if (undiscoveredLazyResults.length > 0) {
569
+ for (const chunk of this.buildToolResultChunks(
570
+ undiscoveredLazyResults,
571
+ finishEvent
572
+ )) {
573
+ yield* this.pipeThroughMiddleware(chunk);
574
+ }
575
+ }
576
+ if (executablePendingCalls.length === 0) {
577
+ return "continue";
578
+ }
579
+ const { approvals, clientToolResults } = this.collectClientState();
580
+ const generator = executeToolCalls(
581
+ executablePendingCalls,
582
+ this.resolveExecutableTools(executablePendingCalls),
583
+ approvals,
584
+ clientToolResults,
585
+ (eventName, data) => this.createCustomEventChunk(eventName, data),
586
+ {
587
+ onBeforeToolCall: async (toolCall, tool, args) => {
588
+ this.logger.tools(`phase=before name=${toolCall.function.name}`, {
589
+ name: toolCall.function.name,
590
+ args
591
+ });
592
+ const hookCtx = {
593
+ toolCall,
594
+ tool,
595
+ args,
596
+ toolName: toolCall.function.name,
597
+ toolCallId: toolCall.id
598
+ };
599
+ return this.middlewareRunner.runOnBeforeToolCall(
600
+ this.middlewareCtx,
601
+ hookCtx
602
+ );
603
+ },
604
+ onAfterToolCall: async (info) => {
605
+ this.logger.tools(`phase=after name=${info.toolName}`, {
606
+ name: info.toolName,
607
+ result: info.result
608
+ });
609
+ await this.middlewareRunner.runOnAfterToolCall(
610
+ this.middlewareCtx,
611
+ info
612
+ );
613
+ }
614
+ },
615
+ this.middlewareCtx.context,
616
+ this.toolAbortSignal
617
+ );
618
+ const executionResult = yield* this.drainToolCallGenerator(generator);
619
+ if (this.isMiddlewareAborted()) {
620
+ this.setToolPhase("stop");
621
+ return "stop";
622
+ }
623
+ await this.middlewareRunner.runOnToolPhaseComplete(this.middlewareCtx, {
624
+ toolCalls: pendingToolCalls,
625
+ results: executionResult.results,
626
+ needsApproval: executionResult.needsApproval,
627
+ needsClientExecution: executionResult.needsClientExecution
628
+ });
629
+ const argsMap = /* @__PURE__ */ new Map();
630
+ for (const tc of pendingToolCalls) {
631
+ argsMap.set(tc.id, tc.function.arguments);
632
+ }
633
+ if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
634
+ if (executionResult.results.length > 0) {
635
+ for (const chunk of this.buildToolResultChunks(
636
+ executionResult.results,
637
+ finishEvent,
638
+ argsMap
639
+ )) {
640
+ yield* this.pipeThroughMiddleware(chunk);
641
+ }
642
+ }
643
+ for (const chunk of this.buildApprovalChunks(
644
+ executionResult.needsApproval,
645
+ finishEvent
646
+ )) {
647
+ yield* this.pipeThroughMiddleware(chunk);
648
+ }
649
+ for (const chunk of this.buildClientToolChunks(
650
+ executionResult.needsClientExecution,
651
+ finishEvent
652
+ )) {
653
+ yield* this.pipeThroughMiddleware(chunk);
654
+ }
655
+ this.setToolPhase("wait");
656
+ return "wait";
657
+ }
658
+ const toolResultChunks = this.buildToolResultChunks(
659
+ executionResult.results,
660
+ finishEvent,
661
+ argsMap
662
+ );
663
+ for (const chunk of toolResultChunks) {
664
+ yield* this.pipeThroughMiddleware(chunk);
665
+ }
666
+ return "continue";
667
+ }
668
+ async *processToolCalls() {
669
+ if (!this.shouldExecuteToolPhase()) {
670
+ this.setToolPhase("stop");
671
+ return;
672
+ }
673
+ const toolCalls = this.toolCallManager.getToolCalls();
674
+ const finishEvent = this.finishedEvent;
675
+ if (!finishEvent || toolCalls.length === 0) {
676
+ this.setToolPhase("stop");
677
+ return;
678
+ }
679
+ this.addAssistantToolCallMessage(toolCalls);
680
+ const undiscoveredLazyResults = [];
681
+ const executableToolCalls = toolCalls.filter((tc) => {
682
+ if (this.lazyToolManager.isUndiscoveredLazyTool(tc.function.name)) {
683
+ undiscoveredLazyResults.push({
684
+ toolCallId: tc.id,
685
+ toolName: tc.function.name,
686
+ result: {
687
+ error: this.lazyToolManager.getUndiscoveredToolError(
688
+ tc.function.name
689
+ )
690
+ },
691
+ state: "output-error"
692
+ });
693
+ return false;
694
+ }
695
+ return true;
696
+ });
697
+ if (undiscoveredLazyResults.length > 0 && this.finishedEvent) {
698
+ for (const chunk of this.buildToolResultChunks(
699
+ undiscoveredLazyResults,
700
+ this.finishedEvent
701
+ )) {
702
+ yield* this.pipeThroughMiddleware(chunk);
703
+ }
704
+ }
705
+ if (executableToolCalls.length === 0) {
706
+ this.toolCallManager.clear();
707
+ this.setToolPhase("continue");
708
+ return;
709
+ }
710
+ this.middlewareCtx.phase = "beforeTools";
711
+ const { approvals, clientToolResults } = this.collectClientState();
712
+ const generator = executeToolCalls(
713
+ executableToolCalls,
714
+ this.resolveExecutableTools(executableToolCalls),
715
+ approvals,
716
+ clientToolResults,
717
+ (eventName, data) => this.createCustomEventChunk(eventName, data),
718
+ {
719
+ onBeforeToolCall: async (toolCall, tool, args) => {
720
+ this.logger.tools(`phase=before name=${toolCall.function.name}`, {
721
+ name: toolCall.function.name,
722
+ args
723
+ });
724
+ const hookCtx = {
725
+ toolCall,
726
+ tool,
727
+ args,
728
+ toolName: toolCall.function.name,
729
+ toolCallId: toolCall.id
730
+ };
731
+ return this.middlewareRunner.runOnBeforeToolCall(
732
+ this.middlewareCtx,
733
+ hookCtx
734
+ );
735
+ },
736
+ onAfterToolCall: async (info) => {
737
+ this.logger.tools(`phase=after name=${info.toolName}`, {
738
+ name: info.toolName,
739
+ result: info.result
740
+ });
741
+ await this.middlewareRunner.runOnAfterToolCall(
742
+ this.middlewareCtx,
743
+ info
744
+ );
745
+ }
746
+ },
747
+ this.middlewareCtx.context,
748
+ this.toolAbortSignal
749
+ );
750
+ const executionResult = yield* this.drainToolCallGenerator(generator);
751
+ this.middlewareCtx.phase = "afterTools";
752
+ if (this.isMiddlewareAborted()) {
753
+ this.setToolPhase("stop");
754
+ return;
755
+ }
756
+ await this.middlewareRunner.runOnToolPhaseComplete(this.middlewareCtx, {
757
+ toolCalls,
758
+ results: executionResult.results,
759
+ needsApproval: executionResult.needsApproval,
760
+ needsClientExecution: executionResult.needsClientExecution
761
+ });
762
+ if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
763
+ if (executionResult.results.length > 0) {
764
+ for (const chunk of this.buildToolResultChunks(
765
+ executionResult.results,
766
+ finishEvent
767
+ )) {
768
+ yield* this.pipeThroughMiddleware(chunk);
769
+ }
770
+ }
771
+ for (const chunk of this.buildApprovalChunks(
772
+ executionResult.needsApproval,
773
+ finishEvent
774
+ )) {
775
+ yield* this.pipeThroughMiddleware(chunk);
776
+ }
777
+ for (const chunk of this.buildClientToolChunks(
778
+ executionResult.needsClientExecution,
779
+ finishEvent
780
+ )) {
781
+ yield* this.pipeThroughMiddleware(chunk);
782
+ }
783
+ this.setToolPhase("wait");
784
+ return;
785
+ }
786
+ const toolResultChunks = this.buildToolResultChunks(
787
+ executionResult.results,
788
+ finishEvent
789
+ );
790
+ for (const chunk of toolResultChunks) {
791
+ yield* this.pipeThroughMiddleware(chunk);
792
+ }
793
+ if (this.lazyToolManager.hasNewlyDiscoveredTools()) {
794
+ this.tools = this.lazyToolManager.getActiveTools();
795
+ this.toolCallManager = new ToolCallManager(this.tools);
796
+ this.setToolPhase("continue");
797
+ return;
798
+ }
799
+ this.toolCallManager.clear();
800
+ this.setToolPhase("continue");
801
+ }
802
+ shouldExecuteToolPhase() {
803
+ return this.finishedEvent?.finishReason === "tool_calls" && this.tools.length > 0 && this.toolCallManager.hasToolCalls();
804
+ }
805
+ addAssistantToolCallMessage(toolCalls) {
806
+ this.finalizeCurrentThinkingStep();
807
+ this.messages = [
808
+ ...this.messages,
809
+ {
810
+ role: "assistant",
811
+ content: this.accumulatedContent || null,
812
+ toolCalls,
813
+ ...this.accumulatedThinking.length > 0 && {
814
+ thinking: this.accumulatedThinking
815
+ }
816
+ }
817
+ ];
818
+ }
819
+ /**
820
+ * Extract client state (approvals and client tool results) from original messages.
821
+ * This is called in the constructor BEFORE converting to ModelMessage format,
822
+ * because the parts array (which contains approval state) is lost during conversion.
823
+ */
824
+ extractClientStateFromOriginalMessages(originalMessages) {
825
+ const approvals = /* @__PURE__ */ new Map();
826
+ const clientToolResults = /* @__PURE__ */ new Map();
827
+ for (const message of originalMessages) {
828
+ if (message.role === "assistant" && message.parts) {
829
+ for (const part of message.parts) {
830
+ if (part.type === "tool-call") {
831
+ if (part.output !== void 0 && !part.approval) {
832
+ clientToolResults.set(part.id, part.output);
833
+ }
834
+ if (part.approval?.id && part.approval?.approved !== void 0 && part.state === "approval-responded") {
835
+ approvals.set(part.approval.id, part.approval.approved);
836
+ }
837
+ }
838
+ }
839
+ }
840
+ }
841
+ return { approvals, clientToolResults };
842
+ }
843
+ collectClientState() {
844
+ const approvals = new Map(this.initialApprovals);
845
+ const clientToolResults = new Map(this.initialClientToolResults);
846
+ for (const message of this.messages) {
847
+ if (message.role === "tool" && message.toolCallId) {
848
+ let output;
849
+ if (Array.isArray(message.content)) {
850
+ output = message.content;
851
+ } else {
852
+ try {
853
+ output = JSON.parse(message.content);
854
+ } catch {
855
+ output = message.content;
856
+ }
857
+ }
858
+ if (output && typeof output === "object" && output.pendingExecution === true) {
859
+ continue;
860
+ }
861
+ clientToolResults.set(message.toolCallId, output);
862
+ }
863
+ }
864
+ return { approvals, clientToolResults };
865
+ }
866
+ buildApprovalChunks(approvals, finishEvent) {
867
+ const chunks = [];
868
+ for (const approval of approvals) {
869
+ chunks.push({
870
+ type: "CUSTOM",
871
+ timestamp: Date.now(),
872
+ model: finishEvent.model,
873
+ name: "approval-requested",
874
+ value: {
875
+ toolCallId: approval.toolCallId,
876
+ toolName: approval.toolName,
877
+ input: approval.input,
878
+ approval: {
879
+ id: approval.approvalId,
880
+ needsApproval: true
881
+ }
882
+ }
883
+ });
884
+ }
885
+ return chunks;
886
+ }
887
+ buildClientToolChunks(clientRequests, finishEvent) {
888
+ const chunks = [];
889
+ for (const clientTool of clientRequests) {
890
+ chunks.push({
891
+ type: "CUSTOM",
892
+ timestamp: Date.now(),
893
+ model: finishEvent.model,
894
+ name: "tool-input-available",
895
+ value: {
896
+ toolCallId: clientTool.toolCallId,
897
+ toolName: clientTool.toolName,
898
+ input: clientTool.input
899
+ }
900
+ });
901
+ }
902
+ return chunks;
903
+ }
904
+ buildToolResultChunks(results, finishEvent, argsMap) {
905
+ const chunks = [];
906
+ for (const result of results) {
907
+ const content = normalizeToolResult(result.result);
908
+ const wireContent = typeof content === "string" ? content : JSON.stringify(content);
909
+ if (argsMap) {
910
+ chunks.push({
911
+ type: "TOOL_CALL_START",
912
+ timestamp: Date.now(),
913
+ model: finishEvent.model,
914
+ toolCallId: result.toolCallId,
915
+ toolCallName: result.toolName,
916
+ toolName: result.toolName
917
+ });
918
+ const args = argsMap.get(result.toolCallId) ?? "{}";
919
+ chunks.push({
920
+ type: "TOOL_CALL_ARGS",
921
+ timestamp: Date.now(),
922
+ model: finishEvent.model,
923
+ toolCallId: result.toolCallId,
924
+ delta: args,
925
+ args
926
+ });
927
+ chunks.push({
928
+ type: "TOOL_CALL_END",
929
+ timestamp: Date.now(),
930
+ model: finishEvent.model,
931
+ toolCallId: result.toolCallId,
932
+ toolCallName: result.toolName,
933
+ toolName: result.toolName,
934
+ result: wireContent,
935
+ ...result.state !== void 0 && { state: result.state }
936
+ });
937
+ }
938
+ chunks.push({
939
+ type: "TOOL_CALL_RESULT",
940
+ timestamp: Date.now(),
941
+ model: finishEvent.model,
942
+ messageId: this.createId("tool-result"),
943
+ toolCallId: result.toolCallId,
944
+ content: wireContent,
945
+ role: "tool",
946
+ ...result.state !== void 0 && { state: result.state }
947
+ });
948
+ const placeholderIdx = this.messages.findIndex((m) => {
949
+ if (m.role !== "tool" || m.toolCallId !== result.toolCallId) {
950
+ return false;
951
+ }
952
+ if (typeof m.content !== "string") return false;
953
+ try {
954
+ return JSON.parse(m.content)?.pendingExecution === true;
955
+ } catch {
956
+ return false;
957
+ }
958
+ });
959
+ const newToolMessage = {
960
+ role: "tool",
961
+ content,
962
+ toolCallId: result.toolCallId
963
+ };
964
+ if (placeholderIdx >= 0) {
965
+ this.messages = [
966
+ ...this.messages.slice(0, placeholderIdx),
967
+ newToolMessage,
968
+ ...this.messages.slice(placeholderIdx + 1)
969
+ ];
970
+ } else {
971
+ this.messages = [...this.messages, newToolMessage];
972
+ }
973
+ }
974
+ return chunks;
975
+ }
976
+ getPendingToolCallsFromMessages() {
977
+ const completedToolIds = /* @__PURE__ */ new Set();
978
+ for (const message of this.messages) {
979
+ if (message.role === "tool" && message.toolCallId) {
980
+ let hasPendingExecution = false;
981
+ if (typeof message.content === "string") {
982
+ try {
983
+ const parsed = JSON.parse(message.content);
984
+ if (parsed.pendingExecution === true) {
985
+ hasPendingExecution = true;
986
+ }
987
+ } catch {
988
+ }
989
+ }
990
+ if (!hasPendingExecution) {
991
+ completedToolIds.add(message.toolCallId);
992
+ }
993
+ }
994
+ }
995
+ const pending = [];
996
+ for (const message of this.messages) {
997
+ if (message.role === "assistant" && message.toolCalls) {
998
+ for (const toolCall of message.toolCalls) {
999
+ if (!completedToolIds.has(toolCall.id)) {
1000
+ pending.push(toolCall);
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+ return pending;
1006
+ }
1007
+ createSyntheticFinishedEvent() {
1008
+ return {
1009
+ type: "RUN_FINISHED",
1010
+ runId: this.createId("pending"),
1011
+ threadId: this.threadId,
1012
+ model: this.params.model,
1013
+ timestamp: Date.now(),
1014
+ finishReason: "tool_calls"
1015
+ };
1016
+ }
1017
+ shouldContinue() {
1018
+ if (this.cyclePhase === "executeToolCalls") {
1019
+ return true;
1020
+ }
1021
+ return this.loopStrategy({
1022
+ iterationCount: this.iterationCount,
1023
+ messages: this.messages,
1024
+ finishReason: this.lastFinishReason
1025
+ }) && this.toolPhase === "continue";
1026
+ }
1027
+ isAborted() {
1028
+ return !!this.effectiveSignal?.aborted;
1029
+ }
1030
+ isMiddlewareAborted() {
1031
+ return !!this.middlewareAbortController?.signal.aborted;
1032
+ }
1033
+ isCancelled() {
1034
+ return this.isAborted() || this.isMiddlewareAborted();
1035
+ }
1036
+ /**
1037
+ * Run the final structured-output adapter call through the middleware
1038
+ * pipeline. Yields chunks to the caller only when
1039
+ * `this.finalStructuredOutput.yieldChunks` is true; otherwise consumes
1040
+ * silently while still piping through middleware.
1041
+ *
1042
+ * On success, populates this.structuredOutputResult.
1043
+ * On failure, populates this.finalizationError.
1044
+ */
1045
+ async *runStructuredFinalization() {
1046
+ if (!this.finalStructuredOutput) {
1047
+ throw new Error(
1048
+ "runStructuredFinalization called without finalStructuredOutput config"
1049
+ );
1050
+ }
1051
+ this.middlewareCtx.phase = "structuredOutput";
1052
+ const baseConfig = this.buildMiddlewareConfig();
1053
+ const { tools: _omitTools, ...baseWithoutTools } = baseConfig;
1054
+ let structuredConfig = {
1055
+ ...baseWithoutTools,
1056
+ outputSchema: this.finalStructuredOutput.jsonSchema
1057
+ };
1058
+ structuredConfig = await this.middlewareRunner.runOnStructuredOutputConfig(
1059
+ this.middlewareCtx,
1060
+ structuredConfig
1061
+ );
1062
+ const { outputSchema: pinnedSchema, ...chatConfigSlice } = structuredConfig;
1063
+ const postOnConfig = await this.middlewareRunner.runOnConfig(
1064
+ this.middlewareCtx,
1065
+ { ...chatConfigSlice, tools: baseConfig.tools }
1066
+ );
1067
+ this.applyMiddlewareConfig(postOnConfig);
1068
+ const structuredCallOptions = {
1069
+ chatOptions: {
1070
+ model: this.params.model,
1071
+ messages: this.messages,
1072
+ metadata: postOnConfig.metadata,
1073
+ modelOptions: postOnConfig.modelOptions,
1074
+ systemPrompts: postOnConfig.systemPrompts,
1075
+ logger: this.logger,
1076
+ threadId: this.threadId,
1077
+ runId: this.runIdOverride,
1078
+ parentRunId: this.parentRunIdOverride,
1079
+ ...this.effectiveRequest ? { request: this.effectiveRequest } : {}
1080
+ },
1081
+ outputSchema: pinnedSchema
1082
+ };
1083
+ let fallbackAdapterError = void 0;
1084
+ const providerStream = this.adapter.structuredOutputStream ? this.adapter.structuredOutputStream(structuredCallOptions) : fallbackStructuredOutputStream(
1085
+ this.adapter,
1086
+ structuredCallOptions,
1087
+ (err) => {
1088
+ fallbackAdapterError = err;
1089
+ }
1090
+ );
1091
+ let startEmitted = false;
1092
+ let structuredMessageId = null;
1093
+ const extractMessageId = (c) => {
1094
+ if (c.type === EventType.TEXT_MESSAGE_START || c.type === EventType.TEXT_MESSAGE_CONTENT || c.type === EventType.TEXT_MESSAGE_END) {
1095
+ return typeof c.messageId === "string" && c.messageId !== "" ? c.messageId : null;
1096
+ }
1097
+ return null;
1098
+ };
1099
+ const buildSynthesizedStart = () => {
1100
+ const idForStart = structuredMessageId ?? generateMessageId();
1101
+ structuredMessageId = idForStart;
1102
+ return {
1103
+ type: EventType.CUSTOM,
1104
+ name: "structured-output.start",
1105
+ value: { messageId: idForStart },
1106
+ model: this.params.model,
1107
+ timestamp: Date.now(),
1108
+ threadId: this.threadId,
1109
+ ...this.runIdOverride ? { runId: this.runIdOverride } : {}
1110
+ };
1111
+ };
1112
+ const pipeThroughMiddleware = async (synthChunk) => this.middlewareRunner.runOnChunk(this.middlewareCtx, synthChunk);
1113
+ let runErrorYielded = false;
1114
+ for await (const chunk of providerStream) {
1115
+ if (this.isCancelled()) {
1116
+ break;
1117
+ }
1118
+ if (!startEmitted && chunk.type === EventType.CUSTOM && chunk.name === "structured-output.start") {
1119
+ startEmitted = true;
1120
+ }
1121
+ if (!structuredMessageId) {
1122
+ const extracted = extractMessageId(chunk);
1123
+ if (extracted) structuredMessageId = extracted;
1124
+ }
1125
+ if (this.finalStructuredOutput.yieldChunks) {
1126
+ if (!startEmitted && (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT || chunk.type === EventType.TEXT_MESSAGE_END)) {
1127
+ startEmitted = true;
1128
+ const synthStart = buildSynthesizedStart();
1129
+ const synthOutputs = await pipeThroughMiddleware(synthStart);
1130
+ for (const outputChunk of synthOutputs) {
1131
+ yield outputChunk;
1132
+ this.middlewareCtx.chunkIndex++;
1133
+ }
1134
+ }
1135
+ if (!startEmitted && chunk.type === EventType.RUN_ERROR) {
1136
+ startEmitted = true;
1137
+ const synthStart = buildSynthesizedStart();
1138
+ const synthOutputs = await pipeThroughMiddleware(synthStart);
1139
+ for (const outputChunk of synthOutputs) {
1140
+ yield outputChunk;
1141
+ this.middlewareCtx.chunkIndex++;
1142
+ }
1143
+ }
1144
+ }
1145
+ let outboundChunk = chunk;
1146
+ if (chunk.type === EventType.CUSTOM && chunk.name === "structured-output.complete") {
1147
+ const parsed = readStructuredOutputCompleteValue(chunk.value);
1148
+ if (parsed) {
1149
+ const object = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed.object) : parsed.object;
1150
+ this.structuredOutputResult = { data: object, rawText: parsed.raw };
1151
+ const value = chunk.value;
1152
+ if (object !== parsed.object && value && typeof value === "object") {
1153
+ outboundChunk = { ...chunk, value: { ...value, object } };
1154
+ }
1155
+ }
1156
+ }
1157
+ if (chunk.type === EventType.RUN_FINISHED && chunk.usage) {
1158
+ await this.middlewareRunner.runOnUsage(this.middlewareCtx, chunk.usage);
1159
+ }
1160
+ if (chunk.type === EventType.RUN_ERROR) {
1161
+ this.finalizationError = {
1162
+ message: chunk.message,
1163
+ ...chunk.code ? { code: chunk.code } : {},
1164
+ ...fallbackAdapterError !== void 0 ? { cause: fallbackAdapterError } : {}
1165
+ };
1166
+ }
1167
+ const outputChunks = await this.middlewareRunner.runOnChunk(
1168
+ this.middlewareCtx,
1169
+ outboundChunk
1170
+ );
1171
+ if (this.finalStructuredOutput.yieldChunks) {
1172
+ for (const outputChunk of outputChunks) {
1173
+ if (outputChunk.type === EventType.RUN_ERROR) {
1174
+ runErrorYielded = true;
1175
+ }
1176
+ yield outputChunk;
1177
+ this.middlewareCtx.chunkIndex++;
1178
+ }
1179
+ }
1180
+ if (this.finalizationError) {
1181
+ break;
1182
+ }
1183
+ }
1184
+ if (this.isCancelled()) {
1185
+ return;
1186
+ }
1187
+ if (!this.structuredOutputResult && !this.finalizationError) {
1188
+ this.finalizationError = {
1189
+ message: "missing structured result",
1190
+ code: "structured-output-missing-result"
1191
+ };
1192
+ }
1193
+ if (this.structuredOutputResult && !this.finalizationError && this.finalStructuredOutput.validate) {
1194
+ try {
1195
+ const validated = this.finalStructuredOutput.validate(
1196
+ this.structuredOutputResult.data
1197
+ );
1198
+ this.validatedStructuredOutput = validated;
1199
+ this.hasValidatedStructuredOutput = true;
1200
+ } catch (err) {
1201
+ const message = err instanceof Error ? err.message : String(err);
1202
+ this.finalizationError = {
1203
+ message,
1204
+ code: "structured-output-validation-failed",
1205
+ cause: err
1206
+ };
1207
+ }
1208
+ }
1209
+ if (this.finalizationError && this.finalStructuredOutput.yieldChunks && !runErrorYielded) {
1210
+ if (!startEmitted) {
1211
+ const synthStart = buildSynthesizedStart();
1212
+ const startOutputs = await pipeThroughMiddleware(synthStart);
1213
+ for (const outputChunk of startOutputs) {
1214
+ yield outputChunk;
1215
+ this.middlewareCtx.chunkIndex++;
1216
+ }
1217
+ startEmitted = true;
1218
+ }
1219
+ const errChunk = {
1220
+ type: EventType.RUN_ERROR,
1221
+ runId: this.runIdOverride ?? this.requestId,
1222
+ model: this.params.model,
1223
+ timestamp: Date.now(),
1224
+ threadId: this.threadId,
1225
+ message: this.finalizationError.message,
1226
+ ...this.finalizationError.code ? { code: this.finalizationError.code } : {},
1227
+ error: {
1228
+ message: this.finalizationError.message,
1229
+ ...this.finalizationError.code ? { code: this.finalizationError.code } : {}
1230
+ }
1231
+ };
1232
+ const outputChunks = await this.middlewareRunner.runOnChunk(
1233
+ this.middlewareCtx,
1234
+ errChunk
1235
+ );
1236
+ for (const outputChunk of outputChunks) {
1237
+ yield outputChunk;
1238
+ this.middlewareCtx.chunkIndex++;
1239
+ }
1240
+ }
1241
+ }
1242
+ /**
1243
+ * Native combined mode: harvest the structured output from the agent
1244
+ * loop's accumulated final-turn text (no separate provider call).
1245
+ *
1246
+ * The adapter wired `outputSchema` into the regular `chatStream` request,
1247
+ * so the model's final-turn text is the schema-constrained JSON. We parse
1248
+ * `this.accumulatedContent`, populate `this.structuredOutputResult`, emit
1249
+ * a synthetic `structured-output.complete` (and a `structured-output.start`
1250
+ * if one wasn't emitted earlier — only happens on the streaming path when
1251
+ * the model returned no text at all), and run the validate callback when
1252
+ * present. Failures populate `this.finalizationError` so the engine's
1253
+ * terminal-hook chooser routes to `onError` (per spec §7.3).
1254
+ *
1255
+ * The `'structuredOutput'` middleware phase intentionally does NOT fire on
1256
+ * this path — middleware sees the run through `beforeModel` / `modelStream`
1257
+ * as usual. See PR #605 / issue #605 for the design rationale.
1258
+ */
1259
+ async *harvestCombinedStructuredOutput() {
1260
+ if (!this.finalStructuredOutput) {
1261
+ throw new Error(
1262
+ "harvestCombinedStructuredOutput called without finalStructuredOutput config"
1263
+ );
1264
+ }
1265
+ const yieldChunks = this.finalStructuredOutput.yieldChunks;
1266
+ const rawText = this.accumulatedContent;
1267
+ if (rawText.length === 0) {
1268
+ this.finalizationError = {
1269
+ message: "missing structured result",
1270
+ code: "structured-output-missing-result"
1271
+ };
1272
+ } else {
1273
+ try {
1274
+ const parsed = JSON.parse(rawText);
1275
+ const data = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed) : parsed;
1276
+ this.structuredOutputResult = { data, rawText };
1277
+ } catch (err) {
1278
+ const detail = rawText.slice(0, 200) + (rawText.length > 200 ? "..." : "");
1279
+ this.finalizationError = {
1280
+ message: `Failed to parse structured output as JSON. Content: ${detail}`,
1281
+ code: "structured-output-parse-failed",
1282
+ cause: err
1283
+ };
1284
+ }
1285
+ }
1286
+ if (this.structuredOutputResult && !this.finalizationError && this.finalStructuredOutput.validate) {
1287
+ try {
1288
+ const validated = this.finalStructuredOutput.validate(
1289
+ this.structuredOutputResult.data
1290
+ );
1291
+ this.validatedStructuredOutput = validated;
1292
+ this.hasValidatedStructuredOutput = true;
1293
+ } catch (err) {
1294
+ const message = err instanceof Error ? err.message : String(err);
1295
+ this.finalizationError = {
1296
+ message,
1297
+ code: "structured-output-validation-failed",
1298
+ cause: err
1299
+ };
1300
+ }
1301
+ }
1302
+ if (!yieldChunks) {
1303
+ return;
1304
+ }
1305
+ if (!this.combinedStartEmitted) {
1306
+ this.combinedStartEmitted = true;
1307
+ const messageId = this.combinedStructuredMessageId ?? generateMessageId();
1308
+ this.combinedStructuredMessageId = messageId;
1309
+ const synthStart = {
1310
+ type: EventType.CUSTOM,
1311
+ name: "structured-output.start",
1312
+ value: { messageId },
1313
+ model: this.params.model,
1314
+ timestamp: Date.now(),
1315
+ threadId: this.threadId,
1316
+ ...this.runIdOverride ? { runId: this.runIdOverride } : {}
1317
+ };
1318
+ const startOutputs = await this.middlewareRunner.runOnChunk(
1319
+ this.middlewareCtx,
1320
+ synthStart
1321
+ );
1322
+ for (const outputChunk of startOutputs) {
1323
+ yield outputChunk;
1324
+ this.middlewareCtx.chunkIndex++;
1325
+ }
1326
+ }
1327
+ if (this.structuredOutputResult && !this.finalizationError) {
1328
+ const completeChunk = {
1329
+ type: EventType.CUSTOM,
1330
+ name: "structured-output.complete",
1331
+ value: {
1332
+ object: this.structuredOutputResult.data,
1333
+ raw: this.structuredOutputResult.rawText,
1334
+ ...this.combinedStructuredMessageId ? { messageId: this.combinedStructuredMessageId } : {}
1335
+ },
1336
+ model: this.params.model,
1337
+ timestamp: Date.now(),
1338
+ threadId: this.threadId,
1339
+ ...this.runIdOverride ? { runId: this.runIdOverride } : {}
1340
+ };
1341
+ const completeOutputs = await this.middlewareRunner.runOnChunk(
1342
+ this.middlewareCtx,
1343
+ completeChunk
1344
+ );
1345
+ for (const outputChunk of completeOutputs) {
1346
+ yield outputChunk;
1347
+ this.middlewareCtx.chunkIndex++;
1348
+ }
1349
+ }
1350
+ if (this.finalizationError) {
1351
+ const errChunk = {
1352
+ type: EventType.RUN_ERROR,
1353
+ runId: this.runIdOverride ?? this.requestId,
1354
+ model: this.params.model,
1355
+ timestamp: Date.now(),
1356
+ threadId: this.threadId,
1357
+ message: this.finalizationError.message,
1358
+ ...this.finalizationError.code ? { code: this.finalizationError.code } : {},
1359
+ error: {
1360
+ message: this.finalizationError.message,
1361
+ ...this.finalizationError.code ? { code: this.finalizationError.code } : {}
1362
+ }
1363
+ };
1364
+ const errOutputs = await this.middlewareRunner.runOnChunk(
1365
+ this.middlewareCtx,
1366
+ errChunk
1367
+ );
1368
+ for (const outputChunk of errOutputs) {
1369
+ yield outputChunk;
1370
+ this.middlewareCtx.chunkIndex++;
1371
+ }
1372
+ }
1373
+ }
1374
+ buildMiddlewareConfig() {
1375
+ return {
1376
+ messages: this.messages,
1377
+ systemPrompts: [...this.systemPrompts],
1378
+ tools: [...this.tools],
1379
+ metadata: this.params.metadata,
1380
+ modelOptions: this.params.modelOptions
1381
+ };
1382
+ }
1383
+ applyMiddlewareConfig(config) {
1384
+ this.messages = config.messages;
1385
+ this.systemPrompts = config.systemPrompts;
1386
+ this.tools = config.tools;
1387
+ this.params = {
1388
+ ...this.params,
1389
+ metadata: config.metadata,
1390
+ modelOptions: config.modelOptions
1391
+ };
1392
+ this.middlewareCtx.messages = this.messages;
1393
+ this.middlewareCtx.systemPrompts = this.systemPrompts;
1394
+ this.middlewareCtx.hasTools = this.tools.length > 0;
1395
+ this.middlewareCtx.toolNames = this.tools.map((t) => t.name);
1396
+ this.middlewareCtx.modelOptions = config.modelOptions;
1397
+ }
1398
+ setToolPhase(phase) {
1399
+ this.toolPhase = phase;
1400
+ }
1401
+ /**
1402
+ * Pipe a single chunk through the middleware pipeline (strip-to-spec, devtools, etc.)
1403
+ * and yield all resulting output chunks.
1404
+ */
1405
+ async *pipeThroughMiddleware(chunk) {
1406
+ const outputChunks = await this.middlewareRunner.runOnChunk(
1407
+ this.middlewareCtx,
1408
+ chunk
1409
+ );
1410
+ for (const outputChunk of outputChunks) {
1411
+ yield outputChunk;
1412
+ this.middlewareCtx.chunkIndex++;
1413
+ }
1414
+ }
1415
+ /**
1416
+ * Drain an executeToolCalls async generator, yielding any CustomEvent chunks
1417
+ * through the middleware pipeline and returning the final ExecuteToolCallsResult.
1418
+ */
1419
+ async *drainToolCallGenerator(generator) {
1420
+ let next = await generator.next();
1421
+ while (!next.done) {
1422
+ yield* this.pipeThroughMiddleware(next.value);
1423
+ next = await generator.next();
1424
+ }
1425
+ return next.value;
1426
+ }
1427
+ createCustomEventChunk(eventName, value) {
1428
+ return {
1429
+ type: "CUSTOM",
1430
+ timestamp: Date.now(),
1431
+ model: this.params.model,
1432
+ name: eventName,
1433
+ value
1434
+ };
1435
+ }
1436
+ createId(prefix) {
1437
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
1438
+ }
1439
+ }
1440
+ function chat(options) {
1441
+ validateCapabilities(options.middleware ?? [], options.adapter);
1442
+ const { outputSchema, stream } = options;
1443
+ if (outputSchema && stream === true) {
1444
+ return runStreamingStructuredOutput({
1445
+ ...options,
1446
+ outputSchema,
1447
+ stream
1448
+ });
1449
+ }
1450
+ if (outputSchema) {
1451
+ return runAgenticStructuredOutput({
1452
+ ...options,
1453
+ outputSchema
1454
+ });
1455
+ }
1456
+ if (stream === false) {
1457
+ return runNonStreamingText({
1458
+ ...options,
1459
+ outputSchema: void 0,
1460
+ stream
1461
+ });
1462
+ }
1463
+ return runStreamingText({
1464
+ ...options,
1465
+ outputSchema: void 0,
1466
+ stream
1467
+ });
1468
+ }
1469
+ async function* runStreamingText(options) {
1470
+ const { adapter, middleware, context, debug, mcp, ...textOptions } = options;
1471
+ const model = adapter.model;
1472
+ const logger = resolveDebugOption(debug);
1473
+ const mcpManager = MCPManager.from(mcp);
1474
+ const mcpTools = await mcpManager.discover();
1475
+ if (mcpTools.length > 0) {
1476
+ textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
1477
+ }
1478
+ const engine = new TextEngine(
1479
+ {
1480
+ adapter,
1481
+ params: { ...textOptions, model, logger },
1482
+ middleware,
1483
+ context
1484
+ },
1485
+ logger
1486
+ );
1487
+ try {
1488
+ for await (const chunk of engine.run()) {
1489
+ yield chunk;
1490
+ }
1491
+ } finally {
1492
+ await mcpManager.dispose();
1493
+ }
1494
+ }
1495
+ function runNonStreamingText(options) {
1496
+ const stream = runStreamingText(
1497
+ // eslint-disable-next-line no-restricted-syntax -- generic-stream remap: caller is non-streaming (false), but runStreamingText is invoked internally to collect text; concrete `false`→`true` literals don't structurally overlap.
1498
+ options
1499
+ );
1500
+ return streamToText(stream);
1501
+ }
1502
+ async function runAgenticStructuredOutput(options) {
1503
+ const {
1504
+ adapter,
1505
+ outputSchema,
1506
+ middleware,
1507
+ context,
1508
+ debug,
1509
+ mcp,
1510
+ ...textOptions
1511
+ } = options;
1512
+ const model = adapter.model;
1513
+ const logger = resolveDebugOption(debug);
1514
+ if (!outputSchema) {
1515
+ throw new Error("outputSchema is required for structured output");
1516
+ }
1517
+ const { jsonSchema, nullWideningMap } = convertSchemaForStructuredOutput(outputSchema);
1518
+ if (!jsonSchema) {
1519
+ throw new Error("Failed to convert output schema to JSON Schema");
1520
+ }
1521
+ const normalize = (data) => undoNullWidening(data, nullWideningMap);
1522
+ const validate = isStandardSchema(outputSchema) ? (data) => parseWithStandardSchema(outputSchema, data) : void 0;
1523
+ const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
1524
+ const mcpManager = MCPManager.from(mcp);
1525
+ const mcpTools = await mcpManager.discover();
1526
+ if (mcpTools.length > 0) {
1527
+ textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
1528
+ }
1529
+ const engine = new TextEngine(
1530
+ {
1531
+ adapter,
1532
+ params: { ...textOptions, model, logger },
1533
+ middleware,
1534
+ context,
1535
+ finalStructuredOutput: {
1536
+ jsonSchema,
1537
+ yieldChunks: false,
1538
+ normalize,
1539
+ ...validate ? { validate } : {},
1540
+ ...nativeCombined ? { nativeCombined: true } : {}
1541
+ }
1542
+ },
1543
+ logger
1544
+ );
1545
+ try {
1546
+ for await (const _chunk of engine.run()) {
1547
+ }
1548
+ } finally {
1549
+ await mcpManager.dispose();
1550
+ }
1551
+ const finalizationError = engine.getFinalizationError();
1552
+ if (finalizationError) {
1553
+ const err = new Error(
1554
+ finalizationError.message,
1555
+ finalizationError.cause !== void 0 ? { cause: finalizationError.cause } : void 0
1556
+ );
1557
+ if (finalizationError.code !== void 0) {
1558
+ Object.defineProperty(err, "code", {
1559
+ value: finalizationError.code,
1560
+ enumerable: true
1561
+ });
1562
+ }
1563
+ throw err;
1564
+ }
1565
+ const validated = engine.getValidatedStructuredOutput();
1566
+ if (validated) {
1567
+ return validated.value;
1568
+ }
1569
+ const result = engine.getStructuredOutputResult();
1570
+ if (!result) {
1571
+ throw new Error("structured output finalization produced no result");
1572
+ }
1573
+ return result.data;
1574
+ }
1575
+ function readStructuredOutputCompleteValue(value) {
1576
+ if (typeof value !== "object" || value === null) return null;
1577
+ if (!("object" in value) || !("raw" in value)) return null;
1578
+ const raw = value.raw;
1579
+ if (typeof raw !== "string") return null;
1580
+ const reasoningField = value.reasoning;
1581
+ const reasoning = typeof reasoningField === "string" ? reasoningField : void 0;
1582
+ return {
1583
+ object: value.object,
1584
+ raw,
1585
+ ...reasoning !== void 0 ? { reasoning } : {}
1586
+ };
1587
+ }
1588
+ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError) {
1589
+ const { chatOptions } = options;
1590
+ const fallbackRand = Math.random().toString(36).slice(2);
1591
+ const runId = chatOptions.runId ?? `fallback-${Date.now()}-${fallbackRand}`;
1592
+ const threadId = chatOptions.threadId ?? `fallback-${Date.now()}-${fallbackRand}`;
1593
+ const messageId = `fallback-${Date.now()}-${fallbackRand}`;
1594
+ const model = chatOptions.model;
1595
+ const timestamp = Date.now();
1596
+ yield {
1597
+ type: EventType.RUN_STARTED,
1598
+ runId,
1599
+ threadId,
1600
+ model,
1601
+ timestamp
1602
+ };
1603
+ let result;
1604
+ try {
1605
+ result = await adapter.structuredOutput(options);
1606
+ } catch (error) {
1607
+ onAdapterError?.(error);
1608
+ const message = error instanceof Error ? error.message : String(error);
1609
+ yield {
1610
+ type: EventType.RUN_ERROR,
1611
+ runId,
1612
+ threadId,
1613
+ model,
1614
+ timestamp,
1615
+ message,
1616
+ error: { message }
1617
+ };
1618
+ return;
1619
+ }
1620
+ yield {
1621
+ type: EventType.TEXT_MESSAGE_START,
1622
+ messageId,
1623
+ role: "assistant",
1624
+ model,
1625
+ timestamp
1626
+ };
1627
+ yield {
1628
+ type: EventType.TEXT_MESSAGE_CONTENT,
1629
+ messageId,
1630
+ delta: result.rawText,
1631
+ model,
1632
+ timestamp
1633
+ };
1634
+ yield {
1635
+ type: EventType.TEXT_MESSAGE_END,
1636
+ messageId,
1637
+ model,
1638
+ timestamp
1639
+ };
1640
+ yield {
1641
+ type: EventType.CUSTOM,
1642
+ name: "structured-output.complete",
1643
+ value: { object: result.data, raw: result.rawText },
1644
+ model,
1645
+ timestamp
1646
+ };
1647
+ yield {
1648
+ type: EventType.RUN_FINISHED,
1649
+ runId,
1650
+ threadId,
1651
+ model,
1652
+ timestamp,
1653
+ finishReason: "stop",
1654
+ // Forward adapter-reported token usage so consumers reading
1655
+ // `RUN_FINISHED.usage` (and the engine's `runOnUsage` middleware hook) see
1656
+ // it on the fallback path, mirroring the native streaming path. The
1657
+ // conditional spread avoids emitting `usage: undefined` for adapters that
1658
+ // don't report it. See #758.
1659
+ ...result.usage ? { usage: result.usage } : {}
1660
+ };
1661
+ }
1662
+ function runStreamingStructuredOutput(options) {
1663
+ const { outputSchema } = options;
1664
+ if (!outputSchema) {
1665
+ throw new Error("outputSchema is required for streaming structured output");
1666
+ }
1667
+ const { jsonSchema, nullWideningMap } = convertSchemaForStructuredOutput(outputSchema);
1668
+ if (!jsonSchema) {
1669
+ throw new Error("Failed to convert output schema to JSON Schema");
1670
+ }
1671
+ const normalize = (data) => undoNullWidening(data, nullWideningMap);
1672
+ return runStreamingStructuredOutputImpl(
1673
+ options,
1674
+ jsonSchema,
1675
+ normalize
1676
+ );
1677
+ }
1678
+ async function* runStreamingStructuredOutputImpl(options, jsonSchema, normalize) {
1679
+ const {
1680
+ adapter,
1681
+ outputSchema,
1682
+ middleware,
1683
+ context,
1684
+ debug,
1685
+ mcp,
1686
+ ...textOptions
1687
+ } = options;
1688
+ const model = adapter.model;
1689
+ const logger = resolveDebugOption(debug);
1690
+ const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
1691
+ const mcpManager = MCPManager.from(mcp);
1692
+ const mcpTools = await mcpManager.discover();
1693
+ if (mcpTools.length > 0) {
1694
+ textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
1695
+ }
1696
+ const engine = new TextEngine(
1697
+ {
1698
+ adapter,
1699
+ params: { ...textOptions, model, logger },
1700
+ middleware,
1701
+ context,
1702
+ finalStructuredOutput: {
1703
+ jsonSchema,
1704
+ yieldChunks: true,
1705
+ normalize,
1706
+ ...nativeCombined ? { nativeCombined: true } : {}
1707
+ }
1708
+ },
1709
+ logger
1710
+ );
1711
+ try {
1712
+ for await (const chunk of engine.run()) {
1713
+ yield chunk;
1714
+ }
1715
+ } finally {
1716
+ await mcpManager.dispose();
1717
+ }
1718
+ }
1719
+ export {
1720
+ chat,
1721
+ createChatOptions,
1722
+ kind
1723
+ };
1724
+ //# sourceMappingURL=index.js.map