Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions doc/specs/Multi-window-agent-pane.md
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,65 @@ work:
- **Pre-warming**: not implemented. First user toggle creates the
helper on demand.

### Z-R9. Yolo mode (auto-approve tool-call permission requests)

Implements GitHub issue #326 ("Allow agent pane to run with options such
as `--allow-all`"). Rather than passing agent-CLI-specific flags
(`--allow-all`/`--yolo`, which differ per agent and don't exist for all
of them), auto-approve is implemented once at the ACP client layer
(`WtaClient::request_permission` in `tools/wta/src/protocol/acp/client.rs`),
so it works uniformly across Copilot/Claude/Gemini/Codex/custom agents
without depending on each CLI's own flag surface.

Two independent, additive enable paths, both gated by the same
`AllowYoloMode` admin policy (`src/cascadia/inc/AgentPolicy.h`):

1. **Global Settings toggle** — "Auto-approve tool calls" in the Settings
UI (`AIAgents.xaml`, backed by `agentPane.yoloMode` /
`GlobalAppSettings::EffectiveAgentPaneYoloMode()`). When on (and not
policy-blocked), `TerminalPage.cpp` appends `--auto-approve-tools` to
the helper's command line, which every ACP session on every tab of
every window inherits for the lifetime of the setting.
2. **Per-session `/yolo` slash command** — scoped to the *current tab's
current* ACP `session_id` only. Implemented as a shared
`Arc<Mutex<HashSet<String>>>` (`App::yolo_sessions`, also held by
`WtaClient`'s `ClientState`): `/yolo` inserts the active session_id;
`request_permission` checks membership. Keying by `session_id` rather
than `tab_id` means the override does **not** persist across `/new`
(a fresh session_id is simply absent from the set) — the user must
run `/yolo` again after starting a new conversation. `/restart` clears
the whole set (every session is being torn down anyway).

Policy enforcement on the Rust side: since wta has no direct registry
access, `TerminalPage.cpp` also passes `--yolo-command-blocked` whenever
`AllowYoloMode` is `Blocked`, independent of whatever the global toggle
happens to be — this lets `/yolo` itself refuse with a clear
"disabled by your organization's policy" message (`cmd_yolo` in
`tools/wta/src/app.rs`) even when the global setting is off.

When either path is active for a given request, `request_permission`
skips the blocking `PermissionRequest` UI event entirely and picks the
best "allow" option (`allow_always` preferred over `allow_once` — see
`pick_allow_option`), responding immediately with no user-visible
notification: auto-approval is deliberately silent so tool calls don't
clutter the conversation with a message per approval.

**Agent-native allow-all (preferred over client-side interception, when
available).** Some agents' ACP servers advertise a per-session
`config_options` entry (category `permissions`, id `allow_all`) in their
`session/new` response — confirmed for Copilot CLI's ACP server.
`permission_select.rs` detects this and, whenever a session is (or
becomes) in yolo mode, calls `session/set_config_option(allow_all, "on")`
for that `session_id` so the agent itself stops sending
`session/request_permission` entirely, rather than WTA intercepting and
auto-answering each request. This is applied at every session-creation
call site in `client.rs`, plus retroactively via
`MasterExtRequest::SetSessionAllowAll` when `/yolo` is typed on an
already-live session. Agents without this option (Claude/Gemini/Codex
adapters, as far as tested) are unaffected — the client-side
`request_permission` interception above remains the unconditional
fallback for every agent.

## What this does NOT solve (out of scope)

- **Cross-process Terminal instances**: if WT is configured for
Expand Down
20 changes: 20 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,26 @@ namespace winrt::TerminalApp::implementation
{
helperCmd.append(L" --no-autofix");
}
// Global "Yolo mode" — auto-approve every ACP permission request on
// this pane without prompting. Policy-gated via
// EffectiveAgentPaneYoloMode() (AgentPolicy::IsYoloModeAllowed()), so
// a GPO-blocked org never spawns a helper with this flag set even if
// the user's settings.json has agentPane.yoloMode: true. This is
// independent from the per-session `/yolo` slash command, which the
// helper enforces itself at runtime against the same policy.
if (globals.EffectiveAgentPaneYoloMode())
{
helperCmd.append(L" --auto-approve-tools");
}
// Independent of the global toggle above: tell the helper whether
// org policy blocks yolo mode outright, so its own `/yolo` slash
// command (a per-session override the user can flip at runtime) can
// refuse and explain why, instead of silently enabling unattended
// tool-call approval in a GPO-managed environment.
if (globals.IsYoloModePolicyLocked())
{
helperCmd.append(L" --yolo-command-blocked");
}
if (const auto lang = _ResolveEffectiveLanguage(globals); !lang.empty())
{
appendHelperFlagValue(L"--language", lang);
Expand Down
20 changes: 20 additions & 0 deletions src/cascadia/TerminalSettingsEditor/AIAgents.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:mtu="using:Microsoft.Terminal.UI"
mc:Ignorable="d">

<Page.Resources>
Expand Down Expand Up @@ -178,6 +179,25 @@
Style="{StaticResource ComboBoxSettingStyle}" />
</local:SettingContainer>

<!-- Global "Yolo mode": every agent pane auto-approves tool-call
permission requests without prompting. High-risk toggle, so
it is disabled outright (IsYoloModePolicyLocked) when the
AllowYoloMode GPO policy blocks it — matching the
auto-fix policy-lock pattern below (AIAgents_PolicyLocked
caption). -->
<local:SettingContainer x:Name="AgentPaneYoloMode"
x:Uid="AIAgents_YoloMode">
<StackPanel>
<ToggleSwitch IsOn="{x:Bind ViewModel.AgentPaneYoloMode, Mode=TwoWay}"
IsEnabled="{x:Bind mtu:Converters.InvertBoolean(ViewModel.IsYoloModePolicyLocked), Mode=OneWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
<TextBlock x:Uid="AIAgents_PolicyLocked"
Visibility="{x:Bind ViewModel.IsYoloModePolicyLocked, Mode=OneWay}"
Opacity="0.6"
Margin="0,4,0,0" />
</StackPanel>
</local:SettingContainer>

<!-- Automatic error detection (parent) + Automatic error
suggestion (dependent child). Modeled as an Expander so the
hierarchy is explicit: detection's master toggle lives in the
Expand Down
25 changes: 25 additions & 0 deletions src/cascadia/TerminalSettingsEditor/AIAgentsViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,31 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_GlobalSettings.EffectiveAutoErrorDetectionEnabled();
}

// ── Yolo mode (global auto-approve) ─────────────────────────────────

bool AIAgentsViewModel::AgentPaneYoloMode() const
{
return _GlobalSettings.EffectiveAgentPaneYoloMode();
}

void AIAgentsViewModel::AgentPaneYoloMode(bool value)
{
// Reject writes when org policy blocks yolo mode (the toggle is
// disabled in that case, but guard against races).
if (_GlobalSettings.IsYoloModePolicyLocked())
{
return;
}
if (_GlobalSettings.AgentPaneYoloMode() == value) return;
_GlobalSettings.AgentPaneYoloMode(value);
_NotifyChanges(L"HasAgentPaneYoloMode", L"AgentPaneYoloMode");
}

bool AIAgentsViewModel::HasAgentPaneYoloMode() const
{
return _GlobalSettings.HasAgentPaneYoloMode();
}

// ── Pane position ────────────────────────────────────────────────────

IObservableVector<Editor::EnumEntry> AIAgentsViewModel::AgentPanePositionList()
Expand Down
5 changes: 5 additions & 0 deletions src/cascadia/TerminalSettingsEditor/AIAgentsViewModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
bool HasAutoFixEnabled() const;
bool CanSuggestErrors() const;

bool AgentPaneYoloMode() const;
void AgentPaneYoloMode(bool value);
bool HasAgentPaneYoloMode() const;

// GPO policy lock indicators
bool IsAgentPolicyLocked() const { return _GlobalSettings.IsAgentPolicyLocked(); }
bool IsCustomAgentPolicyLocked() const { return _GlobalSettings.IsCustomAgentPolicyLocked(); }
bool IsAutoFixPolicyLocked() const { return _GlobalSettings.IsAutoFixPolicyLocked(); }
bool IsAgentSessionHooksPolicyLocked() const { return _GlobalSettings.IsAgentSessionHooksPolicyLocked(); }
bool IsYoloModePolicyLocked() const { return _GlobalSettings.IsYoloModePolicyLocked(); }

winrt::Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::EnumEntry> AgentPanePositionList();
winrt::Windows::Foundation::IInspectable CurrentAgentPanePosition();
Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/TerminalSettingsEditor/AIAgentsViewModel.idl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ namespace Microsoft.Terminal.Settings.Editor
// on and the auto-fix GPO must not be locking it off.
Boolean CanSuggestErrors { get; };

// Global "Yolo mode" — when on, every agent pane on this machine
// auto-approves tool-call permission requests without prompting.
// Getter/setter route through EffectiveAgentPaneYoloMode /
// IsYoloModePolicyLocked so the toggle reads Off and rejects writes
// when org policy blocks it.
Boolean AgentPaneYoloMode;
Boolean HasAgentPaneYoloMode { get; };

// GPO policy lock indicators.
// IsAgentPolicyLocked: AllowedAgents policy is active — agent
// dropdown options are filtered, but the control stays enabled.
Expand All @@ -89,6 +97,7 @@ namespace Microsoft.Terminal.Settings.Editor
Boolean IsCustomAgentPolicyLocked { get; };
Boolean IsAutoFixPolicyLocked { get; };
Boolean IsAgentSessionHooksPolicyLocked { get; };
Boolean IsYoloModePolicyLocked { get; };

event Windows.Foundation.TypedEventHandler<AIAgentsViewModel, Microsoft.Terminal.Settings.Model.ShellIntegrationTarget> InitShellIntegrationRequested;

Expand Down
10 changes: 10 additions & 0 deletions src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,16 @@ In this context, "agent" refers to an AI agent (e.g. Copilot, Claude, Gemini), n
<value>Left</value>
<comment>Value shown in the agent pane position dropdown (selects which side of the terminal the agent pane opens on). Translation should match the existing FreOverlay_PanePositionLeft string used in the first-run experience.</comment>
</data>
<data name="AIAgents_YoloMode.Header" xml:space="preserve">
<value>Auto-approve tool calls (Yolo mode)</value>
<comment>Header for the global setting that makes every agent pane skip its permission prompt and auto-approve tool calls.
In this context, "agent" refers to an AI agent (e.g. Copilot, Claude, Gemini), not a human agent or representative.</comment>
</data>
<data name="AIAgents_YoloMode.HelpText" xml:space="preserve">
<value>The agent runs commands and edits files without asking for confirmation. Only enable this if you trust the agent and the environment it's running in.</value>
<comment>Supplementary risk-warning description for the Yolo mode setting, shown under the toggle.
In this context, "agent" refers to an AI agent (e.g. Copilot, Claude, Gemini), not a human agent or representative.</comment>
</data>
<data name="AIAgents_HooksInstall.Header" xml:space="preserve">
<value>Agent session tracking (hooks)</value>
<comment>{Locked="hooks","Hook","Hooks"} Header for the expandable Agent Hooks section in the Agent pane group. Wording matches the design ask: this UX exists so the agent pane can track agent sessions through installed hooks.
Expand Down
14 changes: 14 additions & 0 deletions src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,20 @@ bool GlobalAppSettings::IsAgentSessionHooksPolicyLocked() const
return AgentPolicy::GetAgentSessionHooksPolicy() == AgentPolicy::PolicyState::Blocked;
}

bool GlobalAppSettings::EffectiveAgentPaneYoloMode() const
{
if (!AgentPolicy::IsYoloModeAllowed())
{
return false;
}
return AgentPaneYoloMode();
}

bool GlobalAppSettings::IsYoloModePolicyLocked() const
{
return AgentPolicy::GetYoloModePolicy() == AgentPolicy::PolicyState::Blocked;
}

// ── Test-only hooks ─────────────────────────────────────────────────
// Defined here so the body executes inside SettingsModel.dll, which
// guarantees we patch the same AgentPolicy::s_snapshot that
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/GlobalAppSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
hstring EffectiveDelegateAgent() const;
bool EffectiveAutoErrorDetectionEnabled() const;
bool EffectiveAutoFixEnabled() const;
bool EffectiveAgentPaneYoloMode() const;

// Whether GPO policy is actively restricting these settings.
bool IsAgentPolicyLocked() const;
bool IsCustomAgentPolicyLocked() const;
bool IsAutoFixPolicyLocked() const;
bool IsAgentSessionHooksPolicyLocked() const;
bool IsYoloModePolicyLocked() const;

// ── Test-only seam ──────────────────────────────────────────────
// Replace the SettingsModel DLL's cached AgentPolicy snapshot
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(IVector<String>, SafeUriSchemes);
INHERITABLE_SETTING(String, DelegateCustomCommand);
INHERITABLE_SETTING(String, AgentPanePosition);
INHERITABLE_SETTING(Boolean, AgentPaneYoloMode);

// AI Integration settings
INHERITABLE_SETTING(Boolean, AiCoordinatorEnabled);
Expand All @@ -140,6 +141,7 @@ namespace Microsoft.Terminal.Settings.Model
String EffectiveDelegateAgent { get; };
Boolean EffectiveAutoErrorDetectionEnabled { get; };
Boolean EffectiveAutoFixEnabled { get; };
Boolean EffectiveAgentPaneYoloMode { get; };

// Whether GPO policy is actively restricting these settings.
// GPO policy indicators.
Expand All @@ -152,6 +154,7 @@ namespace Microsoft.Terminal.Settings.Model
Boolean IsCustomAgentPolicyLocked { get; };
Boolean IsAutoFixPolicyLocked { get; };
Boolean IsAgentSessionHooksPolicyLocked { get; };
Boolean IsYoloModePolicyLocked { get; };

Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/MTSMSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Author(s):
X(hstring, AcpCustomCommand, "acpCustomCommand", L"") \
X(hstring, DelegateCustomCommand, "delegateCustomCommand", L"") \
X(hstring, AgentPanePosition, "agentPanePosition", L"bottom") \
X(bool, AgentPaneYoloMode, "agentPane.yoloMode", false) \
X(bool, AiCoordinatorEnabled, "aiIntegration.coordinator.enabled", false) \
X(hstring, AiCoordinatorCommandline, "aiIntegration.coordinator.commandline", L"wta") \
X(hstring, AiCoordinatorProfile, "aiIntegration.coordinator.profile", L"{fd19208a-412b-4857-8a2d-9ca592b4b16e}") \
Expand Down
Loading
Loading