Skip to content

fix: ask native permissions for OpenCode 2 image saves - #109

Open
oakimov wants to merge 1 commit into
mainfrom
fix/opencode2-image-permission
Open

oakimov wants to merge 1 commit into
mainfrom
fix/opencode2-image-permission

Conversation

@oakimov

@oakimov oakimov commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Problem

On OpenCode 2, cursor_image_save wrote generated images after the containment check without asking anything. Cursor's default assets/ folder is outside the workspace, so OpenCode 1.x asks external_directory and then edit for the same write, but OpenCode 2 asked for neither. OpenCode 2 now gives plugins ctx.permission.assert (added in v2.0.29), so the plugin can ask the same way.

Fix

  • When the host provides ctx.permission, image save asks external_directory and then edit, using the same resources as OpenCode's own write: <dir>/* for the external folder, and the absolute path for an external file (a relative path inside the workspace). The session, agent and tool call are sent as the source, so saved "always" answers and session rules apply.
  • A plain decline is rethrown unchanged, so OpenCode ends the step exactly as it does for its own tools.
  • Deny rules and rejections with feedback keep the existing CursorImagePermissionDenied: marker, so Cursor still receives write_result.permission_denied. The fork PR this is based on rethrew every refusal raw, which turned those into generic tool errors for Cursor.
  • Hosts without ctx.permission keep the previous behaviour: catalog edit gating plus containment.

Based on moritzWa#1 by @moritzWa. That PR's event-subscription cleanup is already on main; its parity report and packed-host script are not included.

OpenCode 1.x is unchanged: it already asks through the tool context's ask.

Verification

  • Rebased onto main after fix: retry an interrupted Run like Cursor CLI #103, fix: open no early edit row when the edit's read goes to the host #107 and ci: measure pull request coverage on the PR head #108 merged. Typecheck/build, the full coverage suite (2,580 pass, zero failures), built architecture checks, Node HTTP/2 regressions and all 54 pricing fixtures pass. The rebased tree exactly matches that tested integration. The original live permission checks below were performed before this rebase; no additional live Cursor run is claimed.
  • New tests in test/plugin-opencode2.test.ts cover: order and request shape; a target inside the workspace; a decline at each step keeping its identity and writing nothing; deny and feedback keeping the marker. Each test fails if the decline handling is removed or replaced with the fork's version.
  • Live on OpenCode 2.0.32 with cursor/composer-2.5 and explicit ask rules. The default build agent allows everything, so there is no prompt without them:
Case Before (main) After
No rules Saved, no prompt Saved, no prompt (default allow)
Reject with feedback Saved, no prompt Prompted external_directory for <assets>/*; Cursor reported permission denied (operation=write …); no file
Deny edit for <assets>/* Saved Denied without a prompt; Cursor got permission denied … Permission denied: edit; no file
Decline edit, no feedback Saved Call marked declined, step ended; on the next message Cursor got rejected (reason=The user declined this tool call); no file

The "Before" column follows from the code on main, which never asks on OpenCode 2; I didn't run it live.

Prompt caching: unaffected. The change only touches the image-save tool's execution; RequestContext and checkpoints are unchanged.

@codecov

codecov Bot commented Oct 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

Adds native permission requests to OpenCode 2 image saves.

  • OpenCode 2 asks before saving Cursor-generated images.

No actionable issues were found. The previous review commit was unavailable, so the full PR diff was reviewed. No previous review threads were supplied. Tests were inspected but not run.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Resolve staged image and check containment] --> B{Host offers permission.assert?}
  B -- No --> C[Keep existing permission fallback]
  B -- Yes --> D[Ask external_directory if outside workspace]
  D --> E[Ask edit]
  E -- Allowed --> F[Write image]
  C --> F
  D -- Plain decline --> G[Rethrow original decline]
  E -- Plain decline --> G
  D -- Deny or feedback --> H[Return marked permission denial]
  E -- Deny or feedback --> H
Loading

Reviews (2) · Last reviewed commit: "fix: ask native permissions for OpenCode..." · Reviewed by Greptile

OpenCode 2 image saves wrote after containment without any prompt,
although Cursor's default assets folder is outside the workspace. Use the
public plugin ctx.permission.assert when the host offers it: ask
external_directory, then edit, with the resources and tool-call source
OpenCode's own write tool uses. Hosts without the domain keep catalog edit
gating plus containment.

A plain decline is rethrown unchanged so OpenCode ends the step as it does
for native tools. Deny rules and rejections with feedback keep the
CursorImagePermissionDenied marker so Cursor still receives
write_result.permission_denied.

Based on moritzWa#1.

Co-authored-by: Moritz Wallawitsch <42035131+moritzWa@users.noreply.github.com>
@oakimov
oakimov force-pushed the fix/opencode2-image-permission branch from cacf0d2 to 43ac233 Compare October 18, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant