fix: show tool inputs before approval (#10932)

This commit is contained in:
Jasper
2026-08-19 17:10:31 +02:00
committed by GitHub
parent 9df520d507
commit 607a77cfb1
2 changed files with 64 additions and 4 deletions
@@ -0,0 +1,55 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { IntlTestWrapper } from '../i18n/test-utils';
import type { ActionRequired } from '../types/message';
import ToolCallConfirmation from './ToolCallConfirmation';
vi.mock('./ToolApprovalButtons', () => ({
default: () => <div data-testid="approval-buttons" />,
}));
const securityPrompt = 'This command sends a local file to a remote service.';
const actionRequiredContent = {
type: 'actionRequired',
data: {
actionType: 'toolConfirmation',
id: 'request-1',
toolName: 'developer__shell',
arguments: {
command: 'upload /home/alice/private.txt to files.example.test',
},
prompt: securityPrompt,
},
} as ActionRequired & { type: 'actionRequired' };
describe('ToolCallConfirmation', () => {
it('shows the concrete tool arguments before approval', () => {
render(
<ToolCallConfirmation
sessionId="session-1"
isClicked={false}
actionRequiredContent={actionRequiredContent}
/>,
{ wrapper: IntlTestWrapper }
);
expect(screen.getByText('command')).toBeInTheDocument();
expect(screen.getByText(/upload \/home\/alice\/private\.txt/)).toBeInTheDocument();
expect(screen.getByTestId('approval-buttons')).toBeInTheDocument();
});
it('shows the security prompt before approval', () => {
render(
<ToolCallConfirmation
sessionId="session-1"
isClicked={false}
actionRequiredContent={actionRequiredContent}
/>,
{ wrapper: IntlTestWrapper }
);
expect(screen.getByText(securityPrompt)).toBeInTheDocument();
expect(screen.getByTestId('approval-buttons')).toBeInTheDocument();
});
});
@@ -2,6 +2,7 @@ import type { ActionRequired } from '../types/message';
import { defineMessages, useIntl } from '../i18n';
import { snakeToTitleCase } from '../utils';
import ToolApprovalButtons from './ToolApprovalButtons';
import { ToolCallArguments, type ToolCallArgumentValue } from './ToolCallArguments';
const i18n = defineMessages({
allowToolCallWithName: {
@@ -35,7 +36,7 @@ export default function ToolConfirmation({
}: ToolConfirmationProps) {
const intl = useIntl();
const data = actionRequiredContent.data as ToolConfirmationData;
const { id, toolName, prompt } = data;
const { id, toolName, arguments: toolArguments, prompt } = data;
const displayName = formatToolName(toolName);
return (
@@ -45,9 +46,13 @@ export default function ToolConfirmation({
? intl.formatMessage(i18n.allowToolCallWithName, { toolName: displayName })
: intl.formatMessage(i18n.gooseWouldLikeToCallWithName, { toolName: displayName })}
</div>
<ToolApprovalButtons
data={{ id, toolName, prompt: prompt ?? undefined, sessionId, isClicked }}
/>
<div className="px-4 pb-2">
{prompt && <div className="py-2 text-sm text-amber-600 dark:text-amber-400">{prompt}</div>}
<ToolCallArguments args={toolArguments as Record<string, ToolCallArgumentValue>} />
<ToolApprovalButtons
data={{ id, toolName, prompt: prompt ?? undefined, sessionId, isClicked }}
/>
</div>
</div>
);
}