JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 4
  • Score
    100M100P100Q64033F
  • License MIT

ARIA Cloudflare Agents integration with proper memory primitives for meaningful multi-turn conversations

Package Exports

  • @projectaria/cf-agents

Readme

@projectaria/cf-agents

ARIA integration for Cloudflare Workers with Durable Objects. Build stateful AI agents with persistent memory, session management, and multi-turn conversations using structured flows.

Installation

npm install @projectaria/cf-agents agents @cloudflare/ai-chat ai

Message Flow Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                          MESSAGE FLOW DIAGRAM                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  CLIENT SIDE                              SERVER SIDE                         │
│  ────────────                             ────────────                        │
│                                                                              │
│  User Input                                                              │
│       │                                                                   │
│       ▼                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    Cloudflare AIChatAgent                            │   │
│  │  this.messages (UIMessage[]) ←── WebSocket/HTTP                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│       │                                                                   │
│       │ onChatMessage()                                                  │
│       ▼                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    AriaCFChatAgent                                    │   │
│  │                                                                     │   │
│  │  1. extractLastUserInput() → userText                                │   │
│  │  2. cloudflareToAriaSession() → AgentSession                         │   │
│  │       │                                                              │   │
│  │       ▼                                                              │   │
│  │  3. ariaAgent.streamText({ input, session })                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│       │                                                                   │
│       │ (AsyncIterable<AriaStreamPart>)                                  │
│       ▼                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         ARIA Agent                                    │   │
│  │  createAgent()                                                       │   │
│  │       │                                                              │   │
│  │       ▼                                                              │   │
│  │  streamText()                                                        │   │
│  │       │                                                              │   │
│  │       ▼                                                              │   │
│  │  ┌────────────────────────────────────────────────────────────────┐  │   │
│  │  │              PromptBuilder + Conversation Context               │  │   │
│  │  └────────────────────────────────────────────────────────────────┘  │   │
│  │       │                                                              │   │
│  │       ▼                                                              │   │
│  │  ┌────────────────────────────────────────────────────────────────┐  │   │
│  │  │                    AI SDK LanguageModel                          │  │   │
│  │  │  streamText(model, messages, tools)                              │  │   │
│  │  └────────────────────────────────────────────────────────────────┘  │   │
│  │       │                                                              │   │
│  │       ▼ (stream of TextStreamPart)                                   │   │
│  │  ┌────────────────────────────────────────────────────────────────┐  │   │
│  │  │              ResponseService / Tool Execution                    │  │   │
│  │  └────────────────────────────────────────────────────────────────┘  │   │
│  │       │                                                              │   │
│  │       ▼ (AriaStreamPart: text-delta, tool-call, tool-result, ...)   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│       │                                                                   │
│       │ ariaToUIStream()                                                 │
│       ▼                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                 aria-to-ui-stream.ts                                  │   │
│  │  AriaStreamPart → TextUIPart | ToolCallUIPart | ToolResultUIPart     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│       │                                                                   │
│       │ createUIMessageStreamResponse()                                  │
│       ▼                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    Response (SSE/WebSocket)                           │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│       │                                                                   │
│       ▼                                                                   │
│  CLIENT UI ←───────────────────────────────────────────────────────────────
│                                                                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                          SESSION & STATE FLOW                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                     VoltRuntime                                       │   │
│  │                                                                     │   │
│  │  ┌───────────────────────────────────────────────────────────────┐  │   │
│  │  │                    FlowSupervisor                              │  │   │
│  │  │  • Selects active flow based on intent                        │  │   │
│  │  │  • Handles flow switching mid-conversation                    │  │   │
│  │  └───────────────────────────────────────────────────────────────┘  │   │
│  │       │                                                             │   │
│  │       ▼                                                             │   │
│  │  ┌───────────────────────────────────────────────────────────────┐  │   │
│  │  │              StreamingFlowManager                              │  │   │
│  │  │  • Executes current node                                       │  │   │
│  │  │  • Validates extracted data                                    │  │   │
│  │  │  • Evaluates transitions                                       │  │   │
│  │  └───────────────────────────────────────────────────────────────┘  │   │
│  │       │                                                             │   │
│  │       ▼                                                             │   │
│  │  ┌───────────────────────────────────────────────────────────────┐  │   │
│  │  │              CombinedAnalysisService                           │  │   │
│  │  │  • LLM validation of extracted fields                          │  │   │
│  │  │  • Structured data extraction                                  │  │   │
│  │  └───────────────────────────────────────────────────────────────┘  │   │
│  │       │                                                             │   │
│  │       ▼                                                             │   │
│  │  ┌───────────────────────────────────────────────────────────────┐  │   │
│  │  │              ProgressionEngine                                  │  │   │
│  │  │  • Evaluates condition expressions                             │  │   │
│  │  │  • Determines next node                                        │  │   │
│  │  └───────────────────────────────────────────────────────────────┘  │   │
│  │       │                                                             │   │
│  │       ▼                                                             │   │
│  │  ┌───────────────────────────────────────────────────────────────┐  │   │
│  │  │              SessionStore (SQLite via D1)                      │  │   │
│  │  │  • Persists session.history                                    │  │   │
│  │  │  • Persists session.workingMemory                              │  │   │
│  │  │  • Restores sessions on agent restart                          │  │   │
│  │  └───────────────────────────────────────────────────────────────┘  │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Message Handling Sequence

// 1. Client sends message via WebSocket or HTTP
//    → Cloudflare AIChatAgent receives it
//    → Stores in this.messages

// 2. AriaCFChatAgent.onChatMessage() is called
async onChatMessage(onFinish, options) {
  // 2a. Extract user text from last message
  const userText = extractLastUserInput(this.messages);

  // 2b. Convert Cloudflare messages to ARIA session format
  const ariaSession = cloudflareToAriaSession(this.messages, this.state);

  // 2c. Start session in VoltRuntime (loads or creates)
  await this.voltRuntime.startSession(ariaSession.sessionId);

  // 2d. Stream from ARIA agent
  const result = await this.ariaAgent.streamText({
    input: userText,
    session: ariaSession,
  });

  // 2e. Convert ARIA stream parts to UI format
  return createARIAStreamResponse(result.fullStream, onFinish);
}

// 3. ARIA Agent processes the stream
streamText({ input, session }) {
  // 3a. Build prompt from flow + session history
  const prompt = PromptBuilder.build({
    flow: currentFlow,
    session,
    instructions,
    guidelines,
  });

  // 3b. Call LLM with prompt + tools
  return streamText(model, messages, tools);
}

// 4. Stream parts are emitted:
//    - text-delta → UI text chunk
//    - tool-call → UI tool call (input-available)
//    - tool-result → UI tool result (output-available)
//    - reasoning-delta → UI reasoning part

// 5. Session is updated with working memory changes
//    → Persisted to SQLite via SessionStore

Quick Start

1. Create Your Agent with a Flow

// src/agent.ts
import { createAriaCFChatAgent } from "@projectaria/cf-agents";
import type { FlowDefinition } from "@projectaria/aria-agents";
import { z } from "zod";

interface Env {
  AI: Ai;
  DB: D1Database;
}

// Define a conversation flow
const greetingFlow: FlowDefinition = {
  id: "greeting",
  name: "Greeting Assistant",
  startNodeId: "greet",
  nodes: {
    greet: {
      id: "greet",
      type: "task",
      instructions: "Welcome the user and ask how you can help.",
      transitions: [
        { nextNodeId: "help", condition: "intent == 'help'" },
      ],
    },
    help: {
      id: "help",
      type: "task",
      instructions: "Provide helpful assistance.",
      transitions: [
        { nextNodeId: "greet", condition: "always" },
      ],
    },
  },
};

// Create agent with any AI SDK model
const MyAgent = createAriaCFChatAgent<Env>({
  flows: new Map([[greetingFlow.id, greetingFlow]]),
  model: env.AI("@cf/meta/llama-3.1-8b-instruct"), // Any LanguageModel
  instructions: "You are a friendly assistant.",
});

export { MyAgent };

2. Configure Your Worker

// src/index.ts
import { routeAgentRequest } from "agents";
import { MyAgent } from "./agent";

export { MyAgent };

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

3. Configure Wrangler

# wrangler.toml
name = "my-assistant"
main = "src/index.ts"
compatibility_date = "2025-02-11"

[[d1_databases]]
binding = "DB"
database_name = "aria-sessions"

[ai]
binding = "AI"

4. Deploy

npx wrangler deploy

Features

Flow Orchestration

Define structured multi-turn conversations:

const orderFlow: FlowDefinition = {
  id: "order",
  startNodeId: "collect-items",
  nodes: {
    "collect-items": {
      id: "collect-items",
      type: "task",
      instructions: "Ask which items they want to order.",
      transitions: [
        { nextNodeId: "confirm", condition: "items selected" },
      ],
    },
    confirm: {
      id: "confirm",
      type: "task",
      instructions: "Show order summary and ask for confirmation.",
      transitions: [
        { nextNodeId: "complete", condition: "confirmed" },
        { nextNodeId: "collect-items", condition: "cancelled" },
      ],
    },
  },
};

Working Memory

Persist data across conversation turns:

const MyAgent = createAriaCFChatAgent<Env>({
  flows: myFlows,
  model: env.AI("@cf/meta/llama-3.1-8b-instruct"),
});

class ShoppingAssistant extends MyAgent {
  async onChatMessage(onFinish) {
    // Working memory is automatically managed by ARIA
    // Access via session.workingMemory in tools or flows
  }
}

Tools

Register custom tools for your agent:

import { tool } from "ai";
import { z } from "zod";

const MyAgent = createAriaCFChatAgent<Env>({
  flows: myFlows,
  model: env.AI("@cf/meta/llama-3.1-8b-instruct"),
  tools: {
    searchProducts: tool({
      description: "Search for products",
      parameters: z.object({
        query: z.string(),
        category: z.string().optional(),
      }),
      execute: async ({ query, category }) => {
        // Your implementation
        return [{ id: "1", name: "Product", price: 9.99 }];
      },
    }),
  },
});

Multiple Model Providers

Use any AI SDK model:

import { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";
import { google } from "@ai-sdk/google";

// OpenAI
const agent1 = createAriaCFChatAgent({
  model: openai("gpt-4o-mini"),
  flows: myFlows,
});

// Anthropic
const agent2 = createAriaCFChatAgent({
  model: anthropic("claude-sonnet-4-20250514"),
  flows: myFlows,
});

// Google
const agent3 = createAriaCFChatAgent({
  model: google("gemini-2.0-flash"),
  flows: myFlows,
});

// Workers AI (Cloudflare)
const agent4 = createAriaCFChatAgent({
  model: env.AI("@cf/meta/llama-3.1-8b-instruct"),
  flows: myFlows,
});

API Reference

createAriaCFChatAgent

function createAriaCFChatAgent<Env>(
  config: AriaCFChatAgentConfig
): new (ctx: AgentContext, env: Env) => AriaCFChatAgent<Env>;

AriaCFChatAgentConfig

interface AriaCFChatAgentConfig {
  // Required
  flows: Map<string, FlowDefinition>;
  model: LanguageModel;  // Any AI SDK model

  // Optional
  instructions?: string;           // Agent instructions
  guidelines?: string[];           // System guidelines
  tools?: ToolRegistry;            // Custom tools
  historyLimit?: number;           // Max history messages (default: 50)
  enablePersistence?: boolean;     // Enable session persistence (default: true)
}

AriaCFChatAgent

Base class extended by the factory:

abstract class AriaCFChatAgent<Env = any> extends AIChatAgent<Env, AriaCFState> {
  // Configuration
  protected readonly config: AriaCFChatAgentConfigResolved;
  protected readonly ariaAgent: AriaAgent;
  protected readonly voltRuntime: VoltRuntime;

  // Override these in your subclass
  protected abstract getConfig(): AriaCFChatAgentConfig;

  // Optional overrides
  protected getModel(): LanguageModel;
  protected getModelId(): string;
  protected getTools(): ToolRegistry;
}

Connecting from Client

Using React (Chat)

import { useAgentChat } from "@cloudflare/ai-chat/react";

function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useAgentChat({
    agent: "my-assistant",
  });

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

Using WebSocket

const ws = new WebSocket("wss://your-worker.workers.dev/agents/my-assistant/session-123");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "cf.agent.chat.request",
    init: {
      method: "POST",
      body: JSON.stringify({
        messages: [{ role: "user", content: "Hello!" }]
      })
    }
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("Response:", data);
};

Using HTTP

const response = await fetch("https://your-worker.workers.dev/agents/my-assistant/session-123", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    messages: [{ role: "user", content: "Hello!" }]
  })
});

Complete Example

See examples/ShoppingAgent.ts for a full shopping assistant example with flows, tools, and persistence.

License

MIT