| name | pi-agent |
| description | Pi agent runtime integration for Cloudflare. Use when implementing the agent loop, tools, extensions, session trees, or multi-provider LLM calls. Triggers on Pi, agent runtime, tool calling, streaming, session tree, extensions, self-extending agent. |
Pi Agent Runtime
Pi provides the agent loop. We wrap it for Cloudflare Durable Objects.
Core Packages
bun add @mariozechner/pi-agent-core @mariozechner/pi-ai
@mariozechner/pi-agent-core — Agent runtime with tool calling and state management
@mariozechner/pi-ai — Unified multi-provider LLM API
Agent in Durable Object
import { Agent } from "@mariozechner/pi-agent-core"
import { getModel } from "@mariozechner/pi-ai"
export class AgentDO extends DurableObject {
private agent: Agent
async initialize() {
this.agent = new Agent({
initialState: {
systemPrompt: await this.loadSystemPrompt(),
model: getModel("anthropic", "claude-sonnet-4-5"),
tools: this.getTools(),
},
transformContext: async (messages) => {
const memories = await this.memory.searchRelevant(messages)
return [...memories, ...messages]
},
})
}
async prompt(text: string): Promise<AgentResponse> {
return this.agent.prompt(text)
}
}
Tools
Pi's 4 core tools (Read, Write, Edit, Bash) plus custom tools:
import { defineTool } from "@mariozechner/pi-agent-core"
const rememberTool = defineTool({
name: "remember",
description: "Store information in encrypted memory for later recall",
parameters: {
type: "object",
properties: {
content: { type: "string", description: "Information to remember" },
tags: { type: "array", items: { type: "string" } },
privacy: { type: "string", enum: ["private", "shared", "public"] }
},
required: ["content"]
},
execute: async (params, context) => {
const id = await context.memory.store({
content: params.content,
tags: params.tags || [],
privacy: params.privacy || "private"
})
return { success: true, id }
}
})
const recallTool = defineTool({
name: "recall",
description: "Search encrypted memories by semantic similarity",
parameters: {
type: "object",
properties: {
query: { type: "string", description: "What to search for" },
limit: { type: "number", description: "Max results (default 5)" }
},
required: ["query"]
},
execute: async (params, context) => {
const results = await context.memory.search(params.query, params.limit || 5)
return { results }
}
})
Extensions
Extensions persist state and can be hot-reloaded:
import { defineExtension } from "@mariozechner/pi-agent-core"
const memoryExtension = defineExtension({
name: "encrypted-memory",
version: "1.0.0",
initialize: async (context) => {
return {
memory: new EncryptedMemory(context.env.D1, context.identity)
}
},
tools: [rememberTool, recallTool],
hooks: {
beforePrompt: async (messages, state) => {
const context = await state.memory.searchRelevant(messages)
return { messages: [...context, ...messages] }
},
afterResponse: async (response, state) => {
if (response.shouldRemember) {
await state.memory.store(response.content)
}
}
}
})
Session Trees
Branch, navigate, rewind conversations:
const branchId = await agent.branch("exploring-option-A")
await agent.prompt("Try approach A...")
await agent.checkout("main")
await agent.rewind(3)
Multi-Provider
Sessions can use different models:
import { getModel } from "@mariozechner/pi-ai"
const claude = getModel("anthropic", "claude-sonnet-4-5")
const gpt = getModel("openai", "gpt-5-3-codex")
const analysisResult = await agent.prompt("Analyze this...", { model: claude })
const codeResult = await agent.prompt("Write code for...", { model: gpt })
Streaming
Stream responses for real-time output:
const stream = agent.promptStream("Generate a story...")
for await (const chunk of stream) {
if (chunk.type === 'text') {
ws.send(JSON.stringify({ type: 'chunk', text: chunk.text }))
} else if (chunk.type === 'tool_call') {
ws.send(JSON.stringify({ type: 'tool', name: chunk.name }))
}
}
Self-Extending Pattern
Pi agents can create their own tools:
const newTool = await agent.prompt(`
Create a tool that converts temperatures between Celsius and Fahrenheit.
Output the tool definition as JSON.
`)
agent.registerTool(JSON.parse(newTool.content))
References