Skip to main content

Introduction

Every call to execute() or executeStreaming() returns an AgentResponse — a readonly DTO that provides a normalized view of the agent’s output regardless of which CLI tool produced it. The response contains the text content, process exit code, session identifier, token usage statistics, cost, tool call records, and parse diagnostics. The AgentResponse class is defined at Cognesy\AgentCtrl\Dto\AgentResponse.

Response Properties

The following public properties are available directly on the AgentResponse object:

agentType: AgentType

The agent type that produced this response. This is one of AgentType::ClaudeCode, AgentType::Codex, AgentType::OpenCode, AgentType::Pi, or AgentType::Gemini:
echo "Produced by: {$response->agentType->value}"; // e.g., "claude-code"
// @doctest id="f5ff"

text: string

The agent’s main text output. This is the concatenation of all text content blocks from the agent’s response. For most use cases, you should access this through the text() method instead:
echo $response->text;
// or
echo $response->text();
// @doctest id="0486"

exitCode: int

The process exit code. A value of 0 indicates success. Non-zero values indicate errors, timeouts, or other failures:
if ($response->exitCode !== 0) {
    echo "Process failed with exit code: {$response->exitCode}";
}
// @doctest id="9f2d"
Common exit codes:
  • 0 — Success
  • 1 — General error (invalid configuration, agent-side failure)
  • 2 — Invalid arguments passed to the CLI
  • 124 / 137 — Process killed due to timeout

usage: ?TokenUsage

Token usage statistics, when available. This is null for agents that do not expose usage data (Claude Code does not; Codex, OpenCode, Pi, and Gemini do when the data is present in the CLI output):
$usage = $response->usage;
if ($usage !== null) {
    echo "Input tokens: {$usage->input}\n";
    echo "Output tokens: {$usage->output}\n";
    echo "Total tokens: {$usage->total()}\n";
}
// @doctest id="ca26"

cost: ?float

The estimated cost in USD, when available. Currently, OpenCode and Pi expose cost data:
if ($response->cost !== null) {
    echo sprintf("Cost: $%.4f", $response->cost);
}
// @doctest id="b3cb"

toolCalls: array

An array of ToolCall objects representing every tool invocation made during the execution. See the Tool Calls section below for details:
echo "Tool calls made: " . count($response->toolCalls);
// @doctest id="b8be"

rawResponse: mixed

The original bridge-specific response object (ClaudeResponse, CodexResponse, OpenCodeResponse, PiResponse, or GeminiResponse). This provides access to agent-specific data that is not part of the normalized response:
// Access the raw Codex response for agent-specific data
$codexResponse = $response->rawResponse;
// @doctest id="43d7"

parseFailures: int

The number of malformed JSON lines that were skipped during response parsing:
if ($response->parseFailures > 0) {
    echo "Warning: {$response->parseFailures} JSON parse failures";
}
// @doctest id="0cf3"

Response Methods

isSuccess(): bool

Returns true if the exit code is 0. This is the recommended way to check whether the execution succeeded:
$response = AgentCtrl::codex()->execute('Create a short summary.');

if ($response->isSuccess()) {
    echo $response->text();
} else {
    echo "Failed with exit code: {$response->exitCode}";
    echo "Partial output: " . $response->text();
}
// @doctest id="1a88"
A completed execution with a non-zero exit code does not throw an exception. Always check isSuccess() before treating the text output as authoritative.

text(): string

Returns the agent’s text output. Equivalent to accessing the text property directly:
echo $response->text();
// @doctest id="67d3"

sessionId(): ?AgentSessionId

Returns the session identifier as an AgentSessionId value object, or null if no session ID was available. The value object implements __toString() for easy serialization:
$sessionId = $response->sessionId();

if ($sessionId !== null) {
    // Store for later resumption
    $cache->set('last_session', (string) $sessionId);
}
// @doctest id="5cea"

usage(): ?TokenUsage

Returns the token usage statistics, or null if the agent does not expose this data:
$usage = $response->usage();
if ($usage !== null) {
    echo "Tokens used: {$usage->total()}";
}
// @doctest id="d900"

cost(): ?float

Returns the estimated cost in USD, or null if the agent does not expose cost data:
$cost = $response->cost();
if ($cost !== null) {
    echo sprintf("This execution cost $%.4f", $cost);
}
// @doctest id="52c6"

parseFailures(): int

Returns the number of malformed JSON lines encountered during parsing:
echo "Parse failures: {$response->parseFailures()}";
// @doctest id="57e9"

parseFailureSamples(): array

Returns a list of sample malformed payload strings (truncated to 200 characters each) for debugging purposes:
if ($response->parseFailures() > 0) {
    echo "Skipped {$response->parseFailures()} malformed JSON lines:\n";
    foreach ($response->parseFailureSamples() as $sample) {
        echo "  - {$sample}\n";
    }
}
// @doctest id="e727"

Tool Calls

The toolCalls array contains ToolCall objects — a normalized representation of every tool invocation across all agent types. Each ToolCall has the following structure:

ToolCall Properties

PropertyTypeDescription
toolstringThe tool name (e.g., 'bash', 'file_change', 'web_search', 'tool_result')
inputarrayThe tool’s input parameters as an associative array
output?stringThe tool’s output, or null if not yet completed
isErrorboolWhether the tool call resulted in an error

ToolCall Methods

MethodReturn TypeDescription
callId()?AgentToolCallIdThe unique identifier for this tool call, or null
isCompleted()boolWhether the tool has produced output (output !== null)

Tool Name Normalization

Agent-Ctrl normalizes tool names across all agents so you can handle them consistently: Claude Code tool calls preserve their original tool names and include separate tool_result entries for tool outputs:
  • Tool invocations: original tool name (e.g., 'Read', 'Edit', 'Bash')
  • Tool results: 'tool_result' with ['tool_use_id' => '...'] in the input
Codex items are normalized as follows:
  • CommandExecution becomes tool: 'bash', input: ['command' => '...']
  • FileChange becomes tool: 'file_change', input: ['path' => '...', 'action' => '...']
  • McpToolCall becomes tool: <original tool name>, input: <arguments>
  • WebSearch becomes tool: 'web_search', input: ['query' => '...']
  • PlanUpdate becomes tool: 'plan_update'
  • Reasoning becomes tool: 'reasoning'
OpenCode tool calls preserve their original tool names from the ToolUseEvent.

Working with Tool Calls

$response = AgentCtrl::claudeCode()->execute('Refactor the UserService.');

foreach ($response->toolCalls as $toolCall) {
    echo "Tool: {$toolCall->tool}\n";
    echo "Input: " . json_encode($toolCall->input) . "\n";

    if ($toolCall->isCompleted()) {
        echo "Output: " . substr($toolCall->output, 0, 200) . "\n";
    }

    if ($toolCall->isError) {
        echo "  [ERROR]\n";
    }

    $callId = $toolCall->callId();
    if ($callId !== null) {
        echo "Call ID: {$callId}\n";
    }

    echo "---\n";
}
// @doctest id="f5fd"

Token Usage

The TokenUsage DTO provides detailed token statistics when the agent’s CLI exposes this data. It is defined at Cognesy\AgentCtrl\Dto\TokenUsage.

TokenUsage Properties

PropertyTypeDescription
inputintNumber of input tokens consumed
outputintNumber of output tokens produced
cacheRead?intTokens read from cache (when available)
cacheWrite?intTokens written to cache (when available)
reasoning?intTokens used for reasoning (when available)

TokenUsage Methods

MethodReturn TypeDescription
total()intSum of input and output tokens

Usage Data Availability by Agent

AgentToken UsageCost
Claude CodeNoNo
CodexYes (input, output, cacheRead)No
OpenCodeYes (input, output, cacheRead, cacheWrite, reasoning)Yes
PiYes (input, output, cacheRead, cacheWrite)Yes
GeminiYes (input, output, cacheRead)No
$response = AgentCtrl::openCode()
    ->withModel('anthropic/claude-sonnet-4-5')
    ->execute('Summarize this project.');

$usage = $response->usage();
if ($usage !== null) {
    echo "Input tokens: {$usage->input}\n";
    echo "Output tokens: {$usage->output}\n";
    echo "Total tokens: {$usage->total()}\n";

    if ($usage->cacheRead !== null) {
        echo "Cache read: {$usage->cacheRead}\n";
    }
    if ($usage->cacheWrite !== null) {
        echo "Cache write: {$usage->cacheWrite}\n";
    }
    if ($usage->reasoning !== null) {
        echo "Reasoning tokens: {$usage->reasoning}\n";
    }
}

$cost = $response->cost();
if ($cost !== null) {
    echo sprintf("Cost: $%.4f\n", $cost);
}
// @doctest id="0550"

Complete Example

use Cognesy\AgentCtrl\AgentCtrl;

$response = AgentCtrl::codex()
    ->withTimeout(300)
    ->inDirectory('/projects/my-app')
    ->execute('Review the authentication module and list any issues.');

// Check success
if (!$response->isSuccess()) {
    echo "Execution failed (exit code {$response->exitCode})\n";
    exit(1);
}

// Display text output
echo $response->text() . "\n";

// Display session info
$sessionId = $response->sessionId();
if ($sessionId !== null) {
    echo "Session: {$sessionId}\n";
}

// Display usage stats
$usage = $response->usage();
if ($usage !== null) {
    echo "Tokens: {$usage->total()} (in: {$usage->input}, out: {$usage->output})\n";
}

// Display cost
if ($response->cost() !== null) {
    echo sprintf("Cost: $%.4f\n", $response->cost());
}

// Summarize tool activity
echo "Tool calls: " . count($response->toolCalls) . "\n";
$toolSummary = [];
foreach ($response->toolCalls as $tc) {
    $toolSummary[$tc->tool] = ($toolSummary[$tc->tool] ?? 0) + 1;
}
foreach ($toolSummary as $tool => $count) {
    echo "  {$tool}: {$count}x\n";
}

// Check parse health
if ($response->parseFailures() > 0) {
    echo "Warning: {$response->parseFailures()} parse failures\n";
}
// @doctest id="01fe"