Repository navigation
fix: respect user decisions on permission.acp_status in host config write (#457, #465) - #466
Merged
Merged
Conversation
…te (#457) The config hook wrote { ...permission, compress: X, acp_status: "allow" } whenever no explicit compress rule existed — the spread re-keyed an explicit user acp_status to "allow" (later key wins), silently inverting the access decision. The inverted map was then cached in hostPermissions.global and read back by the V1 host itself. Extract the merge into pure applyAcpToolPermissions() in lib/host-permissions.ts: explicit compress rule defers fully (map returned unchanged); otherwise acp_status defaults to allow only when absent. Regression tests verified RED against the buggy logic, GREEN after fix.
Contributor
📦 Built Plugin ArtifactBranch: Option A — Install from npm PR tag (recommended)opencode plugin opencode-acp@pr-466 --globalEach push to this PR publishes a new version under the Option B — Install from GitHubopencode plugin "github:ranxianglei/opencode-acp#2026-09-29_respect-explicit-acp-status" --globalOption C — Download artifact
tar xzf opencode-acp-pr466.tgz
cp -r package/dist ~/.cache/opencode/packages/opencode-acp@latest/node_modules/opencode-acp/dist
This comment is automatically updated on each push. |
Owner
Author
…te (#465) The #457 guard only checked for an exact own acp_status key, so a user wildcard decision resolving acp_status ('*': 'deny', 'acp*': 'deny') was still overwritten by ACP appending acp_status: 'allow' last (last-match wins). New anyRuleResolves() gates the default write on rule resolution instead of key presence, reusing the same rule machinery the host resolution models.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem ([#457], [#465])
An explicit user decision on
permission.acp_statusinopencode.jsonwas silently rewritten to"allow"by the plugin's config hook:[#457] — the guard only checked for the
compresskey, neveracp_status. A user config of{ "permission": { "acp_status": "deny" } }(nocompressrule) came out as"allow". The inverted map was then cached intohostPermissions.global, and because the V1 host reads its own permissions from the same object, the deny was honoured by nobody — no log, no error, recovery only via config edit + restart.[#465] — even after the exact-key guard, a user wildcard decision resolving
acp_status("*": "deny","acp*": "deny") still got overwritten: no ownacp_statuskey exists, so ACP appendedacp_status: "allow"after the user's rules — and last-match resolution makes the appended default win. Grouped here per owner scoping; both instances share one root cause: the guard tested key presence instead of rule resolution.Fix
Extracted the merge into a pure function
applyAcpToolPermissions(permissionConfig, compressPermission)inlib/host-permissions.ts(the plugin factory inindex.tshas heavy runtime deps and isn't unit-testable directly):compressrule → the user owns the map, returned unchanged (same identity) — defer-by-design preserved.compressfrom ACP config, and defaultacp_statusto"allow"only when no user rule resolves it.New helper
anyRuleResolves(permissionConfig, tool): true for an exact own key in any value form (scalar, nested pattern object, degenerate empty object — key presence short-circuits), or any wildcard key ("*","acp*") matched through the existing rule machinery (getPermissionRules+findLastMatchingRule+wildcardMatch) — the same last-match model the host resolution uses. An explicitacp_status(deny/ask) and a wildcard resolving it now survive untouched.index.tsreplaces the 8-line inline block with one call to this function. The bili-proxydenyAcpToolspath (intentional full deny) is untouched. Thecompressdefer guard deliberately stays key-based (owner scoped this fix toacp_status; wildcard-deny oncompressis already handled by the separate self-disable path).Behavior change disclosure (old → new)
opencode.json)permission.acp_statusabsent, no wildcard matches itacp_status: "allow"writtenpermission.acp_status: "deny"/"ask", nocompressrule ([#457])"allow"❌acp_statusremoves the tool from the LLM tool list, as denying it should do)acp_status("*","acp*") withdeny/ask/allow, nocompressrule ([#465])acp_status: "allow", which wins last-match and erases the user's decision ❌acp_statusfrom the user's wildcard ✅"bash*": "deny")acp_status: "allow"writtencompressrule presentNo state format, config schema, persistence, or public API change. No version bump (release workflow handles that).
Verification
permission.acp_status: "deny"молча переписывается в"allow"#457]: temporarily reintroduced the unconditional overwrite → exactly the two [BLOCKER][V1] Явныйpermission.acp_status: "deny"молча переписывается в"allow"#457 regression tests fail (13 pass / 2 fail); restored fix → green.{"*":"deny"}→{"*":"deny","compress":"allow","acp_status":"allow"}(wildcard erased); post-fix the same input returns{"*":"deny","compress":"allow"}. New tests written first against the unfixed guard confirmed RED (missing-export failure mode), then green after the fix.tests/host-permissions.test.ts: 15 baseline/[BLOCKER][V1] Явныйpermission.acp_status: "deny"молча переписывается в"allow"#457 + 8 new MINOR: the #457 fix only covers the exact own-key case — a user wildcard decision on acp_status is still overwritten #465 (wildcard*/acp*deny, wildcard ask/allow, non-matching wildcard still gets default, nested pattern form, wildcard+explicit-compressidentity defer,anyRuleResolvesunit matrix incl. degenerate empty object) — targeted suite 23/23 ✅npm run typecheck✅ ·npm run build✅ (both functions present indist/index.js) ·prettier --checkclean ✅/tmpread-only:soft-block.test.tsmkdir EACCES;inactive-block-decompress.test.tshard-coded/tmppath rejected by the toFile validator). Neither file imports any module changed here; both pass on CI where/tmpis writable.Devlog:
devlog/2026-09-29_respect-explicit-acp-status/(REQ + WORKLOG, extended for #465 scope).Fixes #457
Fixes #465