// @vitest-environment node import { describe, expect, it, vi } from 'vitest'; import { createEffectivePluginResolver, type EffectivePluginResolver, } from '../../electron/coding-plugins/effective-resolver'; import { DATA_SERVICE_PLUGIN_DEFINITION, GAME_RESOURCE_BUNDLED_RELEASE_ID, GAME_RESOURCE_PLUGIN_ID, PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID, PROJECT_SCAFFOLD_PLUGIN_ID, type CodingPluginDefinition, } from '../../shared/coding-plugins'; const skillOnlyDefinition: CodingPluginDefinition = { id: 'makelore.notes', version: '1.0.0', contractVersion: 1, displayName: 'Notes', description: 'Notes skill', runtimeKind: 'skill_only', acquisitionMode: 'user_acquired', releaseId: 'notes-1', provenance: { source: 'marketplace', packageRoot: 'C:/packages/notes-1' }, scope: 'project', adapterId: '', requiresBackend: false, skills: [{ id: 'notes', entryPath: 'skills/notes/SKILL.md', grants: [] }], tools: [], operations: [], surfaces: {}, }; const serverDefinition: CodingPluginDefinition = { id: 'makelore.remote', version: '1.0.0', contractVersion: 1, displayName: 'Remote', description: 'Remote capability', runtimeKind: 'platform_hosted', acquisitionMode: 'user_acquired', releaseId: 'remote-1', provenance: { source: 'marketplace', packageRoot: 'C:/packages/remote-1' }, scope: 'project', adapterId: '', requiresBackend: true, skills: [{ id: 'remote', entryPath: 'skills/remote/SKILL.md', grants: ['remote.read'] }], tools: [{ name: 'remote_read', label: 'Remote read', description: 'Read remotely', capabilityId: 'remote.read', operation: 'read', roles: ['parent'], mutation: 'read', projectWriteLease: false, permissions: ['remote.read'], inputSchema: { type: 'object', additionalProperties: false, properties: {} }, }], operations: [{ capabilityId: 'remote.read', operation: 'read', toolName: 'remote_read' }], surfaces: {}, }; const projectScaffoldDefinition: CodingPluginDefinition = { ...skillOnlyDefinition, id: PROJECT_SCAFFOLD_PLUGIN_ID, displayName: 'Project Scaffold', description: 'Project-scoped scaffold Skill', releaseId: PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID, provenance: { source: 'bundled', packageRoot: 'project-scaffold' }, skills: [{ id: 'makelore-project-scaffold', entryPath: 'skills/makelore-project-scaffold/SKILL.md', grants: [], }], }; const binding = { accountKey: 'account-a', epoch: 4 }; function library(runtimeStatus: 'enabled' | 'suspended' = 'enabled', catalogStatus: 'active' | 'retired' = 'active') { return { items: [{ pluginId: 'makelore.notes', title: 'Notes', summary: 'Notes', category: 'productivity', acquisition: 'free' as const, acquisitionMode: 'user_acquired' as const, catalogStatus, runtimeStatus, acquiredAt: null, removedAt: null, stableVersion: '1.0.0', betaVersion: null, }], total: 1, stale: false, fetchedAt: 1, }; } function resolver(overrides: Partial[0]> = {}) { const effective = createEffectivePluginResolver({ definitions: [skillOnlyDefinition], getAccountBinding: () => binding, getLibrary: vi.fn(async () => library()), getInstalled: vi.fn(async () => ({ pluginId: skillOnlyDefinition.id, releaseId: skillOnlyDefinition.releaseId!, version: skillOnlyDefinition.version, packageSchemaVersion: 2, contractVersion: skillOnlyDefinition.contractVersion, runtimeKind: 'skill_only' as const, sha256: 'a'.repeat(64), sizeBytes: 1, installedAt: '2026-08-28T00:00:00.000Z', packageRoot: skillOnlyDefinition.provenance.packageRoot, definition: skillOnlyDefinition, })), getEnabledPluginIds: vi.fn(async () => [skillOnlyDefinition.id]), ...overrides, }); return effective; } function installed(definition: CodingPluginDefinition) { return { pluginId: definition.id, releaseId: definition.releaseId!, version: definition.version, packageSchemaVersion: 2, contractVersion: definition.contractVersion, runtimeKind: definition.runtimeKind, sha256: 'b'.repeat(64), sizeBytes: 1, installedAt: '2026-08-28T00:00:00.000Z', packageRoot: definition.provenance.packageRoot, definition, }; } function currentPolicy() { return { status: 'current' as const, revision: 8, lastVerifiedAt: 1, catalog: { schema_version: 1 as const, catalog_version: 'catalog-a', pricing_version: null, plugins: [{ plugin_id: serverDefinition.id, supported_contract_versions: [1], status: 'active' as const, capabilities: [{ capability_id: 'remote.read', operations: [{ operation: 'read', billing: { mode: 'included' as const, entitlement_scope: null, notice: 'Included' }, }], }], }], }, }; } async function resolve(effective: EffectivePluginResolver, assignedSkillIds = ['notes']) { return await effective.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds, role: 'parent', }); } describe('effective plugin resolver', () => { it('accepts former local Skill IDs and rejects collisions between Marketplace packages', async () => { const coreCollision: CodingPluginDefinition = { ...skillOnlyDefinition, id: 'makelore.core-collision', releaseId: 'core-collision-1', provenance: { source: 'marketplace', packageRoot: 'C:/packages/core-collision-1' }, skills: [{ id: 'agent-browser', entryPath: 'skills/agent-browser/SKILL.md', grants: [] }], }; const packageA: CodingPluginDefinition = { ...skillOnlyDefinition, id: 'makelore.collision-a', releaseId: 'collision-a-1', provenance: { source: 'marketplace', packageRoot: 'C:/packages/collision-a-1' }, skills: [{ id: 'shared-skill', entryPath: 'skills/shared/SKILL.md', grants: [] }], }; const packageB: CodingPluginDefinition = { ...skillOnlyDefinition, id: 'makelore.collision-b', releaseId: 'collision-b-1', provenance: { source: 'marketplace', packageRoot: 'C:/packages/collision-b-1' }, skills: [{ id: 'shared-skill', entryPath: 'skills/shared/SKILL.md', grants: [] }], }; const definitions = [coreCollision, packageA, packageB]; const result = await createEffectivePluginResolver({ definitions, getAccountBinding: () => binding, getLibrary: vi.fn(async () => ({ items: definitions.map((definition) => ({ pluginId: definition.id, title: definition.displayName, summary: definition.description, category: 'tools', acquisition: 'free' as const, acquisitionMode: 'user_acquired' as const, catalogStatus: 'active' as const, runtimeStatus: 'enabled' as const, acquiredAt: null, removedAt: null, stableVersion: definition.version, betaVersion: null, })), total: definitions.length, stale: false, fetchedAt: 1, })), getInstalled: vi.fn(async (pluginId: string) => { const definition = definitions.find((candidate) => candidate.id === pluginId); return definition ? installed(definition) : null; }), getEnabledPluginIds: vi.fn(async () => definitions.map(({ id }) => id)), }).resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['agent-browser', 'shared-skill'], role: 'parent', }); expect(result.effectiveSkillIds).toEqual(['agent-browser', 'shared-skill']); expect(result.skillEntries).toEqual([ { id: 'agent-browser', entryPath: 'skills/agent-browser/SKILL.md', packageRoot: 'C:/packages/core-collision-1', }, { id: 'shared-skill', entryPath: 'skills/shared/SKILL.md', packageRoot: 'C:/packages/collision-a-1' }, ]); expect(result.pluginReleaseIds).toEqual(['core-collision-1', 'collision-a-1']); expect(result.unavailableReasons).toEqual(expect.arrayContaining([ expect.objectContaining({ pluginId: packageB.id, code: 'skill_owner_conflict' }), ])); expect(result.unavailableReasons).not.toEqual(expect.arrayContaining([ expect.objectContaining({ pluginId: coreCollision.id, code: 'skill_owner_conflict' }), ])); }); it('reserves every bundled Skill owner, including Data Service, before accepting Marketplace packages', async () => { const marketplaceDataServiceCollision: CodingPluginDefinition = { ...skillOnlyDefinition, id: 'makelore.data-service-shadow', releaseId: 'data-service-shadow-1', provenance: { source: 'marketplace', packageRoot: 'C:/packages/data-service-shadow-1' }, skills: [{ id: 'data-service', entryPath: 'skills/data-service/SKILL.md', grants: [] }], }; const result = await createEffectivePluginResolver({ definitions: [DATA_SERVICE_PLUGIN_DEFINITION, marketplaceDataServiceCollision], getAccountBinding: () => binding, getLibrary: vi.fn(async () => ({ ...library(), items: [{ ...library().items[0], pluginId: marketplaceDataServiceCollision.id }], })), getInstalled: vi.fn(async (pluginId: string) => pluginId === marketplaceDataServiceCollision.id ? installed(marketplaceDataServiceCollision) : null), getEnabledPluginIds: vi.fn(async () => [ DATA_SERVICE_PLUGIN_DEFINITION.id, marketplaceDataServiceCollision.id, ]), }).resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['data-service'], role: 'parent', }); expect(result.pluginReleaseIds).toEqual([]); expect(result.unavailableReasons).toContainEqual(expect.objectContaining({ pluginId: marketplaceDataServiceCollision.id, code: 'skill_owner_conflict', })); }); it('requires current Library, installed Release, project selection, and assignment', async () => { const effective = resolver(); await expect(resolve(effective)).resolves.toMatchObject({ accountSessionId: 'account-a\u00004', projectId: 'project-a', pluginReleaseIds: ['notes-1'], effectiveSkillIds: ['notes'], skillEntries: [{ id: 'notes', entryPath: 'skills/notes/SKILL.md' }], toolDefinitions: [], runtimePolicies: [], unavailableReasons: [], }); await expect(resolve(effective, [])).resolves.toMatchObject({ effectiveSkillIds: [], skillEntries: [], unavailableReasons: [expect.objectContaining({ pluginId: 'makelore.notes', code: 'skill_unassigned' })], }); await expect(resolve(effective, ['notes', 'removed-plugin-skill'])).resolves.toMatchObject({ effectiveSkillIds: ['notes'], unavailableReasons: [], }); await expect(resolve(resolver({ getEnabledPluginIds: vi.fn(async () => []) }))).resolves.toMatchObject({ effectiveSkillIds: [], unavailableReasons: [expect.objectContaining({ pluginId: 'makelore.notes', code: 'project_disabled' })], }); }); it('keeps retired Library plugins usable but excludes suspended runtime plugins', async () => { await expect(resolve(resolver({ getLibrary: vi.fn(async () => library('enabled', 'retired')) }))) .resolves.toMatchObject({ effectiveSkillIds: ['notes'], pluginReleaseIds: ['notes-1'] }); await expect(resolve(resolver({ getLibrary: vi.fn(async () => library('suspended')) }))) .resolves.toMatchObject({ effectiveSkillIds: [], pluginReleaseIds: [], unavailableReasons: [expect.objectContaining({ pluginId: 'makelore.notes', code: 'runtime_suspended' })], }); }); it('keeps installed skill-only local under a trusted stale Library while hosted stays fail-closed', async () => { const staleNotesLibrary = { ...library(), stale: true }; await expect(resolve(resolver({ getLibrary: vi.fn(async () => staleNotesLibrary) }))) .resolves.toMatchObject({ effectiveSkillIds: ['notes'], pluginReleaseIds: ['notes-1'], unavailableReasons: [], }); const staleHostedLibrary = { ...library(), stale: true, items: [{ ...library().items[0], pluginId: serverDefinition.id }], }; const hosted = createEffectivePluginResolver({ definitions: [serverDefinition], getAccountBinding: () => binding, getLibrary: vi.fn(async () => staleHostedLibrary), getInstalled: vi.fn(async () => installed(serverDefinition)), getEnabledPluginIds: vi.fn(async () => [serverDefinition.id]), policyClient: { getState: currentPolicy, refresh: vi.fn() }, }); await expect(hosted.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['remote'], role: 'parent', })).resolves.toMatchObject({ effectiveSkillIds: [], pluginReleaseIds: [], unavailableReasons: [expect.objectContaining({ pluginId: serverDefinition.id, code: 'library_unavailable', })], }); }); it('keeps a frozen hosted worker usable through a transient stale Library refresh', async () => { const currentHostedLibrary = { ...library(), items: [{ ...library().items[0], pluginId: serverDefinition.id }], }; const getLibrary = vi.fn() .mockResolvedValueOnce(currentHostedLibrary) .mockResolvedValue({ ...currentHostedLibrary, stale: true }); const hosted = createEffectivePluginResolver({ definitions: [serverDefinition], getAccountBinding: () => binding, getLibrary, getInstalled: vi.fn(async () => installed(serverDefinition)), getEnabledPluginIds: vi.fn(async () => [serverDefinition.id]), policyClient: { getState: currentPolicy, refresh: vi.fn() }, }); const input = { projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['remote'], role: 'parent' as const, }; const frozen = await hosted.resolve(input); expect(frozen.toolDefinitions).toEqual([expect.objectContaining({ name: 'remote_read' })]); await expect(hosted.resolveForInvocation(input)).resolves.toMatchObject({ accountSessionId: frozen.accountSessionId, pluginReleaseIds: frozen.pluginReleaseIds, toolDefinitions: [{ name: 'remote_read' }], runtimePolicies: [{ pluginId: serverDefinition.id, operation: 'read' }], unavailableReasons: [], }); await expect(hosted.resolve(input)).resolves.toMatchObject({ toolDefinitions: [], runtimePolicies: [], unavailableReasons: [expect.objectContaining({ code: 'library_unavailable' })], }); }); it('never exposes plugin resources to a child worker', async () => { const result = await resolver().resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['notes'], role: 'child', }); expect(result.pluginReleaseIds).toEqual([]); expect(result.effectiveSkillIds).toEqual([]); expect(result.skillEntries).toEqual([]); expect(result.toolDefinitions).toEqual([]); }); it('keeps skill-only local while requiring current server policy for tools', async () => { const refresh = vi.fn(); const effective = createEffectivePluginResolver({ definitions: [serverDefinition], getAccountBinding: () => binding, getLibrary: vi.fn(async () => ({ ...library(), items: [{ ...library().items[0], pluginId: serverDefinition.id }], })), getInstalled: vi.fn(async () => installed(serverDefinition)), getEnabledPluginIds: vi.fn(async () => [serverDefinition.id]), policyClient: { getState: currentPolicy, refresh }, }); await expect(effective.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['remote'], role: 'parent', })).resolves.toMatchObject({ pluginReleaseIds: ['remote-1'], effectiveSkillIds: ['remote'], skillEntries: [{ id: 'remote', entryPath: 'skills/remote/SKILL.md' }], toolDefinitions: [{ name: 'remote_read' }], runtimePolicies: [{ pluginId: serverDefinition.id, contractVersion: 1, capabilityId: 'remote.read', operation: 'read', }], unavailableReasons: [], }); expect(refresh).not.toHaveBeenCalled(); let policy = { ...currentPolicy(), status: 'stale' as const }; const stalePolicyResolver = createEffectivePluginResolver({ definitions: [serverDefinition], getAccountBinding: () => binding, getLibrary: vi.fn(async () => ({ ...library(), items: [{ ...library().items[0], pluginId: serverDefinition.id }], })), getInstalled: vi.fn(async () => installed(serverDefinition)), getEnabledPluginIds: vi.fn(async () => [serverDefinition.id]), policyClient: { getState: () => policy, refresh: vi.fn(async () => undefined), }, }); await expect(stalePolicyResolver.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['remote'], role: 'parent', })).resolves.toMatchObject({ effectiveSkillIds: [], toolDefinitions: [], runtimePolicies: [], unavailableReasons: [expect.objectContaining({ code: 'policy_unavailable' })], }); const local = createEffectivePluginResolver({ definitions: [skillOnlyDefinition], getAccountBinding: () => binding, getLibrary: vi.fn(async () => library()), getInstalled: vi.fn(async () => installed(skillOnlyDefinition)), getEnabledPluginIds: vi.fn(async () => [skillOnlyDefinition.id]), policyClient: { getState: () => ({ ...currentPolicy(), catalog: null }), refresh }, }); await expect(local.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: ['notes'], role: 'parent', })).resolves.toMatchObject({ effectiveSkillIds: ['notes'], toolDefinitions: [], runtimePolicies: [] }); expect(refresh).not.toHaveBeenCalled(); }); it('materializes an acquired code-owned bundled hosted Plugin without Package Store bytes or Agent assignment', async () => { const bundled: CodingPluginDefinition = { ...serverDefinition, id: GAME_RESOURCE_PLUGIN_ID, releaseId: GAME_RESOURCE_BUNDLED_RELEASE_ID, provenance: { source: 'bundled', packageRoot: 'game-resource' }, skills: [{ id: 'game-resource', entryPath: 'skills/game-resource/SKILL.md', grants: ['remote.read'], }], }; const getInstalled = vi.fn(async () => null); const getInstalledRelease = vi.fn(async () => null); const policy = currentPolicy(); policy.catalog.plugins[0] = { ...policy.catalog.plugins[0], plugin_id: bundled.id }; const effective = createEffectivePluginResolver({ definitions: [bundled], getAccountBinding: () => binding, getLibrary: vi.fn(async () => ({ ...library(), items: [{ ...library().items[0], pluginId: bundled.id }], })), packageStore: { readInstalledIndex: vi.fn(async () => []), getInstalled, getInstalledRelease, }, getEnabledPluginIds: vi.fn(async () => [bundled.id]), policyClient: { getState: () => policy, refresh: vi.fn() }, }); await expect(effective.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: [], role: 'parent', })).resolves.toMatchObject({ pluginReleaseIds: [bundled.releaseId], effectiveSkillIds: ['game-resource'], skillEntries: [{ id: 'game-resource', entryPath: 'skills/game-resource/SKILL.md', }], toolDefinitions: [{ name: 'remote_read' }], unavailableReasons: [], }); expect(getInstalled).not.toHaveBeenCalled(); await expect(effective.getSkillSources()).resolves.toEqual([]); await expect(effective.getInstalledDefinition(bundled.id, bundled.releaseId)) .resolves.toMatchObject({ id: bundled.id, releaseId: bundled.releaseId }); expect(getInstalledRelease).not.toHaveBeenCalled(); }); it('materializes the system-included Data Service after project enablement without Agent assignment', async () => { const policy = currentPolicy(); policy.catalog.plugins[0] = { ...policy.catalog.plugins[0], plugin_id: DATA_SERVICE_PLUGIN_DEFINITION.id, capabilities: DATA_SERVICE_PLUGIN_DEFINITION.operations.map(({ capabilityId, operation }) => ({ capability_id: capabilityId, operations: [{ operation, billing: { mode: 'included' as const, entitlement_scope: null, notice: 'Included' }, }], })), }; const effective = createEffectivePluginResolver({ definitions: [DATA_SERVICE_PLUGIN_DEFINITION], getAccountBinding: () => binding, getEnabledPluginIds: vi.fn(async () => [DATA_SERVICE_PLUGIN_DEFINITION.id]), policyClient: { getState: () => policy, refresh: vi.fn() }, }); await expect(effective.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: [], role: 'parent', })).resolves.toMatchObject({ effectiveSkillIds: ['data-service'], skillEntries: [{ id: 'data-service', entryPath: 'skills/data-service/SKILL.md' }], unavailableReasons: [], }); }); it('materializes the project-wide Scaffold Skill after project enablement without Agent assignment', async () => { const scaffoldLibrary = { ...library(), items: [{ ...library().items[0], pluginId: PROJECT_SCAFFOLD_PLUGIN_ID, title: projectScaffoldDefinition.displayName, }], }; const effective = createEffectivePluginResolver({ definitions: [projectScaffoldDefinition], getAccountBinding: () => binding, getLibrary: vi.fn(async () => scaffoldLibrary), getEnabledPluginIds: vi.fn(async () => [PROJECT_SCAFFOLD_PLUGIN_ID]), }); await expect(effective.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: [], role: 'parent', })).resolves.toMatchObject({ pluginReleaseIds: [PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID], effectiveSkillIds: ['makelore-project-scaffold'], skillEntries: [{ id: 'makelore-project-scaffold', entryPath: 'skills/makelore-project-scaffold/SKILL.md', }], unavailableReasons: [], }); await expect(createEffectivePluginResolver({ definitions: [projectScaffoldDefinition], getAccountBinding: () => binding, getLibrary: vi.fn(async () => scaffoldLibrary), getEnabledPluginIds: vi.fn(async () => []), }).resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: [], role: 'parent', })).resolves.toMatchObject({ effectiveSkillIds: [], unavailableReasons: [expect.objectContaining({ pluginId: PROJECT_SCAFFOLD_PLUGIN_ID, code: 'project_disabled', })], }); await expect(effective.resolve({ projectId: 'project-a', projectPath: 'C:/project-a', assignedSkillIds: [], role: 'child', })).resolves.toMatchObject({ pluginReleaseIds: [], effectiveSkillIds: [], skillEntries: [], }); }); });