Skip to content

Commit 22c6799

Browse files
uinstinctRomneyDa
andauthored
feat: auto-accept agent edits setting auto accept edit tools (#7993)
* feat: auto accept edit tools * add `isAutoEditToolCall` check * show tooltip in tool section * revert accidental change * refactor * fix: tweaks on auto accept agent edit --------- Co-authored-by: Dallin Romney <[email protected]>
1 parent 8367d33 commit 22c6799

File tree

5 files changed

+58
-16
lines changed

5 files changed

+58
-16
lines changed

gui/src/pages/config/components/ToolPolicyItem.tsx

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { useFontSize } from "../../../components/ui/font";
2020
import { useAppSelector } from "../../../redux/hooks";
2121
import { addTool, setToolPolicy } from "../../../redux/slices/uiSlice";
22+
import { isEditTool } from "../../../util/toolCallState";
2223

2324
interface ToolPolicyItemProps {
2425
tool: Tool;
@@ -28,12 +29,22 @@ interface ToolPolicyItemProps {
2829

2930
export function ToolPolicyItem(props: ToolPolicyItemProps) {
3031
const dispatch = useDispatch();
31-
const policy = useAppSelector(
32+
const toolPolicy = useAppSelector(
3233
(state) => state.ui.toolSettings[props.tool.function.name],
3334
);
3435
const [isExpanded, setIsExpanded] = useState(false);
3536
const mode = useAppSelector((state) => state.session.mode);
3637

38+
const autoAcceptEditToolDiffs = useAppSelector(
39+
(state) => state.config.config.ui?.autoAcceptEditToolDiffs,
40+
);
41+
const isAutoAcceptedToolCall =
42+
isEditTool(props.tool.function.name) && autoAcceptEditToolDiffs;
43+
44+
const policy = isAutoAcceptedToolCall
45+
? "allowedWithoutPermission"
46+
: toolPolicy;
47+
3748
useEffect(() => {
3849
if (!policy) {
3950
dispatch(addTool(props.tool));
@@ -53,6 +64,7 @@ export function ToolPolicyItem(props: ToolPolicyItemProps) {
5364
const fontSize = useFontSize(-2);
5465

5566
const disabled =
67+
isAutoAcceptedToolCall ||
5668
!props.isGroupEnabled ||
5769
(mode === "plan" &&
5870
props.tool.group === BUILT_IN_GROUP_NAME &&
@@ -100,6 +112,19 @@ export function ToolPolicyItem(props: ToolPolicyItemProps) {
100112
<InformationCircleIcon className="h-3 w-3 flex-shrink-0 cursor-help text-yellow-500" />
101113
</ToolTip>
102114
) : null}
115+
{isAutoAcceptedToolCall ? (
116+
<ToolTip
117+
place="bottom"
118+
className="flex flex-wrap items-center"
119+
content={
120+
<p className="m-0 p-0">
121+
Auto-Accept Agent Edits setting is on
122+
</p>
123+
}
124+
>
125+
<InformationCircleIcon className="h-3 w-3 flex-shrink-0 cursor-help text-yellow-500" />
126+
</ToolTip>
127+
) : null}
103128
{props.tool.faviconUrl && (
104129
<img
105130
src={props.tool.faviconUrl}
@@ -139,11 +164,13 @@ export function ToolPolicyItem(props: ToolPolicyItemProps) {
139164
data-tooltip-id={disabled ? disabledTooltipId : undefined}
140165
>
141166
<span className="text-xs">
142-
{disabled || policy === "disabled"
143-
? "Excluded"
144-
: policy === "allowedWithoutPermission"
145-
? "Automatic"
146-
: "Ask First"}
167+
{isAutoAcceptedToolCall
168+
? "Automatic"
169+
: disabled || policy === "disabled"
170+
? "Excluded"
171+
: policy === "allowedWithoutPermission"
172+
? "Automatic"
173+
: "Ask First"}
147174
</span>
148175
<ChevronDownIcon className="h-3 w-3" />
149176
</ListboxButton>

gui/src/redux/slices/sessionSlice.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
ToolCallState,
2626
} from "core";
2727
import type { RemoteSessionMetadata } from "core/control-plane/client";
28-
import { BuiltInToolNames } from "core/tools/builtIn";
2928
import { NEW_SESSION_TITLE } from "core/util/constants";
3029
import {
3130
renderChatMessage,
@@ -36,7 +35,7 @@ import { findLastIndex } from "lodash";
3635
import { v4 as uuidv4 } from "uuid";
3736
import { type InlineErrorMessageType } from "../../components/mainInput/InlineErrorMessage";
3837
import { toolCallCtxItemToCtxItemWithId } from "../../pages/gui/ToolCallDiv/utils";
39-
import { addToolCallDeltaToState } from "../../util/toolCallState";
38+
import { addToolCallDeltaToState, isEditTool } from "../../util/toolCallState";
4039
import { RootState } from "../store";
4140
import { streamResponseThunk } from "../thunks/streamResponse";
4241
import { findChatHistoryItemByToolCallId, findToolCallById } from "../util";
@@ -51,17 +50,10 @@ import { findChatHistoryItemByToolCallId, findToolCallById } from "../util";
5150
function filterMultipleEditToolCalls(
5251
toolCalls: ToolCallDelta[],
5352
): ToolCallDelta[] {
54-
const editToolNames = [
55-
BuiltInToolNames.EditExistingFile,
56-
BuiltInToolNames.SingleFindAndReplace,
57-
BuiltInToolNames.MultiEdit,
58-
];
5953
let hasSeenEditTool = false;
6054

6155
return toolCalls.filter((toolCall) => {
62-
const isEditTool = editToolNames.includes(toolCall.function?.name as any);
63-
64-
if (isEditTool) {
56+
if (toolCall.function?.name && isEditTool(toolCall.function?.name)) {
6557
if (hasSeenEditTool) {
6658
return false; // Skip this duplicate edit tool
6759
}

gui/src/redux/thunks/evaluateToolPolicies.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ToolPolicy } from "@continuedev/terminal-security";
22
import { Tool, ToolCallState } from "core";
33
import { IIdeMessenger } from "../../context/IdeMessenger";
4+
import { isEditTool } from "../../util/toolCallState";
45
import { errorToolCall, updateToolCallOutput } from "../slices/sessionSlice";
56
import { DEFAULT_TOOL_SETTING, ToolPolicies } from "../slices/uiSlice";
67
import { AppThunkDispatch } from "../store";
@@ -20,7 +21,16 @@ async function evaluateToolPolicy(
2021
activeTools: Tool[],
2122
toolCallState: ToolCallState,
2223
toolPolicies: ToolPolicies,
24+
autoAcceptEditToolDiffs: boolean | undefined,
2325
): Promise<EvaluatedPolicy> {
26+
// allow edit tool calls without permission if auto-accept is enabled
27+
if (
28+
isEditTool(toolCallState.toolCall.function.name) &&
29+
autoAcceptEditToolDiffs
30+
) {
31+
return { policy: "allowedWithoutPermission", toolCallState };
32+
}
33+
2434
const basePolicy =
2535
toolPolicies[toolCallState.toolCall.function.name] ??
2636
activeTools.find(
@@ -73,6 +83,7 @@ export async function evaluateToolPolicies(
7383
activeTools: Tool[],
7484
generatedToolCalls: ToolCallState[],
7585
toolPolicies: ToolPolicies,
86+
autoAcceptEditToolDiffs: boolean | undefined,
7687
): Promise<EvaluatedPolicy[]> {
7788
// Check if ALL tool calls are auto-approved using dynamic evaluation
7889
const policyResults = await Promise.all(
@@ -82,6 +93,7 @@ export async function evaluateToolPolicies(
8293
activeTools,
8394
toolCallState,
8495
toolPolicies,
96+
autoAcceptEditToolDiffs,
8597
),
8698
),
8799
);

gui/src/redux/thunks/streamNormalInput.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ export const streamNormalInput = createAsyncThunk<
268268
activeTools,
269269
generatedCalls3,
270270
toolPolicies,
271+
state3.config.config.ui?.autoAcceptEditToolDiffs,
271272
);
272273
const anyRequireApproval = policies.find(
273274
({ policy }) => policy === "allowedWithPermission",

gui/src/util/toolCallState.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ToolCallDelta, ToolCallState } from "core";
2+
import { BuiltInToolNames } from "core/tools/builtIn";
23
import { incrementalParseJson } from "core/util/incrementalParseJson";
34

45
// Merge streamed tool calls
@@ -67,3 +68,12 @@ export function addToolCallDeltaToState(
6768
parsedArgs,
6869
};
6970
}
71+
72+
const editToolNames: string[] = [
73+
BuiltInToolNames.EditExistingFile,
74+
BuiltInToolNames.SingleFindAndReplace,
75+
BuiltInToolNames.MultiEdit,
76+
];
77+
export function isEditTool(toolName: string) {
78+
return editToolNames.includes(toolName);
79+
}

0 commit comments

Comments
 (0)