Patterns / Durable approvals

Durable approvals for long-running agents

Agents now run for minutes or hours, and a person may answer an approval long after the request that asked for it is gone. Candor's adapters keep the approval and the run as data, so neither depends on one browser tab or one server process.

With the Claude Agent SDK, permission requests wait in an ApprovalStore. The default is this process's memory; pass your own (a table, a queue) so any instance can take the answer. claudeCanUseTool works anywhere your agent runs, including a workflow engine: it records the request, waits for the answer, and turns it into what the SDK expects with claudePermissionResult. Answer it from your own route, or from the step that signals your workflow.

A ClaudeRunStore keeps a run's events. With detach, the run carries on when the browser goes away; after a reload, useClaudeAgent re-attaches through attachClaudeAgentRun from the run's id. Because the event fold is pure, a finished conversation replays exactly from its stored events (initialEvents).

With the Vercel AI SDK, a WorkflowAgent run streams the same UI messages: a tool that needs approval shows as waiting for as long as the workflow waits, and an approval made automatically is marked as one. The AI SDK's own WorkflowChatTransport re-attaches the stream after a reload.

The components

  • Claude Agent SDK serverFreeApprovalStore, ClaudeRunStore, claudeCanUseTool, attachClaudeAgentRun, stop and the permission mode.
  • Claude Agent SDKFreeThe pure event fold (replay) and claudePermissionResult for an answer that lands anywhere.
  • useClaudeAgentFreeinitialEvents, attach after a reload, onRespond for your own route, runId to save.
  • AI SDKFreeWorkflowAgent's approvals, waiting or automatic, in the same messages.
  • Permission promptFreeThe waiting request, shown again after a reload.
  • Approval requestProA second person's approval, compact for a phone or a notification (Pro).

See it working

More in the examples: Claude Agent SDK

Install

With the shadcn CLI

Each component brings in what it depends on. These are free (MIT).

Good to know

  • A store makes the answer and the record durable, not the agent: the agent still runs in the process that started it. When the process itself mustn't matter, run the agent in your workflow engine with claudeCanUseTool.
  • A late answer to a run that already ended is refused, not lost: answering and closing a request each happen once, whichever comes first.
  • detach needs a process that outlives the response, not a serverless function that's frozen when the response ends. Give it an approvalTimeoutMs, so a request nobody answers expires (denied) instead of holding the run for ever.
  • Saving the conversation and re-attaching both? Pass the hook's promptId to the server's prompt: the re-attached run then replaces its own events in initialEvents instead of adding them twice.
  • Policy lives on the server: editable says which tools a person may edit, and modes (or allowed) which permission modes a run may switch to. The browser can't widen either.