feat: #3586020 Per-tool approval mode with loop pause-resume

Closes #3586020

What this does

Adds a per-tool Approval mode setting on the agent form:

  • Pre-approved: the tool runs without a human gate.
  • Approve once per run: the first call asks; later calls to the same tool in the run are trusted.
  • Always needs approval: every call asks.

Default differs from the issue

The issue proposes Always needs approval as the default for unconfigured tools. This MR defaults to Pre-approved instead (ToolApprovalMode::DEFAULT), so existing agents and callers that don't handle JOB_AWAITING_APPROVAL yet keep working after the update. Switching the default is a one-line change; happy to do it, possibly with an update hook that sets the mode explicitly on existing agents, if maintainers prefer fail-safe.

Loop integration

  • When a queued tool call needs approval, the tool is not executed and the run returns AiAgentInterface::JOB_AWAITING_APPROVAL. This happens before the loop counter moves, so waiting costs no loop.
  • ToolApprovalAwareInterface (implemented by AiAgentEntityWrapper) exposes isAwaitingApproval(), getPendingToolApprovals(), getToolApprovals(), approveToolCall($handle) and rejectToolCall($handle, $reason). The caller decides, then runs the agent again to resume.
  • A rejected call is reported to the model as a tool message, so it can choose another action and the chat history stays valid for providers.
  • Works for looped, non-looped and streamed runs.

Events

  • AgentToolApprovalRequestEvent: subscribers can allow, reject or require approval for a call, with a reason.
  • AgentToolApprovalDecisionEvent: dispatched when a call is approved or rejected.

Tests

  • Kernel: AiAgentToolApprovalTest
  • Functional: AiAgentToolApprovalModeFormTest

🤖 Generated with Claude Code

Edited by Gianfranco Fedele

Merge request reports

Loading
Loading