> ## 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.

# 6 response object

## 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`:

```php theme={null}
echo "Produced by: {$response->agentType->value}"; // e.g., "claude-code"
// @doctest id="c5c2"
```

### `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:

```php theme={null}
echo $response->text;
// or
echo $response->text();
// @doctest id="3b89"
```

### `exitCode: int`

The process exit code. A value of `0` indicates success. Non-zero values indicate errors, timeouts, or other failures:

```php theme={null}
if ($response->exitCode !== 0) {
    echo "Process failed with exit code: {$response->exitCode}";
}
// @doctest id="18ef"
```

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):

```php theme={null}
$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="e6b7"
```

### `cost: ?float`

The estimated cost in USD, when available. Currently, OpenCode and Pi expose cost data:

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

### `toolCalls: array`

An array of `ToolCall` objects representing every tool invocation made during the execution. See the [Tool Calls](#tool-calls) section below for details:

```php theme={null}
echo "Tool calls made: " . count($response->toolCalls);
// @doctest id="75b5"
```

### `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:

```php theme={null}
// Access the raw Codex response for agent-specific data
$codexResponse = $response->rawResponse;
// @doctest id="5710"
```

### `parseFailures: int`

The number of malformed JSON lines that were skipped during response parsing:

```php theme={null}
if ($response->parseFailures > 0) {
    echo "Warning: {$response->parseFailures} JSON parse failures";
}
// @doctest id="369f"
```

## Response Methods

### `isSuccess(): bool`

Returns `true` if the exit code is `0`. This is the recommended way to check whether the execution succeeded:

```php theme={null}
$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="2ebc"
```

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:

```php theme={null}
echo $response->text();
// @doctest id="eb9f"
```

### `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:

```php theme={null}
$sessionId = $response->sessionId();

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

### `usage(): ?TokenUsage`

Returns the token usage statistics, or `null` if the agent does not expose this data:

```php theme={null}
$usage = $response->usage();
if ($usage !== null) {
    echo "Tokens used: {$usage->total()}";
}
// @doctest id="a6a1"
```

### `cost(): ?float`

Returns the estimated cost in USD, or `null` if the agent does not expose cost data:

```php theme={null}
$cost = $response->cost();
if ($cost !== null) {
    echo sprintf("This execution cost $%.4f", $cost);
}
// @doctest id="fb88"
```

### `parseFailures(): int`

Returns the number of malformed JSON lines encountered during parsing:

```php theme={null}
echo "Parse failures: {$response->parseFailures()}";
// @doctest id="c05a"
```

### `parseFailureSamples(): array`

Returns a list of sample malformed payload strings (truncated to 200 characters each) for debugging purposes:

```php theme={null}
if ($response->parseFailures() > 0) {
    echo "Skipped {$response->parseFailures()} malformed JSON lines:\n";
    foreach ($response->parseFailureSamples() as $sample) {
        echo "  - {$sample}\n";
    }
}
// @doctest id="74fb"
```

## 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

| Property  | Type      | Description                                                                      |
| --------- | --------- | -------------------------------------------------------------------------------- |
| `tool`    | `string`  | The tool name (e.g., `'bash'`, `'file_change'`, `'web_search'`, `'tool_result'`) |
| `input`   | `array`   | The tool's input parameters as an associative array                              |
| `output`  | `?string` | The tool's output, or `null` if not yet completed                                |
| `isError` | `bool`    | Whether the tool call resulted in an error                                       |

### ToolCall Methods

| Method          | Return Type        | Description                                              |
| --------------- | ------------------ | -------------------------------------------------------- |
| `callId()`      | `?AgentToolCallId` | The unique identifier for this tool call, or `null`      |
| `isCompleted()` | `bool`             | Whether 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

```php theme={null}
$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="b06a"
```

## 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

| Property     | Type   | Description                                |
| ------------ | ------ | ------------------------------------------ |
| `input`      | `int`  | Number of input tokens consumed            |
| `output`     | `int`  | Number of output tokens produced           |
| `cacheRead`  | `?int` | Tokens read from cache (when available)    |
| `cacheWrite` | `?int` | Tokens written to cache (when available)   |
| `reasoning`  | `?int` | Tokens used for reasoning (when available) |

### TokenUsage Methods

| Method    | Return Type | Description                        |
| --------- | ----------- | ---------------------------------- |
| `total()` | `int`       | Sum of `input` and `output` tokens |

### Usage Data Availability by Agent

| Agent       | Token Usage                                           | Cost |
| ----------- | ----------------------------------------------------- | ---- |
| Claude Code | No                                                    | No   |
| Codex       | Yes (input, output, cacheRead)                        | No   |
| OpenCode    | Yes (input, output, cacheRead, cacheWrite, reasoning) | Yes  |
| Pi          | Yes (input, output, cacheRead, cacheWrite)            | Yes  |
| Gemini      | Yes (input, output, cacheRead)                        | No   |

```php theme={null}
$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="1189"
```

## Complete Example

```php theme={null}
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="5054"
```
