Skip to content

Documentation

Action gates

Fail-closed

Making consequential actions explicit, approved and safe to retry.

Gated actions

workflow.ts
const r = await session.action({
  operationId: "swap-1",           // stable across restarts; reuse with other input is refused
  operationType: "swap",           // matched against validators and reconcilers
  input: { pair: "ETH/USDC", amount: "100" },
  risk: "high",                    // default; needs approve() and a committed PRE_ACTION checkpoint
  run: async (input) => {
    const tx = await venue.swap(input);
    return { result: tx.fill, transactionHash: tx.hash };
  },
});
// r.reused is true when a confirmed swap-1 was found in the journal and not run again

Each operation is journaled before it runs, with its input commitment, attempt, status, transaction hash and result commitment. A confirmed operation is never run again.

Validators and approval

Validators are deterministic checks that run before anything is sent. A rejected action is blocked before broadcast.

options
import { validators } from "./sdk/src/index.ts";

validators: [
  validators.allowlist(["swap", "transfer"]),
  validators.bound("slippage", (i) => (i as { slippageBps: number }).slippageBps <= 50),
],
approve: async ({ operationId, operationType, input }) => askOperator(operationId, input),

A response is never called a hallucination because it looks odd; it is blocked because a named check failed.

Uncertain outcomes

A timeout is not a failure. An operation that ends without a definitive answer is recorded as UNKNOWN (EXTERNAL_ACTION_STATUS_UNKNOWN), and the reconciler is asked before any retry.

options
reconcilers: {
  // Called before any retry of an uncertain operation. Answer only what you know.
  swap: async (op) => {
    const s = await venue.lookup(op.operationId);
    if (s.filled) return { status: "confirmed", result: s.fill, transactionHash: s.txHash };
    if (s.rejected) return { status: "failed" };
    if (s.neverSeen) return { status: "not_executed" };
    return { status: "unknown" };
  },
},

Throw DefinitiveFailure from run only when you know the action did not take effect.

Emergency halt

agent.gate.halt() blocks every new protected action in the process; resumeAfterReview() lifts it. It does not reverse anything. On-chain, the owner or a guardian can pause a workflow, which blocks new checkpoints.