fix: forward entire tool call confirmation object through useToolScheduler (#481)

This commit is contained in:
Brandon Keiji 2025-05-22 06:00:36 +00:00 committed by GitHub
parent 02eec5c8ca
commit 4e3ba687a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 30 additions and 37 deletions

View File

@ -10,7 +10,6 @@ import { DiffRenderer } from './DiffRenderer.js';
import { Colors } from '../../colors.js'; import { Colors } from '../../colors.js';
import { import {
ToolCallConfirmationDetails, ToolCallConfirmationDetails,
ToolEditConfirmationDetails,
ToolConfirmationOutcome, ToolConfirmationOutcome,
ToolExecuteConfirmationDetails, ToolExecuteConfirmationDetails,
} from '@gemini-code/server'; } from '@gemini-code/server';
@ -23,12 +22,6 @@ export interface ToolConfirmationMessageProps {
confirmationDetails: ToolCallConfirmationDetails; confirmationDetails: ToolCallConfirmationDetails;
} }
function isEditDetails(
props: ToolCallConfirmationDetails,
): props is ToolEditConfirmationDetails {
return (props as ToolEditConfirmationDetails).fileName !== undefined;
}
export const ToolConfirmationMessage: React.FC< export const ToolConfirmationMessage: React.FC<
ToolConfirmationMessageProps ToolConfirmationMessageProps
> = ({ confirmationDetails }) => { > = ({ confirmationDetails }) => {
@ -49,7 +42,7 @@ export const ToolConfirmationMessage: React.FC<
RadioSelectItem<ToolConfirmationOutcome> RadioSelectItem<ToolConfirmationOutcome>
>(); >();
if (isEditDetails(confirmationDetails)) { if (confirmationDetails.type === 'edit') {
// Body content is now the DiffRenderer, passing filename to it // Body content is now the DiffRenderer, passing filename to it
// The bordered box is removed from here and handled within DiffRenderer // The bordered box is removed from here and handled within DiffRenderer
bodyContent = ( bodyContent = (

View File

@ -10,6 +10,7 @@ import {
ToolCallResponseInfo, ToolCallResponseInfo,
ToolConfirmationOutcome, ToolConfirmationOutcome,
Tool, Tool,
ToolCallConfirmationDetails,
} from '@gemini-code/server'; } from '@gemini-code/server';
import { Part } from '@google/genai'; import { Part } from '@google/genai';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
@ -55,7 +56,7 @@ type WaitingToolCall = {
status: 'awaiting_approval'; status: 'awaiting_approval';
request: ToolCallRequestInfo; request: ToolCallRequestInfo;
tool: Tool; tool: Tool;
confirm: (outcome: ToolConfirmationOutcome) => Promise<void>; confirmationDetails: ToolCallConfirmationDetails;
}; };
export type Status = ToolCall['status']; export type Status = ToolCall['status'];
@ -119,17 +120,20 @@ export function useToolScheduler(
status: 'awaiting_approval', status: 'awaiting_approval',
request: r, request: r,
tool, tool,
confirm: async (outcome) => { confirmationDetails: {
await userApproval.onConfirm(outcome); ...userApproval,
setToolCalls( onConfirm: async (outcome) => {
outcome === ToolConfirmationOutcome.Cancel await userApproval.onConfirm(outcome);
? setStatus( setToolCalls(
r.callId, outcome === ToolConfirmationOutcome.Cancel
'cancelled', ? setStatus(
'User did not allow tool call', r.callId,
) 'cancelled',
: setStatus(r.callId, 'scheduled'), 'User did not allow tool call',
); )
: setStatus(r.callId, 'scheduled'),
);
},
}, },
}; };
} }
@ -249,7 +253,7 @@ function setStatus(
function setStatus( function setStatus(
targetCallId: string, targetCallId: string,
status: 'awaiting_approval', status: 'awaiting_approval',
confirm: (t: ToolConfirmationOutcome) => Promise<void>, confirm: ToolCallConfirmationDetails,
): (t: ToolCall[]) => ToolCall[]; ): (t: ToolCall[]) => ToolCall[];
function setStatus( function setStatus(
targetCallId: string, targetCallId: string,
@ -296,9 +300,7 @@ function setStatus(
const next: WaitingToolCall = { const next: WaitingToolCall = {
...t, ...t,
status: 'awaiting_approval', status: 'awaiting_approval',
confirm: auxiliaryData as ( confirmationDetails: auxiliaryData as ToolCallConfirmationDetails,
o: ToolConfirmationOutcome,
) => Promise<void>,
}; };
return next; return next;
} }
@ -426,10 +428,7 @@ export function mapToDisplay(
description: t.tool.getDescription(t.request.args), description: t.tool.getDescription(t.request.args),
resultDisplay: undefined, resultDisplay: undefined,
status: mapStatus(t.status), status: mapStatus(t.status),
confirmationDetails: { confirmationDetails: t.confirmationDetails,
title: t.request.name,
onConfirm: t.confirm,
},
}; };
case 'executing': case 'executing':
return { return {

View File

@ -291,6 +291,7 @@ Expectation for parameters:
{ context: 3 }, { context: 3 },
); );
const confirmationDetails: ToolEditConfirmationDetails = { const confirmationDetails: ToolEditConfirmationDetails = {
type: 'edit',
title: `Confirm Edit: ${shortenPath(makeRelative(params.file_path, this.rootDirectory))}`, title: `Confirm Edit: ${shortenPath(makeRelative(params.file_path, this.rootDirectory))}`,
fileName, fileName,
fileDiff, fileDiff,

View File

@ -107,6 +107,7 @@ export class ShellTool extends BaseTool<ShellToolParams, ToolResult> {
return false; // already approved and whitelisted return false; // already approved and whitelisted
} }
const confirmationDetails: ToolExecuteConfirmationDetails = { const confirmationDetails: ToolExecuteConfirmationDetails = {
type: 'exec',
title: 'Confirm Shell Command', title: 'Confirm Shell Command',
command: params.command, command: params.command,
rootCommand, rootCommand,

View File

@ -171,25 +171,23 @@ export interface FileDiff {
fileName: string; fileName: string;
} }
export interface ToolCallConfirmationDetailsDefault { export interface ToolEditConfirmationDetails {
type: 'edit';
title: string; title: string;
onConfirm: (outcome: ToolConfirmationOutcome) => Promise<void>; onConfirm: (outcome: ToolConfirmationOutcome) => Promise<void>;
}
export interface ToolEditConfirmationDetails
extends ToolCallConfirmationDetailsDefault {
fileName: string; fileName: string;
fileDiff: string; fileDiff: string;
} }
export interface ToolExecuteConfirmationDetails export interface ToolExecuteConfirmationDetails {
extends ToolCallConfirmationDetailsDefault { type: 'exec';
title: string;
onConfirm: (outcome: ToolConfirmationOutcome) => Promise<void>;
command: string; command: string;
rootCommand: string; rootCommand: string;
} }
export type ToolCallConfirmationDetails = export type ToolCallConfirmationDetails =
| ToolCallConfirmationDetailsDefault
| ToolEditConfirmationDetails | ToolEditConfirmationDetails
| ToolExecuteConfirmationDetails; | ToolExecuteConfirmationDetails;

View File

@ -158,6 +158,7 @@ export class WriteFileTool extends BaseTool<WriteFileToolParams, ToolResult> {
); );
const confirmationDetails: ToolEditConfirmationDetails = { const confirmationDetails: ToolEditConfirmationDetails = {
type: 'edit',
title: `Confirm Write: ${shortenPath(relativePath)}`, title: `Confirm Write: ${shortenPath(relativePath)}`,
fileName, fileName,
fileDiff, fileDiff,

View File

@ -94,7 +94,7 @@ export async function checkNextSpeaker(
return null; return null;
} catch (error) { } catch (error) {
console.warn( console.warn(
'Failed to talk to Gemiin endpoint when seeing if conversation should continue.', 'Failed to talk to Gemini endpoint when seeing if conversation should continue.',
error, error,
); );
return null; return null;