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=1launches only the selected backend - Bounded execution: Live mode has a 30-second timeout