Skip to content

Setting for auto-accepting agent mode edits #5657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 19, 2025
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
7 changes: 7 additions & 0 deletions core/config/migrateSharedConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ export function migrateJsonSharedConfig(filepath: string, ide: IDE): void {
effected = true;
}

const { autoAcceptEditToolDiffs, ...withoutAutoApply } = migratedUI;
if (autoAcceptEditToolDiffs !== undefined) {
shareConfigUpdates.autoAcceptEditToolDiffs = autoAcceptEditToolDiffs;
migratedUI = withoutAutoApply;
effected = true;
}

const { showChatScrollbar, ...withoutShowChatScrollbar } = migratedUI;
if (showChatScrollbar !== undefined) {
shareConfigUpdates.showChatScrollbar = showChatScrollbar;
Expand Down
8 changes: 6 additions & 2 deletions core/config/sharedConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const sharedConfigSchema = z
codeWrap: z.boolean(),
displayRawMarkdown: z.boolean(),
showChatScrollbar: z.boolean(),
autoAcceptEditToolDiffs: z.boolean(),

// `tabAutocompleteOptions` in `ContinueConfig`
useAutocompleteCache: z.boolean(),
Expand Down Expand Up @@ -106,8 +107,7 @@ export function modifyAnyConfigWithSharedConfig<
sharedConfig.disableAutocompleteInFiles;
}
if (sharedConfig.modelTimeout !== undefined) {
configCopy.tabAutocompleteOptions.modelTimeout =
sharedConfig.modelTimeout;
configCopy.tabAutocompleteOptions.modelTimeout = sharedConfig.modelTimeout;
}
if (sharedConfig.debounceDelay !== undefined) {
configCopy.tabAutocompleteOptions.debounceDelay =
Expand All @@ -134,6 +134,10 @@ export function modifyAnyConfigWithSharedConfig<
if (sharedConfig.showChatScrollbar !== undefined) {
configCopy.ui.showChatScrollbar = sharedConfig.showChatScrollbar;
}
if (sharedConfig.autoAcceptEditToolDiffs !== undefined) {
configCopy.ui.autoAcceptEditToolDiffs =
sharedConfig.autoAcceptEditToolDiffs;
}

if (sharedConfig.allowAnonymousTelemetry !== undefined) {
configCopy.allowAnonymousTelemetry = sharedConfig.allowAnonymousTelemetry;
Expand Down
1 change: 1 addition & 0 deletions core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,7 @@ export interface ContinueUIConfig {
showChatScrollbar?: boolean;
codeWrap?: boolean;
showSessionTabs?: boolean;
autoAcceptEditToolDiffs?: boolean;
}

export interface ContextMenuConfig {
Expand Down
16 changes: 12 additions & 4 deletions docs/docs/customize/deep-dives/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ keywords: [config, settings, configuration, customize, customization, sidebar]

# The User Settings Page

The **User Settings page** can be accessed by clicking the gear icon in the header of the Continue sidebar.
The **User Settings page** can be accessed by clicking the gear icon in the header of the Continue sidebar and then selecting the "Settings" tab.

![slash-commands](/img/header-buttons.png)
![settings](/img/settings-header.png)

Which takes you to this page:
Which takes you to this page (select the "Settings" tab):

![User Settings Page](/img/settings-page.png)

Expand All @@ -26,7 +26,15 @@ Below that, the following settings which are not part of a configuration file ar
- `Enable Indexing`: Enables indexing of the codebase for the @codebase and @code context providers. **On** by default.
- `Font Size`: Specifies base font size for UI elements
- `Multiline Autocompletions`: Controls multiline completions for autocomplete. Can be set to `always`, `never`, or `auto`. Defaults to `auto`
- `Disable autocomplete in files`: List of comma-separated glob pattern to disable autocomplete in matching files. E.g., "\_/.md, \*/.txt"
- `Autocomplete timeout`: Maximum time in milliseconds for autocomplete request/retreival. Defaults to 150
- `Autocomplete debounce`: Minimum time in milliseconds to trigger an autocomplete request after a change. Defaults to 250
- `Disable autocomplete in files`: List of comma-separated glob pattern to disable autocomplete in matching files. E.g., `**/*.{txt,md}`

### Experimental settings

At the bottom of the User Settings page there is an "Experimental Settings" section which contains new or otherwise experimental settings, including:

- `Auto-Accept Agent Edits`: **CAUTION: Be very careful with this setting. When turned on, Agent mode's edit tool can make changes to files with no manual review or guaranteed stopping point. If on, diffs generated by the edit tool are automatically accepted and Agent proceeds with the next conversational turn.** Off by default.

<!-- - `Use autocomplete cache`: If on, caches completions. -->
<!-- - `Use Chromium for Docs Crawling`: Use Chromium to crawl docs locally. Useful if the default Cheerio crawler fails on sites that require JavaScript rendering. Downloads and installs Chromium to ~/.continue/.utils. Off by default -->
Expand Down
Binary file removed docs/static/img/header-buttons.png
Binary file not shown.
Binary file added docs/static/img/settings-header.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 5 additions & 3 deletions gui/src/components/gui/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const NumberInput: React.FC<NumberInputProps> = ({
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newInputValue = e.target.value;
setInputValue(newInputValue);

// Only update the actual value if it's a valid number
const numValue = parseInt(newInputValue, 10);
if (!isNaN(numValue)) {
Expand Down Expand Up @@ -63,18 +63,20 @@ const NumberInput: React.FC<NumberInputProps> = ({
WebkitAppearance: "none",
MozAppearance: "none",
}}
min={min}
max={max}
/>
<div className="flex flex-col">
<button
style={{ fontSize: "10px" }}
style={{ fontSize: "9px" }}
onClick={handleIncrement}
disabled={value >= max}
className="text-vsc-foreground m-0 mb-0.5 cursor-pointer border-none bg-inherit px-1.5 py-0 hover:opacity-80"
>
</button>
<button
style={{ fontSize: "10px" }}
style={{ fontSize: "9px" }}
className="text-vsc-foreground m-0 mb-0.5 cursor-pointer border-none bg-inherit px-1.5 py-0 hover:opacity-80"
onClick={handleDecrement}
disabled={value <= min}
Expand Down
7 changes: 5 additions & 2 deletions gui/src/components/gui/Switch.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import React from "react";
import React, { ReactNode } from "react";
import { vscButtonBackground } from "..";

type ToggleSwitchProps = {
isToggled: boolean;
onToggle: () => void;
text: string;
size?: number;
showIfToggled?: ReactNode;
};

const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
isToggled,
onToggle,
text,
size = 16,
showIfToggled,
}) => {
return (
<div className="flex cursor-pointer select-none items-center justify-between gap-3">
<span className="truncate-right">{text}</span>
<div>
<div className="flex flex-row items-center gap-1">
{isToggled && !!showIfToggled && showIfToggled}
<div
className={`border-vsc-input-border bg-vsc-input-background flex items-center rounded-full border border-solid`}
onClick={onToggle}
Expand Down
69 changes: 60 additions & 9 deletions gui/src/pages/config/UserSettingsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { CheckIcon, XMarkIcon } from "@heroicons/react/24/outline";
import {
CheckIcon,
ChevronRightIcon,
ExclamationTriangleIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
import {
SharedConfigSchema,
modifyAnyConfigWithSharedConfig,
Expand All @@ -8,16 +13,19 @@ import { Input } from "../../components";
import NumberInput from "../../components/gui/NumberInput";
import { Select } from "../../components/gui/Select";
import ToggleSwitch from "../../components/gui/Switch";
import { ToolTip } from "../../components/gui/Tooltip";
import { useFontSize } from "../../components/ui/font";
import { IdeMessengerContext } from "../../context/IdeMessenger";
import { useAppDispatch, useAppSelector } from "../../redux/hooks";
import { updateConfig } from "../../redux/slices/configSlice";
import { getFontSize } from "../../util";
import { setLocalStorage } from "../../util/localStorage";

export function UserSettingsForm() {
/////// User settings section //////
const dispatch = useAppDispatch();
const ideMessenger = useContext(IdeMessengerContext);
const config = useAppSelector((state) => state.config.config);
const [showExperimental, setShowExperimental] = useState(false);

function handleUpdate(sharedConfig: SharedConfigSchema) {
// Optimistic update
Expand Down Expand Up @@ -65,6 +73,7 @@ export function UserSettingsForm() {
const codeWrap = config.ui?.codeWrap ?? false;
const showChatScrollbar = config.ui?.showChatScrollbar ?? false;
const readResponseTTS = config.experimental?.readResponseTTS ?? false;
const autoAcceptEditToolDiffs = config.ui?.autoAcceptEditToolDiffs ?? false;
const displayRawMarkdown = config.ui?.displayRawMarkdown ?? false;
const disableSessionTitles = config.disableSessionTitles ?? false;

Expand All @@ -79,7 +88,7 @@ export function UserSettingsForm() {
config.tabAutocompleteOptions?.multilineCompletions ?? "auto";
const modelTimeout = config.tabAutocompleteOptions?.modelTimeout ?? 150;
const debounceDelay = config.tabAutocompleteOptions?.debounceDelay ?? 250;
const fontSize = getFontSize();
const fontSize = useFontSize();

const cancelChangeDisableAutocomplete = () => {
setFormDisableAutocomplete(disableAutocompleteInFiles);
Expand Down Expand Up @@ -162,7 +171,6 @@ export function UserSettingsForm() {
}
text="Text-to-Speech Output"
/>

{/* <ToggleSwitch
isToggled={useChromiumForDocsCrawling}
onToggle={() =>
Expand Down Expand Up @@ -225,11 +233,12 @@ export function UserSettingsForm() {
<span className="text-left">Font Size</span>
<NumberInput
value={fontSize}
onChange={(val) =>
onChange={(val) => {
setLocalStorage("fontSize", val);
handleUpdate({
fontSize: val,
})
}
});
}}
min={7}
max={50}
/>
Expand All @@ -255,7 +264,7 @@ export function UserSettingsForm() {
</Select>
</label>
<label className="flex items-center justify-between gap-3">
<span className="text-left">Model Timeout (ms)</span>
<span className="text-left">Autocomplete Timeout (ms)</span>
<NumberInput
value={modelTimeout}
onChange={(val) =>
Expand All @@ -268,7 +277,7 @@ export function UserSettingsForm() {
/>
</label>
<label className="flex items-center justify-between gap-3">
<span className="text-left">Model Debounce (ms)</span>
<span className="text-left">Autocomplete Debounce (ms)</span>
<NumberInput
value={debounceDelay}
onChange={(val) =>
Expand Down Expand Up @@ -326,6 +335,48 @@ export function UserSettingsForm() {
</span>
</form>
</div>

<div className="flex flex-col gap-2">
<div
className="flex cursor-pointer items-center gap-2 text-left text-sm font-semibold"
onClick={() => setShowExperimental(!showExperimental)}
>
<ChevronRightIcon
className={`h-4 w-4 transition-transform ${
showExperimental ? "rotate-90" : ""
}`}
/>
<span>Experimental Settings</span>
</div>
<div
className={`duration-400 overflow-hidden transition-all ease-in-out ${
showExperimental ? "max-h-40" : "max-h-0"
}`}
>
<div className="flex flex-col gap-1 pl-6">
<ToggleSwitch
isToggled={autoAcceptEditToolDiffs}
onToggle={() =>
handleUpdate({
autoAcceptEditToolDiffs: !autoAcceptEditToolDiffs,
})
}
text="Auto-Accept Agent Edits"
showIfToggled={
<>
<ExclamationTriangleIcon
data-tooltip-id={`auto-accept-diffs-warning-tooltip`}
className="h-3 w-3 text-yellow-500"
/>
<ToolTip id={`auto-accept-diffs-warning-tooltip`}>
{`Be very careful with this setting. When turned on, Agent mode's edit tool can make changes to files with no manual review or guaranteed stopping point`}
</ToolTip>
</>
}
/>
</div>
</div>
</div>
</div>
) : null}
</div>
Expand Down
16 changes: 15 additions & 1 deletion gui/src/util/clientTools/editImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,21 @@ export const editToolImpl: ClientToolImpl = async (
if (apply.status === "error") {
throw new Error(apply.error);
}

const state = extras.getState();
const autoAccept = !!state.config.config.ui?.autoAcceptEditToolDiffs;
if (autoAccept) {
const out = await extras.ideMessenger.request("acceptDiff", {
streamId: extras.streamId,
filepath: firstUriMatch,
});
if (out.status === "error") {
throw new Error(out.error);
}
return {
respondImmediately: true,
output: undefined, // TODO - feed edit results back to model (also in parallel listeners)
};
}
return {
respondImmediately: false,
output: undefined, // No immediate output.
Expand Down
Loading