feat: use acp for desktop chat prompt (feature toggle off) (#9802)

Signed-off-by: Douwe M Osinga <douwe@sidewalklabs.com>
Co-authored-by: Douwe M Osinga <douwe@sidewalklabs.com>
This commit is contained in:
Lifei Zhou
2026-06-18 10:13:08 +10:00
committed by GitHub
parent 109bda35ac
commit 802dd39165
23 changed files with 2963 additions and 44 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ import { ChatType } from '../types/chat';
import { useIsMobile } from '../hooks/use-mobile';
import { useNavigationContextSafe } from './Layout/NavigationContext';
import { cn } from '../utils';
import { useChatStream } from '../hooks/useChatStream';
import { useChatSession } from '../hooks/useChatSession';
import { useNavigation } from '../hooks/useNavigation';
import { RecipeHeader } from './RecipeHeader';
import { RecipeWarningModal } from './ui/RecipeWarningModal';
@@ -114,7 +114,7 @@ export default function BaseChat({
tokenState,
notifications: toolCallNotifications,
onMessageUpdate,
} = useChatStream({
} = useChatSession({
sessionId,
onStreamFinish,
});
@@ -0,0 +1,89 @@
import { render, type RenderOptions, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { confirmToolAction } from '../api';
import { resolveAcpPermissionRequest } from '../acp/permissionRequests';
import { IntlTestWrapper } from '../i18n/test-utils';
import ToolApprovalButtons from './ToolApprovalButtons';
vi.mock('../api', () => ({
confirmToolAction: vi.fn(),
}));
vi.mock('../acp/permissionRequests', () => ({
resolveAcpPermissionRequest: vi.fn(),
}));
vi.mock('../acpChatFeatureFlag', () => ({
USE_ACP_CHAT: true,
}));
const renderWithIntl = (ui: React.ReactElement, options?: RenderOptions) =>
render(ui, { wrapper: IntlTestWrapper, ...options });
const confirmToolActionMock = vi.mocked(confirmToolAction);
const resolveAcpPermissionRequestMock = vi.mocked(resolveAcpPermissionRequest);
describe('ToolApprovalButtons', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('marks the approval accepted when the ACP request resolves', async () => {
resolveAcpPermissionRequestMock.mockReturnValueOnce(true);
renderWithIntl(
<ToolApprovalButtons
data={{
id: 'tool-call-approved',
toolName: 'developer__shell',
sessionId: 'session-1',
}}
/>
);
await userEvent.click(screen.getByRole('button', { name: 'Allow Once' }));
expect(resolveAcpPermissionRequestMock).toHaveBeenCalledWith(
'session-1',
'tool-call-approved',
'allow_once'
);
expect(confirmToolActionMock).not.toHaveBeenCalled();
expect(screen.getByText('developer__shell - Allowed once')).toBeInTheDocument();
});
it('falls back to the REST confirmation when no ACP request is pending', async () => {
resolveAcpPermissionRequestMock.mockReturnValueOnce(false);
confirmToolActionMock.mockResolvedValueOnce({ error: undefined } as Awaited<
ReturnType<typeof confirmToolAction>
>);
renderWithIntl(
<ToolApprovalButtons
data={{
id: 'tool-call-rerun',
toolName: 'developer__shell',
sessionId: 'session-1',
}}
/>
);
await userEvent.click(screen.getByRole('button', { name: 'Allow Once' }));
expect(resolveAcpPermissionRequestMock).toHaveBeenCalledWith(
'session-1',
'tool-call-rerun',
'allow_once'
);
expect(confirmToolActionMock).toHaveBeenCalledWith({
body: {
sessionId: 'session-1',
id: 'tool-call-rerun',
action: 'allow_once',
principalType: 'Tool',
},
});
expect(screen.getByText('developer__shell - Allowed once')).toBeInTheDocument();
});
});
@@ -1,6 +1,8 @@
import { useState, useEffect } from 'react';
import { Button } from './ui/button';
import { confirmToolAction, Permission } from '../api';
import { resolveAcpPermissionRequest } from '../acp/permissionRequests';
import { USE_ACP_CHAT } from '../acpChatFeatureFlag';
import { defineMessages, useIntl } from '../i18n';
const i18n = defineMessages({
@@ -75,10 +77,18 @@ export default function ToolApprovalButtons({ data }: { data: ToolApprovalData }
}, [id, decision, isClicked]);
const handleAction = async (action: Permission) => {
setDecision(action);
setIsClicked(true);
try {
// Edit-in-place reruns go through the legacy REST path even when ACP chat is
// enabled, so fall back to confirmToolAction when no ACP request is pending.
if (USE_ACP_CHAT && resolveAcpPermissionRequest(sessionId, id, action)) {
setDecision(action);
setIsClicked(true);
return;
}
setDecision(action);
setIsClicked(true);
const response = await confirmToolAction({
body: {
sessionId,