anomalyco / anomalyco/opencode
plugin: permission.evaluate ask verdicts are ignored on beta-19151 (deny still honored)
@jlongster is already working on this.
Since Sep 5, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- Avg merge
- 7h 2m
- Merged PRs (30d)
- 384
Description
Summary
On 0.0.0-beta-19151, an ask verdict set by a permission.evaluate plugin hook is ignored: the tool executes as if allowed and no prompt ever surfaces. A deny verdict from the same hook is still honored (permission.rejected). Any plugin implementing approval policies through this hook (ask-tier) is therefore silently bypassed on this beta, while deny-tier keeps working.
Environment
- opencode version:
opencode2 v0.0.0-beta-19151 - OS: macOS, Darwin 25.5.0 (darwin arm64)
- Terminal: VS Code integrated terminal (
TERM_PROGRAM=vscode,TERM=xterm-256color) - Shell:
/bin/zsh - Install/channel: beta
- Active plugins: one local file plugin (
~/.config/opencode/plugins/security-guard.js, V2 object form) plus, during the investigation only, a minimal tap plugin (removed afterwards; production state restored and verified healthy viascripts/doctor.mjs --live)
Reproduction
-
Install a minimal tap plugin (V2 object form) at
~/.config/opencode/plugins/zzz-eval-tap.js:import { appendFileSync } from "node:fs" export default { id: "zzz-eval-tap", async setup(ctx) { await ctx.permission.hook("evaluate", (event) => { try { const res = Array.isArray(event?.resources) ? event.resources : [] if (res.some((r) => String(r).includes("CANARY-ASK-TEST")) && event) { event.effect = "ask" event.message = "tap canary: direct ask, no plugin logic involved" } if (res.some((r) => String(r).includes("CANARY-DENY-TEST")) && event) { event.effect = "deny" event.message = "tap canary: direct deny, no plugin logic involved" } appendFileSync("/tmp/eval-tap.log", JSON.stringify({ action: event?.action, effect: event?.effect, n: res.length, }) + "\n") } catch {} }) }, } -
Restart the service (
opencode2 service restart). -
In an agent session, run
echo CANARY-ASK-TEST. -
Observe: the command executes (
CANARY-ASK-TESTis printed, no prompt), while/tmp/eval-tap.logcontains{"action":"shell","effect":"ask","n":…}— proving the hook fired and the ask verdict was recorded, then ignored. -
Run
echo CANARY-DENY-TEST. Observe: the command is blocked withpermission.rejectedcarrying the tap message —denyis honored.
Expected Behavior
An ask verdict set by a permission.evaluate hook produces an approval prompt (the behavior on 0.0.0-beta-19086 for non-cd commands, per #47356), blocking execution until the user responds.
Actual Behavior
The ask verdict is silently dropped: execution proceeds as if the effect were allow. Consistently reproducible across many commands and several service restarts. deny from the same hook works correctly, and tool.execute.before throws still block — the gap is specific to the ask effect of the permission pipeline.
Impact: every ask-tier policy implemented via permission.evaluate (approval prompts for ambiguous operations) silently becomes allow on this beta. Only deny-tier enforcement can be relied on.
Additional Context
- Related: #47356 (
permission.evaluatenot firing forcd-led commands on beta-19086 — same channel family, one beta earlier). - Secondary observation: on beta-19151, shell
resourcesarrive pre-segmented — one resource per;-segment (echo a; echo byieldsn:2; a 4-statement command yieldedn:4). The deny canary confirms adenyon the whole event still blocks the full command. Plugin authors matching whole-command patterns againstresourcesshould be aware the unit changed from whole-command (19086) to per-segment (19151). - Service log note: per-command
evaluated permission=…INFO lines present on older betas no longer appear at all on 19151 (onlyspawning processlines), consistent with an evaluation-pipeline change. - Workaround used by the affected plugin: enforce ask-tier verdicts for shell commands as hard blocks inside
tool.execute.before(which still throws reliably), at the cost of losing the approve-and-continue workflow for legitimate ambiguous operations. - Evidence method: tap log lines plus observed command outcomes; the tap plugin was removed and the service restarted afterwards (only the production plugin remains;
doctor --livehealthy).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.