fix(chat): reconcile reply recovery with continuous agent conversations
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
鲨鱼辣椒 committed 2026-09-28 18:14:12 +08:00
1 parent 44b16f8fbd
commit cafebca057
13 files changed
+159 -34

No files matched your search

@@ -41,6 +41,37 @@ const props = { projectId: 'project-a', projectName: '天气', sourceId: 'source
async function mount() { let view!: ReturnType<typeof render>; await act(async () => { view = render(<TeacherChatPanel {...props} />); }); return view; }
describe('continuous agent panel', () => {
it('retries incomplete prose only in its original project/source and keeps current drafts and one send identity', async () => {
const original = { ...request('broken', '现在项目是什么阶段?'), response: '它早就不是', replyIncomplete: true,
references: [{ kind: 'code' as const, text: '原项目代码' }] };
api.conversation.mockResolvedValue({ topic: topic([original]), before: null });
api.seenConversation.mockImplementation(async () => topic([original]));
let finish!: (value: TeacherTopic) => void;
api.sendConversation.mockImplementation(() => new Promise<TeacherTopic>(resolve => { finish = resolve; }));
const view = await mount();
expect(screen.getByRole('status')).toHaveTextContent('这次回复未能完整显示');
expect(screen.queryByTestId('teacher-reply')).toBeNull();
expect(api.sendConversation).not.toHaveBeenCalled();
fireEvent.change(screen.getByRole('textbox'), { target: { value: '仍在编辑的草稿' } });
view.rerender(<TeacherChatPanel {...props} projectId="project-b" sourceId="source-b" />);
expect(screen.getByRole('button', { name: '重新回答' })).toBeDisabled();
view.rerender(<TeacherChatPanel {...props} sourceId="another-source" />);
expect(screen.getByRole('button', { name: '重新回答' })).toBeDisabled();
view.rerender(<TeacherChatPanel {...props} />);
const retry = screen.getByRole('button', { name: '重新回答' });
fireEvent.click(retry); fireEvent.click(retry);
expect(api.sendConversation).toHaveBeenCalledTimes(1);
const sent = api.sendConversation.mock.calls[0][1];
expect(sent).toEqual({ requestId: expect.any(String), projectId: 'project-a', sourceConversationId: 'source-a',
text: original.text, references: original.references });
expect(sent.requestId).not.toBe(original.id);
fireEvent.change(screen.getByRole('textbox'), { target: { value: '等待时补充的草稿' } });
await act(async () => finish({ ...topic([original, { ...request(sent.requestId), response: '完整回答' }]), revision: 2 }));
expect(screen.getByText('完整回答')).toBeVisible();
expect(screen.getByRole('textbox')).toHaveValue('等待时补充的草稿');
expect(JSON.parse(localStorage.getItem(`makelore-consultation-draft:kid-${identity.id}:agent:agent-a`)!)).toMatchObject({ text: '等待时补充的草稿' });
});
it('opens one chat without topic controls or creating a topic, and preserves it across project switches', async () => {
const view = await mount();
expect(screen.queryByRole('button', { name: '智能体新话题' })).toBeNull();