-
Notifications
You must be signed in to change notification settings - Fork 0
approval mode picker
Date: 2026-07-19 Tracking: Issue #75, sub-issues #76, #77, and #78 Status: Reviewed and corrected before implementation
Add an Agent-only permission picker to the chat tool window. The picker provides manual approval, approval by the Codex auto-reviewer, Full access, the effective Codex configuration, and capability-gated custom permission profiles. The existing Chat mode remains read-only.
This revision corrects four blockers in the original plan:
- Approval on the user's behalf is controlled by
approvalsReviewer=auto_review, not the deprecatedapprovalPolicy=on-failurevalue. - Full access bypasses the Codex sandbox and normal approval requests. The Worker policy engine cannot inspect an operation when app-server emits no approval request.
- General
[profiles.*]entries are process-start configuration profiles and cannot be safely reduced to two per-turn strings. Permission entries must come from app-server's permission-profile API instead of an extension-owned TOML parser. -
turn/startoverrides remain effective on subsequent turns. Omitting fields for Custom does not reset a thread that already received an override.
The bundled protocol schemas and current app-server behavior expose these separate axes:
-
approvalPolicy:untrusted,on-failure,on-request, ornever.on-failureis deprecated and is not the auto-review mode. -
approvalsReviewer:user,auto_review, or the legacy compatibility valueguardian_subagent. -
sandboxPolicy: structuredreadOnly,workspaceWrite,externalSandbox, ordangerFullAccesspolicies. -
permissionProfile/list: lists built-in and[permissions.<id>]profiles for a working directory, with pagination. - Experimental turn
permissions: selects a complete permission profile by ID and must not be sent together with low-level sandbox overrides. -
thread/settings/updatedand thread start/resume/fork responses report effective thread permission state. The UI must not treat the persisted default as that state.
Permission-profile selection is enabled only when the configured experimental API and
the runtime capability/schema both support it. Unsupported runtimes retain the four
built-in choices without parsing config.toml directly.
| Picker entry | approvalPolicy | approvalsReviewer | sandboxPolicy | Persisted ID |
|---|---|---|---|---|
| Ask for approval | on-request |
user |
{type: workspaceWrite} |
ask |
| Approve on my behalf | on-request |
auto_review |
{type: workspaceWrite} |
auto |
| Full access | never |
user |
{type: dangerFullAccess} |
full |
| Custom (config.toml) | no override on a new/base thread | no override | no override | custom |
Permission: <id>
|
omitted | omitted | omitted; send permissions=<id>
|
permission:<id> |
| Chat mode | never |
user |
{type: readOnly} |
not persisted by this picker |
untrusted is a stricter safe-command trust policy and is not used as the normal
manual-review preset. Built-in mapping must be resolved as one tuple so policy,
reviewer, and sandbox cannot drift independently.
The UI holds two different values:
- Desired default: the stable selection saved for a future turn or new thread.
- Effective thread state: approval policy, reviewer, sandbox, and active permission profile reported by app-server for the selected thread.
/status displays both when they differ. Thread start, resume, fork, switching, and
thread/settings/updated reconcile the effective value without overwriting the user's
desired default.
Custom means "use the Codex configuration without extension overrides." Because an override sent to an existing thread applies to subsequent turns, switching from Ask, Auto, Full, or a permission profile back to Custom requires a fresh thread unless the target app-server later provides a verified clear operation. The UI must explain and offer that transition; merely serializing null or omitting the fields is not a reset.
Full access disables the Codex sandbox and normal approval prompts. The Worker's
IApprovalPolicyEngine and ProtectedDirectoryPolicy run only after app-server sends
an approval request, so they are not an independent enforcement boundary for Full
access.
Therefore:
- Full access requires an explicit confirmation using the same path from the ComboBox and slash command.
- The warning is visible and available through automation help text; it is not conveyed by color alone.
- A saved Full access ID is not silently restored after restart. The user must confirm it again, otherwise startup uses Custom.
- Tests must verify that Full access can produce no approval request and must not claim that a forced Fake-server request proves end-to-end protection.
- Use a
[DataContract]option type with[DataMember]ID, display name, description, and source. Bind the ComboBox by stable ID (SelectedValuePath) rather than persisting display text. - Mark the option collection, selected ID, and computed Agent-only enablement property
with
[DataMember]. Raise dependentPropertyChangednotifications from Mode changes. - Resolve the full wire tuple through one catalog/resolver used by turn creation and
/status; unknown Mode values fail to Custom and never apply a saved Full access mode. - Inject an extension settings store so tests never write to the user's AppData. Save atomically and serialize concurrent saves.
- Keep a placeholder for a persisted dynamic profile until successful discovery. RPC failure preserves the saved choice. After a successful catalog load, select Custom before removing an absent profile.
- Treat profile IDs/descriptions as untrusted UI input: bound count and length, remove controls/newlines from display text, preserve the raw stable ID separately, reject reserved-ID collisions, and reject ambiguous slash-command matches.
- Preserve Visual Studio ComboBox styles and dynamic theme resources. Validate narrow width, keyboard use, High Contrast, high DPI, and live theme changes.
- Add
AutomationProperties.NameandAutomationProperties.HelpText. The adjacent Mode control also explains that Chat forces read-only behavior because a disabled permission picker is skipped by keyboard navigation.
- Add typed Remote UI options and a stable selected ID.
- Add
ApprovalsReviewertoStartTurnRequest, Worker contracts, Worker serialization, the Fake app-server log, and wire tests. Bump the contract version from 10 to 11. - Apply the reviewed mapping only for an explicit Agent mode; Chat always wins with
never/user/readOnly, and unknown modes fail safely. - Add an injectable, atomic settings store. Restore safe selections, but require renewed confirmation before restoring Full access.
- Add desired/effective state tracking and define the new-thread transition to Custom.
- Add the ComboBox, accurate Full access warning/confirmation, automation metadata, and XAML/DataMember regression coverage.
- Update
/statusto use the same resolver asturn/start.
- Remove the hand-written
[profiles.*]TOML reader from scope. - Add Worker RPC for
permissionProfile/listwithcwd, pagination, cancellation, timeout, page/item limits, and unsupported-method degradation. Bump the contract version from 11 to 12. - List
[permissions.<id>]entries only after experimental/runtime capability checks. - Send a selected ID through experimental turn
permissions, mutually exclusive with low-level approval/reviewer/sandbox overrides. - Reconcile start/resume/fork and
thread/settings/updatedeffective state. - Respect app-server/managed-policy results. Unsupported or temporarily failing profile discovery never weakens a policy and never overwrites the saved selection.
- Expose
/permissionsas the canonical setting command and keep/approveas a compatibility alias. - No argument lists available choices. An argument selects an exact stable ID or an unambiguous display name; ambiguous input is rejected.
- Chat mode reports that permissions are fixed to read-only. A change during an active turn follows the existing coalesced setting-command semantics for the next turn.
- Full access goes through the same confirmation as the ComboBox.
- Update
/status, slash-command references, design, and implementation documentation.
- Regenerate/compare schemas from the pinned target Codex version and test graceful
behavior with a runtime that lacks
approvalsRevieweror permission-profile selection. - Perform one deterministic warnings-as-errors solution build and run both test
projects with
--no-build. - Verify the exact JSON tuple for Ask, Auto, Full, Chat, Custom-on-new-thread, and a permission profile. Cover Ask/Full/Profile to Custom transitions and thread resume.
- Test permission-profile pagination,
cwd, managed restrictions, timeouts, malformed or oversized entries, loading placeholders, missing entries, and transient failure. - Inspect the final VSIX for the Worker, matching Contracts assemblies, and raw embedded Remote UI XAML; verify packaged/deployed assembly hashes.
- In the Experimental Instance, test keyboard, mouse, narrow width, Light/Dark/Blue, High Contrast, high DPI, live theme switching, persistence, confirmation, and both slash-command names.
- With a real supported Codex, verify manual requests reach the user, auto-review routes eligible requests to the reviewer, Full access shows the warning and normally emits no approval request, and Custom starts a new thread with the effective config.