@ai-sdk/workflow 2.0.69 → 2.0.71

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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { n as normalizeUIMessageStreamParts, t as WorkflowChatTransport } from "./workflow-chat-transport-Bduf4ZDx.js";
2
- import { asArray, asSchema, convertUint8ArrayToBase64, generateId, getErrorMessage, isAbortError, jsonSchema, validateTypes, withUserAgentSuffix } from "@ai-sdk/provider-utils";
1
+ import { n as normalizeUIMessageStreamParts, t as WorkflowChatTransport } from "./workflow-chat-transport-Dj1poviu.js";
2
+ import { asArray, asSchema, convertUint8ArrayToBase64, createIdGenerator, generateId, getErrorMessage, isAbortError, jsonSchema, validateTypes, withUserAgentSuffix } from "@ai-sdk/provider-utils";
3
3
  import { DefaultGeneratedFile, InvalidToolApprovalSignatureError, NoOutputGeneratedError, Output, ToolChoiceViolationError, dynamicTool, experimental_filterActiveTools, experimental_streamLanguageModelCall, gateway, isStepCount, tool, wrapLanguageModel } from "ai";
4
- import { DefaultGenerateTextResult, DefaultStepResult, addLanguageModelUsage, asLanguageModelUsage, calculateTokensPerSecond, collectToolApprovals, convertLanguageModelContent, convertToLanguageModelPrompt, createDefaultDownloadFunction, createNullLanguageModelUsage, createRestrictedTelemetryDispatcher, createToolModelOutput, createToolSearchState, downloadAssets, mapToolResultOutput, mergeAbortSignals, mergeCallbacks, parseToolCall, prepareLanguageModelCallOptions, prepareRetries, prepareToolChoice, prepareTools, resolveLanguageModel, signToolApproval, standardizePrompt, validateApprovedToolApprovals, verifyToolApprovalSignature } from "ai/internal";
4
+ import { DefaultGenerateTextResult, DefaultStepResult, addLanguageModelUsage, asLanguageModelUsage, calculateTokensPerSecond, collectToolApprovals, convertLanguageModelContent, convertToLanguageModelPrompt, createDefaultDownloadFunction, createNullLanguageModelUsage, createRestrictedTelemetryDispatcher, createTelemetryDispatcher, createToolModelOutput, createToolSearchState, downloadAssets, isOutputChunk, mapToolResultOutput, mergeAbortSignals, mergeCallbacks, parseToolCall, prepareLanguageModelCallOptions, prepareRetries, prepareToolChoice, prepareTools, resolveLanguageModel, signToolApproval, standardizePrompt, validateApprovedToolApprovals, verifyToolApprovalSignature } from "ai/internal";
5
5
  import { getWorkflowMetadata } from "workflow";
6
6
  import Ajv from "ajv";
7
7
  import Ajv2020 from "ajv/dist/2020.js";
@@ -182,7 +182,7 @@ async function buildModelStepResult(raw, toolCalls, finish, providerExecutedTool
182
182
  durationMs: duration
183
183
  });
184
184
  return new DefaultStepResult({
185
- callId: "workflow-agent",
185
+ callId: opts.callId ?? "workflow-agent",
186
186
  stepNumber: opts.stepNumber,
187
187
  provider: raw.generation.provider,
188
188
  modelId: raw.generation.modelId,
@@ -311,7 +311,7 @@ async function buildModelStepResult(raw, toolCalls, finish, providerExecutedTool
311
311
  }
312
312
  const toolResults = content.filter((part) => part.type === "tool-result");
313
313
  return {
314
- callId: "workflow-agent",
314
+ callId: opts.callId ?? "workflow-agent",
315
315
  stepNumber: opts.stepNumber,
316
316
  model: {
317
317
  provider: responseMetadata?.modelId?.split(":")[0] ?? "unknown",
@@ -382,6 +382,26 @@ async function buildModelStepResult(raw, toolCalls, finish, providerExecutedTool
382
382
  };
383
383
  }
384
384
  //#endregion
385
+ //#region src/language-model-prompt-to-model-messages.ts
386
+ /**
387
+ * Prepares a workflow-side `LanguageModelV4Prompt` for a second, model-aware
388
+ * `convertToLanguageModelPrompt` pass inside a model step.
389
+ *
390
+ * The workflow-side conversion preserves URLs (it cannot download them) and
391
+ * filters out approval responses for tools that are not provider-executed.
392
+ * The remaining approval responses are restored with the `providerExecuted`
393
+ * marker that the second conversion expects.
394
+ */
395
+ function languageModelPromptToModelMessages(prompt) {
396
+ return prompt.map((message) => message.role !== "tool" ? message : {
397
+ ...message,
398
+ content: message.content.map((part) => part.type === "tool-approval-response" ? {
399
+ ...part,
400
+ providerExecuted: true
401
+ } : part)
402
+ });
403
+ }
404
+ //#endregion
385
405
  //#region src/serializable-schema.ts
386
406
  /**
387
407
  * Converts a ToolSet (with Zod/standard schemas and execute functions) to a
@@ -424,6 +444,16 @@ function resolveToolDescription({ tool, toolName, toolsContext, experimental_san
424
444
  experimental_sandbox: sandbox
425
445
  });
426
446
  }
447
+ function formatAjvError(error) {
448
+ const path = `data${error.instancePath}`;
449
+ switch (error.keyword) {
450
+ case "enum": return `${path} must be one of ${error.params.allowedValues.map((value) => JSON.stringify(value)).join(", ")}`;
451
+ case "additionalProperties": return `${path} has unexpected property ${JSON.stringify(error.params.additionalProperty)}`;
452
+ case "const": return `${path} must equal ${JSON.stringify(error.params.allowedValue)}`;
453
+ case "required": return `${path} is missing required property ${JSON.stringify(error.params.missingProperty)}`;
454
+ default: return `${path} ${error.message ?? "is invalid"}`;
455
+ }
456
+ }
427
457
  /**
428
458
  * Reconstructs tool objects from serializable tool definitions inside a step.
429
459
  *
@@ -433,14 +463,14 @@ function resolveToolDescription({ tool, toolName, toolsContext, experimental_san
433
463
  */
434
464
  function resolveSerializableTools(tools) {
435
465
  const ajvOptions = {
466
+ allErrors: true,
436
467
  strict: false,
437
468
  validateFormats: false
438
469
  };
439
470
  const ajv = new Ajv(ajvOptions);
440
471
  const ajv2020 = new Ajv2020(ajvOptions);
441
472
  const createValidatedInputSchema = (inputSchema) => {
442
- const schemaAjv = (typeof inputSchema.$schema === "string" ? inputSchema.$schema.replace(/#$/, "") : void 0) === "https://json-schema.org/draft/2020-12/schema" ? ajv2020 : ajv;
443
- const validateFn = schemaAjv.compile(inputSchema);
473
+ const validateFn = ((typeof inputSchema.$schema === "string" ? inputSchema.$schema.replace(/#$/, "") : void 0) === "https://json-schema.org/draft/2020-12/schema" ? ajv2020 : ajv).compile(inputSchema);
444
474
  return jsonSchema(inputSchema, { validate: (value) => {
445
475
  if (validateFn(value)) return {
446
476
  success: true,
@@ -448,7 +478,7 @@ function resolveSerializableTools(tools) {
448
478
  };
449
479
  return {
450
480
  success: false,
451
- error: new Error(schemaAjv.errorsText(validateFn.errors))
481
+ error: new Error(validateFn.errors.map(formatAjvError).join(", "))
452
482
  };
453
483
  } });
454
484
  };
@@ -494,31 +524,164 @@ function resolveSerializableTools(tools) {
494
524
  }));
495
525
  }
496
526
  //#endregion
527
+ //#region src/model-call-telemetry.ts
528
+ const generateCallId = createIdGenerator({
529
+ prefix: "call",
530
+ size: 24
531
+ });
532
+ /**
533
+ * Global telemetry integrations are not visible in the workflow VM, so the
534
+ * workflow body sends a serializable descriptor and the model step, which
535
+ * runs in Node, dispatches the model call lifecycle itself.
536
+ *
537
+ * Each model call is reported as its own operation with a unique `callId`.
538
+ */
539
+ function shouldDispatchModelCallTelemetryInStep(telemetry) {
540
+ return telemetry?.isEnabled !== false && telemetry?.integrations == null && globalThis.AI_SDK_TELEMETRY_INTEGRATIONS == null;
541
+ }
542
+ async function runModelCallWithTelemetry({ telemetry, modelInit, prompt, serializedTools, options, execute }) {
543
+ if (telemetry == null) return execute(void 0);
544
+ const dispatcher = createTelemetryDispatcher({ telemetry });
545
+ const callId = generateCallId();
546
+ const { provider, modelId } = typeof modelInit === "string" ? {
547
+ provider: "gateway",
548
+ modelId: modelInit
549
+ } : modelInit;
550
+ const tools = serializedTools == null ? void 0 : resolveSerializableTools(serializedTools);
551
+ const messages = prompt;
552
+ await dispatcher.onStart?.({
553
+ callId,
554
+ operationId: "ai.workflowAgent.stream",
555
+ provider,
556
+ modelId,
557
+ system: void 0,
558
+ messages,
559
+ tools,
560
+ toolChoice: options?.toolChoice,
561
+ activeTools: void 0,
562
+ maxOutputTokens: options?.maxOutputTokens,
563
+ temperature: options?.temperature,
564
+ topP: options?.topP,
565
+ topK: options?.topK,
566
+ presencePenalty: options?.presencePenalty,
567
+ frequencyPenalty: options?.frequencyPenalty,
568
+ stopSequences: options?.stopSequences,
569
+ seed: options?.seed,
570
+ maxRetries: options?.maxRetries ?? 2,
571
+ timeout: void 0,
572
+ headers: options?.headers,
573
+ reasoning: options?.reasoning,
574
+ providerOptions: options?.providerOptions,
575
+ output: void 0,
576
+ runtimeContext: {},
577
+ toolsContext: {}
578
+ });
579
+ await dispatcher.onStepStart?.({
580
+ callId,
581
+ provider,
582
+ modelId,
583
+ stepNumber: telemetry.stepNumber,
584
+ system: void 0,
585
+ messages,
586
+ tools,
587
+ toolChoice: options?.toolChoice,
588
+ activeTools: void 0,
589
+ steps: [],
590
+ providerOptions: options?.providerOptions,
591
+ output: void 0,
592
+ runtimeContext: {},
593
+ toolsContext: {}
594
+ });
595
+ let result;
596
+ try {
597
+ result = await execute({
598
+ callId,
599
+ onLanguageModelCallStart: dispatcher.onLanguageModelCallStart,
600
+ onLanguageModelCallEnd: dispatcher.onLanguageModelCallEnd,
601
+ executeLanguageModelCallInTelemetryContext: dispatcher.executeLanguageModelCall
602
+ });
603
+ } catch (error) {
604
+ await dispatcher.onError?.({
605
+ callId,
606
+ error
607
+ });
608
+ throw error;
609
+ }
610
+ if (result.aborted) {
611
+ await dispatcher.onAbort?.({
612
+ callId,
613
+ steps: []
614
+ });
615
+ return result;
616
+ }
617
+ if ("terminalError" in result) {
618
+ await dispatcher.onError?.({
619
+ callId,
620
+ error: result.terminalError
621
+ });
622
+ return result;
623
+ }
624
+ const step = await buildModelStepResult(result.raw, result.toolCalls, result.finish, result.providerExecutedToolResults, {
625
+ callId,
626
+ tools,
627
+ stepNumber: telemetry.stepNumber,
628
+ runtimeContext: {},
629
+ toolsContext: {}
630
+ });
631
+ await dispatcher.onStepEnd?.(step);
632
+ await dispatcher.onEnd?.(Object.assign(Object.create(Object.getPrototypeOf(step)), step, {
633
+ finalStep: step,
634
+ steps: [step],
635
+ usage: step.usage,
636
+ totalUsage: step.usage
637
+ }));
638
+ return result;
639
+ }
640
+ //#endregion
497
641
  //#region src/do-generate-step.ts
498
642
  /** A single durable provider call; orchestration and callbacks stay in the workflow. */
499
643
  async function doGenerateStep(prompt, modelInit, serializedTools, options = {}) {
500
644
  "use step";
501
- try {
502
- return await generateModelCall(prompt, modelInit, serializedTools, options);
503
- } catch (error) {
504
- return {
505
- toolCalls: [],
506
- finish: void 0,
507
- raw: {
508
- content: [],
509
- reasoning: []
510
- },
511
- providerExecutedToolResults: /* @__PURE__ */ new Map(),
512
- terminalError: error
513
- };
514
- }
645
+ return runModelCallWithTelemetry({
646
+ telemetry: options.telemetry,
647
+ modelInit,
648
+ prompt,
649
+ serializedTools,
650
+ options,
651
+ execute: async (languageModelCallTelemetry) => {
652
+ try {
653
+ return await generateModelCall(prompt, modelInit, serializedTools, options, languageModelCallTelemetry);
654
+ } catch (error) {
655
+ return {
656
+ toolCalls: [],
657
+ finish: void 0,
658
+ raw: {
659
+ content: [],
660
+ reasoning: []
661
+ },
662
+ providerExecutedToolResults: /* @__PURE__ */ new Map(),
663
+ terminalError: error
664
+ };
665
+ }
666
+ }
667
+ });
515
668
  }
516
- async function generateModelCall(prompt, modelInit, serializedTools, options) {
669
+ async function generateModelCall(prompt, modelInit, serializedTools, options, languageModelCallTelemetry) {
517
670
  const remaining = options.timeoutAt == null ? void 0 : options.timeoutAt - Date.now();
518
671
  options.abortSignal?.throwIfAborted();
519
672
  if (remaining != null && remaining <= 0) throw new DOMException("The generation deadline expired.", "TimeoutError");
520
673
  const abortSignal = remaining == null ? options.abortSignal : AbortSignal.any([...options.abortSignal == null ? [] : [options.abortSignal], AbortSignal.timeout(remaining)]);
521
674
  const model = resolveLanguageModel(modelInit);
675
+ const modelPrompt = await convertToLanguageModelPrompt({
676
+ prompt: {
677
+ instructions: void 0,
678
+ messages: languageModelPromptToModelMessages(prompt)
679
+ },
680
+ supportedUrls: await model.supportedUrls,
681
+ download: options.download,
682
+ abortSignal,
683
+ provider: model.provider.split(".")[0]
684
+ });
522
685
  const tools = resolveSerializableTools(serializedTools);
523
686
  const toolChoice = prepareToolChoice({ toolChoice: options.toolChoice });
524
687
  const modelTools = await prepareTools({ tools });
@@ -527,28 +690,54 @@ async function generateModelCall(prompt, modelInit, serializedTools, options) {
527
690
  maxRetries: options.maxRetries,
528
691
  abortSignal
529
692
  });
693
+ const callId = languageModelCallTelemetry?.callId;
694
+ const executeLanguageModelCall = languageModelCallTelemetry?.executeLanguageModelCallInTelemetryContext ?? (({ execute }) => execute());
695
+ await languageModelCallTelemetry?.onLanguageModelCallStart?.({
696
+ callId,
697
+ provider: model.provider,
698
+ modelId: model.modelId,
699
+ messages: prompt,
700
+ tools: modelTools,
701
+ ...settings,
702
+ providerOptions: options.providerOptions,
703
+ headers: options.headers
704
+ });
530
705
  const start = Date.now();
531
706
  const response = await retry(async () => {
532
707
  abortSignal?.throwIfAborted();
533
708
  if (options.timeoutAt != null && options.timeoutAt <= Date.now()) throw new DOMException("The generation deadline expired.", "TimeoutError");
534
- return model.doGenerate({
535
- ...settings,
536
- prompt: [...prompt],
537
- tools: modelTools,
538
- toolChoice,
539
- responseFormat: options.responseFormat,
540
- providerOptions: options.providerOptions,
541
- headers: options.headers,
542
- abortSignal
709
+ return executeLanguageModelCall({
710
+ callId: callId ?? "",
711
+ execute: () => model.doGenerate({
712
+ ...settings,
713
+ prompt: modelPrompt,
714
+ tools: modelTools,
715
+ toolChoice,
716
+ responseFormat: options.responseFormat,
717
+ providerOptions: options.providerOptions,
718
+ headers: options.headers,
719
+ abortSignal
720
+ })
543
721
  });
544
722
  });
545
- abortSignal?.throwIfAborted();
546
723
  const responseTimeMs = Date.now() - start;
724
+ await languageModelCallTelemetry?.onLanguageModelCallEnd?.({
725
+ callId,
726
+ provider: model.provider,
727
+ modelId: response.response?.modelId ?? model.modelId,
728
+ finishReason: response.finishReason.unified,
729
+ usage: asLanguageModelUsage(response.usage),
730
+ content: response.content,
731
+ responseId: response.response?.id,
732
+ ...response.providerMetadata != null ? { providerMetadata: response.providerMetadata } : {},
733
+ performance: { responseTimeMs }
734
+ });
735
+ abortSignal?.throwIfAborted();
547
736
  const toolCalls = await Promise.all(response.content.filter((part) => part.type === "tool-call").map((toolCall) => parseToolCall({
548
737
  toolCall,
549
738
  tools,
550
739
  repairToolCall: options.repairToolCall,
551
- instructions: prompt.filter((message) => message.role === "system").map((message) => message.content).join("\n") || void 0,
740
+ instructions: modelPrompt.filter((message) => message.role === "system").map((message) => message.content).join("\n") || void 0,
552
741
  messages: prompt.filter((message) => message.role !== "system")
553
742
  })));
554
743
  if ((toolChoice.type === "required" || toolChoice.type === "tool") && !toolCalls.some((call) => toolChoice.type === "required" || call.toolName === toolChoice.toolName)) throw new ToolChoiceViolationError({
@@ -627,6 +816,17 @@ doGenerateStep.maxRetries = 0;
627
816
  //#region src/do-stream-step.ts
628
817
  async function doStreamStep(conversationPrompt, modelInit, writable, serializedTools, options) {
629
818
  "use step";
819
+ return runModelCallWithTelemetry({
820
+ telemetry: options?.telemetry,
821
+ modelInit,
822
+ prompt: conversationPrompt,
823
+ serializedTools,
824
+ options,
825
+ execute: (languageModelCallTelemetry) => streamModelStep(conversationPrompt, modelInit, writable, serializedTools, options, languageModelCallTelemetry)
826
+ });
827
+ }
828
+ doStreamStep.maxRetries = 0;
829
+ async function streamModelStep(conversationPrompt, modelInit, writable, serializedTools, options, languageModelCallTelemetry) {
630
830
  const timeout = options?.timeoutAt == null ? void 0 : options.timeoutAt - Date.now();
631
831
  if (options?.abortSignal?.aborted || timeout != null && timeout <= 0) return { aborted: true };
632
832
  const abortSignal = timeout == null ? options?.abortSignal : options?.abortSignal == null ? AbortSignal.timeout(timeout) : AbortSignal.any([options.abortSignal, AbortSignal.timeout(timeout)]);
@@ -667,13 +867,7 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
667
867
  try {
668
868
  const { stream } = await retry(() => experimental_streamLanguageModelCall({
669
869
  model: modelWithTransforms,
670
- messages: conversationPrompt.map((message) => message.role !== "tool" ? message : {
671
- ...message,
672
- content: message.content.map((part) => part.type === "tool-approval-response" ? {
673
- ...part,
674
- providerExecuted: true
675
- } : part)
676
- }),
870
+ messages: languageModelPromptToModelMessages(conversationPrompt),
677
871
  allowSystemInMessages: true,
678
872
  tools,
679
873
  toolChoice: options?.toolChoice,
@@ -691,7 +885,9 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
691
885
  frequencyPenalty: options?.frequencyPenalty,
692
886
  stopSequences: options?.stopSequences,
693
887
  seed: options?.seed,
694
- repairToolCall: options?.repairToolCall
888
+ repairToolCall: options?.repairToolCall,
889
+ download: options?.download,
890
+ ...languageModelCallTelemetry
695
891
  }));
696
892
  return stream;
697
893
  } catch (error) {
@@ -712,11 +908,13 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
712
908
  let performance;
713
909
  let terminalError;
714
910
  let hasTerminalError = false;
911
+ let hasReceivedOutputChunk = false;
715
912
  const ongoingToolCallToolNames = /* @__PURE__ */ new Map();
716
913
  const writer = writable?.getWriter();
717
914
  try {
718
915
  await writer?.write({ type: "reset-step" });
719
916
  for await (const part of modelStream) {
917
+ if (isOutputChunk(part)) hasReceivedOutputChunk = true;
720
918
  switch (part.type) {
721
919
  case "tool-input-start":
722
920
  ongoingToolCallToolNames.set(part.id, part.toolName);
@@ -902,6 +1100,10 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
902
1100
  writer?.releaseLock();
903
1101
  }
904
1102
  if (abortSignal?.aborted || options?.timeoutAt != null && options.timeoutAt <= Date.now()) return { aborted: true };
1103
+ if (finish == null && !hasReceivedOutputChunk && !hasTerminalError) {
1104
+ terminalError = new NoOutputGeneratedError({ message: "No output generated. The model stream ended without a finish chunk." });
1105
+ hasTerminalError = true;
1106
+ }
905
1107
  return {
906
1108
  toolCalls,
907
1109
  finish,
@@ -917,7 +1119,6 @@ async function doStreamStep(conversationPrompt, modelInit, writable, serializedT
917
1119
  ...hasTerminalError ? { terminalError } : {}
918
1120
  };
919
1121
  }
920
- doStreamStep.maxRetries = 0;
921
1122
  function applyStreamTransforms({ stream, transforms, tools }) {
922
1123
  const sourceReader = stream.getReader();
923
1124
  let stopped = false;
@@ -1006,7 +1207,7 @@ function mergePrepareStepGenerationSettings(current, overrides) {
1006
1207
  ...definedOverrides
1007
1208
  };
1008
1209
  }
1009
- async function* modelCallIterator({ prompt, mode = "stream", include, initialInstructions, initialMessages = prompt, tools = {}, writable, model, stopConditions, onStepEnd, onStepFinish, onStepStart, onError, prepareStep, generationSettings, toolChoice, runtimeContext, toolsContext, telemetry, includeRawChunks = false, timeoutAt, repairToolCall, responseFormat, experimental_transform, experimental_sandbox: sandbox }) {
1210
+ async function* modelCallIterator({ prompt, mode = "stream", include, initialInstructions, initialMessages = prompt, tools = {}, writable, model, stopConditions, onStepEnd, onStepFinish, onStepStart, onError, prepareStep, generationSettings, toolChoice, runtimeContext, toolsContext, telemetry, includeRawChunks = false, timeoutAt, repairToolCall, download, responseFormat, experimental_transform, experimental_sandbox: sandbox }) {
1010
1211
  let conversationPrompt = [...prompt];
1011
1212
  let currentModel = model;
1012
1213
  let currentGenerationSettings = generationSettings ?? {};
@@ -1032,6 +1233,7 @@ async function* modelCallIterator({ prompt, mode = "stream", include, initialIns
1032
1233
  includeRuntimeContext: telemetry?.includeRuntimeContext,
1033
1234
  includeToolsContext: telemetry?.includeToolsContext
1034
1235
  });
1236
+ const dispatchTelemetryInStep = shouldDispatchModelCallTelemetryInStep(telemetry);
1035
1237
  while (!done) {
1036
1238
  if (currentGenerationSettings.abortSignal?.aborted) {
1037
1239
  if (mode === "generate") currentGenerationSettings.abortSignal.throwIfAborted();
@@ -1133,9 +1335,16 @@ async function* modelCallIterator({ prompt, mode = "stream", include, initialIns
1133
1335
  includeRawChunks,
1134
1336
  timeoutAt,
1135
1337
  repairToolCall,
1338
+ download,
1136
1339
  responseFormat,
1137
1340
  include,
1138
- experimental_transform
1341
+ experimental_transform,
1342
+ telemetry: dispatchTelemetryInStep ? {
1343
+ functionId: telemetry?.functionId,
1344
+ recordInputs: telemetry?.recordInputs,
1345
+ recordOutputs: telemetry?.recordOutputs,
1346
+ stepNumber
1347
+ } : void 0
1139
1348
  };
1140
1349
  const modelCallResult = mode === "generate" ? await doGenerateStep(conversationPrompt, currentModel, serializedTools, callOptions) : await doStreamStep(conversationPrompt, currentModel, writable, serializedTools, callOptions);
1141
1350
  if (modelCallResult.aborted) {
@@ -1182,10 +1391,10 @@ async function* modelCallIterator({ prompt, mode = "stream", include, initialIns
1182
1391
  const isToolExecutionAllowed = finishReason === "tool-calls" || finishReason === "stop";
1183
1392
  for (const toolCall of toolCalls) if (toolCall.providerExecuted && serializedTools[toolCall.toolName]?.supportsDeferredResults && !providerExecutedToolResults.has(toolCall.toolCallId)) pendingDeferredToolCallIds.add(toolCall.toolCallId);
1184
1393
  for (const toolCallId of providerExecutedToolResults.keys()) pendingDeferredToolCallIds.delete(toolCallId);
1185
- const shouldProcessTools = isToolExecutionAllowed && (toolCalls.length > 0 || providerExecutedToolResults.size > 0);
1186
- if (hasTerminalError) done = true;
1187
- else if (shouldProcessTools) {
1394
+ const shouldProcessTools = isToolExecutionAllowed && (toolCalls.length > 0 || providerExecutedToolResults.size > 0) || finishReason === "length" && providerExecutedToolResults.size > 0;
1395
+ if (!hasTerminalError && shouldProcessTools) {
1188
1396
  lastStepWasYielded = true;
1397
+ const processableToolCalls = isToolExecutionAllowed ? toolCalls : toolCalls.filter((toolCall) => !toolCall.providerExecuted && toolCall.invalid || toolCall.providerExecuted && providerExecutedToolResults.has(toolCall.toolCallId));
1189
1398
  const { content: assistantContent, providerExecutedToolResultPositions } = getAssistantMessageContent(step, mode);
1190
1399
  const includedToolCallIds = new Set(assistantContent.flatMap((part) => part.type === "tool-call" ? [part.toolCallId] : []));
1191
1400
  conversationPrompt.push({
@@ -1193,7 +1402,7 @@ async function* modelCallIterator({ prompt, mode = "stream", include, initialIns
1193
1402
  content: [...assistantContent, ...toolCalls.filter((toolCall) => !includedToolCallIds.has(toolCall.toolCallId)).map(toAssistantToolCallContent)]
1194
1403
  });
1195
1404
  const toolResults = yield {
1196
- toolCalls,
1405
+ toolCalls: processableToolCalls,
1197
1406
  tools: effectiveTools,
1198
1407
  messages: conversationPrompt,
1199
1408
  step,
@@ -1206,14 +1415,14 @@ async function* modelCallIterator({ prompt, mode = "stream", include, initialIns
1206
1415
  const responseMessages = addToolResultsToConversation({
1207
1416
  messages: conversationPrompt,
1208
1417
  toolResults,
1209
- providerExecutedToolCallIds: /* @__PURE__ */ new Set([...toolCalls.flatMap((toolCall) => toolCall.providerExecuted ? [toolCall.toolCallId] : []), ...providerExecutedToolResults.keys()]),
1418
+ providerExecutedToolCallIds: /* @__PURE__ */ new Set([...processableToolCalls.flatMap((toolCall) => toolCall.providerExecuted ? [toolCall.toolCallId] : []), ...providerExecutedToolResults.keys()]),
1210
1419
  providerExecutedToolResultPositions
1211
1420
  });
1212
1421
  step.response.messages.push(...mode === "generate" ? toModelResponseMessages(responseMessages) : responseMessages);
1213
1422
  const stopConditionMet = (stopConditions == null ? [] : Array.isArray(stopConditions) ? stopConditions : [stopConditions]).some((test) => test({ steps }));
1214
1423
  const hasClientToolCalls = toolCalls.some((toolCall) => !toolCall.providerExecuted);
1215
- done = stopConditionMet || !hasClientToolCalls && pendingDeferredToolCallIds.size === 0;
1216
- } else if (mode === "generate" || finishReason === "stop" || finishReason === "tool-calls" || finishReason === "length") {
1424
+ done = !isToolExecutionAllowed || stopConditionMet || !hasClientToolCalls && pendingDeferredToolCallIds.size === 0;
1425
+ } else if (hasTerminalError || mode === "generate" || finishReason === "stop" || finishReason === "tool-calls" || finishReason === "length" || finishReason === "content-filter" || finishReason === "error" || finishReason === "other" || finishReason === "unknown" || !finishReason) {
1217
1426
  const { content: assistantContent } = getAssistantMessageContent(step, mode);
1218
1427
  if (assistantContent.length > 0) {
1219
1428
  const assistantMessage = {
@@ -1224,12 +1433,7 @@ async function* modelCallIterator({ prompt, mode = "stream", include, initialIns
1224
1433
  step.response.messages.push(...mode === "generate" ? toModelResponseMessages([assistantMessage]) : [assistantMessage]);
1225
1434
  }
1226
1435
  done = true;
1227
- } else if (finishReason === "content-filter") done = true;
1228
- else if (finishReason === "error") done = true;
1229
- else if (finishReason === "other") done = true;
1230
- else if (finishReason === "unknown") done = true;
1231
- else if (!finishReason) done = true;
1232
- else throw new Error(`Unexpected finish reason: ${typeof finish?.finishReason === "object" ? JSON.stringify(finish?.finishReason) : finish?.finishReason}`);
1436
+ } else throw new Error(`Unexpected finish reason: ${typeof finish?.finishReason === "object" ? JSON.stringify(finish?.finishReason) : finish?.finishReason}`);
1233
1437
  const resolvedOnStepEnd = onStepEnd ?? onStepFinish;
1234
1438
  if (resolvedOnStepEnd) await resolvedOnStepEnd(step);
1235
1439
  await telemetryDispatcher.onStepEnd?.(normalizeStepForTelemetry$1(step));
@@ -1617,7 +1821,7 @@ var WorkflowAgent = class {
1617
1821
  allowSystemInMessages: this.allowSystemInMessages,
1618
1822
  ...effectivePrompt != null ? { prompt: effectivePrompt } : { messages: effectiveMessages }
1619
1823
  });
1620
- const download = effectiveDownloadFromPrepare;
1824
+ const workflowDownload = async (requests) => requests.map(() => null);
1621
1825
  const sandbox = options.experimental_sandbox ?? this.experimentalSandbox;
1622
1826
  const abortSignalTimeout = isInWorkflow() ? void 0 : options.timeout;
1623
1827
  const effectiveAbortSignal = mergeAbortSignals(options.abortSignal ?? effectiveGenerationSettings.abortSignal, abortSignalTimeout);
@@ -1638,7 +1842,8 @@ var WorkflowAgent = class {
1638
1842
  effectiveTelemetry,
1639
1843
  telemetryDispatcher,
1640
1844
  prompt,
1641
- download,
1845
+ download: effectiveDownloadFromPrepare,
1846
+ workflowDownload,
1642
1847
  sandbox,
1643
1848
  effectiveAbortSignal,
1644
1849
  timeoutAt,
@@ -1647,7 +1852,7 @@ var WorkflowAgent = class {
1647
1852
  };
1648
1853
  }
1649
1854
  async execute(options, mode = "stream") {
1650
- const { onEnd, effectiveModel, effectiveInstructions, effectiveGenerationSettings, effectiveRuntimeContext, effectiveToolsContext, effectiveToolChoiceFromPrepare, effectiveStopWhenFromPrepare, effectiveActiveToolsFromPrepare, effectiveToolApprovalSecret, effectiveTelemetry, telemetryDispatcher, prompt, download, sandbox, effectiveAbortSignal, timeoutAt, mergedOnToolExecutionStart, mergedOnToolExecutionEnd } = await this.prepareInvocation(options, mode);
1855
+ const { onEnd, effectiveModel, effectiveInstructions, effectiveGenerationSettings, effectiveRuntimeContext, effectiveToolsContext, effectiveToolChoiceFromPrepare, effectiveStopWhenFromPrepare, effectiveActiveToolsFromPrepare, effectiveToolApprovalSecret, effectiveTelemetry, telemetryDispatcher, prompt, download, workflowDownload, sandbox, effectiveAbortSignal, timeoutAt, mergedOnToolExecutionStart, mergedOnToolExecutionEnd } = await this.prepareInvocation(options, mode);
1651
1856
  const initialResponseMessages = [];
1652
1857
  const abortReason = () => effectiveAbortSignal?.reason === void 0 ? void 0 : { value: effectiveAbortSignal.reason };
1653
1858
  const collectedApprovals = collectToolApprovals({ messages: prompt.messages });
@@ -1689,7 +1894,7 @@ var WorkflowAgent = class {
1689
1894
  tool,
1690
1895
  errorMode: "text",
1691
1896
  supportedUrls: {},
1692
- download
1897
+ download: workflowDownload
1693
1898
  })
1694
1899
  });
1695
1900
  continue;
@@ -1730,7 +1935,7 @@ var WorkflowAgent = class {
1730
1935
  tool,
1731
1936
  errorMode: "text",
1732
1937
  supportedUrls: {},
1733
- download
1938
+ download: workflowDownload
1734
1939
  })
1735
1940
  });
1736
1941
  continue;
@@ -1797,7 +2002,7 @@ var WorkflowAgent = class {
1797
2002
  const modelPrompt = await convertToLanguageModelPrompt({
1798
2003
  prompt,
1799
2004
  supportedUrls: {},
1800
- download
2005
+ download: workflowDownload
1801
2006
  });
1802
2007
  const mergedGenerationSettings = {
1803
2008
  ...effectiveGenerationSettings,
@@ -1894,7 +2099,7 @@ var WorkflowAgent = class {
1894
2099
  const startTime = Date.now();
1895
2100
  let result;
1896
2101
  try {
1897
- const execute = () => executeTool(toolCall, tools, messages, resolvedContext, effectiveAbortSignal, download, stepSandbox);
2102
+ const execute = () => executeTool(toolCall, tools, messages, resolvedContext, effectiveAbortSignal, workflowDownload, stepSandbox);
1898
2103
  result = telemetryDispatcher.executeTool != null ? await telemetryDispatcher.executeTool({
1899
2104
  callId: "workflow-agent",
1900
2105
  toolCallId: toolCall.toolCallId,
@@ -2029,6 +2234,7 @@ var WorkflowAgent = class {
2029
2234
  includeRawChunks: options.includeRawChunks ?? false,
2030
2235
  timeoutAt,
2031
2236
  repairToolCall: options.repairToolCall ?? options.experimental_repairToolCall ?? this.repairToolCall,
2237
+ download,
2032
2238
  responseFormat: await (options.output ?? this.output)?.responseFormat,
2033
2239
  experimental_transform: options.experimental_transform,
2034
2240
  experimental_sandbox: sandbox
@@ -2106,7 +2312,7 @@ var WorkflowAgent = class {
2106
2312
  const executableResults = await Promise.all(executableToolCalls.map((toolCall) => executeToolWithCallbacks(toolCall, stepTools, iterMessages, toolsContext, currentStepNumber, toolExecutionSandbox)));
2107
2313
  const providerResultEntries = await Promise.all(providerToolCallsForResults.filter((call) => !providerApprovalRequests.some((request) => request.toolCall.toolCallId === call.toolCallId)).map(async (toolCall) => ({
2108
2314
  toolCall,
2109
- result: await resolveProviderToolResult(toolCall, capturedProviderToolResults, stepTools, download)
2315
+ result: await resolveProviderToolResult(toolCall, capturedProviderToolResults, stepTools, workflowDownload)
2110
2316
  })));
2111
2317
  await Promise.all(providerResultEntries.flatMap(({ toolCall, result }) => result == null ? [] : [recordProviderExecutedToolTelemetry(toolCall, result, iterMessages, currentStepNumber)]));
2112
2318
  const providerResults = providerResultEntries.flatMap(({ result }) => result == null ? [] : [result]);
@@ -2204,7 +2410,7 @@ var WorkflowAgent = class {
2204
2410
  const clientToolResults = await Promise.all(nonProviderToolCalls.map((toolCall) => executeToolWithCallbacks(toolCall, stepTools, iterMessages, toolsContext, currentStepNumber, toolExecutionSandbox)));
2205
2411
  const providerToolResultEntries = await Promise.all(providerToolCallsForResults.map(async (toolCall) => ({
2206
2412
  toolCall,
2207
- result: await resolveProviderToolResult(toolCall, capturedProviderToolResults, stepTools, download)
2413
+ result: await resolveProviderToolResult(toolCall, capturedProviderToolResults, stepTools, workflowDownload)
2208
2414
  })));
2209
2415
  await Promise.all(providerToolResultEntries.flatMap(({ toolCall, result }) => result == null ? [] : [recordProviderExecutedToolTelemetry(toolCall, result, iterMessages, currentStepNumber)]));
2210
2416
  const providerToolResults = providerToolResultEntries.flatMap(({ result }) => result == null ? [] : [result]);