// @vitest-environment node import { describe, expect, it } from 'vitest'; import type { PluginPolicyClientState } from '../../electron/services/plugin-policy-client'; import { CodingCapabilityRegistryImpl, } from '../../electron/coding-plugins/registry'; import type { CodingPluginAdapter } from '../../electron/coding-plugins/registry'; const policy: PluginPolicyClientState = { status: 'current', revision: 7, lastVerifiedAt: 1, catalog: { schema_version: 1, catalog_version: 'catalog-1', pricing_version: null, plugins: [{ plugin_id: 'makelore.data-service', supported_contract_versions: [1], status: 'active', capabilities: [ { capability_id: 'data-service.control', operations: ['configure', 'inspect', 'list_projects', 'remove_collection', 'reset', 'remove_project'] .map((operation) => ({ operation, billing: { mode: 'included' as const, entitlement_scope: null, notice: 'Included' }, })), }, { capability_id: 'data-service.documents', operations: ['get_document', 'list_documents', 'put_document', 'delete_document'] .map((operation) => ({ operation, billing: { mode: 'included' as const, entitlement_scope: null, notice: 'Included' }, })), }, { capability_id: 'data-service.preview', operations: ['get', 'list', 'put', 'delete'].map((operation) => ({ operation, billing: { mode: 'included' as const, entitlement_scope: null, notice: 'Included' }, })), }, ], }], }, }; const context = { conversationId: 'conversation-a', runId: 'run-a', resourceId: 'resource-a', projectId: 'local-project-a', projectPath: 'C:\\projects\\demo', skillIds: ['data-service'], }; function adapter(): CodingPluginAdapter { return { pluginId: 'makelore.data-service', async inspect() { return { status: 'ready' }; }, async invoke() { return { success: true, status: 200, code: null, error: null, retryable: false, payload_schema: 'data-service.v1', data: { id: 'one', data: {}, revision: 1 }, }; }, }; } function registry(overrides: Partial[0]> = {}) { return new CodingCapabilityRegistryImpl({ policyClient: { getState: () => policy }, getEnabledPluginIds: async () => ['makelore.data-service'], adapters: [adapter()], ...overrides, }); } describe('CodingCapabilityRegistry', () => { it('joins package, selection, grant, role, and server policy for parent resources', async () => { const resources = await registry().resolveWorkerResources({ projectPath: context.projectPath, assignedSkillIds: context.skillIds, role: 'parent', }); expect(resources.catalogRevision).toBe(7); expect(resources.pluginIds).toEqual(['makelore.data-service']); expect(resources.effectiveSkillIds).toEqual(['data-service']); expect(resources.tools.map(({ name }) => name)).toHaveLength(10); }); it('never exposes plugin resources to child workers or before the first catalog', async () => { const child = await registry().resolveWorkerResources({ projectPath: context.projectPath, assignedSkillIds: context.skillIds, role: 'child', }); expect(child.pluginIds).toEqual([]); expect(child.tools).toEqual([]); const unavailable = await registry({ policyClient: { getState: () => ({ ...policy, catalog: null, status: 'unavailable', revision: 0 }) }, }).resolveWorkerResources({ projectPath: context.projectPath, assignedSkillIds: context.skillIds, role: 'parent', }); expect(unavailable.tools).toEqual([]); }); it('revalidates invocation, derives the stable Pi request id, and preserves domain faults', async () => { const result = await registry().invoke({ toolName: 'data_service_get_document', context, workerRole: 'parent', effectiveSkillIds: context.skillIds, value: { collection: 'todos', document_id: 'one' }, }); expect(result.details).toMatchObject({ schema: 'makelore-capability.v1', plugin_id: 'makelore.data-service', capability_id: 'data-service.documents', operation: 'get_document', request_id: 'pi:run-a:resource-a', billing: { mode: 'included', status: 'included' }, payload_schema: 'data-service.v1', }); const invalid = await registry().invoke({ toolName: 'data_service_get_document', context, workerRole: 'parent', effectiveSkillIds: context.skillIds, value: { collection: 'todos', projectPath: 'forbidden' }, }); expect(invalid.details).toMatchObject({ success: false, code: 'plugin_input_invalid' }); const disabled = await registry({ getEnabledPluginIds: async () => [] }).invoke({ toolName: 'data_service_get_document', context, workerRole: 'parent', effectiveSkillIds: context.skillIds, value: { collection: 'todos', document_id: 'one' }, }); expect(disabled.details).toMatchObject({ success: false, code: 'plugin_not_enabled' }); const invalidIdentity = await registry().invoke({ toolName: 'data_service_get_document', context: { ...context, runId: '' }, workerRole: 'parent', effectiveSkillIds: context.skillIds, value: { collection: 'todos', document_id: 'one' }, }); expect(invalidIdentity.details).toMatchObject({ success: false, code: 'plugin_input_invalid', request_id: 'invalid-request-id', }); expect(JSON.stringify(invalidIdentity)).not.toMatch(/[0-9a-f]{8}-[0-9a-f]{4}/i); }); });