agents 0.8.6 → 0.8.7
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.
- package/dist/client.d.ts +2 -2
- package/dist/compaction-helpers-BFTBIzpK.js +312 -0
- package/dist/compaction-helpers-BFTBIzpK.js.map +1 -0
- package/dist/compaction-helpers-DkJreaDR.d.ts +139 -0
- package/dist/{do-oauth-client-provider-D7F2Pw40.d.ts → do-oauth-client-provider-C2jurFjW.d.ts} +1 -1
- package/dist/{email-YAQhwwXb.d.ts → email-DwPlM0bQ.d.ts} +1 -1
- package/dist/email.d.ts +2 -2
- package/dist/experimental/forever.d.ts +1 -1
- package/dist/experimental/memory/session/index.d.ts +193 -203
- package/dist/experimental/memory/session/index.js +673 -294
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/experimental/memory/utils/index.d.ts +59 -0
- package/dist/experimental/memory/utils/index.js +69 -0
- package/dist/experimental/memory/utils/index.js.map +1 -0
- package/dist/{index-DynYigzs.d.ts → index-C-6EMK-E.d.ts} +11 -11
- package/dist/{index-OtkSCU2A.d.ts → index-Ua2Nfvbm.d.ts} +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{internal_context-DgcmHqS1.d.ts → internal_context-DT8RxmAN.d.ts} +1 -1
- package/dist/internal_context.d.ts +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +37 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/observability/index.d.ts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/{retries-JxhDYtYL.d.ts → retries-DXMQGhG3.d.ts} +1 -1
- package/dist/retries.d.ts +1 -1
- package/dist/{serializable-Ch19yA6_.d.ts → serializable-8Jt1B04R.d.ts} +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/{types-2lHHE_uh.d.ts → types-C-m0II8i.d.ts} +3 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/dist/{workflow-types-BVKtSaA7.d.ts → workflow-types-CZNXKj_D.d.ts} +1 -1
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +2 -2
- package/package.json +10 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/experimental/memory/utils/compaction.ts","../../../../src/experimental/memory/utils/tokens.ts","../../../../src/experimental/memory/session/session.ts","../../../../src/experimental/memory/session/providers/agent.ts"],"sourcesContent":["/**\n * MicroCompaction Utilities\n *\n * Internal pure functions for lightweight compaction (no LLM).\n * Not exported to users — used by the Session wrapper.\n */\n\nimport type { UIMessage } from \"ai\";\nimport type { MicroCompactionRules } from \"../session/types\";\n\n/** Default thresholds for microCompaction rules (in chars) */\nexport const DEFAULTS = {\n truncateToolOutputs: 30000,\n truncateText: 10000,\n keepRecent: 4\n};\n\n/** Resolved microCompaction rules with actual numeric thresholds */\nexport interface ResolvedMicroCompactionRules {\n truncateToolOutputs: number | false;\n truncateText: number | false;\n keepRecent: number;\n}\n\n/**\n * Parse microCompaction config into resolved rules.\n * Returns null if disabled.\n */\nexport function parseMicroCompactionRules(\n config: boolean | MicroCompactionRules\n): ResolvedMicroCompactionRules | null {\n if (config === false) return null;\n\n if (config === true) {\n return {\n truncateToolOutputs: DEFAULTS.truncateToolOutputs,\n truncateText: DEFAULTS.truncateText,\n keepRecent: DEFAULTS.keepRecent\n };\n }\n\n // Custom rules object — validate numeric values\n const keepRecent = config.keepRecent ?? DEFAULTS.keepRecent;\n if (!Number.isInteger(keepRecent) || keepRecent < 0) {\n throw new Error(\"keepRecent must be a non-negative integer\");\n }\n\n const truncateToolOutputs =\n config.truncateToolOutputs === false\n ? false\n : config.truncateToolOutputs === true ||\n config.truncateToolOutputs === undefined\n ? DEFAULTS.truncateToolOutputs\n : config.truncateToolOutputs;\n if (typeof truncateToolOutputs === \"number\" && truncateToolOutputs <= 0) {\n throw new Error(\"truncateToolOutputs must be a positive number\");\n }\n\n const truncateText =\n config.truncateText === false\n ? false\n : config.truncateText === true || config.truncateText === undefined\n ? DEFAULTS.truncateText\n : config.truncateText;\n if (typeof truncateText === \"number\" && truncateText <= 0) {\n throw new Error(\"truncateText must be a positive number\");\n }\n\n return { truncateToolOutputs, truncateText, keepRecent };\n}\n\n/**\n * Truncate oversized parts in a single message.\n * Returns the same reference if nothing changed (allows callers to skip no-op updates).\n */\nfunction truncateMessageParts(\n msg: UIMessage,\n rules: ResolvedMicroCompactionRules\n): UIMessage {\n let changed = false;\n\n const compactedParts = msg.parts.map((part) => {\n // Truncate tool outputs\n if (\n rules.truncateToolOutputs !== false &&\n (part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\") &&\n \"output\" in part\n ) {\n const toolPart = part as { output?: unknown };\n if (toolPart.output !== undefined) {\n const outputJson = JSON.stringify(toolPart.output);\n if (outputJson.length > rules.truncateToolOutputs) {\n changed = true;\n return {\n ...part,\n output: `[Truncated ${outputJson.length} bytes] ${outputJson.slice(0, 500)}...`\n };\n }\n }\n }\n\n // Truncate long text parts\n if (\n rules.truncateText !== false &&\n part.type === \"text\" &&\n \"text\" in part\n ) {\n const textPart = part as { type: \"text\"; text: string };\n if (textPart.text.length > rules.truncateText) {\n changed = true;\n return {\n ...part,\n text: `${textPart.text.slice(0, rules.truncateText)}... [truncated ${textPart.text.length} chars]`\n };\n }\n }\n\n return part;\n });\n\n return changed ? ({ ...msg, parts: compactedParts } as UIMessage) : msg;\n}\n\n/**\n * Apply microCompaction to an array of messages.\n * Returns same reference for unchanged messages (enables skip-update optimization).\n *\n * No keepRecent logic — the caller decides which messages to pass.\n */\nexport function microCompact(\n messages: UIMessage[],\n rules: ResolvedMicroCompactionRules\n): UIMessage[] {\n return messages.map((msg) => truncateMessageParts(msg, rules));\n}\n","/**\n * Token Estimation Utilities\n *\n * IMPORTANT: These are heuristic estimates, not actual tokenizer counts.\n *\n * We intentionally avoid real tokenizers (e.g. tiktoken, sentencepiece) because:\n * - A single tiktoken instance costs ~80-120MB of heap\n * - Cloudflare Workers have tight memory limits (128MB)\n * - For compaction thresholds, a conservative estimate is sufficient\n *\n * The hybrid approach (max of character-based and word-based estimates) handles\n * both dense token content (JSON, code) and natural language reasonably well.\n *\n * Calibration notes:\n * - Character-based: ~4 chars per token (conservative, from OpenAI guidance)\n * - Word-based: ~1.3 tokens per word (empirical, from Mastra's memory system)\n * - Per-message overhead: ~4 tokens for role/framing (empirical)\n *\n * These ratios are tuned for English. CJK, emoji-heavy, or highly technical\n * content may have different ratios. The conservative estimates help ensure\n * compaction triggers before context windows are actually exceeded.\n */\n\nimport type { UIMessage } from \"ai\";\n\n/** Approximate characters per token for English text */\nexport const CHARS_PER_TOKEN = 4;\n\n/** Approximate token multiplier per whitespace-separated word */\nexport const WORDS_TOKEN_MULTIPLIER = 1.3;\n\n/** Approximate overhead tokens per message (role, framing) */\nexport const TOKENS_PER_MESSAGE = 4;\n\n/**\n * Estimate token count for a string using a hybrid heuristic.\n *\n * Takes the max of two estimates:\n * - Character-based: `length / 4` — better for dense content (JSON, code, URLs)\n * - Word-based: `words * 1.3` — better for natural language prose\n *\n * This is a heuristic. Do not use where exact counts are required.\n */\nexport function estimateStringTokens(text: string): number {\n if (!text) return 0;\n const charEstimate = text.length / CHARS_PER_TOKEN;\n const wordEstimate =\n text.split(/\\s+/).filter(Boolean).length * WORDS_TOKEN_MULTIPLIER;\n return Math.ceil(Math.max(charEstimate, wordEstimate));\n}\n\n/**\n * Estimate total token count for an array of UIMessages.\n *\n * Walks each message's parts (text, tool invocations, tool results)\n * and applies per-message overhead.\n *\n * This is a heuristic. Do not use where exact counts are required.\n */\nexport function estimateMessageTokens(messages: UIMessage[]): number {\n let tokens = 0;\n for (const msg of messages) {\n tokens += TOKENS_PER_MESSAGE;\n for (const part of msg.parts) {\n if (part.type === \"text\") {\n tokens += estimateStringTokens(\n (part as { type: \"text\"; text: string }).text\n );\n } else if (\n part.type.startsWith(\"tool-\") ||\n part.type === \"dynamic-tool\"\n ) {\n const toolPart = part as { input?: unknown; output?: unknown };\n if (toolPart.input) {\n tokens += estimateStringTokens(JSON.stringify(toolPart.input));\n }\n if (toolPart.output) {\n tokens += estimateStringTokens(JSON.stringify(toolPart.output));\n }\n }\n }\n }\n return tokens;\n}\n","/**\n * Session — top-level API for conversation history with compaction.\n *\n * Wraps any SessionProvider (pure storage) and orchestrates compaction:\n * - microCompaction on every append() — cheap, no LLM\n * - full compaction when token threshold exceeded — user-supplied fn\n */\n\nimport type { UIMessage } from \"ai\";\nimport type { SessionProvider } from \"./provider\";\nimport type {\n MessageQueryOptions,\n SessionProviderOptions,\n CompactResult\n} from \"./types\";\nimport {\n parseMicroCompactionRules,\n microCompact,\n type ResolvedMicroCompactionRules\n} from \"../utils/compaction\";\nimport { estimateMessageTokens } from \"../utils/tokens\";\n\nexport class Session {\n private storage: SessionProvider;\n private microCompactionRules: ResolvedMicroCompactionRules | null;\n private compactionConfig: SessionProviderOptions[\"compaction\"] | null;\n\n constructor(storage: SessionProvider, options?: SessionProviderOptions) {\n this.storage = storage;\n\n const mc = options?.microCompaction ?? true;\n this.microCompactionRules = parseMicroCompactionRules(mc);\n this.compactionConfig = options?.compaction ?? null;\n }\n\n // ── Read (delegated to storage) ────────────────────────────────────\n\n getMessages(options?: MessageQueryOptions): UIMessage[] {\n return this.storage.getMessages(options);\n }\n\n getMessage(id: string): UIMessage | null {\n return this.storage.getMessage(id);\n }\n\n getLastMessages(n: number): UIMessage[] {\n return this.storage.getLastMessages(n);\n }\n\n // ── Write (delegated + compaction) ─────────────────────────────────\n\n async append(messages: UIMessage | UIMessage[]): Promise<void> {\n // 1. Storage inserts\n await this.storage.appendMessages(messages);\n\n // 2. Full compaction if token threshold exceeded — runs instead of microCompaction\n if (this.shouldAutoCompact()) {\n const result = await this.compact();\n if (result.success) return;\n // Fall through to microCompaction if full compaction failed\n }\n\n // 3. MicroCompaction on older messages (only if no full compaction)\n if (this.microCompactionRules) {\n const rules = this.microCompactionRules;\n const older = this.storage.getOlderMessages(rules.keepRecent);\n\n if (older.length > 0) {\n const compacted = microCompact(older, rules);\n for (let i = 0; i < older.length; i++) {\n if (compacted[i] !== older[i]) {\n this.storage.updateMessage(compacted[i]);\n }\n }\n }\n }\n }\n\n updateMessage(message: UIMessage): void {\n this.storage.updateMessage(message);\n }\n\n deleteMessages(messageIds: string[]): void {\n this.storage.deleteMessages(messageIds);\n }\n\n clearMessages(): void {\n this.storage.clearMessages();\n }\n\n // ── Compaction ─────────────────────────────────────────────────────\n\n async compact(): Promise<CompactResult> {\n const messages = this.storage.getMessages();\n\n if (messages.length === 0) {\n return { success: true };\n }\n\n try {\n let result = messages;\n\n if (this.compactionConfig?.fn) {\n result = await this.compactionConfig.fn(result);\n }\n\n await this.storage.replaceMessages(result);\n\n return { success: true };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err)\n };\n }\n }\n\n /**\n * Pre-check for auto-compaction using token estimate heuristic.\n */\n private shouldAutoCompact(): boolean {\n if (!this.compactionConfig?.tokenThreshold) return false;\n\n const messages = this.storage.getMessages();\n const approxTokens = estimateMessageTokens(messages);\n return approxTokens > this.compactionConfig.tokenThreshold;\n }\n}\n","/**\n * Agent Session Provider\n *\n * Pure storage provider that uses the Agent's DO SQLite storage.\n * Compaction is orchestrated by the Session wrapper, not here.\n */\n\nimport type { UIMessage } from \"ai\";\nimport type { SessionProvider } from \"../provider\";\nimport type { MessageQueryOptions } from \"../types\";\n\n/**\n * Interface for objects that provide a sql tagged template method.\n * This matches the Agent class's sql method signature.\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\n/**\n * Session provider that wraps an Agent's SQLite storage.\n * Provides pure CRUD — compaction is handled by the Session wrapper.\n *\n * @example\n * ```typescript\n * import { Session, AgentSessionProvider } from \"agents/experimental/memory/session\";\n *\n * // In your Agent class:\n * session = new Session(new AgentSessionProvider(this));\n *\n * // With compaction options:\n * session = new Session(new AgentSessionProvider(this), {\n * microCompaction: { truncateToolOutputs: 2000, keepRecent: 10 },\n * compaction: { tokenThreshold: 20000, fn: summarize }\n * });\n * ```\n */\nexport class AgentSessionProvider implements SessionProvider {\n private agent: SqlProvider;\n private initialized = false;\n\n constructor(agent: SqlProvider) {\n this.agent = agent;\n }\n\n /**\n * Ensure the messages table exists\n */\n private ensureTable(): void {\n if (this.initialized) return;\n\n this.agent.sql`\n CREATE TABLE IF NOT EXISTS cf_agents_session_messages (\n id TEXT PRIMARY KEY,\n message TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n `;\n this.initialized = true;\n }\n\n /**\n * Get all messages in AI SDK format\n */\n getMessages(options?: MessageQueryOptions): UIMessage[] {\n this.ensureTable();\n\n if (\n options?.limit !== undefined &&\n (!Number.isInteger(options.limit) || options.limit < 0)\n ) {\n throw new Error(\"limit must be a non-negative integer\");\n }\n if (\n options?.offset !== undefined &&\n (!Number.isInteger(options.offset) || options.offset < 0)\n ) {\n throw new Error(\"offset must be a non-negative integer\");\n }\n\n type Row = { id: string; message: string; created_at: string };\n const role = options?.role ?? null;\n const before = options?.before?.toISOString() ?? null;\n const after = options?.after?.toISOString() ?? null;\n const limit = options?.limit ?? -1;\n const offset = options?.offset ?? 0;\n\n const rows = this.agent.sql<Row>`\n SELECT id, message, created_at FROM cf_agents_session_messages\n WHERE (${role} IS NULL OR json_extract(message, '$.role') = ${role})\n AND (${before} IS NULL OR created_at < ${before})\n AND (${after} IS NULL OR created_at > ${after})\n ORDER BY created_at ASC, rowid ASC\n LIMIT ${limit} OFFSET ${offset}\n `;\n\n return this.parseRows(rows);\n }\n\n /**\n * Append one or more messages to storage.\n */\n async appendMessages(messages: UIMessage | UIMessage[]): Promise<void> {\n this.ensureTable();\n\n const messageArray = Array.isArray(messages) ? messages : [messages];\n const now = new Date().toISOString();\n\n for (const message of messageArray) {\n const json = JSON.stringify(message);\n this.agent.sql`\n INSERT INTO cf_agents_session_messages (id, message, created_at)\n VALUES (${message.id}, ${json}, ${now})\n ON CONFLICT(id) DO UPDATE SET message = excluded.message\n `;\n }\n }\n\n /**\n * Update an existing message\n */\n updateMessage(message: UIMessage): void {\n this.ensureTable();\n\n const json = JSON.stringify(message);\n this.agent.sql`\n UPDATE cf_agents_session_messages\n SET message = ${json}\n WHERE id = ${message.id}\n `;\n }\n\n /**\n * Delete messages by their IDs\n */\n deleteMessages(messageIds: string[]): void {\n this.ensureTable();\n\n for (const id of messageIds) {\n this.agent.sql`DELETE FROM cf_agents_session_messages WHERE id = ${id}`;\n }\n }\n\n /**\n * Clear all messages from the session\n */\n clearMessages(): void {\n this.ensureTable();\n this.agent.sql`DELETE FROM cf_agents_session_messages`;\n }\n\n /**\n * Get a single message by ID\n */\n getMessage(id: string): UIMessage | null {\n this.ensureTable();\n\n const rows = this.agent.sql<{ message: string }>`\n SELECT message FROM cf_agents_session_messages WHERE id = ${id}\n `;\n\n if (rows.length === 0) return null;\n\n try {\n const parsed = JSON.parse(rows[0].message);\n return this.isValidMessage(parsed) ? parsed : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Get the last N messages (most recent)\n */\n getLastMessages(n: number): UIMessage[] {\n this.ensureTable();\n\n const rows = this.agent.sql<{ message: string }>`\n SELECT message FROM cf_agents_session_messages\n ORDER BY created_at DESC, rowid DESC\n LIMIT ${n}\n `;\n\n return this.parseRows([...rows].reverse());\n }\n\n /**\n * Fetch messages outside the recent window (for microCompaction).\n * Returns all messages except the most recent `keepRecent`.\n */\n getOlderMessages(keepRecent: number): UIMessage[] {\n this.ensureTable();\n\n type Row = { id: string; message: string };\n const rows = this.agent.sql<Row>`\n SELECT id, message FROM cf_agents_session_messages\n WHERE rowid NOT IN (\n SELECT rowid FROM cf_agents_session_messages\n ORDER BY created_at DESC, rowid DESC\n LIMIT ${keepRecent}\n )\n `;\n\n return this.parseRows(rows);\n }\n\n /**\n * Bulk replace all messages.\n * Preserves original created_at timestamps for surviving messages.\n */\n async replaceMessages(messages: UIMessage[]): Promise<void> {\n this.ensureTable();\n\n // Build timestamp map from existing messages before clearing\n type Row = { id: string; created_at: string };\n const existingRows = this.agent.sql<Row>`\n SELECT id, created_at FROM cf_agents_session_messages\n `;\n const timestampMap = new Map<string, string>();\n for (const row of existingRows) {\n timestampMap.set(row.id, row.created_at);\n }\n\n // Durable Objects auto-coalesces all synchronous SQL writes within\n // a single I/O gate into one atomic batch — no explicit transaction needed.\n this.agent.sql`DELETE FROM cf_agents_session_messages`;\n\n const now = new Date().toISOString();\n for (const message of messages) {\n const json = JSON.stringify(message);\n const created_at = timestampMap.get(message.id) ?? now;\n this.agent.sql`\n INSERT INTO cf_agents_session_messages (id, message, created_at)\n VALUES (${message.id}, ${json}, ${created_at})\n ON CONFLICT(id) DO UPDATE SET message = excluded.message\n `;\n }\n }\n\n /**\n * Validate message structure\n */\n private isValidMessage(msg: unknown): msg is UIMessage {\n if (typeof msg !== \"object\" || msg === null) return false;\n const m = msg as Record<string, unknown>;\n\n if (typeof m.id !== \"string\" || m.id.length === 0) return false;\n if (m.role !== \"user\" && m.role !== \"assistant\" && m.role !== \"system\") {\n return false;\n }\n if (!Array.isArray(m.parts)) return false;\n\n return true;\n }\n\n /**\n * Parse message rows from SQL results into UIMessages.\n */\n private parseRows(rows: { id?: string; message: string }[]): UIMessage[] {\n const messages: UIMessage[] = [];\n for (const row of rows) {\n try {\n const parsed = JSON.parse(row.message);\n if (this.isValidMessage(parsed)) {\n messages.push(parsed);\n }\n } catch {\n if (row.id) {\n console.warn(\n `[AgentSessionProvider] Skipping malformed message ${row.id}`\n );\n }\n }\n }\n return messages;\n }\n}\n"],"mappings":"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"}
|
|
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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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { a as alignBoundaryForward, c as createCompactFunction, d as sanitizeToolPairs, i as alignBoundaryBackward, l as findTailCutByTokens, n as CompactOptions, o as buildSummaryPrompt, r as CompactResult, s as computeSummaryBudget, t as COMPACTION_PREFIX, u as isCompactionMessage } from "../../../compaction-helpers-DkJreaDR.js";
|
|
2
|
+
import { UIMessage } from "ai";
|
|
3
|
+
|
|
4
|
+
//#region src/experimental/memory/utils/tokens.d.ts
|
|
5
|
+
/** Approximate characters per token for English text */
|
|
6
|
+
declare const CHARS_PER_TOKEN = 4;
|
|
7
|
+
/** Approximate token multiplier per whitespace-separated word */
|
|
8
|
+
declare const WORDS_TOKEN_MULTIPLIER = 1.3;
|
|
9
|
+
/** Approximate overhead tokens per message (role, framing) */
|
|
10
|
+
declare const TOKENS_PER_MESSAGE = 4;
|
|
11
|
+
/**
|
|
12
|
+
* Estimate token count for a string using a hybrid heuristic.
|
|
13
|
+
*
|
|
14
|
+
* Takes the max of two estimates:
|
|
15
|
+
* - Character-based: `length / 4` — better for dense content (JSON, code, URLs)
|
|
16
|
+
* - Word-based: `words * 1.3` — better for natural language prose
|
|
17
|
+
*
|
|
18
|
+
* This is a heuristic. Do not use where exact counts are required.
|
|
19
|
+
*/
|
|
20
|
+
declare function estimateStringTokens(text: string): number;
|
|
21
|
+
/**
|
|
22
|
+
* Estimate total token count for an array of UIMessages.
|
|
23
|
+
*
|
|
24
|
+
* Walks each message's parts (text, tool invocations, tool results)
|
|
25
|
+
* and applies per-message overhead.
|
|
26
|
+
*
|
|
27
|
+
* This is a heuristic. Do not use where exact counts are required.
|
|
28
|
+
*/
|
|
29
|
+
declare function estimateMessageTokens(messages: UIMessage[]): number;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/experimental/memory/utils/compaction.d.ts
|
|
32
|
+
interface TruncateOptions {
|
|
33
|
+
/** Number of recent messages to keep intact (default: 4) */
|
|
34
|
+
keepRecent?: number;
|
|
35
|
+
/** Max chars for tool outputs in older messages (default: 500) */
|
|
36
|
+
maxToolOutputChars?: number;
|
|
37
|
+
/** Max chars for text parts in older messages (default: 10000) */
|
|
38
|
+
maxTextChars?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Truncate tool outputs and long text in older messages.
|
|
42
|
+
* Returns a new array — input messages are not mutated.
|
|
43
|
+
*
|
|
44
|
+
* Recent messages (last `keepRecent`) are left intact.
|
|
45
|
+
* Older messages get tool outputs and long text truncated.
|
|
46
|
+
*
|
|
47
|
+
* Use in assembleContext() before sending to the LLM:
|
|
48
|
+
* ```typescript
|
|
49
|
+
* async assembleContext() {
|
|
50
|
+
* const history = this.sessions.getHistory(this._sessionId);
|
|
51
|
+
* const truncated = truncateOlderMessages(history);
|
|
52
|
+
* return convertToModelMessages(truncated);
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function truncateOlderMessages(messages: UIMessage[], options?: TruncateOptions): UIMessage[];
|
|
57
|
+
//#endregion
|
|
58
|
+
export { CHARS_PER_TOKEN, COMPACTION_PREFIX, type CompactOptions, type CompactResult, TOKENS_PER_MESSAGE, type TruncateOptions, WORDS_TOKEN_MULTIPLIER, alignBoundaryBackward, alignBoundaryForward, buildSummaryPrompt, computeSummaryBudget, createCompactFunction, estimateMessageTokens, estimateStringTokens, findTailCutByTokens, isCompactionMessage, sanitizeToolPairs, truncateOlderMessages };
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { a as computeSummaryBudget, c as isCompactionMessage, d as TOKENS_PER_MESSAGE, f as WORDS_TOKEN_MULTIPLIER, i as buildSummaryPrompt, l as sanitizeToolPairs, m as estimateStringTokens, n as alignBoundaryBackward, o as createCompactFunction, p as estimateMessageTokens, r as alignBoundaryForward, s as findTailCutByTokens, t as COMPACTION_PREFIX, u as CHARS_PER_TOKEN } from "../../../compaction-helpers-BFTBIzpK.js";
|
|
2
|
+
//#region src/experimental/memory/utils/compaction.ts
|
|
3
|
+
/**
|
|
4
|
+
* Truncate tool outputs and long text in older messages.
|
|
5
|
+
* Returns a new array — input messages are not mutated.
|
|
6
|
+
*
|
|
7
|
+
* Recent messages (last `keepRecent`) are left intact.
|
|
8
|
+
* Older messages get tool outputs and long text truncated.
|
|
9
|
+
*
|
|
10
|
+
* Use in assembleContext() before sending to the LLM:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* async assembleContext() {
|
|
13
|
+
* const history = this.sessions.getHistory(this._sessionId);
|
|
14
|
+
* const truncated = truncateOlderMessages(history);
|
|
15
|
+
* return convertToModelMessages(truncated);
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
function truncateOlderMessages(messages, options) {
|
|
20
|
+
const keepRecent = options?.keepRecent ?? 4;
|
|
21
|
+
const maxToolOutput = options?.maxToolOutputChars ?? 500;
|
|
22
|
+
const maxText = options?.maxTextChars ?? 1e4;
|
|
23
|
+
if (messages.length <= keepRecent) return messages;
|
|
24
|
+
const cutoff = messages.length - keepRecent;
|
|
25
|
+
const result = [];
|
|
26
|
+
for (let i = 0; i < messages.length; i++) {
|
|
27
|
+
if (i >= cutoff) {
|
|
28
|
+
result.push(messages[i]);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const msg = messages[i];
|
|
32
|
+
let changed = false;
|
|
33
|
+
const truncatedParts = msg.parts.map((part) => {
|
|
34
|
+
if ((part.type.startsWith("tool-") || part.type === "dynamic-tool") && "output" in part) {
|
|
35
|
+
const output = part.output;
|
|
36
|
+
if (output !== void 0) {
|
|
37
|
+
const str = typeof output === "string" ? output : JSON.stringify(output);
|
|
38
|
+
if (str.length > maxToolOutput) {
|
|
39
|
+
changed = true;
|
|
40
|
+
return {
|
|
41
|
+
...part,
|
|
42
|
+
output: `${str.slice(0, maxToolOutput)}... [truncated ${str.length} chars]`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (part.type === "text" && "text" in part) {
|
|
48
|
+
const text = part.text;
|
|
49
|
+
if (text.length > maxText) {
|
|
50
|
+
changed = true;
|
|
51
|
+
return {
|
|
52
|
+
...part,
|
|
53
|
+
text: `${text.slice(0, maxText)}... [truncated ${text.length} chars]`
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return part;
|
|
58
|
+
});
|
|
59
|
+
result.push(changed ? {
|
|
60
|
+
...msg,
|
|
61
|
+
parts: truncatedParts
|
|
62
|
+
} : msg);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { CHARS_PER_TOKEN, COMPACTION_PREFIX, TOKENS_PER_MESSAGE, WORDS_TOKEN_MULTIPLIER, alignBoundaryBackward, alignBoundaryForward, buildSummaryPrompt, computeSummaryBudget, createCompactFunction, estimateMessageTokens, estimateStringTokens, findTailCutByTokens, isCompactionMessage, sanitizeToolPairs, truncateOlderMessages };
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/experimental/memory/utils/compaction.ts"],"sourcesContent":["/**\n * Read-time context truncation.\n *\n * Truncates older tool outputs and long text before sending to the LLM.\n * Does NOT mutate stored messages — operates on a copy.\n */\n\nimport type { UIMessage } from \"ai\";\n\nexport interface TruncateOptions {\n /** Number of recent messages to keep intact (default: 4) */\n keepRecent?: number;\n /** Max chars for tool outputs in older messages (default: 500) */\n maxToolOutputChars?: number;\n /** Max chars for text parts in older messages (default: 10000) */\n maxTextChars?: number;\n}\n\n/**\n * Truncate tool outputs and long text in older messages.\n * Returns a new array — input messages are not mutated.\n *\n * Recent messages (last `keepRecent`) are left intact.\n * Older messages get tool outputs and long text truncated.\n *\n * Use in assembleContext() before sending to the LLM:\n * ```typescript\n * async assembleContext() {\n * const history = this.sessions.getHistory(this._sessionId);\n * const truncated = truncateOlderMessages(history);\n * return convertToModelMessages(truncated);\n * }\n * ```\n */\nexport function truncateOlderMessages(\n messages: UIMessage[],\n options?: TruncateOptions\n): UIMessage[] {\n const keepRecent = options?.keepRecent ?? 4;\n const maxToolOutput = options?.maxToolOutputChars ?? 500;\n const maxText = options?.maxTextChars ?? 10000;\n\n if (messages.length <= keepRecent) return messages;\n\n const cutoff = messages.length - keepRecent;\n const result: UIMessage[] = [];\n\n for (let i = 0; i < messages.length; i++) {\n if (i >= cutoff) {\n result.push(messages[i]);\n continue;\n }\n\n const msg = messages[i];\n let changed = false;\n\n const truncatedParts = msg.parts.map((part) => {\n // Truncate tool outputs\n if (\n (part.type.startsWith(\"tool-\") || part.type === \"dynamic-tool\") &&\n \"output\" in part\n ) {\n const output = (part as { output?: unknown }).output;\n if (output !== undefined) {\n const str =\n typeof output === \"string\" ? output : JSON.stringify(output);\n if (str.length > maxToolOutput) {\n changed = true;\n return {\n ...part,\n output: `${str.slice(0, maxToolOutput)}... [truncated ${str.length} chars]`\n };\n }\n }\n }\n\n // Truncate long text\n if (part.type === \"text\" && \"text\" in part) {\n const text = (part as { text: string }).text;\n if (text.length > maxText) {\n changed = true;\n return {\n ...part,\n text: `${text.slice(0, maxText)}... [truncated ${text.length} chars]`\n };\n }\n }\n\n return part;\n });\n\n result.push(\n changed ? ({ ...msg, parts: truncatedParts } as UIMessage) : msg\n );\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkCA,SAAgB,sBACd,UACA,SACa;CACb,MAAM,aAAa,SAAS,cAAc;CAC1C,MAAM,gBAAgB,SAAS,sBAAsB;CACrD,MAAM,UAAU,SAAS,gBAAgB;AAEzC,KAAI,SAAS,UAAU,WAAY,QAAO;CAE1C,MAAM,SAAS,SAAS,SAAS;CACjC,MAAM,SAAsB,EAAE;AAE9B,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,MAAI,KAAK,QAAQ;AACf,UAAO,KAAK,SAAS,GAAG;AACxB;;EAGF,MAAM,MAAM,SAAS;EACrB,IAAI,UAAU;EAEd,MAAM,iBAAiB,IAAI,MAAM,KAAK,SAAS;AAE7C,QACG,KAAK,KAAK,WAAW,QAAQ,IAAI,KAAK,SAAS,mBAChD,YAAY,MACZ;IACA,MAAM,SAAU,KAA8B;AAC9C,QAAI,WAAW,KAAA,GAAW;KACxB,MAAM,MACJ,OAAO,WAAW,WAAW,SAAS,KAAK,UAAU,OAAO;AAC9D,SAAI,IAAI,SAAS,eAAe;AAC9B,gBAAU;AACV,aAAO;OACL,GAAG;OACH,QAAQ,GAAG,IAAI,MAAM,GAAG,cAAc,CAAC,iBAAiB,IAAI,OAAO;OACpE;;;;AAMP,OAAI,KAAK,SAAS,UAAU,UAAU,MAAM;IAC1C,MAAM,OAAQ,KAA0B;AACxC,QAAI,KAAK,SAAS,SAAS;AACzB,eAAU;AACV,YAAO;MACL,GAAG;MACH,MAAM,GAAG,KAAK,MAAM,GAAG,QAAQ,CAAC,iBAAiB,KAAK,OAAO;MAC9D;;;AAIL,UAAO;IACP;AAEF,SAAO,KACL,UAAW;GAAE,GAAG;GAAK,OAAO;GAAgB,GAAiB,IAC9D;;AAGH,QAAO"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { n as AgentEmail } from "./internal_context-
|
|
2
|
-
import { t as RetryOptions } from "./retries-
|
|
1
|
+
import { n as AgentEmail } from "./internal_context-DT8RxmAN.js";
|
|
2
|
+
import { t as RetryOptions } from "./retries-DXMQGhG3.js";
|
|
3
3
|
import {
|
|
4
4
|
n as Observability,
|
|
5
5
|
r as ObservabilityEvent,
|
|
6
6
|
s as MCPObservabilityEvent
|
|
7
|
-
} from "./index-
|
|
8
|
-
import { t as AgentMcpOAuthProvider } from "./do-oauth-client-provider-
|
|
7
|
+
} from "./index-Ua2Nfvbm.js";
|
|
8
|
+
import { t as AgentMcpOAuthProvider } from "./do-oauth-client-provider-C2jurFjW.js";
|
|
9
9
|
import {
|
|
10
10
|
_ as WorkflowPage,
|
|
11
11
|
g as WorkflowInfo,
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
l as WorkflowCallback,
|
|
14
14
|
s as RunWorkflowOptions,
|
|
15
15
|
y as WorkflowQueryCriteria
|
|
16
|
-
} from "./workflow-types-
|
|
17
|
-
import { t as MessageType } from "./types-
|
|
18
|
-
import { r as EmailResolver } from "./email-
|
|
16
|
+
} from "./workflow-types-CZNXKj_D.js";
|
|
17
|
+
import { t as MessageType } from "./types-C-m0II8i.js";
|
|
18
|
+
import { r as EmailResolver } from "./email-DwPlM0bQ.js";
|
|
19
19
|
import {
|
|
20
20
|
Connection,
|
|
21
21
|
Connection as Connection$1,
|
|
@@ -56,8 +56,8 @@ import {
|
|
|
56
56
|
Tool
|
|
57
57
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
58
58
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
59
|
-
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
60
59
|
import { ToolSet } from "ai";
|
|
60
|
+
import { RequestOptions } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
61
61
|
import {
|
|
62
62
|
Transport,
|
|
63
63
|
TransportSendOptions
|
|
@@ -93,7 +93,7 @@ interface CORSOptions {
|
|
|
93
93
|
interface ServeOptions {
|
|
94
94
|
binding?: string;
|
|
95
95
|
corsOptions?: CORSOptions;
|
|
96
|
-
transport?:
|
|
96
|
+
transport?: TransportType;
|
|
97
97
|
jurisdiction?: DurableObjectJurisdiction;
|
|
98
98
|
}
|
|
99
99
|
type McpClientOptions = ConstructorParameters<typeof Client$1>[1];
|
|
@@ -720,7 +720,7 @@ declare class MCPClientConnection {
|
|
|
720
720
|
*/
|
|
721
721
|
getTransport(
|
|
722
722
|
transportType: BaseTransportType
|
|
723
|
-
):
|
|
723
|
+
): StreamableHTTPClientTransport | SSEClientTransport | RPCClientTransport;
|
|
724
724
|
private tryConnect;
|
|
725
725
|
private _capabilityErrorHandler;
|
|
726
726
|
}
|
|
@@ -2865,4 +2865,4 @@ export {
|
|
|
2865
2865
|
Schedule as y,
|
|
2866
2866
|
MCPServerOptions as z
|
|
2867
2867
|
};
|
|
2868
|
-
//# sourceMappingURL=index-
|
|
2868
|
+
//# sourceMappingURL=index-C-6EMK-E.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as __DO_NOT_USE_WILL_BREAK__agentContext } from "./internal_context-
|
|
2
|
-
import { t as RetryOptions } from "./retries-
|
|
1
|
+
import { r as __DO_NOT_USE_WILL_BREAK__agentContext } from "./internal_context-DT8RxmAN.js";
|
|
2
|
+
import { t as RetryOptions } from "./retries-DXMQGhG3.js";
|
|
3
3
|
import {
|
|
4
4
|
A as routeAgentRequest,
|
|
5
5
|
C as SubAgentClass,
|
|
@@ -33,13 +33,14 @@ import {
|
|
|
33
33
|
w as SubAgentStub,
|
|
34
34
|
x as StateUpdateMessage,
|
|
35
35
|
y as Schedule
|
|
36
|
-
} from "./index-
|
|
36
|
+
} from "./index-C-6EMK-E.js";
|
|
37
37
|
import {
|
|
38
38
|
n as AgentsOAuthProvider,
|
|
39
39
|
r as DurableObjectOAuthClientProvider,
|
|
40
40
|
t as AgentMcpOAuthProvider
|
|
41
|
-
} from "./do-oauth-client-provider-
|
|
42
|
-
import {
|
|
41
|
+
} from "./do-oauth-client-provider-C2jurFjW.js";
|
|
42
|
+
import { t as MessageType } from "./types-C-m0II8i.js";
|
|
43
|
+
import { l as createHeaderBasedEmailResolver } from "./email-DwPlM0bQ.js";
|
|
43
44
|
export {
|
|
44
45
|
AddMcpServerOptions,
|
|
45
46
|
AddRpcMcpServerOptions,
|
|
@@ -59,6 +60,7 @@ export {
|
|
|
59
60
|
MCPServer,
|
|
60
61
|
MCPServerMessage,
|
|
61
62
|
MCPServersState,
|
|
63
|
+
MessageType,
|
|
62
64
|
QueueItem,
|
|
63
65
|
RPCRequest,
|
|
64
66
|
RPCResponse,
|
package/dist/index.js
CHANGED
|
@@ -3048,6 +3048,6 @@ var StreamingResponse = class {
|
|
|
3048
3048
|
}
|
|
3049
3049
|
};
|
|
3050
3050
|
//#endregion
|
|
3051
|
-
export { Agent, DEFAULT_AGENT_STATIC_OPTIONS, DurableObjectOAuthClientProvider, SqlError, StreamingResponse, __DO_NOT_USE_WILL_BREAK__agentContext, callable, createHeaderBasedEmailResolver, getAgentByName, getCurrentAgent, routeAgentEmail, routeAgentRequest, unstable_callable };
|
|
3051
|
+
export { Agent, DEFAULT_AGENT_STATIC_OPTIONS, DurableObjectOAuthClientProvider, MessageType, SqlError, StreamingResponse, __DO_NOT_USE_WILL_BREAK__agentContext, callable, createHeaderBasedEmailResolver, getAgentByName, getCurrentAgent, routeAgentEmail, routeAgentRequest, unstable_callable };
|
|
3052
3052
|
|
|
3053
3053
|
//# sourceMappingURL=index.js.map
|