Pull the plug, then press stop
An agent onboards two new hires, one tool call at a time. Kill Wardrik midway and restart it. The agent resumes without repeating a call. Cancel the turn and kill Wardrik again. It stays cancelled.
Requires Node 22.18+ and jq.
The files
Section titled “The files”Isolate the demo from any existing daemon:
export XDG_CONFIG_HOME=~/plug/config XDG_DATA_HOME=~/plug/data XDG_STATE_HOME=~/plug/statemkdir -p ~/plug/config/wardrik/agentsdefaults: model: anthropic:claude-sonnet-5http: port: 4848 trusted_workers: trueroles: it: rules: [{verbs: [read, events, ask, cancel, serve], kinds: [agent], names: [onboarding]}]bindings: - {org: default, role: it, subjects: [user:default/@it]}trusted_workers: truemarks the script's results as trusted, so later calls don't need approval.- The
itrole allows the script to message the agent, stream its events and serve its tools.
---tools: [workers/*]policy: {allow: [workers/*]}---You set new hires up. For each person in turn: their google, github and slack accounts, then theirlaptop, then a short welcome email. One call at a time. Say when you're done.import { setTimeout as sleep } from "node:timers/promises";import { Wardrik } from "@wardrik/sdk";import { z } from "zod";
const wardrik = new Wardrik({ name: "it" });
// IT keeps what each call did under its idempotency key, as a payment API does.const done = new Map<string, Promise<string>>();
function once(key: string | undefined, what: string, signal: AbortSignal): Promise<string> { const before = key && done.get(key); if (before) return before;
console.log(`[it] ${what}...`); const now = sleep(3000, undefined, { signal }).then(() => { console.log(`[it] ${what}: done`); return `${what}: done`; }); if (key) done.set(key, now); now.catch(() => key && done.delete(key)); return now;}
wardrik.tool({ name: "create_account", description: "Create a new hire's account in one system.", input: z.object({ person: z.string(), system: z.enum(["google", "github", "slack"]) }), idempotent: true, run: ({ person, system }, { idempotencyKey, signal }) => once(idempotencyKey, `${system} account for ${person}`, signal),});
wardrik.tool({ name: "order_laptop", description: "Order a new hire's laptop.", input: z.object({ person: z.string() }), idempotent: true, run: ({ person }, { idempotencyKey, signal }) => once(idempotencyKey, `laptop for ${person}`, signal),});
wardrik.tool({ name: "send_welcome", description: "Email a new hire their welcome.", input: z.object({ person: z.string(), text: z.string() }), idempotent: true, run: ({ person }, { idempotencyKey, signal }) => once(idempotencyKey, `welcome email to ${person}`, signal),});
const working = wardrik.work();const agent = wardrik.agent(process.env.AGENT!);
for await (const event of agent.stream("Ana and Ben start on Monday. Please set them both up.")) { if (event.type === "wardrik.agent.started") console.log(`[agent] started again on ${event.data.node}`); if (event.type === "wardrik.tool_call.completed" && event.data.replayed) console.log("[agent] answered from its log"); if (event.type === "wardrik.run.completed" || event.type === "wardrik.run.cancelled") console.log(`[agent] ${event.type}`);}
await working;idempotent: true allows an interrupted call to be retried with the same key.
Try it
Section titled “Try it”export ANTHROPIC_API_KEY=sk-ant-...wardrik startwardrik token create it --subject user:default/@it --expires 1d # copy the secretwardrik agent show onboarding --json | jq -r .agent.agent # the agent's IDIn another terminal, in a new directory:
WARDRIK_URL=http://127.0.0.1:4848 WARDRIK_TOKEN=... AGENT=agt_... node onboarding.tsWhile a call is running, kill the daemon:
kill -9 "$(wardrik pid)"wardrik start[it] laptop for Ana...[agent] started again on wardrik@localhost[it] laptop for Ana...[it] laptop for Ana: done...[agent] wardrik.run.completedOne laptop each. Now clear the agent, run the script again, and cancel the turn midway:
wardrik agent clear onboardingwardrik agent cancel onboarding # while a step runskill -9 "$(wardrik pid)"wardrik startwardrik ls onboarding # idleNo calls run. The cancellation was persisted, so the agent restarts idle.
What it shows
Section titled “What it shows”- Each round is persisted before it runs, so a restarted Wardrik resumes from it. See Durable runs.
- A completed call returns its result from the log (
"replayed": true) and never runs twice. - A retried call has the same idempotency key.
- Remove
idempotent: trueand kill the daemon. The interrupted call needs approval instead. - A cancelled turn stays cancelled across a crash.
Clean up with wardrik stop && rm -rf ~/plug, then open a new shell.