Agent Control Runtime Switching
Overview¶
AgentCtrl provides a unified API that works across multiple CLI-based code agents. This enables runtime switching between different backends (Claude Code, OpenCode, Codex, Gemini) without changing your application code. Useful for comparing agent performance, failover scenarios, or A/B testing.
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
- AgentCtrlConsoleLogger: Shared logger works across all agent types
Example¶
<?php
require 'examples/boot.php';
use Cognesy\AgentCtrl\AgentCtrl;
use Cognesy\AgentCtrl\Broadcasting\AgentCtrlConsoleLogger;
use Cognesy\AgentCtrl\Enum\AgentType;
// Shared console logger - works across all agent types
$logger = new AgentCtrlConsoleLogger(
useColors: true,
showTimestamps: true,
);
$prompt = 'What design pattern does a class with a static make() method implement? Answer in one sentence.';
// Test the same prompt with multiple agents
$agents = [
'opencode' => 'OpenCode',
'claude-code' => 'Claude Code',
'codex' => 'Codex',
];
foreach ($agents as $agentId => $agentName) {
echo "=== Testing: {$agentName} ===\n\n";
$startTime = microtime(true);
try {
$builder = AgentCtrl::make(AgentType::from($agentId))
->wiretap($logger->wiretap());
// Apply agent-specific configuration
if ($agentId === 'claude-code') {
$builder->withMaxTurns(1);
}
$response = $builder->execute($prompt);
$elapsed = round((microtime(true) - $startTime) * 1000);
echo "\n=== Result ({$elapsed}ms) ===\n";
if ($response->isSuccess()) {
echo "Answer: " . $response->text() . "\n";
if ($response->usage) {
echo "Tokens: {$response->usage->input} in / {$response->usage->output} out\n";
}
if ($response->cost) {
echo "Cost: $" . number_format($response->cost, 4) . "\n";
}
} else {
echo "Failed (exit code: {$response->exitCode})\n";
}
} catch (Throwable $e) {
echo "Error: {$e->getMessage()}\n";
}
echo "\n";
}
?>
Expected Output¶
=== Testing: OpenCode ===
14:32:15.123 [opencode] [EXEC] Execution started [prompt=What design pattern does a class...]
14:32:16.357 [opencode] [DONE] Execution completed [exit=0, tools=0, tokens=62]
=== Result (1234ms) ===
Answer: The Factory Method pattern, which uses a static method to create
and return instances of different subclasses based on parameters.
Tokens: 38 in / 24 out
Cost: $0.0008
=== Testing: Claude Code ===
14:32:17.123 [claude-code] [EXEC] Execution started [prompt=What design pattern does a class...]
14:32:19.279 [claude-code] [DONE] Execution completed [exit=0, tools=0, cost=$0.0015, tokens=64]
=== Result (2156ms) ===
Answer: This is the Factory Method pattern, where a static factory method
determines which concrete class to instantiate based on input.
Tokens: 38 in / 26 out
Cost: $0.0015
=== Testing: Codex ===
14:32:20.123 [codex] [EXEC] Execution started [prompt=What design pattern does a class...]
14:32:21.110 [codex] [DONE] Execution completed [exit=0, tools=0, tokens=60]
=== Result (987ms) ===
Answer: A class using static make() for conditional instantiation typically
implements the Factory Method or Static Factory pattern.
Tokens: 38 in / 22 out
Cost: $0.0012
Key Points¶
- Unified API: Same interface across all agent backends
- Shared logger: One
AgentCtrlConsoleLoggerworks across all agent types, prefixing output with[opencode],[claude-code], etc. - Runtime selection: Choose agent dynamically based on requirements
- Agent comparison: Run the same prompt across multiple agents to compare
- Failover capability: Try alternative agents if primary fails
- Agent-specific tuning: Apply configuration based on agent characteristics
- Performance comparison: Measure response time and cost across agents
- Use cases: A/B testing, load balancing, fallback strategies, feature parity testing