@ai-sdk/provider-utils 5.0.14 → 5.0.16

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ai-sdk/provider-utils
2
2
 
3
+ ## 5.0.16
4
+
5
+ ### Patch Changes
6
+
7
+ - d8210b6: chore: centralize record type guards in provider-utils
8
+ - b192878: feat: add experimental_toolCaller routing to generateText for code mode
9
+
10
+ ## 5.0.15
11
+
12
+ ### Patch Changes
13
+
14
+ - 1659cd5: Prevent validated downloads on Node.js from reaching private or internal services through DNS aliases or DNS rebinding by validating and pinning every resolved address at connection time.
15
+ - 6a5bdff: Fix validated Node.js downloads when the HTTP connector requests a single DNS address.
16
+
3
17
  ## 5.0.14
4
18
 
5
19
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -856,18 +856,16 @@ type FetchFunction = typeof globalThis.fetch;
856
856
  * The returned response is the final (non-redirect) response. The caller is
857
857
  * responsible for checking `response.ok` and reading the body.
858
858
  *
859
- * Not solved here: this does string/literal checks only and does not resolve
860
- * DNS, so a hostname that *resolves* to a private address, and DNS rebinding
861
- * (the resolved IP flipping between validation and connect), are not blocked.
862
- * Server deployments fetching untrusted URLs should constrain egress at the
863
- * network layer or inject a Node `fetch` that pins the resolved IP at connect
864
- * time — those need DNS/socket APIs not available on all target runtimes
865
- * (edge, browser, Bun), so they are intentionally not built in.
859
+ * On Node.js, the default fetch resolves every hostname through a validating
860
+ * lookup hook and passes those exact addresses to the connector, preventing
861
+ * hostname-to-private-IP and DNS-rebinding bypasses. An injected fetch is
862
+ * responsible for equivalent connect-time validation. Other runtimes should
863
+ * constrain egress at the network layer when handling untrusted URLs.
866
864
  *
867
865
  * @throws DownloadError if a hop is unsafe, the redirect limit is exceeded, or
868
866
  * a redirect cannot be validated on a non-browser runtime.
869
867
  */
870
- declare function fetchWithValidatedRedirects({ url, headers, abortSignal, maxRedirects, fetch, trustedOrigin, }: {
868
+ declare function fetchWithValidatedRedirects({ url, headers, abortSignal, maxRedirects, fetch: customFetch, trustedOrigin, }: {
871
869
  url: string;
872
870
  headers?: HeadersInit;
873
871
  abortSignal?: AbortSignal;
@@ -1193,6 +1191,11 @@ declare function isNonNullable<T>(value: T | undefined | null): value is NonNull
1193
1191
  */
1194
1192
  declare function isProviderReference(data: unknown): data is SharedV4ProviderReference;
1195
1193
 
1194
+ /**
1195
+ * Checks whether a value is a non-null, non-array object.
1196
+ */
1197
+ declare function isRecord(value: unknown): value is Record<string, unknown>;
1198
+
1196
1199
  /**
1197
1200
  * Checks if the given URL is supported natively by the model.
1198
1201
  *
@@ -2423,9 +2426,8 @@ declare function validateBaseURL(baseURL: string | undefined): string | undefine
2423
2426
  * Validates that a URL is safe to download from, blocking private/internal addresses
2424
2427
  * to prevent SSRF attacks.
2425
2428
  *
2426
- * Note: this performs string/literal-IP checks only. It does not resolve DNS, so a
2427
- * hostname that resolves to a private address is not blocked here (see callers, which
2428
- * should additionally constrain egress at the network layer when handling untrusted URLs).
2429
+ * Note: this function performs string/literal-IP checks only. The Node.js
2430
+ * download fetch additionally validates and pins DNS results at connect time.
2429
2431
  *
2430
2432
  * @param url - The URL string to validate.
2431
2433
  * @throws DownloadError if the URL is unsafe.
@@ -2607,6 +2609,20 @@ interface ToolCall<NAME extends string, INPUT> {
2607
2609
  dynamic?: boolean;
2608
2610
  }
2609
2611
 
2612
+ declare const toolCallerSymbol: unique symbol;
2613
+ type ToolCallerDefinition = {
2614
+ type: 'local';
2615
+ bind: (tools: ToolSet) => Tool;
2616
+ } | {
2617
+ type: 'provider';
2618
+ prepareProviderOptions: (providerOptions: ProviderOptions | undefined) => ProviderOptions;
2619
+ };
2620
+ type ToolCallerTool<TOOL extends Tool = Tool> = TOOL & {
2621
+ readonly [toolCallerSymbol]: ToolCallerDefinition;
2622
+ };
2623
+ declare function toolCaller<TOOL extends Tool>(tool: TOOL, definition: ToolCallerDefinition): ToolCallerTool<TOOL>;
2624
+ declare function getToolCaller(tool: Tool | undefined): ToolCallerDefinition | undefined;
2625
+
2610
2626
  /**
2611
2627
  * Typed tool result that is returned by `generateText` and `streamText`.
2612
2628
  * It contains the tool call ID, the tool name, the tool arguments, and the tool result.
@@ -2638,4 +2654,4 @@ interface ToolResult<NAME extends string, INPUT, OUTPUT> {
2638
2654
  dynamic?: boolean;
2639
2655
  }
2640
2656
 
2641
- export { type Arrayable, type AssistantContent, type AssistantModelMessage, type Context, type CustomPart, DEFAULT_MAX_DOWNLOAD_SIZE, type DataContent, DelayedPromise, DownloadError, type DynamicTool, TRANSCRIPTION_STREAM_AUDIO_DONE_FRAME_TYPE as EXPERIMENTAL_TRANSCRIPTION_STREAM_AUDIO_DONE_FRAME_TYPE, TRANSCRIPTION_STREAM_START_FRAME_TYPE as EXPERIMENTAL_TRANSCRIPTION_STREAM_START_FRAME_TYPE, type ExecutableTool, type SandboxProcess as Experimental_SandboxProcess, type SandboxSession as Experimental_SandboxSession, type TranscriptionStreamClientFrame as Experimental_TranscriptionStreamClientFrame, type TranscriptionStreamStartFrame as Experimental_TranscriptionStreamStartFrame, type FetchFunction, type FileData, type FileDataData, type FileDataReference, type FileDataText, type FileDataUrl, type FilePart, type FlexibleSchema, type FunctionTool, type HasRequiredKey, type IdGenerator, type ImagePart, type InferSchema, type InferToolContext, type InferToolInput, type InferToolOutput, type InferToolSetContext, type LazySchema, type MaybePromiseLike, type ModelMessage, type ParseResult, type ProviderDefinedTool, type ProviderDefinedToolFactory, type ProviderDefinedToolFactoryWithOutputSchema, type ProviderExecutedTool, type ProviderExecutedToolFactory, type ProviderOptions, type ProviderReference, type ReasoningFilePart, type ReasoningPart, type Resolvable, type ResponseHandler, type RetryDelayProvider, type RetryErrorFactory, type RetryErrorReason, type RetryFunction, type Schema, SerializationError, type ShouldRetryFunction, type StreamingToolCallDelta, StreamingToolCallTracker, type StreamingToolCallTrackerOptions, type SystemModelMessage, type TextPart, type Tool, type ToolApprovalRequest, type ToolApprovalResponse, type ToolCall, type ToolCallPart, type ToolContent, type ToolExecuteFunction, type ToolExecutionOptions, type ToolModelMessage, type ToolNameMapping, type ToolNeedsApprovalFunction, type ToolResult, type ToolResultOutput, type ToolResultPart, type ToolSet, type UserContent, type UserModelMessage, VERSION, type ValidationResult, type WebSocketConnection, type WebSocketConstructor, type WebSocketLike, asArray, asSchema, cancelResponseBody, combineHeaders, connectToWebSocket, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertImageModelFileToDataUri, convertInlineFileDataToUint8Array, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createEventSourceResponseHandler, createIdGenerator, createJsonErrorResponseHandler, createJsonResponseHandler, createProviderDefinedToolFactory, createProviderDefinedToolFactoryWithOutputSchema, createProviderExecutedToolFactory, createStatusCodeErrorResponseHandler, createToolNameMapping, delay, detectMediaType, downloadBlob, dynamicTool, executeTool, parseTranscriptionStreamClientFrame as experimental_parseTranscriptionStreamClientFrame, parseTranscriptionStreamPart as experimental_parseTranscriptionStreamPart, serializeTranscriptionStreamPart as experimental_serializeTranscriptionStreamPart, extractLines, extractResponseHeaders, fetchWithValidatedRedirects, filterNullable, generateId, getFromApi, getRuntimeEnvironmentUserAgent, getTopLevelMediaType, getWebSocketConstructor, injectJsonInstructionIntoMessages, isAbortError, isBrowserRuntime, isBuffer, isCustomReasoning, isExecutableTool, isFullMediaType, isNonNullable, isParsableJson, isProviderReference, isSameOrigin, isUrlSupported, jsonSchema, lazySchema, loadApiKey, loadOptionalSetting, loadSetting, mapReasoningToProviderBudget, mapReasoningToProviderEffort, mediaTypeToExtension, normalizeHeaders, parseJSON, parseJsonEventStream, parseProviderOptions, postFormDataToApi, postJsonToApi, postToApi, readResponseWithSizeLimit, readWebSocketMessageText, removeUndefinedEntries, resolve, resolveFullMediaType, resolveProviderReference, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, secureJsonParse, serializeModelOptions, stripFileExtension, toWebSocketUrl, tool, validateBaseURL, validateDownloadUrl, validateTypes, waitForWebSocketBufferDrain, withUserAgentSuffix, withoutTrailingSlash, zodSchema };
2657
+ export { type Arrayable, type AssistantContent, type AssistantModelMessage, type Context, type CustomPart, DEFAULT_MAX_DOWNLOAD_SIZE, type DataContent, DelayedPromise, DownloadError, type DynamicTool, TRANSCRIPTION_STREAM_AUDIO_DONE_FRAME_TYPE as EXPERIMENTAL_TRANSCRIPTION_STREAM_AUDIO_DONE_FRAME_TYPE, TRANSCRIPTION_STREAM_START_FRAME_TYPE as EXPERIMENTAL_TRANSCRIPTION_STREAM_START_FRAME_TYPE, type ExecutableTool, type SandboxProcess as Experimental_SandboxProcess, type SandboxSession as Experimental_SandboxSession, type ToolCallerDefinition as Experimental_ToolCallerDefinition, type ToolCallerTool as Experimental_ToolCallerTool, type TranscriptionStreamClientFrame as Experimental_TranscriptionStreamClientFrame, type TranscriptionStreamStartFrame as Experimental_TranscriptionStreamStartFrame, type FetchFunction, type FileData, type FileDataData, type FileDataReference, type FileDataText, type FileDataUrl, type FilePart, type FlexibleSchema, type FunctionTool, type HasRequiredKey, type IdGenerator, type ImagePart, type InferSchema, type InferToolContext, type InferToolInput, type InferToolOutput, type InferToolSetContext, type LazySchema, type MaybePromiseLike, type ModelMessage, type ParseResult, type ProviderDefinedTool, type ProviderDefinedToolFactory, type ProviderDefinedToolFactoryWithOutputSchema, type ProviderExecutedTool, type ProviderExecutedToolFactory, type ProviderOptions, type ProviderReference, type ReasoningFilePart, type ReasoningPart, type Resolvable, type ResponseHandler, type RetryDelayProvider, type RetryErrorFactory, type RetryErrorReason, type RetryFunction, type Schema, SerializationError, type ShouldRetryFunction, type StreamingToolCallDelta, StreamingToolCallTracker, type StreamingToolCallTrackerOptions, type SystemModelMessage, type TextPart, type Tool, type ToolApprovalRequest, type ToolApprovalResponse, type ToolCall, type ToolCallPart, type ToolContent, type ToolExecuteFunction, type ToolExecutionOptions, type ToolModelMessage, type ToolNameMapping, type ToolNeedsApprovalFunction, type ToolResult, type ToolResultOutput, type ToolResultPart, type ToolSet, type UserContent, type UserModelMessage, VERSION, type ValidationResult, type WebSocketConnection, type WebSocketConstructor, type WebSocketLike, asArray, asSchema, cancelResponseBody, combineHeaders, connectToWebSocket, convertAsyncIteratorToReadableStream, convertBase64ToUint8Array, convertImageModelFileToDataUri, convertInlineFileDataToUint8Array, convertToBase64, convertToFormData, convertUint8ArrayToBase64, createBinaryResponseHandler, createEventSourceResponseHandler, createIdGenerator, createJsonErrorResponseHandler, createJsonResponseHandler, createProviderDefinedToolFactory, createProviderDefinedToolFactoryWithOutputSchema, createProviderExecutedToolFactory, createStatusCodeErrorResponseHandler, createToolNameMapping, delay, detectMediaType, downloadBlob, dynamicTool, executeTool, getToolCaller as experimental_getToolCaller, parseTranscriptionStreamClientFrame as experimental_parseTranscriptionStreamClientFrame, parseTranscriptionStreamPart as experimental_parseTranscriptionStreamPart, serializeTranscriptionStreamPart as experimental_serializeTranscriptionStreamPart, toolCaller as experimental_toolCaller, extractLines, extractResponseHeaders, fetchWithValidatedRedirects, filterNullable, generateId, getFromApi, getRuntimeEnvironmentUserAgent, getTopLevelMediaType, getWebSocketConstructor, injectJsonInstructionIntoMessages, isAbortError, isBrowserRuntime, isBuffer, isCustomReasoning, isExecutableTool, isFullMediaType, isNonNullable, isParsableJson, isProviderReference, isRecord, isSameOrigin, isUrlSupported, jsonSchema, lazySchema, loadApiKey, loadOptionalSetting, loadSetting, mapReasoningToProviderBudget, mapReasoningToProviderEffort, mediaTypeToExtension, normalizeHeaders, parseJSON, parseJsonEventStream, parseProviderOptions, postFormDataToApi, postJsonToApi, postToApi, readResponseWithSizeLimit, readWebSocketMessageText, removeUndefinedEntries, resolve, resolveFullMediaType, resolveProviderReference, retryWithExponentialBackoff, safeParseJSON, safeValidateTypes, secureJsonParse, serializeModelOptions, stripFileExtension, toWebSocketUrl, tool, validateBaseURL, validateDownloadUrl, validateTypes, waitForWebSocketBufferDrain, withUserAgentSuffix, withoutTrailingSlash, zodSchema };
package/dist/index.js CHANGED
@@ -686,42 +686,6 @@ function isSameOrigin(url, baseUrl) {
686
686
  }
687
687
  }
688
688
 
689
- // src/sanitize-request-headers.ts
690
- var BLOCKED_REQUEST_HEADERS = [
691
- // Hop-by-hop / transport (RFC 7230 §6.1)
692
- "connection",
693
- "keep-alive",
694
- "te",
695
- "trailer",
696
- "transfer-encoding",
697
- "upgrade",
698
- // Host / virtual-host routing
699
- "host",
700
- // Proxy / origin spoofing
701
- "forwarded",
702
- "proxy-authorization",
703
- "via",
704
- "x-forwarded-for",
705
- "x-forwarded-host",
706
- "x-forwarded-proto",
707
- "x-real-ip",
708
- // Cloud metadata (GCP, AWS IMDSv1/v2, Azure, Alibaba, DigitalOcean)
709
- "metadata",
710
- "metadata-flavor",
711
- "x-aws-ec2-metadata-token",
712
- "x-metadata-token",
713
- // Session / cookie
714
- "cookie",
715
- "set-cookie"
716
- ];
717
- function sanitizeRequestHeaders(input) {
718
- const headers = new Headers(input);
719
- for (const name3 of BLOCKED_REQUEST_HEADERS) {
720
- headers.delete(name3);
721
- }
722
- return headers;
723
- }
724
-
725
689
  // src/validate-download-url.ts
726
690
  function validateDownloadUrl(url) {
727
691
  let parsed;
@@ -775,6 +739,19 @@ function validateDownloadUrl(url) {
775
739
  return;
776
740
  }
777
741
  }
742
+ function validateDownloadAddress({
743
+ address,
744
+ family,
745
+ hostname
746
+ }) {
747
+ const isUnsafe = family === 4 ? !isIPv4(address) || isPrivateIPv4(address) : family === 6 ? isPrivateIPv6(address) : true;
748
+ if (isUnsafe) {
749
+ throw new DownloadError({
750
+ url: hostname,
751
+ message: `Hostname ${hostname} resolved to disallowed IP address ${address}`
752
+ });
753
+ }
754
+ }
778
755
  function isIPv4(hostname) {
779
756
  const parts = hostname.split(".");
780
757
  if (parts.length !== 4) return false;
@@ -866,6 +843,138 @@ function isPrivateIPv6(ip) {
866
843
  return false;
867
844
  }
868
845
 
846
+ // src/safe-node-fetch.ts
847
+ function createSafeLookup(lookup) {
848
+ return ((hostname, options, callback) => {
849
+ lookup(hostname, { ...options, all: true }, (error, addresses) => {
850
+ if (error) {
851
+ callback(error);
852
+ return;
853
+ }
854
+ try {
855
+ const [firstAddress] = addresses;
856
+ if (firstAddress == null) {
857
+ throw new Error(`Hostname ${hostname} did not resolve to an address`);
858
+ }
859
+ for (const { address, family } of addresses) {
860
+ validateDownloadAddress({ address, family, hostname });
861
+ }
862
+ if (options.all === true) {
863
+ callback(null, addresses);
864
+ } else {
865
+ callback(
866
+ null,
867
+ firstAddress.address,
868
+ firstAddress.family
869
+ );
870
+ }
871
+ } catch (error2) {
872
+ callback(
873
+ error2 instanceof Error ? error2 : new Error(String(error2))
874
+ );
875
+ }
876
+ });
877
+ });
878
+ }
879
+ var safeNodeFetchPromise;
880
+ var initialGlobalFetch = globalThis.fetch;
881
+ var initialGlobalFetchIsNodeDefault = isNodeDefaultFetch(initialGlobalFetch);
882
+ function isNodeRuntime() {
883
+ var _a3, _b3;
884
+ const runtimeProcess = globalThis.process;
885
+ return ((_a3 = runtimeProcess == null ? void 0 : runtimeProcess.release) == null ? void 0 : _a3.name) === "node" && ((_b3 = runtimeProcess.versions) == null ? void 0 : _b3.bun) == null;
886
+ }
887
+ async function getDefaultDownloadFetch() {
888
+ if (!isNodeRuntime() || !initialGlobalFetchIsNodeDefault || globalThis.fetch !== initialGlobalFetch) {
889
+ return globalThis.fetch;
890
+ }
891
+ return safeNodeFetchPromise != null ? safeNodeFetchPromise : safeNodeFetchPromise = Promise.resolve().then(createSafeNodeFetch);
892
+ }
893
+ function isNodeDefaultFetch(fetch) {
894
+ const source = Function.prototype.toString.call(fetch);
895
+ return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
896
+ }
897
+ function createSafeNodeFetch() {
898
+ const { createRequire } = loadBuiltinModule("node:module");
899
+ const { lookup } = loadBuiltinModule("node:dns");
900
+ const { Agent, fetch } = createRequire(getCurrentModulePath())(
901
+ "undici"
902
+ );
903
+ const dispatcher = new Agent({
904
+ connect: {
905
+ lookup: createSafeLookup(lookup)
906
+ }
907
+ });
908
+ return ((input, init) => fetch(
909
+ input,
910
+ {
911
+ ...init,
912
+ dispatcher
913
+ }
914
+ ));
915
+ }
916
+ function loadBuiltinModule(id) {
917
+ var _a3;
918
+ const processWithBuiltins = globalThis.process;
919
+ const builtinModule = (_a3 = processWithBuiltins == null ? void 0 : processWithBuiltins.getBuiltinModule) == null ? void 0 : _a3.call(processWithBuiltins, id);
920
+ if (builtinModule == null) {
921
+ throw new Error(`Node.js built-in module ${id} is unavailable`);
922
+ }
923
+ return builtinModule;
924
+ }
925
+ function getCurrentModulePath() {
926
+ const originalPrepareStackTrace = Error.prepareStackTrace;
927
+ try {
928
+ Error.prepareStackTrace = (_error, callSites) => callSites;
929
+ const error = new Error("Capture current module path");
930
+ Error.captureStackTrace(error, getCurrentModulePath);
931
+ const [caller] = error.stack;
932
+ const fileName = caller == null ? void 0 : caller.getFileName();
933
+ if (fileName == null) {
934
+ throw new Error("Unable to determine the current module path");
935
+ }
936
+ return fileName;
937
+ } finally {
938
+ Error.prepareStackTrace = originalPrepareStackTrace;
939
+ }
940
+ }
941
+
942
+ // src/sanitize-request-headers.ts
943
+ var BLOCKED_REQUEST_HEADERS = [
944
+ // Hop-by-hop / transport (RFC 7230 §6.1)
945
+ "connection",
946
+ "keep-alive",
947
+ "te",
948
+ "trailer",
949
+ "transfer-encoding",
950
+ "upgrade",
951
+ // Host / virtual-host routing
952
+ "host",
953
+ // Proxy / origin spoofing
954
+ "forwarded",
955
+ "proxy-authorization",
956
+ "via",
957
+ "x-forwarded-for",
958
+ "x-forwarded-host",
959
+ "x-forwarded-proto",
960
+ "x-real-ip",
961
+ // Cloud metadata (GCP, AWS IMDSv1/v2, Azure, Alibaba, DigitalOcean)
962
+ "metadata",
963
+ "metadata-flavor",
964
+ "x-aws-ec2-metadata-token",
965
+ "x-metadata-token",
966
+ // Session / cookie
967
+ "cookie",
968
+ "set-cookie"
969
+ ];
970
+ function sanitizeRequestHeaders(input) {
971
+ const headers = new Headers(input);
972
+ for (const name3 of BLOCKED_REQUEST_HEADERS) {
973
+ headers.delete(name3);
974
+ }
975
+ return headers;
976
+ }
977
+
869
978
  // src/fetch-with-validated-redirects.ts
870
979
  var MAX_DOWNLOAD_REDIRECTS = 10;
871
980
  var REDIRECT_STATUS_CODES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
@@ -874,7 +983,7 @@ async function fetchWithValidatedRedirects({
874
983
  headers,
875
984
  abortSignal,
876
985
  maxRedirects = MAX_DOWNLOAD_REDIRECTS,
877
- fetch = globalThis.fetch,
986
+ fetch: customFetch,
878
987
  trustedOrigin
879
988
  }) {
880
989
  let currentHeaders = headers === void 0 ? void 0 : sanitizeRequestHeaders(headers);
@@ -887,9 +996,11 @@ async function fetchWithValidatedRedirects({
887
996
  };
888
997
  let currentUrl = url;
889
998
  for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
890
- if (trustedOrigin === void 0 || !isSameOrigin(currentUrl, trustedOrigin)) {
999
+ const isTrustedHop = trustedOrigin !== void 0 && isSameOrigin(currentUrl, trustedOrigin);
1000
+ if (!isTrustedHop) {
891
1001
  validateDownloadUrl(currentUrl);
892
1002
  }
1003
+ const fetch = customFetch != null ? customFetch : isTrustedHop ? globalThis.fetch : await getDefaultDownloadFetch();
893
1004
  const response = await fetch(currentUrl, perHopInit("manual"));
894
1005
  if (response.type === "opaqueredirect") {
895
1006
  if (!isBrowserRuntime()) {
@@ -1183,7 +1294,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
1183
1294
  }
1184
1295
 
1185
1296
  // src/version.ts
1186
- var VERSION = true ? "5.0.14" : "0.0.0-test";
1297
+ var VERSION = true ? "5.0.16" : "0.0.0-test";
1187
1298
 
1188
1299
  // src/get-from-api.ts
1189
1300
  var getOriginalFetch = () => globalThis.fetch;
@@ -1193,12 +1304,13 @@ var getFromApi = async ({
1193
1304
  successfulResponseHandler,
1194
1305
  failedResponseHandler,
1195
1306
  abortSignal,
1196
- fetch = getOriginalFetch(),
1307
+ fetch,
1197
1308
  validateUrl,
1198
1309
  credentialedOrigin,
1199
1310
  trustedOrigin
1200
1311
  }) => {
1201
1312
  try {
1313
+ const requestFetch = fetch != null ? fetch : getOriginalFetch();
1202
1314
  const outgoingHeaders = credentialedOrigin !== void 0 && !isSameOrigin(url, credentialedOrigin) ? {} : headers;
1203
1315
  const requestHeaders = withUserAgentSuffix(
1204
1316
  outgoingHeaders,
@@ -1211,7 +1323,7 @@ var getFromApi = async ({
1211
1323
  abortSignal,
1212
1324
  fetch,
1213
1325
  trustedOrigin
1214
- }) : await fetch(url, {
1326
+ }) : await requestFetch(url, {
1215
1327
  method: "GET",
1216
1328
  headers: requestHeaders,
1217
1329
  signal: abortSignal
@@ -1321,6 +1433,11 @@ function isProviderReference(data) {
1321
1433
  return typeof data === "object" && data !== null && !(data instanceof Uint8Array) && !(data instanceof URL) && !(data instanceof ArrayBuffer) && !isBuffer(data) && !("type" in data);
1322
1434
  }
1323
1435
 
1436
+ // src/is-record.ts
1437
+ function isRecord(value) {
1438
+ return value != null && typeof value === "object" && !Array.isArray(value);
1439
+ }
1440
+
1324
1441
  // src/is-url-supported.ts
1325
1442
  function isUrlSupported({
1326
1443
  mediaType,
@@ -3806,15 +3923,15 @@ function parseTranscriptionStreamPart(text) {
3806
3923
  case "stream-start":
3807
3924
  return Array.isArray(part.warnings) && part.warnings.every(isWarning) ? part : void 0;
3808
3925
  case "transcript-delta":
3809
- return isString(part.delta) && isOptional(part.id, isString) && isOptional(part.providerMetadata, isPlainObject) ? part : void 0;
3926
+ return isString(part.delta) && isOptional(part.id, isString) && isOptional(part.providerMetadata, isRecord) ? part : void 0;
3810
3927
  case "transcript-partial":
3811
- return isString(part.text) && isOptional(part.id, isString) && isOptional(part.startSecond, isNumber) && isOptional(part.durationInSeconds, isNumber) && isOptional(part.channelIndex, isNumber) && isOptional(part.providerMetadata, isPlainObject) ? part : void 0;
3928
+ return isString(part.text) && isOptional(part.id, isString) && isOptional(part.startSecond, isNumber) && isOptional(part.durationInSeconds, isNumber) && isOptional(part.channelIndex, isNumber) && isOptional(part.providerMetadata, isRecord) ? part : void 0;
3812
3929
  case "transcript-final":
3813
- return isString(part.text) && isOptional(part.id, isString) && isOptional(part.startSecond, isNumber) && isOptional(part.endSecond, isNumber) && isOptional(part.channelIndex, isNumber) && isOptional(part.providerMetadata, isPlainObject) ? part : void 0;
3930
+ return isString(part.text) && isOptional(part.id, isString) && isOptional(part.startSecond, isNumber) && isOptional(part.endSecond, isNumber) && isOptional(part.channelIndex, isNumber) && isOptional(part.providerMetadata, isRecord) ? part : void 0;
3814
3931
  case "finish":
3815
- return isString(part.text) && Array.isArray(part.segments) && part.segments.every(isSegment) && isOptional(part.language, isString) && isOptional(part.durationInSeconds, isNumber) && isOptional(part.providerMetadata, isPlainObject) ? part : void 0;
3932
+ return isString(part.text) && Array.isArray(part.segments) && part.segments.every(isSegment) && isOptional(part.language, isString) && isOptional(part.durationInSeconds, isNumber) && isOptional(part.providerMetadata, isRecord) ? part : void 0;
3816
3933
  case "response-metadata": {
3817
- if (!(isOptional(part.modelId, isString) && isOptional(part.headers, isPlainObject))) {
3934
+ if (!(isOptional(part.modelId, isString) && isOptional(part.headers, isRecord))) {
3818
3935
  return void 0;
3819
3936
  }
3820
3937
  const timestamp = part.timestamp;
@@ -3844,14 +3961,11 @@ function isNumber(value) {
3844
3961
  function isOptional(value, check) {
3845
3962
  return value === void 0 || check(value);
3846
3963
  }
3847
- function isPlainObject(value) {
3848
- return typeof value === "object" && value != null && !Array.isArray(value);
3849
- }
3850
3964
  function isWarning(value) {
3851
- return isPlainObject(value) && isString(value.type);
3965
+ return isRecord(value) && isString(value.type);
3852
3966
  }
3853
3967
  function isSegment(value) {
3854
- return isPlainObject(value) && isString(value.text) && isNumber(value.startSecond) && isNumber(value.endSecond);
3968
+ return isRecord(value) && isString(value.text) && isNumber(value.startSecond) && isNumber(value.endSecond);
3855
3969
  }
3856
3970
 
3857
3971
  // src/validate-base-url.ts
@@ -3900,6 +4014,17 @@ async function* executeTool({
3900
4014
  }
3901
4015
  }
3902
4016
 
4017
+ // src/types/tool-caller.ts
4018
+ var toolCallerSymbol = /* @__PURE__ */ Symbol.for("vercel.ai.experimental.toolCaller");
4019
+ function toolCaller(tool2, definition) {
4020
+ return Object.defineProperty({ ...tool2 }, toolCallerSymbol, {
4021
+ value: definition
4022
+ });
4023
+ }
4024
+ function getToolCaller(tool2) {
4025
+ return tool2 == null ? void 0 : tool2[toolCallerSymbol];
4026
+ }
4027
+
3903
4028
  // src/index.ts
3904
4029
  import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";
3905
4030
  import {
@@ -3944,9 +4069,11 @@ export {
3944
4069
  downloadBlob,
3945
4070
  dynamicTool,
3946
4071
  executeTool,
4072
+ getToolCaller as experimental_getToolCaller,
3947
4073
  parseTranscriptionStreamClientFrame as experimental_parseTranscriptionStreamClientFrame,
3948
4074
  parseTranscriptionStreamPart as experimental_parseTranscriptionStreamPart,
3949
4075
  serializeTranscriptionStreamPart as experimental_serializeTranscriptionStreamPart,
4076
+ toolCaller as experimental_toolCaller,
3950
4077
  extractLines,
3951
4078
  extractResponseHeaders,
3952
4079
  fetchWithValidatedRedirects,
@@ -3967,6 +4094,7 @@ export {
3967
4094
  isNonNullable,
3968
4095
  isParsableJson,
3969
4096
  isProviderReference,
4097
+ isRecord,
3970
4098
  isSameOrigin,
3971
4099
  isUrlSupported,
3972
4100
  jsonSchema,