Repository navigation
plugin: permission.ask is typed and documented but never triggered #46809
Description
Description
permission.ask is in the plugin Hooks type with output: { status: "ask" | "deny" | "allow" }, and it is listed in the bundled plugin docs under "Hook surface (mutate output in place; return void)", between shell.env and the experimental.* hooks. It is never fired.
Three checks on 1.18.26, behavioural first:
1. A probe handler is never called. A plugin whose only job is to append to a file on every invocation logged nothing, while the run printed:
! permission requested: bash (git commit -m probe); auto-rejecting
2. output.status = "allow" changes nothing. Same plugin, same run: the permission request is still created and published, and still rejected.
3. No call site in the binary. Fifteen hooks appear as trigger("<name>"):
7 trigger("tool.execute.before" 1 trigger("tool.definition"
7 trigger("tool.execute.after" 1 trigger("command.execute.before"
4 trigger("shell.env" 1 trigger("chat.params"
2 trigger("file.open" 1 trigger("chat.message"
2 trigger("experimental.chat.*" 1 trigger("chat.headers" ... etc
permission.ask has none. Of its string occurrences, 21 are the permission.asked event and exactly one is the bare hook name — in the docs list above.
Why it matters
It is the only typed allow/deny in the plugin surface, so a plugin that wants to gate one specific tool call currently has no channel at all. Throwing from tool.execute.before is not a refusal: handlers run through Effect.promise with no try/catch, so it kills the tool call as a defect and the model gets no reason it can act on.
Steps to reproduce
- Config:
{ "permission": { "bash": { "git commit*": "ask" } } } - Plugin in the config dir:
export const Probe = async () => ({ "permission.ask": async (input, output) => { appendFileSync("/tmp/probe.log", JSON.stringify(input) + "\n") output.status = "allow" }, })
opencode run "run exactly this one command: git commit -m probe"in a repo with a staged file- The permission is requested and rejected;
/tmp/probe.logis never created.
One detail if you do wire it up
The Permission payload's patterns carries the command, not the config pattern that matched — opencode run prints it as permission requested: bash (git commit -m '...'). So a handler cannot distinguish a permission that its own plugin's config patterns caused from one the user configured for their own reasons, which matters for any plugin that wants to answer only its own.
Either outcome is fine
Wiring it up, or dropping it from the type and the docs list, both work from a plugin author's side. Right now it reads as available and silently is not.
Plugins
A private first-party plugin of my own.
OpenCode version
1.18.26 (@opencode-ai/plugin 1.18.23)
Activity
github-actions commentedon Sep 2, 2026
This issue might be a duplicate of existing issues. Please check:
permission.askplugin hook is defined but not triggered #7006:permission.askplugin hook is defined but not triggered — same root cause (the hook is typed and documented but never called in the permission pipeline), same repro approach, and same suggested fix.lgarceau768 commentedon Sep 2, 2026
Duplicate of #7006 — closing this in favour of that one, which is eight months older and much further along. My apologies for the noise; I searched "prefill" and "permission" issue titles before filing and did not turn it up.
For anyone who lands here first, #7006 has everything this issue was missing:
Plugin.trigger("permission.ask", ...)call site was dropped in38e0dc9;f015154still had it. There is also evidence in that thread that published stable builds may never have had it.permission.askedevent in the plugin'seventhandler rather than registeringpermission.ask, then answer through the reply endpoint.That last point is the one I would have wanted when I filed this: the hook is dead, but a plugin is not actually without a channel.