fix(pi): recover from Works prompt rejection

This commit is contained in:
2026-08-25 14:50:10 +08:00
parent 4580568384
commit 5f0ef761ea
9 changed files with 439 additions and 31 deletions

View File

@@ -465,11 +465,14 @@ describe('coding Conversation store', () => {
});
it('restores draft attachments and marks the optimistic node on definite rejection', async () => {
const submitPrompt = vi.fn(async () => {
throw new AppError('RUNTIME', 'Model unavailable', undefined, {
const submitPrompt = vi.fn()
.mockRejectedValueOnce(new AppError('RUNTIME', 'Model unavailable', undefined, {
backendCode: 'CODING_MODEL_UNAVAILABLE',
});
});
}))
.mockImplementationOnce(async (input) => acceptance(
input.conversationId,
input.clientRequestId,
));
const store = createCodingConversationStore({
getSnapshot: vi.fn(),
openEvents: vi.fn(),
@@ -477,11 +480,13 @@ describe('coding Conversation store', () => {
createId: ids(),
});
store.getState().applySnapshotEvent(snapshotEvent(snapshot('conversation-a')));
store.getState().applySnapshotEvent(snapshotEvent(snapshot('conversation-b')));
store.getState().setDraft('conversation-a', 'Try again', [{
attachmentId: 'attachment-1',
mime: 'image/png',
previewUrl: 'blob:preview',
}]);
store.getState().setDraft('conversation-b', 'Other conversation draft');
await expect(store.getState().submitPrompt({
conversationId: 'conversation-a',
@@ -494,8 +499,21 @@ describe('coding Conversation store', () => {
});
expect(store.getState().requestsByConversationId['conversation-a']['request-1'])
.toMatchObject({ status: 'rejected', errorCode: 'CODING_MODEL_UNAVAILABLE' });
expect(store.getState().entriesByConversationId['conversation-a'].error).toBeNull();
expect(store.getState().entriesByConversationId['conversation-b']).toMatchObject({
loadState: 'live',
error: null,
});
expect(store.getState().draftsByConversationId['conversation-b'].text)
.toBe('Other conversation draft');
expect(selectCodingConversationSnapshot('conversation-a')(store.getState())?.nodes[0])
.toMatchObject({ id: 'node-1', status: 'error' });
store.getState().setDraft('conversation-a', 'Retry now');
await store.getState().submitPrompt({ conversationId: 'conversation-a', mode: 'prompt' });
expect(submitPrompt).toHaveBeenCalledTimes(2);
expect(store.getState().requestsByConversationId['conversation-a']['request-2'])
.toMatchObject({ status: 'accepted' });
});
it('keeps attachment preparation failures out of runtime recovery and permits a direct retry', async () => {
@@ -574,6 +592,7 @@ describe('coding Conversation store', () => {
expect(store.getState().draftsByConversationId['conversation-a'].text).toBe('Do not replay');
expect(store.getState().requestsByConversationId['conversation-a']['request-1'])
.toMatchObject({ status: 'uncertain', errorCode: 'CODING_REQUEST_UNCERTAIN' });
expect(store.getState().entriesByConversationId['conversation-a'].error).toBeNull();
expect(selectCodingConversationSnapshot('conversation-a')(store.getState())?.nodes[0])
.toMatchObject({ id: 'node-1', status: 'optimistic' });
expect(submitPrompt).toHaveBeenCalledTimes(1);