PluginsManager
Discovers, gates, orders, and boots the plugins that make up a running Milpa application. The direct port of the host's legacy manager with every host coupling replaced: globals became {@see ManagerConfig}, the enabled set reads through {@see PluginRegistryInterface}, the tool registry is the core contract, and the plugin list is instance state.
PluginsManager::__construct()
public function __construct(Milpa\Interfaces\Di\DIContainerInterface $container, Milpa\Plugin\Contracts\PluginRegistryInterface $registry, Milpa\Plugin\Runtime\ManagerConfig $config):Parameters
| Name | Type | Description |
|---|---|---|
| $container | Milpa\Interfaces\Di\DIContainerInterface | |
| $registry | Milpa\Plugin\Contracts\PluginRegistryInterface | |
| $config | Milpa\Plugin\Runtime\ManagerConfig |
PluginsManager::getPluginsMetadata()
public function getPluginsMetadata(): arrayPlugin metadata for every scanned plugin, resequenced to boot order.
PluginsManager::blockingReasonWithout()
public function blockingReasonWithout(string $pluginName): ?stringEl motivo por el que apagar `$pluginName` dejaría el grafo bloqueado, o `null` si no lo haría. Resuelve el MISMO grafo que el arranque, con ese plugin fuera. No es una aproximación: usa el perfil del host y el resolver que van a decidir de verdad, así que una respuesta afirmativa aquí es la misma que se daría en el siguiente arranque. Sin perfil legible contesta `null`, o sea «adelante»: es exactamente lo que hace el resto de esta clase cuando no lo encuentra —{@see self::cachedGraphIsBootable()} lo dice con todas sus letras— porque inventar un perfil bloquearía apagados que hoy funcionan. Un plugin que no está entre los activos tampoco bloquea nada: apagar lo que ya está apagado no cambia el grafo.
Parameters
| Name | Type | Description |
|---|---|---|
| $pluginName | string |
PluginsManager::blockingReasonWith()
public function blockingReasonWith(string $newPluginClass): ?stringEl motivo por el que AGREGAR `$newPluginClass` dejaría el grafo bloqueado, o `null` si cerraría. La otra mitad de {@see self::blockingReasonWithout()}: el invariante es uno —el grafo nunca se deja abierto por una mutación (greenhouse decisions/0178). Un plugin sin `#[PluginMetadata]` no declara `requires`: agregarlo no puede abrir el grafo, así que no hay nada que bloquear. Si el resolver mismo TRUENA, se falla CERRADO al agregar (a diferencia de quitar, que tiene vía de recuperación): un registro que podría abrir el grafo y no se pudo verificar no se autoriza.
Parameters
| Name | Type | Description |
|---|---|---|
| $newPluginClass | string |
PluginsManager::getPlugins()
public function getPlugins(): arrayReturns all booted plugin instances, keyed by plugin name.
PluginsManager::getPlugin()
public function getPlugin(string $name): ?Milpa\Interfaces\Plugin\PluginInterfaceReturns a single booted plugin instance by name, or null if no such plugin has been booted.
Parameters
| Name | Type | Description |
|---|---|---|
| $name | string |
PluginsManager::isEnabled()
public function isEnabled(string $name): boolWhether the given plugin name is currently enabled.
Parameters
| Name | Type | Description |
|---|---|---|
| $name | string |
PluginsManager::getToolProviderPromptSections()
public function getToolProviderPromptSections(): arrayGet prompt sections from all plugins that implement ToolProviderInterface. This allows dynamically building the AI system prompt based on registered tools.
Returns
All prompt sections from tool providers
PluginsManager::addPluginPath()
public function addPluginPath(string $path): voidRegisters a directory where plugins may be found.
Parameters
| Name | Type | Description |
|---|---|---|
| $path | string | Physical path to the plugins directory. |
PluginsManager::removePluginPath()
public function removePluginPath(string $path): voidRemoves a previously registered path from the plugins directory list.
Parameters
| Name | Type | Description |
|---|---|---|
| $path | string | Physical path to the plugins directory to remove. |
PluginsManager::getPluginsPaths()
public function getPluginsPaths(): arrayReturns every path currently registered as a possible plugin location.
PluginsManager::loadPlugins()
public function loadPlugins(): voidLoads and registers plugins from every registered plugin path. Implements caching to avoid a disk scan on every request. The fresh path (cache miss/stale) runs ONE milpa/resolver resolution that both GATES the graph (a `blocked` verdict throws, with the report's learnable first line as the message) and ORDERS the boot ($this->plugins is re-sequenced by the report's loadOrder[]); the cache then persists that order, so the cache-hit path boots it without re-sorting.
PluginsManager::scanPluginsPath()
public function scanPluginsPath(string $pluginPath): voidScan plugins in a path and collect metadata WITHOUT booting. Used to enable contract validation and dependency ordering.
Parameters
| Name | Type | Description |
|---|---|---|
| $pluginPath | string |
PluginsManager::getMetadata()
public function getMetadata(string $className): arrayPlugin metadata read from the #[PluginMetadata] attribute — the single authority for plugin identity (D5). A plugin's milpa.json remains the distribution manifest on disk; divergence between the two surfaces as a doctor parity warning, never as a different boot graph.
Parameters
| Name | Type | Description |
|---|---|---|
| $className | string | Fully-qualified plugin class name. |
Throws
\Exception When the class does not exist or declares no metadata.