@ariaflowagents/cf-agent 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +436 -135
  2. package/dist/AriaFlowAgent.d.ts +109 -0
  3. package/dist/AriaFlowAgent.d.ts.map +1 -0
  4. package/dist/AriaFlowAgent.js +170 -0
  5. package/dist/AriaFlowAgent.js.map +1 -0
  6. package/dist/BridgeSessionStore.d.ts +37 -0
  7. package/dist/BridgeSessionStore.d.ts.map +1 -0
  8. package/dist/BridgeSessionStore.js +120 -0
  9. package/dist/BridgeSessionStore.js.map +1 -0
  10. package/dist/OrchestrationStore.d.ts +25 -0
  11. package/dist/OrchestrationStore.d.ts.map +1 -0
  12. package/dist/OrchestrationStore.js +63 -0
  13. package/dist/OrchestrationStore.js.map +1 -0
  14. package/dist/StreamAdapter.d.ts +19 -0
  15. package/dist/StreamAdapter.d.ts.map +1 -0
  16. package/dist/StreamAdapter.js +205 -0
  17. package/dist/StreamAdapter.js.map +1 -0
  18. package/dist/index.d.ts +33 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +31 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/types.d.ts +37 -0
  23. package/dist/types.d.ts.map +1 -0
  24. package/dist/types.js +6 -0
  25. package/dist/types.js.map +1 -0
  26. package/package.json +37 -27
  27. package/dist/AriaFlowChatAgent.d.ts +0 -24
  28. package/dist/AriaFlowChatAgent.d.ts.map +0 -1
  29. package/dist/AriaFlowChatAgent.js +0 -153
  30. package/dist/AriaFlowChatAgent.js.map +0 -1
  31. package/dist/AriaFlowFlowAgent.d.ts +0 -88
  32. package/dist/AriaFlowFlowAgent.d.ts.map +0 -1
  33. package/dist/AriaFlowFlowAgent.js +0 -185
  34. package/dist/AriaFlowFlowAgent.js.map +0 -1
  35. package/dist/stores/CloudflareSQLiteStore.d.ts +0 -12
  36. package/dist/stores/CloudflareSQLiteStore.d.ts.map +0 -1
  37. package/dist/stores/CloudflareSQLiteStore.js +0 -99
  38. package/dist/stores/CloudflareSQLiteStore.js.map +0 -1
package/README.md CHANGED
@@ -1,196 +1,497 @@
1
1
  # @ariaflowagents/cf-agent
2
2
 
3
- Cloudflare Durable Object base classes for hosting AriaFlow on the edge.
3
+ AriaFlow integration for Cloudflare Workers using AIChatAgent. Build multi-agent AI systems with automatic persistence, resumable streaming, and real-time sync.
4
4
 
5
- ## Two Primitive Classes
5
+ ## Features
6
6
 
7
- This package provides **two separate base classes** for different use cases:
7
+ - **All AriaFlow Agent Types**: LLM, Flow, Triage, and Composite agents
8
+ - **Automatic Persistence**: Messages stored in SQLite, survive restarts
9
+ - **Resumable Streaming**: Reconnect without data loss
10
+ - **Multi-Client Sync**: Real-time updates across all connected clients
11
+ - **Full Tool Support**: Server-side, client-side, and approval tools
12
+ - **Zero Config**: Works out of the box with sensible defaults
8
13
 
9
- | Class | Use When | Config Returns |
10
- |-------|----------|----------------|
11
- | `AriaFlowChatAgent` | Multi-agent systems with handoffs | `HarnessConfig` or `Runtime` |
12
- | `AriaFlowFlowAgent` | Structured single-flow conversations | `AriaFlowFlowConfig` or `AgentFlowManager` |
14
+ ## Quick Start
13
15
 
14
- ---
16
+ ```bash
17
+ # Create a new project
18
+ mkdir my-agent && cd my-agent
19
+ npm init -y
15
20
 
16
- ## AriaFlowChatAgent - Multi-Agent Runtime
21
+ # Install dependencies
22
+ npm install @ariaflowagents/cf-agent ai zod
23
+ npm install -D @cloudflare/vite-plugin vite wrangler
17
24
 
18
- Use for **multi-agent systems** where agents can hand off to each other.
25
+ # Create your agent
26
+ # See examples below...
27
+
28
+ # Run locally
29
+ npm run dev
30
+ ```
31
+
32
+ Open http://localhost:5173 to see your agent in action.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ npm install @ariaflowagents/cf-agent
38
+ ```
39
+
40
+ ## Project Structure
41
+
42
+ ```
43
+ my-agent/
44
+ ├── src/
45
+ │ ├── server.ts # Agent implementation
46
+ │ ├── app.tsx # React chat UI
47
+ │ └── client.tsx # React entry point
48
+ ├── package.json
49
+ ├── tsconfig.json
50
+ ├── vite.config.ts
51
+ └── wrangler.jsonc
52
+ ```
53
+
54
+ ## Basic Example
55
+
56
+ ### 1. Create Agent (`src/server.ts`)
19
57
 
20
58
  ```typescript
21
- import { AriaFlowChatAgent } from '@ariaflowagents/cf-agent';
22
- import { Runtime, type AgentConfig } from '@ariaflowagents/core';
59
+ import { AriaFlowAIChatAgent } from "@ariaflowagents/cf-agent";
60
+ import { createOpenAI } from "@ai-sdk/openai";
61
+ import { tool } from "ai";
62
+ import { z } from "zod";
23
63
 
24
- export class SupportAgent extends AriaFlowChatAgent {
25
- async createRuntimeConfig() {
26
- const triage: AgentConfig = {
27
- id: 'triage',
28
- name: 'Triage',
29
- type: 'triage',
30
- systemPrompt: 'Route customers to specialists.',
31
- routes: [
32
- { agentId: 'orders', description: 'Order questions' },
33
- { agentId: 'billing', description: 'Billing questions' },
34
- ],
35
- };
64
+ interface Env {
65
+ OPENAI_API_KEY: string;
66
+ }
36
67
 
37
- const orders: AgentConfig = {
38
- id: 'orders',
39
- name: 'Orders',
40
- type: 'llm',
41
- systemPrompt: 'Help with order questions.',
42
- };
68
+ export class MyAgent extends AriaFlowAIChatAgent<Env> {
69
+ protected getRuntimeConfig() {
70
+ const openai = createOpenAI({ apiKey: this.env.OPENAI_API_KEY });
43
71
 
44
72
  return {
45
- agents: [triage, orders],
46
- defaultAgentId: 'triage',
73
+ agents: [{
74
+ id: "assistant",
75
+ type: "llm" as const,
76
+ name: "Assistant",
77
+ model: openai("gpt-4o"),
78
+ prompt: "You are a helpful assistant",
79
+ tools: {
80
+ getWeather: tool({
81
+ description: "Get weather for a city",
82
+ inputSchema: z.object({ city: z.string() }),
83
+ execute: async ({ city }) => {
84
+ return { temperature: 72, condition: "sunny" };
85
+ },
86
+ }),
87
+ },
88
+ }],
89
+ defaultAgentId: "assistant",
47
90
  };
48
91
  }
49
92
  }
93
+
94
+ export default MyAgent;
50
95
  ```
51
96
 
52
- **State (Runtime mode):**
97
+ ### 2. Create UI (`src/app.tsx`)
98
+
99
+ ```tsx
100
+ import { useAgent } from "agents/react";
101
+ import { useAgentChat } from "@cloudflare/ai-chat/react";
102
+
103
+ export function Chat() {
104
+ const agent = useAgent({ agent: "MyAgent" });
105
+ const { messages, sendMessage, status } = useAgentChat({ agent });
106
+
107
+ return (
108
+ <div>
109
+ {messages.map((msg) => (
110
+ <div key={msg.id}>
111
+ <strong>{msg.role}:</strong>
112
+ {msg.parts.map((part, i) =>
113
+ part.type === "text" ? <span key={i}>{part.text}</span> : null
114
+ )}
115
+ </div>
116
+ ))}
117
+ <form onSubmit={(e) => {
118
+ e.preventDefault();
119
+ const input = e.currentTarget.elements.namedItem("input") as HTMLInputElement;
120
+ sendMessage({ text: input.value });
121
+ input.value = "";
122
+ }}>
123
+ <input name="input" placeholder="Type a message..." />
124
+ <button type="submit" disabled={status === "streaming"}>Send</button>
125
+ </form>
126
+ </div>
127
+ );
128
+ }
129
+ ```
130
+
131
+ ### 3. Configuration Files
132
+
133
+ **`vite.config.ts`**:
53
134
  ```typescript
54
- state = {
55
- activeAgentId: 'orders', // Current agent
56
- lastHandoffReason: 'Order inquiry',
57
- updatedAt: 1234567890,
135
+ import { defineConfig } from "vite";
136
+ import { cloudflare } from "@cloudflare/vite-plugin";
137
+ import react from "@vitejs/plugin-react";
138
+
139
+ export default defineConfig({
140
+ plugins: [cloudflare(), react()],
141
+ });
142
+ ```
143
+
144
+ **`wrangler.jsonc`**:
145
+ ```json
146
+ {
147
+ "name": "my-agent",
148
+ "main": "src/server.ts",
149
+ "compatibility_date": "2026-01-28",
150
+ "compatibility_flags": ["nodejs_compat"],
151
+ "ai": { "binding": "AI" },
152
+ "durable_objects": {
153
+ "bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
154
+ },
155
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }]
156
+ }
157
+ ```
158
+
159
+ **`package.json`** scripts:
160
+ ```json
161
+ {
162
+ "scripts": {
163
+ "dev": "vite dev",
164
+ "deploy": "vite build && wrangler deploy",
165
+ "types": "wrangler types"
166
+ }
58
167
  }
59
168
  ```
60
169
 
61
- ---
170
+ ## Agent Types
62
171
 
63
- ## AriaFlowFlowAgent - Structured Flow
172
+ ### LLMAgent
64
173
 
65
- Use for **structured, multi-step flows** with guided conversations.
174
+ Standard conversational agent with tools:
66
175
 
67
176
  ```typescript
68
- import { AriaFlowFlowAgent } from '@ariaflowagents/cf-agent';
69
- import { AriaFlowFlowConfig } from '@ariaflowagents/cf-agent';
70
- import { tool } from 'ai';
71
- import { z } from 'zod';
72
- import { createFlowTransition } from '@ariaflowagents/core';
177
+ {
178
+ id: "sales",
179
+ type: "llm" as const,
180
+ name: "Sales Agent",
181
+ model: openai("gpt-4o"),
182
+ prompt: "You are a sales specialist...",
183
+ tools: { /* ... */ },
184
+ canHandoffTo: ["support", "billing"]
185
+ }
186
+ ```
73
187
 
74
- export class ReservationAgent extends AriaFlowFlowAgent {
75
- async createFlowConfig(): Promise<AriaFlowFlowConfig> {
76
- return {
77
- initialNode: 'greeting',
78
- model: this.env.AI as any,
79
- defaultRolePrompt: 'You are a reservation assistant.',
80
- nodes: [
81
- {
82
- name: 'greeting',
83
- taskPrompt: 'Greet warmly and ask for party size.',
84
- tools: {
85
- collect_party_size: tool({
86
- description: 'Record party size',
87
- inputSchema: z.object({
88
- partySize: z.number().min(1).max(20)
89
- }),
90
- execute: async ({ partySize }) =>
91
- createFlowTransition('collect_date', { partySize }),
92
- }),
93
- },
94
- },
95
- {
96
- name: 'collect_date',
97
- taskPrompt: 'Ask for reservation date.',
98
- },
99
- ],
100
- };
188
+ ### FlowAgent
189
+
190
+ Structured node-based conversation flows:
191
+
192
+ ```typescript
193
+ {
194
+ id: "order-flow",
195
+ type: "flow" as const,
196
+ name: "Order Flow",
197
+ model: openai("gpt-4o"),
198
+ flow: {
199
+ nodes: [
200
+ {
201
+ id: "initial",
202
+ task: "Ask what they want",
203
+ transitions: [{ to: "confirm", on: "product_selected" }]
204
+ },
205
+ {
206
+ id: "confirm",
207
+ task: "Confirm order",
208
+ transitions: [{ to: "end", on: "confirmed" }]
209
+ }
210
+ ],
211
+ hybrid: true // Allow off-flow questions
101
212
  }
102
213
  }
103
214
  ```
104
215
 
105
- **State (Flow mode):**
216
+ ### TriageAgent
217
+
218
+ Intelligent routing between agents:
219
+
106
220
  ```typescript
107
- state = {
108
- currentNode: 'collect_date', // Current node
109
- nodeHistory: ['greeting', 'collect_party_size', 'collect_date'],
110
- updatedAt: 1234567890,
221
+ {
222
+ id: "router",
223
+ type: "triage" as const,
224
+ name: "Router",
225
+ model: openai("gpt-4o-mini"),
226
+ routingConfig: {
227
+ agents: ["sales", "support", "billing"],
228
+ instructions: "Route based on user intent..."
229
+ }
111
230
  }
112
231
  ```
113
232
 
114
- ---
233
+ ### CompositeAgent
115
234
 
116
- ## Context Strategies
117
-
118
- Control conversation history behavior:
235
+ Multi-agent coordination:
119
236
 
120
237
  ```typescript
121
238
  {
122
- name: 'confirmation',
123
- taskPrompt: 'Confirm details.',
124
- contextStrategy: {
125
- strategy: 'reset_with_summary' // Summarize instead of full history
126
- },
239
+ id: "supervisor",
240
+ type: "composite" as const,
241
+ name: "Supervisor",
242
+ model: openai("gpt-4o"),
243
+ agents: ["researcher", "analyst", "writer"],
244
+ coordinationMode: "sequential"
127
245
  }
128
246
  ```
129
247
 
130
- | Strategy | Behavior |
131
- |----------|----------|
132
- | `append` | Keep all messages (default) |
133
- | `reset` | Clear messages on node entry |
134
- | `reset_with_summary` | Summarize and replace messages |
248
+ ## Tools
135
249
 
136
- ---
250
+ ### Server-Side Tools
137
251
 
138
- ## Endpoints (Both Classes)
252
+ Run automatically on the server:
139
253
 
140
- | Endpoint | Returns |
141
- |----------|---------|
142
- | `GET /info` | Agent metadata, mode, readiness |
143
- | `GET /state` | Full agent state |
144
- | `WS /` | WebSocket for streaming |
254
+ ```typescript
255
+ tools: {
256
+ getWeather: tool({
257
+ description: "Get weather for a city",
258
+ inputSchema: z.object({ city: z.string() }),
259
+ execute: async ({ city }) => {
260
+ // Runs on server
261
+ return await fetchWeather(city);
262
+ }
263
+ })
264
+ }
265
+ ```
145
266
 
146
- **AriaFlowFlowAgent additional:**
147
- | `GET /flow-state` | Flow-specific state (currentNode, nodeHistory, collectedData) |
267
+ ### Client-Side Tools
148
268
 
149
- ---
269
+ No `execute` function - browser provides the result:
150
270
 
151
- ## Quick Reference
271
+ ```typescript
272
+ // Server
273
+ tools: {
274
+ getLocation: tool({
275
+ description: "Get user location",
276
+ inputSchema: z.object({})
277
+ // No execute - client handles it
278
+ })
279
+ }
152
280
 
153
- | Question | Answer |
154
- |----------|--------|
155
- | Need multiple agents with handoffs? | Use `AriaFlowChatAgent` |
156
- | Need structured step-by-step flow? | Use `AriaFlowFlowAgent` |
157
- | State persists? | Yes, via Durable Object storage |
158
- | WebSocket streaming? | Yes, automatic |
159
- | Can switch modes? | No - choose the right class for your use case |
281
+ // Client
282
+ useAgentChat({
283
+ onToolCall: async ({ toolCall, addToolOutput }) => {
284
+ if (toolCall.toolName === "getLocation") {
285
+ const pos = await navigator.geolocation.getCurrentPosition();
286
+ addToolOutput({
287
+ toolCallId: toolCall.toolCallId,
288
+ output: { lat: pos.coords.latitude, lng: pos.coords.longitude }
289
+ });
290
+ }
291
+ }
292
+ });
293
+ ```
160
294
 
161
- ---
295
+ ### Approval Tools
162
296
 
163
- ## Example: Cloudflare Worker
297
+ Human-in-the-loop for sensitive operations:
164
298
 
165
299
  ```typescript
166
- // Runtime Mode (Multi-Agent)
167
- import { AriaFlowChatAgent } from '@ariaflowagents/cf-agent';
300
+ tools: {
301
+ processRefund: tool({
302
+ description: "Process a refund",
303
+ inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
304
+ needsApproval: ({ amount }) => amount > 100,
305
+ execute: async ({ orderId, amount }) => {
306
+ // Only runs after user approval
307
+ return await processRefund(orderId, amount);
308
+ }
309
+ })
310
+ }
311
+ ```
168
312
 
169
- export class MyAgent extends AriaFlowChatAgent {
170
- async createRuntimeConfig() {
171
- return {
172
- agents: [{ id: 'assistant', name: 'Assistant', systemPrompt: 'Helpful.' }],
173
- defaultAgentId: 'assistant',
174
- };
175
- }
313
+ ## Hooks
314
+
315
+ React to agent lifecycle events:
316
+
317
+ ```typescript
318
+ protected getHooks() {
319
+ return {
320
+ onHandoff: async (context, from, to, reason) => {
321
+ console.log(`Handoff: ${from} -> ${to} (${reason})`);
322
+ },
323
+
324
+ onToolCall: async (context, toolCall) => {
325
+ console.log(`Tool: ${toolCall.toolName}`);
326
+ },
327
+
328
+ onNodeEnter: async (context, node) => {
329
+ console.log(`Flow node: ${node.name}`);
330
+ }
331
+ };
176
332
  }
333
+ ```
177
334
 
178
- // OR Flow Mode (Single Flow)
179
- import { AriaFlowFlowAgent } from '@ariaflowagents/cf-agent';
335
+ ## State Persistence
180
336
 
181
- export class MyFlowAgent extends AriaFlowFlowAgent {
182
- async createFlowConfig() {
183
- return {
184
- initialNode: 'greeting',
185
- model: this.env.AI as any,
186
- nodes: [{ name: 'greeting', taskPrompt: 'Hi!' }],
187
- };
188
- }
337
+ AriaFlow stores two types of data:
338
+
339
+ 1. **CF Tables** (`cf_ai_chat_*`): Chat messages, stream chunks, resumption data
340
+ 2. **AriaFlow Tables** (`ariaflow_sessions`): Session state, working memory, flow state
341
+
342
+ ```
343
+ SQLite Database:
344
+ ├── cf_ai_chat_agent_messages (UIMessages for UI)
345
+ ├── cf_ai_chat_stream_chunks (Stream chunks)
346
+ ├── cf_ai_chat_stream_metadata (Stream state)
347
+ └── ariaflow_sessions (AriaFlow Session state)
348
+ ```
349
+
350
+ ## Custom Configuration
351
+
352
+ ### Stream Adapter
353
+
354
+ Control which events are sent to the client:
355
+
356
+ ```typescript
357
+ protected getStreamAdapterConfig() {
358
+ return {
359
+ includeHandoffs: true, // Show agent handoffs in UI
360
+ includeFlowEvents: true, // Show flow node transitions
361
+ includeTripwires: true, // Show guardrail triggers
362
+ includeStepEvents: false, // Hide step lifecycle
363
+ includeAgentEvents: false // Hide agent lifecycle
364
+ };
189
365
  }
366
+ ```
190
367
 
191
- export default {
192
- async fetch(request: Request, env: Env): Promise<Response> {
193
- return MyAgent.fetch(request, env); // or MyFlowAgent
194
- },
195
- };
368
+ ### Session ID
369
+
370
+ Override default session ID generation:
371
+
372
+ ```typescript
373
+ protected getSessionId(): string {
374
+ // Default uses Durable Object ID
375
+ // Override for custom session management
376
+ return `user-${this.getUserId()}`;
377
+ }
196
378
  ```
379
+
380
+ ## Scripts
381
+
382
+ ```bash
383
+ # Development (with hot reload)
384
+ npm run dev
385
+
386
+ # Deploy to Cloudflare
387
+ npm run deploy
388
+
389
+ # Generate types
390
+ npm run types
391
+
392
+ # Build only
393
+ npm run build
394
+ ```
395
+
396
+ ## Using Different AI Providers
397
+
398
+ ### Workers AI (No API key needed)
399
+
400
+ ```typescript
401
+ import { createWorkersAI } from "workers-ai-provider";
402
+
403
+ const workersai = createWorkersAI({ binding: this.env.AI });
404
+
405
+ // In agent config:
406
+ model: workersai("@cf/meta/llama-3.3-70b-instruct-fp8-fast")
407
+ ```
408
+
409
+ ### OpenAI
410
+
411
+ ```bash
412
+ npm install @ai-sdk/openai
413
+ ```
414
+
415
+ ```typescript
416
+ import { createOpenAI } from "@ai-sdk/openai";
417
+
418
+ const openai = createOpenAI({ apiKey: this.env.OPENAI_API_KEY });
419
+
420
+ // In agent config:
421
+ model: openai("gpt-4o")
422
+ ```
423
+
424
+ ### Anthropic
425
+
426
+ ```bash
427
+ npm install @ai-sdk/anthropic
428
+ ```
429
+
430
+ ```typescript
431
+ import { createAnthropic } from "@ai-sdk/anthropic";
432
+
433
+ const anthropic = createAnthropic({ apiKey: this.env.ANTHROPIC_API_KEY });
434
+
435
+ // In agent config:
436
+ model: anthropic("claude-sonnet-4-20250514")
437
+ ```
438
+
439
+ ## API Reference
440
+
441
+ ### AriaFlowAIChatAgent
442
+
443
+ Abstract base class extending CF's `AIChatAgent`.
444
+
445
+ ```typescript
446
+ abstract class AriaFlowAIChatAgent<Env, State> {
447
+ // Required
448
+ protected abstract getRuntimeConfig(): HarnessConfig;
449
+
450
+ // Optional overrides
451
+ protected getHooks(): Partial<HarnessHooks>;
452
+ protected getStreamAdapterConfig(): Partial<StreamAdapterConfig>;
453
+ protected getSessionId(): string;
454
+
455
+ // Utility methods
456
+ protected async getSession(): Promise<Session | null>;
457
+ protected getSessionStats(): SessionStats | null;
458
+ }
459
+ ```
460
+
461
+ ### Environment Variables
462
+
463
+ ```bash
464
+ # For OpenAI
465
+ OPENAI_API_KEY=your-key
466
+
467
+ # For Anthropic
468
+ ANTHROPIC_API_KEY=your-key
469
+
470
+ # For custom providers
471
+ PROVIDER_API_KEY=your-key
472
+ ```
473
+
474
+ ## Architecture
475
+
476
+ ```
477
+ Client (useAgentChat)
478
+
479
+ ▼ WebSocket
480
+ CF AIChatAgent (Durable Object)
481
+ ├─► CF SQLite: Chat messages, stream chunks
482
+ └─► AriaFlow Runtime
483
+ ├─► CloudflareSessionStore (SQLite)
484
+ ├─► Multi-agent execution
485
+ └─► StreamAdapter
486
+ └─► UIMessageChunk format
487
+ ```
488
+
489
+ ## Learn More
490
+
491
+ - [AriaFlow Documentation](https://github.com/ariaflowagents/ariaflow)
492
+ - [Cloudflare Agents SDK](https://developers.cloudflare.com/agents/)
493
+ - [Vercel AI SDK](https://ai-sdk.dev)
494
+
495
+ ## License
496
+
497
+ MIT