@@ -2,6 +2,7 @@ import { createAsyncThunk, unwrapResult } from "@reduxjs/toolkit";
22import { LLMFullCompletionOptions , ModelDescription } from "core" ;
33import { getRuleId } from "core/llm/rules/getSystemMessageWithRules" ;
44import { ToCoreProtocol } from "core/protocol" ;
5+ import { BUILT_IN_GROUP_NAME } from "core/tools/builtIn" ;
56import { selectActiveTools } from "../selectors/selectActiveTools" ;
67import { selectSelectedChatModel } from "../slices/configSlice" ;
78import {
@@ -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