fix: filter question tool and auto-approve permissions in non-interactive mode#14607
fix: filter question tool and auto-approve permissions in non-interactive mode#14607radical-beard wants to merge 2 commits intoanomalyco:devfrom
Conversation
…ractive mode The question tool bypassed the permission pipeline by calling Question.ask() directly without going through ctx.ask(). Combined with LLM.resolveTools() only consulting agent-level permissions (where the build agent explicitly allows question), this caused opencode run to hang indefinitely when the model invoked the question tool (anomalyco#11899). Three changes fix this: 1. LLM.resolveTools() now accepts session-level permissions and merges them with agent permissions when computing disabled tools, so session rules like question:deny actually filter the tool from the tool list. 2. SessionPrompt passes the session's permission ruleset through to the LLM layer via the new StreamInput.permission field. 3. The question tool now calls ctx.ask() before Question.ask(), so even if the tool isn't filtered out, the permission pipeline is consulted. On denial, it returns a helpful message instead of hanging. Refs anomalyco#11899 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
The following comment was made by an LLM, it may be inaccurate: Found potential related PRs:
These PRs appear to be addressing the same core issues (question tool hangs and permission system blocking operations in non-interactive/automation scenarios). You should review them to ensure this PR doesn't duplicate work or conflict with previous attempts to fix these issues. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
The permission.asked event handler in run.ts auto-rejected all permission requests, causing any tool requiring "ask" permission (like file writes in certain directories) to fail. Users reported that opencode run would start a loop, run ls, then exit without doing useful work (anomalyco#13851). Explicit "deny" rules still throw DeniedError before reaching the event handler, so changing the default reply from "reject" to "once" is safe -- it only affects "ask" rules that need interactive confirmation. A --no-auto-approve flag is added for CI environments where users want strict permission checking (original reject behavior). Refs anomalyco#13851 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
55a8f1c to
4802c33
Compare
Issue for this PR
Closes #11899
Closes #13851
Type of change
What does this PR do?
opencode runhas two compounding bugs that make it unusable for automation:Bug 1 — Question tool hangs (#11899):
run.tssetsquestion: denyon the session, but this never reaches the LLM tool-filtering layer.LLM.resolveTools()only checks agent-level permissions, and thebuildagent explicitly setsquestion: "allow". The question tool also callsQuestion.ask()directly without going throughctx.ask(), so the session deny rule is never evaluated. Result: model calls question tool →Question.ask()returns a Promise waiting forQuestion.reply()which never comes → process hangs.Bug 2 — Permission blocking (#13851): The
permission.askedevent handler inrun.tsauto-rejects all permission requests. Any tool with"ask"permission (file writes in certain directories, .env reads, external directory access) fails immediately. The model gets errors, retries a few times, gives up. Users see: "starts loop, runs ls, exits."Fix 1 (3 files):
llm.ts: Addedpermissionfield toStreamInput.resolveTools()now merges session permissions with agent permissions, soquestion: denyfrom the session actually removes the question tool from the tool list.prompt.ts: Passessession.permissionthrough to the LLM layer.question.ts: Addedctx.ask()call beforeQuestion.ask(). If denied, returns a message ("Make your best judgment and proceed") instead of hanging.Fix 2 (1 file):
run.ts: Changed thepermission.askedhandler reply from"reject"to"once"(auto-approve). This only affects"ask"rules — explicit"deny"rules throwDeniedErrorbefore the event handler is ever reached, so they're unaffected. Added--no-auto-approveflag for CI environments that want the old strict behavior.I understand why these changes work: the permission system uses
findLast()semantics (last matching rule wins). Session permissions appended after agent permissions override them. Thedisabled()function checks fordeny+ wildcard pattern*to filter tools pre-LLM. The event handler only sees"ask"rules because"deny"rules short-circuit withDeniedErrorinPermissionNext.ask().How did you verify your code works?
opencode run "Create a file called test.txt with 'hello world'" --format jsonScreenshots / recordings
N/A — CLI-only change
Checklist