Clutch wraps Laravel AI instead of replacing it. Your agents, tools, and provider configuration stay exactly as they are.
flowchart LR
Controller["Your controller"]
Coordinator["RunCoordinator"]
Worker["Queue worker"]
Record[("sessions · runs · events<br/>approvals · checkpoints · ledger")]
Agent["Your agent<br/>instructions + tools"]
Conversation[("conversations")]
Provider["Model provider"]
Frontend["Frontend"]
Approver["Approver"]
Controller -->|queue a run| Coordinator
Coordinator -->|after commit| Worker
Worker -->|under a lease| Coordinator
Coordinator --> Record
Coordinator -->|run a turn| Agent
Agent --> Conversation
Agent --> Provider
Record -->|replay, then live| Frontend
Record -->|outlives the worker| Approver
Approver -->|resumes the run| Coordinator
classDef yours fill:#4f46e5,stroke:#3730a3,color:#ffffff
classDef harness fill:#0f766e,stroke:#115e59,color:#ffffff
classDef ai fill:#b45309,stroke:#92400e,color:#ffffff
class Controller,Frontend,Approver yours
class Coordinator,Worker,Record harness
class Agent,Conversation,Provider ai
Indigo is yours. Teal is the harness. Amber is Laravel AI.
Laravel AI talks to the model, runs the tool loop, and remembers the conversation. The harness decides when a turn starts, what gets recorded, who may approve what, when to stop, and how to pick the work back up. Your application starts the work, renders progress, and decides who approves.
Durability comes from a few unglamorous rules. The queue job is dispatched only after the run’s state commits. Events are written before they are broadcast. An approval resolved in a completely separate process is what wakes the run back up.
What one turn does
$session->prompt(…)
│
├─ createRun ─────────────── run.created ← one active run per session, held by a row lock
├─ acquire session lease ───────────────────── ← one worker, or the job exits
├─ restore checkpoint ──────────────────────── ← the conversation the last turn left
├─ transition to running ── run.started
│
│ ┌─ driver: Laravel AI stream ──────────────┐
│ │ step.started │
│ │ text.delta × n │
│ │ tool.call.requested ← ledger checks │ budgets checked at each
│ │ tool.call.completed idempotency │ boundary; cancellation
│ │ step.completed │ observed before a new step
│ └──────────────────────────────────────────┘
│
├─ store checkpoint ──────── checkpoint.created
├─ usage.updated
└─ commit terminal state ── run.completed ← state, result and event commit together;
the active-run slot clears in the same write
If the agent hits a tool that needs approval, the middle of that sequence ends at run.awaiting_approval, the worker exits, and everything resumes from the checkpoint once a decision arrives.