Skip to main content
The snippets use mockModel so they run as pasted, with no API key. Swap in a real provider or model for your own agent; nothing else changes. Each section links to the page that has the full detail.

1. A tool that needs approval

Set needsApproval on the tool. The run pauses before the tool executes:
See Approvals for predicates and permission policies.

2. Give the agent a durable store

Pauses and checkpoints live in the agent’s store. The default is in memory, which a restart loses. Use a durable one: SqliteStore (one transactional file, shared by several processes on one host) or fileStore(dir) (plain JSON files). See Choosing a store.

3. Send: the run pauses

send() does not throw at a pause. It resolves with finishReason: 'awaiting-approval' and the id to decide:
Use agent.session({ id }) instead of sessionId when the conversation should keep a transcript across turns (see Sessions); the pause and resume calls below are the same.

4. In a new process: list the pauses and decide one

Start the process again with the same store. agent.approvals.list() returns the pending calls, oldest first, including the ones another process or an earlier run left in the store. agent.approvals.resolve() runs the call (or rejects it), continues the run, and resolves with the result:
  • To reject, pass approved: false and a note the model sees as the reason.
  • A session that paused is found from its pause record, so you do not need to open it or call resume() first.
  • resolve() throws when the id is unknown or already decided, so two processes racing on one pause cannot both run the tool.
  • A pause from send(msg, { sessionId }) shows in agent.pending() with status: 'awaiting-approval' and its approvalId.

5. See the run finish

resolve() resolves with the continued run’s result. The tool ran, the model got its output and answered, and finishReason is 'stop'. If the continued run pauses again (another needsApproval call), you get 'awaiting-approval' and a new id: list and decide again. To show progress to a user instead, use agent.approvals.streamResolve(). Everything else about decisions (several calls in one step, “don’t ask again”, approve with edited arguments, expiry) is in Approvals.

6. When the process dies mid-run

A crash or a kill leaves the run’s checkpoint in the store with status 'in-progress'. A restarted process finds it with agent.pending() and finishes it with agent.resume(sessionId), or hands the whole job to agent.recover(). This snippet stands in for the crash with a tool that throws PropagatingToolError the first time (the way a dying process would stop the step), then recovers:
To do this without writing the loop, start agent.recover() once per process at startup: it polls pending() and resumes every 'in-progress' run, leaves approvals for a human, and is safe to run on every replica (run leases make sure one process resumes each run). See Recovering automatically. At-least-once for the step that was running. Recorded model responses and finished tool results are never repeated: that is why charge ran once above. But a tool that was mid-call when the process died runs again on resume, so its side effect can happen twice. Make such tools idempotent (an idempotency key passed to the payment API, a “have I done this” check). See At-least-once tools. A tool with needsApproval is not affected by this while it waits: a pause records no work, it only waits for the decision.

7. Over HTTP

The built server (see Deployment) does this for you. A client that resends a POST /chat after a dropped connection can send the same Idempotency-Key header: a finished turn is replayed without running anything, and a turn interrupted by a crash is resumed from its last checkpoint instead of starting over. A session paused on an approval answers 409 (LOUSHO_SESSION_AWAITING_APPROVAL) until it is decided. The table of outcomes is in Resending a turn.

Where to go next

  • Approvals: permission policies, several calls in one step, expiry, deciding in code.
  • Sessions: durable session turns and stores.
  • Durable execution: checkpoints, run leases, the guarantees, fork and replay.