Summary
Enabling/disabling a skill from the desktop Skills page does not take effect until the gateway is manually restarted, and it never propagates to conversations that are already open. This is confusing: users toggle a skill on, the agent still can't see it (or still sees a disabled one), with no feedback that a restart is required.
Root cause
Two independent gaps:
-
The toggle bypasses the gateway. The IPC handlers skills:update-allowlist and skills:update-managed-entries (desktop/src/main.ts:2658-2678) write ~/.openclaw/openclaw.json directly via fs.writeFileSync. The long-running gateway loads its config into memory and is never notified, so it keeps serving the skill list it read at boot. There is no lightweight reload RPC — desktop/src/gateway-client.ts:207 explicitly notes gateway.reload does not exist; the only refresh path is a full restart() (config.patch → SIGUSR1).
-
The toggle doesn't participate in openclaw's skill-version mechanism. openclaw already supports refreshing skills in a running conversation: each session stores a skillsSnapshot with a version, and on every turn ensureSkillSnapshot rebuilds it from the current config when a global "skills snapshot version" has bumped (shouldRefreshSnapshot = snapshotVersion > 0 && sessionSnapshotVersion < snapshotVersion). That version is bumped by bumpSkillsSnapshotVersion(...), which today only fires from the chokidar file-watcher on SKILL.md changes (ensureSkillsWatcher / resolveWatchTargets). Toggling a skill only edits openclaw.json (skills.allowBundled / skills.entries) — it touches no skill file, so the version never bumps and existing sessions never rebuild.
(Runtime enforcement itself is correct: openclaw's shouldIncludeSkill() does honor skills.allowBundled and skills.entries.<name>.enabled === false.)
Impact / observed behavior
- Toggle a skill on/off → the change is written to disk but the agent's available-skills list is unchanged.
- After a manual gateway restart, new chats reflect the change — but existing/open chats still do not.
- Note: a plain restart alone does not refresh existing chats either, because the in-memory snapshot version resets to 0 on boot, so restored sessions never satisfy
shouldRefreshSnapshot.
- Contrast: adding/removing actual skill files does propagate to existing chats on their next message, because the file-watcher bumps the version. Only the enable/disable toggle is broken.
Steps to reproduce
- Open a chat with the agent; ask which skills it can see.
- In Settings → Skills, enable a currently-disabled skill (e.g.
bluebubbles) and disable an enabled one (e.g. test-skill).
- In the same chat, ask again — the list is unchanged.
- Even opening a brand-new chat shows no change until the gateway is restarted.
Proposed fix
Make the toggle behave like a real skill add/remove:
- Persist skill config through the gateway (or otherwise update its in-memory config) instead of a raw
fs.writeFileSync, so the gateway sees the change.
- Bump the skills snapshot version after a toggle (call
bumpSkillsSnapshotVersion({ reason: "manual" }), or expose it via a gateway RPC/IPC) so already-open conversations rebuild their skill list from the updated config on their next message.
- Debounce rapid toggles and show a brief "applying…" / "changes apply on the next message" hint in the UI.
References
desktop/src/main.ts:2658-2678 — skills:update-allowlist, skills:update-managed-entries (direct file write)
desktop/src/gateway-client.ts:184-209 — restart(); gateway.reload does not exist
- openclaw runtime:
ensureSkillSnapshot / shouldRefreshSnapshot, bumpSkillsSnapshotVersion, ensureSkillsWatcher, shouldIncludeSkill
Summary
Enabling/disabling a skill from the desktop Skills page does not take effect until the gateway is manually restarted, and it never propagates to conversations that are already open. This is confusing: users toggle a skill on, the agent still can't see it (or still sees a disabled one), with no feedback that a restart is required.
Root cause
Two independent gaps:
The toggle bypasses the gateway. The IPC handlers
skills:update-allowlistandskills:update-managed-entries(desktop/src/main.ts:2658-2678) write~/.openclaw/openclaw.jsondirectly viafs.writeFileSync. The long-running gateway loads its config into memory and is never notified, so it keeps serving the skill list it read at boot. There is no lightweight reload RPC —desktop/src/gateway-client.ts:207explicitly notesgateway.reloaddoes not exist; the only refresh path is a fullrestart()(config.patch → SIGUSR1).The toggle doesn't participate in openclaw's skill-version mechanism. openclaw already supports refreshing skills in a running conversation: each session stores a
skillsSnapshotwith a version, and on every turnensureSkillSnapshotrebuilds it from the current config when a global "skills snapshot version" has bumped (shouldRefreshSnapshot = snapshotVersion > 0 && sessionSnapshotVersion < snapshotVersion). That version is bumped bybumpSkillsSnapshotVersion(...), which today only fires from the chokidar file-watcher onSKILL.mdchanges (ensureSkillsWatcher/resolveWatchTargets). Toggling a skill only editsopenclaw.json(skills.allowBundled/skills.entries) — it touches no skill file, so the version never bumps and existing sessions never rebuild.(Runtime enforcement itself is correct: openclaw's
shouldIncludeSkill()does honorskills.allowBundledandskills.entries.<name>.enabled === false.)Impact / observed behavior
shouldRefreshSnapshot.Steps to reproduce
bluebubbles) and disable an enabled one (e.g.test-skill).Proposed fix
Make the toggle behave like a real skill add/remove:
fs.writeFileSync, so the gateway sees the change.bumpSkillsSnapshotVersion({ reason: "manual" }), or expose it via a gateway RPC/IPC) so already-open conversations rebuild their skill list from the updated config on their next message.References
desktop/src/main.ts:2658-2678—skills:update-allowlist,skills:update-managed-entries(direct file write)desktop/src/gateway-client.ts:184-209—restart();gateway.reloaddoes not existensureSkillSnapshot/shouldRefreshSnapshot,bumpSkillsSnapshotVersion,ensureSkillsWatcher,shouldIncludeSkill