Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
laravel-copilot-sdk-development
laravel-copilot-sdk-development
revolution/laravel-copilot-sdk
claude-code
laravel
copilot-cli
copilot-sdk

Develops features using the Laravel Copilot SDK (revolution/laravel-copilot-sdk) to interact with GitHub Copilot CLI programmatically. Use when building, editing, debugging, or testing any Copilot CLI integration in Laravel — including sending prompts, managing sessions, defining custom tools, streaming responses, testing with fakes, using RPC methods, configuring MCP servers, or working with TCP/stdio transport modes.

Install
php artisan boost:add-skill revolution/laravel-copilot-sdk

Save this content to: .claude/skills/laravel-copilot-sdk-development/SKILL.md

# Laravel Copilot SDK (`revolution/laravel-copilot-sdk`)

Laravel package for the [GitHub Copilot CLI SDK](https://github.com/github/copilot-sdk). Interact with GitHub Copilot CLI programmatically from Laravel applications.

- Namespace: `Revolution\Copilot`
- Facade: `Revolution\Copilot\Facades\Copilot`

## Architecture

The SDK communicates with `copilot --headless` (the CLI running as a server) via JSON-RPC 2.0 over stdio or TCP.

```
Your Application → SDK (Copilot Facade) → JSON-RPC → Copilot CLI (server mode)
```

## Installation

```bash
composer require revolution/laravel-copilot-sdk
```

Optional `.env` configuration:
```dotenv
COPILOT_CLI_PATH=copilot
COPILOT_MODEL=claude-sonnet-4.6
COPILOT_TIMEOUT=60
COPILOT_URL=127.0.0.1:12345       # Set this to enable TCP mode
COPILOT_PERMISSION_APPROVE="deny-all"   # Do not auto-approve permission requests when end users prompt is available
```

---

## Basic Usage

### Run a single prompt

```php
use Revolution\Copilot\Facades\Copilot;

$response = Copilot::run(prompt: 'Tell me something about Laravel.');
$response->content(); // string
```

### Multiple prompts in one session

```php
use Revolution\Copilot\Contracts\CopilotSession;
use Revolution\Copilot\Facades\Copilot;

$content = Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(prompt: 'Tell me something about PHP.');
    dump($response->content());

    // Follow-up in the same session — context is maintained
    $response = $session->sendAndWait(prompt: 'Now tell me about Laravel.');
    return $response->content();
});
```

### `copilot()` helper function

```php
use Revolution\Copilot\Contracts\CopilotSession;
use function Revolution\Copilot\copilot;

// Same as Copilot::run()
$response = copilot('Tell me something about Laravel.');

// Same as Copilot::start()
copilot(function (CopilotSession $session) {
    $session->sendAndWait(prompt: 'Hello');
});

// Same as Facade
copilot()->client()->ping();
```

---

## SessionConfig

Pass configuration via `SessionConfig` class or array to `Copilot::run()` or `Copilot::start()`.

```php
use Revolution\Copilot\Types\SessionConfig;
use Revolution\Copilot\Types\SystemMessageConfig;
use Revolution\Copilot\Types\InfiniteSessionConfig;
use Revolution\Copilot\Enums\ReasoningEffort;

$config = new SessionConfig(
    model: 'auto',
    reasoningEffort: ReasoningEffort::HIGH,
    systemMessage: new SystemMessageConfig(
        content: 'You are a helpful assistant for Laravel developers.',
    ),
    tools: [...],
    streaming: true,
    availableTools: ['read_file', 'write_file'],
    excludedTools: ['shell'],
    mcpServers: [...],
    customAgents: [...],
    skillDirectories: [],
    disabledSkills: [],
    infiniteSessions: new InfiniteSessionConfig(enabled: true),
);

$response = Copilot::run('Hello', config: $config);
```

For simple cases, use an array:
```php
Copilot::run('Hello', config: ['model' => 'auto']);
```

---

## Event Handling with `on()`

`sendAndWait()` returns only the final assistant message. Use `on()` to receive intermediate events.

```php
use Revolution\Copilot\Enums\SessionEventType;
use Revolution\Copilot\Types\SessionEvent;

Copilot::start(function (CopilotSession $session) {
    // Subscribe to all events
    $session->on(function (SessionEvent $event): void {
        if ($event->isAssistantMessage()) {
            dump($event->content());
        } elseif ($event->failed()) {
            dump($event->errorMessage());
        }
    });

// Or subscribe to a specific event type
    $session->on(SessionEventType::ASSISTANT_MESSAGE, function (SessionEvent $event): void {
        dump($event->content());
    });

    $response = $session->sendAndWait(prompt: 'Hello');
});
```

---

## Streaming

Enable streaming with `streaming: true` in SessionConfig to receive `ASSISTANT_MESSAGE_DELTA` events.

```php
use Revolution\Copilot\Types\SessionConfig;

$config = new SessionConfig(streaming: true);

Copilot::start(function (CopilotSession $session) {
    $session->on(function (SessionEvent $event): void {
        if ($event->isAssistantMessageDelta()) {
            echo $event->deltaContent(); // Incremental text chunks
        } elseif ($event->isAssistantMessage()) {
            // Full message after all deltas
        } elseif ($event->isAssistantReasoningDelta()) {
            echo $event->deltaContent(); // Reasoning chunks
        }
    });

    $session->sendAndWait(prompt: 'Tell me about Laravel.');
}, config: $config);
```

### Streaming with SSE (Server-Sent Events)

```php
Route::get('/copilot/sse', function () {
    return response()->eventStream(function () {
        yield from Copilot::stream(function (CopilotSession $session) {
            foreach ($session->sendAndStream('Tell me about Laravel.') as $event) {
                if ($event->isAssistantMessageDelta()) {
                    yield $event->deltaContent();
                }
            }
        }, config: new SessionConfig(streaming: true));
    });
});
```

### Streaming with Livewire `wire:stream`

```php
$session->on(SessionEventType::ASSISTANT_MESSAGE_DELTA, function (SessionEvent $event): void {
    $this->stream(content: $event->deltaContent(), to: 'answer');
});
```

---

## Custom Tools

Define tools that Copilot can call during a conversation.

```php
use Illuminate\JsonSchema\JsonSchema;
use Revolution\Copilot\Types\SessionConfig;
use Revolution\Copilot\Types\Tool;
use Revolution\Copilot\Types\ToolResultObject;

$parameters = JsonSchema::object([
    'topic' => JsonSchema::string()
        ->description('Topic to look up')
        ->required(),
])->toArray();

$config = new SessionConfig(
    tools: [
        Tool::define(
            name: 'lookup_fact',
            description: 'Returns a fun fact about a given topic.',
            parameters: $parameters,
            handler: function (array $params, array $invocation): ToolResultObject {
                $topic = $params['topic'] ?? '';

return new ToolResultObject(
                    textResultForLlm: "Fact about {$topic}",
                    resultType: 'success',
                    sessionLog: "lookup_fact: {$topic}",
                    toolTelemetry: [],
                );
            },
        ),
    ],
);

Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(
        prompt: 'Use lookup_fact to tell me about Laravel.'
    );
    dump($response->content());
}, config: $config);
```

---

## File Attachments

```php
use Revolution\Copilot\Support\Attachment;

$attachments = [
    Attachment::file(path: '/path/to/file.php', displayName: 'My File'),
    Attachment::directory(path: '/path/to/dir/', displayName: 'dir'),
];

$response = Copilot::run(prompt: 'Review this code', attachments: $attachments);
```

---

## MCP Servers

Configure MCP servers in SessionConfig:

```php
$config = new SessionConfig(
    mcpServers: [
        'my-mcp' => [
            'type' => 'local',       // 'local' is required for local MCP
            'command' => 'php',
            'args' => ['artisan', 'boost:mcp'],
            'tools' => ['*'],        // Required — won't be recognized without this
        ],
    ],
);
```

---

## Session Resume

### With fixed session ID

```php
$config = new SessionConfig(sessionId: 'user-123-conversation');

Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(prompt: 'Continue our conversation.');
}, config: $config);
```

### Resume with `resume` parameter

```php
use Revolution\Copilot\Types\ResumeSessionConfig;

Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(prompt: 'Hello again.');
}, config: new ResumeSessionConfig(), resume: 'user-123-conversation');
```

### List existing sessions

```php
$sessions = Copilot::client()->listSessions();
```

---

## RPC Methods

### ServerRpc (Client-scoped)

```php
Copilot::client()->rpc()->ping();
Copilot::client()->rpc()->models()->list();       // ModelList
Copilot::client()->rpc()->tools()->list();
Copilot::client()->rpc()->account()->getQuota();
```

### SessionRpc (Session-scoped)

```php
use Revolution\Copilot\Types\Rpc\ModeSetRequest;
use Revolution\Copilot\Types\Rpc\ModelSwitchToRequest;
use Revolution\Copilot\Types\Rpc\FleetStartRequest;

Copilot::start(function (CopilotSession $session) {
    // Mode: plan, autopilot, etc.
    $session->rpc()->mode()->set(new ModeSetRequest(mode: 'plan'));
    $session->sendAndWait(prompt: 'Create a plan for...');
    $plan = $session->rpc()->plan()->read();

    $session->rpc()->mode()->set(new ModeSetRequest(mode: 'autopilot'));
    $session->sendAndWait(prompt: 'Execute the plan');

    // Model
    $session->rpc()->model()->getCurrent();
    $session->rpc()->model()->switchTo(new ModelSwitchToRequest(modelId: 'gpt-5.2'));

    // Workspace
    $session->rpc()->workspace()->listFiles();

// Fleet (sub-agents)
    $session->rpc()->fleet()->start(new FleetStartRequest(prompt: '...'));

    // Agent
    $session->rpc()->agent()->list();
    $session->rpc()->agent()->getCurrent();

    // History (compaction)
    $session->rpc()->history()->compact();
});
```

RPC params also accept arrays: `$session->rpc()->mode()->set(['mode' => 'plan']);`

---

## Permission Requests

By default (`config/copilot.php` → `permission_approve: 'deny-all'`), all permission requests are denied.
In web-facing applications or when prompts can be influenced by end users, you should **not** rely on blanket auto-approval. Instead, inspect each request and gate high-risk operations behind your own authorization logic.

Custom handler example:

```php
use Revolution\Copilot\Support\PermissionDecision;

$config = new SessionConfig(
    onPermissionRequest: function (array $request, array $invocation) {
        // $request['kind']: "shell" | "write" | "mcp" | "read" | "url" | "custom-tool"
        switch ($request['kind'] ?? null) {
            case 'shell':
            case 'write':
                // High-risk operations: deny by default.
                return PermissionDecision::reject();

            case 'read':
            case 'url':
            case 'mcp':
            case 'custom-tool':
            default:
                // Lower-risk operations: approve once (adjust to your own policies).
                return PermissionDecision::approveOnce();
        }
    },
);
```

---

## TCP Mode

Connect to an already-running Copilot CLI server instead of spawning a new process per request.

```bash
copilot --headless --port 12345
```

```dotenv
COPILOT_URL=127.0.0.1:12345
```

Runtime switching:
```php
$response = Copilot::useTcp(url: 'tcp://127.0.0.1:12345')->run(prompt: 'Hello');
$response = Copilot::useStdio()->run(prompt: 'Hello');
```

---

## GitHub Token Switching (stdio mode only)

Use per-user GitHub tokens at runtime:
```php
$config = array_merge(config('copilot'), [
    'github_token' => $user->github_token,
]);

$response = Copilot::useStdio($config)->run(prompt: '...');
Copilot::stop(); // Dispose client with user token
```

---

## Concurrency

```php
use Illuminate\Support\Facades\Concurrency;

[$gpt, $claude] = Concurrency::run([
    fn () => Copilot::run('Hello', config: ['model' => 'gpt-5.2'])->content(),
    fn () => Copilot::run('Hello', config: ['model' => 'claude-sonnet-4.6'])->content(),
]);
```

---

## Laravel AI SDK Integration (experimental)

```php
// config/ai.php
'default' => 'copilot',
'providers' => [
    'copilot' => ['driver' => 'copilot', 'key' => ''],
],

// Usage
use function Laravel\Ai\agent;

$response = agent(instructions: 'You are an expert.')->prompt('Tell me about Laravel');
echo $response->text;
```

---

## Testing

### `Copilot::fake()`

```php
use Revolution\Copilot\Facades\Copilot;

// Simple fake — always returns '2'
Copilot::fake('2');
$response = Copilot::run(prompt: '1 + 1');
expect($response->content())->toBe('2');
```

### Sequence for multiple calls

```php
Copilot::fake([
    '*' => Copilot::sequence()
            ->push(Copilot::response('2'))
            ->push(Copilot::response('4')),
]);

Copilot::start(function (CopilotSession $session) use (&$r1, &$r2) {
    $r1 = $session->sendAndWait(prompt: '1 + 1');
    $r2 = $session->sendAndWait(prompt: '2 + 2');
});

expect($r1->content())->toBe('2');
expect($r2->content())->toBe('4');
```

### Assertions

```php
Copilot::assertPrompt('1 + *');      // Prompt was called (wildcard match)
Copilot::assertNotPrompt('1 + *');   // Prompt was NOT called
Copilot::assertPromptCount(3);       // Exactly 3 prompts sent
Copilot::assertNothingSent();        // No prompts sent
```

### Prevent stray requests

```php
Copilot::preventStrayRequests();              // Throw on any unfaked request
Copilot::preventStrayRequests(allow: ['ping']); // Allow only 'ping'
Copilot::preventStrayRequests(false);          // Disable prevention
```

---

## Laravel Events

The SDK dispatches Laravel events for logging and debugging:

- `Client\ClientStarted`, `Client\PingPong`, `Client\ToolCall`
- `Session\CreateSession`, `Session\MessageSend`, `Session\MessageSendAndWait`, `Session\ResumeSession`, `Session\SessionEventReceived`
- `JsonRpc\MessageSending`, `MessageReceived`, `ResponseReceived`
- `Process\ProcessStarted`

---

## Key Classes Reference

| Class / Interface           | Purpose                                                                                                                                  |
|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| `Copilot` (Facade)          | Main entry point: `run()`, `start()`, `stream()`, `fake()`                                                                               |
| `CopilotSession` (Contract) | Session interface: `send()`, `sendAndWait()`, `on()`, `rpc()`, `getMessages()`, `destroy()`                                              |
| `CopilotClient` (Contract)  | Client interface: `start()`, `stop()`, `createSession()`, `resumeSession()`, `ping()`, `listModels()`, `listSessions()`, `rpc()`         |
| `SessionConfig`             | Session creation options (model, tools, streaming, MCP, etc.)                                                                            |
| `ResumeSessionConfig`       | Options for resuming a session                                                                                                           |
| `SessionEvent`              | Event object: `content()`, `type()`, `deltaContent()`, `isAssistantMessage()`, `isAssistantMessageDelta()`, `failed()`, `errorMessage()` |
| `Tool`                      | `Tool::define(name, description, parameters, handler)`                                                                                   |
| `ToolResultObject`          | Tool handler return type                                                                                                                 |
| `Attachment`                | `Attachment::file()`, `Attachment::directory()`, `Attachment::selection()`                                                               |
| `ServerRpc` / `SessionRpc`  | Typed RPC layer                                                                                                                          |
| `PermissionHandler`         | `PermissionHandler::approveAll()`, `PermissionHandler::approveSafety()`, `PermissionHandler::denyAll()`                                  |
| `PermissionDecision`        | `approveOnce()`, `approveForSession()`, `approveForLocation()`, `reject()`, `userNotAvailable()`, `noResult()`, `select()`               |

Laravel Copilot SDK (revolution/laravel-copilot-sdk)

Laravel package for the GitHub Copilot CLI SDK. Interact with GitHub Copilot CLI programmatically from Laravel applications.

  • Namespace: Revolution\Copilot
  • Facade: Revolution\Copilot\Facades\Copilot

Architecture

The SDK communicates with copilot --headless (the CLI running as a server) via JSON-RPC 2.0 over stdio or TCP.

Your Application → SDK (Copilot Facade) → JSON-RPC → Copilot CLI (server mode)

Installation

composer require revolution/laravel-copilot-sdk

Optional .env configuration:

COPILOT_CLI_PATH=copilot
COPILOT_MODEL=claude-sonnet-4.6
COPILOT_TIMEOUT=60
COPILOT_URL=127.0.0.1:12345       # Set this to enable TCP mode
COPILOT_PERMISSION_APPROVE="deny-all"   # Do not auto-approve permission requests when end users prompt is available

Basic Usage

Run a single prompt

use Revolution\Copilot\Facades\Copilot;

$response = Copilot::run(prompt: 'Tell me something about Laravel.');
$response->content(); // string

Multiple prompts in one session

use Revolution\Copilot\Contracts\CopilotSession;
use Revolution\Copilot\Facades\Copilot;

$content = Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(prompt: 'Tell me something about PHP.');
    dump($response->content());

    // Follow-up in the same session — context is maintained
    $response = $session->sendAndWait(prompt: 'Now tell me about Laravel.');
    return $response->content();
});

copilot() helper function

use Revolution\Copilot\Contracts\CopilotSession;
use function Revolution\Copilot\copilot;

// Same as Copilot::run()
$response = copilot('Tell me something about Laravel.');

// Same as Copilot::start()
copilot(function (CopilotSession $session) {
    $session->sendAndWait(prompt: 'Hello');
});

// Same as Facade
copilot()->client()->ping();

SessionConfig

Pass configuration via SessionConfig class or array to Copilot::run() or Copilot::start().

use Revolution\Copilot\Types\SessionConfig;
use Revolution\Copilot\Types\SystemMessageConfig;
use Revolution\Copilot\Types\InfiniteSessionConfig;
use Revolution\Copilot\Enums\ReasoningEffort;

$config = new SessionConfig(
    model: 'auto',
    reasoningEffort: ReasoningEffort::HIGH,
    systemMessage: new SystemMessageConfig(
        content: 'You are a helpful assistant for Laravel developers.',
    ),
    tools: [...],
    streaming: true,
    availableTools: ['read_file', 'write_file'],
    excludedTools: ['shell'],
    mcpServers: [...],
    customAgents: [...],
    skillDirectories: [],
    disabledSkills: [],
    infiniteSessions: new InfiniteSessionConfig(enabled: true),
);

$response = Copilot::run('Hello', config: $config);

For simple cases, use an array:

Copilot::run('Hello', config: ['model' => 'auto']);

Event Handling with on()

sendAndWait() returns only the final assistant message. Use on() to receive intermediate events.

use Revolution\Copilot\Enums\SessionEventType;
use Revolution\Copilot\Types\SessionEvent;

Copilot::start(function (CopilotSession $session) {
    // Subscribe to all events
    $session->on(function (SessionEvent $event): void {
        if ($event->isAssistantMessage()) {
            dump($event->content());
        } elseif ($event->failed()) {
            dump($event->errorMessage());
        }
    });

// Or subscribe to a specific event type
    $session->on(SessionEventType::ASSISTANT_MESSAGE, function (SessionEvent $event): void {
        dump($event->content());
    });

    $response = $session->sendAndWait(prompt: 'Hello');
});

Streaming

Enable streaming with streaming: true in SessionConfig to receive ASSISTANT_MESSAGE_DELTA events.

use Revolution\Copilot\Types\SessionConfig;

$config = new SessionConfig(streaming: true);

Copilot::start(function (CopilotSession $session) {
    $session->on(function (SessionEvent $event): void {
        if ($event->isAssistantMessageDelta()) {
            echo $event->deltaContent(); // Incremental text chunks
        } elseif ($event->isAssistantMessage()) {
            // Full message after all deltas
        } elseif ($event->isAssistantReasoningDelta()) {
            echo $event->deltaContent(); // Reasoning chunks
        }
    });

    $session->sendAndWait(prompt: 'Tell me about Laravel.');
}, config: $config);

Streaming with SSE (Server-Sent Events)

Route::get('/copilot/sse', function () {
    return response()->eventStream(function () {
        yield from Copilot::stream(function (CopilotSession $session) {
            foreach ($session->sendAndStream('Tell me about Laravel.') as $event) {
                if ($event->isAssistantMessageDelta()) {
                    yield $event->deltaContent();
                }
            }
        }, config: new SessionConfig(streaming: true));
    });
});

Streaming with Livewire wire:stream

$session->on(SessionEventType::ASSISTANT_MESSAGE_DELTA, function (SessionEvent $event): void {
    $this->stream(content: $event->deltaContent(), to: 'answer');
});

Custom Tools

Define tools that Copilot can call during a conversation.

use Illuminate\JsonSchema\JsonSchema;
use Revolution\Copilot\Types\SessionConfig;
use Revolution\Copilot\Types\Tool;
use Revolution\Copilot\Types\ToolResultObject;

$parameters = JsonSchema::object([
    'topic' => JsonSchema::string()
        ->description('Topic to look up')
        ->required(),
])->toArray();

$config = new SessionConfig(
    tools: [
        Tool::define(
            name: 'lookup_fact',
            description: 'Returns a fun fact about a given topic.',
            parameters: $parameters,
            handler: function (array $params, array $invocation): ToolResultObject {
                $topic = $params['topic'] ?? '';

return new ToolResultObject(
                    textResultForLlm: "Fact about {$topic}",
                    resultType: 'success',
                    sessionLog: "lookup_fact: {$topic}",
                    toolTelemetry: [],
                );
            },
        ),
    ],
);

Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(
        prompt: 'Use lookup_fact to tell me about Laravel.'
    );
    dump($response->content());
}, config: $config);

File Attachments

use Revolution\Copilot\Support\Attachment;

$attachments = [
    Attachment::file(path: '/path/to/file.php', displayName: 'My File'),
    Attachment::directory(path: '/path/to/dir/', displayName: 'dir'),
];

$response = Copilot::run(prompt: 'Review this code', attachments: $attachments);

MCP Servers

Configure MCP servers in SessionConfig:

$config = new SessionConfig(
    mcpServers: [
        'my-mcp' => [
            'type' => 'local',       // 'local' is required for local MCP
            'command' => 'php',
            'args' => ['artisan', 'boost:mcp'],
            'tools' => ['*'],        // Required — won't be recognized without this
        ],
    ],
);

Session Resume

With fixed session ID

$config = new SessionConfig(sessionId: 'user-123-conversation');

Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(prompt: 'Continue our conversation.');
}, config: $config);

Resume with resume parameter

use Revolution\Copilot\Types\ResumeSessionConfig;

Copilot::start(function (CopilotSession $session) {
    $response = $session->sendAndWait(prompt: 'Hello again.');
}, config: new ResumeSessionConfig(), resume: 'user-123-conversation');

List existing sessions

$sessions = Copilot::client()->listSessions();

RPC Methods

ServerRpc (Client-scoped)

Copilot::client()->rpc()->ping();
Copilot::client()->rpc()->models()->list();       // ModelList
Copilot::client()->rpc()->tools()->list();
Copilot::client()->rpc()->account()->getQuota();

SessionRpc (Session-scoped)

use Revolution\Copilot\Types\Rpc\ModeSetRequest;
use Revolution\Copilot\Types\Rpc\ModelSwitchToRequest;
use Revolution\Copilot\Types\Rpc\FleetStartRequest;

Copilot::start(function (CopilotSession $session) {
    // Mode: plan, autopilot, etc.
    $session->rpc()->mode()->set(new ModeSetRequest(mode: 'plan'));
    $session->sendAndWait(prompt: 'Create a plan for...');
    $plan = $session->rpc()->plan()->read();

    $session->rpc()->mode()->set(new ModeSetRequest(mode: 'autopilot'));
    $session->sendAndWait(prompt: 'Execute the plan');

    // Model
    $session->rpc()->model()->getCurrent();
    $session->rpc()->model()->switchTo(new ModelSwitchToRequest(modelId: 'gpt-5.2'));

    // Workspace
    $session->rpc()->workspace()->listFiles();

// Fleet (sub-agents)
    $session->rpc()->fleet()->start(new FleetStartRequest(prompt: '...'));

    // Agent
    $session->rpc()->agent()->list();
    $session->rpc()->agent()->getCurrent();

    // History (compaction)
    $session->rpc()->history()->compact();
});

RPC params also accept arrays: $session->rpc()->mode()->set(['mode' => 'plan']);


Permission Requests

By default (config/copilot.php → permission_approve: 'deny-all'), all permission requests are denied. In web-facing applications or when prompts can be influenced by end users, you should not rely on blanket auto-approval. Instead, inspect each request and gate high-risk operations behind your own authorization logic.

Custom handler example:

use Revolution\Copilot\Support\PermissionDecision;

$config = new SessionConfig(
    onPermissionRequest: function (array $request, array $invocation) {
        // $request['kind']: "shell" | "write" | "mcp" | "read" | "url" | "custom-tool"
        switch ($request['kind'] ?? null) {
            case 'shell':
            case 'write':
                // High-risk operations: deny by default.
                return PermissionDecision::reject();

            case 'read':
            case 'url':
            case 'mcp':
            case 'custom-tool':
            default:
                // Lower-risk operations: approve once (adjust to your own policies).
                return PermissionDecision::approveOnce();
        }
    },
);

TCP Mode

Connect to an already-running Copilot CLI server instead of spawning a new process per request.

copilot --headless --port 12345
COPILOT_URL=127.0.0.1:12345

Runtime switching:

$response = Copilot::useTcp(url: 'tcp://127.0.0.1:12345')->run(prompt: 'Hello');
$response = Copilot::useStdio()->run(prompt: 'Hello');

GitHub Token Switching (stdio mode only)

Use per-user GitHub tokens at runtime:

$config = array_merge(config('copilot'), [
    'github_token' => $user->github_token,
]);

$response = Copilot::useStdio($config)->run(prompt: '...');
Copilot::stop(); // Dispose client with user token

Concurrency

use Illuminate\Support\Facades\Concurrency;

[$gpt, $claude] = Concurrency::run([
    fn () => Copilot::run('Hello', config: ['model' => 'gpt-5.2'])->content(),
    fn () => Copilot::run('Hello', config: ['model' => 'claude-sonnet-4.6'])->content(),
]);

Laravel AI SDK Integration (experimental)

// config/ai.php
'default' => 'copilot',
'providers' => [
    'copilot' => ['driver' => 'copilot', 'key' => ''],
],

// Usage
use function Laravel\Ai\agent;

$response = agent(instructions: 'You are an expert.')->prompt('Tell me about Laravel');
echo $response->text;

Testing

Copilot::fake()

use Revolution\Copilot\Facades\Copilot;

// Simple fake — always returns '2'
Copilot::fake('2');
$response = Copilot::run(prompt: '1 + 1');
expect($response->content())->toBe('2');

Sequence for multiple calls

Copilot::fake([
    '*' => Copilot::sequence()
            ->push(Copilot::response('2'))
            ->push(Copilot::response('4')),
]);

Copilot::start(function (CopilotSession $session) use (&$r1, &$r2) {
    $r1 = $session->sendAndWait(prompt: '1 + 1');
    $r2 = $session->sendAndWait(prompt: '2 + 2');
});

expect($r1->content())->toBe('2');
expect($r2->content())->toBe('4');

Assertions

Copilot::assertPrompt('1 + *');      // Prompt was called (wildcard match)
Copilot::assertNotPrompt('1 + *');   // Prompt was NOT called
Copilot::assertPromptCount(3);       // Exactly 3 prompts sent
Copilot::assertNothingSent();        // No prompts sent

Prevent stray requests

Copilot::preventStrayRequests();              // Throw on any unfaked request
Copilot::preventStrayRequests(allow: ['ping']); // Allow only 'ping'
Copilot::preventStrayRequests(false);          // Disable prevention

Laravel Events

The SDK dispatches Laravel events for logging and debugging:

  • Client\ClientStarted, Client\PingPong, Client\ToolCall
  • Session\CreateSession, Session\MessageSend, Session\MessageSendAndWait, Session\ResumeSession, Session\SessionEventReceived
  • JsonRpc\MessageSending, MessageReceived, ResponseReceived
  • Process\ProcessStarted

Key Classes Reference

Class / Interface Purpose
Copilot (Facade) Main entry point: run(), start(), stream(), fake()
CopilotSession (Contract) Session interface: send(), sendAndWait(), on(), rpc(), getMessages(), destroy()
CopilotClient (Contract) Client interface: start(), stop(), createSession(), resumeSession(), ping(), listModels(), listSessions(), rpc()
SessionConfig Session creation options (model, tools, streaming, MCP, etc.)
ResumeSessionConfig Options for resuming a session
SessionEvent Event object: content(), type(), deltaContent(), isAssistantMessage(), isAssistantMessageDelta(), failed(), errorMessage()
Tool Tool::define(name, description, parameters, handler)
ToolResultObject Tool handler return type
Attachment Attachment::file(), Attachment::directory(), Attachment::selection()
ServerRpc / SessionRpc Typed RPC layer
PermissionHandler PermissionHandler::approveAll(), PermissionHandler::approveSafety(), PermissionHandler::denyAll()
PermissionDecision approveOnce(), approveForSession(), approveForLocation(), reject(), userNotAvailable(), noResult(), select()
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console