Skip to content

Agent Control Runtime Switching

Overview

AgentCtrl provides a unified API that works across multiple CLI-based code agents. This enables typed runtime selection between Claude Code, OpenCode, Codex, Pi, and Gemini without changing the calling code. The default example verifies factory selection offline; set AGENT_CTRL_LIVE=1 to execute one selected backend with a bounded timeout.

Key concepts: - AgentType enum: Specify which agent backend to use - Unified API: Same methods work across all agent types - Runtime selection: Choose agent dynamically based on configuration or logic - Optional live execution: Explicitly opt in to one CLI call

Example

<?php
require 'examples/boot.php';

use Cognesy\AgentCtrl\AgentCtrl;
use Cognesy\AgentCtrl\Broadcasting\AgentCtrlConsoleLogger;
use Cognesy\AgentCtrl\Builder\ClaudeCodeBridgeBuilder;
use Cognesy\AgentCtrl\Builder\CodexBridgeBuilder;
use Cognesy\AgentCtrl\Builder\GeminiBridgeBuilder;
use Cognesy\AgentCtrl\Builder\OpenCodeBridgeBuilder;
use Cognesy\AgentCtrl\Builder\PiBridgeBuilder;
use Cognesy\AgentCtrl\Config\AgentCtrlConfig;
use Cognesy\AgentCtrl\Enum\AgentType;

// Shared console logger - works across all agent types
$logger = new AgentCtrlConsoleLogger(
    useColors: true,
    showTimestamps: true,
);

$configuredAgent = getenv('AGENT_CTRL_DRIVER') ?: AgentType::Gemini->value;
$agentType = AgentType::tryFrom($configuredAgent);

if ($agentType === null) {
    throw new InvalidArgumentException("Unknown AgentCtrl driver: {$configuredAgent}");
}

$builder = AgentCtrl::make($agentType);
$expectedBuilder = match ($agentType) {
    AgentType::ClaudeCode => ClaudeCodeBridgeBuilder::class,
    AgentType::Codex => CodexBridgeBuilder::class,
    AgentType::OpenCode => OpenCodeBridgeBuilder::class,
    AgentType::Pi => PiBridgeBuilder::class,
    AgentType::Gemini => GeminiBridgeBuilder::class,
};

assert($builder instanceof $expectedBuilder, 'Factory must return the builder for the selected agent type');

echo "Selected: {$agentType->value}\n";
echo 'Builder: ' . $builder::class . "\n";

if (getenv('AGENT_CTRL_LIVE') !== '1') {
    echo "Live execution disabled. Set AGENT_CTRL_LIVE=1 to run the selected backend.\n";
    exit(0);
}

$response = $builder
    ->wiretap($logger->wiretap())
    ->withConfig(new AgentCtrlConfig(
        timeout: 30,
        workingDirectory: getcwd() ?: null,
    ))
    ->execute('What design pattern does a class with a static make() method implement? Answer in one sentence.');

assert($response->isSuccess(), "Selected backend failed with exit code {$response->exitCode}");
assert(trim($response->text()) !== '', 'Selected backend must return a non-empty answer');

echo "Answer: {$response->text()}\n";
?>

Expected Output

Selected: gemini
Builder: Cognesy\AgentCtrl\Builder\GeminiBridgeBuilder
Live execution disabled. Set AGENT_CTRL_LIVE=1 to run the selected backend.

Key Points

  • Unified API: Every backend is selected through AgentCtrl::make()
  • Typed selection: AgentType::tryFrom() rejects unknown configured values
  • Deterministic default: Factory selection is verified without launching external processes
  • Explicit live mode: AGENT_CTRL_LIVE=1 launches only the selected backend
  • Bounded execution: Live mode has a 30-second timeout