fix re enter problem

This commit is contained in:
2026-07-10 15:56:41 +08:00
parent c077767e0d
commit e1ae49f114
4 changed files with 123 additions and 40 deletions
@@ -3,7 +3,10 @@
import { act, renderHook, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { EditorAgentMessageResponse } from '../../../../packages/shared/src/contracts/editorAgent.ts';
import type {
EditorAgentMessage,
EditorAgentMessageResponse,
} from '../../../../packages/shared/src/contracts/editorAgent.ts';
import {
type EditorAgentConversationClient,
useEditorAgentConversation,
@@ -305,6 +308,74 @@ describe('useEditorAgentConversation', () => {
expect(onCanvasRefreshRequested).toHaveBeenCalledTimes(1);
});
it('marks a confirmation as executing before the request finishes', async () => {
const client = createClient();
const pendingMessage: EditorAgentMessage = {
id: 0,
role: 'system',
text: '需要生成一张图',
attachments: [],
toolCall: {
toolName: 'edit-image',
summary: '',
status: 'pending_confirmation',
args: { object_image_id: 'source-image', prompt: '换成像素风' },
images: [],
error: null,
},
createdAt: '2026-07-03T00:00:00.000Z',
};
const completedMessage: EditorAgentMessage = {
...pendingMessage,
toolCall: {
...pendingMessage.toolCall!,
status: 'completed',
images: [],
},
};
let resolveConfirmation: ((message: EditorAgentMessage) => void) | undefined;
vi.mocked(client.getConversation).mockResolvedValue({
conversationId: 'conversation-1',
projectId: 'project-1',
title: '角色参考',
messages: [pendingMessage],
createdAt: '2026-07-03T00:00:00.000Z',
updatedAt: '2026-07-03T00:00:00.000Z',
});
vi.mocked(client.confirmToolCall).mockImplementation(
() =>
new Promise<EditorAgentMessage>((resolve) => {
resolveConfirmation = resolve;
}),
);
const { result } = renderHook(() =>
useEditorAgentConversation({ projectId: 'project-1', client }),
);
await waitFor(() => {
expect(result.current.messages[0]?.toolCall?.status).toBe(
'pending_confirmation',
);
});
act(() => {
void result.current.confirmToolCall(0);
});
await waitFor(() => {
expect(result.current.messages[0]?.toolCall?.status).toBe('executing');
});
await result.current.confirmToolCall(0);
expect(client.confirmToolCall).toHaveBeenCalledTimes(1);
await act(async () => {
resolveConfirmation?.(completedMessage);
});
await waitFor(() => {
expect(result.current.messages[0]?.toolCall?.status).toBe('completed');
});
});
it('cancels a pending tool call and keeps the replacement in the same position', async () => {
const client = createClient();
const pendingMessage = {
@@ -409,6 +409,23 @@ export function useEditorAgentConversation({
setToolCallAction(nextAction);
setErrorMessage(null);
if (action === 'confirm') {
setMessages((currentMessages) =>
currentMessages.map((message) =>
message.id === messageId &&
message.toolCall?.status === 'pending_confirmation'
? {
...message,
toolCall: {
...message.toolCall,
status: 'executing',
},
}
: message,
),
);
}
try {
const updatedMessage = await (action === 'confirm'
? client.confirmToolCall(conversationId, messageId)
@@ -436,6 +453,8 @@ export function useEditorAgentConversation({
? '确认画布 Agent 操作失败'
: '取消画布 Agent 操作失败',
);
// The server may have accepted the action even when its response was lost.
void loadConversation(conversationId).catch(() => undefined);
}
return null;
} finally {
@@ -445,7 +464,7 @@ export function useEditorAgentConversation({
}
}
},
[client, requestCanvasRefreshForMessages],
[client, loadConversation, requestCanvasRefreshForMessages],
);
const confirmToolCall = useCallback(