run(); } } /** * Access the booted instance. Null before `plugins_loaded`. */ public static function instance(): ?Plugin { return self::$instance; } /** * Access the container. */ public function container(): Container { return $this->container; } /** * Whether a premium feature flag is unlocked. * * Free core always returns false; the Freemius layer (or an addon) unlocks * flags through this filter. Known flags so far: `remove_branding`. * * @param string $flag Feature flag id. */ public static function feature_enabled( string $flag ): bool { /** * Filter premium feature availability. * * @param bool $enabled Whether the feature is unlocked. Default false. * @param string $flag Feature flag id. */ return (bool) apply_filters( 'agyl_feature_enabled', false, $flag ); } /** * Build the container and attach WordPress hooks. */ private function run(): void { $this->container = $this->build_container(); /** * Filter the service container right after construction. * * Addons may override or add bindings here. * * @param Container $container The Agentyllo container. */ $this->container = apply_filters( 'agyl_container', $this->container ); $container = $this->container; add_action( 'init', static function () use ( $container ): void { // Cheap version check on EVERY request type (front, REST, cron, // admin): a background auto-update must never leave new code // running against an old schema until a human opens wp-admin. $container->get( Migrator::class )->maybe_upgrade(); $container->get( Jobs::class )->register(); } ); // Priority 20: after CPT/taxonomy registration so adapter subtypes() // and delta hooks see every registered type. add_action( 'init', static function () use ( $container ): void { $container->get( IndexManager::class )->register(); $container->get( VectorIndexer::class )->register(); }, 20 ); add_action( 'rest_api_init', static function () use ( $container ): void { $container->get( SettingsController::class )->register_routes(); $container->get( CapabilitiesController::class )->register_routes(); $container->get( DashboardController::class )->register_routes(); $container->get( ProbeController::class )->register_routes(); $container->get( AgentsController::class )->register_routes(); $container->get( KbController::class )->register_routes(); $container->get( SearchController::class )->register_routes(); $container->get( ConfigController::class )->register_routes(); $container->get( ChatController::class )->register_routes(); $container->get( PrivacyController::class )->register_routes(); $container->get( StatsController::class )->register_routes(); $container->get( AddonsController::class )->register_routes(); $container->get( ModelsController::class )->register_routes(); $container->get( CopilotController::class )->register_routes(); } ); // AI settings changed (key/model/provider): stale cached answers and // tripped circuits must not outlive the change. add_action( 'agyl_settings_updated', static function ( string $tab ) use ( $container ): void { if ( 'models' !== $tab ) { return; } $container->get( ResponseCache::class )->flush(); foreach ( array( OpenAIProvider::ID, AnthropicProvider::ID, LocalEndpointProvider::ID ) as $provider ) { $container->get( BudgetManager::class )->reset_circuit( $provider ); } // Embedding provider/model change: drop foreign-model vectors and // (re)start embedding in the background. $container->get( VectorStore::class )->gc( $container->get( EmbeddingRouter::class )->model_key() ); $container->get( VectorIndexer::class )->schedule(); } ); // Manual KB entries: trash is purged after 30 days (hourly maintenance). add_action( 'agyl_maintenance', static function () use ( $container ): void { $container->get( ManualEntries::class )->purge_trashed( 30 ); } ); // Frontend widget (self-guards against admin/feed/login at render time). $container->get( WidgetLoader::class )->register(); // Compliance: WP core privacy exporter/eraser + transparency shortcode. $container->get( Dsar::class )->register(); $container->get( AiAct::class )->register(); // Core roster registration (low priority so addons see the core set). add_filter( 'agyl_register_agents', static function ( array $agents ) use ( $container ): array { $agents[] = $container->get( SentinelAgent::class ); $agents[] = $container->get( JanitorAgent::class ); $agents[] = $container->get( LearnerAgent::class ); $agents[] = $container->get( KbCuratorAgent::class ); $agents[] = $container->get( ContentWatcherAgent::class ); $agents[] = $container->get( ReconcilerAgent::class ); $agents[] = $container->get( RetrieverAgent::class ); $agents[] = $container->get( LinkGrapherAgent::class ); $agents[] = $container->get( SiteProfilerAgent::class ); $agents[] = $container->get( IntentRouterAgent::class ); $agents[] = $container->get( ComposerAgent::class ); $agents[] = $container->get( ScopeGuardAgent::class ); $agents[] = $container->get( LangBrokerAgent::class ); return $agents; }, 5 ); if ( is_admin() ) { add_action( 'admin_menu', static function () use ( $container ): void { $container->get( Menu::class )->register(); } ); add_action( 'admin_enqueue_scripts', static function ( string $hook ) use ( $container ): void { $container->get( Assets::class )->maybe_enqueue( $hook ); } ); // Quick access from the Plugins list: Settings + Set up AI. add_filter( 'plugin_action_links_' . plugin_basename( AGYL_FILE ), static function ( array $links ): array { $cap = (string) apply_filters( 'agyl_capability_map', 'manage_options', 'agyl_manage' ); if ( ! current_user_can( $cap ) ) { return $links; } $own = array( 'settings' => sprintf( '%s', esc_url( admin_url( 'admin.php?page=agentyllo-settings' ) ), esc_html__( 'Settings', 'agentyllo' ) ), 'ai' => sprintf( '%s', esc_url( admin_url( 'admin.php?page=agentyllo-models' ) ), esc_html__( 'Set up AI', 'agentyllo' ) ), ); return array_merge( $own, $links ); } ); // First-run: land the user on the dashboard with a welcome flag // instead of nowhere. Fires once, never during bulk/network activation. add_action( 'admin_init', static function (): void { if ( ! get_transient( 'agyl_activation_redirect' ) ) { return; } delete_transient( 'agyl_activation_redirect' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['activate-multi'] ) || wp_doing_ajax() || is_network_admin() ) { return; } $cap = (string) apply_filters( 'agyl_capability_map', 'manage_options', 'agyl_manage' ); if ( current_user_can( $cap ) ) { wp_safe_redirect( admin_url( 'admin.php?page=agentyllo&welcome=1' ) ); exit; } } ); } // Opt-in anonymous telemetry (off by default). $container->get( Telemetry::class )->register(); /** * Fires once Agentyllo core is wired. Addons register agents, * providers, and settings tabs from this hook. * * @param Container $container The Agentyllo container. */ do_action( 'agyl_init', $this->container ); } /** * Default service bindings. */ private function build_container(): Container { $c = new Container(); $c->singleton( Options::class, static fn (): Options => new Options() ); $c->singleton( SettingsSchema::class, static fn (): SettingsSchema => new SettingsSchema() ); $c->singleton( SettingsStore::class, static fn ( Container $c ): SettingsStore => new SettingsStore( $c->get( SettingsSchema::class ), $c->get( Options::class ) ) ); $c->singleton( Detector::class, static fn ( Container $c ): Detector => new Detector( $c->get( Options::class ) ) ); // Agent kernel. $c->singleton( MemoryStore::class, static fn (): MemoryStore => new MemoryStore() ); $c->singleton( Journal::class, static fn (): Journal => new Journal() ); $c->singleton( EventBus::class, static fn ( Container $c ): EventBus => new EventBus( $c->get( Journal::class ) ) ); $c->singleton( Registry::class, static fn ( Container $c ): Registry => new Registry( $c->get( Options::class ) ) ); $c->singleton( Quarantine::class, static fn ( Container $c ): Quarantine => new Quarantine( $c->get( Registry::class ), $c->get( Journal::class ), $c->get( EventBus::class ) ) ); $c->singleton( AsyncBus::class, static fn (): AsyncBus => new AsyncBus() ); $c->singleton( Orchestrator::class, static fn ( Container $c ): Orchestrator => new Orchestrator( $c->get( Registry::class ), $c->get( MemoryStore::class ), $c->get( Journal::class ), $c->get( EventBus::class ), $c->get( AsyncBus::class ), $c->get( Detector::class ), $c ) ); // Core roster. $c->singleton( SentinelAgent::class, static fn ( Container $c ): SentinelAgent => new SentinelAgent( $c->get( Registry::class ), $c->get( Quarantine::class ) ) ); $c->singleton( JanitorAgent::class, static fn (): JanitorAgent => new JanitorAgent() ); $c->singleton( LearnerAgent::class, static fn (): LearnerAgent => new LearnerAgent() ); // Knowledge base engine. $c->singleton( Tokenizer::class, static fn (): Tokenizer => new Tokenizer() ); $c->singleton( Normalizer::class, static fn (): Normalizer => new Normalizer() ); $c->singleton( Chunker::class, static fn ( Container $c ): Chunker => new Chunker( $c->get( Tokenizer::class ) ) ); $c->singleton( Store::class, static fn ( Container $c ): Store => new Store( $c->get( Tokenizer::class ) ) ); $c->singleton( ElementorAdapter::class, static fn ( Container $c ): ElementorAdapter => new ElementorAdapter( $c->get( Normalizer::class ) ) ); $c->singleton( PostTypeAdapter::class, static fn ( Container $c ): PostTypeAdapter => new PostTypeAdapter( $c->get( Normalizer::class ), $c->get( ElementorAdapter::class ), static fn ( string $key ): mixed => $c->get( SettingsStore::class )->value( 'sources', $key ) ) ); $c->singleton( TaxonomyAdapter::class, static fn ( Container $c ): TaxonomyAdapter => new TaxonomyAdapter( $c->get( Normalizer::class ) ) ); $c->singleton( MenuAdapter::class, static fn (): MenuAdapter => new MenuAdapter() ); $c->singleton( SiteIdentityAdapter::class, static fn (): SiteIdentityAdapter => new SiteIdentityAdapter() ); $c->singleton( ManualAdapter::class, static fn (): ManualAdapter => new ManualAdapter() ); $c->singleton( WooProductAdapter::class, static fn ( Container $c ): WooProductAdapter => new WooProductAdapter( $c->get( Normalizer::class ), static fn (): array => $c->get( SettingsStore::class )->get( 'sources' ) ) ); $c->singleton( AdapterRegistry::class, static fn ( Container $c ): AdapterRegistry => new AdapterRegistry( array( $c->get( SiteIdentityAdapter::class ), $c->get( MenuAdapter::class ), $c->get( PostTypeAdapter::class ), $c->get( WooProductAdapter::class ), $c->get( TaxonomyAdapter::class ), $c->get( ManualAdapter::class ), ) ) ); $c->singleton( IndexManager::class, static fn ( Container $c ): IndexManager => new IndexManager( $c->get( AdapterRegistry::class ), $c->get( Store::class ), $c->get( Chunker::class ), $c->get( Detector::class ), $c->get( Journal::class ), static fn (): array => $c->get( SettingsStore::class )->get( 'sources' ) ) ); $c->singleton( HybridRetriever::class, static fn ( Container $c ): HybridRetriever => new HybridRetriever( $c->get( Tokenizer::class ) ) ); $c->singleton( LinkGraph::class, static fn (): LinkGraph => new LinkGraph() ); $c->singleton( Health::class, static function ( Container $c ): Health { $store = $c->get( SettingsStore::class ); $resolver = static function ( string $source, string $subtype ) use ( $store ): bool { return match ( $source ) { 'post' => match ( $subtype ) { 'post' => (bool) $store->value( 'sources', 'posts_enabled' ), 'page' => (bool) $store->value( 'sources', 'pages_enabled' ), default => (bool) $store->value( 'sources', 'cpt_' . $subtype . '_enabled' ), }, 'menu' => (bool) $store->value( 'sources', 'menus_enabled' ), 'site' => (bool) $store->value( 'sources', 'site_identity_enabled' ), 'taxonomy' => (bool) $store->value( 'sources', 'taxonomies_enabled' ), 'product' => (bool) $store->value( 'sources', 'woocommerce_enabled' ), default => true, }; }; return new Health( $c->get( AdapterRegistry::class ), $resolver, $c->get( Store::class ) ); } ); // KB roster agents. $c->singleton( KbCuratorAgent::class, static fn ( Container $c ): KbCuratorAgent => new KbCuratorAgent( $c->get( IndexManager::class ), $c->get( AdapterRegistry::class ) ) ); $c->singleton( ContentWatcherAgent::class, static fn (): ContentWatcherAgent => new ContentWatcherAgent() ); $c->singleton( ReconcilerAgent::class, static fn ( Container $c ): ReconcilerAgent => new ReconcilerAgent( $c->get( IndexManager::class ) ) ); $c->singleton( RetrieverAgent::class, static fn ( Container $c ): RetrieverAgent => new RetrieverAgent( $c->get( HybridRetriever::class ) ) ); $c->singleton( LinkGrapherAgent::class, static fn ( Container $c ): LinkGrapherAgent => new LinkGrapherAgent( $c->get( LinkGraph::class ) ) ); // KB REST. $c->singleton( KbController::class, static fn ( Container $c ): KbController => new KbController( $c->get( Store::class ), $c->get( IndexManager::class ) ) ); $c->singleton( SearchController::class, static fn ( Container $c ): SearchController => new SearchController( $c->get( HybridRetriever::class ) ) ); // Chat: understanding services + stages. $c->singleton( LanguageDetector::class, static fn (): LanguageDetector => new LanguageDetector() ); $c->singleton( EntityExtractor::class, static fn (): EntityExtractor => new EntityExtractor() ); $c->singleton( IntentClassifier::class, static fn (): IntentClassifier => new IntentClassifier() ); $c->singleton( Templates::class, static fn (): Templates => new Templates() ); $c->singleton( StarterSuggestions::class, static fn (): StarterSuggestions => new StarterSuggestions() ); $c->singleton( NormalizeStage::class, static fn (): NormalizeStage => new NormalizeStage() ); $c->singleton( LanguageDetectStage::class, static fn ( Container $c ): LanguageDetectStage => new LanguageDetectStage( $c->get( LanguageDetector::class ) ) ); $c->singleton( IntentClassifyStage::class, static fn ( Container $c ): IntentClassifyStage => new IntentClassifyStage( $c->get( EntityExtractor::class ), $c->get( IntentClassifier::class ) ) ); $c->singleton( RouteStage::class, static fn ( Container $c ): RouteStage => new RouteStage( $c->get( Store::class ) ) ); $c->singleton( RetrieveStage::class, static fn ( Container $c ): RetrieveStage => new RetrieveStage( $c->get( HybridRetriever::class ), $c->get( EmbeddingRouter::class ), $c->get( VectorStore::class ), $c->get( QueryRewriter::class ) ) ); $c->singleton( ScopeGuardStage::class, static fn ( Container $c ): ScopeGuardStage => new ScopeGuardStage( $c->get( HybridRetriever::class ), static fn (): array => $c->get( SettingsStore::class )->get( 'general' ) ) ); $c->singleton( ComposeStage::class, static fn ( Container $c ): ComposeStage => new ComposeStage( $c->get( Templates::class ), $c->get( HybridRetriever::class ), $c->get( Tokenizer::class ), static fn (): array => $c->get( SettingsStore::class )->get( 'general' ) ) ); $c->singleton( PostProcessStage::class, static fn ( Container $c ): PostProcessStage => new PostProcessStage( static fn (): array => $c->get( SettingsStore::class )->get( 'widget' ) ) ); // AI layer (M7): signed registry, vault, providers, budget, router. $c->singleton( Manifest::class, static fn (): Manifest => new Manifest() ); $c->singleton( Ed25519Verifier::class, static fn (): Ed25519Verifier => new Ed25519Verifier() ); $c->singleton( RemoteSync::class, static fn ( Container $c ): RemoteSync => new RemoteSync( $c->get( Manifest::class ), $c->get( Ed25519Verifier::class ) ) ); $c->singleton( KeyVault::class, static fn (): KeyVault => new KeyVault() ); $c->singleton( StreamingClient::class, static fn (): StreamingClient => new StreamingClient() ); $models_settings = static fn ( Container $c ): callable => static fn (): array => $c->get( SettingsStore::class )->get( 'models' ); $c->singleton( OpenAIProvider::class, static fn ( Container $c ): OpenAIProvider => new OpenAIProvider( $c->get( Manifest::class ), $c->get( KeyVault::class ), $c->get( StreamingClient::class ), $models_settings( $c ) ) ); $c->singleton( AnthropicProvider::class, static fn ( Container $c ): AnthropicProvider => new AnthropicProvider( $c->get( Manifest::class ), $c->get( KeyVault::class ), $c->get( StreamingClient::class ), $models_settings( $c ) ) ); $c->singleton( OpenAIEmbeddings::class, static fn ( Container $c ): OpenAIEmbeddings => new OpenAIEmbeddings( $c->get( Manifest::class ), $c->get( KeyVault::class ), $c->get( StreamingClient::class ), $models_settings( $c ) ) ); $c->singleton( BudgetManager::class, static fn ( Container $c ): BudgetManager => new BudgetManager( $c->get( Manifest::class ), $models_settings( $c ) ) ); $c->singleton( ResponseCache::class, static fn (): ResponseCache => new ResponseCache() ); $c->singleton( LocalEndpointProvider::class, static fn ( Container $c ): LocalEndpointProvider => new LocalEndpointProvider( $c->get( KeyVault::class ), $c->get( StreamingClient::class ), $models_settings( $c ) ) ); $c->singleton( CloudClient::class, static fn (): CloudClient => new CloudClient() ); $c->singleton( AgentylloCloudProvider::class, static fn ( Container $c ): AgentylloCloudProvider => new AgentylloCloudProvider( $c->get( CloudClient::class ), $models_settings( $c ) ) ); $c->singleton( ProviderRouter::class, static fn ( Container $c ): ProviderRouter => new ProviderRouter( $c->get( BudgetManager::class ), array( $c->get( AgentylloCloudProvider::class ), $c->get( OpenAIProvider::class ), $c->get( AnthropicProvider::class ), $c->get( LocalEndpointProvider::class ) ), static fn (): array => $c->get( SettingsStore::class )->get( 'general' ), $models_settings( $c ) ) ); // Dense retrieval (M8): embedding router + vector store + background indexer. $c->singleton( LocalEndpointEmbeddings::class, static fn ( Container $c ): LocalEndpointEmbeddings => new LocalEndpointEmbeddings( $c->get( LocalEndpointProvider::class ), $c->get( KeyVault::class ), $c->get( StreamingClient::class ), $models_settings( $c ) ) ); $c->singleton( EmbeddingRouter::class, static fn ( Container $c ): EmbeddingRouter => new EmbeddingRouter( array( $c->get( OpenAIEmbeddings::class ), $c->get( LocalEndpointEmbeddings::class ) ), $models_settings( $c ) ) ); $c->singleton( VectorStore::class, static fn (): VectorStore => new VectorStore() ); $c->singleton( VectorIndexer::class, static fn ( Container $c ): VectorIndexer => new VectorIndexer( $c->get( EmbeddingRouter::class ), $c->get( VectorStore::class ) ) ); $c->singleton( QueryRewriter::class, static fn ( Container $c ): QueryRewriter => new QueryRewriter( $c->get( ProviderRouter::class ), $c->get( BudgetManager::class ) ) ); $c->singleton( ChatPromptBuilder::class, static fn ( Container $c ): ChatPromptBuilder => new ChatPromptBuilder( $c->get( Manifest::class ) ) ); $c->singleton( AiComposeStage::class, static fn ( Container $c ): AiComposeStage => new AiComposeStage( $c->get( ProviderRouter::class ), $c->get( BudgetManager::class ), $c->get( ResponseCache::class ), $c->get( ChatPromptBuilder::class ), static fn (): array => $c->get( SettingsStore::class )->get( 'general' ), static fn (): array => $c->get( SettingsStore::class )->get( 'language' ), static fn (): array => $c->get( SettingsStore::class )->get( 'privacy' ), $models_settings( $c ) ) ); $c->singleton( ModelsController::class, static fn ( Container $c ): ModelsController => new ModelsController( $c->get( ProviderRouter::class ), $c->get( BudgetManager::class ), $c->get( ResponseCache::class ), $c->get( Manifest::class ), $c->get( RemoteSync::class ), $c->get( SettingsStore::class ), $c->get( SettingsSchema::class ), $c->get( StreamingClient::class ), $c->get( EmbeddingRouter::class ), $c->get( VectorStore::class ), $c->get( VectorIndexer::class ), $c->get( CloudClient::class ) ) ); // Pipeline. Order matters: scope_guard reads the retrieval score; the // AI composer runs after the guard and before the classic composer, // which skips when an AI tier already (verifiably) answered. $c->singleton( Pipeline::class, static fn ( Container $c ): Pipeline => new Pipeline( array( $c->get( NormalizeStage::class ), $c->get( LanguageDetectStage::class ), $c->get( IntentClassifyStage::class ), $c->get( RouteStage::class ), $c->get( RetrieveStage::class ), $c->get( ScopeGuardStage::class ), $c->get( AiComposeStage::class ), $c->get( ComposeStage::class ), $c->get( PostProcessStage::class ), ), $c->get( Journal::class ) ) ); // Chat: sessions, limits, log, REST, widget. $c->singleton( SessionManager::class, static fn (): SessionManager => new SessionManager() ); $c->singleton( RateLimiter::class, static fn (): RateLimiter => new RateLimiter() ); $c->singleton( ConversationLog::class, static fn (): ConversationLog => new ConversationLog() ); $c->singleton( ConfigController::class, static fn ( Container $c ): ConfigController => new ConfigController( $c->get( SettingsStore::class ) ) ); $c->singleton( Stats::class, static fn (): Stats => new Stats() ); $c->singleton( StatsController::class, static fn ( Container $c ): StatsController => new StatsController( $c->get( Stats::class ) ) ); $c->singleton( ChatController::class, static fn ( Container $c ): ChatController => new ChatController( $c->get( SessionManager::class ), $c->get( RateLimiter::class ), $c->get( ConversationLog::class ), $c->get( Pipeline::class ), $c->get( SettingsStore::class ), $c->get( Stats::class ) ) ); $c->singleton( WidgetLoader::class, static fn ( Container $c ): WidgetLoader => new WidgetLoader( static fn (): array => $c->get( SettingsStore::class )->get( 'widget' ) ) ); // Compliance. $c->singleton( Consent::class, static fn (): Consent => new Consent() ); $c->singleton( Dsar::class, static fn (): Dsar => new Dsar() ); $c->singleton( AiAct::class, static fn ( Container $c ): AiAct => new AiAct( $c->get( SettingsStore::class ) ) ); $c->singleton( Retention::class, static fn ( Container $c ): Retention => new Retention( static fn (): array => $c->get( SettingsStore::class )->get( 'privacy' ) ) ); $c->singleton( PrivacyController::class, static fn ( Container $c ): PrivacyController => new PrivacyController( $c->get( SessionManager::class ), $c->get( Consent::class ), $c->get( Dsar::class ), $c->get( AiAct::class ), $c->get( SettingsStore::class ) ) ); // Chat roster agents. $c->singleton( SiteProfilerAgent::class, static fn ( Container $c ): SiteProfilerAgent => new SiteProfilerAgent( $c->get( SettingsStore::class ) ) ); $c->singleton( IntentRouterAgent::class, static fn (): IntentRouterAgent => new IntentRouterAgent() ); $c->singleton( ComposerAgent::class, static fn (): ComposerAgent => new ComposerAgent() ); $c->singleton( ScopeGuardAgent::class, static fn (): ScopeGuardAgent => new ScopeGuardAgent() ); $c->singleton( LangBrokerAgent::class, static fn (): LangBrokerAgent => new LangBrokerAgent() ); $c->singleton( Jobs::class, static fn ( Container $c ): Jobs => new Jobs( $c->get( Detector::class ), $c->get( Orchestrator::class ), $c->get( Health::class ), $c->get( Retention::class ), $c->get( Stats::class ), $c->get( RemoteSync::class ), $c->get( ResponseCache::class ), $c->get( BudgetManager::class ), $models_settings( $c ) ) ); $c->singleton( Migrator::class, static fn (): Migrator => new Migrator() ); $c->singleton( SettingsController::class, static fn ( Container $c ): SettingsController => new SettingsController( $c->get( SettingsStore::class ), $c->get( SettingsSchema::class ) ) ); $c->singleton( CapabilitiesController::class, static fn ( Container $c ): CapabilitiesController => new CapabilitiesController( $c->get( Detector::class ) ) ); $c->singleton( DashboardController::class, static fn ( Container $c ): DashboardController => new DashboardController( $c->get( Detector::class ), $c->get( SettingsStore::class ), $c->get( Stats::class ), $c->get( Registry::class ), $c->get( MemoryStore::class ) ) ); $c->singleton( ProbeController::class, static fn (): ProbeController => new ProbeController() ); $c->singleton( AddonCatalog::class, static fn (): AddonCatalog => new AddonCatalog() ); $c->singleton( AddonsController::class, static fn ( Container $c ): AddonsController => new AddonsController( $c->get( AddonCatalog::class ) ) ); $c->singleton( AgentsController::class, static fn ( Container $c ): AgentsController => new AgentsController( $c->get( Registry::class ), $c->get( Quarantine::class ), $c->get( MemoryStore::class ) ) ); // Copilot (M9): manual entries, action registry, orchestrator, ingestion. $c->singleton( ManualEntries::class, static fn ( Container $c ): ManualEntries => new ManualEntries( $c->get( Store::class ), $c->get( Chunker::class ) ) ); $c->singleton( ActionRegistry::class, static function ( Container $c ): ActionRegistry { $registry = new ActionRegistry(); ( new CoreActions( $c->get( ManualEntries::class ), $c->get( IndexManager::class ), $c->get( SettingsStore::class ), $c->get( SettingsSchema::class ), $c->get( MemoryStore::class ), $c->get( Stats::class ) ) )->register( $registry ); return $registry; } ); $c->singleton( CopilotBrain::class, static fn ( Container $c ): CopilotBrain => new CopilotBrain( $c->get( ProviderRouter::class ), $c->get( HybridRetriever::class ), $c->get( ActionRegistry::class ), static fn (): array => (array) $c->get( SettingsStore::class )->get( 'general' ) ) ); $c->singleton( Copilot::class, static fn ( Container $c ): Copilot => new Copilot( $c->get( ActionRegistry::class ), $c->get( HybridRetriever::class ), $c->get( CopilotBrain::class ) ) ); $c->singleton( FileIngest::class, static fn ( Container $c ): FileIngest => new FileIngest( $c->get( ManualEntries::class ) ) ); $c->singleton( CopilotController::class, static fn ( Container $c ): CopilotController => new CopilotController( $c->get( Copilot::class ), $c->get( FileIngest::class ), $c->get( Stats::class ) ) ); $c->singleton( Menu::class, static fn (): Menu => new Menu() ); $c->singleton( Assets::class, static fn (): Assets => new Assets() ); $c->singleton( Telemetry::class, static fn ( Container $c ): Telemetry => new Telemetry( static fn (): array => (array) $c->get( SettingsStore::class )->get( 'advanced' ) ) ); return $c; } }