mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-15 20:40:35 -07:00
fix: error out on tool approvals instead of hanging
This commit ensures that the interactive agent loop does not hang when a tool requires manual approval (since confirmation dialogs are not yet implemented for the new protocol). Instead of waiting indefinitely, 'LegacyAgentSession' now detects the 'awaiting_approval' status, emits a descriptive error event, and aborts the session. Key changes: - 'LegacyAgentSession' now emits status updates in 'tool_update' events. - 'LegacyAgentSession' aborts and errors on 'awaiting_approval' status. - 'useAgentStream' now correctly tracks and displays tool status transitions and intermediate progress via the 'AgentEvent' stream.
This commit is contained in:
@@ -292,6 +292,53 @@ describe('useAgentStream', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should display error message when a tool call requires approval', async () => {
|
||||
let eventHandler: (event: any) => void = () => {};
|
||||
vi.spyOn(mockLegacyAgentSession, 'subscribe').mockImplementation((handler) => {
|
||||
eventHandler = handler;
|
||||
return () => {};
|
||||
});
|
||||
|
||||
await renderHookWithProviders(() =>
|
||||
useAgentStream(
|
||||
{} as any,
|
||||
[],
|
||||
mockAddItem,
|
||||
mockConfig,
|
||||
mockSettings,
|
||||
mockOnDebugMessage,
|
||||
mockHandleSlashCommand,
|
||||
false,
|
||||
() => undefined,
|
||||
mockOnAuthError,
|
||||
mockPerformMemoryRefresh,
|
||||
false,
|
||||
mockSetModelSwitchedFromQuotaError,
|
||||
mockOnCancelSubmit,
|
||||
mockSetShellInputFocused,
|
||||
80,
|
||||
24,
|
||||
),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
eventHandler({
|
||||
type: 'error',
|
||||
status: 'UNIMPLEMENTED',
|
||||
message: 'TODO: Tool approvals not yet implemented, please switch to YOLO mode to test.',
|
||||
fatal: true,
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: MessageType.ERROR,
|
||||
text: 'TODO: Tool approvals not yet implemented, please switch to YOLO mode to test.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should call session.abort when cancelOngoingRequest is called', async () => {
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useAgentStream(
|
||||
|
||||
@@ -218,7 +218,7 @@ export const useAgentStream = (
|
||||
isClientInitiated: false,
|
||||
originalRequestName: event.name,
|
||||
},
|
||||
status: 'executing',
|
||||
status: 'scheduled',
|
||||
tool: {
|
||||
displayName: (event._meta?.['displayName'] as string) ?? event.name,
|
||||
isOutputMarkdown: (event._meta?.['isOutputMarkdown'] as boolean) ?? false,
|
||||
@@ -236,13 +236,27 @@ export const useAgentStream = (
|
||||
tc.request.callId === event.requestId
|
||||
? ({
|
||||
...tc,
|
||||
liveOutput: event.displayContent?.[0]?.type === 'text' ? event.displayContent[0].text : undefined,
|
||||
progressMessage: event.data?.['progressMessage'] as string | undefined,
|
||||
progress: event.data?.['progress'] as number | undefined,
|
||||
progressTotal: event.data?.['progressTotal'] as number | undefined,
|
||||
pid: event.data?.['pid'] as number | undefined,
|
||||
status: (event.data?.['status'] as any) ?? tc.status,
|
||||
liveOutput:
|
||||
event.displayContent?.[0]?.type === 'text'
|
||||
? event.displayContent[0].text
|
||||
: (tc as any).liveOutput,
|
||||
progressMessage:
|
||||
(event.data?.['progressMessage'] as string | undefined) ??
|
||||
(tc as any).progressMessage,
|
||||
progress:
|
||||
(event.data?.['progress'] as number | undefined) ??
|
||||
(tc as any).progress,
|
||||
progressTotal:
|
||||
(event.data?.['progressTotal'] as number | undefined) ??
|
||||
(tc as any).progressTotal,
|
||||
pid:
|
||||
(event.data?.['pid'] as number | undefined) ??
|
||||
(tc as any).pid,
|
||||
invocation: {
|
||||
getDescription: () => (event._meta?.['description'] as string) ?? (tc as any).invocation?.getDescription(),
|
||||
getDescription: () =>
|
||||
(event._meta?.['description'] as string) ??
|
||||
(tc as any).invocation?.getDescription(),
|
||||
},
|
||||
} as unknown as TrackedToolCall)
|
||||
: tc,
|
||||
@@ -257,7 +271,11 @@ export const useAgentStream = (
|
||||
...tc,
|
||||
status: event.isError ? 'error' : 'success',
|
||||
response: {
|
||||
resultDisplay: event._meta?.['resultDisplay'] ?? (event.displayContent?.[0]?.type === 'text' ? event.displayContent[0].text : undefined),
|
||||
resultDisplay:
|
||||
event._meta?.['resultDisplay'] ??
|
||||
(event.displayContent?.[0]?.type === 'text'
|
||||
? event.displayContent[0].text
|
||||
: undefined),
|
||||
outputFile: event._meta?.['outputFile'] as string | undefined,
|
||||
},
|
||||
responseSubmittedToGemini: true,
|
||||
|
||||
@@ -168,6 +168,19 @@ class LegacyAgentProtocol implements AgentProtocol {
|
||||
const handleToolCallsUpdate = (event: ToolCallsUpdateMessage) => {
|
||||
const toolUpdates: AgentEvent[] = [];
|
||||
for (const tc of event.toolCalls) {
|
||||
if (tc.status === 'awaiting_approval') {
|
||||
this._emit([
|
||||
this._makeErrorEvent({
|
||||
status: 'UNIMPLEMENTED',
|
||||
message:
|
||||
'TODO: Tool approvals not yet implemented, please switch to YOLO mode to test.',
|
||||
fatal: true,
|
||||
}),
|
||||
]);
|
||||
void this.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
if (tc.status === 'executing') {
|
||||
toolUpdates.push(
|
||||
this._makeToolUpdateEvent({
|
||||
|
||||
Reference in New Issue
Block a user