Skip to content

Commit a5ef57a

Browse files
authored
Merge pull request #8927 from uinstinct/approve-read-only
feat: auto approve parallel read only builtin tools
2 parents f5a2707 + 2024317 commit a5ef57a

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

gui/src/pages/gui/ToolCallDiv/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function ToolCallDiv({
2929

3030
const shouldShowGroupedUI = toolCallStates.length > 1 && isStreamingComplete;
3131
const activeCalls = toolCallStates.filter(
32-
(call) => call.status !== "canceled",
32+
(call) => call.status !== "canceled" && call.status !== "done",
3333
);
3434

3535
const renderToolCall = (toolCallState: ToolCallState) => {

gui/src/redux/thunks/streamNormalInput.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createAsyncThunk, unwrapResult } from "@reduxjs/toolkit";
22
import { LLMFullCompletionOptions, ModelDescription } from "core";
33
import { getRuleId } from "core/llm/rules/getSystemMessageWithRules";
44
import { ToCoreProtocol } from "core/protocol";
5+
import { BUILT_IN_GROUP_NAME } from "core/tools/builtIn";
56
import { selectActiveTools } from "../selectors/selectActiveTools";
67
import { selectSelectedChatModel } from "../slices/configSlice";
78
import {
@@ -317,14 +318,43 @@ export const streamNormalInput = createAsyncThunk<
317318
generatedCalls3,
318319
toolPolicies,
319320
);
320-
const anyRequireApproval = policies.find(
321+
const autoApprovedPolicies = policies.filter(
322+
({ policy }) => policy === "allowedWithoutPermission",
323+
);
324+
const needsApprovalPolicies = policies.filter(
321325
({ policy }) => policy === "allowedWithPermission",
322326
);
323327

324328
// 4. Execute remaining tool calls
325-
// Only set inactive if not all tools were auto-approved
326-
// This prevents UI flashing for auto-approved tools
327-
if (originalToolCalls.length === 0 || anyRequireApproval) {
329+
if (originalToolCalls.length === 0) {
330+
dispatch(setInactive());
331+
} else if (needsApprovalPolicies.length > 0) {
332+
const builtInReadonlyAutoApproved = autoApprovedPolicies.filter(
333+
({ toolCallState }) =>
334+
toolCallState.tool?.group === BUILT_IN_GROUP_NAME &&
335+
toolCallState.tool?.readonly,
336+
);
337+
338+
if (builtInReadonlyAutoApproved.length > 0) {
339+
const state4 = getState();
340+
if (streamAborter.signal.aborted || !state4.session.isStreaming) {
341+
return;
342+
}
343+
await Promise.all(
344+
builtInReadonlyAutoApproved.map(async ({ toolCallState }) => {
345+
unwrapResult(
346+
await dispatch(
347+
callToolById({
348+
toolCallId: toolCallState.toolCallId,
349+
isAutoApproved: true,
350+
depth: depth + 1,
351+
}),
352+
),
353+
);
354+
}),
355+
);
356+
}
357+
328358
dispatch(setInactive());
329359
} else {
330360
// auto stream cases increase thunk depth by 1 for debugging
@@ -334,7 +364,6 @@ export const streamNormalInput = createAsyncThunk<
334364
return;
335365
}
336366
if (generatedCalls4.length > 0) {
337-
// All that didn't fail are auto approved - call them
338367
await Promise.all(
339368
generatedCalls4.map(async ({ toolCallId }) => {
340369
unwrapResult(
@@ -349,7 +378,6 @@ export const streamNormalInput = createAsyncThunk<
349378
}),
350379
);
351380
} else {
352-
// All failed - stream on
353381
for (const { toolCallId } of originalToolCalls) {
354382
unwrapResult(
355383
await dispatch(

0 commit comments

Comments
 (0)