laravel-pandora maintained by michal78
⚠ v0.1.0 — first published release, and deliberately
0.xWhat works today: define an agent, start a conversation, dispatch a queued run, stream it over Reverb, persist an immutable trace, reload without losing anything, cancel it, and inspect it in the control center — with tools under five layers of authorization and human approval gates on the risky ones, multi-provider routing with failover and budgets, automations on four engines, scoped memory and context that works with no vector database installed, delegation and MCP where nothing remote is approved by discovering it, workspaces on local or S3-compatible storage, and messaging channels where an unlinked identity gets no run, no session and no seat.
What does not exist yet: release hardening — the full threat-model sweep, performance tests and the example application (Phase 9, at 3 of 34 criteria). See
docs/roadmap.md.Verified by 1,756 tests across SQLite, MySQL 8.4, MariaDB 11, PostgreSQL 17, pgvector and MinIO · PHPStan level 8, no baseline · Pint.
Before depending on this, read
docs/product/support-statement.md— it names what is supported, what is excluded by design, and what ships known untested. The API is usable and in use; it is not yet promised.1.0.0is defined as the day Phase 9's criteria are met, not a date.
Why
Two kinds of thing exist today, and neither fits a Laravel application.
Personal agent daemons (OpenClaw, Hermes Agent) are excellent — for one operator, on one machine. OpenClaw's own security documentation is explicit that it "is not a hostile multi-tenant security boundary." You cannot put one in front of your customers.
LLM SDKs give you ->chat() and leave durability, approvals, tenancy, auditing, cost control and
resumability as an exercise for the reader.
Pandora takes the capabilities of the first and gives them the trust, tenancy and authorization model of the framework you already run on. An agent run is a durable, queued, resumable, authorized, audited unit of work — closer to a job with a state machine than to a chat request.
The idea in one screen
Expose an application action. It is an ordinary PHP class, with ordinary Laravel authorization:
final class IssueRefund extends Tool
{
public function name(): string
{
return 'issue_refund';
}
public function description(): string
{
return 'Issue a refund for an existing order.';
}
public function rules(): array
{
// The JSON schema shown to the model is generated from these, so the
// interface it is told about and the one enforced cannot drift.
return [
'order_id' => 'required|string|exists:orders,id',
'amount_minor' => 'required|integer|min:1|max:100000',
];
}
public function risk(): RiskLevel
{
return RiskLevel::High; // → requires human approval by default
}
public function authorize(ToolInput $input, ToolContext $context): bool
{
// The *acting user's* policy. An agent can never exceed the person it acts for.
return Gate::forUser($context->user())
->allows('refund', Order::find($input->string('order_id')));
}
public function handle(ToolInput $input, ToolContext $context): ToolResult
{
$this->refunds->issue($input->string('order_id'), $input->integer('amount_minor'));
return ToolResult::success('Refund issued.');
}
}
Register it, then grant it — registering installs a tool; each agent must still be given it:
// config/pandora.php
'tools' => ['registered' => [App\Tools\IssueRefund::class]],
Give it to an agent, and run it:
$run = Pandora::agent('support')
->forUser($user)
->withContext(['order_id' => $order->getKey()])
->stream()
->dispatch('Help this customer resolve their order problem.');
The request returns immediately. A queue worker performs the run. The browser streams it over Reverb. The refund pauses for a human approval — costing nothing while it waits, surviving a deploy — then resumes. Every step is in the trace. Every decision is in the audit log.
What makes it different
| Multi-tenant and multi-user from the first migration | Not a single-operator daemon. Tenant and session isolation are tested, not assumed. |
| Authorized against the actor, not the agent | A tool call is checked against the acting user's Laravel gates and policies. An agent cannot do what the user could not. |
| Runs pause for free | An approval pause holds no worker and no memory. It can wait three days across two deploys. |
| Resumable by construction | All continuation state is in the database. A worker crash loses at most one iteration. |
| Memory needs no vector database | Lexical retrieval is the shipped path across all four databases. A vector store is an accelerator, never an authority. |
| Reverb is optional | The database is authoritative; broadcasts are notifications. Turn realtime off and polling is still correct. |
| Redis and Horizon are optional | The core runtime needs a plain Laravel queue and nothing else. |
| Nothing is forced | Headless mode installs no routes, no views, no Livewire. |
Requirements
| PHP | 8.3 · 8.4 |
| Laravel | 13.x |
| Database | SQLite · MySQL 8.4 · MariaDB 11 · PostgreSQL 17 — all in CI |
| Queue | any Laravel queue backend |
| Optional | Livewire 4 (control center) · Reverb (streaming) · Redis/Horizon · pgvector |
Installation
composer require michal78/laravel-pandora
php artisan pandora:install
php artisan queue:work
That is the headless install — agents, tools and runs from your own code, with no routes and no
frontend. The control center at /pandora is Livewire, which Pandora suggests rather than
requires, so add it if you want the UI:
composer require livewire/livewire
Without it, no /pandora route is registered at all — the page 404s rather than erroring, and
php artisan pandora:status tells you which of the two you have.
Want the unstable branch instead? development is where everything lands before it is released, and
Composer will take it without changing your minimum-stability — requiring a dev- version sets the
stability flag for that package on its own:
composer require michal78/laravel-pandora:dev-development
The installer is idempotent, publishes config and migrations, explains Reverb / queue / scheduler setup, and deliberately creates no default agent.
Then define an agent and run it:
final class SupportAgent implements AgentDefinition
{
public function define(AgentBlueprint $agent): AgentBlueprint
{
return $agent
->name('Support')
->instructions('Help customers resolve support issues.')
->model('openai', 'gpt-4o-mini');
}
}
php artisan pandora:agent:run support "Where is order 1234?" --trace
Control center
A Livewire control center ships with the package and looks finished the moment it is installed — its own logo, palette and design tokens, light and dark themes resolved before the first paint, and every colour meeting WCAG AA on the surface it sits on. Retheme it by overriding one layer of custom properties; replace the mark by editing one Blade component.
See docs/visual-identity.md and
docs/brand-guide.md.
// config/pandora.php
'ui' => ['brand' => 'Acme Agents', 'theme' => 'dark'],
Documentation
Guides — installation · quick start · agents · tools · providers · automations · memory · workspaces · MCP · channels · writing extensions
Architecture — overview · security model · execution model · provider model · database model · realtime model
Product — vision · feature parity · terminology
Decisions — ADRs — each with the alternatives and why they lost
Delivery — roadmap · changelog · acceptance plans · progress log · open questions
Security
Pandora executes model-directed actions inside your application. Read
docs/architecture/security-model.md before deploying it.
We do not claim to solve prompt injection. We build layered controls that bound what an injected
instruction can reach: least authority, approval gates, tool allowlists, argument validation, egress
control, budgets and audit. See SECURITY.md for the full statement of limitations —
including where policy restriction is not sandboxing.
Contributing
See CONTRIBUTING.md. Read the ADRs first — most design questions are already
answered there, including the ones where we chose the harder option on purpose.
Acknowledgements
OpenClaw and Hermes Agent shaped what users now expect from a self-hosted agent platform, and studying them publicly informed our parity matrix. Pandora shares no code, assets, wording or implementation details with either project — every capability here is an independent Laravel-native reimplementation.
License
MIT. See LICENSE.md.