feat(harness): file attachment support with filename preservation and text file handling#13574
Conversation
🦋 Changeset detectedLatest commit: 434fb31 The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@saddlepaddle is attempting to deploy a commit to the Mastra Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughReplaces harness Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
698ec6d to
67e0323
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/core/src/agent/message-list/adapters/AIV5Adapter.ts (2)
425-433:⚠️ Potential issue | 🟠 MajorAvoid cast-based filename handling; keep it strictly typed.
The
(p as { filename?: string })and(v2FilePart as Record<string, unknown>)pattern bypasses strict typing and makes filename propagation fragile. Also, truthy checks can unintentionally drop empty-string filenames.💡 Proposed fix
@@ - if (p.type === 'file') { + if (p.type === 'file') { + const filename = 'filename' in p ? (p as { filename?: string }).filename : undefined; return { type: 'file' as const, mimeType: p.mediaType, data: p.url || '', providerMetadata: p.providerMetadata, - ...((p as { filename?: string }).filename ? { filename: (p as { filename?: string }).filename } : {}), + ...(filename !== undefined ? { filename } : {}), }; } @@ - const v2FilePart: MastraDBMessage['content']['parts'][number] = { + const v2FilePart: MastraDBMessage['content']['parts'][number] & { filename?: string } = { type: 'file', data: fileData, mimeType, }; @@ - if ((filePart as { filename?: string }).filename) { - (v2FilePart as Record<string, unknown>).filename = (filePart as { filename?: string }).filename; + if ('filename' in filePart && filePart.filename !== undefined) { + v2FilePart.filename = filePart.filename; }As per coding guidelines,
**/*.{ts,tsx}: Use TypeScript with strict type checking for all packages in the monorepo.Also applies to: 667-677
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/agent/message-list/adapters/AIV5Adapter.ts` around lines 425 - 433, The file-handling branch in AIV5Adapter.ts uses unsafe casts ((p as { filename?: string }) and elsewhere) and a truthy check that drops empty-string filenames; replace these with a proper type-narrowing guard and explicit filename presence check: ensure the file-part union/interface used by the mapping (the object inspected when p.type === 'file') declares filename?: string, then test e.g. 'filename' in p && typeof p.filename === "string" to include filename even when empty, and remove the ad-hoc casts ((p as {...}) and (v2FilePart as Record<string, unknown>)); apply the same change to the other similar block around the later file handling (lines ~667-677) so filename propagation is strictly typed and not lost for empty strings.
196-218:⚠️ Potential issue | 🟠 Major
filenameis not round-tripped back to UI file parts.
fromUIMessage/fromModelMessagecan storefilename, buttoUIMessagedrops it in both file conversion branches. This breaks the PR goal of filename persistence after reload/history reconstruction.💡 Proposed fix
@@ - if (categorized.type === 'url' && typeof part.data === 'string') { + if (categorized.type === 'url' && typeof part.data === 'string') { + const filename = 'filename' in part ? (part as { filename?: string }).filename : undefined; const v5UIPart: AIV5Type.FileUIPart = { type: 'file' as const, url: part.data, mediaType: categorized.mimeType || 'image/png', + ...(filename !== undefined ? { filename } : {}), }; @@ - const v5UIPart: AIV5Type.FileUIPart = { + const filename = 'filename' in part ? (part as { filename?: string }).filename : undefined; + const v5UIPart: AIV5Type.FileUIPart = { type: 'file' as const, url: dataUri, mediaType: finalMimeType, + ...(filename !== undefined ? { filename } : {}), };Also applies to: 246-255
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/agent/message-list/adapters/AIV5Adapter.ts` around lines 196 - 218, The UI file-part conversion in AIV5Adapter (inside the file-to-UI conversion that uses categorizeFileData and constructs AIV5Type.FileUIPart objects) drops the original filename; preserve and propagate part.filename into the constructed v5UIPart in both branches (the 'url' branch that builds {type:'file', url,...} and the alternate/raw branch that creates a file UI part), and also keep existing providerMetadata; update any other conversion branch that handles file parts (the second block noted in the diff) to copy part.filename into the resulting UI file part so filenames round-trip through toUIMessage.
🧹 Nitpick comments (1)
packages/core/src/harness/harness.ts (1)
1327-1332: Preservefilenamewhen normalizingimageparts tofile.The normalization branch keeps data/media type but drops filename metadata if it exists on the source part.
♻️ Proposed refactor
content.push({ type: 'file', data: imgData, mediaType: (part as { mimeType?: string }).mimeType ?? (part as { mediaType?: string }).mediaType ?? 'image/png', + ...((part as { filename?: string }).filename ? { filename: (part as { filename?: string }).filename } : {}), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/harness/harness.ts` around lines 1327 - 1332, The normalization that converts image parts into file parts (the content.push block that creates objects with type: 'file', data: imgData, mediaType: ...) currently drops any filename metadata from the original part; update that creation to copy the filename through (e.g. add filename: (part as { filename?: string }).filename ?? undefined) so the resulting file object preserves the source filename when present (ensure you reference the same `part` and `imgData` variables used in the snippet).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/agent/message-list/adapters/AIV4Adapter.ts`:
- Around line 432-434: The stored file `filename` is not propagated when
converting stored `file` parts into UI `experimental_attachments` in the
AIV4Adapter.toUIMessage conversion, causing filenames to be lost on hydration;
update the code that constructs `experimental_attachments` (within toUIMessage)
to copy the `filename` from the stored part (`aiV4Part` / `part`) into the
attachment object (add a filename field on the built experimental_attachments
entry) for all file-part construction sites (the blocks around lines 88-109 and
the other similar blocks at the ranges you noted) so the round-trip preserves
filename metadata.
In `@packages/core/src/harness/harness.ts`:
- Around line 1099-1107: The current logic only handles ;base64 data URIs and
leaves plain data:text/... URIs as raw strings; update the parsing for f.data so
it matches a full data URI (e.g.
/^data:([^;]+)(?:;charset=[^;]+)?(?:;base64)?,(.*)$/i) and capture the payload;
if the captured group indicates ;base64 then decode with
Buffer.from(...,'base64'), otherwise URL-decode the payload (decodeURIComponent,
handling plus-to-space if needed) and set textContent accordingly; adjust the
existing base64Match usage and textContent assignment in the same block to use
this new match and branching logic.
---
Outside diff comments:
In `@packages/core/src/agent/message-list/adapters/AIV5Adapter.ts`:
- Around line 425-433: The file-handling branch in AIV5Adapter.ts uses unsafe
casts ((p as { filename?: string }) and elsewhere) and a truthy check that drops
empty-string filenames; replace these with a proper type-narrowing guard and
explicit filename presence check: ensure the file-part union/interface used by
the mapping (the object inspected when p.type === 'file') declares filename?:
string, then test e.g. 'filename' in p && typeof p.filename === "string" to
include filename even when empty, and remove the ad-hoc casts ((p as {...}) and
(v2FilePart as Record<string, unknown>)); apply the same change to the other
similar block around the later file handling (lines ~667-677) so filename
propagation is strictly typed and not lost for empty strings.
- Around line 196-218: The UI file-part conversion in AIV5Adapter (inside the
file-to-UI conversion that uses categorizeFileData and constructs
AIV5Type.FileUIPart objects) drops the original filename; preserve and propagate
part.filename into the constructed v5UIPart in both branches (the 'url' branch
that builds {type:'file', url,...} and the alternate/raw branch that creates a
file UI part), and also keep existing providerMetadata; update any other
conversion branch that handles file parts (the second block noted in the diff)
to copy part.filename into the resulting UI file part so filenames round-trip
through toUIMessage.
---
Nitpick comments:
In `@packages/core/src/harness/harness.ts`:
- Around line 1327-1332: The normalization that converts image parts into file
parts (the content.push block that creates objects with type: 'file', data:
imgData, mediaType: ...) currently drops any filename metadata from the original
part; update that creation to copy the filename through (e.g. add filename:
(part as { filename?: string }).filename ?? undefined) so the resulting file
object preserves the source filename when present (ensure you reference the same
`part` and `imgData` variables used in the snippet).
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
mastracode/src/__tests__/create-auth-storage.test.tsmastracode/src/agents/__tests__/tools.test.tsmastracode/src/agents/tools.tsmastracode/src/index.tsmastracode/src/tui/mastra-tui.tspackages/core/src/agent/message-list/adapters/AIV4Adapter.tspackages/core/src/agent/message-list/adapters/AIV5Adapter.tspackages/core/src/harness/harness.tspackages/core/src/harness/types.ts
| // Decode data URI to plain text | ||
| const base64Match = f.data.match(/^data:[^;]*;base64,(.*)$/); | ||
| if (base64Match) { | ||
| try { | ||
| textContent = Buffer.from(base64Match[1]!, 'base64').toString('utf-8'); | ||
| } catch { | ||
| // Fall through with raw data | ||
| } | ||
| } |
There was a problem hiding this comment.
Decode non-base64 data URIs for text attachments.
Line 1100 only decodes ;base64 URIs. For data:text/plain,... payloads, the raw URI string is forwarded as content instead of decoded text.
💡 Proposed fix
- // Decode data URI to plain text
- const base64Match = f.data.match(/^data:[^;]*;base64,(.*)$/);
- if (base64Match) {
- try {
- textContent = Buffer.from(base64Match[1]!, 'base64').toString('utf-8');
- } catch {
- // Fall through with raw data
- }
- }
+ // Decode data URI to plain text (supports base64 and url-encoded forms)
+ const dataUriMatch = f.data.match(/^data:([^,]*),(.*)$/s);
+ if (dataUriMatch) {
+ const meta = dataUriMatch[1] ?? '';
+ const payload = dataUriMatch[2] ?? '';
+ try {
+ textContent = /;base64/i.test(meta)
+ ? Buffer.from(payload, 'base64').toString('utf-8')
+ : decodeURIComponent(payload);
+ } catch {
+ // Fall through with raw data
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Decode data URI to plain text | |
| const base64Match = f.data.match(/^data:[^;]*;base64,(.*)$/); | |
| if (base64Match) { | |
| try { | |
| textContent = Buffer.from(base64Match[1]!, 'base64').toString('utf-8'); | |
| } catch { | |
| // Fall through with raw data | |
| } | |
| } | |
| // Decode data URI to plain text (supports base64 and url-encoded forms) | |
| const dataUriMatch = f.data.match(/^data:([^,]*),(.*)$/s); | |
| if (dataUriMatch) { | |
| const meta = dataUriMatch[1] ?? ''; | |
| const payload = dataUriMatch[2] ?? ''; | |
| try { | |
| textContent = /;base64/i.test(meta) | |
| ? Buffer.from(payload, 'base64').toString('utf-8') | |
| : decodeURIComponent(payload); | |
| } catch { | |
| // Fall through with raw data | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/core/src/harness/harness.ts` around lines 1099 - 1107, The current
logic only handles ;base64 data URIs and leaves plain data:text/... URIs as raw
strings; update the parsing for f.data so it matches a full data URI (e.g.
/^data:([^;]+)(?:;charset=[^;]+)?(?:;base64)?,(.*)$/i) and capture the payload;
if the captured group indicates ;base64 then decode with
Buffer.from(...,'base64'), otherwise URL-decode the payload (decodeURIComponent,
handling plus-to-space if needed) and set textContent accordingly; adjust the
existing base64Match usage and textContent assignment in the same block to use
this new match and branching logic.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
packages/core/src/harness/harness.ts (1)
1099-1107:⚠️ Potential issue | 🟡 MinorDecode non-base64 text data URIs before building text parts.
Line 1100 currently decodes only
;base64payloads.data:text/plain,...and similar URL-encoded forms are passed through undecoded.Suggested fix
- // Decode data URI to plain text - const base64Match = f.data.match(/^data:[^;]*;base64,(.*)$/); - if (base64Match) { - try { - textContent = Buffer.from(base64Match[1]!, 'base64').toString('utf-8'); - } catch { - // Fall through with raw data - } - } + // Decode data URI to plain text (base64 + url-encoded forms) + const dataUriMatch = f.data.match(/^data:([^,]*),(.*)$/s); + if (dataUriMatch) { + const meta = dataUriMatch[1] ?? ''; + const payload = dataUriMatch[2] ?? ''; + try { + textContent = /;base64/i.test(meta) + ? Buffer.from(payload, 'base64').toString('utf-8') + : decodeURIComponent(payload.replace(/\+/g, '%20')); + } catch { + // Fall through with raw data + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/harness/harness.ts` around lines 1099 - 1107, The current block only decodes data URIs with ";base64" and leaves URL-encoded payloads like "data:text/plain,Hello%20World" undecoded; update the logic around f.data and textContent to handle non-base64 data URIs too by matching a non-base64 pattern (e.g. /^data:[^,]*,(.*)$/), and when base64 is not present call decodeURIComponent on the captured payload (with a safe try/catch) to set textContent; keep the existing Buffer.from(...,'base64') branch for the ;base64 case and fall back to the raw data only if both decoding attempts fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/core/src/harness/harness.ts`:
- Around line 1099-1107: The current block only decodes data URIs with ";base64"
and leaves URL-encoded payloads like "data:text/plain,Hello%20World" undecoded;
update the logic around f.data and textContent to handle non-base64 data URIs
too by matching a non-base64 pattern (e.g. /^data:[^,]*,(.*)$/), and when base64
is not present call decodeURIComponent on the captured payload (with a safe
try/catch) to set textContent; keep the existing Buffer.from(...,'base64')
branch for the ;base64 case and fall back to the raw data only if both decoding
attempts fail.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
mastracode/src/tui/mastra-tui.tspackages/core/src/agent/message-list/adapters/AIV4Adapter.tspackages/core/src/agent/message-list/adapters/AIV5Adapter.tspackages/core/src/harness/harness.tspackages/core/src/harness/types.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/core/src/agent/message-list/adapters/AIV5Adapter.ts
- packages/core/src/harness/types.ts
- packages/core/src/agent/message-list/adapters/AIV4Adapter.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.changeset/file-attachment-support.md:
- Around line 6-11: Add a brief public API example and migration note showing
how callers should change sendMessage(images: ...) to sendMessage(files: ...),
include a before/after snippet that demonstrates preserving filename and text
decoding (e.g., passing {filename, mimeType, data} in files) and mention that
HarnessMessageContent now accepts type "file" so convertToHarnessMessage will
round-trip file parts; reference sendMessage, images -> files,
HarnessMessageContent, convertToHarnessMessage, AIV4Adapter, and AIV5Adapter so
readers know the adapters preserve filename and text-based files are decoded to
text parts rather than binary.
- Around line 8-11: Update the changeset bullets to be user-facing: replace
internal references (AIV4Adapter, AIV5Adapter, HarnessMessageContent,
convertToHarnessMessage) with outcome-focused language describing what users can
do and what changed — e.g., mention that sendMessage now accepts files (renamed
from images) of any type and preserves original filenames, text/* and
application/json files are stored/handled as readable text rather than binary,
and the new file message type enables proper round-tripping — keep references to
internal symbols only for maintainers if necessary but primarily state the
visible behaviors and benefits to users.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
.changeset/file-attachment-support.md (1)
26-26: 🛠️ Refactor suggestion | 🟠 MajorMake the bullet more user-facing; avoid focusing on internal type names.
Line 26 mentions
HarnessMessageContent(an internal type) rather than emphasizing the user outcome. Reword to focus on what developers experience.♻️ Suggested rewording
-- `HarnessMessageContent` now includes a `file` type, so file parts round-trip correctly through message history +- File attachments are preserved correctly in message history and remain accessible after page reloadAs per coding guidelines: "Highlight outcomes! What does change for the end user? Do not focus on internal implementation details."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.changeset/file-attachment-support.md at line 26, Summary: The bullet references the internal type HarnessMessageContent and should be reworded to focus on the user-facing outcome. Replace the technical mention of HarnessMessageContent and the `file` type with a developer-facing outcome sentence such as: "Message parts can now include file attachments, so files are preserved and round-trip correctly through message history." Update the bullet text to this user-focused phrasing and remove or avoid mentioning the internal type name HarnessMessageContent or the literal `file` type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In @.changeset/file-attachment-support.md:
- Line 26: Summary: The bullet references the internal type
HarnessMessageContent and should be reworded to focus on the user-facing
outcome. Replace the technical mention of HarnessMessageContent and the `file`
type with a developer-facing outcome sentence such as: "Message parts can now
include file attachments, so files are preserved and round-trip correctly
through message history." Update the bullet text to this user-focused phrasing
and remove or avoid mentioning the internal type name HarnessMessageContent or
the literal `file` type.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/core/src/harness/harness.ts (1)
1099-1107:⚠️ Potential issue | 🟡 MinorHandle non-base64
data:URIs for text attachments.Line 1100 only parses
;base64payloads.data:text/plain,...or other URL-encodeddata:payloads will be forwarded as raw URI text instead of decoded content.💡 Suggested fix
- // Decode data URI to plain text - const base64Match = f.data.match(/^data:[^;]*;base64,(.*)$/); - if (base64Match) { - try { - textContent = Buffer.from(base64Match[1]!, 'base64').toString('utf-8'); - } catch { - // Fall through with raw data - } - } + // Decode data URI to plain text (base64 + URL-encoded forms) + const dataUriMatch = f.data.match(/^data:([^,]*),(.*)$/s); + if (dataUriMatch) { + const meta = dataUriMatch[1] ?? ''; + const payload = dataUriMatch[2] ?? ''; + try { + textContent = /;base64/i.test(meta) + ? Buffer.from(payload, 'base64').toString('utf-8') + : decodeURIComponent(payload.replace(/\+/g, '%20')); + } catch { + // Fall through with raw data + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/harness/harness.ts` around lines 1099 - 1107, The current data URI handling only decodes base64 payloads (using f.data.match(/^data:[^;]*;base64,(.*)$/)), so non-base64 data URIs like data:text/plain,Hello%20World are left as raw URIs; update the parsing in the same block that sets textContent to also match general data URIs (e.g., /^data:([^,]*),(.*)$/), and when the media-type part does not include ";base64" decode the payload part with decodeURIComponent (after replacing '+' with ' ') to produce the plain text; keep the existing base64 branch (Buffer.from(...,'base64').toString('utf-8')) and fall back to the original raw f.data only if decoding fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/harness/harness.ts`:
- Around line 1327-1333: The normalization drops the source part's filename when
converting an image to a file: in the branch that calls content.push({ type:
'file', data: imgData, mediaType: ... }) ensure the original part.filename (if
present) is preserved by copying it into the resulting file object (either as
data.filename or a top-level filename property on the pushed object). Update the
content.push call that constructs the file entry (and the imgData object if
that's where filenames belong) to set filename = (part as any).filename when
available, keeping existing mediaType handling intact.
---
Duplicate comments:
In `@packages/core/src/harness/harness.ts`:
- Around line 1099-1107: The current data URI handling only decodes base64
payloads (using f.data.match(/^data:[^;]*;base64,(.*)$/)), so non-base64 data
URIs like data:text/plain,Hello%20World are left as raw URIs; update the parsing
in the same block that sets textContent to also match general data URIs (e.g.,
/^data:([^,]*),(.*)$/), and when the media-type part does not include ";base64"
decode the payload part with decodeURIComponent (after replacing '+' with ' ')
to produce the plain text; keep the existing base64 branch
(Buffer.from(...,'base64').toString('utf-8')) and fall back to the original raw
f.data only if decoding fails.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
mastracode/src/tui/mastra-tui.tspackages/core/src/harness/harness.tspackages/core/src/harness/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/core/src/harness/types.ts
- mastracode/src/tui/mastra-tui.ts
| content.push({ | ||
| type: 'file', | ||
| data: imgData, | ||
| mediaType: | ||
| (part as { mimeType?: string }).mimeType ?? (part as { mediaType?: string }).mediaType ?? 'image/png', | ||
| }); | ||
| break; |
There was a problem hiding this comment.
Preserve filename when normalizing image parts to file.
This branch converts image to file but drops filename if it exists on the source part, which can still lose metadata in history normalization paths.
💡 Suggested fix
content.push({
type: 'file',
data: imgData,
mediaType:
(part as { mimeType?: string }).mimeType ?? (part as { mediaType?: string }).mediaType ?? 'image/png',
+ ...((part as { filename?: string }).filename ? { filename: (part as { filename?: string }).filename } : {}),
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/core/src/harness/harness.ts` around lines 1327 - 1333, The
normalization drops the source part's filename when converting an image to a
file: in the branch that calls content.push({ type: 'file', data: imgData,
mediaType: ... }) ensure the original part.filename (if present) is preserved by
copying it into the resulting file object (either as data.filename or a
top-level filename property on the pushed object). Update the content.push call
that constructs the file entry (and the imgData object if that's where filenames
belong) to set filename = (part as any).filename when available, keeping
existing mediaType handling intact.
… text file handling - Rename images→files in sendMessage API to support all file types - Use AI SDK FilePart shape (mimeType instead of mediaType) for model compatibility - Preserve filename field through AIV4Adapter and AIV5Adapter when storing file parts to DB - Decode text-based files (text/*, application/json) to text content parts instead of sending as binary file parts, which models cannot process
53f2c85 to
81274b8
Compare
…s step scoring guard - message-list tests: add expected `filename` property after #13574 introduced filename preservation in AIV4Adapter.fromCoreMessage() - evals/run: fix guard that required `stepResult.payload`, which the workflow engine strips when it matches previous output. Use `scoringData.input` as fallback for scorer input. - Remove stale debug console.log from evals test Co-Authored-By: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>
…arts The file part object passed `mimeType` instead of `mediaType`, causing file attachments to be routed through the V4 adapter instead of V5. Introduced in mastra-ai#13574.
…arts (#13833) ## Summary Fixes a typo in `Harness.sendMessage()` where file parts were constructed with `mimeType` instead of `mediaType`. This caused file attachments to be routed through the V4 adapter instead of V5, preventing them from being correctly processed by AI SDK v5 providers. ## Change **`packages/core/src/harness/harness.ts`** ```diff - return { type: 'file' as const, data: f.data, mimeType: f.mediaType, filename: f.filename }; + return { type: 'file' as const, data: f.data, mediaType: f.mediaType, filename: f.filename }; ``` ## Context The `files` parameter was added in #13574, but the file part construction used `mimeType` (V4 format) instead of `mediaType` (V5 format). The AI SDK v5 `FilePart` type expects `mediaType`, so using `mimeType` causes the part to not match the expected shape and fall through to the legacy adapter path. This bug is present in `@mastra/core@1.9.0` on npm. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Corrected file attachment metadata naming in the message system to ensure files are properly routed and processed by supported AI SDK providers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…arts (#13833) ## Summary Fixes a typo in `Harness.sendMessage()` where file parts were constructed with `mimeType` instead of `mediaType`. This caused file attachments to be routed through the V4 adapter instead of V5, preventing them from being correctly processed by AI SDK v5 providers. ## Change **`packages/core/src/harness/harness.ts`** ```diff - return { type: 'file' as const, data: f.data, mimeType: f.mediaType, filename: f.filename }; + return { type: 'file' as const, data: f.data, mediaType: f.mediaType, filename: f.filename }; ``` ## Context The `files` parameter was added in #13574, but the file part construction used `mimeType` (V4 format) instead of `mediaType` (V5 format). The AI SDK v5 `FilePart` type expects `mediaType`, so using `mimeType` causes the part to not match the expected shape and fall through to the legacy adapter path. This bug is present in `@mastra/core@1.9.0` on npm. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Corrected file attachment metadata naming in the message system to ensure files are properly routed and processed by supported AI SDK providers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
… text file handling (#13574) ## Summary - **Rename `images` → `files`** in `sendMessage` API to support all file types, not just images - **Use AI SDK `FilePart` shape** (`mimeType` instead of `mediaType`) for model compatibility - **Preserve `filename` field** through `AIV4Adapter` and `AIV5Adapter` when storing file parts to DB — previously `filename` was silently dropped during serialization, so it was present during streaming but lost in message history - **Handle text-based files** (`text/*`, `application/json`) by decoding them to text content parts instead of sending as binary `file` parts, which models like Claude cannot process inline ## Test plan - [ ] Send an image attachment → model responds correctly, filename persists in message history - [ ] Send a `.md` or `.json` file → model receives file content as text and responds (previously hung) - [ ] Verify filename shows in message history after page reload (not just during streaming) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * File attachment support in messages with optional filename preservation and improved media-type handling. * Text-based files (e.g., text/*, application/json) now decode into readable text content when appropriate. * **Changes** * Message send API parameter renamed from images to files; files include mediaType and optional filename (breaking change). * File metadata is preserved across message conversions, storage, and message history. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…arts (#13833) ## Summary Fixes a typo in `Harness.sendMessage()` where file parts were constructed with `mimeType` instead of `mediaType`. This caused file attachments to be routed through the V4 adapter instead of V5, preventing them from being correctly processed by AI SDK v5 providers. ## Change **`packages/core/src/harness/harness.ts`** ```diff - return { type: 'file' as const, data: f.data, mimeType: f.mediaType, filename: f.filename }; + return { type: 'file' as const, data: f.data, mediaType: f.mediaType, filename: f.filename }; ``` ## Context The `files` parameter was added in #13574, but the file part construction used `mimeType` (V4 format) instead of `mediaType` (V5 format). The AI SDK v5 `FilePart` type expects `mediaType`, so using `mimeType` causes the part to not match the expected shape and fall through to the legacy adapter path. This bug is present in `@mastra/core@1.9.0` on npm. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Corrected file attachment metadata naming in the message system to ensure files are properly routed and processed by supported AI SDK providers. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
images→filesinsendMessageAPI to support all file types, not just imagesFilePartshape (mimeTypeinstead ofmediaType) for model compatibilityfilenamefield throughAIV4AdapterandAIV5Adapterwhen storing file parts to DB — previouslyfilenamewas silently dropped during serialization, so it was present during streaming but lost in message historytext/*,application/json) by decoding them to text content parts instead of sending as binaryfileparts, which models like Claude cannot process inlineTest plan
.mdor.jsonfile → model receives file content as text and responds (previously hung)Summary by CodeRabbit
New Features
Changes