Problem
Plugins can inspect a tool call in tool.execute.before, but they cannot pause that call and request OpenCode’s native once / always / reject permission UI. They can only silently allow or throw a hard denial.
That blocks dynamic safety policies where risk is known only after plugin-side analysis. For example, a plugin can run git diff --diff-filter=D HEAD...<merge-ref> before git merge <merge-ref> and determine that the merge deletes 65 files. The desired behavior is to show a native approval prompt containing that computed reason, then continue the original tool call only when approved.
Proposed API
Expose an async permission-request capability to tool.execute.before, either as input.ask() or through an equivalent plugin context API.
"tool.execute.before": async (input, output) => {
if (input.tool !== "bash") return
const command = output.args.command
const deleted = await countDeletedFiles(command)
if (deleted <= 50) return
await input.ask({
permission: "bash",
patterns: [command],
reason: `This merge would delete ${deleted} files. Review before continuing.`,
})
}
The request should use the existing native permission flow:
once: continue this tool call
always: persist the normal session-scoped approval for the supplied pattern
reject: return a permission-denied tool result and do not execute
Acceptance criteria
- A plugin can request approval from
tool.execute.before after inspecting validated tool args.
- The request uses the existing TUI/Web permission UI rather than a plugin-specific dialog.
- Approval resumes the original pending tool call without asking the model to issue it again.
- Rejection prevents execution and gives the model a human-readable reason.
- Explicit config
deny rules remain terminal and cannot be bypassed by plugins.
- Plugin request failures fail closed to the normal permission prompt, not silent allow.
- Tests cover once, always, reject, explicit-deny precedence, and plugin failure.
Related work and distinction
Why this is general-purpose
Beyond deletion thresholds, this enables policy plugins for schema migration review, release/cutover gates, staged write access, and external governance systems. The plugin decides when a tool call needs human review; OpenCode remains the authority that renders and records the approval.
Problem
Plugins can inspect a tool call in
tool.execute.before, but they cannot pause that call and request OpenCode’s nativeonce / always / rejectpermission UI. They can only silently allow or throw a hard denial.That blocks dynamic safety policies where risk is known only after plugin-side analysis. For example, a plugin can run
git diff --diff-filter=D HEAD...<merge-ref>beforegit merge <merge-ref>and determine that the merge deletes 65 files. The desired behavior is to show a native approval prompt containing that computed reason, then continue the original tool call only when approved.Proposed API
Expose an async permission-request capability to
tool.execute.before, either asinput.ask()or through an equivalent plugin context API.The request should use the existing native permission flow:
once: continue this tool callalways: persist the normal session-scoped approval for the supplied patternreject: return a permission-denied tool result and do not executeAcceptance criteria
tool.execute.beforeafter inspecting validated tool args.denyrules remain terminal and cannot be bypassed by plugins.Related work and distinction
permission.askplugin hook is defined but not triggered #7006 / fix(opencode): add permission.ask plugin hook back #19453: wirepermission.askso plugins can intercept existing staticaskdecisions. This proposal needs the inverse direction: a plugin creates a new approval request after dynamic analysis.ask()capability fromtool.execute.before; it was auto-closed for inactivity. This is a refreshed v2/dev request with concrete dynamic-deletion semantics and acceptance criteria.Why this is general-purpose
Beyond deletion thresholds, this enables policy plugins for schema migration review, release/cutover gates, staged write access, and external governance systems. The plugin decides when a tool call needs human review; OpenCode remains the authority that renders and records the approval.