Files
A file is a file. Where it came from — the user attached it, the model generated it, the agent screenshotted a page or exported it from a sandbox — is provenance, not type, and it does not decide what can be done with it. What a file is decides how it is read; where it came from decides nothing.
This is separate from Documents, which is about a curated corpus the agent searches. This page is about the files in one conversation.
Read anything in the conversation
// The agent calls these; nothing to implement on your side.
list_files() // everything here: attached, generated, screenshotted, exported
read_file(name?) // read one — blank takes the most recent
read_file picks the best available route for that file and that model:
| File | How it is read |
|---|---|
| Text, CSV, JSON, code, logs | Decoded directly — no processor, no model support needed |
| PDF, Word, spreadsheets | Converted to text with the project's document processor |
| Images, audio, video | Put in front of the model to perceive, if it can and input:<kind> allows |
| Anything else | Reported as what it is, with the sandbox suggested |
No sandbox is required for any of it. Reading a file in your own conversation is not a privilege, so these are bound for every chat.
That last row matters: a tool that implied a read it never performed is how an agent ends up handing over a screenshot it never checked. It says so instead.
Producing a file, and sending one
These are two separate acts, and deliberately so. Every producer puts a file into the conversation and hands back an id; nothing reaches the user until the agent says so:
write_file(name, content) // something the agent writes → id
screenshot_url(url) // a captured page → id
computer_export_file(path) // out of a sandbox → id
send_file(id) // give ANY of them to the user
A producer that also delivered would force the decision at the wrong moment: the agent
would have to know at capture time whether a screenshot is worth showing — before it
has looked at it — and could not change its mind after cropping. send_file works on
any file in the conversation whatever produced it and however long ago, so
"screenshot → check → crop → send the crop" is just four calls with the user seeing
only the last.
write_file needs no sandbox — a text-only model can write a CSV and hand it over.
Every file classifies as one of four kinds — image, audio, video, or file for
everything else — and delivery is allowed when either of these is true:
- the token holds
output:file, which permits sending any kind; or - the token holds
output:<that kind>— sooutput:imagealone is enough to send a picture.
So a screenshot goes out under output:file or output:image, and needs neither the
other one nor both.
output:file is the broad oneoutput:file means "the agent may hand the user a file", full stop — a screenshot it
took, a chart it rendered in a sandbox, a CSV it wrote. Those are files it produced,
and a project that granted "you may send files" did not mean "except pictures".
output:image|audio|video answer a different question — whether the model may
generate that medium — and they additionally permit delivering their own kind, since
generated media the user can never be shown is generated for nobody.
Delivery is deliberately not gated on the turn's requested output_modalities: a
screenshot the agent took is a file it produced, not a modality the model emitted.
Files the user never sees
Anything produced but not sent stays in the conversation for the agent's own use — an
intermediate screenshot, a scratch CSV — and list_files still shows it. That is what
keeps working notes out of the chat without throwing them away.
Moving between layers
computer_fetch_attachment(name?) // conversation → sandbox
computer_fetch_document("prices.csv") // knowledge base → sandbox (name or id)
computer_export_file(path) // sandbox → the conversation (then send_file)
All of these refer to the same descriptors, so a file crosses a boundary without changing identity: screenshot a page, pull it into a sandbox, annotate it with ImageMagick, send the result — and read any of them back at any point.
Note the third one: exporting brings a file into the conversation, it does not hand it
over. That is send_file, for the reason the producers
section gives — and it is the step easiest to forget, because the failure looks like
success from every angle: the tool succeeded, the file exists, nothing errored, and
attachments is empty.