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
threadIdstringrequiredThe thread identifier
statsbooleanReturn storage statistics instead of file list
grepstringFull-text search pattern
findstringGlob 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=*.jpgGet File
threadIdstringrequiredThe thread identifier
pathstringrequiredFile path (e.g., attachments/photo.jpg)
thumbnailbooleanReturn image thumbnail instead of full file
formatstringSet 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=jsonLocal 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
threadIdstringrequiredThe thread identifier
pathstringrequiredFile path to write
datastringBase64-encoded file data
mimeTypestringMIME type of the file
metadataobjectOptional custom metadata
widthnumberImage width in pixels (for images)
heightnumberImage height in pixels (for images)
thumbnailstringBase64-encoded thumbnail for images
sizenumberSize in bytes for an external reference.
typestringSet to "directory" to create a directory
actionstringSet 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.
locationstringExternal 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
threadIdstringrequiredThe thread identifier
pathstringrequiredFile 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)
}