Skip to content

feat(plugin): let tool.execute.before request native permission approval #37164

Description

@shlee87

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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions