@@ -65,7 +65,7 @@ Error codes are grouped by band:
6565| ` 400xx ` | Bad request | ` 40001 ` validation failed (` details ` lists each field), ` 40003 ` provider is OAuth-managed |
6666| ` 401xx ` | Auth and readiness | ` 40101 ` unauthorized, ` 40110 ` no provider configured, ` 40113 ` model not resolved |
6767| ` 404xx ` | Not found | ` 40401 ` session, ` 40408 ` MCP server, ` 40409 ` file path |
68- | ` 409xx ` | State conflict | ` 40901 ` session busy, ` 40902 ` approval already resolved, ` 40922 ` page conditions mismatch ` page_token ` |
68+ | ` 409xx ` | State conflict | ` 40901 ` session busy, ` 40902 ` approval already resolved, ` 40922 ` page conditions mismatch ` page_token ` , ` 40928 ` ` fs:write ` ` base_etag ` is stale |
6969| ` 410xx ` | Expired | ` 41001 ` approval timed out, ` 41002 ` question timed out, ` 41003 ` temporary file expired |
7070| ` 413xx ` | Size or boundary exceeded | ` 41302 ` file read over 10 MB, ` 41304 ` path escapes the session directory |
7171| ` 429xx ` | Rate limited | ` 42901 ` auth-failure ban, ` 42902 ` too many fs watches |
@@ -219,7 +219,7 @@ PTY terminal endpoints; mounted only on loopback binds.
219219
220220### File system
221221
222- In-session file operations go through ` POST /api/v1/sessions/{session_id}/fs:{action} ` with JSON bodies; actions are ` list ` / ` read ` / ` list_many ` / ` stat ` / ` stat_many ` / ` mkdir ` / ` search ` / ` grep ` / ` git_status ` / ` diff ` / ` open ` / ` open-in ` / ` reveal ` . In addition:
222+ In-session file operations go through ` POST /api/v1/sessions/{session_id}/fs:{action} ` with JSON bodies; actions are ` list ` / ` read ` / ` list_many ` / ` stat ` / ` stat_many ` / ` mkdir ` / ` write ` / ` search ` / ` grep ` / ` git_status ` / ` diff ` / ` open ` / ` open-in ` / ` reveal ` . In addition:
223223
224224| Method and path | Description |
225225| --- | --- |
@@ -230,6 +230,27 @@ In-session file operations go through `POST /api/v1/sessions/{session_id}/fs:{ac
230230| ` GET /api/v1/fs:content ` | Raw bytes of any host file (gated only by the token — be careful when exposing the port) |
231231| ` POST /api/v1/fs:mkdir ` | Create a directory by absolute path |
232232
233+ #### Writing a file
234+
235+ ` POST /api/v1/sessions/{session_id}/fs:write ` saves a workspace file. It resolves
236+ and checks the path exactly as ` fs:read ` does, so it cannot escape the session
237+ directory or follow a symlink out of it, and it rejects a body over 10 MiB.
238+
239+ | Field | Description |
240+ | --- | --- |
241+ | ` path ` | Session-relative path to write |
242+ | ` content ` | File contents, encoded per ` encoding ` |
243+ | ` encoding ` | ` utf-8 ` (default) or ` base64 ` |
244+ | ` base_etag ` | The etag the edit was based on; omit to write unconditionally |
245+
246+ The response carries ` { path, size, etag, created } ` — ` etag ` is the value to send
247+ as the next write's ` base_etag ` , and ` created ` distinguishes a new file from an
248+ overwrite.
249+
250+ Passing ` base_etag ` makes the write optimistically concurrent: if the file changed
251+ since that etag, the write is refused with ` 40928 ` (` FS_CONFLICT ` ) instead of
252+ overwriting the other change. Re-read the file, rebase the edit, and retry.
253+
233254### File uploads
234255
235256| Method and path | Description |
0 commit comments