qwen-code/packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
Taylor Mullen 81f0f618f7 Fix Gemini Code's (GC) smarts.
- The tl;dr; is that GC couldn't see what the user was saying when tool call events happened in response. The rason why this was happening was because we were instantly invoking tools that the model told us to invoke and then instantly re-requesting. This resulted in the bug because the genai APIs can't update the chat history before a full response has been completed (doesn't know how to update if it's incomplete).
- To address the above issue I had to do quite the large refactor. The gist is that now turns truly drive everything on the server (vs. a server client split). This ensured that when we got tool invocations we could control when/how re-requesting would happen and then also ensure that history was updated. This change also meant that the server would act as an event publisher to enable the client to react to events rather than try and weave in complex logic between the events.
- A BIG change that this changeset incudes is the removal of all of the CLI tools in favor of the server tools.
- Removed some dead code as part of this
- **NOTE: Confirmations are still broken (they were broken prior to this); however, I've set them up to be able to work in the future, I'll dot hat in a follow up to be less breaking to others.**

Fixes https://b.corp.google.com/issues/412320087
2025-04-21 11:07:09 -04:00

125 lines
3.5 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import React from 'react';
import { Box, Text, useInput } from 'ink';
import SelectInput from 'ink-select-input';
import { PartListUnion } from '@google/genai';
import { DiffRenderer } from './DiffRenderer.js';
import { UI_WIDTH } from '../../constants.js';
import { Colors } from '../../colors.js';
import {
ToolCallConfirmationDetails,
ToolEditConfirmationDetails,
ToolConfirmationOutcome,
ToolExecuteConfirmationDetails,
} from '@gemini-code/server';
export interface ToolConfirmationMessageProps {
confirmationDetails: ToolCallConfirmationDetails;
onSubmit: (value: PartListUnion) => void;
}
function isEditDetails(
props: ToolCallConfirmationDetails,
): props is ToolEditConfirmationDetails {
return (props as ToolEditConfirmationDetails).fileName !== undefined;
}
interface InternalOption {
label: string;
value: ToolConfirmationOutcome;
}
export const ToolConfirmationMessage: React.FC<
ToolConfirmationMessageProps
> = ({ confirmationDetails }) => {
const { onConfirm } = confirmationDetails;
useInput((_, key) => {
if (key.escape) {
onConfirm(ToolConfirmationOutcome.Cancel);
}
});
const handleSelect = (item: InternalOption) => {
onConfirm(item.value);
};
let bodyContent: React.ReactNode | null = null; // Removed contextDisplay here
let question: string;
const options: InternalOption[] = [];
if (isEditDetails(confirmationDetails)) {
// Body content is now the DiffRenderer, passing filename to it
// The bordered box is removed from here and handled within DiffRenderer
bodyContent = <DiffRenderer diffContent={confirmationDetails.fileDiff} />;
question = `Apply this change?`;
options.push(
{
label: '1. Yes, apply change',
value: ToolConfirmationOutcome.ProceedOnce,
},
{
label: '2. Yes, always apply file edits',
value: ToolConfirmationOutcome.ProceedAlways,
},
{ label: '3. No (esc)', value: ToolConfirmationOutcome.Cancel },
);
} else {
const executionProps =
confirmationDetails as ToolExecuteConfirmationDetails;
// For execution, we still need context display and description
const commandDisplay = (
<Text color={Colors.AccentCyan}>{executionProps.command}</Text>
);
// Combine command and description into bodyContent for layout consistency
bodyContent = (
<Box flexDirection="column">
<Box paddingX={1} marginLeft={1}>
{commandDisplay}
</Box>
</Box>
);
question = `Allow execution?`;
const alwaysLabel = `2. Yes, always allow '${executionProps.rootCommand}' commands`;
options.push(
{
label: '1. Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
},
{
label: alwaysLabel,
value: ToolConfirmationOutcome.ProceedAlways,
},
{ label: '3. No (esc)', value: ToolConfirmationOutcome.Cancel },
);
}
return (
<Box flexDirection="column" padding={1} minWidth={UI_WIDTH}>
{/* Body Content (Diff Renderer or Command Info) */}
{/* No separate context display here anymore for edits */}
<Box flexGrow={1} flexShrink={1} overflow="hidden" marginBottom={1}>
{bodyContent}
</Box>
{/* Confirmation Question */}
<Box marginBottom={1} flexShrink={0}>
<Text>{question}</Text>
</Box>
{/* Select Input for Options */}
<Box flexShrink={0}>
<SelectInput items={options} onSelect={handleSelect} />
</Box>
</Box>
);
};