PHPackages                             filippo-toso/pi - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. filippo-toso/pi

ActiveLibrary

filippo-toso/pi
===============

Laravel package for running pi.dev coding agents via RPC

v1.0.0(yesterday)01↓100%MITPHPPHP ^8.2

Since Apr 6Pushed yesterdayCompare

[ Source](https://github.com/filippotoso/pi)[ Packagist](https://packagist.org/packages/filippo-toso/pi)[ RSS](/packages/filippo-toso-pi/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (3)Versions (2)Used By (0)

Pi Laravel
==========

[](#pi-laravel)

A Laravel package for running [pi](https://github.com/badlogic/pi-mono) coding agents via RPC. Spawn headless pi agent subprocesses from your Laravel app, send prompts, stream responses, execute tools, and manage sessions — all through a clean PHP API.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11 or 12
- `pi` CLI installed and accessible from `$PATH` (or configure a custom binary path)

Installation
------------

[](#installation)

```
composer require filippo-toso/pi
```

Publish the config file:

```
php artisan vendor:publish --tag=pi-config
```

Configuration
-------------

[](#configuration)

Set these in your `.env`:

```
PI_BINARY=pi              # Path to pi binary (default: "pi" from $PATH)
PI_PROVIDER=anthropic      # Default LLM provider
PI_MODEL=                  # Default model pattern (e.g. "anthropic/claude-sonnet-4-20250514")
PI_SESSION=false           # Persist sessions to disk
PI_SESSION_DIR=            # Custom session storage directory
PI_WORKING_DIR=            # Working directory for pi (default: Laravel base_path())
PI_TIMEOUT=                # Response timeout in seconds (null = no timeout)
```

Or edit `config/pi.php` directly.

Quick Start
-----------

[](#quick-start)

### Simple Ask (Blocking)

[](#simple-ask-blocking)

```
use FilippoToso\Pi\Facades\Pi;

$agent = Pi::create();
$answer = $agent->ask('What is the capital of France?');
echo $answer; // "The capital of France is Paris."
$agent->close();
```

### Streaming Responses

[](#streaming-responses)

```
$agent = Pi::create();
$agent->prompt('Write a haiku about Laravel');

foreach ($agent->stream() as $event) {
    if ($text = $event->getTextDelta()) {
        echo $text;
    }
}

$agent->close();
```

### Streaming with Callbacks

[](#streaming-with-callbacks)

```
$agent = Pi::create();
$agent->prompt('Refactor the User model');

$fullText = $agent->streamWith(
    onText: fn(string $delta) => echo $delta,
    onThinking: fn(string $delta) => logger()->debug("Thinking: $delta"),
    onToolStart: fn(array $data) => logger()->info("Tool started: {$data['toolName']}"),
    onToolEnd: fn(array $data) => logger()->info("Tool finished: {$data['toolName']}"),
    onEvent: fn($event) => null, // access every raw event
);

$agent->close();
```

### Custom Agent Options

[](#custom-agent-options)

```
$agent = Pi::create([
    'provider' => 'openai',
    'model' => 'o3',
    'working_dir' => '/path/to/project',
    'timeout' => 120,
    'session' => true,
    'session_dir' => storage_path('pi-sessions'),
    'env' => [
        'OPENAI_API_KEY' => config('services.openai.key'),
    ],
]);
```

API Reference
-------------

[](#api-reference)

### Creating Agents

[](#creating-agents)

```
// Via facade
$agent = Pi::create();
$agent = Pi::create(['provider' => 'anthropic', 'model' => 'claude-sonnet-4-20250514']);

// Via dependency injection
public function __construct(private PiAgentFactory $factory) {}
$agent = $this->factory->create();
```

### Prompting

[](#prompting)

```
// Send a prompt (non-blocking, use stream() to consume events)
$agent->prompt('Hello!');

// Send with images
$agent->prompt('What is in this image?', images: [
    ['type' => 'image', 'data' => base64_encode($imageData), 'mimeType' => 'image/png'],
]);

// Send while agent is already streaming
$agent->prompt('New instruction', streamingBehavior: 'steer');
$agent->prompt('After you finish...', streamingBehavior: 'followUp');

// Simple blocking ask
$text = $agent->ask('What is 2+2?');

// Steering (while agent is running)
$agent->steer('Focus on error handling instead');

// Follow-up (after agent finishes)
$agent->followUp('Now summarize what you did');

// Abort current operation
$agent->abort();
```

### Streaming Events

[](#streaming-events)

```
$agent->prompt('Do something');

foreach ($agent->stream() as $event) {
    match ($event->type) {
        'message_update' => handleDelta($event->getTextDelta()),
        'tool_execution_start' => handleToolStart($event->data),
        'tool_execution_end' => handleToolEnd($event->data),
        'agent_end' => handleComplete($event->getMessages()),
        default => null,
    };
}
```

### Model Management

[](#model-management)

```
$agent->setModel('anthropic', 'claude-sonnet-4-20250514');
$agent->cycleModel();
$models = $agent->getAvailableModels();
```

### Thinking Level

[](#thinking-level)

```
$agent->setThinkingLevel('high');   // off, minimal, low, medium, high, xhigh
$agent->cycleThinkingLevel();
```

### Bash Execution

[](#bash-execution)

```
// Execute a command (output included in next LLM prompt)
$result = $agent->bash('ls -la');
echo $result['output'];
echo $result['exitCode'];

// Abort a running command
$agent->abortBash();
```

### Session Management

[](#session-management)

```
$state = $agent->getState();
$stats = $agent->getSessionStats();
$messages = $agent->getMessages();

$agent->newSession();
$agent->switchSession('/path/to/session.jsonl');
$agent->setSessionName('my-feature-work');

// Forking
$forkable = $agent->getForkMessages();
$agent->fork($forkable[0]['entryId']);

// Export
$htmlPath = $agent->exportHtml('/tmp/session.html');

// Last assistant text
$lastText = $agent->getLastAssistantText();
```

### Compaction

[](#compaction)

```
$agent->compact();
$agent->compact(customInstructions: 'Focus on code changes');
$agent->setAutoCompaction(true);
```

### Retry

[](#retry)

```
$agent->setAutoRetry(true);
$agent->abortRetry();
```

### Queue Modes

[](#queue-modes)

```
// Steering: deliver all queued messages at once, or one per turn
$agent->setSteeringMode('all');           // or 'one-at-a-time'
$agent->setFollowUpMode('one-at-a-time'); // or 'all'
```

### Commands

[](#commands)

```
$commands = $agent->getCommands();
// Returns: [['name' => 'fix-tests', 'source' => 'prompt', ...], ...]

// Invoke a command via prompt
$agent->prompt('/fix-tests');
```

### Extension UI Protocol

[](#extension-ui-protocol)

```
// When streaming, handle extension UI requests
foreach ($agent->stream() as $event) {
    if ($event->isExtensionUiRequest()) {
        $method = $event->data['method'];
        $id = $event->data['id'];

        match ($method) {
            'select' => $agent->respondToExtensionUi($id, ['value' => 'Allow']),
            'confirm' => $agent->respondToExtensionUi($id, ['confirmed' => true]),
            'input' => $agent->respondToExtensionUi($id, ['value' => 'user input']),
            'notify' => null, // fire-and-forget, no response needed
            default => $agent->respondToExtensionUi($id, ['cancelled' => true]),
        };
    }
}
```

### Low-Level Access

[](#low-level-access)

```
// Send any raw command
$response = $agent->sendCommand(['type' => 'get_state']);

// Fire-and-forget (no response waiting)
$agent->sendRaw(['type' => 'abort']);

// Read a single event
$event = $agent->readEvent(timeout: 5.0);

// Access the underlying connection
$connection = $agent->getConnection();
$stderrOutput = $connection->readStderr();
```

### Lifecycle

[](#lifecycle)

```
$agent->isRunning(); // Check if the pi process is alive
$agent->close();     // Terminate the subprocess (also called on __destruct)
```

Event Types
-----------

[](#event-types)

Events received during streaming:

EventDescription`agent_start`Agent begins processing`agent_end`Agent completes (includes all generated messages)`turn_start`New turn begins`turn_end`Turn completes`message_start`Message begins`message_update`Streaming delta (text/thinking/tool call)`message_end`Message completes`tool_execution_start`Tool begins`tool_execution_update`Tool progress`tool_execution_end`Tool completes`queue_update`Pending queue changed`compaction_start`Compaction begins`compaction_end`Compaction completes`auto_retry_start`Retry begins`auto_retry_end`Retry completes`extension_error`Extension error`extension_ui_request`Extension UI interaction neededRpcEvent Helpers
----------------

[](#rpcevent-helpers)

```
$event->type;                   // Event type string
$event->isResponse();           // Is this a command response?
$event->isExtensionUiRequest(); // Is this an extension UI request?
$event->getTextDelta();         // Get text delta string (or null)
$event->getDelta();             // Get full assistantMessageEvent array
$event->getMessage();           // Get message from turn_end/message_start/message_end
$event->getMessages();          // Get messages from agent_end
$event->toArray();              // Get raw data array
```

Error Handling
--------------

[](#error-handling)

```
use FilippoToso\Pi\Exceptions\PiException;
use FilippoToso\Pi\Exceptions\PiCommandException;
use FilippoToso\Pi\Exceptions\PiConnectionException;

try {
    $agent = Pi::create();
    $answer = $agent->ask('Hello');
} catch (PiConnectionException $e) {
    // Process failed to start, pipe broken, timeout
} catch (PiCommandException $e) {
    // Command returned success: false
    echo $e->command; // The command type that failed
} catch (PiException $e) {
    // Base exception for all pi errors
}
```

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/55d89f2d44fb12225de2119994028ee69e36770bcf33c2b1ddf0d6672d28151b?d=identicon)[filippo.toso](/maintainers/filippo.toso)

---

Top Contributors

[![filippotoso](https://avatars.githubusercontent.com/u/26958813?v=4)](https://github.com/filippotoso "filippotoso (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/filippo-toso-pi/health.svg)

```
[![Health](https://phpackages.com/badges/filippo-toso-pi/health.svg)](https://phpackages.com/packages/filippo-toso-pi)
```

###  Alternatives

[fumeapp/modeltyper

Generate TypeScript interfaces from Laravel Models

196277.9k](/packages/fumeapp-modeltyper)[slowlyo/owl-admin

基于 laravel、amis 开发的后台框架~

61214.2k26](/packages/slowlyo-owl-admin)[highsolutions/eloquent-sequence

A Laravel package for easy creation and management sequence support for Eloquent models with elastic configuration.

121130.3k](/packages/highsolutions-eloquent-sequence)[glhd/linen

21135.6k](/packages/glhd-linen)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
