home / skills / sstobo / convex-skills / convex-agents-files
This skill helps agents manage file uploads, attachments, and image processing across conversations to enable visual analysis and file workflows.
npx playbooks add skill sstobo/convex-skills --skill convex-agents-filesReview the files below or copy the command above to add this skill to your agents.
---
name: "Convex Agents Files"
description: "Handles file uploads, image attachments, and media processing in agent conversations. Use this when agents analyze images, process documents, or generate files."
---
## Purpose
Files and images let agents understand and generate visual content. Covers uploading, storing, attaching to messages, and managing file lifecycle.
## When to Use This Skill
- Users upload images for agent analysis
- Agents need to process documents or files
- Agents generate images with DALL-E
- Building file-based workflows
- Implementing file cleanup and tracking
## Upload and Store a File
```typescript
import { storeFile } from "@convex-dev/agent";
export const uploadFile = action({
args: { fileData: v.string(), filename: v.string(), mimeType: v.string() },
handler: async (ctx, { fileData, filename, mimeType }) => {
const bytes = Buffer.from(fileData, "base64");
const { file } = await storeFile(
ctx,
components.agent,
new Blob([bytes], { type: mimeType }),
{ filename, sha256: "hash" }
);
return {
fileId: file.fileId,
url: file.url,
storageId: file.storageId,
};
},
});
```
## Send File with Message (2-Step)
Upload first, then send message with attachment:
```typescript
import { saveMessage, getFile } from "@convex-dev/agent";
// Step 1: Save message with file
export const submitFileQuestion = mutation({
args: { threadId: v.string(), fileId: v.string(), question: v.string() },
handler: async (ctx, { threadId, fileId, question }) => {
const { imagePart, filePart } = await getFile(ctx, components.agent, fileId);
const { messageId } = await saveMessage(ctx, components.agent, {
threadId,
message: {
role: "user",
content: [
imagePart ?? filePart,
{ type: "text", text: question },
],
},
metadata: { fileIds: [fileId] },
});
return { messageId };
},
});
// Step 2: Generate response
export const generateFileResponse = action({
args: { threadId: v.string(), promptMessageId: v.string() },
handler: async (ctx, { threadId, promptMessageId }) => {
const { thread } = await myAgent.continueThread(ctx, { threadId });
await thread.generateText({ promptMessageId });
},
});
```
## Inline File Saving (Action Only)
Pass file directly in generation:
```typescript
export const analyzeImageInline = action({
args: { threadId: v.string(), imageData: v.string(), question: v.string() },
handler: async (ctx, { threadId, imageData, question }) => {
const { thread } = await myAgent.continueThread(ctx, { threadId });
await thread.generateText({
message: {
role: "user",
content: [
{
type: "image",
image: Buffer.from(imageData, "base64"),
mimeType: "image/png",
},
{ type: "text", text: question },
],
},
});
},
});
```
## Generate and Save Images
```typescript
export const generateAndSaveImage = action({
args: { threadId: v.string(), prompt: v.string() },
handler: async (ctx, { threadId, prompt }) => {
const { image } = await generateImage({
model: openai.image("dall-e-2"),
prompt,
});
const { file } = await storeFile(ctx, components.agent, image, {
filename: `generated-${Date.now()}.png`,
});
return { fileId: file.fileId };
},
});
```
## Key Principles
- **Automatic storage**: Files > 64KB stored automatically
- **File tracking**: Metadata tracks which messages reference files
- **URL generation**: Signed URLs prevent unauthorized access
- **MIME type inference**: Auto-detected if not provided
- **Cleanup**: Files tracked for garbage collection
## Next Steps
- See **messages** for message management
- See **streaming** for streamed file processing
- See **fundamentals** for agent setup
This skill handles file uploads, image attachments, and media processing inside agent conversations. It provides methods to upload and store files, attach files to messages, process inline media during generation, and generate images programmatically. Use it when agents must analyze images, process documents, or produce downloadable media.
The skill exposes functions to store files, retrieve file parts (image or generic file parts), and attach those parts to messages so agents can reference them during generation. It supports two-step workflows (upload then send) and inline actions that pass binary data directly into a generation request. Generated images from models can be saved back into storage and tracked via file IDs and signed URLs.
How do I attach an uploaded file to a message?
Upload the file with the storeFile helper, get the fileId, then retrieve the file parts with getFile and include imagePart or filePart in the message content. Save the message with metadata.fileIds to enable tracking.
When should I use inline file passing versus uploading first?
Use inline passing for quick, ephemeral analysis during generation. Upload first when you need persistent storage, reuse of the file across messages, or lifecycle tracking and garbage collection.