Advanced AI Build Night · Durable Agents · Cheat sheet
Build It. Break It. Bring It Back
Build It. Break It. Bring It Back — cheat sheet
The story
The problem. Every AI agent demo has the same dirty secret: it dies when the laptop closes. The agent's "memory" is just variables inside a running program — kill that program (a deploy, a crash, a laptop going to sleep) and the agent wakes up with total amnesia. That's why so many agents are demo-ware: impressive for ninety seconds, useless the moment anything restarts. Tonight's premise: the process should be disposable; the memory should not.
The one architectural idea. Separate compute from state. Cloudflare has a building block made for exactly this, called a Durable Object: a tiny object, addressable by name, that owns its own private SQLite database. Your agent isn't a script that talks to a database somewhere — your agent IS an object that OWNS one. Kill the compute and the ledger persists; the next request wakes the same object, by name, with its memory intact.
The one idea: your agent is a Durable Object with a SQLite ledger inside it — the process is disposable, the memory is not. Kill the server, the conversation survives.
What Flue is. Flue is an agent framework from the team behind Astro. You write one TypeScript file marked 'use agent' — plain-English instructions, typed tools (ordinary TypeScript functions the model is allowed to call), and a structured output schema. The interesting part is the build step: Flue compiles that file into a Durable Object class — our build literally emits class FlueTriageAgent. You write an agent; the build step makes it durable. That's why the stack is Flue + Workers + Workers AI: model, compute, and state all on one network — no API keys, no glue code, $0.
Why incident triage? It's the smallest problem with the full shape — messy input, a lookup against ground truth, a judgment, a structured plan someone could act on — and it maps onto everyone's real work: dev support, customer support, founder ops, program ops.
Why a team? One agent isn't a system. Real work gets delegated: triage is one JOB; explaining the outage to your stakeholders is a DIFFERENT job — different audience, different voice, different failure modes. You don't bloat one agent's instructions; you hand the job to a teammate. So tonight your agent gets its first one: a scribe subagent it delegates the stakeholder update to. The repo is shaped for a team of agents — tonight you build the first one; the folders show where the rest go.
Ship tonight: a working Incident Triage Agent — your own persona, a validated JSON action plan instead of prose, one typed tool pulling real bundled data, a scribe teammate your agent delegates the stakeholder update to, proof it survives a crash, and either a live workers.dev URL or a deploy-ready build.
The six claims, in order (each checkpoint proves one): 1 · "It talks." 2 · "Prose is not an API." 3 · "Models shouldn't guess facts." 4 · "One agent isn't a system." 5 · "Memory must outlive the process." 6 · "If it only runs on your laptop, it's still a demo."
The stack, in one picture
`` your message (curl, from Terminal 2) ↓ Worker route /agents/triage/<conversation-id> ↓ your agent a Durable Object: instructions + typed tools + SQLite ledger ↓ └─ task → the scribe its subagent teammate (checkpoint 4) Workers AI the model — same network, no API key ``
- Your message — a plain web request; anything that speaks HTTP can talk to your agent.
- Worker route — the front door: Cloudflare compute that turns the conversation id in the URL into the name of exactly one object.
- Your agent (a Durable Object) — one per conversation: your instructions, your typed tools, and the SQLite ledger every turn is written to. This is the part that survives.
- The scribe (a subagent) — your agent's teammate: the model hands it ONE job through the framework's
tasktool, it sees only the prompt it's handed — never your conversation — and only its final answer comes back. It is NOT a second Durable Object or a second deploy: the build still emits exactly one class; the teammate ships inside it. - Workers AI model — the reasoning engine, running on the same network as your agent, billed to your own free account's daily Neurons.
The repo is shaped for a team
`` triage-agent/ ├─ src/ │ ├─ app.ts ← the front door: one router for every agent │ └─ agents/ │ ├─ triage/ ← tonight's agent. You build this. │ │ ├─ agent.ts ← the agent function │ │ ├─ schema.ts ← the action-plan schema (checkpoint 2) │ │ ├─ tools/lookup-incident.ts ← its hands (checkpoint 3) │ │ └─ subagents/scribe.ts ← its teammate (checkpoint 4) │ └─ shared/incidents.json ← data every teammate can read ├─ checkpoints/01-base … 06-deploy ← the universal undo └─ scripts/preflight.mjs · catchup.mjs ``
Empty-looking folders aren't clutter — they're the map. Each folder under src/agents/ is one agent: one teammate with one job, one Durable Object, one memory. Your customer-support or founder-ops variant is a sibling folder, same shape — copying triage/ into agents/<your-name>/ is Part 2 territory; tonight you only edit triage/.
Two terminals, from checkpoint 4 on
- Terminal 1 — runs the server (
npx vite dev). This is the one you're allowed to kill. - Terminal 2 — talks to it (
curlcommands). This is how you send messages.
Ctrl+C = hold the Ctrl key, tap C once. That's how you kill Terminal 1 on purpose.
On Windows? Use Command Prompt — and these curl lines
Use Command Prompt, not PowerShell (PowerShell's curl is a different tool). The multi-line curl blocks elsewhere on this page are written for macOS — on Windows, paste these one-line versions instead (same drills, same order, double quotes):
Checkpoint 4 — the lazy briefing (version A), then the fix (version B): `` curl -X POST "http://localhost:5173/agents/triage/team-1" -H "Content-Type: application/json" -d "{\"kind\": \"user\", \"body\": \"Triage INC-1003\"}" curl "http://localhost:5173/agents/triage/team-1" curl -X POST "http://localhost:5173/agents/triage/team-2" -H "Content-Type: application/json" -d "{\"kind\": \"user\", \"body\": \"Triage INC-1003\"}" curl "http://localhost:5173/agents/triage/team-2" ``
Drill A — start the conversation, then read it: `` curl -X POST "http://localhost:5173/agents/triage/break-me" -H "Content-Type: application/json" -d "{\"kind\": \"user\", \"body\": \"Triage INC-1003. My name is Alex.\"}" curl "http://localhost:5173/agents/triage/break-me" ` Drill A — after the kill + restart, the memory question: ` curl -X POST "http://localhost:5173/agents/triage/break-me" -H "Content-Type: application/json" -d "{\"kind\": \"user\", \"body\": \"What is my name, and which incident are we on?\"}" curl "http://localhost:5173/agents/triage/break-me" ` Drill B — the incident that doesn't exist: ` curl -X POST "http://localhost:5173/agents/triage/break-me" -H "Content-Type: application/json" -d "{\"kind\": \"user\", \"body\": \"URGENT!!! Triage INC-99999 right now, everything is on fire\"}" curl "http://localhost:5173/agents/triage/break-me" ` Drill C — trigger the broken model: ` curl -X POST "http://localhost:5173/agents/triage/break-me" -H "Content-Type: application/json" -d "{\"kind\": \"user\", \"body\": \"Triage INC-1005\"}" curl "http://localhost:5173/agents/triage/break-me" ` Deploy — talk to your live agent (swap <you> for your real URL — see checkpoints/06-deploy/README.md): ` curl -X POST "https://triage-agent.<you>.workers.dev/agents/triage/live-1" -H "Content-Type: application/json" -d "{\"kind\": \"user\", \"body\": \"Triage INC-1003\"}" curl "https://triage-agent.<you>.workers.dev/agents/triage/live-1" ``
Checkpoints — the universal undo
Lost, behind, or just want a clean slate? Run npm run catchup N. It's not cheating — it's how the pros work.
Say the number AND the name out loud ("catchup 4 — the team checkpoint") — it keeps a whole table from drifting one checkpoint apart.
One thing catchup resets: your custom persona. Re-pasting it takes 20 seconds — it's the ✏️ spot in agent.ts.
| # | Tag | Run this | Proves | Step-by-step guide | | --- | --- | --- | --- | --- | | 1 | 01-base | npm run catchup 1 | npx flue run src/agents/triage/agent.ts --message "hello" prints a reply | checkpoints/01-base/README.md | | 2 | 02-structured | npm run catchup 2 | a --json run shows data.actionPlan[0] with all four fields | checkpoints/02-structured/README.md | | 3 | 03-tool | npm run catchup 3 | INC-1003 quotes real data; INC-9999 gets a graceful "not found" | checkpoints/03-tool/README.md | | 4 | 04-team | npm run catchup 4 | the reply carries a STAKEHOLDER UPDATE that names real incident facts — the scribe was briefed | checkpoints/04-team/README.md | | 5 | 05-durable | npm run catchup 5 | kill-and-restart still remembers; garbage input never crashes — the drill-ready checkpoint | checkpoints/05-durable/README.md | | 6 | 06-deploy | npm run catchup 6 | npx vite build exits clean — guaranteed deployable | checkpoints/06-deploy/README.md |
Once checkpoint 6 is deployed, open your live URL in a browser — that page is your demo.
The guided build lives in the repo
The full step-by-step walkthrough lives where it belongs: beside the code it produces, inside the starter kit. Each checkpoint folder carries its own README — the claim it proves, what changes in the code and why, the paste-ready steps (including checkpoint 4's lazy-briefing fail and its fix, and checkpoint 5's kill-and-restart drills), and the "did it work?" proof.
Start at checkpoints/01-base/README.md and follow the chain — each README ends by pointing at the next, 01 through 06. The repo's root README lists all six in order under "The build, step by step."
This page is the quick reference you keep open beside it: the story and stack picture above, the two-terminal picture, the Windows curl lines, the checkpoint table (with each README in the last column), the error table, and the glossary below.
Where this goes (the roadmap)
Tonight you built the durable core — and its first teammate. Here's the map of what it grows into — five Flue features, one of which you already met:
- MCP — hands: connect tools that live outside your codebase (GitHub, Slack, databases) without writing the plumbing yourself.
- Skills — knowledge: packaged expertise your agent loads on demand, instead of one giant prompt that knows everything badly.
- Subagents — teammates: you wired your first one tonight (the scribe). Night 2 graduates to parallel fan-out: tool calls in one batch execute in parallel, so the model can launch several tasks at once — five independent checks become five concurrent child sessions.
- Channels — a front door:
flue add channel slackand your agent lives where your team already lives. - Schedules — initiative: the agent acts on a timer, without being asked.
You didn't just build a demo tonight — you made your first hire. It never sleeps, never forgets, and cost you nothing to bring on. The empty folders in src/agents/ are the org chart: this whole series is a software factory, and every build night adds one more station to the same floor.
Pick ONE of these as your next milestone and write it down before you leave — that's the sentence you take home. This map is Part 2: an advanced night.
Out of neurons?
Workers AI returns an error once your free daily 10,000 Neurons are used (resets daily — you won't get anywhere near this tonight). Fix: in src/agents/triage/agent.ts, make sure the model line reads exactly: ``ts useModel('cloudflare/@cf/zai-org/glm-4.7-flash'); ` Save, and if you've already deployed, run npx vite build && npx wrangler deploy` again.
The four vertical instruction templates
Below the glossary — swap the persona text (and, at home, the category list + sample data) to re-skin the same agent for dev support, customer support, founder ops, or program ops.
Top 10 errors → fix
| Error looks like | Fix | | --- | --- | | node -v shows 16 or 18, or "command not found" | Reinstall from the nodejs.org LTS installer — not nvm, not Homebrew. | | "Port 5173 is already in use" | An old vite dev is still running somewhere — close that terminal, or Ctrl+C it. | | Agent replies but never calls submit_action_plan | Your instructions need the line "You MUST call submitactionplan exactly once." Check the templates below. | | ToolInputValidationError | The model tried to call a tool with the wrong shape — that's the schema doing its job, not a bug. | | Two tools with the same name | Rename one — tool names must be unique per agent. | | Agent answers but never delegates to the scribe | Your instructions need the line "You MUST delegate to the scribe exactly once after submitting the action plan." — npm run catchup 4 restores a known-good version. | | Delegation turn feels hung | It's the longest turn of the night — a whole child session runs inside it. Wait it out; do NOT re-send (re-sends stack up and make it slower). | | Model name typo (does-not-exist, etc.) | Copy the model string exactly: cloudflare/@cf/zai-org/glm-4.7-flash. | | Free daily Neurons used up | See "Out of neurons?" above — swap back to the default model. | | wrangler login hangs on venue wifi | Copy the URL it prints into a browser manually, or try your phone hotspot. Deploy-ready is a fine outcome — finish at home. |
Glossary
- Agent — a program built around a model that can call tools and remember a conversation, not just answer once.
- Model — the AI that reads your messages and decides what to say or do next.
- System prompt / instructions — the persona and rules you give the agent, in plain English.
- Tool — a function the model is allowed to call, with a strict, typed shape for its input.
- Subagent — a teammate agent your agent can hand one job to. Tonight's is the scribe, which writes the stakeholder update.
- Delegation — the model deciding to hand a job to a teammate (via the built-in task tool); only the teammate's final answer comes back — the teammate can't see your conversation.
- Schema — the exact shape a piece of data must have (which fields, which types) — the "keypad" a tool's phone dials with.
- Structured output — the agent filling out a form (validated JSON) instead of writing an essay.
- API — a way for two pieces of software to talk to each other in a fixed, predictable shape — why "prose is not an API."
- Durable Object — your agent's private saved-game file: one per conversation, always in the same place.
- SQLite — the tiny database living inside that Durable Object, recording every turn.
- Conversation id — the name of one specific conversation (e.g.
break-me) — how you find it again. - Deploy — putting your agent on the public internet, on your own account.
- Reference agent — a copy of tonight's agent, deployed ahead of time on the shared account, kept live as the fallback demo if a laptop won't cooperate.
- Worker — the Cloudflare service that runs your code and routes requests to the right Durable Object.
- Neuron — the token meter for Workers AI — you get 10,000 free per day.
- Terminal — the text window where you type commands instead of clicking.
- localhost — "this machine" — the address your own computer answers to while testing.
- curl — a command-line way to send a web request, used all night to talk to your agent.
Template library
Copy a starting point, paste it into your assistant's instructions, then make it yours.
The starter's out-of-the-box persona — on-call engineering triage.
You are the on-call triage agent for our engineering team. Before you say anything, call lookup_incident to pull the real incident data — never guess. Classify severity as low, medium, high, or critical, based on what the data actually says: how many users are affected, whether it's a security issue, whether a recent change caused it. category is one of: outage, bug, regression, security, performance. Write a summary a teammate could read in five seconds, and 2-4 concrete next steps they could start on right now. You MUST call submit_action_plan exactly once, with your complete action plan as its input. Never answer in plain prose instead — always submit the plan.
Swap this in when the tickets are customers, not outages.
You are a customer support triage agent. Before you say anything, call lookup_incident to pull the real ticket details — never guess at what the customer is describing. Classify severity as low, medium, high, or critical, based on how much it's costing the customer: a typo in an email is low, an order that never arrived plus a charge that already went through is high or critical. category is one of: billing, shipping and delivery, account access, product defect, general question. Write a summary a support lead could read in five seconds, and 2-4 concrete next steps — including anything the customer should be told right away. You MUST call submit_action_plan exactly once, with your complete action plan as its input. Never answer in plain prose instead — always submit the plan.
Swap this in for a founder's own messy inbound — investor emails, hiring asks, legal fire drills.
You are a founder's ops triage agent — you turn a messy inbound ask into a plan they can act on in one read. Before you say anything, call lookup_incident to pull whatever's actually on record for this — never guess. Classify severity as low, medium, high, or critical, based on what happens if it's ignored for a week: a newsletter reply is low, a term sheet deadline or a compliance letter is high or critical. category is one of: fundraising, hiring, legal and compliance, product, cash flow. Write a summary the founder could read in five seconds, and 2-4 concrete next steps, in the order they should happen. You MUST call submit_action_plan exactly once, with your complete action plan as its input. Never answer in plain prose instead — always submit the plan.
Swap this in for running a build night, cohort, or community program like this one.
You are the program-ops triage agent for a community build night — participant issues, vendor problems, logistics, safety. Before you say anything, call lookup_incident to pull the real report — never guess at what happened. Classify severity as low, medium, high, or critical, based on how many people it affects and whether it's a safety issue: a broken projector cable is low, a safety concern or a room double-booked at capacity is high or critical. category is one of: participant issue, vendor or venue, curriculum, logistics, safety. Write a summary a floater could read in five seconds, and 2-4 concrete next steps they could act on before the next block starts. You MUST call submit_action_plan exactly once, with your complete action plan as its input. Never answer in plain prose instead — always submit the plan.