Pattern · Agents

New

Agent Approval SwiftUI Block

An agent asks before it acts: the action, its risk and a preview of the change, then Allow once, Always allow or Deny.

The permission prompt for an agent about to act. A card names the agent and tool with a tinted risk chip, states the action and why, counts down to an automatic deny when it has a deadline, and previews the change as an email, a list of affected items or a value before and after. Each answer runs your closure in place, and the block folds to a one-line outcome.

  • SwiftUI
  • iOS 17.0+
  • agent-approval
Get SwiftPieces Pro

$199 once, every piece included. See how installing works · Sign in

01

What's included

SwiftUI source you own, built on Apple frameworks only.

  • The action as a verb and target, the agent and tool, and a Low, Medium or High risk chip
  • Previews an email, the affected items with Show all, or a value before and after
  • Allow once, Always allow for the tool and Deny, each with working and failed in place
  • An optional countdown that denies on its own
  • Folds to "Allowed once · 2:41 PM" once decided

02

Implementation notes

How it behaves, what it handles, and the decisions behind it.

One surface card names the agent and the tool it would use beside a tinted risk chip (Low, Medium or High risk), then what it wants to do as a verb and a target, "Send an email to 3 people", and why. When the request has a deadline, a line counts down ("Auto-denies in 1:24"), turns to the warning colour in its last ten seconds and denies the request on its own at zero. Preview opens what would change: the email with its recipients, subject and body; the affected items, three at first with Show all; or a value before and after. Allow once is the primary action, Always allow for the tool the secondary one and Deny a plain one. The tapped button shows progress while your closure runs and the others wait; a thrown error shows under them and keeps the request open. Once decided, the block folds to one line with its symbol: "Allowed once · 2:41 PM", "Always allowed", "Denied" or "Expired". nil shows grey shapes in the block's own layout.

Notes

  • AgentApprovalBlock(request:showsPreview:onDecision:) takes one Request and the decision closure. It is a section: no navigation stack, background, padding or scroll view of its own, so place it in your own column or a sheet.
  • AgentApprovalBlock.Request has a stable id, the action, an optional reason, the agent, the tool and its SF Symbol, a Risk (.low, .medium, .high), an optional Preview, an optional expires date and an optional resolution. Request.sample is the email from the preview.
  • Request.Preview is .message(to:subject:body:), .items([Item]) or .change(label:before:after:). An Item has a title, an optional detail and a symbol.
  • onDecision receives .allowOnce, .alwaysAllow or .deny, and .deny when the request expires. Return to settle on the outcome. Throwing shows the error's errorDescription (or "Your answer didn't reach the agent. Try again."); throw CancellationError() to go back quietly.
  • Pass a resolution to show a request decided earlier or elsewhere. A request with a new id resets the block.
  • Haptics mark success when an action is allowed, a warning when it is denied and an error when sending the answer fails.
  • VoiceOver reads the agent and tool ("Assistant wants to use Mail"), the action as a header, the countdown in seconds, the change as "From $180.00 left to $156.00 left", and announces the outcome. The risk chip moves under the source line and before and after stack when text is large.
  • Reduce Motion turns opening the preview and folding into short fades.
  • AgentApprovalBlockExample at the end of AgentApprovalBlock.swift is the screen in the preview: the email request with 90 seconds before it expires, and Allow once taking a moment before it folds. Pass wrapsInNavigationStack: false to push it in your own stack.

Theming

  • Reads SPTheme from @Environment(\.spTheme) and requires the design-system item.
  • The card is color.surface with radius.lg, 16 point padding and an SPHairline above Preview; the preview sits on fill at radius.sm. The buttons sit 24 points under the card.
  • The action is type.title3, the reason and email body type.subhead, the source line, fields and countdown type.footnote.
  • The risk chip is success, warning or danger on a 14% tint of it. The countdown turns warning in its last ten seconds; errors are danger; an allowed outcome's symbol is success.
  • Allow once is the accent with accentOn text, the only accent on the block; Always allow is the secondary button on fill, Deny plain textPrimary text.
  • Changes animate with motion.smooth and buttons with motion.snappy; every animation becomes a short fade under Reduce Motion.