Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
748 changes: 748 additions & 0 deletions src/content/docs/agents/agent-system/api-reference.mdx

Large diffs are not rendered by default.

510 changes: 510 additions & 0 deletions src/content/docs/agents/agent-system/architecture.mdx

Large diffs are not rendered by default.

324 changes: 324 additions & 0 deletions src/content/docs/agents/agent-system/getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
---
title: Getting started with Agent System
pcx_content_type: get-started
sidebar:
order: 1
---

import { TypeScriptExample, WranglerConfig, PackageManagers } from "~/components";

This guide walks through wiring the Agent System runtime into a Cloudflare Worker, defining an agent, and interacting with it.

This guide assumes you already know your way around Workers and Wrangler.

## Add the runtime to your Worker

In your Worker entrypoint (for example, `src/index.ts`):

<TypeScriptExample>

```ts
import { AgentSystem } from "agents/sys";
// (Optional) import custom tools / middleware and add them later

// Build an AgentSystem with a default LLM model
const system = new AgentSystem({
defaultModel: "openai:gpt-4.1-mini"
})
.defaults() // planning + filesystem + subagents
.addAgent({
name: "manager-agent",
description: "Main agent.",
prompt: "You are a helpful assistant.",
tags: ["default"] // selects which tools/middleware apply
});

// Export configured DO classes and HTTP handler
const { SystemAgent, Agency, handler } = system.export();

export { SystemAgent, Agency };
export default handler;
```

</TypeScriptExample>

What `.defaults()` does:

- Registers `planning` middleware (todo list + `write_todos` tool)
- Registers `filesystem` middleware (`ls`, `read_file`, `write_file`, `edit_file`)
- Registers `subagents` middleware (`task` tool for child agents)

All of those middlewares are tagged with `"default"`, so any agent blueprint that includes `"default"` in its `tags` will use them.

## Bind Durable Objects and KV

Wire the `SystemAgent` and `Agency` DOs and the KV registry that stores agencies:

<WranglerConfig>

```jsonc
{
"name": "my-agent-worker",
"main": "src/index.ts",
"compatibility_date": "2025-03-14",
"compatibility_flags": ["nodejs_compat"],

"durable_objects": {
"bindings": [
{
"name": "SYSTEM_AGENT",
"class_name": "SystemAgent"
},
{
"name": "AGENCY",
"class_name": "Agency"
}
]
},

"kv_namespaces": [
{
"binding": "AGENCY_REGISTRY",
"id": "my-agency-registry-kv-namespace-id"
}
],

"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["SystemAgent", "Agency"]
}
]
}
```

</WranglerConfig>

Run the migration once:

<PackageManagers pkg="wrangler" args="deploy --migrations" />

## Configure an LLM provider

By default, `AgentSystem` will use OpenAI's Chat Completions API via `makeOpenAI` if you do not pass a provider explicitly.

You need:

- `LLM_API_KEY` – secret (OpenAI API key or gateway key)
- Optional: `LLM_API_BASE` – custom base URL (proxy/gateway)

Set them as Worker secrets:

<PackageManagers pkg="wrangler" args="secret put LLM_API_KEY" />
<PackageManagers pkg="wrangler" args="secret put LLM_API_BASE" />

Then `SystemAgent.provider` will:

- Build an OpenAI provider using `makeOpenAI(apiKey, apiBase)`
- Wrap it to automatically emit `MODEL_STARTED` / `MODEL_COMPLETED` events

If you want to use a custom provider, you can pass a `Provider` into `new AgentSystem({ provider })`. That provider is a simple interface:

<TypeScriptExample>

```ts
interface Provider {
invoke(
req: ModelRequest,
opts: { signal?: AbortSignal }
): Promise<ModelResult>;
stream(
req: ModelRequest,
onDelta: (chunk: string) => void
): Promise<ModelResult>;
}
```

</TypeScriptExample>

:::note

Advanced: wiring a provider that depends on `env` requires patching `SystemAgent.provider`, so for now sticking to the OpenAI path is easiest.

:::

## Run locally

Start dev mode:

<PackageManagers pkg="wrangler" args="dev" />

Then open the Worker URL (default `http://127.0.0.1:8787/`) in a browser.

You should see the built-in **Agent Dashboard** (`client.html`), which is being served by the exported `handler`.

## Create an Agency and an Agent

In the dashboard:

1. Use the **New Agency** button to create an agency.

Under the hood this calls:
- `POST /agencies` → creates a new `Agency` DO instance
- The metadata (ID, name, createdAt) is stored in `AGENCY_REGISTRY` KV

2. Select that agency from the **Agencies** dropdown.

3. Select **New Thread** to create an agent thread, and pick your agent type (for example, `"manager-agent"` from the example).

Under the hood this calls:
- `POST /agency/:agencyId/agents` with `{ agentType }`
- The `Agency` DO:
- assigns a new `id` for the agent thread
- stores metadata in its local SQLite
- spawns a `SystemAgent` DO, calling `/register` with `ThreadMetadata`

4. Select the thread in the sidebar and start sending messages from the chat panel.

## Talk to an agent programmatically

If you do not care about the dashboard, you can hit the REST-ish endpoints directly.

### Create an agency

<TypeScriptExample>

```ts
const res = await fetch("https://your-worker.example.com/agencies", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ name: "Production" })
});

const agency = await res.json(); // { id, name, createdAt }
```

</TypeScriptExample>

### List blueprints available in that agency

Static blueprints from `AgentSystem.addAgent` plus any overrides stored inside the `Agency` DO:

<TypeScriptExample>

```ts
const res = await fetch(
`https://your-worker.example.com/agency/${agency.id}/blueprints`
);
const { blueprints } = await res.json();
```

</TypeScriptExample>

### Create a new agent thread

<TypeScriptExample>

```ts
const res = await fetch(
`https://your-worker.example.com/agency/${agency.id}/agents`,
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ agentType: "manager-agent" })
}
);

const thread: {
id: string;
agentType: string;
createdAt: string;
request: any;
agencyId: string;
} = await res.json();
```

</TypeScriptExample>

### Send a message

<TypeScriptExample>

```ts
await fetch(
`https://your-worker.example.com/agency/${agency.id}/agent/${thread.id}/invoke`,
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
messages: [{ role: "user", content: "Hello, what can you do?" }]
})
}
);
// HTTP 202, run happens asynchronously inside the DO
```

</TypeScriptExample>

### Poll state

<TypeScriptExample>

```ts
const res = await fetch(
`https://your-worker.example.com/agency/${agency.id}/agent/${thread.id}/state`
);
const { state, run } = await res.json();

/*
state: AgentState (messages, tools, thread, todos, files, subagents, ...)
run: RunState (runId, status, step, reason, nextAlarmAt)
*/
```

</TypeScriptExample>

### Listen to events live (WebSocket)

`client.html` uses a WebSocket at:

```text
/ws → /agency/:agencyId/agent/:agentId/ws
```

`Agent` base class calls `broadcast()` on the DO whenever an `AgentEvent` is emitted in `SystemAgent.emit`, so you can just reuse that endpoint if you want a custom UI.

## Security: lock down the handler

`createHandler` supports a simple shared-secret auth mechanism:

<TypeScriptExample>

```ts
const system = new AgentSystem({
defaultModel: "openai:gpt-4.1-mini",
handlerOptions: {
secret: "some-long-random-string" // clients must send X-SECRET header
}
});
```

</TypeScriptExample>

When `secret` is set:

- All non-`GET /` requests must include `X-SECRET: <value>` or you get `401`
- This gates both the dashboard and the raw REST API

Use this if your Worker is directly exposed to the public internet and you do not have another auth layer in front.

## Example

Refer to the `examples/deep/` folder in the [cloudflare/agents](https://github.com/cloudflare/agents) repository for a real setup:

- `AgentSystem` with:
- a **security analytics subagent** blueprint (`security-agent`)
- a **manager** blueprint (`manager-agent`) that orchestrates subagents

- Custom tools that talk to the Cloudflare Analytics GraphQL API
- A prompt that explains how the manager agent should:
- plan with todos
- spawn analytics subagents via `task`
- read/write `report.md` in the virtual filesystem

This is a good reference for more complex multi-agent patterns.
23 changes: 23 additions & 0 deletions src/content/docs/agents/agent-system/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: Agent System
pcx_content_type: navigation
sidebar:
order: 10
---

The Agent System (`agents/sys`) is a framework for building agent systems with middleware, tools, and blueprints on Cloudflare Workers.

Agent System provides:

- **Middleware pipeline**: Extend agent behavior with reusable middleware for planning, filesystem operations, and subagents
- **Tool system**: Define and register tools that agents can use to perform tasks
- **Blueprint management**: Configure agent types with specific capabilities, prompts, and middleware
- **Multi-agent support**: Spawn child agents (subagents) that can work on subtasks independently
- **Built-in dashboard**: Web-based interface for managing agencies, agents, and threads
- **Event system**: Real-time event streaming via WebSocket for monitoring agent execution

## Get started

- [Getting started](/agents/agent-system/getting-started/) - Set up your first Agent System
- [Architecture](/agents/agent-system/architecture/) - Understand how Agent System works
- [API reference](/agents/agent-system/api-reference/) - Complete API documentation
Loading
Loading