Domain

ApprovalCard

Durable human-in-the-loop approval surface for reviewing risky tool operations before they execute.

import { ApprovalCard } from '@lostgradient/cinder/approval-card';
approvalhuman-in-the-looptool
01

Overview

Presentational human-in-the-loop approval surface for reviewing a tool operation before the host application executes it.

Usage

svelte
<script lang="ts">
  import ApprovalCard from '@lostgradient/cinder/approval-card';
</script>

<ApprovalCard
  tool={{ name: 'deploy-cloud', risk: 'medium' }}
  sandbox={{
    provider: 'codex',
    name: 'workspace-write',
    workingDir: '/workspace/project',
  }}
  operation={{
    kind: 'command',
    command: 'bun run --filter=@lostgradient/cinder validate',
    argsPreview: { package: '@lostgradient/cinder' },
  }}
  env={['DATABASE_URL', 'OPENAI_API_KEY']}
  policyVersion="policy-2026-06"
  idempotencyKey="approval-01JZ8T"
  state="pending"
  onResolve={(resolution) => console.log(resolution)}
/>

ApprovalCard is fully controlled. It does not execute commands, apply patches, persist policy decisions, or read environment values. The component only renders the supplied context and invokes callback props for host-owned actions.

Resolution callback

onResolve is the single decision contract. It fires for every action — Approve, Approve with edits, Deny, and Dismiss — with the complete ApprovalResolution payload: the selected decision, parsed editedArgs for edited approvals, optional reason text, and the remember checkbox state. Action buttons render only while the request is actionable and onResolve is wired; a card without the callback is purely presentational.

decision: 'deny' means the approver actively refused the operation. decision: 'cancel' (the Dismiss button) means the prompt was dismissed without a decision.

Operation rendering

  • tool.name renders in monospace inside the title, distinguishing the identifier from the surrounding sentence.
  • tool.risk renders as a stacked-bar signal icon (bar count scales with risk, so it doesn't rely on color alone) with a tooltip carrying the risk label; the icon itself is the accessible name via aria-label.
  • operation.kind: 'command' renders the command as a syntax-highlighted shell CodeBlock.
  • operation.kind: 'patch' renders the supplied unified patch as a syntax-highlighted diff.
  • operation.filesTouched renders one row per unique path, each with a copy button. Duplicate paths are collapsed.
  • operation.argsPreview renders through PayloadInspector; oversized previews are replaced with a bounded truncation notice before rendering.
  • env renders variable names only, as plain text. Values are not accepted and are stripped if a caller accidentally passes NAME=value.
  • Sandbox context, environment names, and the policy version / idempotency key / snapshot id all live in a single collapsed "Details" disclosure so supporting context stays out of the approver's way until they ask for it. The action buttons are the last element of a pending card.

Approval states

Pending requests render action buttons. Non-pending requests render a read-only, state-tinted summary. When expiresAt passes while state is still pending, the effective state becomes expired, actions disappear, and no callback fires automatically.

Heading levels

The card title defaults to an h3 with section headings one level deeper. Pass headingLevel to fit the card into the host page's document outline, matching the Card convention.

Live preview
02

When to use

Use when
  • A tool call, command, file write, or patch needs explicit human approval before execution.
  • Showing policy, sandbox, idempotency, environment-name, and argument context for an approval request.
Avoid when
  • The action has already completed and only needs historical display — use feed or run-step-timeline instead.
  • Collecting arbitrary form input for a workflow — compose form controls directly instead.
03

Examples

Command approval

A pending command approval with sandbox context, arguments, environment names, and editable arguments.

Expired approval

A read-only approval request whose pending decision window has expired.

Patch approval

A high-risk patch approval that previews the proposed diff, touched files, and arguments.

04

Props

Props for approval-card
NameTypeDefaultDescription
tool required { /** Human-readable tool name requesting approval. */ name: string; /** Risk level assigned by the policy evaluator. */ risk: ApprovalToolRisk; } Tool requesting approval.
sandbox { /** Sandbox provider, such as Codex or a remote execution backend. */ provider: string; /** Sandbox profile or policy name. */ name: string; /** Wor…
Show full type { /** Sandbox provider, such as Codex or a remote execution backend. */ provider: string; /** Sandbox profile or policy name. */ name: string; /** Working directory for the pending operation. */ workingDir: string; }
Optional sandbox context in which the operation will run.
operation required { /** Operation family being approved. */ kind: 'command'; /** Shell command for command approvals. */ command: string; /** File paths that the operat…
Show full type { /** Operation family being approved. */ kind: 'command'; /** Shell command for command approvals. */ command: string; /** File paths that the operation may read or write. Duplicate paths are collapsed for display. */ filesTouched?: string[]; /** * JSON-like argument preview shown to the approver. * * @schemaPermissive */ argsPreview?: unknown; }
Operation details shown to the approver.
env string[] [] Environment variable names only. Values are ignored if accidentally supplied as NAME=value.
snapshotId text Snapshot identifier for the pending approval context.
policyVersion required text Policy version that produced the approval request.
idempotencyKey required text Idempotency key that makes repeated decisions durable.
expiresAt text Optional ISO timestamp after which a pending approval is treated as expired.
state required 'pending''approved''approved_with_edits''denied''expired''cancelled' Persisted approval state.
editableArgs boolean false Whether approving with edited JSON arguments is available. Default false.
headingLevel 23456 3 Heading level for the card title; section headings render one level deeper. Default 3.
onResolve (resolution: ApprovalResolution) => void Called for approve, approve-with-edits, deny, and dismiss with the complete resolution payload.
id text
05

Accessibility

Renders as an article with native button controls so approval actions are discoverable without polluting the landmark list.
Expiration changes update the visible state but do not fire approval callbacks automatically.