JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 4
  • Score
    100M100P100Q64272F
  • 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.

Installation

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

Quick Start

1. Create Your Agent

// src/agent.ts
import { AriaAgent } from "@projectaria/cf-agents";
import { createAgent } from "@projectaria/aria-agents";
import { openai } from "@ai-sdk/openai";

export class MyAssistant extends AriaAgent {
  // Create an ARIA agent with your configuration
  private ariaAgent = createAgent({
    id: "assistant",
    name: "My Assistant",
    instructions: "You are a helpful assistant that remembers user preferences.",
    model: openai("gpt-4o"),
  });

  // Handle incoming chat messages
  async onChatMessage(onFinish: () => void) {
    // Get the current session with message history
    const session = this.getSession();
    const lastMessage = session.messages[session.messages.length - 1];

    if (!lastMessage) {
      return;
    }

    // Run the ARIA agent with automatic memory persistence
    const result = await this.runAriaAgent(this.ariaAgent, lastMessage.content);

    // Call onFinish to signal completion
    onFinish();

    // Return response (for HTTP) or it's already sent via WebSocket
    return new Response(result.text);
  }
}

2. Configure Your Worker

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

export { MyAssistant };

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

3. Configure Wrangler

// wrangler.jsonc
{
  "name": "my-assistant",
  "main": "src/index.ts",
  "compatibility_date": "2025-02-11",
  "compatibility_flags": ["nodejs_compat"],
  "durable_objects": {
    "bindings": [
      {
        "name": "MyAssistant",
        "class_name": "MyAssistant"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["MyAssistant"]
    }
  ]
}

4. Deploy

npx wrangler deploy

Features

Memory Primitives

AriaAgent provides three levels of memory:

Memory Type Scope Use Case
Session Current conversation Message history, flow state
State Working memory Shopping cart, form data
Memory Cross-session User preferences, learned facts

Working Memory (Session-scoped)

class MyAgent extends AriaAgent {
  async onChatMessage(onFinish) {
    // Store data for current session
    this.setWorkingMemory("cart", { items: ["item1", "item2"] });
    
    // Retrieve working memory
    const memory = this.getWorkingMemory();
    console.log(memory.cart); // { items: ["item1", "item2"] }
  }
}

Cross-Session Memory

class MyAgent extends AriaAgent {
  async onChatMessage(onFinish) {
    // Store a preference that persists across sessions
    this.storeMemory({
      key: "user:theme",
      value: { theme: "dark", fontSize: "large" },
      type: "preference",
    });

    // Search memories
    const results = this.searchMemories({
      query: "theme",
      types: ["preference"],
      limit: 5,
    });
  }
}

Flow Orchestration

import { createFlow } from "@projectaria/aria-agents";

class MyAgent extends AriaAgent {
  private orderFlow = createFlow({
    id: "order-flow",
    nodes: [
      { id: "collect-items", ... },
      { id: "confirm-order", ... },
      { id: "payment", ... },
    ],
  });

  constructor(ctx, env) {
    super(ctx, env);
    this.registerFlow(this.orderFlow);
  }
}

API Reference

AriaAgent Class

class AriaAgent extends AIChatAgent<AriaEnv, AriaSessionState> {
  // Session Management
  getSession(): AriaSession
  
  // Working Memory
  getWorkingMemory(): WorkingMemory
  setWorkingMemory(key: string, value: unknown): void
  clearWorkingMemory(): void
  
  // Cross-Session Memory
  storeMemory(memory: Omit<AriaMemory, "id" | ...>): string
  searchMemories(query: MemorySearchQuery): MemorySearchResult[]
  
  // ARIA Integration
  runAriaAgent(agent, input, options?): Promise<{ text: string; session: AgentSession }>
  streamAriaAgent(agent, input, options?): AsyncIterable<string>
  
  // Flow Orchestration
  registerFlow(flow: FlowDefinition): void
  transitionFlow(flowId: string, toNodeId: string): void
}

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 React (State Synchronization)

The useAgent hook provides real-time state synchronization between your React app and the Agent:

import { useState } from "react";
import { useAgent } from "agents/react";

function ShoppingCart() {
  const [state, setState] = useState({ 
    cart: [], 
    workingMemory: {} 
  });

  // Connect to the agent and sync state
  const agent = useAgent({
    agent: "shopping-assistant",
    name: "user-session-123",
    onStateUpdate: (newState) => setState(newState),
  });

  const addToCart = (item: string) => {
    // Update state on both client and server
    agent.setState({
      ...state,
      workingMemory: {
        ...state.workingMemory,
        cart: [...(state.workingMemory.cart || []), item],
      },
    });
  };

  return (
    <div>
      <h2>Cart ({state.workingMemory.cart?.length || 0} items)</h2>
      <ul>
        {state.workingMemory.cart?.map((item, i) => (
          <li key={i}>{item}</li>
        ))}
      </ul>
      <button onClick={() => addToCart("New Item")}>Add Item</button>
    </div>
  );
}

State synchronization features:

  • Automatically syncs Agent state to all connected clients
  • Handles disconnections and reconnections gracefully
  • Provides immediate local updates
  • Supports multiple simultaneous client connections

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!" }]
  })
});

Environment Configuration

// src/types.ts
interface Env {
  MyAssistant: DurableObjectNamespace;
  OPENAI_API_KEY: string;
}
# wrangler.toml (secrets)
[vars]
# Set via: npx wrangler secret put OPENAI_API_KEY

Complete Example

// src/shopping-assistant.ts
import { AriaAgent } from "@projectaria/cf-agents";
import { createAgent } from "@projectaria/aria-agents";
import { openai } from "@ai-sdk/openai";
import { tool } from "ai";
import { z } from "zod";

export class ShoppingAssistant extends AriaAgent {
  private agent = createAgent({
    id: "shopping-assistant",
    name: "Shopping Assistant",
    instructions: `You are a helpful shopping assistant.
You remember user preferences and can help them find products.
Use the cart tools to manage their shopping cart.`,
    model: openai("gpt-4o"),
    tools: {
      addToCart: tool({
        description: "Add an item to the cart",
        parameters: z.object({
          item: z.string(),
          quantity: z.number().default(1),
        }),
        execute: async ({ item, quantity }) => {
          const cart = this.getWorkingMemory().cart || [];
          cart.push({ item, quantity });
          this.setWorkingMemory("cart", cart);
          return { success: true, cart };
        },
      }),
      viewCart: tool({
        description: "View the current cart",
        parameters: z.object({}),
        execute: async () => {
          return this.getWorkingMemory().cart || [];
        },
      }),
    },
  });

  async onChatMessage(onFinish: () => void) {
    const session = this.getSession();
    const lastMessage = session.messages[session.messages.length - 1];

    if (!lastMessage) return;

    // Check for returning user preferences
    const prefs = this.searchMemories({
      types: ["preference"],
      limit: 5,
    });

    // Run agent with context
    const result = await this.runAriaAgent(this.agent, lastMessage.content);

    onFinish();
    return new Response(result.text);
  }
}

License

MIT