Skip to content
Merged
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
18 changes: 13 additions & 5 deletions apps/web/src/components/settings/ForkSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@ function CustomizationsGroup({
}

type ServerSwitchKey =
| "dontAskMode"
| "autoOrganizeByFolder"
| "keepThreadTitlesCurrent"
| "agentBrowserTabLimits"
Expand Down Expand Up @@ -548,10 +547,19 @@ function ForkGeneralBehaviorRows() {
const mixedIdleTimeout = useScopedSettingsMixed(["idleAgentSessionMinutes"]);
return (
<SettingsSection title="Agent sessions & background behavior">
<ServerSwitchRow
settingKey="dontAskMode"
searchId="dont-ask-mode"
description="While you're away, answer agents' questions with their recommended option and grant their approvals. Turn off when you return to answer them yourself."
<SettingsRow
serverScoped
settingKeys={["dontAskMode"]}
{...searchableSetting("dont-ask-mode")}
description="Let agents ask you questions and wait for your answers and approvals. Turn off when you step away to answer questions with their recommended option and grant approvals automatically."
control={
<ScopedSwitch
settingKeys={["dontAskMode"]}
checked={!settings.dontAskMode}
onCheckedChange={(checked) => updateSettings({ dontAskMode: !checked })}
aria-label="Ask mode"
/>
}
/>
{isElectron && !isMacPlatform(navigator.platform) ? (
<SettingsRow
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/components/settings/ForkSettingsSearch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ describe("fork settings destinations", () => {
["thread rows text size", "sidebar-thread-text-size", "/settings/appearance"],
["agent rows text size", "sidebar-agent-text-size", "/settings/appearance"],
["process rows text size", "sidebar-process-text-size", "/settings/appearance"],
["ask", "dont-ask-mode", "/settings/general"],
["ask mode", "dont-ask-mode", "/settings/general"],
["don't ask", "dont-ask-mode", "/settings/general"],
["don't ask mode", "dont-ask-mode", "/settings/general"],
["inside project local .worktrees", "storage-worktrees-location", "/settings/storage"],
])("finds %s on its settings page", (query, id, to) => {
Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/components/settings/ForkSettingsSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import type { SettingsSearchItem } from "./settingsSearch";
export const FORK_SETTINGS_SEARCH_ITEMS = [
{
id: "dont-ask-mode",
title: "Don't ask mode",
title: "Ask mode",
to: "/settings/general",
scope: "environment-defaults",
searchTerms: ["fork automatic questions recommended answers approvals away unattended"],
searchTerms: [
"don't ask dont ask fork automatic questions recommended answers approvals away unattended",
],
},
{
id: "storage-worktrees-location",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function UsageLimitRecoverySettings() {
serverScoped
{...searchableSetting("usage-limit-recovery")}
settingKeys={["usageLimitRecovery"]}
description="Choose what happens after a usage limit. Switching moves only the limited thread and continues without asking, including in Don't ask mode. If no alternative is available, it waits for reset."
description="Choose what happens after a usage limit. Switching moves only the limited thread and continues without asking, including when Ask mode is off. If no alternative is available, it waits for reset."
control={
<Select
value={settings.usageLimitRecovery}
Expand Down
25 changes: 13 additions & 12 deletions apps/web/src/components/sidebar/SidebarChrome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import {
ArrowLeftIcon,
ChartNoAxesColumnIcon,
CircleDotIcon,
MessageCircleOffIcon,
MessageCircleQuestionMarkIcon,
MessageCircleQuestionIcon,
SettingsIcon,
} from "lucide-react";
import type { CSSProperties, ReactNode } from "react";
Expand Down Expand Up @@ -175,13 +174,15 @@ function SidebarUtilityItem({
);
}

/** The primary server's Don't ask mode: while on, it answers every agent question and approval. */
function SidebarDontAskModeItem() {
/** Ask mode reflects the inverse of the primary server's automatic-answer setting. */
function SidebarAskModeItem() {
const available = usePrimarySettingsAvailable();
const on = usePrimarySettings((settings) => settings.dontAskMode);
const on = usePrimarySettings((settings) => !settings.dontAskMode);
const updateSettings = useUpdatePrimarySettings();
if (!available) return null;
const label = on ? "Don't ask mode is on" : "Don't ask mode is off";
const label = on
? "Ask mode: on (agents can ask you questions)"
: "Ask mode: off (agents decide without asking)";
return (
// Last in the footer. The desktop update control before it already pushes right (`ml-auto`), so
// it drops its own push there instead of splitting the free space with it.
Expand All @@ -192,16 +193,16 @@ function SidebarDontAskModeItem() {
<SidebarMenuButton
aria-label={label}
aria-pressed={on}
onClick={() => updateSettings({ dontAskMode: !on })}
onClick={() => updateSettings({ dontAskMode: on })}
size="icon"
// On: the orange the sidebar uses for the questions and approvals it would answer.
// Orange matches the sidebar's questions waiting for you.
style={
on
? ({ "--sidebar-icon-color": "var(--warning-foreground)" } as CSSProperties)
: undefined
}
>
{on ? <MessageCircleOffIcon /> : <MessageCircleQuestionMarkIcon />}
<MessageCircleQuestionIcon />
</SidebarMenuButton>
}
/>
Expand All @@ -210,8 +211,8 @@ function SidebarDontAskModeItem() {
<span className="font-medium text-foreground">{label}</span>
<span className="text-muted-foreground">
{on
? "Agents' questions get their recommended option and approvals are granted. Click when you're back."
: "Turn on when you step away: agents' questions get their recommended option and approvals are granted."}
? "Turn off when you step away: questions get their recommended option and approvals are granted."
: "Questions get their recommended option and approvals are granted. Turn on to answer them yourself."}
</span>
</div>
</TooltipPopup>
Expand Down Expand Up @@ -300,7 +301,7 @@ export const SidebarUtilityMenu = memo(function SidebarUtilityMenu() {
</>
)}
<SidebarUpdatePill />
{isOnUtilityPage ? null : <SidebarDontAskModeItem />}
{isOnUtilityPage ? null : <SidebarAskModeItem />}
</SidebarMenu>
);
});
Expand Down
29 changes: 16 additions & 13 deletions docs/user/permission-modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@ and modes you choose in a draft keep their permissions.
Approve or reject requests in the conversation to let the agent continue. Permission modes do
not prevent the agent from asking questions about the task.

## Don't ask mode

Turn on **Don't ask mode** with the speech-bubble switch at the bottom left of the sidebar when you
step away. While it is on, the server answers for you the moment an agent waits: it approves each
request once, and answers each question with its first option (the agent's recommendation) plus a
note asking the agent to list the decisions it made. Switching it on also answers everything
already waiting in every thread, including delegated ones. Answers it gave are marked **Answered by
Don't ask mode** or **Approved by Don't ask mode** in the conversation. Switch it off to have agents
wait for you again.

The switch belongs to your primary environment's server, so it keeps working with no client open
and stays on after a restart. Requests left waiting across a server restart are handled the way a
restart normally handles them, not by Don't ask mode. Agents cannot change it.
## Ask mode

**Ask mode** lets agents wait for your answers and approvals. Toggle it in the sidebar footer or
in **Settings → General → Ask mode** (also available in **Customizations**). Orange means it is on;
gray means it is off.

Turn Ask mode off when you step away. While it is off, the server answers for you the moment an
agent waits: it approves each request once, and answers each question with its first option (the
agent's recommendation) plus a note asking the agent to list the decisions it made. Turning it off
also answers everything already waiting in every thread, including delegated ones. Automatic
answers retain the conversation labels **Answered by Don't ask mode** and **Approved by Don't ask
mode**. Turn Ask mode on to have agents wait for you again.

The sidebar switch belongs to your primary environment's server, so automatic answers keep working
with no client open and the setting persists after a restart. Requests left waiting across a
server restart are handled the way a restart normally handles them. Agents cannot change the setting.

## Provider differences

Expand Down
2 changes: 1 addition & 1 deletion docs/user/thread-sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ archiving, or settling it prevents a pending continuation from starting.
- **Wait for reset** snoozes and continues limited threads when the reported allowance returns.
- **Switch provider** moves the limited thread between Codex and Claude and sends
“Continue where you left off.” with a context handoff. Other threads and children keep running.
Switching proceeds without asking, including in Don't ask mode.
Switching proceeds without asking, including when Ask mode is off.

A thread uses its role's available alternatives, ordered by weekly headroom. Implementor threads
use the profile matching their current model and effort, or the first configured Implementor
Expand Down