Human-in-the-loop

Pause the runner before risky tools execute — approvals as runtime events.

Human-in-the-loop

Human-in-the-loop means a run can pause before a side effect. The pause is a runtime event with arguments, policy, and a decision — not a hidden callback in your route handler.

Why approvals are runtime events#

You need to answer:

  • Which tool was about to run?
  • With which arguments?
  • Who approved or rejected?
  • How did the run continue?

Those questions require structure. Studio’s approval center and traces consume the same events the runner emits.

Marking a tool#

import { tool } from "@shiro-sdk/core";
import { z } from "zod";

const deleteResource = tool({
  name: "deleteResource",
  description: "Permanently delete a cloud resource.",
  requiresApproval: true,
  approvalDescription: "Destructive delete — irreversible.",
  parameters: z.object({
    resourceId: z.string(),
  }),
  execute: async ({ resourceId }) => resources.delete(resourceId),
});

Until approval is granted, execute should not run. Rejection is a valid outcome, not necessarily an unexpected exception.

When to require approval#

Require approvalUsually skip
Refunds, payoutsRead-only lookups
Deletes, revokesIdempotent cache warmups
Deployments, permission changesFormatting / pure compute
Outbound customer messagesInternal dry-run tools

Guardrails vs approvals#

  • Guardrails — policy checks that can allow, block, or modify a stage
  • Approvals — explicit human (or external) decision gates on tool calls

Both belong in the runtime so they appear in traces. See core Guardrail types and ApprovalManager / policies when you need custom reviewers.

Workflow#

model requests tool
  → approval.requested  (Studio / reviewer notified)
  → approval.granted | approval.rejected
  → tool executes or run continues without the side effect

Best practices#

  • Approve the tool call, not the final chat sentence
  • Include enough args for a reviewer to decide without reading code
  • Log / trace the decision with the run id
  • Train operators on Studio’s approval center during development

Common mistakes#

  • Asking the model to “confirm” in natural language and treating that as security
  • Retrying a rejected delete automatically
  • Implementing approvals outside Shiro so Studio never sees the pause
  • Approving after the mutation already happened