Paste this prompt into the OpenCode TUI to let your AI agent walk you through this lesson:
Step 0: Navigate to the lesson directory
Open your terminal and navigate to the lesson-10-agents-sdk directory:
cd ..\lesson-10-agents-sdk
This directory contains the app pre-configured for the Agents SDK lesson. The STATE variable is already set to state-5-agent in wrangler.jsonc.
What is the Agents SDK?
Agents SDK is Cloudflare’s framework for building stateful, durable AI agents on Workers. The core primitive is the Agent class — a Durable Object subclass with built-in support for:
- Persistent state —
this.setState()andthis.statebacked by the DO’s SQLite storage. State survives between requests automatically. - RPC methods — Public methods on your Agent class are callable from your Worker via the DO stub. No REST API boilerplate needed.
- Lifecycle hooks —
onStart(),onConnect(),onClose()for managing the agent’s lifecycle. - Scoped tools — Define what tools and capabilities each agent has access to. Different agents can have different tool sets.
The key insight: an Agent is a Durable Object with opinions. Instead of writing raw DO code with storage APIs and WebSocket handlers, the Agent class gives you a structured pattern for state, communication, and orchestration.
Why agents instead of plain Workers AI calls?
In lesson 09, you called env.AI.run() directly from the Worker. That works, but it’s stateless — every investigate call starts from scratch. The Worker doesn’t remember what it analyzed before, can’t hold intermediate results, and can’t run multi-step workflows.
An Agent-based approach gives you:
- Durable state — The agent remembers what it has investigated. If you investigate an incident, leave, and come back, the results are still there.
- Composability — You can add sub-agents, tools, and multi-step flows. The Coordinator pattern in lesson 11 becomes natural.
- Lifecycle — The agent can run workflows that span multiple requests. Start an investigation now, receive results later.
- Scoped tools — Each agent only has access to what it needs. The triage agent can call Workers AI; the response agent can read playbooks. Separation of concerns.
The Agent class pattern
Here’s the simplified structure of the TriageAgent class:
import { Agent } from "agents";
export interface TriageState {
incident_id: string;
status: "idle" | "investigating" | "triaged" | "responded" | "error";
triage_card?: object;
updated_at: string;
}
export class TriageAgent extends Agent<Env, TriageState> {
initialState: TriageState = {
incident_id: "",
status: "idle",
updated_at: new Date().toISOString(),
};
// RPC: invoked from the Worker via getAgentByName(...).investigate(incident)
async investigate(incident: any): Promise<TriageState> {
this.setState({
incident_id: incident.incident_id ?? "unknown",
status: "investigating",
updated_at: new Date().toISOString(),
});
// Run the triage harness with Workers AI (single call here; lesson 11
// expands this to a coordinator + parallel sub-agents).
const triageCard = await this.runTriage(this.env.AI, incident);
const finalState: TriageState = {
incident_id: incident.incident_id ?? "unknown",
status: "responded",
triage_card: triageCard,
updated_at: new Date().toISOString(),
};
this.setState(finalState); // persisted to the agent's SQLite storage
return finalState;
}
}
The real triage-agent.ts you’ll open already runs the full harness (coordinator + four
sub-agents + synthesizer + response agent) — this snippet is trimmed to show the Agent shape.
Key things to notice:
extends Agent<Env, State>— The generic parameters type the environment bindings and the agent’s state shape.initialState— Defines the default state for a new agent instance. When the agent is first created,this.statereturns this value.this.setState()— Persists state to the DO’s SQLite storage. The state survives between requests and even between Worker restarts.this.env— The agent has access to the same environment bindings as the Worker. It can call D1, Workers AI, or any other binding.investigate()— This is an RPC method. The Worker calls it via the DO stub.
How the Worker routes to the agent
In src/server.ts, the investigate handler now routes through the agent instead of calling Workers AI directly:
import { getAgentByName } from "agents";
// Resolve (or create) this incident's TriageAgent instance by name,
// then call its investigate() method directly over RPC.
const agent = await getAgentByName(env.TRIAGE_AGENT, incidentId);
const result = await agent.investigate(incident);
getAgentByName(env.TRIAGE_AGENT, incidentId) is the key — it returns the agent instance for that name, creating it on first use. Every request for the same incident resolves to the same agent instance, with the same persisted state. Call it once or a hundred times — the same TriageAgent handles it.
Step 1: Review the TriageAgent class
Open lesson-10-agents-sdk\src\agents\triage-agent.ts and read through the code. Identify:
- The
initialStatedefinition - The
investigate()method - Where
this.setState()is called and what state transitions happen - How
this.env.AIis used inside the agent
The agent wraps the same Workers AI call from lesson 09, but now it’s inside a durable context with persistent state.
Step 2: Redeploy
Deploy the updated configuration:
npm run deploy
Wrangler will create the TriageAgent’s Durable Object class during deployment. You should see the deployment succeed with the new class registered.
Step 3: Verify the agent-driven flow
- Open your app and navigate to the incident queue.
- Click on any incident and click “Investigate.”
- The analysis should appear as before — same quality, same structure.
The difference is under the hood: the analysis now runs through a durable agent lifecycle. The TriageAgent persists its state, so if you reload the page after investigating, the results are still there. In lesson 09, that state would have been lost.
Try this: investigate an incident, go back to the queue, then re-open the same incident. The triage results should still be visible without making another AI call. That’s the agent’s durable state at work.
What comes next
Right now, the TriageAgent makes a single Workers AI call — the same pattern as lesson 09, just wrapped in an agent. In lesson 11, you’ll replace that single call with a Coordinator + 4 parallel sub-agents pattern. The agent’s investigate() method will fan out to four specialized analyzers (Command-Line, Identity, Network, Activity), collect their results in parallel, and synthesize them into a unified triage card.
The Agents SDK makes this decomposition natural — each sub-agent analysis can be a separate function with its own system prompt, and the Coordinator orchestrates them with Promise.all.