Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .changeset/quiet-lifecycles-grow.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"agents": minor
---

Vendor the required PartyServer runtime into `agents/lifecycle` and add a reusable Durable Object lifecycle for startup, request interception, alarms, and WebSockets. `Agent` now directly extends Cloudflare's `DurableObject` and composes the same lifecycle used by standalone objects; standalone hosts use the explicit `Lifecycle.install(this)` factory (or the expanded `new ...` plus `installHandlers()` form).
Vendor the required PartyServer runtime into `agents/lifecycle` and add a reusable Durable Object lifecycle for startup, request interception, alarms, and WebSockets. `Agent` now directly extends Cloudflare's `DurableObject` and composes the same lifecycle used by standalone objects; standalone hosts use the explicit `Lifecycle.install(this)` factory (or the expanded `new ...` plus `installHandlers()` form). Both Agent subclasses and standalone hosts use the existing `routeAgentRequest()` API and `/agents` URL prefix; the lifecycle entry point does not introduce a second public router.

Lifecycle WebSockets always use Cloudflare's Hibernation API; the `static options.hibernate` switch and in-memory connection mode are removed. Named Durable Objects use native `ctx.id.name`, while a read-only `__ps_name` fallback migrates objects created by older releases without writing new compatibility state.
2 changes: 1 addition & 1 deletion design/readonly-connections.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Readonly is a WebSocket concept. HTTP requests (`onRequest`, `agentFetch`, `getA

This is by design:

- **Callables are WebSocket-only** — there's no HTTP callable path. `routeAgentRequest` only handles WebSocket upgrades; plain HTTP falls through to `onRequest`. So clients can't invoke `@callable()` methods over HTTP.
- **Callables are WebSocket-only** — `routeAgentRequest` forwards both HTTP and WebSocket requests, but automatic `@callable()` dispatch exists only in the WebSocket message protocol. HTTP is delivered to the Agent's `onRequest`, so clients cannot invoke callable methods over HTTP unless application code exposes its own endpoint.
- **`onRequest` is developer-authored** — unlike the WebSocket message handler (which has automatic setState/RPC processing), `onRequest` is entirely custom code. There's no framework behavior to gate.
- **HTTP requests are stateless** — there's no persistent "connection" to mark as readonly. Each request stands alone. Standard HTTP auth (tokens, headers, cookies) is the right tool here.

Expand Down
6 changes: 3 additions & 3 deletions docs/agents/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ constructor(ctx: DurableObjectState, env: Env) {
Route named objects from the outer Worker when you want URL routing:

```ts
import { routeDurableObjectRequest } from "agents/lifecycle";
import { routeAgentRequest } from "agents";

export default {
async fetch(request: Request, env: Env): Promise<Response> {
return (
(await routeDurableObjectRequest(request, env)) ??
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
}
};
```

The default URL shape is `/objects/:binding/:name`. Direct
The default URL shape is `/agents/:binding/:name`. Direct
`env.MY_OBJECT.getByName(name).fetch(request)` calls work as well.

`Agent` already constructs this lifecycle. Existing Agent classes continue to
Expand Down
6 changes: 3 additions & 3 deletions examples/next/lifecycle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ Exercise the named object `demo`:

```sh
# Host onRequest; also increments the capability's request count.
curl http://localhost:8787/objects/do-agent/demo
curl http://localhost:8787/agents/do-agent/demo

# Intercepted by ActivityCapability before the host onRequest.
curl http://localhost:8787/objects/do-agent/demo/stats
curl http://localhost:8787/agents/do-agent/demo/stats
```

Connect a WebSocket to `ws://localhost:8787/objects/do-agent/demo`. The object
Connect a WebSocket to `ws://localhost:8787/agents/do-agent/demo`. The object
sends a connection snapshot and echoes each message. The socket uses Cloudflare's
WebSocket Hibernation API, so it remains connected while the Durable Object can
leave memory.
Expand Down
4 changes: 2 additions & 2 deletions examples/next/lifecycle/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DurableObject } from "cloudflare:workers";
import { routeAgentRequest } from "agents";
import {
Lifecycle,
routeDurableObjectRequest,
type CapabilityRequestContext,
type Connection,
type DurableObjectCapability,
Expand Down Expand Up @@ -110,7 +110,7 @@ export class DoAgent extends DurableObject<Env> {
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return (
(await routeDurableObjectRequest(request, env)) ??
(await routeAgentRequest(request, env)) ??
Comment thread
mattzcarey marked this conversation as resolved.
new Response("Not found", { status: 404 })
);
}
Expand Down
5 changes: 3 additions & 2 deletions packages/agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Each export maps to a public entry point that users `import` from. These are the
| ---------------------------- | ---------------------------- | ---------------------------------------------------------------------------- |
| `agents` | `src/index.ts` | Agent base class, routing, connections, RPC, state, scheduling, SQL |
| `agents/client` | `src/client.ts` | Browser/Node WebSocket client (`AgentClient`) via partysocket |
| `agents/lifecycle` | `src/lifecycle/index.ts` | Composable Durable Object lifecycle, routing, and hibernating connections |
| `agents/lifecycle` | `src/lifecycle/index.ts` | Composable Durable Object lifecycle and hibernating connections |
| `agents/react` | `src/react.tsx` | `useAgent` React hook, state sync, RPC from components |
| `agents/chat` | `src/chat/index.ts` | Shared chat primitives used by `@cloudflare/ai-chat` and `@cloudflare/think` |
| `agents/chat/transport` | `src/chat/transport.ts` | Framework-neutral WebSocket chat transport for AI SDK clients |
Expand Down Expand Up @@ -48,10 +48,11 @@ src/
types.ts # Shared message type enums
utils.ts # Helpers (camelCaseToKebabCase, etc.)
internal_context.ts # AsyncLocalStorage context for getCurrentAgent()
agent-routing.ts # Agent URL routing, named lookup, CORS, placement, retries

lifecycle/ # Public Durable Object lifecycle composition
index.ts # Intentionally small public lifecycle surface
durable-object-lifecycle.ts # Runtime handlers, routing, capabilities
durable-object-lifecycle.ts # Runtime handlers and capabilities
connection.ts # Hibernating WebSocket connection management

chat/ # Shared chat toolkit (mostly for sibling packages)
Expand Down
Loading
Loading