fix(coding): reconcile snapshot after abort
This commit is contained in:
@@ -63,6 +63,7 @@ async function installCodingFirstChatHost(
|
||||
captured: CapturedRequest[];
|
||||
conversationCreated: boolean;
|
||||
interactionAnswered: boolean;
|
||||
abortRequested: boolean;
|
||||
releaseSnapshot: (() => void) | null;
|
||||
snapshotPending: boolean;
|
||||
};
|
||||
@@ -73,6 +74,7 @@ async function installCodingFirstChatHost(
|
||||
captured: [],
|
||||
conversationCreated: false,
|
||||
interactionAnswered: false,
|
||||
abortRequested: false,
|
||||
releaseSnapshot: null,
|
||||
snapshotPending: false,
|
||||
};
|
||||
@@ -470,9 +472,44 @@ async function installCodingFirstChatHost(
|
||||
state.snapshotPending = false;
|
||||
}
|
||||
return respond({
|
||||
snapshot: state.interactionAnswered
|
||||
? { ...snapshot, pendingInteractions: [] }
|
||||
: snapshot,
|
||||
snapshot: state.abortRequested
|
||||
? {
|
||||
...snapshot,
|
||||
nodes: snapshot.nodes.map((node) => {
|
||||
if (node.kind === 'message' && node.role === 'assistant') {
|
||||
return {
|
||||
...node,
|
||||
status: 'aborted',
|
||||
blocks: node.blocks.map((block) => ({ ...block, status: 'complete' })),
|
||||
};
|
||||
}
|
||||
if (node.kind === 'subagent') {
|
||||
return {
|
||||
...node,
|
||||
details: {
|
||||
...node.details,
|
||||
tasks: node.details.tasks.map((task) => (
|
||||
task.status === 'running' ? { ...task, status: 'aborted' } : task
|
||||
)),
|
||||
},
|
||||
};
|
||||
}
|
||||
return node;
|
||||
}),
|
||||
run: {
|
||||
status: 'idle',
|
||||
runId: 'run-e2e-feature',
|
||||
mode: 'prompt',
|
||||
settledAt: 12_000,
|
||||
terminalReason: 'aborted',
|
||||
},
|
||||
queue: { items: [] },
|
||||
pendingInteractions: [],
|
||||
cursor: { workerGeneration: 1, seq: 1 },
|
||||
}
|
||||
: state.interactionAnswered
|
||||
? { ...snapshot, pendingInteractions: [] }
|
||||
: snapshot,
|
||||
});
|
||||
}
|
||||
if (path === `/api/coding/conversations/${secondConversation.id}/snapshot`) {
|
||||
@@ -489,7 +526,11 @@ async function installCodingFirstChatHost(
|
||||
},
|
||||
}, 202);
|
||||
}
|
||||
if (/^\/api\/coding\/conversations\/[^/]+\/(abort|compact|recover)$/.test(path) && method === 'POST') {
|
||||
if (/^\/api\/coding\/conversations\/[^/]+\/abort$/.test(path) && method === 'POST') {
|
||||
state.abortRequested = true;
|
||||
return respond({});
|
||||
}
|
||||
if (/^\/api\/coding\/conversations\/[^/]+\/(compact|recover)$/.test(path) && method === 'POST') {
|
||||
return respond({});
|
||||
}
|
||||
if (/^\/api\/coding\/conversations\/[^/]+\/model$/.test(path) && method === 'POST') {
|
||||
@@ -795,6 +836,9 @@ test('PI feature UI isolates Conversations and exposes queue, interaction, model
|
||||
await expect(runtimeSettings).not.toHaveClass(/bg-surface-subtle\/75/);
|
||||
await expect(runtimeSettings).toBeDisabled();
|
||||
await page.getByRole('button', { name: '中止', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: '中止', exact: true })).toHaveCount(0);
|
||||
await expect(composer.getByRole('button', { name: '中止生成' })).toHaveCount(0);
|
||||
await expect(runtimeSettings).toBeEnabled();
|
||||
|
||||
const builderConversations = page.getByRole('group', { name: 'Builder 的对话' });
|
||||
await expect(builderConversations).toBeVisible();
|
||||
@@ -806,6 +850,7 @@ test('PI feature UI isolates Conversations and exposes queue, interaction, model
|
||||
await expect(page.getByText('本地编程运行时暂时不可用')).toHaveCount(0);
|
||||
await builderConversations.getByRole('button', { name: /^新对话/ }).click();
|
||||
await expect(page.getByText('Durable user fork source')).toBeVisible();
|
||||
await page.getByTestId('coding-process-group').locator('summary').first().click();
|
||||
await expect(page.getByText('Durable assistant response')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: '从这里创建新对话分支' })).toHaveCount(1);
|
||||
await builderConversations.getByRole('button', { name: 'Second Conversation' }).click();
|
||||
|
||||
Reference in New Issue
Block a user