feat(coding-runtime): add managed Pi extension host
This commit is contained in:
1 parent
47159b3cbd
commit
3861c3286a
22 files changed
+1673
-16
No files matched your search
@@ -0,0 +1,78 @@
|
||||
// @vitest-environment node
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { ConversationInteraction } from '../../electron/coding-runtime/contracts';
|
||||
import { PiInteractionStore } from '../../electron/coding-runtime/pi/interaction';
|
||||
import type { PiRpcCommand } from '../../electron/coding-runtime/pi/rpc-client';
|
||||
import type { PiGenerationResourceInput } from '../../electron/coding-runtime/pi/worker-pool';
|
||||
|
||||
describe('Pi interaction store', () => {
|
||||
it('responds by exact id and option, then rejects stale generation responses', async () => {
|
||||
const sent: PiRpcCommand[] = [];
|
||||
const changes: ConversationInteraction[] = [];
|
||||
const resources = new Map<string, () => void>();
|
||||
let generation = 1;
|
||||
let runId = 'run-1';
|
||||
const store = new PiInteractionStore({
|
||||
getState: () => ({
|
||||
conversationId: 'conversation-a', workerId: 'worker-a', state: 'running', generation,
|
||||
session: { piSessionId: 'session-a', sessionKey: 'key-a' },
|
||||
}),
|
||||
getActiveRun: () => ({ generation, runId }),
|
||||
send: async (_conversationId, command) => { sent.push(command); },
|
||||
trackGenerationResource: (input: PiGenerationResourceInput) => {
|
||||
resources.set(input.id, input.cancel);
|
||||
return () => resources.delete(input.id);
|
||||
},
|
||||
}, (interaction) => changes.push(interaction));
|
||||
|
||||
const opened = store.open('conversation-a', 1, 'run-1', {
|
||||
type: 'extension_ui_request', id: 'question-1', method: 'select', title: 'Choose',
|
||||
options: ['Alpha', 'Beta'],
|
||||
});
|
||||
expect(opened?.status).toBe('pending');
|
||||
await store.respond('conversation-a', {
|
||||
interactionId: 'question-1', optionId: 'question-1:option:1',
|
||||
});
|
||||
expect(sent).toEqual([{
|
||||
type: 'extension_ui_response', id: 'question-1', value: 'Beta',
|
||||
}]);
|
||||
expect(changes.at(-1)?.status).toBe('answered');
|
||||
await expect(store.respond('conversation-a', {
|
||||
interactionId: 'question-1', optionId: 'question-1:option:0',
|
||||
})).rejects.toThrow('not pending');
|
||||
|
||||
store.open('conversation-a', 1, 'run-1', {
|
||||
type: 'extension_ui_request', id: 'question-2', method: 'confirm', title: 'Continue?', message: 'Proceed',
|
||||
});
|
||||
generation = 2;
|
||||
runId = 'run-2';
|
||||
await expect(store.respond('conversation-a', {
|
||||
interactionId: 'question-2', confirmed: true,
|
||||
})).rejects.toThrow('stale');
|
||||
expect(changes.at(-1)?.status).toBe('cancelled');
|
||||
});
|
||||
|
||||
it('cancels every pending dialog on abort', async () => {
|
||||
const sent: PiRpcCommand[] = [];
|
||||
const changes: ConversationInteraction[] = [];
|
||||
const store = new PiInteractionStore({
|
||||
getState: () => ({
|
||||
conversationId: 'conversation-a', workerId: 'worker-a', state: 'running', generation: 1,
|
||||
session: { piSessionId: 'session-a', sessionKey: 'key-a' },
|
||||
}),
|
||||
getActiveRun: () => ({ generation: 1, runId: 'run-1' }),
|
||||
send: async (_conversationId, command) => { sent.push(command); },
|
||||
trackGenerationResource: () => () => undefined,
|
||||
}, (interaction) => changes.push(interaction));
|
||||
for (const id of ['question-1', 'question-2']) {
|
||||
store.open('conversation-a', 1, 'run-1', {
|
||||
type: 'extension_ui_request', id, method: 'input', title: id,
|
||||
});
|
||||
}
|
||||
await store.cancelRun('conversation-a', 'run-1', true);
|
||||
expect(sent).toHaveLength(2);
|
||||
expect(changes.map(({ status }) => status)).toEqual(['cancelled', 'cancelled']);
|
||||
expect(store.list()).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user