agents 0.8.7 → 0.9.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/experimental/memory/session/context.ts","../../../../src/experimental/memory/session/providers/agent.ts","../../../../src/experimental/memory/session/providers/agent-context.ts","../../../../src/experimental/memory/session/session.ts"],"sourcesContent":["/**\n * Context Block Management\n *\n * Persistent key-value blocks (MEMORY, USER, SOUL, etc.) that are:\n * - Loaded from their providers at init\n * - Frozen into a snapshot when toSystemPrompt() is called\n * - Updated via setBlock() which writes to the provider immediately\n * but does NOT update the frozen snapshot (preserves LLM prefix cache)\n * - Re-snapshotted on next toSystemPrompt() call\n */\n\nimport { jsonSchema, type ToolSet } from \"ai\";\nimport { estimateStringTokens } from \"../utils/tokens\";\n\n/**\n * Storage interface for a single context block.\n * Each block can have its own backing store (R2, SQLite, KV, in-memory, etc.)\n * If `set` is omitted, the block is readonly.\n */\nexport interface ContextProvider {\n get(): Promise<string | null>;\n set?(content: string): Promise<void>;\n}\n\n/**\n * Configuration for a context block.\n */\nexport interface ContextConfig {\n /** Block label — used as key and in tool descriptions */\n label: string;\n /** Human-readable description (shown to AI in tool) */\n description?: string;\n /** Initial content — used when provider returns null or is absent */\n initialContent?: string;\n /** Maximum tokens allowed. Enforced on set. */\n maxTokens?: number;\n /** If true, AI cannot modify this block via tools */\n readonly?: boolean;\n /** Storage provider. If omitted, auto-wired to SQLite when using builder. */\n provider?: ContextProvider;\n}\n\n/**\n * A loaded context block with computed token count.\n */\nexport interface ContextBlock {\n label: string;\n description?: string;\n content: string;\n tokens: number;\n maxTokens?: number;\n readonly?: boolean;\n}\n\n/**\n * Manages context blocks with frozen snapshot support.\n */\nexport class ContextBlocks {\n private configs: ContextConfig[];\n private blocks = new Map<string, ContextBlock>();\n private snapshot: string | null = null;\n private loaded = false;\n private promptStore: ContextProvider | null;\n\n constructor(configs: ContextConfig[], promptStore?: ContextProvider) {\n this.configs = configs;\n this.promptStore = promptStore ?? null;\n }\n\n isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Load all blocks from their providers.\n * Called once at session init.\n */\n async load(): Promise<void> {\n for (const config of this.configs) {\n let content: string | null = null;\n\n if (config.provider) {\n content = await config.provider.get();\n }\n\n content = content ?? config.initialContent ?? \"\";\n\n this.blocks.set(config.label, {\n label: config.label,\n description: config.description,\n content,\n tokens: estimateStringTokens(content),\n maxTokens: config.maxTokens,\n readonly: config.readonly\n });\n }\n this.loaded = true;\n }\n\n /**\n * Get a block by label.\n */\n getBlock(label: string): ContextBlock | null {\n return this.blocks.get(label) ?? null;\n }\n\n /**\n * Get all blocks.\n */\n getBlocks(): ContextBlock[] {\n return Array.from(this.blocks.values());\n }\n\n /**\n * Set block content. Writes to provider immediately.\n * Does NOT update the frozen snapshot.\n */\n async setBlock(label: string, content: string): Promise<ContextBlock> {\n if (!this.loaded) await this.load();\n const config = this.configs.find((c) => c.label === label);\n const existing = this.blocks.get(label);\n\n if (existing?.readonly || config?.readonly) {\n throw new Error(`Block \"${label}\" is readonly`);\n }\n\n const tokens = estimateStringTokens(content);\n const maxTokens = config?.maxTokens ?? existing?.maxTokens;\n\n if (maxTokens !== undefined && tokens > maxTokens) {\n throw new Error(\n `Block \"${label}\" exceeds maxTokens: ${tokens} > ${maxTokens}`\n );\n }\n\n const block: ContextBlock = {\n label,\n description: config?.description ?? existing?.description,\n content,\n tokens,\n maxTokens,\n readonly: false\n };\n\n this.blocks.set(label, block);\n\n // Write to provider immediately (durable)\n if (config?.provider?.set) {\n await config.provider.set(content);\n }\n\n // Snapshot is NOT updated — frozen until next toSystemPrompt() call\n\n return block;\n }\n\n /**\n * Append content to a block.\n */\n async appendToBlock(label: string, content: string): Promise<ContextBlock> {\n if (!this.loaded) await this.load();\n const existing = this.blocks.get(label);\n if (!existing) {\n throw new Error(`Block \"${label}\" not found`);\n }\n return this.setBlock(label, existing.content + content);\n }\n\n /**\n * Get the system prompt string with context blocks.\n *\n * Returns a frozen snapshot: first call renders and caches,\n * subsequent calls return the same string (preserves LLM prefix cache).\n * Call refreshSnapshot() to re-render after block changes take effect.\n */\n toSystemPrompt(): string {\n if (!this.loaded) {\n throw new Error(\"Context blocks not loaded. Call load() first.\");\n }\n\n // Return frozen snapshot if already captured\n if (this.snapshot !== null) {\n return this.snapshot;\n }\n\n return this.captureSnapshot();\n }\n\n /**\n * Force re-render the snapshot from current block state.\n * Call this at the start of a new session to pick up changes\n * made by setBlock() during the previous session.\n */\n refreshSnapshot(): string {\n return this.captureSnapshot();\n }\n\n private captureSnapshot(): string {\n const parts: string[] = [];\n const sep = \"═\".repeat(46);\n\n for (const block of this.blocks.values()) {\n if (!block.content) continue;\n\n let header = block.label.toUpperCase();\n if (block.description) header += ` (${block.description})`;\n if (block.maxTokens) {\n const pct = Math.round((block.tokens / block.maxTokens) * 100);\n header += ` [${pct}% — ${block.tokens}/${block.maxTokens} tokens]`;\n }\n if (block.readonly) header += \" [readonly]\";\n\n parts.push(`${sep}\\n${header}\\n${sep}\\n${block.content}`);\n }\n\n this.snapshot = parts.join(\"\\n\\n\");\n return this.snapshot;\n }\n\n /**\n * Get writable blocks (for tool description).\n */\n getWritableBlocks(): ContextBlock[] {\n return Array.from(this.blocks.values()).filter((b) => !b.readonly);\n }\n\n /**\n * Get writable block configs — doesn't require blocks to be loaded.\n */\n getWritableConfigs(): ContextConfig[] {\n return this.configs.filter((c) => !c.readonly);\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n /**\n * Frozen system prompt. On first call:\n * 1. Checks store for a persisted prompt (survives DO eviction)\n * 2. If none, loads blocks from providers, renders, and persists\n *\n * Subsequent calls return the stored version — true prefix cache stability.\n */\n async freezeSystemPrompt(): Promise<string> {\n if (this.promptStore) {\n const stored = await this.promptStore.get();\n if (stored !== null) return stored;\n }\n\n if (!this.loaded) await this.load();\n const prompt = this.toSystemPrompt();\n\n if (this.promptStore?.set) {\n await this.promptStore.set(prompt);\n }\n\n return prompt;\n }\n\n /**\n * Re-render the system prompt from current block state and persist.\n * Call after compaction or at session boundaries to pick up writes.\n */\n async refreshSystemPrompt(): Promise<string> {\n if (!this.loaded) await this.load();\n const prompt = this.refreshSnapshot();\n\n if (this.promptStore?.set) {\n await this.promptStore.set(prompt);\n }\n\n return prompt;\n }\n\n /**\n * AI tool for updating context blocks. Loads blocks lazily on first execute.\n */\n async tools(): Promise<ToolSet> {\n if (!this.loaded) await this.load();\n\n const writable = this.getWritableBlocks();\n if (writable.length === 0) return {};\n\n const blockDescriptions = writable\n .map((b) => `- \"${b.label}\": ${b.description ?? \"no description\"}`)\n .join(\"\\n\");\n\n const ctx = this;\n\n return {\n update_context: {\n description: `Update a context block. Available blocks:\\n${blockDescriptions}\\n\\nWrites are durable and persist across sessions.`,\n inputSchema: jsonSchema({\n type: \"object\" as const,\n properties: {\n label: {\n type: \"string\" as const,\n enum: writable.map((b) => b.label),\n description: \"Block label to update\"\n },\n content: {\n type: \"string\" as const,\n description: \"Content to write\"\n },\n action: {\n type: \"string\" as const,\n enum: [\"replace\", \"append\"],\n description: \"replace (default) or append\"\n }\n },\n required: [\"label\", \"content\"]\n }),\n execute: async ({\n label,\n content,\n action\n }: {\n label: string;\n content: string;\n action?: string;\n }) => {\n try {\n const block =\n action === \"append\"\n ? await ctx.appendToBlock(label, content)\n : await ctx.setBlock(label, content);\n const usage = block.maxTokens\n ? `${Math.round((block.tokens / block.maxTokens) * 100)}% (${block.tokens}/${block.maxTokens} tokens)`\n : `${block.tokens} tokens`;\n return `Written to ${label}. Usage: ${usage}`;\n } catch (err) {\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n }\n }\n }\n };\n }\n}\n","/**\n * Agent Session Provider\n *\n * SQLite-backed provider with tree-structured messages (branching),\n * compaction overlays, and FTS5 search.\n */\n\nimport type { UIMessage } from \"ai\";\nimport type {\n SessionProvider,\n SearchResult,\n StoredCompaction\n} from \"../provider\";\nimport { COMPACTION_PREFIX } from \"../../utils/compaction-helpers\";\n\nexport interface SqlProvider {\n sql<T = Record<string, string | number | boolean | null>>(\n strings: TemplateStringsArray,\n ...values: (string | number | boolean | null)[]\n ): T[];\n}\n\nexport class AgentSessionProvider implements SessionProvider {\n private agent: SqlProvider;\n private initialized = false;\n private sessionId: string;\n\n /**\n * @param agent - Agent or any object with a `sql` tagged template method\n * @param sessionId - Optional session ID to isolate multiple sessions in the same DO.\n * Messages are filtered by session_id within shared tables.\n */\n constructor(agent: SqlProvider, sessionId?: string) {\n this.agent = agent;\n this.sessionId = sessionId ?? \"\";\n }\n\n private ensureTable(): void {\n if (this.initialized) return;\n\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS assistant_messages (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL DEFAULT '',\n parent_id TEXT,\n role TEXT NOT NULL,\n content TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n\n this.agent.sql`\n CREATE INDEX IF NOT EXISTS idx_assistant_msg_parent\n ON assistant_messages(parent_id)\n `;\n\n this.agent.sql`\n CREATE INDEX IF NOT EXISTS idx_assistant_msg_session\n ON assistant_messages(session_id)\n `;\n\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS assistant_compactions (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL DEFAULT '',\n summary TEXT NOT NULL,\n from_message_id TEXT NOT NULL,\n to_message_id TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n\n this.agent.sql`\n CREATE VIRTUAL TABLE IF NOT EXISTS assistant_fts\n USING fts5(id UNINDEXED, session_id UNINDEXED, role UNINDEXED, content, tokenize='porter unicode61')\n `;\n\n // Reserved for SessionManager metadata (PR #1167) and Think integration (PR #1169)\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS assistant_config (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `;\n\n this.initialized = true;\n }\n\n // ── Read ───────────────────────────────────────────────────────\n\n getMessage(id: string): UIMessage | null {\n this.ensureTable();\n const rows = this.agent.sql<{ content: string }>`\n SELECT content FROM assistant_messages WHERE id = ${id} AND session_id = ${this.sessionId}\n `;\n return rows.length > 0 ? this.parse(rows[0].content) : null;\n }\n\n getHistory(leafId?: string | null): UIMessage[] {\n this.ensureTable();\n\n const leaf = leafId\n ? this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${leafId} AND session_id = ${this.sessionId}\n `[0]\n : this.latestLeafRow();\n\n if (!leaf) return [];\n\n const path = this.agent.sql<{ content: string }>`\n WITH RECURSIVE path AS (\n SELECT *, 0 as depth FROM assistant_messages WHERE id = ${leaf.id}\n UNION ALL\n SELECT m.*, p.depth + 1 FROM assistant_messages m\n JOIN path p ON m.id = p.parent_id\n WHERE m.session_id = ${this.sessionId} AND p.depth < 10000\n )\n SELECT content FROM path ORDER BY depth DESC\n `;\n\n const messages = this.parseRows(path);\n const compactions = this.getCompactions();\n if (compactions.length === 0) return messages;\n return this.applyCompactions(messages, compactions);\n }\n\n getLatestLeaf(): UIMessage | null {\n this.ensureTable();\n const row = this.latestLeafRow();\n return row ? this.parse(row.content) : null;\n }\n\n getBranches(messageId: string): UIMessage[] {\n this.ensureTable();\n const rows = this.agent.sql<{ content: string }>`\n SELECT content FROM assistant_messages\n WHERE parent_id = ${messageId} AND session_id = ${this.sessionId} ORDER BY created_at ASC\n `;\n return this.parseRows(rows);\n }\n\n getPathLength(leafId?: string | null): number {\n this.ensureTable();\n const leaf = leafId\n ? this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${leafId} AND session_id = ${this.sessionId}\n `[0]\n : this.latestLeafRow();\n if (!leaf) return 0;\n\n const rows = this.agent.sql<{ count: number }>`\n WITH RECURSIVE path AS (\n SELECT id, parent_id, 0 as depth FROM assistant_messages WHERE id = ${leaf.id}\n UNION ALL\n SELECT m.id, m.parent_id, p.depth + 1 FROM assistant_messages m\n JOIN path p ON m.id = p.parent_id\n WHERE m.session_id = ${this.sessionId} AND p.depth < 10000\n )\n SELECT COUNT(*) as count FROM path\n `;\n return rows[0]?.count ?? 0;\n }\n\n // ── Write ──────────────────────────────────────────────────────\n\n appendMessage(message: UIMessage, parentId?: string | null): void {\n this.ensureTable();\n // Skip if message already exists (INSERT OR IGNORE idempotency)\n const existing = this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${message.id} AND session_id = ${this.sessionId}\n `;\n if (existing.length > 0) return;\n\n let parent = parentId ?? this.latestLeafRow()?.id ?? null;\n\n // Validate parentId belongs to this session\n if (parent) {\n const valid = this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${parent} AND session_id = ${this.sessionId}\n `;\n if (valid.length === 0) parent = null;\n }\n\n const json = JSON.stringify(message);\n\n this.agent.sql`\n INSERT INTO assistant_messages (id, session_id, parent_id, role, content)\n VALUES (${message.id}, ${this.sessionId}, ${parent}, ${message.role}, ${json})\n `;\n this.indexFTS(message);\n }\n\n updateMessage(message: UIMessage): void {\n this.ensureTable();\n this.agent.sql`\n UPDATE assistant_messages SET content = ${JSON.stringify(message)}\n WHERE id = ${message.id} AND session_id = ${this.sessionId}\n `;\n this.indexFTS(message);\n }\n\n deleteMessages(messageIds: string[]): void {\n this.ensureTable();\n for (const id of messageIds) {\n this.agent\n .sql`DELETE FROM assistant_messages WHERE id = ${id} AND session_id = ${this.sessionId}`;\n this.deleteFTS(id);\n }\n }\n\n clearMessages(): void {\n this.ensureTable();\n this.agent\n .sql`DELETE FROM assistant_messages WHERE session_id = ${this.sessionId}`;\n this.agent\n .sql`DELETE FROM assistant_compactions WHERE session_id = ${this.sessionId}`;\n // FTS5 requires delete by rowid\n const ftsRows = this.agent.sql<{ rowid: number }>`\n SELECT rowid FROM assistant_fts WHERE session_id = ${this.sessionId}\n `;\n for (const row of ftsRows) {\n this.agent.sql`DELETE FROM assistant_fts WHERE rowid = ${row.rowid}`;\n }\n }\n\n // ── Compaction ─────────────────────────────────────────────────\n\n addCompaction(\n summary: string,\n fromMessageId: string,\n toMessageId: string\n ): StoredCompaction {\n this.ensureTable();\n const id = crypto.randomUUID();\n this.agent.sql`\n INSERT INTO assistant_compactions (id, session_id, summary, from_message_id, to_message_id)\n VALUES (${id}, ${this.sessionId}, ${summary}, ${fromMessageId}, ${toMessageId})\n `;\n return {\n id,\n summary,\n fromMessageId,\n toMessageId,\n createdAt: new Date().toISOString()\n };\n }\n\n getCompactions(): StoredCompaction[] {\n this.ensureTable();\n type Row = {\n id: string;\n summary: string;\n from_message_id: string;\n to_message_id: string;\n created_at: string;\n };\n return this.agent.sql<Row>`\n SELECT * FROM assistant_compactions WHERE session_id = ${this.sessionId} ORDER BY created_at ASC\n `.map((r) => ({\n id: r.id,\n summary: r.summary,\n fromMessageId: r.from_message_id,\n toMessageId: r.to_message_id,\n createdAt: r.created_at\n }));\n }\n\n // ── Search ─────────────────────────────────────────────────────\n\n searchMessages(query: string, limit = 20): SearchResult[] {\n this.ensureTable();\n // Sanitize query: wrap in double quotes to treat as literal phrase,\n // escaping any existing double quotes to prevent FTS5 syntax injection\n const sanitized = `\"${query.replace(/\"/g, '\"\"')}\"`;\n try {\n return this.agent.sql<{ id: string; role: string; content: string }>`\n SELECT f.id, f.role, f.content FROM assistant_fts f\n INNER JOIN assistant_messages m ON m.id = f.id AND m.session_id = f.session_id\n WHERE assistant_fts MATCH ${sanitized} AND f.session_id = ${this.sessionId}\n ORDER BY rank LIMIT ${limit}\n `.map((r) => ({\n id: r.id,\n role: r.role,\n content: r.content\n }));\n } catch {\n // Malformed FTS query — return empty results\n return [];\n }\n }\n\n // ── Internal ───────────────────────────────────────────────────\n\n private latestLeafRow(): { id: string; content: string } | null {\n const rows = this.agent.sql<{ id: string; content: string }>`\n SELECT m.id, m.content FROM assistant_messages m\n LEFT JOIN assistant_messages c ON c.parent_id = m.id AND c.session_id = ${this.sessionId}\n WHERE c.id IS NULL AND m.session_id = ${this.sessionId}\n ORDER BY m.created_at DESC, m.rowid DESC LIMIT 1\n `;\n return rows[0] ?? null;\n }\n\n private indexFTS(message: UIMessage): void {\n const text = message.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\" \");\n // Always delete old entry first — handles text→no-text transitions\n this.deleteFTS(message.id);\n if (text) {\n this.agent.sql`\n INSERT INTO assistant_fts (id, session_id, role, content)\n VALUES (${message.id}, ${this.sessionId}, ${message.role}, ${text})\n `;\n }\n }\n\n private deleteFTS(id: string): void {\n const rows = this.agent.sql<{ rowid: number }>`\n SELECT rowid FROM assistant_fts WHERE id = ${id} AND session_id = ${this.sessionId}\n `;\n for (const row of rows) {\n this.agent.sql`DELETE FROM assistant_fts WHERE rowid = ${row.rowid}`;\n }\n }\n\n private applyCompactions(\n messages: UIMessage[],\n compactions: StoredCompaction[]\n ): UIMessage[] {\n const ids = messages.map((m) => m.id);\n const result: UIMessage[] = [];\n let i = 0;\n while (i < messages.length) {\n // Find all compactions starting at this message, pick the latest\n // (widest range) so newer compactions supersede older ones\n const matching = compactions.filter((c) => c.fromMessageId === ids[i]);\n const comp =\n matching.length > 1 ? matching[matching.length - 1] : matching[0];\n if (comp) {\n const endIdx = ids.indexOf(comp.toMessageId);\n if (endIdx >= i) {\n result.push({\n id: `${COMPACTION_PREFIX}${comp.id}`,\n role: \"assistant\",\n parts: [\n {\n type: \"text\",\n text: comp.summary\n }\n ],\n createdAt: new Date()\n } as UIMessage);\n i = endIdx + 1;\n continue;\n }\n }\n result.push(messages[i]);\n i++;\n }\n return result;\n }\n\n private parse(json: string): UIMessage | null {\n try {\n const msg = JSON.parse(json);\n if (\n typeof msg?.id === \"string\" &&\n typeof msg?.role === \"string\" &&\n Array.isArray(msg?.parts)\n ) {\n return msg;\n }\n } catch {\n /* skip */\n }\n return null;\n }\n\n private parseRows(rows: { content: string }[]): UIMessage[] {\n const result: UIMessage[] = [];\n for (const row of rows) {\n const msg = this.parse(row.content);\n if (msg) result.push(msg);\n }\n return result;\n }\n}\n","/**\n * SQLite Context Block Provider\n *\n * Default durable storage for context blocks using DO SQLite.\n * Each block is a row in cf_agents_context_blocks.\n */\n\nimport type { ContextProvider } from \"../context\";\nimport type { SqlProvider } from \"./agent\";\n\nexport class AgentContextProvider implements ContextProvider {\n private agent: SqlProvider;\n private label: string;\n private initialized = false;\n\n constructor(agent: SqlProvider, label: string) {\n this.agent = agent;\n this.label = label;\n }\n\n private ensureTable(): void {\n if (this.initialized) return;\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS cf_agents_context_blocks (\n label TEXT PRIMARY KEY,\n content TEXT NOT NULL,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n this.initialized = true;\n }\n\n async get(): Promise<string | null> {\n this.ensureTable();\n const rows = this.agent.sql<{ content: string }>`\n SELECT content FROM cf_agents_context_blocks WHERE label = ${this.label}\n `;\n return rows[0]?.content ?? null;\n }\n\n async set(content: string): Promise<void> {\n this.ensureTable();\n this.agent.sql`\n INSERT INTO cf_agents_context_blocks (label, content)\n VALUES (${this.label}, ${content})\n ON CONFLICT(label) DO UPDATE SET content = ${content}, updated_at = CURRENT_TIMESTAMP\n `;\n }\n}\n","/**\n * Session — conversation history, context blocks, compaction, search, and tools.\n */\n\nimport type { ToolSet } from \"ai\";\nimport type { UIMessage } from \"ai\";\nimport type { SessionProvider, StoredCompaction } from \"./provider\";\nimport type { SessionOptions } from \"./types\";\nimport {\n ContextBlocks,\n type ContextBlock,\n type ContextConfig,\n type ContextProvider\n} from \"./context\";\nimport { AgentSessionProvider, type SqlProvider } from \"./providers/agent\";\nimport { AgentContextProvider } from \"./providers/agent-context\";\nimport type { CompactResult } from \"../utils/compaction-helpers\";\nimport { estimateMessageTokens } from \"../utils/tokens\";\nimport { MessageType } from \"../../../types\";\n\nexport type SessionContextOptions = Omit<ContextConfig, \"label\">;\n\n// Raw builder entry — provider resolved at init time so chain order doesn't matter\ninterface PendingContext {\n label: string;\n options: SessionContextOptions;\n}\n\n/** Agent-like object that can broadcast to connected clients */\ninterface Broadcaster {\n broadcast(message: string | ArrayBufferLike): void;\n}\n\nfunction isBroadcaster(obj: unknown): obj is Broadcaster {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"broadcast\" in obj &&\n typeof (obj as Broadcaster).broadcast === \"function\"\n );\n}\n\nexport class Session {\n private storage!: SessionProvider;\n private context!: ContextBlocks;\n\n // Builder state — only used with Session.create()\n private _agent?: SqlProvider;\n private _broadcaster?: Broadcaster;\n private _sessionId?: string;\n private _pending?: PendingContext[];\n private _cachedPrompt?: ContextProvider | true;\n private _compactionFn?:\n | ((messages: UIMessage[]) => Promise<CompactResult | null>)\n | null;\n private _tokenThreshold?: number;\n private _ready = false;\n\n constructor(storage: SessionProvider, options?: SessionOptions) {\n this.storage = storage;\n this.context = new ContextBlocks(\n options?.context ?? [],\n options?.promptStore\n );\n this._ready = true;\n }\n\n /**\n * Chainable session creation with auto-wired SQLite providers.\n * Chain methods in any order — providers are resolved lazily on first use.\n *\n * @example\n * ```ts\n * const session = Session.create(this)\n * .withContext(\"soul\", { initialContent: \"You are helpful.\", readonly: true })\n * .withContext(\"memory\", { description: \"Learned facts\", maxTokens: 1100 })\n * .withCachedPrompt();\n *\n * // Custom storage (R2, KV, etc.)\n * const session = Session.create(this)\n * .withContext(\"workspace\", {\n * provider: {\n * get: () => env.BUCKET.get(\"ws.md\").then(o => o?.text() ?? null),\n * set: (c) => env.BUCKET.put(\"ws.md\", c),\n * }\n * })\n * .withCachedPrompt();\n * ```\n */\n static create(agent: SqlProvider): Session {\n const session: Session = Object.create(Session.prototype);\n session._agent = agent;\n if (isBroadcaster(agent)) {\n session._broadcaster = agent;\n }\n session._pending = [];\n session._ready = false;\n return session;\n }\n\n // ── Builder methods ─────────────────────────────────────────────\n\n forSession(sessionId: string): this {\n this._sessionId = sessionId;\n return this;\n }\n\n withContext(label: string, options?: SessionContextOptions): this {\n this._pending!.push({ label, options: options ?? {} });\n return this;\n }\n\n withCachedPrompt(provider?: ContextProvider): this {\n this._cachedPrompt = provider ?? true;\n return this;\n }\n\n /**\n * Register a compaction function. Called by `compact()` to compress\n * message history into a summary overlay.\n */\n onCompaction(\n fn: (messages: UIMessage[]) => Promise<CompactResult | null>\n ): this {\n this._compactionFn = fn;\n return this;\n }\n\n /**\n * Auto-compact when estimated token count exceeds the threshold.\n * Checked after each `appendMessage`. Requires `onCompaction()`.\n */\n compactAfter(tokenThreshold: number): this {\n this._tokenThreshold = tokenThreshold;\n return this;\n }\n\n // ── Lazy init ───────────────────────────────────────────────────\n\n private _ensureReady(): void {\n if (this._ready) return;\n\n // Resolve context configs — sessionId is final by now\n const configs: ContextConfig[] = (this._pending ?? []).map(\n ({ label, options: opts }) => {\n let provider = opts.provider;\n if (!provider && !opts.readonly) {\n const key = this._sessionId ? `${label}_${this._sessionId}` : label;\n provider = new AgentContextProvider(this._agent!, key);\n }\n return {\n label,\n description: opts.description,\n initialContent: opts.initialContent,\n maxTokens: opts.maxTokens,\n readonly: opts.readonly,\n provider\n };\n }\n );\n\n // Resolve prompt store\n let promptStore: ContextProvider | undefined;\n if (this._cachedPrompt === true) {\n const key = this._sessionId\n ? `_system_prompt_${this._sessionId}`\n : \"_system_prompt\";\n promptStore = new AgentContextProvider(this._agent!, key);\n } else if (this._cachedPrompt) {\n promptStore = this._cachedPrompt;\n }\n\n this.storage = new AgentSessionProvider(this._agent!, this._sessionId);\n this.context = new ContextBlocks(configs, promptStore);\n this._ready = true;\n }\n\n // ── History (tree-structured) ─────────────────────────────────\n\n getHistory(leafId?: string | null): UIMessage[] {\n this._ensureReady();\n return this.storage.getHistory(leafId);\n }\n\n getMessage(id: string): UIMessage | null {\n this._ensureReady();\n return this.storage.getMessage(id);\n }\n\n getLatestLeaf(): UIMessage | null {\n this._ensureReady();\n return this.storage.getLatestLeaf();\n }\n\n getBranches(messageId: string): UIMessage[] {\n this._ensureReady();\n return this.storage.getBranches(messageId);\n }\n\n getPathLength(leafId?: string | null): number {\n this._ensureReady();\n return this.storage.getPathLength(leafId);\n }\n\n // ── Broadcast ──────────────────────────────────────────────────\n\n private _broadcast(type: MessageType, data: Record<string, unknown>): void {\n if (!this._broadcaster) return;\n this._broadcaster.broadcast(JSON.stringify({ type, ...data }));\n }\n\n private _emitStatus(\n phase: \"idle\" | \"compacting\",\n extra?: Record<string, unknown>\n ): number {\n const tokenEstimate = estimateMessageTokens(this.getHistory());\n this._broadcast(MessageType.CF_AGENT_SESSION, {\n phase,\n tokenEstimate,\n tokenThreshold: this._tokenThreshold ?? null,\n ...extra\n });\n return tokenEstimate;\n }\n\n private _emitError(error: string): void {\n this._broadcast(MessageType.CF_AGENT_SESSION_ERROR, { error });\n }\n\n // ── Write ─────────────────────────────────────────────────────\n\n async appendMessage(\n message: UIMessage,\n parentId?: string | null\n ): Promise<void> {\n this._ensureReady();\n this.storage.appendMessage(message, parentId);\n\n const tokenEstimate = this._emitStatus(\"idle\");\n\n if (\n this._tokenThreshold != null &&\n this._compactionFn &&\n tokenEstimate > this._tokenThreshold\n ) {\n try {\n await this.compact();\n } catch {\n // Auto-compact failure is non-fatal — message is already appended\n }\n }\n }\n\n updateMessage(message: UIMessage): void {\n this._ensureReady();\n this.storage.updateMessage(message);\n this._emitStatus(\"idle\");\n }\n\n deleteMessages(messageIds: string[]): void {\n this._ensureReady();\n this.storage.deleteMessages(messageIds);\n this._emitStatus(\"idle\");\n }\n\n clearMessages(): void {\n this._ensureReady();\n this.storage.clearMessages();\n this._emitStatus(\"idle\");\n }\n\n // ── Compaction ────────────────────────────────────────────────\n\n addCompaction(\n summary: string,\n fromMessageId: string,\n toMessageId: string\n ): StoredCompaction {\n this._ensureReady();\n return this.storage.addCompaction(summary, fromMessageId, toMessageId);\n }\n\n getCompactions(): StoredCompaction[] {\n this._ensureReady();\n return this.storage.getCompactions();\n }\n\n /**\n * Run the registered compaction function and store the result as an overlay.\n * Requires `onCompaction()` to be called first.\n */\n async compact(): Promise<CompactResult | null> {\n this._ensureReady();\n if (!this._compactionFn) {\n throw new Error(\n \"No compaction function registered. Call onCompaction() first.\"\n );\n }\n\n const tokensBefore = this._emitStatus(\"compacting\");\n\n let result: CompactResult | null;\n try {\n result = await this._compactionFn(this.getHistory());\n } catch (err) {\n this._emitError(err instanceof Error ? err.message : String(err));\n return null;\n }\n\n if (!result) {\n this._emitStatus(\"idle\");\n return null;\n }\n\n // Validate toMessageId exists in the history\n const historyIds = new Set(this.getHistory().map((m) => m.id));\n if (!historyIds.has(result.toMessageId)) {\n this._emitStatus(\"idle\");\n return null;\n }\n\n // Iterative compaction — extend from earliest existing compaction's start\n const existing = this.getCompactions();\n const fromId =\n existing.length > 0 ? existing[0].fromMessageId : result.fromMessageId;\n\n this.addCompaction(result.summary, fromId, result.toMessageId);\n await this.refreshSystemPrompt();\n\n this._emitStatus(\"idle\", {\n compacted: { tokensBefore }\n });\n\n return { ...result, fromMessageId: fromId };\n }\n\n // ── Context Blocks ────────────────────────────────────────────\n\n getContextBlock(label: string): ContextBlock | null {\n this._ensureReady();\n return this.context.getBlock(label);\n }\n\n getContextBlocks(): ContextBlock[] {\n this._ensureReady();\n return this.context.getBlocks();\n }\n\n async replaceContextBlock(\n label: string,\n content: string\n ): Promise<ContextBlock> {\n this._ensureReady();\n return this.context.setBlock(label, content);\n }\n\n async appendContextBlock(\n label: string,\n content: string\n ): Promise<ContextBlock> {\n this._ensureReady();\n return this.context.appendToBlock(label, content);\n }\n\n // ── System Prompt ─────────────────────────────────────────────\n\n async freezeSystemPrompt(): Promise<string> {\n this._ensureReady();\n return this.context.freezeSystemPrompt();\n }\n\n async refreshSystemPrompt(): Promise<string> {\n this._ensureReady();\n return this.context.refreshSystemPrompt();\n }\n\n // ── Search ────────────────────────────────────────────────────\n\n search(\n query: string,\n options?: { limit?: number }\n ): Array<{\n id: string;\n role: string;\n content: string;\n createdAt?: string;\n }> {\n this._ensureReady();\n if (!this.storage.searchMessages) {\n throw new Error(\"Session provider does not support search\");\n }\n return this.storage.searchMessages(query, options?.limit ?? 20);\n }\n\n // ── Tools ─────────────────────────────────────────────────────\n\n /** Returns update_context tool for writing to context blocks. */\n async tools(): Promise<ToolSet> {\n this._ensureReady();\n return this.context.tools();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/experimental/memory/session/skills.ts","../../../../src/experimental/memory/session/context.ts","../../../../src/experimental/memory/session/providers/agent.ts","../../../../src/experimental/memory/session/providers/agent-context.ts","../../../../src/experimental/memory/session/session.ts","../../../../src/experimental/memory/session/manager.ts"],"sourcesContent":["/**\n * Skill Provider — on-demand keyed document collections.\n *\n * Extends ContextProvider with `load()` for on-demand content fetching\n * and a keyed `set()` for writing individual entries.\n *\n * Duck-typed: if a provider has a `load` method, it's a SkillProvider.\n */\n\nimport type { ContextProvider } from \"./context\";\n\n/**\n * Storage interface for skill collections.\n *\n * - `get()` returns metadata listing (rendered into system prompt)\n * - `load(key)` fetches full content (via load_context tool)\n * - `set(key, content, description?)` writes an entry (via set_context tool)\n */\nexport interface SkillProvider extends ContextProvider {\n load(key: string): Promise<string | null>;\n set?(key: string, content: string, description?: string): Promise<void>;\n}\n\n/**\n * Check if a provider is a SkillProvider (has a `load` method).\n */\nexport function isSkillProvider(provider: unknown): provider is SkillProvider {\n return (\n typeof provider === \"object\" &&\n provider !== null &&\n \"load\" in provider &&\n typeof (provider as SkillProvider).load === \"function\"\n );\n}\n\n// ── R2 Skill Provider ──────────────────────────────────────────────\n\n/**\n * SkillProvider backed by an R2 bucket.\n *\n * - `get()` returns a metadata listing of all skills (key + description)\n * - `load(key)` fetches a skill's full content\n * - `set(key, content, description?)` writes a skill\n *\n * Descriptions are pulled from R2 custom metadata (`description` key).\n * If a prefix is provided, it is prepended on storage operations and\n * stripped from keys in metadata.\n *\n * @example\n * ```ts\n * const skills = new R2SkillProvider(env.SKILLS_BUCKET, { prefix: \"skills/\" });\n * ```\n */\nexport class R2SkillProvider implements SkillProvider {\n private bucket: R2Bucket;\n private prefix: string;\n\n constructor(bucket: R2Bucket, options?: { prefix?: string }) {\n this.bucket = bucket;\n this.prefix = options?.prefix ?? \"\";\n }\n\n async get(): Promise<string | null> {\n const entries: string[] = [];\n let cursor: string | undefined;\n let truncated = true;\n while (truncated) {\n const listed = await this.bucket.list({\n prefix: this.prefix,\n cursor,\n include: [\"customMetadata\"]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any);\n for (const obj of listed.objects) {\n const key = obj.key.slice(this.prefix.length);\n const desc = obj.customMetadata?.description;\n entries.push(`- ${key}${desc ? `: ${desc}` : \"\"}`);\n }\n truncated = listed.truncated;\n cursor = listed.truncated ? listed.cursor : undefined;\n }\n return entries.length > 0 ? entries.join(\"\\n\") : null;\n }\n\n async load(key: string): Promise<string | null> {\n const obj = await this.bucket.get(this.prefix + key);\n if (!obj) return null;\n return obj.text();\n }\n\n async set(key: string, content: string, description?: string): Promise<void> {\n await this.bucket.put(this.prefix + key, content, {\n customMetadata: description ? { description } : undefined\n });\n }\n}\n","/**\n * Context Block Management\n *\n * Persistent key-value blocks (MEMORY, USER, SOUL, etc.) that are:\n * - Loaded from their providers at init\n * - Frozen into a snapshot when toSystemPrompt() is called\n * - Updated via setBlock() which writes to the provider immediately\n * but does NOT update the frozen snapshot (preserves LLM prefix cache)\n * - Re-snapshotted on next toSystemPrompt() call\n *\n * Provider type determines behavior:\n * - ContextProvider (get only) → readonly block in system prompt\n * - WritableContextProvider (get+set) → writable via set_context tool\n * - SkillProvider (get+load+set?) → metadata in prompt, load_context tool\n */\n\nimport { jsonSchema, type ToolSet } from \"ai\";\nimport { estimateStringTokens } from \"../utils/tokens\";\nimport { isSkillProvider, type SkillProvider } from \"./skills\";\n\n/**\n * Base storage interface for a context block.\n * A provider with only `get()` is readonly.\n */\nexport interface ContextProvider {\n get(): Promise<string | null>;\n}\n\n/**\n * Writable context provider — extends ContextProvider with `set()`.\n * Blocks backed by this provider are writable via the `set_context` tool.\n */\nexport interface WritableContextProvider extends ContextProvider {\n set(content: string): Promise<void>;\n}\n\n/**\n * Check if a provider is writable (has a `set` method).\n */\nexport function isWritableProvider(\n provider: unknown\n): provider is WritableContextProvider {\n return (\n typeof provider === \"object\" &&\n provider !== null &&\n \"set\" in provider &&\n typeof (provider as WritableContextProvider).set === \"function\"\n );\n}\n\n/**\n * Configuration for a context block.\n */\nexport interface ContextConfig {\n /** Block label — used as key and in tool descriptions */\n label: string;\n /** Human-readable description (shown to AI in tool) */\n description?: string;\n /** Maximum tokens allowed. Enforced on set. */\n maxTokens?: number;\n /** Storage provider. Determines block behavior:\n * - ContextProvider (get only) → readonly\n * - WritableContextProvider (get+set) → writable via set_context\n * - SkillProvider (get+load+set?) → on-demand via load_context\n * If omitted, auto-wired to writable SQLite when using builder. */\n provider?: ContextProvider | WritableContextProvider | SkillProvider;\n}\n\n/**\n * A loaded context block with computed token count.\n */\nexport interface ContextBlock {\n label: string;\n description?: string;\n content: string;\n tokens: number;\n maxTokens?: number;\n /** True if provider is writable (has set) */\n writable: boolean;\n /** True if backed by a SkillProvider */\n isSkill: boolean;\n}\n\n/**\n * Manages context blocks with frozen snapshot support.\n */\nexport class ContextBlocks {\n private configs: ContextConfig[];\n private blocks = new Map<string, ContextBlock>();\n private snapshot: string | null = null;\n private loaded = false;\n private promptStore: WritableContextProvider | null;\n\n constructor(configs: ContextConfig[], promptStore?: WritableContextProvider) {\n this.configs = configs;\n this.promptStore = promptStore ?? null;\n }\n\n isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Load all blocks from their providers.\n * Called once at session init.\n */\n async load(): Promise<void> {\n for (const config of this.configs) {\n const content = config.provider\n ? ((await config.provider.get()) ?? \"\")\n : \"\";\n\n const skill = config.provider ? isSkillProvider(config.provider) : false;\n const writable = config.provider\n ? isWritableProvider(config.provider) ||\n (skill && !!(config.provider as SkillProvider).set)\n : false;\n\n this.blocks.set(config.label, {\n label: config.label,\n description: config.description,\n content,\n tokens: estimateStringTokens(content),\n maxTokens: config.maxTokens,\n writable,\n isSkill: skill\n });\n }\n this.loaded = true;\n }\n\n /**\n * Get a block by label.\n */\n getBlock(label: string): ContextBlock | null {\n return this.blocks.get(label) ?? null;\n }\n\n /**\n * Get all blocks.\n */\n getBlocks(): ContextBlock[] {\n return Array.from(this.blocks.values());\n }\n\n /**\n * Set block content. Writes to provider immediately.\n * Does NOT update the frozen snapshot.\n */\n async setBlock(label: string, content: string): Promise<ContextBlock> {\n if (!this.loaded) await this.load();\n const config = this.configs.find((c) => c.label === label);\n const existing = this.blocks.get(label);\n\n if (!existing?.writable) {\n throw new Error(`Block \"${label}\" is readonly`);\n }\n\n if (existing.isSkill) {\n throw new Error(\n `Block \"${label}\" is a skill provider. Use setSkill() instead.`\n );\n }\n\n const tokens = estimateStringTokens(content);\n const maxTokens = config?.maxTokens ?? existing?.maxTokens;\n\n if (maxTokens !== undefined && tokens > maxTokens) {\n throw new Error(\n `Block \"${label}\" exceeds maxTokens: ${tokens} > ${maxTokens}`\n );\n }\n\n const block: ContextBlock = {\n label,\n description: config?.description ?? existing?.description,\n content,\n tokens,\n maxTokens,\n writable: true,\n isSkill: false\n };\n\n this.blocks.set(label, block);\n\n // Write to provider immediately (durable)\n if (config?.provider && isWritableProvider(config.provider)) {\n await config.provider.set(content);\n }\n\n return block;\n }\n\n /**\n * Set a skill entry within a skill block.\n */\n async setSkill(\n label: string,\n key: string,\n content: string,\n description?: string\n ): Promise<void> {\n if (!this.loaded) await this.load();\n const config = this.configs.find((c) => c.label === label);\n const existing = this.blocks.get(label);\n\n if (!existing?.isSkill) {\n throw new Error(`Block \"${label}\" is not a skill provider`);\n }\n\n const provider = config?.provider;\n if (!provider || !isSkillProvider(provider) || !provider.set) {\n throw new Error(`Block \"${label}\" does not support writes`);\n }\n\n await provider.set(key, content, description);\n\n // Refresh metadata\n const metadata = await provider.get();\n if (metadata) {\n existing.content = metadata;\n existing.tokens = estimateStringTokens(metadata);\n }\n }\n\n /**\n * Load a skill's full content from a skill block.\n */\n async loadSkill(label: string, key: string): Promise<string | null> {\n if (!this.loaded) await this.load();\n const config = this.configs.find((c) => c.label === label);\n\n if (!config?.provider || !isSkillProvider(config.provider)) {\n throw new Error(`Block \"${label}\" is not a skill provider`);\n }\n\n return config.provider.load(key);\n }\n\n /**\n * Append content to a block.\n */\n async appendToBlock(label: string, content: string): Promise<ContextBlock> {\n if (!this.loaded) await this.load();\n const existing = this.blocks.get(label);\n if (!existing) {\n throw new Error(`Block \"${label}\" not found`);\n }\n return this.setBlock(label, existing.content + content);\n }\n\n /**\n * Get the system prompt string with context blocks.\n *\n * Returns a frozen snapshot: first call renders and caches,\n * subsequent calls return the same string (preserves LLM prefix cache).\n * Call refreshSnapshot() to re-render after block changes take effect.\n */\n toSystemPrompt(): string {\n if (!this.loaded) {\n throw new Error(\"Context blocks not loaded. Call load() first.\");\n }\n\n if (this.snapshot !== null) {\n return this.snapshot;\n }\n\n return this.captureSnapshot();\n }\n\n /**\n * Force re-render the snapshot from current block state.\n */\n refreshSnapshot(): string {\n return this.captureSnapshot();\n }\n\n private captureSnapshot(): string {\n const parts: string[] = [];\n const sep = \"═\".repeat(46);\n\n for (const block of this.blocks.values()) {\n if (!block.content) continue;\n\n let header = block.label.toUpperCase();\n if (block.description && block.isSkill) {\n header += ` (${block.description} — use load_context to load)`;\n } else if (block.description) {\n header += ` (${block.description})`;\n } else if (block.isSkill) {\n header += \" (use load_context to load)\";\n }\n if (block.maxTokens) {\n const pct = Math.round((block.tokens / block.maxTokens) * 100);\n header += ` [${pct}% — ${block.tokens}/${block.maxTokens} tokens]`;\n }\n if (!block.writable) header += \" [readonly]\";\n\n parts.push(`${sep}\\n${header}\\n${sep}\\n${block.content}`);\n }\n\n this.snapshot = parts.join(\"\\n\\n\");\n return this.snapshot;\n }\n\n /**\n * Get writable blocks (for tool description).\n */\n getWritableBlocks(): ContextBlock[] {\n return Array.from(this.blocks.values()).filter((b) => b.writable);\n }\n\n /**\n * Check if any skill providers are registered.\n */\n hasSkillBlocks(): boolean {\n return Array.from(this.blocks.values()).some((b) => b.isSkill);\n }\n\n /**\n * Get skill block labels.\n */\n getSkillLabels(): string[] {\n return Array.from(this.blocks.values())\n .filter((b) => b.isSkill)\n .map((b) => b.label);\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n /**\n * Frozen system prompt. On first call:\n * 1. Checks store for a persisted prompt (survives DO eviction)\n * 2. If none, loads blocks from providers, renders, and persists\n */\n async freezeSystemPrompt(): Promise<string> {\n if (this.promptStore) {\n const stored = await this.promptStore.get();\n if (stored !== null) return stored;\n }\n\n if (!this.loaded) await this.load();\n const prompt = this.toSystemPrompt();\n\n if (this.promptStore) {\n await this.promptStore.set(prompt);\n }\n\n return prompt;\n }\n\n /**\n * Re-render the system prompt from current block state and persist.\n */\n async refreshSystemPrompt(): Promise<string> {\n if (!this.loaded) await this.load();\n const prompt = this.refreshSnapshot();\n\n if (this.promptStore) {\n await this.promptStore.set(prompt);\n }\n\n return prompt;\n }\n\n /**\n * AI tools for context blocks.\n *\n * Auto-wired based on provider capabilities:\n * - `set_context` — when any block is writable\n * - `load_context` — when any block is a skill provider\n */\n async tools(): Promise<ToolSet> {\n if (!this.loaded) await this.load();\n\n const writable = this.getWritableBlocks();\n const hasSkills = this.hasSkillBlocks();\n const toolSet: ToolSet = {};\n\n // ── set_context ──────────────────────────────────────────────\n\n if (writable.length > 0) {\n const regularBlocks = writable.filter((b) => !b.isSkill);\n const skillBlocks = writable.filter((b) => b.isSkill);\n\n const blockDescriptions: string[] = [];\n for (const b of regularBlocks) {\n blockDescriptions.push(\n `- \"${b.label}\": ${b.description ?? \"no description\"}`\n );\n }\n for (const b of skillBlocks) {\n blockDescriptions.push(\n `- \"${b.label}\": skill collection (requires key and optional description)`\n );\n }\n\n const properties: Record<string, unknown> = {\n label: {\n type: \"string\" as const,\n enum: writable.map((b) => b.label),\n description: \"Block label to write to\"\n },\n content: {\n type: \"string\" as const,\n description: \"Content to write\"\n },\n action: {\n type: \"string\" as const,\n enum: [\"replace\", \"append\"],\n description: \"replace (default) or append\"\n }\n };\n\n const required = [\"label\", \"content\"];\n\n if (skillBlocks.length > 0) {\n properties.key = {\n type: \"string\" as const,\n description:\n \"Skill key (required for skill blocks: \" +\n skillBlocks.map((b) => `\"${b.label}\"`).join(\", \") +\n \")\"\n };\n properties.description = {\n type: \"string\" as const,\n description: \"Short description for the skill entry\"\n };\n }\n\n toolSet.set_context = {\n description: `Write to a context block. Available blocks:\\n${blockDescriptions.join(\"\\n\")}\\n\\nWrites are durable and persist across sessions.`,\n inputSchema: jsonSchema({\n type: \"object\" as const,\n properties: properties as Record<string, object>,\n required\n }),\n execute: async ({\n label,\n content,\n key,\n description,\n action\n }: {\n label: string;\n content: string;\n key?: string;\n description?: string;\n action?: string;\n }) => {\n try {\n const block = this.blocks.get(label);\n if (!block) return `Error: block \"${label}\" not found`;\n\n if (block.isSkill) {\n if (!key)\n return `Error: key is required for skill block \"${label}\"`;\n await this.setSkill(label, key, content, description);\n return `Written skill \"${key}\" to ${label}.`;\n }\n\n const updated =\n action === \"append\"\n ? await this.appendToBlock(label, content)\n : await this.setBlock(label, content);\n const usage = updated.maxTokens\n ? `${Math.round((updated.tokens / updated.maxTokens) * 100)}% (${updated.tokens}/${updated.maxTokens} tokens)`\n : `${updated.tokens} tokens`;\n return `Written to ${label}. Usage: ${usage}`;\n } catch (err) {\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n }\n }\n };\n }\n\n // ── load_context ─────────────────────────────────────────────\n\n if (hasSkills) {\n const skillLabels = this.getSkillLabels();\n\n toolSet.load_context = {\n description:\n \"Load a document from a skill block by key. \" +\n \"Available skill blocks: \" +\n skillLabels.map((l) => `\"${l}\"`).join(\", \") +\n \". Check the system prompt for available keys.\",\n inputSchema: jsonSchema({\n type: \"object\" as const,\n properties: {\n label: {\n type: \"string\" as const,\n enum: skillLabels,\n description: \"Skill block label\"\n },\n key: {\n type: \"string\" as const,\n description: \"Skill key to load\"\n }\n },\n required: [\"label\", \"key\"]\n }),\n execute: async ({ label, key }: { label: string; key: string }) => {\n try {\n const content = await this.loadSkill(label, key);\n return content ?? `Not found: ${key}`;\n } catch (err) {\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n }\n }\n };\n }\n\n return toolSet;\n }\n}\n","/**\n * Agent Session Provider\n *\n * SQLite-backed provider with tree-structured messages (branching),\n * compaction overlays, and FTS5 search.\n */\n\nimport type { UIMessage } from \"ai\";\nimport type {\n SessionProvider,\n SearchResult,\n StoredCompaction\n} from \"../provider\";\nimport { COMPACTION_PREFIX } from \"../../utils/compaction-helpers\";\n\nexport interface SqlProvider {\n sql<T = Record<string, string | number | boolean | null>>(\n strings: TemplateStringsArray,\n ...values: (string | number | boolean | null)[]\n ): T[];\n}\n\nexport class AgentSessionProvider implements SessionProvider {\n private agent: SqlProvider;\n private initialized = false;\n private sessionId: string;\n\n /**\n * @param agent - Agent or any object with a `sql` tagged template method\n * @param sessionId - Optional session ID to isolate multiple sessions in the same DO.\n * Messages are filtered by session_id within shared tables.\n */\n constructor(agent: SqlProvider, sessionId?: string) {\n this.agent = agent;\n this.sessionId = sessionId ?? \"\";\n }\n\n private ensureTable(): void {\n if (this.initialized) return;\n\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS assistant_messages (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL DEFAULT '',\n parent_id TEXT,\n role TEXT NOT NULL,\n content TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n\n this.agent.sql`\n CREATE INDEX IF NOT EXISTS idx_assistant_msg_parent\n ON assistant_messages(parent_id)\n `;\n\n this.agent.sql`\n CREATE INDEX IF NOT EXISTS idx_assistant_msg_session\n ON assistant_messages(session_id)\n `;\n\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS assistant_compactions (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL DEFAULT '',\n summary TEXT NOT NULL,\n from_message_id TEXT NOT NULL,\n to_message_id TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n\n this.agent.sql`\n CREATE VIRTUAL TABLE IF NOT EXISTS assistant_fts\n USING fts5(id UNINDEXED, session_id UNINDEXED, role UNINDEXED, content, tokenize='porter unicode61')\n `;\n\n // Reserved for SessionManager metadata (PR #1167) and Think integration (PR #1169)\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS assistant_config (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `;\n\n this.initialized = true;\n }\n\n // ── Read ───────────────────────────────────────────────────────\n\n getMessage(id: string): UIMessage | null {\n this.ensureTable();\n const rows = this.agent.sql<{ content: string }>`\n SELECT content FROM assistant_messages WHERE id = ${id} AND session_id = ${this.sessionId}\n `;\n return rows.length > 0 ? this.parse(rows[0].content) : null;\n }\n\n getHistory(leafId?: string | null): UIMessage[] {\n this.ensureTable();\n\n const leaf = leafId\n ? this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${leafId} AND session_id = ${this.sessionId}\n `[0]\n : this.latestLeafRow();\n\n if (!leaf) return [];\n\n const path = this.agent.sql<{ content: string }>`\n WITH RECURSIVE path AS (\n SELECT *, 0 as depth FROM assistant_messages WHERE id = ${leaf.id}\n UNION ALL\n SELECT m.*, p.depth + 1 FROM assistant_messages m\n JOIN path p ON m.id = p.parent_id\n WHERE m.session_id = ${this.sessionId} AND p.depth < 10000\n )\n SELECT content FROM path ORDER BY depth DESC\n `;\n\n const messages = this.parseRows(path);\n const compactions = this.getCompactions();\n if (compactions.length === 0) return messages;\n return this.applyCompactions(messages, compactions);\n }\n\n getLatestLeaf(): UIMessage | null {\n this.ensureTable();\n const row = this.latestLeafRow();\n return row ? this.parse(row.content) : null;\n }\n\n getBranches(messageId: string): UIMessage[] {\n this.ensureTable();\n const rows = this.agent.sql<{ content: string }>`\n SELECT content FROM assistant_messages\n WHERE parent_id = ${messageId} AND session_id = ${this.sessionId} ORDER BY created_at ASC\n `;\n return this.parseRows(rows);\n }\n\n getPathLength(leafId?: string | null): number {\n this.ensureTable();\n const leaf = leafId\n ? this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${leafId} AND session_id = ${this.sessionId}\n `[0]\n : this.latestLeafRow();\n if (!leaf) return 0;\n\n const rows = this.agent.sql<{ count: number }>`\n WITH RECURSIVE path AS (\n SELECT id, parent_id, 0 as depth FROM assistant_messages WHERE id = ${leaf.id}\n UNION ALL\n SELECT m.id, m.parent_id, p.depth + 1 FROM assistant_messages m\n JOIN path p ON m.id = p.parent_id\n WHERE m.session_id = ${this.sessionId} AND p.depth < 10000\n )\n SELECT COUNT(*) as count FROM path\n `;\n return rows[0]?.count ?? 0;\n }\n\n // ── Write ──────────────────────────────────────────────────────\n\n appendMessage(message: UIMessage, parentId?: string | null): void {\n this.ensureTable();\n // Skip if message already exists (INSERT OR IGNORE idempotency)\n const existing = this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${message.id} AND session_id = ${this.sessionId}\n `;\n if (existing.length > 0) return;\n\n let parent = parentId ?? this.latestLeafRow()?.id ?? null;\n\n // Validate parentId belongs to this session\n if (parent) {\n const valid = this.agent.sql<{ id: string }>`\n SELECT id FROM assistant_messages WHERE id = ${parent} AND session_id = ${this.sessionId}\n `;\n if (valid.length === 0) parent = null;\n }\n\n const json = JSON.stringify(message);\n\n this.agent.sql`\n INSERT INTO assistant_messages (id, session_id, parent_id, role, content)\n VALUES (${message.id}, ${this.sessionId}, ${parent}, ${message.role}, ${json})\n `;\n this.indexFTS(message);\n }\n\n updateMessage(message: UIMessage): void {\n this.ensureTable();\n this.agent.sql`\n UPDATE assistant_messages SET content = ${JSON.stringify(message)}\n WHERE id = ${message.id} AND session_id = ${this.sessionId}\n `;\n this.indexFTS(message);\n }\n\n deleteMessages(messageIds: string[]): void {\n this.ensureTable();\n for (const id of messageIds) {\n this.agent\n .sql`DELETE FROM assistant_messages WHERE id = ${id} AND session_id = ${this.sessionId}`;\n this.deleteFTS(id);\n }\n }\n\n clearMessages(): void {\n this.ensureTable();\n this.agent\n .sql`DELETE FROM assistant_messages WHERE session_id = ${this.sessionId}`;\n this.agent\n .sql`DELETE FROM assistant_compactions WHERE session_id = ${this.sessionId}`;\n // FTS5 requires delete by rowid\n const ftsRows = this.agent.sql<{ rowid: number }>`\n SELECT rowid FROM assistant_fts WHERE session_id = ${this.sessionId}\n `;\n for (const row of ftsRows) {\n this.agent.sql`DELETE FROM assistant_fts WHERE rowid = ${row.rowid}`;\n }\n }\n\n // ── Compaction ─────────────────────────────────────────────────\n\n addCompaction(\n summary: string,\n fromMessageId: string,\n toMessageId: string\n ): StoredCompaction {\n this.ensureTable();\n const id = crypto.randomUUID();\n this.agent.sql`\n INSERT INTO assistant_compactions (id, session_id, summary, from_message_id, to_message_id)\n VALUES (${id}, ${this.sessionId}, ${summary}, ${fromMessageId}, ${toMessageId})\n `;\n return {\n id,\n summary,\n fromMessageId,\n toMessageId,\n createdAt: new Date().toISOString()\n };\n }\n\n getCompactions(): StoredCompaction[] {\n this.ensureTable();\n type Row = {\n id: string;\n summary: string;\n from_message_id: string;\n to_message_id: string;\n created_at: string;\n };\n return this.agent.sql<Row>`\n SELECT * FROM assistant_compactions WHERE session_id = ${this.sessionId} ORDER BY created_at ASC\n `.map((r) => ({\n id: r.id,\n summary: r.summary,\n fromMessageId: r.from_message_id,\n toMessageId: r.to_message_id,\n createdAt: r.created_at\n }));\n }\n\n // ── Search ─────────────────────────────────────────────────────\n\n searchMessages(query: string, limit = 20): SearchResult[] {\n this.ensureTable();\n // Sanitize query: wrap in double quotes to treat as literal phrase,\n // escaping any existing double quotes to prevent FTS5 syntax injection\n const sanitized = `\"${query.replace(/\"/g, '\"\"')}\"`;\n try {\n return this.agent.sql<{ id: string; role: string; content: string }>`\n SELECT f.id, f.role, f.content FROM assistant_fts f\n INNER JOIN assistant_messages m ON m.id = f.id AND m.session_id = f.session_id\n WHERE assistant_fts MATCH ${sanitized} AND f.session_id = ${this.sessionId}\n ORDER BY rank LIMIT ${limit}\n `.map((r) => ({\n id: r.id,\n role: r.role,\n content: r.content\n }));\n } catch {\n // Malformed FTS query — return empty results\n return [];\n }\n }\n\n // ── Internal ───────────────────────────────────────────────────\n\n private latestLeafRow(): { id: string; content: string } | null {\n const rows = this.agent.sql<{ id: string; content: string }>`\n SELECT m.id, m.content FROM assistant_messages m\n LEFT JOIN assistant_messages c ON c.parent_id = m.id AND c.session_id = ${this.sessionId}\n WHERE c.id IS NULL AND m.session_id = ${this.sessionId}\n ORDER BY m.created_at DESC, m.rowid DESC LIMIT 1\n `;\n return rows[0] ?? null;\n }\n\n private indexFTS(message: UIMessage): void {\n const text = message.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\" \");\n // Always delete old entry first — handles text→no-text transitions\n this.deleteFTS(message.id);\n if (text) {\n this.agent.sql`\n INSERT INTO assistant_fts (id, session_id, role, content)\n VALUES (${message.id}, ${this.sessionId}, ${message.role}, ${text})\n `;\n }\n }\n\n private deleteFTS(id: string): void {\n const rows = this.agent.sql<{ rowid: number }>`\n SELECT rowid FROM assistant_fts WHERE id = ${id} AND session_id = ${this.sessionId}\n `;\n for (const row of rows) {\n this.agent.sql`DELETE FROM assistant_fts WHERE rowid = ${row.rowid}`;\n }\n }\n\n private applyCompactions(\n messages: UIMessage[],\n compactions: StoredCompaction[]\n ): UIMessage[] {\n const ids = messages.map((m) => m.id);\n const result: UIMessage[] = [];\n let i = 0;\n while (i < messages.length) {\n // Find all compactions starting at this message, pick the latest\n // (widest range) so newer compactions supersede older ones\n const matching = compactions.filter((c) => c.fromMessageId === ids[i]);\n const comp =\n matching.length > 1 ? matching[matching.length - 1] : matching[0];\n if (comp) {\n const endIdx = ids.indexOf(comp.toMessageId);\n if (endIdx >= i) {\n result.push({\n id: `${COMPACTION_PREFIX}${comp.id}`,\n role: \"assistant\",\n parts: [\n {\n type: \"text\",\n text: comp.summary\n }\n ],\n createdAt: new Date()\n } as UIMessage);\n i = endIdx + 1;\n continue;\n }\n }\n result.push(messages[i]);\n i++;\n }\n return result;\n }\n\n private parse(json: string): UIMessage | null {\n try {\n const msg = JSON.parse(json);\n if (\n typeof msg?.id === \"string\" &&\n typeof msg?.role === \"string\" &&\n Array.isArray(msg?.parts)\n ) {\n return msg;\n }\n } catch {\n /* skip */\n }\n return null;\n }\n\n private parseRows(rows: { content: string }[]): UIMessage[] {\n const result: UIMessage[] = [];\n for (const row of rows) {\n const msg = this.parse(row.content);\n if (msg) result.push(msg);\n }\n return result;\n }\n}\n","/**\n * SQLite Context Block Provider\n *\n * Default durable storage for context blocks using DO SQLite.\n * Each block is a row in cf_agents_context_blocks.\n */\n\nimport type { ContextProvider } from \"../context\";\nimport type { SqlProvider } from \"./agent\";\n\nexport class AgentContextProvider implements ContextProvider {\n private agent: SqlProvider;\n private label: string;\n private initialized = false;\n\n constructor(agent: SqlProvider, label: string) {\n this.agent = agent;\n this.label = label;\n }\n\n private ensureTable(): void {\n if (this.initialized) return;\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS cf_agents_context_blocks (\n label TEXT PRIMARY KEY,\n content TEXT NOT NULL,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n this.initialized = true;\n }\n\n async get(): Promise<string | null> {\n this.ensureTable();\n const rows = this.agent.sql<{ content: string }>`\n SELECT content FROM cf_agents_context_blocks WHERE label = ${this.label}\n `;\n return rows[0]?.content ?? null;\n }\n\n async set(content: string): Promise<void> {\n this.ensureTable();\n this.agent.sql`\n INSERT INTO cf_agents_context_blocks (label, content)\n VALUES (${this.label}, ${content})\n ON CONFLICT(label) DO UPDATE SET content = ${content}, updated_at = CURRENT_TIMESTAMP\n `;\n }\n}\n","/**\n * Session — conversation history, context blocks, compaction, search, and tools.\n */\n\nimport type { ToolSet } from \"ai\";\nimport type { UIMessage } from \"ai\";\nimport type { SessionProvider, StoredCompaction } from \"./provider\";\nimport type { SessionOptions } from \"./types\";\nimport {\n ContextBlocks,\n type ContextBlock,\n type ContextConfig,\n type WritableContextProvider\n} from \"./context\";\nimport { AgentSessionProvider, type SqlProvider } from \"./providers/agent\";\nimport { AgentContextProvider } from \"./providers/agent-context\";\nimport type { CompactResult } from \"../utils/compaction-helpers\";\nimport { estimateMessageTokens } from \"../utils/tokens\";\nimport { MessageType } from \"../../../types\";\n\nexport type SessionContextOptions = Omit<ContextConfig, \"label\">;\n\n// Raw builder entry — provider resolved at init time so chain order doesn't matter\ninterface PendingContext {\n label: string;\n options: SessionContextOptions;\n}\n\n/** Agent-like object that can broadcast to connected clients */\ninterface Broadcaster {\n broadcast(message: string | ArrayBufferLike): void;\n}\n\nfunction isBroadcaster(obj: unknown): obj is Broadcaster {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"broadcast\" in obj &&\n typeof (obj as Broadcaster).broadcast === \"function\"\n );\n}\n\nexport class Session {\n private storage!: SessionProvider;\n private context!: ContextBlocks;\n\n // Builder state — only used with Session.create()\n private _agent?: SqlProvider;\n private _broadcaster?: Broadcaster;\n private _sessionId?: string;\n private _pending?: PendingContext[];\n private _cachedPrompt?: WritableContextProvider | true;\n private _compactionFn?:\n | ((messages: UIMessage[]) => Promise<CompactResult | null>)\n | null;\n private _tokenThreshold?: number;\n private _ready = false;\n\n constructor(storage: SessionProvider, options?: SessionOptions) {\n this.storage = storage;\n this.context = new ContextBlocks(\n options?.context ?? [],\n options?.promptStore\n );\n this._ready = true;\n }\n\n /**\n * Chainable session creation with auto-wired SQLite providers.\n * Chain methods in any order — providers are resolved lazily on first use.\n *\n * @example\n * ```ts\n * const session = Session.create(this)\n * .withContext(\"soul\", { provider: { get: async () => \"You are helpful.\" } })\n * .withContext(\"memory\", { description: \"Learned facts\", maxTokens: 1100 })\n * .withCachedPrompt();\n *\n * // Skills from R2 (on-demand loading via load_context tool)\n * const session = Session.create(this)\n * .withContext(\"skills\", {\n * provider: new R2SkillProvider(env.SKILLS_BUCKET, { prefix: \"skills/\" })\n * })\n * .withCachedPrompt();\n * ```\n */\n static create(agent: SqlProvider): Session {\n const session: Session = Object.create(Session.prototype);\n session._agent = agent;\n if (isBroadcaster(agent)) {\n session._broadcaster = agent;\n }\n session._pending = [];\n session._ready = false;\n return session;\n }\n\n // ── Builder methods ─────────────────────────────────────────────\n\n forSession(sessionId: string): this {\n this._sessionId = sessionId;\n return this;\n }\n\n withContext(label: string, options?: SessionContextOptions): this {\n this._pending!.push({ label, options: options ?? {} });\n return this;\n }\n\n withCachedPrompt(provider?: WritableContextProvider): this {\n this._cachedPrompt = provider ?? true;\n return this;\n }\n\n /**\n * Register a compaction function. Called by `compact()` to compress\n * message history into a summary overlay.\n */\n onCompaction(\n fn: (messages: UIMessage[]) => Promise<CompactResult | null>\n ): this {\n this._compactionFn = fn;\n return this;\n }\n\n /**\n * Auto-compact when estimated token count exceeds the threshold.\n * Checked after each `appendMessage`. Requires `onCompaction()`.\n */\n compactAfter(tokenThreshold: number): this {\n this._tokenThreshold = tokenThreshold;\n return this;\n }\n\n // ── Lazy init ───────────────────────────────────────────────────\n\n private _ensureReady(): void {\n if (this._ready) return;\n\n // Resolve context configs — sessionId is final by now\n const configs: ContextConfig[] = (this._pending ?? []).map(\n ({ label, options: opts }) => {\n let provider = opts.provider;\n if (!provider) {\n // No provider → auto-wire to writable SQLite\n const key = this._sessionId ? `${label}_${this._sessionId}` : label;\n provider = new AgentContextProvider(this._agent!, key);\n }\n return {\n label,\n description: opts.description,\n maxTokens: opts.maxTokens,\n provider\n };\n }\n );\n\n // Resolve prompt store\n let promptStore: WritableContextProvider | undefined;\n if (this._cachedPrompt === true) {\n const key = this._sessionId\n ? `_system_prompt_${this._sessionId}`\n : \"_system_prompt\";\n promptStore = new AgentContextProvider(this._agent!, key);\n } else if (this._cachedPrompt) {\n promptStore = this._cachedPrompt;\n }\n\n this.storage = new AgentSessionProvider(this._agent!, this._sessionId);\n this.context = new ContextBlocks(configs, promptStore);\n this._ready = true;\n }\n\n // ── History (tree-structured) ─────────────────────────────────\n\n getHistory(leafId?: string | null): UIMessage[] {\n this._ensureReady();\n return this.storage.getHistory(leafId);\n }\n\n getMessage(id: string): UIMessage | null {\n this._ensureReady();\n return this.storage.getMessage(id);\n }\n\n getLatestLeaf(): UIMessage | null {\n this._ensureReady();\n return this.storage.getLatestLeaf();\n }\n\n getBranches(messageId: string): UIMessage[] {\n this._ensureReady();\n return this.storage.getBranches(messageId);\n }\n\n getPathLength(leafId?: string | null): number {\n this._ensureReady();\n return this.storage.getPathLength(leafId);\n }\n\n // ── Broadcast ──────────────────────────────────────────────────\n\n private _broadcast(type: MessageType, data: Record<string, unknown>): void {\n if (!this._broadcaster) return;\n this._broadcaster.broadcast(JSON.stringify({ type, ...data }));\n }\n\n private _emitStatus(\n phase: \"idle\" | \"compacting\",\n extra?: Record<string, unknown>\n ): number {\n const tokenEstimate = estimateMessageTokens(this.getHistory());\n this._broadcast(MessageType.CF_AGENT_SESSION, {\n phase,\n tokenEstimate,\n tokenThreshold: this._tokenThreshold ?? null,\n ...extra\n });\n return tokenEstimate;\n }\n\n private _emitError(error: string): void {\n this._broadcast(MessageType.CF_AGENT_SESSION_ERROR, { error });\n }\n\n // ── Write ─────────────────────────────────────────────────────\n\n async appendMessage(\n message: UIMessage,\n parentId?: string | null\n ): Promise<void> {\n this._ensureReady();\n this.storage.appendMessage(message, parentId);\n\n const tokenEstimate = this._emitStatus(\"idle\");\n\n if (\n this._tokenThreshold != null &&\n this._compactionFn &&\n tokenEstimate > this._tokenThreshold\n ) {\n try {\n await this.compact();\n } catch {\n // Auto-compact failure is non-fatal — message is already appended\n }\n }\n }\n\n updateMessage(message: UIMessage): void {\n this._ensureReady();\n this.storage.updateMessage(message);\n this._emitStatus(\"idle\");\n }\n\n deleteMessages(messageIds: string[]): void {\n this._ensureReady();\n this.storage.deleteMessages(messageIds);\n this._emitStatus(\"idle\");\n }\n\n clearMessages(): void {\n this._ensureReady();\n this.storage.clearMessages();\n this._emitStatus(\"idle\");\n }\n\n // ── Compaction ────────────────────────────────────────────────\n\n addCompaction(\n summary: string,\n fromMessageId: string,\n toMessageId: string\n ): StoredCompaction {\n this._ensureReady();\n return this.storage.addCompaction(summary, fromMessageId, toMessageId);\n }\n\n getCompactions(): StoredCompaction[] {\n this._ensureReady();\n return this.storage.getCompactions();\n }\n\n /**\n * Run the registered compaction function and store the result as an overlay.\n * Requires `onCompaction()` to be called first.\n */\n async compact(): Promise<CompactResult | null> {\n this._ensureReady();\n if (!this._compactionFn) {\n throw new Error(\n \"No compaction function registered. Call onCompaction() first.\"\n );\n }\n\n const tokensBefore = this._emitStatus(\"compacting\");\n\n let result: CompactResult | null;\n try {\n result = await this._compactionFn(this.getHistory());\n } catch (err) {\n this._emitError(err instanceof Error ? err.message : String(err));\n return null;\n }\n\n if (!result) {\n this._emitStatus(\"idle\");\n return null;\n }\n\n // Validate toMessageId exists in the history\n const historyIds = new Set(this.getHistory().map((m) => m.id));\n if (!historyIds.has(result.toMessageId)) {\n this._emitStatus(\"idle\");\n return null;\n }\n\n // Iterative compaction — extend from earliest existing compaction's start\n const existing = this.getCompactions();\n const fromId =\n existing.length > 0 ? existing[0].fromMessageId : result.fromMessageId;\n\n this.addCompaction(result.summary, fromId, result.toMessageId);\n await this.refreshSystemPrompt();\n\n this._emitStatus(\"idle\", {\n compacted: { tokensBefore }\n });\n\n return { ...result, fromMessageId: fromId };\n }\n\n // ── Context Blocks ────────────────────────────────────────────\n\n getContextBlock(label: string): ContextBlock | null {\n this._ensureReady();\n return this.context.getBlock(label);\n }\n\n getContextBlocks(): ContextBlock[] {\n this._ensureReady();\n return this.context.getBlocks();\n }\n\n async replaceContextBlock(\n label: string,\n content: string\n ): Promise<ContextBlock> {\n this._ensureReady();\n return this.context.setBlock(label, content);\n }\n\n async appendContextBlock(\n label: string,\n content: string\n ): Promise<ContextBlock> {\n this._ensureReady();\n return this.context.appendToBlock(label, content);\n }\n\n // ── System Prompt ─────────────────────────────────────────────\n\n async freezeSystemPrompt(): Promise<string> {\n this._ensureReady();\n return this.context.freezeSystemPrompt();\n }\n\n async refreshSystemPrompt(): Promise<string> {\n this._ensureReady();\n return this.context.refreshSystemPrompt();\n }\n\n // ── Search ────────────────────────────────────────────────────\n\n search(\n query: string,\n options?: { limit?: number }\n ): Array<{\n id: string;\n role: string;\n content: string;\n createdAt?: string;\n }> {\n this._ensureReady();\n if (!this.storage.searchMessages) {\n throw new Error(\"Session provider does not support search\");\n }\n return this.storage.searchMessages(query, options?.limit ?? 20);\n }\n\n // ── Tools ─────────────────────────────────────────────────────\n\n /** Returns set_context and load_context tools. */\n async tools(): Promise<ToolSet> {\n this._ensureReady();\n return this.context.tools();\n }\n}\n","/**\n * SessionManager — registry of named sessions.\n *\n * Lifecycle: create, get, list, delete, rename.\n * Convenience methods for message ops by session ID.\n * Cross-session search and tools.\n */\n\nimport type { UIMessage } from \"ai\";\nimport { jsonSchema, type ToolSet } from \"ai\";\nimport type { CompactResult } from \"../utils/compaction-helpers\";\nimport type { WritableContextProvider } from \"./context\";\nimport type { StoredCompaction } from \"./provider\";\nimport type { SqlProvider } from \"./providers/agent\";\nimport { Session, type SessionContextOptions } from \"./session\";\n\nexport interface SessionInfo {\n id: string;\n name: string;\n parent_session_id: string | null;\n model: string | null;\n source: string | null;\n input_tokens: number;\n output_tokens: number;\n estimated_cost: number;\n end_reason: string | null;\n created_at: string;\n updated_at: string;\n}\n\n// Pending context entry — resolved per-session with namespaced providers\ninterface PendingManagerContext {\n label: string;\n options: SessionContextOptions;\n}\n\nexport interface SessionManagerOptions {\n maxContextMessages?: number;\n}\n\nexport class SessionManager {\n private agent!: SqlProvider;\n private _maxContextMessages = 100;\n private _pending: PendingManagerContext[] = [];\n private _cachedPrompt?: WritableContextProvider | true;\n private _compactionFn?:\n | ((messages: UIMessage[]) => Promise<CompactResult | null>)\n | null;\n private _tokenThreshold?: number;\n private _sessions = new Map<string, Session>();\n private _tableReady = false;\n private _ready = false;\n\n constructor(agent: SqlProvider, options: SessionManagerOptions = {}) {\n this.agent = agent;\n this._maxContextMessages = options.maxContextMessages ?? 100;\n this._ready = true;\n this._ensureTable();\n }\n\n /**\n * Chainable SessionManager creation with auto-wired context for all sessions.\n *\n * @example\n * ```ts\n * const manager = SessionManager.create(this)\n * .withContext(\"soul\", { provider: { get: async () => \"You are helpful.\" } })\n * .withContext(\"memory\", { description: \"Learned facts\", maxTokens: 1100 })\n * .withCachedPrompt()\n * .maxContextMessages(50);\n *\n * // Each getSession(id) auto-creates namespaced providers:\n * // memory key: \"memory_<sessionId>\"\n * // prompt key: \"_system_prompt_<sessionId>\"\n * const session = manager.getSession(\"chat-123\");\n * ```\n */\n static create(agent: SqlProvider): SessionManager {\n const mgr: SessionManager = Object.create(SessionManager.prototype);\n mgr.agent = agent;\n mgr._maxContextMessages = 100;\n mgr._pending = [];\n mgr._compactionFn = null;\n mgr._tokenThreshold = undefined;\n mgr._sessions = new Map();\n mgr._tableReady = false;\n mgr._ready = false;\n return mgr;\n }\n\n // ── Builder methods ─────────────────────────────────────────────\n\n withContext(label: string, options?: SessionContextOptions): this {\n this._pending.push({ label, options: options ?? {} });\n return this;\n }\n\n withCachedPrompt(provider?: WritableContextProvider): this {\n this._cachedPrompt = provider ?? true;\n return this;\n }\n\n maxContextMessages(count: number): this {\n this._maxContextMessages = count;\n return this;\n }\n\n /**\n * Register a compaction function propagated to all sessions.\n * Called by `Session.compact()` to compress message history.\n */\n onCompaction(\n fn: (messages: UIMessage[]) => Promise<CompactResult | null>\n ): this {\n this._compactionFn = fn;\n return this;\n }\n\n /**\n * Auto-compact when estimated token count exceeds the threshold.\n * Propagated to all sessions. Requires `onCompaction()`.\n */\n compactAfter(tokenThreshold: number): this {\n this._tokenThreshold = tokenThreshold;\n return this;\n }\n\n // ── Lazy init ───────────────────────────────────────────────────\n\n private _ensureReady(): void {\n if (this._ready) return;\n this._ready = true;\n this._ensureTable();\n }\n\n private _ensureTable(): void {\n if (this._tableReady) return;\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS assistant_sessions (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n parent_session_id TEXT,\n model TEXT,\n source TEXT,\n input_tokens INTEGER DEFAULT 0,\n output_tokens INTEGER DEFAULT 0,\n estimated_cost REAL DEFAULT 0,\n end_reason TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n this.agent.sql`\n CREATE VIRTUAL TABLE IF NOT EXISTS assistant_fts\n USING fts5(id UNINDEXED, session_id UNINDEXED, role UNINDEXED, content, tokenize='porter unicode61')\n `;\n this._tableReady = true;\n }\n\n // ── Session access ────────────────────────────────────────────\n\n /** Get or create the Session instance for a session ID. */\n getSession(sessionId: string): Session {\n this._ensureReady();\n let session = this._sessions.get(sessionId);\n if (!session) {\n const s = Session.create(this.agent).forSession(sessionId);\n for (const { label, options } of this._pending) {\n s.withContext(label, options);\n }\n if (this._cachedPrompt === true) {\n s.withCachedPrompt();\n } else if (this._cachedPrompt) {\n s.withCachedPrompt(this._cachedPrompt);\n }\n if (this._compactionFn) {\n s.onCompaction(this._compactionFn);\n }\n if (this._tokenThreshold != null) {\n s.compactAfter(this._tokenThreshold);\n }\n session = s;\n this._sessions.set(sessionId, session);\n }\n return session;\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────\n\n create(\n name: string,\n opts?: { parentSessionId?: string; model?: string; source?: string }\n ): SessionInfo {\n this._ensureReady();\n const id = crypto.randomUUID();\n this.agent.sql`\n INSERT INTO assistant_sessions (id, name, parent_session_id, model, source)\n VALUES (${id}, ${name}, ${opts?.parentSessionId ?? null}, ${opts?.model ?? null}, ${opts?.source ?? null})\n `;\n return this.get(id)!;\n }\n\n get(sessionId: string): SessionInfo | null {\n this._ensureReady();\n const rows = this.agent.sql`\n SELECT * FROM assistant_sessions WHERE id = ${sessionId}\n ` as unknown as SessionInfo[];\n return rows[0] ?? null;\n }\n\n list(): SessionInfo[] {\n this._ensureReady();\n return this.agent.sql`\n SELECT * FROM assistant_sessions ORDER BY updated_at DESC\n ` as unknown as SessionInfo[];\n }\n\n delete(sessionId: string): void {\n this.getSession(sessionId).clearMessages();\n this.agent.sql`DELETE FROM assistant_sessions WHERE id = ${sessionId}`;\n this._sessions.delete(sessionId);\n }\n\n rename(sessionId: string, name: string): void {\n this._ensureReady();\n this.agent.sql`\n UPDATE assistant_sessions SET name = ${name}, updated_at = CURRENT_TIMESTAMP\n WHERE id = ${sessionId}\n `;\n }\n\n // ── Message convenience methods ───────────────────────────────\n\n async append(\n sessionId: string,\n message: UIMessage,\n parentId?: string\n ): Promise<string> {\n await this.getSession(sessionId).appendMessage(message, parentId);\n this._touch(sessionId);\n return message.id;\n }\n\n async upsert(\n sessionId: string,\n message: UIMessage,\n parentId?: string\n ): Promise<string> {\n const session = this.getSession(sessionId);\n const existing = session.getMessage(message.id);\n if (existing) {\n session.updateMessage(message);\n } else {\n await session.appendMessage(message, parentId);\n }\n this._touch(sessionId);\n return message.id;\n }\n\n async appendAll(\n sessionId: string,\n messages: UIMessage[],\n parentId?: string\n ): Promise<string | null> {\n const session = this.getSession(sessionId);\n let lastParent = parentId ?? null;\n for (const msg of messages) {\n await session.appendMessage(msg, lastParent);\n lastParent = msg.id;\n }\n this._touch(sessionId);\n return lastParent;\n }\n\n getHistory(sessionId: string, leafId?: string): UIMessage[] {\n return this.getSession(sessionId).getHistory(leafId);\n }\n\n getMessageCount(sessionId: string): number {\n return this.getSession(sessionId).getPathLength();\n }\n\n clearMessages(sessionId: string): void {\n this.getSession(sessionId).clearMessages();\n this._touch(sessionId);\n }\n\n deleteMessages(sessionId: string, messageIds: string[]): void {\n this.getSession(sessionId).deleteMessages(messageIds);\n this._touch(sessionId);\n }\n\n // ── Branching ──────────────────────────────────────────────────\n\n getBranches(sessionId: string, messageId: string): UIMessage[] {\n return this.getSession(sessionId).getBranches(messageId);\n }\n\n /**\n * Fork a session at a specific message, creating a new session\n * with the history up to that point copied over.\n */\n async fork(\n sessionId: string,\n atMessageId: string,\n newName: string\n ): Promise<SessionInfo> {\n const info = this.create(newName, { parentSessionId: sessionId });\n const history = this.getSession(sessionId).getHistory(atMessageId);\n const newSession = this.getSession(info.id);\n\n let parentId: string | null = null;\n for (const msg of history) {\n const newId = crypto.randomUUID();\n const copy: UIMessage = { ...msg, id: newId };\n await newSession.appendMessage(copy, parentId);\n parentId = newId;\n }\n\n this._touch(info.id);\n return info;\n }\n\n // ── Compaction ────────────────────────────────────────────────\n\n needsCompaction(sessionId: string): boolean {\n return (\n this.getSession(sessionId).getPathLength() > this._maxContextMessages\n );\n }\n\n addCompaction(\n sessionId: string,\n summary: string,\n fromId: string,\n toId: string\n ): StoredCompaction {\n return this.getSession(sessionId).addCompaction(summary, fromId, toId);\n }\n\n getCompactions(sessionId: string): StoredCompaction[] {\n return this.getSession(sessionId).getCompactions();\n }\n\n async compactAndSplit(\n sessionId: string,\n summary: string,\n newName?: string\n ): Promise<SessionInfo> {\n const old = this.get(sessionId);\n this.agent.sql`\n UPDATE assistant_sessions SET end_reason = 'compaction', updated_at = CURRENT_TIMESTAMP\n WHERE id = ${sessionId}\n `;\n\n const info = this.create(newName ?? old?.name ?? \"Compacted\", {\n parentSessionId: sessionId,\n model: old?.model ?? undefined,\n source: old?.source ?? undefined\n });\n\n await this.append(info.id, {\n id: crypto.randomUUID(),\n role: \"assistant\",\n parts: [\n { type: \"text\", text: `[Context from previous session]\\n\\n${summary}` }\n ]\n });\n\n return info;\n }\n\n // ── Usage tracking ────────────────────────────────────────────\n\n addUsage(\n sessionId: string,\n inputTokens: number,\n outputTokens: number,\n cost: number\n ): void {\n this._ensureReady();\n this.agent.sql`\n UPDATE assistant_sessions SET\n input_tokens = input_tokens + ${inputTokens},\n output_tokens = output_tokens + ${outputTokens},\n estimated_cost = estimated_cost + ${cost},\n updated_at = CURRENT_TIMESTAMP\n WHERE id = ${sessionId}\n `;\n }\n\n // ── Search ────────────────────────────────────────────────────\n\n search(query: string, options?: { limit?: number }) {\n this._ensureReady();\n const limit = options?.limit ?? 20;\n // Quote each word individually to prevent FTS5 syntax injection\n // while preserving implicit AND between terms\n const sanitized = query\n .split(/\\s+/)\n .filter(Boolean)\n .map((w) => `\"${w.replace(/\"/g, '\"\"')}\"`)\n .join(\" \");\n if (!sanitized) return [];\n try {\n return this.agent.sql<{ id: string; role: string; content: string }>`\n SELECT id, role, content FROM assistant_fts\n WHERE assistant_fts MATCH ${sanitized}\n ORDER BY rank LIMIT ${limit}\n `.map((r) => ({\n id: r.id,\n role: r.role,\n content: r.content,\n createdAt: \"\"\n }));\n } catch {\n return [];\n }\n }\n\n // ── Tools ─────────────────────────────────────────────────────\n\n tools(): ToolSet {\n return {\n session_search: {\n description:\n \"Search past conversations for relevant context. Searches across all sessions.\",\n inputSchema: jsonSchema({\n type: \"object\" as const,\n properties: {\n query: { type: \"string\" as const, description: \"Search query\" }\n },\n required: [\"query\"]\n }),\n execute: async ({ query }: { query: string }) => {\n try {\n const results = this.search(query, { limit: 10 });\n if (results.length === 0) return \"No results found.\";\n return results\n .map((r) => `[${r.role}] ${r.content}`)\n .join(\"\\n---\\n\");\n } catch (err) {\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n }\n }\n }\n };\n }\n\n // ── Internal ──────────────────────────────────────────────────\n\n private _touch(sessionId: string): void {\n this.agent.sql`\n UPDATE assistant_sessions SET updated_at = CURRENT_TIMESTAMP\n WHERE id = ${sessionId}\n `;\n }\n}\n"],"mappings":"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"}
@@ -55,8 +55,8 @@ import {
55
55
  ServerCapabilities,
56
56
  Tool
57
57
  } from "@modelcontextprotocol/sdk/types.js";
58
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
59
58
  import { ToolSet } from "ai";
59
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
60
60
  import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
61
61
  import {
62
62
  Transport,
@@ -629,6 +629,7 @@ declare class MCPClientConnection {
629
629
  name: string;
630
630
  description?: string | undefined;
631
631
  mimeType?: string | undefined;
632
+ size?: number | undefined;
632
633
  annotations?:
633
634
  | {
634
635
  audience?: ("user" | "assistant")[] | undefined;
@@ -720,7 +721,7 @@ declare class MCPClientConnection {
720
721
  */
721
722
  getTransport(
722
723
  transportType: BaseTransportType
723
- ): StreamableHTTPClientTransport | SSEClientTransport | RPCClientTransport;
724
+ ): RPCClientTransport | SSEClientTransport | StreamableHTTPClientTransport;
724
725
  private tryConnect;
725
726
  private _capabilityErrorHandler;
726
727
  }
@@ -1157,6 +1158,7 @@ declare class MCPClientManager {
1157
1158
  type: "resource_link";
1158
1159
  description?: string | undefined;
1159
1160
  mimeType?: string | undefined;
1161
+ size?: number | undefined;
1160
1162
  annotations?:
1161
1163
  | {
1162
1164
  audience?: ("user" | "assistant")[] | undefined;
@@ -1327,6 +1329,7 @@ declare class MCPClientManager {
1327
1329
  type: "resource_link";
1328
1330
  description?: string | undefined;
1329
1331
  mimeType?: string | undefined;
1332
+ size?: number | undefined;
1330
1333
  annotations?:
1331
1334
  | {
1332
1335
  audience?: ("user" | "assistant")[] | undefined;
@@ -2865,4 +2868,4 @@ export {
2865
2868
  Schedule as y,
2866
2869
  MCPServerOptions as z
2867
2870
  };
2868
- //# sourceMappingURL=index-C-6EMK-E.d.ts.map
2871
+ //# sourceMappingURL=index-BtHngIIG.d.ts.map
package/dist/index.d.ts CHANGED
@@ -33,7 +33,7 @@ import {
33
33
  w as SubAgentStub,
34
34
  x as StateUpdateMessage,
35
35
  y as Schedule
36
- } from "./index-C-6EMK-E.js";
36
+ } from "./index-BtHngIIG.js";
37
37
  import {
38
38
  n as AgentsOAuthProvider,
39
39
  r as DurableObjectOAuthClientProvider,
@@ -1,2 +1,2 @@
1
- import { B as RegisterServerOptions, F as MCPClientOAuthResult, I as MCPConnectionResult, L as MCPDiscoverResult, M as MCPClientManager, N as MCPClientManagerOptions, P as MCPClientOAuthCallbackConfig, R as MCPOAuthCallbackResult, V as getNamespacedData, z as MCPServerOptions } from "../index-C-6EMK-E.js";
1
+ import { B as RegisterServerOptions, F as MCPClientOAuthResult, I as MCPConnectionResult, L as MCPDiscoverResult, M as MCPClientManager, N as MCPClientManagerOptions, P as MCPClientOAuthCallbackConfig, R as MCPOAuthCallbackResult, V as getNamespacedData, z as MCPServerOptions } from "../index-BtHngIIG.js";
2
2
  export { MCPClientManager, MCPClientManagerOptions, MCPClientOAuthCallbackConfig, MCPClientOAuthResult, MCPConnectionResult, MCPDiscoverResult, MCPOAuthCallbackResult, MCPServerOptions, RegisterServerOptions, getNamespacedData };
@@ -1,2 +1,2 @@
1
- import { $ as experimental_createMcpHandler, F as MCPClientOAuthResult, G as RPCServerTransportOptions, H as RPCClientTransport, I as MCPConnectionResult, J as ElicitRequestSchema, K as RPC_DO_PREFIX, L as MCPDiscoverResult, P as MCPClientOAuthCallbackConfig, Q as createMcpHandler, U as RPCClientTransportOptions, W as RPCServerTransport, X as McpAgent, Y as ElicitResult, Z as CreateMcpHandlerOptions, at as SSEEdgeClientTransport, et as McpAuthContext, it as WorkerTransportOptions, nt as TransportState, ot as StreamableHTTPEdgeClientTransport, q as ElicitRequest, rt as WorkerTransport, st as McpClientOptions, tt as getMcpAuthContext, z as MCPServerOptions } from "../index-C-6EMK-E.js";
1
+ import { $ as experimental_createMcpHandler, F as MCPClientOAuthResult, G as RPCServerTransportOptions, H as RPCClientTransport, I as MCPConnectionResult, J as ElicitRequestSchema, K as RPC_DO_PREFIX, L as MCPDiscoverResult, P as MCPClientOAuthCallbackConfig, Q as createMcpHandler, U as RPCClientTransportOptions, W as RPCServerTransport, X as McpAgent, Y as ElicitResult, Z as CreateMcpHandlerOptions, at as SSEEdgeClientTransport, et as McpAuthContext, it as WorkerTransportOptions, nt as TransportState, ot as StreamableHTTPEdgeClientTransport, q as ElicitRequest, rt as WorkerTransport, st as McpClientOptions, tt as getMcpAuthContext, z as MCPServerOptions } from "../index-BtHngIIG.js";
2
2
  export { CreateMcpHandlerOptions, ElicitRequest, ElicitRequestSchema, ElicitResult, MCPClientOAuthCallbackConfig, MCPClientOAuthResult, MCPConnectionResult, MCPDiscoverResult, MCPServerOptions, McpAgent, McpAuthContext, McpClientOptions, RPCClientTransport, RPCClientTransportOptions, RPCServerTransport, RPCServerTransportOptions, RPC_DO_PREFIX, SSEEdgeClientTransport, StreamableHTTPEdgeClientTransport, TransportState, WorkerTransport, WorkerTransportOptions, createMcpHandler, experimental_createMcpHandler, getMcpAuthContext };
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { h as MCPServersState } from "./index-C-6EMK-E.js";
1
+ import { h as MCPServersState } from "./index-BtHngIIG.js";
2
2
  import {
3
3
  AgentPromiseReturnType,
4
4
  AgentStub,
@@ -1,4 +1,4 @@
1
- import { r as Agent } from "./index-C-6EMK-E.js";
1
+ import { r as Agent } from "./index-BtHngIIG.js";
2
2
  import {
3
3
  S as WorkflowTrackingRow,
4
4
  _ as WorkflowPage,
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "durable objects"
10
10
  ],
11
11
  "type": "module",
12
- "version": "0.8.7",
12
+ "version": "0.9.0",
13
13
  "license": "MIT",
14
14
  "repository": {
15
15
  "directory": "packages/agents",
@@ -26,7 +26,7 @@
26
26
  "dependencies": {
27
27
  "@babel/plugin-proposal-decorators": "^7.29.0",
28
28
  "@cfworker/json-schema": "^4.1.1",
29
- "@modelcontextprotocol/sdk": "1.28.0",
29
+ "@modelcontextprotocol/sdk": "1.29.0",
30
30
  "@rolldown/plugin-babel": "^0.2.2",
31
31
  "cron-schedule": "^6.0.0",
32
32
  "json-schema": "^0.4.0",
@@ -39,14 +39,14 @@
39
39
  "yargs": "^18.0.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@ai-sdk/openai": "^3.0.48",
43
- "@ai-sdk/react": "^3.0.143",
44
- "@cloudflare/workers-oauth-provider": "^0.3.1",
42
+ "@ai-sdk/openai": "^3.0.49",
43
+ "@ai-sdk/react": "^3.0.144",
44
+ "@cloudflare/workers-oauth-provider": "^0.4.0",
45
45
  "@types/react": "^19.2.14",
46
46
  "@types/yargs": "^17.0.35",
47
47
  "@x402/core": "^2.8.0",
48
48
  "@x402/evm": "^2.8.0",
49
- "ai": "^6.0.141",
49
+ "ai": "^6.0.142",
50
50
  "react": "^19.2.4",
51
51
  "vitest-browser-react": "^2.1.0",
52
52
  "zod": "^4.3.6"
@@ -54,8 +54,8 @@
54
54
  "peerDependencies": {
55
55
  "@ai-sdk/openai": "^3.0.0",
56
56
  "@ai-sdk/react": "^3.0.0",
57
- "@cloudflare/ai-chat": "^0.2.6",
58
- "@cloudflare/codemode": "^0.3.2",
57
+ "@cloudflare/ai-chat": "^0.3.2",
58
+ "@cloudflare/codemode": "^0.3.3",
59
59
  "@x402/core": "^2.0.0",
60
60
  "@x402/evm": "^2.0.0",
61
61
  "ai": "^6.0.0",
@@ -167,6 +167,11 @@
167
167
  "import": "./dist/mcp/x402.js",
168
168
  "require": "./dist/mcp/x402.js"
169
169
  },
170
+ "./chat": {
171
+ "types": "./dist/chat/index.d.ts",
172
+ "import": "./dist/chat/index.js",
173
+ "require": "./dist/chat/index.js"
174
+ },
170
175
  "./ai-chat-agent": {
171
176
  "types": "./dist/ai-chat-agent.d.ts",
172
177
  "import": "./dist/ai-chat-agent.js",
@@ -220,6 +225,7 @@
220
225
  "test:cli": "vitest --project cli",
221
226
  "test:react": "vitest --project react",
222
227
  "test:workers": "vitest --project workers",
228
+ "test:chat": "vitest --project chat",
223
229
  "test:x402": "vitest --project x402"
224
230
  }
225
231
  }