agent/docs/php-agent/RFC.md

417 lines
16 KiB
Markdown
Raw Permalink Normal View History

# core/php/agent RFC — Agentic Module (PHP Implementation)
> The PHP implementation of the agent system, specced from existing code.
> Implements `code/core/agent/RFC.md` contract in PHP.
> An agent should be able to build agent features from this document alone.
**Module:** `dappco.re/php/agent`
**Namespace:** `Core\Mod\Agentic\*`
**Sub-specs:** [Actions](RFC.actions.md) | [Architecture](RFC.architecture.md) | [Commands](RFC.commands.md) | [Endpoints](RFC.endpoints.md) | [MCP Tools](RFC.mcp-tools.md) | [Models](RFC.models.md) | [OpenBrain Design](RFC.openbrain-design.md) | [OpenBrain Impl](RFC.openbrain-impl.md) | [Porting Plan](RFC.porting-plan.md) | [Security](RFC.security.md) | [UI](RFC.ui.md)
---
## 1. Domain Model
| Model | Table | Purpose |
|-------|-------|---------|
| `AgentPlan` | `agent_plans` | Structured work plans with phases, soft-deleted, activity-logged |
| `AgentPhase` | `agent_phases` | Individual phase within a plan (tasks, deps, status) |
| `AgentSession` | `agent_sessions` | Agent work sessions (context, work_log, artefacts, handoff) |
| `AgentMessage` | `agent_messages` | Direct agent-to-agent messaging (chronological, not semantic) |
| `AgentApiKey` | `agent_api_keys` | External agent access keys (hashed, scoped, rate-limited) |
| `BrainMemory` | `brain_memories` | Semantic knowledge store (tags, confidence, vector-indexed) |
| `Issue` | `issues` | Bug/feature/task tracking (labels, priority, sprint) |
| `IssueComment` | `issue_comments` | Comments on issues |
| `Sprint` | `sprints` | Time-boxed iterations grouping issues |
| `Task` | `tasks` | Simple tasks (title, status, file/line ref) |
| `Prompt` | `prompts` | Reusable AI prompt templates (system + user template) |
| `PromptVersion` | `prompt_versions` | Immutable prompt snapshots |
| `PlanTemplateVersion` | `plan_template_versions` | Immutable YAML template snapshots |
| `WorkspaceState` | `workspace_states` | Key-value state per plan (typed, shared across sessions) |
---
## 2. Actions
Single-responsibility action classes in `Actions/`:
### Brain
| Action | Method | Purpose |
|--------|--------|---------|
| `ForgetKnowledge` | `execute(id)` | Delete a memory |
| `ListKnowledge` | `execute(filters)` | List memories with filtering |
| `RecallKnowledge` | `execute(query)` | Semantic search via Qdrant |
| `RememberKnowledge` | `execute(content, tags)` | Store + embed memory |
### Forge
| Action | Method | Purpose |
|--------|--------|---------|
| `AssignAgent` | `execute(issue, agent)` | Assign agent to Forge issue |
| `CreatePlanFromIssue` | `execute(issue)` | Generate plan from issue description |
| `ManagePullRequest` | `execute(pr)` | Review/merge/close PRs |
| `ReportToIssue` | `execute(issue, report)` | Post agent findings to issue |
| `ScanForWork` | `execute()` | Scan Forge repos for actionable issues |
### Issue
| Action | Method | Purpose |
|--------|--------|---------|
| `CreateIssue` | `execute(data)` | Create issue |
| `GetIssue` | `execute(id)` | Get issue by ID |
| `ListIssues` | `execute(filters)` | List with filtering |
| `UpdateIssue` | `execute(id, data)` | Update fields |
| `AddIssueComment` | `execute(id, body)` | Add comment |
| `ArchiveIssue` | `execute(id)` | Soft delete |
### Plan
| Action | Method | Purpose |
|--------|--------|---------|
| `CreatePlan` | `execute(data)` | Create plan with phases |
| `GetPlan` | `execute(id)` | Get plan by ID/slug |
| `ListPlans` | `execute(filters)` | List plans |
| `UpdatePlanStatus` | `execute(id, status)` | Update plan status |
| `ArchivePlan` | `execute(id)` | Soft delete plan |
### Phase
| Action | Method | Purpose |
|--------|--------|---------|
| `GetPhase` | `execute(id)` | Get phase details |
| `UpdatePhaseStatus` | `execute(id, status)` | Update phase status |
| `AddCheckpoint` | `execute(id, checkpoint)` | Record checkpoint |
### Session
| Action | Method | Purpose |
|--------|--------|---------|
| `StartSession` | `execute(data)` | Start agent session |
| `ContinueSession` | `execute(id, data)` | Resume session |
| `EndSession` | `execute(id, summary)` | End session with summary |
| `GetSession` | `execute(id)` | Get session details |
| `ListSessions` | `execute(filters)` | List sessions |
### Sprint
| Action | Method | Purpose |
|--------|--------|---------|
| `CreateSprint` | `execute(data)` | Create sprint |
| `GetSprint` | `execute(id)` | Get sprint |
| `ListSprints` | `execute(filters)` | List sprints |
| `UpdateSprint` | `execute(id, data)` | Update sprint |
| `ArchiveSprint` | `execute(id)` | Soft delete |
### Task
| Action | Method | Purpose |
|--------|--------|---------|
| `ToggleTask` | `execute(id)` | Toggle task completion |
| `UpdateTask` | `execute(id, data)` | Update task fields |
---
## 3. API Endpoints
Routes in `Routes/api.php`, auth via `AgentApiAuth` middleware:
### Brain (`/v1/brain/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/brain/remember` | RememberKnowledge |
| POST | `/v1/brain/recall` | RecallKnowledge |
| DELETE | `/v1/brain/forget/{id}` | ForgetKnowledge |
| GET | `/v1/brain/list` | ListKnowledge |
### Plans (`/v1/plans/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/plans` | CreatePlan |
| GET | `/v1/plans` | ListPlans |
| GET | `/v1/plans/{id}` | GetPlan |
| PATCH | `/v1/plans/{id}/status` | UpdatePlanStatus |
| DELETE | `/v1/plans/{id}` | ArchivePlan |
### Sessions (`/v1/sessions/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/sessions` | StartSession |
| GET | `/v1/sessions` | ListSessions |
| GET | `/v1/sessions/{id}` | GetSession |
| POST | `/v1/sessions/{id}/continue` | ContinueSession |
| POST | `/v1/sessions/{id}/end` | EndSession |
### Messages (`/v1/messages/*`)
| Method | Endpoint | Action |
|--------|----------|--------|
| POST | `/v1/messages/send` | AgentSend |
| GET | `/v1/messages/inbox` | AgentInbox |
| GET | `/v1/messages/conversation/{agent}` | AgentConversation |
### Issues, Sprints, Tasks, Phases — similar CRUD patterns.
### Auth (`/v1/agent/auth/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/agent/auth/provision` | ProvisionAgentKey | OAuth (Authentik) |
| DELETE | `/v1/agent/auth/revoke/{key_id}` | RevokeAgentKey | AgentApiKey |
### Fleet (`/v1/fleet/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/fleet/register` | RegisterNode | AgentApiKey |
| POST | `/v1/fleet/heartbeat` | NodeHeartbeat | AgentApiKey |
| POST | `/v1/fleet/deregister` | DeregisterNode | AgentApiKey |
| GET | `/v1/fleet/nodes` | ListNodes | AgentApiKey |
| POST | `/v1/fleet/task/assign` | AssignTask | AgentApiKey |
| POST | `/v1/fleet/task/complete` | CompleteTask | AgentApiKey |
| GET | `/v1/fleet/task/next` | GetNextTask | AgentApiKey |
### Fleet Events (SSE)
| Method | Path | Purpose | Auth |
|--------|------|---------|------|
| GET | `/v1/fleet/events` | SSE stream — pushes task assignments to connected nodes | AgentApiKey |
The SSE connection stays open. When the scheduler assigns a task to a node, it pushes a `task.assigned` event. Nodes that can't hold SSE connections fall back to polling `GET /v1/fleet/task/next`.
### Fleet Stats (`/v1/fleet/stats`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| GET | `/v1/fleet/stats` | GetFleetStats | AgentApiKey |
Returns: nodes_online, tasks_today, tasks_week, repos_touched, findings_total, compute_hours.
### Sync (`/v1/agent/sync/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/agent/sync` | PushDispatchHistory | AgentApiKey |
| GET | `/v1/agent/context` | PullFleetContext | AgentApiKey |
| GET | `/v1/agent/status` | GetAgentSyncStatus | AgentApiKey |
### Credits (`/v1/credits/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/credits/award` | AwardCredits | Internal |
| GET | `/v1/credits/balance/{agent_id}` | GetBalance | AgentApiKey |
| GET | `/v1/credits/history/{agent_id}` | GetCreditHistory | AgentApiKey |
### Subscription (`/v1/subscription/*`)
| Method | Path | Action | Auth |
|--------|------|--------|------|
| POST | `/v1/subscription/detect` | DetectCapabilities | AgentApiKey |
| GET | `/v1/subscription/budget/{agent_id}` | GetNodeBudget | AgentApiKey |
| PUT | `/v1/subscription/budget/{agent_id}` | UpdateBudget | AgentApiKey |
---
## 4. MCP Tools
Registered via `AgentToolRegistry` in `onMcpTools()`:
### Brain Tools
| Tool | MCP Name | Maps To |
|------|----------|---------|
| `BrainRemember` | `brain_remember` | RememberKnowledge action |
| `BrainRecall` | `brain_recall` | RecallKnowledge action |
| `BrainForget` | `brain_forget` | ForgetKnowledge action |
| `BrainList` | `brain_list` | ListKnowledge action |
### Messaging Tools
| Tool | MCP Name | Maps To |
|------|----------|---------|
| `AgentSend` | `agent_send` | POST /v1/messages/send |
| `AgentInbox` | `agent_inbox` | GET /v1/messages/inbox |
| `AgentConversation` | `agent_conversation` | GET /v1/messages/conversation |
### Plan/Session/Phase/Task/Template tools — same pattern.
---
## 5. OpenBrain
OpenBrain architecture (storage layers, schema, flow, lifecycle) is defined in `code/core/agent/RFC.md` section "OpenBrain Architecture". PHP provides the MariaDB persistence layer, Qdrant integration, and Ollama embedding via `BrainService`.
---
## 6. Provider Abstraction
```php
interface AgenticProviderInterface
{
public function generate(string $prompt, array $options = []): string;
public function stream(string $prompt, array $options = [], callable $onToken): void;
public function name(): string;
public function defaultModel(): string;
public function isAvailable(): bool;
}
```
`AgenticManager` registers providers (Claude, Gemini, OpenAI) with retry + exponential backoff.
---
## 7. Session Lifecycle
```
StartSession(plan_id, agent) -> active session with context
-> Agent works, appends to work_log
-> ContinueSession(id, work) -> resume from last state
-> EndSession(id, summary, handoff_notes) -> closed
-> session_handoff tool: {summary, next_steps, blockers, context_for_next}
-> session_replay tool: recover context from completed session
```
### Workspace State
Key-value store shared between sessions within a plan:
```php
// Agent A discovers something
WorkspaceState::set($planId, 'discovered_pattern', 'observer');
// Agent B reads it later
$pattern = WorkspaceState::get($planId, 'discovered_pattern');
```
---
## 8. API Key Security
- **Hashing**: Argon2id (upgraded from SHA-256 Jan 2026)
- **Scoping**: Permission strings (`plans:read`, `plans:write`, `sessions:write`, `brain:recall`)
- **IP restriction**: IPv4/IPv6/CIDR whitelist via `IpRestrictionService`
- **Rate limiting**: Per-key configurable limits
- **Display**: Key shown once on creation, stored hashed, prefix `ak_` for identification
---
## 9. Services
| Service | Purpose |
|---------|---------|
| `AgenticManager` | Provider registry (claude, gemini, openai) |
| `AgentSessionService` | Session lifecycle management |
| `AgentApiKeyService` | API key CRUD + hashing |
| `AgentToolRegistry` | MCP tool registration |
| `BrainService` | Qdrant + Ollama integration (embed, search, store) |
| `ClaudeService` | Anthropic API client |
| `GeminiService` | Google Gemini API client |
| `OpenAIService` | OpenAI API client |
| `ForgejoService` | Forgejo API client (issues, PRs, repos) |
| `ContentService` | AI content generation pipeline |
| `PlanTemplateService` | YAML template loading + versioning |
| `IpRestrictionService` | IP whitelist enforcement |
| `AgentDetection` | Detect agent type from request headers |
---
## 10. Console Commands
| Command | Artisan | Purpose |
|---------|---------|---------|
| `TaskCommand` | `agentic:task` | Manage tasks |
| `PlanCommand` | `agentic:plan` | Manage plans |
| `GenerateCommand` | `agentic:generate` | AI content generation |
| `PlanRetentionCommand` | `agentic:plan-cleanup` | Clean old plans (scheduled daily) |
| `BrainSeedMemoryCommand` | `brain:seed-memory` | Seed brain from files |
| `BrainIngestCommand` | `brain:ingest` | Bulk ingest into brain |
| `ScanCommand` | `agentic:scan` | Scan Forge for work (every 5 min) |
| `DispatchCommand` | `agentic:dispatch` | Dispatch agents (every 2 min) |
| `PrManageCommand` | `agentic:pr-manage` | Manage PRs (every 5 min) |
| `PrepWorkspaceCommand` | `agentic:prep-workspace` | Prepare agent workspace |
---
## 11. Admin UI (Livewire)
| Component | Route | Purpose |
|-----------|-------|---------|
| `Dashboard` | `/admin/agentic` | Overview stats |
| `Plans` | `/admin/agentic/plans` | Plan listing |
| `PlanDetail` | `/admin/agentic/plans/{id}` | Single plan view |
| `Sessions` | `/admin/agentic/sessions` | Session listing |
| `SessionDetail` | `/admin/agentic/sessions/{id}` | Single session view |
| `ApiKeys` | `/admin/agentic/api-keys` | Key management |
| `ApiKeyManager` | — | Key CRUD modal |
| `Templates` | `/admin/agentic/templates` | Template management |
| `ToolAnalytics` | `/admin/agentic/tools` | Tool usage stats |
| `ToolCalls` | `/admin/agentic/tool-calls` | Tool call log |
| `Playground` | `/admin/agentic/playground` | AI playground |
| `RequestLog` | `/admin/agentic/requests` | API request log |
---
## 12. Content Generation Pipeline
The agentic module was originally built for AI-driven content generation. This is the PHP side's primary product — the Go agent inherited dispatch/workspace/brain but content generation stays PHP.
### Pipeline
```
Product Briefs (per service)
-> Prompt Templates (system + user, versioned)
-> AI Generation (Claude/Gemini via AgenticManager)
-> Drafts (blog posts, help articles, social media)
-> Quality Refinement (scoring, rewriting)
-> Publication (CMS, social scheduler, help desk)
```
### Product Briefs
Each service has a brief (`Resources/briefs/`) that gives AI the product context.
| Brief | Product |
|-------|---------|
| `host-link.md` | LinkHost |
| `host-social.md` | SocialHost |
| `host-analytics.md` | AnalyticsHost |
| `host-trust.md` | TrustHost |
| `host-notify.md` | NotifyHost |
### Prompt Templates
Versioned prompt templates in `Resources/prompts/`:
| Category | Templates |
|----------|-----------|
| **Content** | blog-post, help-article, landing-page, social-media, quality-refinement |
| **Development** | architecture-review, code-review, debug-session, test-generation |
| **Visual** | infographic, logo-generation, social-graphics |
| **System** | dappcore-writer (brand voice) |
### Natural Progression SEO
Content changes create **future revisions** (scheduled posts with no date). When Googlebot visits a page with pending revisions, the system schedules publication 8-62 minutes later — making updates appear as natural content evolution rather than bulk changes.
### MCP Content Tools
```
content_generate — Generate content from brief + prompt template
content_batch — Batch generation across services
content_brief_create — Create new product brief
```
### SEO Schema Generation
Structured data templates for generated content:
- Article (BlogPosting, TechArticle)
- FAQ (FAQPage)
- HowTo (step-by-step guides)
---
## 13. Reference Material
| Resource | Location |
|----------|----------|
| Agent contract (cross-cutting) | `code/core/agent/RFC.md` |
| Go implementation | `code/core/go/agent/RFC.md` |
| lthn.sh platform | `project/lthn/ai/RFC.md` |
---
## Changelog
- 2026-03-29: Restructured as PHP implementation spec. OpenBrain architecture and polyglot mapping moved to `code/core/agent/RFC.md`. Added contract reference. Kept all PHP-specific detail (Eloquent, Livewire, actions, services, commands, admin UI, content pipeline).
- 2026-03-27: Initial RFC specced from existing PHP codebase. 14 models, 30+ actions, 20+ API endpoints, 12 MCP tools, 10 console commands, 12 admin UI components.