/** * AriaFlowAgent -- AriaFlow on Cloudflare Durable Objects. * * Extends CF's AIChatAgent and works WITH it, not against it: * * CF owns: messages, persistence, WebSocket, resumability * AriaFlow owns: agent orchestration (current agent, working memory, * flow state, handoff history, extraction data) * * On each chat message: * 1. CF calls onChatMessage() with this.messages already populated * 2. We build a BridgeSessionStore from CF messages + orchestration state * 3. AriaFlow Runtime runs the agent pipeline * 4. We return an SSE Response in AI SDK format * 5. CF's _reply() reads the SSE stream, builds message parts, * calls persistMessages(), broadcasts to clients, handles resumability * * AriaFlow's orchestration state (current agent, working memory, flow state) * is stored in a separate lightweight SQLite table via OrchestrationStore. */ import { AIChatAgent } from '@cloudflare/ai-chat'; import type { HarnessConfig } from '@ariaflowagents/core'; import type { StreamTextOnFinishCallback, ToolSet } from 'ai'; import type { OnChatMessageOptions } from '@cloudflare/ai-chat'; import type { StreamAdapterConfig } from './types.js'; /** * Abstract base class for running AriaFlow agents on Cloudflare. * * @example * ```typescript * import { AriaFlowAgent } from '@ariaflowagents/cf-agent'; * import { openai } from '@ai-sdk/openai'; * * class MyAgent extends AriaFlowAgent { * protected getAgents(): HarnessConfig['agents'] { * return [{ * id: 'assistant', * name: 'Assistant', * type: 'llm', * model: openai('gpt-4o', { apiKey: this.env.OPENAI_API_KEY }), * prompt: 'You are a helpful assistant.', * }]; * } * * protected getDefaultAgentId() { return 'assistant'; } * } * ``` */ export declare abstract class AriaFlowAgent extends AIChatAgent { private runtime; /** * Required: Define the agents for this runtime. */ protected abstract getAgents(): HarnessConfig['agents']; /** * Required: Which agent handles the first message. */ protected abstract getDefaultAgentId(): string; /** * Optional: Additional runtime config (hooks, model, processors, etc.). * Merged with agents + defaultAgentId + sessionStore. */ protected getRuntimeConfig(): Partial; /** * Optional: Configure which AriaFlow events become data parts in the stream. */ protected getStreamConfig(): Partial; /** * Get the SQL executor for the Durable Object. * CF's AIChatAgent exposes this.sql as a tagged template function. */ private getSql; /** * Get the Durable Object ID as the session identifier. */ private getSessionId; /** * Called by CF when a chat message arrives. * * CF has already: * 1. Received the WebSocket message from the client * 2. Parsed and validated it * 3. Persisted the user message to cf_ai_chat_agent_messages * 4. Populated this.messages with the full conversation history * * We: * 1. Create a BridgeSessionStore (CF messages + orchestration state) * 2. Build and run AriaFlow Runtime * 3. Return an SSE Response * * CF then: * 1. Reads the SSE stream via _reply() * 2. Builds assistant message parts via applyChunkToParts() * 3. Persists the assistant message * 4. Broadcasts to all connected clients * 5. Handles stream resumability */ onChatMessage(onFinish: StreamTextOnFinishCallback, options?: OnChatMessageOptions): Promise; /** * Extract the text content of the last user message from CF's messages array. */ private getLastUserInput; /** * Delete orchestration rows older than `maxAgeMs`. Returns the number of * rows removed. * * No automatic scheduling — callers opt in from their own `alarm()` or an * HTTP endpoint. This keeps retention policy explicit rather than hiding * it behind a probabilistic tick that could silently destroy state. * * Typical usage from a subclass `alarm()`: * ```ts * async alarm() { * await this.cleanupOrchestrationRows(30 * 24 * 60 * 60 * 1000); // 30 days * // ... other alarm work ... * } * ``` */ protected cleanupOrchestrationRows(maxAgeMs: number): Promise; /** * HTTP endpoint handler. * Adds AriaFlow-specific endpoints on top of CF's defaults. */ onRequest(request: Request): Promise; }