fix: close PI core chat review gaps

This commit is contained in:
2026-08-24 01:28:06 +08:00
parent 612135f911
commit bec93d0918
15 changed files with 1094 additions and 122 deletions

View File

@@ -705,6 +705,74 @@ describe('coding Conversation store', () => {
});
});
it('refreshes a target Snapshot once more when the first recovery response is still behind', async () => {
const firstRecovery = deferred<ConversationSnapshot>();
const secondRecovery = deferred<ConversationSnapshot>();
const getSnapshot = vi.fn()
.mockImplementationOnce(() => firstRecovery.promise)
.mockImplementationOnce(() => secondRecovery.promise);
const store = createCodingConversationStore({
getSnapshot,
openEvents: vi.fn(),
submitPrompt: vi.fn(),
createId: ids(),
});
store.getState().applySnapshotEvent(snapshotEvent(snapshot('conversation-a')));
store.getState().applyPatchBatchEvent(patchBatch('conversation-a', 3, [
{ op: 'run.state', run: { status: 'running', runId: 'run-a' } },
{
op: 'context.replace',
context: { usedTokens: 20, contextWindow: 100, compaction: 'idle' },
},
]));
firstRecovery.resolve(snapshot('conversation-a', 1, 1));
await waitFor(() => expect(getSnapshot).toHaveBeenCalledTimes(2));
expect(store.getState().entriesByConversationId['conversation-a'].loadState).toBe('recovering');
secondRecovery.resolve(snapshot('conversation-a', 1, 2));
await waitFor(() => {
expect(selectCodingConversationSnapshot('conversation-a')(store.getState()))
.toMatchObject({
cursor: { workerGeneration: 1, seq: 4 },
context: { usedTokens: 20, contextWindow: 100 },
run: { status: 'running', runId: 'run-a' },
});
});
expect(store.getState().entriesByConversationId['conversation-a'].loadState).toBe('live');
});
it('returns a repeatedly stale recovery Snapshot to a retryable error state', async () => {
const firstRecovery = deferred<ConversationSnapshot>();
const secondRecovery = deferred<ConversationSnapshot>();
const getSnapshot = vi.fn()
.mockImplementationOnce(() => firstRecovery.promise)
.mockImplementationOnce(() => secondRecovery.promise);
const store = createCodingConversationStore({
getSnapshot,
openEvents: vi.fn(),
submitPrompt: vi.fn(),
createId: ids(),
});
store.getState().applySnapshotEvent(snapshotEvent(snapshot('conversation-a')));
store.getState().applyPatchBatchEvent(patchEvent('conversation-a', 3, {
op: 'run.state',
run: { status: 'running', runId: 'run-a' },
}));
firstRecovery.resolve(snapshot('conversation-a', 1, 1));
await waitFor(() => expect(getSnapshot).toHaveBeenCalledTimes(2));
secondRecovery.resolve(snapshot('conversation-a', 1, 1));
await waitFor(() => {
expect(store.getState().entriesByConversationId['conversation-a']).toMatchObject({
loadState: 'error',
error: 'Conversation patch batch is not continuous',
});
});
expect(getSnapshot).toHaveBeenCalledTimes(2);
});
it('does not rerender a selected timeline when a hidden Conversation streams', () => {
const store = createCodingConversationStore({
getSnapshot: vi.fn(),