'use strict'; var chunkPWQW5NYR_cjs = require('./chunk-PWQW5NYR.cjs'); var chunk4SCYX53T_cjs = require('./chunk-4SCYX53T.cjs'); var chunkI4YELKDU_cjs = require('./chunk-I4YELKDU.cjs'); var chunkPAMAGEYY_cjs = require('./chunk-PAMAGEYY.cjs'); var chunkXSOONORA_cjs = require('./chunk-XSOONORA.cjs'); var uuid = require('@lukeed/uuid'); var providerUtilsV5 = require('@ai-sdk/provider-utils-v5'); // src/tools/payload-transform.ts function normalizeToolPayloadTransformPolicy(policy) { if (!policy) { return void 0; } if ("transformToolPayload" in policy) { return policy; } if ("projectToolPayload" in policy && policy.projectToolPayload) { return { transformToolPayload: policy.projectToolPayload, targets: policy.targets }; } return void 0; } var PHASE_TO_TOOL_TRANSFORMER = { "input-delta": "inputDelta", "input-available": "input", "output-available": "output", error: "error", approval: "approval", suspend: "suspend", resume: "resume" }; function isPolicyConfiguredForTarget(policy, target) { return Boolean(policy?.transformToolPayload && (!policy.targets || policy.targets.includes(target))); } function isTransformConfigured(source, target) { return Boolean(isPolicyConfiguredForTarget(source?.policy, target) || source?.toolTransform?.[target]); } function getToolTransformer(source, target, phase) { return source?.toolTransform?.[target]?.[PHASE_TO_TOOL_TRANSFORMER[phase]]; } function safePlaceholder(context) { return { message: `Tool ${context.phase} payload unavailable` }; } async function transformOneTarget(context, source, logger) { const configured = isTransformConfigured(source, context.target); if (!configured) { return void 0; } const transformers = [ isPolicyConfiguredForTarget(source?.policy, context.target) ? source?.policy?.transformToolPayload : void 0, getToolTransformer(source, context.target, context.phase) ].filter(Boolean); if (transformers.length === 0) { return context.phase === "input-delta" ? { suppress: true } : { transformed: safePlaceholder(context) }; } for (const transformer of transformers) { try { const transformed = await transformer(context); if (transformed !== void 0) { return { transformed }; } } catch (error) { logger?.warn?.("Tool payload transform failed", { toolName: context.toolName, toolCallId: context.toolCallId, target: context.target, phase: context.phase, error }); return context.phase === "input-delta" ? { suppress: true, failed: true } : { transformed: safePlaceholder(context), failed: true }; } } return context.phase === "input-delta" ? { suppress: true } : { transformed: safePlaceholder(context) }; } async function transformToolPayloadForTargets(context, source, logger) { const display = await transformOneTarget({ ...context, target: "display" }, source, logger); const transcript = await transformOneTarget({ ...context, target: "transcript" }, source, logger); if (!display && !transcript) { return void 0; } return { ...display ? { display: { [context.phase]: display } } : {}, ...transcript ? { transcript: { [context.phase]: transcript } } : {} }; } function getTransformedToolPayload(metadata, target, phase) { const mastraMetadata = metadata?.mastra; const state = mastraMetadata?.toolPayloadTransform?.[target]?.[phase] ?? mastraMetadata?.toolPayloadProjection?.[target]?.[phase]; if (state && Object.prototype.hasOwnProperty.call(state, "projected") && !Object.prototype.hasOwnProperty.call(state, "transformed")) { const { projected, ...rest } = state; return { ...rest, transformed: projected }; } return state; } function hasTransformedToolPayload(transform) { return Boolean(transform && Object.prototype.hasOwnProperty.call(transform, "transformed")); } function mergeTransformMetadata(existing, next) { if (!existing) { return next; } if (!next) { return existing; } return { display: { ...existing.display ?? {}, ...next.display ?? {} }, transcript: { ...existing.transcript ?? {}, ...next.transcript ?? {} } }; } function withToolPayloadTransformProviderMetadata(providerMetadata, transformMetadata) { const transform = transformMetadata?.mastra?.toolPayloadTransform; if (!transform) { return providerMetadata; } return { ...providerMetadata ?? {}, mastra: { ...providerMetadata?.mastra ?? {}, toolPayloadTransform: mergeTransformMetadata(providerMetadata?.mastra?.toolPayloadTransform, transform) } }; } function withToolPayloadTransformMetadata(chunk, transform) { if (!transform) { return chunk; } return { ...chunk, metadata: { ...chunk.metadata ?? {}, mastra: { ...chunk.metadata?.mastra ?? {}, toolPayloadTransform: mergeTransformMetadata(chunk.metadata?.mastra?.toolPayloadTransform, transform) } } }; } function convertDataContentToBase64String(content) { if (typeof content === "string") { return content; } if (content instanceof ArrayBuffer) { return providerUtilsV5.convertUint8ArrayToBase64(new Uint8Array(content)); } return providerUtilsV5.convertUint8ArrayToBase64(content); } // src/agent/signals.ts function isMastraSignalMessage(message) { return message.role === "signal"; } function normalizeSignalType(input) { if (input.type === "user-message") { return { type: "user", tagName: input.tagName ?? "user" }; } if (input.type === "system-reminder") { return { type: "reactive", tagName: input.tagName ?? "system-reminder" }; } if (input.type === "user" || input.type === "state" || input.type === "notification") { return { type: input.type, tagName: input.tagName ?? input.type }; } if (input.type === "reactive") { return { type: "reactive", tagName: input.tagName ?? "system-reminder" }; } throw new Error( `Invalid signal type: ${input.type}. Use a supported signal type and set tagName for custom XML tags.` ); } function normalizeSignal(signal) { const { type, tagName } = normalizeSignalType(signal); return { ...signal, type, tagName, id: signal.id ?? crypto.randomUUID(), createdAt: signal.createdAt instanceof Date ? signal.createdAt : signal.createdAt ? new Date(signal.createdAt) : /* @__PURE__ */ new Date(), acceptedAt: signal.acceptedAt instanceof Date ? signal.acceptedAt : signal.acceptedAt ? new Date(signal.acceptedAt) : void 0 }; } function escapeXml(value) { return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"); } function escapeXmlAttribute(value) { return escapeXml(value).replaceAll('"', """); } var XML_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_.-]*$/; function assertXmlName(name, label) { if (!XML_NAME_PATTERN.test(name)) { throw new Error(`Invalid signal XML ${label}: ${name}`); } } function signalAttributesToXml(attributes) { if (!attributes) { return ""; } const serialized = Object.entries(attributes).filter((entry) => entry[1] !== null && entry[1] !== void 0).map(([key, value]) => { assertXmlName(key, "attribute name"); return `${key}="${escapeXmlAttribute(String(value))}"`; }).join(" "); return serialized ? ` ${serialized}` : ""; } function signalToXmlMarkup(signal) { const tagName = signal.tagName ?? normalizeSignalType(signal).tagName; assertXmlName(tagName, "tag name"); const attributesXml = signalAttributesToXml(signal.attributes); if (!signal.contents) return `<${tagName}${attributesXml} />`; return `<${tagName}${attributesXml}>${escapeXml(signal.contents)}`; } function legacyContentsToSignalContents(value) { if (typeof value === "string") return value; if (Array.isArray(value)) { const parts = []; for (const entry of value) { if (typeof entry === "string") { parts.push({ type: "text", text: entry }); continue; } const decoded2 = legacyEntryToParts(entry); if (!decoded2) return void 0; parts.push(...decoded2); } return collapseLegacyParts(parts); } const decoded = legacyEntryToParts(value); return decoded ? collapseLegacyParts(decoded) : void 0; } function legacyEntryToParts(entry) { if (!entry || typeof entry !== "object") return void 0; const record = entry; if (record.role === "user" && "content" in record) { const content = record.content; if (typeof content === "string") return [{ type: "text", text: content }]; if (Array.isArray(content)) { const inner = []; for (const part2 of content) { const decoded = legacyPartToSignalPart(part2); if (!decoded) return void 0; inner.push(decoded); } return inner; } return void 0; } const part = legacyPartToSignalPart(record); return part ? [part] : void 0; } function legacyPartToSignalPart(part) { if (!part || typeof part !== "object") return void 0; const record = part; const providerOptions = record.providerOptions && typeof record.providerOptions === "object" && !Array.isArray(record.providerOptions) ? record.providerOptions : void 0; if (record.type === "text" && typeof record.text === "string") { return { type: "text", text: record.text, ...providerOptions ? { providerOptions } : {} }; } if (record.type === "file" || record.type === "image") { const data = record.type === "image" ? record.image ?? record.data : record.data; if (typeof data !== "string") return void 0; const mediaType = typeof record.mediaType === "string" ? record.mediaType : typeof record.mimeType === "string" ? record.mimeType : record.type === "image" ? "image/png" : ""; if (!mediaType) return void 0; return { type: "file", data, mediaType, ...typeof record.filename === "string" ? { filename: record.filename } : {}, ...providerOptions ? { providerOptions } : {} }; } return void 0; } function collapseLegacyParts(parts) { if (parts.length === 0) return void 0; const first = parts[0]; if (parts.length === 1 && first?.type === "text") return first.text; return parts; } function contentsToSignalParts(contents) { if (typeof contents === "string") return [{ type: "text", text: contents }]; return contents.map((part) => { if (part.type === "file") { const data = part.data instanceof URL ? part.data.toString() : convertDataContentToBase64String(part.data); return { type: "file", data, mediaType: part.mediaType, ...part.filename ? { filename: part.filename } : {}, ...part.providerOptions ? { providerOptions: part.providerOptions } : {} }; } return { type: "text", text: part.text, ...part.providerOptions ? { providerOptions: part.providerOptions } : {} }; }); } function storagePartsToSignalParts(parts) { const out = []; for (const part of parts) { const providerOptions = part.providerMetadata; if (part.type === "text") { out.push({ type: "text", text: part.text, ...providerOptions ? { providerOptions } : {} }); } else if (part.type === "file" && typeof part.data === "string") { const file = part; out.push({ type: "file", data: file.data, mediaType: typeof file.mimeType === "string" ? file.mimeType : "", ...typeof file.filename === "string" ? { filename: file.filename } : {}, ...providerOptions ? { providerOptions } : {} }); } } return out; } function partsToSignalContents(parts) { if (parts.length === 1 && parts[0]?.type === "text" && !parts[0].providerOptions) return parts[0].text; return parts.map( (part) => part.type === "file" ? { type: "file", data: part.data, mediaType: part.mediaType, ...part.filename ? { filename: part.filename } : {}, ...part.providerOptions ? { providerOptions: part.providerOptions } : {} } : { type: "text", text: part.text, ...part.providerOptions ? { providerOptions: part.providerOptions } : {} } ); } function hasMeaningfulAttributes(attributes) { if (!attributes) return false; return Object.keys(attributes).some((key) => { const value = attributes[key]; return value !== null && value !== void 0; }); } function injectMarkerInline(signal, parts) { let wrapped = false; const out = []; for (const part of parts) { if (!wrapped && part.type === "text") { wrapped = true; out.push({ ...part, text: signalToXmlMarkup({ ...signal, contents: part.text }) }); } else { out.push(part); } } if (!wrapped) { const markerText = signalToXmlMarkup({ type: signal.type, tagName: signal.tagName, attributes: signal.attributes }); out.unshift({ type: "text", text: markerText }); } return out; } function signalToLLMMessage(signal, parts) { const isUserMessage = signal.type === "user"; const hasAttrs = hasMeaningfulAttributes(signal.attributes); const anyPartProviderOptions = parts.some((part) => part.providerOptions); let content; if (isUserMessage && !hasAttrs) { content = parts.length === 1 && parts[0]?.type === "text" && !parts[0].providerOptions ? parts[0].text : parts; } else if (parts.every((part) => part.type === "text") && !anyPartProviderOptions) { content = signalToXmlMarkup({ ...signal, contents: parts.map((part) => part.text).join("\n") }); } else { content = injectMarkerInline(signal, parts); } return { role: "user", content, ...signal.providerOptions ? { providerOptions: signal.providerOptions } : {} }; } function signalToDataPart(signal, parts) { return { type: signal.type === "user" ? "data-user-message" : "data-signal", data: { id: signal.id, type: signal.type, tagName: signal.tagName, contents: partsToSignalContents(parts), createdAt: signal.createdAt.toISOString(), ...signal.acceptedAt ? { acceptedAt: signal.acceptedAt.toISOString() } : {}, ...signal.attributes ? { attributes: signal.attributes } : {}, ...signal.metadata ? { metadata: signal.metadata } : {} }, transient: true }; } function signalToDBMessage(signal, parts, options) { const storageParts = parts.length > 0 ? parts.map( (part) => part.type === "file" ? { type: "file", data: part.data, mimeType: part.mediaType, ...part.filename ? { filename: part.filename } : {}, ...part.providerOptions ? { providerMetadata: part.providerOptions } : {} } : { type: "text", text: part.text, ...part.providerOptions ? { providerMetadata: part.providerOptions } : {} } ) : [{ type: "text", text: "" }]; return { id: signal.id, role: "signal", createdAt: signal.createdAt, threadId: options?.threadId, resourceId: options?.resourceId, type: signal.tagName, content: { format: 2, parts: storageParts, ...signal.providerOptions ? { providerMetadata: signal.providerOptions } : {}, metadata: { signal: { id: signal.id, type: signal.type, tagName: signal.tagName, createdAt: signal.createdAt.toISOString(), ...signal.acceptedAt ? { acceptedAt: signal.acceptedAt.toISOString() } : {}, ...signal.attributes ? { attributes: signal.attributes } : {}, ...signal.metadata ? { metadata: signal.metadata } : {} } } } }; } function isCreatedAgentSignal(input) { if (!input || typeof input !== "object" || Array.isArray(input)) return false; const candidate = input; return candidate.__isCreatedSignal === true; } function createSignal(input) { const signal = normalizeSignal(input); const parts = contentsToSignalParts(signal.contents); return { ...signal, __isCreatedSignal: true, toDBMessage: (options) => signalToDBMessage(signal, parts, options), toLLMMessage: () => signalToLLMMessage(signal, parts), toDataPart: () => signalToDataPart(signal, parts) }; } function resolveDeliveryAttributes(signal, attributes) { if (!attributes || Object.keys(attributes).length === 0) return signal; return createSignal({ ...signal, attributes: { ...signal.attributes, ...attributes } }); } function signalToMessage(signal) { return createSignal(signal).toLLMMessage(); } function signalToMastraDBMessage(signal, options) { return createSignal(signal).toDBMessage(options); } function signalToDataPartFormat(signal) { return createSignal(signal).toDataPart(); } function mastraDBMessageToSignal(message) { const metadataSignal = message.content.metadata?.signal; const signalMetadata = metadataSignal && typeof metadataSignal === "object" && !Array.isArray(metadataSignal) ? metadataSignal : void 0; const rawType = typeof signalMetadata?.type === "string" ? signalMetadata.type : message.type ?? "user-message"; const type = rawType; const tagName = typeof signalMetadata?.tagName === "string" ? signalMetadata.tagName : void 0; const rawLegacyContents = signalMetadata && "contents" in signalMetadata ? signalMetadata.contents : void 0; const legacyContents = legacyContentsToSignalContents(rawLegacyContents); const partsContents = partsToSignalContents(storagePartsToSignalParts(message.content.parts)); const contents = legacyContents ?? partsContents; const providerMetadata = message.content.providerMetadata; const base = { id: typeof signalMetadata?.id === "string" ? signalMetadata.id : message.id, createdAt: typeof signalMetadata?.createdAt === "string" ? signalMetadata.createdAt : message.createdAt, acceptedAt: typeof signalMetadata?.acceptedAt === "string" ? signalMetadata.acceptedAt : void 0, attributes: signalMetadata?.attributes && typeof signalMetadata.attributes === "object" && !Array.isArray(signalMetadata.attributes) ? signalMetadata.attributes : void 0, metadata: signalMetadata?.metadata && typeof signalMetadata.metadata === "object" && !Array.isArray(signalMetadata.metadata) ? signalMetadata.metadata : void 0, providerOptions: providerMetadata && typeof providerMetadata === "object" && !Array.isArray(providerMetadata) ? providerMetadata : void 0 }; return createSignal({ ...base, type, tagName, contents }); } function createMessageSignal(input, options) { const message = typeof input === "string" || Array.isArray(input) ? { contents: input } : input; return createSignal({ ...message, ...options, type: "user", tagName: "user" }); } function dataPartToSignal(part) { return createSignal(part.data); } // src/agent/message-list/detection/TypeDetector.ts 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)}` ); } }; // src/agent/message-list/prompt/image-utils.ts 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(";"); const mimeType = semicolonIndex !== -1 ? header.substring(0, semicolonIndex) : header; return { isDataUri: true, mimeType: mimeType || void 0, base64Content }; } function createDataUri(base64Content, mimeType = "application/octet-stream") { if (base64Content.startsWith("data:")) { return base64Content; } return `data:${mimeType};base64,${base64Content}`; } 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 = convertDataContentToBase64String(image); return base64; } return String(image); } 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; } 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; } } 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 (isValidUrl(data)) { return { type: "url", mimeType, data }; } return { type: "raw", mimeType, data }; } // src/agent/message-list/utils/response-item-metadata.ts var 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 azureMetadata = providerMetadata.azure; const azureItemId = azureMetadata?.itemId; const openaiMetadata = providerMetadata.openai; const openaiItemId = openaiMetadata?.itemId; if (typeof azureItemId === "string" && azureItemId === openaiItemId) { return [{ provider: "azure", itemId: azureItemId }]; } return RESPONSE_ITEM_ID_PROVIDERS.flatMap((provider) => { const metadata = providerMetadata[provider]; const itemId = metadata?.itemId; return typeof itemId === "string" ? [{ provider, itemId }] : []; }); } function getResponseProviderItemKeys(providerMetadata) { return getResponseProviderItemIds(providerMetadata).map( ({ provider, itemId }) => formatResponseProviderItemKey(provider, itemId) ); } // src/agent/message-list/utils/provider-compat.ts 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; } function ensureAnthropicCompatibleMessages(messages, dbMessages) { return messages.map((msg) => enrichToolResultsWithInput(msg, dbMessages)); } 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 useIds = /* @__PURE__ */ new Set(); const inlineResultIds = /* @__PURE__ */ new Set(); for (const part of current.content) { if (part.type === "tool-call") useIds.add(part.toolCallId); else if (part.type === "tool-result") inlineResultIds.add(part.toolCallId); } const next = messages[i + 1]; const nextResultIds = /* @__PURE__ */ new Set(); if (next && next.role === "tool" && Array.isArray(next.content)) { for (const part of next.content) { if (part.type === "tool-result") nextResultIds.add(part.toolCallId); } } const validPairs = new Set([...useIds].filter((id) => inlineResultIds.has(id) || nextResultIds.has(id))); 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; } 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; }) }; } function hasOpenAIReasoningItemId(part) { return Boolean(getOpenAIReasoningItemId(part)); } function hasResponseProviderItemId(part) { return Boolean(getResponseProviderItemIdFromPart(part)); } function getOpenAIReasoningItemId(part) { if (!part || typeof part !== "object") return void 0; const partAny = part; const providerMetadata = partAny.providerMetadata; const openaiMetadata = 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); } 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 {}; } // src/agent/message-list/adapters/AIV4Adapter.ts function getDisplayTransform(providerMetadata, phase, fallback, enabled = true) { if (!enabled) { return fallback; } const transform = getTransformedToolPayload(providerMetadata, "display", phase); return hasTransformedToolPayload(transform) ? transform.transformed : fallback; } function transformV4ToolInvocationForDisplay(invocation, providerMetadata, enabled) { return { ...invocation, args: getDisplayTransform(providerMetadata, "input-available", invocation.args, enabled), ...invocation.state === "result" ? { result: getDisplayTransform( providerMetadata, "output-available", getDisplayTransform(providerMetadata, "error", invocation.result, enabled), enabled ) } : {} }; } function preserveExtendedParts(parts) { return parts; } function filterEmptyTextParts(parts) { const hasNonEmptyParts = parts.some((part) => !(part.type === "text" && part.text === "")); if (!hasNonEmptyParts) return parts; return parts.filter((part) => { if (part.type === "text") { return part.text !== ""; } return true; }); } function getSignalType(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(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(message); if (type === "user") return "user"; if (type === "reactive") return message.type; return type; } function isUserSignalType(type) { return type === "user" || type === "user-message"; } function toSignalDataPart(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(message) ?? "signal"; const tagName = getSignalTagName(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 } : {} } }; } 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`) { let normalizedUrl; if (typeof part.data === "string") { const categorized = categorizeFileData(part.data, part.mimeType); if (categorized.type === "raw") { normalizedUrl = createDataUri(part.data, part.mimeType || "application/octet-stream"); } else { normalizedUrl = part.data; } } else { normalizedUrl = part.data; } experimentalAttachments.push({ contentType: part.mimeType, 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 toolInvocation = { ...part.toolInvocation, args: getDisplayTransform( part.providerMetadata, "input-available", part.toolInvocation.args, transformToolPayloads ), ...part.toolInvocation.state === "result" ? { result: getDisplayTransform( part.providerMetadata, "output-available", getDisplayTransform( part.providerMetadata, "error", part.toolInvocation.result, transformToolPayloads ), transformToolPayloads ) } : {} }; 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 signalType = m.role === "signal" ? getSignalType(m) : void 0; const isUserMessageSignal = isUserSignalType(signalType); const v4Parts = preserveExtendedParts( m.role === "signal" && !isUserMessageSignal ? [toSignalDataPart(m, m.content.content || contentString)] : parts ); if (m.role === `user`) { const uiMessage2 = { id: m.id, role: m.role, content: m.content.content || contentString, createdAt: m.createdAt, parts: v4Parts, experimental_attachments: experimentalAttachments }; if (m.content.metadata) { uiMessage2.metadata = m.content.metadata; } return uiMessage2; } else if (m.role === `assistant`) { const isSingleTextContentArray = Array.isArray(m.content.content) && m.content.content.length === 1 && m.content.content[0].type === `text`; const uiMessage2 = { 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) { uiMessage2.metadata = m.content.metadata; } return uiMessage2; } 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 chunkXSOONORA_cjs.MastraError({ id: "INVALID_SYSTEM_MESSAGE_FORMAT", domain: chunkXSOONORA_cjs.ErrorDomain.AGENT, category: chunkXSOONORA_cjs.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 filteredParts = message.parts ? filterEmptyTextParts(message.parts) : []; const content = { format: 2, parts: filteredParts }; 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: "", 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") { 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: 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: 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 filteredParts = filterEmptyTextParts(parts); const content = { format: 2, parts: filteredParts }; 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 }; } }; // src/agent/message-list/utils/tool-name.ts var TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/; var FALLBACK_TOOL_NAME = "unknown_tool"; function sanitizeToolName(toolName) { if (typeof toolName !== "string") { return FALLBACK_TOOL_NAME; } return TOOL_NAME_PATTERN.test(toolName) ? toolName : FALLBACK_TOOL_NAME; } // src/agent/message-list/adapters/AIV5Adapter.ts function filterEmptyTextParts2(parts) { const hasNonEmptyParts = parts.some((part) => !(part.type === "text" && part.text === "")); if (!hasNonEmptyParts) return parts; return parts.filter((part) => { if (part.type === "text") { return part.text !== ""; } return true; }); } function getSignalType2(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 getSignalTagName2(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 = getSignalType2(message); if (type === "user") return "user"; if (type === "reactive") return message.type; return type; } function isUserSignalType2(type) { return type === "user" || type === "user-message"; } function getTextContent(message) { return typeof message.content.content === "string" ? message.content.content : message.content.parts.find((part) => part.type === "text")?.text ?? ""; } function toSignalDataPart2(message) { 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 = getSignalType2(message) ?? "signal"; const tagName = getSignalTagName2(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 : getTextContent(message), 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 } : {} } }; } function getToolName(type) { if (typeof type === "object" && type && "type" in type) { type = type.type; } if (typeof type !== "string") { return sanitizeToolName(type); } if (type === "dynamic-tool") { return "dynamic-tool"; } if (type.startsWith("tool-")) { return sanitizeToolName(type.slice("tool-".length)); } return sanitizeToolName(type); } function mergeMastraCreatedAt(metadata, createdAt) { if (createdAt == null) { return metadata; } return { ...metadata || {}, mastra: { ...(metadata || {}).mastra || {}, createdAt } }; } function getMastraCreatedAt(providerMetadata) { const value = providerMetadata?.mastra; if (!value || typeof value !== "object") { return void 0; } const createdAt = value.createdAt; return typeof createdAt === "number" ? createdAt : void 0; } function getDisplayTransform2(providerMetadata, phase, fallback, enabled = true) { if (!enabled) { return fallback; } const transform = getTransformedToolPayload(providerMetadata, "display", phase); return hasTransformedToolPayload(transform) ? transform.transformed : fallback; } function transformToolStateDataForDisplay(data, phase, enabled = true) { if (!enabled) { return data; } if (!data || typeof data !== "object") { return data; } const stateData = data; const metadata = stateData.metadata ?? stateData.providerMetadata; const argsTransform = getTransformedToolPayload(metadata, "display", phase); const inputTransform = getTransformedToolPayload(metadata, "display", "input-available"); const transformedArgs = phase === "approval" ? hasTransformedToolPayload(argsTransform) ? argsTransform.transformed : hasTransformedToolPayload(inputTransform) ? inputTransform.transformed : void 0 : hasTransformedToolPayload(inputTransform) ? inputTransform.transformed : hasTransformedToolPayload(argsTransform) ? argsTransform.transformed : void 0; const transformedSuspendPayload = phase === "suspend" && hasTransformedToolPayload(argsTransform) ? argsTransform.transformed : void 0; return { ...stateData, ...transformedArgs !== void 0 ? { args: transformedArgs } : {}, ...transformedSuspendPayload !== void 0 ? { suspendPayload: transformedSuspendPayload } : {} }; } var AIV5Adapter = class { /** * Direct conversion from MastraDBMessage to AIV5 UIMessage */ static toUIMessage(dbMsg, options) { const signalType = dbMsg.role === "signal" ? getSignalType2(dbMsg) : void 0; const isUserMessageSignal = isUserSignalType2(signalType); const transformToolPayloads = options?.transformToolPayloads ?? true; const parts = []; const metadata = { ...dbMsg.content.metadata || {} }; if (dbMsg.role === "signal" && !isUserMessageSignal) { parts.push(toSignalDataPart2(dbMsg)); } if (dbMsg.createdAt) metadata.createdAt = dbMsg.createdAt; if (dbMsg.threadId) metadata.threadId = dbMsg.threadId; if (dbMsg.resourceId) metadata.resourceId = dbMsg.resourceId; if (dbMsg.content.providerMetadata) { metadata.providerMetadata = dbMsg.content.providerMetadata; } if (dbMsg.role === "signal" && !isUserMessageSignal) { return { id: dbMsg.id, role: "system", metadata, parts }; } const hasToolInvocationParts = dbMsg.content.parts?.some((p) => p.type === "tool-invocation"); if (dbMsg.content.toolInvocations && !hasToolInvocationParts) { for (const invocation of dbMsg.content.toolInvocations) { if (invocation.state === "result") { parts.push({ type: `tool-${invocation.toolName}`, toolCallId: invocation.toolCallId, state: "output-available", input: invocation.args, output: invocation.result }); } else { parts.push({ type: `tool-${invocation.toolName}`, toolCallId: invocation.toolCallId, state: invocation.state === "call" ? "input-available" : "input-streaming", input: invocation.args }); } } } const hasReasoningInParts = dbMsg.content.parts?.some((p) => p.type === "reasoning"); const hasFileInParts = dbMsg.content.parts?.some((p) => p.type === "file"); if (dbMsg.content.reasoning && !hasReasoningInParts) { parts.push({ type: "reasoning", text: dbMsg.content.reasoning }); } const attachmentUrls = /* @__PURE__ */ new Set(); if (dbMsg.content.experimental_attachments && !hasFileInParts) { for (const attachment of dbMsg.content.experimental_attachments) { attachmentUrls.add(attachment.url); parts.push({ type: "file", url: attachment.url, mediaType: attachment.contentType || "unknown" }); } } let hasNonToolReasoningParts = false; if (dbMsg.content.parts) { for (const part of dbMsg.content.parts) { if (part.type === "tool-invocation" && part.toolInvocation) { const inv = part.toolInvocation; if (inv.state === "result") { parts.push({ type: `tool-${inv.toolName}`, toolCallId: inv.toolCallId, input: getDisplayTransform2(part.providerMetadata, "input-available", inv.args, transformToolPayloads), output: getDisplayTransform2( part.providerMetadata, "output-available", getDisplayTransform2(part.providerMetadata, "error", inv.result, transformToolPayloads), transformToolPayloads ), state: "output-available", callProviderMetadata: mergeMastraCreatedAt(part.providerMetadata, part.createdAt), providerExecuted: part.providerExecuted }); } else if (inv.state === "output-error") { parts.push({ type: `tool-${inv.toolName}`, toolCallId: inv.toolCallId, input: getDisplayTransform2(part.providerMetadata, "input-available", inv.args, transformToolPayloads), errorText: getDisplayTransform2( part.providerMetadata, "error", inv.errorText || "", transformToolPayloads ), state: "output-error", callProviderMetadata: mergeMastraCreatedAt(part.providerMetadata, part.createdAt), providerExecuted: part.providerExecuted }); } else { parts.push({ type: `tool-${inv.toolName}`, toolCallId: inv.toolCallId, input: getDisplayTransform2(part.providerMetadata, "input-available", inv.args, transformToolPayloads), state: "input-available", callProviderMetadata: mergeMastraCreatedAt(part.providerMetadata, part.createdAt), providerExecuted: part.providerExecuted }); } continue; } if (part.type === "reasoning") { const text = part.reasoning || (part.details?.reduce((p, c) => { if (c.type === `text` && c.text) return p + c.text; return p; }, "") ?? ""); if (text || part.details?.length) { const v5UIPart = { type: "reasoning", text: text || "", state: "done" }; v5UIPart.providerMetadata = mergeMastraCreatedAt(part.providerMetadata, part.createdAt); parts.push(v5UIPart); } continue; } if (part.type === "tool-invocation" || part.type.startsWith("tool-")) { continue; } if (part.type === "file") { if (typeof part.data === "string" && attachmentUrls.has(part.data)) { continue; } const categorized = typeof part.data === "string" ? categorizeFileData(part.data, part.mimeType) : { type: "raw", mimeType: part.mimeType}; if (categorized.type === "url" && typeof part.data === "string") { const v5UIPart = { type: "file", url: part.data, mediaType: categorized.mimeType || "image/png" }; v5UIPart.providerMetadata = mergeMastraCreatedAt(part.providerMetadata, part.createdAt); parts.push(v5UIPart); } else { let filePartData; let extractedMimeType = part.mimeType; if (typeof part.data === "string") { const parsed = parseDataUri(part.data); if (parsed.isDataUri) { filePartData = parsed.base64Content; if (parsed.mimeType) { extractedMimeType = extractedMimeType || parsed.mimeType; } } else { filePartData = part.data; } } else { filePartData = part.data; } const finalMimeType = extractedMimeType || "image/png"; let dataUri; if (typeof filePartData === "string" && filePartData.startsWith("data:")) { dataUri = filePartData; } else { dataUri = createDataUri(filePartData, finalMimeType); } const v5UIPart = { type: "file", url: dataUri, mediaType: finalMimeType }; v5UIPart.providerMetadata = mergeMastraCreatedAt(part.providerMetadata, part.createdAt); parts.push(v5UIPart); } } else if (part.type === "source") { const v5UIPart = { type: "source-url", url: part.source.url, sourceId: part.source.id, title: part.source.title }; v5UIPart.providerMetadata = mergeMastraCreatedAt(part.providerMetadata, part.createdAt); parts.push(v5UIPart); } else if (part.type === "source-document") { continue; } else if (part.type === "text") { const v5UIPart = { type: "text", text: part.text }; v5UIPart.providerMetadata = mergeMastraCreatedAt(part.providerMetadata, part.createdAt); parts.push(v5UIPart); hasNonToolReasoningParts = true; } else if (part.type === "data-tool-call-suspended" || part.type === "data-tool-call-approval") { parts.push({ ...part, data: transformToolStateDataForDisplay( part.data, part.type === "data-tool-call-suspended" ? "suspend" : "approval", transformToolPayloads ) }); } else { parts.push(part); hasNonToolReasoningParts = true; } } } if (dbMsg.content.content && !hasNonToolReasoningParts) { parts.push({ type: "text", text: dbMsg.content.content }); } const existingToolStateDataPartIds = new Set( parts.filter( (part) => part.type === "data-tool-call-suspended" || part.type === "data-tool-call-approval" ).map((part) => { const data = part.data; return typeof data?.toolCallId === "string" ? data.toolCallId : void 0; }).filter((toolCallId) => typeof toolCallId === "string") ); const insertToolStateDataPart = (toolCallId, toolStateDataPart) => { const toolPartIndex = parts.findIndex( (part) => part.type.startsWith("tool-") && part.toolCallId === toolCallId ); if (toolPartIndex === -1) { parts.push(toolStateDataPart); return; } parts.splice(toolPartIndex + 1, 0, toolStateDataPart); }; const suspendedTools = metadata.suspendedTools; if (suspendedTools && typeof suspendedTools === "object") { for (const suspendedTool of Object.values(suspendedTools)) { if (!suspendedTool || typeof suspendedTool !== "object") { continue; } const toolCallId = "toolCallId" in suspendedTool ? suspendedTool.toolCallId : void 0; if (typeof toolCallId !== "string" || existingToolStateDataPartIds.has(toolCallId)) { continue; } insertToolStateDataPart(toolCallId, { type: "data-tool-call-suspended", data: transformToolStateDataForDisplay(suspendedTool, "suspend", transformToolPayloads) }); existingToolStateDataPartIds.add(toolCallId); } } const pendingToolApprovals = metadata.pendingToolApprovals; if (pendingToolApprovals && typeof pendingToolApprovals === "object") { for (const pendingToolApproval of Object.values(pendingToolApprovals)) { if (!pendingToolApproval || typeof pendingToolApproval !== "object") { continue; } const toolCallId = "toolCallId" in pendingToolApproval ? pendingToolApproval.toolCallId : void 0; if (typeof toolCallId !== "string" || existingToolStateDataPartIds.has(toolCallId)) { continue; } insertToolStateDataPart(toolCallId, { type: "data-tool-call-approval", data: transformToolStateDataForDisplay(pendingToolApproval, "approval", transformToolPayloads) }); existingToolStateDataPartIds.add(toolCallId); } } return { id: dbMsg.id, role: dbMsg.role === "signal" ? isUserMessageSignal ? "user" : "system" : dbMsg.role, metadata, parts }; } /** * Direct conversion from AIV5 UIMessage to MastraDBMessage */ static fromUIMessage(uiMsg) { const { parts, metadata: rawMetadata } = uiMsg; const metadata = rawMetadata || {}; const createdAtValue = metadata.createdAt; const createdAt = createdAtValue ? typeof createdAtValue === "string" ? new Date(createdAtValue) : createdAtValue instanceof Date ? createdAtValue : /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date(); const threadId = metadata.threadId; const resourceId = metadata.resourceId; const cleanMetadata = { ...metadata }; delete cleanMetadata.createdAt; delete cleanMetadata.threadId; delete cleanMetadata.resourceId; const toolInvocationParts = parts.filter((p) => chunkPWQW5NYR_cjs.isToolUIPart(p)); const reasoningParts = parts.filter((p) => p.type === "reasoning"); const fileParts = parts.filter((p) => p.type === "file"); const textParts = parts.filter((p) => p.type === "text"); let toolInvocations = void 0; if (toolInvocationParts.length > 0) { toolInvocations = toolInvocationParts.map((p) => { const toolName = getToolName(p); if (p.state === "output-available") { return { args: p.input, result: typeof p.output === "object" && p.output && "value" in p.output ? p.output.value : p.output, toolCallId: p.toolCallId, toolName, state: "result" }; } return { args: p.input, toolCallId: p.toolCallId, toolName, state: "call" }; }); } let reasoning = void 0; if (reasoningParts.length > 0) { reasoning = reasoningParts.map((p) => p.text).join("\n"); } let experimental_attachments = void 0; if (fileParts.length > 0) { experimental_attachments = fileParts.map((p) => ({ url: p.url || "", contentType: p.mediaType })); } let content = void 0; if (textParts.length > 0) { content = textParts.map((p) => p.text).join(""); } const v2Parts = parts.map((p) => { if (chunkPWQW5NYR_cjs.isToolUIPart(p)) { const toolName = getToolName(p); const callProviderMetadata = "callProviderMetadata" in p ? p.callProviderMetadata : void 0; if (p.state === "output-available") { return { type: "tool-invocation", toolInvocation: { toolCallId: p.toolCallId, toolName, args: p.input, result: typeof p.output === "object" && p.output && "value" in p.output ? p.output.value : p.output, state: "result" }, providerMetadata: callProviderMetadata, createdAt: getMastraCreatedAt(callProviderMetadata) }; } return { type: "tool-invocation", toolInvocation: { toolCallId: p.toolCallId, toolName, args: p.input, state: "call" }, providerMetadata: callProviderMetadata, createdAt: getMastraCreatedAt(callProviderMetadata) }; } if (p.type === "reasoning") { return { type: "reasoning", reasoning: "", details: [ { type: "text", text: p.text } ], providerMetadata: p.providerMetadata, createdAt: getMastraCreatedAt(p.providerMetadata) }; } if (p.type === "file") { return { type: "file", mimeType: p.mediaType, data: p.url || "", providerMetadata: p.providerMetadata, createdAt: getMastraCreatedAt(p.providerMetadata), ...p.filename ? { filename: p.filename } : {} }; } if (p.type === "source-url") { return { type: "source", source: { url: p.url, sourceType: "url", id: p.url, providerMetadata: p.providerMetadata }, providerMetadata: p.providerMetadata, createdAt: getMastraCreatedAt(p.providerMetadata) }; } if (p.type === "text") { return { type: "text", text: p.text, providerMetadata: p.providerMetadata, createdAt: getMastraCreatedAt(p.providerMetadata) }; } if (p.type === "step-start") { return p; } if (typeof p.type === "string" && p.type.startsWith("data-")) { return { type: p.type, data: "data" in p ? p.data : void 0 }; } return null; }).filter((p) => p !== null); const filteredV2Parts = filterEmptyTextParts2(v2Parts); return { id: uiMsg.id, role: uiMsg.role, createdAt, threadId, resourceId, content: { format: 2, parts: filteredV2Parts, toolInvocations, reasoning, experimental_attachments, content, metadata: Object.keys(cleanMetadata).length > 0 ? cleanMetadata : void 0 } }; } /** * Convert image or file to data URI or URL for V2 file part */ static getDataStringFromAIV5DataPart(part) { let mimeType; let data; if ("data" in part) { mimeType = part.mediaType || "application/octet-stream"; data = part.data; } else if ("image" in part) { mimeType = part.mediaType || "image/jpeg"; data = part.image; } else if ("url" in part && typeof part.url === "string") { return part.url; } else { throw new chunkXSOONORA_cjs.MastraError({ id: "MASTRA_AIV5_DATA_PART_INVALID", domain: chunkXSOONORA_cjs.ErrorDomain.AGENT, category: chunkXSOONORA_cjs.ErrorCategory.USER, text: "Invalid AIV5 data part in getDataStringFromAIV5DataPart", details: { part } }); } if (data instanceof URL) { return data.toString(); } else { if (data instanceof Buffer) { const base64 = data.toString("base64"); return `data:${mimeType};base64,${base64}`; } else if (typeof data === "string") { return data.startsWith("data:") || data.startsWith("http") ? data : `data:${mimeType};base64,${data}`; } else if (data instanceof Uint8Array) { const base64 = Buffer.from(data).toString("base64"); return `data:${mimeType};base64,${base64}`; } else if (data instanceof ArrayBuffer) { const base64 = Buffer.from(data).toString("base64"); return `data:${mimeType};base64,${base64}`; } else { return ""; } } } /** * Direct conversion from AIV5 ModelMessage to MastraDBMessage */ static fromModelMessage(modelMsg, _messageSource) { const content = Array.isArray(modelMsg.content) ? modelMsg.content : [{ type: "text", text: modelMsg.content }]; const mastraDBParts = []; const toolInvocations = []; const reasoningParts = []; const experimental_attachments = []; for (const part of content) { if (part.type === "text") { const textPart = { type: "text", text: part.text }; if (part.providerOptions) { textPart.providerMetadata = part.providerOptions; textPart.createdAt = getMastraCreatedAt(part.providerOptions); } mastraDBParts.push(textPart); } else if (part.type === "tool-call") { const toolCallPart = part; const toolInvocationPart = { type: "tool-invocation", toolInvocation: { toolCallId: toolCallPart.toolCallId, toolName: sanitizeToolName(toolCallPart.toolName), args: toolCallPart.input, state: "call" } }; if (part.providerOptions) { toolInvocationPart.providerMetadata = part.providerOptions; toolInvocationPart.createdAt = getMastraCreatedAt(part.providerOptions); } mastraDBParts.push(toolInvocationPart); toolInvocations.push({ toolCallId: toolCallPart.toolCallId, toolName: sanitizeToolName(toolCallPart.toolName), args: toolCallPart.input, state: "call" }); } else if (part.type === "tool-result") { const toolResultPart = part; const matchingCall = toolInvocations.find((inv) => inv.toolCallId === toolResultPart.toolCallId); const matchingV2Part = mastraDBParts.find( (p) => p.type === "tool-invocation" && "toolInvocation" in p && p.toolInvocation.toolCallId === toolResultPart.toolCallId ); const updateMatchingCallInvocationResult = (toolResultPart2, matchingCall2) => { matchingCall2.state = "result"; matchingCall2.result = typeof toolResultPart2.output === "object" && toolResultPart2.output && "value" in toolResultPart2.output ? toolResultPart2.output.value : toolResultPart2.output; }; if (matchingCall) { updateMatchingCallInvocationResult(toolResultPart, matchingCall); } else { const call = { state: "call", toolCallId: toolResultPart.toolCallId, toolName: sanitizeToolName(toolResultPart.toolName), args: {} }; updateMatchingCallInvocationResult(toolResultPart, call); toolInvocations.push(call); } if (matchingV2Part && matchingV2Part.type === "tool-invocation") { updateMatchingCallInvocationResult(toolResultPart, matchingV2Part.toolInvocation); if (toolResultPart.providerOptions) { matchingV2Part.providerMetadata = toolResultPart.providerOptions; matchingV2Part.createdAt = getMastraCreatedAt(toolResultPart.providerOptions) ?? matchingV2Part.createdAt; } } else { const toolInvocationPart = { type: "tool-invocation", toolInvocation: { toolCallId: toolResultPart.toolCallId, toolName: sanitizeToolName(toolResultPart.toolName), args: {}, state: "call" } }; updateMatchingCallInvocationResult(toolResultPart, toolInvocationPart.toolInvocation); if (toolResultPart.providerOptions) { toolInvocationPart.providerMetadata = toolResultPart.providerOptions; toolInvocationPart.createdAt = getMastraCreatedAt(toolResultPart.providerOptions); } mastraDBParts.push(toolInvocationPart); } } else if (part.type === "reasoning") { const v2ReasoningPart = { type: "reasoning", reasoning: "", details: [{ type: "text", text: part.text }] }; if (part.providerOptions) { v2ReasoningPart.providerMetadata = part.providerOptions; v2ReasoningPart.createdAt = getMastraCreatedAt(part.providerOptions); } mastraDBParts.push(v2ReasoningPart); reasoningParts.push(part.text); } else if (part.type === "image") { const imagePart = part; const mimeType = imagePart.mediaType || "image/jpeg"; const imageData = this.getDataStringFromAIV5DataPart(imagePart); const imageFilePart = { type: "file", data: imageData, mimeType }; if (part.providerOptions) { imageFilePart.providerMetadata = part.providerOptions; imageFilePart.createdAt = getMastraCreatedAt(part.providerOptions); } mastraDBParts.push(imageFilePart); experimental_attachments.push({ url: imageData, contentType: mimeType }); } else if (part.type === "file") { const filePart = part; const mimeType = filePart.mediaType || "application/octet-stream"; const fileData = this.getDataStringFromAIV5DataPart(filePart); const v2FilePart = { type: "file", data: fileData, mimeType }; if (part.providerOptions) { v2FilePart.providerMetadata = part.providerOptions; v2FilePart.createdAt = getMastraCreatedAt(part.providerOptions); } if (filePart.filename) { v2FilePart.filename = filePart.filename; } mastraDBParts.push(v2FilePart); experimental_attachments.push({ url: fileData, contentType: mimeType }); } } const filteredMastraDBParts = filterEmptyTextParts2(mastraDBParts); const contentString = filteredMastraDBParts.filter((p) => p.type === "text").map((p) => p.text).join("\n"); const metadata = "metadata" in modelMsg && modelMsg.metadata !== null && modelMsg.metadata !== void 0 ? modelMsg.metadata : {}; const id = `id` in modelMsg && typeof modelMsg.id === `string` ? modelMsg.id : `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const message = { id, role: modelMsg.role === "tool" ? "assistant" : modelMsg.role, createdAt: /* @__PURE__ */ new Date(), content: { format: 2, parts: filteredMastraDBParts, toolInvocations: toolInvocations.length > 0 ? toolInvocations : void 0, reasoning: reasoningParts.length > 0 ? reasoningParts.join("\n") : void 0, experimental_attachments: experimental_attachments.length > 0 ? experimental_attachments : void 0, content: contentString || void 0, metadata: Object.keys(metadata).length > 0 ? metadata : void 0 } }; if (modelMsg.providerOptions) { message.content.providerMetadata = modelMsg.providerOptions; } return message; } }; // src/agent/message-list/adapters/AIV6Adapter.ts function withOptionalFields(target, fields) { for (const [key, value] of Object.entries(fields)) { if (value !== void 0) { target[key] = value; } } return target; } function getDisplayTransform3(providerMetadata, phase, fallback) { const transform = getTransformedToolPayload(providerMetadata, "display", phase); return hasTransformedToolPayload(transform) ? transform.transformed : fallback; } function getToolNameFromType(type) { return type.startsWith("tool-") ? sanitizeToolName(type.slice("tool-".length)) : sanitizeToolName(type); } function normalizeToolArgs(input) { return typeof input === "object" && input !== null && !Array.isArray(input) ? input : {}; } function normalizeToolResult(output) { return typeof output === "object" && output && "value" in output ? output.value : output; } function isV6OnlyToolState(state) { return state === "approval-requested" || state === "approval-responded" || state === "output-denied"; } function toMastraApproval(approval) { if (!approval) return void 0; return { id: approval.id, approved: "approved" in approval ? approval.approved : void 0, reason: "reason" in approval ? approval.reason : void 0 }; } function toMastraProviderMetadata(providerMetadata) { return providerMetadata; } function getToolNameFromUIPart(part) { return part.type === "dynamic-tool" ? sanitizeToolName(part.toolName) : getToolNameFromType(part.type); } function createToolInvocationPartFromUIPart(part) { const base = { toolCallId: part.toolCallId, toolName: getToolNameFromUIPart(part), args: normalizeToolArgs(part.input), approval: "approval" in part ? toMastraApproval(part.approval) : void 0, providerMetadata: "callProviderMetadata" in part ? toMastraProviderMetadata(part.callProviderMetadata) : void 0, providerExecuted: part.providerExecuted, title: part.title, preliminary: "preliminary" in part ? part.preliminary : void 0 }; switch (part.state) { case "input-streaming": return createToolInvocationPart({ ...base, state: "partial-call" }); case "input-available": return createToolInvocationPart({ ...base, state: "call" }); case "output-available": return createToolInvocationPart({ ...base, state: "result", result: normalizeToolResult(part.output) }); case "output-error": return createToolInvocationPart({ ...base, state: "output-error", errorText: part.errorText, rawInput: "rawInput" in part ? part.rawInput : void 0 }); case "approval-requested": case "approval-responded": case "output-denied": return createToolInvocationPart({ ...base, state: part.state }); } } function normalizeV6PartForV5Bridge(part) { if (part.type === "dynamic-tool" && !isV6OnlyToolState(part.state)) { return { ...part, type: `tool-${sanitizeToolName(part.toolName)}` }; } return part; } function createToolInvocationPart({ toolCallId, toolName, args, state, approval, result, errorText, rawInput, providerMetadata, providerExecuted, title, preliminary }) { return withOptionalFields( { type: "tool-invocation", toolInvocation: withOptionalFields( { toolCallId, toolName, args, state }, { approval, result, errorText, rawInput } ) }, { providerMetadata, providerExecuted, title, preliminary } ); } function findToolInvocationPart(parts, toolCallId) { for (const part of parts) { if (part.type === "tool-invocation" && part.toolInvocation.toolCallId === toolCallId) { return part; } } return void 0; } function findApprovalRequest(dbMessages, approvalId) { if (!dbMessages) return void 0; for (const message of [...dbMessages].reverse()) { for (const part of [...message.content.parts || []].reverse()) { if (part.type === "tool-invocation" && part.toolInvocation.approval?.id === approvalId && part.toolInvocation.state === "approval-requested") { return part; } } } return void 0; } function createLegacyToolInvocations(parts) { const toolInvocations = []; for (const part of parts) { if (part.type !== "tool-invocation") continue; const invocation = part.toolInvocation; if (invocation.state === "result") { toolInvocations.push({ args: invocation.args, result: invocation.result, toolCallId: invocation.toolCallId, toolName: invocation.toolName, state: "result" }); continue; } if (invocation.state === "call" || invocation.state === "partial-call") { toolInvocations.push({ args: invocation.args, toolCallId: invocation.toolCallId, toolName: invocation.toolName, state: invocation.state }); } } return toolInvocations.length > 0 ? toolInvocations : void 0; } var AIV6Adapter = class _AIV6Adapter { static toUIMessage(dbMsg) { const v5Message = AIV5Adapter.toUIMessage(dbMsg); const metadata = v5Message.metadata || {}; const parts = []; if (dbMsg.role === "signal" && v5Message.role !== "user") { return { id: dbMsg.id, role: "system", metadata: Object.keys(metadata).length > 0 ? metadata : void 0, parts: v5Message.parts.map((part) => _AIV6Adapter.toUIPartFromV5(part)) }; } const dbParts = dbMsg.content.parts || []; const hasToolInvocationParts = dbParts.some((part) => part.type === "tool-invocation"); const hasReasoningParts = dbParts.some((part) => part.type === "reasoning"); const hasFileParts = dbParts.some((part) => part.type === "file"); const hasTextParts = dbParts.some((part) => part.type === "text"); for (const part of dbParts) { parts.push(_AIV6Adapter.toUIPart(part)); } if (!hasToolInvocationParts || !hasReasoningParts || !hasFileParts || !hasTextParts) { for (const part of v5Message.parts) { if (chunkPWQW5NYR_cjs.isToolUIPart(part)) { if (!hasToolInvocationParts) { parts.push(_AIV6Adapter.toUIPartFromV5(part)); } continue; } if (part.type === "reasoning") { if (!hasReasoningParts) { parts.push(_AIV6Adapter.toUIPartFromV5(part)); } continue; } if (part.type === "file") { if (!hasFileParts) { parts.push(_AIV6Adapter.toUIPartFromV5(part)); } continue; } if (part.type === "text" && !hasTextParts) { parts.push(_AIV6Adapter.toUIPartFromV5(part)); } } } return { id: dbMsg.id, role: dbMsg.role === "signal" ? v5Message.role : dbMsg.role, metadata: Object.keys(metadata).length > 0 ? metadata : void 0, parts }; } static fromUIMessage(uiMsg) { const compatibleParts = uiMsg.parts.filter((part) => { if (part.type === "source-document") return false; if (chunk4SCYX53T_cjs.isToolUIPart(part)) return false; return true; }); const baseDb = AIV5Adapter.fromUIMessage({ ...uiMsg, parts: compatibleParts.map((part) => normalizeV6PartForV5Bridge(part)) }); const baseParts = baseDb.content.parts || []; const parts = []; let basePartIndex = 0; for (const part of uiMsg.parts) { if (part.type === "source-document") { parts.push( withOptionalFields( { type: "source-document", sourceId: part.sourceId, mediaType: part.mediaType, title: part.title }, { filename: part.filename, providerMetadata: toMastraProviderMetadata(part.providerMetadata) } ) ); continue; } if (!chunk4SCYX53T_cjs.isToolUIPart(part)) { const basePart = baseParts[basePartIndex++]; if (basePart) { parts.push(basePart); } continue; } parts.push(createToolInvocationPartFromUIPart(part)); } return { ...baseDb, content: { ...baseDb.content, parts, toolInvocations: createLegacyToolInvocations(parts) || baseDb.content.toolInvocations } }; } static fromModelMessage(modelMsg, _messageSource, context = {}) { const content = Array.isArray(modelMsg.content) ? modelMsg.content : [{ type: "text", text: modelMsg.content }]; const compatibleContent = content.filter( (part) => part.type !== "tool-approval-request" && part.type !== "tool-approval-response" ); const baseDb = AIV5Adapter.fromModelMessage( { ...modelMsg, content: compatibleContent }, _messageSource ); const parts = [...baseDb.content.parts]; if (modelMsg.role === "assistant") { const toolCalls = /* @__PURE__ */ new Map(); for (const part of content) { if (part.type === "tool-call") { toolCalls.set(part.toolCallId, { toolName: sanitizeToolName(part.toolName), args: normalizeToolArgs(part.input) }); continue; } if (part.type !== "tool-approval-request") { continue; } const call = toolCalls.get(part.toolCallId); const existingPart = findToolInvocationPart(parts, part.toolCallId); if (existingPart) { existingPart.toolInvocation.state = "approval-requested"; existingPart.toolInvocation.approval = { id: part.approvalId }; continue; } parts.push( createToolInvocationPart({ toolCallId: part.toolCallId, toolName: call?.toolName || "unknown", args: call?.args || {}, state: "approval-requested", approval: { id: part.approvalId } }) ); } } else if (modelMsg.role === "tool") { for (const part of content) { if (part.type !== "tool-approval-response") { continue; } const request = findApprovalRequest(context.dbMessages, part.approvalId); if (!request) { continue; } parts.push( createToolInvocationPart({ toolCallId: request.toolInvocation.toolCallId, toolName: request.toolInvocation.toolName, args: request.toolInvocation.args, state: "approval-responded", approval: { id: part.approvalId, approved: part.approved, reason: part.reason }, providerMetadata: request.providerMetadata, providerExecuted: request.providerExecuted, title: request.title }) ); } } return { ...baseDb, content: { ...baseDb.content, parts } }; } static toUIPart(part) { if (part.type === "tool-invocation") { const base = withOptionalFields( { type: `tool-${sanitizeToolName(part.toolInvocation.toolName)}`, toolCallId: part.toolInvocation.toolCallId, providerExecuted: part.providerExecuted }, { callProviderMetadata: part.providerMetadata, title: part.title } ); switch (part.toolInvocation.state) { case "partial-call": return { ...base, state: "input-streaming", input: getDisplayTransform3(part.providerMetadata, "input-available", part.toolInvocation.args) }; case "call": return { ...base, state: "input-available", input: getDisplayTransform3(part.providerMetadata, "input-available", part.toolInvocation.args) }; case "approval-requested": return { ...base, state: "approval-requested", input: getDisplayTransform3(part.providerMetadata, "input-available", part.toolInvocation.args), approval: { id: part.toolInvocation.approval?.id || part.toolInvocation.toolCallId } }; case "approval-responded": return { ...base, state: "approval-responded", input: getDisplayTransform3(part.providerMetadata, "input-available", part.toolInvocation.args), approval: { id: part.toolInvocation.approval?.id || part.toolInvocation.toolCallId, approved: part.toolInvocation.approval?.approved ?? false, reason: part.toolInvocation.approval?.reason } }; case "output-error": return withOptionalFields( { ...base, state: "output-error", input: getDisplayTransform3(part.providerMetadata, "input-available", part.toolInvocation.args), errorText: getDisplayTransform3( part.providerMetadata, "error", part.toolInvocation.errorText || "" ) }, { rawInput: part.toolInvocation.rawInput, approval: part.toolInvocation.approval?.approved === true ? { id: part.toolInvocation.approval.id, approved: true, reason: part.toolInvocation.approval.reason } : void 0 } ); case "output-denied": return { ...base, state: "output-denied", input: getDisplayTransform3(part.providerMetadata, "input-available", part.toolInvocation.args), approval: { id: part.toolInvocation.approval?.id || part.toolInvocation.toolCallId, approved: false, reason: part.toolInvocation.approval?.reason } }; case "result": return withOptionalFields( { ...base, state: "output-available", input: getDisplayTransform3(part.providerMetadata, "input-available", part.toolInvocation.args), output: getDisplayTransform3( part.providerMetadata, "output-available", getDisplayTransform3(part.providerMetadata, "error", part.toolInvocation.result) ) }, { preliminary: part.preliminary, approval: part.toolInvocation.approval?.approved === true ? { id: part.toolInvocation.approval.id, approved: true, reason: part.toolInvocation.approval.reason } : void 0 } ); default: throw new Error(`Unhandled toolInvocation.state: ${String(part.toolInvocation.state)}`); } } if (part.type === "source-document") { return withOptionalFields( { type: "source-document", sourceId: part.sourceId, mediaType: part.mediaType, title: part.title }, { filename: part.filename, providerMetadata: part.providerMetadata } ); } return _AIV6Adapter.toUIPartFromV5( AIV5Adapter.toUIMessage({ id: "tmp", role: "assistant", createdAt: /* @__PURE__ */ new Date(), content: { format: 2, parts: [part] } }).parts[0] ); } static toUIPartFromV5(part) { if (chunkPWQW5NYR_cjs.isToolUIPart(part)) { const base = { type: part.type, toolCallId: part.toolCallId, providerExecuted: part.providerExecuted }; switch (part.state) { case "input-streaming": return withOptionalFields( { ...base, state: "input-streaming", input: part.input }, { callProviderMetadata: "callProviderMetadata" in part ? part.callProviderMetadata : void 0, title: "title" in part ? part.title : void 0 } ); case "input-available": return withOptionalFields( { ...base, state: "input-available", input: part.input }, { callProviderMetadata: "callProviderMetadata" in part ? part.callProviderMetadata : void 0, title: "title" in part ? part.title : void 0 } ); case "output-available": return withOptionalFields( { ...base, state: "output-available", input: part.input, output: part.output }, { callProviderMetadata: "callProviderMetadata" in part ? part.callProviderMetadata : void 0, preliminary: "preliminary" in part ? part.preliminary : void 0, title: "title" in part ? part.title : void 0 } ); case "output-error": return withOptionalFields( { ...base, state: "output-error", input: part.input, errorText: part.errorText }, { rawInput: "rawInput" in part ? part.rawInput : void 0, callProviderMetadata: "callProviderMetadata" in part ? part.callProviderMetadata : void 0, title: "title" in part ? part.title : void 0 } ); } } switch (part.type) { case "text": return withOptionalFields( { type: "text", text: part.text }, { providerMetadata: part.providerMetadata } ); case "reasoning": return withOptionalFields( { type: "reasoning", text: part.text, state: part.state }, { providerMetadata: part.providerMetadata } ); case "file": return withOptionalFields( { type: "file", url: part.url, mediaType: part.mediaType }, { filename: "filename" in part ? part.filename : void 0, providerMetadata: part.providerMetadata } ); case "source-url": return withOptionalFields( { type: "source-url", sourceId: part.sourceId, url: part.url }, { title: part.title, providerMetadata: part.providerMetadata } ); case "step-start": return { type: "step-start" }; default: if (typeof part.type === "string" && part.type.startsWith("data-")) { return { type: part.type, data: "data" in part ? part.data : void 0 }; } return part; } } }; // src/agent/message-list/cache/stable-stringify.ts function stableStringify(value) { return JSON.stringify(value, (_key, val) => { if (val && typeof val === "object" && !Array.isArray(val)) { const sorted = {}; for (const k of Object.keys(val).sort()) { sorted[k] = val[k]; } return sorted; } return val; }); } // src/agent/message-list/cache/CacheKeyGenerator.ts function appendResponseProviderItemKeys(cacheKey, ...providerSources) { const itemKeys = new Set( providerSources.flatMap((source) => getResponseProviderItemKeys(source)) ); for (const itemKey of itemKeys) { cacheKey += `|${itemKey}`; } return cacheKey; } var CacheKeyGenerator = class _CacheKeyGenerator { /** * Generate cache key from AIV4 UIMessage parts */ static fromAIV4Parts(parts) { let key = ""; for (const part of parts) { key += part.type; key += _CacheKeyGenerator.fromAIV4Part(part); } return key; } /** * Generate cache key from a single AIV4 UIMessage part */ static fromAIV4Part(part) { let cacheKey = ""; if (part.type === "text") { cacheKey += part.text; cacheKey = appendResponseProviderItemKeys(cacheKey, part.providerMetadata); } if (part.type === "tool-invocation") { if (!part.toolInvocation) return cacheKey; cacheKey += part.toolInvocation.toolCallId; cacheKey += part.toolInvocation.state; } if (part.type === "reasoning") { cacheKey += part.reasoning; cacheKey += part.details.reduce((prev, current) => { if (current.type === "text") { return prev + current.text.length + (current.signature?.length || 0); } return prev; }, 0); const partAny = part; if (partAny && Object.hasOwn(partAny, "providerMetadata")) { cacheKey = appendResponseProviderItemKeys(cacheKey, partAny.providerMetadata); } } if (part.type === "file") { cacheKey += part.data; cacheKey += part.mimeType; } return cacheKey; } /** * Generate cache key from MastraDB message parts */ static fromDBParts(parts) { let key = ""; for (const part of parts) { key += part.type; if (part.type.startsWith("data-")) { const data = part.data; key += stableStringify(data); } else { key += _CacheKeyGenerator.fromAIV4Part(part); } } return key; } /** * Generate cache key from AIV4 CoreMessage content */ static fromAIV4CoreMessageContent(content) { if (typeof content === "string") return content; let key = ""; for (const part of content) { key += part.type; if (part.type === "text") { key += part.text.length; const partAny = part; key = appendResponseProviderItemKeys(key, partAny.providerMetadata, partAny.providerOptions); } if (part.type === "reasoning") { key += part.text.length; const partAny = part; key = appendResponseProviderItemKeys(key, partAny.providerMetadata, partAny.providerOptions); } if (part.type === "tool-call") { key += part.toolCallId; key += part.toolName; } if (part.type === "tool-result") { key += part.toolCallId; key += part.toolName; } if (part.type === "file") { key += part.filename; key += part.mimeType; } if (part.type === "image") { key += getImageCacheKey(part.image); key += part.mimeType; } if (part.type === "redacted-reasoning") { key += part.data.length; } } return key; } /** * Generate cache key from AIV5 UIMessage parts */ static fromAIV5Parts(parts) { let key = ""; for (const part of parts) { key += part.type; if (part.type === "text") { key += part.text; key = appendResponseProviderItemKeys(key, part.providerMetadata); } if (chunkPWQW5NYR_cjs.isToolUIPart(part) || part.type === "dynamic-tool") { key += part.toolCallId; key += part.state; } if (part.type === "reasoning") { key += part.text; key = appendResponseProviderItemKeys(key, part.providerMetadata); } if (part.type === "file") { key += part.url.length; key += part.mediaType; key += part.filename || ""; } } return key; } /** * Generate cache key from AIV5 ModelMessage content */ static fromAIV5ModelMessageContent(content) { if (typeof content === "string") return content; let key = ""; for (const part of content) { key += part.type; if (part.type === "text") { key += part.text.length; key = appendResponseProviderItemKeys(key, part.providerOptions); } if (part.type === "reasoning") { key += part.text.length; key = appendResponseProviderItemKeys(key, part.providerOptions); } if (part.type === "tool-call") { key += part.toolCallId; key += part.toolName; } if (part.type === "tool-result") { key += part.toolCallId; key += part.toolName; } if (part.type === "file") { key += part.filename; key += part.mediaType; } if (part.type === "image") { key += getImageCacheKey(part.image); key += part.mediaType; } } return key; } }; // src/agent/message-list/conversion/to-prompt.ts function aiV4CoreMessageToV1PromptMessage(coreMessage) { if (coreMessage.role === `system`) { return coreMessage; } if (typeof coreMessage.content === `string` && (coreMessage.role === `assistant` || coreMessage.role === `user`)) { return { ...coreMessage, content: [{ type: "text", text: coreMessage.content }] }; } if (typeof coreMessage.content === `string`) { throw new Error( `Saw text content for input CoreMessage, but the role is ${coreMessage.role}. This is only allowed for "system", "assistant", and "user" roles.` ); } const roleContent = { user: [], assistant: [], tool: [] }; const role = coreMessage.role; for (const part of coreMessage.content) { const incompatibleMessage = `Saw incompatible message content part type ${part.type} for message role ${role}`; switch (part.type) { case "text": { if (role === `tool`) { throw new Error(incompatibleMessage); } roleContent[role].push(part); break; } case "redacted-reasoning": case "reasoning": { if (role !== `assistant`) { throw new Error(incompatibleMessage); } roleContent[role].push(part); break; } case "tool-call": { if (role === `tool` || role === `user`) { throw new Error(incompatibleMessage); } roleContent[role].push({ ...part, toolName: sanitizeToolName(part.toolName) }); break; } case "tool-result": { if (role === `assistant` || role === `user`) { throw new Error(incompatibleMessage); } roleContent[role].push({ ...part, toolName: sanitizeToolName(part.toolName) }); break; } case "image": { if (role === `tool` || role === `assistant`) { throw new Error(incompatibleMessage); } let processedImage; if (part.image instanceof URL || part.image instanceof Uint8Array) { processedImage = part.image; } else if (Buffer.isBuffer(part.image) || part.image instanceof ArrayBuffer) { processedImage = new Uint8Array(part.image); } else { const categorized = categorizeFileData(part.image, part.mimeType); if (categorized.type === "raw") { processedImage = new Uint8Array(Buffer.from(part.image, "base64")); } else { processedImage = new URL(part.image); } } roleContent[role].push({ ...part, image: processedImage }); break; } case "file": { if (role === `tool`) { throw new Error(incompatibleMessage); } roleContent[role].push({ ...part, data: part.data instanceof URL ? part.data : typeof part.data === "string" ? part.data : convertDataContentToBase64String(part.data) }); break; } } } if (role === `tool`) { return { ...coreMessage, content: roleContent[role] }; } if (role === `user`) { return { ...coreMessage, content: roleContent[role] }; } if (role === `assistant`) { return { ...coreMessage, content: roleContent[role] }; } throw new Error( `Encountered unknown role ${role} when converting V4 CoreMessage -> V4 LanguageModelV1Prompt, input message: ${JSON.stringify(coreMessage, null, 2)}` ); } function aiV5ModelMessageToV2PromptMessage(modelMessage) { if (modelMessage.role === `system`) { return modelMessage; } if (typeof modelMessage.content === `string` && (modelMessage.role === `assistant` || modelMessage.role === `user`)) { return { role: modelMessage.role, content: [{ type: "text", text: modelMessage.content }], providerOptions: modelMessage.providerOptions }; } if (typeof modelMessage.content === `string`) { throw new Error( `Saw text content for input ModelMessage, but the role is ${modelMessage.role}. This is only allowed for "system", "assistant", and "user" roles.` ); } const roleContent = { user: [], assistant: [], tool: [] }; const role = modelMessage.role; for (const part of modelMessage.content) { const incompatibleMessage = `Saw incompatible message content part type ${part.type} for message role ${role}`; switch (part.type) { case "text": { if (role === `tool`) { throw new Error(incompatibleMessage); } roleContent[role].push(part); break; } case "reasoning": { if (role === `tool` || role === `user`) { throw new Error(incompatibleMessage); } roleContent[role].push(part); break; } case "tool-call": { if (role !== `assistant`) { throw new Error(incompatibleMessage); } roleContent[role].push({ ...part, toolName: sanitizeToolName(part.toolName) }); break; } case "tool-result": { if (role === `user`) { throw new Error(incompatibleMessage); } roleContent[role].push({ ...part, toolName: sanitizeToolName(part.toolName) }); break; } case "file": { if (role === `tool`) { throw new Error(incompatibleMessage); } roleContent[role].push({ ...part, data: part.data instanceof ArrayBuffer ? new Uint8Array(part.data) : part.data }); break; } case "image": { if (role === `tool`) { throw new Error(incompatibleMessage); } roleContent[role].push({ ...part, mediaType: part.mediaType || "image/unknown", type: "file", data: part.image instanceof ArrayBuffer ? new Uint8Array(part.image) : part.image }); break; } } } if (role === `tool`) { return { ...modelMessage, content: roleContent[role] }; } if (role === `user`) { return { ...modelMessage, content: roleContent[role] }; } if (role === `assistant`) { return { ...modelMessage, content: roleContent[role] }; } throw new Error( `Encountered unknown role ${role} when converting V5 ModelMessage -> V5 LanguageModelV2Message, input message: ${JSON.stringify(modelMessage, null, 2)}` ); } // src/agent/message-list/conversion/utils.ts function coreContentToString(content) { if (typeof content === `string`) return content; return content.reduce((p, c) => { if (c.type === `text`) { p += c.text; } return p; }, ""); } function messagesAreEqual(one, two) { const oneUIV4 = TypeDetector.isAIV4UIMessage(one) && one; const twoUIV4 = TypeDetector.isAIV4UIMessage(two) && two; if (oneUIV4 && !twoUIV4) return false; if (oneUIV4 && twoUIV4) { return CacheKeyGenerator.fromAIV4Parts(one.parts) === CacheKeyGenerator.fromAIV4Parts(two.parts); } const oneCMV4 = TypeDetector.isAIV4CoreMessage(one) && one; const twoCMV4 = TypeDetector.isAIV4CoreMessage(two) && two; if (oneCMV4 && !twoCMV4) return false; if (oneCMV4 && twoCMV4) { return CacheKeyGenerator.fromAIV4CoreMessageContent(oneCMV4.content) === CacheKeyGenerator.fromAIV4CoreMessageContent(twoCMV4.content); } const oneMM1 = TypeDetector.isMastraMessageV1(one) && one; const twoMM1 = TypeDetector.isMastraMessageV1(two) && two; if (oneMM1 && !twoMM1) return false; if (oneMM1 && twoMM1) { return oneMM1.id === twoMM1.id && CacheKeyGenerator.fromAIV4CoreMessageContent(oneMM1.content) === CacheKeyGenerator.fromAIV4CoreMessageContent(twoMM1.content); } const oneMM2 = TypeDetector.isMastraDBMessage(one) && one; const twoMM2 = TypeDetector.isMastraDBMessage(two) && two; if (oneMM2 && !twoMM2) return false; if (oneMM2 && twoMM2) { return oneMM2.id === twoMM2.id && CacheKeyGenerator.fromDBParts(oneMM2.content.parts) === CacheKeyGenerator.fromDBParts(twoMM2.content.parts); } const oneUIV5 = TypeDetector.isAIV5UIMessage(one) && one; const twoUIV5 = TypeDetector.isAIV5UIMessage(two) && two; if (oneUIV5 && !twoUIV5) return false; if (oneUIV5 && twoUIV5) { return CacheKeyGenerator.fromAIV5Parts(one.parts) === CacheKeyGenerator.fromAIV5Parts(two.parts); } const oneCMV5 = TypeDetector.isAIV5CoreMessage(one) && one; const twoCMV5 = TypeDetector.isAIV5CoreMessage(two) && two; if (oneCMV5 && !twoCMV5) return false; if (oneCMV5 && twoCMV5) { return CacheKeyGenerator.fromAIV5ModelMessageContent(oneCMV5.content) === CacheKeyGenerator.fromAIV5ModelMessageContent(twoCMV5.content); } return true; } // src/agent/message-list/utils/stamp-part.ts function stampPart(part) { if (part.createdAt == null) { part.createdAt = Date.now(); } return part; } function stampMessageParts(message, source) { if (source === "memory" || !Array.isArray(message.content.parts)) { return message; } message.content.parts = message.content.parts.map((part) => stampPart(part)); return message; } // src/agent/message-list/conversion/input-converter.ts function inputToMastraDBMessage(message, messageSource, context) { if (messageSource !== `memory` && `threadId` in message && message.threadId && context.memoryInfo && message.threadId !== context.memoryInfo.threadId) { throw new Error( `Received input message with wrong threadId. Input ${message.threadId}, expected ${context.memoryInfo.threadId}` ); } if (`resourceId` in message && message.resourceId && context.memoryInfo?.resourceId && message.resourceId !== context.memoryInfo.resourceId) { throw new Error( `Received input message with wrong resourceId. Input ${message.resourceId}, expected ${context.memoryInfo.resourceId}` ); } if (TypeDetector.isMastraMessageV1(message)) { return stampMessageParts(mastraMessageV1ToMastraDBMessage(message, messageSource, context), messageSource); } if (TypeDetector.isMastraDBMessage(message)) { return stampMessageParts(hydrateMastraDBMessageFields(message, context, messageSource), messageSource); } if (TypeDetector.isAIV4CoreMessage(message)) { return stampMessageParts(AIV4Adapter.fromCoreMessage(message, context, messageSource), messageSource); } if (TypeDetector.isAIV4UIMessage(message)) { return stampMessageParts( AIV4Adapter.fromUIMessage(message, context, messageSource), messageSource ); } const hasOriginalId = "id" in message && typeof message.id === "string"; const id = hasOriginalId ? message.id : context.newMessageId(); if (TypeDetector.isAIV6CoreMessage(message)) { const dbMsg = AIV6Adapter.fromModelMessage(message, messageSource, context); const rawCreatedAt = "metadata" in message && message.metadata && typeof message.metadata === "object" && "createdAt" in message.metadata ? message.metadata.createdAt : void 0; return { ...dbMsg, id, createdAt: context.generateCreatedAt(messageSource, rawCreatedAt), threadId: context.memoryInfo?.threadId, resourceId: context.memoryInfo?.resourceId }; } if (TypeDetector.isAIV6UIMessage(message)) { const dbMsg = AIV6Adapter.fromUIMessage(message); const rawCreatedAt = "createdAt" in message ? message.createdAt : void 0; return { ...dbMsg, id, createdAt: context.generateCreatedAt(messageSource, rawCreatedAt), threadId: context.memoryInfo?.threadId, resourceId: context.memoryInfo?.resourceId }; } if (TypeDetector.isAIV5CoreMessage(message)) { const dbMsg = AIV5Adapter.fromModelMessage(message, messageSource); const rawCreatedAt = "metadata" in message && message.metadata && typeof message.metadata === "object" && "createdAt" in message.metadata ? message.metadata.createdAt : void 0; return stampMessageParts( { ...dbMsg, id, createdAt: context.generateCreatedAt(messageSource, rawCreatedAt), threadId: context.memoryInfo?.threadId, resourceId: context.memoryInfo?.resourceId }, messageSource ); } if (TypeDetector.isAIV5UIMessage(message)) { const dbMsg = AIV5Adapter.fromUIMessage(message); const rawCreatedAt = "createdAt" in message ? message.createdAt : void 0; return stampMessageParts( { ...dbMsg, id, createdAt: context.generateCreatedAt(messageSource, rawCreatedAt), threadId: context.memoryInfo?.threadId, resourceId: context.memoryInfo?.resourceId }, messageSource ); } throw new Error(`Found unhandled message ${JSON.stringify(message)}`); } function mastraMessageV1ToMastraDBMessage(message, messageSource, context) { const coreV2 = AIV4Adapter.fromCoreMessage( { content: message.content, role: message.role }, context, messageSource ); return { id: message.id, role: coreV2.role, createdAt: context.generateCreatedAt(messageSource, message.createdAt), threadId: message.threadId, resourceId: message.resourceId, content: coreV2.content }; } function hydrateMastraDBMessageFields(message, context, messageSource) { if (!message.id) { message.id = context.newMessageId(); } if (message.createdAt === void 0 || message.createdAt === null) { message.createdAt = context.generateCreatedAt(messageSource); } else if (!(message.createdAt instanceof Date)) { message.createdAt = new Date(message.createdAt); } if (message.content.toolInvocations && message.content.parts) { message.content.toolInvocations = message.content.toolInvocations.map((ti) => { if (!ti.args || Object.keys(ti.args).length === 0) { const partWithArgs = message.content.parts.find( (part) => part.type === "tool-invocation" && part.toolInvocation && part.toolInvocation.toolCallId === ti.toolCallId && part.toolInvocation.args && Object.keys(part.toolInvocation.args).length > 0 ); if (partWithArgs && partWithArgs.type === "tool-invocation") { return { ...ti, args: partWithArgs.toolInvocation.args }; } } return ti; }); } if (!message.threadId && context.memoryInfo?.threadId) { message.threadId = context.memoryInfo.threadId; if (!message.resourceId && context.memoryInfo?.resourceId) { message.resourceId = context.memoryInfo.resourceId; } } return message; } // src/agent/message-list/conversion/output-converter.ts function isTextMergePassThroughPart(part) { return part.type.startsWith("source-"); } function mergeTextPartsWithDuplicateItemIds(parts) { const result = []; for (const part of parts) { if (part.type !== "text") { result.push(part); continue; } const textPart = part; const itemId = getResponseProviderItemKey(textPart.providerMetadata); if (!itemId) { result.push(part); continue; } let merged = false; for (let index = result.length - 1; index >= 0; index--) { const previous = result[index]; if (previous.type === "text") { const previousTextPart = previous; const previousItemId = getResponseProviderItemKey(previousTextPart.providerMetadata); if (previousItemId === itemId) { result[index] = { ...previousTextPart, text: previousTextPart.text + textPart.text }; merged = true; } break; } if (!isTextMergePassThroughPart(previous)) { break; } } if (merged) { continue; } result.push(part); } return result; } function sanitizeAIV4UIMessages(messages) { const msgs = messages.map((m) => { if (m.parts.length === 0) return false; const safeParts = m.parts.filter( (p) => p.type !== `tool-invocation` || // calls and partial-calls should be updated to be results at this point // if they haven't we can't send them back to the llm and need to remove them. p.toolInvocation.state !== `call` && p.toolInvocation.state !== `partial-call` ); if (!safeParts.length) return false; const sanitized = { ...m, parts: safeParts }; if (`toolInvocations` in m && m.toolInvocations) { sanitized.toolInvocations = m.toolInvocations.filter((t) => t.state === `result`); } return sanitized; }).filter((m) => Boolean(m)); return msgs; } function sanitizeV5UIMessages(messages, filterIncompleteToolCalls = false) { let lastUserIdx = -1; for (let i = messages.length - 1; i >= 0; i--) { if (messages[i].role === "user") { lastUserIdx = i; break; } } const getSafeParts = (m, assistantTurnStillOpen) => m.parts.filter((p) => { if (typeof p.type === "string" && p.type.startsWith("data-")) { return false; } if (p.type === "text" && (!("text" in p) || p.text === "" || p.text?.trim() === "")) { if (m.role === "user") return false; const hasNonEmptyParts = m.parts.some( (part) => !(part.type === "text" && (!("text" in part) || part.text === "" || part.text?.trim() === "")) ); if (hasNonEmptyParts) return false; } if (!chunkPWQW5NYR_cjs.isToolUIPart(p)) return true; if (filterIncompleteToolCalls) { if (p.state === "output-available" || p.state === "output-error") return true; if (p.state === "input-available" && p.providerExecuted && assistantTurnStillOpen) return true; return false; } return p.state !== "input-streaming"; }); let lastSurvivingAssistantIdx = -1; if (lastUserIdx !== messages.length - 1) { for (let i = messages.length - 1; i > lastUserIdx; i--) { const message = messages[i]; if (message.role !== "assistant" || message.parts.length === 0) continue; if (getSafeParts(message, true).length > 0) { lastSurvivingAssistantIdx = i; break; } } } const msgs = messages.map((m, idx) => { if (m.parts.length === 0) return false; const assistantTurnStillOpen = m.role === "assistant" && idx === lastSurvivingAssistantIdx; const safeParts = getSafeParts(m, assistantTurnStillOpen); if (!safeParts.length) return false; const mergedParts = mergeTextPartsWithDuplicateItemIds(safeParts); const sanitized = { ...m, parts: mergedParts.map((part) => { if (chunkPWQW5NYR_cjs.isToolUIPart(part) && part.state === "output-available") { return { ...part, output: (() => { const o = part.output; if (o == null || typeof o !== "object") return o; const obj = o; if (obj.type === "content" && Array.isArray(obj.value)) return o; if ("value" in obj) return obj.value; return o; })() }; } return part; }) }; return sanitized; }).filter((m) => Boolean(m)); return msgs; } function addStartStepPartsForAIV5(messages) { for (const message of messages) { if (message.role !== `assistant`) continue; for (const [index, part] of message.parts.entries()) { if (!chunkPWQW5NYR_cjs.isToolUIPart(part)) continue; const nextPart = message.parts.at(index + 1); if (nextPart && nextPart.type !== `step-start` && !chunkPWQW5NYR_cjs.isToolUIPart(nextPart)) { message.parts.splice(index + 1, 0, { type: "step-start" }); } if (nextPart && chunkPWQW5NYR_cjs.isToolUIPart(nextPart) && !part.providerExecuted && nextPart.providerExecuted && (nextPart.state === "output-available" || nextPart.state === "output-error")) { message.parts.splice(index + 1, 0, { type: "step-start" }); } } } return messages; } function aiV4UIMessagesToAIV4CoreMessages(messages) { return chunkI4YELKDU_cjs.convertToCoreMessages(sanitizeAIV4UIMessages(messages)); } function convertMcpContentToolResultOutput(output) { if (!output || typeof output !== "object") return void 0; const content = output.content; if (!Array.isArray(content)) return void 0; const hasValidMultimodal = content.some((part) => { if (!part || typeof part !== "object") return false; const typedPart = part; return (typedPart.type === "image" || typedPart.type === "audio") && typeof typedPart.data === "string"; }); if (!hasValidMultimodal) return void 0; const value = content.map((part) => { if (!part || typeof part !== "object") return null; const typedPart = part; switch (typedPart.type) { case "text": return { type: "text", text: String(typedPart.text ?? "") }; case "image": return typeof typedPart.data === "string" ? { type: "image-data", data: typedPart.data, mediaType: String(typedPart.mimeType ?? "image/png") } : { type: "text", text: JSON.stringify(typedPart) }; case "audio": return typeof typedPart.data === "string" ? { type: "file-data", data: typedPart.data, mediaType: String(typedPart.mimeType ?? "audio/wav") } : { type: "text", text: JSON.stringify(typedPart) }; default: return { type: "text", text: JSON.stringify(typedPart) }; } }).filter(Boolean); return value.length > 0 ? { type: "content", value } : void 0; } function collectRawToolResultOutputs(dbMessages) { const outputs = /* @__PURE__ */ new Map(); for (const message of dbMessages) { if (message.content?.format !== 2 || !message.content.parts) continue; for (const part of message.content.parts) { if (part.type !== "tool-invocation" || part.toolInvocation?.state !== "result") continue; outputs.set(part.toolInvocation.toolCallId, part.toolInvocation.result); } } return outputs; } function isDefaultToolResultOutput(output, rawOutput) { if (!output || typeof output !== "object") return false; const typedOutput = output; if (typedOutput.type !== "json") return false; return JSON.stringify(typedOutput.value) === JSON.stringify(rawOutput); } function applyMcpContentToolResultOutputs(modelMessages, dbMessages) { const rawOutputs = collectRawToolResultOutputs(dbMessages); if (rawOutputs.size === 0) return modelMessages; return modelMessages.map((message) => { if (message.role !== "tool" || !Array.isArray(message.content)) return message; let modified = false; const content = message.content.map((part) => { if (part.type !== "tool-result" || !rawOutputs.has(part.toolCallId)) return part; const rawOutput = rawOutputs.get(part.toolCallId); if (!isDefaultToolResultOutput(part.output, rawOutput)) return part; const converted = convertMcpContentToolResultOutput(rawOutput); if (!converted) return part; modified = true; return { ...part, output: converted }; }); return modified ? { ...message, content } : message; }); } function restoreAssistantFileProviderMetadata(modelMessages, uiMessages) { const fileMetadata = []; for (const msg of uiMessages) { if (msg.role !== "assistant") continue; for (const part of msg.parts) { if (part.type === "file") { fileMetadata.push(part.providerMetadata ?? void 0); } } } if (fileMetadata.length === 0 || fileMetadata.every((m) => m == null)) return modelMessages; let metadataIndex = 0; return modelMessages.map((msg) => { if (msg.role !== "assistant" || typeof msg.content === "string") return msg; let modified = false; const content = msg.content.map((part) => { if (part.type !== "file" || metadataIndex >= fileMetadata.length) return part; const metadata = fileMetadata[metadataIndex++]; if (part.providerOptions || !metadata) return part; modified = true; return { ...part, providerOptions: metadata }; }); return modified ? { ...msg, content } : msg; }); } function aiV5UIMessagesToAIV5ModelMessages(messages, dbMessages, filterIncompleteToolCalls = false) { const sanitized = sanitizeV5UIMessages(messages, filterIncompleteToolCalls); const preprocessed = addStartStepPartsForAIV5(sanitized); const converted = []; for (const uiMsg of preprocessed) { const produced = chunkPWQW5NYR_cjs.convertToModelMessages([uiMsg]); if (produced.length === 0) continue; const providerMetadata = uiMsg.metadata && typeof uiMsg.metadata === "object" && "providerMetadata" in uiMsg.metadata ? uiMsg.metadata.providerMetadata : void 0; if (providerMetadata) { let target = -1; for (let index = produced.length - 1; index >= 0; index--) { if (produced[index]?.role === uiMsg.role) { target = index; break; } } if (target !== -1) { produced[target] = { ...produced[target], providerOptions: providerMetadata }; } } converted.push(...produced); } const withFileMetadata = restoreAssistantFileProviderMetadata(converted, preprocessed); const withMcpContentOutputs = applyMcpContentToolResultOutputs(withFileMetadata, dbMessages); const anthropicCompat = ensureAnthropicCompatibleMessages(withMcpContentOutputs, dbMessages); return filterIncompleteToolCalls ? sanitizeOrphanedToolPairs(anthropicCompat) : anthropicCompat; } function aiV4CoreMessagesToAIV5ModelMessages(messages, source, adapterContext, dbMessages) { return aiV5UIMessagesToAIV5ModelMessages( messages.map((m) => AIV4Adapter.fromCoreMessage(m, adapterContext, source)).map((m) => AIV5Adapter.toUIMessage(m)), dbMessages ); } function systemMessageToAIV4Core(message) { if (typeof message === `string`) { return { role: "system", content: message }; } if (TypeDetector.isAIV6CoreMessage(message)) { const dbMsg = AIV6Adapter.fromModelMessage(message, "system"); return AIV4Adapter.systemToV4Core(dbMsg); } if (TypeDetector.isAIV5CoreMessage(message)) { const dbMsg = AIV5Adapter.fromModelMessage(message, "system"); return AIV4Adapter.systemToV4Core(dbMsg); } if (TypeDetector.isMastraDBMessage(message)) { return AIV4Adapter.systemToV4Core(message); } return message; } var DefaultGeneratedFile = class { base64Data; uint8ArrayData; mediaType; constructor({ data, mediaType }) { const isUint8Array = data instanceof Uint8Array; this.base64Data = isUint8Array ? void 0 : data; this.uint8ArrayData = isUint8Array ? data : void 0; this.mediaType = mediaType; } // lazy conversion with caching to avoid unnecessary conversion overhead: get base64() { if (this.base64Data == null) { this.base64Data = providerUtilsV5.convertUint8ArrayToBase64(this.uint8ArrayData); } return this.base64Data; } // lazy conversion with caching to avoid unnecessary conversion overhead: get uint8Array() { if (this.uint8ArrayData == null) { this.uint8ArrayData = providerUtilsV5.convertBase64ToUint8Array(this.base64Data); } return this.uint8ArrayData; } }; var DefaultGeneratedFileWithType = class extends DefaultGeneratedFile { type = "file"; constructor(options) { super(options); } }; // src/agent/message-list/conversion/step-content.ts var StepContentExtractor = class _StepContentExtractor { /** * Extract content for a specific step number from UI messages * * @param uiMessages - Array of AI SDK V5 UI messages * @param stepNumber - Step number to extract (1-indexed, or -1 for last step) * @param stepContentFn - Function to convert model messages to step content * @returns Step content array */ static extractStepContent(uiMessages, stepNumber, stepContentFn) { const uiMessagesParts = uiMessages.flatMap((item) => item.parts); const stepBoundaries = []; uiMessagesParts.forEach((part, index) => { if (part.type === "step-start") { stepBoundaries.push(index); } }); if (stepNumber === -1) { return _StepContentExtractor.extractLastStep(uiMessagesParts, stepBoundaries, stepContentFn); } if (stepNumber === 1) { return _StepContentExtractor.extractFirstStep(uiMessagesParts, stepBoundaries, stepContentFn); } return _StepContentExtractor.extractMiddleStep(uiMessagesParts, stepBoundaries, stepNumber, stepContentFn); } /** * Extract the last step content (stepNumber === -1) */ static extractLastStep(uiMessagesParts, stepBoundaries, stepContentFn) { const toolParts = uiMessagesParts.filter((p) => p.type?.startsWith("tool-")); const hasStepStart = stepBoundaries.length > 0; if (!hasStepStart && toolParts.length > 0) { const lastToolPart = toolParts[toolParts.length - 1]; if (!lastToolPart) { return []; } const lastToolIndex = uiMessagesParts.indexOf(lastToolPart); const previousToolPart = toolParts[toolParts.length - 2]; const previousToolIndex = previousToolPart ? uiMessagesParts.indexOf(previousToolPart) : -1; const startIndex = previousToolIndex + 1; const stepParts2 = uiMessagesParts.slice(startIndex, lastToolIndex + 1); return _StepContentExtractor.convertPartsToContent(stepParts2, "last-step", stepContentFn); } const totalSteps = stepBoundaries.length + 1; if (totalSteps === 1 && !hasStepStart) { return _StepContentExtractor.convertPartsToContent(uiMessagesParts, "last-step", stepContentFn); } const lastStepStart = stepBoundaries[stepBoundaries.length - 1]; if (lastStepStart === void 0) { return []; } const stepParts = uiMessagesParts.slice(lastStepStart + 1); if (stepParts.length === 0) { return []; } return _StepContentExtractor.convertPartsToContent(stepParts, "last-step", stepContentFn); } /** * Extract the first step content (stepNumber === 1) */ static extractFirstStep(uiMessagesParts, stepBoundaries, stepContentFn) { const firstStepStart = stepBoundaries[0] ?? uiMessagesParts.length; if (firstStepStart === 0) { return []; } const stepParts = uiMessagesParts.slice(0, firstStepStart); return _StepContentExtractor.convertPartsToContent(stepParts, "step-1", stepContentFn); } /** * Extract content for steps 2+ (between step-start markers) */ static extractMiddleStep(uiMessagesParts, stepBoundaries, stepNumber, stepContentFn) { const stepIndex = stepNumber - 2; if (stepIndex < 0 || stepIndex >= stepBoundaries.length) { return []; } const startIndex = (stepBoundaries[stepIndex] ?? 0) + 1; const endIndex = stepBoundaries[stepIndex + 1] ?? uiMessagesParts.length; if (startIndex >= endIndex) { return []; } const stepParts = uiMessagesParts.slice(startIndex, endIndex); return _StepContentExtractor.convertPartsToContent(stepParts, `step-${stepNumber}`, stepContentFn); } /** * Convert UI message parts to step content */ static convertPartsToContent(parts, stepId, stepContentFn) { const stepUiMessages = [ { id: stepId, role: "assistant", parts } ]; const modelMessages = chunkPWQW5NYR_cjs.convertToModelMessages(sanitizeV5UIMessages(stepUiMessages)); return modelMessages.flatMap(stepContentFn); } /** * Convert a single model message content to step result content * * This handles: * - Tool results: adding input field from DB messages * - Files: converting to GeneratedFile format * - Images: converting to file format with proper media type * - Other content: passed through as-is * * @param message - Model message to convert (or undefined to use latest) * @param dbMessages - Database messages for looking up tool call args * @param getLatestMessage - Function to get the latest model message if not provided */ static convertToStepContent(message, dbMessages, getLatestMessage) { const latest = message ? message : getLatestMessage(); if (!latest) return []; if (typeof latest.content === "string") { return [{ type: "text", text: latest.content }]; } return latest.content.map((c) => { if (c.type === "tool-result") { return { type: "tool-result", input: findToolCallArgs(dbMessages, c.toolCallId), output: c.output, toolCallId: c.toolCallId, toolName: c.toolName }; } if (c.type === "file") { return { type: "file", file: new DefaultGeneratedFileWithType({ data: typeof c.data === "string" ? parseDataUri(c.data).base64Content : c.data instanceof URL ? c.data.toString() : convertDataContentToBase64String(c.data), mediaType: c.mediaType }) }; } if (c.type === "image") { return { type: "file", file: new DefaultGeneratedFileWithType({ data: typeof c.image === "string" ? parseDataUri(c.image).base64Content : c.image instanceof URL ? c.image.toString() : convertDataContentToBase64String(c.image), mediaType: c.mediaType || "unknown" }) }; } return { ...c }; }); } }; // src/agent/message-list/merge/MessageMerger.ts var MessageMerger = class _MessageMerger { /** * Check if a message is sealed (should not be merged into). * Messages are sealed after observation to preserve observation markers. */ static isSealed(message) { const metadata = message.content?.metadata; return metadata?.mastra?.sealed === true; } /** * Check if we should merge an incoming message with the latest message * * @param latestMessage - The most recent message in the list * @param incomingMessage - The message being added * @param messageSource - The source of the incoming message ('memory', 'input', 'response', 'context') * @param isLatestFromMemory - Whether the latest message is from memory * @param agentNetworkAppend - Whether agent network append mode is enabled */ static shouldMerge(latestMessage, incomingMessage, messageSource, isLatestFromMemory, agentNetworkAppend = false) { if (!latestMessage) return false; if (_MessageMerger.isSealed(latestMessage)) return false; if (latestMessage.content?.metadata?.mastra?.responseBoundary) { return false; } if (incomingMessage.content.metadata?.completionResult || latestMessage.content.metadata?.completionResult || incomingMessage.content.metadata?.isTaskCompleteResult || latestMessage.content.metadata?.isTaskCompleteResult) { return false; } const latestParts = latestMessage.content?.parts ?? []; const latestOnlyHasDataParts = latestParts.length > 0 && latestParts.every((part) => part.type.startsWith("data-")); if (latestOnlyHasDataParts && latestMessage.id !== incomingMessage.id) { return false; } const shouldAppendToLastAssistantMessage = latestMessage.role === "assistant" && incomingMessage.role === "assistant" && latestMessage.threadId === incomingMessage.threadId && // If the message is from memory, don't append to the last assistant message messageSource !== "memory"; const appendNetworkMessage = agentNetworkAppend ? !isLatestFromMemory : true; return shouldAppendToLastAssistantMessage && appendNetworkMessage; } /** * Merge an incoming assistant message into the latest assistant message. * * This preserves the existing message-level createdAt. OM uses that timestamp * as its observation boundary, so moving it forward can make an already * observed message look unobserved and eligible for reprocessing. * * This handles: * - Updating tool invocations with their results * - Adding new parts in the correct order using anchor maps * - Inserting step-start markers where needed * - Updating content strings */ static merge(latestMessage, incomingMessage) { if (incomingMessage.content.metadata) { latestMessage.content.metadata = { ...latestMessage.content.metadata ?? {}, ...incomingMessage.content.metadata }; } const toolResultAnchorMap = /* @__PURE__ */ new Map(); const partsToAdd = /* @__PURE__ */ new Map(); for (const [index, part] of incomingMessage.content.parts.entries()) { if (part.type === "tool-invocation") { if (!part.toolInvocation) continue; const existingCallPart = [...latestMessage.content.parts].reverse().find((p) => p.type === "tool-invocation" && p.toolInvocation?.toolCallId === part.toolInvocation.toolCallId); const existingCallToolInvocation = !!existingCallPart && existingCallPart.type === "tool-invocation"; if (existingCallToolInvocation) { if (part.toolInvocation.state === "result") { existingCallPart.toolInvocation = { ...existingCallPart.toolInvocation, step: part.toolInvocation.step, state: "result", result: part.toolInvocation.result, args: { ...existingCallPart.toolInvocation.args, ...part.toolInvocation.args } }; if (part.providerMetadata) { existingCallPart.providerMetadata = { ...existingCallPart.providerMetadata, ...part.providerMetadata }; } if (!latestMessage.content.toolInvocations) { latestMessage.content.toolInvocations = []; } const toolInvocationIndex = latestMessage.content.toolInvocations.findIndex( (t) => t.toolCallId === existingCallPart.toolInvocation.toolCallId ); if (toolInvocationIndex === -1) { latestMessage.content.toolInvocations.push( existingCallPart.toolInvocation ); } else { latestMessage.content.toolInvocations[toolInvocationIndex] = existingCallPart.toolInvocation; } } else if (part.toolInvocation.state === "approval-requested" || part.toolInvocation.state === "approval-responded" || part.toolInvocation.state === "output-denied" || part.toolInvocation.state === "output-error") { existingCallPart.toolInvocation = { ...existingCallPart.toolInvocation, state: part.toolInvocation.state, approval: part.toolInvocation.approval, errorText: part.toolInvocation.errorText, rawInput: part.toolInvocation.rawInput, args: { ...existingCallPart.toolInvocation.args, ...part.toolInvocation.args } }; if (part.providerMetadata) { existingCallPart.providerMetadata = { ...existingCallPart.providerMetadata, ...part.providerMetadata }; } if ("providerExecuted" in part && part.providerExecuted !== void 0) { existingCallPart.providerExecuted = part.providerExecuted; } if ("title" in part && part.title !== void 0) { existingCallPart.title = part.title; } if ("preliminary" in part && part.preliminary !== void 0) { existingCallPart.preliminary = part.preliminary; } } const existingIndex = latestMessage.content.parts.findIndex((p) => p === existingCallPart); toolResultAnchorMap.set(index, existingIndex); } else { partsToAdd.set(index, part); } } else { partsToAdd.set(index, part); } } _MessageMerger.addPartsToMessage({ latestMessage, incomingMessage, anchorMap: toolResultAnchorMap, partsToAdd }); if (!latestMessage.content.content && incomingMessage.content.content) { latestMessage.content.content = incomingMessage.content.content; } if (latestMessage.content.content && incomingMessage.content.content && latestMessage.content.content !== incomingMessage.content.content) { latestMessage.content.content = incomingMessage.content.content; } } /** * Add parts from the incoming message to the latest message using anchor positions */ static addPartsToMessage({ latestMessage, incomingMessage, anchorMap, partsToAdd }) { for (let i = 0; i < incomingMessage.content.parts.length; ++i) { const part = incomingMessage.content.parts[i]; if (!part) continue; const key = CacheKeyGenerator.fromDBParts([part]); const partToAdd = partsToAdd.get(i); if (!key || !partToAdd) continue; if (anchorMap.size > 0) { if (anchorMap.has(i)) continue; const leftAnchorV2 = [...anchorMap.keys()].filter((idx) => idx < i).pop() ?? -1; const rightAnchorV2 = [...anchorMap.keys()].find((idx) => idx > i) ?? -1; const leftAnchorLatest = leftAnchorV2 !== -1 ? anchorMap.get(leftAnchorV2) : 0; const offset = leftAnchorV2 === -1 ? i : i - leftAnchorV2; const insertAt = leftAnchorLatest + offset; const rightAnchorLatest = rightAnchorV2 !== -1 ? anchorMap.get(rightAnchorV2) : latestMessage.content.parts.length; if (insertAt >= 0 && insertAt <= rightAnchorLatest && !latestMessage.content.parts.slice(insertAt, rightAnchorLatest).some((p) => CacheKeyGenerator.fromDBParts([p]) === CacheKeyGenerator.fromDBParts([part]))) { _MessageMerger.pushNewPart({ latestMessage, newMessage: incomingMessage, part, insertAt }); for (const [v2Idx, latestIdx] of anchorMap.entries()) { if (latestIdx >= insertAt) { anchorMap.set(v2Idx, latestIdx + 1); } } } } else { _MessageMerger.pushNewPart({ latestMessage, newMessage: incomingMessage, part }); } } } /** * Push a new message part to the latest message */ static pushNewPart({ latestMessage, newMessage, part, insertAt }) { const partKey = CacheKeyGenerator.fromDBParts([part]); const latestPartCount = latestMessage.content.parts.filter( (p) => CacheKeyGenerator.fromDBParts([p]) === partKey ).length; const newPartCount = newMessage.content.parts.filter((p) => CacheKeyGenerator.fromDBParts([p]) === partKey).length; if (latestPartCount < newPartCount) { const partIndex = newMessage.content.parts.indexOf(part); const hasStepStartBefore = partIndex > 0 && newMessage.content.parts[partIndex - 1]?.type === "step-start"; const needsStepStart = latestMessage.role === "assistant" && part.type === "text" && !hasStepStartBefore && latestMessage.content.parts.length > 0 && latestMessage.content.parts.at(-1)?.type === "tool-invocation"; const previousStepStart = [...latestMessage.content.parts].reverse().find((p) => p.type === "step-start"); const stepStartPart = previousStepStart?.model ? stampPart({ type: "step-start", model: previousStepStart.model }) : { type: "step-start" }; if (typeof insertAt === "number") { if (needsStepStart) { latestMessage.content.parts.splice(insertAt, 0, stepStartPart); latestMessage.content.parts.splice(insertAt + 1, 0, part); } else { latestMessage.content.parts.splice(insertAt, 0, part); } } else { if (needsStepStart) { latestMessage.content.parts.push(stepStartPart); } latestMessage.content.parts.push(part); } } } }; // src/stream/aisdk/v5/compat/content.ts function splitDataUrl(dataUrl) { try { const [header, base64Content] = dataUrl.split(","); return { mediaType: header?.split(";")[0]?.split(":")[1], base64Content }; } catch { return { mediaType: void 0, base64Content: void 0 }; } } function convertToDataContent(content) { if (content instanceof Uint8Array) { return { data: content, mediaType: void 0 }; } if (content instanceof ArrayBuffer) { return { data: new Uint8Array(content), mediaType: void 0 }; } if (typeof content === "string") { try { content = new URL(content); } catch { } } if (content instanceof URL && content.protocol === "data:") { const { mediaType: dataUrlMediaType, base64Content } = splitDataUrl(content.toString()); if (dataUrlMediaType == null || base64Content == null) { throw new chunkXSOONORA_cjs.MastraError({ id: "INVALID_DATA_URL_FORMAT", text: `Invalid data URL format in content ${content.toString()}`, domain: chunkXSOONORA_cjs.ErrorDomain.LLM, category: chunkXSOONORA_cjs.ErrorCategory.USER }); } return { data: base64Content, mediaType: dataUrlMediaType }; } return { data: content, mediaType: void 0 }; } var imageMediaTypeSignatures = [ { mediaType: "image/gif", bytesPrefix: [71, 73, 70], base64Prefix: "R0lG" }, { mediaType: "image/png", bytesPrefix: [137, 80, 78, 71], base64Prefix: "iVBORw" }, { mediaType: "image/jpeg", bytesPrefix: [255, 216], base64Prefix: "/9j/" }, { mediaType: "image/webp", bytesPrefix: [82, 73, 70, 70], base64Prefix: "UklGRg" }, { mediaType: "image/bmp", bytesPrefix: [66, 77], base64Prefix: "Qk" }, { mediaType: "image/tiff", bytesPrefix: [73, 73, 42, 0], base64Prefix: "SUkqAA" }, { mediaType: "image/tiff", bytesPrefix: [77, 77, 0, 42], base64Prefix: "TU0AKg" }, { mediaType: "image/avif", bytesPrefix: [0, 0, 0, 32, 102, 116, 121, 112, 97, 118, 105, 102], base64Prefix: "AAAAIGZ0eXBhdmlm" }, { mediaType: "image/heic", bytesPrefix: [0, 0, 0, 32, 102, 116, 121, 112, 104, 101, 105, 99], base64Prefix: "AAAAIGZ0eXBoZWlj" } ]; var stripID3 = (data) => { const bytes = typeof data === "string" ? providerUtilsV5.convertBase64ToUint8Array(data) : data; const id3Size = ( // @ts-expect-error - bytes array access (bytes[6] & 127) << 21 | // @ts-expect-error - bytes array access (bytes[7] & 127) << 14 | // @ts-expect-error - bytes array access (bytes[8] & 127) << 7 | // @ts-expect-error - bytes array access bytes[9] & 127 ); return bytes.slice(id3Size + 10); }; function stripID3TagsIfPresent(data) { const hasId3 = typeof data === "string" && data.startsWith("SUQz") || typeof data !== "string" && data.length > 10 && data[0] === 73 && // 'I' data[1] === 68 && // 'D' data[2] === 51; return hasId3 ? stripID3(data) : data; } function detectMediaType({ data, signatures }) { const processedData = stripID3TagsIfPresent(data); for (const signature of signatures) { if (typeof processedData === "string" ? processedData.startsWith(signature.base64Prefix) : processedData.length >= signature.bytesPrefix.length && signature.bytesPrefix.every((byte, index) => processedData[index] === byte)) { return signature.mediaType; } } return void 0; } // src/agent/message-list/prompt/convert-file.ts function convertImageFilePart(part, downloadedAssets) { let originalData; const type = part.type; switch (type) { case "image": originalData = part.image; break; case "file": originalData = part.data; break; default: throw new Error(`Unsupported part type: ${type}`); } const { data: convertedData, mediaType: convertedMediaType } = convertToDataContent(originalData); let mediaType = convertedMediaType ?? part.mediaType; let data = convertedData; if (data instanceof URL && downloadedAssets) { const downloadedFile = downloadedAssets[data.toString()]; if (downloadedFile) { data = downloadedFile.data; mediaType ??= downloadedFile.mediaType; } } switch (type) { case "image": { if (data instanceof Uint8Array || typeof data === "string") { mediaType = detectMediaType({ data, signatures: imageMediaTypeSignatures }) ?? mediaType; } return { type: "file", mediaType: mediaType ?? "image/*", // any image filename: void 0, data, providerOptions: part.providerOptions }; } case "file": { if (mediaType == null) { throw new Error(`Media type is missing for file part`); } return { type: "file", mediaType, filename: part.filename, data, providerOptions: part.providerOptions }; } } } // src/agent/message-list/prompt/attachments-to-parts.ts function attachmentsToParts(attachments) { const parts = []; for (const attachment of attachments) { const categorized = categorizeFileData(attachment.url, attachment.contentType); let urlString = attachment.url; if (categorized.type === "raw") { urlString = createDataUri(attachment.url, attachment.contentType || "application/octet-stream"); } let url; try { url = new URL(urlString); } catch { throw new Error(`Invalid URL: ${attachment.url}`); } switch (url.protocol) { case "http:": case "https:": // Cloud storage protocols supported by AI providers (e.g., Vertex AI for gs://, Bedrock for s3://) case "gs:": case "s3:": { if (attachment.contentType?.startsWith("image/")) { parts.push({ type: "image", image: url.toString(), mimeType: attachment.contentType }); } else { if (!attachment.contentType) { throw new Error("If the attachment is not an image, it must specify a content type"); } parts.push({ type: "file", data: url.toString(), mimeType: attachment.contentType }); } break; } case "data:": { if (attachment.contentType?.startsWith("image/")) { parts.push({ type: "image", image: urlString, mimeType: attachment.contentType }); } else if (attachment.contentType?.startsWith("text/")) { parts.push({ type: "file", data: urlString, mimeType: attachment.contentType }); } else { if (!attachment.contentType) { throw new Error("If the attachment is not an image or text, it must specify a content type"); } parts.push({ type: "file", data: urlString, mimeType: attachment.contentType }); } break; } default: { throw new Error(`Unsupported URL protocol: ${url.protocol}`); } } } return parts; } // src/agent/message-list/prompt/convert-to-mastra-v1.ts var makePushOrCombine = (v1Messages) => { const idUsageCount = /* @__PURE__ */ new Map(); const SPLIT_SUFFIX_PATTERN = /__split-\d+$/; return (msg) => { const previousMessage = v1Messages.at(-1); if (msg.role === previousMessage?.role && Array.isArray(previousMessage.content) && Array.isArray(msg.content) && // we were creating new messages for tool calls before and not appending to the assistant message // so don't append here so everything works as before (msg.role !== `assistant` || msg.role === `assistant` && msg.content.at(-1)?.type !== `tool-call`)) { for (const part of msg.content) { previousMessage.content.push(part); } } else { let baseId = msg.id; const hasSplitSuffix = SPLIT_SUFFIX_PATTERN.test(baseId); if (hasSplitSuffix) { v1Messages.push(msg); return; } const currentCount = idUsageCount.get(baseId) || 0; if (currentCount > 0) { msg.id = `${baseId}__split-${currentCount}`; } idUsageCount.set(baseId, currentCount + 1); v1Messages.push(msg); } }; }; function convertToV1Messages(messages) { const v1Messages = []; const pushOrCombine = makePushOrCombine(v1Messages); for (let i = 0; i < messages.length; i++) { const message = messages[i]; const isLastMessage = i === messages.length - 1; if (!message?.content) continue; const { content, experimental_attachments: inputAttachments = [], parts: inputParts } = message.content; const { role } = message; const fields = { id: message.id, createdAt: message.createdAt, resourceId: message.resourceId, threadId: message.threadId }; const experimental_attachments = [...inputAttachments]; const parts = []; for (const part of inputParts) { if (part.type === "file") { experimental_attachments.push({ url: part.data, contentType: part.mimeType }); } else { parts.push(part); } } switch (role) { case "user": { if (parts == null) { const userContent = experimental_attachments ? [{ type: "text", text: content || "" }, ...attachmentsToParts(experimental_attachments)] : { type: "text", text: content || "" }; pushOrCombine({ role: "user", ...fields, type: "text", // @ts-expect-error - content type mismatch in conversion content: userContent }); } else { const textParts = message.content.parts.filter((part) => part.type === "text").map((part) => ({ type: "text", text: part.text })); const userContent = experimental_attachments ? [...textParts, ...attachmentsToParts(experimental_attachments)] : textParts; pushOrCombine({ role: "user", ...fields, type: "text", content: Array.isArray(userContent) && userContent.length === 1 && userContent[0]?.type === `text` && typeof content !== `undefined` ? content : userContent }); } break; } case "assistant": { if (message.content.parts != null) { let processBlock2 = function() { const content2 = []; for (const part of block) { switch (part.type) { case "file": case "text": { content2.push(part); break; } case "reasoning": { for (const detail of part.details) { switch (detail.type) { case "text": content2.push({ type: "reasoning", text: detail.text, signature: detail.signature }); break; case "redacted": content2.push({ type: "redacted-reasoning", data: detail.data }); break; } } break; } case "tool-invocation": if (part.toolInvocation.toolName !== "updateWorkingMemory") { content2.push({ type: "tool-call", toolCallId: part.toolInvocation.toolCallId, toolName: part.toolInvocation.toolName, args: part.toolInvocation.args }); } break; } } pushOrCombine({ role: "assistant", ...fields, type: content2.some((c) => c.type === `tool-call`) ? "tool-call" : "text", content: typeof content2 !== `string` && Array.isArray(content2) && content2.length === 1 && content2[0]?.type === `text` ? content2[0].text : content2 }); const stepInvocations = block.filter((part) => `type` in part && part.type === "tool-invocation").map((part) => part.toolInvocation).filter((ti) => ti.toolName !== "updateWorkingMemory"); const invocationsWithResults = stepInvocations.filter((ti) => ti.state === "result" && "result" in ti); if (invocationsWithResults.length > 0) { pushOrCombine({ role: "tool", ...fields, type: "tool-result", content: invocationsWithResults.map((toolInvocation) => { const { toolCallId, toolName, result } = toolInvocation; return { type: "tool-result", toolCallId, toolName, result }; }) }); } block = []; blockHasToolInvocations = false; currentStep++; }; let currentStep = 0; let blockHasToolInvocations = false; let block = []; for (const part of message.content.parts) { switch (part.type) { case "text": { if (blockHasToolInvocations) { processBlock2(); } block.push(part); break; } case "file": case "reasoning": { block.push(part); break; } case "tool-invocation": { const hasNonToolContent = block.some( (p) => p.type === "text" || p.type === "file" || p.type === "reasoning" ); if (hasNonToolContent || (part.toolInvocation.step ?? 0) !== currentStep) { processBlock2(); } block.push(part); blockHasToolInvocations = true; break; } } } processBlock2(); const toolInvocations2 = message.content.toolInvocations; if (toolInvocations2 && toolInvocations2.length > 0) { const processedToolCallIds = /* @__PURE__ */ new Set(); for (const part of message.content.parts) { if (part.type === "tool-invocation" && part.toolInvocation.toolCallId) { processedToolCallIds.add(part.toolInvocation.toolCallId); } } const unprocessedToolInvocations = toolInvocations2.filter( (ti) => !processedToolCallIds.has(ti.toolCallId) && ti.toolName !== "updateWorkingMemory" ); if (unprocessedToolInvocations.length > 0) { const invocationsByStep = /* @__PURE__ */ new Map(); for (const inv of unprocessedToolInvocations) { const step = inv.step ?? 0; if (!invocationsByStep.has(step)) { invocationsByStep.set(step, []); } invocationsByStep.get(step).push(inv); } const sortedSteps = Array.from(invocationsByStep.keys()).sort((a, b) => a - b); for (const step of sortedSteps) { const stepInvocations = invocationsByStep.get(step); pushOrCombine({ role: "assistant", ...fields, type: "tool-call", content: [ ...stepInvocations.map(({ toolCallId, toolName, args }) => ({ type: "tool-call", toolCallId, toolName, args })) ] }); const invocationsWithResults = stepInvocations.filter((ti) => ti.state === "result" && "result" in ti); if (invocationsWithResults.length > 0) { pushOrCombine({ role: "tool", ...fields, type: "tool-result", content: invocationsWithResults.map((toolInvocation) => { const { toolCallId, toolName, result } = toolInvocation; return { type: "tool-result", toolCallId, toolName, result }; }) }); } } } } break; } const toolInvocations = message.content.toolInvocations; if (toolInvocations == null || toolInvocations.length === 0) { pushOrCombine({ role: "assistant", ...fields, content: content || "", type: "text" }); break; } const maxStep = toolInvocations.reduce((max, toolInvocation) => { return Math.max(max, toolInvocation.step ?? 0); }, 0); for (let i2 = 0; i2 <= maxStep; i2++) { const stepInvocations = toolInvocations.filter( (toolInvocation) => (toolInvocation.step ?? 0) === i2 && toolInvocation.toolName !== "updateWorkingMemory" ); if (stepInvocations.length === 0) { continue; } pushOrCombine({ role: "assistant", ...fields, type: "tool-call", content: [ ...isLastMessage && content && i2 === 0 ? [{ type: "text", text: content }] : [], ...stepInvocations.map(({ toolCallId, toolName, args }) => ({ type: "tool-call", toolCallId, toolName, args })) ] }); const invocationsWithResults = stepInvocations.filter((ti) => ti.state === "result" && "result" in ti); if (invocationsWithResults.length > 0) { pushOrCombine({ role: "tool", ...fields, type: "tool-result", content: invocationsWithResults.map((toolInvocation) => { const { toolCallId, toolName, result } = toolInvocation; return { type: "tool-result", toolCallId, toolName, result }; }) }); } } if (content && !isLastMessage) { pushOrCombine({ role: "assistant", ...fields, type: "text", content: content || "" }); } break; } } } return v1Messages; } function redactUrlForLog(url) { return `${url.origin}${url.pathname}`; } var downloadFromUrl = async ({ url, downloadRetries }) => { const urlText = url.toString(); const safeUrl = redactUrlForLog(url); try { const response = await chunkPAMAGEYY_cjs.fetchWithRetry( urlText, { method: "GET" }, downloadRetries, { shouldRetryResponse: (response2) => response2.status >= 500 } ); if (!response.ok) { throw new chunkXSOONORA_cjs.MastraError({ id: "DOWNLOAD_ASSETS_FAILED", text: `Failed to download asset: ${safeUrl}`, domain: chunkXSOONORA_cjs.ErrorDomain.LLM, category: chunkXSOONORA_cjs.ErrorCategory.USER, details: { url: urlText } }); } return { data: new Uint8Array(await response.arrayBuffer()), mediaType: response.headers.get("content-type") ?? void 0 }; } catch (error) { throw new chunkXSOONORA_cjs.MastraError( { id: "DOWNLOAD_ASSETS_FAILED", text: `Failed to download asset: ${safeUrl}`, domain: chunkXSOONORA_cjs.ErrorDomain.LLM, category: chunkXSOONORA_cjs.ErrorCategory.USER, details: { url: urlText } }, error ); } }; async function downloadAssetsFromMessages({ messages, downloadConcurrency = 10, downloadRetries = 3, supportedUrls }) { const pMap = (await import('p-map')).default; const filesToDownload = messages.filter((message) => message.role === "user").map((message) => message.content).filter((content) => Array.isArray(content)).flat().filter((part) => part.type === "image" || part.type === "file").map((part) => { const mediaType = part.mediaType ?? (part.type === "image" ? "image/*" : void 0); let data = part.type === "image" ? part.image : part.data; if (typeof data === "string") { try { data = new URL(data); } catch { } } return { mediaType, data }; }).filter((part) => part.data instanceof URL).map((part) => { return { url: part.data, isUrlSupportedByModel: part.mediaType != null && providerUtilsV5.isUrlSupported({ url: part.data.toString(), mediaType: part.mediaType, supportedUrls: supportedUrls ?? {} }) }; }); const downloadedFiles = await pMap( filesToDownload, async (fileItem) => { if (fileItem.isUrlSupportedByModel) { return null; } return { url: fileItem.url.toString(), ...await downloadFromUrl({ url: fileItem.url, downloadRetries }) }; }, { concurrency: downloadConcurrency } ); const downloadFileList = downloadedFiles.filter( (downloadedFile) => downloadedFile?.data != null ).map(({ url, data, mediaType }) => [url, { data, mediaType }]); return Object.fromEntries(downloadFileList); } // src/agent/message-list/state/serialization.ts function serializeMessage(message) { return { ...message, createdAt: message.createdAt.toISOString() }; } function deserializeMessage(message) { return { ...message, createdAt: new Date(message.createdAt) }; } function serializeMessages(messages) { return messages.map(serializeMessage); } function deserializeMessages(messages) { return messages.map(deserializeMessage); } // src/agent/message-list/state/MessageStateManager.ts var MessageStateManager = class { // Messages tracked by source memoryMessages = /* @__PURE__ */ new Set(); newUserMessages = /* @__PURE__ */ new Set(); newResponseMessages = /* @__PURE__ */ new Set(); userContextMessages = /* @__PURE__ */ new Set(); // Persisted message tracking memoryMessagesPersisted = /* @__PURE__ */ new Set(); newUserMessagesPersisted = /* @__PURE__ */ new Set(); newResponseMessagesPersisted = /* @__PURE__ */ new Set(); userContextMessagesPersisted = /* @__PURE__ */ new Set(); /** * Add a message to the appropriate source set and persisted set */ addToSource(message, source) { switch (source) { case "memory": this.memoryMessages.add(message); this.memoryMessagesPersisted.add(message); break; case "response": if (this.memoryMessages.has(message)) { this.memoryMessages.delete(message); } this.newResponseMessages.add(message); this.newResponseMessagesPersisted.add(message); if (this.newUserMessages.has(message)) { this.newUserMessages.delete(message); } break; case "input": case "user": this.newUserMessages.add(message); this.newUserMessagesPersisted.add(message); break; case "context": this.userContextMessages.add(message); this.userContextMessagesPersisted.add(message); break; default: throw new Error(`Missing message source for message ${message}`); } } /** * Check if a message belongs to the memory source */ isMemoryMessage(message) { return this.memoryMessages.has(message); } /** * Check if a message belongs to the input source */ isUserMessage(message) { return this.newUserMessages.has(message); } /** * Check if a message belongs to the response source */ isResponseMessage(message) { return this.newResponseMessages.has(message); } /** * Check if a message belongs to the context source */ isContextMessage(message) { return this.userContextMessages.has(message); } /** * Get all memory messages */ getMemoryMessages() { return this.memoryMessages; } /** * Get all user/input messages */ getUserMessages() { return this.newUserMessages; } /** * Get all response messages */ getResponseMessages() { return this.newResponseMessages; } /** * Get all context messages */ getContextMessages() { return this.userContextMessages; } /** * Get persisted memory messages */ getMemoryMessagesPersisted() { return this.memoryMessagesPersisted; } /** * Get persisted user/input messages */ getUserMessagesPersisted() { return this.newUserMessagesPersisted; } /** * Get persisted response messages */ getResponseMessagesPersisted() { return this.newResponseMessagesPersisted; } /** * Get persisted context messages */ getContextMessagesPersisted() { return this.userContextMessagesPersisted; } /** * Remove a message from all source sets */ removeMessage(message) { this.memoryMessages.delete(message); this.newUserMessages.delete(message); this.newResponseMessages.delete(message); this.userContextMessages.delete(message); } /** * Clear all user messages */ clearUserMessages() { this.newUserMessages.clear(); } /** * Clear all response messages */ clearResponseMessages() { this.newResponseMessages.clear(); } /** * Clear all context messages */ clearContextMessages() { this.userContextMessages.clear(); } /** * Clear all messages from all sources (but not persisted tracking) */ clearAll() { this.newUserMessages.clear(); this.newResponseMessages.clear(); this.userContextMessages.clear(); } /** * Create a lookup function to determine message source */ createSourceChecker() { const sources = { memory: new Set(Array.from(this.memoryMessages.values()).map((m) => m.id)), output: new Set(Array.from(this.newResponseMessages.values()).map((m) => m.id)), input: new Set(Array.from(this.newUserMessages.values()).map((m) => m.id)), context: new Set(Array.from(this.userContextMessages.values()).map((m) => m.id)) }; return { ...sources, getSource: (msg) => { if (sources.memory.has(msg.id)) return "memory"; if (sources.input.has(msg.id)) return "input"; if (sources.output.has(msg.id)) return "response"; if (sources.context.has(msg.id)) return "context"; return null; } }; } /** * Check if a message is a new (unsaved) user or response message by ID */ isNewMessage(messageOrId) { const id = typeof messageOrId === "string" ? messageOrId : messageOrId.id; if (typeof messageOrId !== "string") { if (this.newUserMessages.has(messageOrId) || this.newResponseMessages.has(messageOrId)) { return true; } } return Array.from(this.newUserMessages).some((m) => m.id === id) || Array.from(this.newResponseMessages).some((m) => m.id === id); } /** * Serialize source tracking state (message IDs only) */ serializeSourceTracking() { const serializeSet = (set) => Array.from(set).map((value) => value.id); return { memoryMessages: serializeSet(this.memoryMessages), newUserMessages: serializeSet(this.newUserMessages), newResponseMessages: serializeSet(this.newResponseMessages), userContextMessages: serializeSet(this.userContextMessages), memoryMessagesPersisted: serializeSet(this.memoryMessagesPersisted), newUserMessagesPersisted: serializeSet(this.newUserMessagesPersisted), newResponseMessagesPersisted: serializeSet(this.newResponseMessagesPersisted), userContextMessagesPersisted: serializeSet(this.userContextMessagesPersisted) }; } /** * Deserialize source tracking state from message IDs */ deserializeSourceTracking(state, messages) { const deserializeSet = (ids) => new Set(ids.map((id) => messages.find((m) => m.id === id)).filter(Boolean)); this.memoryMessages = deserializeSet(state.memoryMessages); this.newUserMessages = deserializeSet(state.newUserMessages); this.newResponseMessages = deserializeSet(state.newResponseMessages); this.userContextMessages = deserializeSet(state.userContextMessages); this.memoryMessagesPersisted = deserializeSet(state.memoryMessagesPersisted); this.newUserMessagesPersisted = deserializeSet(state.newUserMessagesPersisted); this.newResponseMessagesPersisted = deserializeSet(state.newResponseMessagesPersisted); this.userContextMessagesPersisted = deserializeSet(state.userContextMessagesPersisted); } /** * Serialize all MessageList state for workflow suspend/resume */ serializeAll(data) { return { messages: serializeMessages(data.messages), systemMessages: data.systemMessages, taggedSystemMessages: data.taggedSystemMessages, memoryInfo: data.memoryInfo, _agentNetworkAppend: data.agentNetworkAppend, ...this.serializeSourceTracking() }; } /** * Deserialize all MessageList state from workflow suspend/resume */ deserializeAll(state) { const messages = deserializeMessages(state.messages); this.deserializeSourceTracking( { memoryMessages: state.memoryMessages, newUserMessages: state.newUserMessages, newResponseMessages: state.newResponseMessages, userContextMessages: state.userContextMessages, memoryMessagesPersisted: state.memoryMessagesPersisted, newUserMessagesPersisted: state.newUserMessagesPersisted, newResponseMessagesPersisted: state.newResponseMessagesPersisted, userContextMessagesPersisted: state.userContextMessagesPersisted }, messages ); return { messages, systemMessages: state.systemMessages, taggedSystemMessages: state.taggedSystemMessages, memoryInfo: state.memoryInfo, agentNetworkAppend: state._agentNetworkAppend }; } }; // src/agent/message-list/message-list.ts function isSignalDataMessage(message) { return message.role === "system" && message.parts.length > 0 && message.parts.every((p) => p.type.startsWith("data-")); } function mergeSignalDataParts(messages) { const result = []; for (let idx = 0; idx < messages.length; idx++) { const message = messages[idx]; if (!isSignalDataMessage(message)) { result.push(message); continue; } const prev = result[result.length - 1]; const next = messages[idx + 1]; if (prev && prev.role === "assistant") { result[result.length - 1] = { ...prev, parts: [...prev.parts, ...message.parts] }; } else if (next && next.role === "assistant") { messages[idx + 1] = { ...next, parts: [...message.parts, ...next.parts] }; } else { result.push({ ...message, role: "assistant" }); } } return result; } var MessageList = class { messages = []; // passed in by dev in input or context systemMessages = []; // passed in by us for a specific purpose, eg memory system message taggedSystemMessages = {}; memoryInfo = null; // Centralized state management for message tracking stateManager = new MessageStateManager(); // Legacy getters for backward compatibility - delegate to stateManager get memoryMessages() { return this.stateManager.getMemoryMessages(); } get newUserMessages() { return this.stateManager.getUserMessages(); } get newResponseMessages() { return this.stateManager.getResponseMessages(); } get userContextMessages() { return this.stateManager.getContextMessages(); } get memoryMessagesPersisted() { return this.stateManager.getMemoryMessagesPersisted(); } get newUserMessagesPersisted() { return this.stateManager.getUserMessagesPersisted(); } get newResponseMessagesPersisted() { return this.stateManager.getResponseMessagesPersisted(); } get userContextMessagesPersisted() { return this.stateManager.getContextMessagesPersisted(); } generateMessageId; _agentNetworkAppend = false; filterIncompleteToolCalls; logger; toAIV5UIMessages(messages, options) { return mergeSignalDataParts(messages.map((message) => AIV5Adapter.toUIMessage(message, options))); } toAIV4UIMessages(messages, options) { return mergeSignalDataParts(messages.map((message) => AIV4Adapter.toUIMessage(message, options))); } toAIV6UIMessages(messages) { return mergeSignalDataParts(messages.map(AIV6Adapter.toUIMessage)); } // Event recording for observability isRecording = false; recordedEvents = []; constructor({ threadId, resourceId, generateMessageId, logger, filterIncompleteToolCalls, // @ts-expect-error Flag for agent network messages _agentNetworkAppend } = {}) { if (threadId) { this.memoryInfo = { threadId, resourceId }; } this.generateMessageId = generateMessageId; this.logger = logger; this.filterIncompleteToolCalls = filterIncompleteToolCalls ?? true; this._agentNetworkAppend = _agentNetworkAppend || false; } /** * Start recording mutations to the MessageList for observability/tracing */ startRecording() { this.isRecording = true; this.recordedEvents = []; } hasRecordedEvents() { return this.recordedEvents.length > 0; } getRecordedEvents() { const events = [...this.recordedEvents]; return events; } /** * Stop recording and return the list of recorded events */ stopRecording() { this.isRecording = false; const events = this.getRecordedEvents(); this.recordedEvents = []; return events; } addSignal(signal, options) { const source = options?.source ?? "input"; const createdAt = this.generateCreatedAt(source, /* @__PURE__ */ new Date()); const acceptedAt = signal.acceptedAt ?? signal.createdAt; const signalForTranscript = createSignal({ id: signal.id, type: signal.type, tagName: signal.tagName, contents: signal.contents, attributes: signal.attributes, metadata: signal.metadata, providerOptions: signal.providerOptions, createdAt, acceptedAt }); this.addOne(signalForTranscript.toDBMessage(this.memoryInfo ?? void 0), source); return signalForTranscript; } add(messages, messageSource) { if (messageSource === `user`) messageSource = `input`; if (!messages) return this; const messageArray = Array.isArray(messages) ? messages : [messages]; if (this.isRecording) { this.recordedEvents.push({ type: "add", source: messageSource, count: messageArray.length }); } for (const message of messageArray) { if (isCreatedAgentSignal(message) && messageSource === "input") { this.addSignal(message, { source: messageSource }); continue; } const messageInput = isCreatedAgentSignal(message) ? message.toDBMessage(this.memoryInfo ?? void 0) : typeof message === `string` ? { role: "user", content: message } : message; if (Array.isArray(messageInput)) { for (const nestedMessage of messageInput) { this.addOne( typeof nestedMessage === `string` ? { role: "user", content: nestedMessage } : nestedMessage, messageSource ); } continue; } this.addOne( typeof messageInput === `string` ? { role: "user", content: messageInput } : messageInput, messageSource ); } return this; } serialize() { return this.stateManager.serializeAll({ messages: this.messages, systemMessages: this.systemMessages, taggedSystemMessages: this.taggedSystemMessages, memoryInfo: this.memoryInfo, agentNetworkAppend: this._agentNetworkAppend }); } /** * Custom serialization for tracing/observability spans. * Returns a clean representation with just the essential data, * excluding internal state tracking, methods, and implementation details. * * This is automatically called by the span serialization system when * a MessageList instance appears in span input/output/attributes. */ serializeForSpan() { const coreMessages = this.all.aiV4.core(); return { messages: coreMessages.map((msg) => ({ role: msg.role, content: msg.content })), systemMessages: [ // Untagged first (base instructions) ...this.systemMessages.map((m) => ({ role: m.role, content: m.content })), // Tagged after (contextual additions) ...Object.entries(this.taggedSystemMessages).flatMap( ([tag, msgs]) => msgs.map((m) => ({ role: m.role, content: m.content, tag })) ) ] }; } deserialize(state) { const data = this.stateManager.deserializeAll(state); this.messages = data.messages; this.systemMessages = data.systemMessages; this.taggedSystemMessages = data.taggedSystemMessages; this.memoryInfo = data.memoryInfo; this._agentNetworkAppend = data.agentNetworkAppend; for (const message of this.messages) { this.updateLastCreatedAt(message); } return this; } getMessagesForModelPrompt() { return this.messages.flatMap((message) => { if (message.role !== "signal") { return [message]; } return this.convertSignalForModelPrompt(message); }); } convertSignalForModelPrompt(message) { const signalMessage = mastraDBMessageToSignal(message).toLLMMessage(); const createdAt = message.createdAt; const promptMessage = { ...signalMessage, id: message.id, metadata: { createdAt } }; return [ inputToMastraDBMessage(promptMessage, "input", { memoryInfo: this.memoryInfo, newMessageId: () => message.id, generateCreatedAt: (_messageSource, start) => { if (start instanceof Date) return start; if (typeof start === "string" || typeof start === "number") return new Date(start); return createdAt; }, dbMessages: this.messages }) ]; } makeMessageSourceChecker() { return this.stateManager.createSourceChecker(); } getLatestUserContent() { const currentUserMessages = this.all.core().filter((m) => m.role === "user"); const content = currentUserMessages.at(-1)?.content; if (!content) return null; return coreContentToString(content); } get get() { return { all: this.all, remembered: this.remembered, input: this.input, response: this.response }; } get getPersisted() { return { remembered: this.rememberedPersisted, input: this.inputPersisted, taggedSystemMessages: this.taggedSystemMessages, response: this.responsePersisted }; } get clear() { return { all: { db: () => { const allMessages = [...this.messages]; this.messages = []; this.stateManager.clearAll(); if (this.isRecording && allMessages.length > 0) { this.recordedEvents.push({ type: "clear", count: allMessages.length }); } return allMessages; } }, input: { db: () => { const userMessages = Array.from(this.stateManager.getUserMessages()); this.messages = this.messages.filter((m) => !this.stateManager.isUserMessage(m)); this.stateManager.clearUserMessages(); if (this.isRecording && userMessages.length > 0) { this.recordedEvents.push({ type: "clear", source: "input", count: userMessages.length }); } return userMessages; } }, response: { db: () => { const responseMessages = Array.from(this.stateManager.getResponseMessages()); this.messages = this.messages.filter((m) => !this.stateManager.isResponseMessage(m)); this.stateManager.clearResponseMessages(); if (this.isRecording && responseMessages.length > 0) { this.recordedEvents.push({ type: "clear", source: "response", count: responseMessages.length }); } return responseMessages; } } }; } /** * Remove messages by ID * @param ids - Array of message IDs to remove * @returns Array of removed messages */ removeByIds(ids) { const idsSet = new Set(ids); const removed = []; this.messages = this.messages.filter((m) => { if (idsSet.has(m.id)) { removed.push(m); this.stateManager.removeMessage(m); return false; } return true; }); if (this.isRecording && removed.length > 0) { this.recordedEvents.push({ type: "removeByIds", ids, count: removed.length }); } return removed; } all = { db: () => this.messages, v1: () => convertToV1Messages(this.all.db()), aiV5: { model: () => { const promptMessages = this.getMessagesForModelPrompt(); return aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(promptMessages, { transformToolPayloads: false }), promptMessages ); }, ui: () => this.toAIV5UIMessages(this.all.db()), // Used when calling AI SDK streamText/generateText prompt: () => { const systemMessages = aiV4CoreMessagesToAIV5ModelMessages( [...this.systemMessages, ...Object.values(this.taggedSystemMessages).flat()], `system`, this.createAdapterContext(), this.messages ); const promptMessages = this.getMessagesForModelPrompt(); const modelMessages = aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(promptMessages, { transformToolPayloads: false }), promptMessages, this.filterIncompleteToolCalls ); const messages = [...systemMessages, ...modelMessages]; return ensureGeminiCompatibleMessages(messages, this.logger); }, // Used for creating LLM prompt messages without AI SDK streamText/generateText llmPrompt: async (options = { downloadConcurrency: 10, downloadRetries: 3 }) => { const promptMessages = this.getMessagesForModelPrompt(); const modelMessages = aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(promptMessages, { transformToolPayloads: false }), promptMessages, this.filterIncompleteToolCalls ); const storedModelOutputs = /* @__PURE__ */ new Map(); for (const dbMsg of this.messages) { if (dbMsg.content?.format !== 2 || !dbMsg.content.parts) continue; for (const part of dbMsg.content.parts) { if (part.type === "tool-invocation" && part.toolInvocation?.state === "result" && part.providerMetadata?.mastra && typeof part.providerMetadata.mastra === "object" && "modelOutput" in part.providerMetadata.mastra) { storedModelOutputs.set( part.toolInvocation.toolCallId, part.providerMetadata.mastra.modelOutput ); } } } if (storedModelOutputs.size > 0) { for (const modelMsg of modelMessages) { if (modelMsg.role !== "tool" || !Array.isArray(modelMsg.content)) continue; for (let i = 0; i < modelMsg.content.length; i++) { const part = modelMsg.content[i]; if (part.type === "tool-result" && storedModelOutputs.has(part.toolCallId)) { modelMsg.content[i] = { ...part, output: storedModelOutputs.get(part.toolCallId) }; } } } } const systemMessages = aiV4CoreMessagesToAIV5ModelMessages( [...this.systemMessages, ...Object.values(this.taggedSystemMessages).flat()], `system`, this.createAdapterContext(), this.messages ); const downloadedAssets = await downloadAssetsFromMessages({ messages: modelMessages, downloadConcurrency: options?.downloadConcurrency, downloadRetries: options?.downloadRetries, supportedUrls: options?.supportedUrls }); let messages = [...systemMessages, ...modelMessages]; const hasImageOrFileContent = modelMessages.some( (message) => (message.role === "user" || message.role === "assistant") && typeof message.content !== "string" && message.content.some((part) => part.type === "image" || part.type === "file") ); if (hasImageOrFileContent) { messages = messages.map((message) => { if (message.role === "user") { if (typeof message.content === "string") { return { role: "user", content: [{ type: "text", text: message.content }], providerOptions: message.providerOptions }; } const convertedContent = message.content.map((part) => { if (part.type === "image" || part.type === "file") { return convertImageFilePart(part, downloadedAssets); } return part; }).filter((part) => part.type !== "text" || part.text !== ""); return { role: "user", content: convertedContent, providerOptions: message.providerOptions }; } if (message.role === "assistant" && typeof message.content !== "string") { const convertedContent = message.content.map((part) => { if (part.type === "file") { return convertImageFilePart(part, downloadedAssets); } return part; }); return { ...message, content: convertedContent }; } return message; }); } messages = ensureGeminiCompatibleMessages(messages, this.logger); return messages.map(aiV5ModelMessageToV2PromptMessage).filter( (message) => message.role === "system" || typeof message.content === "string" || message.content.length > 0 ); } }, aiV6: { ui: () => this.toAIV6UIMessages(this.all.db()) }, /* @deprecated use list.get.all.aiV4.prompt() instead */ prompt: () => this.all.aiV4.prompt(), /* @deprecated use list.get.all.aiV4.ui() */ ui: () => this.toAIV4UIMessages(this.all.db()), /* @deprecated use list.get.all.aiV4.core() */ core: () => aiV4UIMessagesToAIV4CoreMessages(this.toAIV4UIMessages(this.all.db(), { transformToolPayloads: false })), aiV4: { ui: () => this.toAIV4UIMessages(this.all.db()), core: () => aiV4UIMessagesToAIV4CoreMessages( this.toAIV4UIMessages(this.getMessagesForModelPrompt(), { transformToolPayloads: false }) ), // Used when calling AI SDK streamText/generateText prompt: () => { const coreMessages = this.all.aiV4.core(); const messages = [...this.systemMessages, ...Object.values(this.taggedSystemMessages).flat(), ...coreMessages]; return ensureGeminiCompatibleMessages(messages, this.logger); }, // Used for creating LLM prompt messages without AI SDK streamText/generateText llmPrompt: () => { const coreMessages = this.all.aiV4.core(); const systemMessages = [...this.systemMessages, ...Object.values(this.taggedSystemMessages).flat()]; let messages = [...systemMessages, ...coreMessages]; messages = ensureGeminiCompatibleMessages(messages, this.logger); return messages.map(aiV4CoreMessageToV1PromptMessage); } } }; remembered = { db: () => this.messages.filter((m) => this.memoryMessages.has(m)), v1: () => convertToV1Messages(this.remembered.db()), aiV5: { model: () => aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(this.remembered.db(), { transformToolPayloads: false }), this.messages ), ui: () => this.toAIV5UIMessages(this.remembered.db()) }, aiV6: { ui: () => this.toAIV6UIMessages(this.remembered.db()) }, /* @deprecated use list.get.remembered.aiV4.ui() */ ui: () => this.toAIV4UIMessages(this.remembered.db()), /* @deprecated use list.get.remembered.aiV4.core() */ core: () => aiV4UIMessagesToAIV4CoreMessages(this.toAIV4UIMessages(this.remembered.db(), { transformToolPayloads: false })), aiV4: { ui: () => this.toAIV4UIMessages(this.remembered.db()), core: () => aiV4UIMessagesToAIV4CoreMessages(this.toAIV4UIMessages(this.remembered.db(), { transformToolPayloads: false })) } }; rememberedPersisted = { db: () => this.all.db().filter((m) => this.memoryMessagesPersisted.has(m)), v1: () => convertToV1Messages(this.rememberedPersisted.db()), aiV5: { model: () => aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(this.rememberedPersisted.db(), { transformToolPayloads: false }), this.messages ), ui: () => this.toAIV5UIMessages(this.rememberedPersisted.db()) }, aiV6: { ui: () => this.toAIV6UIMessages(this.rememberedPersisted.db()) }, /* @deprecated use list.getPersisted.remembered.aiV4.ui() */ ui: () => this.toAIV4UIMessages(this.rememberedPersisted.db()), /* @deprecated use list.getPersisted.remembered.aiV4.core() */ core: () => aiV4UIMessagesToAIV4CoreMessages( this.toAIV4UIMessages(this.rememberedPersisted.db(), { transformToolPayloads: false }) ), aiV4: { ui: () => this.toAIV4UIMessages(this.rememberedPersisted.db()), core: () => aiV4UIMessagesToAIV4CoreMessages( this.toAIV4UIMessages(this.rememberedPersisted.db(), { transformToolPayloads: false }) ) } }; input = { db: () => this.messages.filter((m) => this.newUserMessages.has(m)), v1: () => convertToV1Messages(this.input.db()), aiV5: { model: () => aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(this.input.db(), { transformToolPayloads: false }), this.messages ), ui: () => this.toAIV5UIMessages(this.input.db()) }, aiV6: { ui: () => this.toAIV6UIMessages(this.input.db()) }, /* @deprecated use list.get.input.aiV4.ui() instead */ ui: () => this.toAIV4UIMessages(this.input.db()), /* @deprecated use list.get.core.aiV4.ui() instead */ core: () => aiV4UIMessagesToAIV4CoreMessages(this.toAIV4UIMessages(this.input.db(), { transformToolPayloads: false })), aiV4: { ui: () => this.toAIV4UIMessages(this.input.db()), core: () => aiV4UIMessagesToAIV4CoreMessages(this.toAIV4UIMessages(this.input.db(), { transformToolPayloads: false })) } }; inputPersisted = { db: () => this.messages.filter((m) => this.newUserMessagesPersisted.has(m)), v1: () => convertToV1Messages(this.inputPersisted.db()), aiV5: { model: () => aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(this.inputPersisted.db(), { transformToolPayloads: false }), this.messages ), ui: () => this.toAIV5UIMessages(this.inputPersisted.db()) }, aiV6: { ui: () => this.toAIV6UIMessages(this.inputPersisted.db()) }, /* @deprecated use list.getPersisted.input.aiV4.ui() */ ui: () => this.toAIV4UIMessages(this.inputPersisted.db()), /* @deprecated use list.getPersisted.input.aiV4.core() */ core: () => aiV4UIMessagesToAIV4CoreMessages( this.toAIV4UIMessages(this.inputPersisted.db(), { transformToolPayloads: false }) ), aiV4: { ui: () => this.toAIV4UIMessages(this.inputPersisted.db()), core: () => aiV4UIMessagesToAIV4CoreMessages( this.toAIV4UIMessages(this.inputPersisted.db(), { transformToolPayloads: false }) ) } }; response = { db: () => this.messages.filter((m) => this.newResponseMessages.has(m)), v1: () => convertToV1Messages(this.response.db()), aiV5: { ui: () => this.toAIV5UIMessages(this.response.db()), model: () => aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(this.response.db(), { transformToolPayloads: false }), this.messages ).filter((m) => m.role === `tool` || m.role === `assistant`), modelContent: (stepNumber) => { if (typeof stepNumber === "number") { return StepContentExtractor.extractStepContent( this.response.aiV5.ui(), stepNumber, this.response.aiV5.stepContent ); } return this.response.aiV5.model().map(this.response.aiV5.stepContent).flat(); }, stepContent: (message) => { return StepContentExtractor.convertToStepContent( message, this.messages, () => this.response.aiV5.model().at(-1) ); } }, aiV6: { ui: () => this.toAIV6UIMessages(this.response.db()) }, aiV4: { ui: () => this.toAIV4UIMessages(this.response.db()), core: () => aiV4UIMessagesToAIV4CoreMessages(this.toAIV4UIMessages(this.response.db(), { transformToolPayloads: false })) } }; responsePersisted = { db: () => this.messages.filter((m) => this.newResponseMessagesPersisted.has(m)), aiV5: { model: () => aiV5UIMessagesToAIV5ModelMessages( this.toAIV5UIMessages(this.responsePersisted.db(), { transformToolPayloads: false }), this.messages ), ui: () => this.toAIV5UIMessages(this.responsePersisted.db()) }, aiV6: { ui: () => this.toAIV6UIMessages(this.responsePersisted.db()) }, /* @deprecated use list.getPersisted.response.aiV4.ui() */ ui: () => this.toAIV4UIMessages(this.responsePersisted.db()), aiV4: { ui: () => this.toAIV4UIMessages(this.responsePersisted.db()), core: () => aiV4UIMessagesToAIV4CoreMessages( this.toAIV4UIMessages(this.responsePersisted.db(), { transformToolPayloads: false }) ) } }; drainUnsavedMessages() { const messages = this.messages.filter((m) => this.newUserMessages.has(m) || this.newResponseMessages.has(m)); this.newUserMessages.clear(); this.newResponseMessages.clear(); return messages.map((message) => this.transformMessageForTranscript(message)); } transformToolStateDataForTranscript(data, phase) { if (!data || typeof data !== "object") { return data; } const stateData = data; const metadata = stateData.metadata ?? stateData.providerMetadata; const phaseTransform = getTransformedToolPayload(metadata, "transcript", phase); const inputTransform = getTransformedToolPayload(metadata, "transcript", "input-available"); const transformedArgs = phase === "approval" ? hasTransformedToolPayload(phaseTransform) ? phaseTransform.transformed : hasTransformedToolPayload(inputTransform) ? inputTransform.transformed : void 0 : hasTransformedToolPayload(inputTransform) ? inputTransform.transformed : hasTransformedToolPayload(phaseTransform) ? phaseTransform.transformed : void 0; const transformedSuspendPayload = phase === "suspend" && hasTransformedToolPayload(phaseTransform) ? phaseTransform.transformed : void 0; return { ...stateData, ...transformedArgs !== void 0 ? { args: transformedArgs } : {}, ...transformedSuspendPayload !== void 0 ? { suspendPayload: transformedSuspendPayload } : {} }; } transformMessageForTranscript(message) { if (message.content?.format !== 2 || !message.content.parts) { return message; } let changed = false; const transformedByToolCallId = /* @__PURE__ */ new Map(); const parts = message.content.parts.map((part) => { if (part.type === "tool-invocation" && part.toolInvocation) { const inputTransform = getTransformedToolPayload(part.providerMetadata, "transcript", "input-available"); const outputTransform = part.toolInvocation.state === "result" ? getTransformedToolPayload(part.providerMetadata, "transcript", "output-available") ?? getTransformedToolPayload(part.providerMetadata, "transcript", "error") : part.toolInvocation.state === "output-error" ? getTransformedToolPayload(part.providerMetadata, "transcript", "error") : void 0; if (!inputTransform && !outputTransform) { return part; } changed = true; const transformedArgs = hasTransformedToolPayload(inputTransform) ? inputTransform.transformed : part.toolInvocation.args; const transformedResult = part.toolInvocation.state === "result" ? hasTransformedToolPayload(outputTransform) ? outputTransform.transformed : part.toolInvocation.result : void 0; const transformedErrorText = part.toolInvocation.state === "output-error" ? hasTransformedToolPayload(outputTransform) ? outputTransform.transformed : part.toolInvocation.errorText : void 0; transformedByToolCallId.set(part.toolInvocation.toolCallId, { args: transformedArgs, ...part.toolInvocation.state === "result" ? { result: transformedResult } : {}, ...part.toolInvocation.state === "output-error" ? { errorText: transformedErrorText } : {} }); return { ...part, toolInvocation: { ...part.toolInvocation, args: transformedArgs, ...part.toolInvocation.state === "result" ? { result: transformedResult } : {}, ...part.toolInvocation.state === "output-error" ? { errorText: transformedErrorText } : {} } }; } if (part.type === "data-tool-call-suspended" || part.type === "data-tool-call-approval") { changed = true; return { ...part, data: this.transformToolStateDataForTranscript( part.data, part.type === "data-tool-call-suspended" ? "suspend" : "approval" ) }; } return part; }); const toolInvocations = message.content.toolInvocations?.map((invocation) => { const transformed = transformedByToolCallId.get(invocation.toolCallId); if (!transformed) { return invocation; } const invocationState = invocation.state; changed = true; return { ...invocation, ...transformed.args !== void 0 ? { args: transformed.args } : {}, ...invocation.state === "result" && transformed.result !== void 0 ? { result: transformed.result } : {}, ...invocationState === "output-error" && transformed.errorText !== void 0 ? { errorText: transformed.errorText } : {} }; }); const metadata = message.content.metadata && typeof message.content.metadata === "object" ? { ...message.content.metadata } : message.content.metadata; if (metadata && typeof metadata === "object") { for (const [key, phase] of [ ["suspendedTools", "suspend"], ["pendingToolApprovals", "approval"] ]) { const toolStates = metadata[key]; if (!toolStates || typeof toolStates !== "object") { continue; } changed = true; metadata[key] = Object.fromEntries( Object.entries(toolStates).map(([toolName, state]) => [ toolName, this.transformToolStateDataForTranscript(state, phase) ]) ); } } if (!changed) { return message; } return { ...message, content: { ...message.content, parts, ...toolInvocations ? { toolInvocations } : {}, ...metadata ? { metadata } : {} } }; } getEarliestUnsavedMessageTimestamp() { const unsavedMessages = this.messages.filter((m) => this.newUserMessages.has(m) || this.newResponseMessages.has(m)); if (unsavedMessages.length === 0) return void 0; return Math.min(...unsavedMessages.map((m) => new Date(m.createdAt).getTime())); } /** * Check if a message is a new user or response message that should be saved. * Checks by message ID to handle cases where the message object may be a copy. */ isNewMessage(messageOrId) { return this.stateManager.isNewMessage(messageOrId); } /** * Replace a tool-invocation part matching the given toolCallId with the * provided result part. Walks backwards through messages to find the match. * If the message was already persisted (e.g. as a memory message), it is * moved to the response source so it will be re-saved. * * @returns true if the tool call was found and updated, false otherwise. */ updateToolInvocation(inputPart, metadata) { if (!inputPart.toolInvocation?.toolCallId) { return false; } const toolCallId = inputPart.toolInvocation.toolCallId; for (let m = this.messages.length - 1; m >= 0; m--) { const msg = this.messages[m]; if (msg.role !== "assistant" || !msg.content?.parts) continue; for (let i = 0; i < msg.content.parts.length; i++) { const part = msg.content.parts[i]; if (part?.type === "tool-invocation" && part.toolInvocation?.toolCallId === toolCallId) { const originalPart = part; const inputPartWithMeta = inputPart; const mergedProviderMetadata = originalPart.providerMetadata !== void 0 || inputPartWithMeta.providerMetadata !== void 0 ? { ...originalPart.providerMetadata ?? {}, ...inputPartWithMeta.providerMetadata ?? {} } : void 0; msg.content.parts[i] = { ...inputPart, toolInvocation: { ...inputPart.toolInvocation, args: part.toolInvocation.args }, // Preserve providerExecuted from original call if not in result ...originalPart.providerExecuted !== void 0 && inputPartWithMeta.providerExecuted === void 0 ? { providerExecuted: originalPart.providerExecuted } : {}, ...mergedProviderMetadata !== void 0 ? { providerMetadata: mergedProviderMetadata } : {} }; this.lastCreatedAt = Math.max(this.lastCreatedAt || 0, Date.now()); this.updateLastCreatedAt(msg); const existingMeta = msg.content.metadata ?? {}; const incomingMeta = metadata ?? {}; const existingBgTasks = existingMeta.backgroundTasks; const incomingBgTasks = incomingMeta.backgroundTasks; msg.content.metadata = { ...existingMeta, ...incomingMeta, ...existingBgTasks || incomingBgTasks ? { backgroundTasks: { ...existingBgTasks ?? {}, ...incomingBgTasks ?? {} } } : {} }; if (!this.stateManager.isResponseMessage(msg)) { this.stateManager.removeMessage(msg); this.stateManager.addToSource(msg, "response"); } return true; } } } this.logger?.warn(`updateToolInvocation: no matching tool call found for toolCallId=${toolCallId}`); return false; } /** * Append a `step-start` boundary to the last assistant message. * This marks the beginning of a new loop iteration so that * `convertToModelMessages` splits sequential tool-call turns into * separate message blocks instead of collapsing them into one. * * Respects sealed messages (post-observation) — if the last assistant * message is sealed, the step-start is not added. * * If the message was loaded from memory it is moved to the response * source so the updated content is re-saved. */ stepStart() { const lastMsg = this.messages[this.messages.length - 1]; if (!lastMsg || lastMsg.role !== "assistant" || !lastMsg.content?.parts) { return false; } if (MessageMerger.isSealed(lastMsg)) { return false; } const lastPart = lastMsg.content.parts[lastMsg.content.parts.length - 1]; if (lastPart?.type === "step-start") { return false; } lastMsg.content.parts.push(stampPart({ type: "step-start" })); if (!this.stateManager.isResponseMessage(lastMsg)) { this.stateManager.removeMessage(lastMsg); this.stateManager.addToSource(lastMsg, "response"); } return true; } markResponseMessageBoundary(messageId) { const message = messageId ? this.messages.find((message2) => message2.id === messageId) : [...this.messages].reverse().find((message2) => message2.role === "assistant"); if (!message || message.role !== "assistant") { return false; } message.content.metadata = { ...message.content.metadata ?? {}, mastra: { ...message.content.metadata?.mastra ?? {}, responseBoundary: true } }; if (!this.stateManager.isResponseMessage(message)) { this.stateManager.removeMessage(message); this.stateManager.addToSource(message, "response"); } return true; } enrichLastStepStart(model) { const lastMsg = this.messages[this.messages.length - 1]; if (!lastMsg || lastMsg.role !== "assistant" || !lastMsg.content?.parts) { return false; } if (MessageMerger.isSealed(lastMsg)) { return false; } for (let i = lastMsg.content.parts.length - 1; i >= 0; i--) { const part = lastMsg.content.parts[i]; if (part?.type !== "step-start") { continue; } if (part.model) { return false; } part.model = model; if (!this.stateManager.isResponseMessage(lastMsg)) { this.stateManager.removeMessage(lastMsg); this.stateManager.addToSource(lastMsg, "response"); } return true; } return false; } getSystemMessages(tag) { if (tag) { return this.taggedSystemMessages[tag] || []; } return this.systemMessages; } /** * Get all system messages (both tagged and untagged) * @returns Array of all system messages */ getAllSystemMessages() { return [...this.systemMessages, ...Object.values(this.taggedSystemMessages).flat()]; } /** * Clear system messages, optionally for a specific tag * @param tag - If provided, only clears messages with this tag. Otherwise clears untagged messages. */ clearSystemMessages(tag) { if (tag) { delete this.taggedSystemMessages[tag]; } else { this.systemMessages = []; } return this; } /** * Replace the untagged system message bucket with the provided array while * leaving tagged system message buckets (owned by other processors) intact. * @param messages - Array of system messages to set as untagged */ replaceAllSystemMessages(messages) { this.systemMessages = []; for (const message of messages) { if (message.role !== "system") continue; this.systemMessages.push(message); } return this; } addSystem(messages, tag) { if (!messages) return this; for (const message of Array.isArray(messages) ? messages : [messages]) { this.addOneSystem(message, tag); } return this; } addOneSystem(message, tag) { const coreMessage = systemMessageToAIV4Core(message); if (coreMessage.role !== `system`) { throw new Error( `Expected role "system" but saw ${coreMessage.role} for message ${JSON.stringify(coreMessage, null, 2)}` ); } if (tag && !this.isDuplicateSystem(coreMessage, tag)) { this.taggedSystemMessages[tag] ||= []; this.taggedSystemMessages[tag].push(coreMessage); if (this.isRecording) { this.recordedEvents.push({ type: "addSystem", tag, message: coreMessage }); } } else if (!tag && !this.isDuplicateSystem(coreMessage)) { this.systemMessages.push(coreMessage); if (this.isRecording) { this.recordedEvents.push({ type: "addSystem", message: coreMessage }); } } } isDuplicateSystem(message, tag) { if (tag) { if (!this.taggedSystemMessages[tag]) return false; return this.taggedSystemMessages[tag].some( (m) => CacheKeyGenerator.fromAIV4CoreMessageContent(m.content) === CacheKeyGenerator.fromAIV4CoreMessageContent(message.content) ); } return this.systemMessages.some( (m) => CacheKeyGenerator.fromAIV4CoreMessageContent(m.content) === CacheKeyGenerator.fromAIV4CoreMessageContent(message.content) ); } getMessageById(id) { return this.messages.find((m) => m.id === id); } shouldReplaceMessage(message) { if (!this.messages.length) return { exists: false }; if (!(`id` in message) || !message?.id) { return { exists: false }; } const existingMessage = this.getMessageById(message.id); if (!existingMessage) return { exists: false }; return { exists: true, shouldReplace: !messagesAreEqual(existingMessage, message), id: existingMessage.id }; } addOne(message, messageSource) { if ((!(`content` in message) || !message.content && // allow empty strings typeof message.content !== "string") && (!(`parts` in message) || !message.parts)) { throw new chunkXSOONORA_cjs.MastraError({ id: "INVALID_MESSAGE_CONTENT", domain: chunkXSOONORA_cjs.ErrorDomain.AGENT, category: chunkXSOONORA_cjs.ErrorCategory.USER, text: `Message with role "${message.role}" must have either a 'content' property (string or array) or a 'parts' property (array) that is not empty, null, or undefined. Received message: ${JSON.stringify(message, null, 2)}`, details: { role: message.role, messageSource, hasContent: "content" in message, hasParts: "parts" in message } }); } if (message.role === `system`) { if (messageSource === `memory`) return null; const isSupportedSystemFormat = TypeDetector.isAIV4CoreMessage(message) || TypeDetector.isAIV6CoreMessage(message) || TypeDetector.isAIV5CoreMessage(message) || TypeDetector.isMastraDBMessage(message); if (isSupportedSystemFormat) { return this.addSystem(message); } throw new chunkXSOONORA_cjs.MastraError({ id: "INVALID_SYSTEM_MESSAGE_FORMAT", domain: chunkXSOONORA_cjs.ErrorDomain.AGENT, category: chunkXSOONORA_cjs.ErrorCategory.USER, text: `Invalid system message format. System messages must be CoreMessage format with 'role' and 'content' properties. The content should be a string or valid content array.`, details: { messageSource, receivedMessage: JSON.stringify(message, null, 2) } }); } const messageV2 = inputToMastraDBMessage(message, messageSource, this.createAdapterContext()); const signalMetadata = messageV2.role === "signal" ? messageV2.content.metadata?.signal : void 0; if (messageSource === "input" && messageV2.role === "signal" && !signalMetadata?.acceptedAt) { const acceptedAt = signalMetadata?.createdAt ?? messageV2.createdAt.toISOString(); messageV2.createdAt = this.generateCreatedAt(messageSource, messageV2.createdAt); messageV2.content.metadata = { ...messageV2.content.metadata, signal: { ...signalMetadata, createdAt: messageV2.createdAt.toISOString(), acceptedAt } }; } const { exists, shouldReplace, id } = this.shouldReplaceMessage(messageV2); const latestSealedIndex = this.messages.findLastIndex((message2) => MessageMerger.isSealed(message2)); const latestMessage = this.messages.at(-1); const latestMessageIndex = this.messages.length - 1; const latestMessageIsAfterSealedBoundary = latestSealedIndex === -1 || latestMessageIndex > latestSealedIndex; if (messageSource === `memory`) { for (const existingMessage of this.messages) { if (messagesAreEqual(existingMessage, messageV2)) { return; } } } const replacementTarget = exists && id ? this.messages.find((m) => m.id === id) : void 0; const hasSealedReplacementTarget = !!replacementTarget && MessageMerger.isSealed(replacementTarget); const isLatestFromMemory = latestMessage ? this.memoryMessages.has(latestMessage) : false; const shouldMerge = latestMessageIsAfterSealedBoundary && !hasSealedReplacementTarget && MessageMerger.shouldMerge(latestMessage, messageV2, messageSource, isLatestFromMemory, this._agentNetworkAppend); if (shouldMerge && latestMessage) { MessageMerger.merge(latestMessage, messageV2); this.updateLastCreatedAt(latestMessage); this.pushMessageToSource(latestMessage, messageSource); } else { let existingIndex = -1; if (shouldReplace) { existingIndex = this.messages.findIndex((m) => m.id === id); } const existingMessage = existingIndex !== -1 && this.messages[existingIndex]; if (shouldReplace && existingMessage) { const existingIsAtOrBeforeSealedBoundary = latestSealedIndex !== -1 && existingIndex <= latestSealedIndex; if (MessageMerger.isSealed(existingMessage)) { const existingParts = existingMessage.content?.parts || []; let sealedPartCount = 0; for (let i = existingParts.length - 1; i >= 0; i--) { const part = existingParts[i]; if (part?.metadata?.mastra?.sealedAt) { sealedPartCount = i + 1; break; } } if (sealedPartCount === 0) { sealedPartCount = existingParts.length; } const incomingParts = messageV2.content.parts; let newParts; if (incomingParts.length <= sealedPartCount) { if (messagesAreEqual(existingMessage, messageV2)) { return this; } newParts = incomingParts; } else { newParts = incomingParts.slice(sealedPartCount); } if (newParts.length > 0) { messageV2.id = this.generateMessageId?.({ idType: "message", source: "memory" }) ?? uuid.v4(); messageV2.content.parts = newParts; if (messageV2.createdAt <= existingMessage.createdAt) { messageV2.createdAt = new Date(existingMessage.createdAt.getTime() + 1); } this.messages.push(messageV2); } } else if (existingIsAtOrBeforeSealedBoundary) { messageV2.id = this.generateMessageId?.({ idType: "message", source: "memory" }) ?? uuid.v4(); if (messageV2.createdAt <= existingMessage.createdAt) { messageV2.createdAt = new Date(existingMessage.createdAt.getTime() + 1); } this.messages.push(messageV2); } else { const isExistingFromMemory = this.memoryMessages.has(existingMessage); const shouldMergeIntoExisting = MessageMerger.shouldMerge( existingMessage, messageV2, messageSource, isExistingFromMemory, this._agentNetworkAppend ); if (shouldMergeIntoExisting) { MessageMerger.merge(existingMessage, messageV2); this.updateLastCreatedAt(existingMessage); this.pushMessageToSource(existingMessage, messageSource); this.messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()); return this; } this.messages[existingIndex] = messageV2; } } else if (!exists) { this.messages.push(messageV2); } this.pushMessageToSource(messageV2, messageSource); } for (const storedMessage of this.messages) { this.updateLastCreatedAt(storedMessage); } this.messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()); return this; } pushMessageToSource(messageV2, messageSource) { this.stateManager.addToSource(messageV2, messageSource); } lastCreatedAt; updateLastCreatedAt(message) { this.lastCreatedAt = Math.max(this.lastCreatedAt || 0, message.createdAt.getTime()); } // this makes sure messages added in order will always have a date atleast 1ms apart. generateCreatedAt(messageSource, start) { const startDate = start instanceof Date ? start : typeof start === "string" || typeof start === "number" ? new Date(start) : void 0; if (startDate && !this.lastCreatedAt) { this.lastCreatedAt = startDate.getTime(); return startDate; } if (startDate && messageSource === `memory`) { return startDate; } const now = /* @__PURE__ */ new Date(); const nowTime = startDate?.getTime() || now.getTime(); const lastTime = this.lastCreatedAt || 0; if (nowTime <= lastTime) { const newDate = new Date(lastTime + 1); this.lastCreatedAt = newDate.getTime(); return newDate; } this.lastCreatedAt = nowTime; return startDate ?? now; } newMessageId(role) { if (this.generateMessageId) { return this.generateMessageId({ idType: "message", source: "agent", threadId: this.memoryInfo?.threadId, resourceId: this.memoryInfo?.resourceId, role }); } return uuid.v4(); } createAdapterContext() { return { memoryInfo: this.memoryInfo, newMessageId: () => this.newMessageId(), generateCreatedAt: (messageSource, start) => this.generateCreatedAt(messageSource, start), dbMessages: this.messages }; } }; // src/agent/message-list/utils/convert-messages.ts var MessageConverter = class { messageList; constructor(messages) { this.messageList = new MessageList(); this.messageList.add(messages, "memory"); } to(format) { switch (format) { // Old format keys (backward compatibility) case "Mastra.V2": return this.messageList.get.all.db(); case "AIV4.UI": return this.messageList.get.all.aiV4.ui(); case "AIV4.Core": return this.messageList.get.all.aiV4.core(); case "AIV5.UI": return this.messageList.get.all.aiV5.ui(); case "AIV5.Model": return this.messageList.get.all.aiV5.model(); case "AIV6.UI": return this.messageList.get.all.aiV6.ui(); default: throw new Error(`Unsupported output format: ${format}`); } } }; function convertMessages(messages) { return new MessageConverter(messages); } exports.AIV4Adapter = AIV4Adapter; exports.AIV5Adapter = AIV5Adapter; exports.AIV6Adapter = AIV6Adapter; exports.CacheKeyGenerator = CacheKeyGenerator; exports.DefaultGeneratedFile = DefaultGeneratedFile; exports.DefaultGeneratedFileWithType = DefaultGeneratedFileWithType; exports.MessageList = MessageList; exports.MessageMerger = MessageMerger; exports.MessageStateManager = MessageStateManager; exports.TypeDetector = TypeDetector; exports.aiV4CoreMessageToV1PromptMessage = aiV4CoreMessageToV1PromptMessage; exports.aiV5ModelMessageToV2PromptMessage = aiV5ModelMessageToV2PromptMessage; exports.convertMessages = convertMessages; exports.coreContentToString = coreContentToString; exports.createMessageSignal = createMessageSignal; exports.createSignal = createSignal; exports.dataPartToSignal = dataPartToSignal; exports.ensureAnthropicCompatibleMessages = ensureAnthropicCompatibleMessages; exports.ensureGeminiCompatibleMessages = ensureGeminiCompatibleMessages; exports.findToolCallArgs = findToolCallArgs; exports.getOpenAIReasoningItemId = getOpenAIReasoningItemId; exports.getResponseProviderItemId = getResponseProviderItemId; exports.getResponseProviderItemIdFromPart = getResponseProviderItemIdFromPart; exports.getResponseProviderItemIds = getResponseProviderItemIds; exports.getResponseProviderItemKey = getResponseProviderItemKey; exports.getResponseProviderItemKeys = getResponseProviderItemKeys; exports.getTransformedToolPayload = getTransformedToolPayload; exports.hasOpenAIReasoningItemId = hasOpenAIReasoningItemId; exports.hasResponseProviderItemId = hasResponseProviderItemId; exports.hasTransformedToolPayload = hasTransformedToolPayload; exports.isCreatedAgentSignal = isCreatedAgentSignal; exports.isMastraSignalMessage = isMastraSignalMessage; exports.mastraDBMessageToSignal = mastraDBMessageToSignal; exports.messagesAreEqual = messagesAreEqual; exports.normalizeToolPayloadTransformPolicy = normalizeToolPayloadTransformPolicy; exports.resolveDeliveryAttributes = resolveDeliveryAttributes; exports.sanitizeOrphanedToolPairs = sanitizeOrphanedToolPairs; exports.sanitizeToolName = sanitizeToolName; exports.signalToDataPartFormat = signalToDataPartFormat; exports.signalToMastraDBMessage = signalToMastraDBMessage; exports.signalToMessage = signalToMessage; exports.signalToXmlMarkup = signalToXmlMarkup; exports.stableStringify = stableStringify; exports.transformToolPayloadForTargets = transformToolPayloadForTargets; exports.withToolPayloadTransformMetadata = withToolPayloadTransformMetadata; exports.withToolPayloadTransformProviderMetadata = withToolPayloadTransformProviderMetadata; //# sourceMappingURL=chunk-2TATDSHU.cjs.map //# sourceMappingURL=chunk-2TATDSHU.cjs.map