Repository navigation
Description
Description
The app can automatically approve a permission request for a project whose auto-accept setting is explicitly Off when the app-wide default is On. This happens when the request belongs to a session that the app has not loaded yet. The same project setting works correctly once the session's information is available in the local session store, so the effective approval policy depends on the client's cache state.
Project overrides allow a user to enable automatic approval generally while retaining manual approval for a particular project. For example, one project can follow the global On setting while another project stays Off because its tool requests should be reviewed individually. An incoming request from that second project should remain pending regardless of whether its session already has an open tab, was created by another client, or is a child session that has not been visited.
The problem is in packages/app/src/session/requests/auto-approve.ts. The consider() function takes a shortcut when covers(undefined) is true. That check evaluates the app-wide default without a project ID. With the default On, it immediately calls approve(permission) instead of loading the unknown session's information. The default argument in approve() also reads from the local session store. Because that session is absent, the project ID is still undefined, and the eventual settings lookup again resolves to the global default. The app sends a permission reply with decision: "once" even though the request's actual project has an explicit Off override.
For a cached session, approve() can obtain the project ID and apply the override normally. This explains why opening a session first can make the behavior look correct, and why a test that covers only already-loaded sessions does not catch the problem. The important trigger is a permission event arriving before the client has session metadata, rather than a failure to save or display the project preference.
The expected behavior is to resolve the request's project before deciding whether it is covered by automatic approval. A cached session can continue using its existing information. An unknown session should use the existing session-get operation, then pass the returned project ID through the same server-scoped settings lookup. If that lookup returns Off, the client should leave the request pending for manual review. Projects set to On, and projects that follow an On default, should continue receiving automatic replies.
Plugins
No third-party plugin is required. The reproduction uses the app's existing permission auto-approver and the shared multi-server E2E fixture server.
OpenCode version
Source version 2.0.28. The affected implementation is present on dev at 23e3493ab.
Steps to reproduce
- Connect the app to a server with two projects, such as
apiandweb, and open an existing session so that the connection and settings are ready. - While the app-wide default is Off, set
apito On andwebto Off in project settings. Then turn the app-wide default On. The explicit Off setting forwebshould remain in effect. - Allow the initial permission sweep to finish. Create additional sessions on the server after that sweep, without opening them or sending the app session-created or session-updated events that would populate its cache. The keeper reproduction uses one child session in each project.
- Deliver a
permission.askedevent for each new session. Both requests can ask for the same action, such as runninggit status; their project settings are the relevant difference. - Observe permission replies. The
apirequest should receive one automatic reply, while thewebrequest should receive none. With the affected implementation, both requests receive an automatic reply.
The regression is reproducible in the existing multi-server.spec.ts keeper. The extended project-override case confirms that the sessions were not previously fetched, checks the exact reply identities, opens the blocked session to verify that its permission remains visible, and verifies that returning the project to the global default releases the pending requests. With the proposed fix, all five cases in this keeper pass, and the app package typecheck passes.
Screenshot and/or share link
No screenshot or share link is needed to reproduce this event-handling issue. The deterministic keeper observes the outgoing permission replies and the pending permission shown in the session UI.
Operating System
Verified locally on Windows using Bun 1.4.2. Browser verification used the installed Chromium headless-shell build 1200 with Playwright's shared fixture harness; the project normally selects build 1217.
Terminal
PowerShell was used to run the local checks. The bug is in the app's permission handling and does not depend on the terminal used to launch it.
Linked fix
A focused fix is available in #55178.
Activity
opencode-agent commentedon Oct 13, 2026
Thanks for the detailed report. I reproduced this on the latest
dev(23e3493ab). The auto-approve code there is unchanged from v2.0.28.The script below runs
createPermissionAutoApproverwith a mocked settings module and SDK:apiis set to On, and projectwebis set to Off.permission.askedevent is sent per session.Both requests received an automatic
oncereply, including the one from thewebproject. Only theapirequest should have been answered.The cause is what you described: the
covers(undefined)shortcut inconsider()approves without fetching the session's project first. The open PR #55178 looks like it targets this.Reproduction script (auto-approve-repro.test.ts)
Run it:
bun auto-approve-repro.test.ts