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
{{ message }}
Repository navigation
skills: inline @mention injects a denied skill's body without a permission check #49891
A skill denied via permissions is still injected into the session when referenced with an @<skill-id> mention. The mention path loads the skill body directly and does not consult the skill permission.
The full SKILL.md body is injected into the conversation despite the deny.
Control: loading the same skill via the skill tool returns {"type":"permission.rejected","message":"Permission denied: skill"}.
Expected Behavior
Per the docs, a deny effect "Hides matching skills from the model and rejects loading." An explicit mention should not bypass it, or the docs should define explicit user activation as a separate source/permission action.
Actual Behavior
The mention injects the skill body with no permission check. This was reproduced after a full client restart.
Additional Context
Also tried metadata.opencode/slash: false and top-level slash: false in the skill frontmatter; neither affected the mention injection.
Reproduced the same gap — in packages/core/src/session/prompt.ts the mention-sourced skills attachments are resolved straight through skillService.get() + Skill.prepare() (around line 56-71) with no permission consultation, while the skill tool path properly returns permission.rejected. So a denied skill can be pulled into context just by mentioning it.
Happy to fix this: route mention-sourced skills through the same permission check the tool uses (reject denied ones instead of silently injecting the body), plus a regression test for the deny + @mention combo.
Independent reproduction on v2.0.26 (release/latest, Linux), with a different config shape than the OP: the deny is set in an agent's frontmatter, not root permissions.
Reproduction: new session in an empty directory, send @teach (also reproduced with @opencode). The full body is injected despite deny.
Evidence from the session DB (~/.local/share/opencode/opencode.db, table session_message, agent klean):
user message: "text":"@teach oh my pi" + "skills":[{"id":"teach","name":"teach","text":"<skill_content name=\"teach\">…"}]
no skill tool call anywhere in the session.
So the mention path injects the body into the user message with no permission consult — consistent with the root-cause note above.
Extra surface worth noting: the /skills selector lists the full discovered catalog, unfiltered by the skill permission — denied skills (and skills with metadata.opencode/autoinvoke: false) are still listed and selectable. So under deny, the model can't initiate a skill, but a user @mention or a selector pick still lands the body in context.
Still reproduces as of 2026-10-09, so the fix isn't in latest yet.
In v2 this is currently intentional, not an accidental bypass. A skill permission governs the model's skill tool: what's advertised to the model and what it can load. An explicit @skill-id mention adds the skill's instructions directly to your prompt and never goes through that tool. This was changed in 2.0.0 in response to #43872, which asked for the opposite of what you're asking: that users can still invoke skills that are denied to the model. The docs page says "To load a skill yourself, mention it in your prompt as @skill-id", but you're right that the permissions table's "rejects loading" doesn't say whether that covers user mentions.
I'll treat this as a feature request: a way for deny (or a separate permission action) to also block a user's explicit @ activation of a skill, and clearer docs on how mentions and the skill permission interact. Maintainers will consider it.
For now, the only way to make @create-plan unavailable is to remove the skill from your skill sources. A plugin can also do this by dropping it via ctx.skill.transform, which removes it for both you and the model.
Activity
holny commentedon Sep 19, 2026
Reproduced the same gap — in
packages/core/src/session/prompt.tsthe mention-sourcedskillsattachments are resolved straight throughskillService.get()+Skill.prepare()(around line 56-71) with no permission consultation, while theskilltool path properly returnspermission.rejected. So a denied skill can be pulled into context just by mentioning it.Happy to fix this: route mention-sourced skills through the same permission check the tool uses (reject denied ones instead of silently injecting the body), plus a regression test for the deny + @mention combo.
pablokan commentedon Oct 9, 2026
Independent reproduction on v2.0.26 (release/latest, Linux), with a different config shape than the OP: the deny is set in an agent's frontmatter, not root
permissions.~/.config/opencode/agents/klean.md:opencode.jsonsets"default_agent": "klean".Reproduction: new session in an empty directory, send
@teach(also reproduced with@opencode). The full body is injected despitedeny.Evidence from the session DB (
~/.local/share/opencode/opencode.db, tablesession_message, agentklean):"text":"@teach oh my pi"+"skills":[{"id":"teach","name":"teach","text":"<skill_content name=\"teach\">…"}]skilltool call anywhere in the session.So the mention path injects the body into the user message with no permission consult — consistent with the root-cause note above.
Extra surface worth noting: the
/skillsselector lists the full discovered catalog, unfiltered by theskillpermission — denied skills (and skills withmetadata.opencode/autoinvoke: false) are still listed and selectable. So underdeny, the model can't initiate a skill, but a user@mentionor a selector pick still lands the body in context.Still reproduces as of 2026-10-09, so the fix isn't in latest yet.
opencodejr commentedon Oct 28, 2026
Thanks for the detailed report!
In v2 this is currently intentional, not an accidental bypass. A
skillpermission governs the model'sskilltool: what's advertised to the model and what it can load. An explicit@skill-idmention adds the skill's instructions directly to your prompt and never goes through that tool. This was changed in 2.0.0 in response to #43872, which asked for the opposite of what you're asking: that users can still invoke skills that are denied to the model. The docs page says "To load a skill yourself, mention it in your prompt as@skill-id", but you're right that the permissions table's "rejects loading" doesn't say whether that covers user mentions.I'll treat this as a feature request: a way for
deny(or a separate permission action) to also block a user's explicit@activation of a skill, and clearer docs on how mentions and theskillpermission interact. Maintainers will consider it.For now, the only way to make
@create-planunavailable is to remove the skill from your skill sources. A plugin can also do this by dropping it viactx.skill.transform, which removes it for both you and the model.Related: