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

# Agent loop iterate

## Overview

`AgentLoop::iterate()` yields the agent state after each step, giving you fine-grained
control over the execution. This is useful for streaming progress, implementing custom
stop logic, or inspecting intermediate states between LLM calls.

You can combine `iterate()` with `AgentEventConsoleObserver` to get detailed event output
(tool calls, inference, continuation decisions) alongside your own step-by-step logic.
The logger hooks into the event system and prints as events fire during iteration.

Key concepts:

* `iterate()`: Returns an iterable that yields `AgentState` after each step
* `AgentEventConsoleObserver`: Attach via `wiretap()` for detailed execution logging
* Step-by-step inspection of tool calls, token usage, and agent decisions
* Early termination by breaking out of the loop

## Example

```php theme={null}
<?php
require 'examples/boot.php';

use Cognesy\Agents\AgentLoop;
use Cognesy\Agents\Capability\File\ReadFileTool;
use Cognesy\Agents\Data\AgentState;
use Cognesy\Agents\Events\Support\AgentEventConsoleObserver;
use Cognesy\Messages\Messages;

$workDir = dirname(__DIR__, 3);

// AgentEventConsoleObserver provides detailed event output during iteration
$logger = new AgentEventConsoleObserver(
    useColors: true,
    showTimestamps: true,
    showContinuation: true,
);

$loop = AgentLoop::default()
    ->withTool(ReadFileTool::inDirectory($workDir))
    ->wiretap($logger->wiretap());

$state = AgentState::empty()->withMessages(
    Messages::fromString('Read the composer.json file and tell me the project name.')
);

echo "=== Stepping through agent loop ===\n\n";

// iterate() yields state after each step; the logger prints events as they fire
$stepNum = 0;
foreach ($loop->iterate($state) as $stepState) {
    $stepNum++;
    // At yield time the step has been completed, so use lastStep()
    $step = $stepState->lastStep();

    $hasToolCalls = $step?->hasToolCalls() ?? false;
    $tokens = $stepState->usage()->total();
    $status = $stepState->status()->value;

    // Custom per-step output alongside the logger's event output
    $toolsLabel = $hasToolCalls ? 'yes' : 'no';
    echo "  >> Step {$stepNum}: status={$status}, has_tools={$toolsLabel}, tokens={$tokens}\n\n";

    // Early termination example: stop after 5 steps regardless
    if ($stepNum >= 5) {
        echo "\n[Breaking early after {$stepNum} steps]\n";
        break;
    }
}

echo "\n=== Final Result ===\n";
$response = $stepState->finalResponse()->toString() ?: 'No response';
echo "Answer: {$response}\n";
echo "Total steps: {$stepState->stepCount()}\n";
echo "Total tokens: {$stepState->usage()->total()}\n";

if ($stepState->status()->value !== 'completed') {
    echo "Skipping assertions because execution status is {$stepState->status()->value}.\n";
    exit(1);
}

// Assertions
assert($stepNum >= 1, 'Expected at least 1 step from iterate()');
assert(!empty($stepState->currentResponse()->toString()), 'Expected non-empty response');
assert($stepState->usage()->total() > 0, 'Expected token usage > 0');
?>
```
