camelAI Documentation

Guides

Files

Attach files to messages, read what the agent wrote, share volumes between agents, and sign links

Every agent has files: its own workspace at /workspace by default, or the volumes you mount. Files you attach to a message land there, the model works on them with its file tools and with code, tools save their outputs there, and you read what the agent made by path.

Attach files to a message

const run = await agent.run("What changed in Q3?", {
  files: ["./q3.pdf", screenshotBytes, new File([csv], "data.csv"), { path: "/workspace/notes.md" }],
});

A file is bytes, a Blob or File, {name, data, contentType?}, a local path (from @camelai/run/node in TypeScript, or a str or Path in Python), or {path} for a file already in the agent's mounts. The SDKs upload each file first, to uploads/<run id>/<name>, then send the message referring to them by path, so a retried run never uploads twice.

  • At most 20 files per message, each at most 256 MiB.
  • Over REST, upload with PUT /v1/agents/{id}/uploads/{requestId}/{name}, then send POST /v1/agents/{id}/prompt {text, requestId, files: [{path}]}. The prompt also takes small files inline, {name, data: <base64>, contentType?}, up to 4 MiB in all.

What the model sees

The message carries a line per file, such as [File /workspace/uploads/r1/q3.pdf (application/pdf, 2.1 MB)], and the file itself where the model can take it:

Shown nativelyLimits
Images (PNG, JPEG, GIF, WebP)Models with image input5 MiB and 8,000 px a side each
PDFsAnthropic, Google, OpenAI and OpenRouter models with image input16 MiB and 100 pages each

Anything else is only named, and the model reads it with its file tools.

The agent's file tools

read, write, edit, ls, glob and grep work on mount paths such as /workspace/notes.md. Every file has a version, and write and edit take one, so an edit based on a stale read fails and the model reads again. Pass fileTools: false when the agent is made to leave them out.

Code in js_exec has fs over the same mounts:

js
const csv = await fs.readFile("/workspace/data.csv", { encoding: "utf8" });
await fs.writeFile("/workspace/out/chart.png", png, { contentType: "image/png" });
await fs.list("/workspace/out");

Files out

A run lists what it wrote in run.files ([{path, version, size, contentType}], up to 100). Files the model handed over with present_file are also sent as file_presented events, each with a signed download url.

const { data, contentType } = await agent.files.download("/workspace/out/chart.png");
const listing = await agent.files.list({ path: "/workspace/out" });
const link = await agent.files.link("/workspace/out/report.pdf", { expiresIn: 3600 });
await agent.files.upload("/workspace/in/config.json", JSON.stringify(config));

Volumes

A volume is a shared file tree. Without mounts, each agent gets its own workspace volume at /workspace. Mount the same volume in several agents to share files, read-only or read-write:

ts
const docs = await agents.runtime.createVolume({ name: "shared docs" });
await agents.runtime.volume(docs.id).write("handbook.md", handbook);
const agent = await agents.upsert("support", {
  mounts: [{ volumeId: docs.id, path: "/docs", mode: "ro" }],
  // ...
});
  • Mounts are {volumeId, path, mode: "ro" | "rw", subpath?, notify?}, at most 16 per agent. notify prompts the agent when others change files under the mount. An agent's mounts are fixed when it is made.
  • volume.write(path, data, { version }) writes only if nobody changed the file since that version (0 means it must not exist yet).
  • volume.snapshot() and volume.fork({ snapshot }) copy metadata only: a fork shares its source's content and diverges independently.
  • A volume holds up to 100,000 files.

agent.files.link(path, options) or volume.link(path, options) returns {url, expiresAt}: a URL that downloads (GET) or uploads (PUT, with maxBytes and contentType) that one file without a token, so a browser or another service moves the bytes directly. Links last 15 minutes by default and at most 24 hours, and can't be revoked sooner.

From your server, POST /v1/agents/{id}/links {path, method?, expiresIn?} signs a link by the path the agent sees, and POST /v1/volumes/{id}/links by a volume's own path.