Agent attachments#

Validating files a user sends to the copilot before they reach the model, and giving each one a stable reference the agent can talk about.

import { acceptAttachments, AttachmentError } from '@janux/agent';

acceptAttachments(attachments, policy?)#

try {
  const accepted = await acceptAttachments(incoming, { maxFiles: 2, allowedTypes: ['image/png'] });
  // accepted: [{ name, mediaType, data, ref: 'att_1', bytes: 12345 }, …]
} catch (error) {
  if (error instanceof AttachmentError) respondWith(error.code);
}

An IncomingAttachment is { name, mediaType, data }, where data is either a base64 payload or a storage marker like s3://…. Accepted attachments come back with two extra fields:

Field What it is
ref A stable att_N reference, 1-based, in request order
bytes Decoded size, computed from the base64 length (padding accounted for)

The ref is the point: the model refers to att_1 instead of carrying the payload around, so tool calls stay small and a file can be resolved server-side when it's actually needed.

The policy and its defaults#

AttachmentPolicy Default
allowedTypes ['image/jpeg', 'image/png', 'image/webp', 'application/pdf']
maxFiles 4
maxFileBytes 10 MB
maxRequestBytes 15 MB

Two limits, on purpose: maxFileBytes stops one big file, maxRequestBytes stops many medium ones adding up. A policy you pass is merged over these defaults, so narrowing one field keeps the rest.

AttachmentError#

Thrown — not returned — with a machine-readable code:

Code Meaning
too_many More files than maxFiles
bad_type A mediaType outside allowedTypes
too_big One file over maxFileBytes
request_too_big The batch over maxRequestBytes

Map the code to your HTTP response and to a message the user can act on. Rejection is all-or-nothing: a batch with one bad file is refused whole, so the model never sees a partially accepted upload and can't reason about a file that was dropped.

Where it belongs#

Call it at the edge — in the handler that receives the copilot request — before the turn is built, so an oversized or wrong-typed file never enters memory, never hits the provider and never gets stored. Pair it with dropzone on the client, whose accept/maxSize should mirror this policy so users get instant feedback and the server stays the authority.

Related: The agent and your copilot · HTTP handlers & uploads · Client state