You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(serve): add workspace file write/edit routes
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): bind file hashes to text snapshots
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): tighten read-bytes snapshot and create-mode publish
- readBytesWindow: re-stat the open fd after read and require
unchanged ino+size+mtime before emitting the response. Mirrors
the hardened text-snapshot path so the full-window hash can no
longer pair with bytes that drifted under in-place rewrite or
append. Surface drift as retryable hash_mismatch.
- atomicWriteTextResolvedFile: reject a symlinked parent up-front
as defense-in-depth ahead of the parent-fd publish follow-up
referenced by assertInodeStableAfterRead.
- atomicWriteTextResolvedFile: publish create-mode writes via
link()+unlink() instead of rename(). POSIX rename() overwrites
an existing regular file, so a racing external process could
break the public create contract; link() returns EEXIST
atomically and is portable across POSIX/NTFS. The early
assertCreateTargetAbsent check stays for friendlier errors on
the non-racing path.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
`session_scope_override` is the negotiation handle for the per-request `sessionScope` field on `POST /session` (see below). Older daemons silently ignore the field, so SDK clients should pre-flight `caps.features` for this tag before sending it.
@@ -118,6 +119,15 @@ registry. Clients **must** gate UI off `features`, not off `mode` (per design
118
119
119
120
> ⚠️ **PR 14 v1 scope: per-session, not per-workspace.** Each ACP session inside the daemon constructs its own `Config` + `McpClientManager` (via `acpAgent.newSessionConfig`). The budget caps live MCP clients **per session**; each session independently reads `QWEN_SERVE_MCP_CLIENT_BUDGET` from the forwarded env. With `--mcp-client-budget=10` and 5 concurrent ACP sessions, the actual live MCP client count can reach 5 × 10 = 50 across the daemon. The `GET /workspace/mcp` snapshot reads the **bootstrap session's**`McpClientManager` accounting only — the `budgets[0].scope: 'session'` value is the honest signal that this is per-session, not aggregated. **Wave 5 PR 23 (shared MCP pool)** will introduce a workspace-scoped manager and add a `scope: 'workspace'` cell alongside the per-session cell for true cross-session aggregation. v1 is the in-process counter + soft enforcement foundation that PR 23 builds on.
120
121
122
+
`workspace_file_read` covers the text/list/stat/glob workspace file routes
covers `GET /file/bytes`, which was added later so clients can pre-flight raw
125
+
byte-window support against PR19-era daemons. `workspace_file_write` covers
126
+
the hash-aware text mutation routes (`POST /file/write`, `POST /file/edit`).
127
+
The write tag means the route contract exists; it does not mean the current
128
+
deployment is open for anonymous mutation. Write/edit are strict mutation
129
+
routes and require a configured bearer token even on loopback.
130
+
121
131
**Conditional tags.** A small number of feature tags are advertised only when the matching deployment toggle is on. Tag presence = behavior is on; absence = either an older daemon predating the tag, OR a current daemon where the operator did not opt in. Currently:
122
132
123
133
| Tag | Advertised when … |
@@ -605,6 +615,173 @@ carries a single `ServeStatusCell` describing the failure and the cells
605
615
fall back to `not_started` ACP placeholders. Daemon-level cells are still
606
616
returned.
607
617
618
+
### Workspace file routes
619
+
620
+
All file paths are resolved through the daemon's bound workspace. Responses use
621
+
workspace-relative paths and never return absolute filesystem paths for normal
622
+
success cases. Successful file responses include:
623
+
624
+
```http
625
+
Cache-Control: no-store
626
+
X-Content-Type-Options: nosniff
627
+
```
628
+
629
+
Filesystem errors use this JSON shape:
630
+
631
+
```json
632
+
{
633
+
"errorKind": "hash_mismatch",
634
+
"error": "expected sha256:..., found sha256:...",
635
+
"hint": "re-read the file and retry with the latest hash",
636
+
"status": 409
637
+
}
638
+
```
639
+
640
+
`errorKind` values include `path_outside_workspace`, `symlink_escape`,
0 commit comments