Laravel AI SDK 1.0 Tool Approval Lets Your Agent Ask Before It Acts

Laravel AI SDK 1.0 can pause an agent before a sensitive tool runs. Learn the Approvable contract, decisions, and the details that bite in production.

5 min read0 comments
Laravel AI SDK 1.0 Tool Approval Lets Your Agent Ask Before It Acts

Laravel AI SDK 1.0 shipped on September 23, 2026, and one feature deserves attention if your agents touch money, files or customer email: tool approval. An agent can now stop before a sensitive tool runs, hand the pending call back to your code, and wait for a person to approve it, reject it with a reason, or edit the arguments.

Below is how it works, a small working shape you can adapt, and the production details that are easy to get wrong.

Key takeaways

  • Approval is opt in per tool through the Approvable contract and the InteractsWithApprovals trait.

  • You can require approval always, or only when the arguments cross a threshold.

  • The agent must be conversational with persisted history, otherwise there is nothing to resume.

  • Pauses are per call, so other tools in the same step still run immediately. Keep side effects idempotent.

Developer typing code on a laptop beside a plant and a mug
Photo by Nubelson Fernandes on Unsplash

Why approval exists

Before this feature, an agent that started calling tools ran to completion on its own. That is fine for read-only lookups. It is a harder sell for a tool that refunds a payment, deletes a file or emails a customer. The human-in-the-loop API was announced at Laracon US 2026 and first shipped in laravel/ai v0.10.0 on July 21, then landed in the stable 1.0 release, according to Laravel News.

Marking a tool as approvable

Implement the Approvable contract and use the trait. With no other code, the tool asks for approval every time. To decide from the arguments, define needsApproval and return a boolean or an Approval with a reason.

use Laravel\Ai\Approvals\Approval;
use Laravel\Ai\Concerns\InteractsWithApprovals;
use Laravel\Ai\Contracts\Approvable;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;

class IssueRefund implements Approvable, Tool
{
    use InteractsWithApprovals;

    protected function needsApproval(Request $request): Approval|bool
    {
        // amount is in cents in this example
        return $request['amount'] <= 5000
            ? false
            : Approval::required('Refunds above 50 dollars need a manager.');
    }

    // handle() performs the refund
}

You can also override the requirement where the agent registers its tools, using withoutApproval() and requireApproval(), which is handy when the same tool class is used in two agents with different risk levels.

Approving, rejecting or editing a call

When the model calls an approvable tool, the response carries the pending calls, each with a call ID, tool name, arguments and reason. You resume the conversation by passing decisions keyed by call ID.

$response = (new SupportAgent)
    ->forUser($user)
    ->prompt('Refund the damaged headphones on order 4192.');

if ($response->hasPendingApprovals()) {
    foreach ($response->pendingApprovals as $approval) {
        // $approval->id, $approval->tool, $approval->arguments, $approval->reason
    }
}

$response = (new SupportAgent)
    ->continue($conversationId, as: $user)
    ->prompt(Decisions::from([
        'call_abc' => Decision::approve(),
        'call_ghi' => Decision::reject('Outside the return window.'),
    ]));

Every pending call needs a decision, or you get an ApprovalMismatchException. The helpers approveRemaining() and rejectRemaining() set a default for the rest. A rejection that includes a reason goes back to the model so it can keep responding. A rejection with no result records the rejection and stops the generation loop.

Production details that matter

  • The agent has to be Conversational with its history persisted. The RemembersConversations trait handles that.

  • Approval works with prompt, stream, queue and the broadcast methods. While streaming, a pause arrives as a tool_approval_request event, and queued agents dispatch a ToolApprovalRequested event.

  • Pauses are per call, not per step. Tools in the same step that do not need approval run immediately, so use the tool call ID to keep external side effects idempotent.

  • Laravel records an approved tool's result before asking the model to continue. If generation fails after that, the approval is already spent, so resume with a normal text prompt instead of resubmitting decisions.

Plan the upgrade

Version 0.10 added a nullable approval_state column on the conversation messages table. The 1.0 release also changed conversation storage to a steps JSON column, so run the backfill migration from the upgrade guide before you deploy.

What 1.0 adds around it

The same release adds middleware that wraps each generation step, a ToolSearch wrapper so agents do not send all thirty tools on every request, a CodeExecution provider tool, and unified token usage naming. If you are building a retrieval feature next, our walkthrough of RAG in Laravel 13 with pgvector pairs well with this.

Frequently asked questions

Does every tool need approval?

No. Approval is opt in per tool. Only tools that implement Approvable and use the trait pause the agent.

Can I approve based on the amount or arguments?

Yes. Define needsApproval and return false for safe calls or Approval::required with a reason for risky ones.

Can a reviewer change the arguments?

Yes. A decision can approve the call, reject it with a reason, or edit the arguments before the tool runs.

Does approval work with queues and streaming?

Yes. It supports prompt, stream, queue and the broadcast methods, with events for streaming and queued runs.

What is required to resume a paused agent?

The agent must be conversational and its history must be stored, so the SDK can find the paused call.

  • #AI Agents
  • #Human-in-the-Loop
  • #Laravel
  • #Laravel AI SDK
  • #PHP

Share this article

Comments

No comments yet — be the first to share your thoughts.

Leave a comment