fix(pi): retain ownership for uncertain mutations
This commit is contained in:
@@ -182,7 +182,7 @@ describe('PI-130 feature-complete Coding UI', () => {
|
||||
expect(await screen.findByText(/请求可能已失效/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('keeps model, thinking, abort, metadata, and fork controls on the selected Conversation', async () => {
|
||||
it('keeps model, thinking, metadata, and fork controls on the selected idle Conversation', async () => {
|
||||
const { useProviderStore } = await import('@/stores/providers');
|
||||
useProviderStore.setState({
|
||||
accounts: [{
|
||||
@@ -239,7 +239,7 @@ describe('PI-130 feature-complete Coding UI', () => {
|
||||
},
|
||||
},
|
||||
nodes: [],
|
||||
run: { status: 'running', runId: 'run-1', mode: 'prompt' },
|
||||
run: { status: 'idle' },
|
||||
queue: { items: [] },
|
||||
context: { usedTokens: 200, contextWindow: 1000, compaction: 'idle' },
|
||||
pendingInteractions: [],
|
||||
@@ -270,13 +270,84 @@ describe('PI-130 feature-complete Coding UI', () => {
|
||||
expect(within(thinkingSelect).getAllByRole('option')).toHaveLength(1);
|
||||
expect(within(thinkingSelect).getByRole('option', { name: '高思考' })).toBeInTheDocument();
|
||||
expect(within(thinkingSelect).queryByRole('option', { name: '关闭思考' })).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '中止' }));
|
||||
await waitFor(() => expect(interactionApi.abort).toHaveBeenCalledWith('conversation-1'));
|
||||
fireEvent.click(screen.getByRole('button', { name: '创建分支' }));
|
||||
await waitFor(() => expect(callbacks.fork).toHaveBeenCalledOnce());
|
||||
expect(screen.queryByText(/分享|回滚|待办/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('blocks overlapping mutations but keeps abort and recover available while confirmation is uncertain', async () => {
|
||||
interactionApi.abort.mockResolvedValue(undefined);
|
||||
const recover = vi.fn(async () => undefined);
|
||||
const fork = vi.fn(async () => undefined);
|
||||
const { CodingConversationHeader } = await import('@/pages/Chat/CodingConversationHeader');
|
||||
render(
|
||||
<CodingConversationHeader
|
||||
conversation={{
|
||||
id: 'conversation-uncertain',
|
||||
agentId: 'agent-1',
|
||||
title: 'Slow provider',
|
||||
archivedAt: null,
|
||||
unread: false,
|
||||
createdAt: '2026-08-25T00:00:00.000Z',
|
||||
updatedAt: '2026-08-25T00:00:00.000Z',
|
||||
model: { accountId: 'account-1', modelId: 'model-a', thinkingLevel: 'medium' },
|
||||
modelResolution: 'resolved',
|
||||
}}
|
||||
snapshot={{
|
||||
schemaVersion: 1,
|
||||
conversation: {
|
||||
id: 'conversation-uncertain',
|
||||
projectId: 'project-1',
|
||||
agentId: 'agent-1',
|
||||
title: 'Slow provider',
|
||||
model: {
|
||||
model: { accountId: 'account-1', modelId: 'model-a', thinkingLevel: 'medium' },
|
||||
modelResolution: 'resolved',
|
||||
availableThinkingLevels: ['off', 'medium'],
|
||||
},
|
||||
},
|
||||
nodes: [],
|
||||
run: {
|
||||
status: 'running',
|
||||
runId: 'run-uncertain',
|
||||
mode: 'prompt',
|
||||
error: {
|
||||
code: 'CODING_REQUEST_UNCERTAIN',
|
||||
message: '请求确认延迟,可能仍在执行。',
|
||||
recoverable: true,
|
||||
},
|
||||
},
|
||||
queue: { items: [] },
|
||||
context: { usedTokens: 200, contextWindow: 1000, compaction: 'idle' },
|
||||
pendingInteractions: [],
|
||||
worker: { status: 'ready', generation: 1 },
|
||||
cursor: { workerGeneration: 1, seq: 2 },
|
||||
}}
|
||||
connectionState="live"
|
||||
onRename={vi.fn(async () => undefined)}
|
||||
onArchive={vi.fn(async () => undefined)}
|
||||
onToggleUnread={vi.fn(async () => undefined)}
|
||||
onFork={fork}
|
||||
onRefresh={vi.fn(async () => undefined)}
|
||||
onRecover={recover}
|
||||
onOpenInspector={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole('combobox', { name: '当前对话模型' })).toBeDisabled();
|
||||
expect(screen.getByRole('combobox', { name: '当前对话思考级别' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: '整理上下文' })).toBeDisabled();
|
||||
expect(screen.getByRole('button', { name: '创建分支' })).toBeDisabled();
|
||||
expect(screen.queryByText('本地编程运行时暂时不可用')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '中止' }));
|
||||
await waitFor(() => expect(interactionApi.abort).toHaveBeenCalledWith('conversation-uncertain'));
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: '恢复' })).toBeEnabled());
|
||||
fireEvent.click(screen.getByRole('button', { name: '恢复' }));
|
||||
await waitFor(() => expect(recover).toHaveBeenCalledOnce());
|
||||
expect(fork).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not let an old tools load overwrite the newly selected Conversation inspector', async () => {
|
||||
let resolveOld!: (value: { commands: Array<{ name: string; title: string; description: string; source: 'makelore' }> }) => void;
|
||||
const oldCommands = new Promise<{ commands: Array<{ name: string; title: string; description: string; source: 'makelore' }> }>((resolve) => {
|
||||
|
||||
Reference in New Issue
Block a user