A sandbox run produces two kinds of file traffic: inputs (files attached by the user before dispatch) and outputs (files the agent created during the run). Both flow through a per-conversation or per-execution file bag — a temporary, scoped object store that the workspace and the sandbox both can read.
The chat file bag (Assistant)
When a user drags a file into an Assistant chat, the upload service stores the object at assistant-chat-bag/{workspaceId}/{chatId}/{name} and inserts a row into assistantChatBagFiles:
| Column | Notes |
|---|---|
chatId | Scopes the bag to one conversation. |
messageId | Set when the file was attached to a specific user turn. |
name, mimeType, size, sha256 | File metadata. |
storageKey | Object key in the chat-bag storage context. |
source | 'user' (uploaded by user) or 'sandbox' (written back by a run). |
Names collide-resolve by appending a -<n> suffix; user uploads keep their original filenames otherwise (see file-bag/naming.ts).
Projecting into the sandbox
When a chat turn dispatches with attachmentIds, the route resolves the rows and sets chat_id on the envelope. The sandbox config endpoint (/config) then projects the bag as a signed zip URL into the config payload — same shape as the Agents-block assets block.
The signed URL has a short TTL (CHAT_BAG_ZIP_URL_TTL_SECONDS) and is verified on download. The sandbox extracts the zip into its workspace directory before the agent loop starts.
Writeback — files generated during a run
When the agent loop finishes, the sandbox can POST new files back to the workspace using its upload OTP:
POST {agentUrl}/results
Authorization: <upload OTP>
Content-Type: multipart/form-dataFor the Agents block, the writeback target is /root/workspace/assets/ inside the container. Anything in that directory at the end of the run is collected and surfaced on the block's files output (see Agents block).
For the Assistant, written-back files land in the same assistantChatBagFiles table with source: 'sandbox', scoped to the same chatId so subsequent turns of the conversation see them automatically.
Lifecycle
Bag files are not durable workspace files by default. They live alongside the conversation or workflow execution that produced them:
- Chat bag — tied to the chat. Soft-deleting the conversation soft-deletes the bag (
softDeleteChatBagFile). Files persist across turns of the same conversation but do not migrate into the workspace Files module automatically. - Agents block writeback — surfaced on the block's
filesoutput. Pipe the output into a Save File block (or any block that consumesfile[]) to persist into Files.
A sandbox is ephemeral. Anything not written back through the upload OTP — temp files, intermediate artifacts, anything outside /root/workspace/assets/ — is gone when the container exits. There is no scrape-after-the-fact recovery.
Source
apps/actana/lib/assistant-sandbox/file-bag/db.ts—assistantChatBagFilesreads + writesapps/actana/lib/assistant-sandbox/file-bag/signed-url.ts— zip URL signing + verificationapps/actana/lib/assistant-sandbox/file-bag/naming.ts— collision-safe namingapps/actana/app/api/assistant/sandbox/[sessionId]/results/— Assistant writeback routeapps/actana/executor/handlers/agents/agents-handler.ts— Agents-block writeback dispatchpackages/db/schema.ts—assistantChatBagFiles