Skip to content

Add a skip field to ool.execute.before (for deterministic pre-execution gating) #52836

Closed

Description

@joeskeen

Hi — I just watched the Fireship video on OpenAPPA ("Did a 50 year old military secret just solve agent prompt injection?") and immediately thought: this would be a great thing to be able to use with OpenCode. The blocker is one missing primitive on the plugin side, which is what this issue is about. APPA just happens to be the first concrete use case I have — the change is generally useful for any plugin that wants to deterministically deny a tool call before it runs.

What's missing

A plugin can hook tool.execute.before today (packages/plugin/src/index.ts:266) and mutate output.args, but the mutated args are discarded at every call site I checked — packages/opencode/src/session/tools.ts:102-132, :175-219, :255-303, :325-385, :398-490, and packages/opencode/src/session/prompt.ts:301-380 all pass the closure-captured original args to item.execute(args, ctx) and ignore the trigger's output. So a plugin cannot currently block a tool call before it executes.

What I'd like

Add a new field to the trigger output, say skip: boolean, and honor it at each call site. Something like:

// packages/plugin/src/index.ts
"tool.execute.before"?: (
  input: { tool: string; sessionID: string; callID: string },
  output: { args: any; skip?: boolean },
) => Promise<void>
// packages/opencode/src/session/tools.ts (illustrative)
const hookOut = { args }
yield* plugin.trigger("tool.execute.before", { tool, sessionID, callID }, hookOut)
if (hookOut.skip) {
  // Synthesize a tool error the model can see.
  return { title: "blocked", output: "blocked by plugin", metadata: { error: "plugin-blocked" } }
}
yield* item.execute(hookOut.args, ctx)

That's it. ~30 lines across the call sites, plus a one-line doc update.

Why not permission.ask?

I considered that, but it's interactive — the user gets a prompt and the block is not deterministic. APPA's contract is "this value cannot flow to this sink, full stop" — no prompt, no override. So I can't use it without weakening the guarantee.

Why not throw?

tool.execute.before exceptions are caught and ignored by the plugin trigger (packages/opencode/src/plugin/index.ts:285-298), so throwing is silently swallowed. Not viable.

Backwards compatibility

The new skip field is opt-in (defaults to false). No existing plugin breaks.

What I'd do

If this lands, I'll maintain an OpenCode plugin that wraps OpenAPPA in the opencode-ai org. The plugin is straightforward and follows exactly the shape Amp's appa plugin uses today — OpenAPPA already serves Claude Code, Amp, and Kagent the same way.

I'm happy to:

  • Open a draft PR with the change.
  • Write a small unit test that asserts skip short-circuits the call.
  • Add the field to the plugin docs in packages/docs/.

Before I do that, two questions:

  1. Naming. skip? denied? output.skip is my preference because it parallels output.args and reads naturally ("the trigger skipped the call"). Open to alternatives.
  2. Where the skip surfaces to the model. Two options: (a) silent — the tool returns a synthetic error block the model sees as [blocked by plugin] <reason>, no TUI event; or (b) emit a permission.asked-style event so the TUI shows the block reason. Option (a) is what Amp plugins do ({ action: 'reject-and-continue' }); option (b) is more visible. Lean toward (a) for the first cut, but happy to do (b).

If this isn't a direction the maintainers want, I'd appreciate a pointer to a better extension point. The integration degrades to a post-hoc-only design without it (block-after-run), which is weaker but still shippable — so I'm not blocked on this landing, just constrained.

Thanks!

Activity

  1. github-actions commented on Oct 2, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • The Feature Request title must start with [FEATURE]:.
    • Add the required verification checkbox from the Feature Request template, confirming the feature has not already been suggested.

    Please edit this issue to address the above within 72 hours, or it will be automatically closed.


    This issue might be a duplicate of existing issues. Please check:

    If you believe this was flagged incorrectly, please let a maintainer know.

  2. joeskeen commented on Oct 2, 2026

    @joeskeen
    Author

    Closing this as a duplicate of #52837 — same ask, same proposal. Filing the fix to the contributing guidelines on #52837.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

needs:complianceThis means the issue will auto-close after 2 hours.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions