> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instructorphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agents

# Code Agents With `AgentCtrl`

The `AgentCtrl` facade provides a unified interface for invoking CLI-based code agents that can execute code, modify files, and perform complex multi-step tasks. Each agent runs as an external process, and the facade handles process management, output parsing, and response structuring.

This guide is specifically for external CLI code agents. Native `Cognesy\Agents` runtime integration uses a separate Laravel config namespace under `instructor.agents` and is documented separately in [Native Agents](native-agents).

## Setup

Before using `AgentCtrl`, install the CLI agent you want to run and make sure its executable is available in `PATH`. The Laravel package does not install or authenticate these tools for you.

| Agent       | Required binary | Setup note                                                                      |
| ----------- | --------------- | ------------------------------------------------------------------------------- |
| Claude Code | `claude`        | Install Claude Code separately and sign in with its normal workflow             |
| Codex       | `codex`         | Install the Codex CLI and ensure `codex` resolves on the server running Laravel |
| OpenCode    | `opencode`      | Install OpenCode and ensure `opencode` resolves on the server running Laravel   |

After the binary is available, configure Laravel defaults in `config/instructor.php` under `agent_ctrl` for timeout, working directory, sandbox driver, and per-agent model overrides.

## Supported Agents

| Agent           | Description                                  | Use Case                                              |
| --------------- | -------------------------------------------- | ----------------------------------------------------- |
| **Claude Code** | Anthropic's Claude agent with code execution | General coding tasks, refactoring, file modifications |
| **Codex**       | OpenAI's Codex agent                         | Code generation and completion                        |
| **OpenCode**    | Multi-model code agent                       | Research and coding with model flexibility            |

## Quick Start

```php theme={null}
use Cognesy\AgentCtrl\Config\AgentCtrlConfig;
use Cognesy\Instructor\Laravel\Facades\AgentCtrl;

// Execute a task with Claude Code
$response = AgentCtrl::claudeCode()
    ->withConfig(new AgentCtrlConfig(
        timeout: 300,
        workingDirectory: base_path(),
    ))
    ->execute('Generate a Laravel migration for a users table with name, email, and password fields');

// Check if successful
if ($response->isSuccess()) {
    echo $response->text();
}
// @doctest id="42cc"
```

## Agent Selection

### Claude Code

Best for general coding tasks with Anthropic's Claude models. Supports sandbox isolation, session resumption, and streaming output.

```php theme={null}
use Cognesy\Instructor\Laravel\Facades\AgentCtrl;

$response = AgentCtrl::claudeCode()
    ->withModel('claude-opus-4-5')
    ->inDirectory(base_path())
    ->withTimeout(300)
    ->execute('Refactor the User model to use DTOs');

echo $response->text();
echo "Session ID: " . (string) ($response->sessionId() ?? '');
// @doctest id="2eb3"
```

### Codex

Best for OpenAI-powered code generation.

```php theme={null}
$response = AgentCtrl::codex()
    ->withModel('codex')
    ->execute('Write unit tests for the UserService class');
// @doctest id="5fd1"
```

### OpenCode

Best for multi-model flexibility. Specify the model using the `provider/model` format.

```php theme={null}
$response = AgentCtrl::openCode()
    ->withModel('anthropic/claude-sonnet-4-5')
    ->execute('Analyze the codebase architecture');
// @doctest id="3d30"
```

### Dynamic Selection

Select agent type at runtime based on configuration or business logic.

```php theme={null}
use Cognesy\AgentCtrl\Enum\AgentType;

$agentType = AgentType::from(config('app.default_agent'));

$response = AgentCtrl::make($agentType)
    ->execute('Generate API documentation');
// @doctest id="4fe4"
```

## Configuration

### Builder Methods

All agents support the same set of builder methods for configuration. Use `withConfig()` when you want one typed object for the shared options, then layer agent-specific methods on top as needed. Builder methods override any defaults set in the Laravel config file.

```php theme={null}
use Cognesy\AgentCtrl\Config\AgentCtrlConfig;
use Cognesy\Sandbox\Enums\SandboxDriver;

AgentCtrl::claudeCode()
    ->withConfig(new AgentCtrlConfig(
        model: 'claude-opus-4-5',
        timeout: 300,
        workingDirectory: '/path/to/project',
        sandboxDriver: SandboxDriver::Host,
    ))
    ->execute('Your prompt');
// @doctest id="200d"
```

`AgentCtrlConfig::fromArray()` also accepts the Laravel config-style keys used in `config/instructor.php`, so `directory` and `sandbox` are mapped automatically.

### Laravel Configuration

Configure defaults in `config/instructor.php`. The facade automatically reads these values and applies them when you create a builder. Builder methods then override any defaults for that specific call.

```php theme={null}
return [
    'agent_ctrl' => [
        // Default timeout for all agents
        'timeout' => env('INSTRUCTOR_AGENT_TIMEOUT', 300),
        // Default working directory
        'directory' => env('INSTRUCTOR_AGENT_DIRECTORY'),
        // Default sandbox driver: host, docker, podman, firejail, bubblewrap
        'sandbox' => env('INSTRUCTOR_AGENT_SANDBOX', 'host'),
        // Claude Code specific
        'claude_code' => [
            'model' => env('CLAUDE_CODE_MODEL', 'claude-sonnet-4-20250514'),
            'timeout' => env('CLAUDE_CODE_TIMEOUT'),
            'directory' => env('CLAUDE_CODE_DIRECTORY'),
            'sandbox' => env('CLAUDE_CODE_SANDBOX'),
        ],
        // Codex specific
        'codex' => [
            'model' => env('CODEX_MODEL', 'codex'),
            'timeout' => env('CODEX_TIMEOUT'),
            'directory' => env('CODEX_DIRECTORY'),
            'sandbox' => env('CODEX_SANDBOX'),
        ],
        // OpenCode specific
        'opencode' => [
            'model' => env('OPENCODE_MODEL', 'anthropic/claude-sonnet-4-20250514'),
            'timeout' => env('OPENCODE_TIMEOUT'),
            'directory' => env('OPENCODE_DIRECTORY'),
            'sandbox' => env('OPENCODE_SANDBOX'),
        ],
    ],
];
// @doctest id="4fe2"
```

Agent-specific settings (e.g., `claude_code.timeout`) take precedence over the global defaults (e.g., `agent_ctrl.timeout`).

### Environment Variables

```env theme={null}
# Default agent settings
INSTRUCTOR_AGENT_TIMEOUT=300
INSTRUCTOR_AGENT_DIRECTORY=/path/to/project
INSTRUCTOR_AGENT_SANDBOX=host

# Claude Code
CLAUDE_CODE_MODEL=claude-opus-4-5

# Codex
CODEX_MODEL=codex

# OpenCode
OPENCODE_MODEL=anthropic/claude-sonnet-4-20250514
// @doctest id="86fb"
```

## Streaming

Process output in real-time with streaming callbacks. The three callback types fire at different points during execution.

```php theme={null}
$response = AgentCtrl::claudeCode()
    ->onText(function (string $text) {
        // Called as text is generated -- use for live output
        echo $text;
    })
    ->onToolUse(function (string $tool, array $input, ?string $output) {
        // Called when agent uses a tool (file read, shell command, etc.)
        echo "Tool: $tool\n";
        echo "Input: " . json_encode($input) . "\n";
    })
    ->onComplete(function (AgentResponse $response) {
        // Called once when execution finishes
        echo "\nDone! Exit code: " . $response->exitCode;
    })
    ->executeStreaming('Generate a REST API for products');
// @doctest id="e501"
```

## Response Object

The `AgentResponse` object contains the agent's output along with metadata about the execution.

```php theme={null}
$response = AgentCtrl::claudeCode()->execute('...');

// Main content
$response->text();           // string -- Generated text output
$response->isSuccess();      // bool -- True if exitCode is 0

// Metadata
$response->exitCode;         // int -- Process exit code
$response->sessionId();      // AgentSessionId|null -- Session ID for resuming
$response->agentType;        // AgentType -- Which agent was used

// Usage (when available)
$response->usage;            // ?TokenUsage -- Token statistics
$response->usage->input;     // int -- Input tokens
$response->usage->output;    // int -- Output tokens
$response->usage->total();   // int -- Total tokens

// Cost (when available)
$response->cost;             // ?float -- Cost in USD

// Tool calls
$response->toolCalls;        // array<ToolCall> -- Tools used during execution
foreach ($response->toolCalls as $call) {
    $call->tool;             // string -- Tool name
    $call->input;            // array -- Tool input parameters
    $call->output;           // ?string -- Tool output
    $call->isError;          // bool -- Whether the tool call failed
}
// @doctest id="3d4b"
```

## Session Management

Resume previous sessions for continued work. This is useful for multi-turn interactions where the agent needs context from a prior execution.

```php theme={null}
// First execution
$response = AgentCtrl::claudeCode()
    ->execute('Start refactoring the User model');

$sessionId = (string) ($response->sessionId() ?? '');

// Later: Resume the session with full context from the previous run
$response = AgentCtrl::claudeCode()
    ->resumeSession($sessionId)
    ->execute('Continue with the Address model');
// @doctest id="d6c1"
```

## Error Handling

Always check `isSuccess()` and handle failures gracefully. Agent executions can fail due to timeouts, sandbox errors, or issues in the generated code.

```php theme={null}
use Cognesy\Instructor\Laravel\Facades\AgentCtrl;

try {
    $response = AgentCtrl::claudeCode()
        ->withTimeout(60)
        ->execute($prompt);

    if (!$response->isSuccess()) {
        // Non-zero exit code
        Log::error('Agent failed', [
            'exit_code' => $response->exitCode,
            'output' => $response->text(),
        ]);
        return null;
    }

    // Check for tool errors
    foreach ($response->toolCalls as $call) {
        if ($call->isError) {
            Log::warning('Tool error', [
                'tool' => $call->tool,
                'error' => $call->output,
            ]);
        }
    }

    return $response->text();

} catch (\Throwable $e) {
    // Timeout, sandbox error, etc.
    Log::error('Agent exception', ['error' => $e->getMessage()]);
    return null;
}
// @doctest id="71d5"
```

## Testing

Use `AgentCtrl::fake()` for testing without actual agent execution. See the [Testing](testing) guide for full documentation of `AgentCtrlFake`.

```php theme={null}
use Cognesy\Instructor\Laravel\Facades\AgentCtrl;

test('generates migration code', function () {
    // Setup fake with expected responses
    $fake = AgentCtrl::fake([
        'Generated migration file: 2024_01_01_create_users_table.php',
    ]);

    // Execute code under test
    $result = app(MigrationGenerator::class)->generate('users');

    // Assertions
    $fake->assertExecuted();
    $fake->assertExecutedTimes(1);
    $fake->assertUsedClaudeCode();
    $fake->assertExecutedWith('migration');

    expect($result)->toContain('users_table');
});
// @doctest id="85ac"
```

## Real-World Examples

### Code Generation Service

```php theme={null}
namespace App\Services;

use Cognesy\Instructor\Laravel\Facades\AgentCtrl;

class CodeGenerationService
{
    public function generateMigration(array $schema): string
    {
        $prompt = $this->buildMigrationPrompt($schema);

        $response = AgentCtrl::claudeCode()
            ->inDirectory(database_path('migrations'))
            ->execute($prompt);

        if (!$response->isSuccess()) {
            throw new \RuntimeException('Failed to generate migration');
        }

        return $response->text();
    }

    public function generateTest(string $className): string
    {
        $response = AgentCtrl::claudeCode()
            ->inDirectory(base_path('tests'))
            ->execute("Generate comprehensive tests for: $className");

        return $response->text();
    }

    private function buildMigrationPrompt(array $schema): string
    {
        return "Generate a Laravel migration for:\n" . json_encode($schema, JSON_PRETTY_PRINT);
    }
}
// @doctest id="b090"
```

### Queued Code Generation

For long-running agent tasks, dispatch them to a queue so the user does not have to wait.

```php theme={null}
namespace App\Jobs;

use Cognesy\Instructor\Laravel\Facades\AgentCtrl;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class GenerateCodeJob implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public string $prompt,
        public string $outputPath,
    ) {}

    public function handle(): void
    {
        $response = AgentCtrl::claudeCode()
            ->withTimeout(600) // 10 minutes for complex tasks
            ->inDirectory(dirname($this->outputPath))
            ->execute($this->prompt);

        if ($response->isSuccess()) {
            file_put_contents($this->outputPath, $response->text());
        }
    }
}

// Usage
GenerateCodeJob::dispatch(
    'Generate a complete CRUD controller for Products',
    app_path('Http/Controllers/ProductController.php')
);
// @doctest id="ebde"
```

### Interactive Code Review

```php theme={null}
use Cognesy\Instructor\Laravel\Facades\AgentCtrl;

class CodeReviewer
{
    public function review(string $filePath, callable $onProgress = null): array
    {
        $builder = AgentCtrl::claudeCode()
            ->inDirectory(dirname($filePath));

        if ($onProgress) {
            $builder->onText($onProgress);
        }

        $response = $builder->execute(
            "Review this file for bugs, security issues, and improvements: $filePath"
        );

        return [
            'review' => $response->text(),
            'success' => $response->isSuccess(),
            'session' => (string) ($response->sessionId() ?? ''),
        ];
    }
}
// @doctest id="f411"
```

## Sandbox Drivers

Control the isolation level of agent execution. The sandbox driver determines whether the agent runs directly on the host or inside a container.

| Driver       | Description                     | Use Case                          |
| ------------ | ------------------------------- | --------------------------------- |
| `host`       | Direct execution (no isolation) | Development, trusted environments |
| `docker`     | Docker container isolation      | Production, untrusted code        |
| `podman`     | Podman container isolation      | Rootless containers               |
| `firejail`   | Linux sandbox                   | Lightweight isolation             |
| `bubblewrap` | Minimal sandbox                 | CI/CD environments                |

```php theme={null}
use Cognesy\Sandbox\Enums\SandboxDriver;

// Development (direct execution)
AgentCtrl::claudeCode()
    ->withSandboxDriver(SandboxDriver::Host)
    ->execute('...');

// Production (Docker isolation)
AgentCtrl::claudeCode()
    ->withSandboxDriver(SandboxDriver::Docker)
    ->execute('...');
// @doctest id="ee03"
```

## Best Practices

1. **Set Timeouts** -- Always set appropriate timeouts for your use case. Complex code generation can take several minutes.
2. **Use Sandbox Isolation** -- In production, use Docker or another container-based sandbox driver to prevent agents from making unintended changes.
3. **Handle Errors** -- Check `isSuccess()` and handle failures gracefully. Agents can fail for many reasons, including API limits, invalid code, and sandbox restrictions.
4. **Log Sessions** -- Store session IDs for debugging and continuation. They let you resume work and trace agent behavior.
5. **Test with Fakes** -- Use `AgentCtrl::fake()` in tests to avoid API calls and process execution.
6. **Queue Long Tasks** -- Use Laravel queues for time-consuming code generation to keep web responses fast.
