feat(coding): complete PI conversation UI

This commit is contained in:
2026-08-24 08:59:16 +08:00
parent 2613d6530b
commit 863f005203
21 changed files with 2040 additions and 137 deletions

View File

@@ -79,4 +79,62 @@ describe('coding Conversations Host facade', () => {
'events:/api/coding/events?conversationId=conversation%2Fa',
]);
});
it('routes every PI-130 Conversation control through the product Host surface', async () => {
const model = {
model: { accountId: 'account/1', modelId: 'model/1', thinkingLevel: 'medium' },
modelResolution: 'resolved',
};
const forked = { id: 'forked-1' };
const diagnostics = { revision: { provider: 1, resources: 2 }, workers: [] };
hostApi.fetch
.mockResolvedValueOnce({})
.mockResolvedValueOnce({ model })
.mockResolvedValueOnce({ model })
.mockResolvedValueOnce({})
.mockResolvedValueOnce({})
.mockResolvedValueOnce({ conversation: forked })
.mockResolvedValueOnce({ interactions: [{ id: 'interaction-1' }] })
.mockResolvedValueOnce({})
.mockResolvedValueOnce({ runtime: diagnostics });
const facade = await import('@/lib/coding-conversations');
await facade.abortCodingConversation('conversation/a');
await expect(facade.setCodingConversationModel('conversation/a', model.model)).resolves.toBe(model);
await expect(facade.setCodingConversationThinking('conversation/a', 'high')).resolves.toBe(model);
await facade.compactCodingConversation('conversation/a');
await facade.recoverCodingConversation('conversation/a');
await expect(facade.forkCodingConversation('conversation/a', 'entry/1')).resolves.toBe(forked);
await facade.listCodingConversationInteractions('conversation/a');
await facade.respondCodingConversationInteraction('conversation/a', {
interactionId: 'interaction/1',
optionId: 'option-1',
});
await expect(facade.getCodingRuntimeDiagnostics()).resolves.toBe(diagnostics);
expect(hostApi.fetch.mock.calls).toEqual([
['/api/coding/conversations/conversation%2Fa/abort', { method: 'POST', body: '{}' }],
['/api/coding/conversations/conversation%2Fa/model', {
method: 'POST',
body: JSON.stringify({ model: model.model }),
}],
['/api/coding/conversations/conversation%2Fa/thinking', {
method: 'POST',
body: JSON.stringify({ thinkingLevel: 'high' }),
}],
['/api/coding/conversations/conversation%2Fa/compact', { method: 'POST', body: '{}' }],
['/api/coding/conversations/conversation%2Fa/recover', { method: 'POST', body: '{}' }],
['/api/coding/conversations/conversation%2Fa/fork', {
method: 'POST',
body: JSON.stringify({ sourceEntryId: 'entry/1' }),
}],
['/api/coding/interactions?conversationId=conversation%2Fa'],
['/api/coding/interactions/interaction%2F1/respond', {
method: 'POST',
body: JSON.stringify({ conversationId: 'conversation/a', optionId: 'option-1' }),
}],
['/api/coding/runtime/diagnostics'],
]);
expect(JSON.stringify(hostApi.fetch.mock.calls)).not.toContain('/api/opencode');
});
});