fix(sessions): seed the immediate-retry approval bypass for every approved scope - #1800
Merged
Aaronontheweb merged 3 commits intoAug 7, 2026
Conversation
…roved scope A shell command the user approved could still throw ToolApprovalRequiredException on its immediate retry. When the command is a pipeline, its standalone verbs (base64, head, ...) have no path argument, so "Always here" cannot persist a directory-scoped grant for them (by design). The one-time bypass that lets a just-approved call run this once was seeded ONLY for ApprovedOnce, so ApprovedSession / ApprovedAlways re-hit the gate on retry — the verbs the durable grant could not cover read as unapproved — and the turn failed with "I encountered an error executing a tool" for a command the user had approved. This is the trigger behind the self-hosted/DeepSeek approval failures. In a parallel batch approved call-by-call it presented as one sibling running and the other throwing (session-scoped verb-only match worked; the persistent grant did not cover the standalone verbs). Seed the one-time bypass for the just-approved call regardless of scope, in both the live pipeline (SessionToolExecutionPipeline) and the cold re-drive plan (LlmSessionActor.BuildApprovalRedrivePlan). This matches the sub-agent loop, which already seeds for every approved scope. The bypass is per-call, pattern-scoped, and cleared after the attempt, so it only authorizes the immediate retry the user approved; broader scopes still record their durable grant separately for future calls. Regression test drives ApprovedAlways through the pipeline against an executor that requires the bypass on retry: ToolExecutionFailed before, runs after.
…dicate Route the three "approval granted" seed sites through one predicate instead of duplicating the four-scope list: the live pipeline retry (SessionToolExecutionPipeline), the cold re-drive plan (LlmSessionActor.BuildApprovalRedrivePlan), and the sub-agent loop (SubAgentActor). Adds ApprovalDecision.IsApprovalGrant() and the ParentApprovalDecision twin. A future approval scope now updates one predicate, not three. Missing a site had reintroduced exactly the "approved command still fails on retry" bug this branch fixes, for the new scope (addresses the code-review maintainability finding).
Reference the tracking issue from the pipeline/session-actor fix comments and the pipeline regression test, and trim the inline explanation now that the issue carries the full analysis.
Aaronontheweb
commented
Aug 7, 2026
| /// <summary> | ||
| /// Extensions over <see cref="ParentApprovalDecision"/>. | ||
| /// </summary> | ||
| public static class ParentApprovalDecisionExtensions |
| // Retry execution now that approval is granted | ||
| // (Approve-once is retried through transient context state; broader scopes | ||
| // are also recorded by the session actor into the shared approval service.) | ||
| if (decision == ApprovalDecision.ApprovedOnce) |
Aaronontheweb
enabled auto-merge (squash)
August 7, 2026 19:27
Aaronontheweb
disabled auto-merge
August 7, 2026 19:52
Merged
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.
Closes #1802
The bug
A shell command the user approved could still fail with "I encountered an error executing a tool." This is the trigger behind the self-hosted/DeepSeek approval failures (the history-wedge amplifier was fixed separately in #1796).
Confirmed mechanism (not theorized)
For a piped command like
gh api … 2>/dev/null | base64 -d | head, the approval gate extracts these candidates:The standalone pipeline verbs
base64/headhave no path argument, so:Always here(persistent, directory-scoped) cannot persist a grant for them — their directory defaults tocwd, and whencwdis the session scratch dir the dead-on-arrival guard drops them. This is by design (you can't foldcurl/gh/base64to a directory).This chat(session-scoped) records them verb-only, so it works.The one-time bypass that lets the just-approved call run this once was seeded only for
ApprovedOnce(SessionToolExecutionPipeline.cs). SoApprovedSession/ApprovedAlwaysre-hit the gate on retry, the uncovered verbs read as unapproved, and the turn failed — for a command the user had just approved. In a parallel batch approved call-by-call this looked like one sibling running (session, verb-only match) and the other throwing (persistent grant didn't cover the standalone verbs), with noapproval_near_misslogged (nothing was persisted to compare) — exactly what the production logs showed.The fix
Seed the one-time bypass for the just-approved call regardless of scope, in both the live pipeline and the cold re-drive plan (
LlmSessionActor.BuildApprovalRedrivePlan). This matches the sub-agent loop (SubAgentActor), which already seeds for every approved scope and documents why. The bypass is per-call, pattern-scoped, and cleared after the attempt, so it only authorizes the immediate retry the user approved; broader scopes still record their durable grant separately for future calls. It does not change what "Always here" persists (standalone verbs remaining non-persistable is intentional).Reproduction & tests
A pipeline regression test drives
ApprovedAlwaysagainst an executor that requires the bypass on retry (standing in for a command whose durable grant doesn't cover every verb):ToolExecutionFailedbefore, runs after. Full Sessions + Tools suites stay green (1331 passed), including the existingApprovedOnce/ApprovedSession/ApprovedAlwayspipeline tests, the shell-approval matrix, and all sub-agent tests.