Documentation
Action gates
Making consequential actions explicit, approved and safe to retry.
Gated actions
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 againEach 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.
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.
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.