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