@funtuantw/pi-agent-cf
v0.1.0
Published
Deploy pi-mono AI agents on Cloudflare Workers with Durable Objects. Dependency-injection friendly SDK.
Maintainers
Readme
@funtuantw/pi-agent-cf
Deploy pi-mono AI agents on Cloudflare Workers with Durable Objects.
Dependency-injection friendly SDK — inject your own tools, LLM providers, API keys, and storage in a few lines.
Features
- WebSocket streaming — real-time bidirectional communication via Durable Objects Hibernation API
- REST API — create sessions, query state, send prompts over HTTP
- Session persistence — messages auto-saved to DO storage, survives restarts
- Dependency injection — factory function accepts tools, API keys, system prompt, event hooks, auth middleware
- Multi-client — multiple WebSocket clients can share the same agent session
- Idle cleanup — configurable alarm auto-evicts unused sessions from memory
Quick Start
1. Install
npm install @funtuantw/pi-agent-cf @mariozechner/pi-agent-core @mariozechner/pi-ai2. Create your worker
// src/index.ts
import { createAgentWorker, type AgentEnv, type AgentTool } from '@funtuantw/pi-agent-cf';
import { Type } from '@sinclair/typebox';
interface Env extends AgentEnv {
GOOGLE_API_KEY?: string;
}
// Define a custom tool
const GetTime: AgentTool<typeof Type.Object({})> = {
name: 'get_time',
label: 'Get Time',
description: 'Returns the current UTC time.',
parameters: Type.Object({}),
execute: async () => ({
content: [{ type: 'text', text: new Date().toISOString() }],
details: null,
}),
};
// Create the worker with DI config
const worker = createAgentWorker<Env>({
systemPrompt: 'You are a helpful assistant.',
tools: () => [GetTime],
getApiKey: (provider, env) => {
if (provider === 'google') return env.GOOGLE_API_KEY;
},
});
export const AgentSessionDO = worker.AgentSessionDO;
export default worker.handler;3. Configure wrangler.toml
name = "my-agent"
main = "src/index.ts"
compatibility_date = "2025-01-01"
compatibility_flags = ["nodejs_compat"]
[durable_objects]
bindings = [
{ name = "AGENT_SESSION", class_name = "AgentSessionDO" }
]
[[migrations]]
tag = "v1"
new_classes = ["AgentSessionDO"]
# Required: AJV uses new Function() which is forbidden in CF Workers.
# This stub disables schema validation; pi-ai falls back to trusting LLM output.
[alias]
"ajv" = "node_modules/@funtuantw/pi-agent-cf/stubs/ajv.js"4. Set secrets & deploy
wrangler secret put GOOGLE_API_KEY
wrangler deployAPI
REST Endpoints
| Method | Path | Description |
|--------|------|-------------|
| POST | /sessions | Create a new session → { sessionId, createdAt } |
| GET | /sessions/:id/ws | WebSocket upgrade |
| GET | /sessions/:id/state | Get current agent state |
| POST | /sessions/:id/prompt | Send a prompt (fire-and-forget) |
| DELETE | /sessions/:id | Delete session & data |
| GET | /health | Health check |
WebSocket Protocol
Connect to /sessions/:id/ws and send JSON messages:
Client → Server:
{ "type": "prompt", "text": "Hello!" } // Start a conversation
{ "type": "steer", "message": { ... } } // Inject mid-turn
{ "type": "follow_up", "message": { ... } } // Queue post-turn
{ "type": "abort" } // Stop generation
{ "type": "get_state" } // Query state
{ "type": "set_model", "provider": "anthropic", "modelId": "claude-sonnet-4-20250514" }
{ "type": "set_thinking_level", "level": "medium" }
{ "type": "clear_messages" } // Clear history
{ "type": "reset" } // Full reset
{ "type": "restore" } // Restore from storage
{ "type": "ping" }Server → Client:
{ "type": "event", "event": { "type": "message_update", ... } } // AgentEvent stream
{ "type": "state", "state": { ... } } // State response
{ "type": "restored", "messages": [...] } // Restored messages
{ "type": "error", "message": "...", "code": "..." }
{ "type": "session_created", "sessionId": "..." }
{ "type": "pong" }Configuration (AgentWorkerConfig)
| Property | Type | Description |
|----------|------|-------------|
| systemPrompt | string \| (env) => string | Required. System prompt for the agent |
| getApiKey | (provider, env) => string? | Required. Resolve API keys from env secrets |
| tools | (env) => AgentTool[] | Custom tools (receive CF env for bindings) |
| model | Model | Default LLM model |
| streamFn | StreamFn | Custom stream function (e.g. AI Gateway routing) |
| transformContext | fn | Transform context before LLM call (e.g. RAG injection) |
| convertToLlm | fn | Custom message format conversion |
| thinkingLevel | ThinkingLevel | Default thinking level |
| onEvent | (sessionId, event, env) => void | Global event hook for logging/analytics |
| authenticate | (request, env) => boolean | Auth middleware |
| maxSessionIdleMs | number | Idle timeout before memory cleanup (default: 5min) |
| maxPersistedMessages | number | Max messages to persist (default: 200) |
Architecture
Client (Browser/App)
↕ WebSocket / REST
CF Worker (Router)
↕ Durable Object stub
AgentSession DO (one per session)
├── Agent instance (pi-agent-core)
├── StreamFn → LLM Provider (pi-ai fetch)
├── Custom Tools (injected via config)
└── DO Storage (persistent messages)- Worker Router — handles HTTP routing, CORS, auth, creates DO stubs
- AgentSession DO — one instance per conversation session
- Manages the
Agentlifecycle frompi-agent-core - Accepts WebSocket connections with Hibernation API (cost-efficient)
- Broadcasts all
AgentEvents to connected clients - Persists messages to DO storage on each turn completion
- Cleans up in-memory resources after idle timeout via alarm
- Manages the
Compatibility
Important:
pi-aidepends on AJV for JSON-schema validation, which usesnew Function()internally — forbidden in Cloudflare Workers. The SDK ships a stub atstubs/ajv.jsthat disables AJV sopi-aifalls back to trusting the LLM output directly. You must add the[alias]section to yourwrangler.toml(see step 3 above).Also, if you use
undici>= 7 (pulled bypi-ai), add"undici": "^6.21.0"to youroverridesinpackage.jsonto avoidnode:sqlitebundling issues.
The SDK uses only pi-agent-core (zero Node.js deps) and pi-ai (uses fetch() for LLM calls). The following providers work out of the box on CF Workers:
- ✅ Google (Gemini)
- ✅ Anthropic (Claude)
- ✅ OpenAI (GPT, o-series)
- ✅ Azure OpenAI
- ✅ OpenAI-compatible (Groq, OpenRouter, etc.)
- ❌ AWS Bedrock (requires AWS SDK)
- ❌ OAuth flows (require HTTP callback server)
License
MIT
