@statelyai/agent 2.0.0-alpha.28 → 2.0.0-alpha.30

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 (80) hide show
  1. package/dist/ai-sdk.cjs +150 -27
  2. package/dist/ai-sdk.d.cts +59 -36
  3. package/dist/ai-sdk.d.mts +59 -36
  4. package/dist/ai-sdk.mjs +148 -24
  5. package/dist/{decision-RQ0iv0oZ.mjs → decision-BHkvuImD.mjs} +74 -8
  6. package/dist/{decision-CmM8-HHz.cjs → decision-CIb7ddIv.cjs} +113 -30
  7. package/dist/{text-logic-f5F5sCdc.cjs → event-log-BZRfBRd7.cjs} +814 -7
  8. package/dist/{text-logic-CIqHAmBi.mjs → event-log-DJCMHek_.mjs} +680 -4
  9. package/dist/{event-log-store-BIPOz-di.d.mts → event-log-store-CIIJohjj.d.mts} +90 -5
  10. package/dist/{event-log-store-2xzFmgr2.d.cts → event-log-store-CLAC9q2Y.d.cts} +90 -5
  11. package/dist/event-log-store-DZR4IIWe.cjs +95 -0
  12. package/dist/event-log-store-DsP6GCRh.mjs +84 -0
  13. package/dist/index.cjs +576 -58
  14. package/dist/index.d.cts +104 -510
  15. package/dist/index.d.mts +104 -510
  16. package/dist/index.mjs +547 -32
  17. package/dist/log.cjs +77 -20
  18. package/dist/log.d.cts +5 -5
  19. package/dist/log.d.mts +5 -5
  20. package/dist/log.mjs +58 -3
  21. package/dist/machines.cjs +1 -1
  22. package/dist/machines.d.cts +15 -15
  23. package/dist/machines.d.mts +15 -15
  24. package/dist/machines.mjs +1 -1
  25. package/dist/model-source-BCKg4qJ1.cjs +44 -0
  26. package/dist/model-source-BaHwJ1wK.mjs +39 -0
  27. package/dist/openai.cjs +11 -11
  28. package/dist/openai.d.cts +28 -26
  29. package/dist/openai.d.mts +28 -26
  30. package/dist/openai.mjs +3 -3
  31. package/dist/otel.cjs +5 -4
  32. package/dist/otel.d.cts +5 -7
  33. package/dist/otel.d.mts +5 -7
  34. package/dist/otel.mjs +5 -3
  35. package/dist/{run-agent-xNTxpt82.mjs → run-agent-BpHqkaZw.mjs} +181 -44
  36. package/dist/{run-agent-COXXc0RW.d.mts → run-agent-CAVt5R-r.d.mts} +95 -30
  37. package/dist/{run-agent-ByO-Bxyd.d.cts → run-agent-CTlMc3Jb.d.cts} +95 -30
  38. package/dist/{run-agent-Dmf8GV2d.cjs → run-agent-ctLCvNgL.cjs} +230 -88
  39. package/dist/{setup-agent-BuanDXeh.mjs → setup-agent-7rV_cujV.mjs} +15 -9
  40. package/dist/setup-agent-CGELn1qZ.d.mts +577 -0
  41. package/dist/setup-agent-CLPCkXsu.d.cts +577 -0
  42. package/dist/{setup-agent-DV4cTou-.cjs → setup-agent-DoUk7EdC.cjs} +31 -20
  43. package/dist/tanstack-ai.cjs +627 -0
  44. package/dist/tanstack-ai.d.cts +204 -0
  45. package/dist/tanstack-ai.d.mts +204 -0
  46. package/dist/tanstack-ai.mjs +618 -0
  47. package/dist/testing.cjs +204 -985
  48. package/dist/testing.d.cts +132 -347
  49. package/dist/testing.d.mts +132 -347
  50. package/dist/testing.mjs +191 -973
  51. package/dist/{text-logic-IjfYy9DP.d.cts → text-logic-BRYOPWvY.d.mts} +52 -12
  52. package/dist/{text-logic-D-czVPYp.d.mts → text-logic-DmciuVUQ.d.cts} +52 -12
  53. package/dist/{types-BgNbNKnJ.d.mts → types-Dp5_Ru4R.d.cts} +2 -2
  54. package/dist/{types-hR5f5YXa.d.cts → types-Dp5_Ru4R.d.mts} +2 -2
  55. package/dist/usage-BkM3z02Y.d.mts +17 -0
  56. package/dist/usage-Cm_ovz9V.d.cts +17 -0
  57. package/dist/{utils-_q7F-AZu.d.mts → utils-BnXdWih3.d.mts} +1 -2
  58. package/dist/{utils-o4YRdHJX.d.cts → utils-ebVpCYoF.d.cts} +1 -2
  59. package/dist/validate.cjs +239 -8
  60. package/dist/validate.d.cts +110 -3
  61. package/dist/validate.d.mts +110 -3
  62. package/dist/validate.mjs +238 -7
  63. package/dist/verify-COFWyprJ.mjs +1181 -0
  64. package/dist/verify-CVlsTZ-V.cjs +1222 -0
  65. package/dist/verify-Cn86rT8l.d.mts +414 -0
  66. package/dist/verify-Dzwd4Mvk.d.cts +414 -0
  67. package/package.json +44 -31
  68. package/readme.md +40 -28
  69. package/skills/add-evals/SKILL.md +129 -0
  70. package/skills/add-traces/SKILL.md +135 -0
  71. package/skills/generate-machine/SKILL.md +143 -69
  72. package/skills/graph-engineering/SKILL.md +33 -0
  73. package/skills/improve-machine/SKILL.md +241 -0
  74. package/skills/improve-machine/reference.md +101 -0
  75. package/skills/loop-to-machine/SKILL.md +183 -0
  76. package/skills/loop-to-machine/policy.md +158 -0
  77. package/dist/event-log-store-2pAFiWuc.mjs +0 -576
  78. package/dist/event-log-store-C-COhIXP.cjs +0 -702
  79. package/dist/usage-BdEjFuIi.mjs +0 -5
  80. package/dist/usage-efOQPdf_.cjs +0 -10
@@ -0,0 +1,627 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_event_log = require("./event-log-BZRfBRd7.cjs");
3
+ const require_decision = require("./decision-CIb7ddIv.cjs");
4
+ const require_model_source = require("./model-source-BCKg4qJ1.cjs");
5
+ let _tanstack_ai = require("@tanstack/ai");
6
+ //#region src/tanstack-ai/index.ts
7
+ /**
8
+ * The TanStack AI adapter: builds the `{ generateText, streamText, decide }`
9
+ * executor set consumed by `createAgentRuntime`/`executeAgentRequest`, mapped
10
+ * onto TanStack AI's `chat()` activity and any text adapter
11
+ * (`openaiText(...)`, `anthropicText(...)`, `geminiText(...)`, …).
12
+ *
13
+ * `@tanstack/ai` is an OPTIONAL peer dependency, imported only here. No
14
+ * provider package (`@tanstack/ai-openai`, …) is a dependency: the host passes
15
+ * its own text adapters. Compare `@statelyai/agent/ai-sdk`, the same contract
16
+ * over the Vercel AI SDK.
17
+ */
18
+ /** Thrown for a message part TanStack AI's model messages cannot carry —
19
+ * dropping it would silently change what the model sees. */
20
+ function unsupportedPart(role, type) {
21
+ throw new Error(`createTanStackAiExecutors: a ${role} message part of type '${type}' has no TanStack AI equivalent. Send text, image, tool-call, or tool-result parts, or convert it before the request.`);
22
+ }
23
+ /** A `DataContent`/`URL` image as a TanStack AI content source: an http(s)
24
+ * string or `URL` is a `url` source; a `data:` URL, a bare base64 string, and
25
+ * raw bytes are a base64 `data` source. */
26
+ function toImagePart(image, mediaType) {
27
+ const mimeType = mediaType ?? "image/jpeg";
28
+ if (image instanceof URL || typeof image === "string" && /^https?:/.test(image)) return {
29
+ type: "image",
30
+ source: {
31
+ type: "url",
32
+ value: String(image)
33
+ }
34
+ };
35
+ if (typeof image === "string") {
36
+ const dataUrl = /^data:([^;,]+)?(?:;base64)?,(.*)$/.exec(image);
37
+ return {
38
+ type: "image",
39
+ source: {
40
+ type: "data",
41
+ value: dataUrl ? dataUrl[2] : image,
42
+ mimeType: dataUrl?.[1] ?? mimeType
43
+ }
44
+ };
45
+ }
46
+ const bytes = image instanceof Uint8Array ? image : new Uint8Array(image);
47
+ const encode = globalThis.btoa;
48
+ if (!encode) throw new Error("createTanStackAiExecutors: binary image parts need a global `btoa` to base64-encode. Pass the image as a URL or a base64 string instead.");
49
+ let binary = "";
50
+ for (const byte of bytes) binary += String.fromCharCode(byte);
51
+ return {
52
+ type: "image",
53
+ source: {
54
+ type: "data",
55
+ value: encode(binary),
56
+ mimeType
57
+ }
58
+ };
59
+ }
60
+ /** One tool message per `ToolResultPart` — the tool role is one message per result. */
61
+ function toToolMessage(part) {
62
+ return {
63
+ role: "tool",
64
+ toolCallId: part.toolCallId,
65
+ content: part.output.type === "text" || part.output.type === "error-text" ? part.output.value : JSON.stringify(part.output.value)
66
+ };
67
+ }
68
+ /**
69
+ * Maps `AgentTextRequest.messages`/`system`/`prompt` onto `chat()`'s
70
+ * `systemPrompts` and `messages`. TanStack AI's model messages have no
71
+ * `system` role — a provider adapter may forward one as a user turn — so every
72
+ * system message, and the request's `system`, becomes a system prompt.
73
+ */
74
+ function toTanStackAiMessages(request) {
75
+ const systemPrompts = request.system ? [request.system] : [];
76
+ if (!request.messages) return {
77
+ systemPrompts,
78
+ messages: [{
79
+ role: "user",
80
+ content: request.prompt ?? ""
81
+ }]
82
+ };
83
+ return {
84
+ systemPrompts,
85
+ messages: request.messages.flatMap((message) => {
86
+ switch (message.role) {
87
+ case "system":
88
+ systemPrompts.push(message.content);
89
+ return [];
90
+ case "user":
91
+ if (typeof message.content === "string") return [{
92
+ role: "user",
93
+ content: message.content
94
+ }];
95
+ return [{
96
+ role: "user",
97
+ content: message.content.map((part) => {
98
+ switch (part.type) {
99
+ case "text": return {
100
+ type: "text",
101
+ content: part.text
102
+ };
103
+ case "image": return toImagePart(part.image, part.mediaType);
104
+ default: return unsupportedPart("user", part.type);
105
+ }
106
+ })
107
+ }];
108
+ case "assistant": {
109
+ if (typeof message.content === "string") return [{
110
+ role: "assistant",
111
+ content: message.content
112
+ }];
113
+ let text = "";
114
+ const toolCalls = [];
115
+ const trailing = [];
116
+ for (const part of message.content) switch (part.type) {
117
+ case "text":
118
+ text += part.text;
119
+ break;
120
+ case "tool-call":
121
+ toolCalls.push({
122
+ id: part.toolCallId,
123
+ type: "function",
124
+ function: {
125
+ name: part.toolName,
126
+ arguments: JSON.stringify(part.input ?? {})
127
+ }
128
+ });
129
+ break;
130
+ case "tool-result":
131
+ trailing.push(toToolMessage(part));
132
+ break;
133
+ default: unsupportedPart("assistant", part.type);
134
+ }
135
+ return [{
136
+ role: "assistant",
137
+ content: text || null,
138
+ ...toolCalls.length > 0 ? { toolCalls } : {}
139
+ }, ...trailing];
140
+ }
141
+ case "tool": return message.content.map(toToolMessage);
142
+ }
143
+ })
144
+ };
145
+ }
146
+ /** A tool `description` may be a string or, for an AI SDK v7 tool, a function
147
+ * of the call's context. TanStack AI tools carry the static form. */
148
+ function staticDescription(descriptor) {
149
+ if (typeof descriptor === "function") return "";
150
+ return typeof descriptor.description === "string" ? descriptor.description : "";
151
+ }
152
+ /**
153
+ * One TanStack AI tool per `AgentTools` entry. A tool with an `execute` (or a
154
+ * bare function) is a server tool `chat()` runs in its loop; a tool without one
155
+ * is a client tool, so the run stops at its call and hands the call back.
156
+ * Input schemas travel as JSON Schema.
157
+ */
158
+ function toTanStackAiTools(tools) {
159
+ return Object.entries(tools).flatMap(([name, descriptor]) => {
160
+ if (!descriptor) return [];
161
+ const inputSchema = typeof descriptor === "function" ? void 0 : descriptor.inputSchema;
162
+ const execute = typeof descriptor === "function" ? descriptor : descriptor.execute;
163
+ return [{
164
+ name,
165
+ description: staticDescription(descriptor),
166
+ inputSchema: (require_event_log.isStandardSchema(inputSchema) ? require_event_log.getJsonSchemaSync(inputSchema) : void 0) ?? { type: "object" },
167
+ ...typeof execute === "function" ? { execute: (input) => execute(input) } : {}
168
+ }];
169
+ });
170
+ }
171
+ /** One client tool per candidate decision event: no `execute`, so the run
172
+ * stops at the model's first call — the "tool-per-event" recipe. */
173
+ function toTanStackAiEventTools(events) {
174
+ return events.map((event) => ({
175
+ name: event.toolName,
176
+ description: `Choose the '${event.type}' move.`,
177
+ inputSchema: require_event_log.getJsonSchemaSync(event.inputSchema) ?? { type: "object" }
178
+ }));
179
+ }
180
+ /** Drops keys whose value is `undefined`, so a spread cannot erase what it lands on. */
181
+ function defined(settings) {
182
+ return Object.fromEntries(Object.entries(settings).filter(([, value]) => value !== void 0));
183
+ }
184
+ /**
185
+ * Maps the request's portable generation settings and tool choice onto the
186
+ * `modelOptions` of the adapter's provider. TanStack AI has no portable
187
+ * sampling or tool-choice options — each provider adapter names its own — so
188
+ * the mapping is per provider (`adapter.name`):
189
+ *
190
+ * - `openai` (Responses API): `temperature`, `top_p`, `max_output_tokens`,
191
+ * `tool_choice`. The API has no `top_k`, `seed`, or `stop`; those are dropped.
192
+ * - `anthropic`: `temperature`, `top_p`, `top_k`, `max_tokens`,
193
+ * `stop_sequences`, `tool_choice`. `seed` is dropped.
194
+ * - `gemini`: `temperature`, `topP`, `topK`, `maxOutputTokens`, `seed`,
195
+ * `stopSequences`, `toolConfig.functionCallingConfig`.
196
+ *
197
+ * Any other provider gets none of them: map them yourself through the
198
+ * `settings` option, which receives the request.
199
+ */
200
+ function toTanStackAiModelOptions(adapter, request) {
201
+ const { toolChoice } = request;
202
+ switch (adapter.name) {
203
+ case "openai": return defined({
204
+ temperature: request.temperature,
205
+ top_p: request.topP,
206
+ max_output_tokens: request.maxOutputTokens,
207
+ tool_choice: typeof toolChoice === "object" ? {
208
+ type: "function",
209
+ name: toolChoice.name
210
+ } : toolChoice
211
+ });
212
+ case "anthropic": return defined({
213
+ temperature: request.temperature,
214
+ top_p: request.topP,
215
+ top_k: request.topK,
216
+ max_tokens: request.maxOutputTokens,
217
+ stop_sequences: request.stopSequences,
218
+ tool_choice: typeof toolChoice === "object" ? {
219
+ type: "tool",
220
+ name: toolChoice.name
221
+ } : toolChoice === "required" ? { type: "any" } : toolChoice === void 0 ? void 0 : { type: toolChoice }
222
+ });
223
+ case "gemini": return defined({
224
+ temperature: request.temperature,
225
+ topP: request.topP,
226
+ topK: request.topK,
227
+ maxOutputTokens: request.maxOutputTokens,
228
+ seed: request.seed,
229
+ stopSequences: request.stopSequences,
230
+ toolConfig: toolChoice === void 0 ? void 0 : { functionCallingConfig: typeof toolChoice === "object" ? {
231
+ mode: "ANY",
232
+ allowedFunctionNames: [toolChoice.name]
233
+ } : { mode: toolChoice === "required" ? "ANY" : toolChoice.toUpperCase() } }
234
+ });
235
+ default: return {};
236
+ }
237
+ }
238
+ /**
239
+ * Messages for a decision request, with prior failed `attempts` rendered as
240
+ * appended user messages so retries converge. Mirrors the AI SDK adapter's
241
+ * `toDecisionMessages`.
242
+ */
243
+ function toDecisionMessages(request) {
244
+ const mapped = toTanStackAiMessages(request);
245
+ for (const attempt of require_decision.renderDecisionAttempts(request)) mapped.messages.push({
246
+ role: "user",
247
+ content: attempt.content
248
+ });
249
+ return mapped;
250
+ }
251
+ /**
252
+ * Folds TanStack AI's `TokenUsage` onto the flat {@link AgentCallUsage} field
253
+ * names core aggregates. A field the provider omitted stays omitted, so it
254
+ * never contributes a `0` to a run's partial sums.
255
+ */
256
+ function toAgentCallUsage(usage) {
257
+ if (!usage) return;
258
+ const reasoningTokens = usage.completionTokensDetails?.reasoningTokens;
259
+ const cachedInputTokens = usage.promptTokensDetails?.cachedTokens;
260
+ return defined({
261
+ inputTokens: usage.promptTokens,
262
+ outputTokens: usage.completionTokens,
263
+ totalTokens: usage.totalTokens,
264
+ reasoningTokens,
265
+ cachedInputTokens
266
+ });
267
+ }
268
+ /** Adds two model turns' usage together, field by field. A field neither
269
+ * reported stays absent. */
270
+ function addUsage(total, next) {
271
+ if (!total || !next) return total ?? next;
272
+ const sum = {};
273
+ for (const field of require_event_log.AGENT_USAGE_TOKEN_FIELDS) if (total[field] !== void 0 || next[field] !== void 0) sum[field] = (total[field] ?? 0) + (next[field] ?? 0);
274
+ return sum;
275
+ }
276
+ /**
277
+ * Maps a TanStack AI finish reason onto the portable {@link AgentFinishReason}.
278
+ * Anything unrecognized, including a missing reason, lands on `'other'`.
279
+ */
280
+ function toAgentFinishReason(finishReason) {
281
+ switch (finishReason) {
282
+ case "stop": return "stop";
283
+ case "length": return "length";
284
+ case "tool_calls": return "tool-calls";
285
+ case "content_filter": return "content-filter";
286
+ default: return "other";
287
+ }
288
+ }
289
+ function toToolResultOutput(content) {
290
+ if (typeof content !== "string") return {
291
+ type: "json",
292
+ value: content
293
+ };
294
+ try {
295
+ return {
296
+ type: "json",
297
+ value: JSON.parse(content)
298
+ };
299
+ } catch {
300
+ return {
301
+ type: "text",
302
+ value: content
303
+ };
304
+ }
305
+ }
306
+ /** A `RUN_ERROR` chunk as the `Error` it reports, `code` included. */
307
+ var TanStackAiRunError = class extends Error {
308
+ code;
309
+ constructor(message, code) {
310
+ super(message);
311
+ this.name = "TanStackAiRunError";
312
+ this.code = code;
313
+ }
314
+ };
315
+ /**
316
+ * Reads a `chat()` stream to the end. Text arrives as `TEXT_MESSAGE_CONTENT`
317
+ * deltas; every model turn ends in its own `RUN_FINISHED`, so a tool loop's
318
+ * usage is summed and the last finish reason wins. A provider failure arrives
319
+ * as a `RUN_ERROR` chunk rather than a throw, and is thrown here.
320
+ */
321
+ async function readRun(stream, onDelta) {
322
+ const summary = {
323
+ text: "",
324
+ structured: void 0,
325
+ usage: void 0,
326
+ finishReason: "other",
327
+ toolCalls: [],
328
+ messages: [],
329
+ chunks: []
330
+ };
331
+ const args = /* @__PURE__ */ new Map();
332
+ let assistant;
333
+ let results;
334
+ const flushAssistant = () => {
335
+ if (assistant?.length) summary.messages.push({
336
+ role: "assistant",
337
+ content: assistant
338
+ });
339
+ assistant = void 0;
340
+ };
341
+ const flushResults = () => {
342
+ if (results?.length) summary.messages.push({
343
+ role: "tool",
344
+ content: results
345
+ });
346
+ results = void 0;
347
+ };
348
+ const assistantParts = () => {
349
+ flushResults();
350
+ return assistant ??= [];
351
+ };
352
+ for await (const chunk of stream) {
353
+ summary.chunks.push(chunk);
354
+ switch (chunk.type) {
355
+ case "TEXT_MESSAGE_CONTENT": {
356
+ summary.text += chunk.delta;
357
+ onDelta?.(chunk.delta);
358
+ const parts = assistantParts();
359
+ const last = parts.at(-1);
360
+ if (last?.type === "text") last.text += chunk.delta;
361
+ else parts.push({
362
+ type: "text",
363
+ text: chunk.delta
364
+ });
365
+ break;
366
+ }
367
+ case "TOOL_CALL_START": {
368
+ const part = {
369
+ type: "tool-call",
370
+ toolCallId: chunk.toolCallId,
371
+ toolName: chunk.toolCallName,
372
+ input: {}
373
+ };
374
+ assistantParts().push(part);
375
+ args.set(chunk.toolCallId, {
376
+ part,
377
+ json: ""
378
+ });
379
+ break;
380
+ }
381
+ case "TOOL_CALL_ARGS": {
382
+ const call = args.get(chunk.toolCallId);
383
+ if (call) call.json += chunk.delta;
384
+ break;
385
+ }
386
+ case "TOOL_CALL_RESULT":
387
+ flushAssistant();
388
+ (results ??= []).push({
389
+ type: "tool-result",
390
+ toolCallId: chunk.toolCallId,
391
+ toolName: args.get(chunk.toolCallId)?.part.toolName ?? "",
392
+ output: toToolResultOutput(chunk.content)
393
+ });
394
+ break;
395
+ case "CUSTOM":
396
+ if (chunk.name === "structured-output.complete") summary.structured = chunk.value?.object;
397
+ break;
398
+ case "RUN_FINISHED":
399
+ summary.finishReason = toAgentFinishReason(chunk.finishReason ?? (chunk.metadata?.tanstack)?.finishReason);
400
+ if (chunk.usage && !Array.isArray(chunk.usage)) summary.usage = addUsage(summary.usage, toAgentCallUsage(chunk.usage));
401
+ break;
402
+ case "RUN_ERROR": throw new TanStackAiRunError(chunk.message, chunk.code ?? chunk.error?.code);
403
+ }
404
+ }
405
+ for (const { part, json } of args.values()) part.input = json ? JSON.parse(json) : {};
406
+ flushAssistant();
407
+ flushResults();
408
+ summary.toolCalls = [...args.values()].map(({ part }) => ({
409
+ toolCallId: part.toolCallId,
410
+ toolName: part.toolName,
411
+ input: part.input
412
+ }));
413
+ return summary;
414
+ }
415
+ function isAdapterWithSettings(entry) {
416
+ return "adapter" in entry && !("kind" in entry);
417
+ }
418
+ /** The text adapter inside a map entry, whichever form it takes. */
419
+ function toTextAdapter(entry) {
420
+ return isAdapterWithSettings(entry) ? entry.adapter : entry;
421
+ }
422
+ function truncated(request, info, partialOutput, cause) {
423
+ return new require_event_log.AgentTruncatedError(`createTanStackAiExecutors: request '${request.name ?? "(unnamed)"}' hit the output token limit — its structured output never completed. Raise \`maxOutputTokens\`, shorten the input, or ask for a smaller output schema.`, {
424
+ requestName: request.name ?? "(unnamed)",
425
+ ...info?.requestId !== void 0 ? { requestId: info.requestId } : {},
426
+ ...partialOutput !== void 0 ? { partialOutput } : {},
427
+ ...cause !== void 0 ? { cause } : {}
428
+ });
429
+ }
430
+ function isTruncationError(error) {
431
+ const { code, message } = error ?? {};
432
+ return code === "max_tokens" || code === "incomplete" || message === "max_output_tokens";
433
+ }
434
+ function usageField(usage) {
435
+ return usage ? { usage } : {};
436
+ }
437
+ /**
438
+ * Runs one `chat()` call to the end. `chat()` takes an `AbortController`, not
439
+ * a signal, so the runtime's signal is bridged to one — and unhooked once the
440
+ * run settles, so a long-lived signal does not collect a listener per request.
441
+ * An aborted `chat()` stream just ends, so the abort is surfaced as the throw
442
+ * it is.
443
+ */
444
+ async function runChat(options, signal, onDelta) {
445
+ const controller = new AbortController();
446
+ const abort = () => controller.abort(signal?.reason);
447
+ if (signal?.aborted) abort();
448
+ else signal?.addEventListener("abort", abort, { once: true });
449
+ let run;
450
+ try {
451
+ run = await readRun((0, _tanstack_ai.chat)({
452
+ ...options,
453
+ abortController: controller
454
+ }), onDelta);
455
+ } finally {
456
+ signal?.removeEventListener("abort", abort);
457
+ }
458
+ if (signal?.aborted) throw signal.reason ?? /* @__PURE__ */ new Error("createTanStackAiExecutors: the request was aborted.");
459
+ return run;
460
+ }
461
+ /**
462
+ * A request's tool choice belongs to its first model turn. `chat()` reuses
463
+ * `modelOptions` for every turn of its tool loop, so a forced choice left in
464
+ * place would force another tool call after each result, and the request could
465
+ * never answer. From the second turn on, the choice's keys go back to what the
466
+ * host set, or away.
467
+ */
468
+ function toolChoiceOnFirstTurn(keys, later) {
469
+ return {
470
+ name: "statelyai:tool-choice-first-turn",
471
+ onConfig: (ctx, config) => {
472
+ if (ctx.iteration === 0) return;
473
+ const modelOptions = { ...config.modelOptions };
474
+ for (const key of keys) if (key in later) modelOptions[key] = later[key];
475
+ else delete modelOptions[key];
476
+ return { modelOptions };
477
+ }
478
+ };
479
+ }
480
+ function createTanStackAiExecutors(source) {
481
+ const options = require_model_source.resolveModelSource("createTanStackAiExecutors", source);
482
+ const resolve = (request, portable) => {
483
+ let adapter;
484
+ let entrySettings;
485
+ if (options.resolveModel) adapter = options.resolveModel(request.model);
486
+ else {
487
+ const entry = options.models?.[request.model];
488
+ if (!options.models) throw new Error(`createTanStackAiExecutors: no model resolver configured for '${request.model}'.`);
489
+ if (!entry) throw new Error(`createTanStackAiExecutors: unknown model '${request.model}'.`);
490
+ adapter = toTextAdapter(entry);
491
+ entrySettings = isAdapterWithSettings(entry) ? entry.settings : void 0;
492
+ }
493
+ const host = (typeof options.settings === "function" ? options.settings(request) : options.settings) ?? {};
494
+ const merged = {
495
+ ...host,
496
+ ...entrySettings
497
+ };
498
+ const base = {
499
+ ...host.modelOptions,
500
+ ...entrySettings?.modelOptions
501
+ };
502
+ const generation = toTanStackAiModelOptions(adapter, {
503
+ ...portable,
504
+ toolChoice: void 0
505
+ });
506
+ const firstTurn = toTanStackAiModelOptions(adapter, portable);
507
+ const choiceKeys = Object.keys(firstTurn).filter((key) => !(key in generation));
508
+ return {
509
+ adapter,
510
+ settings: {
511
+ ...merged,
512
+ modelOptions: {
513
+ ...base,
514
+ ...firstTurn
515
+ },
516
+ ...choiceKeys.length > 0 ? { middleware: [...merged.middleware ?? [], toolChoiceOnFirstTurn(choiceKeys, {
517
+ ...base,
518
+ ...generation
519
+ })] } : {}
520
+ }
521
+ };
522
+ };
523
+ const loopBound = (request) => (0, _tanstack_ai.maxIterations)(typeof request.maxSteps === "number" && Number.isFinite(request.maxSteps) ? Math.max(1, Math.floor(request.maxSteps)) : 1);
524
+ const generateText = async (request, info) => {
525
+ const { adapter, settings } = resolve(request, request);
526
+ const tools = toTanStackAiTools(request.tools);
527
+ const structured = require_event_log.getAgentOutputMode(request.outputSchema) === "structured";
528
+ const outputSchema = structured ? await require_event_log.getJsonSchema(require_event_log.providerOutputSchema(request.outputSchema, { reasoning: request.includeReasoning })) : void 0;
529
+ let run;
530
+ try {
531
+ run = await runChat({
532
+ ...settings,
533
+ adapter,
534
+ ...toTanStackAiMessages(request),
535
+ ...tools.length > 0 ? { tools } : {},
536
+ ...outputSchema ? {
537
+ outputSchema,
538
+ stream: true
539
+ } : {},
540
+ agentLoopStrategy: loopBound(request)
541
+ }, info?.signal);
542
+ } catch (error) {
543
+ throw structured && isTruncationError(error) ? truncated(request, info, void 0, error) : error;
544
+ }
545
+ if (outputSchema) {
546
+ if (run.finishReason === "length") throw truncated(request, info, run.structured ?? run.text);
547
+ const parsed = require_event_log.parseProviderOutput(request, run.structured !== void 0 ? run.structured : JSON.parse(run.text));
548
+ return {
549
+ result: parsed.result,
550
+ ...typeof parsed.reasoning === "string" ? { reasoning: parsed.reasoning } : {},
551
+ ...usageField(run.usage),
552
+ finishReason: run.finishReason,
553
+ toolCalls: run.toolCalls,
554
+ messages: run.messages,
555
+ raw: run.chunks
556
+ };
557
+ }
558
+ return {
559
+ result: run.text,
560
+ ...usageField(run.usage),
561
+ finishReason: run.finishReason,
562
+ toolCalls: run.toolCalls,
563
+ messages: run.messages,
564
+ raw: run.chunks
565
+ };
566
+ };
567
+ const streamText = async (request, info) => {
568
+ if (require_event_log.getAgentOutputMode(request.outputSchema) === "structured") throw new Error("createTanStackAiExecutors: streamText streams text — a request declaring a structured output schema must be routed to generateText.");
569
+ const { adapter, settings } = resolve(request, request);
570
+ const tools = toTanStackAiTools(request.tools ?? {});
571
+ const run = await runChat({
572
+ ...settings,
573
+ adapter,
574
+ ...toTanStackAiMessages(request),
575
+ ...tools.length > 0 ? { tools } : {},
576
+ agentLoopStrategy: loopBound(request)
577
+ }, info?.signal, (delta) => info?.onChunk?.(delta));
578
+ return {
579
+ result: run.text,
580
+ ...usageField(run.usage),
581
+ finishReason: run.finishReason,
582
+ messages: run.messages,
583
+ raw: run.chunks
584
+ };
585
+ };
586
+ const decide = async (request, info) => {
587
+ const { adapter, settings } = resolve(request, {
588
+ ...request,
589
+ toolChoice: "required"
590
+ });
591
+ const run = await runChat({
592
+ ...settings,
593
+ adapter,
594
+ ...toDecisionMessages(request),
595
+ tools: toTanStackAiEventTools(request.events),
596
+ agentLoopStrategy: (0, _tanstack_ai.maxIterations)(1)
597
+ }, info?.signal);
598
+ const toolCall = run.toolCalls[0];
599
+ if (!toolCall) throw new Error("createTanStackAiExecutors: decide — model did not call an event tool.");
600
+ const chosenEvent = request.events.find((event) => event.toolName === toolCall.toolName);
601
+ if (!chosenEvent) throw new Error(`createTanStackAiExecutors: decide — model called unknown tool '${toolCall.toolName}'.`);
602
+ return {
603
+ event: {
604
+ ...toolCall.input && typeof toolCall.input === "object" ? toolCall.input : {},
605
+ type: chosenEvent.type
606
+ },
607
+ ...usageField(run.usage),
608
+ finishReason: run.finishReason,
609
+ raw: run.chunks
610
+ };
611
+ };
612
+ return {
613
+ generateText,
614
+ streamText,
615
+ decide
616
+ };
617
+ }
618
+ //#endregion
619
+ exports.createTanStackAiExecutors = createTanStackAiExecutors;
620
+ exports.toAgentCallUsage = toAgentCallUsage;
621
+ exports.toAgentFinishReason = toAgentFinishReason;
622
+ exports.toDecisionMessages = toDecisionMessages;
623
+ exports.toTanStackAiEventTools = toTanStackAiEventTools;
624
+ exports.toTanStackAiMessages = toTanStackAiMessages;
625
+ exports.toTanStackAiModelOptions = toTanStackAiModelOptions;
626
+ exports.toTanStackAiTools = toTanStackAiTools;
627
+ exports.toTextAdapter = toTextAdapter;