Skip to main content

Overview

This example demonstrates real-time streaming output from the OpenCode CLI. Events are parsed and displayed as they arrive, providing live feedback on the agent’s progress.

Example

<?php
require 'examples/boot.php';

use Cognesy\Auxiliary\Agents\OpenCode\Application\Builder\OpenCodeCommandBuilder;
use Cognesy\Auxiliary\Agents\OpenCode\Application\Dto\OpenCodeRequest;
use Cognesy\Auxiliary\Agents\OpenCode\Domain\Dto\StreamEvent\StreamEvent;
use Cognesy\Auxiliary\Agents\OpenCode\Domain\Dto\StreamEvent\StepStartEvent;
use Cognesy\Auxiliary\Agents\OpenCode\Domain\Dto\StreamEvent\TextEvent;
use Cognesy\Auxiliary\Agents\OpenCode\Domain\Dto\StreamEvent\ToolUseEvent;
use Cognesy\Auxiliary\Agents\OpenCode\Domain\Dto\StreamEvent\StepFinishEvent;
use Cognesy\Auxiliary\Agents\OpenCode\Domain\Enum\OutputFormat;
use Cognesy\Auxiliary\Agents\Common\Execution\SandboxCommandExecutor;

// Create a request - using JSON format for streaming events
$request = new OpenCodeRequest(
    prompt: 'Read the first 5 lines of composer.json in this directory and describe what you see.',
    outputFormat: OutputFormat::Json,
);

print("Streaming OpenCode CLI output...\n");
print("Prompt: {$request->prompt()}\n\n");

// Build command
$builder = new OpenCodeCommandBuilder();
$spec = $builder->buildRun($request);

// Execute with streaming callback
$executor = SandboxCommandExecutor::forOpenCode();

$execResult = $executor->executeStreaming($spec, function (string $type, string $chunk) {
    if ($type !== 'out') {
        return;
    }

    // Each line is a JSON object
    $lines = preg_split('/\r\n|\r|\n/', $chunk);
    foreach ($lines as $line) {
        $trimmed = trim($line);
        if ($trimmed === '') {
            continue;
        }

        $decoded = json_decode($trimmed, true);
        if (!is_array($decoded)) {
            continue;
        }

        $event = StreamEvent::fromArray($decoded);

        // Handle different event types
        match (true) {
            $event instanceof StepStartEvent => print("[STEP] Started (session: {$event->sessionId})\n"),
            $event instanceof TextEvent => handleText($event),
            $event instanceof ToolUseEvent => handleToolUse($event),
            $event instanceof StepFinishEvent => handleStepFinish($event),
            default => print("[{$event->type()}]\n"),
        };
    }
});

function handleText(TextEvent $event): void {
    $preview = substr($event->text, 0, 100);
    $suffix = strlen($event->text) > 100 ? '...' : '';
    print("[TEXT] {$preview}{$suffix}\n");
}

function handleToolUse(ToolUseEvent $event): void {
    print("[TOOL] {$event->tool} ({$event->status})\n");
    if ($event->isCompleted()) {
        $preview = substr($event->output, 0, 80);
        $suffix = strlen($event->output) > 80 ? '...' : '';
        print("       Output: {$preview}{$suffix}\n");
    }
}

function handleStepFinish(StepFinishEvent $event): void {
    print("[STEP] Finished (reason: {$event->reason})");
    if ($event->tokens) {
        print(" - Tokens: {$event->tokens->input} in / {$event->tokens->output} out");
    }
    if ($event->cost > 0) {
        print(" - Cost: \${$event->cost}");
    }
    print("\n");
}

print("\nFinal exit code: {$execResult->exitCode()}\n");

assert($execResult->exitCode() === 0, 'Command should execute successfully');
?>