4.0 KiB
Agent Note: GUI Full access risk confirmation
Status: implemented
English | 中文
Problem
Switching the web client to danger-full-access was a single click on either permission surface (the composer's Access chip and the /permission popup picker), with the preset shown as the title-cased machine name Danger Full Access. Full access reduces confirmation steps and lets the agent run sensitive operations, modify files, or execute external commands, so an accidental pick armed the most dangerous preset with no deliberate acknowledgement step.
Decision
Both permission surfaces gate danger-full-access behind one shared in-page RiskConfirmation dialog whose enabling action stays disabled until an explicit acknowledgement checkbox is checked; the preset renders under the product label Full access; every dismissal path submits nothing.
RiskConfirmation(ui-primitives) is a controlled Modal composition: title, description, acknowledgement checkbox, cancel, and a confirm button disabled untilacknowledged. It stays an in-page dialog — the Modal portals to this document's body and never opens a native or separate browser window that could land on another display.Modalgains acontentClassNameseat so the warning body scrolls inside constrained mobile/landscape viewports while the action row stays fixed.- The composer chip (
PermissionSelect, ui-conversation) intercepts a Full-access pick before the/permissionsubmit:confirmation/acknowledgedcomponent state opens the dialog, confirm submits/permission danger-full-accessthrough the same injectedcommandpath as every other pick, and cancel/Escape/close/mask leave the current preset untouched with the checkbox reset. The confirmation revokes itself when the session locks (locked/value-absent effect) and resets across task switches (key={sessionId}remount). Copy rides the standardconversationlocale seat asaccess.confirm.*keys. - The
/permissionpopup (ui-permission over the ui-command shell) gates through data, not a second dialog implementation:SelectOptiongrows an optionalconfirmationpayload, the popup controller owns theconfirming/acknowledgedstate transitions, andPopupSelectViewswaps the picker card for the sameRiskConfirmationwhile a gated option is pending. Full accessintentionally overrides the kebab-to-title display transform on both surfaces (option rows, trigger label, settled command rows keep the machine name on the wire); the warning body remains locale-aware in Chinese and English.
Alternatives considered
A native/OS or separate-window confirmation. Rejected: the dialog must stay inside the current WebUI window; a second window can appear on another display and detaches the decision from the page state it guards.
One shared locale namespace for both surfaces' safety copy. Rejected: the ui-permission bundle and ui-conversation load independently, so each registers the same copy under its own namespace (permission.access beside the conversation dictionary); the duplication is fenced with an explanatory jscpd:ignore block rather than a cross-bundle import.
Gating in the host/permission backend. Out of scope by design: the change is browser-client confirmation flow only; backend permission semantics, defaults, and the safer presets' one-click behavior are unchanged.
Consequences
Every visible GUI path into Full access now requires a deliberate, informed acknowledgement, at the cost of one extra dialog step for users who genuinely want the preset. New pickers reuse the gate by attaching a confirmation payload (popup path) or the chip's state machine (composer path) instead of inventing bespoke dialogs. Acceptance: the composer flow's four gated cases in input-bar.spec.tsx, the popup gate in popup-view.spec.tsx and popup.spec.ts, the Modal/RiskConfirmation contract in atoms.spec.tsx, and the assembled access-confirmation web e2e whose golden pins the product-default Chinese dictionary copy.