You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today the approval card on a session offers three buttons: Allow once, Always allow, Deny. The label Always allow reads as a global, persistent allow list, but the underlying rule is actually session-scoped on the agent side:
Claude Code: the rule is a permissionUpdates entry with destination: "session" attached to one Claude session id (server/lib/agents.ts, buildClaudeApprovalResponse). It dies with that session.
Codex: the rule is acceptForSession inside one live codex app-server thread runtime (server/lib/codex-app-server.ts, mapApprovalDecision). It dies with the thread.
Because Controller keeps Claude and Codex sessions alive across turns (and across user visits to the same conversation), in practice these rules can live as long as the conversation does — but they still don't leak across conversations or worktrees. A "Reset" affordance in Settings (#259, PR #260) was the wrong surface for this: Codex's app-server manager is process-global rather than worktree-scoped, so the copy couldn't honestly claim a worktree scope, and the action is easy to regret. #260 is being closed in favor of fixing this at the source: the approval card itself.
Goal
Make the scope of "Always allow" obvious at the moment the user clicks it, so they don't grant it thinking they're adding a permanent global rule.
Proposed changes
All in client/src/pages/SessionView.tsx, inside ToolApprovalBlock and the tool_approval_response renderer.
Rename the button from Always allow to Allow for this session. Drop the parenthetical — the new label stands on its own.
Add a tooltip / help line under the button row explaining what "session" means in this product. A muted one-liner under the three buttons:
"Allow for this session = same conversation. Won't ask again for similar commands here. Closed conversations forget it."
Keep it short; if we add it to every approval card we should not blow up the visual weight — text-xs text-muted-foreground under the button row.
Update the post-decision summary so the user can see what just happened. Today the tool_approval_response event renders a centered divider with one of: Approved, Approved (always allow), Denied. Change it to:
Allowed this once for allow_once
Allowed for this session for always_allow, and where the agent's suggestions let us, append the rule that was actually stored — e.g. Allowed for this session — Edit on src/api.ts. If we don't have the structured rule handy in data, fall back to Allowed for this session (no append) so we never show wrong info.
Denied unchanged.
The per-file/per-command suffix is the "Edit has been approved for this file" idea the original issue hinted at — only render it when we have a real rule to show.
Acceptance criteria
Approval card button reads Allow for this session instead of Always allow.
A short scope explainer is visible on every approval card (button row + muted line), explaining that "session" = this conversation.
The post-decision summary line reflects the new labels and, when structured data is available, names the actual rule that was stored.
No changes to the decision enum on the wire (allow_once / always_allow / deny stays as-is in client/src/api.ts and the server handlers).
No changes to plan-mode flow (Approve & implement / Keep planning already says what it does).
Context
Today the approval card on a session offers three buttons: Allow once, Always allow, Deny. The label Always allow reads as a global, persistent allow list, but the underlying rule is actually session-scoped on the agent side:
permissionUpdatesentry withdestination: "session"attached to one Claude session id (server/lib/agents.ts,buildClaudeApprovalResponse). It dies with that session.acceptForSessioninside one livecodex app-serverthread runtime (server/lib/codex-app-server.ts,mapApprovalDecision). It dies with the thread.Because Controller keeps Claude and Codex sessions alive across turns (and across user visits to the same conversation), in practice these rules can live as long as the conversation does — but they still don't leak across conversations or worktrees. A "Reset" affordance in Settings (#259, PR #260) was the wrong surface for this: Codex's app-server manager is process-global rather than worktree-scoped, so the copy couldn't honestly claim a worktree scope, and the action is easy to regret. #260 is being closed in favor of fixing this at the source: the approval card itself.
Goal
Make the scope of "Always allow" obvious at the moment the user clicks it, so they don't grant it thinking they're adding a permanent global rule.
Proposed changes
All in
client/src/pages/SessionView.tsx, insideToolApprovalBlockand thetool_approval_responserenderer.Rename the button from
Always allowtoAllow for this session. Drop the parenthetical — the new label stands on its own.Add a tooltip / help line under the button row explaining what "session" means in this product. A muted one-liner under the three buttons:
Keep it short; if we add it to every approval card we should not blow up the visual weight —
text-xs text-muted-foregroundunder the button row.Update the post-decision summary so the user can see what just happened. Today the
tool_approval_responseevent renders a centered divider with one of:Approved,Approved (always allow),Denied. Change it to:Allowed this onceforallow_onceAllowed for this sessionforalways_allow, and where the agent's suggestions let us, append the rule that was actually stored — e.g.Allowed for this session — Edit on src/api.ts. If we don't have the structured rule handy indata, fall back toAllowed for this session(no append) so we never show wrong info.Deniedunchanged.The per-file/per-command suffix is the "Edit has been approved for this file" idea the original issue hinted at — only render it when we have a real rule to show.
Acceptance criteria
Allow for this sessioninstead ofAlways allow.allow_once/always_allow/denystays as-is inclient/src/api.tsand the server handlers).Approve & implement/Keep planningalready says what it does).Out of scope
always_allowdecision value used internally — it's already a fine wire name; only the user-facing label changes.References
client/src/pages/SessionView.tsx:2255-2272— currentAlways allowbuttonclient/src/pages/SessionView.tsx:1490-1512— currentApproved (always allow)summary line