| import { asSchema, type ToolSet } from '@ai-sdk/provider-utils'; | ||
| import { hashCanonical } from '../util/canonical-hash'; | ||
| /** | ||
| * Tag a tool description for hashing. A function description is developer-owned | ||
| * (evaluated per call from local context), not a server-controlled "rug pull" | ||
| * vector, so only its presence is pinned, not its identity. The tagged shape | ||
| * keeps a literal string equal to some placeholder from ever hashing like a | ||
| * function. | ||
| */ | ||
| function tagDescription(description: unknown) { | ||
| if (typeof description === 'string') { | ||
| return { type: 'string', value: description } as const; | ||
| } | ||
| if (description == null) { | ||
| return { type: 'none' } as const; | ||
| } | ||
| return { type: 'function' } as const; | ||
| } | ||
| /** | ||
| * Fingerprint the server-controlled, security-relevant fields of each tool in a | ||
| * `ToolSet`: `description` (string form only), the resolved input JSON schema, | ||
| * and `title`. Returns a map of tool name to a stable digest. | ||
| * | ||
| * Capture a baseline at trust time (first connect, human-reviewed) and compare | ||
| * later fetches with {@link detectToolDrift} to catch MCP tool-definition drift | ||
| * ("rug pull"). Baseline storage and the drift response are the app's concern. | ||
| */ | ||
| export async function fingerprintTools( | ||
| tools: ToolSet, | ||
| ): Promise<Record<string, string>> { | ||
| const entries = await Promise.all( | ||
| Object.keys(tools).map(async name => { | ||
| const tool = tools[name]; | ||
| const digest = await hashCanonical({ | ||
| description: tagDescription(tool.description), | ||
| inputSchema: await asSchema(tool.inputSchema).jsonSchema, | ||
| title: tool.title, | ||
| }); | ||
| return [name, digest] as const; | ||
| }), | ||
| ); | ||
| return Object.fromEntries(entries); | ||
| } | ||
| /** | ||
| * Pure diff of two fingerprint maps produced by {@link fingerprintTools}. | ||
| * `added`/`removed` are tools present in only one map; `changed` are tools whose | ||
| * pinned definition differs. Uses own-property lookups so a tool literally named | ||
| * `constructor` or `toString` diffs correctly. | ||
| */ | ||
| export function detectToolDrift( | ||
| current: Record<string, string>, | ||
| baseline: Record<string, string>, | ||
| ): { added: string[]; removed: string[]; changed: string[] } { | ||
| const added: string[] = []; | ||
| const removed: string[] = []; | ||
| const changed: string[] = []; | ||
| for (const name of Object.keys(current)) { | ||
| if (!Object.hasOwn(baseline, name)) { | ||
| added.push(name); | ||
| } else if (current[name] !== baseline[name]) { | ||
| changed.push(name); | ||
| } | ||
| } | ||
| for (const name of Object.keys(baseline)) { | ||
| if (!Object.hasOwn(current, name)) { | ||
| removed.push(name); | ||
| } | ||
| } | ||
| return { added, removed, changed }; | ||
| } |
| import { convertUint8ArrayToBase64 } from '@ai-sdk/provider-utils'; | ||
| const encoder = new TextEncoder(); | ||
| /** | ||
| * Deterministic JSON serialization: object keys are sorted so that two | ||
| * structurally-equal values always produce the same string regardless of key | ||
| * insertion order. Used as the input to content hashing. | ||
| */ | ||
| export function canonicalJSON(value: unknown): string { | ||
| if (value === null || value === undefined) { | ||
| return JSON.stringify(value); | ||
| } | ||
| if (typeof value !== 'object') { | ||
| return JSON.stringify(value); | ||
| } | ||
| if (Array.isArray(value)) { | ||
| return `[${value.map(canonicalJSON).join(',')}]`; | ||
| } | ||
| const keys = Object.keys(value as Record<string, unknown>).sort(); | ||
| const entries = keys.map( | ||
| k => | ||
| `${JSON.stringify(k)}:${canonicalJSON((value as Record<string, unknown>)[k])}`, | ||
| ); | ||
| return `{${entries.join(',')}}`; | ||
| } | ||
| export function toBase64url(bytes: Uint8Array): string { | ||
| return convertUint8ArrayToBase64(bytes) | ||
| .replace(/\+/g, '-') | ||
| .replace(/\//g, '_') | ||
| .replace(/=+$/g, ''); | ||
| } | ||
| /** | ||
| * Canonical SHA-256 digest (base64url) of an arbitrary JSON-serializable value. | ||
| */ | ||
| export async function hashCanonical(value: unknown): Promise<string> { | ||
| const digest = await crypto.subtle.digest( | ||
| 'SHA-256', | ||
| encoder.encode(canonicalJSON(value)), | ||
| ); | ||
| return toBase64url(new Uint8Array(digest)); | ||
| } |
| /** | ||
| * Reads a property by an untrusted key, ignoring inherited prototype members. | ||
| * | ||
| * Tool sets, tool contexts, and similar lookup objects are indexed by names | ||
| * that can come from model output or client-supplied message history. Plain | ||
| * bracket access (`obj[name]`) resolves names such as `constructor`, | ||
| * `toString`, or `__proto__` to values on `Object.prototype`, which would slip | ||
| * past the `== null` / `!value` guards that treat an unknown name as "not | ||
| * present". This helper returns `undefined` unless `key` is an own property. | ||
| */ | ||
| export function getOwn<T extends object>( | ||
| obj: T | undefined | null, | ||
| key: string, | ||
| ): T[keyof T] | undefined { | ||
| return obj != null && Object.hasOwn(obj, key) | ||
| ? obj[key as keyof T] | ||
| : undefined; | ||
| } |
+4
-4
| { | ||
| "name": "ai", | ||
| "version": "7.0.18", | ||
| "version": "7.0.19", | ||
| "type": "module", | ||
@@ -45,5 +45,5 @@ "description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.", | ||
| "dependencies": { | ||
| "@ai-sdk/gateway": "4.0.14", | ||
| "@ai-sdk/provider": "4.0.2", | ||
| "@ai-sdk/provider-utils": "5.0.6" | ||
| "@ai-sdk/gateway": "4.0.15", | ||
| "@ai-sdk/provider": "4.0.3", | ||
| "@ai-sdk/provider-utils": "5.0.7" | ||
| }, | ||
@@ -50,0 +50,0 @@ "devDependencies": { |
@@ -39,4 +39,15 @@ import type { | ||
| // gather tool calls and prepare lookup | ||
| const toolCallsByToolCallId: Record<string, TypedToolCall<TOOLS>> = {}; | ||
| // gather tool calls and prepare lookup. | ||
| // | ||
| // These maps are keyed by client-supplied ids (`toolCallId`, `approvalId`) | ||
| // from the message history. Using `Object.create(null)` gives them no | ||
| // prototype, so an id that matches an inherited object property (e.g. | ||
| // `toString`, `constructor`, `__proto__`) is treated as absent instead of | ||
| // resolving to a prototype value and slipping past the `== null` guards | ||
| // below (which would otherwise skip the InvalidToolApproval / | ||
| // ToolCallNotFound checks). | ||
| const toolCallsByToolCallId: Record< | ||
| string, | ||
| TypedToolCall<TOOLS> | ||
| > = Object.create(null); | ||
| for (const message of messages) { | ||
@@ -55,3 +66,3 @@ if (message.role === 'assistant' && typeof message.content !== 'string') { | ||
| const toolApprovalRequestsByApprovalId: Record<string, ToolApprovalRequest> = | ||
| {}; | ||
| Object.create(null); | ||
| for (const message of messages) { | ||
@@ -69,3 +80,5 @@ if (message.role === 'assistant' && typeof message.content !== 'string') { | ||
| // gather tool results from the last tool message | ||
| const toolResults: Record<string, TypedToolResult<TOOLS>> = {}; | ||
| const toolResults: Record<string, TypedToolResult<TOOLS>> = Object.create( | ||
| null, | ||
| ); | ||
| for (const part of lastMessage.content) { | ||
@@ -72,0 +85,0 @@ if (part.type === 'tool-result') { |
@@ -16,2 +16,3 @@ import { | ||
| import type { TelemetryDispatcher } from '../telemetry/telemetry'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import { mergeAbortSignals } from '../util/merge-abort-signals'; | ||
@@ -87,3 +88,3 @@ import { notify } from '../util/notify'; | ||
| const { toolName, toolCallId, input } = toolCall; | ||
| const tool = tools?.[toolName]; | ||
| const tool = getOwn(tools, toolName); | ||
@@ -96,3 +97,3 @@ if (!isExecutableTool(tool)) { | ||
| toolName, | ||
| context: toolsContext?.[toolName as keyof typeof toolsContext], | ||
| context: getOwn(toolsContext, toolName), | ||
| contextSchema: tool.contextSchema, | ||
@@ -99,0 +100,0 @@ }); |
@@ -12,2 +12,3 @@ import type { | ||
| import type { Telemetry, TelemetryDispatcher } from '../telemetry/telemetry'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import { executeToolCall } from './execute-tool-call'; | ||
@@ -99,3 +100,3 @@ import { resolveToolApproval } from './resolve-tool-approval'; | ||
| const tool = tools?.[chunk.toolName]; | ||
| const tool = getOwn(tools, chunk.toolName); | ||
@@ -102,0 +103,0 @@ if (tool == null) { |
@@ -83,2 +83,3 @@ export type { ActiveTools } from './active-tools'; | ||
| } from './tool-approval-configuration'; | ||
| export { detectToolDrift, fingerprintTools } from './tool-fingerprint'; | ||
| export type { ToolApprovalRequestOutput } from './tool-approval-request-output'; | ||
@@ -85,0 +86,0 @@ export type { ToolApprovalResponseOutput } from './tool-approval-response-output'; |
| import type { Context, ModelMessage, ToolSet } from '@ai-sdk/provider-utils'; | ||
| import { createIdMap } from '../util/create-id-map'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import type { LanguageModelStreamPart } from './stream-language-model-call'; | ||
@@ -22,3 +24,3 @@ | ||
| const ongoingToolCallToolNames: Record<string, string> = {}; | ||
| const ongoingToolCallToolNames: Record<string, string> = createIdMap(); | ||
@@ -34,3 +36,3 @@ return stream.pipeThrough( | ||
| const tool = tools?.[chunk.toolName]; | ||
| const tool = getOwn(tools, chunk.toolName); | ||
| if (tool?.onInputStart != null) { | ||
@@ -50,3 +52,3 @@ await tool.onInputStart({ | ||
| const toolName = ongoingToolCallToolNames[chunk.id]; | ||
| const tool = tools?.[toolName]; | ||
| const tool = getOwn(tools, toolName); | ||
@@ -68,3 +70,3 @@ if (tool?.onInputDelta != null) { | ||
| const toolName = ongoingToolCallToolNames[chunk.toolCallId]; | ||
| const tool = tools?.[toolName]; | ||
| const tool = getOwn(tools, toolName); | ||
@@ -71,0 +73,0 @@ delete ongoingToolCallToolNames[chunk.toolCallId]; |
@@ -14,2 +14,3 @@ import type { LanguageModelV4ToolCall } from '@ai-sdk/provider'; | ||
| import type { Instructions } from '../prompt'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import type { DynamicToolCall, TypedToolCall } from './tool-call'; | ||
@@ -70,3 +71,3 @@ import type { ToolCallRepairFunction } from './tool-call-repair-function'; | ||
| inputSchema: async ({ toolName }) => { | ||
| const { inputSchema } = tools[toolName]; | ||
| const inputSchema = getOwn(tools, toolName)?.inputSchema; | ||
| return await asSchema(inputSchema).jsonSchema; | ||
@@ -100,3 +101,3 @@ }, | ||
| const input = parsedInput.success ? parsedInput.value : toolCall.input; | ||
| const tool = tools?.[toolCall.toolName]; | ||
| const tool = getOwn(tools, toolCall.toolName); | ||
@@ -127,3 +128,3 @@ // TODO AI SDK 6: special invalid tool call parts | ||
| }): Promise<TypedToolCall<TOOLS>> { | ||
| const refine = refineToolInput?.[toolCall.toolName]; | ||
| const refine = getOwn(refineToolInput, toolCall.toolName); | ||
@@ -176,3 +177,3 @@ if (refine == null) { | ||
| const tool = tools[toolName]; | ||
| const tool = getOwn(tools, toolName); | ||
@@ -179,0 +180,0 @@ if (tool == null) { |
@@ -12,2 +12,3 @@ import type { | ||
| } from './tool-approval-configuration'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import type { TypedToolCall } from './tool-call'; | ||
@@ -79,3 +80,7 @@ import { validateToolContext } from './validate-tool-context'; | ||
| const toolName = toolCall.toolName; | ||
| const tool = tools?.[toolName]; | ||
| // `toolName` can be client-controlled, so look tools/approvals up by own | ||
| // property only: a name that matches an inherited object property (e.g. | ||
| // `constructor`, `toString`) resolves to "no such tool"/"unconfigured" | ||
| // instead of a value on `Object.prototype`. | ||
| const tool = getOwn(tools, toolName); | ||
@@ -86,3 +91,3 @@ // assume that the input has been validated and matches the tool's input schema | ||
| // user-defined per-tool approval | ||
| const userDefinedToolApprovalStatus = toolApproval?.[toolName]; | ||
| const userDefinedToolApprovalStatus = getOwn(toolApproval, toolName); | ||
| if (userDefinedToolApprovalStatus != null) { | ||
@@ -96,4 +101,3 @@ const approvalStatus: ToolApprovalStatus | undefined = | ||
| toolName, | ||
| context: | ||
| toolsContext?.[toolName as keyof InferToolSetContext<TOOLS>], | ||
| context: getOwn(toolsContext, toolName), | ||
| contextSchema: tool?.contextSchema, | ||
@@ -120,4 +124,3 @@ }), | ||
| toolName, | ||
| context: | ||
| toolsContext?.[toolName as keyof InferToolSetContext<TOOLS>], | ||
| context: getOwn(toolsContext, toolName), | ||
| contextSchema: tool?.contextSchema, | ||
@@ -124,0 +127,0 @@ }), |
@@ -19,2 +19,3 @@ import { | ||
| import { resolveLanguageModel } from '../model/resolve-model'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import type { Instructions, Prompt } from '../prompt'; | ||
@@ -719,3 +720,3 @@ import { convertToLanguageModelPrompt } from '../prompt/convert-to-language-model-prompt'; | ||
| case 'tool-input-start': { | ||
| const tool = tools?.[chunk.toolName]; | ||
| const tool = getOwn(tools, chunk.toolName); | ||
@@ -722,0 +723,0 @@ controller.enqueue({ |
@@ -8,2 +8,3 @@ import type { | ||
| import { createToolModelOutput } from '../prompt/create-tool-model-output'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import type { ContentPart } from './content-part'; | ||
@@ -101,3 +102,3 @@ import type { ToolSet } from '@ai-sdk/provider-utils'; | ||
| input: part.input, | ||
| tool: tools?.[part.toolName], | ||
| tool: getOwn(tools, part.toolName), | ||
| output: part.output, | ||
@@ -119,3 +120,3 @@ errorMode: 'none', | ||
| input: part.input, | ||
| tool: tools?.[part.toolName], | ||
| tool: getOwn(tools, part.toolName), | ||
| output: part.error, | ||
@@ -195,3 +196,3 @@ errorMode: 'json', | ||
| input: part.input, | ||
| tool: tools?.[part.toolName], | ||
| tool: getOwn(tools, part.toolName), | ||
| output: part.type === 'tool-result' ? part.output : part.error, | ||
@@ -198,0 +199,0 @@ errorMode: part.type === 'tool-error' ? 'text' : 'none', |
@@ -1,33 +0,6 @@ | ||
| import { | ||
| convertBase64ToUint8Array, | ||
| convertUint8ArrayToBase64, | ||
| } from '@ai-sdk/provider-utils'; | ||
| import { convertBase64ToUint8Array } from '@ai-sdk/provider-utils'; | ||
| import { hashCanonical, toBase64url } from '../util/canonical-hash'; | ||
| const encoder = new TextEncoder(); | ||
| function canonicalJSON(value: unknown): string { | ||
| if (value === null || value === undefined) { | ||
| return JSON.stringify(value); | ||
| } | ||
| if (typeof value !== 'object') { | ||
| return JSON.stringify(value); | ||
| } | ||
| if (Array.isArray(value)) { | ||
| return `[${value.map(canonicalJSON).join(',')}]`; | ||
| } | ||
| const keys = Object.keys(value as Record<string, unknown>).sort(); | ||
| const entries = keys.map( | ||
| k => | ||
| `${JSON.stringify(k)}:${canonicalJSON((value as Record<string, unknown>)[k])}`, | ||
| ); | ||
| return `{${entries.join(',')}}`; | ||
| } | ||
| function toBase64url(bytes: Uint8Array): string { | ||
| return convertUint8ArrayToBase64(bytes) | ||
| .replace(/\+/g, '-') | ||
| .replace(/\//g, '_') | ||
| .replace(/=+$/g, ''); | ||
| } | ||
| function fromBase64url(str: string): Uint8Array { | ||
@@ -48,11 +21,2 @@ return convertBase64ToUint8Array(str); | ||
| async function hashInput(input: unknown): Promise<string> { | ||
| const canonical = canonicalJSON(input); | ||
| const digest = await crypto.subtle.digest( | ||
| 'SHA-256', | ||
| encoder.encode(canonical), | ||
| ); | ||
| return toBase64url(new Uint8Array(digest)); | ||
| } | ||
| function buildPayload( | ||
@@ -83,3 +47,3 @@ approvalId: string, | ||
| const key = await importKey(secret); | ||
| const inputDigest = await hashInput(input); | ||
| const inputDigest = await hashCanonical(input); | ||
| const payload = buildPayload(approvalId, toolCallId, toolName, inputDigest); | ||
@@ -106,3 +70,3 @@ const sig = await crypto.subtle.sign('HMAC', key, payload); | ||
| const key = await importKey(secret); | ||
| const inputDigest = await hashInput(input); | ||
| const inputDigest = await hashCanonical(input); | ||
| const payload = buildPayload(approvalId, toolCallId, toolName, inputDigest); | ||
@@ -109,0 +73,0 @@ const sigBytes = fromBase64url(signature); |
@@ -12,2 +12,3 @@ import { | ||
| import { InvalidToolInputError } from '../error/invalid-tool-input-error'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import type { CollectedToolApprovals } from './collect-tool-approvals'; | ||
@@ -51,3 +52,7 @@ import { resolveToolApproval } from './resolve-tool-approval'; | ||
| const { toolCall, approvalRequest } = approval; | ||
| const tool = tools?.[toolCall.toolName]; | ||
| // Look up the tool by own property only: `toolName` comes from | ||
| // client-supplied history, so a name matching an inherited object property | ||
| // (e.g. `constructor`, `toString`) must resolve to "no such tool" rather | ||
| // than a prototype value that would silently skip input validation below. | ||
| const tool = getOwn(tools, toolCall.toolName); | ||
@@ -54,0 +59,0 @@ if (toolApprovalSecret != null) { |
@@ -31,3 +31,2 @@ import type { | ||
| import { splitDataUrl } from '../prompt/split-data-url'; | ||
| import { convertDataContentToUint8Array } from '../prompt/data-content'; | ||
@@ -53,3 +52,3 @@ export type GenerateVideoPrompt = | ||
| * @param frameImages - Role-tagged image inputs for image-to-video and first-last-frame generation. | ||
| * @param inputReferences - Reference image inputs for reference-to-video generation. | ||
| * @param inputReferences - Reference image or video inputs for reference-to-video generation. | ||
| * @param generateAudio - Whether the model should generate audio alongside the video. | ||
@@ -146,6 +145,23 @@ * @param providerOptions - Additional provider-specific options that are passed through to the provider | ||
| /** | ||
| * Reference image inputs for reference-to-video generation. | ||
| * Reference inputs for reference-to-video generation. | ||
| * | ||
| * Each entry may be a plain image/video ({@link DataContent}), or an object | ||
| * form that carries an explicit `mediaType`. | ||
| */ | ||
| inputReferences?: Array<DataContent>; | ||
| inputReferences?: Array< | ||
| | DataContent | ||
| | { | ||
| /** | ||
| * The reference image or video. | ||
| */ | ||
| data: DataContent; | ||
| /** | ||
| * The media type of the reference (e.g. 'image/png', | ||
| * 'video/mp4'). | ||
| */ | ||
| mediaType?: string; | ||
| } | ||
| >; | ||
| /** | ||
@@ -207,12 +223,15 @@ * Whether the model should generate audio alongside the video. | ||
| | Array<Experimental_VideoModelV4FrameImage> | ||
| | undefined = frameImages?.map(frame => ({ | ||
| image: normalizeImageData(frame.image), | ||
| frameType: frame.frameType, | ||
| })); | ||
| | undefined = frameImages?.flatMap(frame => { | ||
| const normalizedImage = normalizeImageData(frame.image); | ||
| return normalizedImage != null | ||
| ? [{ image: normalizedImage, frameType: frame.frameType }] | ||
| : []; | ||
| }); | ||
| const normalizedInputReferences: | ||
| | Array<Experimental_VideoModelV4File> | ||
| | undefined = inputReferences?.map(reference => | ||
| normalizeImageData(reference), | ||
| ); | ||
| | undefined = inputReferences?.flatMap(reference => { | ||
| const normalized = normalizeReferenceData(reference); | ||
| return normalized != null ? [normalized] : []; | ||
| }); | ||
@@ -433,2 +452,12 @@ const effectiveInputReferences = | ||
| function detectFileMediaType( | ||
| data: Uint8Array, | ||
| restrictToImages: boolean, | ||
| ): string { | ||
| const detected = restrictToImages | ||
| ? detectMediaType({ data, topLevelType: 'image' }) | ||
| : detectMediaType({ data }); | ||
| return detected ?? 'image/png'; | ||
| } | ||
| /** | ||
@@ -440,3 +469,4 @@ * Normalizes a {@link DataContent} image into a {@link Experimental_VideoModelV4File}. | ||
| dataContent: DataContent, | ||
| ): Experimental_VideoModelV4File { | ||
| { restrictToImages = true }: { restrictToImages?: boolean } = {}, | ||
| ): Experimental_VideoModelV4File | undefined { | ||
| if (typeof dataContent === 'string') { | ||
@@ -455,6 +485,7 @@ if ( | ||
| const { mediaType, base64Content } = splitDataUrl(dataContent); | ||
| const data = convertBase64ToUint8Array(base64Content ?? ''); | ||
| return { | ||
| type: 'file', | ||
| mediaType: mediaType ?? 'image/png', | ||
| data: convertBase64ToUint8Array(base64Content ?? ''), | ||
| mediaType: mediaType ?? detectFileMediaType(data, restrictToImages), | ||
| data, | ||
| }; | ||
@@ -466,4 +497,3 @@ } | ||
| type: 'file', | ||
| mediaType: | ||
| detectMediaType({ data: bytes, topLevelType: 'image' }) ?? 'image/png', | ||
| mediaType: detectFileMediaType(bytes, restrictToImages), | ||
| data: bytes, | ||
@@ -473,8 +503,53 @@ }; | ||
| const bytes = convertDataContentToUint8Array(dataContent); | ||
| if (dataContent instanceof Uint8Array || dataContent instanceof ArrayBuffer) { | ||
| const bytes = | ||
| dataContent instanceof Uint8Array | ||
| ? dataContent | ||
| : new Uint8Array(dataContent); | ||
| return { | ||
| type: 'file', | ||
| mediaType: detectFileMediaType(bytes, restrictToImages), | ||
| data: bytes, | ||
| }; | ||
| } | ||
| return undefined; | ||
| } | ||
| /** | ||
| * Normalizes a reference input into a {@link Experimental_VideoModelV4File}, | ||
| * accepting either a plain {@link DataContent} or the object form that carries | ||
| * an explicit `mediaType`. | ||
| */ | ||
| function normalizeReferenceData( | ||
| reference: | ||
| | DataContent | ||
| | { | ||
| data: DataContent; | ||
| mediaType?: string; | ||
| }, | ||
| ): Experimental_VideoModelV4File | undefined { | ||
| const isObjectForm = | ||
| typeof reference === 'object' && | ||
| reference != null && | ||
| !(reference instanceof Uint8Array) && | ||
| !(reference instanceof ArrayBuffer) && | ||
| 'data' in reference; | ||
| if (!isObjectForm) { | ||
| return normalizeImageData(reference as DataContent, { | ||
| restrictToImages: false, | ||
| }); | ||
| } | ||
| const normalized = normalizeImageData(reference.data, { | ||
| restrictToImages: false, | ||
| }); | ||
| if (normalized == null) { | ||
| return normalized; | ||
| } | ||
| return { | ||
| type: 'file', | ||
| mediaType: | ||
| detectMediaType({ data: bytes, topLevelType: 'image' }) ?? 'image/png', | ||
| data: bytes, | ||
| ...normalized, | ||
| ...(reference.mediaType != null ? { mediaType: reference.mediaType } : {}), | ||
| }; | ||
@@ -481,0 +556,0 @@ } |
@@ -14,2 +14,3 @@ import { | ||
| import { MessageConversionError } from '../prompt/message-conversion-error'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import { | ||
@@ -260,3 +261,3 @@ getToolName, | ||
| : part.output, | ||
| tool: options?.tools?.[toolName], | ||
| tool: getOwn(options?.tools, toolName), | ||
| errorMode: | ||
@@ -380,3 +381,3 @@ part.state === 'output-error' ? 'json' : 'none', | ||
| : toolPart.output, | ||
| tool: options?.tools?.[toolName], | ||
| tool: getOwn(options?.tools, toolName), | ||
| errorMode: | ||
@@ -383,0 +384,0 @@ toolPart.state === 'output-error' ? 'text' : 'none', |
@@ -734,2 +734,5 @@ import type { JSONObject } from '@ai-sdk/provider'; | ||
| ...(approval.isAutomatic === true ? { isAutomatic: true } : {}), | ||
| ...(approval.signature != null | ||
| ? { signature: approval.signature } | ||
| : {}), | ||
| }; | ||
@@ -736,0 +739,0 @@ if (chunk.providerExecuted != null) { |
@@ -12,2 +12,3 @@ import { TypeValidationError, type JSONObject } from '@ai-sdk/provider'; | ||
| import { jsonValueSchema } from '../types/json-value'; | ||
| import { getOwn } from '../util/get-own'; | ||
| import { providerMetadataSchema } from '../types/provider-metadata'; | ||
@@ -460,3 +461,3 @@ import type { | ||
| const toolName = toolPart.type.slice(5); | ||
| const tool = tools[toolName]; | ||
| const tool = getOwn(tools, toolName); | ||
@@ -463,0 +464,0 @@ if ( |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
6360140
0.34%607
0.5%65415
0.49%+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
Updated
Updated
Updated