Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions docs/design/extension-management-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,15 @@ revisioned credential snapshots but does not change the store, cache, runtime,
or credentials selected by the installed artifact. Prepared mutations enter a
separate single-concurrency FIFO commit queue in the order preparation
finishes. Activation and uninstall enter only the commit queue; check-updates
enters only the preparation queue. Manual refresh is serialized through the
commit queue. Its HTTP timeout releases that lane so a stalled runtime refresh
cannot permanently block later extension mutations; the already-started refresh
may still settle afterward. Sensitive settings are staged as one atomic secret
bundle under a per-prepare revision. A non-secret selector records that revision
and secure-storage backend inside the staged artifact, so only the winning
artifact commit activates a complete bundle. The store commit is therefore the
enters only the preparation queue. Activation operations finish at the durable
commit point and do not directly refresh active sessions. Manual refresh is
serialized through the commit queue. Its HTTP timeout releases that lane so a
stalled runtime refresh cannot permanently block later extension mutations; the
already-started refresh may still settle afterward. Sensitive settings are
staged as one atomic secret bundle under a per-prepare revision. A non-secret
selector records that revision and secure-storage backend inside the staged
artifact, so only the winning artifact commit activates a complete bundle. The
store commit is therefore the
durability point and releases the commit lane immediately. Extension reload,
legacy per-key settings synchronization, manager runtime refresh, prepared-file
cleanup, and daemon runtime reconciliation run outside it. These post-commit
Expand All @@ -170,9 +172,10 @@ limits, and archive-entry validation before extraction.

## Runtime reconciliation

A successful commit invalidates local status and refreshes affected runtimes.
Global artifact/default changes reconcile all runtimes in this daemon; an exact
workspace override reconciles only its target. Runtime reconciliation refreshes
A successful artifact commit invalidates local status and refreshes affected
runtimes. Activation commits only persist policy; clients that need immediate
application submit the independent runtime-refresh operation. Global artifact
changes reconcile all runtimes in this daemon. Runtime reconciliation refreshes
extension and skill caches, extension tools, hierarchical memory, active chat
system instructions, and available commands. A failed component does not skip
the remaining refresh components; the session RPC reports the combined failure
Expand Down Expand Up @@ -208,15 +211,19 @@ share the store.
`workspace_extensions` remains the capability for the existing singular
surface. Its handlers call the same manager/coordinator and adapt responses:
project activation becomes a primary workspace override; user activation keeps
the legacy rule-clearing behavior; global mutation reconciles every local
runtime. The legacy operation endpoint maps V2 warning completion back to the
published legacy refresh-error status.
the legacy rule-clearing behavior. Activation through either surface is
commit-only when `extension_activation_explicit_refresh` is advertised. The
legacy operation endpoint maps V2 warning completion back to the published
legacy refresh-error status.

Clients must check `extension_management_v2`; neither daemon mode nor another
workspace capability implies this API. Batch activation additionally requires
`extension_batch_activation_v2`, because older V2 daemons expose only singular
activation routes. The abandoned
`workspace_qualified_extensions` proposal is not part of the protocol.
activation routes. Clients that require activation changes to apply to active
sessions must check `extension_activation_explicit_refresh`: when present, wait
for the activation operation to commit and then submit the independent refresh
operation; older daemons already refresh within the activation operation. The
abandoned `workspace_qualified_extensions` proposal is not part of the protocol.

## Non-goals

Expand Down
2 changes: 1 addition & 1 deletion docs/developers/daemon/00-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Use these anchors when moving from the docs into the latest `main` code:
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| HTTP routes | The route catalog lives in `qwen-serve-protocol.md`; this daemon set only references it and explains implementation ownership. | [`../qwen-serve-protocol.md`](../qwen-serve-protocol.md), [`20`](./20-quickstart-operations.md) |
| Event schema | `EVENT_SCHEMA_VERSION = 1`; 53 known event types; id-less subscriber synthetic frames; `_meta.serverTimestamp` stamped by `EventBus.publish()` (with `formatSseFrame()` fallback for synthetic frames). | [`09`](./09-event-schema.md), [`10`](./10-event-bus.md) |
| Capabilities | `SERVE_PROTOCOL_VERSION = 'v1'`; 151 registered tags; 44 conditional tags. | [`11`](./11-capabilities-versioning.md) |
| Capabilities | `SERVE_PROTOCOL_VERSION = 'v1'`; 152 registered tags; 44 conditional tags. | [`11`](./11-capabilities-versioning.md) |
| Session shell | `POST /session/:id/shell` exists behind `--enable-session-shell`, bearer or trusted-loopback authority, and session-bound `X-Qwen-Client-Id`; capability tag is conditional. | [`11`](./11-capabilities-versioning.md), [`17`](./17-configuration.md), [`20`](./20-quickstart-operations.md) |
| Rate limiting | Optional per-tier HTTP rate limit is exposed by CLI flags/env and conditional capability tag. | [`11`](./11-capabilities-versioning.md), [`17`](./17-configuration.md) |

Expand Down
2 changes: 2 additions & 0 deletions docs/developers/daemon/11-capabilities-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Local Extension installation: `extension_local_path_install` allows an absolute

V2 Extension batch activation: `extension_batch_activation_v2` adds queued global default-activation and selected-workspace override batches to `extension_management_v2`. Clients must pre-flight it independently because older V2 daemons expose only singular activation routes.

Explicit Extension activation refresh: `extension_activation_explicit_refresh` means singular and batch activation operations finish at the durable policy commit without directly refreshing active sessions. Clients that need immediate application should wait for activation success and submit the existing workspace runtime-refresh operation. Daemons without this tag already include refresh in the activation operation, so callers must not submit a compatibility refresh there.

Workspace-qualified session reads: `workspace_persisted_transcript`, `workspace_session_export`, `workspace_archived_session_export`, `workspace_session_live_state`. The active and archived export tags are independent from each other and from `session_export` and `workspace_qualified_rest_core`, so clients must pre-flight the exact storage state they intend to export. Persisted transcript paging permits an untrusted secondary under its bounded read policy; both full export paths remain trusted-only. `workspace_session_live_state` is likewise independent from `workspace_qualified_rest_core` and is trusted-only: it serves the selected runtime's memory-only live-session snapshot and catalog version and does not extend the untrusted-secondary persisted read policy to live bridge state.

Workspace mutation (Wave 4+): `workspace_memory`, `workspace_agents`, `workspace_agent_generate`, `workspace_acp_preheat`, `workspace_tool_toggle`, `workspace_skill_settings_toggle`, `workspace_skill_settings_batch_toggle`, **`workspace_settings`** (conditional), `workspace_permissions`, `workspace_init`, `workspace_github_setup`, `workspace_trust`, `workspace_mcp_restart`, `workspace_mcp_manage`, `workspace_file_read`, `workspace_file_bytes`, `workspace_file_read_cursor`, `workspace_file_write`, `workspace_file_upload`, **`workspace_reload`** (conditional). The two Skill settings tags replace the retired catalog-validated `workspace_skill_toggle` and `workspace_skill_batch_toggle` tags.
Expand Down
2 changes: 1 addition & 1 deletion docs/developers/daemon/13-sdk-daemon-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ const workspaceHandle = await client
const operation = await client.waitForExtensionOperation(workspaceHandle);
```

The terminal operation result contains ordered `results`. Targets do not need to be installed when setting `enabled` or `disabled`: the daemon stores a name declaration and preserves that activation policy when an Extension with that name is installed later. All changed targets share one Extension Store generation and one reconciliation pass. Global default batches reconcile every registered runtime; workspace batches resolve and reconcile only the selected trusted runtime. Workspace `inherit` clears the exact override but does not create a declaration for an unknown name; an all-unknown clear succeeds as a no-op without reconciliation. Singular activation methods remain installed-only.
The terminal operation result contains ordered `results`. Targets do not need to be installed when setting `enabled` or `disabled`: the daemon stores a name declaration and preserves that activation policy when an Extension with that name is installed later. All changed targets share one Extension Store generation. When `extension_activation_explicit_refresh` is advertised, activation operations finish after the durable policy commit without refreshing active sessions. A caller that needs immediate application should then submit `workspace.refreshExtensionRuntime(clientId)`; the refresh is a separate operation and may be awaited or left in the background. Older daemons already refresh inside the activation operation, so clients must not submit the extra refresh unless the capability is present. The 30-second generation reconciler remains an independent eventual-convergence path. Workspace `inherit` clears the exact override but does not create a declaration for an unknown name; an all-unknown clear succeeds as a no-op. Singular activation methods remain installed-only.

For workspace-internal Extension Skill switches, preflight `extension_state` and use the resource-grouped REST methods. These do not write Skill settings or activate a disabled parent Extension:

Expand Down
9 changes: 6 additions & 3 deletions docs/developers/qwen-serve-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ registry. Clients **must** gate UI off `features`, not off `mode` (per design
'workspace_file_upload',
'session_approval_mode_control', 'workspace_tool_toggle',
'workspace_skill_settings_toggle', 'workspace_skill_settings_batch_toggle',
'extension_batch_activation_v2', 'extension_state',
'extension_batch_activation_v2', 'extension_activation_explicit_refresh',
'extension_state',
'workspace_settings', 'workspace_init', 'workspace_mcp_restart',
'session_recap', 'session_generation', 'session_btw', 'session_shell_command',
'standalone_sessions_v1', 'standalone_session_options_v1',
Expand Down Expand Up @@ -296,7 +297,7 @@ registry. Clients **must** gate UI off `features`, not off `mode` (per design

`session_info` advertises `GET /workspace/:id/session-info` and its `/workspaces/:workspace/session-info` twin. The response aggregates persisted active and archived session counts without hydrating list metadata. It is an explicit O(n) disk scan and must not be polled; clients should treat `truncated: true` as a lower-bound result.

`session_approval_mode_control`, `workspace_tool_toggle`, `workspace_skill_settings_toggle`, `workspace_skill_settings_batch_toggle`, `extension_batch_activation_v2`, `workspace_init`, and `workspace_mcp_restart` advertise the mutation control routes documented below. Approval-mode control retains its non-strict compatibility gate. The other controls are strict-gated by operator authority: trusted-loopback primary, bearer-authenticated, or paired Local Control requests pass. A token-less primary request that reaches the strict gate without trusted-loopback authority returns 401 `token_required`; missing or invalid configured credentials and unpaired Local Control credentials are rejected earlier by bearer middleware with plain `401 Unauthorized`. Daemons that lack one of these routes return `404`. The settings-specific Skill tags are different: daemons from the retired-tag generation advertise `workspace_skill_toggle` and `workspace_skill_batch_toggle` and serve their catalog-validated contract at the same paths. The retired single-target route can return HTTP `404 skill_not_found` or `409 skill_not_toggleable`; the retired batch route returns HTTP 200 and places catalog-derived failures in `errors[]`. Pre-flight each tag before exposing its affordance, and do not infer the settings-specific Skill contract by probing route reachability. The route paths and request bodies did not change.
`session_approval_mode_control`, `workspace_tool_toggle`, `workspace_skill_settings_toggle`, `workspace_skill_settings_batch_toggle`, `extension_batch_activation_v2`, `extension_activation_explicit_refresh`, `workspace_init`, and `workspace_mcp_restart` advertise the mutation control routes documented below. Approval-mode control retains its non-strict compatibility gate. The other controls are strict-gated by operator authority: trusted-loopback primary, bearer-authenticated, or paired Local Control requests pass. A token-less primary request that reaches the strict gate without trusted-loopback authority returns 401 `token_required`; missing or invalid configured credentials and unpaired Local Control credentials are rejected earlier by bearer middleware with plain `401 Unauthorized`. Daemons that lack one of these routes return `404`. The settings-specific Skill tags are different: daemons from the retired-tag generation advertise `workspace_skill_toggle` and `workspace_skill_batch_toggle` and serve their catalog-validated contract at the same paths. The retired single-target route can return HTTP `404 skill_not_found` or `409 skill_not_toggleable`; the retired batch route returns HTTP 200 and places catalog-derived failures in `errors[]`. Pre-flight each tag before exposing its affordance, and do not infer the settings-specific Skill contract by probing route reachability. The route paths and request bodies did not change.

`mcp_guardrails` (issue [#4175](https://github.com/QwenLM/qwen-code/issues/4175) PR 14) covers the MCP budget surface: the `clientCount` / `clientBudget` / `budgetMode` / `budgets[]` fields on `GET /workspace/mcp`, the `disabledReason` field on per-server cells, and the `--mcp-client-budget` / `--mcp-budget-mode` CLI flags. Older daemons omit the new fields entirely; SDK clients pre-flight this tag before relying on `budgets[]` semantics. The registry descriptor also carries `modes: ['warn', 'enforce']` for future feature-modes exposure — for now, clients infer mode from the snapshot's `budgetMode` field. Server refusal under `enforce` mode is deterministic by `Object.entries(mcpServers)` declaration order; a future scope-precedence layer (if qwen-code adopts one) would shift this to "lowest-precedence first" to mirror claude-code's `plugin < user < project < local` convention.

Expand Down Expand Up @@ -326,7 +327,9 @@ The same tag also exposes workspace-qualified project-agent CRUD at `/workspaces

`extension_local_path_install` advertises daemon-local Extension sources on both `POST /workspace/extensions/install` and `POST /extensions/install`. The `source` must be an absolute path that exists on the daemon host. Relative paths remain unsupported so daemon process cwd cannot change source identity or shadow a GitHub `owner/repo` shorthand. The existing install operation copies the Extension into managed storage; it does not link the source. Clients must preflight this tag because older daemons reject local sources.

`extension_batch_activation_v2` adds `PUT /extensions/activation` and `PUT /workspaces/:workspace/extensions/activation`. Both accept 1–100 names in `extensionNames`, deduplicate them case-insensitively while preserving first-seen order, persist changed targets in one generation, and return one `202` operation handle. A target does not need to be installed when setting `enabled` or `disabled`: its name creates a desired-state declaration that is preserved when an Extension with that name is installed. The global route accepts `state: "enabled" | "disabled"`, writes V2 `defaultActivation`, and reconciles every registered runtime. The workspace route also accepts `"inherit"`, applies or clears exact overrides for the selected trusted runtime, and reconciles only that runtime. `inherit` does not declare an unknown name; an all-unknown clear reports `updated: false` and skips reconciliation. Singular activation routes remain installed-only and id-addressed.
`extension_batch_activation_v2` adds `PUT /extensions/activation` and `PUT /workspaces/:workspace/extensions/activation`. Both accept 1–100 names in `extensionNames`, deduplicate them case-insensitively while preserving first-seen order, persist changed targets in one generation, and return one `202` operation handle. A target does not need to be installed when setting `enabled` or `disabled`: its name creates a desired-state declaration that is preserved when an Extension with that name is installed. The global route accepts `state: "enabled" | "disabled"` and writes V2 `defaultActivation`; the workspace route also accepts `"inherit"` and applies or clears exact overrides for the selected trusted runtime. `inherit` does not declare an unknown name, and an all-unknown clear reports `updated: false`.

`extension_activation_explicit_refresh` means singular and batch activation operations finish after the durable policy commit without directly refreshing active sessions. Callers that need immediate application should wait for activation success and then submit either the synchronous primary-workspace `POST /workspace/extensions/refresh`, which returns refresh counts directly, or the selected workspace's asynchronous `POST /workspaces/:workspace/extensions/refresh`, which returns a separate operation handle. A refresh failure does not roll back or downgrade the activation result. Daemons without this capability already include runtime refresh in activation, so compatibility clients must not submit a second refresh. The independent 30-second generation reconciler remains enabled and normally applies the committed policy by its next pass; failed reconciliation is retried by later passes.

### Extension Management V2 wire contract

Expand Down
Loading
Loading