agents Documentation

Build and deploy AI Agents on Cloudflare

5,267 stars TypeScript TypeScript 1 file ยท ~10,530 tokens #agents#ai#cloudflare#durable-objects#workflows
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy agents on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name agents --restart always livekit/agents:latest
Configured Services & Ports
โš™๏ธ toxiproxy
image: build locally
โš™๏ธ app
image: build locally
docker-compose.yml
yaml (docker-compose.yml)
services:
  toxiproxy:
    build:
      context: ..
      dockerfile: tests/Dockerfile.toxiproxy
    command: ["toxiproxy-server", "-host", "0.0.0.0", "-port", "8474"]
    environment:
      - LOG_LEVEL=info
    # ports:
    #   - "8474:8474"
    #   - "443:443"
    networks:
      toxinet:
        ipv4_address: 172.30.0.10

  app:
    build:
      context: ..
      dockerfile: tests/Dockerfile.tests
    command: tail -f /dev/null
    volumes:
      - ../tests:/app/tests
      - ../livekit-agents:/app/livekit-agents
      - ../livekit-plugins:/app/livekit-plugins
      # workspace members: without them uv sees a stale lock and re-resolves
      - ../examples:/app/examples
      - ../pyproject.toml:/app/pyproject.toml
      - ../uv.lock:/app/uv.lock
      - ../lk_dump:/app/lk_dump
    environment:
      # debug
      - LK_DUMP_TTS=1
      - LK_OPENAI_DEBUG=1

      - LIVEKIT_URL
      - LIVEKIT_API_KEY
      - LIVEKIT_API_SECRET
      - DEEPGRAM_API_KEY
      - OPENAI_API_KEY
      - ELEVEN_API_KEY
      - CARTESIA_API_KEY
      - AZURE_SPEECH_KEY
      - AZURE_SPEECH_REGION
      - GOOGLE_CREDENTIALS_JSON
      - ANTHROPIC_API_KEY
      - GROQ_API_KEY
      - ASSEMBLYAI_API_KEY
      - FAL_KEY
      - PLAYHT_API_KEY
      - PLAYHT_USER_ID
      - GOOGLE_API_KEY
      - RIME_API_KEY
      - SPEECHMATICS_API_KEY
      - GOOGLE_APPLICATION_CREDENTIALS
      - AWS_ACCESS_KEY_ID
      - AWS_SECRET_ACCESS_KEY
      - NEUPHONIC_API_KEY
      - RESEMBLE_API_KEY
      - SPEECHIFY_API_KEY
      - HUME_API_KEY
      - SPITCH_API_KEY
      - LMNT_API_KEY
      - INWORLD_API_KEY
      - MISTRAL_API_KEY
      - MURF_API_KEY

    extra_hosts:
      - "polly.us-west-2.amazonaws.com:172.30.0.10"
      - "westus.tts.speech.microsoft.com:172.30.0.10"
      - "api.cartesia.ai:172.30.0.10"
      - "api.deepgram.com:172.30.0.10"
      - "api.elevenlabs.io:172.30.0.10"
      - "api.sws.speechify.com:172.30.0.10"
      - "texttospeech.googleapis.com:172.30.0.10"
      - "api.groq.com:172.30.0.10"
      - "api.neuphonic.com:172.30.0.10"
      - "api.openai.com:172.30.0.10"
      - "api.play.ht:172.30.0.10"
      - "f.cluster.resemble.ai:172.30.0.10"
      - "websocket.cluster.resemble.ai:172.30.0.10"
      - "users.rime.ai:172.30.0.10"
      - "api.rime.ai:172.30.0.10"
      - "api.hume.ai:172.30.0.10"
      - "api.lmnt.com:172.30.0.10"
      - "api.inworld.ai:172.30.0.10"
      - "api.mistralai.com:172.30.0.10"
      - "api.murf.ai:172.30.0.10"
    networks:
      - toxinet

networks:
  toxinet:
    driver: bridge
    ipam:
      config:
        - subnet: "172.30.0.0/16"

1. Project Overview & Quickstart (cloudflare/agents)

File: README.md

Cloudflare Agents


Agents are persistent, stateful execution environments for agentic workloads, powered by Cloudflare Durable Objects. Each agent has its own state, storage, and lifecycle โ€” with built-in support for real-time communication, scheduling, AI model calls, MCP, workflows, and more.

Agents hibernate when idle and wake on demand. You can run millions of them โ€” one per user, per session, per game room โ€” each costs nothing when inactive.

sh
npm create cloudflare@latest -- --template cloudflare/agents-starter

Or add to an existing project:

sh
npm install agents

Read the docs โ€” getting started, API reference, guides, and more.

Quick Example

A counter agent with persistent state, callable methods, and real-time sync to a React frontend:

typescript
// server.ts
import { Agent, routeAgentRequest, callable } from "agents";

export type CounterState = { count: number };

export class CounterAgent extends Agent<Env, CounterState> {
  initialState = { count: 0 };

  @callable()
  increment() {
    this.setState({ count: this.state.count + 1 });
    return this.state.count;
  }

  @callable()
  decrement() {
    this.setState({ count: this.state.count - 1 });
    return this.state.count;
  }
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    return (
      (await routeAgentRequest(request, env)) ??
      new Response("Not found", { status: 404 })
    );
  }
};
tsx
// client.tsx
import { useAgent } from "agents/react";
import { useState } from "react";
import type { CounterAgent, CounterState } from "./server";

function Counter() {
  const [count, setCount] = useState(0);

  const agent = useAgent<CounterAgent, CounterState>({
    agent: "CounterAgent",
    onStateUpdate: (state) => setCount(state.count)
  });

  return (
    
      {count}
       agent.stub.increment()}>+
       agent.stub.decrement()}>-
    
  );
}

State changes sync to all connected clients automatically. Call methods like they're local functions.

The agent is a Durable Object, so it needs a binding and a SQLite migration in wrangler.jsonc:

jsonc
{
  "name": "counter",
  "main": "server.ts",
  "compatibility_date": "2026-06-11",
  "compatibility_flags": ["nodejs_compat"],
  "durable_objects": {
    "bindings": [{ "name": "CounterAgent", "class_name": "CounterAgent" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["CounterAgent"] }]
}

Features

Feature Description
Persistent State Syncs to all connected clients, survives restarts
Callable Methods Type-safe RPC via the @callable() decorator
Sub-agents Parent/child DO composition via facets, nested routing, and typed parent lookup
Agent Tools Run chat-capable sub-agents as tools with streaming child timelines
Scheduling One-time, recurring, and cron-based tasks
WebSockets Real-time bidirectional communication with lifecycle hooks
AI Chat Message persistence, resumable streaming, server/client tool execution
MCP Act as MCP servers or connect as MCP clients (HTTP, SSE, RPC, elicitation)
WebMCP Expose page-local tools or bridge remote MCP tools to browser agents
Workflows Durable multi-step tasks with human-in-the-loop approval
Email Send, receive, and reply via Cloudflare Email Service
Voice Continuous STT, streaming TTS, VAD, interruption, SFU utilities
Browser Agents Run agents in the browser tab with agents/browser
Code Mode LLMs generate executable TypeScript instead of individual tool calls
Sandboxed Execution Run generated code inside an isolated Worker with a virtual filesystem
x402 Payments Pay-per-call APIs and tools via the x402 protocol
Observability Built-in tracing, metrics, and structured logs
SQL Direct SQLite queries via Durable Objects
React Hooks useAgent, useAgentChat, useVoiceAgent for frontend integration
Vanilla JS Client AgentClient and VoiceClient for non-React environments

Packages

Package Description
agents Core SDK โ€” Agent class, routing, state, scheduling, MCP, email, workflows, x402, browser agents
@cloudflare/ai-chat Higher-level AI chat โ€” persistent messages, resumable streaming, tool execution
@cloudflare/think Opinionated chat agent base โ€” agentic loop, stream resumption, client tools, workspace tools
@cloudflare/codemode LLMs write executable code that calls your tools, instead of one tool call at a time
@cloudflare/shell Sandboxed JS execution + virtual filesystem (Workspace) for agents
@cloudflare/voice Voice pipeline โ€” STT, TTS, VAD, streaming, SFU utilities
@cloudflare/worker-bundler Build and bundle Workers at runtime, for use with the Worker Loader binding
hono-agents Hono middleware for adding agents to Hono apps

AI-chat modules used to live in agents/ai-chat-agent, agents/chat, agents/ai-react, and agents/ai-types. Those entry points still re-export, but they're deprecated โ€” import from @cloudflare/ai-chat directly. New chat-from-scratch projects should look at @cloudflare/think.

Examples

The examples/ directory has 30+ self-contained demos. A non-exhaustive tour:

Examples using the OpenAI Agents SDK live in openai-sdk/. Work-in-progress experiments live in experimental/ (no stability guarantees).

Run any example locally:

sh
cd examples/playground
npm start

Documentation

  • Full docs on developers.cloudflare.com
  • docs/ directory in this repo (synced upstream)
  • Anthropic Patterns guide โ€” sequential, routing, parallel, orchestrator, evaluator
  • Human-in-the-Loop guide โ€” approval workflows with pause/resume
  • design/ โ€” architecture and design decision records (chat API, sub-agents, agent tools, workspace, voice, browser tools, retries, and more)

Repository Structure

Directory Description
packages/agents/ Core SDK
packages/ai-chat/ AI chat layer
packages/think/ Opinionated chat agent base
packages/codemode/ Code Mode
packages/shell/ Sandboxed execution + filesystem
packages/voice/ Voice pipeline
packages/worker-bundler/ Runtime Workers bundler
packages/hono-agents/ Hono integration
examples/ Self-contained demo apps
experimental/ Work-in-progress experiments (not published)
openai-sdk/ Examples using the OpenAI Agents SDK
guides/ In-depth pattern tutorials
docs/ Markdown docs synced to developers.cloudflare.com
site/ Deployed websites (agents.cloudflare.com, AI playground)
design/ Architecture and design decision records
scripts/ Repo-wide tooling

Development

Node 24+ required. pnpm workspaces with Nx for task orchestration, caching, and affected detection.

sh
pnpm install                 # install all workspaces
pnpm run build               # build all packages (Nx, cached, dependency-ordered)
pnpm run check               # sherif + export checks + oxfmt + oxlint + typecheck
pnpm run test                # vitest + vitest-pool-workers (Workers runtime)
pnpm run test:react          # Playwright-based React hook tests
pnpm exec nx affected -t build # build only what changed
pnpm exec nx affected -t test  # test only what changed

Changes to packages/ need a changeset:

sh
pnpm exec changeset

See AGENTS.md for deeper contributor guidance.

Contributing

We are not accepting external pull requests at this time โ€” the SDK is evolving quickly and we want to keep the surface area manageable. That said, we'd love to hear from you:

License

MIT


File: docs/index.md

Cloudflare Agents SDK Documentation

Documentation is grouped by the package that owns it:

  • agents โ€” Durable Object agents, state, routing, scheduling, sessions, MCP, and shared primitives
  • @cloudflare/think โ€” opinionated agent harness built on Agents, Codemode, and Shell
  • @cloudflare/codemode โ€” sandboxed code execution, tool providers, connectors, approvals, and snippets
  • @cloudflare/shell โ€” durable workspaces and filesystem tools
  • @cloudflare/voice โ€” voice agents, clients, audio pipelines, and provider contracts

Each package listed above includes its own documentation under docs/. Other published packages use their package README as their documentation entry point. Package indexes name relevant dependency documentation by package path, for example agents/docs/index.md or @cloudflare/codemode/docs/index.md.


File: examples/tictactoe/README.md

Tic Tac Toe

A simple demo showing a smart agent playing Tic Tac Toe against a human. The AI opponent uses GPT-4o to make strategic moves.

Quick Start

Copy .env.example to .env and fill in your OpenAI API key.

bash
npm i && npm start

Visit http://localhost:5174 to play against the agent.


File: examples/playground/README.md

Agents SDK Playground

An interactive demo application showcasing the major feature areas of the Cloudflare Agents SDK. Use it to learn the SDK, test features, and understand how agents work.

Getting Started

bash
# Install dependencies
npm install

# Start the development server
npm start

Visit http://localhost:5173 to explore the playground.

Features

The playground is organized into feature categories, each with interactive demos:

Core

Demo Description
State Real-time state synchronization with setState() and onStateChanged()
Callable RPC methods using the @callable decorator
Streaming Streaming responses with StreamingResponse
Schedule One-time and recurring task scheduling
Connections WebSocket lifecycle, client tracking, and broadcasting
SQL Direct SQLite queries using this.sql template literal
Routing Agent naming strategies (per-user, shared, per-session)
Readonly Read-only agent access
Retry Retry with backoff and shouldRetry

Durable Execution

Demo Description
Fibers Long-running work with checkpoints and recovery hooks

Multi-Agent

Demo Description
Supervisor Manager-child agent pattern using getAgentByName() for RPC
Chat Rooms Lobby with room agents for multi-user chat
Workers Fan-out parallel processing
Pipeline Chain of responsibility pattern

AI

Demo Description
Chat AIChatAgent with message persistence and streaming
Tools Client-side tool execution with confirmation flows
Codemode AI code generation and editing
Agent Tools Delegate chat work to child agents with inline timelines
Think + Shell Assistant runtime with durable workspace and state tools

MCP (Model Context Protocol)

Demo Description
Server Creating MCP servers with tools, resources, and prompts
Client Connecting to external MCP servers
OAuth OAuth authentication for MCP connections
Advanced MCP Transports, elicitation, codemode, and x402 patterns

Workflows

Demo Description
Basic Interactive multi-step workflow simulation with progress
Approval Human-in-the-loop approval/rejection patterns

Email

Demo Description
Receive Receive real emails via Cloudflare Email Routing
Secure Replies Send HMAC-signed replies for secure routing back to the agent

Note: Email demos require deployment to Cloudflare. A warning banner is shown when running locally.

Product Integrations

Demo Description
Integration Stories Email, webhooks, push, A2A, x402, and browser-tool stories

Project Structure

text
playground/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ demos/           # Demo pages and agent definitions
โ”‚   โ”‚   โ”œโ”€โ”€ core/        # State, callable, streaming, schedule, etc.
โ”‚   โ”‚   โ”œโ”€โ”€ ai/          # Chat, tools, codemode
โ”‚   โ”‚   โ”œโ”€โ”€ durable/     # Fibers and durable execution
โ”‚   โ”‚   โ”œโ”€โ”€ mcp/         # Server, client, OAuth
โ”‚   โ”‚   โ”œโ”€โ”€ multi-agent/ # Supervisor, chat rooms, workers, pipeline
โ”‚   โ”‚   โ”œโ”€โ”€ workflow/    # Basic, approval
โ”‚   โ”‚   โ”œโ”€โ”€ email/       # Receive, secure replies
โ”‚   โ”‚   โ””โ”€โ”€ integrations/ # Product integration explainers
โ”‚   โ”œโ”€โ”€ components/      # Shared UI components
โ”‚   โ”œโ”€โ”€ layout/          # App layout (sidebar, wrapper)
โ”‚   โ”œโ”€โ”€ hooks/           # React hooks (theme, userId, logs)
โ”‚   โ”œโ”€โ”€ pages/           # Home page
โ”‚   โ”œโ”€โ”€ client.tsx       # Client entry point
โ”‚   โ”œโ”€โ”€ server.ts        # Worker entry point
โ”‚   โ””โ”€โ”€ styles.css       # Tailwind styles
โ”œโ”€โ”€ testing.md           # Manual testing guide
โ”œโ”€โ”€ TODO.md              # Planned improvements
โ””โ”€โ”€ wrangler.jsonc       # Cloudflare configuration

Testing

See testing.md for a comprehensive guide on manually testing every feature.

Configuration

Each demo has its own Durable Object agent. The full list of agents and workflows is defined in wrangler.jsonc.

Environment Variables

For the email demos, set EMAIL_SECRET for HMAC-signed replies:

bash
# Production
wrangler secret put EMAIL_SECRET

For local development, add it to a .env file:

text
EMAIL_SECRET=your-secret-for-email-signing

Email Routing Setup

To test the email demos with real emails:

  1. Deploy to Cloudflare: npm run deploy
  2. Go to Cloudflare Dashboard โ†’ Email โ†’ Email Routing
  3. Add a routing rule to forward emails to your Worker
  4. Send emails to:

Dark Mode

Click the theme toggle in the sidebar footer to switch between Light and Dark themes.


File: examples/mcp-client/README.md

MCP Client

An Agent that acts as an MCP client โ€” dynamically connects to remote MCP servers, handles OAuth authentication, and aggregates tools, prompts, and resources from all connected servers.

What it demonstrates

  • addMcpServer / removeMcpServer โ€” managing MCP server connections from an Agent
  • onMcpUpdate โ€” real-time state updates pushed to the React frontend via WebSocket
  • this.mcp.configureElicitationHandlers โ€” handles both Stateless Elicitation (input_required through MRTR) and Legacy Elicitation (pushed elicitation/create); the Agent broadcasts requests to the browser and the human's answer resolves the pending operation via a @callable method
  • OAuth popup flow โ€” configureOAuthCallback with a custom handler that closes the popup after auth
  • agentFetch โ€” making HTTP requests to the Agent's custom endpoints from the client

Running

sh
npm install
npm run start

The UI lets you add MCP server URLs, see their connection state, browse their tools, prompts, and resources, and run tools. If a tool call triggers an elicitation, a card appears asking for your input.

To test with an authenticated server, run mcp-worker-authenticated alongside this example and add its URL. For Stateless Elicitation, run mcp-elicitation-mrtr and call increase-counter. To test pushed form- and URL-mode Legacy Elicitation, run mcp-elicitation. Each endpoint is /mcp (for example, http://localhost:8787/mcp).

Environment variables

Copy .env.example to .env if you need to override the OAuth callback host:

sh
cp .env.example .env

How it works

Server side

The Agent manages MCP connections via the built-in mcp property:

typescript
export class MyAgent extends Agent {
  onStart() {
    this.mcp.configureOAuthCallback({
      customHandler: (result) => {
        if (result.authSuccess) {
          return new Response("<script>window.close();</script>", {
            headers: { "content-type": "text/html" }
          });
        }
        return new Response(`Auth failed: ${result.authError}`, {
          status: 400
        });
      }
    });
  }

  async onRequest(request) {
    // Custom endpoints for the frontend
    if (url.pathname.endsWith("add-mcp")) {
      const { name, url } = await request.json();
      await this.addMcpServer(name, url);
      return new Response("Ok");
    }
  }
}

Client side

The React frontend uses useAgent with onMcpUpdate to receive real-time server state:

typescript
const agent = useAgent({
  agent: "my-agent",
  name: sessionId,
  onMcpUpdate: (mcpServers) => setMcpState(mcpServers),
  onOpen: () => setConnected(true)
});
  • mcp โ€” stateful MCP server (good target to connect to)
  • mcp-worker-authenticated โ€” authenticated server (tests the OAuth flow)

File: examples/mcp/README.md

Stateful MCP Server

A deprecated, feature-frozen stateful MCP server retained only for existing deployments during migration. State persists across requests through McpAgent and a Durable Object. New servers should use the stateless SDK v2 createMcpHandler path.

What it demonstrates

  • McpAgent โ€” the Agents SDK class for building MCP servers with persistent state
  • Tools โ€” registering an add tool that modifies the counter
  • Resources โ€” exposing the counter value as an MCP resource
  • State management โ€” setState and onStateChanged for durable state
  • Streamable HTTP transport โ€” the default transport for McpAgent

Running

sh
pnpm install
pnpm start

Open the browser to see the built-in tool tester. You can also connect with the MCP Inspector โ€” set the transport to Streamable HTTP and URL to http://localhost:5173/mcp.

How it works

The McpAgent class extends Agent with MCP protocol support. Define your tools and resources in the init() method:

typescript
export class MyMCP extends McpAgent<Env, State, {}> {
  server = new McpServer({ name: "Demo", version: "1.0.0" });
  initialState: State = { counter: 1 };

  async init() {
    this.server.resource("counter", "mcp://resource/counter", (uri) => ({
      contents: [{ text: String(this.state.counter), uri: uri.href }]
    }));

    this.server.registerTool(
      "add",
      { description: "Add to the counter", inputSchema: { a: z.number() } },
      async ({ a }) => {
        this.setState({ ...this.state, counter: this.state.counter + a });
        return {
          content: [{ type: "text", text: `Total: ${this.state.counter}` }]
        };
      }
    );
  }
}

export default MyMCP.serve("/mcp", { binding: "MyMCP" });

File: examples/cross-domain/README.md

Cross-Domain Communication Demo

A simple demo showing WebSocket and HTTP communication between a React client and Cloudflare Worker server, when running on different domains. tl;dr - make sure you pass cors: true to routeAgentRequest.

Quick Start

Run the front end and agents server:

bash
npm i && npm start

File: guides/human-in-the-loop/README.md

Human in the Loop with Cloudflare Agents

This guide demonstrates human-in-the-loop patterns using AIChatAgent from @cloudflare/ai-chat. Tools can require user approval before executing, and tools that need browser APIs are handled client-side.

Key Patterns

  1. needsApproval -- Server-side tools that pause for user approval before executing
  2. onToolCall -- Client-side tool execution for tools that need browser APIs
  3. addToolApprovalResponse -- Client responds to approval requests

Server

Tools are defined on the server. Use needsApproval for tools requiring human confirmation:

ts
import { AIChatAgent } from "@cloudflare/ai-chat";
import { streamText, convertToModelMessages, tool } from "ai";
import { z } from "zod";

export class HumanInTheLoop extends AIChatAgent {
  async onChatMessage() {
    const result = streamText({
      model: openai("gpt-4o"),
      messages: await convertToModelMessages(this.messages),
      tools: {
        // Requires approval -- needsApproval: true
        getWeather: tool({
          inputSchema: z.object({ city: z.string() }),
          needsApproval: true,
          execute: async ({ city }) => `The weather in ${city} is sunny.`
        }),

        // Client-side tool -- no execute function
        getLocalTime: tool({
          inputSchema: z.object({ location: z.string() })
        }),

        // Automatic -- no approval, runs server-side
        getNews: tool({
          inputSchema: z.object({ location: z.string() }),
          execute: async ({ location }) => `${location} news: all good!`
        })
      },
      maxSteps: 5
    });

    return result.toUIMessageStreamResponse();
  }
}

Client

Handle approvals with addToolApprovalResponse and client-side tools with onToolCall:

tsx
import { useAgent } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";

function Chat() {
  const agent = useAgent({ agent: "human-in-the-loop" });

  const { messages, sendMessage, addToolApprovalResponse } = useAgentChat({
    agent,
    onToolCall: async ({ toolCall, addToolOutput }) => {
      if (toolCall.toolName === "getLocalTime") {
        const time = new Date().toLocaleTimeString();
        addToolOutput({ toolCallId: toolCall.toolCallId, output: time });
      }
    }
  });

  return (
    
      {messages.map((msg) => (
        
          {msg.parts.map((part, i) => {
            if (part.type === "text") return {part.text};

            // Tool approval request
            if ("approval" in part && part.state === "approval-requested") {
              return (
                
                  Approve {getToolName(part)}?
                  
                      addToolApprovalResponse({
                        id: part.approval.id,
                        approved: true
                      })
                    }
                  >
                    Approve
                  
                  
                      addToolApprovalResponse({
                        id: part.approval.id,
                        approved: false
                      })
                    }
                  >
                    Reject
                  
                
              );
            }

            return null;
          })}
        
      ))}
    
  );
}

Running

bash
npm install
npm run dev

Requires OPENAI_API_KEY in .env.

Learn More


File: guides/anthropic-patterns/README.md

Agent Patterns Demo

This project demonstrates different patterns for building AI agents, based on Anthropic's research, implemented using Cloudflare's Durable Objects and the AI SDK.

Overview

The demo showcases five fundamental patterns for building AI agents:

  1. Prompt Chaining: Sequential processing where each step builds on the previous
  2. Routing: Intelligent classification and routing of tasks
  3. Parallelization: Concurrent execution of multiple subtasks
  4. Orchestrator-Workers: Dynamic task breakdown and delegation
  5. Evaluator-Optimizer: Iterative improvement through feedback loops

Each pattern is implemented as a Durable Object, providing persistence, real-time updates, and scalability.

Architecture

Frontend

  • React application with TypeScript
  • Real-time WebSocket connections to Durable Objects (powered by PartySocket, a reconnecting websocket library with a react hook)
  • Dark mode support
  • Responsive design

Backend

  • Cloudflare Durable Objects + PartyServer for agent state management
  • WebSocket connections for live updates
  • OpenAI integration via AI SDK
  • AI SDK for defining agent workflows
  • AI Gateway for caching and rate limiting

Features

  • Live Agent Interaction: Test each pattern with real inputs
  • Real-time Updates: See agent progress as it happens
  • Persistent State: Agents continue running even if you close the browser
  • Global Scaling: Runs on Cloudflare's edge network
  • Bonus: Dark Mode: Supports system preferences and manual toggle

Getting Started

  1. Clone the repository

  2. Install dependencies:

    bash
    npm install
  3. Create a .env file with your API keys:

    text
    OPENAI_API_KEY=your_key_here
    AI_GATEWAY_TOKEN=your_key_here
  4. Edit wrangler.json to set your account ID and ai gateway ID details:

    json
    "account_id": "your_account_id_here"
    "vars": {
      "AI_GATEWAY_ACCOUNT_ID": "your_account_id_here",
      "AI_GATEWAY_ID": "your_gateway_id_here"
    }
  5. Run the development server:

    bash
    npm start

Deployment

The application is designed to be deployed to Cloudflare Workers:

bash
npm run deploy

Implementation Details

Durable Objects

We have an createAgent factory function that creates a new Durable Object class for a given pattern/workflow. The template for the class includes basic state management and websocket communication. So by calling createAgent(async () => {/* run the pattern */}) we get a new Durable Object class that can be used to create an instance. We create the 5 types of agents and export them from the worker, and wire them up as bindings in wrangler.jsonc.

WebSocket Communication

The frontend maintains WebSocket connections to each Durable Object instance. The websocket sends state updates to the frontend.

typescript
const socket = usePartySocket({
  party: type,
  room: "default-room",
  onMessage: (e) => {
    const data = JSON.parse(e.data);
    switch (data.type) {
      case "state":
        setWorkflowState(data.state);
        break;
    }
  }
});

The frontend also triggers the workflow by sending a run message to the websocket with initial input.

State Management

Each Durable Object maintains its own state:

typescript
state: {
  isRunning: boolean;
  output: any;
} = {
  isRunning: false,
  output: undefined,
};

Read more in the source code.

Why Durable Objects?

Durable Objects provide several key benefits for hosting AI agents:

  1. Persistence: Agents continue running even when clients disconnect
  2. Real-time Updates: WebSocket connections enable live progress streaming
  3. Global Scale: Automatic distribution across Cloudflare's network
  4. Flexible Triggers: Can be activated by HTTP, cron jobs, or other events
  5. Memory Isolation: Each agent runs in its own environment
  6. Cost Effective: Pay only for actual compute time used

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests.

License

MIT License - feel free to use this code in your own projects.

Acknowledgments


File: packages/hono-agents/README.md

hono-agents

๐Ÿ”ฅ Hono โจ‰ ๐Ÿง  Cloudflare Agents

Add intelligent, stateful AI agents to your Hono app. Create persistent AI agents that can think, communicate, and evolve over time, all integrated seamlessly with your Hono application.

Installation

bash
npm install agents hono hono-agents

Usage

ts
import { Hono } from "hono";
import { Agent } from "agents";
import { agentsMiddleware } from "hono-agents";

// Define your agent classes
export class ChatAgent extends Agent {
  async onRequest(_request: Request) {
    return new Response("Ready to assist with chat.");
  }
}

export class AssistantAgent extends Agent {
  async onRequest(_request: Request) {
    return new Response("I'm your AI assistant.");
  }
}

// Basic setup
const app = new Hono();
app.use("*", agentsMiddleware());

export default app;

Authentication

Replace the basic middleware registration with one that authenticates both
WebSocket connections and HTTP requests:

ts
const authorizeAgentRequest = async (req: Request) => {
  const token = req.headers.get("authorization");
  // Validate token
  if (!token) return new Response("Unauthorized", { status: 401 });
};

app.use(
  "*",
  agentsMiddleware({
    options: {
      onBeforeConnect: authorizeAgentRequest,
      onBeforeRequest: authorizeAgentRequest
    }
  })
);

Error handling

Replace the basic middleware registration to add an error handler:

ts
app.use("*", agentsMiddleware({ onError: (error) => console.error(error) }));

Custom routing

Replace the basic middleware registration to customize routing:

ts
app.use(
  "*",
  agentsMiddleware({
    options: {
      prefix: "agents" // Handles /agents/* routes only
    }
  })
);

Configuration

To properly configure your Cloudflare Workers project to use agents, add bindings to your wrangler.jsonc file:

json
{
  "durable_objects": {
    "bindings": [
      { "name": "ChatAgent", "class_name": "ChatAgent" },
      { "name": "AssistantAgent", "class_name": "AssistantAgent" }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["ChatAgent", "AssistantAgent"]
    }
  ]
}

How It Works

The agentsMiddleware function:

  1. Detects whether the incoming request is a WebSocket connection or standard HTTP request
  2. Routes the request to the appropriate agent
  3. Handles WebSocket upgrades for persistent connections
  4. Provides error handling and custom routing options

Requests that do not match an Agent route continue through later Hono middleware
and routes. Once an Agent route matches, its responseโ€”including an HTTP
rejectionโ€”is returned without invoking later handlers. Mount agentsMiddleware
on a narrower path or configure a distinct prefix if the app has other WebSocket
routes under the same URL prefix.

Agents can:

  • Maintain state across requests
  • Handle both HTTP and WebSocket connections
  • Schedule tasks for future execution
  • Communicate with AI services
  • Integrate seamlessly with React applications

License

Learn more about Cloudflare Agents at https://www.npmjs.com/package/agents

ISC

2. Official Technical Reference & Guides (cloudflare/docs)

AMPProject.org [](https://travis-ci.org/ampproject/docs)

You're looking at the source code of the official website of the AMP Project.
Even if you don't plan on contributing, explore how we've built the site for
a good example of how to build a canonical, responsive standalone AMP site.

How to build the site

Install

Install Grow and npm dependencies:

sh
$ curl https://install.growsdk.org | bash
$ npm install

Get a GitHub token or app id/secret

The docs in the reference section of the AMP Project site are not part of this repository; instead they are read from the amphtml GitHub repository "source of truth." When building the site these docs are fetched via GitHub.

Due to GitHub quotas this fetching will quickly fail unless additional information is provided in the request. This can be either a personal access token or an application id/secret.

Personal access token

  1. Request a GitHub personal access token. Since the docs are fetched from a public repository you only need to select the "public_repo" scope when creating the token. (It's a good practice to limit the token to the scope you need.)
  2. In your shell export the generated personal access token as AMP_DOC_TOKEN

Application client id and secret

If you prefer not to use a personal access token, you can also create a GitHub application and use its id and secret.

  1. Register for a GitHub application
  2. In your shell export the application client id as AMP_DOC_ID and the client secret as AMP_DOC_SECRET

Build

sh
$ grow build

This will generate a static, complete build of the site into the *build folder.

Develop

Note: Be sure to run grow build at least once to pull in reference docs before running the following command.

sh
$ grow run

You can now open http://localhost:8080/ and continue working on the source files, then reload the page to see changes appear.

Support

If you've found an error or inconsistency, please file an issue:
https://github.com/ampproject/docs/issues

Patches are encouraged, and may be submitted by forking this project and
submitting a pull request through GitHub.

License

All image and audio files except in folders "source/img/partners",
"source/img/publishers" and "source/img/quotes" (including *.png, *.jpg, *.svg,
*.mp3, *.wav and *.ogg) are licensed under the CC-BY-NC license. Images in the
excluded folders are not licensed.

All other files are licensed under the Apache 2 license.


Copyright 2015 Google, Inc.

Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this
file to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.