Skip to content

permission.ask plugin hook is defined but not triggered #7006

@markerikson

Description

@markerikson

Description

Hi! First time filing an issue with OpenCode. Brand new user within the last few days. Tried using the new Permissions system from #6319 , tried to use it in a Plugin to customize behavior with some auto-approvals. Opus wrote the plugin for me, but it doesn't seem to be doing anything. It's being detected, loaded, and initialized, but not actually executing.

Here's the Opus-generated issue description and suggested fix:

Description

The permission.ask hook is defined in the plugin SDK types (@opencode-ai/plugin) but is never actually triggered by the permission system. This prevents plugins from intercepting permission requests and programmatically allowing/denying them.

Current Behavior

The PermissionNext.ask() function in packages/opencode/src/permission/next.ts evaluates rules and, if the result is "ask", immediately publishes a bus event for the UI:

if (rule.action === "ask") {
  const id = input.id ?? Identifier.ascending("permission")
  return new Promise<void>((resolve, reject) => {
    const info: Request = {
      id,
      ...request,
    }
    s.pending[id] = {
      info,
      resolve,
      reject,
    }
    Bus.publish(Event.Asked, info)  // Goes straight to UI
  })
}

The Plugin.trigger("permission.ask", ...) is never called.

Expected Behavior

Before prompting the user, the system should call the permission.ask plugin hook, allowing plugins to override the decision:

if (rule.action === "ask") {
  const id = input.id ?? Identifier.ascending("permission")
  const info: Request = {
    id,
    ...request,
  }
  
  // Let plugins intercept
  const hookOutput = await Plugin.trigger(
    "permission.ask",
    info,
    { status: "ask" as "ask" | "allow" | "deny" }
  )
  
  if (hookOutput.status === "allow") {
    return  // Auto-approved by plugin
  }
  if (hookOutput.status === "deny") {
    throw new DeniedError([])  // Or a new PluginDeniedError
  }
  
  // Still "ask" - prompt the user
  return new Promise<void>((resolve, reject) => {
    s.pending[id] = {
      info,
      resolve,
      reject,
    }
    Bus.publish(Event.Asked, info)
  })
}

Use Case

This would enable plugins like:

export const AutoApprovePlugin: Plugin = async (ctx) => {
  return {
    "permission.ask": async (input, output) => {
      // Auto-approve bash commands matching safe patterns
      if (input.permission === "bash") {
        const cmd = input.metadata?.command ?? input.patterns[0]
        if (isSafeCommand(cmd)) {
          output.status = "allow"
        }
      }
      
      // Auto-approve file ops in specific directories
      if (["read", "edit"].includes(input.permission)) {
        if (input.patterns.every(p => p.startsWith("/safe/path/"))) {
          output.status = "allow"
        }
      }
    },
  }
}

This is similar to Claude Code's PreToolUse hook pattern, which allows compositional command analysis (e.g., timeout 30 git status → strip wrapper → check core command).

Repro

Here's a tiny permissions plugin that demonstrates the intended usage:

// ~/.config/opencode/plugin/test-permission-hook.ts
import type { Plugin } from "@opencode-ai/plugin"
import { appendFileSync } from "fs"

const LOG = "/tmp/permission-hook-test.log"

function log(msg: string) {
  appendFileSync(LOG, `[${new Date().toISOString()}] ${msg}\n`)
}

const TestPermissionHook: Plugin = async () => {
  log("Plugin loaded")

  return {
    "permission.ask": async (input, output) => {
      log(`permission.ask called: ${input.permission}`)
      log(`  patterns: ${JSON.stringify(input.patterns)}`)
      log(`  metadata: ${JSON.stringify(input.metadata)}`)

      // Auto-approve any 'date' command as a test
      if (input.permission === "bash" && input.patterns.some(p => p.includes("date"))) {
        log(`  -> Setting status to "allow"`)
        output.status = "allow"
      }
    },
  }
}

export default TestPermissionHook

Suggested Fix

diff --git a/packages/opencode/src/permission/next.ts b/packages/opencode/src/permission/next.ts
index abc123..def456 100644
--- a/packages/opencode/src/permission/next.ts
+++ b/packages/opencode/src/permission/next.ts
@@ -5,6 +5,7 @@ import { Identifier } from "@/id/id"
 import { Instance } from "@/project/instance"
 import { Storage } from "@/storage/storage"
 import { fn } from "@/util/fn"
 import { Log } from "@/util/log"
 import { Wildcard } from "@/util/wildcard"
+import { Plugin } from "@/plugin"
 import z from "zod"
 
 export namespace PermissionNext {
@@ -97,15 +98,27 @@ export namespace PermissionNext {
       if (rule.action === "deny")
         throw new DeniedError(ruleset.filter((r) => Wildcard.match(request.permission, r.permission)))
       if (rule.action === "ask") {
         const id = input.id ?? Identifier.ascending("permission")
+        const info: Request = {
+          id,
+          ...request,
+        }
+
+        // Let plugins intercept the permission request
+        const hookResult = await Plugin.trigger(
+          "permission.ask",
+          info,
+          { status: "ask" as Action }
+        )
+
+        if (hookResult.status === "allow") {
+          log.info("plugin auto-approved", { permission: request.permission, pattern })
+          return
+        }
+        if (hookResult.status === "deny") {
+          log.info("plugin auto-denied", { permission: request.permission, pattern })
+          throw new DeniedError([{ permission: request.permission, pattern, action: "deny" }])
+        }
+
         return new Promise<void>((resolve, reject) => {
-          const info: Request = {
-            id,
-            ...request,
-          }
           s.pending[id] = {
             info,
             resolve,
             reject,
           }
           Bus.publish(Event.Asked, info)
         })
       }
       if (rule.action === "allow") continue
     }
   },
 )

Environment

  • OpenCode version: 1.1.x (post permissions PR Permission rework #6319)
  • The hook type exists in @opencode-ai/plugin but implementation is missing

Plugins

CodeNomad; custom permissions auto-approve plugin

OpenCode version

1.1.2

Steps to reproduce

  1. Add the listed repro permissions plugin to OpenCode
  2. Run a command with date
  3. See that the command was not auto-approved

Screenshot and/or share link

No response

Operating System

Windows 11, WSL Ubuntu

Terminal

CodeNomad UI (server in WSL, browser UI in Windows Chrome)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions