Repository navigation
feat(daemon): add workspace skill toggle API #6816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
bf754fe
feat(daemon): add workspace skill toggle API
callmeYe d21728f
test(daemon): cover skill toggle capability integration
callmeYe 31da97a
Merge remote-tracking branch 'origin/main' into codex/daemon-skill-to…
callmeYe 87cf6e0
fix(daemon): harden skill refresh handling
callmeYe 0955627
fix(daemon): improve skill refresh diagnostics
callmeYe 9d4c961
merge: sync origin/main into daemon-skill-toggle branch
qwen-code-dev-bot c63cf74
test(daemon): expand skill toggle coverage
callmeYe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # Daemon Skill Toggle | ||
|
|
||
| ## Goal | ||
|
|
||
| Expose the CLI `/skills` panel's workspace enable/disable behavior through daemon REST and the TypeScript SDK, including immediate refresh of active ACP sessions. | ||
|
|
||
| ## Public contract | ||
|
|
||
| - `POST /workspace/skills/:name/enable` | ||
| - `POST /workspaces/:workspace/skills/:name/enable` | ||
| - Request body: `{ "enabled": boolean }` | ||
| - SDK: `DaemonClient.setWorkspaceSkillEnabled` and `WorkspaceDaemonClient.setWorkspaceSkillEnabled` | ||
| - Capability: `workspace_skill_toggle` | ||
|
|
||
| The response contains the canonical skill name, requested state, whether persistence changed, activation state, and session refresh counts. `applied` means every active session refreshed, `deferred` means no ACP child was running, and `partial` means at least one session failed to refresh after persistence committed. | ||
|
|
||
| ## Semantics | ||
|
|
||
| The API changes only workspace `skills.disabled`. Skill lookup is case-insensitive, but the canonical discovered name is persisted. Updating one target removes target duplicates and case variants without deleting orphan entries for unavailable skills. A second identical request is a no-op. | ||
|
|
||
| The route rejects states the CLI panel cannot toggle: | ||
|
|
||
| - unknown skill: `404 skill_not_found`; | ||
| - `userInvocable === false`: `409 skill_not_toggleable`; | ||
| - skill from an inactive extension: `409 skill_not_toggleable`; | ||
| - disabled in system defaults, user, or system scope: `409 skill_not_toggleable` with the locking scope; | ||
| - untrusted workspace: `403 untrusted_workspace`. | ||
|
|
||
| The scope lock check and workspace read-modify-write happen inside the daemon's per-workspace settings lock. A failed write stops before refresh and event publication. | ||
|
|
||
| ## `skills.disabled` versus `disable-model-invocation` | ||
|
|
||
| `skills.disabled` is an operator setting merged as a case-insensitive union across scopes. It removes matching skill slash commands and model-visible skill entries, and execution-time validation rejects the skill. The daemon endpoint writes the workspace member of this union. | ||
|
|
||
| `disable-model-invocation` is SKILL.md metadata. It hides a skill from model invocation while preserving direct user invocation. The existing managed-skill ACP operation edits that metadata and is intentionally not reused by this API. | ||
|
|
||
| ## Activation flow | ||
|
|
||
| 1. Resolve the canonical, toggleable skill from the workspace status snapshot. | ||
| 2. Under the workspace settings lock, re-read every scope, reject higher-scope locks, and commit the canonical workspace list. | ||
| 3. Invalidate the daemon's cached skill status. | ||
| 4. If an ACP child is live, invoke `qwen/control/workspace/skills/refresh`. | ||
| 5. The child reloads its bootstrap workspace settings and refreshes every active session, including busy sessions. | ||
| 6. Each session reloads its own workspace settings, rebuilds and pushes `available_commands_update`, and notifies SkillManager consumers. | ||
| 7. Publish the existing workspace `settings_changed` event for `skills.disabled`. | ||
|
|
||
| An in-flight model request cannot be rewritten. Subsequent skill execution checks, command snapshots, and model contexts read the new state. | ||
|
|
||
| ## Downstream consumers | ||
|
|
||
| - Settings merge: system defaults, user, workspace, and system `skills.disabled` form the effective disabled-name set. | ||
| - Workspace status: ACP and daemon-local skill mapping expose disabled state and false-only `userInvocable`. | ||
| - Slash commands: available-command construction removes disabled skills and sends updated command metadata to daemon clients. | ||
| - Model context: SkillManager change listeners refresh the Skill tool description and available-skill context. | ||
| - Execution validation: the Skill tool re-reads the disabled-name provider before invocation, so later calls are rejected immediately. | ||
| - Extension state: inactive extension skills remain non-toggleable even when they are not disabled by settings. | ||
| - Daemon cache: the cached live-child skill snapshot is invalidated after persistence so later GET requests cannot replay stale state. | ||
| - SDK consumers: both primary-workspace and workspace-qualified clients share the response and error contract. | ||
| - Events: existing `settings_changed` consumers observe the committed `skills.disabled` value; there is no new event type. | ||
|
|
||
| ## Failure behavior | ||
|
|
||
| - Persistence failure: the HTTP request fails; no ACP refresh and no event. | ||
| - No child: persistence succeeds with `deferred`; the next child loads the setting at startup. | ||
| - Per-session refresh failure: persistence remains committed; successful sessions stay refreshed and the response is `partial`. | ||
| - Child transport race: if the child disappears after the liveness check, the response is `deferred`; other refresh failures are reported as `partial`. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.