UNPKG

@mastra/core

Version:
1,230 lines 470 kB
const require_error = require("./error-B-e62x-A.cjs"); const require_deep_equal = require("./deep-equal-BvQBG8wE.cjs"); const require_payload_transform = require("./payload-transform-DJzDdfpE.cjs"); const require_signals = require("./signals-D2CulJo3.cjs"); const require_dist = require("./dist-BGdEcgoh.cjs"); const require_dist_3jq9sPhA = require("./dist-3jq9sPhA-BtxtCkky.cjs"); const require_dist$1 = require("./dist-CN0r_lDr.cjs"); const require_content = require("./content-fINNgB3A.cjs"); const require_file = require("./file-BdP8crPU.cjs"); let zod_v4 = require("zod/v4"); let _lukeed_uuid = require("@lukeed/uuid"); let _ai_sdk_provider_utils_v5 = require("@ai-sdk/provider-utils-v5"); //#region src/agent/message-list/detection/TypeDetector.ts /** * TypeDetector - Centralized type detection for different message formats * * This class provides consistent type detection across all message formats, * which is critical for: * - Determining which conversion path to use * - Validating incoming message formats * - Providing better TypeScript type narrowing * * The detection order is important because some formats share similar properties. */ var TypeDetector = class TypeDetector { /** * Check if a message is a MastraDBMessage (format 2) */ static isMastraDBMessage(msg) { return Boolean("content" in msg && msg.content && !Array.isArray(msg.content) && typeof msg.content !== "string" && "format" in msg.content && msg.content.format === 2); } /** * Check if a message is a MastraMessageV1 (legacy format) */ static isMastraMessageV1(msg) { return !TypeDetector.isMastraDBMessage(msg) && ("threadId" in msg || "resourceId" in msg); } /** * Check if a message is either Mastra format (V1 or V2/DB) */ static isMastraMessage(msg) { return TypeDetector.isMastraDBMessage(msg) || TypeDetector.isMastraMessageV1(msg); } /** * Check if a message is an AIV4 UIMessage */ static isAIV4UIMessage(msg) { return !TypeDetector.isMastraMessage(msg) && !TypeDetector.isAIV4CoreMessage(msg) && "parts" in msg && !TypeDetector.hasAIV5UIMessageCharacteristics(msg); } /** * Check if a message is an AIV6 UIMessage. * * At runtime, the v5 and v6 UI shapes overlap heavily. We only treat a * message as distinctly v6 if it uses v6-only parts or tool states. */ static isAIV6UIMessage(msg) { return !TypeDetector.isMastraMessage(msg) && !TypeDetector.isAIV4CoreMessage(msg) && "parts" in msg && TypeDetector.hasAIV6UIMessageCharacteristics(msg); } /** * Check if a message is an AIV5 UIMessage */ static isAIV5UIMessage(msg) { return !TypeDetector.isMastraMessage(msg) && !TypeDetector.isAIV6UIMessage(msg) && !TypeDetector.isAIV5CoreMessage(msg) && "parts" in msg && TypeDetector.hasAIV5UIMessageCharacteristics(msg); } /** * Check if a message is an AIV4 CoreMessage */ static isAIV4CoreMessage(msg) { return !TypeDetector.isMastraMessage(msg) && !("parts" in msg) && "content" in msg && !TypeDetector.hasAIV5CoreMessageCharacteristics(msg); } /** * Check if a message is an AIV6 ModelMessage (CoreMessage equivalent). */ static isAIV6CoreMessage(msg) { return !TypeDetector.isMastraMessage(msg) && !("parts" in msg) && "content" in msg && TypeDetector.hasAIV6CoreMessageCharacteristics(msg); } /** * Check if a message is an AIV5 ModelMessage (CoreMessage equivalent) */ static isAIV5CoreMessage(msg) { return !TypeDetector.isMastraMessage(msg) && !TypeDetector.isAIV6CoreMessage(msg) && !("parts" in msg) && "content" in msg && TypeDetector.hasAIV5CoreMessageCharacteristics(msg); } /** * Check if a message has AIV6-only UI characteristics. */ static hasAIV6UIMessageCharacteristics(msg) { if (!("parts" in msg) || !msg.parts) return false; for (const part of msg.parts) { if (part.type === "source-document") return true; if (part.type === "dynamic-tool") return true; if ("toolCallId" in part && "state" in part && (part.state === "approval-requested" || part.state === "approval-responded" || part.state === "output-denied")) return true; } return false; } /** * Check if a message has AIV5 UIMessage characteristics * * V5 UIMessages have specific part types and field names that differ from V4. */ static hasAIV5UIMessageCharacteristics(msg) { if ("toolInvocations" in msg || "reasoning" in msg || "experimental_attachments" in msg || "data" in msg || "annotations" in msg) return false; if (!msg.parts) return false; for (const part of msg.parts) { if ("metadata" in part) return true; if ("toolInvocation" in part) return false; if ("toolCallId" in part) return true; if (part.type === "source") return false; if (part.type === "source-url") return true; if (part.type === "reasoning") { if ("state" in part || "text" in part) return true; if ("reasoning" in part || "details" in part) return false; } if (part.type === "file" && "mediaType" in part) return true; } return false; } /** * Check if a message has AIV6-only core characteristics. */ static hasAIV6CoreMessageCharacteristics(msg) { if ("parts" in msg || typeof msg.content === "string") return false; return msg.content.some((part) => part.type === "tool-approval-request" || part.type === "tool-approval-response"); } /** * Check if a message has AIV5 CoreMessage characteristics * * V5 ModelMessages use different field names from v4 * (for example `output` vs `result`, `input` vs `args`, * `mediaType` vs `mimeType`). */ static hasAIV5CoreMessageCharacteristics(msg) { if ("experimental_providerMetadata" in msg) return false; if (typeof msg.content === "string") return true; for (const part of msg.content) { if (part.type === "tool-result" && "output" in part) return true; if (part.type === "tool-call" && "input" in part) return true; if (part.type === "tool-result" && "result" in part) return false; if (part.type === "tool-call" && "args" in part) return false; if ("mediaType" in part) return true; if ("mimeType" in part) return false; if ("experimental_providerMetadata" in part) return false; if (part.type === "reasoning" && "signature" in part) return false; if (part.type === "redacted-reasoning") return false; } return true; } /** * Get the normalized role for a message * Maps `tool` to `assistant` because tool messages are displayed as part of * the assistant conversation. */ static getRole(message) { if (message.role === "assistant" || message.role === "tool") return "assistant"; if (message.role === "user") return "user"; if (message.role === "system") return "system"; throw new Error(`BUG: add handling for message role ${message.role} in message ${JSON.stringify(message, null, 2)}`); } }; //#endregion //#region src/agent/message-list/prompt/image-utils.ts /** * Parses a data URI string into its components. * Format: data:[<mediatype>][;base64],<data> * * @param dataUri - The data URI string to parse * @returns Parsed components including MIME type and base64 content */ function parseDataUri(dataUri) { if (!dataUri.startsWith("data:")) return { isDataUri: false, base64Content: dataUri }; const base64Index = dataUri.indexOf(","); if (base64Index === -1) return { isDataUri: true, base64Content: dataUri }; const header = dataUri.substring(5, base64Index); const base64Content = dataUri.substring(base64Index + 1); const semicolonIndex = header.indexOf(";"); return { isDataUri: true, mimeType: (semicolonIndex !== -1 ? header.substring(0, semicolonIndex) : header) || void 0, base64Content }; } /** * Creates a data URI from base64 content and MIME type. * * @param base64Content - The base64 encoded content * @param mimeType - The MIME type (defaults to 'application/octet-stream') * @returns A properly formatted data URI */ function createDataUri(base64Content, mimeType = "application/octet-stream") { if (base64Content.startsWith("data:")) return base64Content; return `data:${mimeType};base64,${base64Content}`; } /** * Converts various image data formats to a string representation. * - Strings are returned as-is (could be URLs or data URIs) * - URL objects are converted to strings * - Binary data (Uint8Array, ArrayBuffer, Buffer) is converted to base64 * * @param image - The image data in various formats * @param fallbackMimeType - MIME type to use when creating data URIs from binary data * @returns String representation of the image (URL, data URI, or base64) */ function imageContentToString(image, fallbackMimeType) { if (typeof image === "string") return image; if (image instanceof URL) return image.toString(); if (image instanceof Uint8Array || image instanceof ArrayBuffer || globalThis.Buffer && Buffer.isBuffer(image)) { const base64 = require_signals.convertDataContentToBase64String(image); if (fallbackMimeType && !base64.startsWith("data:")) return `data:${fallbackMimeType};base64,${base64}`; return base64; } return String(image); } /** * Gets a stable cache key component for image content. * Used for generating hash keys for caching purposes. * * @param image - The image data in various formats * @returns A string or number suitable for cache key generation */ function getImageCacheKey(image) { if (image instanceof URL) return image.toString(); if (typeof image === "string") return image.length; if (image instanceof Uint8Array) return image.byteLength; if (image instanceof ArrayBuffer) return image.byteLength; return image; } /** * Checks if a string is a valid URL (including protocol-relative URLs). * * @param str - The string to check * @returns true if the string is a valid URL */ function isValidUrl(str) { try { new URL(str); return true; } catch { if (str.startsWith("//")) try { new URL(`https:${str}`); return true; } catch { return false; } return false; } } /** * Categorizes a string as a URL, data URI, or raw data (base64/other). * Also extracts MIME type from data URIs when present. * * @param data - The string data to categorize * @param fallbackMimeType - Optional fallback MIME type * @returns Categorized data with type and extracted MIME type */ function categorizeFileData(data, fallbackMimeType) { const parsed = parseDataUri(data); const mimeType = parsed.isDataUri && parsed.mimeType ? parsed.mimeType : fallbackMimeType; if (parsed.isDataUri) return { type: "dataUri", mimeType, data }; if (data.startsWith("file-")) return { type: "providerFileId", mimeType, data }; if (isValidUrl(data)) return { type: "url", mimeType, data }; return { type: "raw", mimeType, data }; } /** * Resolve a stored file part's media type and payload across the AI SDK v4 and v5 shapes. * * Stored "v2" file parts are typed as the AI SDK v4 UI shape (`mimeType`/`data`), but * v5-shaped file parts (`mediaType`/`url`, renamed in the v5 Media Type Standardization) * reach the same read sites. Reading only the v4 fields leaves a v5 part with both values * `undefined`, which downstream becomes `contentType: undefined` (making `attachmentsToParts` * throw) or collapses distinct parts onto a single cache key. Read whichever shape is present. * * Returns the RAW resolved values (undefined-preserving); call sites that build a * `contentType` should apply their own `'application/octet-stream'` fallback. Mirrors #17366. */ function resolveFilePartMediaTypeAndData(part) { const filePart = part; return { mediaType: filePart.mimeType ?? filePart.mediaType, data: filePart.data ?? filePart.url }; } //#endregion //#region src/agent/message-list/utils/response-item-metadata.ts const RESPONSE_ITEM_ID_PROVIDERS = ["openai", "azure"]; function formatResponseProviderItemKey(provider, itemId) { return `${provider}:${itemId}`; } function getResponseProviderItemId(providerMetadata) { return getResponseProviderItemIds(providerMetadata)[0]; } function getResponseProviderItemKey(providerMetadata) { const item = getResponseProviderItemId(providerMetadata); return item ? formatResponseProviderItemKey(item.provider, item.itemId) : void 0; } function getResponseProviderItemIds(providerMetadata) { if (!providerMetadata) return []; const azureItemId = providerMetadata.azure?.itemId; const openaiItemId = providerMetadata.openai?.itemId; if (typeof azureItemId === "string" && azureItemId === openaiItemId) return [{ provider: "azure", itemId: azureItemId }]; return RESPONSE_ITEM_ID_PROVIDERS.flatMap((provider) => { const itemId = providerMetadata[provider]?.itemId; return typeof itemId === "string" ? [{ provider, itemId }] : []; }); } function getResponseProviderItemKeys(providerMetadata) { return getResponseProviderItemIds(providerMetadata).map(({ provider, itemId }) => formatResponseProviderItemKey(provider, itemId)); } //#endregion //#region src/agent/message-list/utils/provider-compat.ts /** * Ensures message array is compatible with Gemini API requirements. * * Gemini API requires: * 1. The first non-system message must be from the user role * 2. Cannot have only system messages - at least one user/assistant is required * * @param messages - Array of model messages to validate and fix * @param logger - Optional logger for warnings * @returns Modified messages array that satisfies Gemini requirements * * @see https://github.com/mastra-ai/mastra/issues/7287 - Tool call ordering * @see https://github.com/mastra-ai/mastra/issues/8053 - Single turn validation * @see https://github.com/mastra-ai/mastra/issues/13045 - Empty thread support */ function ensureGeminiCompatibleMessages(messages, logger) { const result = [...messages]; const firstNonSystemIndex = result.findIndex((m) => m.role !== "system"); if (firstNonSystemIndex === -1) { if (result.length > 0) logger?.warn("No user or assistant messages in the request. Some providers (e.g. Gemini) require at least one user message to generate a response."); } else if (result[firstNonSystemIndex]?.role === "assistant") result.splice(firstNonSystemIndex, 0, { role: "user", content: "." }); return result; } /** * Ensures model messages are compatible with Anthropic API requirements. * * Anthropic API requires tool-result parts to include an 'input' field * that matches the original tool call arguments. * * @param messages - Array of model messages to transform * @param dbMessages - MastraDB messages to look up tool call args from * @returns Messages with tool-result parts enriched with input field * * @see https://github.com/mastra-ai/mastra/issues/11376 - Anthropic models fail with empty object tool input */ function ensureAnthropicCompatibleMessages(messages, dbMessages) { return messages.map((msg) => enrichToolResultsWithInput(msg, dbMessages)); } /** * Tool call ids in the assistant message at `index` that already have a matching tool_result, * either inline in the same message or in the tool message immediately after it — the only * two positions providers accept. */ function collectPairedToolCallIds(messages, index) { const current = messages[index]; if (!Array.isArray(current.content)) return /* @__PURE__ */ new Set(); const useIds = /* @__PURE__ */ new Set(); const resultIds = /* @__PURE__ */ new Set(); for (const part of current.content) if (part.type === "tool-call") useIds.add(part.toolCallId); else if (part.type === "tool-result") resultIds.add(part.toolCallId); const next = messages[index + 1]; if (next && next.role === "tool" && Array.isArray(next.content)) { for (const part of next.content) if (part.type === "tool-result") resultIds.add(part.toolCallId); } return new Set([...useIds].filter((id) => resultIds.has(id))); } /** * Removes orphan tool_use / tool_result blocks. Anthropic requires every tool_result * to be in the message immediately after its matching tool_use, and every tool_use * to have a matching tool_result in the next message. Recall windows can slice * through a parallel tool-call group and leave behind half a pair. */ function sanitizeOrphanedToolPairs(messages) { const filteredContents = messages.map((m) => Array.isArray(m.content) ? [...m.content] : null); for (let i = 0; i < messages.length; i++) { const current = messages[i]; if (current.role === "assistant" && Array.isArray(current.content)) { const validPairs = collectPairedToolCallIds(messages, i); const next = messages[i + 1]; filteredContents[i] = filteredContents[i].filter((p) => { if (p.type !== "tool-call") return true; const tc = p; return tc.providerExecuted === true || validPairs.has(tc.toolCallId); }); if (next && next.role === "tool" && Array.isArray(next.content)) filteredContents[i + 1] = filteredContents[i + 1].filter((p) => p.type !== "tool-result" || validPairs.has(p.toolCallId)); } else if (current.role === "tool" && Array.isArray(current.content)) { const prev = messages[i - 1]; if (!prev || prev.role !== "assistant" || !Array.isArray(prev.content)) filteredContents[i] = filteredContents[i].filter((p) => p.type !== "tool-result"); } } const result = []; for (let i = 0; i < messages.length; i++) { const original = messages[i]; const filtered = filteredContents[i]; if (filtered == null) { result.push(original); continue; } if (filtered.length === 0) continue; if (Array.isArray(original.content) && filtered.length === original.content.length) { result.push(original); continue; } result.push({ ...original, content: filtered }); } return result; } /** * Keeps result-less tool calls in the prompt by pairing each one with a placeholder result. * * Used when the caller opted to keep suspended tool calls visible to the agent * (`filterIncompleteToolCalls: false`). Providers reject a tool_use with no matching * tool_result, so dropping the call is not the only option — synthesizing the missing * half keeps the pending call in context while satisfying the pairing requirement. * * Provider-executed calls are left alone: they may be legitimately deferred to the next * request, and giving them a result would resolve a call the provider intends to resume. * * @see https://github.com/mastra-ai/mastra/issues/20610 */ function pairOrphanedToolCalls(messages) { const paired = []; for (let i = 0; i < messages.length; i++) { const current = messages[i]; paired.push(current); if (current.role !== "assistant" || !Array.isArray(current.content)) continue; const pairedIds = collectPairedToolCallIds(messages, i); const placeholders = []; for (const part of current.content) { if (part.type !== "tool-call") continue; const tc = part; if (tc.providerExecuted === true || pairedIds.has(tc.toolCallId)) continue; placeholders.push({ type: "tool-result", toolCallId: tc.toolCallId, toolName: tc.toolName, output: { type: "json", value: { status: "pending" } } }); } if (placeholders.length === 0) continue; const next = messages[i + 1]; if (next && next.role === "tool" && Array.isArray(next.content)) { paired.push({ ...next, content: [...next.content, ...placeholders] }); i++; } else paired.push({ role: "tool", content: placeholders }); } return sanitizeOrphanedToolPairs(paired); } /** * Enriches a single message's tool-result parts with input field */ function enrichToolResultsWithInput(message, dbMessages) { if (message.role !== "tool" || !Array.isArray(message.content)) return message; return { ...message, content: message.content.map((part) => { if (part.type === "tool-result") return { ...part, input: findToolCallArgs(dbMessages, part.toolCallId) }; return part; }) }; } /** * Checks if a message part has an OpenAI reasoning itemId. * * OpenAI Responses reasoning items are tracked via `providerMetadata.openai.itemId`. * Each reasoning item has a unique itemId that must be preserved for proper deduplication. * * @param part - A message part to check * @returns true if the part has an OpenAI itemId * * @see https://github.com/mastra-ai/mastra/issues/9005 - OpenAI reasoning items filtering */ function hasOpenAIReasoningItemId(part) { return Boolean(getOpenAIReasoningItemId(part)); } /** * Checks if a message part has an OpenAI-compatible Responses itemId. * * Provider-neutral Responses item IDs are tracked via provider metadata or * provider options fields such as `openai.itemId` or `azure.itemId`. */ function hasResponseProviderItemId(part) { return Boolean(getResponseProviderItemIdFromPart(part)); } /** * Extracts an OpenAI itemId from a message part if present. * * This only inspects `providerMetadata.openai.itemId`; use * `getResponseProviderItemIdFromPart` for provider-aware Azure/OpenAI lookups. * * @param part - A message part to extract from * @returns The itemId string or undefined if not present */ function getOpenAIReasoningItemId(part) { if (!part || typeof part !== "object") return void 0; const openaiMetadata = part.providerMetadata?.openai; return typeof openaiMetadata?.itemId === "string" ? openaiMetadata.itemId : void 0; } function getResponseProviderItemIdFromPart(part) { if (!part || typeof part !== "object") return void 0; const partAny = part; return getResponseProviderItemId(partAny.providerMetadata) || getResponseProviderItemId(partAny.providerOptions); } /** * Finds the tool call args for a given toolCallId by searching through messages. * This is used to reconstruct the input field when converting tool-result parts to StaticToolResult. * * Searches through messages in reverse order (most recent first) for better performance. * Checks both content.parts (v2 format) and toolInvocations (legacy AIV4 format). * * @param messages - Array of MastraDB messages to search through * @param toolCallId - The ID of the tool call to find args for * @returns The args object from the matching tool call, or an empty object if not found */ function findToolCallArgs(messages, toolCallId) { for (let i = messages.length - 1; i >= 0; i--) { const msg = messages[i]; if (!msg || msg.role !== "assistant") continue; if (msg.content.parts) { const toolCallPart = msg.content.parts.find((p) => p.type === "tool-invocation" && p.toolInvocation.toolCallId === toolCallId); if (toolCallPart && toolCallPart.type === "tool-invocation") { const args = toolCallPart.toolInvocation.args || {}; if (typeof args === "object" && Object.keys(args).length > 0) return args; } } if (msg.content.toolInvocations) { const toolInvocation = msg.content.toolInvocations.find((inv) => inv.toolCallId === toolCallId); if (toolInvocation) { const args = toolInvocation.args || {}; if (typeof args === "object" && Object.keys(args).length > 0) return args; } } } return {}; } //#endregion //#region src/agent/message-list/adapters/AIV4Adapter.ts function getDisplayTransform$2(providerMetadata, phase, fallback, enabled = true) { if (!enabled) return fallback; const transform = require_payload_transform.getTransformedToolPayload(providerMetadata, "display", phase); return require_payload_transform.hasTransformedToolPayload(transform) ? transform.transformed : fallback; } function transformV4ToolInvocationForDisplay(invocation, providerMetadata, enabled) { return { ...invocation, args: getDisplayTransform$2(providerMetadata, "input-available", invocation.args, enabled), ...invocation.state === "result" ? { result: getDisplayTransform$2(providerMetadata, "output-available", getDisplayTransform$2(providerMetadata, "error", invocation.result, enabled), enabled) } : {} }; } /** * Cast Mastra parts (including data-* extensions) to the V4 UI parts type. * Data-* parts (e.g. data-tool-call-suspended) are not natively typed in AI SDK V4, * but must be preserved so features like HITL workflow resumption work after a page refresh. */ function preserveExtendedParts(parts) { return parts; } /** * Filter out empty text parts from message parts array. * Empty text blocks are not allowed by Anthropic's API and cause request failures. * This can happen during streaming when text-start/text-end events occur without actual content. * However, if the only part is an empty text part, it is preserved as a legitimate placeholder * (e.g. empty assistant messages between tool results and user messages). */ function filterEmptyTextParts$1(parts) { if (!parts.some((part) => !(part.type === "text" && part.text === ""))) return parts; return parts.filter((part) => { if (part.type === "text") return part.text !== ""; return true; }); } function getSignalType$1(message) { const signal = message.content.metadata?.signal; if (signal && typeof signal === "object" && !Array.isArray(signal)) { const type = signal.type; return typeof type === "string" ? type : message.type; } return message.type; } function getSignalTagName$1(message) { const signal = message.content.metadata?.signal; if (signal && typeof signal === "object" && !Array.isArray(signal)) { const tagName = signal.tagName; if (typeof tagName === "string") return tagName; } const type = getSignalType$1(message); if (type === "user") return "user"; if (type === "reactive") return message.type; return type; } function isUserSignalType$1(type) { return type === "user" || type === "user-message"; } function toSignalDataPart$1(message, contents) { const signal = message.content.metadata?.signal && typeof message.content.metadata.signal === "object" ? message.content.metadata.signal : {}; const metadata = signal.metadata && typeof signal.metadata === "object" && !Array.isArray(signal.metadata) ? signal.metadata : {}; const attributes = signal.attributes && typeof signal.attributes === "object" && !Array.isArray(signal.attributes) ? signal.attributes : {}; const type = getSignalType$1(message) ?? "signal"; const tagName = getSignalTagName$1(message) ?? type; return { type: type === "user" ? "data-user-message" : "data-signal", data: { id: typeof signal.id === "string" ? signal.id : message.id, type, tagName, contents: "contents" in signal ? signal.contents : contents, createdAt: typeof signal.createdAt === "string" ? signal.createdAt : message.createdAt.toISOString(), ...typeof signal.acceptedAt === "string" ? { acceptedAt: signal.acceptedAt } : {}, ...Object.keys(attributes).length ? { attributes } : {}, ...Object.keys(metadata).length ? { metadata } : {} } }; } /** * AIV4Adapter - Handles conversions between MastraDBMessage and AI SDK V4 formats * * This adapter centralizes all AI SDK V4 (UIMessage and CoreMessage) conversion logic. */ var AIV4Adapter = class { /** * Convert MastraDBMessage to AI SDK V4 UIMessage */ static toUIMessage(m, options) { const transformToolPayloads = options?.transformToolPayloads ?? true; const experimentalAttachments = m.content.experimental_attachments ? [...m.content.experimental_attachments] : []; const contentString = typeof m.content.content === `string` && m.content.content !== "" ? m.content.content : (m.content.parts ?? []).reduce((prev, part) => { if (part.type === `text`) return part.text; return prev; }, ""); const parts = []; const sourceParts = m.content.parts ?? []; if (sourceParts.length) for (const part of sourceParts) if (part.type === `file`) { const { mediaType: fileMimeType, data: fileData } = resolveFilePartMediaTypeAndData(part); let normalizedUrl; if (typeof fileData === "string") if (categorizeFileData(fileData, fileMimeType).type === "raw") normalizedUrl = createDataUri(fileData, fileMimeType || "application/octet-stream"); else normalizedUrl = fileData; else normalizedUrl = imageContentToString(fileData, fileMimeType); experimentalAttachments.push({ contentType: fileMimeType ?? "application/octet-stream", url: normalizedUrl }); } else if (part.type === "tool-invocation" && (part.toolInvocation.state === "call" || part.toolInvocation.state === "partial-call")) continue; else if (part.type === "tool-invocation") { const isDeniedApproval = part.toolInvocation.state === "output-denied"; const toolInvocation = { ...part.toolInvocation, ...isDeniedApproval ? { state: "result" } : {}, args: getDisplayTransform$2(part.providerMetadata, "input-available", part.toolInvocation.args, transformToolPayloads), ...part.toolInvocation.state === "result" ? { result: getDisplayTransform$2(part.providerMetadata, "output-available", getDisplayTransform$2(part.providerMetadata, "error", part.toolInvocation.result, transformToolPayloads), transformToolPayloads) } : isDeniedApproval ? { result: part.toolInvocation.approval?.reason ?? "Tool call was not approved by the user" } : {} }; let currentStep = -1; let toolStep = -1; for (const innerPart of sourceParts) { if (innerPart.type === `step-start`) currentStep++; if (innerPart.type === `tool-invocation` && innerPart.toolInvocation.toolCallId === part.toolInvocation.toolCallId) { toolStep = currentStep; break; } } if (toolStep >= 0) { const preparedInvocation = { step: toolStep, ...toolInvocation }; parts.push({ type: "tool-invocation", toolInvocation: preparedInvocation }); } else parts.push({ type: "tool-invocation", toolInvocation }); } else parts.push(part); if (parts.length === 0 && experimentalAttachments.length > 0) parts.push({ type: "text", text: "" }); const isUserMessageSignal = isUserSignalType$1(m.role === "signal" ? getSignalType$1(m) : void 0); const v4Parts = preserveExtendedParts(m.role === "signal" && !isUserMessageSignal ? [toSignalDataPart$1(m, m.content.content || contentString)] : parts); if (m.role === `user`) { const uiMessage = { id: m.id, role: m.role, content: m.content.content || contentString, createdAt: m.createdAt, parts: v4Parts, experimental_attachments: experimentalAttachments }; if (m.content.metadata) uiMessage.metadata = m.content.metadata; return uiMessage; } else if (m.role === `assistant`) { const isSingleTextContentArray = Array.isArray(m.content.content) && m.content.content.length === 1 && m.content.content[0].type === `text`; const uiMessage = { id: m.id, role: m.role, content: isSingleTextContentArray ? contentString : m.content.content || contentString, createdAt: m.createdAt, parts: v4Parts, reasoning: void 0, toolInvocations: `toolInvocations` in m.content ? m.content.toolInvocations?.filter((t) => t.state === "result").map((toolInvocation) => { const partProviderMetadata = m.content.parts?.find((part) => part.type === "tool-invocation" && part.toolInvocation.toolCallId === toolInvocation.toolCallId)?.providerMetadata; return transformV4ToolInvocationForDisplay(toolInvocation, partProviderMetadata, transformToolPayloads); }) : void 0 }; if (m.content.metadata) uiMessage.metadata = m.content.metadata; return uiMessage; } const uiMessage = { id: m.id, role: m.role === "signal" ? isUserMessageSignal ? "user" : "system" : m.role, content: m.role === "signal" && !isUserMessageSignal ? "" : m.content.content || contentString, createdAt: m.createdAt, parts: v4Parts, experimental_attachments: experimentalAttachments }; if (m.content.metadata) uiMessage.metadata = m.content.metadata; return uiMessage; } /** * Converts a MastraDBMessage system message directly to AIV4 CoreMessage format */ static systemToV4Core(message) { if (message.role !== `system` || !message.content.content) throw new require_error.MastraError({ id: "INVALID_SYSTEM_MESSAGE_FORMAT", domain: require_error.ErrorDomain.AGENT, category: require_error.ErrorCategory.USER, text: `Invalid system message format. System messages must include 'role' and 'content' properties. The content should be a string.`, details: { receivedMessage: JSON.stringify(message, null, 2) } }); const coreMessage = { role: "system", content: message.content.content }; if (message.content.providerMetadata) coreMessage.experimental_providerMetadata = message.content.providerMetadata; return coreMessage; } /** * Convert AI SDK V4 UIMessage to MastraDBMessage */ static fromUIMessage(message, ctx, messageSource) { const content = { format: 2, parts: message.parts ? filterEmptyTextParts$1(message.parts) : [] }; if (message.toolInvocations) content.toolInvocations = message.toolInvocations; if (message.reasoning) content.reasoning = message.reasoning; if (message.annotations) content.annotations = message.annotations; if (message.experimental_attachments) content.experimental_attachments = message.experimental_attachments; if ("metadata" in message && message.metadata !== null && message.metadata !== void 0) content.metadata = message.metadata; return { id: message.id || ctx.newMessageId(), role: TypeDetector.getRole(message), createdAt: ctx.generateCreatedAt(messageSource, message.createdAt), threadId: ctx.memoryInfo?.threadId, resourceId: ctx.memoryInfo?.resourceId, content }; } /** * Convert AI SDK V4 CoreMessage to MastraDBMessage */ static fromCoreMessage(coreMessage, ctx, messageSource) { const id = `id` in coreMessage ? coreMessage.id : ctx.newMessageId(); const parts = []; const experimentalAttachments = []; const toolInvocations = []; const isSingleTextContent = messageSource === `response` && Array.isArray(coreMessage.content) && coreMessage.content.length === 1 && coreMessage.content[0] && coreMessage.content[0].type === `text` && `text` in coreMessage.content[0] && coreMessage.content[0].text; if (isSingleTextContent && messageSource === `response`) coreMessage.content = isSingleTextContent; if (typeof coreMessage.content === "string") parts.push({ type: "text", text: coreMessage.content }); else if (Array.isArray(coreMessage.content)) for (const aiV4Part of coreMessage.content) switch (aiV4Part.type) { case "text": { const prevPart = parts.at(-1); if (coreMessage.role === "assistant" && prevPart && prevPart.type === "tool-invocation") parts.push({ type: "step-start" }); const part = { type: "text", text: aiV4Part.text }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; parts.push(part); break; } case "tool-call": { const part = { type: "tool-invocation", toolInvocation: { state: "call", toolCallId: aiV4Part.toolCallId, toolName: aiV4Part.toolName, args: aiV4Part.args } }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; parts.push(part); break; } case "tool-result": { let toolArgs = {}; const toolCallInSameMsg = coreMessage.content.find((p) => p.type === "tool-call" && p.toolCallId === aiV4Part.toolCallId); if (toolCallInSameMsg && toolCallInSameMsg.type === "tool-call") toolArgs = toolCallInSameMsg.args; if (Object.keys(toolArgs).length === 0 && ctx.dbMessages) toolArgs = findToolCallArgs(ctx.dbMessages, aiV4Part.toolCallId); const invocation = { state: "result", toolCallId: aiV4Part.toolCallId, toolName: aiV4Part.toolName, result: aiV4Part.result ?? "", args: toolArgs }; const part = { type: "tool-invocation", toolInvocation: invocation }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; parts.push(part); toolInvocations.push(invocation); } break; case "reasoning": { const part = { type: "reasoning", reasoning: aiV4Part.text, details: [{ type: "text", text: aiV4Part.text, signature: aiV4Part.signature }] }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; parts.push(part); } break; case "redacted-reasoning": { const part = { type: "reasoning", reasoning: "", details: [{ type: "redacted", data: aiV4Part.data }] }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; parts.push(part); } break; case "image": { const part = { type: "file", data: imageContentToString(aiV4Part.image), mimeType: aiV4Part.mimeType }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; parts.push(part); break; } case "file": if (aiV4Part.data instanceof URL) { const part = { type: "file", data: aiV4Part.data.toString(), mimeType: aiV4Part.mimeType }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; if (aiV4Part.filename) part.filename = aiV4Part.filename; parts.push(part); } else if (typeof aiV4Part.data === "string") { const categorized = categorizeFileData(aiV4Part.data, aiV4Part.mimeType); if (categorized.type === "url" || categorized.type === "dataUri" || categorized.type === "providerFileId") { const part = { type: "file", data: aiV4Part.data, mimeType: categorized.mimeType || "image/png" }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; if (aiV4Part.filename) part.filename = aiV4Part.filename; parts.push(part); } else try { const part = { type: "file", mimeType: categorized.mimeType || "image/png", data: require_signals.convertDataContentToBase64String(aiV4Part.data) }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; if (aiV4Part.filename) part.filename = aiV4Part.filename; parts.push(part); } catch (error) { console.error(`Failed to convert binary data to base64 in CoreMessage file part: ${error}`, error); } } else try { const part = { type: "file", mimeType: aiV4Part.mimeType, data: require_signals.convertDataContentToBase64String(aiV4Part.data) }; if (aiV4Part.providerOptions) part.providerMetadata = aiV4Part.providerOptions; if (aiV4Part.filename) part.filename = aiV4Part.filename; parts.push(part); } catch (error) { console.error(`Failed to convert binary data to base64 in CoreMessage file part: ${error}`, error); } break; } const content = { format: 2, parts: filterEmptyTextParts$1(parts) }; if (toolInvocations.length) content.toolInvocations = toolInvocations; if (typeof coreMessage.content === `string`) content.content = coreMessage.content; if (experimentalAttachments.length) content.experimental_attachments = experimentalAttachments; if (coreMessage.providerOptions) content.providerMetadata = coreMessage.providerOptions; else if ("experimental_providerMetadata" in coreMessage && coreMessage.experimental_providerMetadata) content.providerMetadata = coreMessage.experimental_providerMetadata; if ("metadata" in coreMessage && coreMessage.metadata !== null && coreMessage.metadata !== void 0) content.metadata = coreMessage.metadata; const rawCreatedAt = "metadata" in coreMessage && coreMessage.metadata && typeof coreMessage.metadata === "object" && "createdAt" in coreMessage.metadata ? coreMessage.metadata.createdAt : void 0; return { id, role: TypeDetector.getRole(coreMessage), createdAt: ctx.generateCreatedAt(messageSource, rawCreatedAt), threadId: ctx.memoryInfo?.threadId, resourceId: ctx.memoryInfo?.resourceId, content }; } }; //#endregion //#region ../_vendored/ai_v6/dist/index.js const VERSION$1 = "1.9.1"; const re = /^(\d+)\.(\d+)\.(\d+)(-(.+))?$/; /** * Create a function to test an API version to see if it is compatible with the provided ownVersion. * * The returned function has the following semantics: * - Exact match is always compatible * - Major versions must match exactly * - 1.x package cannot use global 2.x package * - 2.x package cannot use global 1.x package * - The minor version of the API module requesting access to the global API must be less than or equal to the minor version of this API * - 1.3 package may use 1.4 global because the later global contains all functions 1.3 expects * - 1.4 package may NOT use 1.3 global because it may try to call functions which don't exist on 1.3 * - If the major version is 0, the minor version is treated as the major and the patch is treated as the minor * - Patch and build tag differences are not considered at this time * * @param ownVersion version which should be checked against */ function _makeCompatibilityCheck(ownVersion) { const acceptedVersions = /* @__PURE__ */ new Set([ownVersion]); const rejectedVersions = /* @__PURE__ */ new Set(); const myVersionMatch = ownVersion.match(re); if (!myVersionMatch) return () => false; const ownVersionParsed = { major: +myVersionMatch[1], minor: +myVersionMatch[2], patch: +myVersionMatch[3], prerelease: myVersionMatch[4] }; if (ownVersionParsed.prerelease != null) return function isExactmatch(globalVersion) { return globalVersion === ownVersion; }; function _reject(v) { rejectedVersions.add(v); return false; } function _accept(v) { acceptedVersions.add(v); return true; } return function isCompatible(globalVersion) { if (acceptedVersions.has(globalVersion)) return true; if (rejectedVersions.has(globalVersion)) return false; const globalVersionMatch = globalVersion.match(re); if (!globalVersionMatch) return _reject(globalVersion); const globalVersionParsed = { major: +globalVersionMatch[1], minor: +globalVersionMatch[2], patch: +globalVersionMatch[3], prerelease: globalVersionMatch[4] }; if (globalVersionParsed.prerelease != null) return _reject(globalVersion); if (ownVersionParsed.major !== globalVersionParsed.major) return _reject(globalVersion); if (ownVersionParsed.major === 0) { if (ownVersionParsed.minor === globalVersionParsed.minor && ownVersionParsed.patch <= globalVersionParsed.patch) return _accept(globalVersion); return _reject(globalVersion); } if (ownVersionParsed.minor <= globalVersionParsed.minor) return _accept(globalVersion); return _reject(globalVersion); }; } /** * Test an API version to see if it is compatible with this API. * * - Exact match is always compatible * - Major versions must match exactly * - 1.x package cannot use global 2.x package * - 2.x package cannot use global 1.x package * - The minor version of the API module requesting access to the global API must be less than or equal to the minor version of this API * - 1.3 package may use 1.4 global because the later global contains all functions 1.3 expects * - 1.4 package may NOT use 1.3 global because it may try to call functions which don't exist on 1.3 * - If the major version is 0, the minor version is treated as the major and the patch is treated as the minor * - Patch and build tag differences are not considered at this time * * @param version version of the API requesting an instance of the global API */ const isCompatible = _makeCompatibilityCheck(VERSION$1); const major = VERSION$1.split(".")[0]; const GLOBAL_OPENTELEMETRY_API_KEY = Symbol.for(`opentelemetry.js.api.${major}`); const _global = typeof globalThis === "object" ? globalThis : typeof self === "object" ? self : typeof window === "object" ? window : typeof global === "object" ? global : {}; function registerGlobal(type, instance, diag, allowOverride = false) { var _a; const api = _global[GLOBAL_OPENTELEMETRY_API_KEY] = (_a = _global[GLOBAL_OPENTELEMETRY_API_KEY]) !== null && _a !== void 0 ? _a : { version: VERSION$1 }; if (!allowOverride && api[type]) { const err = /* @__PURE__ */ new Error(`@opentelemetry/api: Attempted duplicate registration of API: ${type}`); diag.error(err.stack || err.message); return false; } if (api.version !== "1.9.1") { const err = /* @__PURE__ */ new Error(`@opentelemetry/api: Registration of version v${api.version} for ${type} does not match previously registered API v${VERSION$1}`); diag.error(err.stack || err.message); return false; } api[type] = instance; diag.debug(`@opentelemetry/api: Registered a global for ${type} v${VERSION$1}.`); return true; } function getGlobal(type) { var _a, _b; const globalVersion = (_a = _global[GLOBAL_OPENTELEMETRY_API_KEY]) === null || _a === void 0 ? void 0 : _a.version; if (!globalVersion || !isCompatible(globalVersion)) return; return (_b = _global[GLOBAL_OPENTELEMETRY_API_KEY]) === null || _b === void 0 ? void 0 : _b[type]; } function unregisterGlobal(type, diag) { diag.debug(`@opentelemetry/api: Unregistering a global for ${type} v${VERSION$1}.`); const api = _global[GLOBAL_OPENTELEMETRY_API_KEY]; if (api) delete api[type]; } /** * Component Logger which is meant to be used as part of any component which * will add automatically additional namespace in front of the log message. * It will then forward all message to global diag logger * @example * const cLogger = diag.createComponentLogger({ namespace: '@opentelemetry/instrumentation-http' }); * cLogger.debug('test'); * // @opentelemetry/instrumentation-http test */ var DiagComponentLogger = class { constructor(props) { this._namespace = props.namespace || "DiagComponentLogger"; } debug(...args) { return logProxy("debug", this._namespace, args); } error(...args) { return logProxy("error", this._namespace, args); } info(...args) { return logProxy("info", this._namespace, args); } warn(...args) { return logProxy("warn", this._namespace, args); } verbose(...args) { return logProxy("verbose", this._namespace, args); } }; function logProxy(funcName, namespace, args) { const logger = getGlobal("diag"); if (!logger) return; return logger[funcName](namespace, ...args); } /** * Defines the available internal logging levels for the diagnostic logger, the numeric values * of the levels are defined to match the original values from the initial LogLevel to avoid * compatibility/migration issues for any implementation that assume the numeric ordering. */ var DiagLogLevel; (function(DiagLogLevel) { /** Diagnostic Logging level setting to disable all logging (except and forced logs) */ DiagLogLevel[DiagLogLevel["NONE"] = 0] = "NONE"; /** Identifies an error scenario */ DiagLogLevel[DiagLogLevel["ERROR"] = 30] = "ERROR"; /** Identifies a warning scenario */ DiagLogLevel[DiagLogLevel["WARN"] = 50] = "WARN"; /** General informational log message */ DiagLogLevel[DiagLogLevel["INFO"] = 60] = "INFO"; /** General debug log message */ DiagLogLevel[DiagLogLevel["DEBUG"] = 70] = "DEBUG"; /** * Detailed trace level logging should only be used for development, should only be set * in a development environment. */ DiagLogLevel[DiagLogLevel["VERBOSE"] = 80] = "VERBOSE"; /** Used to set the logging level to include all logging */ DiagLogLevel[DiagLogLevel["ALL"] = 9999] = "ALL"; })(DiagLogLevel || (DiagLogLevel = {})); function createLogLevelDiagLogger(maxLevel, logger) { if (maxLevel < DiagLogLevel.NONE) maxLevel = DiagLogLevel.NONE; else if (maxLevel > DiagLogLevel.ALL) maxLevel = DiagLogLevel.ALL; logger = logger || {}; function _filterFunc(funcName, theLevel) { const theFunc = logger[funcName]; if (typeof theFunc === "function" && maxLevel >= theLevel) return theFunc.bind(logger); return function() {}; } return { error: _filterFunc("error", DiagLogLevel.ERROR), warn: _filterFunc("warn", DiagLogLevel.WARN), info: _filterFunc("info", DiagLogLevel.INFO), debug: _filterFunc("debug", DiagLogLevel.DEBUG), verbose: _filterFunc("verbose", DiagLogLevel.VERBOSE) }; } const API_NAME$2 = "diag"; /** * Singleton object which represents the entry point to the OpenTelemetry internal * diagnostic API * * @since 1.0.0 */ var DiagAPI = class DiagAPI { /** Get the singleton instance of the DiagAPI API */ static instance() { if (!this._instance) this._instance = new DiagAPI(); return this._instance; } /** * Private internal constructor * @private */ constructor() { function _logProxy(funcName) { return function(...args) { const logger = getGlobal("diag"); if (!logger) return; return logger[funcName](...args); }; } const self = this; const setLogger = (logger, optionsOrLogLevel = { logLevel: DiagLogLevel.INFO }) => { var _a, _b, _c; if (logger === self) { const err = /* @__PURE__ */ new Error("Cannot use diag as the logger for itself. Please use a DiagLogger implementation like ConsoleDiagLogger or a custom implementation"); self.error((_a = err.stack) !== null && _a !== void 0 ? _a : err.message); return false; } if (typeof optionsOrLogLevel === "number") optionsOrLogLevel = { logLevel: optionsOrLogLevel }; const oldLogger = getGlobal("diag"); const newLogger = createLogLevelDiagLogger((_b = optionsOrLogLevel.logLevel) !== null && _b !== void 0 ? _b : DiagLogLevel.INFO, logger); if (oldLogger && !optionsOrLogLevel.suppressOverrideMessage) { const stack = (_c = (/* @__PURE__ */ new Error()).stack) !== null && _c !== void 0 ? _c : "<failed to generate stacktrace>"; oldLogger.warn(`Current logger will be overwritten from ${stack}`); newLogger.warn(`Current logger will overwrite one already registered from ${stack}`); } return registerGlobal("diag", newLogger, self, true); }; self.setLogger = setLogger; self.disable = () => { unregisterGlobal(API_NAME$2, self); }; self.createComponentLogger = (options) => { return new DiagComponentLogger(options); }; self.verbose = _logProxy("verbose"); self.debug = _logProxy("debug"); self.info = _logProxy("info"); self.warn = _logProxy("warn"); self.error = _logProxy("error"); } }; /** * Get a key to uniquely identify a context value * * @since 1.0.0 */ function createContextKey(description) { return Symbol.for(description); } /** * The root context is used as the default parent context when there is no active context * * @since 1.0.0 */ const ROOT_CONTEXT = new class BaseContext { /** * Construct a new context which inherits val