// @vitest-environment node import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { createCodingConversationStore } from '../../electron/coding-projects/conversation-store'; import { createCodingProjectAgent } from '../../electron/coding-projects/project-config'; import { createCodingProjectStore, createLocalCodingProject, createMemoryCodingProjectStorage, } from '../../electron/coding-projects/project-store'; import { PiSessionRegistry } from '../../electron/coding-runtime/pi/session-registry'; const roots: string[] = []; const NOW = '2026-08-22T14:00:00.000Z'; afterEach(async () => { await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))); }); describe('Pi session registry', () => { it('persists one binding and target-only model state across registry reopen', async () => { const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-pi-registry-')); roots.push(projectPath); const projectStore = createCodingProjectStore(createMemoryCodingProjectStorage(), { createId: () => 'project-a', now: () => NOW, }); const { project } = await createLocalCodingProject({ projectPath, now: NOW }, projectStore); await createCodingProjectAgent(projectPath, { id: 'agent-a', avatarId: 'avatar-01', roleName: 'Implementer', name: 'Agent A', model: { accountId: 'account-a', modelId: 'model-a', thinkingLevel: 'medium' }, modelResolution: 'resolved', responsibility: { mission: 'Implement', owns: [], boundaries: [], collaborators: [], principles: [] }, prompt: 'Managed prompt', skillIds: ['tdd'], }, { now: NOW }); const conversations = createCodingConversationStore(projectPath, { createId: () => 'f47ac10b-58cc-4372-a567-0e02b2c3d479', now: () => NOW, }); const created = await conversations.create({ agentId: 'agent-a', title: 'Conversation A', model: { accountId: 'account-a', modelId: 'model-a', thinkingLevel: 'medium' }, modelResolution: 'resolved', }); const input = { conversationId: created.id, projectId: 'project-a', agentId: 'agent-a', title: created.title, model: { model: created.model, modelResolution: created.modelResolution }, } as const; const registry = new PiSessionRegistry({ projectStore }); const createBinding = vi.fn(async () => ({ piSessionId: 'pi-session-a', sessionKey: 'session-key-a', })); const [first, duplicate] = await Promise.all([ registry.ensureBinding(input, createBinding), registry.ensureBinding(input, createBinding), ]); expect(createBinding).toHaveBeenCalledTimes(1); expect(first.session).toEqual({ piSessionId: 'pi-session-a', sessionKey: 'session-key-a' }); expect(duplicate).toEqual(first); await registry.setModel(created.id, { model: { accountId: 'account-b', modelId: 'model-b', thinkingLevel: 'high' }, modelResolution: 'resolved', }); const reopened = await new PiSessionRegistry({ projectStore }).prepare({ ...input, model: { model: { thinkingLevel: 'high', modelId: 'model-b', accountId: 'account-b' }, modelResolution: 'resolved', }, }); expect(reopened).toMatchObject({ projectPath: project.path, agent: { id: 'agent-a', prompt: 'Managed prompt', skillIds: ['tdd'] }, conversation: { id: created.id, model: { accountId: 'account-b', modelId: 'model-b', thinkingLevel: 'high' }, }, session: { piSessionId: 'pi-session-a', sessionKey: 'session-key-a' }, }); }); });