Laravel AI SDK Adds Human-in-the-Loop Tool Approval
Laravel AI #Laravel AI SDK #HITL #AI Agents #Laracon US 2026 #Tool Approval

Laravel AI SDK Adds Human-in-the-Loop Tool Approval

3 min read Mohamed Said Mohamed Said

Laravel AI SDK v0.10.0 Brings Human-in-the-Loop Control to Agents

Announced at Laracon US 2026 in Boston and shipped on July 21, Laravel AI SDK v0.10.0 introduces a human-in-the-loop (HITL) API for AI agents. Before this release, an agent that started executing tools ran straight through to completion with no way to intervene. That is acceptable for read-only operations, but it is a serious concern when a tool can delete a file, issue a refund, or send an email to a customer.

The new API lets you intercept specific tool calls, pause the agent, and require a human decision — approve, reject, or modify the arguments — before execution resumes.

Marking a Tool as Approvable

Approval is opt-in per tool. Implement the Approvable contract and add the InteractsWithApprovals trait:

use Laravel\Ai\Concerns\InteractsWithApprovals;
use Laravel\Ai\Contracts\Approvable;
use Laravel\Ai\Contracts\Tool;

class IssueRefund implements Approvable, Tool
{
    use InteractsWithApprovals;

    public function handle(Request $request): Stringable|string
    {
        $order = Order::findOrFail($request['order_id']);
        $order->refund($request['amount']);
        return "Refunded {$request['amount']} on order {$order->id}.";
    }
}

When blanket approval is too broad, define a needsApproval method that inspects the incoming arguments and returns a boolean or an Approval instance with a reason:

use Laravel\Ai\Approvals\Approval;

protected function needsApproval(Request $request): Approval|bool
{
    return $request['amount'] <= 2000
        ? false
        : Approval::required('Refunds over $20 need a manager.');
}

You can also override approval requirements at the agent level using withoutApproval() and requireApproval().

Handling Pending Approvals

When the model calls an approvable tool, the agent pauses and surfaces the pending calls on the response object:

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

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

To resume, continue the conversation and pass a Decisions instance keyed by tool call ID:

use Laravel\Ai\Approvals\Decision;
use Laravel\Ai\Approvals\Decisions;

$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 must receive a decision, or an ApprovalMismatchException is thrown. Use approveRemaining() or rejectRemaining() to set a bulk default for any calls not explicitly addressed.

Key Details to Know Before Upgrading

  • The agent must implement Conversational with persisted history (via RemembersConversations) — there is nothing to resume otherwise.
  • HITL works with prompt, stream, queue, broadcast, broadcastNow, and broadcastOnQueue.
  • During streaming, a pause arrives as a tool_approval_request event; queued agents dispatch a ToolApprovalRequested event.
  • Pauses are per call, not per step — tools in the same step that do not require approval run immediately. Keep side effects idempotent using $request->toolCallId().
  • If generation fails after an approval result has already been stored, resume with a plain text prompt rather than resubmitting decisions.

Breaking Changes in v0.10.0

  • A new nullable approval_state column is added to the conversation messages table.
  • Custom ConversationStore implementations must add a storeApprovalResults() method.

Takeaways

  • Approval is opt-in per tool via the Approvable contract and InteractsWithApprovals trait.
  • needsApproval() lets you apply conditional logic based on tool arguments.
  • Pending approvals are surfaced on the response and resumed via a Decisions instance.
  • Streaming, broadcasting, and queued agents all support the HITL pause/resume flow.
  • Upgrading requires a migration for the new approval_state column and a storeApprovalResults() method on custom stores.

Full documentation is available in the Laravel AI SDK docs. Source: Laravel News

Found this useful?

Frequently Asked Questions

3 questions
Q01 Does every tool call require human approval when using the HITL API?
No. Approval is opt-in per tool. You can make approval unconditional, apply conditional logic via a `needsApproval()` method that inspects the arguments, or override the requirement at the agent level with `withoutApproval()` and `requireApproval()`.
Q02 What happens if I do not provide a decision for every pending tool call?
An `ApprovalMismatchException` is thrown. You must supply a decision for every pending call, or use `approveRemaining()` / `rejectRemaining()` to set a default for any calls not explicitly addressed.
Q03 What are the breaking changes when upgrading to Laravel AI SDK v0.10.0?
Two breaking changes: a new nullable `approval_state` column is added to the conversation messages table (requiring a migration), and any custom `ConversationStore` implementation must add a `storeApprovalResults()` method.

Continue reading

More Articles

View all