Skip to content

File System

Per-thread file storage with SQLite backing

Each thread has isolated file storage accessible via these endpoints. Files are stored in the thread's SQLite database with support for external references (URLs, S3, R2).

File paths in URLs

{path} is the file's path in the thread file system without its leading /, with each path segment percent-encoded. The path of an attachment is such a path, so encode each segment before placing it in a URL:

const encoded = path.replace(/^\/+/, '').split('/').map(encodeURIComponent).join('/')
const url = `/api/threads/${threadId}/fs/${encoded}`

The file /my dir/a file.txt is served at /api/threads/{threadId}/fs/my%20dir/a%20file.txt. getFileUrl() in @standardagents/client, @standardagents/react, and @standardagents/vue encodes paths this way.

The server decodes each segment separately, for reads, writes, and deletes. An encoded slash (%2F) inside a segment is not a path separator, and no file name contains a slash. A path with such a segment or with a malformed escape (for example a bare %) names no file: a read or delete returns 404, and a write returns 400.

List Directory / Query Files

threadIdstringrequired

The thread identifier

statsboolean

Return storage statistics instead of file list

grepstring

Full-text search pattern

findstring

Glob pattern for path matching (e.g., *.jpg)

# List root directory
GET /api/threads/{threadId}/fs

# Get storage stats
GET /api/threads/{threadId}/fs?stats=1

# Full-text search
GET /api/threads/{threadId}/fs?grep=searchterm

# Find by pattern
GET /api/threads/{threadId}/fs?find=*.jpg

Get File

threadIdstringrequired

The thread identifier

pathstringrequired

File path (e.g., attachments/photo.jpg)

thumbnailboolean

Return image thumbnail instead of full file

formatstring

Set to json to return metadata only

# Get file content
GET /api/threads/{threadId}/fs/{path}

# Get thumbnail
GET /api/threads/{threadId}/fs/{path}?thumbnail=1

# Get metadata only
GET /api/threads/{threadId}/fs/{path}?format=json

Local files return binary data with Content-Type and X-Content-Type-Options: nosniff. HTML, SVG, and XHTML files use Content-Disposition: attachment. format=json returns { file: FileRecord }. An external reference returns { file, external: true, location }. A directory path returns { files, path }.


Write File

threadIdstringrequired

The thread identifier

pathstringrequired

File path to write

datastring

Base64-encoded file data

mimeTypestring

MIME type of the file

metadataobject

Optional custom metadata

widthnumber

Image width in pixels (for images)

heightnumber

Image height in pixels (for images)

thumbnailstring

Base64-encoded thumbnail for images

sizenumber

Size in bytes for an external reference.

typestring

Set to "directory" to create a directory

actionstring

Set move with a destination path in destination. Set rename with a filename in newName. A successful move or rename returns 200 with an attachment reference.

locationstring

External URL for linked files (instead of data)

# Write file (JSON)
POST /api/threads/{threadId}/fs/{path}
Content-Type: application/json
{
  "data": "base64...",
  "mimeType": "image/jpeg"
}

# Create directory
POST /api/threads/{threadId}/fs/{path}
Content-Type: application/json
{ "type": "directory" }

# Link external file
POST /api/threads/{threadId}/fs/{path}
Content-Type: application/json
{
  "location": "https://example.com/file.pdf",
  "mimeType": "application/pdf",
  "size": 1024000
}

# Raw binary upload (auto-chunks large files)
POST /api/threads/{threadId}/fs/{path}
Content-Type: application/octet-stream
[binary data]

Delete File

threadIdstringrequired

The thread identifier

pathstringrequired

File or directory path to delete

DELETE /api/threads/{threadId}/fs/{path}

Returns 204 No Content on success. Directories must be empty to delete.


FileRecord Schema

interface FileRecord {
  path: string;           // Full path (e.g., "/attachments/photo.jpg")
  name: string;           // Filename
  mimeType: string;       // MIME type
  storage: 'local' | 'url' | 's3' | 'r2';
  location?: string | null; // External URL for linked files
  size: number;           // Size in bytes
  metadata?: Record<string, unknown> | null; // Custom metadata
  isDirectory: boolean;
  createdAt: number;      // Unix timestamp (ms)
  width?: number | null;  // Image width in pixels
  height?: number | null; // Image height in pixels
  isChunked?: boolean;    // True if file is stored in chunks (>1.75MB)
  chunkCount?: number;    // Number of chunks (if chunked)
}