@yourgpt/llm-sdk 2.6.0-beta.3 → 2.6.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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +46 -10
  3. package/dist/adapters/index.d.mts +11 -13
  4. package/dist/adapters/index.d.ts +11 -13
  5. package/dist/adapters/index.js +327 -100
  6. package/dist/adapters/index.mjs +327 -100
  7. package/dist/audio-K-Z3-Bce.d.mts +191 -0
  8. package/dist/audio-K-Z3-Bce.d.ts +191 -0
  9. package/dist/{base-BICaQaRS.d.mts → base-6LzczuCg.d.ts} +8 -1
  10. package/dist/{base-CpRPB8u8.d.ts → base-DySbTP8C.d.mts} +8 -1
  11. package/dist/decisions-CJzOt87t.d.mts +137 -0
  12. package/dist/decisions-CJzOt87t.d.ts +137 -0
  13. package/dist/fallback/index.d.mts +4 -4
  14. package/dist/fallback/index.d.ts +4 -4
  15. package/dist/http-BQxUT5A9.d.mts +9 -0
  16. package/dist/http-BQxUT5A9.d.ts +9 -0
  17. package/dist/index.d.mts +14 -8
  18. package/dist/index.d.ts +14 -8
  19. package/dist/index.js +599 -195
  20. package/dist/index.mjs +596 -196
  21. package/dist/providers/anthropic/index.d.mts +7 -4
  22. package/dist/providers/anthropic/index.d.ts +7 -4
  23. package/dist/providers/anthropic/index.js +383 -102
  24. package/dist/providers/anthropic/index.mjs +383 -102
  25. package/dist/providers/azure/index.d.mts +3 -3
  26. package/dist/providers/azure/index.d.ts +3 -3
  27. package/dist/providers/cloudflare/index.d.mts +11 -0
  28. package/dist/providers/cloudflare/index.d.ts +11 -0
  29. package/dist/providers/cloudflare/index.js +743 -0
  30. package/dist/providers/cloudflare/index.mjs +741 -0
  31. package/dist/providers/fireworks/index.d.mts +3 -3
  32. package/dist/providers/fireworks/index.d.ts +3 -3
  33. package/dist/providers/fireworks/index.js +108 -59
  34. package/dist/providers/fireworks/index.mjs +108 -59
  35. package/dist/providers/google/index.d.mts +3 -3
  36. package/dist/providers/google/index.d.ts +3 -3
  37. package/dist/providers/google/index.js +108 -59
  38. package/dist/providers/google/index.mjs +108 -59
  39. package/dist/providers/ollama/index.d.mts +4 -4
  40. package/dist/providers/ollama/index.d.ts +4 -4
  41. package/dist/providers/openai/index.d.mts +38 -6
  42. package/dist/providers/openai/index.d.ts +38 -6
  43. package/dist/providers/openai/index.js +1378 -84
  44. package/dist/providers/openai/index.mjs +1376 -85
  45. package/dist/providers/openrouter/index.d.mts +21 -4
  46. package/dist/providers/openrouter/index.d.ts +21 -4
  47. package/dist/providers/openrouter/index.js +967 -130
  48. package/dist/providers/openrouter/index.mjs +967 -131
  49. package/dist/providers/togetherai/index.d.mts +3 -3
  50. package/dist/providers/togetherai/index.d.ts +3 -3
  51. package/dist/providers/togetherai/index.js +108 -59
  52. package/dist/providers/togetherai/index.mjs +108 -59
  53. package/dist/providers/typesafe/index.d.mts +8 -0
  54. package/dist/providers/typesafe/index.d.ts +8 -0
  55. package/dist/providers/typesafe/index.js +735 -0
  56. package/dist/providers/typesafe/index.mjs +733 -0
  57. package/dist/providers/xai/index.d.mts +3 -3
  58. package/dist/providers/xai/index.d.ts +3 -3
  59. package/dist/providers/xai/index.js +108 -59
  60. package/dist/providers/xai/index.mjs +108 -59
  61. package/dist/{types-DCFqkJAy.d.ts → types-B0y6VZSQ.d.ts} +1 -1
  62. package/dist/{types-DHQEMFHA.d.mts → types-BXQMtaCX.d.ts} +17 -3
  63. package/dist/{types-C3RgRbEC.d.mts → types-BY6BVhFg.d.mts} +1 -1
  64. package/dist/{types-Dikj3En4.d.ts → types-C7_qVSSd.d.mts} +17 -3
  65. package/dist/{types-Dg1Bcg1k.d.mts → types-D7lzMMLu.d.mts} +13 -1
  66. package/dist/{types-Dg1Bcg1k.d.ts → types-D7lzMMLu.d.ts} +13 -1
  67. package/dist/yourgpt/index.d.mts +1 -1
  68. package/dist/yourgpt/index.d.ts +1 -1
  69. package/docs/decisions.md +174 -0
  70. package/docs/media.md +201 -0
  71. package/docs/multimodal-and-audio.md +9 -2
  72. package/package.json +34 -12
  73. package/dist/audio-BBSwnjo8.d.mts +0 -92
  74. package/dist/audio-BBSwnjo8.d.ts +0 -92
package/dist/index.mjs CHANGED
@@ -150,7 +150,22 @@ function zodToJsonSchema(schema) {
150
150
  case "ZodString":
151
151
  return { type: "string", description };
152
152
  case "ZodNumber":
153
- return { type: "number", description };
153
+ return {
154
+ type: def.checks?.some(
155
+ (check) => check.kind === "int"
156
+ ) ? "integer" : "number",
157
+ description
158
+ };
159
+ case "ZodNull":
160
+ return { type: "null", description };
161
+ case "ZodAny":
162
+ case "ZodUnknown":
163
+ return { description };
164
+ case "ZodEffects":
165
+ return {
166
+ ...zodToJsonSchema(def.schema),
167
+ ...description ? { description } : {}
168
+ };
154
169
  case "ZodBoolean":
155
170
  return { type: "boolean", description };
156
171
  case "ZodEnum": {
@@ -171,10 +186,13 @@ function zodToJsonSchema(schema) {
171
186
  const properties = {};
172
187
  const required = [];
173
188
  for (const [key, value] of Object.entries(shapeObj)) {
174
- properties[key] = zodToJsonSchema(value);
175
- const valueDef = value._def;
176
- const isOptional = valueDef.typeName === "ZodOptional" || valueDef.typeName === "ZodNullable" || valueDef.typeName === "ZodDefault";
177
- if (!isOptional) {
189
+ Object.defineProperty(properties, key, {
190
+ value: zodToJsonSchema(value),
191
+ enumerable: true,
192
+ configurable: true,
193
+ writable: true
194
+ });
195
+ if (!acceptsMissingInput(value)) {
178
196
  required.push(key);
179
197
  }
180
198
  }
@@ -185,8 +203,13 @@ function zodToJsonSchema(schema) {
185
203
  description
186
204
  };
187
205
  }
188
- case "ZodOptional":
189
206
  case "ZodNullable": {
207
+ return {
208
+ anyOf: [zodToJsonSchema(def.innerType), { type: "null" }],
209
+ description
210
+ };
211
+ }
212
+ case "ZodOptional": {
190
213
  const innerType = def.innerType;
191
214
  return zodToJsonSchema(innerType);
192
215
  }
@@ -196,6 +219,7 @@ function zodToJsonSchema(schema) {
196
219
  }
197
220
  case "ZodLiteral": {
198
221
  const value = def.value;
222
+ if (value === null) return { type: "null", description };
199
223
  if (typeof value === "string") {
200
224
  return { type: "string", enum: [value], description };
201
225
  }
@@ -219,12 +243,21 @@ function zodToJsonSchema(schema) {
219
243
  if (stringLiterals.length === options.length) {
220
244
  return { type: "string", enum: stringLiterals, description };
221
245
  }
222
- return zodToJsonSchema(options[0]);
246
+ return { anyOf: options.map(zodToJsonSchema), description };
223
247
  }
224
248
  default:
225
249
  return { type: "string", description };
226
250
  }
227
251
  }
252
+ function acceptsMissingInput(schema) {
253
+ const def = schema._def;
254
+ if (["ZodOptional", "ZodDefault", "ZodAny", "ZodUnknown"].includes(def.typeName))
255
+ return true;
256
+ if (def.typeName === "ZodNullable") return acceptsMissingInput(def.innerType);
257
+ if (def.typeName === "ZodEffects") return acceptsMissingInput(def.schema);
258
+ if (def.typeName === "ZodUnion") return def.options.some(acceptsMissingInput);
259
+ return false;
260
+ }
228
261
  function formatToolsForOpenAI(tools) {
229
262
  return Object.entries(tools).map(([name, toolDef]) => ({
230
263
  type: "function",
@@ -255,6 +288,27 @@ function formatToolsForGoogle(tools) {
255
288
  }
256
289
 
257
290
  // src/core/provider-error.ts
291
+ function invalidProviderResponse(message) {
292
+ return Object.assign(new Error(message), {
293
+ name: "InvalidProviderResponseError",
294
+ status: 502,
295
+ code: "INVALID_PROVIDER_RESPONSE"
296
+ });
297
+ }
298
+ function providerErrorEvent(error, code) {
299
+ const details = error ?? {};
300
+ const event = {
301
+ type: "error",
302
+ message: error instanceof Error ? error.message : String(error),
303
+ code,
304
+ ...typeof details.status === "number" ? { status: details.status } : {},
305
+ ...typeof details.code === "string" ? { providerCode: details.code } : {},
306
+ ...typeof details.param === "string" ? { param: details.param } : {},
307
+ ...typeof details.request_id === "string" ? { requestId: details.request_id } : {}
308
+ };
309
+ Object.defineProperty(event, "cause", { value: error, enumerable: false });
310
+ return event;
311
+ }
258
312
  function throwIfAborted(signal) {
259
313
  if (!signal?.aborted) return;
260
314
  const error = new Error("The operation was aborted");
@@ -262,9 +316,42 @@ function throwIfAborted(signal) {
262
316
  throw error;
263
317
  }
264
318
 
319
+ // src/core/tool-validation.ts
320
+ function parseToolArguments(value) {
321
+ let args = value;
322
+ if (typeof value === "string") {
323
+ try {
324
+ args = JSON.parse(value || "{}");
325
+ } catch {
326
+ throw invalidProviderResponse("Tool arguments are not valid JSON");
327
+ }
328
+ }
329
+ if (args === null || typeof args !== "object" || Array.isArray(args))
330
+ throw invalidProviderResponse("Tool arguments must be a JSON object");
331
+ return args;
332
+ }
333
+ function validateToolCalls(calls) {
334
+ const ids = /* @__PURE__ */ new Set();
335
+ for (const call of calls) {
336
+ if (!call || typeof call.id !== "string" || !call.id || typeof call.name !== "string" || !call.name || ids.has(call.id))
337
+ throw invalidProviderResponse(
338
+ "Tool calls have missing or duplicate identities"
339
+ );
340
+ ids.add(call.id);
341
+ if (typeof call.args === "string")
342
+ throw invalidProviderResponse("Parsed tool arguments must be an object");
343
+ parseToolArguments(call.args);
344
+ }
345
+ }
346
+ function validateMaxSteps(value) {
347
+ if (!Number.isSafeInteger(value) || value < 1)
348
+ throw new Error("maxSteps must be a positive safe integer");
349
+ }
350
+
265
351
  // src/core/generate-text.ts
266
352
  async function generateText(params) {
267
353
  const { model, tools, maxSteps = 1, signal } = params;
354
+ validateMaxSteps(maxSteps);
268
355
  if (params.responseFormat && model.capabilities.supportsJsonMode === false) {
269
356
  console.warn(
270
357
  `[llm-sdk] ${model.provider}/${model.modelId} does not support structured output (responseFormat); the request will be sent but the provider may ignore it.`
@@ -289,12 +376,13 @@ async function generateText(params) {
289
376
  });
290
377
  throwIfAborted(signal);
291
378
  const toolCalls = result.toolCalls ?? [];
379
+ validateToolCalls(toolCalls);
380
+ allToolCalls.push(...toolCalls);
292
381
  const stepToolResults = [];
293
382
  if (toolCalls.length > 0 && tools) {
294
- allToolCalls.push(...toolCalls);
295
383
  for (const call of toolCalls) {
296
384
  throwIfAborted(signal);
297
- const toolDef = tools[call.name];
385
+ const toolDef = Object.prototype.hasOwnProperty.call(tools, call.name) ? tools[call.name] : void 0;
298
386
  if (!toolDef) {
299
387
  const errorResult = {
300
388
  toolCallId: call.id,
@@ -337,6 +425,7 @@ async function generateText(params) {
337
425
  }
338
426
  steps.push({
339
427
  text: result.text,
428
+ ...result.reasoningText !== void 0 ? { reasoningText: result.reasoningText } : {},
340
429
  toolCalls,
341
430
  toolResults: stepToolResults,
342
431
  finishReason: result.finishReason,
@@ -350,12 +439,12 @@ async function generateText(params) {
350
439
  ...result.providerMetadata ? { providerMetadata: responseReplayMetadata(result.providerMetadata) } : {}
351
440
  };
352
441
  messages = [...messages, assistantMessage];
353
- if (toolCalls.length === 0) break;
442
+ if (toolCalls.length === 0 || !tools) break;
354
443
  for (const tr of stepToolResults) {
355
444
  const toolMessage = {
356
445
  role: "tool",
357
446
  toolCallId: tr.toolCallId,
358
- content: tr.modelOutput ? "[Tool returned content]" : JSON.stringify(tr.result),
447
+ content: tr.modelOutput ? "[Tool returned content]" : JSON.stringify(tr.result ?? null),
359
448
  ...tr.modelOutput ? { modelOutput: tr.modelOutput } : {}
360
449
  };
361
450
  messages = [...messages, toolMessage];
@@ -364,6 +453,7 @@ async function generateText(params) {
364
453
  const lastStep = steps[steps.length - 1];
365
454
  return {
366
455
  text: lastStep?.text ?? "",
456
+ ...lastStep?.reasoningText !== void 0 ? { reasoningText: lastStep.reasoningText } : {},
367
457
  providerMetadata: lastStep?.providerMetadata,
368
458
  usage: sumUsage(steps),
369
459
  finishReason: lastStep?.finishReason ?? "stop",
@@ -410,9 +500,250 @@ function sumUsage(steps) {
410
500
  );
411
501
  }
412
502
 
503
+ // src/core/image.ts
504
+ async function generateImage({
505
+ model,
506
+ ...options
507
+ }) {
508
+ options.signal?.throwIfAborted();
509
+ if (typeof options.prompt !== "string" || !options.prompt.trim())
510
+ throw new MediaAPIError("Image prompt must not be empty", "invalid_input");
511
+ const result = await model.doGenerate(options);
512
+ options.signal?.throwIfAborted();
513
+ if (!result.images?.length || result.images.some(
514
+ (image) => !(image.uint8Array instanceof Uint8Array) || !image.uint8Array.length
515
+ ))
516
+ throw new MediaAPIError(
517
+ "Image provider returned empty images",
518
+ "invalid_response"
519
+ );
520
+ return result;
521
+ }
522
+ var marker = /* @__PURE__ */ Symbol.for("@yourgpt/llm-sdk/MediaAPIError");
523
+ var _a;
524
+ var MediaAPIError = class extends Error {
525
+ constructor(message, code, status, requestId) {
526
+ super(message);
527
+ this.code = code;
528
+ this.status = status;
529
+ this.requestId = requestId;
530
+ this[_a] = true;
531
+ this.name = "MediaAPIError";
532
+ }
533
+ static [(_a = marker, Symbol.hasInstance)](value) {
534
+ return !!value && typeof value === "object" && value[marker] === true;
535
+ }
536
+ };
537
+
538
+ // src/core/decision-validation.ts
539
+ function decisionInputError(message) {
540
+ throw new DecisionAPIError(message, "invalid_input");
541
+ }
542
+ function decisionResponseError(message) {
543
+ throw new DecisionAPIError(message, "invalid_response");
544
+ }
545
+ function isDecisionRecord(value) {
546
+ if (value === null || typeof value !== "object" || Array.isArray(value))
547
+ return false;
548
+ const proto = Object.getPrototypeOf(value);
549
+ return proto === Object.prototype || proto === null;
550
+ }
551
+ function json(value, seen = /* @__PURE__ */ new Set(), depth = 0) {
552
+ if (depth > 64) return false;
553
+ if (value === null || typeof value === "string" || typeof value === "boolean")
554
+ return true;
555
+ if (typeof value === "number") return Number.isFinite(value);
556
+ if (typeof value !== "object" || !Array.isArray(value) && !isDecisionRecord(value) || seen.has(value))
557
+ return false;
558
+ seen.add(value);
559
+ const valid = Object.getOwnPropertySymbols(value).length === 0 && (Array.isArray(value) ? Array.from(value) : Object.values(value)).every(
560
+ (item) => json(item, seen, depth + 1)
561
+ );
562
+ seen.delete(value);
563
+ return valid;
564
+ }
565
+ function input(value) {
566
+ return (typeof value === "string" || Array.isArray(value) || isDecisionRecord(value)) && json(value);
567
+ }
568
+ function assertDecisionKeys(value, allowed, label) {
569
+ if (Object.keys(value).some((key) => !allowed.includes(key)))
570
+ decisionInputError(`${label} contains an unsupported field`);
571
+ }
572
+ function validateDecisionInput(options) {
573
+ if (!isDecisionRecord(options))
574
+ decisionInputError("Decisions options must be an object");
575
+ assertDecisionKeys(
576
+ options,
577
+ ["state", "questions", "images", "signal", "timeoutMs", "providerOptions"],
578
+ "Decisions options"
579
+ );
580
+ if (!input(options.state))
581
+ decisionInputError(
582
+ "state must be finite, acyclic JSON text, object, or array (maximum depth 64)"
583
+ );
584
+ if (!isDecisionRecord(options.questions) || Object.keys(options.questions).length === 0)
585
+ decisionInputError("questions must be a nonempty map");
586
+ for (const [id, question] of Object.entries(options.questions)) {
587
+ if (!id.length || !isDecisionRecord(question) || !input(question.instructions))
588
+ decisionInputError(
589
+ "Every question needs a nonempty ID and JSON-compatible instructions"
590
+ );
591
+ assertDecisionKeys(
592
+ question,
593
+ ["type", "instructions", "criteria"],
594
+ "Question"
595
+ );
596
+ if (question.type === "boolean") {
597
+ if (question.criteria !== void 0 && (!isDecisionRecord(question.criteria) || Object.entries(question.criteria).some(
598
+ ([key, value]) => !["true", "false"].includes(key) || !input(value)
599
+ )))
600
+ decisionInputError("Boolean criteria may describe only true and false");
601
+ } else if (question.type === "choice") {
602
+ if (!isDecisionRecord(question.criteria) || Object.keys(question.criteria).length < 2 || Object.keys(question.criteria).length > 255 || Object.entries(question.criteria).some(
603
+ ([key, value]) => !key.length || value !== null && !input(value)
604
+ ))
605
+ decisionInputError(
606
+ "Choice criteria require 2\u2013255 named options with JSON descriptions or null"
607
+ );
608
+ } else if (question.type === "score") {
609
+ if (!Array.isArray(question.criteria) || question.criteria.length < 2 || !Array.from(question.criteria).every(input))
610
+ decisionInputError(
611
+ "Score criteria require at least two ordered JSON descriptions"
612
+ );
613
+ } else
614
+ decisionInputError("Question type must be boolean, choice, or score");
615
+ }
616
+ if (options.timeoutMs !== void 0 && (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 2147483647))
617
+ decisionInputError("timeoutMs must be a positive 32-bit integer");
618
+ if (options.providerOptions !== void 0 && (!isDecisionRecord(options.providerOptions) || !json(options.providerOptions)))
619
+ decisionInputError("providerOptions must be a JSON object");
620
+ if (options.images !== void 0 && (!Array.isArray(options.images) || !Array.from(options.images).every(
621
+ (image) => isDecisionRecord(image) && image.data instanceof Uint8Array
622
+ )))
623
+ decisionInputError(
624
+ "Decision images must contain encoded bytes; hosted image URLs are unsupported"
625
+ );
626
+ }
627
+ function probability(value) {
628
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
629
+ }
630
+ function exactKeys(value, keys) {
631
+ return Object.keys(value).length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
632
+ }
633
+ function validateDecisionResult(questions, result) {
634
+ if (!isDecisionRecord(result) || !isDecisionRecord(result.answers) || !exactKeys(result.answers, Object.keys(questions)))
635
+ decisionResponseError(
636
+ "Decisions must return exactly one answer per question"
637
+ );
638
+ const rounding = result.rounding;
639
+ let pError = 0, sError = 0;
640
+ if (rounding !== void 0) {
641
+ if (!isDecisionRecord(rounding) || ![rounding.probabilityDecimals, rounding.scoreDecimals].every(
642
+ (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 15
643
+ ))
644
+ decisionResponseError("Invalid decision rounding precision");
645
+ pError = 0.5 * 10 ** -rounding.probabilityDecimals;
646
+ sError = 0.5 * 10 ** -rounding.scoreDecimals;
647
+ }
648
+ const epsilon = 1e-6;
649
+ for (const [id, question] of Object.entries(questions)) {
650
+ const answer = result.answers[id];
651
+ if (!isDecisionRecord(answer))
652
+ decisionResponseError("Invalid decision answer");
653
+ if (answer.type === "refusal") continue;
654
+ if (answer.type !== question.type)
655
+ decisionResponseError("Decision answer type does not match its question");
656
+ if (answer.confidence !== void 0 && !probability(answer.confidence))
657
+ decisionResponseError(
658
+ "Confidence must be finite and between zero and one"
659
+ );
660
+ if (question.type === "boolean") {
661
+ if (!probability(answer.probability))
662
+ decisionResponseError(
663
+ "Boolean probability must be between zero and one"
664
+ );
665
+ continue;
666
+ }
667
+ const keys = question.type === "choice" ? Object.keys(question.criteria) : question.criteria.map((_, index) => String(index));
668
+ if (question.type === "choice" && (typeof answer.choice !== "string" || !keys.includes(answer.choice)))
669
+ decisionResponseError("Decision selected an unknown choice");
670
+ if (question.type === "score" && (typeof answer.score !== "number" || !Number.isFinite(answer.score) || answer.score < 0 || answer.score > keys.length - 1))
671
+ decisionResponseError("Decision score is outside its ordered levels");
672
+ if (answer.probabilities === void 0) continue;
673
+ const distribution = answer.probabilities;
674
+ if (!isDecisionRecord(distribution) || !exactKeys(distribution, keys) || !Object.values(distribution).every(probability))
675
+ decisionResponseError(
676
+ "Decision distribution must contain every option exactly once with finite probabilities"
677
+ );
678
+ const values = Object.values(distribution);
679
+ if (Math.abs(values.reduce((a, b) => a + b, 0) - 1) > epsilon + keys.length * pError)
680
+ decisionResponseError("Decision probabilities do not sum to one");
681
+ if (question.type === "choice") {
682
+ if (values.some(
683
+ (v) => v > distribution[answer.choice] + epsilon + 2 * pError
684
+ ))
685
+ decisionResponseError(
686
+ "Decision choice is not a highest-probability option"
687
+ );
688
+ } else {
689
+ const mean = keys.reduce(
690
+ (sum, key) => sum + Number(key) * distribution[key],
691
+ 0
692
+ );
693
+ const tolerance = epsilon + sError + pError * keys.reduce((sum, key) => sum + Number(key), 0);
694
+ if (Math.abs(answer.score - mean) > tolerance)
695
+ decisionResponseError(
696
+ "Decision score does not match its probability-weighted levels"
697
+ );
698
+ }
699
+ }
700
+ }
701
+
702
+ // src/core/decisions.ts
703
+ var decisionErrorMarker = /* @__PURE__ */ Symbol.for("yourgpt.llm-sdk.DecisionAPIError");
704
+ var _a2, _b;
705
+ var DecisionAPIError = class extends (_b = Error, _a2 = decisionErrorMarker, _b) {
706
+ constructor(message, code, status, requestId) {
707
+ super(message);
708
+ this.code = code;
709
+ this.status = status;
710
+ this.requestId = requestId;
711
+ this.name = "DecisionAPIError";
712
+ this[_a2] = true;
713
+ }
714
+ /** Works across CJS/ESM and separately bundled provider entry points. */
715
+ static isInstance(error) {
716
+ return typeof error === "object" && error !== null && /* @__PURE__ */ Symbol.for("yourgpt.llm-sdk.DecisionAPIError") in error;
717
+ }
718
+ /** Advisory only: this SDK does not retry Decisions automatically. */
719
+ get retryable() {
720
+ return this.code === "network_error" || this.code === "http_error" && (this.status === 408 || this.status === 429 || this.status != null && this.status >= 500);
721
+ }
722
+ };
723
+ async function experimental_decide(options) {
724
+ const { model, ...request } = options;
725
+ validateDecisionInput(request);
726
+ request.signal?.throwIfAborted();
727
+ const result = await model.doDecide(request);
728
+ request.signal?.throwIfAborted();
729
+ validateDecisionResult(request.questions, result);
730
+ return result;
731
+ }
732
+
413
733
  // src/core/stream-text.ts
414
734
  async function streamText(params) {
415
- const { model, tools, maxSteps = 1, signal } = params;
735
+ const { model, tools, maxSteps = 1, signal: callerSignal } = params;
736
+ validateMaxSteps(maxSteps);
737
+ const controller = new AbortController();
738
+ const signal = controller.signal;
739
+ const relayAbort = () => controller.abort(callerSignal?.reason);
740
+ if (callerSignal?.aborted) relayAbort();
741
+ else callerSignal?.addEventListener("abort", relayAbort, { once: true });
742
+ const cleanup = () => callerSignal?.removeEventListener("abort", relayAbort);
743
+ const cancel = () => {
744
+ controller.abort();
745
+ cleanup();
746
+ };
416
747
  if (params.responseFormat && model.capabilities.supportsJsonMode === false) {
417
748
  console.warn(
418
749
  `[llm-sdk] ${model.provider}/${model.modelId} does not support structured output (responseFormat); the request will be sent but the provider may ignore it.`
@@ -439,6 +770,7 @@ async function streamText(params) {
439
770
  let stepProviderMetadata;
440
771
  const toolCalls = [];
441
772
  let stepFinishReason = "stop";
773
+ let receivedFinish = false;
442
774
  try {
443
775
  for await (const chunk of model.doStream({
444
776
  messages,
@@ -452,6 +784,9 @@ async function streamText(params) {
452
784
  })) {
453
785
  throwIfAborted(signal);
454
786
  switch (chunk.type) {
787
+ case "reasoning-delta":
788
+ yield chunk;
789
+ break;
455
790
  case "text-delta":
456
791
  stepText += chunk.text;
457
792
  fullText += chunk.text;
@@ -465,6 +800,11 @@ async function streamText(params) {
465
800
  };
466
801
  break;
467
802
  case "finish":
803
+ if (receivedFinish)
804
+ throw invalidProviderResponse(
805
+ "Model stream returned multiple finish events"
806
+ );
807
+ receivedFinish = true;
468
808
  stepProviderMetadata = chunk.providerMetadata;
469
809
  finalProviderMetadata = chunk.providerMetadata;
470
810
  stepFinishReason = chunk.finishReason;
@@ -482,6 +822,12 @@ async function streamText(params) {
482
822
  return;
483
823
  }
484
824
  }
825
+ throwIfAborted(signal);
826
+ if (!receivedFinish)
827
+ throw invalidProviderResponse(
828
+ "Model stream ended without a finish event"
829
+ );
830
+ validateToolCalls(toolCalls);
485
831
  } catch (error) {
486
832
  yield {
487
833
  type: "error",
@@ -507,7 +853,7 @@ async function streamText(params) {
507
853
  if (toolCalls.length === 0 || !tools) break;
508
854
  for (const call of toolCalls) {
509
855
  throwIfAborted(signal);
510
- const toolDef = tools[call.name];
856
+ const toolDef = Object.prototype.hasOwnProperty.call(tools, call.name) ? tools[call.name] : void 0;
511
857
  if (!toolDef) {
512
858
  const errorResult = { error: `Tool not found: ${call.name}` };
513
859
  yield {
@@ -548,7 +894,7 @@ async function streamText(params) {
548
894
  {
549
895
  role: "tool",
550
896
  toolCallId: call.id,
551
- content: modelOutput ? "[Tool returned content]" : JSON.stringify(result),
897
+ content: modelOutput ? "[Tool returned content]" : JSON.stringify(result ?? null),
552
898
  ...modelOutput ? { modelOutput } : {}
553
899
  }
554
900
  ];
@@ -590,11 +936,17 @@ async function streamText(params) {
590
936
  pending = (async () => {
591
937
  try {
592
938
  const next = await source.next();
593
- if (next.done) finished = true;
594
- else parts.push(next.value);
939
+ if (next.done) {
940
+ finished = true;
941
+ cleanup();
942
+ } else {
943
+ parts.push(next.value);
944
+ if (next.value.type === "error") cleanup();
945
+ }
595
946
  } catch (error) {
596
947
  failure = error;
597
948
  finished = true;
949
+ cleanup();
598
950
  }
599
951
  })();
600
952
  }
@@ -645,7 +997,7 @@ async function streamText(params) {
645
997
  return consumeStream().then(() => finalFinishReason);
646
998
  },
647
999
  toTextStreamResponse(options) {
648
- const stream = createTextStreamReadable(createTextStream());
1000
+ const stream = createTextStreamReadable(createTextStream(), cancel);
649
1001
  return new Response(stream, {
650
1002
  status: options?.status ?? 200,
651
1003
  headers: {
@@ -657,7 +1009,7 @@ async function streamText(params) {
657
1009
  });
658
1010
  },
659
1011
  toDataStreamResponse(options) {
660
- const stream = createDataStreamReadable(sharedStream());
1012
+ const stream = createDataStreamReadable(sharedStream(), cancel);
661
1013
  return new Response(stream, {
662
1014
  status: options?.status ?? 200,
663
1015
  headers: {
@@ -694,35 +1046,51 @@ function formatToolsForProvider2(tools, provider) {
694
1046
  return formatToolsForOpenAI(tools);
695
1047
  }
696
1048
  }
697
- function createTextStreamReadable(textStream) {
1049
+ function createTextStreamReadable(textStream, abort) {
698
1050
  const encoder = new TextEncoder();
1051
+ const iterator = textStream[Symbol.asyncIterator]();
1052
+ let cancelled = false;
699
1053
  return new ReadableStream({
700
- async start(controller) {
1054
+ async pull(controller) {
701
1055
  try {
702
- for await (const text of textStream) {
703
- controller.enqueue(encoder.encode(text));
704
- }
705
- controller.close();
1056
+ const next = await iterator.next();
1057
+ if (cancelled) return;
1058
+ if (next.done) controller.close();
1059
+ else controller.enqueue(encoder.encode(next.value));
706
1060
  } catch (error) {
707
- controller.error(error);
1061
+ if (!cancelled) controller.error(error);
708
1062
  }
1063
+ },
1064
+ cancel() {
1065
+ cancelled = true;
1066
+ abort();
1067
+ void iterator.return?.().catch(() => {
1068
+ });
709
1069
  }
710
1070
  });
711
1071
  }
712
- function createDataStreamReadable(fullStream) {
1072
+ function createDataStreamReadable(fullStream, abort) {
713
1073
  const encoder = new TextEncoder();
1074
+ const iterator = fullStream[Symbol.asyncIterator]();
1075
+ let cancelled = false;
714
1076
  return new ReadableStream({
715
- async start(controller) {
1077
+ async pull(controller) {
716
1078
  try {
717
- for await (const part of fullStream) {
718
- const data = JSON.stringify(part);
719
- controller.enqueue(encoder.encode(`data: ${data}
720
-
721
- `));
1079
+ const next = await iterator.next();
1080
+ if (cancelled) return;
1081
+ if (next.done) {
1082
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
1083
+ controller.close();
1084
+ return;
722
1085
  }
723
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
724
- controller.close();
1086
+ if (next.value.type === "error") throw next.value.error;
1087
+ controller.enqueue(
1088
+ encoder.encode(`data: ${JSON.stringify(next.value)}
1089
+
1090
+ `)
1091
+ );
725
1092
  } catch (error) {
1093
+ if (cancelled) return;
726
1094
  const errorData = JSON.stringify({
727
1095
  type: "error",
728
1096
  error: error instanceof Error ? error.message : String(error)
@@ -731,7 +1099,15 @@ function createDataStreamReadable(fullStream) {
731
1099
 
732
1100
  `));
733
1101
  controller.close();
1102
+ void iterator.return?.().catch(() => {
1103
+ });
734
1104
  }
1105
+ },
1106
+ cancel() {
1107
+ cancelled = true;
1108
+ abort();
1109
+ void iterator.return?.().catch(() => {
1110
+ });
735
1111
  }
736
1112
  });
737
1113
  }
@@ -1267,6 +1643,9 @@ var StreamResult = class {
1267
1643
  }
1268
1644
  break;
1269
1645
  }
1646
+ case "error":
1647
+ collected.error = { message: event.message, code: event.code };
1648
+ break;
1270
1649
  case "done":
1271
1650
  if (event.messages) {
1272
1651
  collected.messages.push(...event.messages);
@@ -1733,7 +2112,7 @@ function buildToolResultForAI(tool2, result, args) {
1733
2112
  return aiContext ?? `[Tool ${tool2?.name ?? "unknown"} executed successfully]`;
1734
2113
  case "full":
1735
2114
  default:
1736
- const fullData = JSON.stringify(result);
2115
+ const fullData = JSON.stringify(result ?? null);
1737
2116
  return aiContext ? `${aiContext}
1738
2117
 
1739
2118
  Full data: ${fullData}` : fullData;
@@ -2413,7 +2792,8 @@ var Runtime = class {
2413
2792
  const systemPrompt = request.systemPrompt || this.config.systemPrompt || "";
2414
2793
  let accumulatedText = "";
2415
2794
  const toolCalls = [];
2416
- let currentToolCall = null;
2795
+ const pendingToolCalls = /* @__PURE__ */ new Map();
2796
+ const endedToolIds = /* @__PURE__ */ new Set();
2417
2797
  const serverToolResults = [];
2418
2798
  const toolContextData = "toolContext" in this.config ? this.config.toolContext : void 0;
2419
2799
  let adapterProviderMetadata;
@@ -2438,147 +2818,151 @@ var Runtime = class {
2438
2818
  debug
2439
2819
  };
2440
2820
  const stream = this.adapter.stream(completionRequest);
2441
- for await (const event of stream) {
2442
- switch (event.type) {
2443
- case "message:start":
2444
- yield event;
2445
- break;
2446
- case "message:end":
2447
- yield {
2448
- ...event,
2449
- providerMetadata: responseReplayMetadata(event.providerMetadata)
2450
- };
2451
- break;
2452
- case "message:delta":
2453
- accumulatedText += event.content;
2454
- yield event;
2455
- break;
2456
- case "action:start":
2457
- currentToolCall = {
2458
- id: event.id,
2459
- name: event.name,
2460
- args: "",
2461
- ...event.extra_content ? { extra_content: event.extra_content } : {}
2462
- };
2463
- if (debug) {
2464
- console.log(`[Copilot SDK] Tool call started: ${event.name}`);
2821
+ try {
2822
+ for await (const event of stream) {
2823
+ throwIfAborted(signal);
2824
+ switch (event.type) {
2825
+ case "message:start":
2826
+ yield event;
2827
+ break;
2828
+ case "message:end":
2829
+ yield {
2830
+ ...event,
2831
+ providerMetadata: responseReplayMetadata(event.providerMetadata)
2832
+ };
2833
+ break;
2834
+ case "message:delta":
2835
+ accumulatedText += event.content;
2836
+ yield event;
2837
+ break;
2838
+ case "action:start":
2839
+ if (!event.id || !event.name || pendingToolCalls.has(event.id) || toolCalls.some((call) => call.id === event.id))
2840
+ throw invalidProviderResponse(
2841
+ "Tool calls have missing or duplicate identities"
2842
+ );
2843
+ pendingToolCalls.set(event.id, {
2844
+ id: event.id,
2845
+ name: event.name,
2846
+ ...event.extra_content ? { extra_content: event.extra_content } : {}
2847
+ });
2848
+ if (debug) {
2849
+ console.log(`[Copilot SDK] Tool call started: ${event.name}`);
2850
+ }
2851
+ yield event;
2852
+ break;
2853
+ case "action:args": {
2854
+ const current = pendingToolCalls.get(event.id);
2855
+ if (!current)
2856
+ throw invalidProviderResponse(
2857
+ "Tool arguments have no matching call"
2858
+ );
2859
+ toolCalls.push({
2860
+ ...current,
2861
+ args: parseToolArguments(event.args)
2862
+ });
2863
+ pendingToolCalls.delete(event.id);
2864
+ yield event;
2865
+ break;
2465
2866
  }
2466
- yield event;
2467
- break;
2468
- case "action:args":
2469
- if (currentToolCall) {
2470
- try {
2471
- const parsedArgs = JSON.parse(event.args || "{}");
2867
+ case "action:end": {
2868
+ const tc = toolCalls.find((t) => t.id === event.id);
2869
+ const eventName = event.name;
2870
+ if (!tc || eventName && eventName !== tc.name || endedToolIds.has(event.id))
2871
+ throw invalidProviderResponse(
2872
+ "Tool completion has no matching unexecuted call"
2873
+ );
2874
+ endedToolIds.add(event.id);
2875
+ const toolName = tc.name;
2876
+ const tool2 = toolName ? selectedToolMap.get(toolName) : void 0;
2877
+ if (tool2?.location === "server" && tool2.handler) {
2472
2878
  if (debug) {
2473
2879
  console.log(
2474
- `[Copilot SDK] Tool args for ${currentToolCall.name}:`,
2475
- parsedArgs
2880
+ `[Copilot SDK] Executing server-side tool: ${toolName}`
2476
2881
  );
2477
2882
  }
2478
- toolCalls.push({
2479
- id: currentToolCall.id,
2480
- name: currentToolCall.name,
2481
- args: parsedArgs,
2482
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
2483
- });
2484
- } catch (e) {
2485
- console.error(
2486
- "[Copilot SDK] Failed to parse tool args:",
2487
- event.args,
2488
- e
2883
+ const args = tc.args;
2884
+ const toolContext = buildToolContext(
2885
+ event.id,
2886
+ signal,
2887
+ request.threadId,
2888
+ _httpRequest,
2889
+ toolContextData
2489
2890
  );
2490
- toolCalls.push({
2491
- id: currentToolCall.id,
2492
- name: currentToolCall.name,
2493
- args: {},
2494
- ...currentToolCall.extra_content ? { extra_content: currentToolCall.extra_content } : {}
2495
- });
2891
+ try {
2892
+ throwIfAborted(signal);
2893
+ const result = await tool2.handler(args, toolContext);
2894
+ throwIfAborted(signal);
2895
+ const modelOutput = resolveToolOutput(
2896
+ result,
2897
+ tool2.toModelOutput?.(result)
2898
+ );
2899
+ serverToolResults.push({
2900
+ id: event.id,
2901
+ name: toolName,
2902
+ args,
2903
+ result,
2904
+ modelOutput,
2905
+ tool: tool2
2906
+ });
2907
+ yield {
2908
+ type: "action:end",
2909
+ id: event.id,
2910
+ name: toolName,
2911
+ result,
2912
+ modelOutput
2913
+ };
2914
+ } catch (error) {
2915
+ throwIfAborted(signal);
2916
+ const errorResult = {
2917
+ success: false,
2918
+ error: error instanceof Error ? error.message : "Tool execution failed"
2919
+ };
2920
+ serverToolResults.push({
2921
+ id: event.id,
2922
+ name: toolName,
2923
+ args,
2924
+ result: errorResult,
2925
+ tool: tool2
2926
+ });
2927
+ yield {
2928
+ type: "action:end",
2929
+ id: event.id,
2930
+ name: toolName,
2931
+ error: error instanceof Error ? error.message : "Tool execution failed"
2932
+ };
2933
+ }
2934
+ } else {
2935
+ yield event;
2496
2936
  }
2497
- currentToolCall = null;
2937
+ break;
2498
2938
  }
2499
- yield event;
2500
- break;
2501
- case "action:end": {
2502
- const toolName = event.name;
2503
- const tool2 = toolName ? selectedToolMap.get(toolName) : void 0;
2504
- if (tool2?.location === "server" && tool2.handler) {
2505
- if (debug) {
2506
- console.log(
2507
- `[Copilot SDK] Executing server-side tool: ${toolName}`
2508
- );
2509
- }
2510
- const tc = toolCalls.find((t) => t.id === event.id);
2511
- const args = tc?.args ?? {};
2512
- const toolContext = buildToolContext(
2513
- event.id,
2514
- signal,
2515
- request.threadId,
2516
- _httpRequest,
2517
- toolContextData
2939
+ case "citation":
2940
+ yield event;
2941
+ break;
2942
+ case "error":
2943
+ yield event;
2944
+ return;
2945
+ // Exit on error
2946
+ case "done":
2947
+ adapterProviderMetadata = responseReplayMetadata(
2948
+ event.providerMetadata
2518
2949
  );
2519
- try {
2520
- const result = await tool2.handler(args, toolContext);
2521
- const modelOutput = resolveToolOutput(
2522
- result,
2523
- tool2.toModelOutput?.(result)
2524
- );
2525
- serverToolResults.push({
2526
- id: event.id,
2527
- name: toolName,
2528
- args,
2529
- result,
2530
- modelOutput,
2531
- tool: tool2
2532
- });
2533
- yield {
2534
- type: "action:end",
2535
- id: event.id,
2536
- name: toolName,
2537
- result,
2538
- modelOutput
2539
- };
2540
- } catch (error) {
2541
- const errorResult = {
2542
- success: false,
2543
- error: error instanceof Error ? error.message : "Tool execution failed"
2544
- };
2545
- serverToolResults.push({
2546
- id: event.id,
2547
- name: toolName,
2548
- args,
2549
- result: errorResult,
2550
- tool: tool2
2551
- });
2552
- yield {
2553
- type: "action:end",
2554
- id: event.id,
2555
- name: toolName,
2556
- error: error instanceof Error ? error.message : "Tool execution failed"
2557
- };
2950
+ if (event.usage) {
2951
+ adapterUsage = event.usage;
2558
2952
  }
2559
- } else {
2953
+ break;
2954
+ default:
2560
2955
  yield event;
2561
- }
2562
- break;
2563
2956
  }
2564
- case "citation":
2565
- yield event;
2566
- break;
2567
- case "error":
2568
- yield event;
2569
- return;
2570
- // Exit on error
2571
- case "done":
2572
- adapterProviderMetadata = responseReplayMetadata(
2573
- event.providerMetadata
2574
- );
2575
- if (event.usage) {
2576
- adapterUsage = event.usage;
2577
- }
2578
- break;
2579
- default:
2580
- yield event;
2581
2957
  }
2958
+ throwIfAborted(signal);
2959
+ if (pendingToolCalls.size || toolCalls.some((call) => !endedToolIds.has(call.id)))
2960
+ throw invalidProviderResponse(
2961
+ "Stream ended with incomplete tool calls"
2962
+ );
2963
+ } catch (error) {
2964
+ yield providerErrorEvent(error, "RUNTIME_STREAM_ERROR");
2965
+ return;
2582
2966
  }
2583
2967
  if (toolCalls.length > 0) {
2584
2968
  if (debug) {
@@ -2687,7 +3071,18 @@ var Runtime = class {
2687
3071
  nextToolSearchState,
2688
3072
  _iteration + 1
2689
3073
  )) {
2690
- yield event;
3074
+ if (event.type === "done" && adapterUsage) {
3075
+ const promptTokens = adapterUsage.prompt_tokens + (event.usage?.prompt_tokens ?? 0);
3076
+ const completionTokens = adapterUsage.completion_tokens + (event.usage?.completion_tokens ?? 0);
3077
+ yield {
3078
+ ...event,
3079
+ usage: {
3080
+ prompt_tokens: promptTokens,
3081
+ completion_tokens: completionTokens,
3082
+ total_tokens: (adapterUsage.total_tokens ?? adapterUsage.prompt_tokens + adapterUsage.completion_tokens) + (event.usage?.total_tokens ?? (event.usage?.prompt_tokens ?? 0) + (event.usage?.completion_tokens ?? 0))
3083
+ }
3084
+ };
3085
+ } else yield event;
2691
3086
  }
2692
3087
  return;
2693
3088
  }
@@ -2823,6 +3218,8 @@ var Runtime = class {
2823
3218
  };
2824
3219
  try {
2825
3220
  const result = await this.adapter.complete(completionRequest);
3221
+ throwIfAborted(signal);
3222
+ validateToolCalls(result.toolCalls);
2826
3223
  if (result.usage) {
2827
3224
  accumulatedUsage.prompt_tokens += result.usage.promptTokens;
2828
3225
  accumulatedUsage.completion_tokens += result.usage.completionTokens;
@@ -2876,6 +3273,7 @@ var Runtime = class {
2876
3273
  const serverToolResults = [];
2877
3274
  const toolContextData = "toolContext" in this.config ? this.config.toolContext : void 0;
2878
3275
  for (const tc of serverToolCalls) {
3276
+ throwIfAborted(signal);
2879
3277
  const tool2 = selectedToolMap.get(tc.name);
2880
3278
  if (tool2?.handler) {
2881
3279
  if (debug) {
@@ -2890,6 +3288,7 @@ var Runtime = class {
2890
3288
  );
2891
3289
  try {
2892
3290
  const toolResult = await tool2.handler(tc.args, toolContext);
3291
+ throwIfAborted(signal);
2893
3292
  const modelOutput = resolveToolOutput(
2894
3293
  toolResult,
2895
3294
  tool2.toModelOutput?.(toolResult)
@@ -2910,6 +3309,7 @@ var Runtime = class {
2910
3309
  modelOutput
2911
3310
  };
2912
3311
  } catch (error) {
3312
+ throwIfAborted(signal);
2913
3313
  const errorResult = {
2914
3314
  success: false,
2915
3315
  error: error instanceof Error ? error.message : "Tool execution failed"
@@ -3572,20 +3972,11 @@ function parseToolCalls(response) {
3572
3972
  const message = choices?.[0]?.message;
3573
3973
  const toolCalls = message?.tool_calls || [];
3574
3974
  return toolCalls.map((tc) => {
3575
- let input = {};
3576
- try {
3577
- input = JSON.parse(tc.function.arguments);
3578
- } catch (e) {
3579
- console.error(
3580
- "Failed to parse tool arguments:",
3581
- tc.function.arguments,
3582
- e
3583
- );
3584
- }
3975
+ const input2 = parseToolArguments(tc.function.arguments);
3585
3976
  return {
3586
3977
  id: tc.id,
3587
3978
  name: tc.function.name,
3588
- input
3979
+ input: input2
3589
3980
  };
3590
3981
  });
3591
3982
  }
@@ -3666,7 +4057,7 @@ function parseToolCalls2(response) {
3666
4057
  return content.filter((block) => block?.type === "tool_use").map((block) => ({
3667
4058
  id: block.id,
3668
4059
  name: block.name,
3669
- input: block.input || {}
4060
+ input: parseToolArguments(block.input)
3670
4061
  }));
3671
4062
  }
3672
4063
  function extractTextContent2(response) {
@@ -4000,7 +4391,11 @@ async function* runAgentLoop(options) {
4000
4391
  providerTools,
4001
4392
  providerToolOptions
4002
4393
  );
4394
+ throwIfAborted(signal);
4003
4395
  const toolCalls = formatter.parseToolCalls(response);
4396
+ validateToolCalls(
4397
+ toolCalls.map((call) => ({ ...call, args: call.input }))
4398
+ );
4004
4399
  const textContent = formatter.extractTextContent(response);
4005
4400
  if (textContent) {
4006
4401
  const messageId = generateMessageId();
@@ -4015,15 +4410,13 @@ async function* runAgentLoop(options) {
4015
4410
  toolCalls.map((tc) => tc.name)
4016
4411
  );
4017
4412
  }
4018
- const results = await executeToolCalls(
4413
+ const results = yield* executeToolCalls(
4019
4414
  toolCalls,
4020
4415
  effectiveSelectedTools,
4021
4416
  executeServerTool,
4022
4417
  waitForClientToolResult,
4023
- function* (event) {
4024
- yield event;
4025
- },
4026
- debug
4418
+ debug,
4419
+ signal
4027
4420
  );
4028
4421
  for (const result of results) {
4029
4422
  const toolCall = toolCalls.find((tc) => tc.id === result.toolCallId);
@@ -4112,9 +4505,10 @@ function buildConversation(messages, systemPrompt) {
4112
4505
  }
4113
4506
  return conversation;
4114
4507
  }
4115
- async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClientToolResult, emitEvent, debug) {
4508
+ async function* executeToolCalls(toolCalls, tools, executeServerTool, waitForClientToolResult, debug, signal) {
4116
4509
  const results = [];
4117
4510
  for (const toolCall of toolCalls) {
4511
+ throwIfAborted(signal);
4118
4512
  const tool2 = tools.find((t) => t.name === toolCall.name);
4119
4513
  if (!tool2) {
4120
4514
  if (debug) {
@@ -4131,22 +4525,26 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4131
4525
  });
4132
4526
  continue;
4133
4527
  }
4134
- emitEvent?.({
4528
+ yield {
4135
4529
  type: "action:start",
4136
4530
  id: toolCall.id,
4137
4531
  name: toolCall.name,
4138
4532
  hidden: tool2.hidden ?? false
4139
- });
4140
- emitEvent?.({
4533
+ };
4534
+ yield {
4141
4535
  type: "action:args",
4142
4536
  id: toolCall.id,
4143
4537
  args: JSON.stringify(toolCall.input)
4144
- });
4538
+ };
4145
4539
  try {
4146
4540
  let response;
4541
+ throwIfAborted(signal);
4147
4542
  if (tool2.location === "server") {
4148
4543
  if (tool2.handler) {
4149
- response = await tool2.handler(toolCall.input);
4544
+ response = await tool2.handler(toolCall.input, {
4545
+ signal,
4546
+ toolCallId: toolCall.id
4547
+ });
4150
4548
  } else if (executeServerTool) {
4151
4549
  response = await executeServerTool(toolCall.name, toolCall.input);
4152
4550
  } else {
@@ -4169,12 +4567,13 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4169
4567
  };
4170
4568
  }
4171
4569
  }
4172
- emitEvent?.({
4570
+ throwIfAborted(signal);
4571
+ yield {
4173
4572
  type: "action:end",
4174
4573
  id: toolCall.id,
4175
4574
  name: toolCall.name,
4176
4575
  result: response
4177
- });
4576
+ };
4178
4577
  results.push({
4179
4578
  toolCallId: toolCall.id,
4180
4579
  content: JSON.stringify(response),
@@ -4182,13 +4581,14 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4182
4581
  error: response.error
4183
4582
  });
4184
4583
  } catch (error) {
4584
+ throwIfAborted(signal);
4185
4585
  const errorMessage = error instanceof Error ? error.message : "Tool execution failed";
4186
- emitEvent?.({
4586
+ yield {
4187
4587
  type: "action:end",
4188
4588
  id: toolCall.id,
4189
4589
  name: toolCall.name,
4190
4590
  error: errorMessage
4191
- });
4591
+ };
4192
4592
  results.push({
4193
4593
  toolCallId: toolCall.id,
4194
4594
  content: JSON.stringify({
@@ -4205,17 +4605,17 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
4205
4605
 
4206
4606
  // src/core/audio.ts
4207
4607
  var audioErrorMarker = /* @__PURE__ */ Symbol.for("@yourgpt/llm-sdk/AudioAPIError");
4208
- var _a;
4608
+ var _a3;
4209
4609
  var AudioAPIError = class extends Error {
4210
4610
  constructor(message, code, status, requestId) {
4211
4611
  super(message);
4212
4612
  this.code = code;
4213
4613
  this.status = status;
4214
4614
  this.requestId = requestId;
4215
- this[_a] = true;
4615
+ this[_a3] = true;
4216
4616
  this.name = "AudioAPIError";
4217
4617
  }
4218
- static [(_a = audioErrorMarker, Symbol.hasInstance)](value) {
4618
+ static [(_a3 = audioErrorMarker, Symbol.hasInstance)](value) {
4219
4619
  return !!value && typeof value === "object" && value[audioErrorMarker] === true;
4220
4620
  }
4221
4621
  };
@@ -4250,6 +4650,6 @@ async function transcribe({
4250
4650
  return result;
4251
4651
  }
4252
4652
 
4253
- export { AudioAPIError, DEFAULT_CAPABILITIES, DEFAULT_MAX_ITERATIONS, GenerateResult, Runtime, StreamResult, buildProviderToolOptions, createEventStream, createExpressHandler, createExpressMiddleware, createHonoApp, createNextHandler, createNodeHandler, createRuntime, createSSEHeaders, createSSEResponse, createStreamResult, createTextStreamHeaders, createTextStreamResponse, extractInputMessages, formatSSEData, formatToolsForAnthropic, formatToolsForGoogle, formatToolsForOpenAI, generateMessageId, generateSpeech, generateText, generateThreadId, generateToolCallId, mapOutputMessages, pipeSSEToResponse, pipeTextToResponse, runAgentLoop, searchTools, selectTools, shouldExposeToolSearch, streamText, tool, transcribe };
4653
+ export { AudioAPIError, DEFAULT_CAPABILITIES, DEFAULT_MAX_ITERATIONS, DecisionAPIError, GenerateResult, MediaAPIError, Runtime, StreamResult, buildProviderToolOptions, createEventStream, createExpressHandler, createExpressMiddleware, createHonoApp, createNextHandler, createNodeHandler, createRuntime, createSSEHeaders, createSSEResponse, createStreamResult, createTextStreamHeaders, createTextStreamResponse, experimental_decide, extractInputMessages, formatSSEData, formatToolsForAnthropic, formatToolsForGoogle, formatToolsForOpenAI, generateImage, generateMessageId, generateSpeech, generateText, generateThreadId, generateToolCallId, mapOutputMessages, pipeSSEToResponse, pipeTextToResponse, runAgentLoop, searchTools, selectTools, shouldExposeToolSearch, streamText, tool, transcribe };
4254
4654
  //# sourceMappingURL=index.mjs.map
4255
4655
  //# sourceMappingURL=index.mjs.map