agent/php/Boot.php

251 lines
10 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Core\Mod\Agentic;
use Core\Events\AdminPanelBooting;
use Core\Events\ApiRoutesRegistering;
use Core\Events\ConsoleBooting;
use Core\Events\McpToolsRegistering;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
class Boot extends ServiceProvider
{
protected string $moduleName = 'agentic';
/**
* Events this module listens to for lazy loading.
*
* @var array<class-string, string>
*/
public static array $listens = [
AdminPanelBooting::class => 'onAdminPanel',
ApiRoutesRegistering::class => 'onApiRoutes',
ConsoleBooting::class => 'onConsole',
McpToolsRegistering::class => 'onMcpTools',
];
public function boot(): void
{
$this->loadMigrationsFrom(__DIR__.'/Migrations');
$this->loadTranslationsFrom(__DIR__.'/Lang', 'agentic');
$this->configureRateLimiting();
$this->scheduleCommands();
}
/**
* Register all scheduled commands.
*/
protected function scheduleCommands(): void
{
$this->app->booted(function (): void {
$schedule = $this->app->make(Schedule::class);
$schedule->command('agentic:plan-cleanup')->daily();
// Forgejo pipeline — only active when a token is configured
if (config('agentic.forge_token') !== '') {
$schedule->command('agentic:scan')->everyFiveMinutes();
$schedule->command('agentic:dispatch')->everyTwoMinutes();
$schedule->command('agentic:pr-manage')->everyFiveMinutes();
}
});
}
/**
* Configure rate limiting for agentic endpoints.
*/
protected function configureRateLimiting(): void
{
// Rate limit for the for-agents.json endpoint
// Allow 60 requests per minute per IP
RateLimiter::for('agentic-api', function (Request $request) {
return Limit::perMinute(60)->by($request->ip());
});
}
public function register(): void
{
$this->mergeConfigFrom(
__DIR__.'/config.php',
'mcp'
);
$this->mergeConfigFrom(
__DIR__.'/agentic.php',
'agentic'
);
// Register the dedicated brain database connection.
// Falls back to the app's default DB when no BRAIN_DB_* env vars are set.
$brainDb = config('mcp.brain.database');
if (is_array($brainDb) && ! empty($brainDb['host'])) {
config(['database.connections.brain' => $brainDb]);
}
$this->app->singleton(\Core\Mod\Agentic\Services\AgenticManager::class);
$this->app->singleton(\Core\Mod\Agentic\Services\AgentToolRegistry::class);
$this->app->singleton(\Core\Mod\Agentic\Services\ForgejoService::class, function ($app) {
return new \Core\Mod\Agentic\Services\ForgejoService(
baseUrl: (string) config('agentic.forge_url', 'https://forge.lthn.ai'),
token: (string) config('agentic.forge_token', ''),
);
});
$this->app->singleton(\Core\Mod\Agentic\Services\BrainService::class, function ($app) {
$ollamaUrl = config('mcp.brain.ollama_url', 'http://localhost:11434');
$qdrantUrl = config('mcp.brain.qdrant_url', 'http://localhost:6334');
// Skip TLS verification for non-public TLDs (self-signed certs behind Traefik)
$hasLocalTld = static fn (string $url): bool => (bool) preg_match(
'/\.(lan|lab|local|test)(?:[:\/]|$)/',
parse_url($url, PHP_URL_HOST) ?? ''
);
$verifySsl = ! ($hasLocalTld($ollamaUrl) || $hasLocalTld($qdrantUrl));
return new \Core\Mod\Agentic\Services\BrainService(
ollamaUrl: $ollamaUrl,
qdrantUrl: $qdrantUrl,
collection: config('mcp.brain.collection', 'openbrain'),
embeddingModel: config('mcp.brain.embedding_model', 'nomic-embed-text'),
verifySsl: $verifySsl,
);
});
}
// -------------------------------------------------------------------------
// Event-driven handlers (for lazy loading once event system is integrated)
// -------------------------------------------------------------------------
/**
* Handle API routes registration event.
*
* Registers REST API endpoints for go-agentic Client consumption.
* Routes at /v1/* Go client uses BaseURL + "/v1/plans" directly.
*/
public function onApiRoutes(ApiRoutesRegistering $event): void
{
// Register agent API auth middleware alias
$event->middleware('agent.auth', Middleware\AgentApiAuth::class);
// Scoped via event — only loaded for API requests
if (file_exists(__DIR__.'/Routes/api.php')) {
$event->routes(fn () => require __DIR__.'/Routes/api.php');
}
}
/**
* Handle admin panel booting event.
*/
public function onAdminPanel(AdminPanelBooting $event): void
{
$event->views($this->moduleName, __DIR__.'/View/Blade');
// Register admin routes
if (file_exists(__DIR__.'/Routes/admin.php')) {
$event->routes(fn () => require __DIR__.'/Routes/admin.php');
}
// Register Livewire components
$event->livewire('agentic.admin.dashboard', View\Modal\Admin\Dashboard::class);
$event->livewire('agentic.admin.plans', View\Modal\Admin\Plans::class);
$event->livewire('agentic.admin.plan-detail', View\Modal\Admin\PlanDetail::class);
$event->livewire('agentic.admin.sessions', View\Modal\Admin\Sessions::class);
$event->livewire('agentic.admin.session-detail', View\Modal\Admin\SessionDetail::class);
$event->livewire('agentic.admin.tool-analytics', View\Modal\Admin\ToolAnalytics::class);
$event->livewire('agentic.admin.tool-calls', View\Modal\Admin\ToolCalls::class);
$event->livewire('agentic.admin.api-keys', View\Modal\Admin\ApiKeys::class);
$event->livewire('agentic.admin.templates', View\Modal\Admin\Templates::class);
// Note: Navigation is registered via AdminMenuProvider interface
// in the existing boot() method until we migrate to pure event-driven nav.
}
/**
* Handle console booting event.
*/
public function onConsole(ConsoleBooting $event): void
{
// Register middleware alias for CLI context (artisan route:list)
$event->middleware('agent.auth', Middleware\AgentApiAuth::class);
$event->command(Console\Commands\TaskCommand::class);
$event->command(Console\Commands\PlanCommand::class);
$event->command(Console\Commands\AgenticGenerateCommand::class);
$event->command(Console\Commands\GenerateCommand::class);
$event->command(Console\Commands\PlanRetentionCommand::class);
$event->command(Console\Commands\BrainSeedMemoryCommand::class);
$event->command(Console\Commands\BrainIngestCommand::class);
$event->command(Console\Commands\ScanCommand::class);
$event->command(Console\Commands\DispatchCommand::class);
$event->command(Console\Commands\PrManageCommand::class);
$event->command(Console\Commands\PrepWorkspaceCommand::class);
}
/**
* Handle MCP tools registration event.
*
* Note: Agent tools (plan_create, session_start, etc.) are implemented in
* the Mcp module at Mod\Mcp\Tools\Agent\* and registered via AgentToolRegistry.
* Brain tools are registered here as they belong to the Agentic module.
*/
public function onMcpTools(McpToolsRegistering $event): void
{
$registry = $this->app->make(Services\AgentToolRegistry::class);
$toolClasses = [
Mcp\Tools\Agent\Brain\BrainRemember::class,
Mcp\Tools\Agent\Brain\BrainRecall::class,
Mcp\Tools\Agent\Brain\BrainForget::class,
Mcp\Tools\Agent\Brain\BrainList::class,
Mcp\Tools\Agent\Messaging\AgentSend::class,
Mcp\Tools\Agent\Messaging\AgentInbox::class,
Mcp\Tools\Agent\Messaging\AgentConversation::class,
Mcp\Tools\Agent\Plan\PlanCreate::class,
Mcp\Tools\Agent\Plan\PlanGet::class,
Mcp\Tools\Agent\Plan\PlanList::class,
Mcp\Tools\Agent\Plan\PlanUpdateStatus::class,
Mcp\Tools\Agent\Plan\PlanArchive::class,
Mcp\Tools\Agent\Phase\PhaseGet::class,
Mcp\Tools\Agent\Phase\PhaseUpdateStatus::class,
Mcp\Tools\Agent\Phase\PhaseAddCheckpoint::class,
Mcp\Tools\Agent\Session\SessionStart::class,
Mcp\Tools\Agent\Session\SessionEnd::class,
Mcp\Tools\Agent\Session\SessionLog::class,
Mcp\Tools\Agent\Session\SessionHandoff::class,
Mcp\Tools\Agent\Session\SessionResume::class,
Mcp\Tools\Agent\Session\SessionReplay::class,
Mcp\Tools\Agent\Session\SessionContinue::class,
Mcp\Tools\Agent\Session\SessionArtifact::class,
Mcp\Tools\Agent\Session\SessionList::class,
Mcp\Tools\Agent\State\StateSet::class,
Mcp\Tools\Agent\State\StateGet::class,
Mcp\Tools\Agent\State\StateList::class,
Mcp\Tools\Agent\Task\TaskUpdate::class,
Mcp\Tools\Agent\Task\TaskToggle::class,
Mcp\Tools\Agent\Template\TemplateList::class,
Mcp\Tools\Agent\Template\TemplatePreview::class,
Mcp\Tools\Agent\Template\TemplateCreatePlan::class,
Mcp\Tools\Agent\Content\ContentGenerate::class,
Mcp\Tools\Agent\Content\ContentBatchGenerate::class,
Mcp\Tools\Agent\Content\ContentBriefCreate::class,
Mcp\Tools\Agent\Content\ContentBriefGet::class,
Mcp\Tools\Agent\Content\ContentBriefList::class,
Mcp\Tools\Agent\Content\ContentStatus::class,
Mcp\Tools\Agent\Content\ContentUsageStats::class,
Mcp\Tools\Agent\Content\ContentFromPlan::class,
];
$registry->registerMany(array_map(
static fn (string $toolClass) => new $toolClass(),
$toolClasses,
));
}
}