import type { ConfigRegistry } from '../config/registry.js'; import type { MCPToolDiscoveryOptions } from '../connector/mcp/discovery.js'; import type { MCPToolPolicy } from '../connector/mcp/policy.js'; import type { PluginRegistry } from '../registry/plugin/index.js'; import type { SkillRegistry } from '../skills/registry.js'; import type { PluginId } from '../types/ids/index.js'; import type { PluginDefinition, PluginEventListener, PluginHookContext, PluginHookDefinition, PluginHookEvent, PluginHookResult, PluginScope } from '../types/plugin/index.js'; import type { SessionEvent } from '../types/session/index.js'; import type { ToolRegistryContract } from '../types/tool/index.js'; import type { Logger } from '../utils/logger.js'; export interface PluginLifecycleManagerConfig { pluginRegistry: PluginRegistry; toolRegistry: ToolRegistryContract; /** * Filesystem authorities for each plugin scope. * * Required because `scope` is not an authority by itself. The manager * canonicalizes a plugin against the matching root before reading its * manifest and repeats that admission for legacy registry records before * enabling them. A project manager normally uses the trusted working * directory; a user manager normally uses the user's home directory. */ scopeRoots: Readonly>; /** * Where a plugin's declared skills land. * * Optional, and its absence is enforced rather than tolerated: a * manifest that declares skills is REFUSED when this is missing, the * same way it was refused before the manifest path existed. Accepting it * and dropping the skills would produce a plugin reporting `enabled` * that contributes nothing its author declared. */ skillRegistry?: SkillRegistry; log: Logger; hookTimeoutMs?: number; /** * Where each MCP server's reconnect policy is registered, so an operator * can retune it while a turn is live. * * Optional, and its absence is not a degraded mode: without one the * supervisor uses its own defaults, which is what it did before this * existed. What a registry buys is the ability to change them without * restarting the process — see `config/registry.ts`. */ configRegistry?: ConfigRegistry; /** * What each MCP server a plugin brings is allowed to contribute, keyed by * server name. `'*'` covers every server not named explicitly. * * Absent admits everything, which is what this path did unconditionally * until now. `MCPToolDiscovery` has held this boundary — and the drift * detection below — since it was written, and nothing outside its own * tests ever constructed one: `attachMCPServer` called `listTools()` * directly and registered whatever came back. So the least-privilege * check existed, was tested, was exported, and was not on the path any * real MCP server takes. */ mcpToolPolicies?: Readonly>; /** * Called when a server's admitted tool set differs from the last time it * was discovered. * * Reported rather than blocked, for the reason `MCPToolDiscovery` * already gives: a development server legitimately changes between sessions, * while a production one changing mid-session is the rug pull — advertise * something benign at approval time, swap it afterwards. Only the host * knows which it is looking at. */ onMCPToolDrift?: MCPToolDiscoveryOptions['onDrift']; } export declare class PluginLifecycleManager { private pluginRegistry; private toolRegistry; private listeners; private hookHandlers; private pluginContributions; /** Immutable executable admissions owned by this manager, never by the mutable registry. */ private pluginAdmissions; private hookTimeoutMs; private readonly configRegistry; private readonly scopeRoots; private log; /** * The admission boundary for everything a plugin's MCP servers advertise. * * One instance for the whole manager rather than one per plugin, and that * is the part that matters: the instance is what remembers each server's * previously admitted tool set, so a server that changes between one * plugin being disabled and the next being enabled is still noticed. A * per-plugin instance would forget on every teardown, which is precisely * the window a rug pull uses. * * Its client list is deliberately left empty — `discoverFrom` takes the * client directly, so registering it here would be bookkeeping nothing * reads. */ private mcpDiscovery; private readonly skillRegistry; constructor(config: PluginLifecycleManagerConfig); /** * Attach a hook without installing a plugin from disk. * * Registration was reachable only through `enable()`, which loads a * manifest and imports modules by path — so a host that wanted one * in-process guard had to lay out a plugin directory to get it. That * also left this class's own tests reaching into the private map, * which is how they came to construct entries the real path would * never produce. * * Hooks are held in priority order (lower first), ties keeping * registration order. */ registerHook(pluginId: PluginId, hook: PluginHookDefinition): void; on(listener: PluginEventListener): void; off(listener: PluginEventListener): void; install(pluginDir: string, scope: PluginScope): Promise; private definitionFrom; private readLegacyAdmission; enable(pluginId: PluginId): Promise; private attachMCPServer; private rollbackContributions; disable(pluginId: PluginId): Promise; uninstall(pluginId: PluginId): Promise; executeHooks(event: PluginHookEvent, context: Omit, emitSessionEvent?: (event: SessionEvent) => Promise): Promise; private emit; } //# sourceMappingURL=lifecycle.d.ts.map