agents 0.24.0 → 0.25.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.
- package/dist/{agent-routing-B2XLNMxq.d.ts → agent-routing-0DR0Tk2v.d.ts} +205 -132
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-routing.js.map +1 -1
- package/dist/agent-tool-types.d.ts +28 -22
- package/dist/agent-tool-types.js +23 -1
- package/dist/agent-tool-types.js.map +1 -1
- package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
- package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
- package/dist/{agent-tools-Dj5Mzlw7.d.ts → agent-tools-YrATZJu2.d.ts} +34 -20
- package/dist/agent-tools.d.ts +20 -18
- package/dist/agent-tools.js.map +1 -1
- package/dist/ai-fm7xx1BK.js +312 -0
- package/dist/ai-fm7xx1BK.js.map +1 -0
- package/dist/base64-redaction-C1Uu29t5.js +59 -0
- package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
- package/dist/browser/ai-sdk.d.ts +90 -0
- package/dist/browser/ai-sdk.js +51 -0
- package/dist/browser/ai-sdk.js.map +1 -0
- package/dist/browser/ai.d.ts +4 -4
- package/dist/browser/ai.js +1 -347
- package/dist/browser/index.d.ts +81 -79
- package/dist/browser/index.js +361 -2
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/tanstack-ai.d.ts +96 -1
- package/dist/browser/tanstack-ai.js +62 -2
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/browser-DE3d-RLk.d.ts +275 -0
- package/dist/browser-tool-CQ10e6Kj.js +480 -0
- package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
- package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
- package/dist/capability-B4WbF81e.js.map +1 -1
- package/dist/{capability-runner-BUBa6Ake.d.ts → capability-runner-DBAnGr6L.d.ts} +7 -2
- package/dist/chat/index.d.ts +340 -234
- package/dist/chat/index.js +592 -66
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +42 -2
- package/dist/chat/react.js +191 -35
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.d.ts +1 -1
- package/dist/chat/transport.js +1 -1
- package/dist/chat-sdk/index.d.ts +17 -3
- package/dist/chat-sdk/index.js +28 -2
- package/dist/chat-sdk/index.js.map +1 -1
- package/dist/{client-C9DAjZIY.js → client-4OlMIJx-.js} +102 -29
- package/dist/client-4OlMIJx-.js.map +1 -0
- package/dist/{client-BxdkCuVy.d.ts → client-je3koD9Z.d.ts} +45 -10
- package/dist/client.d.ts +7 -5
- package/dist/client.js +2 -2
- package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
- package/dist/connector-58XDGvc3.js.map +1 -0
- package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
- package/dist/context/index.d.ts +29 -0
- package/dist/context/index.js +64 -17
- package/dist/context/index.js.map +1 -1
- package/dist/current-agent-DhoDkSnH.js.map +1 -1
- package/dist/{current-agent-C4w86kcN.d.ts → current-agent-vAk_5JMt.d.ts} +4 -4
- package/dist/{durable-object-lifecycle-ei7M7lqB.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +15 -7
- package/dist/harnesses/pi/index.d.ts +348 -0
- package/dist/harnesses/pi/index.js +754 -0
- package/dist/harnesses/pi/index.js.map +1 -0
- package/dist/images-iiTDSyRu.js +1631 -0
- package/dist/images-iiTDSyRu.js.map +1 -0
- package/dist/{index-Dlgtd3Mj.d.ts → index-BreL0YrQ.d.ts} +2 -2
- package/dist/index-kbSUIer3.d.ts +343 -0
- package/dist/index.d.ts +46 -44
- package/dist/index.js +1 -1
- package/dist/internal_context.d.ts +1 -1
- package/dist/lifecycle/index.d.ts +3 -3
- package/dist/lifecycle/index.js +1 -1
- package/dist/{lifecycle-Mm_jQh7r.js → lifecycle-CFu6OV03.js} +39 -10
- package/dist/{lifecycle-Mm_jQh7r.js.map → lifecycle-CFu6OV03.js.map} +1 -1
- package/dist/mcp/client/index.d.ts +20 -20
- package/dist/mcp/client/x402.js +21 -19
- package/dist/mcp/client/x402.js.map +1 -1
- package/dist/mcp/index.d.ts +35 -35
- package/dist/mcp/index.js +1 -1
- package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
- package/dist/message-builder-DAl9AhHH.js.map +1 -0
- package/dist/models/ai-sdk/index.d.ts +353 -0
- package/dist/models/ai-sdk/index.js +2443 -0
- package/dist/models/ai-sdk/index.js.map +1 -0
- package/dist/models/pi-ai/index.d.ts +313 -0
- package/dist/models/pi-ai/index.js +1795 -0
- package/dist/models/pi-ai/index.js.map +1 -0
- package/dist/observability/ai/index.js +20 -4
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
- package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
- package/dist/queue/index.d.ts +3 -3
- package/dist/queue/index.js +1 -1
- package/dist/{queue-BC9F5cUo.js → queue-BB-zfIYF.js} +2 -2
- package/dist/{queue-BC9F5cUo.js.map → queue-BB-zfIYF.js.map} +1 -1
- package/dist/react.d.ts +6 -6
- package/dist/react.js +160 -64
- package/dist/react.js.map +1 -1
- package/dist/{retries-D9B2UCq3.d.ts → retries-CV6AkXmQ.d.ts} +18 -9
- package/dist/retries.d.ts +11 -9
- package/dist/retries.js +11 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +2 -2
- package/dist/routing/index.js +1 -1
- package/dist/{scheduler-D_KHqxBa.js → scheduler-DZ3xQfeK.js} +2 -2
- package/dist/{scheduler-D_KHqxBa.js.map → scheduler-DZ3xQfeK.js.map} +1 -1
- package/dist/{scheduler-BGq6M5Kd.d.ts → scheduler-RdACNXDP.d.ts} +3 -3
- package/dist/schedules/index.d.ts +1 -1
- package/dist/schedules/index.js +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +70 -3
- package/dist/sessions/index.js +276 -67
- package/dist/sessions/index.js.map +1 -1
- package/dist/skills/index.d.ts +29 -314
- package/dist/socket-address.d.ts +16 -0
- package/dist/socket-address.js +19 -0
- package/dist/socket-address.js.map +1 -0
- package/dist/{src-BNU3ZiJM.js → src-BZeu-SCD.js} +423 -115
- package/dist/src-BZeu-SCD.js.map +1 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/index.js +1 -1
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.js +1 -1
- package/dist/{streams-BcE8JP2X.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
- package/dist/{streams-CCPRV6dt.js → streams-DW-UCOUc.js} +9 -2
- package/dist/streams-DW-UCOUc.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/tasks/index.d.ts +1 -1
- package/dist/tasks/index.js +1 -1
- package/dist/{tasks-D4nLqVSI.js → tasks-68DESxf0.js} +2 -2
- package/dist/{tasks-D4nLqVSI.js.map → tasks-68DESxf0.js.map} +1 -1
- package/dist/{tasks-V2lCT8ZV.d.ts → tasks-9YxYtByh.d.ts} +2 -2
- package/dist/transport-PfFnt-Ld.d.ts +525 -0
- package/dist/{types-DCD6BNZH.d.ts → types-BA4anrjI.d.ts} +2 -2
- package/dist/voice/index.d.ts +2 -2
- package/dist/websockets/index.d.ts +23 -6
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-D7IzWbZb.js → websockets-Ci-LZFHH.js} +45 -20
- package/dist/{websockets-D7IzWbZb.js.map → websockets-Ci-LZFHH.js.map} +1 -1
- package/dist/wire-types-CnMt6_HR.js.map +1 -1
- package/dist/wire-types-CrjKvDyi.d.ts +300 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +21 -21
- package/dist/workflows.js +1 -1
- package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
- package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
- package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
- package/docs/agent-tools.md +29 -4
- package/docs/browse-the-web.md +87 -5
- package/docs/chat-agents.md +124 -16
- package/docs/context.md +27 -0
- package/docs/durable-execution.md +2 -0
- package/docs/harnesses/pi.md +91 -0
- package/docs/human-in-the-loop.md +4 -0
- package/docs/index.md +3 -0
- package/docs/lifecycle.md +20 -4
- package/docs/models-pi-ai.md +325 -0
- package/docs/models.md +504 -0
- package/docs/resumable-streaming.md +2 -0
- package/docs/routing.md +13 -13
- package/docs/sessions.md +36 -1
- package/docs/state.md +1 -0
- package/docs/sub-agents.md +9 -0
- package/package.json +40 -2
- package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
- package/dist/browser/ai.js.map +0 -1
- package/dist/client-C9DAjZIY.js.map +0 -1
- package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
- package/dist/connector-CptFKzRh.js.map +0 -1
- package/dist/message-builder-BymO4N_D.js.map +0 -1
- package/dist/src-BNU3ZiJM.js.map +0 -1
- package/dist/streams-CCPRV6dt.js.map +0 -1
- package/dist/ws-chat-transport-rWwta645.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/context/search.ts","../../src/context/blocks.ts","../../src/context/sqlite-provider.ts"],"sourcesContent":["/**\n * Search Provider — full-text searchable context blocks.\n *\n * Extends ContextProvider with `search()` for querying indexed content\n * and a keyed `set()` for indexing individual entries.\n *\n * Duck-typed: if a provider has a `search` method, it's a SearchProvider.\n */\n\nimport type { ContextProvider } from \"./blocks\";\nimport type { SqlProvider } from \"./sqlite-provider\";\n\n/**\n * Storage interface for searchable context.\n *\n * - `get()` returns a summary of indexed content (rendered into system prompt)\n * - `search(query)` full-text search (via search_context tool)\n * - `set(key, content)` indexes content under a key (via set_context tool)\n */\nexport interface SearchProvider extends ContextProvider {\n search(query: string): Promise<string | null>;\n set?(key: string, content: string): Promise<void>;\n}\n\n/**\n * Check if a provider is a SearchProvider (has a `search` method).\n */\nexport function isSearchProvider(\n provider: unknown\n): provider is SearchProvider {\n return (\n typeof provider === \"object\" &&\n provider !== null &&\n \"search\" in provider &&\n typeof (provider as SearchProvider).search === \"function\"\n );\n}\n\n// ── Agent Search Provider (DO SQLite FTS5) ─────────────────────────\n\n/**\n * SearchProvider backed by Durable Object SQLite with FTS5.\n *\n * - `get()` returns a count of indexed entries\n * - `search(query)` full-text search using FTS5\n * - `set(key, content)` indexes or replaces content under a key\n *\n * Entries live in one namespaced FTS5 table, separate from the session\n * message index.\n *\n * @example\n * ```ts\n * const context = new ContextBlocks([\n * { label: \"knowledge\", provider: new AgentSearchProvider(this) }\n * ]);\n * ```\n */\nexport class AgentSearchProvider implements SearchProvider {\n private agent: SqlProvider;\n private label = \"\";\n private initialized = false;\n\n constructor(agent: SqlProvider) {\n this.agent = agent;\n }\n\n init(label: string): void {\n this.label = label;\n }\n\n private ensureTable(): void {\n if (this.initialized) return;\n // The FTS table is the only store: a mirror row table would double the\n // billed writes of every indexed entry to serve a count and a lookup the\n // index already answers.\n this.agent.sql`\n CREATE VIRTUAL TABLE IF NOT EXISTS cf_agents_search_fts\n USING fts5(\n label UNINDEXED,\n key UNINDEXED,\n content,\n tokenize='porter unicode61'\n )\n `;\n this.agent.sql`DROP TABLE IF EXISTS cf_agents_search_entries`;\n this.initialized = true;\n }\n\n async get(): Promise<string | null> {\n this.ensureTable();\n const rows = this.agent.sql<{ count: number }>`\n SELECT COUNT(*) as count FROM cf_agents_search_fts\n WHERE label = ${this.label}\n `;\n const count = rows[0]?.count ?? 0;\n if (count === 0) return null;\n return `${count} entries indexed.`;\n }\n\n async search(query: string): Promise<string | null> {\n this.ensureTable();\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 null;\n try {\n const rows = this.agent.sql<{\n key: string;\n content: string;\n }>`\n SELECT f.key, f.content\n FROM cf_agents_search_fts f\n WHERE cf_agents_search_fts MATCH ${sanitized}\n AND f.label = ${this.label}\n ORDER BY rank\n LIMIT 10\n `;\n if (rows.length === 0) return null;\n return rows.map((r) => `[${r.key}]\\n${r.content}`).join(\"\\n\\n\");\n } catch {\n // Malformed FTS query\n return null;\n }\n }\n\n async set(key: string, content: string): Promise<void> {\n this.ensureTable();\n this.agent.sql`\n DELETE FROM cf_agents_search_fts\n WHERE label = ${this.label} AND key = ${key}\n `;\n this.agent.sql`\n INSERT INTO cf_agents_search_fts (label, key, content)\n VALUES (${this.label}, ${key}, ${content})\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 * - SearchProvider (get+search+set?) → searchable via search_context tool\n */\n\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport { estimateStringTokens } from \"../sessions/tokens\";\nimport { isSearchProvider, type SearchProvider } from \"./search\";\n\nfunction slugify(text: string): string {\n return text\n .slice(0, 60)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction stableHash(text: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(36);\n}\n\nfunction contextEntryKey(metadataTitle: string | undefined, content: string) {\n if (metadataTitle?.trim()) {\n const slug = slugify(metadataTitle);\n return slug || `entry-${stableHash(metadataTitle)}`;\n }\n\n const slug = slugify(content) || \"entry\";\n return `${slug}-${stableHash(content)}`;\n}\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 /** Called by the context system to provide the block label before first use. */\n init?(label: string): void;\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 * - SearchProvider (get+search+set?) → searchable via search_context\n * If omitted, auto-wired to writable SQLite when using builder. */\n provider?: ContextProvider | WritableContextProvider | SearchProvider;\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 SearchProvider */\n isSearchable: 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 private defaultProvider: ((label: string) => ContextProvider) | null;\n\n /**\n * @param configs Blocks to load on first use.\n * @param promptStore Persists the frozen system prompt, keeping the\n * provider's prefix cache warm across wakes.\n * @param defaultProvider Supplies storage for blocks declared without a\n * provider, so a host can offer durable writable blocks by label alone.\n */\n constructor(\n configs: ContextConfig[],\n promptStore?: WritableContextProvider,\n defaultProvider?: (label: string) => ContextProvider\n ) {\n this.configs = configs;\n this.promptStore = promptStore ?? null;\n this.defaultProvider = defaultProvider ?? null;\n }\n\n /** Fill in the host's storage for a block declared without a provider. */\n private withDefaultProvider(config: ContextConfig): ContextConfig {\n if (config.provider || !this.defaultProvider) return config;\n return { ...config, provider: this.defaultProvider(config.label) };\n }\n\n /**\n * Load all blocks from their providers. Hosts call this once at startup;\n * every other entry point loads lazily.\n */\n async load(): Promise<void> {\n this.configs = this.configs.map((config) =>\n this.withDefaultProvider(config)\n );\n for (const config of this.configs) {\n this.blocks.set(config.label, await this.loadBlock(config));\n }\n this.loaded = true;\n }\n\n /** Initialize a block's provider and read its current content. */\n private async loadBlock(config: ContextConfig): Promise<ContextBlock> {\n const provider = config.provider;\n provider?.init?.(config.label);\n const content = provider ? ((await provider.get()) ?? \"\") : \"\";\n const searchable = isSearchProvider(provider);\n return {\n label: config.label,\n description: config.description,\n content,\n tokens: estimateStringTokens(content),\n maxTokens: config.maxTokens,\n writable: isWritableProvider(provider) || (searchable && !!provider?.set),\n isSearchable: searchable\n };\n }\n\n /**\n * Dynamically register a new context block after initialization.\n * Used by extensions to contribute context at runtime.\n *\n * If blocks have already been loaded, the new block's provider is\n * initialized and loaded immediately. The snapshot is NOT updated\n * automatically — call `refreshSystemPrompt()` to rebuild.\n */\n async addBlock(input: ContextConfig): Promise<ContextBlock> {\n if (!this.loaded) await this.load();\n\n if (this.configs.some((c) => c.label === input.label)) {\n throw new Error(`Block \"${input.label}\" already exists`);\n }\n\n const config = this.withDefaultProvider(input);\n this.configs.push(config);\n const block = await this.loadBlock(config);\n this.blocks.set(config.label, block);\n return block;\n }\n\n /**\n * Remove a dynamically registered context block.\n * Used during extension unload cleanup.\n *\n * Returns true if the block existed and was removed.\n * The snapshot is NOT updated automatically — call\n * `refreshSystemPrompt()` to rebuild.\n */\n removeBlock(label: string): boolean {\n const idx = this.configs.findIndex((c) => c.label === label);\n if (idx === -1) return false;\n\n this.configs.splice(idx, 1);\n this.blocks.delete(label);\n return 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.isSearchable) {\n throw new Error(\n `Block \"${label}\" is a keyed provider. Use setSearchEntry() 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 isSearchable: 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 /** Index a search entry within a searchable block. */\n private async setSearchEntry(\n label: string,\n key: string,\n content: 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?.isSearchable) {\n throw new Error(`Block \"${label}\" is not a search provider`);\n }\n\n const provider = config?.provider;\n if (!provider || !isSearchProvider(provider) || !provider.set) {\n throw new Error(`Block \"${label}\" does not support writes`);\n }\n\n await provider.set(key, content);\n\n // Refresh summary\n const summary = await provider.get();\n existing.content = summary ?? \"\";\n existing.tokens = estimateStringTokens(existing.content);\n }\n\n /** Search a searchable block. */\n private async searchContext(\n label: string,\n query: string\n ): 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 || !isSearchProvider(config.provider)) {\n throw new Error(`Block \"${label}\" is not a search provider`);\n }\n\n return config.provider.search(query);\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 const needsSep = existing.content.length > 0 && !content.startsWith(\"\\n\");\n return this.setBlock(\n label,\n existing.content + (needsSep ? \"\\n\" : \"\") + content\n );\n }\n\n private renderPrompt(): string {\n const parts: string[] = [];\n const sep = \"═\".repeat(46);\n\n for (const block of this.blocks.values()) {\n // Skip empty readonly blocks — writable and searchable blocks always\n // render so the LLM knows which tools can address them.\n if (!block.content && !block.writable && !block.isSearchable) 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.isSearchable) header += \" [searchable]\";\n else if (!block.writable) header += \" [readonly]\";\n else header += \" [writable]\";\n\n parts.push(`${sep}\\n${header}\\n${sep}\\n${block.content}`);\n }\n\n return parts.join(\"\\n\\n\");\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n /**\n * The frozen system prompt. The first call renders the blocks and stores\n * the result; every later call returns that same string, so the model\n * provider's prefix cache stays warm. Block edits do not change it until\n * `refreshSystemPrompt()` is called.\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 if (this.snapshot !== null) return this.snapshot;\n\n if (!this.loaded) await this.load();\n this.snapshot = this.renderPrompt();\n await this.promptStore?.set(this.snapshot);\n return this.snapshot;\n }\n\n /**\n * Reload every block from its provider, re-render the system prompt, and\n * persist it. Use this after block content has changed.\n */\n async refreshSystemPrompt(): Promise<string> {\n this.loaded = false;\n await this.load();\n this.snapshot = this.renderPrompt();\n await this.promptStore?.set(this.snapshot);\n return this.snapshot;\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 * - `search_context` — when any block is a search provider\n */\n async tools(): Promise<ToolSet> {\n if (!this.loaded) await this.load();\n\n const blocks = Array.from(this.blocks.values());\n const writable = blocks.filter((b) => b.writable);\n const searchLabels = blocks\n .filter((b) => b.isSearchable)\n .map((b) => b.label);\n const toolSet: ToolSet = {};\n\n // ── set_context ──────────────────────────────────────────────\n\n if (writable.length > 0) {\n const blockDescriptions = writable.map((b) => {\n const kind = b.isSearchable ? \"searchable, keyed entries\" : \"writable\";\n return `- \"${b.label}\" (${kind}): ${b.description ?? \"no description\"}`;\n });\n const keyedBlocks = writable.filter((b) => b.isSearchable);\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: \"The main content to write to the block.\"\n },\n action: {\n type: \"string\" as const,\n enum: [\"replace\", \"append\"],\n description: \"replace (default) or append\"\n }\n };\n\n if (keyedBlocks.length > 0) {\n properties.metadata = {\n type: \"object\" as const,\n description:\n \"Optional metadata for keyed entries (searchable blocks: \" +\n keyedBlocks.map((b) => `\"${b.label}\"`).join(\", \") +\n \"). A title keeps updates stable; a description helps the model \" +\n \"pick the right entry.\",\n properties: {\n title: {\n type: \"string\" as const,\n description:\n \"Short title. Used as a stable identifier — entries with the \" +\n \"same title are updated in place, different titles create new entries.\"\n },\n description: {\n type: \"string\" as const,\n description:\n \"One-line summary shown alongside the title in the system prompt \" +\n \"so the model can decide when to load the entry.\"\n }\n }\n };\n }\n\n const metadataHint =\n keyedBlocks.length > 0\n ? \"\\n\\nFor searchable blocks, pass `metadata: { title, description }` \" +\n \"— title stabilises updates, description helps the model pick \" +\n \"entries. Metadata is optional.\"\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.${metadataHint}`,\n inputSchema: z.fromJSONSchema({\n type: \"object\" as const,\n properties: properties as Record<string, Record<string, unknown>>,\n required: [\"label\", \"content\"]\n }),\n execute: async ({\n label,\n content,\n metadata,\n action\n }: {\n label: string;\n content: string;\n metadata?: { title?: string; 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.isSearchable) {\n const key = contextEntryKey(metadata?.title, content);\n await this.setSearchEntry(label, key, content);\n return `Indexed \"${key}\" in ${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 // ── search_context ────────────────────────────────────────────\n\n if (searchLabels.length > 0) {\n toolSet.search_context = {\n description:\n \"Search for information in a searchable context block. \" +\n \"ONLY these blocks are searchable: \" +\n searchLabels.map((l) => `\"${l}\"`).join(\", \") +\n \". Other blocks cannot be searched.\",\n inputSchema: z.fromJSONSchema({\n type: \"object\" as const,\n properties: {\n label: {\n type: \"string\" as const,\n enum: searchLabels,\n description: \"Searchable block label\"\n },\n query: {\n type: \"string\" as const,\n description: \"Search query\"\n }\n },\n required: [\"label\", \"query\"]\n }),\n execute: async ({ label, query }: { label: string; query: string }) => {\n try {\n if (!searchLabels.includes(label)) {\n return `Error: \"${label}\" is not searchable. Searchable blocks: ${searchLabels.join(\", \")}`;\n }\n const results = await this.searchContext(label, query);\n return results ?? \"No results found.\";\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 * 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 { WritableContextProvider } from \"./blocks\";\n\n/** Minimal tagged-template SQL surface used by SQLite context providers. */\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/** SQLite-backed writable context block provider. */\nexport class AgentContextProvider implements WritableContextProvider {\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 init(label: string): void {\n if (!this.label) {\n this.label = label;\n }\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"],"mappings":";;;;;;AA2BA,SAAgB,iBACd,UAC4B;CAC5B,OACE,OAAO,aAAa,YACpB,aAAa,QACb,YAAY,YACZ,OAAQ,SAA4B,WAAW;AAEnD;;;;;;;;;;;;;;;;;;AAqBA,IAAa,sBAAb,MAA2D;CAKzD,YAAY,OAAoB;EAHhC,KAAQ,QAAQ;EAChB,KAAQ,cAAc;EAGpB,KAAK,QAAQ;CACf;CAEA,KAAK,OAAqB;EACxB,KAAK,QAAQ;CACf;CAEA,cAA4B;EAC1B,IAAI,KAAK,aAAa;EAItB,KAAK,MAAM,GAAG;;;;;;;;;EASd,KAAK,MAAM,GAAG;EACd,KAAK,cAAc;CACrB;CAEA,MAAM,MAA8B;EAClC,KAAK,YAAY;EAKjB,MAAM,QAAQ,KAJI,MAAM,GAAsB;;sBAE5B,KAAK,MAAM;MAEV,EAAE,EAAE,SAAS;EAChC,IAAI,UAAU,GAAG,OAAO;EACxB,OAAO,GAAG,MAAM;CAClB;CAEA,MAAM,OAAO,OAAuC;EAClD,KAAK,YAAY;EAGjB,MAAM,YAAY,MACf,MAAM,KAAK,CAAC,CACZ,OAAO,OAAO,CAAC,CACf,KAAK,MAAM,IAAI,EAAE,QAAQ,MAAM,MAAI,EAAE,EAAE,CAAC,CACxC,KAAK,GAAG;EACX,IAAI,CAAC,WAAW,OAAO;EACvB,IAAI;GACF,MAAM,OAAO,KAAK,MAAM,GAGtB;;;2CAGmC,UAAU;0BAC3B,KAAK,MAAM;;;;GAI/B,IAAI,KAAK,WAAW,GAAG,OAAO;GAC9B,OAAO,KAAK,KAAK,MAAM,IAAI,EAAE,IAAI,KAAK,EAAE,SAAS,CAAC,CAAC,KAAK,MAAM;EAChE,QAAQ;GAEN,OAAO;EACT;CACF;CAEA,MAAM,IAAI,KAAa,SAAgC;EACrD,KAAK,YAAY;EACjB,KAAK,MAAM,GAAG;;sBAEI,KAAK,MAAM,aAAa,IAAI;;EAE9C,KAAK,MAAM,GAAG;;gBAEF,KAAK,MAAM,IAAI,IAAI,IAAI,QAAQ;;CAE7C;AACF;;;ACvHA,SAAS,QAAQ,MAAsB;CACrC,OAAO,KACJ,MAAM,GAAG,EAAE,CAAC,CACZ,YAAY,CAAC,CACb,QAAQ,eAAe,GAAG,CAAC,CAC3B,QAAQ,UAAU,EAAE;AACzB;AAEA,SAAS,WAAW,MAAsB;CACxC,IAAI,OAAO;CACX,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,QAAQ,KAAK,WAAW,CAAC;EACzB,OAAO,KAAK,KAAK,MAAM,QAAU;CACnC;CACA,QAAQ,SAAS,EAAA,CAAG,SAAS,EAAE;AACjC;AAEA,SAAS,gBAAgB,eAAmC,SAAiB;CAC3E,IAAI,eAAe,KAAK,GAEtB,OADa,QAAQ,aACX,KAAK,SAAS,WAAW,aAAa;CAIlD,OAAO,GADM,QAAQ,OAAO,KAAK,QAClB,GAAG,WAAW,OAAO;AACtC;;;;AAuBA,SAAgB,mBACd,UACqC;CACrC,OACE,OAAO,aAAa,YACpB,aAAa,QACb,SAAS,YACT,OAAQ,SAAqC,QAAQ;AAEzD;;;;AAsCA,IAAa,gBAAb,MAA2B;;;;;;;;CAezB,YACE,SACA,aACA,iBACA;EAjBF,KAAQ,yBAAS,IAAI,IAA0B;EAC/C,KAAQ,WAA0B;EAClC,KAAQ,SAAS;EAgBf,KAAK,UAAU;EACf,KAAK,cAAc,eAAe;EAClC,KAAK,kBAAkB,mBAAmB;CAC5C;;CAGA,oBAA4B,QAAsC;EAChE,IAAI,OAAO,YAAY,CAAC,KAAK,iBAAiB,OAAO;EACrD,OAAO;GAAE,GAAG;GAAQ,UAAU,KAAK,gBAAgB,OAAO,KAAK;EAAE;CACnE;;;;;CAMA,MAAM,OAAsB;EAC1B,KAAK,UAAU,KAAK,QAAQ,KAAK,WAC/B,KAAK,oBAAoB,MAAM,CACjC;EACA,KAAK,MAAM,UAAU,KAAK,SACxB,KAAK,OAAO,IAAI,OAAO,OAAO,MAAM,KAAK,UAAU,MAAM,CAAC;EAE5D,KAAK,SAAS;CAChB;;CAGA,MAAc,UAAU,QAA8C;EACpE,MAAM,WAAW,OAAO;EACxB,UAAU,OAAO,OAAO,KAAK;EAC7B,MAAM,UAAU,WAAa,MAAM,SAAS,IAAI,KAAM,KAAM;EAC5D,MAAM,aAAa,iBAAiB,QAAQ;EAC5C,OAAO;GACL,OAAO,OAAO;GACd,aAAa,OAAO;GACpB;GACA,QAAQ,qBAAqB,OAAO;GACpC,WAAW,OAAO;GAClB,UAAU,mBAAmB,QAAQ,KAAM,cAAc,CAAC,CAAC,UAAU;GACrE,cAAc;EAChB;CACF;;;;;;;;;CAUA,MAAM,SAAS,OAA6C;EAC1D,IAAI,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK;EAElC,IAAI,KAAK,QAAQ,MAAM,MAAM,EAAE,UAAU,MAAM,KAAK,GAClD,MAAM,IAAI,MAAM,UAAU,MAAM,MAAM,iBAAiB;EAGzD,MAAM,SAAS,KAAK,oBAAoB,KAAK;EAC7C,KAAK,QAAQ,KAAK,MAAM;EACxB,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM;EACzC,KAAK,OAAO,IAAI,OAAO,OAAO,KAAK;EACnC,OAAO;CACT;;;;;;;;;CAUA,YAAY,OAAwB;EAClC,MAAM,MAAM,KAAK,QAAQ,WAAW,MAAM,EAAE,UAAU,KAAK;EAC3D,IAAI,QAAQ,IAAI,OAAO;EAEvB,KAAK,QAAQ,OAAO,KAAK,CAAC;EAC1B,KAAK,OAAO,OAAO,KAAK;EACxB,OAAO;CACT;;;;CAKA,SAAS,OAAoC;EAC3C,OAAO,KAAK,OAAO,IAAI,KAAK,KAAK;CACnC;;;;CAKA,YAA4B;EAC1B,OAAO,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC;CACxC;;;;;CAMA,MAAM,SAAS,OAAe,SAAwC;EACpE,IAAI,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK;EAClC,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,UAAU,KAAK;EACzD,MAAM,WAAW,KAAK,OAAO,IAAI,KAAK;EAEtC,IAAI,CAAC,UAAU,UACb,MAAM,IAAI,MAAM,UAAU,MAAM,cAAc;EAGhD,IAAI,SAAS,cACX,MAAM,IAAI,MACR,UAAU,MAAM,qDAClB;EAGF,MAAM,SAAS,qBAAqB,OAAO;EAC3C,MAAM,YAAY,QAAQ,aAAa,UAAU;EAEjD,IAAI,cAAc,KAAA,KAAa,SAAS,WACtC,MAAM,IAAI,MACR,UAAU,MAAM,uBAAuB,OAAO,KAAK,WACrD;EAGF,MAAM,QAAsB;GAC1B;GACA,aAAa,QAAQ,eAAe,UAAU;GAC9C;GACA;GACA;GACA,UAAU;GACV,cAAc;EAChB;EAEA,KAAK,OAAO,IAAI,OAAO,KAAK;EAG5B,IAAI,QAAQ,YAAY,mBAAmB,OAAO,QAAQ,GACxD,MAAM,OAAO,SAAS,IAAI,OAAO;EAGnC,OAAO;CACT;;CAGA,MAAc,eACZ,OACA,KACA,SACe;EACf,IAAI,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK;EAClC,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,UAAU,KAAK;EACzD,MAAM,WAAW,KAAK,OAAO,IAAI,KAAK;EAEtC,IAAI,CAAC,UAAU,cACb,MAAM,IAAI,MAAM,UAAU,MAAM,2BAA2B;EAG7D,MAAM,WAAW,QAAQ;EACzB,IAAI,CAAC,YAAY,CAAC,iBAAiB,QAAQ,KAAK,CAAC,SAAS,KACxD,MAAM,IAAI,MAAM,UAAU,MAAM,0BAA0B;EAG5D,MAAM,SAAS,IAAI,KAAK,OAAO;EAI/B,SAAS,UAAU,MADG,SAAS,IAAI,KACL;EAC9B,SAAS,SAAS,qBAAqB,SAAS,OAAO;CACzD;;CAGA,MAAc,cACZ,OACA,OACwB;EACxB,IAAI,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK;EAClC,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,UAAU,KAAK;EAEzD,IAAI,CAAC,QAAQ,YAAY,CAAC,iBAAiB,OAAO,QAAQ,GACxD,MAAM,IAAI,MAAM,UAAU,MAAM,2BAA2B;EAG7D,OAAO,OAAO,SAAS,OAAO,KAAK;CACrC;;;;CAKA,MAAM,cAAc,OAAe,SAAwC;EACzE,IAAI,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK;EAClC,MAAM,WAAW,KAAK,OAAO,IAAI,KAAK;EACtC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,UAAU,MAAM,YAAY;EAE9C,MAAM,WAAW,SAAS,QAAQ,SAAS,KAAK,CAAC,QAAQ,WAAW,IAAI;EACxE,OAAO,KAAK,SACV,OACA,SAAS,WAAW,WAAW,OAAO,MAAM,OAC9C;CACF;CAEA,eAA+B;EAC7B,MAAM,QAAkB,CAAC;EACzB,MAAM,MAAM,IAAI,OAAO,EAAE;EAEzB,KAAK,MAAM,SAAS,KAAK,OAAO,OAAO,GAAG;GAGxC,IAAI,CAAC,MAAM,WAAW,CAAC,MAAM,YAAY,CAAC,MAAM,cAAc;GAE9D,IAAI,SAAS,MAAM,MAAM,YAAY;GACrC,IAAI,MAAM,aAAa,UAAU,KAAK,MAAM,YAAY;GACxD,IAAI,MAAM,WAAW;IACnB,MAAM,MAAM,KAAK,MAAO,MAAM,SAAS,MAAM,YAAa,GAAG;IAC7D,UAAU,KAAK,IAAI,MAAM,MAAM,OAAO,GAAG,MAAM,UAAU;GAC3D;GACA,IAAI,MAAM,cAAc,UAAU;QAC7B,IAAI,CAAC,MAAM,UAAU,UAAU;QAC/B,UAAU;GAEf,MAAM,KAAK,GAAG,IAAI,IAAI,OAAO,IAAI,IAAI,IAAI,MAAM,SAAS;EAC1D;EAEA,OAAO,MAAM,KAAK,MAAM;CAC1B;;;;;;;CAUA,MAAM,qBAAsC;EAC1C,IAAI,KAAK,aAAa;GACpB,MAAM,SAAS,MAAM,KAAK,YAAY,IAAI;GAC1C,IAAI,WAAW,MAAM,OAAO;EAC9B;EACA,IAAI,KAAK,aAAa,MAAM,OAAO,KAAK;EAExC,IAAI,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK;EAClC,KAAK,WAAW,KAAK,aAAa;EAClC,MAAM,KAAK,aAAa,IAAI,KAAK,QAAQ;EACzC,OAAO,KAAK;CACd;;;;;CAMA,MAAM,sBAAuC;EAC3C,KAAK,SAAS;EACd,MAAM,KAAK,KAAK;EAChB,KAAK,WAAW,KAAK,aAAa;EAClC,MAAM,KAAK,aAAa,IAAI,KAAK,QAAQ;EACzC,OAAO,KAAK;CACd;;;;;;;;CASA,MAAM,QAA0B;EAC9B,IAAI,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK;EAElC,MAAM,SAAS,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC;EAC9C,MAAM,WAAW,OAAO,QAAQ,MAAM,EAAE,QAAQ;EAChD,MAAM,eAAe,OAClB,QAAQ,MAAM,EAAE,YAAY,CAAC,CAC7B,KAAK,MAAM,EAAE,KAAK;EACrB,MAAM,UAAmB,CAAC;EAI1B,IAAI,SAAS,SAAS,GAAG;GACvB,MAAM,oBAAoB,SAAS,KAAK,MAAM;IAC5C,MAAM,OAAO,EAAE,eAAe,8BAA8B;IAC5D,OAAO,MAAM,EAAE,MAAM,KAAK,KAAK,KAAK,EAAE,eAAe;GACvD,CAAC;GACD,MAAM,cAAc,SAAS,QAAQ,MAAM,EAAE,YAAY;GAEzD,MAAM,aAAsC;IAC1C,OAAO;KACL,MAAM;KACN,MAAM,SAAS,KAAK,MAAM,EAAE,KAAK;KACjC,aAAa;IACf;IACA,SAAS;KACP,MAAM;KACN,aAAa;IACf;IACA,QAAQ;KACN,MAAM;KACN,MAAM,CAAC,WAAW,QAAQ;KAC1B,aAAa;IACf;GACF;GAEA,IAAI,YAAY,SAAS,GACvB,WAAW,WAAW;IACpB,MAAM;IACN,aACE,6DACA,YAAY,KAAK,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,IAAI,IAChD;IAEF,YAAY;KACV,OAAO;MACL,MAAM;MACN,aACE;KAEJ;KACA,aAAa;MACX,MAAM;MACN,aACE;KAEJ;IACF;GACF;GAGF,MAAM,eACJ,YAAY,SAAS,IACjB,mKAGA;GAEN,QAAQ,cAAc;IACpB,aAAa,gDAAgD,kBAAkB,KAAK,IAAI,EAAE,qDAAqD;IAC/I,aAAa,EAAE,eAAe;KAC5B,MAAM;KACM;KACZ,UAAU,CAAC,SAAS,SAAS;IAC/B,CAAC;IACD,SAAS,OAAO,EACd,OACA,SACA,UACA,aAMI;KACJ,IAAI;MACF,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK;MACnC,IAAI,CAAC,OAAO,OAAO,iBAAiB,MAAM;MAE1C,IAAI,MAAM,cAAc;OACtB,MAAM,MAAM,gBAAgB,UAAU,OAAO,OAAO;OACpD,MAAM,KAAK,eAAe,OAAO,KAAK,OAAO;OAC7C,OAAO,YAAY,IAAI,OAAO,MAAM;MACtC;MAEA,MAAM,UACJ,WAAW,WACP,MAAM,KAAK,cAAc,OAAO,OAAO,IACvC,MAAM,KAAK,SAAS,OAAO,OAAO;MAIxC,OAAO,cAAc,MAAM,WAHb,QAAQ,YAClB,GAAG,KAAK,MAAO,QAAQ,SAAS,QAAQ,YAAa,GAAG,EAAE,KAAK,QAAQ,OAAO,GAAG,QAAQ,UAAU,YACnG,GAAG,QAAQ,OAAO;KAExB,SAAS,KAAK;MACZ,OAAO,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;KAClE;IACF;GACF;EACF;EAIA,IAAI,aAAa,SAAS,GACxB,QAAQ,iBAAiB;GACvB,aACE,6FAEA,aAAa,KAAK,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAC3C;GACF,aAAa,EAAE,eAAe;IAC5B,MAAM;IACN,YAAY;KACV,OAAO;MACL,MAAM;MACN,MAAM;MACN,aAAa;KACf;KACA,OAAO;MACL,MAAM;MACN,aAAa;KACf;IACF;IACA,UAAU,CAAC,SAAS,OAAO;GAC7B,CAAC;GACD,SAAS,OAAO,EAAE,OAAO,YAA8C;IACrE,IAAI;KACF,IAAI,CAAC,aAAa,SAAS,KAAK,GAC9B,OAAO,WAAW,MAAM,0CAA0C,aAAa,KAAK,IAAI;KAG1F,OAAO,MADe,KAAK,cAAc,OAAO,KAAK,KACnC;IACpB,SAAS,KAAK;KACZ,OAAO,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;IAClE;GACF;EACF;EAGF,OAAO;CACT;AACF;;;;ACvhBA,IAAa,uBAAb,MAAqE;CAKnE,YAAY,OAAoB,OAAgB;EAFhD,KAAQ,cAAc;EAGpB,KAAK,QAAQ;EACb,KAAK,QAAQ,SAAS;CACxB;CAEA,KAAK,OAAqB;EACxB,IAAI,CAAC,KAAK,OACR,KAAK,QAAQ;CAEjB;CAEA,cAA4B;EAC1B,IAAI,KAAK,aAAa;EACtB,KAAK,MAAM,GAAG;;;;;;;EAOd,KAAK,cAAc;CACrB;CAEA,MAAM,MAA8B;EAClC,KAAK,YAAY;EAIjB,OAAO,KAHW,MAAM,GAAwB;mEACe,KAAK,MAAM;MAE9D,EAAE,EAAE,WAAW;CAC7B;CAEA,MAAM,IAAI,SAAgC;EACxC,KAAK,YAAY;EACjB,KAAK,MAAM,GAAG;;gBAEF,KAAK,MAAM,IAAI,QAAQ;mDACY,QAAQ;;CAEzD;AACF"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/context/search.ts","../../src/context/blocks.ts","../../src/context/sqlite-provider.ts"],"sourcesContent":["/**\n * Search Provider — full-text searchable context blocks.\n *\n * Extends ContextProvider with `search()` for querying indexed content\n * and a keyed `set()` for indexing individual entries.\n *\n * Duck-typed: if a provider has a `search` method, it's a SearchProvider.\n */\n\nimport type { ContextProvider } from \"./blocks\";\nimport type { SqlProvider } from \"./sqlite-provider\";\n\n/**\n * Storage interface for searchable context.\n *\n * - `get()` returns a summary of indexed content (rendered into system prompt)\n * - `search(query)` full-text search (via search_context tool)\n * - `set(key, content)` indexes content under a key (via set_context tool)\n */\nexport interface SearchProvider extends ContextProvider {\n search(query: string): Promise<string | null>;\n set?(key: string, content: string): Promise<void>;\n}\n\n/**\n * Check if a provider is a SearchProvider (has a `search` method).\n */\nexport function isSearchProvider(\n provider: unknown\n): provider is SearchProvider {\n return (\n typeof provider === \"object\" &&\n provider !== null &&\n \"search\" in provider &&\n typeof (provider as SearchProvider).search === \"function\"\n );\n}\n\n// ── Agent Search Provider (DO SQLite FTS5) ─────────────────────────\n\n/**\n * SearchProvider backed by Durable Object SQLite with FTS5.\n *\n * - `get()` returns a count of indexed entries\n * - `search(query)` full-text search using FTS5\n * - `set(key, content)` indexes or replaces content under a key\n *\n * Entries live in one namespaced FTS5 table, separate from the session\n * message index.\n *\n * @example\n * ```ts\n * const context = new ContextBlocks([\n * { label: \"knowledge\", provider: new AgentSearchProvider(this) }\n * ]);\n * ```\n */\nexport class AgentSearchProvider implements SearchProvider {\n private agent: SqlProvider;\n private label = \"\";\n private initialized = false;\n\n constructor(agent: SqlProvider) {\n this.agent = agent;\n }\n\n init(label: string): void {\n this.label = label;\n }\n\n private ensureTable(): void {\n if (this.initialized) return;\n // The FTS table is the only store: a mirror row table would double the\n // billed writes of every indexed entry to serve a count and a lookup the\n // index already answers.\n this.agent.sql`\n CREATE VIRTUAL TABLE IF NOT EXISTS cf_agents_search_fts\n USING fts5(\n label UNINDEXED,\n key UNINDEXED,\n content,\n tokenize='porter unicode61'\n )\n `;\n this.agent.sql`DROP TABLE IF EXISTS cf_agents_search_entries`;\n this.initialized = true;\n }\n\n async get(): Promise<string | null> {\n this.ensureTable();\n const rows = this.agent.sql<{ count: number }>`\n SELECT COUNT(*) as count FROM cf_agents_search_fts\n WHERE label = ${this.label}\n `;\n const count = rows[0]?.count ?? 0;\n if (count === 0) return null;\n return `${count} entries indexed.`;\n }\n\n async search(query: string): Promise<string | null> {\n this.ensureTable();\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 null;\n try {\n const rows = this.agent.sql<{\n key: string;\n content: string;\n }>`\n SELECT f.key, f.content\n FROM cf_agents_search_fts f\n WHERE cf_agents_search_fts MATCH ${sanitized}\n AND f.label = ${this.label}\n ORDER BY rank\n LIMIT 10\n `;\n if (rows.length === 0) return null;\n return rows.map((r) => `[${r.key}]\\n${r.content}`).join(\"\\n\\n\");\n } catch {\n // Malformed FTS query\n return null;\n }\n }\n\n async set(key: string, content: string): Promise<void> {\n this.ensureTable();\n this.agent.sql`\n DELETE FROM cf_agents_search_fts\n WHERE label = ${this.label} AND key = ${key}\n `;\n this.agent.sql`\n INSERT INTO cf_agents_search_fts (label, key, content)\n VALUES (${this.label}, ${key}, ${content})\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 * - Reported by reminder(), when declared `whenChanged: \"remind\"`, while\n * their current value differs from the frozen snapshot\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 * - SearchProvider (get+search+set?) → searchable via search_context tool\n */\n\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport { estimateStringTokens } from \"../sessions/tokens\";\nimport { isSearchProvider, type SearchProvider } from \"./search\";\n\nfunction slugify(text: string): string {\n return text\n .slice(0, 60)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction stableHash(text: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(36);\n}\n\nfunction contextEntryKey(metadataTitle: string | undefined, content: string) {\n if (metadataTitle?.trim()) {\n const slug = slugify(metadataTitle);\n return slug || `entry-${stableHash(metadataTitle)}`;\n }\n\n const slug = slugify(content) || \"entry\";\n return `${slug}-${stableHash(content)}`;\n}\n\nconst SECTION_RULE = \"═\".repeat(46);\n\nconst REMINDER_PREAMBLE =\n \"These context blocks changed after the system prompt was written. \" +\n \"Their current values below replace the ones in the system prompt.\";\n\n/**\n * Empty readonly blocks are left out of the prompt. Writable and searchable\n * blocks always render so the model knows which tools can address them.\n * Remind blocks always render so `promptRendersBlock` can match them exactly:\n * an absent section cannot be told apart from another label's by prefix.\n */\nfunction rendersInPrompt(block: ContextBlock, remind: boolean): boolean {\n return !!block.content || block.writable || block.isSearchable || remind;\n}\n\nfunction renderSection(block: ContextBlock): string {\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.isSearchable) header += \" [searchable]\";\n else if (!block.writable) header += \" [readonly]\";\n else header += \" [writable]\";\n return `${SECTION_RULE}\\n${header}\\n${SECTION_RULE}\\n${block.content}`;\n}\n\n/**\n * Whether `prompt` renders `block` exactly as it stands now. Sections are\n * joined by a blank line and each opens with a rule, so padding the prompt\n * with a blank line in front and a rule behind lets one containment test\n * pin both ends of a section: content that only grew or shrank at the\n * edges does not pass for unchanged.\n */\nfunction promptRendersBlock(prompt: string, block: ContextBlock): boolean {\n const padded = `\\n\\n${prompt}\\n\\n${SECTION_RULE}\\n`;\n return padded.includes(`\\n\\n${renderSection(block)}\\n\\n${SECTION_RULE}\\n`);\n}\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 /** Called by the context system to provide the block label before first use. */\n init?(label: string): void;\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 * - SearchProvider (get+search+set?) → searchable via search_context\n * If omitted, auto-wired to writable SQLite when using builder. */\n provider?: ContextProvider | WritableContextProvider | SearchProvider;\n /**\n * What the model sees when this block's value moves on after the system\n * prompt was frozen.\n *\n * - `\"wait\"` (default): nothing, until `refreshSystemPrompt()` rebuilds\n * the prompt.\n * - `\"remind\"`: `reminder()` re-reads the block and, while it differs from\n * the frozen prompt, returns its current value for the host to send\n * after the cached prefix. The frozen prompt, and the provider's prefix\n * cache, stay intact until `refreshSystemPrompt()` promotes the value.\n * The block renders in the prompt even when empty.\n *\n * A change is any difference in what the provider returns, so return only\n * what should count: a date rather than a timestamp.\n */\n whenChanged?: \"wait\" | \"remind\";\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 SearchProvider */\n isSearchable: 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 private defaultProvider: ((label: string) => ContextProvider) | null;\n\n /**\n * @param configs Blocks to load on first use.\n * @param promptStore Persists the frozen system prompt, keeping the\n * provider's prefix cache warm across wakes.\n * @param defaultProvider Supplies storage for blocks declared without a\n * provider, so a host can offer durable writable blocks by label alone.\n */\n constructor(\n configs: ContextConfig[],\n promptStore?: WritableContextProvider,\n defaultProvider?: (label: string) => ContextProvider\n ) {\n this.configs = configs;\n this.promptStore = promptStore ?? null;\n this.defaultProvider = defaultProvider ?? null;\n }\n\n /** Fill in the host's storage for a block declared without a provider. */\n private withDefaultProvider(config: ContextConfig): ContextConfig {\n if (config.provider || !this.defaultProvider) return config;\n return { ...config, provider: this.defaultProvider(config.label) };\n }\n\n /**\n * Load all blocks from their providers. Hosts call this once at startup;\n * every other entry point loads lazily.\n */\n async load(): Promise<void> {\n this.configs = this.configs.map((config) =>\n this.withDefaultProvider(config)\n );\n for (const config of this.configs) {\n this.blocks.set(config.label, await this.loadBlock(config));\n }\n this.loaded = true;\n }\n\n /** Initialize a block's provider and read its current content. */\n private async loadBlock(config: ContextConfig): Promise<ContextBlock> {\n config.provider?.init?.(config.label);\n return this.readBlock(config);\n }\n\n private async readBlock(config: ContextConfig): Promise<ContextBlock> {\n const provider = config.provider;\n const content = provider ? ((await provider.get()) ?? \"\") : \"\";\n const searchable = isSearchProvider(provider);\n return {\n label: config.label,\n description: config.description,\n content,\n tokens: estimateStringTokens(content),\n maxTokens: config.maxTokens,\n writable: isWritableProvider(provider) || (searchable && !!provider?.set),\n isSearchable: searchable\n };\n }\n\n /**\n * Dynamically register a new context block after initialization.\n * Used by extensions to contribute context at runtime.\n *\n * If blocks have already been loaded, the new block's provider is\n * initialized and loaded immediately. The snapshot is NOT updated\n * automatically — call `refreshSystemPrompt()` to rebuild.\n */\n async addBlock(input: ContextConfig): Promise<ContextBlock> {\n if (!this.loaded) await this.load();\n\n if (this.configs.some((c) => c.label === input.label)) {\n throw new Error(`Block \"${input.label}\" already exists`);\n }\n\n const config = this.withDefaultProvider(input);\n this.configs.push(config);\n const block = await this.loadBlock(config);\n this.blocks.set(config.label, block);\n return block;\n }\n\n /**\n * Remove a dynamically registered context block.\n * Used during extension unload cleanup.\n *\n * Returns true if the block existed and was removed.\n * The snapshot is NOT updated automatically — call\n * `refreshSystemPrompt()` to rebuild.\n */\n removeBlock(label: string): boolean {\n const idx = this.configs.findIndex((c) => c.label === label);\n if (idx === -1) return false;\n\n this.configs.splice(idx, 1);\n this.blocks.delete(label);\n return 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.isSearchable) {\n throw new Error(\n `Block \"${label}\" is a keyed provider. Use setSearchEntry() 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 isSearchable: 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 /** Index a search entry within a searchable block. */\n private async setSearchEntry(\n label: string,\n key: string,\n content: 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?.isSearchable) {\n throw new Error(`Block \"${label}\" is not a search provider`);\n }\n\n const provider = config?.provider;\n if (!provider || !isSearchProvider(provider) || !provider.set) {\n throw new Error(`Block \"${label}\" does not support writes`);\n }\n\n await provider.set(key, content);\n\n // Refresh summary\n const summary = await provider.get();\n existing.content = summary ?? \"\";\n existing.tokens = estimateStringTokens(existing.content);\n }\n\n /** Search a searchable block. */\n private async searchContext(\n label: string,\n query: string\n ): 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 || !isSearchProvider(config.provider)) {\n throw new Error(`Block \"${label}\" is not a search provider`);\n }\n\n return config.provider.search(query);\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 const needsSep = existing.content.length > 0 && !content.startsWith(\"\\n\");\n return this.setBlock(\n label,\n existing.content + (needsSep ? \"\\n\" : \"\") + content\n );\n }\n\n private renderPrompt(): string {\n const remind = new Set(\n this.configs\n .filter((config) => config.whenChanged === \"remind\")\n .map((config) => config.label)\n );\n return Array.from(this.blocks.values())\n .filter((block) => rendersInPrompt(block, remind.has(block.label)))\n .map(renderSection)\n .join(\"\\n\\n\");\n }\n\n // ── Public API ──────────────────────────────────────────────────\n\n /**\n * The frozen system prompt. The first call renders the blocks and stores\n * the result; every later call returns that same string, so the model\n * provider's prefix cache stays warm. Block edits do not change it until\n * `refreshSystemPrompt()` is called.\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 if (this.snapshot !== null) return this.snapshot;\n\n if (!this.loaded) await this.load();\n this.snapshot = this.renderPrompt();\n await this.promptStore?.set(this.snapshot);\n return this.snapshot;\n }\n\n /**\n * Reload every block from its provider, re-render the system prompt, and\n * persist it. Use this after block content has changed.\n */\n async refreshSystemPrompt(): Promise<string> {\n this.loaded = false;\n await this.load();\n this.snapshot = this.renderPrompt();\n await this.promptStore?.set(this.snapshot);\n return this.snapshot;\n }\n\n /**\n * Current values of the `whenChanged: \"remind\"` blocks that no longer\n * match the frozen prompt, rendered for the host to send after the cached\n * prefix: at the end of the conversation, not in the system prompt. `null`\n * when nothing has changed or nothing is frozen yet.\n *\n * Each call re-reads those blocks from their providers. The comparison is\n * against the frozen prompt itself, so it holds across restarts, and a\n * reminder keeps appearing on every call until `refreshSystemPrompt()`\n * folds the value in.\n */\n async reminder(): Promise<string | null> {\n if (!this.loaded) await this.load();\n const frozen = this.promptStore\n ? ((await this.promptStore.get()) ?? this.snapshot)\n : this.snapshot;\n if (frozen === null) return null;\n\n const sections: string[] = [];\n for (const config of this.configs) {\n if (config.whenChanged !== \"remind\") continue;\n const block = await this.readBlock(config);\n this.blocks.set(config.label, block);\n if (!promptRendersBlock(frozen, block)) {\n sections.push(renderSection(block));\n }\n }\n if (sections.length === 0) return null;\n return [REMINDER_PREAMBLE, ...sections].join(\"\\n\\n\");\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 * - `search_context` — when any block is a search provider\n */\n async tools(): Promise<ToolSet> {\n if (!this.loaded) await this.load();\n\n const blocks = Array.from(this.blocks.values());\n const writable = blocks.filter((b) => b.writable);\n const searchLabels = blocks\n .filter((b) => b.isSearchable)\n .map((b) => b.label);\n const toolSet: ToolSet = {};\n\n // ── set_context ──────────────────────────────────────────────\n\n if (writable.length > 0) {\n const blockDescriptions = writable.map((b) => {\n const kind = b.isSearchable ? \"searchable, keyed entries\" : \"writable\";\n return `- \"${b.label}\" (${kind}): ${b.description ?? \"no description\"}`;\n });\n const keyedBlocks = writable.filter((b) => b.isSearchable);\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: \"The main content to write to the block.\"\n },\n action: {\n type: \"string\" as const,\n enum: [\"replace\", \"append\"],\n description: \"replace (default) or append\"\n }\n };\n\n if (keyedBlocks.length > 0) {\n properties.metadata = {\n type: \"object\" as const,\n description:\n \"Optional metadata for keyed entries (searchable blocks: \" +\n keyedBlocks.map((b) => `\"${b.label}\"`).join(\", \") +\n \"). A title keeps updates stable; a description helps the model \" +\n \"pick the right entry.\",\n properties: {\n title: {\n type: \"string\" as const,\n description:\n \"Short title. Used as a stable identifier — entries with the \" +\n \"same title are updated in place, different titles create new entries.\"\n },\n description: {\n type: \"string\" as const,\n description:\n \"One-line summary shown alongside the title in the system prompt \" +\n \"so the model can decide when to load the entry.\"\n }\n }\n };\n }\n\n const metadataHint =\n keyedBlocks.length > 0\n ? \"\\n\\nFor searchable blocks, pass `metadata: { title, description }` \" +\n \"— title stabilises updates, description helps the model pick \" +\n \"entries. Metadata is optional.\"\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.${metadataHint}`,\n inputSchema: z.fromJSONSchema({\n type: \"object\" as const,\n properties: properties as Record<string, Record<string, unknown>>,\n required: [\"label\", \"content\"]\n }),\n execute: async ({\n label,\n content,\n metadata,\n action\n }: {\n label: string;\n content: string;\n metadata?: { title?: string; 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.isSearchable) {\n const key = contextEntryKey(metadata?.title, content);\n await this.setSearchEntry(label, key, content);\n return `Indexed \"${key}\" in ${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 // ── search_context ────────────────────────────────────────────\n\n if (searchLabels.length > 0) {\n toolSet.search_context = {\n description:\n \"Search for information in a searchable context block. \" +\n \"ONLY these blocks are searchable: \" +\n searchLabels.map((l) => `\"${l}\"`).join(\", \") +\n \". Other blocks cannot be searched.\",\n inputSchema: z.fromJSONSchema({\n type: \"object\" as const,\n properties: {\n label: {\n type: \"string\" as const,\n enum: searchLabels,\n description: \"Searchable block label\"\n },\n query: {\n type: \"string\" as const,\n description: \"Search query\"\n }\n },\n required: [\"label\", \"query\"]\n }),\n execute: async ({ label, query }: { label: string; query: string }) => {\n try {\n if (!searchLabels.includes(label)) {\n return `Error: \"${label}\" is not searchable. Searchable blocks: ${searchLabels.join(\", \")}`;\n }\n const results = await this.searchContext(label, query);\n return results ?? \"No results found.\";\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 * 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 { WritableContextProvider } from \"./blocks\";\n\n/** Minimal tagged-template SQL surface used by SQLite context providers. */\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/** SQLite-backed writable context block provider. */\nexport class AgentContextProvider implements WritableContextProvider {\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 init(label: string): void {\n if (!this.label) {\n this.label = label;\n }\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"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"current-agent-DhoDkSnH.js","names":[],"sources":["../src/lifecycle/current-agent.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { DurableObject } from \"cloudflare:workers\";\n\nimport type { MemoryLimitContext } from \"./capability-runner\";\nimport type { Lifecycle } from \"./durable-object-lifecycle\";\nimport type { Connection } from \"./types\";\nimport type { LifecycleJobContext, LifecycleJobOutcome } from \"./job-queue\";\n\n/**\n * A Durable Object that has installed the Agents SDK Lifecycle.\n *\n * Pass a more specific Lifecycle Object type to {@link getCurrentAgent} when\n * shared host code needs APIs implemented by a particular object.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport interface LifecycleObject<\n Env extends object = Cloudflare.Env,\n Props extends
|
|
1
|
+
{"version":3,"file":"current-agent-DhoDkSnH.js","names":[],"sources":["../src/lifecycle/current-agent.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { DurableObject } from \"cloudflare:workers\";\n\nimport type { MemoryLimitContext } from \"./capability-runner\";\nimport type { Lifecycle } from \"./durable-object-lifecycle\";\nimport type { Connection } from \"./types\";\nimport type { LifecycleJobContext, LifecycleJobOutcome } from \"./job-queue\";\n\n/**\n * A Durable Object that has installed the Agents SDK Lifecycle.\n *\n * Pass a more specific Lifecycle Object type to {@link getCurrentAgent} when\n * shared host code needs APIs implemented by a particular object.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport interface LifecycleObject<\n Env extends object = Cloudflare.Env,\n Props extends object = object\n> extends DurableObject<Env> {\n readonly lifecycle: Lifecycle<Env, Props>;\n onStart?(props?: Props): void | Promise<void>;\n onRequest?(request: Request): Response | Promise<Response>;\n onAlarm?(): void | Promise<void>;\n onJob?(\n context: LifecycleJobContext\n ): LifecycleJobOutcome | void | Promise<LifecycleJobOutcome | void>;\n /**\n * Host domain policy applied when the alarm memory-limit circuit breaker\n * records a strike (#1825), after every capability's `onMemoryLimit` hook.\n *\n * Lifecycle dispatches host hooks structurally through its internal host\n * cast, so TS visibility is the host's to choose: a framework host whose\n * implementation is internal machinery declares the hook `protected` (the\n * pattern — see `AIChatAgent`/`Think`) and keeps the real work in a\n * `private _cf_`-prefixed method, without falling out of this contract at\n * runtime.\n */\n onAlarmMemoryLimit?(context: MemoryLimitContext): void | Promise<void>;\n}\n\n/** Values associated with the currently executing Lifecycle host. */\nexport type AgentContextStore = {\n /** Lifecycle host selected for this invocation. */\n agent: unknown;\n /** WebSocket connection selected for this invocation, when applicable. */\n connection: Connection | undefined;\n /** HTTP request selected for this invocation, when applicable. */\n request: Request | undefined;\n /** Extension-owned value selected for this invocation, when applicable. */\n email: unknown;\n};\n\n/** Values returned by {@link getCurrentAgent}. */\nexport type CurrentAgentContext<\n Host extends DurableObject = LifecycleObject,\n Email = unknown\n> = {\n agent: Host | undefined;\n connection: Connection | undefined;\n request: Request | undefined;\n email: Email | undefined;\n};\n\n/**\n * Shared invocation context for Lifecycle, Agent, AIChatAgent, and Think.\n *\n * @internal Importing or relying on this symbol will break your code in a\n * future release. Use {@link getCurrentAgent} to read the public context.\n */\nexport const __DO_NOT_USE_WILL_BREAK__agentContext =\n new AsyncLocalStorage<AgentContextStore>();\n\n/**\n * Return the current Agent or Lifecycle Object and invocation-specific values.\n *\n * Lifecycle host startup and alarm hooks receive the current object with no\n * request. Request hooks additionally receive the request being handled.\n * Lifecycle-managed WebSocket hooks receive their connection and, during\n * connect, its upgrade request. Agent extensions may also establish context\n * for email, chat turns, callable methods, and detached work. Capability hooks\n * do not run in this ambient context.\n */\nexport function getCurrentAgent<\n Host extends DurableObject = LifecycleObject\n>(): CurrentAgentContext<Host> {\n const store = __DO_NOT_USE_WILL_BREAK__agentContext.getStore();\n if (!store) {\n return {\n agent: undefined,\n connection: undefined,\n request: undefined,\n email: undefined\n };\n }\n\n return {\n agent: store.agent as Host,\n connection: store.connection,\n request: store.request,\n email: store.email\n };\n}\n\ntype LifecycleHostContext<Env extends object, Props extends object> = {\n readonly host: LifecycleObject<Env, Props>;\n readonly connection?: Connection;\n readonly request?: Request;\n};\n\n/** Run one host hook in the context of its Lifecycle Object. */\nexport function runInLifecycleHostContext<\n Env extends object,\n Props extends object,\n T\n>(context: LifecycleHostContext<Env, Props>, operation: () => T): T {\n return __DO_NOT_USE_WILL_BREAK__agentContext.run(\n {\n agent: context.host,\n connection: context.connection,\n request: context.request,\n email: undefined\n },\n operation\n );\n}\n\n/** Run a capability hook without inheriting a current Agent or host context. */\nexport function runWithoutCurrentAgent<T>(operation: () => T): T {\n return __DO_NOT_USE_WILL_BREAK__agentContext.exit(operation);\n}\n"],"mappings":";;;;;;;;AAsEA,MAAa,wCACX,IAAI,kBAAqC;;;;;;;;;;;AAY3C,SAAgB,kBAEe;CAC7B,MAAM,QAAQ,sCAAsC,SAAS;CAC7D,IAAI,CAAC,OACH,OAAO;EACL,OAAO,KAAA;EACP,YAAY,KAAA;EACZ,SAAS,KAAA;EACT,OAAO,KAAA;CACT;CAGF,OAAO;EACL,OAAO,MAAM;EACb,YAAY,MAAM;EAClB,SAAS,MAAM;EACf,OAAO,MAAM;CACf;AACF;;AASA,SAAgB,0BAId,SAA2C,WAAuB;CAClE,OAAO,sCAAsC,IAC3C;EACE,OAAO,QAAQ;EACf,YAAY,QAAQ;EACpB,SAAS,QAAQ;EACjB,OAAO,KAAA;CACT,GACA,SACF;AACF;;AAGA,SAAgB,uBAA0B,WAAuB;CAC/D,OAAO,sCAAsC,KAAK,SAAS;AAC7D"}
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
o as MemoryLimitContext,
|
|
4
4
|
v as LifecycleJobOutcome,
|
|
5
5
|
x as Connection
|
|
6
|
-
} from "./capability-runner-
|
|
7
|
-
import { t as Lifecycle } from "./durable-object-lifecycle-
|
|
6
|
+
} from "./capability-runner-DBAnGr6L.js";
|
|
7
|
+
import { t as Lifecycle } from "./durable-object-lifecycle-HhgOMWq9.js";
|
|
8
8
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
9
9
|
import { DurableObject } from "cloudflare:workers";
|
|
10
10
|
|
|
@@ -19,7 +19,7 @@ import { DurableObject } from "cloudflare:workers";
|
|
|
19
19
|
*/
|
|
20
20
|
interface LifecycleObject<
|
|
21
21
|
Env extends object = Cloudflare.Env,
|
|
22
|
-
Props extends
|
|
22
|
+
Props extends object = object
|
|
23
23
|
> extends DurableObject<Env> {
|
|
24
24
|
readonly lifecycle: Lifecycle<Env, Props>;
|
|
25
25
|
onStart?(props?: Props): void | Promise<void>;
|
|
@@ -90,4 +90,4 @@ export {
|
|
|
90
90
|
LifecycleObject as r,
|
|
91
91
|
AgentContextStore as t
|
|
92
92
|
};
|
|
93
|
-
//# sourceMappingURL=current-agent-
|
|
93
|
+
//# sourceMappingURL=current-agent-vAk_5JMt.d.ts.map
|
package/dist/{durable-object-lifecycle-ei7M7lqB.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts}
RENAMED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
b as LifecycleJobs,
|
|
4
4
|
i as DurableObjectCapability,
|
|
5
5
|
u as LifecycleRouteAddress
|
|
6
|
-
} from "./capability-runner-
|
|
6
|
+
} from "./capability-runner-DBAnGr6L.js";
|
|
7
7
|
import { DurableObject } from "cloudflare:workers";
|
|
8
8
|
|
|
9
9
|
//#region src/lifecycle/durable-object-lifecycle.d.ts
|
|
@@ -24,7 +24,7 @@ type LifecycleOptions = {
|
|
|
24
24
|
};
|
|
25
25
|
declare class Lifecycle<
|
|
26
26
|
Env extends object = Cloudflare.Env,
|
|
27
|
-
Props extends
|
|
27
|
+
Props extends object = object
|
|
28
28
|
> {
|
|
29
29
|
#private;
|
|
30
30
|
/**
|
|
@@ -33,10 +33,7 @@ declare class Lifecycle<
|
|
|
33
33
|
* @param host - The Durable Object whose runtime handlers the lifecycle owns.
|
|
34
34
|
* @returns The installed lifecycle.
|
|
35
35
|
*/
|
|
36
|
-
static install<
|
|
37
|
-
Env extends object,
|
|
38
|
-
Props extends Record<string, unknown> = Record<string, unknown>
|
|
39
|
-
>(
|
|
36
|
+
static install<Env extends object, Props extends object = object>(
|
|
40
37
|
host: DurableObject<Env>,
|
|
41
38
|
options?: LifecycleOptions
|
|
42
39
|
): Lifecycle<Env, Props>;
|
|
@@ -106,9 +103,20 @@ declare class Lifecycle<
|
|
|
106
103
|
* Runtime fetch, alarm, and WebSocket entry points call this automatically.
|
|
107
104
|
* RPC methods may call it explicitly because native RPC bypasses fetch.
|
|
108
105
|
*
|
|
106
|
+
* Concurrent callers share one startup and all observe its outcome. A call
|
|
107
|
+
* made from inside that startup, such as a capability or `onStart` calling
|
|
108
|
+
* back into the host, returns immediately instead of waiting on itself.
|
|
109
|
+
*
|
|
109
110
|
* @param props - Optional properties supplied to capability and host startup.
|
|
111
|
+
* Ignored while a startup is already in flight.
|
|
110
112
|
*/
|
|
111
113
|
start(props?: Props): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Whether lifecycle startup has completed for this instance.
|
|
116
|
+
*
|
|
117
|
+
* @returns `true` after capabilities and the host `onStart` have finished.
|
|
118
|
+
*/
|
|
119
|
+
isStarted(): boolean;
|
|
112
120
|
/**
|
|
113
121
|
* The name used to address this Durable Object.
|
|
114
122
|
*
|
|
@@ -154,4 +162,4 @@ declare class Lifecycle<
|
|
|
154
162
|
}
|
|
155
163
|
//#endregion
|
|
156
164
|
export { LifecycleOptions as n, LifecycleRouteEnvelope as r, Lifecycle as t };
|
|
157
|
-
//# sourceMappingURL=durable-object-lifecycle-
|
|
165
|
+
//# sourceMappingURL=durable-object-lifecycle-HhgOMWq9.d.ts.map
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import {
|
|
2
|
+
_ as LifecycleJobContext,
|
|
3
|
+
n as CapabilityStartContext,
|
|
4
|
+
s as LifecycleCapability,
|
|
5
|
+
v as LifecycleJobOutcome
|
|
6
|
+
} from "../../capability-runner-DBAnGr6L.js";
|
|
7
|
+
import { T as SkillSource } from "../../index-kbSUIer3.js";
|
|
8
|
+
import {
|
|
9
|
+
AgentEventStream,
|
|
10
|
+
Conversation,
|
|
11
|
+
ConversationId,
|
|
12
|
+
ConversationRetryPolicy,
|
|
13
|
+
EntryRecord,
|
|
14
|
+
Harness,
|
|
15
|
+
ModelRef,
|
|
16
|
+
Registry,
|
|
17
|
+
ToolRegistration,
|
|
18
|
+
Tx,
|
|
19
|
+
UserInput
|
|
20
|
+
} from "@earendil-works/pi-durable";
|
|
21
|
+
import { SqliteStorage } from "@earendil-works/pi-durable/storage/sqlite";
|
|
22
|
+
import { ModelThinkingLevel } from "@earendil-works/pi-ai";
|
|
23
|
+
|
|
24
|
+
//#region src/harnesses/pi/context.d.ts
|
|
25
|
+
/**
|
|
26
|
+
* pi's invocation context, as `@earendil-works/chord` defines it.
|
|
27
|
+
*
|
|
28
|
+
* pi-durable takes a chord `Context` on every call but does not re-export
|
|
29
|
+
* the type or its constructors. The harness needs only a background context
|
|
30
|
+
* and a way to add an abort signal, so both are inlined here instead of
|
|
31
|
+
* depending on chord directly. A chord `Context` is a plain structural
|
|
32
|
+
* interface (an abort signal, keyed values, and a name), so these interoperate
|
|
33
|
+
* with chord's own contexts inside pi.
|
|
34
|
+
*
|
|
35
|
+
* One limit: chord's own derived contexts read the abort signal through a
|
|
36
|
+
* private key rather than `abortSignal`, so a chord `withContextValue`
|
|
37
|
+
* wrapped around one of these would not see its signal. pi 0.99.2 never
|
|
38
|
+
* wraps a caller's context that way; `withAbortSignal` and `withCancel`
|
|
39
|
+
* read `abortSignal` and work.
|
|
40
|
+
*
|
|
41
|
+
* If pi-durable starts re-exporting chord's context helpers, replace this
|
|
42
|
+
* file with those.
|
|
43
|
+
*/
|
|
44
|
+
type Context$1 = Parameters<Harness["close"]>[0];
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/harnesses/pi/session-store.d.ts
|
|
47
|
+
/**
|
|
48
|
+
* pi's session store on a Durable Object's SQLite database.
|
|
49
|
+
*
|
|
50
|
+
* pi owns its sessions: conversations, entries, tasks, submissions, and
|
|
51
|
+
* documents live in pi's own schema, created and migrated by pi's portable
|
|
52
|
+
* `SqliteStorage`. This file only supplies the synchronous database facade
|
|
53
|
+
* pi asks for, over `ctx.storage.sql`, and moves pi's tables under a prefix
|
|
54
|
+
* so they cannot collide with the SDK's or the host's tables in the same
|
|
55
|
+
* object.
|
|
56
|
+
*
|
|
57
|
+
* It is the Durable Object counterpart of `agents/sessions` for a harness
|
|
58
|
+
* that brings its own session model, and has nothing pi-harness specific in
|
|
59
|
+
* it: any harness built on pi-durable can open its storage with it.
|
|
60
|
+
*/
|
|
61
|
+
type PiSessionStoreOptions = {
|
|
62
|
+
/**
|
|
63
|
+
* Prefix for every table and index pi creates. Must not start with `_cf_`,
|
|
64
|
+
* which Durable Objects reserve. Default `pi_`.
|
|
65
|
+
*/
|
|
66
|
+
readonly prefix?: string;
|
|
67
|
+
};
|
|
68
|
+
/** Open pi-durable's storage over this object's SQLite database. */
|
|
69
|
+
declare function openPiSessionStore(
|
|
70
|
+
storage: DurableObjectStorage,
|
|
71
|
+
options?: PiSessionStoreOptions
|
|
72
|
+
): Promise<SqliteStorage>;
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/harnesses/pi/types.d.ts
|
|
75
|
+
/**
|
|
76
|
+
* `PiHarness`'s own types: how the harness addresses sessions and reports
|
|
77
|
+
* operations. Anything pi already publishes (input, transcript entries,
|
|
78
|
+
* events) is pi's type, used directly.
|
|
79
|
+
*/
|
|
80
|
+
/** A pi conversation, addressed by its id as a string. The root is `"1"`. */
|
|
81
|
+
type PiSessionId = string;
|
|
82
|
+
/**
|
|
83
|
+
* What a submission does when the session is already running.
|
|
84
|
+
*
|
|
85
|
+
* - `followUp` (default): answered after the current run, as its own run.
|
|
86
|
+
* - `steer`: joins the running work after the current tool round.
|
|
87
|
+
*/
|
|
88
|
+
type PiWhenBusy = "followUp" | "steer";
|
|
89
|
+
type PiSubmitOptions = {
|
|
90
|
+
/** Default: the root session. */ readonly session?: PiSessionId /** Idempotency key. Submitting the same id twice returns the same receipt. */;
|
|
91
|
+
readonly operationId?: string;
|
|
92
|
+
readonly whenBusy?: PiWhenBusy;
|
|
93
|
+
};
|
|
94
|
+
type PiSessionOptions = {
|
|
95
|
+
/** Default: the root session. */ readonly session?: PiSessionId;
|
|
96
|
+
};
|
|
97
|
+
/** Returned once a submission is durable. It says nothing about the model yet. */
|
|
98
|
+
type PiReceipt = {
|
|
99
|
+
readonly operationId: string;
|
|
100
|
+
readonly session: PiSessionId /** False when this operation id was already submitted. */;
|
|
101
|
+
readonly accepted: boolean;
|
|
102
|
+
};
|
|
103
|
+
/** How one operation ended. */
|
|
104
|
+
type PiOperationResult = {
|
|
105
|
+
readonly operationId: string;
|
|
106
|
+
readonly session: PiSessionId /** `done`: answered. `unanswered`: failed, aborted, or withdrawn. */;
|
|
107
|
+
readonly status:
|
|
108
|
+
| "done"
|
|
109
|
+
| "unanswered" /** Why an unanswered operation ended, in pi's words. */;
|
|
110
|
+
readonly reason?: string /** The final assistant text, when answered. */;
|
|
111
|
+
readonly text?: string;
|
|
112
|
+
};
|
|
113
|
+
type PiPromptResponse = PiOperationResult & {
|
|
114
|
+
/** The session's active transcript after the operation. */ readonly messages: readonly EntryRecord[];
|
|
115
|
+
};
|
|
116
|
+
/** A submission pi has not settled yet. */
|
|
117
|
+
type PiPendingOperation = {
|
|
118
|
+
readonly operationId: string;
|
|
119
|
+
readonly session: PiSessionId /** `queued` in pi's inbox, or `running` as part of the current run. */;
|
|
120
|
+
readonly status: "queued" | "running";
|
|
121
|
+
};
|
|
122
|
+
type PiSessionInfo = {
|
|
123
|
+
readonly id: PiSessionId /** The session this one was forked from. */;
|
|
124
|
+
readonly parent?: PiSessionId;
|
|
125
|
+
readonly busy: boolean;
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/harnesses/pi/harness.d.ts
|
|
129
|
+
/** The root session's id. */
|
|
130
|
+
declare const ROOT_SESSION: PiSessionId;
|
|
131
|
+
/**
|
|
132
|
+
* What the harness factory is handed. The store is already open and pi's
|
|
133
|
+
* migrations have run, so a factory only has to decide how to call
|
|
134
|
+
* `Harness.open` — or call something else that satisfies the same contract.
|
|
135
|
+
*/
|
|
136
|
+
type PiHarnessContext = {
|
|
137
|
+
/** pi's storage over this object's SQLite, tables under the store prefix. */ readonly storage: SqliteStorage /** Background context, for the open itself. */;
|
|
138
|
+
readonly context: Context$1;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Builds pi's `Harness`. Return value is adopted as-is: the caller owns the
|
|
142
|
+
* registry (tools, system prompt, hooks, skills), `models`, `env`,
|
|
143
|
+
* `onReport` and anything else `HarnessOptions` grows.
|
|
144
|
+
*
|
|
145
|
+
* ```ts
|
|
146
|
+
* harness: ({ storage, context }) =>
|
|
147
|
+
* Harness.open(storage, { models, registry }, context)
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
type PiHarnessFactory = (
|
|
151
|
+
context: PiHarnessContext
|
|
152
|
+
) => Harness | Promise<Harness>;
|
|
153
|
+
/**
|
|
154
|
+
* Applied to a session the first time it is created. Without a model, a
|
|
155
|
+
* session's generation fails as unanswered until `session.setModel` sets one.
|
|
156
|
+
*/
|
|
157
|
+
type PiSessionDefaults = {
|
|
158
|
+
/** Model for new sessions. Change one session's with `session.setModel`. */ readonly model?: ModelRef;
|
|
159
|
+
readonly thinkingLevel?: ModelThinkingLevel /** pi's generation retries for new sessions. */;
|
|
160
|
+
readonly retry?: ConversationRetryPolicy;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* How long the wake waits and when it hands a wait to the alarm. Defaults
|
|
164
|
+
* suit a real deployment; a test shortens them so a suite does not sit on
|
|
165
|
+
* the real heartbeat.
|
|
166
|
+
*/
|
|
167
|
+
type PiWakeTiming = {
|
|
168
|
+
/** A pi wait further away than this goes to the alarm. Default 60_000. */ readonly sleepThresholdMs?: number /** Longest one wake waits inside an alarm. Default 600_000. */;
|
|
169
|
+
readonly waitBudgetMs?: number /** Heartbeat while waiting, and the re-check for background work. Default 30_000. */;
|
|
170
|
+
readonly heartbeatMs?: number;
|
|
171
|
+
};
|
|
172
|
+
/** `PiHarness`'s options. Only `harness`, which opens pi, is required. */
|
|
173
|
+
type PiHarnessOptions = {
|
|
174
|
+
/** Opens pi's `Harness` over the store this object prepared. */ readonly harness: PiHarnessFactory /** Applied to new sessions. */;
|
|
175
|
+
readonly defaults?: PiSessionDefaults;
|
|
176
|
+
readonly store?: PiSessionStoreOptions;
|
|
177
|
+
readonly timing?: PiWakeTiming;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* pi-durable hosted in a Durable Object, behind a small harness interface:
|
|
181
|
+
* `harness.prompt()`, `harness.sessions`, `harness.session(id)`. How a session reaches a
|
|
182
|
+
* client (sockets, SSE, RPC) is the host's glue, built on `session.events()`.
|
|
183
|
+
*
|
|
184
|
+
* pi owns everything about a run: the transcript, the inbox of steers and
|
|
185
|
+
* follow-ups, generation and tool tasks, retries, crash recovery, and the
|
|
186
|
+
* live view. It keeps all of it in its own tables in this object's SQLite
|
|
187
|
+
* database (see `session-store.ts`).
|
|
188
|
+
*
|
|
189
|
+
* What pi cannot do on a Durable Object is wake itself: its scheduler runs
|
|
190
|
+
* in memory, and an evicted object has no memory. The harness is that wake,
|
|
191
|
+
* with one Lifecycle job per session. Input goes to pi once, in `submit()`,
|
|
192
|
+
* after the session's job is scheduled. The job waits while pi has live
|
|
193
|
+
* tasks in the session, rescheduling itself as a heartbeat, and completes
|
|
194
|
+
* when there are none. An eviction mid-run leaves the job due, so its alarm
|
|
195
|
+
* restarts the object, pi reopens and resumes its own tasks, and the job
|
|
196
|
+
* waits again.
|
|
197
|
+
*
|
|
198
|
+
* @experimental The API may change between releases.
|
|
199
|
+
*/
|
|
200
|
+
declare class PiHarness extends LifecycleCapability {
|
|
201
|
+
#private;
|
|
202
|
+
readonly sessions: PiSessions;
|
|
203
|
+
constructor(options: PiHarnessOptions);
|
|
204
|
+
onStart(_context: CapabilityStartContext): Promise<void>;
|
|
205
|
+
onJob(context: LifecycleJobContext): Promise<LifecycleJobOutcome | void>;
|
|
206
|
+
/** Close pi's in-memory resources. Durable state is untouched. */
|
|
207
|
+
dispose(): Promise<void>;
|
|
208
|
+
/** A handle on one session. No I/O until you call it. */
|
|
209
|
+
session(id?: PiSessionId): PiSession;
|
|
210
|
+
/** Submit a prompt and wait for its answer. */
|
|
211
|
+
prompt(
|
|
212
|
+
input: UserInput,
|
|
213
|
+
options?: PiSubmitOptions
|
|
214
|
+
): Promise<PiPromptResponse>;
|
|
215
|
+
/** Durably submit a prompt. Resolves before the model runs. */
|
|
216
|
+
submit(input: UserInput, options?: PiSubmitOptions): Promise<PiReceipt>;
|
|
217
|
+
/** Stop one operation, or everything running in a session. */
|
|
218
|
+
abort(
|
|
219
|
+
options?: PiSessionOptions & {
|
|
220
|
+
readonly operationId?: string;
|
|
221
|
+
}
|
|
222
|
+
): Promise<boolean>;
|
|
223
|
+
wait(
|
|
224
|
+
operationId: string,
|
|
225
|
+
options?: PiSessionOptions & {
|
|
226
|
+
readonly signal?: AbortSignal;
|
|
227
|
+
}
|
|
228
|
+
): Promise<PiOperationResult>;
|
|
229
|
+
/** The session's active transcript, as pi's entries. */
|
|
230
|
+
messages(options?: PiSessionOptions): Promise<EntryRecord[]>;
|
|
231
|
+
/** Submissions pi has not settled yet, oldest first. */
|
|
232
|
+
pending(options?: PiSessionOptions): Promise<PiPendingOperation[]>;
|
|
233
|
+
/** The opened pi Harness, for anything the interface does not cover. */
|
|
234
|
+
pi(): Promise<Harness>;
|
|
235
|
+
/** @internal */
|
|
236
|
+
conversation(session: PiSessionId): Promise<Conversation>;
|
|
237
|
+
/** @internal */
|
|
238
|
+
storage(): Promise<SqliteStorage>;
|
|
239
|
+
/** @internal Configure a new session like the root. */
|
|
240
|
+
initSession(tx: Tx, id: ConversationId): Promise<void>;
|
|
241
|
+
/** @internal */
|
|
242
|
+
enqueue(
|
|
243
|
+
session: PiSessionId,
|
|
244
|
+
input: UserInput,
|
|
245
|
+
options: PiSubmitOptions
|
|
246
|
+
): Promise<PiReceipt>;
|
|
247
|
+
/** @internal Withdraw a queued input, or abort the run it joined. */
|
|
248
|
+
withdraw(session: PiSessionId, operationId: string): Promise<boolean>;
|
|
249
|
+
/** @internal Wait for pi to settle an operation, by its request id. */
|
|
250
|
+
settled(
|
|
251
|
+
session: PiSessionId,
|
|
252
|
+
operationId: string,
|
|
253
|
+
signal?: AbortSignal
|
|
254
|
+
): Promise<PiOperationResult>;
|
|
255
|
+
}
|
|
256
|
+
/** One pi conversation, addressed through the harness. */
|
|
257
|
+
declare class PiSession {
|
|
258
|
+
#private;
|
|
259
|
+
readonly id: PiSessionId;
|
|
260
|
+
constructor(harness: PiHarness, id: PiSessionId);
|
|
261
|
+
/** Durably submit a prompt. Resolves before the model runs. */
|
|
262
|
+
submit(input: UserInput, options?: PiSubmitOptions): Promise<PiReceipt>;
|
|
263
|
+
/** Submit and wait for the answer and the updated transcript. */
|
|
264
|
+
prompt(
|
|
265
|
+
input: UserInput,
|
|
266
|
+
options?: PiSubmitOptions
|
|
267
|
+
): Promise<PiPromptResponse>;
|
|
268
|
+
/** Join the running work after its current tool round. */
|
|
269
|
+
steer(
|
|
270
|
+
input: UserInput,
|
|
271
|
+
options?: Omit<PiSubmitOptions, "whenBusy">
|
|
272
|
+
): Promise<PiReceipt>;
|
|
273
|
+
/** Wait for an operation to settle. Aborting `signal` stops only the wait. */
|
|
274
|
+
wait(operationId: string, signal?: AbortSignal): Promise<PiOperationResult>;
|
|
275
|
+
/**
|
|
276
|
+
* Withdraw one queued operation (or abort the run it joined), or, with no
|
|
277
|
+
* id, abort the session: pi withdraws queued inputs and aborts the
|
|
278
|
+
* running work.
|
|
279
|
+
*/
|
|
280
|
+
abort(operationId?: string): Promise<boolean>;
|
|
281
|
+
/** Start a new context, optionally from a handoff note. */
|
|
282
|
+
reset(handoff?: string): Promise<void>;
|
|
283
|
+
setModel(model: ModelRef): Promise<void>;
|
|
284
|
+
/** The active transcript, as pi's entries since the newest reset. */
|
|
285
|
+
messages(): Promise<EntryRecord[]>;
|
|
286
|
+
/** pi's agent events for this session: a snapshot, then one batch per commit. */
|
|
287
|
+
events(context?: Context$1): Promise<AgentEventStream>;
|
|
288
|
+
busy(): Promise<boolean>;
|
|
289
|
+
}
|
|
290
|
+
/** Every pi conversation in this object. */
|
|
291
|
+
declare class PiSessions {
|
|
292
|
+
#private;
|
|
293
|
+
constructor(harness: PiHarness);
|
|
294
|
+
get(id: PiSessionId): PiSession;
|
|
295
|
+
/** A new top-level session, configured like the root. */
|
|
296
|
+
create(): Promise<PiSession>;
|
|
297
|
+
/** A new session that sees `from`'s history up to its newest entry. */
|
|
298
|
+
fork(from: PiSessionId): Promise<PiSession>;
|
|
299
|
+
/**
|
|
300
|
+
* Every conversation, including ones subagent tools own. Read from pi's
|
|
301
|
+
* storage directly: the Harness has no conversation listing.
|
|
302
|
+
*/
|
|
303
|
+
list(): Promise<PiSessionInfo[]>;
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/harnesses/pi/skills.d.ts
|
|
307
|
+
/** Skills resolved from `agents/skills` sources for one process lifetime. */
|
|
308
|
+
type ResolvedSkills = {
|
|
309
|
+
/** Changes when any source's fingerprint changes. */ readonly fingerprint: string /** Model-facing activation tools; empty when there are no skills. */;
|
|
310
|
+
readonly tools: readonly ToolRegistration[] /** System-prompt catalog, or null when there are no skills. */;
|
|
311
|
+
readonly catalog: string | null;
|
|
312
|
+
readonly warnings: readonly string[];
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Register `agents/skills` sources on a pi registry: the activation tools
|
|
316
|
+
* and a "skills" system prompt section. Resolution reads the sources, so a
|
|
317
|
+
* host awaits this in its `harness` factory, before `Harness.open`.
|
|
318
|
+
*/
|
|
319
|
+
declare function addSkills(
|
|
320
|
+
registry: Registry,
|
|
321
|
+
sources: readonly SkillSource[]
|
|
322
|
+
): Promise<ResolvedSkills>;
|
|
323
|
+
//#endregion
|
|
324
|
+
export {
|
|
325
|
+
PiHarness,
|
|
326
|
+
type PiHarnessContext,
|
|
327
|
+
type PiHarnessFactory,
|
|
328
|
+
type PiHarnessOptions,
|
|
329
|
+
type PiOperationResult,
|
|
330
|
+
type PiPendingOperation,
|
|
331
|
+
type PiPromptResponse,
|
|
332
|
+
type PiReceipt,
|
|
333
|
+
PiSession,
|
|
334
|
+
type PiSessionDefaults,
|
|
335
|
+
type PiSessionId,
|
|
336
|
+
type PiSessionInfo,
|
|
337
|
+
type PiSessionOptions,
|
|
338
|
+
type PiSessionStoreOptions,
|
|
339
|
+
PiSessions,
|
|
340
|
+
type PiSubmitOptions,
|
|
341
|
+
type PiWakeTiming,
|
|
342
|
+
type PiWhenBusy,
|
|
343
|
+
ROOT_SESSION,
|
|
344
|
+
type ResolvedSkills,
|
|
345
|
+
addSkills,
|
|
346
|
+
openPiSessionStore
|
|
347
|
+
};
|
|
348
|
+
//# sourceMappingURL=index.d.ts.map
|