mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-20 22:25:30 +00:00
feat(cli): Add session owner index for workspace runtimes
Route live session ownership through a registry-backed owner index so multi-workspace sessions can resolve active sessions without scanning every bridge first. Expand trusted workspace load/resume and live read routing while keeping non-session surfaces primary-only. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
955ad27fc7
commit
a7f29a7cde
15 changed files with 1445 additions and 337 deletions
|
|
@ -114,6 +114,22 @@ Attaches to existing sessions are NOT counted toward the cap, so an idle daemon'
|
|||
|
||||
Fired when a `session/load` is issued for an id that already has a `session/resume` in flight (or vice versa). Wait at least `Retry-After` seconds and retry — the underlying restore completes within `initTimeoutMs` (default 10s). Same-action races (`load` vs `load`, `resume` vs `resume`) coalesce instead of erroring.
|
||||
|
||||
`SessionWorkspaceConflictError` — emitted by `POST /session/:id/load` and `POST /session/:id/resume` when the requested `cwd` targets one registered workspace but the same session id is already live or being restored by another runtime — returns `409` with:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Session \"<sid>\" is already live or restoring in another workspace runtime.",
|
||||
"code": "session_workspace_conflict",
|
||||
"sessionId": "<sid>",
|
||||
"workspaceCwd": "/requested/workspace",
|
||||
"workspaceId": "requested-workspace-id",
|
||||
"liveWorkspaceCwd": "/live/owner/workspace",
|
||||
"liveWorkspaceId": "live-owner-workspace-id"
|
||||
}
|
||||
```
|
||||
|
||||
Clients should retry with the owning workspace or wait for the in-flight restore to finish before restoring the id into a different workspace. Same-workspace restore races continue to use the bridge's `restore_in_progress` / coalescing behavior.
|
||||
|
||||
`SessionArchivedError` is emitted when a caller tries to load or resume a session whose JSONL is under `chats/archive/`:
|
||||
|
||||
```json
|
||||
|
|
@ -415,7 +431,13 @@ the daemon log path; `full` may include it for authenticated operators.
|
|||
"supported": ["v1"]
|
||||
},
|
||||
"mode": "http-bridge",
|
||||
"features": ["health", "daemon_status", "capabilities", "multi_workspace_sessions", "..."],
|
||||
"features": [
|
||||
"health",
|
||||
"daemon_status",
|
||||
"capabilities",
|
||||
"multi_workspace_sessions",
|
||||
"..."
|
||||
],
|
||||
"limits": {
|
||||
"maxPendingPromptsPerSession": 5,
|
||||
"maxSessionsPerWorkspace": 20,
|
||||
|
|
@ -1258,9 +1280,9 @@ Request:
|
|||
}
|
||||
```
|
||||
|
||||
| Field | Required | Notes |
|
||||
| ----- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `cwd` | no | Same canonicalization + `workspace_mismatch` rules as `POST /session`. Omit to inherit `/capabilities.workspaceCwd`. `mcpServers` is intentionally NOT accepted here — daemon-wide MCP is settings-driven (matches `POST /session`). |
|
||||
| Field | Required | Notes |
|
||||
| ----- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `cwd` | no | Same canonicalization + `workspace_mismatch` rules as `POST /session`. Omit to inherit `/capabilities.workspaceCwd`. When `features` contains `multi_workspace_sessions`, callers may pass any trusted registered `workspaces[].cwd`; untrusted non-primary workspaces return `403 untrusted_workspace`. `mcpServers` is intentionally NOT accepted here — daemon-wide MCP is settings-driven (matches `POST /session`). |
|
||||
|
||||
Response:
|
||||
|
||||
|
|
@ -1287,8 +1309,10 @@ Response:
|
|||
|
||||
- `404` — persisted session id doesn't exist (`SessionNotFoundError`).
|
||||
- `400` — `workspace_mismatch` (same shape as `POST /session`).
|
||||
- `403` — `untrusted_workspace` when `cwd` targets an untrusted non-primary workspace.
|
||||
- `503` — `session_limit_exceeded` (counts against `--max-sessions`; in-flight restores are accounted for too).
|
||||
- `409` — `restore_in_progress` (a `session/resume` for the same id is already in flight). `Retry-After: 5`. Same-action races (two concurrent `session/load` for the same id) coalesce — exactly one returns `attached: false`, the rest return `attached: true` with the same `state`.
|
||||
- `409` — `session_workspace_conflict` when the same session id is already live or being restored by another workspace runtime.
|
||||
- `409` — `session_archived` when the id exists only under `chats/archive/`; call `POST /sessions/unarchive` before `load` or `resume`.
|
||||
- `409` — `session_archiving` when archive or unarchive is in flight for the same id. `Retry-After: 5`.
|
||||
- `409` — `session_conflict` when the id exists in both `chats/` and `chats/archive/`; delete the session with `POST /sessions/delete` before loading.
|
||||
|
|
@ -1303,13 +1327,14 @@ Use `/load` when the client has no history rendered (cold reconnect, picker →
|
|||
|
||||
> ⚠️ **Why is `unstable_session_resume` still advertised?** The daemon's HTTP route and `session_resume` capability are stable for v1, but the bridge still calls ACP's `connection.unstable_resumeSession`. The old tag remains only so SDKs that shipped before `session_resume` can keep working.
|
||||
|
||||
### `GET /workspace/:id/sessions`
|
||||
### `GET /workspace/:id/sessions` and `GET /workspaces/:workspace/sessions`
|
||||
|
||||
List persisted sessions whose canonical workspace matches `:id` (URL-encoded absolute cwd). The default list is active sessions from `chats/`; pass `archiveState=archived` to list archived sessions from `chats/archive/`. `archiveState=all` is not supported in v1. The default response and numeric `cursor` semantics are unchanged by `session_organization`.
|
||||
List sessions whose canonical workspace matches `:id` or `:workspace`. The path parameter first resolves as an exact workspace id and then as a URL-encoded absolute cwd. `GET /workspaces/:workspace/sessions` is a plural alias with the same response shape. Primary workspaces include the existing persisted/live merge: the default list is active sessions from `chats/`; pass `archiveState=archived` to list archived sessions from `chats/archive/`. Non-primary workspaces are live-only in the current multi-workspace sessions surface and reject archived, organized, or grouped queries. `archiveState=all` is not supported in v1. The default response and numeric `cursor` semantics are unchanged by `session_organization`.
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:4170/workspace/$(jq -rn --arg c "$PWD" '$c|@uri')/sessions
|
||||
curl http://127.0.0.1:4170/workspace/$(jq -rn --arg c "$PWD" '$c|@uri')/sessions?archiveState=archived
|
||||
curl http://127.0.0.1:4170/workspaces/<workspace-id>/sessions
|
||||
```
|
||||
|
||||
Query parameters:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue