05-Agent-Providers
Two layers: low-level providers (OpenAI, Claude API, Claude Code, local shell) and high-level specialized agents (Architect, Backend, Frontend, iPhone, RAG, QA, DevOps, Documentation).
1. Provider interface
interface AgentProviderInterface {
public function key(): string; // 'openai' | 'claude_api' | 'claude_code' | 'local_shell'
public function capabilities(): array; // ['chat','tools','streaming','code_exec']
public function prepareContext(AgentRun $run, RagResult $rag): ProviderContext;
public function startRun(ProviderContext $ctx): ProviderStream;
public function continueRun(AgentRun $run, string $userInput): ProviderStream;
public function pauseRun(AgentRun $run): void;
public function cancelRun(AgentRun $run): void;
public function summarizeRun(AgentRun $run): RunSummary;
public function extractFileChanges(ProviderEvent $e): array; // FileChange[]
public function streamEvents(ProviderStream $s): iterable; // yields ProviderEvent
}ProviderEvent is normalized:
final class ProviderEvent {
public string $kind; // 'message','tool_call','tool_result','file_change','command','done','error','pause'
public array $payload;
public ?string $rawProviderType = null;
}The orchestrator maps ProviderEvent โ agent_events rows via ConsoleEventService.
2. Provider implementations
| Service | Notes |
|---|---|
OpenAIAgentService | Uses Responses API with tools; streams via SSE; converts tool calls โ orchestrator-mediated tool executions. |
ClaudeAgentService | Anthropic Messages API with tool use; same normalization. |
ClaudeCodeAgentService | Wraps the Claude Code CLI/SDK. Sends workspace path, allowed tools, structured stop signals. |
LocalShellRunner | Internal-only provider used by the orchestrator for tool execution; not chosen by users. |
All provider classes are constructor-injected and bound to the container in AgentProvidersServiceProvider. The AgentProviderResolver returns the right instance from selected_model / selected_agent.
3. Orchestrator
AgentRunOrchestrator is the single state machine. Responsibilities:
- acquire workspace lock
- snapshot
pre_run
- create Git branch
agent/run-{id}-{slug}
- retrieve RAG context (unless disabled)
- call
provider->prepareContext()andstartRun()
- iterate normalized events:
- tool calls โ
CommandExecutionService/WorkspaceService/GitService
- file changes โ
WorkspaceService::applyChange()+WorkspaceFilerow + diff event
- pause โ set status
waiting_for_user, persist, broadcast
- tool calls โ
- on completion โ run
DocumentationAutoUpdateService, writefinal_summary, release lock
- on failure โ write
failed_reason, still release lock, emitfinal_summaryevent withseverity: error
State transitions match the diagram in the master page (ยง4).
4. Tool surface exposed to providers
| Tool | Backed by | Notes |
|---|---|---|
read_file(path) | WorkspaceService | Path is normalized & sandboxed. |
list_files(glob?) | WorkspaceService | Returns relative paths only. |
apply_patch(unified_diff) | WorkspaceService | Pre-validates against working tree; emits file_* events. |
run_command(cmd, args, timeout?) | CommandExecutionService | Allowlist-gated; destructive โ auto pre_command snapshot. |
rag_search(query, opts?) | RagContextService | Returns chunks + debug. |
git(op, args?) | GitService | Constrained to status/diff/branch/commit/restore. |
ask_user(question, options?) | Orchestrator | Triggers paused_for_input. |
finish(summary) | Orchestrator | Triggers final_summary โข run completion. |
Tools are advertised to each provider in its native schema. Provider-specific shaping lives in the provider class.
5. Specialized agents
Each specialized agent is a configuration object:
final class AgentDefinition {
public string $key; // 'architect', 'backend', ...
public string $displayName;
public string $systemPrompt; // role-specific
public array $allowedTools; // subset of the tool surface
public array $defaultModel; // ['provider' => 'openai', 'model' => 'gpt-4.1']
public array $ragFilters; // default source_types to bias retrieval
}Roles
| Agent | Bias | Allowed tools (extra) |
|---|---|---|
| Architect | reads structure, writes plans, never edits prod code without explicit ask | read_file, list_files, rag_search, ask_user, finish |
| Backend | controllers, models, migrations, services, routes, APIs, tests | all |
| Frontend | Blade / Livewire / Vue / React, UI, forms | all except destructive Git |
| iPhone | SwiftUI screens, API client, auth flow | read_file, list_files, apply_patch, rag_search, finish |
| RAG | indexes, embeddings, stale detection | read_file, list_files, run_command (index), rag_search, finish |
| QA | tests, route checks, migration checks, API responses, UI flows | run_command (test), read_file, list_files, finish |
| DevOps | server params, queues, Redis, Git, deploys | run_command (whitelisted ops), git, finish |
| Documentation | updates docs/changelogs/diagrams/rules | read_file, apply_patch (docs only), rag_search, finish |
All agents share the same RAG, same event log, same workspace.
6. Prompt skeleton (shared system prompt header)
You are the {agent_display_name} for the AI Coding Agent Workspace.
Project: {project.name} ({framework}/{language}).
Branch: {run.branch_name} (an isolated branch for this run).
Safe mode: {safe_mode_on_off}.
RAG: {rag_on_off}. If ON, you must call rag_search before any plan or edit.
Never guess when context exists. If unsure, call ask_user.
Never push, never delete history, never run blocked commands.
Always finish() with a structured summary describing changes, risks, and tests.Role-specific suffix appended per agent.
7. Failure handling
- Provider error โ emit
errorevent, transition tofailed, do not auto-rollback (leave the user to choose).
- Tool error โ propagate to provider as a tool_result with
is_error: true; agent decides recovery.
- Timeout โ kill subprocess, mark
was_killed=trueon the command row, transition tofailedunless agent recovers gracefully.
8. Pause / resume
paused_for_inputevent carries{ question, options?, deadline? }.
- Orchestrator persists provider conversation state in
agent_runs.metadata_json.provider_state.
POST /api/runs/{run}/resumewith{ input: "โฆ" }rehydrates and callsprovider->continueRun().