@deepagents/text2sql 0.12.1 → 0.13.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/index.js +128 -69
- package/dist/index.js.map +3 -3
- package/dist/lib/adapters/groundings/index.js +65 -6
- package/dist/lib/adapters/groundings/index.js.map +3 -3
- package/dist/lib/adapters/mysql/index.js +65 -6
- package/dist/lib/adapters/mysql/index.js.map +3 -3
- package/dist/lib/adapters/postgres/index.js +65 -6
- package/dist/lib/adapters/postgres/index.js.map +3 -3
- package/dist/lib/adapters/spreadsheet/index.js +1 -0
- package/dist/lib/adapters/spreadsheet/index.js.map +3 -3
- package/dist/lib/adapters/sqlite/index.js +65 -6
- package/dist/lib/adapters/sqlite/index.js.map +3 -3
- package/dist/lib/adapters/sqlserver/index.js +65 -6
- package/dist/lib/adapters/sqlserver/index.js.map +3 -3
- package/dist/lib/agents/result-tools.d.ts +9 -3
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/instructions.d.ts.map +1 -1
- package/dist/lib/sql.d.ts +4 -15
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +65 -6
- package/dist/lib/synthesis/index.js.map +3 -3
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../src/lib/synthesis/types.ts", "../../../src/lib/synthesis/decorators/filtered-producer.ts", "../../../src/lib/synthesis/decorators/deduplicated-producer.ts", "../../../src/lib/synthesis/decorators/validated-producer.ts", "../../../src/lib/synthesis/extractors/message-extractor.ts", "../../../src/lib/synthesis/extractors/base-contextual-extractor.ts", "../../../../context/src/lib/
|
|
4
|
-
"sourcesContent": ["/**\n * A question/SQL pair extracted or synthesized for training data.\n */\nexport interface ExtractedPair {\n question: string;\n sql: string;\n context?: string[];\n success: boolean;\n}\n\n/**\n * Interface for all pair producers (extractors and synthesizers).\n * Implementations encapsulate their specific inputs and logic.\n */\nexport abstract class PairProducer<T extends ExtractedPair = ExtractedPair> {\n /**\n * Produce question/SQL pairs.\n */\n abstract produce(): AsyncGenerator<T[], void, unknown>;\n\n protected from(producer: PairProducer<ExtractedPair> | ExtractedPair[]) {\n return Array.isArray(producer)\n ? (async function* (pairs: ExtractedPair[]) {\n yield pairs;\n })(producer)\n : producer.produce();\n }\n\n public toPairs(): Promise<T[]> {\n return toPairs(this);\n }\n}\n\n/**\n * Entry point for producing pairs from any source.\n */\nexport async function toPairs<T extends ExtractedPair>(\n producer: PairProducer<T>,\n): Promise<T[]> {\n const pairs: T[] = [];\n for await (const chunk of producer.produce()) {\n pairs.push(...chunk);\n }\n return pairs;\n}\n", "import { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface FilteredProducerOptions {\n successOnly?: boolean;\n tables?: string[];\n filter?: (pair: ExtractedPair) => boolean;\n}\n\n/**\n * FilteredProducer - Filter pairs from another producer.\n *\n * Wraps another PairProducer and filters the output based on criteria.\n */\nexport class FilteredProducer extends PairProducer {\n /**\n * @param producer - Source producer to filter\n * @param options - Filter configuration\n */\n constructor(\n private producer: PairProducer,\n private options: FilteredProducerOptions = {},\n ) {\n super();\n }\n\n /**\n * Produces pairs filtered by success status, table usage, and custom predicates.\n * @returns Pairs matching all configured filter criteria\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n for await (const chunk of this.producer.produce()) {\n const filtered = chunk.filter((pair) => {\n if (this.options.successOnly !== false && !pair.success) {\n return false;\n }\n\n if (this.options.tables?.length) {\n const sqlLower = pair.sql.toLowerCase();\n const hasTable = this.options.tables.some((t) =>\n sqlLower.includes(t.toLowerCase()),\n );\n if (!hasTable) return false;\n }\n\n if (this.options.filter && !this.options.filter(pair)) {\n return false;\n }\n\n return true;\n });\n\n if (filtered.length) {\n yield filtered;\n }\n }\n }\n}\n", "import { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface DeduplicatedProducerOptions {\n strategy?: 'exact' | 'sql-only' | 'question-only';\n}\n\n/**\n * DeduplicatedProducer - Remove duplicate pairs from another producer.\n *\n * Wraps another PairProducer and removes duplicates based on\n * exact match or semantic similarity.\n */\nexport class DeduplicatedProducer extends PairProducer {\n /**\n * @param producer - Source producer to deduplicate\n * @param options - Deduplication configuration\n */\n constructor(\n private producer: PairProducer,\n private options: DeduplicatedProducerOptions = {},\n ) {\n super();\n }\n\n /**\n * Produces pairs with duplicates removed based on the configured strategy.\n * @returns Unique pairs after deduplication\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n const { strategy = 'exact' } = this.options;\n const seen = new Set<string>();\n\n for await (const chunk of this.producer.produce()) {\n const unique: ExtractedPair[] = [];\n\n for (const pair of chunk) {\n let key: string;\n\n switch (strategy) {\n case 'sql-only':\n key = this.normalizeSQL(pair.sql);\n break;\n case 'question-only':\n key = pair.question.toLowerCase().trim();\n break;\n case 'exact':\n default:\n key = `${pair.question.toLowerCase().trim()}|||${this.normalizeSQL(pair.sql)}`;\n }\n\n if (!seen.has(key)) {\n seen.add(key);\n unique.push(pair);\n }\n }\n\n if (unique.length) {\n yield unique;\n }\n }\n }\n\n private normalizeSQL(sql: string): string {\n return sql.toLowerCase().replace(/\\s+/g, ' ').trim();\n }\n}\n", "import type { Adapter } from '../../adapters/adapter.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface ValidatedProducerOptions {\n execute?: boolean;\n removeInvalid?: boolean;\n}\n\nexport interface ValidatedPair extends ExtractedPair {\n rowCount?: number;\n error?: string;\n}\n/**\n * ValidatedProducer - Validate SQL from another producer.\n *\n * Wraps another PairProducer and validates each SQL query,\n * optionally executing to attach results.\n */\nexport class ValidatedProducer extends PairProducer<ValidatedPair> {\n /**\n * @param producer - Source producer to validate\n * @param adapter - Database adapter for SQL validation\n * @param options - Validation configuration\n */\n constructor(\n private producer: PairProducer,\n private adapter: Adapter,\n private options: ValidatedProducerOptions = {},\n ) {\n super();\n }\n\n /**\n * Produces pairs with SQL validation applied, optionally executing queries.\n * @returns Validated pairs with error/rowCount metadata attached\n */\n async *produce(): AsyncGenerator<ValidatedPair[]> {\n for await (const chunk of this.producer.produce()) {\n const validated: ValidatedPair[] = [];\n\n for (const pair of chunk) {\n const error = await this.adapter.validate(pair.sql);\n\n if (error) {\n if (!this.options.removeInvalid) {\n validated.push({\n ...pair,\n success: false,\n error,\n });\n }\n continue;\n }\n\n let rowCount: number | undefined;\n if (this.options.execute) {\n try {\n const result = await this.adapter.execute(pair.sql);\n rowCount = Array.isArray(result) ? result.length : undefined;\n } catch {\n // no op\n }\n }\n\n validated.push({\n ...pair,\n success: true,\n rowCount,\n });\n }\n\n if (validated.length) {\n yield validated;\n }\n }\n }\n}\n", "import {\n type UIMessage,\n getToolOrDynamicToolName,\n isToolOrDynamicToolUIPart,\n} from 'ai';\n\nimport { type ExtractedPair, PairProducer } from '../types.ts';\nimport {\n type DbQueryInput,\n getMessageText,\n} from './base-contextual-extractor.ts';\n\nexport interface MessageExtractorOptions {\n includeFailures?: boolean;\n toolName?: string;\n}\n/**\n * MessageExtractor - Extract pairs from chat history by parsing tool calls.\n *\n * Deterministic extraction: parses db_query tool calls and pairs them\n * with the preceding user message.\n */\nexport class MessageExtractor extends PairProducer {\n #messages: UIMessage[];\n #options: MessageExtractorOptions;\n\n /**\n * @param messages - Chat history to extract pairs from\n * @param options - Extraction configuration\n */\n constructor(messages: UIMessage[], options: MessageExtractorOptions = {}) {\n super();\n this.#messages = messages;\n this.#options = options;\n }\n\n /**\n * Extracts question-SQL pairs by parsing tool calls and pairing with user messages.\n * @returns Pairs extracted from db_query tool invocations\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n const { includeFailures = false, toolName = 'db_query' } = this.#options;\n let lastUserMessage: UIMessage | null = null;\n\n for (const message of this.#messages) {\n if (message.role === 'user') {\n lastUserMessage = message;\n continue;\n }\n\n if (message.role === 'assistant' && lastUserMessage) {\n for (const part of message.parts) {\n if (!isToolOrDynamicToolUIPart(part)) {\n continue;\n }\n\n if (getToolOrDynamicToolName(part) !== toolName) {\n continue;\n }\n\n // Handle both static and dynamic tool part shapes\n const toolInput = ('input' in part ? part.input : undefined) as\n | DbQueryInput\n | undefined;\n if (!toolInput?.sql) {\n continue;\n }\n\n const success = part.state === 'output-available';\n const failed = part.state === 'output-error';\n\n if (failed && !includeFailures) {\n continue;\n }\n\n // Skip incomplete tool calls (streaming or pending)\n if (!success && !failed) {\n continue;\n }\n\n const question = getMessageText(lastUserMessage);\n if (!question) {\n continue;\n }\n\n yield [\n {\n question,\n sql: toolInput.sql,\n success,\n },\n ];\n }\n }\n }\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport {\n type UIMessage,\n getToolOrDynamicToolName,\n isTextUIPart,\n isToolOrDynamicToolUIPart,\n} from 'ai';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface DbQueryInput {\n sql: string;\n reasoning?: string;\n}\n\nexport interface SqlWithContext {\n sql: string;\n success: boolean;\n conversationContext: string[];\n}\n\nexport interface BaseContextualExtractorOptions {\n includeFailures?: boolean;\n toolName?: string;\n}\n\nconst contextResolverSchema = z.object({\n question: z\n .string()\n .describe(\n 'A standalone natural language question that the SQL query answers',\n ),\n});\n\n/**\n * Resolves a SQL query with conversation context into a standalone question.\n */\nexport async function resolveContext(params: {\n conversation: string;\n sql: string;\n introspection?: string;\n}): Promise<{ question: string }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `context-resolver-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'context_resolver',\n role: 'You are an expert at understanding conversational context and generating clear, standalone questions from multi-turn conversations.',\n objective:\n 'Transform context-dependent messages into standalone questions that fully capture user intent',\n }),\n ...(params.introspection\n ? [fragment('database_schema', params.introspection)]\n : []),\n fragment('conversation', params.conversation),\n fragment('sql', params.sql),\n fragment(\n 'task',\n dedent`\n Given the conversation above and the SQL query that was executed,\n generate a single, standalone natural language question that:\n 1. Fully captures the user's intent without needing prior context\n 2. Uses natural business language (not SQL terminology)\n 3. Could be asked by someone who hasn't seen the conversation\n 4. Accurately represents what the SQL query answers\n `,\n ),\n fragment(\n 'examples',\n dedent`\n Conversation: \"Show me customers\" \u2192 \"Filter to NY\" \u2192 \"Sort by revenue\"\n SQL: SELECT * FROM customers WHERE region = 'NY' ORDER BY revenue DESC\n Question: \"Show me customers in the NY region sorted by revenue\"\n\n Conversation: \"What were sales last month?\" \u2192 \"Break it down by category\"\n SQL: SELECT category, SUM(amount) FROM sales WHERE date >= '2024-11-01' GROUP BY category\n Question: \"What were sales by category for last month?\"\n `,\n ),\n user('Generate a standalone question for this SQL query.'),\n );\n\n const resolverOutput = structuredOutput({\n model: groq('openai/gpt-oss-20b'),\n context,\n schema: contextResolverSchema,\n });\n\n return resolverOutput.generate();\n}\n\nexport function getMessageText(message: UIMessage): string {\n const textParts = message.parts.filter(isTextUIPart).map((part) => part.text);\n return textParts.join(' ').trim();\n}\n\nexport function formatConversation(messages: string[]): string {\n return messages.map((msg, i) => `[${i + 1}] ${msg}`).join('\\n');\n}\n\n/**\n * Abstract base class for contextual extractors using Template Pattern.\n *\n * The `produce()` method defines the algorithm skeleton:\n * 1. Iterate through messages\n * 2. Call `onUserMessage()` hook for user messages\n * 3. Extract SQL from assistant messages using `getContextSnapshot()` hook\n * 4. Resolve questions using LLM\n *\n * Subclasses implement the hooks to customize context management.\n */\nexport abstract class BaseContextualExtractor extends PairProducer {\n protected context: string[] = [];\n protected results: SqlWithContext[] = [];\n protected messages: UIMessage[];\n protected adapter: Adapter;\n protected options: BaseContextualExtractorOptions;\n\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: BaseContextualExtractorOptions = {},\n ) {\n super();\n this.messages = messages;\n this.adapter = adapter;\n this.options = options;\n }\n\n /**\n * Template method - defines the extraction algorithm skeleton.\n * Subclasses customize behavior via hooks, not by overriding this method.\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n // Reset state for each produce() invocation to prevent race conditions\n // if produce() is called multiple times concurrently\n this.context = [];\n this.results = [];\n\n const { includeFailures = false, toolName = 'db_query' } = this.options;\n\n // Step 1: Extract SQLs with context (calls hooks)\n await this.extractSqlsWithContext(toolName, includeFailures);\n\n if (this.results.length === 0) {\n return;\n }\n\n // Step 2: Get introspection for schema context\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n\n // Step 3: Resolve each SQL's context into a standalone question\n yield* this.resolveQuestions(introspection);\n }\n\n /**\n * Core extraction loop - iterates through messages and calls hooks.\n */\n private async extractSqlsWithContext(\n toolName: string,\n includeFailures: boolean,\n ): Promise<void> {\n for (const message of this.messages) {\n if (message.role === 'user') {\n const text = getMessageText(message);\n if (text) {\n await this.onUserMessage(text);\n }\n continue;\n }\n\n if (message.role === 'assistant') {\n await this.extractFromAssistant(message, toolName, includeFailures);\n }\n }\n }\n\n /**\n * Extract SQL from assistant message parts.\n */\n private async extractFromAssistant(\n message: UIMessage,\n toolName: string,\n includeFailures: boolean,\n ): Promise<void> {\n for (const part of message.parts) {\n if (!isToolOrDynamicToolUIPart(part)) {\n continue;\n }\n\n if (getToolOrDynamicToolName(part) !== toolName) {\n continue;\n }\n\n // Use 'input' property (not 'args') to match useChat structure\n const toolInput = ('input' in part ? part.input : undefined) as\n | DbQueryInput\n | undefined;\n if (!toolInput?.sql) {\n continue;\n }\n\n const success = part.state === 'output-available';\n const failed = part.state === 'output-error';\n\n if (failed && !includeFailures) {\n continue;\n }\n\n // Skip if still streaming or not yet executed\n if (!success && !failed) {\n continue;\n }\n\n const snapshot = this.getContextSnapshot();\n // Skip if no context available\n if (snapshot.length === 0) {\n continue;\n }\n\n this.results.push({\n sql: toolInput.sql,\n success,\n conversationContext: snapshot,\n });\n }\n\n // Add assistant text responses to context (for multi-turn understanding)\n const assistantText = getMessageText(message);\n if (assistantText) {\n this.context.push(`Assistant: ${assistantText}`);\n }\n }\n\n /**\n * Resolve extracted SQL contexts into standalone questions using LLM.\n */\n protected async *resolveQuestions(\n introspection: string,\n ): AsyncGenerator<ExtractedPair[]> {\n for (const item of this.results) {\n const output = await resolveContext({\n conversation: formatConversation(item.conversationContext),\n sql: item.sql,\n introspection,\n });\n\n yield [\n {\n question: output.question,\n sql: item.sql,\n context: item.conversationContext,\n success: item.success,\n },\n ];\n }\n }\n\n /**\n * Hook called when a user message is encountered.\n * Subclasses implement this to decide how to update context.\n */\n protected abstract onUserMessage(text: string): Promise<void>;\n\n /**\n * Hook called when extracting SQL to get the current context snapshot.\n * Subclasses implement this to decide what context to include.\n */\n protected abstract getContextSnapshot(): string[];\n}\n", "import { encode } from 'gpt-tokenizer';\n\nimport type { ContextFragment } from './fragments.ts';\nimport type { Models } from './models.generated.ts';\nimport type { ContextRenderer } from './renderers/abstract.renderer.ts';\n\n/**\n * Cost information for a model (prices per 1M tokens)\n */\nexport interface ModelCost {\n input: number;\n output: number;\n cache_read?: number;\n cache_write?: number;\n reasoning?: number;\n}\n\n/**\n * Model information from models.dev\n */\nexport interface ModelInfo {\n id: string;\n name: string;\n family: string;\n cost: ModelCost;\n limit: {\n context: number;\n output: number;\n };\n provider: string;\n}\n\n/**\n * Estimate for a single fragment\n */\nexport interface FragmentEstimate {\n name: string;\n id?: string;\n tokens: number;\n cost: number;\n}\n\n/**\n * Estimate result returned by the estimate function\n */\nexport interface EstimateResult {\n model: string;\n provider: string;\n tokens: number;\n cost: number;\n limits: {\n context: number;\n output: number;\n exceedsContext: boolean;\n };\n fragments: FragmentEstimate[];\n}\n\n/**\n * Tokenizer interface for counting tokens\n */\nexport interface Tokenizer {\n encode(text: string): number[];\n count(text: string): number;\n}\n\n/**\n * Default tokenizer using gpt-tokenizer\n * Works reasonably well for most models (~5-10% variance)\n */\nexport const defaultTokenizer: Tokenizer = {\n encode(text: string): number[] {\n return encode(text);\n },\n count(text: string): number {\n return encode(text).length;\n },\n};\n\ntype ModelsDevResponse = Record<\n string,\n {\n id: string;\n name: string;\n models: Record<\n string,\n {\n id: string;\n name: string;\n family: string;\n cost: ModelCost;\n limit: { context: number; output: number };\n }\n >;\n }\n>;\n\n/**\n * Registry for AI model information from models.dev\n * Caches data and provides lookup by model ID\n */\nexport class ModelsRegistry {\n #cache: Map<string, ModelInfo> = new Map();\n #loaded = false;\n #tokenizers: Map<string, Tokenizer> = new Map();\n #defaultTokenizer: Tokenizer = defaultTokenizer;\n\n /**\n * Load models data from models.dev API\n */\n async load(): Promise<void> {\n if (this.#loaded) return;\n\n const response = await fetch('https://models.dev/api.json');\n if (!response.ok) {\n throw new Error(`Failed to fetch models: ${response.statusText}`);\n }\n\n const data = (await response.json()) as ModelsDevResponse;\n\n for (const [providerId, provider] of Object.entries(data)) {\n for (const [modelId, model] of Object.entries(provider.models)) {\n const info: ModelInfo = {\n id: model.id,\n name: model.name,\n family: model.family,\n cost: model.cost,\n limit: model.limit,\n provider: providerId,\n };\n\n // Store by full ID (provider:model)\n this.#cache.set(`${providerId}:${modelId}`, info);\n }\n }\n\n this.#loaded = true;\n }\n\n /**\n * Get model info by ID\n * @param modelId - Model ID (e.g., \"openai:gpt-4o\")\n */\n get(modelId: string): ModelInfo | undefined {\n return this.#cache.get(modelId);\n }\n\n /**\n * Check if a model exists in the registry\n */\n has(modelId: string): boolean {\n return this.#cache.has(modelId);\n }\n\n /**\n * List all available model IDs\n */\n list(): string[] {\n return [...this.#cache.keys()];\n }\n\n /**\n * Register a custom tokenizer for specific model families\n * @param family - Model family name (e.g., \"llama\", \"claude\")\n * @param tokenizer - Tokenizer implementation\n */\n registerTokenizer(family: string, tokenizer: Tokenizer): void {\n this.#tokenizers.set(family, tokenizer);\n }\n\n /**\n * Set the default tokenizer used when no family-specific tokenizer is registered\n */\n setDefaultTokenizer(tokenizer: Tokenizer): void {\n this.#defaultTokenizer = tokenizer;\n }\n\n /**\n * Get the appropriate tokenizer for a model\n */\n getTokenizer(modelId: string): Tokenizer {\n const model = this.get(modelId);\n if (model) {\n const familyTokenizer = this.#tokenizers.get(model.family);\n if (familyTokenizer) {\n return familyTokenizer;\n }\n }\n return this.#defaultTokenizer;\n }\n\n /**\n * Estimate token count and cost for given text and model\n * @param modelId - Model ID to use for pricing (e.g., \"openai:gpt-4o\")\n * @param input - Input text (prompt)\n */\n estimate(modelId: Models, input: string): EstimateResult {\n const model = this.get(modelId);\n if (!model) {\n throw new Error(\n `Model \"${modelId}\" not found. Call load() first or check model ID.`,\n );\n }\n\n const tokenizer = this.getTokenizer(modelId);\n const tokens = tokenizer.count(input);\n const cost = (tokens / 1_000_000) * model.cost.input;\n\n return {\n model: model.id,\n provider: model.provider,\n tokens,\n cost,\n limits: {\n context: model.limit.context,\n output: model.limit.output,\n exceedsContext: tokens > model.limit.context,\n },\n fragments: [],\n };\n }\n}\n\n// Singleton instance for convenience\nlet _registry: ModelsRegistry | null = null;\n\n/**\n * Get the shared ModelsRegistry instance\n */\nexport function getModelsRegistry(): ModelsRegistry {\n if (!_registry) {\n _registry = new ModelsRegistry();\n }\n return _registry;\n}\n\n/**\n * Convenience function to estimate cost for a model\n * Automatically loads the registry if not already loaded\n *\n * @param modelId - Model ID (e.g., \"openai:gpt-4o\", \"anthropic:claude-3-5-sonnet\")\n * @param renderer - Renderer to use for converting fragments to text\n * @param fragments - Context fragments to estimate\n */\nexport async function estimate(\n modelId: Models,\n renderer: ContextRenderer,\n ...fragments: ContextFragment[]\n): Promise<EstimateResult> {\n const registry = getModelsRegistry();\n await registry.load();\n\n // Calculate total (all fragments rendered together)\n const input = renderer.render(fragments);\n const model = registry.get(modelId);\n if (!model) {\n throw new Error(\n `Model \"${modelId}\" not found. Call load() first or check model ID.`,\n );\n }\n\n const tokenizer = registry.getTokenizer(modelId);\n const totalTokens = tokenizer.count(input);\n const totalCost = (totalTokens / 1_000_000) * model.cost.input;\n\n // Calculate per-fragment estimates\n const fragmentEstimates: FragmentEstimate[] = fragments.map((fragment) => {\n const rendered = renderer.render([fragment]);\n const tokens = tokenizer.count(rendered);\n const cost = (tokens / 1_000_000) * model.cost.input;\n return {\n id: fragment.id,\n name: fragment.name,\n tokens,\n cost,\n };\n });\n\n return {\n model: model.id,\n provider: model.provider,\n tokens: totalTokens,\n cost: totalCost,\n limits: {\n context: model.limit.context,\n output: model.limit.output,\n exceedsContext: totalTokens > model.limit.context,\n },\n fragments: fragmentEstimates,\n };\n}\n", "import { type UIMessage, generateId } from 'ai';\n\nimport type { FragmentCodec } from './codec.ts';\n\n/**\n * Fragment type identifier.\n * - 'fragment': Regular context fragment (default)\n * - 'message': Conversation message (user/assistant)\n */\nexport type FragmentType = 'fragment' | 'message';\n\n/**\n * A context fragment containing a name and associated data.\n */\nexport interface ContextFragment<T extends FragmentData = FragmentData> {\n /**\n * Unique identifier for this fragment.\n * Auto-generated for user/assistant messages, optional for other fragments.\n */\n id?: string;\n name: string;\n data: T;\n /**\n * Fragment type for categorization.\n * Messages use 'message' type and are handled separately during resolve().\n */\n type?: FragmentType;\n /**\n * When true, this fragment will be persisted to the store on save().\n */\n persist?: boolean;\n /**\n * Codec for encoding/decoding this fragment.\n * Used by resolve() to convert to AI SDK format.\n */\n codec?: FragmentCodec;\n /**\n * Optional metadata for internal tracking.\n * Not rendered to prompt, used for operational purposes like path remapping.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Fragment data can be a primitive, array, object, or nested fragment.\n */\nexport type FragmentData =\n | string\n | number\n | null\n | undefined\n | boolean\n | ContextFragment\n | FragmentData[]\n | { [key: string]: FragmentData };\n\n/**\n * Type guard to check if data is a ContextFragment.\n */\nexport function isFragment(data: unknown): data is ContextFragment {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'name' in data &&\n 'data' in data &&\n typeof (data as ContextFragment).name === 'string'\n );\n}\n\n/**\n * A plain object with string keys and FragmentData values.\n */\nexport type FragmentObject = Record<string, FragmentData>;\n\n/**\n * Type guard to check if data is a plain object (not array, not fragment, not primitive).\n */\nexport function isFragmentObject(data: unknown): data is FragmentObject {\n return (\n typeof data === 'object' &&\n data !== null &&\n !Array.isArray(data) &&\n !isFragment(data)\n );\n}\n\n/**\n * Type guard to check if a fragment is a message fragment.\n */\nexport function isMessageFragment(fragment: ContextFragment): boolean {\n return fragment.type === 'message';\n}\n\nexport function fragment(\n name: string,\n ...children: FragmentData[]\n): ContextFragment {\n return {\n name,\n data: children,\n };\n}\n\n/**\n * Create a user message fragment.\n * Message fragments are separated from regular fragments during resolve().\n *\n * @param content - The message content\n * @param options - Optional settings (id)\n *\n * @example\n * ```ts\n * context.set(user('Hello')); // Auto-generated ID\n * context.set(user('Hello', { id: 'msg-1' })); // Custom ID\n * ```\n */\nexport function user(content: string | UIMessage): ContextFragment {\n const message =\n typeof content === 'string'\n ? {\n id: generateId(),\n role: 'user',\n parts: [{ type: 'text', text: content }],\n }\n : content;\n return {\n id: message.id,\n name: 'user',\n data: 'content',\n type: 'message',\n persist: true,\n codec: {\n decode() {\n return message;\n },\n encode() {\n return message;\n },\n },\n };\n}\n\n/**\n * Create an assistant message fragment.\n * Message fragments are separated from regular fragments during resolve().\n *\n * @param message - The message content\n * @param options - Optional settings (id)\n *\n * @example\n * ```ts\n * context.set(assistant('Hi there!')); // Auto-generated ID\n * context.set(assistant('Hi there!', { id: 'resp-1' })); // Custom ID\n * ```\n */\nexport function assistant(message: UIMessage): ContextFragment {\n return {\n id: message.id,\n name: 'assistant',\n data: 'content',\n type: 'message',\n persist: true,\n codec: {\n decode() {\n return message;\n },\n encode() {\n return message;\n },\n },\n };\n}\nexport function message(content: string | UIMessage): ContextFragment {\n const message =\n typeof content === 'string'\n ? {\n id: generateId(),\n role: 'user' as const,\n parts: [{ type: 'text', text: content }],\n }\n : content;\n return {\n id: message.id,\n name: message.role,\n data: 'content',\n type: 'message',\n persist: true,\n codec: {\n decode() {\n return message;\n },\n encode() {\n return message;\n },\n },\n };\n}\n\n/**\n * Create an assistant message fragment from text content.\n * Convenience wrapper that creates a UIMessage internally.\n *\n * @param content - The message text content\n * @param options - Optional settings (id)\n *\n * @example\n * ```ts\n * context.set(assistantText('Hi there!')); // Auto-generated ID\n * context.set(assistantText('Hi there!', { id: 'resp-1' })); // Custom ID\n * ```\n */\nexport function assistantText(\n content: string,\n options?: { id?: string },\n): ContextFragment {\n const id = options?.id ?? crypto.randomUUID();\n return assistant({\n id,\n role: 'assistant',\n parts: [{ type: 'text', text: content }],\n });\n}\n\n/**\n * Symbol to mark fragments for lazy ID resolution.\n * @internal\n */\nexport const LAZY_ID = Symbol('lazy-id');\n\n/**\n * Lazy fragment configuration for ID resolution.\n */\nexport interface LazyConfig {\n type: 'last-assistant';\n content: string;\n}\n\n/**\n * Lazy fragment that gets its ID resolved during save().\n */\nexport interface LazyFragment extends ContextFragment {\n [LAZY_ID]?: LazyConfig;\n}\n\n/**\n * Check if a fragment needs lazy ID resolution.\n */\nexport function isLazyFragment(\n fragment: ContextFragment,\n): fragment is LazyFragment {\n return LAZY_ID in fragment;\n}\n\n/**\n * Create an assistant message fragment that uses the last assistant's ID.\n *\n * - If a pending/persisted assistant message exists, updates it\n * - If none exists, creates a new assistant message\n *\n * Useful for self-correction flows where retries should update\n * the same message instead of creating duplicates.\n *\n * @example\n * ```ts\n * // In guardrail retry loop:\n * context.set(lastAssistantMessage(correctedContent));\n * await context.save(); // ID resolved here\n * ```\n */\nexport function lastAssistantMessage(content: string): ContextFragment {\n return {\n name: 'assistant',\n type: 'message',\n persist: true,\n data: 'content',\n [LAZY_ID]: {\n type: 'last-assistant',\n content,\n },\n } as LazyFragment;\n}\n", "import pluralize from 'pluralize';\nimport { titlecase } from 'stringcase';\n\nimport {\n type ContextFragment,\n type FragmentData,\n type FragmentObject,\n isFragment,\n isFragmentObject,\n} from '../fragments.ts';\n\n/**\n * Render context passed through the template method.\n */\nexport interface RenderContext {\n depth: number;\n path: string[];\n}\n\n/**\n * Options for renderers.\n */\nexport interface RendererOptions {\n /**\n * When true, fragments with the same name are grouped under a pluralized parent tag.\n * e.g., multiple <hint> become <hints><hint>...</hint><hint>...</hint></hints>\n */\n groupFragments?: boolean;\n}\n\n/**\n * Base renderer implementing the Template Method pattern.\n * Subclasses implement the specific formatting hooks.\n */\nexport abstract class ContextRenderer {\n protected options: RendererOptions;\n\n constructor(options: RendererOptions = {}) {\n this.options = options;\n }\n\n abstract render(fragments: ContextFragment[]): string;\n\n /**\n * Check if data is a primitive (string, number, boolean).\n */\n protected isPrimitive(data: FragmentData): data is string | number | boolean {\n return (\n typeof data === 'string' ||\n typeof data === 'number' ||\n typeof data === 'boolean'\n );\n }\n\n /**\n * Group fragments by name for groupFragments option.\n */\n protected groupByName(\n fragments: ContextFragment[],\n ): Map<string, ContextFragment[]> {\n const groups = new Map<string, ContextFragment[]>();\n for (const fragment of fragments) {\n const existing = groups.get(fragment.name) ?? [];\n existing.push(fragment);\n groups.set(fragment.name, existing);\n }\n return groups;\n }\n\n /**\n * Remove null/undefined from fragments and fragment data recursively.\n * This protects renderers from nullish values and ensures they are ignored\n * consistently across all output formats.\n */\n protected sanitizeFragments(fragments: ContextFragment[]): ContextFragment[] {\n const sanitized: ContextFragment[] = [];\n for (const fragment of fragments) {\n const cleaned = this.sanitizeFragment(fragment, new WeakSet<object>());\n if (cleaned) {\n sanitized.push(cleaned);\n }\n }\n return sanitized;\n }\n\n protected sanitizeFragment(\n fragment: ContextFragment,\n seen: WeakSet<object>,\n ): ContextFragment | null {\n const data = this.sanitizeData(fragment.data, seen);\n if (data == null) {\n return null;\n }\n return {\n ...fragment,\n data,\n };\n }\n\n protected sanitizeData(\n data: FragmentData,\n seen: WeakSet<object>,\n ): FragmentData | undefined {\n if (data == null) {\n return undefined;\n }\n\n if (isFragment(data)) {\n return this.sanitizeFragment(data, seen) ?? undefined;\n }\n\n if (Array.isArray(data)) {\n if (seen.has(data)) {\n return undefined;\n }\n seen.add(data);\n\n const cleaned: FragmentData[] = [];\n for (const item of data) {\n const sanitizedItem = this.sanitizeData(item, seen);\n if (sanitizedItem != null) {\n cleaned.push(sanitizedItem);\n }\n }\n return cleaned;\n }\n\n if (isFragmentObject(data)) {\n if (seen.has(data)) {\n return undefined;\n }\n seen.add(data);\n\n const cleaned: FragmentObject = {};\n for (const [key, value] of Object.entries(data)) {\n const sanitizedValue = this.sanitizeData(value, seen);\n if (sanitizedValue != null) {\n cleaned[key] = sanitizedValue;\n }\n }\n return cleaned;\n }\n\n return data;\n }\n\n /**\n * Template method - dispatches value to appropriate handler.\n */\n protected renderValue(\n key: string,\n value: unknown,\n ctx: RenderContext,\n ): string {\n if (value == null) {\n return '';\n }\n if (isFragment(value)) {\n return this.renderFragment(value, ctx);\n }\n if (Array.isArray(value)) {\n return this.renderArray(key, value, ctx);\n }\n if (isFragmentObject(value)) {\n return this.renderObject(key, value, ctx);\n }\n return this.renderPrimitive(key, String(value), ctx);\n }\n\n /**\n * Render a nested fragment - subclasses implement this.\n */\n protected abstract renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string;\n\n /**\n * Render all entries of an object.\n */\n protected renderEntries(data: FragmentObject, ctx: RenderContext): string[] {\n return Object.entries(data)\n .map(([key, value]) => this.renderValue(key, value, ctx))\n .filter(Boolean);\n }\n\n // Hooks - subclasses implement these\n protected abstract renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string;\n protected abstract renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string;\n protected abstract renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string;\n}\n\n/**\n * Renders context fragments as XML.\n */\nexport class XmlRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n const sanitized = this.sanitizeFragments(fragments);\n return sanitized\n .map((f) => this.#renderTopLevel(f))\n .filter(Boolean)\n .join('\\n');\n }\n\n #renderTopLevel(fragment: ContextFragment): string {\n if (this.isPrimitive(fragment.data)) {\n return this.#leafRoot(fragment.name, String(fragment.data));\n }\n if (Array.isArray(fragment.data)) {\n return this.#renderArray(fragment.name, fragment.data, 0);\n }\n if (isFragment(fragment.data)) {\n const child = this.renderFragment(fragment.data, { depth: 1, path: [] });\n return this.#wrap(fragment.name, [child]);\n }\n if (isFragmentObject(fragment.data)) {\n return this.#wrap(\n fragment.name,\n this.renderEntries(fragment.data, { depth: 1, path: [] }),\n );\n }\n return '';\n }\n\n #renderArray(name: string, items: FragmentData[], depth: number): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter((item) => !isFragment(item));\n\n const children: string[] = [];\n\n // Render non-fragment items\n for (const item of nonFragmentItems) {\n if (item != null) {\n if (isFragmentObject(item)) {\n // Recursively render plain objects\n children.push(\n this.#wrapIndented(\n pluralize.singular(name),\n this.renderEntries(item, { depth: depth + 2, path: [] }),\n depth + 1,\n ),\n );\n } else {\n children.push(\n this.#leaf(pluralize.singular(name), String(item), depth + 1),\n );\n }\n }\n }\n\n // Render fragment items (possibly grouped)\n if (this.options.groupFragments && fragmentItems.length > 0) {\n const groups = this.groupByName(fragmentItems);\n for (const [groupName, groupFragments] of groups) {\n const groupChildren = groupFragments.map((frag) =>\n this.renderFragment(frag, { depth: depth + 2, path: [] }),\n );\n const pluralName = pluralize.plural(groupName);\n children.push(this.#wrapIndented(pluralName, groupChildren, depth + 1));\n }\n } else {\n for (const frag of fragmentItems) {\n children.push(\n this.renderFragment(frag, { depth: depth + 1, path: [] }),\n );\n }\n }\n\n return this.#wrap(name, children);\n }\n\n #leafRoot(tag: string, value: string): string {\n const safe = this.#escape(value);\n if (safe.includes('\\n')) {\n return `<${tag}>\\n${this.#indent(safe, 2)}\\n</${tag}>`;\n }\n return `<${tag}>${safe}</${tag}>`;\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n if (this.isPrimitive(data)) {\n return this.#leaf(name, String(data), ctx.depth);\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, { ...ctx, depth: ctx.depth + 1 });\n return this.#wrapIndented(name, [child], ctx.depth);\n }\n if (Array.isArray(data)) {\n return this.#renderArrayIndented(name, data, ctx.depth);\n }\n if (isFragmentObject(data)) {\n const children = this.renderEntries(data, {\n ...ctx,\n depth: ctx.depth + 1,\n });\n return this.#wrapIndented(name, children, ctx.depth);\n }\n return '';\n }\n\n #renderArrayIndented(\n name: string,\n items: FragmentData[],\n depth: number,\n ): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter((item) => !isFragment(item));\n\n const children: string[] = [];\n\n // Render non-fragment items\n for (const item of nonFragmentItems) {\n if (item != null) {\n if (isFragmentObject(item)) {\n // Recursively render plain objects\n children.push(\n this.#wrapIndented(\n pluralize.singular(name),\n this.renderEntries(item, { depth: depth + 2, path: [] }),\n depth + 1,\n ),\n );\n } else {\n children.push(\n this.#leaf(pluralize.singular(name), String(item), depth + 1),\n );\n }\n }\n }\n\n // Render fragment items (possibly grouped)\n if (this.options.groupFragments && fragmentItems.length > 0) {\n const groups = this.groupByName(fragmentItems);\n for (const [groupName, groupFragments] of groups) {\n const groupChildren = groupFragments.map((frag) =>\n this.renderFragment(frag, { depth: depth + 2, path: [] }),\n );\n const pluralName = pluralize.plural(groupName);\n children.push(this.#wrapIndented(pluralName, groupChildren, depth + 1));\n }\n } else {\n for (const frag of fragmentItems) {\n children.push(\n this.renderFragment(frag, { depth: depth + 1, path: [] }),\n );\n }\n }\n\n return this.#wrapIndented(name, children, depth);\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n return this.#leaf(key, value, ctx.depth);\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n if (!items.length) {\n return '';\n }\n const itemTag = pluralize.singular(key);\n const children = items\n .filter((item) => item != null)\n .map((item) => {\n // Check for ContextFragment first (has name + data properties)\n if (isFragment(item)) {\n return this.renderFragment(item, { ...ctx, depth: ctx.depth + 1 });\n }\n // Then check for plain objects\n if (isFragmentObject(item)) {\n return this.#wrapIndented(\n itemTag,\n this.renderEntries(item, { ...ctx, depth: ctx.depth + 2 }),\n ctx.depth + 1,\n );\n }\n // Primitives\n return this.#leaf(itemTag, String(item), ctx.depth + 1);\n });\n return this.#wrapIndented(key, children, ctx.depth);\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const children = this.renderEntries(obj, { ...ctx, depth: ctx.depth + 1 });\n return this.#wrapIndented(key, children, ctx.depth);\n }\n\n #escape(value: string): string {\n if (value == null) {\n return '';\n }\n return value\n .replaceAll(/&/g, '&')\n .replaceAll(/</g, '<')\n .replaceAll(/>/g, '>')\n .replaceAll(/\"/g, '"')\n .replaceAll(/'/g, ''');\n }\n\n #indent(text: string, spaces: number): string {\n if (!text.trim()) {\n return '';\n }\n const padding = ' '.repeat(spaces);\n return text\n .split('\\n')\n .map((line) => (line.length ? padding + line : padding))\n .join('\\n');\n }\n\n #leaf(tag: string, value: string, depth: number): string {\n const safe = this.#escape(value);\n const pad = ' '.repeat(depth);\n if (safe.includes('\\n')) {\n return `${pad}<${tag}>\\n${this.#indent(safe, (depth + 1) * 2)}\\n${pad}</${tag}>`;\n }\n return `${pad}<${tag}>${safe}</${tag}>`;\n }\n\n #wrap(tag: string, children: string[]): string {\n const content = children.filter(Boolean).join('\\n');\n if (!content) {\n return '';\n }\n return `<${tag}>\\n${content}\\n</${tag}>`;\n }\n\n #wrapIndented(tag: string, children: string[], depth: number): string {\n const content = children.filter(Boolean).join('\\n');\n if (!content) {\n return '';\n }\n const pad = ' '.repeat(depth);\n return `${pad}<${tag}>\\n${content}\\n${pad}</${tag}>`;\n }\n}\n\n/**\n * Renders context fragments as Markdown.\n */\nexport class MarkdownRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n return this.sanitizeFragments(fragments)\n .map((f) => {\n const title = `## ${titlecase(f.name)}`;\n if (this.isPrimitive(f.data)) {\n return `${title}\\n${String(f.data)}`;\n }\n if (Array.isArray(f.data)) {\n return `${title}\\n${this.#renderArray(f.data, 0)}`;\n }\n if (isFragment(f.data)) {\n return `${title}\\n${this.renderFragment(f.data, { depth: 0, path: [] })}`;\n }\n if (isFragmentObject(f.data)) {\n return `${title}\\n${this.renderEntries(f.data, { depth: 0, path: [] }).join('\\n')}`;\n }\n return `${title}\\n`;\n })\n .join('\\n\\n');\n }\n\n #renderArray(items: FragmentData[], depth: number): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter((item) => !isFragment(item));\n\n const lines: string[] = [];\n\n // Render non-fragment items\n for (const item of nonFragmentItems) {\n if (item != null) {\n lines.push(`${this.#pad(depth)}- ${String(item)}`);\n }\n }\n\n // Render fragment items (possibly grouped)\n if (this.options.groupFragments && fragmentItems.length > 0) {\n const groups = this.groupByName(fragmentItems);\n for (const [groupName, groupFragments] of groups) {\n const pluralName = pluralize.plural(groupName);\n lines.push(`${this.#pad(depth)}- **${titlecase(pluralName)}**:`);\n for (const frag of groupFragments) {\n lines.push(this.renderFragment(frag, { depth: depth + 1, path: [] }));\n }\n }\n } else {\n for (const frag of fragmentItems) {\n lines.push(this.renderFragment(frag, { depth, path: [] }));\n }\n }\n\n return lines.join('\\n');\n }\n\n #pad(depth: number): string {\n return ' '.repeat(depth);\n }\n\n #leaf(key: string, value: string, depth: number): string {\n return `${this.#pad(depth)}- **${key}**: ${value}`;\n }\n\n #arrayItem(item: unknown, depth: number): string {\n if (isFragment(item)) {\n return this.renderFragment(item, { depth, path: [] });\n }\n if (isFragmentObject(item)) {\n return this.renderEntries(item, {\n depth,\n path: [],\n }).join('\\n');\n }\n return `${this.#pad(depth)}- ${String(item)}`;\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n const header = `${this.#pad(ctx.depth)}- **${name}**:`;\n if (this.isPrimitive(data)) {\n return `${this.#pad(ctx.depth)}- **${name}**: ${String(data)}`;\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, { ...ctx, depth: ctx.depth + 1 });\n return [header, child].join('\\n');\n }\n if (Array.isArray(data)) {\n const children = data\n .filter((item) => item != null)\n .map((item) => this.#arrayItem(item, ctx.depth + 1));\n return [header, ...children].join('\\n');\n }\n if (isFragmentObject(data)) {\n const children = this.renderEntries(data, {\n ...ctx,\n depth: ctx.depth + 1,\n }).join('\\n');\n return [header, children].join('\\n');\n }\n return header;\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n return this.#leaf(key, value, ctx.depth);\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n const header = `${this.#pad(ctx.depth)}- **${key}**:`;\n const children = items\n .filter((item) => item != null)\n .map((item) => this.#arrayItem(item, ctx.depth + 1));\n return [header, ...children].join('\\n');\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const header = `${this.#pad(ctx.depth)}- **${key}**:`;\n const children = this.renderEntries(obj, {\n ...ctx,\n depth: ctx.depth + 1,\n }).join('\\n');\n return [header, children].join('\\n');\n }\n}\n\n/**\n * Renders context fragments as TOML.\n */\nexport class TomlRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n const rendered: string[] = [];\n for (const f of this.sanitizeFragments(fragments)) {\n if (this.isPrimitive(f.data)) {\n rendered.push(`${f.name} = ${this.#formatValue(f.data)}`);\n } else if (Array.isArray(f.data)) {\n rendered.push(this.#renderTopLevelArray(f.name, f.data));\n } else if (isFragment(f.data)) {\n rendered.push(\n [\n `[${f.name}]`,\n this.renderFragment(f.data, { depth: 0, path: [f.name] }),\n ].join('\\n'),\n );\n } else if (isFragmentObject(f.data)) {\n const entries = this.#renderObjectEntries(f.data, [f.name]);\n rendered.push([`[${f.name}]`, ...entries].join('\\n'));\n }\n }\n return rendered.join('\\n\\n');\n }\n\n #renderTopLevelArray(name: string, items: FragmentData[]): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter(\n (item) => !isFragment(item) && item != null,\n );\n\n // If array contains fragments, render as sections\n if (fragmentItems.length > 0) {\n const parts: string[] = [`[${name}]`];\n for (const frag of fragmentItems) {\n parts.push(this.renderFragment(frag, { depth: 0, path: [name] }));\n }\n return parts.join('\\n');\n }\n\n // Otherwise render as inline array\n const values = nonFragmentItems.map((item) => this.#formatValue(item));\n return `${name} = [${values.join(', ')}]`;\n }\n\n /**\n * Override renderValue to preserve type information for TOML formatting.\n */\n protected override renderValue(\n key: string,\n value: unknown,\n ctx: RenderContext,\n ): string {\n if (value == null) {\n return '';\n }\n if (isFragment(value)) {\n return this.renderFragment(value, ctx);\n }\n if (Array.isArray(value)) {\n return this.renderArray(key, value, ctx);\n }\n if (isFragmentObject(value)) {\n return this.renderObject(key, value, ctx);\n }\n // Preserve original type for TOML formatting\n return `${key} = ${this.#formatValue(value)}`;\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n void ctx;\n return `${key} = ${this.#formatValue(value)}`;\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n void ctx;\n const values = items\n .filter((item) => item != null)\n .map((item) => this.#formatValue(item));\n return `${key} = [${values.join(', ')}]`;\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const newPath = [...ctx.path, key];\n const entries = this.#renderObjectEntries(obj, newPath);\n return ['', `[${newPath.join('.')}]`, ...entries].join('\\n');\n }\n\n #renderObjectEntries(obj: FragmentObject, path: string[]): string[] {\n return Object.entries(obj)\n .map(([key, value]) => {\n if (value == null) {\n return '';\n }\n if (isFragmentObject(value)) {\n const newPath = [...path, key];\n const entries = this.#renderObjectEntries(value, newPath);\n return ['', `[${newPath.join('.')}]`, ...entries].join('\\n');\n }\n if (Array.isArray(value)) {\n const values = value\n .filter((item) => item != null)\n .map((item) => this.#formatValue(item));\n return `${key} = [${values.join(', ')}]`;\n }\n return `${key} = ${this.#formatValue(value)}`;\n })\n .filter(Boolean);\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n const newPath = [...ctx.path, name];\n if (this.isPrimitive(data)) {\n return `${name} = ${this.#formatValue(data)}`;\n }\n if (isFragment(data)) {\n return [\n '',\n `[${newPath.join('.')}]`,\n this.renderFragment(data, { ...ctx, path: newPath }),\n ].join('\\n');\n }\n if (Array.isArray(data)) {\n const fragmentItems = data.filter(isFragment);\n const nonFragmentItems = data.filter(\n (item) => !isFragment(item) && item != null,\n );\n\n if (fragmentItems.length > 0) {\n const parts: string[] = ['', `[${newPath.join('.')}]`];\n for (const frag of fragmentItems) {\n parts.push(this.renderFragment(frag, { ...ctx, path: newPath }));\n }\n return parts.join('\\n');\n }\n\n const values = nonFragmentItems.map((item) => this.#formatValue(item));\n return `${name} = [${values.join(', ')}]`;\n }\n if (isFragmentObject(data)) {\n const entries = this.#renderObjectEntries(data, newPath);\n return ['', `[${newPath.join('.')}]`, ...entries].join('\\n');\n }\n return '';\n }\n\n #escape(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n }\n\n #formatValue(value: unknown): string {\n if (typeof value === 'string') {\n return `\"${this.#escape(value)}\"`;\n }\n if (typeof value === 'boolean' || typeof value === 'number') {\n return String(value);\n }\n if (typeof value === 'object' && value !== null) {\n return JSON.stringify(value);\n }\n return `\"${String(value)}\"`;\n }\n}\n\n/**\n * Renders context fragments as TOON (Token-Oriented Object Notation).\n * TOON is a compact, token-efficient format for LLM prompts that combines\n * YAML-like indentation with CSV-like tabular arrays.\n */\nexport class ToonRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n const sanitized = this.sanitizeFragments(fragments);\n return sanitized\n .map((f) => this.#renderTopLevel(f))\n .filter(Boolean)\n .join('\\n');\n }\n\n #renderTopLevel(fragment: ContextFragment): string {\n const { name, data } = fragment;\n if (this.isPrimitive(data)) {\n return `${name}: ${this.#formatValue(data)}`;\n }\n if (Array.isArray(data)) {\n return this.#renderArrayField(name, data, 0);\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, { depth: 1, path: [] });\n return `${name}:\\n${child}`;\n }\n if (isFragmentObject(data)) {\n const entries = this.#renderObjectEntries(data, 1);\n if (!entries) {\n return `${name}:`;\n }\n return `${name}:\\n${entries}`;\n }\n return `${name}:`;\n }\n\n #renderArrayField(key: string, items: FragmentData[], depth: number): string {\n const filtered = items.filter((item) => item != null);\n if (filtered.length === 0) {\n return `${this.#pad(depth)}${key}[0]:`;\n }\n\n // Check for ContextFragment items\n const fragmentItems = filtered.filter(isFragment);\n if (fragmentItems.length > 0) {\n return this.#renderMixedArray(key, filtered, depth);\n }\n\n // Check if all items are primitives\n if (filtered.every((item) => this.#isPrimitiveValue(item))) {\n return this.#renderPrimitiveArray(key, filtered, depth);\n }\n\n // Check if tabular (uniform objects with primitive values)\n if (this.#isTabularArray(filtered)) {\n return this.#renderTabularArray(key, filtered, depth);\n }\n\n // Mixed array\n return this.#renderMixedArray(key, filtered, depth);\n }\n\n #isPrimitiveValue(value: unknown): boolean {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n );\n }\n\n #isTabularArray(items: FragmentData[]): items is FragmentObject[] {\n if (items.length === 0) return false;\n\n // All items must be objects (not arrays, not primitives, not fragments)\n const objects = items.filter(isFragmentObject);\n if (objects.length !== items.length) return false;\n\n // Determine if there is at least one shared field across all rows.\n // We treat null/undefined/missing as \"empty\" cells, but we still require\n // a non-empty key intersection so non-uniform objects are not\n // forced into a tabular shape.\n let intersection = new Set<string>(Object.keys(objects[0]));\n for (const obj of objects) {\n const keys = new Set(Object.keys(obj));\n intersection = new Set([...intersection].filter((k) => keys.has(k)));\n\n for (const value of Object.values(obj)) {\n if (value == null) continue;\n if (!this.#isPrimitiveValue(value)) {\n return false;\n }\n }\n }\n\n return intersection.size > 0;\n }\n\n #renderPrimitiveArray(\n key: string,\n items: FragmentData[],\n depth: number,\n ): string {\n const values = items.map((item) => this.#formatValue(item)).join(',');\n return `${this.#pad(depth)}${key}[${items.length}]: ${values}`;\n }\n\n #renderTabularArray(\n key: string,\n items: FragmentObject[],\n depth: number,\n ): string {\n if (items.length === 0) {\n return `${this.#pad(depth)}${key}[0]:`;\n }\n\n const fields = Array.from(\n new Set(items.flatMap((obj) => Object.keys(obj))),\n );\n const header = `${this.#pad(depth)}${key}[${items.length}]{${fields.join(',')}}:`;\n\n const rows = items.map((obj) => {\n const values = fields.map((f) => {\n const value = obj[f];\n if (value == null) return '';\n return this.#formatValue(value);\n });\n return `${this.#pad(depth + 1)}${values.join(',')}`;\n });\n\n return [header, ...rows].join('\\n');\n }\n\n #renderMixedArray(key: string, items: FragmentData[], depth: number): string {\n const header = `${this.#pad(depth)}${key}[${items.length}]:`;\n const lines = items.map((item) => this.#renderListItem(item, depth + 1));\n return [header, ...lines].join('\\n');\n }\n\n #renderListItem(item: FragmentData, depth: number): string {\n if (this.#isPrimitiveValue(item)) {\n return `${this.#pad(depth)}- ${this.#formatValue(item)}`;\n }\n if (isFragment(item)) {\n const rendered = this.renderFragment(item, {\n depth: depth + 1,\n path: [],\n });\n // For fragments, render key: value on same line as hyphen if primitive\n if (this.isPrimitive(item.data)) {\n return `${this.#pad(depth)}- ${item.name}: ${this.#formatValue(item.data)}`;\n }\n return `${this.#pad(depth)}- ${item.name}:\\n${rendered.split('\\n').slice(1).join('\\n')}`;\n }\n if (Array.isArray(item)) {\n // Nested array\n const content = this.#renderArrayField('', item, depth + 1);\n return `${this.#pad(depth)}-${content.trimStart()}`;\n }\n if (isFragmentObject(item)) {\n // Object in list\n const entries = this.#renderObjectEntries(item, depth + 1);\n if (!entries) {\n return `${this.#pad(depth)}-`;\n }\n // First line on same line as hyphen\n const lines = entries.split('\\n');\n const first = lines[0].trimStart();\n const rest = lines.slice(1).join('\\n');\n return rest\n ? `${this.#pad(depth)}- ${first}\\n${rest}`\n : `${this.#pad(depth)}- ${first}`;\n }\n return `${this.#pad(depth)}- ${this.#formatValue(item)}`;\n }\n\n #renderObjectEntries(obj: FragmentObject, depth: number): string {\n const lines: string[] = [];\n for (const [key, value] of Object.entries(obj)) {\n if (value == null) continue;\n\n if (this.#isPrimitiveValue(value)) {\n lines.push(`${this.#pad(depth)}${key}: ${this.#formatValue(value)}`);\n } else if (Array.isArray(value)) {\n lines.push(this.#renderArrayField(key, value, depth));\n } else if (isFragmentObject(value)) {\n const nested = this.#renderObjectEntries(value, depth + 1);\n if (nested) {\n lines.push(`${this.#pad(depth)}${key}:\\n${nested}`);\n } else {\n lines.push(`${this.#pad(depth)}${key}:`);\n }\n }\n }\n return lines.join('\\n');\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n if (this.isPrimitive(data)) {\n return `${this.#pad(ctx.depth)}${name}: ${this.#formatValue(data)}`;\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, {\n ...ctx,\n depth: ctx.depth + 1,\n });\n return `${this.#pad(ctx.depth)}${name}:\\n${child}`;\n }\n if (Array.isArray(data)) {\n return this.#renderArrayField(name, data, ctx.depth);\n }\n if (isFragmentObject(data)) {\n const entries = this.#renderObjectEntries(data, ctx.depth + 1);\n if (!entries) {\n return `${this.#pad(ctx.depth)}${name}:`;\n }\n return `${this.#pad(ctx.depth)}${name}:\\n${entries}`;\n }\n return `${this.#pad(ctx.depth)}${name}:`;\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n return `${this.#pad(ctx.depth)}${key}: ${this.#formatValue(value)}`;\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n return this.#renderArrayField(key, items, ctx.depth);\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const entries = this.#renderObjectEntries(obj, ctx.depth + 1);\n if (!entries) {\n return `${this.#pad(ctx.depth)}${key}:`;\n }\n return `${this.#pad(ctx.depth)}${key}:\\n${entries}`;\n }\n\n #pad(depth: number): string {\n return ' '.repeat(depth);\n }\n\n #needsQuoting(value: string): boolean {\n if (value === '') return true;\n if (value !== value.trim()) return true;\n if (['true', 'false', 'null'].includes(value.toLowerCase())) return true;\n if (/^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$/i.test(value)) return true;\n if (/[:\\\\\"'[\\]{}|,\\t\\n\\r]/.test(value)) return true;\n if (value.startsWith('-')) return true;\n return false;\n }\n\n #escape(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t');\n }\n\n #canonicalizeNumber(n: number): string {\n if (!Number.isFinite(n)) return 'null';\n if (Object.is(n, -0)) return '0';\n return String(n);\n }\n\n #formatValue(value: unknown): string {\n if (value === null) return 'null';\n if (typeof value === 'boolean') return String(value);\n if (typeof value === 'number') return this.#canonicalizeNumber(value);\n if (typeof value === 'string') {\n if (this.#needsQuoting(value)) {\n return `\"${this.#escape(value)}\"`;\n }\n return value;\n }\n // Fallback for objects/arrays in primitive context\n return `\"${this.#escape(JSON.stringify(value))}\"`;\n }\n}\n", "/**\n * Graph-based context store types and abstract interface.\n *\n * The storage model uses a DAG (Directed Acyclic Graph) for messages:\n * - Messages are immutable nodes with parentId forming the graph\n * - Branches are pointers to head (tip) messages\n * - Checkpoints are pointers to specific messages\n * - History is preserved through branching (rewind creates new branch)\n */\n\n// ============================================================================\n// Chat Types\n// ============================================================================\n\n/**\n * Data for creating/storing a chat.\n */\nexport interface ChatData {\n id: string;\n userId: string;\n title?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Stored chat data returned from database (includes timestamps).\n */\nexport interface StoredChatData extends ChatData {\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * Information about a chat for listing.\n */\nexport interface ChatInfo {\n id: string;\n userId: string;\n title?: string;\n messageCount: number;\n branchCount: number;\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * Options for listing chats.\n */\nexport interface ListChatsOptions {\n /** Filter by user ID */\n userId?: string;\n /** Maximum number of results to return */\n limit?: number;\n /** Number of results to skip (for pagination) */\n offset?: number;\n}\n\n/**\n * Options for deleting a chat.\n */\nexport interface DeleteChatOptions {\n /** If provided, only delete if chat belongs to this user */\n userId?: string;\n}\n\n// ============================================================================\n// Message Types (Graph Nodes)\n// ============================================================================\n\n/**\n * Data for creating/storing a message (graph node).\n */\nexport interface MessageData {\n id: string;\n chatId: string;\n parentId: string | null; // null for root messages\n name: string; // 'user', 'assistant', 'role', 'hint', etc.\n type?: string; // 'message', 'fragment'\n data: unknown; // JSON-serializable content\n createdAt: number;\n}\n\n/**\n * Message with computed properties for listing.\n */\nexport interface MessageInfo extends MessageData {\n hasChildren: boolean;\n}\n\n// ============================================================================\n// Branch Types\n// ============================================================================\n\n/**\n * Data for creating/storing a branch.\n * A branch is a pointer to a head message in the graph.\n */\nexport interface BranchData {\n id: string;\n chatId: string;\n name: string; // 'main', 'alt-1', etc.\n headMessageId: string | null; // null if branch is empty\n isActive: boolean;\n createdAt: number;\n}\n\n/**\n * Information about a branch for listing.\n */\nexport interface BranchInfo {\n id: string;\n name: string;\n headMessageId: string | null;\n isActive: boolean;\n messageCount: number; // count of messages in this branch's chain\n createdAt: number;\n}\n\n// ============================================================================\n// Checkpoint Types\n// ============================================================================\n\n/**\n * Data for creating/storing a checkpoint.\n * A checkpoint is a pointer to a specific message in the graph.\n */\nexport interface CheckpointData {\n id: string;\n chatId: string;\n name: string;\n messageId: string;\n createdAt: number;\n}\n\n/**\n * Information about a checkpoint for listing.\n */\nexport interface CheckpointInfo {\n id: string;\n name: string;\n messageId: string;\n createdAt: number;\n}\n\n// ============================================================================\n// Search Types\n// ============================================================================\n\n/**\n * Options for searching messages.\n */\nexport interface SearchOptions {\n /** Only search in specific roles (e.g., ['user', 'assistant']) */\n roles?: string[];\n /** Maximum results to return (default: 20) */\n limit?: number;\n}\n\n/**\n * Search result with relevance ranking.\n */\nexport interface SearchResult {\n /** The matched message */\n message: MessageData;\n /** BM25 relevance score (lower = more relevant) */\n rank: number;\n /** Highlighted snippet with matched terms */\n snippet?: string;\n}\n\n// ============================================================================\n// Graph Visualization Types\n// ============================================================================\n\n/**\n * A node in the visualization graph.\n */\nexport interface GraphNode {\n id: string;\n parentId: string | null;\n role: string; // 'user', 'assistant', etc.\n content: string; // Truncated preview of message content\n createdAt: number;\n}\n\n/**\n * A branch pointer for visualization.\n */\nexport interface GraphBranch {\n name: string;\n headMessageId: string | null;\n isActive: boolean;\n}\n\n/**\n * A checkpoint pointer for visualization.\n */\nexport interface GraphCheckpoint {\n name: string;\n messageId: string;\n}\n\n/**\n * Complete graph data for visualization.\n */\nexport interface GraphData {\n chatId: string;\n nodes: GraphNode[];\n branches: GraphBranch[];\n checkpoints: GraphCheckpoint[];\n}\n\n// ============================================================================\n// Abstract Store Interface\n// ============================================================================\n\n/**\n * Abstract base class for graph-based context storage.\n *\n * Implementations provide persistence for the message graph, branches,\n * and checkpoints. The graph model enables:\n * - Branching: rewind creates a new branch, original stays intact\n * - Checkpoints: pointers to specific messages for easy restore\n * - No data loss: soft delete only, all history preserved\n */\nexport abstract class ContextStore {\n // ==========================================================================\n // Chat Operations\n // ==========================================================================\n\n /**\n * Create a new chat.\n */\n abstract createChat(chat: ChatData): Promise<void>;\n\n /**\n * Create a chat if it doesn't exist, or return existing one.\n * Returns the stored chat data with timestamps.\n */\n abstract upsertChat(chat: ChatData): Promise<StoredChatData>;\n\n /**\n * Get a chat by ID.\n */\n abstract getChat(chatId: string): Promise<StoredChatData | undefined>;\n\n /**\n * Update chat metadata.\n * Note: updatedAt is automatically managed by database triggers.\n * Returns the updated chat data.\n */\n abstract updateChat(\n chatId: string,\n updates: Partial<Pick<ChatData, 'title' | 'metadata'>>,\n ): Promise<StoredChatData>;\n\n /**\n * List chats, sorted by updatedAt descending.\n * @param options - Optional filters for userId, limit, offset\n */\n abstract listChats(options?: ListChatsOptions): Promise<ChatInfo[]>;\n\n /**\n * Delete a chat and all associated data (messages, branches, checkpoints).\n * Returns true if deleted, false if not found or userId mismatch.\n */\n abstract deleteChat(\n chatId: string,\n options?: DeleteChatOptions,\n ): Promise<boolean>;\n\n // ==========================================================================\n // Message Operations (Graph Nodes)\n // ==========================================================================\n\n /**\n * Add a message to the graph.\n */\n abstract addMessage(message: MessageData): Promise<void>;\n\n /**\n * Get a message by ID.\n */\n abstract getMessage(messageId: string): Promise<MessageData | undefined>;\n\n /**\n * Walk up the parent chain from a head message, returning messages in\n * chronological order (root first).\n */\n abstract getMessageChain(headId: string): Promise<MessageData[]>;\n\n /**\n * Get all messages for a chat from the active branch.\n * Returns messages in chronological order (oldest first).\n *\n * @throws Error if chat doesn't exist\n * @returns Empty array if chat has no active branch or branch has no messages\n */\n abstract getMessages(chatId: string): Promise<MessageData[]>;\n\n /**\n * Check if a message has children (is a fork point).\n */\n abstract hasChildren(messageId: string): Promise<boolean>;\n\n // ==========================================================================\n // Branch Operations\n // ==========================================================================\n\n /**\n * Create a new branch.\n */\n abstract createBranch(branch: BranchData): Promise<void>;\n\n /**\n * Get a branch by chat ID and name.\n */\n abstract getBranch(\n chatId: string,\n name: string,\n ): Promise<BranchData | undefined>;\n\n /**\n * Get the active branch for a chat.\n */\n abstract getActiveBranch(chatId: string): Promise<BranchData | undefined>;\n\n /**\n * Set a branch as active (and deactivate others).\n */\n abstract setActiveBranch(chatId: string, branchId: string): Promise<void>;\n\n /**\n * Update a branch's head message.\n */\n abstract updateBranchHead(\n branchId: string,\n messageId: string | null,\n ): Promise<void>;\n\n /**\n * List all branches for a chat.\n */\n abstract listBranches(chatId: string): Promise<BranchInfo[]>;\n\n // ==========================================================================\n // Checkpoint Operations\n // ==========================================================================\n\n /**\n * Create a checkpoint.\n */\n abstract createCheckpoint(checkpoint: CheckpointData): Promise<void>;\n\n /**\n * Get a checkpoint by chat ID and name.\n */\n abstract getCheckpoint(\n chatId: string,\n name: string,\n ): Promise<CheckpointData | undefined>;\n\n /**\n * List all checkpoints for a chat.\n */\n abstract listCheckpoints(chatId: string): Promise<CheckpointInfo[]>;\n\n /**\n * Delete a checkpoint.\n */\n abstract deleteCheckpoint(chatId: string, name: string): Promise<void>;\n\n // ==========================================================================\n // Search Operations\n // ==========================================================================\n\n /**\n * Search messages using full-text search.\n *\n * @param chatId - The chat to search in\n * @param query - FTS5 query string (supports AND, OR, NOT, phrases, prefix*)\n * @param options - Search options\n * @returns Search results ordered by relevance (lower rank = more relevant)\n */\n abstract searchMessages(\n chatId: string,\n query: string,\n options?: SearchOptions,\n ): Promise<SearchResult[]>;\n\n // ==========================================================================\n // Visualization Operations\n // ==========================================================================\n\n /**\n * Get the complete graph data for a chat.\n * Returns all messages, branches, and checkpoints.\n */\n abstract getGraph(chatId: string): Promise<GraphData>;\n}\n", "import {\n type EstimateResult,\n type FragmentEstimate,\n getModelsRegistry,\n} from './estimate.ts';\nimport type { ContextFragment, LazyFragment } from './fragments.ts';\nimport {\n LAZY_ID,\n assistantText,\n isLazyFragment,\n isMessageFragment,\n message,\n} from './fragments.ts';\nimport type { Models } from './models.generated.ts';\nimport {\n type ContextRenderer,\n XmlRenderer,\n} from './renderers/abstract.renderer.ts';\nimport type { SkillPathMapping } from './skills/types.ts';\nimport {\n type BranchData,\n type BranchInfo,\n type ChatData,\n type CheckpointData,\n type CheckpointInfo,\n ContextStore,\n type GraphData,\n type MessageData,\n type StoredChatData,\n} from './store/store.ts';\n\n/**\n * Result of resolving context - ready for AI SDK consumption.\n */\nexport interface ResolveResult {\n /** Rendered non-message fragments for system prompt */\n systemPrompt: string;\n /** Message fragments decoded to AI SDK format */\n messages: unknown[];\n}\n\n/**\n * Options for resolve().\n */\nexport interface ResolveOptions {\n /** Renderer to use for system prompt (defaults to XmlRenderer) */\n renderer: ContextRenderer;\n}\n\n/**\n * Options for creating a ContextEngine.\n */\nexport interface ContextEngineOptions {\n /** Store for persisting fragments (required) */\n store: ContextStore;\n /** Unique identifier for this chat (required) */\n chatId: string;\n /** User who owns this chat (required) */\n userId: string;\n}\n\n/**\n * Metadata about a chat.\n */\nexport interface ChatMeta {\n /** Unique chat identifier */\n id: string;\n /** User who owns this chat */\n userId: string;\n /** When the chat was created */\n createdAt: number;\n /** When the chat was last updated */\n updatedAt: number;\n /** Optional user-provided title */\n title?: string;\n /** Optional custom metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for context inspection.\n */\nexport interface InspectOptions {\n /** Model ID for cost estimation (required) */\n modelId: Models;\n /** Renderer for estimation (required) */\n renderer: ContextRenderer;\n}\n\n/**\n * Result of inspecting context state.\n * JSON-serializable snapshot for debugging.\n */\nexport interface InspectResult {\n /** Token usage and cost estimation */\n estimate: EstimateResult;\n /** Rendered output using the provided renderer */\n rendered: string;\n /** Fragment structure breakdown */\n fragments: {\n /** Non-message fragments (role, hints, etc.) */\n context: ContextFragment[];\n /** Pending messages not yet saved to store */\n pending: ContextFragment[];\n /** Persisted messages from the store */\n persisted: MessageData[];\n };\n /** Conversation graph with branches and checkpoints */\n graph: GraphData;\n /** Inspection metadata */\n meta: {\n chatId: string;\n branch: string;\n timestamp: number;\n };\n}\n\n/**\n * Context engine for managing AI conversation context with graph-based storage.\n *\n * The engine uses a DAG (Directed Acyclic Graph) model for messages:\n * - Messages are immutable nodes with parentId forming the graph\n * - Branches are pointers to head (tip) messages\n * - Checkpoints are pointers to specific messages\n * - History is preserved through branching (rewind creates new branch)\n */\nexport class ContextEngine {\n /** Non-message fragments (role, hints, etc.) - not persisted in graph */\n #fragments: ContextFragment[] = [];\n /** Pending message fragments to be added to graph */\n #pendingMessages: ContextFragment[] = [];\n #store: ContextStore;\n #chatId: string;\n #userId: string;\n #branchName: string;\n #branch: BranchData | null = null;\n #chatData: StoredChatData | null = null;\n #initialized = false;\n\n constructor(options: ContextEngineOptions) {\n if (!options.chatId) {\n throw new Error('chatId is required');\n }\n if (!options.userId) {\n throw new Error('userId is required');\n }\n this.#store = options.store;\n this.#chatId = options.chatId;\n this.#userId = options.userId;\n this.#branchName = 'main';\n }\n\n /**\n * Initialize the chat and branch if they don't exist.\n */\n async #ensureInitialized(): Promise<void> {\n if (this.#initialized) {\n return;\n }\n\n this.#chatData = await this.#store.upsertChat({\n id: this.#chatId,\n userId: this.#userId,\n });\n\n // \"main\" branch is guaranteed to exist after upsertChat\n this.#branch = (await this.#store.getActiveBranch(this.#chatId))!;\n\n this.#initialized = true;\n }\n\n /**\n * Create a new branch from a specific message.\n * Shared logic between rewind() and btw().\n */\n async #createBranchFrom(\n messageId: string,\n switchTo: boolean,\n ): Promise<BranchInfo> {\n // Generate branch name based on same-prefix count (e.g., main-v2, main-v3)\n const branches = await this.#store.listBranches(this.#chatId);\n const samePrefix = branches.filter(\n (b) =>\n b.name === this.#branchName ||\n b.name.startsWith(`${this.#branchName}-v`),\n );\n const newBranchName = `${this.#branchName}-v${samePrefix.length + 1}`;\n\n // Create new branch pointing to the target message\n const newBranch: BranchData = {\n id: crypto.randomUUID(),\n chatId: this.#chatId,\n name: newBranchName,\n headMessageId: messageId,\n isActive: false,\n createdAt: Date.now(),\n };\n await this.#store.createBranch(newBranch);\n\n if (switchTo) {\n // Switch to the new branch\n await this.#store.setActiveBranch(this.#chatId, newBranch.id);\n this.#branch = { ...newBranch, isActive: true };\n this.#branchName = newBranchName;\n // Clear pending messages (they were for the old branch)\n this.#pendingMessages = [];\n }\n\n // Get message count for branch info\n const chain = await this.#store.getMessageChain(messageId);\n\n return {\n id: newBranch.id,\n name: newBranch.name,\n headMessageId: newBranch.headMessageId,\n isActive: switchTo,\n messageCount: chain.length,\n createdAt: newBranch.createdAt,\n };\n }\n\n /**\n * Get the current chat ID.\n */\n public get chatId(): string {\n return this.#chatId;\n }\n\n /**\n * Get the current branch name.\n */\n public get branch(): string {\n return this.#branchName;\n }\n\n /**\n * Get metadata for the current chat.\n * Returns null if the chat hasn't been initialized yet.\n */\n public get chat(): ChatMeta | null {\n if (!this.#chatData) {\n return null;\n }\n return {\n id: this.#chatData.id,\n userId: this.#chatData.userId,\n createdAt: this.#chatData.createdAt,\n updatedAt: this.#chatData.updatedAt,\n title: this.#chatData.title,\n metadata: this.#chatData.metadata,\n };\n }\n\n /**\n * Add fragments to the context.\n *\n * - Message fragments (user/assistant) are queued for persistence\n * - Non-message fragments (role/hint) are kept in memory for system prompt\n */\n public set(...fragments: ContextFragment[]) {\n for (const fragment of fragments) {\n if (isMessageFragment(fragment)) {\n this.#pendingMessages.push(fragment);\n } else {\n this.#fragments.push(fragment);\n }\n }\n return this;\n }\n\n // Unset a fragment by ID (not implemented yet)\n public unset(fragmentId: string) {\n //\n }\n\n /**\n * Render all fragments using the provided renderer.\n * @internal Use resolve() instead for public API.\n */\n public render(renderer: ContextRenderer) {\n return renderer.render(this.#fragments);\n }\n\n /**\n * Resolve context into AI SDK-ready format.\n *\n * - Initializes chat and branch if needed\n * - Loads message history from the graph (walking parent chain)\n * - Separates context fragments for system prompt\n * - Combines with pending messages\n *\n * @example\n * ```ts\n * const context = new ContextEngine({ store, chatId: 'chat-1', userId: 'user-1' })\n * .set(role('You are helpful'), user('Hello'));\n *\n * const { systemPrompt, messages } = await context.resolve();\n * await generateText({ system: systemPrompt, messages });\n * ```\n */\n public async resolve(options: ResolveOptions): Promise<ResolveResult> {\n await this.#ensureInitialized();\n\n const systemPrompt = options.renderer.render(this.#fragments);\n\n // Get persisted messages from graph\n const messages: unknown[] = [];\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n\n for (const msg of chain) {\n messages.push(message(msg.data as never).codec?.decode());\n }\n }\n\n // Add pending messages (not yet saved)\n for (const fragment of this.#pendingMessages) {\n const decoded = fragment.codec!.decode();\n messages.push(decoded);\n }\n\n return { systemPrompt, messages };\n }\n\n /**\n * Save pending messages to the graph.\n *\n * Each message is added as a node with parentId pointing to the previous message.\n * The branch head is updated to point to the last message.\n *\n * @example\n * ```ts\n * context.set(user('Hello'));\n * // AI responds...\n * context.set(assistant('Hi there!'));\n * await context.save(); // Persist to graph\n * ```\n */\n public async save(): Promise<void> {\n await this.#ensureInitialized();\n\n if (this.#pendingMessages.length === 0) {\n return;\n }\n\n // Resolve any lazy fragments before processing\n for (let i = 0; i < this.#pendingMessages.length; i++) {\n const fragment = this.#pendingMessages[i];\n if (isLazyFragment(fragment)) {\n this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment);\n }\n }\n\n let parentId = this.#branch!.headMessageId;\n const now = Date.now();\n\n // Add each pending message to the graph\n for (const fragment of this.#pendingMessages) {\n const messageData: MessageData = {\n id: fragment.id ?? crypto.randomUUID(),\n chatId: this.#chatId,\n parentId,\n name: fragment.name,\n type: fragment.type,\n data: fragment.codec!.encode(),\n createdAt: now,\n };\n\n await this.#store.addMessage(messageData);\n parentId = messageData.id;\n }\n\n // Update branch head to last message\n await this.#store.updateBranchHead(this.#branch!.id, parentId);\n this.#branch!.headMessageId = parentId;\n\n // Clear pending messages\n this.#pendingMessages = [];\n }\n\n /**\n * Resolve a lazy fragment by finding the appropriate ID.\n */\n async #resolveLazyFragment(fragment: LazyFragment): Promise<ContextFragment> {\n const lazy = fragment[LAZY_ID]!;\n\n if (lazy.type === 'last-assistant') {\n const lastId = await this.#getLastAssistantId();\n return assistantText(lazy.content, { id: lastId ?? crypto.randomUUID() });\n }\n\n throw new Error(`Unknown lazy fragment type: ${lazy.type}`);\n }\n\n /**\n * Find the most recent assistant message ID (pending or persisted).\n */\n async #getLastAssistantId(): Promise<string | undefined> {\n // Check pending messages first (excluding lazy ones)\n for (let i = this.#pendingMessages.length - 1; i >= 0; i--) {\n const msg = this.#pendingMessages[i];\n if (msg.name === 'assistant' && !isLazyFragment(msg)) {\n return msg.id;\n }\n }\n\n // Check persisted messages at branch head\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n for (let i = chain.length - 1; i >= 0; i--) {\n if (chain[i].name === 'assistant') {\n return chain[i].id;\n }\n }\n }\n\n return undefined;\n }\n\n /**\n * Estimate token count and cost for the full context.\n *\n * Includes:\n * - System prompt fragments (role, hints, etc.)\n * - Persisted chat messages (from store)\n * - Pending messages (not yet saved)\n *\n * @param modelId - Model ID (e.g., \"openai:gpt-4o\", \"anthropic:claude-3-5-sonnet\")\n * @param options - Optional settings\n * @returns Estimate result with token counts, costs, and per-fragment breakdown\n */\n public async estimate(\n modelId: Models,\n options: {\n renderer?: ContextRenderer;\n } = {},\n ): Promise<EstimateResult> {\n await this.#ensureInitialized();\n\n const renderer = options.renderer ?? new XmlRenderer();\n const registry = getModelsRegistry();\n await registry.load();\n\n const model = registry.get(modelId);\n if (!model) {\n throw new Error(\n `Model \"${modelId}\" not found. Call load() first or check model ID.`,\n );\n }\n\n const tokenizer = registry.getTokenizer(modelId);\n const fragmentEstimates: FragmentEstimate[] = [];\n\n // 1. Estimate context fragments (system prompt)\n for (const fragment of this.#fragments) {\n const rendered = renderer.render([fragment]);\n const tokens = tokenizer.count(rendered);\n const cost = (tokens / 1_000_000) * model.cost.input;\n fragmentEstimates.push({\n id: fragment.id,\n name: fragment.name,\n tokens,\n cost,\n });\n }\n\n // 2. Estimate persisted messages from store\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n for (const msg of chain) {\n const content = String(msg.data);\n const tokens = tokenizer.count(content);\n const cost = (tokens / 1_000_000) * model.cost.input;\n fragmentEstimates.push({\n name: msg.name,\n id: msg.id,\n tokens,\n cost,\n });\n }\n }\n\n // 3. Estimate pending messages (not yet saved)\n for (const fragment of this.#pendingMessages) {\n const content = String(fragment.data);\n const tokens = tokenizer.count(content);\n const cost = (tokens / 1_000_000) * model.cost.input;\n fragmentEstimates.push({\n name: fragment.name,\n id: fragment.id,\n tokens,\n cost,\n });\n }\n\n // Calculate totals\n const totalTokens = fragmentEstimates.reduce((sum, f) => sum + f.tokens, 0);\n const totalCost = fragmentEstimates.reduce((sum, f) => sum + f.cost, 0);\n\n return {\n model: model.id,\n provider: model.provider,\n tokens: totalTokens,\n cost: totalCost,\n limits: {\n context: model.limit.context,\n output: model.limit.output,\n exceedsContext: totalTokens > model.limit.context,\n },\n fragments: fragmentEstimates,\n };\n }\n\n /**\n * Rewind to a specific message by ID.\n *\n * Creates a new branch from that message, preserving the original branch.\n * The new branch becomes active.\n *\n * @param messageId - The message ID to rewind to\n * @returns The new branch info\n *\n * @example\n * ```ts\n * context.set(user('What is 2 + 2?', { id: 'q1' }));\n * context.set(assistant('The answer is 5.', { id: 'wrong' })); // Oops!\n * await context.save();\n *\n * // Rewind to the question, creates new branch\n * const newBranch = await context.rewind('q1');\n *\n * // Now add correct answer on new branch\n * context.set(assistant('The answer is 4.'));\n * await context.save();\n * ```\n */\n public async rewind(messageId: string): Promise<BranchInfo> {\n await this.#ensureInitialized();\n\n // Verify the message exists\n const message = await this.#store.getMessage(messageId);\n if (!message) {\n throw new Error(`Message \"${messageId}\" not found`);\n }\n if (message.chatId !== this.#chatId) {\n throw new Error(`Message \"${messageId}\" belongs to a different chat`);\n }\n\n return this.#createBranchFrom(messageId, true);\n }\n\n /**\n * Create a checkpoint at the current position.\n *\n * A checkpoint is a named pointer to the current branch head.\n * Use restore() to return to this point later.\n *\n * @param name - Name for the checkpoint\n * @returns The checkpoint info\n *\n * @example\n * ```ts\n * context.set(user('I want to learn a new skill.'));\n * context.set(assistant('Would you like coding or cooking?'));\n * await context.save();\n *\n * // Save checkpoint before user's choice\n * const cp = await context.checkpoint('before-choice');\n * ```\n */\n public async checkpoint(name: string): Promise<CheckpointInfo> {\n await this.#ensureInitialized();\n\n if (!this.#branch?.headMessageId) {\n throw new Error('Cannot create checkpoint: no messages in conversation');\n }\n\n const checkpoint: CheckpointData = {\n id: crypto.randomUUID(),\n chatId: this.#chatId,\n name,\n messageId: this.#branch.headMessageId,\n createdAt: Date.now(),\n };\n\n await this.#store.createCheckpoint(checkpoint);\n\n return {\n id: checkpoint.id,\n name: checkpoint.name,\n messageId: checkpoint.messageId,\n createdAt: checkpoint.createdAt,\n };\n }\n\n /**\n * Restore to a checkpoint by creating a new branch from that point.\n *\n * @param name - Name of the checkpoint to restore\n * @returns The new branch info\n *\n * @example\n * ```ts\n * // User chose cooking, but wants to try coding path\n * await context.restore('before-choice');\n *\n * context.set(user('I want to learn coding.'));\n * context.set(assistant('Python is a great starting language!'));\n * await context.save();\n * ```\n */\n public async restore(name: string): Promise<BranchInfo> {\n await this.#ensureInitialized();\n\n const checkpoint = await this.#store.getCheckpoint(this.#chatId, name);\n if (!checkpoint) {\n throw new Error(\n `Checkpoint \"${name}\" not found in chat \"${this.#chatId}\"`,\n );\n }\n\n // Rewind to the checkpoint's message\n return this.rewind(checkpoint.messageId);\n }\n\n /**\n * Switch to a different branch by name.\n *\n * @param name - Branch name to switch to\n *\n * @example\n * ```ts\n * // List branches (via store)\n * const branches = await store.listBranches(context.chatId);\n * console.log(branches); // [{name: 'main', ...}, {name: 'main-v2', ...}]\n *\n * // Switch to original branch\n * await context.switchBranch('main');\n * ```\n */\n public async switchBranch(name: string): Promise<void> {\n await this.#ensureInitialized();\n\n const branch = await this.#store.getBranch(this.#chatId, name);\n if (!branch) {\n throw new Error(`Branch \"${name}\" not found in chat \"${this.#chatId}\"`);\n }\n\n await this.#store.setActiveBranch(this.#chatId, branch.id);\n this.#branch = { ...branch, isActive: true };\n this.#branchName = name;\n\n // Clear pending messages (they were for the old branch)\n this.#pendingMessages = [];\n }\n\n /**\n * Create a parallel branch from the current position (\"by the way\").\n *\n * Use this when you want to fork the conversation without leaving\n * the current branch. Common use case: user wants to ask another\n * question while waiting for the model to respond.\n *\n * Unlike rewind(), this method:\n * - Uses the current HEAD (no messageId needed)\n * - Does NOT switch to the new branch\n * - Keeps pending messages intact\n *\n * @returns The new branch info (does not switch to it)\n * @throws Error if no messages exist in the conversation\n *\n * @example\n * ```ts\n * // User asked a question, model is generating...\n * context.set(user('What is the weather?'));\n * await context.save();\n *\n * // User wants to ask something else without waiting\n * const newBranch = await context.btw();\n * // newBranch = { name: 'main-v2', ... }\n *\n * // Later, switch to the new branch and add the question\n * await context.switchBranch(newBranch.name);\n * context.set(user('Also, what time is it?'));\n * await context.save();\n * ```\n */\n public async btw(): Promise<BranchInfo> {\n await this.#ensureInitialized();\n\n if (!this.#branch?.headMessageId) {\n throw new Error('Cannot create btw branch: no messages in conversation');\n }\n\n return this.#createBranchFrom(this.#branch.headMessageId, false);\n }\n\n /**\n * Update metadata for the current chat.\n *\n * @param updates - Partial metadata to merge (title, metadata)\n *\n * @example\n * ```ts\n * await context.updateChat({\n * title: 'Coding Help Session',\n * metadata: { tags: ['python', 'debugging'] }\n * });\n * ```\n */\n public async updateChat(\n updates: Partial<Pick<ChatMeta, 'title' | 'metadata'>>,\n ): Promise<void> {\n await this.#ensureInitialized();\n\n const storeUpdates: Partial<Pick<ChatData, 'title' | 'metadata'>> = {};\n\n if (updates.title !== undefined) {\n storeUpdates.title = updates.title;\n }\n if (updates.metadata !== undefined) {\n // Merge with existing metadata\n storeUpdates.metadata = {\n ...this.#chatData?.metadata,\n ...updates.metadata,\n };\n }\n\n this.#chatData = await this.#store.updateChat(this.#chatId, storeUpdates);\n }\n\n /**\n * Consolidate context fragments (no-op for now).\n *\n * This is a placeholder for future functionality that merges context fragments\n * using specific rules. Currently, it does nothing.\n *\n * @experimental\n */\n public consolidate(): void {\n return void 0;\n }\n\n /**\n * Extract skill path mappings from available_skills fragments.\n * Returns array of { host, sandbox } for mounting in sandbox filesystem.\n *\n * Reads the original `paths` configuration stored in fragment metadata\n * by the skills() fragment helper.\n *\n * @example\n * ```ts\n * const context = new ContextEngine({ store, chatId, userId })\n * .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));\n *\n * const mounts = context.getSkillMounts();\n * // [{ host: './skills', sandbox: '/skills' }]\n * ```\n */\n public getSkillMounts(): SkillPathMapping[] {\n const mounts: SkillPathMapping[] = [];\n\n for (const fragment of this.#fragments) {\n if (\n fragment.name === 'available_skills' &&\n fragment.metadata &&\n Array.isArray(fragment.metadata.paths)\n ) {\n for (const mapping of fragment.metadata.paths) {\n if (\n typeof mapping === 'object' &&\n mapping !== null &&\n typeof mapping.host === 'string' &&\n typeof mapping.sandbox === 'string'\n ) {\n mounts.push({ host: mapping.host, sandbox: mapping.sandbox });\n }\n }\n }\n }\n\n return mounts;\n }\n\n /**\n * Inspect the full context state for debugging.\n * Returns a JSON-serializable object with context information.\n *\n * @param options - Inspection options (modelId and renderer required)\n * @returns Complete inspection data including estimates, rendered output, fragments, and graph\n *\n * @example\n * ```ts\n * const inspection = await context.inspect({\n * modelId: 'openai:gpt-4o',\n * renderer: new XmlRenderer(),\n * });\n * console.log(JSON.stringify(inspection, null, 2));\n *\n * // Or write to file for analysis\n * await fs.writeFile('context-debug.json', JSON.stringify(inspection, null, 2));\n * ```\n */\n public async inspect(options: InspectOptions): Promise<InspectResult> {\n await this.#ensureInitialized();\n\n const { renderer } = options;\n\n // Get token/cost estimation\n const estimateResult = await this.estimate(options.modelId, { renderer });\n\n // Render using provided renderer\n const rendered = renderer.render(this.#fragments);\n\n // Get persisted messages from store\n const persistedMessages: MessageData[] = [];\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n persistedMessages.push(...chain);\n }\n\n // Get conversation graph\n const graph = await this.#store.getGraph(this.#chatId);\n\n return {\n estimate: estimateResult,\n rendered,\n fragments: {\n context: [...this.#fragments],\n pending: [...this.#pendingMessages],\n persisted: persistedMessages,\n },\n graph,\n meta: {\n chatId: this.#chatId,\n branch: this.#branchName,\n timestamp: Date.now(),\n },\n };\n }\n}\n", "import type { ContextFragment, FragmentData } from '../fragments.ts';\n\n/**\n * Domain knowledge fragment builders.\n *\n * These fragments capture domain-specific knowledge that can be injected\n * into AI prompts. Use with renderers (XML, Markdown, TOML, TOON) to format.\n *\n * @example\n * ```ts\n * import { term, hint, guardrail } from '@deepagents/context';\n *\n * context.set(\n * term('NPL', 'non-performing loan'),\n * hint('Always filter by status'),\n * guardrail({ rule: 'Never expose PII' }),\n * );\n * ```\n */\n\n/**\n * Define domain-specific vocabulary and business terminology.\n *\n * Use this to define simple, direct mappings between business terms and their meanings.\n * The system will understand these terms when users mention them in queries.\n *\n * @param name - The business term or acronym to define\n * @param definition - What the term means in your domain\n *\n * @example\n * // Logistics/Transportation dataset\n * term(\"deadhead miles\", \"distance driven with empty truck between deliveries\")\n * term(\"dwell time\", \"total time a truck spends at a loading dock or warehouse\")\n * term(\"LTL\", \"less than truckload - shipment that doesn't fill entire truck\")\n *\n * @example\n * // Education/University dataset\n * term(\"matriculation\", \"students who completed enrollment and started classes\")\n * term(\"DFW rate\", \"percentage of students receiving D, F, or Withdrawal in a course\")\n * term(\"cohort\", \"group of students who entered the same semester or academic year\")\n *\n * @example\n * // Finance/Banking dataset\n * term(\"NPL\", \"non-performing loan - loan past due 90+ days\")\n * term(\"basis points\", \"one hundredth of a percentage point (1% = 100 bps)\")\n * term(\"AUM\", \"assets under management - total market value of client investments\")\n */\nexport function term(name: string, definition: string): ContextFragment {\n return {\n name: 'term',\n data: { name, definition },\n };\n}\n\n/**\n * Define behavioral rules and constraints that should always apply.\n *\n * Use this for business logic, data quality rules, or query preferences that should\n * be automatically applied to all relevant queries.\n *\n * @param text - The rule or constraint to follow (use imperative language)\n *\n * @example\n * // Manufacturing/Supply Chain dataset\n * hint(\"Always exclude work orders with status = 'simulation' from production metrics\")\n * hint(\"When calculating OEE (overall equipment effectiveness), only count scheduled production time\")\n * hint(\"Defect rates should be calculated per batch, not per individual unit, for consistency\")\n *\n * @example\n * // Real Estate/Property dataset\n * hint(\"Never include properties with listing_status = 'draft' in market analysis\")\n * hint(\"Always filter out duplicate MLS listings - use the earliest listing_date for each property_id\")\n * hint(\"Square footage comparisons must specify if including or excluding basement/garage\")\n *\n * @example\n * // Social Media/Content Platform dataset\n * hint(\"Engagement metrics should exclude bot accounts identified by is_verified_human = false\")\n * hint(\"View counts reset daily - always use cumulative_views for historical analysis\")\n * hint(\"Default content filters to published_status = 'public' unless analyzing drafts\")\n */\nexport function hint(text: string): ContextFragment {\n return {\n name: 'hint',\n data: text,\n };\n}\n\n/**\n * Define hard guardrails, safety rules, and compliance boundaries.\n *\n * Use this for \"never do\" rules, sensitive data handling, and required behaviors when\n * certain conditions occur. Guardrails should be explicit and action-oriented.\n *\n * @param input.rule - The guardrail or restriction to enforce\n * @param input.reason - Why this guardrail exists (compliance, security, performance)\n * @param input.action - What to do when this guardrail is triggered\n *\n * @example\n * // Healthcare dataset\n * guardrail({\n * rule: \"Never return PHI like SSN, MRN, or full address in query results\",\n * reason: \"HIPAA compliance\",\n * action: \"If asked, state that identifiable patient data cannot be shared; offer de-identified aggregates instead\"\n * })\n *\n * @example\n * // Finance dataset\n * guardrail({\n * rule: \"Block any query exposing employee-level compensation by name\",\n * reason: \"Confidential payroll data\",\n * action: \"Provide ranges grouped by department or level instead of individual salaries\"\n * })\n *\n * @example\n * // E-commerce dataset\n * guardrail({\n * rule: \"Warn when a query would scan more than 10 million rows; require a narrower date range\",\n * reason: \"Performance and cost control\",\n * action: \"Ask the user to add filters (recent timeframe, specific categories) before proceeding\"\n * })\n */\nexport function guardrail(input: {\n rule: string;\n reason?: string;\n action?: string;\n}): ContextFragment {\n return {\n name: 'guardrail',\n data: {\n rule: input.rule,\n ...(input.reason && { reason: input.reason }),\n ...(input.action && { action: input.action }),\n },\n };\n}\n\n/**\n * Define a rich understanding of a single concept using metaphors and explanations.\n *\n * Use this when a simple term definition isn't enough - when you need to convey deeper\n * understanding about how to think about and calculate a metric or concept.\n *\n * @param input.concept - The concept being explained\n * @param input.explanation - A metaphor or detailed explanation\n * @param input.therefore - Optional actionable instruction based on this understanding\n *\n * @example\n * // Gaming/Entertainment dataset\n * explain({\n * concept: \"daily active users to monthly active users ratio\",\n * explanation: \"like measuring how many club members visit daily vs just once a month - shows stickiness\",\n * therefore: \"Calculate as DAU / MAU, where higher ratio (closer to 1) means more engaged user base\"\n * })\n *\n * @example\n * // HR/Employee Management dataset\n * explain({\n * concept: \"time to fill\",\n * explanation: \"like measuring how long a house sits on the market - from posting job to accepting offer\",\n * therefore: \"Calculate as days between job_posted_date and offer_accepted_date, exclude cancelled requisitions\"\n * })\n *\n * @example\n * // Telecommunications dataset\n * explain({\n * concept: \"network congestion ratio\",\n * explanation: \"like rush hour traffic density - measures actual usage vs total capacity at peak times\",\n * therefore: \"Calculate as (peak_hour_bandwidth_used / total_bandwidth_capacity) during busiest hour of day\"\n * })\n */\nexport function explain(input: {\n concept: string;\n explanation: string;\n therefore?: string;\n}): ContextFragment {\n return {\n name: 'explain',\n data: {\n concept: input.concept,\n explanation: input.explanation,\n ...(input.therefore && { therefore: input.therefore }),\n },\n };\n}\n\n/**\n * Define concrete examples of question \u2192 answer pairs.\n *\n * Use this for few-shot learning - show the system exactly how to translate\n * specific types of questions. Great for establishing patterns.\n *\n * @param input.question - The natural language question or request\n * @param input.answer - The correct answer that responds to the question\n * @param input.note - Optional note or explanation about the example\n *\n * @example\n * // Energy/Utilities dataset\n * example({\n * question: \"show me peak demand hours for the last week\",\n * answer: \"SELECT DATE_TRUNC('hour', reading_timestamp) as hour, MAX(consumption_kwh) as peak_demand FROM meter_readings WHERE reading_timestamp >= CURRENT_DATE - INTERVAL '7 days' GROUP BY hour ORDER BY peak_demand DESC LIMIT 10\"\n * })\n *\n * @example\n * // Agriculture/Farm Management dataset\n * example({\n * question: \"what is the average yield per acre by crop type this season\",\n * answer: \"SELECT crop_type, AVG(harvest_quantity / field_acres) as yield_per_acre FROM harvests WHERE harvest_date >= '2024-01-01' GROUP BY crop_type ORDER BY yield_per_acre DESC\"\n * })\n *\n * @example\n * // Travel/Hospitality dataset\n * example({\n * question: \"show me hotel occupancy rate for this month\",\n * answer: \"SELECT hotel_name, (SUM(occupied_rooms) / SUM(total_rooms)) * 100 as occupancy_rate FROM daily_occupancy WHERE date >= DATE_TRUNC('month', CURRENT_DATE) GROUP BY hotel_id, hotel_name ORDER BY occupancy_rate DESC\",\n * note: \"Occupancy rate is a percentage - multiply by 100 for readable output\"\n * })\n */\nexport function example(input: {\n question: string;\n answer: string;\n note?: string;\n}): ContextFragment {\n return {\n name: 'example',\n data: {\n question: input.question,\n answer: input.answer,\n ...(input.note && { note: input.note }),\n },\n };\n}\n\n/**\n * Define when and what to ask for clarification.\n *\n * Use this to handle ambiguous terms or situations where the system should\n * proactively ask the user for more information.\n *\n * @param input.when - The condition or trigger that should prompt clarification\n * @param input.ask - The question to ask the user\n * @param input.reason - Why this clarification is necessary\n *\n * @example\n * // Marketing/Advertising dataset\n * clarification({\n * when: \"user asks for 'conversion rate'\",\n * ask: \"Which conversion: click-to-lead, lead-to-opportunity, or opportunity-to-customer?\",\n * reason: \"Conversion rate means different things at each funnel stage - need to specify which metric\"\n * })\n *\n * @example\n * // Food Delivery dataset\n * clarification({\n * when: \"user asks about 'delivery time'\",\n * ask: \"Do you mean estimated time at order, actual delivery time, or time from kitchen to door?\",\n * reason: \"Multiple time metrics exist - estimated vs actual impacts customer satisfaction differently\"\n * })\n *\n * @example\n * // Fitness/Gym Management dataset\n * clarification({\n * when: \"user mentions 'active members'\",\n * ask: \"Do you mean paid memberships or members who actually visited in last 30 days?\",\n * reason: \"Many paid members don't use facilities - different metrics for revenue vs utilization\"\n * })\n */\nexport function clarification(input: {\n when: string;\n ask: string;\n reason: string;\n}): ContextFragment {\n return {\n name: 'clarification',\n data: {\n when: input.when,\n ask: input.ask,\n reason: input.reason,\n },\n };\n}\n\n/**\n * Define multi-step analytical processes that require sequential logic.\n *\n * Use this for complex analytical tasks that require multiple steps or specific\n * methodologies. Workflows teach the system HOW to approach a type of analysis.\n *\n * @param input.task - Name of the analytical task\n * @param input.steps - Sequential steps to execute\n * @param input.triggers - Optional phrases that should activate this workflow\n * @param input.notes - Optional additional context, warnings, or guidance\n *\n * @example\n * // Insurance dataset\n * workflow({\n * task: \"Claims Loss Ratio Analysis\",\n * triggers: [\"loss ratio\", \"claims ratio\", \"underwriting performance\"],\n * steps: [\n * \"Calculate total claims paid for each policy period\",\n * \"Calculate total premiums earned for same period\",\n * \"Compute loss ratio as (claims_paid / premiums_earned) * 100\",\n * \"Segment by policy type, geography, and underwriter\",\n * \"Identify policies with loss ratio > 100% (losing money)\",\n * \"Calculate trend over time using rolling 12-month windows\"\n * ],\n * notes: \"Use incurred date for claims, not paid date. Exclude reinsurance recoveries from claims total.\"\n * })\n *\n * @example\n * // Media/Publishing dataset\n * workflow({\n * task: \"Content Performance Funnel\",\n * triggers: [\"content funnel\", \"engagement funnel\", \"content performance\"],\n * steps: [\n * \"Count total impressions (articles shown) per content piece\",\n * \"Count click-throughs (articles opened)\",\n * \"Count scroll depth > 50% (meaningful engagement)\",\n * \"Count shares, comments, or saves (viral actions)\",\n * \"Calculate conversion rate at each funnel stage\",\n * \"Identify top-performing content by final conversion rate\"\n * ],\n * notes: \"Requires multiple event types. Join events table multiple times or use conditional aggregation.\"\n * })\n *\n * @example\n * // Sports Analytics dataset\n * workflow({\n * task: \"Player Performance Rating Calculation\",\n * triggers: [\"player rating\", \"performance score\", \"player analytics\"],\n * steps: [\n * \"Aggregate per-game stats: points, assists, rebounds, turnovers\",\n * \"Calculate efficiency metrics: shooting percentage, plus/minus\",\n * \"Normalize each metric using z-scores vs league average\",\n * \"Apply position-specific weights to each metric\",\n * \"Combine weighted scores into overall performance rating (0-100)\",\n * \"Rank players within position group and overall\"\n * ],\n * notes: \"Requires league-wide statistics for normalization. Update weights each season based on game trends.\"\n * })\n */\nexport function workflow(input: {\n task: string;\n steps: string[];\n triggers?: string[];\n notes?: string;\n}): ContextFragment {\n return {\n name: 'workflow',\n data: {\n task: input.task,\n steps: input.steps,\n ...(input.triggers?.length && { triggers: input.triggers }),\n ...(input.notes && { notes: input.notes }),\n },\n };\n}\n\n/**\n * Define data quirks, edge cases, or database-specific issues and their workarounds.\n *\n * Use this to document weird data patterns, database limitations, or special handling\n * required for specific scenarios.\n *\n * @param input.issue - Description of the quirk, edge case, or problem\n * @param input.workaround - How to handle or work around this issue\n *\n * @example\n * // Government/Public Services dataset\n * quirk({\n * issue: \"Citizen IDs contain leading zeros but are stored as integers, losing the zeros\",\n * workaround: \"Always cast to VARCHAR and use LPAD(citizen_id::VARCHAR, 10, '0') to restore leading zeros\"\n * })\n *\n * @example\n * // Aviation dataset\n * quirk({\n * issue: \"Flight times crossing midnight show as negative duration (landing before takeoff)\",\n * workaround: \"Add 24 hours when calculated duration < 0: CASE WHEN duration < 0 THEN duration + INTERVAL '24 hours' ELSE duration END\"\n * })\n *\n * @example\n * // Automotive/Dealership dataset\n * quirk({\n * issue: \"VIN numbers with letter 'O' were incorrectly entered as zero '0' in legacy data\",\n * workaround: \"When searching by VIN, use REPLACE(vin, '0', 'O') or fuzzy matching to handle both cases\"\n * })\n */\nexport function quirk(input: {\n issue: string;\n workaround: string;\n}): ContextFragment {\n return {\n name: 'quirk',\n data: {\n issue: input.issue,\n workaround: input.workaround,\n },\n };\n}\n\n/**\n * Define style preferences and coding standards.\n *\n * Use this to enforce consistent formatting, naming conventions, and best practices\n * specific to your team or organization.\n *\n * @param input.prefer - Preferred style or pattern\n * @param input.never - Optional anti-pattern to avoid\n * @param input.always - Optional rule that must always be followed\n *\n * @example\n * // Non-profit/Charity dataset\n * styleGuide({\n * prefer: \"Use donor-centric language in column aliases: 'donor_name' not 'customer_name'\",\n * never: \"Never expose internal donor IDs in external reports - use public gift IDs\",\n * always: \"Always include fiscal year in date-based aggregations (FY starts July 1)\"\n * })\n *\n * @example\n * // Legal/Law Firm dataset\n * styleGuide({\n * prefer: \"Use billable_hours with 2 decimal precision for accurate client billing\",\n * never: \"Never include attorney_rate in queries visible to paralegals - confidential data\",\n * always: \"Always filter by matter_status = 'open' unless specifically analyzing closed cases\"\n * })\n *\n * @example\n * // Inventory/Warehouse dataset\n * styleGuide({\n * prefer: \"Use location_id in joins rather than location_name (duplicates exist across warehouses)\",\n * never: \"Never aggregate inventory without grouping by warehouse_id first\",\n * always: \"Always use inventory_on_hand - inventory_reserved for available stock calculations\"\n * })\n */\nexport function styleGuide(input: {\n prefer: string;\n never?: string;\n always?: string;\n}): ContextFragment {\n return {\n name: 'styleGuide',\n data: {\n prefer: input.prefer,\n ...(input.never && { never: input.never }),\n ...(input.always && { always: input.always }),\n },\n };\n}\n\n/**\n * Define comparisons between related concepts through real-world analogies.\n *\n * Use this to teach relational understanding between concepts by drawing comparisons\n * to familiar real-world scenarios.\n *\n * @param input.concepts - Array of related concepts to compare\n * @param input.relationship - The comparison/analogy using real-world examples\n * @param input.insight - Optional key insight the analogy reveals\n * @param input.therefore - Optional actionable instruction\n * @param input.pitfall - Optional common mistake to avoid\n *\n * @example\n * // E-commerce dataset\n * analogy({\n * concepts: [\"cart abandonment\", \"browse abandonment\"],\n * relationship: \"Cart abandonment is like leaving items at a checkout counter, browse abandonment is like window shopping without picking anything up\",\n * insight: \"Cart abandonment shows purchase intent (added to cart), browse abandonment shows only interest\",\n * therefore: \"Prioritize cart abandonment recovery campaigns - higher conversion potential than browse\",\n * pitfall: \"Don't combine both into generic 'abandonment rate' - they need different marketing strategies\"\n * })\n *\n * @example\n * // SaaS dataset\n * analogy({\n * concepts: [\"logo churn\", \"revenue churn\"],\n * relationship: \"Logo churn is like counting how many customers left the store, revenue churn is how much money walked out\",\n * insight: \"Losing 10 small customers (high logo churn) might hurt less than losing 1 enterprise customer (high revenue churn)\",\n * therefore: \"Always report both metrics - logo churn for customer satisfaction, revenue churn for financial health\",\n * pitfall: \"Don't use logo churn to predict revenue impact - customer size distribution matters\"\n * })\n *\n * @example\n * // Healthcare dataset\n * analogy({\n * concepts: [\"incidence\", \"prevalence\"],\n * relationship: \"Incidence is like new house sales this month, prevalence is total houses currently occupied\",\n * insight: \"Incidence measures new cases over time, prevalence measures all existing cases at a point in time\",\n * therefore: \"For tracking disease outbreaks use incidence rate, for resource planning use prevalence\",\n * pitfall: \"Don't sum incidence rates across time periods - it's a rate not a count\"\n * })\n */\nexport function analogy(input: {\n concepts: string[];\n relationship: string;\n insight?: string;\n therefore?: string;\n pitfall?: string;\n}): ContextFragment {\n return {\n name: 'analogy',\n data: {\n concepts: input.concepts,\n relationship: input.relationship,\n ...(input.insight && { insight: input.insight }),\n ...(input.therefore && { therefore: input.therefore }),\n ...(input.pitfall && { pitfall: input.pitfall }),\n },\n };\n}\n\n/**\n * Map business terms directly to expressions or fragments.\n *\n * Use this to teach the system how to CALCULATE or QUERY specific business concepts.\n * The system will substitute these patterns when users mention the term.\n *\n * **Glossary vs Alias:**\n * - `alias` = user vocabulary \u2192 table/column name (\"the big table\" \u2192 \"orders table\")\n * - `glossary` = business term \u2192 SQL expression (\"revenue\" \u2192 \"SUM(orders.total_amount)\")\n *\n * In short: alias renames, glossary computes.\n *\n * @param entries - Record mapping business terms to their expressions\n *\n * @example\n * glossary({\n * \"revenue\": \"SUM(orders.total_amount)\",\n * \"average order value\": \"AVG(orders.total_amount)\",\n * \"active user\": \"last_login > NOW() - INTERVAL '30 days'\",\n * \"churned\": \"status = 'churned'\",\n * \"power user\": \"order_count > 10\",\n * \"net revenue\": \"SUM(orders.total_amount) - SUM(refunds.amount)\",\n * })\n */\nexport function glossary(entries: Record<string, string>): ContextFragment {\n return {\n name: 'glossary',\n data: Object.entries(entries).map(([term, expression]) => ({\n term,\n expression,\n })),\n };\n}\n\n/**\n * Create a role fragment for system prompt instructions.\n */\nexport function role(content: string): ContextFragment {\n return {\n name: 'role',\n data: content,\n };\n}\n\n/**\n * Define a guiding principle that shapes agent behavior.\n *\n * Use this to establish high-level rules for decision-making, reasoning, or domain behavior.\n * Principles can contain policies (specific rules that implement the principle).\n *\n * @param input.title - Name/title of the principle\n * @param input.description - What this principle means and why it matters\n * @param input.policies - Optional specific rules that implement this principle\n *\n * @example\n * // Logical dependencies principle\n * principle({\n * title: \"Logical dependencies and constraints\",\n * description: \"Analyze intended actions against factors in order of importance\",\n * policies: [\n * \"Policy-based rules, mandatory prerequisites, and constraints\",\n * \"Order of operations: Ensure actions don't prevent subsequent necessary actions\",\n * \"Other prerequisites (information and/or actions needed)\",\n * \"Explicit user constraints or preferences\"\n * ]\n * })\n *\n * @example\n * // Risk assessment principle\n * principle({\n * title: \"Risk assessment\",\n * description: \"Evaluate consequences before taking action\",\n * policies: [\n * \"For exploratory tasks, missing optional parameters is LOW risk\",\n * \"Prefer calling tools with available information over asking the user\"\n * ]\n * })\n *\n * @example\n * // Design principle\n * principle({\n * title: \"Separation of concerns\",\n * description: \"Each module should have a single, well-defined responsibility\",\n * policies: [\n * \"Data access logic stays in repository layer\",\n * \"Business rules stay in service layer\",\n * \"Presentation logic stays in controller/view layer\"\n * ]\n * })\n */\nexport function principle(input: {\n title: string;\n description: string;\n policies?: FragmentData[];\n}): ContextFragment {\n return {\n name: 'principle',\n data: {\n title: input.title,\n description: input.description,\n ...(input.policies?.length && { policies: input.policies }),\n },\n };\n}\n\n/**\n * Define a policy rule, optionally with prerequisites or nested sub-policies.\n *\n * Policies can be used in two ways:\n * 1. Prerequisite rules: \"must do X before Y\" using the `before` parameter\n * 2. Sub-policies: nested rules within a principle using the `policies` parameter\n *\n * Policies differ from guardrails: policies are prerequisites (do this first),\n * guardrails are prohibitions (never do this).\n *\n * @param input.rule - The policy rule to enforce\n * @param input.before - What action this is a prerequisite for (optional for sub-policies)\n * @param input.reason - Why this rule matters\n * @param input.policies - Nested sub-policies for hierarchical structure\n *\n * @example\n * // Prerequisite rule with \"before\"\n * policy({\n * rule: \"Validate SQL syntax\",\n * before: \"executing any query against the database\",\n * reason: \"Catches errors early and allows correction before execution\"\n * })\n *\n * @example\n * // Sub-policy within a principle (no \"before\" needed)\n * policy({ rule: \"Policy-based rules, mandatory prerequisites, and constraints.\" })\n *\n * @example\n * // Nested sub-policies (hierarchical structure like 1.2 \u2192 1.2.1)\n * policy({\n * rule: \"Order of operations: Ensure taking an action does not prevent a subsequent necessary action.\",\n * policies: [\n * \"The user may request actions in a random order, but you may need to reorder operations.\",\n * ],\n * })\n */\nexport function policy(input: {\n rule: string;\n before?: string;\n reason?: string;\n policies?: FragmentData[];\n}): ContextFragment {\n return {\n name: 'policy',\n data: {\n rule: input.rule,\n ...(input.before && { before: input.before }),\n ...(input.reason && { reason: input.reason }),\n ...(input.policies?.length && { policies: input.policies }),\n },\n };\n}\n", "import type { ContextFragment } from '../fragments.ts';\n\n/**\n * User-specific fragment builders.\n *\n * These fragments capture user context, preferences, and personalization data\n * that can be injected into AI prompts to tailor responses.\n *\n * @example\n * ```ts\n * import { identity, persona, preference } from '@deepagents/context';\n *\n * context.set(\n * identity({ name: 'John', role: 'VP of Sales' }),\n * persona({ name: 'Freya', role: 'Data Assistant', tone: 'professional' }),\n * preference('date format', 'YYYY-MM-DD'),\n * );\n * ```\n */\n\n/**\n * Define the user's identity including name and/or role.\n *\n * Use this to capture who the user is and what lens they view data through.\n * Helps tailor explanations, terminology, and focus areas.\n *\n * @param input.name - The user's name (optional)\n * @param input.role - The user's role or position (optional)\n *\n * @example\n * identity({ name: \"John\", role: \"VP of Sales\" })\n * identity({ role: \"Data analyst in the marketing team\" })\n * identity({ name: \"Sarah\" })\n * identity({ role: \"Finance manager focused on cost optimization\" })\n */\nexport function identity(input: {\n name?: string;\n role?: string;\n}): ContextFragment {\n return {\n name: 'identity',\n data: {\n ...(input.name && { name: input.name }),\n ...(input.role && { role: input.role }),\n },\n };\n}\n\n/**\n * Define an AI persona with a name, role, objective, and communication tone.\n *\n * Use this to customize the assistant's identity and what it should accomplish.\n *\n * @param input.name - The persona's name\n * @param input.role - The persona's expertise/identity (what they are)\n * @param input.objective - What the persona should accomplish (the goal)\n * @param input.tone - The communication style (e.g., friendly, professional, concise)\n *\n * @example\n * persona({ name: \"DataBot\", role: \"SQL Expert\", objective: \"Generate accurate SQL queries from natural language\" })\n * persona({ name: \"QueryMaster\", role: \"Database Analyst\", objective: \"Help users explore database schemas\" })\n */\nexport function persona(input: {\n name: string;\n role?: string;\n objective?: string;\n tone?: string;\n}): ContextFragment {\n return {\n name: 'persona',\n data: {\n name: input.name,\n ...(input.role && { role: input.role }),\n ...(input.objective && { objective: input.objective }),\n ...(input.tone && { tone: input.tone }),\n },\n };\n}\n\n/**\n * Define user-specific term meanings and vocabulary.\n *\n * Use this when the user has their own definitions for terms that might\n * differ from standard or domain definitions. Like `term()` but personal.\n *\n * @param term - The term the user uses\n * @param meaning - What the user means by this term\n *\n * @example\n * alias(\"revenue\", \"gross revenue before deductions, not net\")\n * alias(\"active users\", \"users who logged in within the last 30 days\")\n * alias(\"the big table\", \"the orders table\")\n * alias(\"Q4\", \"October through December, not fiscal Q4\")\n */\nexport function alias(term: string, meaning: string): ContextFragment {\n return {\n name: 'alias',\n data: { term, meaning },\n };\n}\n\n/**\n * Define how the user prefers results presented.\n *\n * Use this to capture output formatting, style, and behavioral preferences\n * that should apply to all interactions with this user.\n *\n * @param aspect - What aspect of output this preference applies to\n * @param value - The user's preference\n *\n * @example\n * preference(\"date format\", \"YYYY-MM-DD\")\n * preference(\"output style\", \"tables over charts unless trend data\")\n * preference(\"detail level\", \"always show the SQL query in responses\")\n * preference(\"row limit\", \"default to 50 rows unless I ask for more\")\n * preference(\"explanation style\", \"brief and to the point\")\n */\nexport function preference(aspect: string, value: string): ContextFragment {\n return {\n name: 'preference',\n data: { aspect, value },\n };\n}\n\n/**\n * Define the user's current working focus or project.\n *\n * Use this to capture temporary context that helps inform defaults,\n * assumptions, and suggestions. Should be updated as focus changes.\n *\n * @param description - What the user is currently working on\n *\n * @example\n * userContext(\"Preparing Q4 board presentation\")\n * userContext(\"Investigating drop in signups last week\")\n * userContext(\"Working on EMEA regional analysis for strategy meeting\")\n * userContext(\"Debugging discrepancy in revenue numbers\")\n */\nexport function userContext(description: string): ContextFragment {\n return {\n name: 'userContext',\n data: description,\n };\n}\n\n/**\n * Record a correction the user made to previous understanding.\n *\n * Use this when the user corrects a misunderstanding about data, columns,\n * or business logic. Prevents repeating the same mistake.\n *\n * @param subject - What was misunderstood\n * @param clarification - The correct understanding\n *\n * @example\n * correction(\"status column\", \"1 = active, 0 = inactive, not boolean true/false\")\n * correction(\"orders table\", \"Use orders_v2, not the deprecated legacy_orders table\")\n * correction(\"date field\", \"order_date is when order was placed, ship_date is when shipped\")\n * correction(\"revenue calculation\", \"Must exclude refunds and chargebacks\")\n */\nexport function correction(\n subject: string,\n clarification: string,\n): ContextFragment {\n return {\n name: 'correction',\n data: { subject, clarification },\n };\n}\n", "/**\n * Guardrail system for real-time stream interception and self-correction.\n *\n * Guardrails inspect streaming parts and can either:\n * - `pass(part)`: Allow the part through (optionally modified)\n * - `fail(feedback)`: Abort the stream and retry with self-correction feedback\n *\n * When a guardrail fails, the accumulated text is combined with the feedback\n * to create a \"self-correction\" that appears as if the agent caught itself.\n *\n * @example\n * ```typescript\n * const safetyGuardrail: Guardrail = {\n * id: 'safety',\n * name: 'Safety Filter',\n * handle: (part, context) => {\n * if (part.type === 'text-delta' && part.delta.includes('unsafe')) {\n * return fail('I should not provide this information. Let me help differently.');\n * }\n * if (part.type === 'error' && context.availableTools.length > 0) {\n * return fail(`Try using: ${context.availableTools.join(', ')}`);\n * }\n * return pass(part);\n * },\n * };\n *\n * const agent = agent({\n * name: 'safe_assistant',\n * context,\n * model,\n * guardrails: [safetyGuardrail],\n * });\n * ```\n */\nimport type { InferUIMessageChunk, UIDataTypes, UIMessage } from 'ai';\n\n/**\n * Type alias for stream parts from the AI SDK's UI message stream.\n * This is the full chunk type that includes text-delta, error, reasoning-delta, etc.\n */\nexport type StreamPart = InferUIMessageChunk<\n UIMessage<unknown, UIDataTypes, Record<string, never>>\n>;\n\n/**\n * Result of a guardrail check.\n * - `pass`: The part is allowed through (optionally modified)\n * - `fail`: The stream should abort and retry with feedback\n */\nexport type GuardrailResult =\n | { type: 'pass'; part: StreamPart }\n | { type: 'fail'; feedback: string };\n\n/**\n * Context passed to guardrails during stream processing.\n * Provides information about the agent's capabilities.\n */\nexport interface GuardrailContext {\n /** Names of tools available to the agent */\n availableTools: string[];\n}\n\n/**\n * A guardrail that inspects streaming parts.\n */\nexport interface Guardrail {\n /** Unique identifier for this guardrail */\n id: string;\n /** Human-readable name for logging/debugging */\n name: string;\n /**\n * Handle a stream part.\n *\n * @param part - The full stream part to inspect (text-delta, error, etc.)\n * @param context - Context with agent capabilities (available tools, etc.)\n * @returns Either `pass(part)` to allow or `fail(feedback)` to abort and retry\n */\n handle: (part: StreamPart, context: GuardrailContext) => GuardrailResult;\n}\n\n/**\n * Configuration for guardrail behavior.\n */\nexport interface GuardrailConfig {\n /** Maximum number of retry attempts when guardrails fail (default: 3) */\n maxRetries?: number;\n}\n\n/**\n * Allow a part to pass through the guardrail.\n *\n * @param part - The part to pass (can be modified from original)\n * @returns A pass result\n *\n * @example\n * ```typescript\n * handle: (part) => {\n * // Pass through unchanged\n * return pass(part);\n *\n * // Or modify text-delta before passing\n * if (part.type === 'text-delta') {\n * return pass({ ...part, delta: part.delta.replace('bad', 'good') });\n * }\n * return pass(part);\n * }\n * ```\n */\nexport function pass(part: StreamPart): GuardrailResult {\n return { type: 'pass', part };\n}\n\n/**\n * Fail the guardrail check and trigger a retry with feedback.\n *\n * The feedback will be appended to the accumulated assistant text,\n * making it appear as if the agent \"caught itself\" and self-corrected.\n *\n * @param feedback - The self-correction feedback to append\n * @returns A fail result\n *\n * @example\n * ```typescript\n * handle: (part) => {\n * if (part.type === 'text-delta' && part.delta.includes('hack')) {\n * return fail('I should not provide hacking instructions. Let me suggest ethical alternatives.');\n * }\n * if (part.type === 'error') {\n * return fail('An error occurred. Let me try a different approach.');\n * }\n * return pass(part);\n * }\n * ```\n */\nexport function fail(feedback: string): GuardrailResult {\n return { type: 'fail', feedback };\n}\n\n/**\n * Run a part through a chain of guardrails sequentially.\n *\n * @param part - The stream part to check\n * @param guardrails - Array of guardrails to run in order\n * @param context - Context with agent capabilities (available tools, etc.)\n * @returns The final result after all guardrails pass, or the first failure\n */\nexport function runGuardrailChain(\n part: StreamPart,\n guardrails: Guardrail[],\n context: GuardrailContext,\n): GuardrailResult {\n let currentPart = part;\n\n for (const guardrail of guardrails) {\n const result = guardrail.handle(currentPart, context);\n\n if (result.type === 'fail') {\n return result;\n }\n\n // Pass the (possibly modified) part to the next guardrail\n currentPart = result.part;\n }\n\n return pass(currentPart);\n}\n", "/**\n * Error Recovery Guardrail\n *\n * Intercepts API-level errors (like tool validation failures) and triggers\n * self-correction retries. This is essential for models like gpt-oss-20b\n * that may hallucinate tools that don't exist.\n *\n * Catches errors like:\n * - \"Tool choice is none, but model called a tool\"\n * - \"attempted to call tool 'X' which was not in request.tools\"\n * - \"Failed to parse tool call arguments as JSON\" (malformed JSON)\n * - Parsing failures\n *\n * @example\n * ```typescript\n * const myAgent = agent({\n * name: 'my_agent',\n * model: groq('openai/gpt-oss-20b'),\n * tools: { bash, sql },\n * guardrails: [errorRecoveryGuardrail],\n * maxGuardrailRetries: 3,\n * });\n * ```\n */\nimport chalk from 'chalk';\n\nimport type { Guardrail } from '../guardrail.ts';\nimport { fail, pass } from '../guardrail.ts';\n\nexport const errorRecoveryGuardrail: Guardrail = {\n id: 'error-recovery',\n name: 'API Error Recovery',\n handle: (part, context) => {\n // Only handle error parts\n if (part.type !== 'error') {\n return pass(part);\n }\n\n const errorText = part.errorText || '';\n const prefix = chalk.bold.magenta('[ErrorRecovery]');\n\n console.log(\n `${prefix} ${chalk.red('Caught error:')} ${chalk.dim(errorText.slice(0, 150))}`,\n );\n\n // Helper to log and return fail\n const logAndFail = (pattern: string, feedback: string) => {\n console.log(\n `${prefix} ${chalk.yellow('Pattern:')} ${chalk.cyan(pattern)}`,\n );\n console.log(\n `${prefix} ${chalk.green('Feedback:')} ${chalk.dim(feedback.slice(0, 80))}...`,\n );\n return fail(feedback);\n };\n\n // Pattern: No tools available but model tried to call one\n if (errorText.includes('Tool choice is none')) {\n if (context.availableTools.length > 0) {\n return logAndFail(\n 'Tool choice is none',\n `I tried to call a tool that doesn't exist. Available tools: ${context.availableTools.join(', ')}. Let me use one of these instead.`,\n );\n }\n return logAndFail(\n 'Tool choice is none (no tools)',\n 'I tried to call a tool, but no tools are available. Let me respond with plain text instead.',\n );\n }\n\n // Pattern: Tool not found in request.tools\n if (\n errorText.includes('not in request.tools') ||\n (errorText.includes('tool') && errorText.includes('not found'))\n ) {\n const toolMatch = errorText.match(/tool '([^']+)'/);\n const toolName = toolMatch ? toolMatch[1] : 'unknown';\n if (context.availableTools.length > 0) {\n return logAndFail(\n `Unregistered tool: ${toolName}`,\n `I tried to call \"${toolName}\" but it doesn't exist. Available tools: ${context.availableTools.join(', ')}. Let me use one of these instead.`,\n );\n }\n return logAndFail(\n `Unregistered tool: ${toolName} (no tools)`,\n `I tried to call \"${toolName}\" but no tools are available. Let me respond with plain text instead.`,\n );\n }\n\n // Pattern: Failed to parse tool arguments as JSON\n if (\n errorText.includes('Failed to parse tool call arguments') ||\n errorText.includes('parse tool call') ||\n errorText.includes('invalid JSON')\n ) {\n return logAndFail(\n 'Malformed JSON arguments',\n 'I generated malformed JSON for the tool arguments. Let me format my tool call properly with valid JSON.',\n );\n }\n\n // Pattern: Parsing failed (generic)\n if (errorText.includes('Parsing failed')) {\n return logAndFail(\n 'Parsing failed',\n 'My response format was invalid. Let me try again with a properly formatted response.',\n );\n }\n\n // Unknown error - still try to recover\n return logAndFail(\n 'Unknown error',\n `An error occurred: ${errorText}. Let me try a different approach.`,\n );\n },\n};\n", "import { existsSync } from 'fs';\nimport { type CustomCommand, defineCommand } from 'just-bash';\nimport spawn from 'nano-spawn';\nimport * as path from 'path';\n\nexport interface BinaryBridgeConfig {\n /** Command name in the sandbox (what the agent types) */\n name: string;\n /** Actual binary path on the host system (defaults to name) */\n binaryPath?: string;\n /** Optional regex to restrict allowed arguments for security */\n allowedArgs?: RegExp;\n}\n\nexport type BinaryBridgeInput = string | BinaryBridgeConfig;\n\n/**\n * Creates custom commands that bridge to real system binaries.\n *\n * This allows just-bash sandboxed environments to execute specific\n * host system binaries while maintaining control over which binaries\n * are accessible.\n *\n * @example\n * // Simple - just strings (name === binaryPath)\n * createBinaryBridges('presenterm', 'node', 'cargo')\n *\n * @example\n * // Mixed - strings and config objects\n * createBinaryBridges(\n * 'presenterm',\n * { name: 'python', binaryPath: 'python3' },\n * { name: 'git', allowedArgs: /^(status|log|diff)/ }\n * )\n */\nexport function createBinaryBridges(\n ...binaries: BinaryBridgeInput[]\n): CustomCommand[] {\n return binaries.map((input) => {\n const config: BinaryBridgeConfig =\n typeof input === 'string' ? { name: input } : input;\n\n const { name, binaryPath = name, allowedArgs } = config;\n\n return defineCommand(name, async (args, ctx) => {\n // Validate args against pattern if specified\n if (allowedArgs) {\n const invalidArg = args.find((arg) => !allowedArgs.test(arg));\n if (invalidArg) {\n return {\n stdout: '',\n stderr: `${name}: argument '${invalidArg}' not allowed by security policy`,\n exitCode: 1,\n };\n }\n }\n\n try {\n // Resolve the real working directory from the virtual filesystem\n // just-bash uses virtual paths like /home/user, we need the real host path\n const realCwd = resolveRealCwd(ctx);\n\n // Resolve file paths in arguments relative to the real cwd\n const resolvedArgs = args.map((arg) => {\n // Skip flags and options\n if (arg.startsWith('-')) {\n return arg;\n }\n\n // Check if arg looks like a path:\n // 1. Has a file extension (e.g., file.md, script.py)\n // 2. Contains path separator (e.g., src/file, dir\\file)\n // 3. Is a relative path starting with . (e.g., ., .., ./foo)\n const hasExtension = path.extname(arg) !== '';\n const hasPathSep = arg.includes(path.sep) || arg.includes('/');\n const isRelative = arg.startsWith('.');\n\n if (hasExtension || hasPathSep || isRelative) {\n // Resolve relative to the real cwd\n return path.resolve(realCwd, arg);\n }\n\n return arg;\n });\n\n // Merge environments but preserve process.env.PATH for binary resolution\n // ctx.env.PATH is the virtual PATH (/bin:/usr/bin) which doesn't include host binaries\n const mergedEnv = {\n ...process.env,\n ...ctx.env,\n PATH: process.env.PATH, // Always use host PATH for binary bridges\n };\n\n const result = await spawn(binaryPath, resolvedArgs, {\n cwd: realCwd,\n env: mergedEnv,\n });\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: 0,\n };\n } catch (error) {\n // nano-spawn wraps ENOENT (missing binary) into a SubprocessError\n // with exitCode undefined and the real cause on error.cause.\n if (error && typeof error === 'object') {\n const err = error as { cause?: unknown; message?: string };\n const cause = err.cause as\n | { code?: string; path?: string; syscall?: string }\n | undefined;\n\n if (cause?.code === 'ENOENT') {\n return {\n stdout: '',\n stderr: `${name}: ${binaryPath} not found`,\n exitCode: 127,\n };\n }\n }\n\n // nano-spawn throws SubprocessError for non-zero exits\n if (error && typeof error === 'object' && 'exitCode' in error) {\n const subprocessError = error as {\n exitCode?: number;\n stdout: string;\n stderr: string;\n };\n return {\n stdout: subprocessError.stdout ?? '',\n stderr: subprocessError.stderr ?? '',\n exitCode: subprocessError.exitCode ?? 1,\n };\n }\n\n // Unknown error (e.g., binary not found)\n return {\n stdout: '',\n stderr: `${name}: ${error instanceof Error ? error.message : String(error)}`,\n exitCode: 127,\n };\n }\n });\n });\n}\n\n/**\n * Resolves the real filesystem path from a just-bash virtual path.\n *\n * just-bash filesystems (ReadWriteFs, OverlayFs) use virtual paths like /home/user\n * but we need the actual host filesystem path for spawning processes.\n */\nfunction resolveRealCwd(ctx: { cwd: string; fs: unknown }): string {\n const fs = ctx.fs as {\n toRealPath?: (p: string) => string | null;\n root?: string;\n getMountPoint?: () => string;\n };\n\n let realCwd: string;\n\n if (fs.root) {\n // ReadWriteFs - virtual paths are relative to root\n // e.g., root=/Users/x/project, cwd=/ -> /Users/x/project\n realCwd = path.join(fs.root, ctx.cwd);\n } else if (\n typeof fs.getMountPoint === 'function' &&\n typeof fs.toRealPath === 'function'\n ) {\n // OverlayFs - use toRealPath for proper path mapping\n const real = fs.toRealPath(ctx.cwd);\n realCwd = real ?? process.cwd();\n } else {\n // Fallback for InMemoryFs or unknown filesystems\n realCwd = process.cwd();\n }\n\n // Verify the path exists, fall back to process.cwd() if not\n if (!existsSync(realCwd)) {\n realCwd = process.cwd();\n }\n\n return realCwd;\n}\n", "import { type CommandResult, type Sandbox } from 'bash-tool';\nimport spawn from 'nano-spawn';\nimport { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\n\n// Re-export types from bash-tool for convenience\nexport type { CommandResult as ExecResult, Sandbox } from 'bash-tool';\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Error Classes\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Base error for all Docker sandbox operations.\n */\nexport class DockerSandboxError extends Error {\n readonly containerId?: string;\n\n constructor(message: string, containerId?: string) {\n super(message);\n this.name = 'DockerSandboxError';\n this.containerId = containerId;\n }\n}\n\n/**\n * Thrown when Docker daemon is not available.\n */\nexport class DockerNotAvailableError extends DockerSandboxError {\n constructor() {\n super('Docker is not available. Ensure Docker daemon is running.');\n this.name = 'DockerNotAvailableError';\n }\n}\n\n/**\n * Thrown when container creation fails.\n */\nexport class ContainerCreationError extends DockerSandboxError {\n readonly image: string;\n override cause?: Error;\n\n constructor(message: string, image: string, cause?: Error) {\n super(`Failed to create container from image \"${image}\": ${message}`);\n this.name = 'ContainerCreationError';\n this.image = image;\n this.cause = cause;\n }\n}\n\n/**\n * Thrown when package installation fails.\n */\nexport class PackageInstallError extends DockerSandboxError {\n readonly packages: string[];\n readonly image: string;\n readonly packageManager: 'apk' | 'apt-get';\n readonly stderr: string;\n\n constructor(\n packages: string[],\n image: string,\n packageManager: 'apk' | 'apt-get',\n stderr: string,\n containerId?: string,\n ) {\n super(\n `Package installation failed for [${packages.join(', ')}] ` +\n `using ${packageManager} on ${image}: ${stderr}`,\n containerId,\n );\n this.name = 'PackageInstallError';\n this.packages = packages;\n this.image = image;\n this.packageManager = packageManager;\n this.stderr = stderr;\n }\n}\n\n/**\n * Thrown when a binary installation from URL fails.\n */\nexport class BinaryInstallError extends DockerSandboxError {\n readonly binaryName: string;\n readonly url: string;\n readonly reason: string;\n\n constructor(\n binaryName: string,\n url: string,\n reason: string,\n containerId?: string,\n ) {\n super(\n `Failed to install binary \"${binaryName}\" from ${url}: ${reason}`,\n containerId,\n );\n this.name = 'BinaryInstallError';\n this.binaryName = binaryName;\n this.url = url;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown when a mount path doesn't exist on the host.\n */\nexport class MountPathError extends DockerSandboxError {\n readonly hostPath: string;\n readonly containerPath: string;\n\n constructor(hostPath: string, containerPath: string) {\n super(\n `Mount path does not exist on host: \"${hostPath}\" -> \"${containerPath}\"`,\n );\n this.name = 'MountPathError';\n this.hostPath = hostPath;\n this.containerPath = containerPath;\n }\n}\n\n/**\n * Thrown when Dockerfile build fails.\n */\nexport class DockerfileBuildError extends DockerSandboxError {\n readonly stderr: string;\n\n constructor(stderr: string) {\n super(`Dockerfile build failed: ${stderr}`);\n this.name = 'DockerfileBuildError';\n this.stderr = stderr;\n }\n}\n\n/**\n * Thrown when docker compose up fails.\n */\nexport class ComposeStartError extends DockerSandboxError {\n readonly composeFile: string;\n readonly stderr: string;\n\n constructor(composeFile: string, stderr: string) {\n super(`Docker Compose failed to start: ${stderr}`);\n this.name = 'ComposeStartError';\n this.composeFile = composeFile;\n this.stderr = stderr;\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Interfaces\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Configuration for mounting a host directory into the container.\n */\nexport interface DockerMount {\n /** Absolute path on the host machine */\n hostPath: string;\n /** Path inside the container */\n containerPath: string;\n /** Whether the mount is read-only (default: true) */\n readOnly?: boolean;\n}\n\n/**\n * Resource limits for the container.\n */\nexport interface DockerResources {\n /** Memory limit (e.g., '1g', '512m') */\n memory?: string;\n /** CPU limit (number of CPUs) */\n cpus?: number;\n}\n\n/**\n * Architecture-specific URL mapping for binary downloads.\n * Maps container architecture (from `uname -m`) to download URLs.\n */\nexport interface ArchitectureUrls {\n /** URL for x86_64 architecture (amd64) */\n x86_64?: string;\n /** URL for ARM64 architecture (aarch64) */\n aarch64?: string;\n /** URL for ARMv7 architecture */\n armv7l?: string;\n}\n\n/**\n * Configuration for installing a binary from a URL.\n *\n * Binaries are downloaded, extracted (if tar.gz), and installed to /usr/local/bin.\n */\nexport interface BinaryInstall {\n /** Name of the binary (used for the final executable name) */\n name: string;\n /**\n * URL or architecture-specific URLs.\n * - If a string, used for all architectures\n * - If ArchitectureUrls, selects based on container architecture\n */\n url: string | ArchitectureUrls;\n /**\n * Optional: The binary filename inside the archive if different from `name`.\n * Useful when the archive contains versioned binaries like \"presenterm-0.15.1\".\n */\n binaryPath?: string;\n}\n\n/**\n * Options for RuntimeStrategy - installs packages/binaries at container runtime.\n */\nexport interface RuntimeSandboxOptions {\n /** Docker image to use (default: 'alpine:latest') */\n image?: string;\n /** Packages to install in the container via package manager (apk/apt) */\n packages?: string[];\n /** Binaries to install from URLs (for tools not in package managers) */\n binaries?: BinaryInstall[];\n /** Directories to mount from host */\n mounts?: DockerMount[];\n /** Resource limits */\n resources?: DockerResources;\n}\n\n/**\n * Options for DockerfileStrategy - builds custom image from Dockerfile.\n */\nexport interface DockerfileSandboxOptions {\n /** Dockerfile content (if contains newlines) or path to Dockerfile */\n dockerfile: string;\n /** Build context directory (default: '.') */\n context?: string;\n /** Directories to mount from host */\n mounts?: DockerMount[];\n /** Resource limits */\n resources?: DockerResources;\n}\n\n/**\n * Options for ComposeStrategy - manages multi-container environments.\n */\nexport interface ComposeSandboxOptions {\n /** Path to docker-compose.yml file */\n compose: string;\n /** Service name to execute commands in (required) */\n service: string;\n /** Resource limits (applied to target service only) */\n resources?: DockerResources;\n // Note: mounts must be defined in compose file, not here\n}\n\n/**\n * Union type for Docker sandbox options.\n * - RuntimeSandboxOptions: Runtime package/binary installation\n * - DockerfileSandboxOptions: Pre-built images from Dockerfile\n * - ComposeSandboxOptions: Multi-container environments via Docker Compose\n */\nexport type DockerSandboxOptions =\n | RuntimeSandboxOptions\n | DockerfileSandboxOptions\n | ComposeSandboxOptions;\n\n/**\n * Extended sandbox interface with disposal method.\n */\nexport interface DockerSandbox extends Sandbox {\n /** Stop and remove the container */\n dispose(): Promise<void>;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Helper Functions\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Detects if the image is Debian-based (uses apt-get) or Alpine-based (uses apk).\n */\nfunction isDebianBased(image: string): boolean {\n const debianPatterns = ['debian', 'ubuntu', 'node', 'python'];\n return debianPatterns.some((pattern) =>\n image.toLowerCase().includes(pattern),\n );\n}\n\n/**\n * Type guard to determine if options are for DockerfileStrategy.\n */\nexport function isDockerfileOptions(\n opts: DockerSandboxOptions,\n): opts is DockerfileSandboxOptions {\n return 'dockerfile' in opts;\n}\n\n/**\n * Type guard to determine if options are for ComposeStrategy.\n */\nexport function isComposeOptions(\n opts: DockerSandboxOptions,\n): opts is ComposeSandboxOptions {\n return 'compose' in opts;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Strategy Pattern - Base Class\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Internal context shared across strategy methods.\n */\ninterface StrategyContext {\n containerId: string;\n image: string;\n}\n\n/**\n * Abstract base class for Docker sandbox creation strategies.\n *\n * Uses the Template Method pattern to define the skeleton of the sandbox\n * creation algorithm, deferring specific steps to subclasses.\n *\n * @example Extending the strategy\n * ```typescript\n * class CustomStrategy extends DockerSandboxStrategy {\n * protected async getImage(): Promise<string> {\n * // Custom image resolution logic\n * return 'my-custom-image:latest';\n * }\n *\n * protected async configure(): Promise<void> {\n * // Custom configuration after container starts\n * }\n * }\n * ```\n */\nexport abstract class DockerSandboxStrategy {\n protected context!: StrategyContext;\n protected mounts: DockerMount[];\n protected resources: DockerResources;\n\n constructor(mounts: DockerMount[] = [], resources: DockerResources = {}) {\n this.mounts = mounts;\n this.resources = resources;\n }\n\n /**\n * Template method - defines the algorithm skeleton for creating a sandbox.\n *\n * Steps:\n * 1. Validate mount paths exist on host\n * 2. Get/build the Docker image (strategy-specific)\n * 3. Start the container\n * 4. Configure the container (strategy-specific)\n * 5. Create and return sandbox methods\n */\n async create(): Promise<DockerSandbox> {\n this.validateMounts();\n const image = await this.getImage();\n const containerId = await this.startContainer(image);\n this.context = { containerId, image };\n\n try {\n await this.configure();\n } catch (error) {\n // Clean up container if configuration fails\n await this.stopContainer(containerId);\n throw error;\n }\n\n return this.createSandboxMethods();\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Common implementations (shared by all strategies)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Validates that all mount paths exist on the host filesystem.\n */\n protected validateMounts(): void {\n for (const mount of this.mounts) {\n if (!existsSync(mount.hostPath)) {\n throw new MountPathError(mount.hostPath, mount.containerPath);\n }\n }\n }\n\n /**\n * Builds the docker run command arguments.\n */\n protected buildDockerArgs(image: string, containerId: string): string[] {\n const { memory = '1g', cpus = 2 } = this.resources;\n\n const args: string[] = [\n 'run',\n '-d', // Detached mode\n '--rm', // Remove container when stopped\n '--name',\n containerId,\n `--memory=${memory}`,\n `--cpus=${cpus}`,\n '-w',\n '/workspace', // Set working directory\n ];\n\n // Add mounts\n for (const mount of this.mounts) {\n const mode = mount.readOnly !== false ? 'ro' : 'rw';\n args.push('-v', `${mount.hostPath}:${mount.containerPath}:${mode}`);\n }\n\n // Add image and command to keep container alive\n args.push(image, 'tail', '-f', '/dev/null');\n\n return args;\n }\n\n /**\n * Starts a Docker container with the given image.\n */\n protected async startContainer(image: string): Promise<string> {\n const containerId = `sandbox-${crypto.randomUUID().slice(0, 8)}`;\n const args = this.buildDockerArgs(image, containerId);\n\n try {\n await spawn('docker', args);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n if (\n err.message?.includes('Cannot connect') ||\n err.message?.includes('docker daemon') ||\n err.stderr?.includes('Cannot connect')\n ) {\n throw new DockerNotAvailableError();\n }\n throw new ContainerCreationError(err.message || String(err), image, err);\n }\n\n return containerId;\n }\n\n /**\n * Stops a Docker container.\n */\n protected async stopContainer(containerId: string): Promise<void> {\n try {\n await spawn('docker', ['stop', containerId]);\n } catch {\n // Container may already be stopped, ignore errors\n }\n }\n\n /**\n * Executes a command in the container.\n */\n protected async exec(command: string): Promise<CommandResult> {\n try {\n const result = await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n command,\n ]);\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: 0,\n };\n } catch (error) {\n const err = error as Error & {\n stdout?: string;\n stderr?: string;\n exitCode?: number;\n };\n return {\n stdout: err.stdout || '',\n stderr: err.stderr || err.message || '',\n exitCode: err.exitCode ?? 1,\n };\n }\n }\n\n /**\n * Creates the DockerSandbox interface with all methods.\n */\n protected createSandboxMethods(): DockerSandbox {\n const { containerId } = this.context;\n\n const sandbox: DockerSandbox = {\n executeCommand: async (command: string): Promise<CommandResult> => {\n return this.exec(command);\n },\n\n readFile: async (path: string): Promise<string> => {\n // Use base64 encoding to preserve exact content (including trailing newlines)\n // nano-spawn strips trailing newlines from stdout, so we encode/decode\n const result = await sandbox.executeCommand(`base64 \"${path}\"`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read file \"${path}\": ${result.stderr}`);\n }\n return Buffer.from(result.stdout, 'base64').toString('utf-8');\n },\n\n writeFiles: async (\n files: Array<{ path: string; content: string }>,\n ): Promise<void> => {\n for (const file of files) {\n // Create parent directories\n const dir = file.path.substring(0, file.path.lastIndexOf('/'));\n if (dir) {\n await sandbox.executeCommand(`mkdir -p \"${dir}\"`);\n }\n\n // Use base64 encoding for binary-safe file writes\n const base64Content = Buffer.from(file.content).toString('base64');\n const result = await sandbox.executeCommand(\n `echo \"${base64Content}\" | base64 -d > \"${file.path}\"`,\n );\n\n if (result.exitCode !== 0) {\n throw new Error(\n `Failed to write file \"${file.path}\": ${result.stderr}`,\n );\n }\n }\n },\n\n dispose: async (): Promise<void> => {\n await this.stopContainer(containerId);\n },\n };\n\n return sandbox;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Strategy-specific hooks (to be implemented by subclasses)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Returns the Docker image to use for the container.\n * For RuntimeStrategy: returns the image name directly.\n * For DockerfileStrategy: builds the image and returns the tag.\n */\n protected abstract getImage(): Promise<string>;\n\n /**\n * Configures the container after it starts.\n * For RuntimeStrategy: installs packages and binaries.\n * For DockerfileStrategy: no-op (Dockerfile already configured).\n */\n protected abstract configure(): Promise<void>;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// RuntimeStrategy - Installs packages/binaries at container runtime\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Strategy that uses an existing Docker image and installs packages/binaries\n * at container runtime.\n *\n * This is the \"configure-on-demand\" approach - starts a vanilla image and\n * customizes it by executing installation commands.\n *\n * @example\n * ```typescript\n * const strategy = new RuntimeStrategy(\n * 'alpine:latest',\n * ['curl', 'jq'],\n * [{ name: 'presenterm', url: {...} }],\n * );\n * const sandbox = await strategy.create();\n * ```\n */\nexport class RuntimeStrategy extends DockerSandboxStrategy {\n private image: string;\n private packages: string[];\n private binaries: BinaryInstall[];\n\n constructor(\n image = 'alpine:latest',\n packages: string[] = [],\n binaries: BinaryInstall[] = [],\n mounts?: DockerMount[],\n resources?: DockerResources,\n ) {\n super(mounts, resources);\n this.image = image;\n this.packages = packages;\n this.binaries = binaries;\n }\n\n protected async getImage(): Promise<string> {\n return this.image;\n }\n\n protected async configure(): Promise<void> {\n await this.installPackages();\n await this.installBinaries();\n }\n\n /**\n * Installs packages using the appropriate package manager (apk/apt-get).\n */\n private async installPackages(): Promise<void> {\n if (this.packages.length === 0) return;\n\n const useApt = isDebianBased(this.image);\n const installCmd = useApt\n ? `apt-get update && apt-get install -y ${this.packages.join(' ')}`\n : `apk add --no-cache ${this.packages.join(' ')}`;\n\n try {\n await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n installCmd,\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new PackageInstallError(\n this.packages,\n this.image,\n useApt ? 'apt-get' : 'apk',\n err.stderr || err.message,\n this.context.containerId,\n );\n }\n }\n\n /**\n * Installs binaries from URLs.\n */\n private async installBinaries(): Promise<void> {\n if (this.binaries.length === 0) return;\n\n // Ensure curl is available for downloading\n await this.ensureCurl();\n\n // Detect container architecture\n const arch = await this.detectArchitecture();\n\n // Install each binary\n for (const binary of this.binaries) {\n await this.installBinary(binary, arch);\n }\n }\n\n /**\n * Ensures curl is installed in the container.\n */\n private async ensureCurl(): Promise<void> {\n const checkResult = await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'which',\n 'curl',\n ]).catch(() => null);\n\n if (checkResult) return; // curl already installed\n\n const useApt = isDebianBased(this.image);\n const curlInstallCmd = useApt\n ? 'apt-get update && apt-get install -y curl'\n : 'apk add --no-cache curl';\n\n try {\n await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n curlInstallCmd,\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new BinaryInstallError(\n 'curl',\n 'package-manager',\n `Required for binary downloads: ${err.stderr || err.message}`,\n this.context.containerId,\n );\n }\n }\n\n /**\n * Detects the container's CPU architecture.\n */\n private async detectArchitecture(): Promise<string> {\n try {\n const result = await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'uname',\n '-m',\n ]);\n return result.stdout.trim();\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new DockerSandboxError(\n `Failed to detect container architecture: ${err.stderr || err.message}`,\n this.context.containerId,\n );\n }\n }\n\n /**\n * Installs a single binary from URL.\n */\n private async installBinary(\n binary: BinaryInstall,\n arch: string,\n ): Promise<void> {\n // Resolve URL based on architecture\n let url: string;\n if (typeof binary.url === 'string') {\n url = binary.url;\n } else {\n const archUrl = binary.url[arch as keyof ArchitectureUrls];\n if (!archUrl) {\n throw new BinaryInstallError(\n binary.name,\n `arch:${arch}`,\n `No URL provided for architecture \"${arch}\". Available: ${Object.keys(binary.url).join(', ')}`,\n this.context.containerId,\n );\n }\n url = archUrl;\n }\n\n // Download and install the binary\n const isTarGz = url.endsWith('.tar.gz') || url.endsWith('.tgz');\n let installCmd: string;\n\n if (isTarGz) {\n const binaryPathInArchive = binary.binaryPath || binary.name;\n installCmd = `\n set -e\n TMPDIR=$(mktemp -d)\n cd \"$TMPDIR\"\n curl -fsSL \"${url}\" -o archive.tar.gz\n tar -xzf archive.tar.gz\n BINARY_FILE=$(find . -name \"${binaryPathInArchive}\" -o -name \"${binary.name}\" | head -1)\n if [ -z \"$BINARY_FILE\" ]; then\n echo \"Binary not found in archive. Contents:\" >&2\n find . -type f >&2\n exit 1\n fi\n chmod +x \"$BINARY_FILE\"\n mv \"$BINARY_FILE\" /usr/local/bin/${binary.name}\n cd /\n rm -rf \"$TMPDIR\"\n `;\n } else {\n installCmd = `\n curl -fsSL \"${url}\" -o /usr/local/bin/${binary.name}\n chmod +x /usr/local/bin/${binary.name}\n `;\n }\n\n try {\n await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n installCmd,\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new BinaryInstallError(\n binary.name,\n url,\n err.stderr || err.message,\n this.context.containerId,\n );\n }\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// DockerfileStrategy - Builds image from Dockerfile\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Strategy that builds a custom Docker image from a Dockerfile.\n *\n * This is the \"build-once, run-many\" approach - builds the image upfront\n * (with caching) and runs containers from the pre-configured image.\n *\n * Image caching: Uses a deterministic tag based on Dockerfile content hash.\n * If the same Dockerfile is used, the existing image is reused (cache hit).\n *\n * @example Inline Dockerfile\n * ```typescript\n * const strategy = new DockerfileStrategy(`\n * FROM alpine:latest\n * RUN apk add --no-cache curl jq\n * `);\n * const sandbox = await strategy.create();\n * ```\n *\n * @example Dockerfile path\n * ```typescript\n * const strategy = new DockerfileStrategy(\n * './Dockerfile.sandbox',\n * './docker', // build context\n * );\n * const sandbox = await strategy.create();\n * ```\n */\nexport class DockerfileStrategy extends DockerSandboxStrategy {\n private imageTag: string;\n private dockerfile: string;\n private dockerContext: string;\n\n constructor(\n dockerfile: string,\n dockerContext = '.',\n mounts?: DockerMount[],\n resources?: DockerResources,\n ) {\n super(mounts, resources);\n this.dockerfile = dockerfile;\n this.dockerContext = dockerContext;\n this.imageTag = this.computeImageTag();\n }\n\n /**\n * Computes a deterministic image tag based on Dockerfile content.\n * Same Dockerfile \u2192 same tag \u2192 Docker skips rebuild if image exists.\n */\n private computeImageTag(): string {\n const content = this.isInlineDockerfile()\n ? this.dockerfile\n : readFileSync(this.dockerfile, 'utf-8');\n const hash = createHash('sha256')\n .update(content)\n .digest('hex')\n .slice(0, 12);\n return `sandbox-${hash}`;\n }\n\n /**\n * Checks if the dockerfile property is inline content or a file path.\n */\n private isInlineDockerfile(): boolean {\n return this.dockerfile.includes('\\n');\n }\n\n protected async getImage(): Promise<string> {\n // Check if image already exists (cache hit)\n const exists = await this.imageExists();\n if (!exists) {\n await this.buildImage();\n }\n return this.imageTag;\n }\n\n protected async configure(): Promise<void> {\n // No-op - Dockerfile already configured the image\n }\n\n /**\n * Checks if the image already exists locally.\n */\n private async imageExists(): Promise<boolean> {\n try {\n await spawn('docker', ['image', 'inspect', this.imageTag]);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Builds the Docker image from the Dockerfile.\n */\n private async buildImage(): Promise<void> {\n try {\n if (this.isInlineDockerfile()) {\n // Inline Dockerfile - use heredoc via shell\n const buildCmd = `echo '${this.dockerfile.replace(/'/g, \"'\\\\''\")}' | docker build -t ${this.imageTag} -f - ${this.dockerContext}`;\n await spawn('sh', ['-c', buildCmd]);\n } else {\n // Path to Dockerfile\n await spawn('docker', [\n 'build',\n '-t',\n this.imageTag,\n '-f',\n this.dockerfile,\n this.dockerContext,\n ]);\n }\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new DockerfileBuildError(err.stderr || err.message);\n }\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// ComposeStrategy - Multi-container environments via Docker Compose\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Strategy that manages multi-container environments using Docker Compose.\n *\n * Unlike other strategies that manage a single container, ComposeStrategy\n * orchestrates multiple services as a unit using docker compose commands.\n *\n * @example\n * ```typescript\n * const strategy = new ComposeStrategy(\n * './docker-compose.yml',\n * 'app', // Service to execute commands in\n * );\n * const sandbox = await strategy.create();\n *\n * // Commands run in the 'app' service\n * await sandbox.executeCommand('node --version');\n *\n * // Can communicate with other services via service names\n * await sandbox.executeCommand('curl http://api:3000/health');\n *\n * // Stops ALL services\n * await sandbox.dispose();\n * ```\n */\nexport class ComposeStrategy extends DockerSandboxStrategy {\n private projectName: string;\n private composeFile: string;\n private service: string;\n\n constructor(\n composeFile: string,\n service: string,\n resources?: DockerResources,\n ) {\n // Pass empty mounts - compose handles its own volumes\n super([], resources);\n this.composeFile = composeFile;\n this.service = service;\n this.projectName = this.computeProjectName();\n }\n\n /**\n * Deterministic project name based on compose file content for caching.\n * Same compose file \u2192 same project name \u2192 faster subsequent startups.\n */\n private computeProjectName(): string {\n const content = readFileSync(this.composeFile, 'utf-8');\n const hash = createHash('sha256').update(content).digest('hex').slice(0, 8);\n return `sandbox-${hash}`;\n }\n\n /**\n * Override: No image to get - compose manages its own images.\n */\n protected async getImage(): Promise<string> {\n return ''; // Not used for compose\n }\n\n /**\n * Override: Start all services with docker compose up.\n */\n protected override async startContainer(_image: string): Promise<string> {\n try {\n await spawn('docker', [\n 'compose',\n '-f',\n this.composeFile,\n '-p',\n this.projectName,\n 'up',\n '-d',\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n if (err.stderr?.includes('Cannot connect')) {\n throw new DockerNotAvailableError();\n }\n throw new ComposeStartError(this.composeFile, err.stderr || err.message);\n }\n\n // Return project name as the \"container ID\" for context\n return this.projectName;\n }\n\n protected async configure(): Promise<void> {\n // No additional configuration - compose file defines everything\n }\n\n /**\n * Override: Execute commands in the target service.\n */\n protected override async exec(command: string): Promise<CommandResult> {\n try {\n const result = await spawn('docker', [\n 'compose',\n '-f',\n this.composeFile,\n '-p',\n this.projectName,\n 'exec',\n '-T', // -T disables pseudo-TTY\n this.service,\n 'sh',\n '-c',\n command,\n ]);\n return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };\n } catch (error) {\n const err = error as Error & {\n stdout?: string;\n stderr?: string;\n exitCode?: number;\n };\n return {\n stdout: err.stdout || '',\n stderr: err.stderr || err.message || '',\n exitCode: err.exitCode ?? 1,\n };\n }\n }\n\n /**\n * Override: Stop all services with docker compose down.\n */\n protected override async stopContainer(_containerId: string): Promise<void> {\n try {\n await spawn('docker', [\n 'compose',\n '-f',\n this.composeFile,\n '-p',\n this.projectName,\n 'down',\n ]);\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Factory Function\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Creates a Docker-based sandbox for executing commands in an isolated container.\n *\n * Supports three strategies:\n * - **RuntimeStrategy**: Uses existing image, installs packages/binaries at runtime\n * - **DockerfileStrategy**: Builds custom image from Dockerfile (with caching)\n * - **ComposeStrategy**: Multi-container environments via Docker Compose\n *\n * @example RuntimeStrategy (default)\n * ```typescript\n * const sandbox = await createDockerSandbox({\n * image: 'alpine:latest',\n * packages: ['curl', 'jq'],\n * binaries: [{ name: 'presenterm', url: {...} }],\n * });\n * await sandbox.executeCommand('curl --version');\n * await sandbox.dispose();\n * ```\n *\n * @example DockerfileStrategy\n * ```typescript\n * const sandbox = await createDockerSandbox({\n * dockerfile: `\n * FROM alpine:latest\n * RUN apk add --no-cache curl jq\n * `,\n * context: '.',\n * });\n * await sandbox.executeCommand('curl --version');\n * await sandbox.dispose();\n * ```\n *\n * @example ComposeStrategy\n * ```typescript\n * const sandbox = await createDockerSandbox({\n * compose: './docker-compose.yml',\n * service: 'app',\n * });\n * // Commands run in the 'app' service\n * await sandbox.executeCommand('node --version');\n * // Can reach other services by name\n * await sandbox.executeCommand('curl http://db:5432');\n * await sandbox.dispose(); // Stops ALL services\n * ```\n */\nexport async function createDockerSandbox(\n options: DockerSandboxOptions = {},\n): Promise<DockerSandbox> {\n let strategy: DockerSandboxStrategy;\n\n if (isComposeOptions(options)) {\n strategy = new ComposeStrategy(\n options.compose,\n options.service,\n options.resources,\n );\n } else if (isDockerfileOptions(options)) {\n strategy = new DockerfileStrategy(\n options.dockerfile,\n options.context,\n options.mounts,\n options.resources,\n );\n } else {\n strategy = new RuntimeStrategy(\n options.image,\n options.packages,\n options.binaries,\n options.mounts,\n options.resources,\n );\n }\n\n return strategy.create();\n}\n\n/**\n * Execute a function with a Docker sandbox that auto-disposes on completion.\n * Ensures cleanup even if the function throws.\n *\n * @example\n * ```typescript\n * const output = await useSandbox(\n * { packages: ['curl', 'jq'] },\n * async (sandbox) => {\n * const result = await sandbox.executeCommand('curl --version');\n * return result.stdout;\n * },\n * );\n * // Container is automatically disposed - no try/finally needed\n * ```\n */\nexport async function useSandbox<T>(\n options: DockerSandboxOptions,\n fn: (sandbox: DockerSandbox) => Promise<T>,\n): Promise<T> {\n const sandbox = await createDockerSandbox(options);\n try {\n return await fn(sandbox);\n } finally {\n await sandbox.dispose();\n }\n}\n", "import {\n createBashTool,\n type CreateBashToolOptions,\n type BashToolkit,\n} from 'bash-tool';\n\nimport {\n createDockerSandbox,\n isComposeOptions,\n isDockerfileOptions,\n type BinaryInstall,\n type DockerMount,\n type DockerResources,\n type DockerSandbox,\n type DockerSandboxOptions,\n} from './docker-sandbox.ts';\n\n/**\n * Base options shared by RuntimeContainerToolOptions and DockerfileContainerToolOptions.\n */\ninterface BaseContainerToolOptions\n extends Omit<CreateBashToolOptions, 'sandbox' | 'uploadDirectory'> {\n /** Directories to mount from host into the container */\n mounts?: DockerMount[];\n /** Resource limits for the container */\n resources?: DockerResources;\n}\n\n/**\n * Options for container tool using RuntimeStrategy.\n * Installs packages/binaries at container runtime.\n */\nexport interface RuntimeContainerToolOptions extends BaseContainerToolOptions {\n /** Docker image to use (default: 'alpine:latest') */\n image?: string;\n /** Packages to install in the container via package manager (apk/apt) */\n packages?: string[];\n /** Binaries to install from URLs (for tools not in package managers) */\n binaries?: BinaryInstall[];\n}\n\n/**\n * Options for container tool using DockerfileStrategy.\n * Builds custom image from Dockerfile (with caching).\n */\nexport interface DockerfileContainerToolOptions extends BaseContainerToolOptions {\n /** Dockerfile content (if contains newlines) or path to Dockerfile */\n dockerfile: string;\n /** Build context directory (default: '.') */\n context?: string;\n}\n\n/**\n * Options for container tool using ComposeStrategy.\n * Manages multi-container environments via Docker Compose.\n */\nexport interface ComposeContainerToolOptions\n extends Omit<CreateBashToolOptions, 'sandbox' | 'uploadDirectory'> {\n /** Path to docker-compose.yml file */\n compose: string;\n /** Service name to execute commands in (required) */\n service: string;\n /** Resource limits for the container */\n resources?: DockerResources;\n // Note: mounts must be defined in compose file, not here\n}\n\n/**\n * Union type for container tool options.\n * - RuntimeContainerToolOptions: Runtime package/binary installation\n * - DockerfileContainerToolOptions: Pre-built images from Dockerfile\n * - ComposeContainerToolOptions: Multi-container environments via Docker Compose\n */\nexport type ContainerToolOptions =\n | RuntimeContainerToolOptions\n | DockerfileContainerToolOptions\n | ComposeContainerToolOptions;\n\n/**\n * Result of creating a container tool.\n * Extends BashToolkit but with DockerSandbox (which has dispose()) instead of base Sandbox.\n */\nexport type ContainerToolResult = Omit<BashToolkit, 'sandbox'> & {\n sandbox: DockerSandbox;\n};\n\n/**\n * Creates a bash tool that runs in a Docker container.\n *\n * This is a high-level wrapper that combines `createDockerSandbox()` and\n * `createBashTool()` into a single call. It provides a convenient way to\n * get a bash tool that executes real binaries in an isolated container.\n *\n * Supports three strategies:\n * - **RuntimeStrategy**: Uses existing image, installs packages/binaries at runtime\n * - **DockerfileStrategy**: Builds custom image from Dockerfile (with caching)\n * - **ComposeStrategy**: Multi-container environments via Docker Compose\n *\n * @example RuntimeStrategy (default)\n * ```typescript\n * const { bash, tools, sandbox } = await createContainerTool({\n * packages: ['curl', 'jq'],\n * mounts: [{\n * hostPath: process.cwd(),\n * containerPath: '/workspace',\n * readOnly: false,\n * }],\n * });\n *\n * // Use with AI SDK\n * const response = await generateText({\n * model: yourModel,\n * tools,\n * prompt: 'Fetch the weather data and parse it with jq',\n * });\n *\n * // Clean up when done\n * await sandbox.dispose();\n * ```\n *\n * @example DockerfileStrategy\n * ```typescript\n * const { bash, tools, sandbox } = await createContainerTool({\n * dockerfile: `\n * FROM python:3.11-slim\n * RUN pip install pandas numpy\n * `,\n * context: '.',\n * mounts: [{\n * hostPath: process.cwd(),\n * containerPath: '/workspace',\n * }],\n * });\n * ```\n *\n * @example ComposeStrategy\n * ```typescript\n * const { bash, tools, sandbox } = await createContainerTool({\n * compose: './docker-compose.yml',\n * service: 'app',\n * });\n * // Commands run in the 'app' service, can reach other services by name\n * await sandbox.dispose(); // Stops ALL services\n * ```\n *\n * @example With hooks for logging\n * ```typescript\n * const { bash, sandbox } = await createContainerTool({\n * packages: ['python3'],\n * onBeforeBashCall: ({ command }) => {\n * console.log('Running:', command);\n * },\n * onAfterBashCall: ({ command, result }) => {\n * console.log(`Exit code: ${result.exitCode}`);\n * },\n * });\n * ```\n */\nexport async function createContainerTool(\n options: ContainerToolOptions = {},\n): Promise<ContainerToolResult> {\n // Extract sandbox options from bash tool options\n let sandboxOptions: DockerSandboxOptions;\n let bashOptions: Omit<CreateBashToolOptions, 'sandbox' | 'uploadDirectory'>;\n\n if (isComposeOptions(options)) {\n const { compose, service, resources, ...rest } = options;\n sandboxOptions = { compose, service, resources };\n bashOptions = rest;\n } else if (isDockerfileOptions(options)) {\n const { dockerfile, context, mounts, resources, ...rest } = options;\n sandboxOptions = { dockerfile, context, mounts, resources };\n bashOptions = rest;\n } else {\n const { image, packages, binaries, mounts, resources, ...rest } = options;\n sandboxOptions = { image, packages, binaries, mounts, resources };\n bashOptions = rest;\n }\n\n // Create the Docker sandbox\n const sandbox = await createDockerSandbox(sandboxOptions);\n\n // Create the bash tool with our Docker sandbox\n const toolkit = await createBashTool({\n ...bashOptions,\n sandbox,\n });\n\n return {\n bash: toolkit.bash,\n tools: toolkit.tools,\n sandbox,\n };\n}\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport YAML from 'yaml';\n\nimport type { ParsedSkillMd, SkillMetadata } from './types.ts';\n\n/**\n * Parse YAML frontmatter from a SKILL.md file content.\n *\n * Frontmatter format:\n * ```\n * ---\n * name: skill-name\n * description: Skill description here\n * ---\n *\n * # Markdown body\n * ```\n */\nexport function parseFrontmatter(content: string): ParsedSkillMd {\n const frontmatterRegex = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?([\\s\\S]*)$/;\n const match = content.match(frontmatterRegex);\n\n if (!match) {\n throw new Error('Invalid SKILL.md: missing or malformed frontmatter');\n }\n\n const [, yamlContent, body] = match;\n const frontmatter = YAML.parse(yamlContent) as Record<string, unknown>;\n\n if (!frontmatter.name || typeof frontmatter.name !== 'string') {\n throw new Error('Invalid SKILL.md: frontmatter must have a \"name\" field');\n }\n\n if (!frontmatter.description || typeof frontmatter.description !== 'string') {\n throw new Error(\n 'Invalid SKILL.md: frontmatter must have a \"description\" field',\n );\n }\n\n return {\n frontmatter: frontmatter as ParsedSkillMd['frontmatter'],\n body: body.trim(),\n };\n}\n\n/**\n * Load skill metadata from a SKILL.md file.\n * Only parses frontmatter, does not load full body into memory.\n * This is the core of progressive disclosure - metadata only at startup.\n */\nexport function loadSkillMetadata(skillMdPath: string): SkillMetadata {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n const parsed = parseFrontmatter(content);\n const skillDir = path.dirname(skillMdPath);\n\n return {\n name: parsed.frontmatter.name,\n description: parsed.frontmatter.description,\n path: skillDir,\n skillMdPath,\n };\n}\n\n/**\n * Discover all skills in a directory.\n * Looks for subdirectories containing SKILL.md files.\n * Only loads metadata - full content is read by LLM when needed.\n */\nexport function discoverSkillsInDirectory(directory: string): SkillMetadata[] {\n const skills: SkillMetadata[] = [];\n\n // Expand ~ to home directory\n const expandedDir = directory.startsWith('~')\n ? path.join(process.env.HOME || '', directory.slice(1))\n : directory;\n if (!fs.existsSync(expandedDir)) {\n return skills;\n }\n\n const entries = fs.readdirSync(expandedDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const skillMdPath = path.join(expandedDir, entry.name, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) continue;\n\n try {\n const metadata = loadSkillMetadata(skillMdPath);\n skills.push(metadata);\n } catch (error) {\n // Skip invalid skills, log warning\n console.warn(`Warning: Failed to load skill at ${skillMdPath}:`, error);\n }\n }\n\n return skills;\n}\n", "import type { ContextFragment } from '../fragments.ts';\nimport { discoverSkillsInDirectory } from './loader.ts';\nimport type { SkillMetadata, SkillsFragmentOptions } from './types.ts';\n\n/**\n * Create a context fragment containing available skills metadata.\n *\n * Follows Anthropic's progressive disclosure pattern:\n * - At startup: only skill metadata (name, description, path) is injected\n * - At runtime: LLM reads full SKILL.md using file tools when relevant\n *\n * @param options - Configuration including paths to scan and optional filtering\n *\n * @example\n * ```ts\n * const context = new ContextEngine({ userId: 'demo-user', store, chatId: 'demo' })\n * .set(\n * role('You are a helpful assistant.'),\n * skills({\n * paths: [\n * { host: './skills', sandbox: '/skills/skills' }\n * ]\n * }),\n * );\n *\n * // LLM now sees skill metadata with sandbox paths and can read full SKILL.md\n * ```\n */\nexport function skills(options: SkillsFragmentOptions): ContextFragment {\n // Build host-to-sandbox mapping for path rewriting\n const pathMapping = new Map<string, string>();\n for (const { host, sandbox } of options.paths) {\n pathMapping.set(host, sandbox);\n }\n\n // Discover skills from all host paths (later paths override earlier ones)\n const skillsMap = new Map<string, SkillMetadata>();\n for (const { host } of options.paths) {\n const discovered = discoverSkillsInDirectory(host);\n for (const skill of discovered) {\n skillsMap.set(skill.name, skill);\n }\n }\n const allSkills = Array.from(skillsMap.values());\n\n // Apply filtering\n let filteredSkills = allSkills;\n if (options.include) {\n filteredSkills = allSkills.filter((s) => options.include!.includes(s.name));\n }\n if (options.exclude) {\n filteredSkills = filteredSkills.filter(\n (s) => !options.exclude!.includes(s.name),\n );\n }\n\n // Convert skills to ContextFragments with sandbox paths\n const skillFragments: ContextFragment[] = filteredSkills.map((skill) => {\n const originalPath = skill.skillMdPath;\n let sandboxPath = originalPath;\n\n // Rewrite path from host to sandbox\n for (const [host, sandbox] of pathMapping) {\n if (originalPath.startsWith(host)) {\n const relativePath = originalPath.slice(host.length);\n sandboxPath = sandbox + relativePath;\n break;\n }\n }\n\n return {\n name: 'skill',\n data: {\n name: skill.name,\n path: sandboxPath,\n description: skill.description,\n },\n metadata: { originalPath },\n };\n });\n\n return {\n name: 'available_skills',\n data: [\n {\n name: 'instructions',\n data: SKILLS_INSTRUCTIONS,\n } as ContextFragment,\n ...skillFragments,\n ],\n metadata: {\n paths: options.paths, // Store original path mappings for getSkillMounts()\n },\n };\n}\n\n/**\n * Instructions for the LLM on how to use available skills.\n * Follows Anthropic's progressive disclosure - LLM reads files when needed.\n */\nconst SKILLS_INSTRUCTIONS = `When a user's request matches one of the skills listed below, read the skill's SKILL.md file to get detailed instructions before proceeding. Skills provide specialized knowledge and workflows for specific tasks.\n\nTo use a skill:\n1. Identify if the user's request matches a skill's description\n2. Read the SKILL.md file at the skill's path to load full instructions\n3. Follow the skill's guidance to complete the task\n\nSkills are only loaded when relevant - don't read skill files unless needed.`;\n", "import { DatabaseSync, type SQLInputValue } from 'node:sqlite';\n\nimport type {\n BranchData,\n BranchInfo,\n ChatData,\n ChatInfo,\n CheckpointData,\n CheckpointInfo,\n DeleteChatOptions,\n GraphBranch,\n GraphCheckpoint,\n GraphData,\n GraphNode,\n ListChatsOptions,\n MessageData,\n SearchOptions,\n SearchResult,\n StoredChatData,\n} from './store.ts';\nimport { ContextStore } from './store.ts';\n\nconst STORE_DDL = `\n-- Chats table\n-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates\nCREATE TABLE IF NOT EXISTS chats (\n id TEXT PRIMARY KEY,\n userId TEXT NOT NULL,\n title TEXT,\n metadata TEXT,\n createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),\n updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);\nCREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);\n\n-- Messages table (nodes in the DAG)\nCREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n parentId TEXT,\n name TEXT NOT NULL,\n type TEXT,\n data TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (parentId) REFERENCES messages(id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);\nCREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);\n\n-- Branches table (pointers to head messages)\nCREATE TABLE IF NOT EXISTS branches (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n headMessageId TEXT,\n isActive INTEGER NOT NULL DEFAULT 0,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (headMessageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);\n\n-- Checkpoints table (pointers to message nodes)\nCREATE TABLE IF NOT EXISTS checkpoints (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n messageId TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (messageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);\n\n-- FTS5 virtual table for full-text search\n-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)\n-- Only 'content' is indexed for full-text search\nCREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(\n messageId UNINDEXED,\n chatId UNINDEXED,\n name UNINDEXED,\n content,\n tokenize='porter unicode61'\n);\n`;\n\n/**\n * SQLite-based context store using graph model.\n *\n * Uses node:sqlite's synchronous DatabaseSync for persistence.\n * Messages are stored as nodes in a DAG with parentId links.\n */\nexport class SqliteContextStore extends ContextStore {\n #db: DatabaseSync;\n\n constructor(path: string) {\n super();\n this.#db = new DatabaseSync(path);\n this.#db.exec('PRAGMA foreign_keys = ON');\n this.#db.exec(STORE_DDL);\n }\n\n /**\n * Execute a function within a transaction.\n * Automatically commits on success or rolls back on error.\n */\n #useTransaction<T>(fn: () => T): T {\n this.#db.exec('BEGIN TRANSACTION');\n try {\n const result = fn();\n this.#db.exec('COMMIT');\n return result;\n } catch (error) {\n this.#db.exec('ROLLBACK');\n throw error;\n }\n }\n\n // ==========================================================================\n // Chat Operations\n // ==========================================================================\n\n async createChat(chat: ChatData): Promise<void> {\n this.#useTransaction(() => {\n // Create chat (createdAt and updatedAt are auto-set by SQLite DEFAULT)\n this.#db\n .prepare(\n `INSERT INTO chats (id, userId, title, metadata)\n VALUES (?, ?, ?, ?)`,\n )\n .run(\n chat.id,\n chat.userId,\n chat.title ?? null,\n chat.metadata ? JSON.stringify(chat.metadata) : null,\n );\n\n // Create \"main\" branch\n this.#db\n .prepare(\n `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)\n VALUES (?, ?, 'main', NULL, 1, ?)`,\n )\n .run(crypto.randomUUID(), chat.id, Date.now());\n });\n }\n\n async upsertChat(chat: ChatData): Promise<StoredChatData> {\n return this.#useTransaction(() => {\n // Insert if not exists, no-op update if exists (to trigger RETURNING)\n const row = this.#db\n .prepare(\n `INSERT INTO chats (id, userId, title, metadata)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET id = excluded.id\n RETURNING *`,\n )\n .get(\n chat.id,\n chat.userId,\n chat.title ?? null,\n chat.metadata ? JSON.stringify(chat.metadata) : null,\n ) as {\n id: string;\n userId: string;\n title: string | null;\n metadata: string | null;\n createdAt: number;\n updatedAt: number;\n };\n\n // Ensure \"main\" branch exists (INSERT OR IGNORE uses UNIQUE(chatId, name) constraint)\n this.#db\n .prepare(\n `INSERT OR IGNORE INTO branches (id, chatId, name, headMessageId, isActive, createdAt)\n VALUES (?, ?, 'main', NULL, 1, ?)`,\n )\n .run(crypto.randomUUID(), chat.id, Date.now());\n\n return {\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n });\n }\n\n async getChat(chatId: string): Promise<StoredChatData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM chats WHERE id = ?')\n .get(chatId) as\n | {\n id: string;\n userId: string;\n title: string | null;\n metadata: string | null;\n createdAt: number;\n updatedAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n }\n\n async updateChat(\n chatId: string,\n updates: Partial<Pick<ChatData, 'title' | 'metadata'>>,\n ): Promise<StoredChatData> {\n const setClauses: string[] = [\"updatedAt = strftime('%s', 'now') * 1000\"];\n const params: SQLInputValue[] = [];\n\n if (updates.title !== undefined) {\n setClauses.push('title = ?');\n params.push(updates.title ?? null);\n }\n if (updates.metadata !== undefined) {\n setClauses.push('metadata = ?');\n params.push(JSON.stringify(updates.metadata));\n }\n\n params.push(chatId);\n const row = this.#db\n .prepare(\n `UPDATE chats SET ${setClauses.join(', ')} WHERE id = ? RETURNING *`,\n )\n .get(...params) as {\n id: string;\n userId: string;\n title: string | null;\n metadata: string | null;\n createdAt: number;\n updatedAt: number;\n };\n\n return {\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n }\n\n async listChats(options?: ListChatsOptions): Promise<ChatInfo[]> {\n const params: SQLInputValue[] = [];\n let whereClause = '';\n let limitClause = '';\n\n // Build WHERE clause for userId filter\n if (options?.userId) {\n whereClause = 'WHERE c.userId = ?';\n params.push(options.userId);\n }\n\n // Build LIMIT/OFFSET clause\n if (options?.limit !== undefined) {\n limitClause = ' LIMIT ?';\n params.push(options.limit);\n if (options.offset !== undefined) {\n limitClause += ' OFFSET ?';\n params.push(options.offset);\n }\n }\n\n const rows = this.#db\n .prepare(\n `SELECT\n c.id,\n c.userId,\n c.title,\n c.createdAt,\n c.updatedAt,\n COUNT(DISTINCT m.id) as messageCount,\n COUNT(DISTINCT b.id) as branchCount\n FROM chats c\n LEFT JOIN messages m ON m.chatId = c.id\n LEFT JOIN branches b ON b.chatId = c.id\n ${whereClause}\n GROUP BY c.id\n ORDER BY c.updatedAt DESC${limitClause}`,\n )\n .all(...params) as {\n id: string;\n userId: string;\n title: string | null;\n createdAt: number;\n updatedAt: number;\n messageCount: number;\n branchCount: number;\n }[];\n\n return rows.map((row) => ({\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n messageCount: row.messageCount,\n branchCount: row.branchCount,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n }));\n }\n\n async deleteChat(\n chatId: string,\n options?: DeleteChatOptions,\n ): Promise<boolean> {\n return this.#useTransaction(() => {\n // Get message IDs before deletion for FTS cleanup\n const messageIds = this.#db\n .prepare('SELECT id FROM messages WHERE chatId = ?')\n .all(chatId) as { id: string }[];\n\n // Build the delete query with optional userId check\n let sql = 'DELETE FROM chats WHERE id = ?';\n const params: SQLInputValue[] = [chatId];\n\n if (options?.userId !== undefined) {\n sql += ' AND userId = ?';\n params.push(options.userId);\n }\n\n const result = this.#db.prepare(sql).run(...params);\n\n // Clean up FTS entries (CASCADE handles messages, branches, checkpoints)\n if (result.changes > 0 && messageIds.length > 0) {\n const placeholders = messageIds.map(() => '?').join(', ');\n this.#db\n .prepare(\n `DELETE FROM messages_fts WHERE messageId IN (${placeholders})`,\n )\n .run(...messageIds.map((m) => m.id));\n }\n\n return result.changes > 0;\n });\n }\n\n // ==========================================================================\n // Message Operations (Graph Nodes)\n // ==========================================================================\n\n async addMessage(message: MessageData): Promise<void> {\n const existingParent =\n message.parentId === message.id\n ? (this.#db\n .prepare('SELECT parentId FROM messages WHERE id = ?')\n .get(message.id) as { parentId: string | null } | undefined)\n : undefined;\n const parentId =\n message.parentId === message.id\n ? (existingParent?.parentId ?? null)\n : message.parentId;\n\n // Upsert the message\n this.#db\n .prepare(\n `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n name = excluded.name,\n type = excluded.type,\n data = excluded.data`,\n )\n .run(\n message.id,\n message.chatId,\n parentId,\n message.name,\n message.type ?? null,\n JSON.stringify(message.data),\n message.createdAt,\n );\n\n // Index in FTS for search\n const content =\n typeof message.data === 'string'\n ? message.data\n : JSON.stringify(message.data);\n\n // Delete existing FTS entry if any (for upsert), then insert new one\n this.#db\n .prepare(`DELETE FROM messages_fts WHERE messageId = ?`)\n .run(message.id);\n this.#db\n .prepare(\n `INSERT INTO messages_fts(messageId, chatId, name, content)\n VALUES (?, ?, ?, ?)`,\n )\n .run(message.id, message.chatId, message.name, content);\n }\n\n async getMessage(messageId: string): Promise<MessageData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM messages WHERE id = ?')\n .get(messageId) as\n | {\n id: string;\n chatId: string;\n parentId: string | null;\n name: string;\n type: string | null;\n data: string;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n parentId: row.parentId,\n name: row.name,\n type: row.type ?? undefined,\n data: JSON.parse(row.data),\n createdAt: row.createdAt,\n };\n }\n\n async getMessageChain(headId: string): Promise<MessageData[]> {\n // Walk up the parent chain using recursive CTE with depth tracking\n // The CTE walks from head (newest) to root (oldest), so we track depth\n // and order by depth DESC to get chronological order (root first)\n const rows = this.#db\n .prepare(\n `WITH RECURSIVE chain AS (\n SELECT *, 0 as depth FROM messages WHERE id = ?\n UNION ALL\n SELECT m.*, c.depth + 1 FROM messages m\n INNER JOIN chain c ON m.id = c.parentId\n )\n SELECT * FROM chain\n ORDER BY depth DESC`,\n )\n .all(headId) as {\n id: string;\n chatId: string;\n parentId: string | null;\n name: string;\n type: string | null;\n data: string;\n createdAt: number;\n depth: number;\n }[];\n\n return rows.map((row) => ({\n id: row.id,\n chatId: row.chatId,\n parentId: row.parentId,\n name: row.name,\n type: row.type ?? undefined,\n data: JSON.parse(row.data),\n createdAt: row.createdAt,\n }));\n }\n\n async hasChildren(messageId: string): Promise<boolean> {\n const row = this.#db\n .prepare(\n 'SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren',\n )\n .get(messageId) as { hasChildren: number };\n\n return row.hasChildren === 1;\n }\n\n async getMessages(chatId: string): Promise<MessageData[]> {\n const chat = await this.getChat(chatId);\n if (!chat) {\n throw new Error(`Chat \"${chatId}\" not found`);\n }\n\n const activeBranch = await this.getActiveBranch(chatId);\n if (!activeBranch?.headMessageId) {\n return [];\n }\n\n return this.getMessageChain(activeBranch.headMessageId);\n }\n\n // ==========================================================================\n // Branch Operations\n // ==========================================================================\n\n async createBranch(branch: BranchData): Promise<void> {\n this.#db\n .prepare(\n `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)\n VALUES (?, ?, ?, ?, ?, ?)`,\n )\n .run(\n branch.id,\n branch.chatId,\n branch.name,\n branch.headMessageId,\n branch.isActive ? 1 : 0,\n branch.createdAt,\n );\n }\n\n async getBranch(\n chatId: string,\n name: string,\n ): Promise<BranchData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM branches WHERE chatId = ? AND name = ?')\n .get(chatId, name) as\n | {\n id: string;\n chatId: string;\n name: string;\n headMessageId: string | null;\n isActive: number;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n name: row.name,\n headMessageId: row.headMessageId,\n isActive: row.isActive === 1,\n createdAt: row.createdAt,\n };\n }\n\n async getActiveBranch(chatId: string): Promise<BranchData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM branches WHERE chatId = ? AND isActive = 1')\n .get(chatId) as\n | {\n id: string;\n chatId: string;\n name: string;\n headMessageId: string | null;\n isActive: number;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n name: row.name,\n headMessageId: row.headMessageId,\n isActive: true,\n createdAt: row.createdAt,\n };\n }\n\n async setActiveBranch(chatId: string, branchId: string): Promise<void> {\n // Deactivate all branches for this chat\n this.#db\n .prepare('UPDATE branches SET isActive = 0 WHERE chatId = ?')\n .run(chatId);\n\n // Activate the specified branch\n this.#db\n .prepare('UPDATE branches SET isActive = 1 WHERE id = ?')\n .run(branchId);\n }\n\n async updateBranchHead(\n branchId: string,\n messageId: string | null,\n ): Promise<void> {\n this.#db\n .prepare('UPDATE branches SET headMessageId = ? WHERE id = ?')\n .run(messageId, branchId);\n }\n\n async listBranches(chatId: string): Promise<BranchInfo[]> {\n // Get branches with message count by walking the chain\n const branches = this.#db\n .prepare(\n `SELECT\n b.id,\n b.name,\n b.headMessageId,\n b.isActive,\n b.createdAt\n FROM branches b\n WHERE b.chatId = ?\n ORDER BY b.createdAt ASC`,\n )\n .all(chatId) as {\n id: string;\n name: string;\n headMessageId: string | null;\n isActive: number;\n createdAt: number;\n }[];\n\n // For each branch, count messages in the chain\n const result: BranchInfo[] = [];\n for (const branch of branches) {\n let messageCount = 0;\n if (branch.headMessageId) {\n const countRow = this.#db\n .prepare(\n `WITH RECURSIVE chain AS (\n SELECT id, parentId FROM messages WHERE id = ?\n UNION ALL\n SELECT m.id, m.parentId FROM messages m\n INNER JOIN chain c ON m.id = c.parentId\n )\n SELECT COUNT(*) as count FROM chain`,\n )\n .get(branch.headMessageId) as { count: number };\n messageCount = countRow.count;\n }\n\n result.push({\n id: branch.id,\n name: branch.name,\n headMessageId: branch.headMessageId,\n isActive: branch.isActive === 1,\n messageCount,\n createdAt: branch.createdAt,\n });\n }\n\n return result;\n }\n\n // ==========================================================================\n // Checkpoint Operations\n // ==========================================================================\n\n async createCheckpoint(checkpoint: CheckpointData): Promise<void> {\n this.#db\n .prepare(\n `INSERT INTO checkpoints (id, chatId, name, messageId, createdAt)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(chatId, name) DO UPDATE SET\n messageId = excluded.messageId,\n createdAt = excluded.createdAt`,\n )\n .run(\n checkpoint.id,\n checkpoint.chatId,\n checkpoint.name,\n checkpoint.messageId,\n checkpoint.createdAt,\n );\n }\n\n async getCheckpoint(\n chatId: string,\n name: string,\n ): Promise<CheckpointData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM checkpoints WHERE chatId = ? AND name = ?')\n .get(chatId, name) as\n | {\n id: string;\n chatId: string;\n name: string;\n messageId: string;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n name: row.name,\n messageId: row.messageId,\n createdAt: row.createdAt,\n };\n }\n\n async listCheckpoints(chatId: string): Promise<CheckpointInfo[]> {\n const rows = this.#db\n .prepare(\n `SELECT id, name, messageId, createdAt\n FROM checkpoints\n WHERE chatId = ?\n ORDER BY createdAt DESC`,\n )\n .all(chatId) as {\n id: string;\n name: string;\n messageId: string;\n createdAt: number;\n }[];\n\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n messageId: row.messageId,\n createdAt: row.createdAt,\n }));\n }\n\n async deleteCheckpoint(chatId: string, name: string): Promise<void> {\n this.#db\n .prepare('DELETE FROM checkpoints WHERE chatId = ? AND name = ?')\n .run(chatId, name);\n }\n\n // ==========================================================================\n // Search Operations\n // ==========================================================================\n\n async searchMessages(\n chatId: string,\n query: string,\n options?: SearchOptions,\n ): Promise<SearchResult[]> {\n const limit = options?.limit ?? 20;\n const roles = options?.roles;\n\n // Build the query dynamically based on options\n let sql = `\n SELECT\n m.id,\n m.chatId,\n m.parentId,\n m.name,\n m.type,\n m.data,\n m.createdAt,\n fts.rank,\n snippet(messages_fts, 3, '<mark>', '</mark>', '...', 32) as snippet\n FROM messages_fts fts\n JOIN messages m ON m.id = fts.messageId\n WHERE messages_fts MATCH ?\n AND fts.chatId = ?\n `;\n\n const params: SQLInputValue[] = [query, chatId];\n\n if (roles && roles.length > 0) {\n const placeholders = roles.map(() => '?').join(', ');\n sql += ` AND fts.name IN (${placeholders})`;\n params.push(...roles);\n }\n\n sql += ' ORDER BY fts.rank LIMIT ?';\n params.push(limit);\n\n const rows = this.#db.prepare(sql).all(...params) as {\n id: string;\n chatId: string;\n parentId: string | null;\n name: string;\n type: string | null;\n data: string;\n createdAt: number;\n rank: number;\n snippet: string;\n }[];\n\n return rows.map((row) => ({\n message: {\n id: row.id,\n chatId: row.chatId,\n parentId: row.parentId,\n name: row.name,\n type: row.type ?? undefined,\n data: JSON.parse(row.data),\n createdAt: row.createdAt,\n },\n rank: row.rank,\n snippet: row.snippet,\n }));\n }\n\n // ==========================================================================\n // Visualization Operations\n // ==========================================================================\n\n async getGraph(chatId: string): Promise<GraphData> {\n // Get all messages for complete graph\n const messageRows = this.#db\n .prepare(\n `SELECT id, parentId, name, data, createdAt\n FROM messages\n WHERE chatId = ?\n ORDER BY createdAt ASC`,\n )\n .all(chatId) as {\n id: string;\n parentId: string | null;\n name: string;\n data: string;\n createdAt: number;\n }[];\n\n const nodes: GraphNode[] = messageRows.map((row) => {\n const data = JSON.parse(row.data);\n const content = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n id: row.id,\n parentId: row.parentId,\n role: row.name,\n content: content.length > 50 ? content.slice(0, 50) + '...' : content,\n createdAt: row.createdAt,\n };\n });\n\n // Get all branches\n const branchRows = this.#db\n .prepare(\n `SELECT name, headMessageId, isActive\n FROM branches\n WHERE chatId = ?\n ORDER BY createdAt ASC`,\n )\n .all(chatId) as {\n name: string;\n headMessageId: string | null;\n isActive: number;\n }[];\n\n const branches: GraphBranch[] = branchRows.map((row) => ({\n name: row.name,\n headMessageId: row.headMessageId,\n isActive: row.isActive === 1,\n }));\n\n // Get all checkpoints\n const checkpointRows = this.#db\n .prepare(\n `SELECT name, messageId\n FROM checkpoints\n WHERE chatId = ?\n ORDER BY createdAt ASC`,\n )\n .all(chatId) as {\n name: string;\n messageId: string;\n }[];\n\n const checkpoints: GraphCheckpoint[] = checkpointRows.map((row) => ({\n name: row.name,\n messageId: row.messageId,\n }));\n\n return {\n chatId,\n nodes,\n branches,\n checkpoints,\n };\n }\n}\n", "import { SqliteContextStore } from './sqlite.store.ts';\n\n/**\n * In-memory context store.\n *\n * Uses SQLite's :memory: database for non-persistent storage.\n * Useful for testing and short-lived sessions.\n */\nexport class InMemoryContextStore extends SqliteContextStore {\n constructor() {\n super(':memory:');\n }\n}\n", "import type { GraphData, GraphNode } from './store/store.ts';\n\n/**\n * Render a graph as ASCII art.\n *\n * @param data - The graph data to visualize\n * @returns ASCII art representation of the graph\n *\n * @example\n * ```ts\n * const graph = await store.getGraph('my-chat');\n * console.log(visualizeGraph(graph));\n * ```\n */\nexport function visualizeGraph(data: GraphData): string {\n if (data.nodes.length === 0) {\n return `[chat: ${data.chatId}]\\n\\n(empty)`;\n }\n\n // Build lookup maps\n const childrenByParentId = new Map<string | null, GraphNode[]>();\n const branchHeads = new Map<string, string[]>(); // messageId -> branch names\n const checkpointsByMessageId = new Map<string, string[]>(); // messageId -> checkpoint names\n\n for (const node of data.nodes) {\n const children = childrenByParentId.get(node.parentId) ?? [];\n children.push(node);\n childrenByParentId.set(node.parentId, children);\n }\n\n for (const branch of data.branches) {\n if (branch.headMessageId) {\n const heads = branchHeads.get(branch.headMessageId) ?? [];\n heads.push(branch.isActive ? `${branch.name} *` : branch.name);\n branchHeads.set(branch.headMessageId, heads);\n }\n }\n\n for (const checkpoint of data.checkpoints) {\n const cps = checkpointsByMessageId.get(checkpoint.messageId) ?? [];\n cps.push(checkpoint.name);\n checkpointsByMessageId.set(checkpoint.messageId, cps);\n }\n\n // Find root nodes (parentId === null)\n const roots = childrenByParentId.get(null) ?? [];\n\n const lines: string[] = [`[chat: ${data.chatId}]`, ''];\n\n // Recursively render the tree\n function renderNode(\n node: GraphNode,\n prefix: string,\n isLast: boolean,\n isRoot: boolean,\n ): void {\n const connector = isRoot ? '' : isLast ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 ';\n const contentPreview = node.content.replace(/\\n/g, ' ');\n\n let line = `${prefix}${connector}${node.id.slice(0, 8)} (${node.role}): \"${contentPreview}\"`;\n\n // Add branch markers\n const branches = branchHeads.get(node.id);\n if (branches) {\n line += ` <- [${branches.join(', ')}]`;\n }\n\n // Add checkpoint markers\n const checkpoints = checkpointsByMessageId.get(node.id);\n if (checkpoints) {\n line += ` {${checkpoints.join(', ')}}`;\n }\n\n lines.push(line);\n\n // Render children\n const children = childrenByParentId.get(node.id) ?? [];\n const childPrefix = isRoot ? '' : prefix + (isLast ? ' ' : '\u2502 ');\n\n for (let i = 0; i < children.length; i++) {\n renderNode(children[i], childPrefix, i === children.length - 1, false);\n }\n }\n\n // Render each root\n for (let i = 0; i < roots.length; i++) {\n renderNode(roots[i], '', i === roots.length - 1, true);\n }\n\n // Add legend\n lines.push('');\n lines.push('Legend: * = active branch, {...} = checkpoint');\n\n return lines.join('\\n');\n}\n", "import { groq } from '@ai-sdk/groq';\nimport {\n type GenerateTextResult,\n NoSuchToolError,\n Output,\n type StreamTextResult,\n type StreamTextTransform,\n type ToolCallRepairFunction,\n type ToolChoice,\n type ToolSet,\n type UIMessageStreamWriter,\n convertToModelMessages,\n createUIMessageStream,\n generateId,\n generateText,\n smoothStream,\n stepCountIs,\n streamText,\n} from 'ai';\nimport chalk from 'chalk';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\n\nimport { type ContextEngine, XmlRenderer } from '../index.ts';\nimport { lastAssistantMessage } from './fragments.ts';\nimport {\n type Guardrail,\n type GuardrailContext,\n runGuardrailChain,\n} from './guardrail.ts';\n\nexport interface CreateAgent<CIn, COut = CIn> {\n name: string;\n context?: ContextEngine;\n tools?: ToolSet;\n model?: AgentModel;\n toolChoice?: ToolChoice<Record<string, COut>>;\n providerOptions?: Parameters<typeof generateText>[0]['providerOptions'];\n logging?: boolean;\n /**\n * Guardrails to apply during streaming.\n * Each guardrail inspects text chunks and can trigger self-correction retries.\n */\n guardrails?: Guardrail[];\n /**\n * Maximum number of retry attempts when guardrails fail (default: 3).\n */\n maxGuardrailRetries?: number;\n}\n\nclass Agent<CIn, COut = CIn> {\n #options: CreateAgent<CIn, COut>;\n #guardrails: Guardrail[] = [];\n readonly tools: ToolSet;\n constructor(options: CreateAgent<CIn, COut>) {\n this.#options = options;\n this.tools = options.tools || {};\n this.#guardrails = options.guardrails || [];\n }\n\n public async generate<COut, CIn = COut>(\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n },\n ): Promise<GenerateTextResult<ToolSet, Output.Output<string, string, any>>> {\n if (!this.#options.context) {\n throw new Error(`Agent ${this.#options.name} is missing a context.`);\n }\n if (!this.#options.model) {\n throw new Error(`Agent ${this.#options.name} is missing a model.`);\n }\n const { messages, systemPrompt } = await this.#options.context.resolve({\n renderer: new XmlRenderer(),\n });\n return generateText({\n abortSignal: config?.abortSignal,\n providerOptions: this.#options.providerOptions,\n model: this.#options.model,\n system: systemPrompt,\n messages: await convertToModelMessages(messages as never),\n stopWhen: stepCountIs(25),\n tools: this.#options.tools,\n experimental_context: contextVariables,\n experimental_repairToolCall: repairToolCall,\n toolChoice: this.#options.toolChoice,\n onStepFinish: (step) => {\n const toolCall = step.toolCalls.at(-1);\n if (toolCall) {\n console.log(\n `Debug: ${chalk.yellow('ToolCalled')}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`,\n );\n }\n },\n });\n }\n\n /**\n * Stream a response from the agent.\n *\n * When guardrails are configured, `toUIMessageStream()` is wrapped to provide\n * self-correction behavior. Direct access to fullStream/textStream bypasses guardrails.\n *\n * @example\n * ```typescript\n * const stream = await agent.stream({});\n *\n * // With guardrails - use toUIMessageStream for protection\n * await printer.readableStream(stream.toUIMessageStream());\n *\n * // Or use printer.stdout which uses toUIMessageStream internally\n * await printer.stdout(stream);\n * ```\n */\n public async stream<COut, CIn = COut>(\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n maxRetries?: number;\n },\n ): Promise<StreamTextResult<ToolSet, never>> {\n if (!this.#options.context) {\n throw new Error(`Agent ${this.#options.name} is missing a context.`);\n }\n if (!this.#options.model) {\n throw new Error(`Agent ${this.#options.name} is missing a model.`);\n }\n\n const result = await this.#createRawStream(contextVariables, config);\n\n if (this.#guardrails.length === 0) {\n return result;\n }\n\n return this.#wrapWithGuardrails(result, contextVariables, config);\n }\n\n /**\n * Create a raw stream without guardrail processing.\n */\n async #createRawStream<COut, CIn = COut>(\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n },\n ) {\n const { messages, systemPrompt } = await this.#options.context!.resolve({\n renderer: new XmlRenderer(),\n });\n\n const runId = generateId();\n return streamText({\n abortSignal: config?.abortSignal,\n providerOptions: this.#options.providerOptions,\n model: this.#options.model!,\n system: systemPrompt,\n messages: await convertToModelMessages(messages as never),\n experimental_repairToolCall: repairToolCall,\n stopWhen: stepCountIs(50),\n experimental_transform: config?.transform ?? smoothStream(),\n tools: this.#options.tools,\n experimental_context: contextVariables,\n toolChoice: this.#options.toolChoice,\n onStepFinish: (step) => {\n const toolCall = step.toolCalls.at(-1);\n if (toolCall) {\n console.log(\n `Debug: (${runId}) ${chalk.bold.yellow('ToolCalled')}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`,\n );\n }\n },\n });\n }\n\n /**\n * Wrap a StreamTextResult with guardrail protection on toUIMessageStream().\n *\n * When a guardrail fails:\n * 1. Accumulated text + feedback is appended as the assistant's self-correction\n * 2. The feedback is written to the output stream (user sees the correction)\n * 3. A new stream is started and the model continues from the correction\n */\n #wrapWithGuardrails<CIn>(\n result: StreamTextResult<ToolSet, never>,\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n maxRetries?: number;\n },\n ): StreamTextResult<ToolSet, never> {\n const maxRetries =\n config?.maxRetries ?? this.#options.maxGuardrailRetries ?? 3;\n const context = this.#options.context!;\n\n // Save original method BEFORE override (prevents infinite recursion)\n const originalToUIMessageStream = result.toUIMessageStream.bind(result);\n\n // Override toUIMessageStream with guardrail logic\n result.toUIMessageStream = (options) => {\n return createUIMessageStream({\n generateId,\n execute: async ({ writer }) => {\n let currentResult: StreamTextResult<ToolSet, never> = result;\n let attempt = 0;\n\n // Create guardrail context with available tools\n const guardrailContext: GuardrailContext = {\n availableTools: Object.keys(this.tools),\n };\n\n while (attempt < maxRetries) {\n // Check if request was cancelled before starting new attempt\n if (config?.abortSignal?.aborted) {\n writer.write({ type: 'finish' });\n return;\n }\n\n attempt++;\n let accumulatedText = '';\n let guardrailFailed = false;\n let failureFeedback = '';\n\n // Use original method for first result (avoids recursion), new results have their own original\n const uiStream =\n currentResult === result\n ? originalToUIMessageStream(options)\n : currentResult.toUIMessageStream(options);\n\n for await (const part of uiStream) {\n // Run through guardrail chain - guardrails can handle any part type\n const checkResult = runGuardrailChain(\n part,\n this.#guardrails,\n guardrailContext,\n );\n\n if (checkResult.type === 'fail') {\n guardrailFailed = true;\n failureFeedback = checkResult.feedback;\n\n console.log(\n chalk.yellow(\n `[${this.#options.name}] Guardrail triggered (attempt ${attempt}/${maxRetries}): ${failureFeedback.slice(0, 50)}...`,\n ),\n );\n\n break; // Exit stream processing\n }\n\n // Guardrail passed - track text for self-correction context\n if (checkResult.part.type === 'text-delta') {\n accumulatedText += checkResult.part.delta;\n }\n\n // Write the (possibly modified) part to output\n writer.write(checkResult.part as typeof part);\n }\n\n if (!guardrailFailed) {\n // Stream completed successfully\n writer.write({ type: 'finish' });\n return;\n }\n\n // Check if we've exceeded max retries BEFORE writing feedback\n if (attempt >= maxRetries) {\n console.error(\n chalk.red(\n `[${this.#options.name}] Guardrail retry limit (${maxRetries}) exceeded.`,\n ),\n );\n writer.write({ type: 'finish' });\n return;\n }\n\n // Guardrail failed but we have retries left - prepare for retry\n // Write the self-correction feedback to the output stream\n writeText(writer, failureFeedback);\n\n // Add the partial assistant message + feedback to context\n // Uses lastAssistantMessage which finds/reuses the last assistant ID\n const selfCorrectionText = accumulatedText + ' ' + failureFeedback;\n context.set(lastAssistantMessage(selfCorrectionText));\n\n // Save to persist the self-correction (prevents duplicate messages on next resolve)\n await context.save();\n\n // Create new stream for retry\n currentResult = await this.#createRawStream(\n contextVariables,\n config,\n );\n }\n },\n onError: (error) => {\n const message =\n error instanceof Error ? error.message : String(error);\n return `Stream failed: ${message}`;\n },\n });\n };\n\n return result;\n }\n\n clone(overrides?: Partial<CreateAgent<CIn, COut>>): Agent<CIn, COut> {\n return new Agent<CIn, COut>({\n ...this.#options,\n ...overrides,\n });\n }\n}\n\nexport function agent<CIn, COut = CIn>(\n options: CreateAgent<CIn, COut>,\n): Agent<CIn, COut> {\n return new Agent(options);\n}\n\n/**\n * Options for creating a structured output handler.\n */\nexport interface StructuredOutputOptions<TSchema extends z.ZodType> {\n context?: ContextEngine;\n model?: AgentModel;\n schema: TSchema;\n providerOptions?: Parameters<typeof generateText>[0]['providerOptions'];\n tools?: ToolSet;\n}\n\n/**\n * Create a structured output handler that provides simplified access to structured output.\n *\n * @param options - Configuration options including schema\n * @returns Object with generate() and stream() methods\n *\n * @example\n * ```typescript\n * const output = structuredOutput({\n * name: 'extractor',\n * model: groq('...'),\n * context,\n * schema: z.object({\n * name: z.string(),\n * age: z.number(),\n * }),\n * });\n *\n * // Generate - returns only the structured output\n * const result = await output.generate({});\n * // result: { name: string, age: number }\n *\n * // Stream - returns the full stream\n * const stream = await output.stream({});\n * ```\n */\nexport interface StructuredOutputResult<TSchema extends z.ZodType> {\n generate<CIn>(\n contextVariables?: CIn,\n config?: { abortSignal?: AbortSignal },\n ): Promise<z.infer<TSchema>>;\n stream<CIn>(\n contextVariables?: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n },\n ): Promise<StreamTextResult<ToolSet, any>>;\n}\n\nexport function structuredOutput<TSchema extends z.ZodType>(\n options: StructuredOutputOptions<TSchema>,\n): StructuredOutputResult<TSchema> {\n return {\n async generate<CIn>(\n contextVariables?: CIn,\n config?: { abortSignal?: AbortSignal },\n ): Promise<z.infer<TSchema>> {\n if (!options.context) {\n throw new Error(`structuredOutput is missing a context.`);\n }\n if (!options.model) {\n throw new Error(`structuredOutput is missing a model.`);\n }\n\n const { messages, systemPrompt } = await options.context.resolve({\n renderer: new XmlRenderer(),\n });\n\n const result = await generateText({\n abortSignal: config?.abortSignal,\n providerOptions: options.providerOptions,\n model: options.model,\n system: systemPrompt,\n messages: await convertToModelMessages(messages as never),\n stopWhen: stepCountIs(25),\n experimental_repairToolCall: repairToolCall,\n experimental_context: contextVariables,\n output: Output.object({ schema: options.schema }),\n tools: options.tools,\n });\n\n return result.output as z.infer<TSchema>;\n },\n\n async stream<CIn>(\n contextVariables?: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?:\n | StreamTextTransform<ToolSet>\n | StreamTextTransform<ToolSet>[];\n },\n ) {\n if (!options.context) {\n throw new Error(`structuredOutput is missing a context.`);\n }\n if (!options.model) {\n throw new Error(`structuredOutput is missing a model.`);\n }\n\n const { messages, systemPrompt } = await options.context.resolve({\n renderer: new XmlRenderer(),\n });\n\n return streamText({\n abortSignal: config?.abortSignal,\n providerOptions: options.providerOptions,\n model: options.model,\n system: systemPrompt,\n experimental_repairToolCall: repairToolCall,\n messages: await convertToModelMessages(messages as never),\n stopWhen: stepCountIs(50),\n experimental_transform: config?.transform ?? smoothStream(),\n experimental_context: contextVariables,\n output: Output.object({ schema: options.schema }),\n tools: options.tools,\n });\n },\n };\n}\n\nconst repairToolCall: ToolCallRepairFunction<ToolSet> = async ({\n toolCall,\n tools,\n inputSchema,\n error,\n}) => {\n console.log(\n `Debug: ${chalk.yellow('RepairingToolCall')}: ${toolCall.toolName}`,\n error.name,\n );\n if (NoSuchToolError.isInstance(error)) {\n return null; // do not attempt to fix invalid tool names\n }\n\n const tool = tools[toolCall.toolName as keyof typeof tools];\n\n const { output } = await generateText({\n model: groq('openai/gpt-oss-20b'),\n output: Output.object({ schema: tool.inputSchema }),\n prompt: [\n `The model tried to call the tool \"${toolCall.toolName}\"` +\n ` with the following inputs:`,\n JSON.stringify(toolCall.input),\n `The tool accepts the following schema:`,\n JSON.stringify(inputSchema(toolCall)),\n 'Please fix the inputs.',\n ].join('\\n'),\n });\n\n return { ...toolCall, input: JSON.stringify(output) };\n};\n\nfunction writeText(writer: UIMessageStreamWriter, text: string) {\n const feedbackPartId = generateId();\n writer.write({\n id: feedbackPartId,\n type: 'text-start',\n });\n writer.write({\n id: feedbackPartId,\n type: 'text-delta',\n delta: ` ${text}`,\n });\n writer.write({\n id: feedbackPartId,\n type: 'text-end',\n });\n}\n", "import { type ContextFragment, fragment } from './fragments.ts';\nimport { XmlRenderer } from './renderers/abstract.renderer.ts';\n\n/**\n * Render fragments to XML.\n *\n * Wraps fragments in a parent tag and renders using XmlRenderer.\n *\n * @param tag - Parent tag name (e.g., 'instructions')\n * @param fragments - Fragments to render\n * @returns XML string\n *\n * @example\n * ```ts\n * const xml = render(\n * 'instructions',\n * persona({ name: 'Freya', role: 'Data Assistant' }),\n * guardrail({ rule: 'Never expose PII' }),\n * );\n * ```\n */\nexport function render(tag: string, ...fragments: ContextFragment[]): string {\n if (fragments.length === 0) {\n return '';\n }\n\n const renderer = new XmlRenderer();\n const wrapped = fragment(tag, ...fragments);\n return renderer.render([wrapped]);\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface SqlExtractorOptions {\n validateSql?: boolean;\n skipInvalid?: boolean;\n}\n\nconst outputSchema = z.object({\n question: z\n .string()\n .describe('A natural language question that the SQL query answers'),\n});\n/**\n * SqlExtractor - Generate questions for existing SQL queries.\n *\n * Given a list of SQL queries, uses an LLM to generate the natural\n * language questions they answer.\n */\nexport class SqlExtractor extends PairProducer {\n #sqls: string[];\n #adapter: Adapter;\n #options: SqlExtractorOptions;\n\n /**\n * @param sql - SQL query or queries to generate questions for\n * @param adapter - Database adapter for validation and schema introspection\n * @param options - Extraction configuration\n */\n constructor(\n sql: string[] | string,\n adapter: Adapter,\n options: SqlExtractorOptions = {},\n ) {\n super();\n this.#sqls = Array.isArray(sql) ? sql : [sql];\n this.#adapter = adapter;\n this.#options = options;\n }\n\n /**\n * Generates natural language questions for each SQL query using an LLM.\n * @returns Pairs with generated questions and original SQL\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n const { validateSql = true, skipInvalid = false } = this.#options;\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.#adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n\n for (const sql of this.#sqls) {\n let isValid = true;\n if (validateSql) {\n const error = await this.#adapter.validate(sql);\n isValid = error === undefined || error === null;\n\n if (!isValid && skipInvalid) {\n continue;\n }\n }\n\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `sql-to-question-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'sql_to_question',\n role: 'You are an expert at understanding SQL queries and generating clear, natural language questions that describe what the query retrieves.',\n objective:\n 'Generate clear, natural language questions that describe what SQL queries retrieve',\n }),\n fragment('database_schema', introspection),\n fragment('sql', sql),\n fragment(\n 'task',\n dedent`\n Given the database schema and the SQL query above, generate a single\n natural language question that:\n 1. Accurately describes what information the query retrieves\n 2. Uses natural business language (not SQL terminology)\n 3. Could be asked by a non-technical user\n 4. Is concise but complete\n `,\n ),\n fragment(\n 'examples',\n dedent`\n SQL: SELECT COUNT(*) FROM customers WHERE region = 'NY'\n Question: \"How many customers do we have in New York?\"\n\n SQL: SELECT product_name, SUM(quantity) as total FROM orders GROUP BY product_name ORDER BY total DESC LIMIT 10\n Question: \"What are our top 10 products by quantity sold?\"\n\n SQL: SELECT c.name, COUNT(o.id) FROM customers c LEFT JOIN orders o ON c.id = o.customer_id GROUP BY c.id HAVING COUNT(o.id) = 0\n Question: \"Which customers have never placed an order?\"\n `,\n ),\n user('Generate a natural language question for this SQL query.'),\n );\n\n const sqlToQuestionOutput = structuredOutput({\n model: groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n const output = await sqlToQuestionOutput.generate();\n\n yield [\n {\n question: output.question,\n sql,\n success: isValid,\n },\n ];\n }\n }\n}\n", "import type { UIMessage } from 'ai';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n} from './base-contextual-extractor.ts';\n\nexport type FullContextExtractorOptions = BaseContextualExtractorOptions;\n\n/**\n * Extracts SQL pairs with full conversation context.\n *\n * @example\n * ```typescript\n * const extractor = new FullContextExtractor(messages, adapter);\n * const pairs = await extractor.produce();\n * ```\n */\nexport class FullContextExtractor extends BaseContextualExtractor {\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: FullContextExtractorOptions = {},\n ) {\n super(messages, adapter, options);\n }\n\n /**\n * Add user message to context (keeps all messages).\n */\n protected async onUserMessage(text: string): Promise<void> {\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return all context accumulated so far.\n */\n protected getContextSnapshot(): string[] {\n return [...this.context];\n }\n}\n", "import type { UIMessage } from 'ai';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n} from './base-contextual-extractor.ts';\n\nexport interface WindowedContextExtractorOptions\n extends BaseContextualExtractorOptions {\n windowSize: number;\n}\n\n/**\n * Extracts SQL pairs with a sliding window of conversation context.\n *\n * @example\n * ```typescript\n * const extractor = new WindowedContextExtractor(messages, adapter, {\n * windowSize: 5,\n * });\n * const pairs = await extractor.produce();\n * ```\n */\nexport class WindowedContextExtractor extends BaseContextualExtractor {\n private windowSize: number;\n\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: WindowedContextExtractorOptions,\n ) {\n super(messages, adapter, options);\n this.windowSize = options.windowSize;\n }\n\n /**\n * Add user message to context (keeps all, windowing happens on snapshot).\n */\n protected async onUserMessage(text: string): Promise<void> {\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return only the last N messages based on window size.\n */\n protected getContextSnapshot(): string[] {\n if (this.context.length <= this.windowSize) {\n return [...this.context];\n }\n return this.context.slice(-this.windowSize);\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport type { UIMessage } from 'ai';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n formatConversation,\n resolveContext,\n} from './base-contextual-extractor.ts';\n\nexport type SegmentedContextExtractorOptions = BaseContextualExtractorOptions;\n\nconst topicChangeSchema = z.object({\n isTopicChange: z\n .boolean()\n .describe('Whether the new message represents a topic change'),\n reason: z.string().describe('Brief explanation for the decision'),\n});\n\n/**\n * Detects if a new message represents a topic change from the prior context.\n */\nasync function detectTopicChange(params: {\n context: string;\n newMessage: string;\n}): Promise<{ isTopicChange: boolean; reason: string }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `topic-change-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'topic_change_detector',\n role: 'You are an expert at understanding conversational flow and detecting topic changes.',\n objective: 'Detect significant topic changes in database conversations',\n }),\n fragment('conversation_context', params.context || '(no prior context)'),\n fragment('new_message', params.newMessage),\n fragment(\n 'task',\n dedent`\n Determine if the new message represents a significant topic change from the\n prior conversation context. A topic change occurs when:\n 1. The user asks about a completely different entity/table/domain\n 2. The user starts a new analytical question unrelated to prior discussion\n 3. There's a clear shift in what data or metrics are being discussed\n\n NOT a topic change:\n - Follow-up questions refining the same query (\"filter by...\", \"sort by...\")\n - Questions about the same entities with different conditions\n - Requests for more details on the same topic\n `,\n ),\n fragment(\n 'examples',\n dedent`\n Context: \"Show me customers in NY\" \u2192 \"Sort by revenue\"\n New: \"Filter to those with orders over $1000\"\n Decision: NOT a topic change (still refining customer query)\n\n Context: \"Show me customers in NY\" \u2192 \"Sort by revenue\"\n New: \"What were our total sales last quarter?\"\n Decision: Topic change (shifted from customers to sales metrics)\n\n Context: \"List all products\"\n New: \"How many orders did we have last month?\"\n Decision: Topic change (products \u2192 orders/sales)\n `,\n ),\n user('Determine if this is a topic change.'),\n );\n\n const topicOutput = structuredOutput({\n model: groq('openai/gpt-oss-20b'),\n context,\n schema: topicChangeSchema,\n });\n\n return topicOutput.generate();\n}\n\n/**\n * Extracts SQL pairs with topic-aware context segmentation.\n *\n * When a topic change is detected:\n * 1. The triggering message is resolved to standalone form using LLM\n * 2. Context is reset\n * 3. The resolved message becomes the start of the new context\n *\n * @example\n * ```typescript\n * const extractor = new SegmentedContextExtractor(messages, adapter);\n * const pairs = await extractor.produce();\n * ```\n */\nexport class SegmentedContextExtractor extends BaseContextualExtractor {\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: SegmentedContextExtractorOptions = {},\n ) {\n super(messages, adapter, options);\n }\n\n /**\n * Handle user message with topic change detection.\n * If topic changes, resolve the message to standalone form before resetting.\n *\n * Note: We capture context snapshot before async LLM calls to prevent race conditions\n * where context might be modified during the async operation.\n */\n protected async onUserMessage(text: string): Promise<void> {\n // Check for topic change if we have enough context\n if (this.context.length >= 2) {\n // Capture snapshot BEFORE async calls to prevent race conditions\n const contextSnapshot = [...this.context];\n const { isTopicChange } = await detectTopicChange({\n context: formatConversation(contextSnapshot),\n newMessage: text,\n });\n if (isTopicChange) {\n // Resolve the triggering message BEFORE resetting context\n const resolved = await this.resolveToStandalone(text, contextSnapshot);\n this.context = [`User: ${resolved}`];\n return;\n }\n }\n\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return all context in current topic segment.\n */\n protected getContextSnapshot(): string[] {\n return [...this.context];\n }\n\n /**\n * Resolve a context-dependent message into a standalone question.\n * Called when topic change is detected to preserve the meaning of\n * the triggering message before context is reset.\n * @param text - The user message to resolve\n * @param contextSnapshot - Snapshot of context captured before this async call\n */\n private async resolveToStandalone(\n text: string,\n contextSnapshot: string[],\n ): Promise<string> {\n const output = await resolveContext({\n conversation: formatConversation([...contextSnapshot, `User: ${text}`]),\n sql: '', // No SQL yet, just resolving the question\n });\n\n return output.question;\n }\n}\n", "import type { UIMessage } from 'ai';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport type { ExtractedPair } from '../types.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n formatConversation,\n resolveContext,\n} from './base-contextual-extractor.ts';\n\nexport type LastQueryExtractorOptions = BaseContextualExtractorOptions;\n\n/**\n * Extracts only the last SQL query with its resolved question.\n *\n * @example\n * ```typescript\n * const extractor = new LastQueryExtractor(messages, adapter);\n * const pairs = await extractor.toPairs(); // Returns array with at most 1 pair\n * ```\n */\nexport class LastQueryExtractor extends BaseContextualExtractor {\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: LastQueryExtractorOptions = {},\n ) {\n super(messages, adapter, options);\n }\n\n /**\n * Add user message to context (keeps all messages).\n */\n protected async onUserMessage(text: string): Promise<void> {\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return all context accumulated so far.\n */\n protected getContextSnapshot(): string[] {\n return [...this.context];\n }\n\n /**\n * Override to only resolve the LAST query instead of all queries.\n */\n protected override async *resolveQuestions(\n introspection: string,\n ): AsyncGenerator<ExtractedPair[]> {\n if (this.results.length === 0) {\n return;\n }\n\n const last = this.results.at(-1)!;\n const output = await resolveContext({\n conversation: formatConversation(last.conversationContext),\n sql: last.sql,\n introspection,\n });\n\n yield [\n {\n question: output.question,\n sql: last.sql,\n context: last.conversationContext,\n success: last.success,\n },\n ];\n }\n}\n", "import pLimit from 'p-limit';\n\nimport type { AgentModel } from '@deepagents/agent';\nimport type { ContextFragment } from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n type QuestionComplexity,\n generateQuestions,\n} from '../../agents/question.agent.ts';\nimport {\n type ToSqlResult,\n UnanswerableSQLError,\n toSql,\n} from '../../agents/sql.agent.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\nimport type { Persona } from './persona-generator.ts';\n\nexport interface SchemaSynthesizerOptions {\n count: number;\n complexity?: QuestionComplexity | QuestionComplexity[];\n personas?: Persona[];\n teachings?: ContextFragment[];\n model: AgentModel;\n concurrency?: number;\n}\n/**\n * SchemaSynthesizer - Generate pairs from database schema.\n *\n * Fully synthetic: generates natural language questions at specified\n * complexity levels and personas, then generates SQL for each question.\n * Iterates through all persona \u00D7 complexity combinations.\n */\nexport class SchemaSynthesizer extends PairProducer {\n #complexities: QuestionComplexity[] = [];\n #personas: (Persona | undefined)[] = [];\n #limit: ReturnType<typeof pLimit>;\n\n /**\n * @param adapter - Database adapter for schema introspection and SQL validation\n * @param options - Synthesis configuration including count, complexity, and concurrency\n */\n constructor(\n private adapter: Adapter,\n private options: SchemaSynthesizerOptions,\n ) {\n super();\n this.#complexities = Array.isArray(this.options.complexity)\n ? this.options.complexity\n : [this.options.complexity ?? 'moderate'];\n\n this.#personas = this.options.personas ?? [undefined];\n this.#limit = pLimit(this.options.concurrency ?? 5);\n }\n\n /**\n * Generates question-SQL pairs by iterating through all persona \u00D7 complexity combinations.\n * Uses parallel processing bounded by the configured concurrency limit.\n * Yields results as each combination completes (streaming pattern).\n * @returns Generated pairs from all combinations\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n\n const combinations = this.#personas.flatMap((persona) =>\n this.#complexities.map((complexity) => ({ persona, complexity })),\n );\n\n // Process each combination and yield immediately as it completes\n // pLimit handles concurrency - no need to create all promises upfront\n for (const { persona, complexity } of combinations) {\n const pairs = await this.#processCombination(\n introspection,\n persona,\n complexity,\n );\n if (pairs.length) {\n yield pairs;\n }\n }\n }\n\n /**\n * Processes a single persona \u00D7 complexity combination by generating questions\n * and converting each to SQL in parallel.\n */\n async #processCombination(\n introspection: string, // TODO: This was Awaited<ReturnType<Adapter['introspect']>> - needs update when synthesis uses fragments\n persona: Persona | undefined,\n complexity: QuestionComplexity,\n ): Promise<ExtractedPair[]> {\n const personaContext = persona\n ? `As ${persona.role}, ${persona.perspective}\\n\\nGenerate questions this persona would ask.`\n : undefined;\n\n const prompt = personaContext\n ? `${personaContext}\\n\\nGenerate ${this.options.count} questions at ${complexity} complexity.`\n : undefined;\n\n const { questions } = await this.#limit(() =>\n generateQuestions({\n introspection,\n complexity,\n count: this.options.count,\n prompt,\n model: this.options.model,\n }),\n );\n\n const pairs = await Promise.all(\n questions.map(async (question) => {\n const result = await this.#limit(async () => {\n try {\n // TODO: Update to use schemaFragments instead of introspection string\n return await toSql({\n input: question,\n adapter: this.adapter,\n schemaFragments: [], // Placeholder - needs to pass actual fragments\n instructions: this.options.teachings ?? [],\n model: this.options.model,\n });\n } catch (error) {\n if (UnanswerableSQLError.isInstance(error)) {\n return {\n attempts: 0,\n sql: '',\n errors: [\n `Cannot answer the question ${question} because ${error.message}`,\n ],\n } satisfies ToSqlResult;\n }\n throw error;\n }\n });\n\n return {\n question,\n sql: result.sql,\n success: !result.errors || result.errors.length === 0,\n };\n }),\n );\n\n return pairs;\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n guardrail,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nexport type QuestionComplexity =\n | 'simple'\n | 'moderate'\n | 'complex'\n | 'high complex';\n\nconst complexityInstructions: Record<QuestionComplexity, string> = {\n simple: dedent`\n Generate simple questions that require:\n - Basic SELECT with single table\n - Simple WHERE clauses with one condition\n - COUNT(*) or basic aggregations\n - No joins required\n Examples: \"How many customers do we have?\", \"List all products\", \"What is the total revenue?\"\n `,\n moderate: dedent`\n Generate moderate questions that require:\n - JOINs between 2-3 tables\n - Multiple WHERE conditions (AND/OR)\n - GROUP BY with HAVING clauses\n - ORDER BY with LIMIT\n - Basic subqueries\n Examples: \"What are the top 5 customers by total orders?\", \"Which products have never been ordered?\"\n `,\n complex: dedent`\n Generate complex questions that require:\n - Multiple JOINs (3+ tables)\n - Nested subqueries or CTEs\n - Complex aggregations with multiple GROUP BY columns\n - CASE expressions\n - Date/time calculations\n Examples: \"What is the month-over-month growth rate?\", \"Which customers have increased spending compared to last year?\"\n `,\n 'high complex': dedent`\n Generate highly complex questions that require advanced SQL features:\n - Window functions (ROW_NUMBER, RANK, DENSE_RANK)\n - LAG, LEAD for comparisons\n - Running totals (SUM OVER)\n - Moving averages\n - PARTITION BY clauses\n - Complex CTEs with multiple levels\n Examples: \"What is the running total of sales per month?\", \"Rank customers by their purchase frequency within each region\"\n `,\n};\n\nconst outputSchema = z.object({\n questions: z\n .array(z.string().describe('A natural language question about the data'))\n .min(1)\n .describe('List of natural language questions a user might ask'),\n});\n\nexport interface GenerateQuestionsParams {\n /** Database schema introspection */\n introspection: string;\n /** Complexity level for generated questions */\n complexity: QuestionComplexity;\n /** Number of questions to generate */\n count: number;\n /** Optional prompt to prepend (e.g., persona context) */\n prompt?: string;\n /** Optional model override */\n model?: AgentModel;\n}\n\nexport interface GenerateQuestionsResult {\n questions: string[];\n}\n\n/**\n * Generate natural language questions from database schema.\n * Used for creating synthetic training data for text-to-SQL models.\n */\nexport async function generateQuestions(\n params: GenerateQuestionsParams,\n): Promise<GenerateQuestionsResult> {\n const { introspection, complexity, count, prompt, model } = params;\n\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `question-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'question_generator',\n role: 'You are a synthetic data generator specializing in creating realistic natural language questions that users might ask about a database.',\n objective:\n 'Generate diverse, realistic natural language questions that match the specified complexity level',\n }),\n fragment('database_schema', introspection || ''),\n fragment(\n 'complexity',\n { level: complexity },\n complexityInstructions[complexity],\n ),\n fragment(\n 'task',\n dedent`\n Generate exactly ${count} natural language questions at the \"${complexity}\" complexity level.\n The questions should:\n 1. Match the complexity requirements above\n 2. Use natural business language, not technical SQL terms\n 3. Be realistic questions a non-technical user would actually ask\n 4. Cover different tables and relationships when possible\n `,\n ),\n guardrail({\n rule: 'Questions MUST ONLY reference tables and columns that exist in the schema above',\n }),\n guardrail({\n rule: 'Before generating each question, verify that ALL entities (tables, columns, relationships) you reference are explicitly listed in the schema',\n }),\n guardrail({\n rule: 'DO NOT invent or assume tables/columns that are not explicitly shown in the schema',\n }),\n guardrail({\n rule: 'Use natural language without SQL keywords like SELECT, WHERE, etc.',\n }),\n guardrail({\n rule: 'All questions must match the specified complexity level',\n }),\n user(\n prompt ??\n `Generate ${count} questions at ${complexity} complexity given db schema.`,\n ),\n );\n\n const questionOutput = structuredOutput({\n model: model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n return questionOutput.generate();\n}\n", "import { groq } from '@ai-sdk/groq';\nimport {\n APICallError,\n JSONParseError,\n NoContentGeneratedError,\n NoObjectGeneratedError,\n NoOutputGeneratedError,\n TypeValidationError,\n defaultSettingsMiddleware,\n wrapLanguageModel,\n} from 'ai';\nimport { Console } from 'node:console';\nimport { createWriteStream } from 'node:fs';\nimport pRetry from 'p-retry';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n type ContextFragment,\n InMemoryContextStore,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../adapters/adapter.ts';\n\nexport interface ToSqlOptions {\n /** The natural language input to convert to SQL */\n input: string;\n /** Database adapter for validation */\n adapter: Adapter;\n /** Schema fragments from adapter introspection */\n schemaFragments: ContextFragment[];\n /** Instructions/teachings to include */\n instructions: ContextFragment[];\n /** Optional model override */\n model: AgentModel;\n /** Maximum retry attempts on validation failure (default: 3) */\n maxRetries?: number;\n}\n\nexport interface ToSqlResult {\n /** The generated SQL query */\n sql: string;\n /** Number of attempts made */\n attempts: number;\n /** Validation errors encountered (if any retries occurred) */\n errors?: string[];\n}\n\nconst logger = new Console({\n stdout: createWriteStream('./sql-agent.log', { flags: 'a' }),\n stderr: createWriteStream('./sql-agent-error.log', { flags: 'a' }),\n inspectOptions: { depth: null },\n});\n\n/** Temperature progression for retries: deterministic first, then increasingly exploratory */\nconst RETRY_TEMPERATURES = [0, 0.2, 0.3];\n\n/** Extract SQL from markdown fenced code block if present */\nfunction extractSql(output: string): string {\n const match = output.match(/```sql\\n?([\\s\\S]*?)```/);\n return match ? match[1].trim() : output.trim();\n}\n\nconst marker = Symbol('SQLValidationError');\n/**\n * Error thrown when SQL validation fails.\n */\nexport class SQLValidationError extends Error {\n [marker]: true;\n constructor(message: string) {\n super(message);\n this.name = 'SQLValidationError';\n this[marker] = true;\n }\n static isInstance(error: unknown): error is SQLValidationError {\n return error instanceof SQLValidationError && error[marker] === true;\n }\n}\n\n/**\n * Error thrown when the question cannot be answered with the given schema.\n */\nexport class UnanswerableSQLError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UnanswerableSQLError';\n }\n static isInstance(error: unknown): error is UnanswerableSQLError {\n return error instanceof UnanswerableSQLError;\n }\n}\n\nexport async function toSql(options: ToSqlOptions): Promise<ToSqlResult> {\n const { maxRetries = 3 } = options;\n\n return withRetry(\n async (attemptNumber, errors, attempts) => {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `sql-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'Freya',\n role: 'You are an expert SQL query generator. You translate natural language questions into precise, efficient SQL queries based on the provided database schema.',\n objective:\n 'Translate natural language questions into precise, efficient SQL queries',\n }),\n ...options.instructions,\n ...options.schemaFragments,\n );\n\n // Add user message(s)\n if (errors.length) {\n context.set(\n user(options.input),\n user(\n `<validation_error>Your previous SQL query had the following error: ${errors.at(-1)?.message}. Please fix the query.</validation_error>`,\n ),\n );\n } else {\n context.set(user(options.input));\n }\n\n // Create structured output with schema\n const temperature =\n RETRY_TEMPERATURES[attemptNumber - 1] ??\n RETRY_TEMPERATURES[RETRY_TEMPERATURES.length - 1];\n const baseModel = options.model ?? groq('openai/gpt-oss-20b');\n const model = wrapLanguageModel({\n model: baseModel,\n middleware: defaultSettingsMiddleware({ settings: { temperature } }),\n });\n const sqlOutput = structuredOutput({\n model: model,\n context,\n schema: z.union([\n z.object({\n sql: z.string().describe('The SQL query that answers the question'),\n reasoning: z\n .string()\n .optional()\n .describe('The reasoning steps taken to generate the SQL'),\n }),\n z.object({\n error: z\n .string()\n .describe(\n 'Error message explaining why the question cannot be answered with the given schema',\n ),\n }),\n ]),\n });\n\n const output = await sqlOutput.generate();\n\n // Handle error responses (question is unanswerable with given schema)\n if ('error' in output) {\n throw new UnanswerableSQLError(output.error);\n }\n\n const sql = extractSql(output.sql);\n\n // Validate the generated SQL\n const validationError = await options.adapter.validate(sql);\n if (validationError) {\n throw new SQLValidationError(validationError);\n }\n\n return {\n attempts,\n sql,\n errors: errors.length ? errors.map(formatErrorMessage) : undefined,\n };\n },\n { retries: maxRetries - 1 },\n );\n}\n\nfunction formatErrorMessage(error: Error) {\n if (APICallError.isInstance(error)) {\n if (error.message.startsWith('Failed to validate JSON')) {\n return `Schema validation failed: ${error.message}`;\n }\n return error.message;\n }\n if (SQLValidationError.isInstance(error)) {\n return `SQL Validation Error: ${error.message}`;\n }\n return error.message;\n}\n\nasync function withRetry<T>(\n computation: (\n attemptNumber: number,\n errors: Error[],\n attempts: number,\n ) => Promise<T>,\n options: { retries: number } = { retries: 3 },\n) {\n const errors: Error[] = [];\n let attempts = 0;\n return pRetry(\n (attemptNumber) => {\n return computation(attemptNumber, errors, ++attempts);\n },\n {\n retries: options.retries,\n shouldRetry: (context) => {\n // Don't retry if unanswerable - it's intentional\n if (UnanswerableSQLError.isInstance(context.error)) {\n return false;\n }\n // Retry on validation errors\n if (SQLValidationError.isInstance(context.error)) {\n return true;\n }\n console.log({\n NoObjectGeneratedError: NoObjectGeneratedError.isInstance(\n context.error,\n ),\n NoOutputGeneratedError: NoOutputGeneratedError.isInstance(\n context.error,\n ),\n APICallError: APICallError.isInstance(context.error),\n JSONParseError: JSONParseError.isInstance(context.error),\n TypeValidationError: TypeValidationError.isInstance(context.error),\n NoContentGeneratedError: NoContentGeneratedError.isInstance(\n context.error,\n ),\n });\n // Retry on AI SDK errors\n return (\n APICallError.isInstance(context.error) ||\n JSONParseError.isInstance(context.error) ||\n TypeValidationError.isInstance(context.error) ||\n NoObjectGeneratedError.isInstance(context.error) ||\n NoOutputGeneratedError.isInstance(context.error) ||\n NoContentGeneratedError.isInstance(context.error)\n );\n },\n onFailedAttempt(context) {\n logger.error(`toSQL`, context.error);\n console.log(\n `Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`,\n );\n // console.dir(context.error, { depth: null });\n errors.push(context.error);\n },\n },\n );\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport pLimit from 'p-limit';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n guardrail,\n persona as personaFragment,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport { type ExtractedPair, PairProducer } from '../types.ts';\nimport type { Persona } from './persona-generator.ts';\nimport { styleInstructions } from './styles.ts';\n\nexport interface BreadthEvolverOptions {\n count: number;\n persona?: Persona;\n model?: AgentModel;\n concurrency?: number;\n}\n\nconst paraphraserOutputSchema = z.object({\n paraphrases: z\n .array(\n z.string().describe('A paraphrased version of the original question'),\n )\n .min(1)\n .describe('List of paraphrased questions that would produce the same SQL'),\n});\n\n/**\n * Generates paraphrased versions of a question while preserving SQL equivalence.\n */\nasync function paraphraseQuestion(params: {\n question: string;\n sql: string;\n count: number;\n persona?: Persona;\n model?: AgentModel;\n}): Promise<{ paraphrases: string[] }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `paraphraser-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n const personaInstruction = params.persona\n ? dedent`\n <persona role=\"${params.persona.role}\">\n ${params.persona.perspective}\n\n Paraphrase the question as this persona would naturally ask it.\n Use their vocabulary, priorities, and framing style.\n </persona>\n `\n : '';\n\n const styleInstruction =\n params.persona?.styles && params.persona.styles.length > 0\n ? dedent`\n <communication_styles>\n Generate paraphrases using these communication styles: ${params.persona.styles.join(', ')}\n\n Style definitions:\n ${params.persona.styles.map((s) => `- ${s}: ${styleInstructions[s]}`).join('\\n')}\n\n Distribute paraphrases across these styles for variety.\n </communication_styles>\n `\n : '';\n\n context.set(\n personaFragment({\n name: 'question_paraphraser',\n role: 'You are a linguistic expert specializing in paraphrasing database questions. Your task is to generate alternative phrasings of questions that preserve the exact same semantic meaning - they must all produce the identical SQL query.',\n objective:\n 'Generate paraphrased versions of questions that preserve exact semantic meaning and produce identical SQL',\n }),\n fragment('original_question', params.question),\n fragment(\n 'reference_sql',\n params.sql,\n 'This SQL shows what the question is really asking - all paraphrases must ask for exactly this',\n ),\n ...(personaInstruction ? [fragment('persona', personaInstruction)] : []),\n ...(styleInstruction\n ? [fragment('communication_styles', styleInstruction)]\n : []),\n fragment(\n 'task',\n dedent`\n Generate exactly ${params.count} paraphrased versions of the original question.\n\n Requirements:\n 1. Each paraphrase must be semantically equivalent - it should produce the EXACT same SQL\n 2. Vary the sentence structure, word choice, and phrasing style\n 3. Use natural language without SQL keywords (SELECT, WHERE, JOIN, etc.)\n 4. Keep paraphrases realistic - how actual users would ask\n 5. Do not add or remove any conditions, filters, or requirements from the original\n ${params.persona?.styles?.length ? '6. Apply the specified communication styles to create diverse phrasings' : ''}\n `,\n ),\n guardrail({ rule: 'NEVER change what data is being requested' }),\n guardrail({\n rule: 'NEVER add filters, aggregations, or conditions not in the original',\n }),\n guardrail({\n rule: 'NEVER remove any specificity from the original question',\n }),\n guardrail({\n rule: 'All paraphrases must be answerable by the exact same SQL query',\n }),\n user(\n `Paraphrase this question ${params.count} times: \"${params.question}\"`,\n ),\n );\n\n const paraphraserOutput = structuredOutput({\n model: params.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: paraphraserOutputSchema,\n });\n\n return paraphraserOutput.generate();\n}\n/**\n * BreadthEvolver - Generate paraphrased variations of questions (in-breadth evolution).\n *\n * Takes existing question/SQL pairs and generates variations of the questions\n * while keeping the SQL identical. This creates training data diversity where\n * many different phrasings map to the same SQL query.\n *\n * Based on Microsoft's Evol-Instruct methodology for in-breadth evolution.\n */\nexport class BreadthEvolver extends PairProducer {\n #limit: ReturnType<typeof pLimit>;\n\n /**\n * @param source - Source pairs or producer to evolve\n * @param options - Evolution options including count, persona, and concurrency\n */\n constructor(\n private source: PairProducer | ExtractedPair[],\n private options: BreadthEvolverOptions,\n ) {\n super();\n this.#limit = pLimit(this.options.concurrency ?? 4);\n }\n\n /**\n * Batch pairs within each chunk for concurrent processing.\n * Uses pLimit for concurrency control, yields results per pair after chunk completes.\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n for await (const chunk of this.from(this.source)) {\n const tasks = chunk.map((pair) =>\n this.#limit(async () => {\n const result = await paraphraseQuestion({\n question: pair.question,\n sql: pair.sql,\n count: this.options.count,\n persona: this.options.persona,\n model: this.options.model,\n });\n\n return result.paraphrases.map((paraphrase: string) => ({\n question: paraphrase,\n sql: pair.sql,\n context: pair.context,\n success: pair.success,\n }));\n }),\n );\n\n const results = await Promise.all(tasks);\n yield results.flat();\n }\n }\n}\n", "/**\n * Natural language styles for text-to-SQL question generation.\n * Based on OmniSQL paper (March 2025): https://arxiv.org/html/2503.02240\n */\n\nexport const ALL_STYLES = [\n 'formal', // Professional business language\n 'colloquial', // Casual everyday speech\n 'imperative', // Commands: \"Show me...\", \"Get...\"\n 'interrogative', // Questions: \"What is...\", \"How many...\"\n 'descriptive', // Verbose, detailed\n 'concise', // Brief, minimal\n 'vague', // Ambiguous, hedging\n 'metaphorical', // Figurative language\n 'conversational', // Chat-like\n] as const;\n\nexport type NLStyle = (typeof ALL_STYLES)[number];\n\nexport const styleInstructions: Record<NLStyle, string> = {\n formal: 'Use professional business language, complete sentences, no slang',\n colloquial: 'Use casual everyday speech, contractions, informal tone',\n imperative: 'Phrase as commands: \"Show me...\", \"Get...\", \"List...\"',\n interrogative: 'Phrase as questions: \"What is...\", \"How many...\", \"Which...\"',\n descriptive: 'Use detailed, verbose phrasing with extra context',\n concise: 'Use minimal words, telegram-style brevity',\n vague: 'Be intentionally ambiguous, use hedging language',\n metaphorical: 'Use figurative language, analogies, creative phrasing',\n conversational: 'Chat-like tone, as if talking to a colleague',\n};\n", "import { groq } from '@ai-sdk/groq';\nimport { NoObjectGeneratedError, NoOutputGeneratedError } from 'ai';\nimport dedent from 'dedent';\nimport pLimit from 'p-limit';\nimport pRetry from 'p-retry';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n guardrail,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport { UnanswerableSQLError, toSql } from '../../agents/sql.agent.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\n/**\n * Techniques for evolving questions into more complex versions.\n * Each technique transforms the question in a specific way.\n */\nexport type DepthTechnique =\n | 'add-aggregation'\n | 'add-filter'\n | 'add-join'\n | 'add-reasoning'\n | 'hypothetical';\n\nconst techniqueInstructions: Record<DepthTechnique, string> = {\n 'add-aggregation': dedent`\n Add aggregation requirements to the question.\n Transform it to require GROUP BY, COUNT, SUM, AVG, MIN, MAX, or similar operations.\n Examples:\n - \"Show orders\" \u2192 \"Show total order count by customer\"\n - \"List products\" \u2192 \"What is the average price per category?\"\n - \"Get employees\" \u2192 \"How many employees are in each department?\"\n `,\n 'add-filter': dedent`\n Add filtering conditions to the question.\n Transform it to require WHERE clauses with specific conditions.\n Examples:\n - \"Show orders\" \u2192 \"Show orders from the last 30 days\"\n - \"List customers\" \u2192 \"List customers who have made more than 5 purchases\"\n - \"Get products\" \u2192 \"Get products with price above $100\"\n `,\n 'add-join': dedent`\n Add requirements that need data from related tables.\n Transform it to require JOIN operations between multiple tables.\n Examples:\n - \"Show orders\" \u2192 \"Show orders with customer names and addresses\"\n - \"List products\" \u2192 \"List products with their supplier information\"\n - \"Get employees\" \u2192 \"Get employees with their department and manager names\"\n `,\n 'add-reasoning': dedent`\n Add multi-step reasoning requirements.\n Transform it to require logical deduction, comparisons, or derived calculations.\n Examples:\n - \"Show orders\" \u2192 \"Which customers have orders above the average order value?\"\n - \"List products\" \u2192 \"Which products are underperforming compared to their category average?\"\n - \"Get revenue\" \u2192 \"Which month had the highest growth compared to the previous month?\"\n `,\n hypothetical: dedent`\n Add a hypothetical or speculative scenario.\n Transform it to require applying calculations or projections.\n Examples:\n - \"Show revenue\" \u2192 \"What would revenue be if we increased all prices by 15%?\"\n - \"List inventory\" \u2192 \"How many days of stock remain at current sales rate?\"\n - \"Get costs\" \u2192 \"What would be the impact of a 10% discount on profit margins?\"\n `,\n};\n\nexport interface DepthEvolverOptions {\n techniques?: DepthTechnique[];\n count?: number;\n model: AgentModel;\n concurrency?: number;\n}\n\nconst evolverOutputSchema = z.object({\n evolvedQuestion: z\n .string()\n .describe('The evolved, more complex version of the original question'),\n});\n\n/**\n * Evolves a simple question into a more complex version using a specific technique.\n */\nasync function evolveQuestion(params: {\n question: string;\n sql: string;\n schema: string;\n technique: DepthTechnique;\n techniqueInstruction: string;\n model?: AgentModel;\n}): Promise<{ evolvedQuestion: string }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `evolver-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'question_evolver',\n role: 'You are an expert at evolving simple database questions into more complex ones. Your task is to take a basic question and transform it into a more sophisticated version that requires advanced SQL techniques to answer.',\n objective:\n 'Transform simple questions into complex versions requiring advanced SQL techniques',\n }),\n fragment('original_question', params.question),\n fragment(\n 'original_sql',\n params.sql,\n '(This shows what the original question required)',\n ),\n fragment('database_schema', params.schema),\n fragment(\n 'technique',\n { name: params.technique },\n params.techniqueInstruction,\n ),\n fragment(\n 'task',\n dedent`\n Evolve the original question using the \"${params.technique}\" technique.\n\n Requirements:\n 1. The evolved question must be MORE COMPLEX than the original\n 2. Apply the specific technique described above\n 3. The evolved question must be answerable using the provided schema\n 4. Use natural language - no SQL keywords\n 5. Keep the question realistic and practical\n 6. The evolved question should build upon the original topic/domain\n `,\n ),\n guardrail({\n rule: 'The evolved question MUST require more complex SQL than the original',\n }),\n guardrail({\n rule: 'Do not ask for data that does not exist in the schema',\n }),\n guardrail({\n rule: 'Keep the question grounded in the same domain as the original',\n }),\n guardrail({ rule: 'Make sure the question is clear and unambiguous' }),\n user(\n `Evolve this question using \"${params.technique}\": \"${params.question}\"`,\n ),\n );\n\n const evolverOutput = structuredOutput({\n model: params.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: evolverOutputSchema,\n });\n\n return evolverOutput.generate();\n}\n\nconst ALL_TECHNIQUES: DepthTechnique[] = [\n 'add-aggregation',\n 'add-filter',\n 'add-join',\n 'add-reasoning',\n 'hypothetical',\n];\n/**\n * DepthEvolver - Evolve questions into more complex versions (in-depth evolution).\n *\n * Takes existing question/SQL pairs and evolves them into more complex versions\n * using specific techniques. Both the question AND SQL change - the evolved\n * question requires a more sophisticated query to answer.\n *\n * Based on Microsoft's Evol-Instruct methodology for in-depth evolution.\n */\nexport class DepthEvolver extends PairProducer {\n #limit: ReturnType<typeof pLimit>;\n\n /**\n * @param source - Source pairs or producer to evolve\n * @param adapter - Database adapter for SQL generation\n * @param options - Evolution options including techniques, count, and concurrency\n */\n constructor(\n private source: PairProducer | ExtractedPair[],\n private adapter: Adapter,\n private options: DepthEvolverOptions,\n ) {\n super();\n this.#limit = pLimit(this.options?.concurrency ?? 4);\n }\n\n /**\n * Yields evolved pairs as each completes (streaming pattern).\n * Removes batch barrier - no longer waits for all evolutions before yielding.\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n const count = this.options?.count ?? 1;\n const techniques = this.options?.techniques ?? ALL_TECHNIQUES;\n\n let pairIndex = 0;\n for await (const chunk of this.from(this.source)) {\n for (const pair of chunk) {\n const tasks = Array.from({ length: count }, (_, i) => {\n const technique = this.options?.techniques\n ? techniques[i % techniques.length]\n : techniques[(pairIndex * count + i) % techniques.length];\n return this.#limit(() =>\n this.#processTask(pair, technique, introspection),\n );\n });\n\n const results = await Promise.all(tasks);\n yield results;\n pairIndex++;\n }\n }\n }\n\n async #processTask(\n pair: ExtractedPair,\n technique: DepthTechnique,\n introspection: string,\n ) {\n const output = await withRetry(() =>\n evolveQuestion({\n question: pair.question,\n sql: pair.sql,\n schema: introspection,\n technique,\n techniqueInstruction: techniqueInstructions[technique],\n model: this.options?.model,\n }),\n );\n\n const evolvedQuestion = output.evolvedQuestion;\n try {\n // TODO: Update to use schemaFragments instead of introspection string\n const sqlResult = await toSql({\n input: evolvedQuestion,\n adapter: this.adapter,\n schemaFragments: [], // Placeholder - needs to pass actual fragments\n instructions: [],\n model: this.options?.model,\n });\n\n return {\n question: evolvedQuestion,\n sql: sqlResult.sql,\n context: pair.context,\n success: !sqlResult.errors || sqlResult.errors.length === 0,\n };\n } catch (error) {\n if (UnanswerableSQLError.isInstance(error)) {\n return {\n question: evolvedQuestion,\n sql: '',\n context: pair.context,\n success: false,\n errors: [\n `Cannot answer the question ${evolvedQuestion} because ${error.message}`,\n ],\n };\n }\n throw error;\n }\n }\n}\n\nasync function withRetry<T>(computation: () => Promise<T>): Promise<T> {\n return pRetry(computation, {\n retries: 3,\n shouldRetry: (context) => {\n console.log({\n NoObjectGeneratedError: NoObjectGeneratedError.isInstance(\n context.error,\n ),\n NoOutputGeneratedError: NoOutputGeneratedError.isInstance(\n context.error,\n ),\n });\n return (\n NoObjectGeneratedError.isInstance(context.error) ||\n NoOutputGeneratedError.isInstance(context.error)\n );\n },\n onFailedAttempt(context) {\n console.log(\n `Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`,\n );\n console.dir(context.error, { depth: null });\n },\n });\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n type ContextFragment,\n InMemoryContextStore,\n XmlRenderer,\n fragment,\n guardrail,\n persona as personaFragment,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport { ALL_STYLES, type NLStyle } from './styles.ts';\n\nexport interface Persona {\n role: string;\n perspective: string;\n styles: NLStyle[];\n}\n\nexport interface PersonaGeneratorOptions {\n count?: number;\n model?: AgentModel;\n}\n\nconst outputSchema = z.object({\n personas: z\n .array(\n z.object({\n role: z.string().describe('The job title or role of this persona'),\n perspective: z\n .string()\n .describe(\n 'Rich description of what this persona cares about when querying the database',\n ),\n styles: z\n .array(z.enum(ALL_STYLES))\n .min(1)\n .max(3)\n .describe(\n 'Typical communication styles for this persona (1-3 styles)',\n ),\n }),\n )\n .min(1)\n .describe('List of personas who would query this database'),\n});\n\n/**\n * Generate personas by analyzing database schema.\n * Analyzes the schema to infer who would query this database and what\n * they care about. Generated personas can be used with BreadthEvolver\n * to create diverse question paraphrases from different perspectives.\n *\n * @param schemaFragments - Schema fragments from adapter.introspect()\n * @param options - Generation options including count and model\n * @returns Array of personas with roles and perspectives\n */\nexport async function generatePersonas(\n schemaFragments: ContextFragment[],\n options?: PersonaGeneratorOptions,\n): Promise<Persona[]> {\n const schema = new XmlRenderer().render(schemaFragments);\n const count = options?.count ?? 5;\n\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `persona-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n personaFragment({\n name: 'persona_generator',\n role: 'You are an expert at understanding database schemas and inferring who would use them.',\n objective:\n 'Generate realistic personas representing users who would query this database',\n }),\n fragment('database_schema', schema),\n fragment(\n 'task',\n dedent`\n Analyze the database schema and generate realistic personas representing\n the different types of users who would query this database.\n\n For each persona, provide:\n 1. **role**: Their job title or role (e.g., \"Financial Analyst\", \"Customer Support Rep\")\n 2. **perspective**: A rich description of what they care about, including:\n - What questions they typically ask\n - What metrics/data points matter to them\n - How they prefer data formatted or presented\n - Their priorities (speed vs accuracy, detail vs summary)\n - Domain-specific concerns relevant to their role\n 3. **styles**: 1-3 communication styles typical for this persona. Choose from:\n - formal: Professional business language, complete sentences\n - colloquial: Casual everyday speech, contractions\n - imperative: Commands like \"Show me...\", \"Get...\", \"List...\"\n - interrogative: Questions like \"What is...\", \"How many...\"\n - descriptive: Verbose, detailed phrasing\n - concise: Brief, minimal words\n - vague: Ambiguous, hedging language\n - metaphorical: Figurative language, analogies\n - conversational: Chat-like, casual tone\n\n Requirements:\n - Personas should be realistic for the given schema\n - Each persona should have distinct concerns and priorities\n - Perspectives should be detailed enough to guide question paraphrasing\n - Cover different levels of technical expertise (some technical, some business-focused)\n - Styles should match how this persona would naturally communicate\n `,\n ),\n fragment(\n 'example',\n dedent`\n For an e-commerce schema with orders, customers, products tables:\n\n {\n \"role\": \"Customer Support Rep\",\n \"perspective\": \"As customer support, I care about:\\\\n- Quick lookups by order ID or customer email\\\\n- Order status and shipping tracking\\\\n- Return and refund history\\\\n- Customer contact details and order history\\\\n- I need fast answers, not complex analysis\",\n \"styles\": [\"imperative\", \"concise\"]\n }\n\n {\n \"role\": \"Inventory Manager\",\n \"perspective\": \"As inventory manager, I care about:\\\\n- Current stock levels and reorder points\\\\n- Product availability across warehouses\\\\n- Slow-moving inventory identification\\\\n- Supplier lead times and pending orders\\\\n- I need accurate counts, often aggregated by location\",\n \"styles\": [\"formal\", \"interrogative\"]\n }\n `,\n ),\n guardrail({\n rule: 'Only generate personas relevant to the actual schema provided',\n }),\n guardrail({\n rule: 'Do not invent tables or data that do not exist in the schema',\n }),\n guardrail({\n rule: 'Ensure perspectives are specific to the domain, not generic',\n }),\n user(\n `Generate exactly ${count} distinct personas who would query this database.`,\n ),\n );\n\n const personaOutput = structuredOutput({\n model: options?.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n const output = await personaOutput.generate();\n return output.personas;\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n type ContextFragment,\n InMemoryContextStore,\n analogy,\n clarification,\n example,\n explain,\n fragment,\n guardrail,\n hint,\n persona,\n quirk,\n structuredOutput,\n styleGuide,\n term,\n user,\n workflow,\n} from '@deepagents/context';\n\nconst outputSchema = z.object({\n terms: z\n .array(z.object({ name: z.string(), definition: z.string() }))\n .optional()\n .describe('Domain terminology definitions'),\n hints: z\n .array(z.object({ text: z.string() }))\n .optional()\n .describe('Helpful hints for SQL generation'),\n guardrails: z\n .array(\n z.object({\n rule: z.string(),\n reason: z.string().optional(),\n action: z.string().optional(),\n }),\n )\n .optional()\n .describe('Safety rules and constraints'),\n explains: z\n .array(\n z.object({\n concept: z.string(),\n explanation: z.string(),\n therefore: z.string().optional(),\n }),\n )\n .optional()\n .describe('Concept explanations'),\n examples: z\n .array(\n z.object({\n question: z.string(),\n answer: z.string(),\n note: z.string().optional(),\n }),\n )\n .optional()\n .describe('Example question-answer pairs'),\n clarifications: z\n .array(z.object({ when: z.string(), ask: z.string(), reason: z.string() }))\n .optional()\n .describe('When to ask for clarification'),\n workflows: z\n .array(\n z.object({\n task: z.string(),\n steps: z.array(z.string()).min(1),\n triggers: z.array(z.string()).optional(),\n notes: z.string().optional(),\n }),\n )\n .optional()\n .describe('Multi-step workflows'),\n quirks: z\n .array(z.object({ issue: z.string(), workaround: z.string() }))\n .optional()\n .describe('Known issues and workarounds'),\n styleGuides: z\n .array(\n z.object({\n prefer: z.string(),\n never: z.string().optional(),\n always: z.string().optional(),\n }),\n )\n .optional()\n .describe('SQL style preferences'),\n analogies: z\n .array(\n z.object({\n concepts: z.array(z.string()).min(2),\n relationship: z.string(),\n insight: z.string().optional(),\n therefore: z.string().optional(),\n pitfall: z.string().optional(),\n }),\n )\n .optional()\n .describe('Concept analogies'),\n});\n\ntype TeachablesOutput = z.infer<typeof outputSchema>;\n\nexport interface GenerateToTeachingsOptions {\n model?: AgentModel;\n}\n\nexport async function toTeachings(\n input: { schema: string; context?: string },\n options?: GenerateToTeachingsOptions,\n): Promise<ContextFragment[]> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `teachables-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'teachables-author',\n role: 'You design \"fragments\" for a Text2SQL system. Fragments become structured XML instructions.',\n objective:\n 'Choose only high-impact items that improve accuracy, safety, or clarity for this database',\n }),\n fragment('database_schema', input.schema),\n ...(input.context ? [fragment('additional_context', input.context)] : []),\n fragment(\n 'output_structure',\n dedent`\n Output a JSON object with these optional arrays (include only relevant ones):\n - terms: [{ name: string, definition: string }] - Domain terminology\n - hints: [{ text: string }] - Helpful SQL generation hints\n - guardrails: [{ rule: string, reason?: string, action?: string }] - Safety constraints\n - explains: [{ concept: string, explanation: string, therefore?: string }] - Concept explanations\n - examples: [{ question: string, answer: string, note?: string }] - Q&A examples\n - clarifications: [{ when: string, ask: string, reason: string }] - Clarification triggers\n - workflows: [{ task: string, steps: string[], triggers?: string[], notes?: string }] - Multi-step tasks\n - quirks: [{ issue: string, workaround: string }] - Known issues\n - styleGuides: [{ prefer: string, never?: string, always?: string }] - SQL style rules\n - analogies: [{ concepts: string[], relationship: string, insight?: string, therefore?: string, pitfall?: string }]\n `,\n ),\n fragment(\n 'task',\n dedent`\n 1. Analyze the schema to infer domain, relationships, and sensitive columns.\n 2. Generate 3-10 fragments total across all categories, prioritizing:\n - guardrails for PII columns (email, ssn, phone, etc)\n - hints for status/enum columns\n - clarifications for ambiguous terms\n 3. Ground everything in the schema - do not invent tables/columns.\n 4. Only include categories that are relevant to this schema.\n `,\n ),\n user(\n `Analyze this database schema and generate fragments that will help an AI generate accurate SQL queries.`,\n ),\n );\n\n const teachablesOutput = structuredOutput({\n model: options?.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n const result = await teachablesOutput.generate();\n\n const fragments: ContextFragment[] = [];\n\n // Convert generated output to ContextFragments\n result.terms?.forEach((t) => fragments.push(term(t.name, t.definition)));\n result.hints?.forEach((h) => fragments.push(hint(h.text)));\n result.guardrails?.forEach((g) =>\n fragments.push(\n guardrail({ rule: g.rule, reason: g.reason, action: g.action }),\n ),\n );\n result.explains?.forEach((e) =>\n fragments.push(\n explain({\n concept: e.concept,\n explanation: e.explanation,\n therefore: e.therefore,\n }),\n ),\n );\n result.examples?.forEach((e) =>\n fragments.push(\n example({ question: e.question, answer: e.answer, note: e.note }),\n ),\n );\n result.clarifications?.forEach((c) =>\n fragments.push(\n clarification({ when: c.when, ask: c.ask, reason: c.reason }),\n ),\n );\n result.workflows?.forEach((w) =>\n fragments.push(\n workflow({\n task: w.task,\n steps: w.steps,\n triggers: w.triggers,\n notes: w.notes,\n }),\n ),\n );\n result.quirks?.forEach((q) =>\n fragments.push(quirk({ issue: q.issue, workaround: q.workaround })),\n );\n result.styleGuides?.forEach((s) =>\n fragments.push(\n styleGuide({ prefer: s.prefer, never: s.never, always: s.always }),\n ),\n );\n result.analogies?.forEach((a) =>\n fragments.push(\n analogy({\n concepts: a.concepts,\n relationship: a.relationship,\n insight: a.insight,\n therefore: a.therefore,\n pitfall: a.pitfall,\n }),\n ),\n );\n\n return fragments;\n}\n", "import type { AgentModel } from '@deepagents/agent';\nimport { type ContextFragment, XmlRenderer } from '@deepagents/context';\n\nimport { toTeachings } from '../../agents/teachables.agent.ts';\n\nexport interface TeachingsGeneratorOptions {\n context?: string;\n model?: AgentModel;\n maxRetries?: number;\n}\n\n/**\n * Generate domain-specific teachings from database schema.\n * Analyzes the schema to generate teachings that improve SQL generation accuracy.\n * Teachings include domain vocabulary, SQL patterns, guardrails, and examples\n * that help the SQL generator understand the domain and produce semantically\n * correct queries.\n *\n * @param schemaFragments - Schema fragments from adapter.introspect()\n * @param options - Generation options including context, model, and maxRetries\n * @returns Array of teachings including vocabulary, patterns, and guardrails\n */\nexport async function generateTeachings(\n schemaFragments: ContextFragment[],\n options?: TeachingsGeneratorOptions,\n): Promise<ContextFragment[]> {\n const schema = new XmlRenderer().render(schemaFragments);\n const maxRetries = options?.maxRetries ?? 3;\n\n let lastError: Error | undefined;\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await toTeachings(\n { schema, context: options?.context },\n { model: options?.model },\n );\n } catch (error) {\n lastError = error as Error;\n const isRetryable =\n lastError.message.includes('parse') ||\n lastError.message.includes('schema') ||\n lastError.message.includes('No object generated') ||\n lastError.name.includes('AI_');\n if (!isRetryable) {\n throw lastError;\n }\n }\n }\n\n throw lastError;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../../../src/lib/synthesis/types.ts", "../../../src/lib/synthesis/decorators/filtered-producer.ts", "../../../src/lib/synthesis/decorators/deduplicated-producer.ts", "../../../src/lib/synthesis/decorators/validated-producer.ts", "../../../src/lib/synthesis/extractors/message-extractor.ts", "../../../src/lib/synthesis/extractors/base-contextual-extractor.ts", "../../../../context/src/lib/engine.ts", "../../../../context/src/lib/estimate.ts", "../../../../context/src/lib/fragments.ts", "../../../../context/src/lib/renderers/abstract.renderer.ts", "../../../../context/src/lib/store/store.ts", "../../../../context/src/lib/fragments/domain.ts", "../../../../context/src/lib/fragments/user.ts", "../../../../context/src/lib/guardrail.ts", "../../../../context/src/lib/guardrails/error-recovery.guardrail.ts", "../../../../context/src/lib/sandbox/binary-bridges.ts", "../../../../context/src/lib/sandbox/docker-sandbox.ts", "../../../../context/src/lib/sandbox/container-tool.ts", "../../../../context/src/lib/skills/loader.ts", "../../../../context/src/lib/skills/fragments.ts", "../../../../context/src/lib/store/sqlite.store.ts", "../../../../context/src/lib/store/memory.store.ts", "../../../../context/src/lib/visualize.ts", "../../../../context/src/lib/agent.ts", "../../../../context/src/lib/render.ts", "../../../src/lib/synthesis/extractors/sql-extractor.ts", "../../../src/lib/synthesis/extractors/full-context-extractor.ts", "../../../src/lib/synthesis/extractors/windowed-context-extractor.ts", "../../../src/lib/synthesis/extractors/segmented-context-extractor.ts", "../../../src/lib/synthesis/extractors/last-query-extractor.ts", "../../../src/lib/synthesis/synthesizers/schema-synthesizer.ts", "../../../src/lib/agents/question.agent.ts", "../../../src/lib/agents/sql.agent.ts", "../../../src/lib/synthesis/synthesizers/breadth-evolver.ts", "../../../src/lib/synthesis/synthesizers/styles.ts", "../../../src/lib/synthesis/synthesizers/depth-evolver.ts", "../../../src/lib/synthesis/synthesizers/persona-generator.ts", "../../../src/lib/agents/teachables.agent.ts", "../../../src/lib/synthesis/synthesizers/teachings-generator.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * A question/SQL pair extracted or synthesized for training data.\n */\nexport interface ExtractedPair {\n question: string;\n sql: string;\n context?: string[];\n success: boolean;\n}\n\n/**\n * Interface for all pair producers (extractors and synthesizers).\n * Implementations encapsulate their specific inputs and logic.\n */\nexport abstract class PairProducer<T extends ExtractedPair = ExtractedPair> {\n /**\n * Produce question/SQL pairs.\n */\n abstract produce(): AsyncGenerator<T[], void, unknown>;\n\n protected from(producer: PairProducer<ExtractedPair> | ExtractedPair[]) {\n return Array.isArray(producer)\n ? (async function* (pairs: ExtractedPair[]) {\n yield pairs;\n })(producer)\n : producer.produce();\n }\n\n public toPairs(): Promise<T[]> {\n return toPairs(this);\n }\n}\n\n/**\n * Entry point for producing pairs from any source.\n */\nexport async function toPairs<T extends ExtractedPair>(\n producer: PairProducer<T>,\n): Promise<T[]> {\n const pairs: T[] = [];\n for await (const chunk of producer.produce()) {\n pairs.push(...chunk);\n }\n return pairs;\n}\n", "import { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface FilteredProducerOptions {\n successOnly?: boolean;\n tables?: string[];\n filter?: (pair: ExtractedPair) => boolean;\n}\n\n/**\n * FilteredProducer - Filter pairs from another producer.\n *\n * Wraps another PairProducer and filters the output based on criteria.\n */\nexport class FilteredProducer extends PairProducer {\n /**\n * @param producer - Source producer to filter\n * @param options - Filter configuration\n */\n constructor(\n private producer: PairProducer,\n private options: FilteredProducerOptions = {},\n ) {\n super();\n }\n\n /**\n * Produces pairs filtered by success status, table usage, and custom predicates.\n * @returns Pairs matching all configured filter criteria\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n for await (const chunk of this.producer.produce()) {\n const filtered = chunk.filter((pair) => {\n if (this.options.successOnly !== false && !pair.success) {\n return false;\n }\n\n if (this.options.tables?.length) {\n const sqlLower = pair.sql.toLowerCase();\n const hasTable = this.options.tables.some((t) =>\n sqlLower.includes(t.toLowerCase()),\n );\n if (!hasTable) return false;\n }\n\n if (this.options.filter && !this.options.filter(pair)) {\n return false;\n }\n\n return true;\n });\n\n if (filtered.length) {\n yield filtered;\n }\n }\n }\n}\n", "import { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface DeduplicatedProducerOptions {\n strategy?: 'exact' | 'sql-only' | 'question-only';\n}\n\n/**\n * DeduplicatedProducer - Remove duplicate pairs from another producer.\n *\n * Wraps another PairProducer and removes duplicates based on\n * exact match or semantic similarity.\n */\nexport class DeduplicatedProducer extends PairProducer {\n /**\n * @param producer - Source producer to deduplicate\n * @param options - Deduplication configuration\n */\n constructor(\n private producer: PairProducer,\n private options: DeduplicatedProducerOptions = {},\n ) {\n super();\n }\n\n /**\n * Produces pairs with duplicates removed based on the configured strategy.\n * @returns Unique pairs after deduplication\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n const { strategy = 'exact' } = this.options;\n const seen = new Set<string>();\n\n for await (const chunk of this.producer.produce()) {\n const unique: ExtractedPair[] = [];\n\n for (const pair of chunk) {\n let key: string;\n\n switch (strategy) {\n case 'sql-only':\n key = this.normalizeSQL(pair.sql);\n break;\n case 'question-only':\n key = pair.question.toLowerCase().trim();\n break;\n case 'exact':\n default:\n key = `${pair.question.toLowerCase().trim()}|||${this.normalizeSQL(pair.sql)}`;\n }\n\n if (!seen.has(key)) {\n seen.add(key);\n unique.push(pair);\n }\n }\n\n if (unique.length) {\n yield unique;\n }\n }\n }\n\n private normalizeSQL(sql: string): string {\n return sql.toLowerCase().replace(/\\s+/g, ' ').trim();\n }\n}\n", "import type { Adapter } from '../../adapters/adapter.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface ValidatedProducerOptions {\n execute?: boolean;\n removeInvalid?: boolean;\n}\n\nexport interface ValidatedPair extends ExtractedPair {\n rowCount?: number;\n error?: string;\n}\n/**\n * ValidatedProducer - Validate SQL from another producer.\n *\n * Wraps another PairProducer and validates each SQL query,\n * optionally executing to attach results.\n */\nexport class ValidatedProducer extends PairProducer<ValidatedPair> {\n /**\n * @param producer - Source producer to validate\n * @param adapter - Database adapter for SQL validation\n * @param options - Validation configuration\n */\n constructor(\n private producer: PairProducer,\n private adapter: Adapter,\n private options: ValidatedProducerOptions = {},\n ) {\n super();\n }\n\n /**\n * Produces pairs with SQL validation applied, optionally executing queries.\n * @returns Validated pairs with error/rowCount metadata attached\n */\n async *produce(): AsyncGenerator<ValidatedPair[]> {\n for await (const chunk of this.producer.produce()) {\n const validated: ValidatedPair[] = [];\n\n for (const pair of chunk) {\n const error = await this.adapter.validate(pair.sql);\n\n if (error) {\n if (!this.options.removeInvalid) {\n validated.push({\n ...pair,\n success: false,\n error,\n });\n }\n continue;\n }\n\n let rowCount: number | undefined;\n if (this.options.execute) {\n try {\n const result = await this.adapter.execute(pair.sql);\n rowCount = Array.isArray(result) ? result.length : undefined;\n } catch {\n // no op\n }\n }\n\n validated.push({\n ...pair,\n success: true,\n rowCount,\n });\n }\n\n if (validated.length) {\n yield validated;\n }\n }\n }\n}\n", "import {\n type UIMessage,\n getToolOrDynamicToolName,\n isToolOrDynamicToolUIPart,\n} from 'ai';\n\nimport { type ExtractedPair, PairProducer } from '../types.ts';\nimport {\n type DbQueryInput,\n getMessageText,\n} from './base-contextual-extractor.ts';\n\nexport interface MessageExtractorOptions {\n includeFailures?: boolean;\n toolName?: string;\n}\n/**\n * MessageExtractor - Extract pairs from chat history by parsing tool calls.\n *\n * Deterministic extraction: parses db_query tool calls and pairs them\n * with the preceding user message.\n */\nexport class MessageExtractor extends PairProducer {\n #messages: UIMessage[];\n #options: MessageExtractorOptions;\n\n /**\n * @param messages - Chat history to extract pairs from\n * @param options - Extraction configuration\n */\n constructor(messages: UIMessage[], options: MessageExtractorOptions = {}) {\n super();\n this.#messages = messages;\n this.#options = options;\n }\n\n /**\n * Extracts question-SQL pairs by parsing tool calls and pairing with user messages.\n * @returns Pairs extracted from db_query tool invocations\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n const { includeFailures = false, toolName = 'db_query' } = this.#options;\n let lastUserMessage: UIMessage | null = null;\n\n for (const message of this.#messages) {\n if (message.role === 'user') {\n lastUserMessage = message;\n continue;\n }\n\n if (message.role === 'assistant' && lastUserMessage) {\n for (const part of message.parts) {\n if (!isToolOrDynamicToolUIPart(part)) {\n continue;\n }\n\n if (getToolOrDynamicToolName(part) !== toolName) {\n continue;\n }\n\n // Handle both static and dynamic tool part shapes\n const toolInput = ('input' in part ? part.input : undefined) as\n | DbQueryInput\n | undefined;\n if (!toolInput?.sql) {\n continue;\n }\n\n const success = part.state === 'output-available';\n const failed = part.state === 'output-error';\n\n if (failed && !includeFailures) {\n continue;\n }\n\n // Skip incomplete tool calls (streaming or pending)\n if (!success && !failed) {\n continue;\n }\n\n const question = getMessageText(lastUserMessage);\n if (!question) {\n continue;\n }\n\n yield [\n {\n question,\n sql: toolInput.sql,\n success,\n },\n ];\n }\n }\n }\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport {\n type UIMessage,\n getToolOrDynamicToolName,\n isTextUIPart,\n isToolOrDynamicToolUIPart,\n} from 'ai';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface DbQueryInput {\n sql: string;\n reasoning?: string;\n}\n\nexport interface SqlWithContext {\n sql: string;\n success: boolean;\n conversationContext: string[];\n}\n\nexport interface BaseContextualExtractorOptions {\n includeFailures?: boolean;\n toolName?: string;\n}\n\nconst contextResolverSchema = z.object({\n question: z\n .string()\n .describe(\n 'A standalone natural language question that the SQL query answers',\n ),\n});\n\n/**\n * Resolves a SQL query with conversation context into a standalone question.\n */\nexport async function resolveContext(params: {\n conversation: string;\n sql: string;\n introspection?: string;\n}): Promise<{ question: string }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `context-resolver-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'context_resolver',\n role: 'You are an expert at understanding conversational context and generating clear, standalone questions from multi-turn conversations.',\n objective:\n 'Transform context-dependent messages into standalone questions that fully capture user intent',\n }),\n ...(params.introspection\n ? [fragment('database_schema', params.introspection)]\n : []),\n fragment('conversation', params.conversation),\n fragment('sql', params.sql),\n fragment(\n 'task',\n dedent`\n Given the conversation above and the SQL query that was executed,\n generate a single, standalone natural language question that:\n 1. Fully captures the user's intent without needing prior context\n 2. Uses natural business language (not SQL terminology)\n 3. Could be asked by someone who hasn't seen the conversation\n 4. Accurately represents what the SQL query answers\n `,\n ),\n fragment(\n 'examples',\n dedent`\n Conversation: \"Show me customers\" \u2192 \"Filter to NY\" \u2192 \"Sort by revenue\"\n SQL: SELECT * FROM customers WHERE region = 'NY' ORDER BY revenue DESC\n Question: \"Show me customers in the NY region sorted by revenue\"\n\n Conversation: \"What were sales last month?\" \u2192 \"Break it down by category\"\n SQL: SELECT category, SUM(amount) FROM sales WHERE date >= '2024-11-01' GROUP BY category\n Question: \"What were sales by category for last month?\"\n `,\n ),\n user('Generate a standalone question for this SQL query.'),\n );\n\n const resolverOutput = structuredOutput({\n model: groq('openai/gpt-oss-20b'),\n context,\n schema: contextResolverSchema,\n });\n\n return resolverOutput.generate();\n}\n\nexport function getMessageText(message: UIMessage): string {\n const textParts = message.parts.filter(isTextUIPart).map((part) => part.text);\n return textParts.join(' ').trim();\n}\n\nexport function formatConversation(messages: string[]): string {\n return messages.map((msg, i) => `[${i + 1}] ${msg}`).join('\\n');\n}\n\n/**\n * Abstract base class for contextual extractors using Template Pattern.\n *\n * The `produce()` method defines the algorithm skeleton:\n * 1. Iterate through messages\n * 2. Call `onUserMessage()` hook for user messages\n * 3. Extract SQL from assistant messages using `getContextSnapshot()` hook\n * 4. Resolve questions using LLM\n *\n * Subclasses implement the hooks to customize context management.\n */\nexport abstract class BaseContextualExtractor extends PairProducer {\n protected context: string[] = [];\n protected results: SqlWithContext[] = [];\n protected messages: UIMessage[];\n protected adapter: Adapter;\n protected options: BaseContextualExtractorOptions;\n\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: BaseContextualExtractorOptions = {},\n ) {\n super();\n this.messages = messages;\n this.adapter = adapter;\n this.options = options;\n }\n\n /**\n * Template method - defines the extraction algorithm skeleton.\n * Subclasses customize behavior via hooks, not by overriding this method.\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n // Reset state for each produce() invocation to prevent race conditions\n // if produce() is called multiple times concurrently\n this.context = [];\n this.results = [];\n\n const { includeFailures = false, toolName = 'db_query' } = this.options;\n\n // Step 1: Extract SQLs with context (calls hooks)\n await this.extractSqlsWithContext(toolName, includeFailures);\n\n if (this.results.length === 0) {\n return;\n }\n\n // Step 2: Get introspection for schema context\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n\n // Step 3: Resolve each SQL's context into a standalone question\n yield* this.resolveQuestions(introspection);\n }\n\n /**\n * Core extraction loop - iterates through messages and calls hooks.\n */\n private async extractSqlsWithContext(\n toolName: string,\n includeFailures: boolean,\n ): Promise<void> {\n for (const message of this.messages) {\n if (message.role === 'user') {\n const text = getMessageText(message);\n if (text) {\n await this.onUserMessage(text);\n }\n continue;\n }\n\n if (message.role === 'assistant') {\n await this.extractFromAssistant(message, toolName, includeFailures);\n }\n }\n }\n\n /**\n * Extract SQL from assistant message parts.\n */\n private async extractFromAssistant(\n message: UIMessage,\n toolName: string,\n includeFailures: boolean,\n ): Promise<void> {\n for (const part of message.parts) {\n if (!isToolOrDynamicToolUIPart(part)) {\n continue;\n }\n\n if (getToolOrDynamicToolName(part) !== toolName) {\n continue;\n }\n\n // Use 'input' property (not 'args') to match useChat structure\n const toolInput = ('input' in part ? part.input : undefined) as\n | DbQueryInput\n | undefined;\n if (!toolInput?.sql) {\n continue;\n }\n\n const success = part.state === 'output-available';\n const failed = part.state === 'output-error';\n\n if (failed && !includeFailures) {\n continue;\n }\n\n // Skip if still streaming or not yet executed\n if (!success && !failed) {\n continue;\n }\n\n const snapshot = this.getContextSnapshot();\n // Skip if no context available\n if (snapshot.length === 0) {\n continue;\n }\n\n this.results.push({\n sql: toolInput.sql,\n success,\n conversationContext: snapshot,\n });\n }\n\n // Add assistant text responses to context (for multi-turn understanding)\n const assistantText = getMessageText(message);\n if (assistantText) {\n this.context.push(`Assistant: ${assistantText}`);\n }\n }\n\n /**\n * Resolve extracted SQL contexts into standalone questions using LLM.\n */\n protected async *resolveQuestions(\n introspection: string,\n ): AsyncGenerator<ExtractedPair[]> {\n for (const item of this.results) {\n const output = await resolveContext({\n conversation: formatConversation(item.conversationContext),\n sql: item.sql,\n introspection,\n });\n\n yield [\n {\n question: output.question,\n sql: item.sql,\n context: item.conversationContext,\n success: item.success,\n },\n ];\n }\n }\n\n /**\n * Hook called when a user message is encountered.\n * Subclasses implement this to decide how to update context.\n */\n protected abstract onUserMessage(text: string): Promise<void>;\n\n /**\n * Hook called when extracting SQL to get the current context snapshot.\n * Subclasses implement this to decide what context to include.\n */\n protected abstract getContextSnapshot(): string[];\n}\n", "import type { LanguageModelUsage } from 'ai';\nimport { mergeWith } from 'lodash-es';\n\nimport {\n type EstimateResult,\n type FragmentEstimate,\n getModelsRegistry,\n} from './estimate.ts';\nimport type { ContextFragment, LazyFragment } from './fragments.ts';\nimport {\n LAZY_ID,\n assistantText,\n isLazyFragment,\n isMessageFragment,\n message,\n} from './fragments.ts';\nimport type { Models } from './models.generated.ts';\nimport {\n type ContextRenderer,\n XmlRenderer,\n} from './renderers/abstract.renderer.ts';\nimport type { SkillPathMapping } from './skills/types.ts';\nimport {\n type BranchData,\n type BranchInfo,\n type ChatData,\n type CheckpointData,\n type CheckpointInfo,\n ContextStore,\n type GraphData,\n type MessageData,\n type StoredChatData,\n} from './store/store.ts';\n\n/**\n * Result of resolving context - ready for AI SDK consumption.\n */\nexport interface ResolveResult {\n /** Rendered non-message fragments for system prompt */\n systemPrompt: string;\n /** Message fragments decoded to AI SDK format */\n messages: unknown[];\n}\n\n/**\n * Options for resolve().\n */\nexport interface ResolveOptions {\n /** Renderer to use for system prompt (defaults to XmlRenderer) */\n renderer: ContextRenderer;\n}\n\n/**\n * Options for creating a ContextEngine.\n */\nexport interface ContextEngineOptions {\n /** Store for persisting fragments (required) */\n store: ContextStore;\n /** Unique identifier for this chat (required) */\n chatId: string;\n /** User who owns this chat (required) */\n userId: string;\n /** Optional initial metadata for the chat (merged with existing if chat exists) */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Metadata about a chat.\n */\nexport interface ChatMeta {\n /** Unique chat identifier */\n id: string;\n /** User who owns this chat */\n userId: string;\n /** When the chat was created */\n createdAt: number;\n /** When the chat was last updated */\n updatedAt: number;\n /** Optional user-provided title */\n title?: string;\n /** Optional custom metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for context inspection.\n */\nexport interface InspectOptions {\n /** Model ID for cost estimation (required) */\n modelId: Models;\n /** Renderer for estimation (required) */\n renderer: ContextRenderer;\n}\n\n/**\n * Result of inspecting context state.\n * JSON-serializable snapshot for debugging.\n */\nexport interface InspectResult {\n /** Token usage and cost estimation */\n estimate: EstimateResult;\n /** Rendered output using the provided renderer */\n rendered: string;\n /** Fragment structure breakdown */\n fragments: {\n /** Non-message fragments (role, hints, etc.) */\n context: ContextFragment[];\n /** Pending messages not yet saved to store */\n pending: ContextFragment[];\n /** Persisted messages from the store */\n persisted: MessageData[];\n };\n /** Conversation graph with branches and checkpoints */\n graph: GraphData;\n /** Inspection metadata */\n meta: {\n chatId: string;\n branch: string;\n timestamp: number;\n };\n}\n\n/**\n * Context engine for managing AI conversation context with graph-based storage.\n *\n * The engine uses a DAG (Directed Acyclic Graph) model for messages:\n * - Messages are immutable nodes with parentId forming the graph\n * - Branches are pointers to head (tip) messages\n * - Checkpoints are pointers to specific messages\n * - History is preserved through branching (rewind creates new branch)\n */\nexport class ContextEngine {\n /** Non-message fragments (role, hints, etc.) - not persisted in graph */\n #fragments: ContextFragment[] = [];\n /** Pending message fragments to be added to graph */\n #pendingMessages: ContextFragment[] = [];\n #store: ContextStore;\n #chatId: string;\n #userId: string;\n #branchName: string;\n #branch: BranchData | null = null;\n #chatData: StoredChatData | null = null;\n #initialized = false;\n /** Initial metadata to merge on first initialization */\n #initialMetadata: Record<string, unknown> | undefined;\n\n constructor(options: ContextEngineOptions) {\n if (!options.chatId) {\n throw new Error('chatId is required');\n }\n if (!options.userId) {\n throw new Error('userId is required');\n }\n this.#store = options.store;\n this.#chatId = options.chatId;\n this.#userId = options.userId;\n this.#branchName = 'main';\n this.#initialMetadata = options.metadata;\n }\n\n /**\n * Initialize the chat and branch if they don't exist.\n */\n async #ensureInitialized(): Promise<void> {\n if (this.#initialized) {\n return;\n }\n\n this.#chatData = await this.#store.upsertChat({\n id: this.#chatId,\n userId: this.#userId,\n });\n\n // Merge initial metadata if provided (handles both new and existing chats)\n if (this.#initialMetadata) {\n this.#chatData = await this.#store.updateChat(this.#chatId, {\n metadata: {\n ...this.#chatData.metadata,\n ...this.#initialMetadata,\n },\n });\n // Clear after use to prevent memory leak\n this.#initialMetadata = undefined;\n }\n\n // \"main\" branch is guaranteed to exist after upsertChat\n this.#branch = (await this.#store.getActiveBranch(this.#chatId))!;\n\n this.#initialized = true;\n }\n\n /**\n * Create a new branch from a specific message.\n * Shared logic between rewind() and btw().\n */\n async #createBranchFrom(\n messageId: string,\n switchTo: boolean,\n ): Promise<BranchInfo> {\n // Generate branch name based on same-prefix count (e.g., main-v2, main-v3)\n const branches = await this.#store.listBranches(this.#chatId);\n const samePrefix = branches.filter(\n (b) =>\n b.name === this.#branchName ||\n b.name.startsWith(`${this.#branchName}-v`),\n );\n const newBranchName = `${this.#branchName}-v${samePrefix.length + 1}`;\n\n // Create new branch pointing to the target message\n const newBranch: BranchData = {\n id: crypto.randomUUID(),\n chatId: this.#chatId,\n name: newBranchName,\n headMessageId: messageId,\n isActive: false,\n createdAt: Date.now(),\n };\n await this.#store.createBranch(newBranch);\n\n if (switchTo) {\n // Switch to the new branch\n await this.#store.setActiveBranch(this.#chatId, newBranch.id);\n this.#branch = { ...newBranch, isActive: true };\n this.#branchName = newBranchName;\n // Clear pending messages (they were for the old branch)\n this.#pendingMessages = [];\n }\n\n // Get message count for branch info\n const chain = await this.#store.getMessageChain(messageId);\n\n return {\n id: newBranch.id,\n name: newBranch.name,\n headMessageId: newBranch.headMessageId,\n isActive: switchTo,\n messageCount: chain.length,\n createdAt: newBranch.createdAt,\n };\n }\n\n /**\n * Get the current chat ID.\n */\n public get chatId(): string {\n return this.#chatId;\n }\n\n /**\n * Get the current branch name.\n */\n public get branch(): string {\n return this.#branchName;\n }\n\n /**\n * Get metadata for the current chat.\n * Returns null if the chat hasn't been initialized yet.\n */\n public get chat(): ChatMeta | null {\n if (!this.#chatData) {\n return null;\n }\n return {\n id: this.#chatData.id,\n userId: this.#chatData.userId,\n createdAt: this.#chatData.createdAt,\n updatedAt: this.#chatData.updatedAt,\n title: this.#chatData.title,\n metadata: this.#chatData.metadata,\n };\n }\n\n /**\n * Add fragments to the context.\n *\n * - Message fragments (user/assistant) are queued for persistence\n * - Non-message fragments (role/hint) are kept in memory for system prompt\n */\n public set(...fragments: ContextFragment[]) {\n for (const fragment of fragments) {\n if (isMessageFragment(fragment)) {\n this.#pendingMessages.push(fragment);\n } else {\n this.#fragments.push(fragment);\n }\n }\n return this;\n }\n\n // Unset a fragment by ID (not implemented yet)\n public unset(fragmentId: string) {\n //\n }\n\n /**\n * Render all fragments using the provided renderer.\n * @internal Use resolve() instead for public API.\n */\n public render(renderer: ContextRenderer) {\n return renderer.render(this.#fragments);\n }\n\n /**\n * Resolve context into AI SDK-ready format.\n *\n * - Initializes chat and branch if needed\n * - Loads message history from the graph (walking parent chain)\n * - Separates context fragments for system prompt\n * - Combines with pending messages\n *\n * @example\n * ```ts\n * const context = new ContextEngine({ store, chatId: 'chat-1', userId: 'user-1' })\n * .set(role('You are helpful'), user('Hello'));\n *\n * const { systemPrompt, messages } = await context.resolve();\n * await generateText({ system: systemPrompt, messages });\n * ```\n */\n public async resolve(options: ResolveOptions): Promise<ResolveResult> {\n await this.#ensureInitialized();\n\n const systemPrompt = options.renderer.render(this.#fragments);\n\n // Get persisted messages from graph\n const messages: unknown[] = [];\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n\n for (const msg of chain) {\n messages.push(message(msg.data as never).codec?.decode());\n }\n }\n\n // Add pending messages (not yet saved)\n for (const fragment of this.#pendingMessages) {\n const decoded = fragment.codec!.decode();\n messages.push(decoded);\n }\n\n return { systemPrompt, messages };\n }\n\n /**\n * Save pending messages to the graph.\n *\n * Each message is added as a node with parentId pointing to the previous message.\n * The branch head is updated to point to the last message.\n *\n * @example\n * ```ts\n * context.set(user('Hello'));\n * // AI responds...\n * context.set(assistant('Hi there!'));\n * await context.save(); // Persist to graph\n * ```\n */\n public async save(): Promise<void> {\n await this.#ensureInitialized();\n\n if (this.#pendingMessages.length === 0) {\n return;\n }\n\n // Resolve any lazy fragments before processing\n for (let i = 0; i < this.#pendingMessages.length; i++) {\n const fragment = this.#pendingMessages[i];\n if (isLazyFragment(fragment)) {\n this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment);\n }\n }\n\n let parentId = this.#branch!.headMessageId;\n const now = Date.now();\n\n // Add each pending message to the graph\n for (const fragment of this.#pendingMessages) {\n const messageData: MessageData = {\n id: fragment.id ?? crypto.randomUUID(),\n chatId: this.#chatId,\n parentId,\n name: fragment.name,\n type: fragment.type,\n data: fragment.codec!.encode(),\n createdAt: now,\n };\n\n await this.#store.addMessage(messageData);\n parentId = messageData.id;\n }\n\n // Update branch head to last message\n await this.#store.updateBranchHead(this.#branch!.id, parentId);\n this.#branch!.headMessageId = parentId;\n\n // Clear pending messages\n this.#pendingMessages = [];\n }\n\n /**\n * Resolve a lazy fragment by finding the appropriate ID.\n */\n async #resolveLazyFragment(fragment: LazyFragment): Promise<ContextFragment> {\n const lazy = fragment[LAZY_ID]!;\n\n if (lazy.type === 'last-assistant') {\n const lastId = await this.#getLastAssistantId();\n return assistantText(lazy.content, { id: lastId ?? crypto.randomUUID() });\n }\n\n throw new Error(`Unknown lazy fragment type: ${lazy.type}`);\n }\n\n /**\n * Find the most recent assistant message ID (pending or persisted).\n */\n async #getLastAssistantId(): Promise<string | undefined> {\n // Check pending messages first (excluding lazy ones)\n for (let i = this.#pendingMessages.length - 1; i >= 0; i--) {\n const msg = this.#pendingMessages[i];\n if (msg.name === 'assistant' && !isLazyFragment(msg)) {\n return msg.id;\n }\n }\n\n // Check persisted messages at branch head\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n for (let i = chain.length - 1; i >= 0; i--) {\n if (chain[i].name === 'assistant') {\n return chain[i].id;\n }\n }\n }\n\n return undefined;\n }\n\n /**\n * Estimate token count and cost for the full context.\n *\n * Includes:\n * - System prompt fragments (role, hints, etc.)\n * - Persisted chat messages (from store)\n * - Pending messages (not yet saved)\n *\n * @param modelId - Model ID (e.g., \"openai:gpt-4o\", \"anthropic:claude-3-5-sonnet\")\n * @param options - Optional settings\n * @returns Estimate result with token counts, costs, and per-fragment breakdown\n */\n public async estimate(\n modelId: Models,\n options: {\n renderer?: ContextRenderer;\n } = {},\n ): Promise<EstimateResult> {\n await this.#ensureInitialized();\n\n const renderer = options.renderer ?? new XmlRenderer();\n const registry = getModelsRegistry();\n await registry.load();\n\n const model = registry.get(modelId);\n if (!model) {\n throw new Error(\n `Model \"${modelId}\" not found. Call load() first or check model ID.`,\n );\n }\n\n const tokenizer = registry.getTokenizer(modelId);\n const fragmentEstimates: FragmentEstimate[] = [];\n\n // 1. Estimate context fragments (system prompt)\n for (const fragment of this.#fragments) {\n const rendered = renderer.render([fragment]);\n const tokens = tokenizer.count(rendered);\n const cost = (tokens / 1_000_000) * model.cost.input;\n fragmentEstimates.push({\n id: fragment.id,\n name: fragment.name,\n tokens,\n cost,\n });\n }\n\n // 2. Estimate persisted messages from store\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n for (const msg of chain) {\n const content = String(msg.data);\n const tokens = tokenizer.count(content);\n const cost = (tokens / 1_000_000) * model.cost.input;\n fragmentEstimates.push({\n name: msg.name,\n id: msg.id,\n tokens,\n cost,\n });\n }\n }\n\n // 3. Estimate pending messages (not yet saved)\n for (const fragment of this.#pendingMessages) {\n const content = String(fragment.data);\n const tokens = tokenizer.count(content);\n const cost = (tokens / 1_000_000) * model.cost.input;\n fragmentEstimates.push({\n name: fragment.name,\n id: fragment.id,\n tokens,\n cost,\n });\n }\n\n // Calculate totals\n const totalTokens = fragmentEstimates.reduce((sum, f) => sum + f.tokens, 0);\n const totalCost = fragmentEstimates.reduce((sum, f) => sum + f.cost, 0);\n\n return {\n model: model.id,\n provider: model.provider,\n tokens: totalTokens,\n cost: totalCost,\n limits: {\n context: model.limit.context,\n output: model.limit.output,\n exceedsContext: totalTokens > model.limit.context,\n },\n fragments: fragmentEstimates,\n };\n }\n\n /**\n * Rewind to a specific message by ID.\n *\n * Creates a new branch from that message, preserving the original branch.\n * The new branch becomes active.\n *\n * @param messageId - The message ID to rewind to\n * @returns The new branch info\n *\n * @example\n * ```ts\n * context.set(user('What is 2 + 2?', { id: 'q1' }));\n * context.set(assistant('The answer is 5.', { id: 'wrong' })); // Oops!\n * await context.save();\n *\n * // Rewind to the question, creates new branch\n * const newBranch = await context.rewind('q1');\n *\n * // Now add correct answer on new branch\n * context.set(assistant('The answer is 4.'));\n * await context.save();\n * ```\n */\n public async rewind(messageId: string): Promise<BranchInfo> {\n await this.#ensureInitialized();\n\n // Verify the message exists\n const message = await this.#store.getMessage(messageId);\n if (!message) {\n throw new Error(`Message \"${messageId}\" not found`);\n }\n if (message.chatId !== this.#chatId) {\n throw new Error(`Message \"${messageId}\" belongs to a different chat`);\n }\n\n return this.#createBranchFrom(messageId, true);\n }\n\n /**\n * Create a checkpoint at the current position.\n *\n * A checkpoint is a named pointer to the current branch head.\n * Use restore() to return to this point later.\n *\n * @param name - Name for the checkpoint\n * @returns The checkpoint info\n *\n * @example\n * ```ts\n * context.set(user('I want to learn a new skill.'));\n * context.set(assistant('Would you like coding or cooking?'));\n * await context.save();\n *\n * // Save checkpoint before user's choice\n * const cp = await context.checkpoint('before-choice');\n * ```\n */\n public async checkpoint(name: string): Promise<CheckpointInfo> {\n await this.#ensureInitialized();\n\n if (!this.#branch?.headMessageId) {\n throw new Error('Cannot create checkpoint: no messages in conversation');\n }\n\n const checkpoint: CheckpointData = {\n id: crypto.randomUUID(),\n chatId: this.#chatId,\n name,\n messageId: this.#branch.headMessageId,\n createdAt: Date.now(),\n };\n\n await this.#store.createCheckpoint(checkpoint);\n\n return {\n id: checkpoint.id,\n name: checkpoint.name,\n messageId: checkpoint.messageId,\n createdAt: checkpoint.createdAt,\n };\n }\n\n /**\n * Restore to a checkpoint by creating a new branch from that point.\n *\n * @param name - Name of the checkpoint to restore\n * @returns The new branch info\n *\n * @example\n * ```ts\n * // User chose cooking, but wants to try coding path\n * await context.restore('before-choice');\n *\n * context.set(user('I want to learn coding.'));\n * context.set(assistant('Python is a great starting language!'));\n * await context.save();\n * ```\n */\n public async restore(name: string): Promise<BranchInfo> {\n await this.#ensureInitialized();\n\n const checkpoint = await this.#store.getCheckpoint(this.#chatId, name);\n if (!checkpoint) {\n throw new Error(\n `Checkpoint \"${name}\" not found in chat \"${this.#chatId}\"`,\n );\n }\n\n // Rewind to the checkpoint's message\n return this.rewind(checkpoint.messageId);\n }\n\n /**\n * Switch to a different branch by name.\n *\n * @param name - Branch name to switch to\n *\n * @example\n * ```ts\n * // List branches (via store)\n * const branches = await store.listBranches(context.chatId);\n * console.log(branches); // [{name: 'main', ...}, {name: 'main-v2', ...}]\n *\n * // Switch to original branch\n * await context.switchBranch('main');\n * ```\n */\n public async switchBranch(name: string): Promise<void> {\n await this.#ensureInitialized();\n\n const branch = await this.#store.getBranch(this.#chatId, name);\n if (!branch) {\n throw new Error(`Branch \"${name}\" not found in chat \"${this.#chatId}\"`);\n }\n\n await this.#store.setActiveBranch(this.#chatId, branch.id);\n this.#branch = { ...branch, isActive: true };\n this.#branchName = name;\n\n // Clear pending messages (they were for the old branch)\n this.#pendingMessages = [];\n }\n\n /**\n * Create a parallel branch from the current position (\"by the way\").\n *\n * Use this when you want to fork the conversation without leaving\n * the current branch. Common use case: user wants to ask another\n * question while waiting for the model to respond.\n *\n * Unlike rewind(), this method:\n * - Uses the current HEAD (no messageId needed)\n * - Does NOT switch to the new branch\n * - Keeps pending messages intact\n *\n * @returns The new branch info (does not switch to it)\n * @throws Error if no messages exist in the conversation\n *\n * @example\n * ```ts\n * // User asked a question, model is generating...\n * context.set(user('What is the weather?'));\n * await context.save();\n *\n * // User wants to ask something else without waiting\n * const newBranch = await context.btw();\n * // newBranch = { name: 'main-v2', ... }\n *\n * // Later, switch to the new branch and add the question\n * await context.switchBranch(newBranch.name);\n * context.set(user('Also, what time is it?'));\n * await context.save();\n * ```\n */\n public async btw(): Promise<BranchInfo> {\n await this.#ensureInitialized();\n\n if (!this.#branch?.headMessageId) {\n throw new Error('Cannot create btw branch: no messages in conversation');\n }\n\n return this.#createBranchFrom(this.#branch.headMessageId, false);\n }\n\n /**\n * Update metadata for the current chat.\n *\n * @param updates - Partial metadata to merge (title, metadata)\n *\n * @example\n * ```ts\n * await context.updateChat({\n * title: 'Coding Help Session',\n * metadata: { tags: ['python', 'debugging'] }\n * });\n * ```\n */\n public async updateChat(\n updates: Partial<Pick<ChatMeta, 'title' | 'metadata'>>,\n ): Promise<void> {\n await this.#ensureInitialized();\n\n const storeUpdates: Partial<Pick<ChatData, 'title' | 'metadata'>> = {};\n\n if (updates.title !== undefined) {\n storeUpdates.title = updates.title;\n }\n if (updates.metadata !== undefined) {\n // Merge with existing metadata\n storeUpdates.metadata = {\n ...this.#chatData?.metadata,\n ...updates.metadata,\n };\n }\n\n this.#chatData = await this.#store.updateChat(this.#chatId, storeUpdates);\n }\n\n /**\n * Track token usage for the current chat.\n * Accumulates usage metrics in chat.metadata.usage.\n *\n * @param usage - Token usage from AI SDK (LanguageModelUsage)\n *\n * @example\n * ```ts\n * // In onFinish callback\n * const usage = await result.totalUsage;\n * await context.trackUsage(usage);\n * ```\n */\n public async trackUsage(usage: LanguageModelUsage): Promise<void> {\n await this.#ensureInitialized();\n\n // Read fresh data from store to prevent race conditions with concurrent calls\n const freshChatData = await this.#store.getChat(this.#chatId);\n\n // Get current usage from metadata (if any)\n const currentUsage = (freshChatData?.metadata?.usage ??\n {}) as Partial<LanguageModelUsage>;\n\n // Accumulate usage - recursively add all numeric fields\n const updatedUsage = mergeWith({}, currentUsage, usage, (a, b) =>\n typeof a === 'number' || typeof b === 'number'\n ? (a ?? 0) + (b ?? 0)\n : undefined,\n ) as LanguageModelUsage;\n\n // Update chat metadata with accumulated usage\n this.#chatData = await this.#store.updateChat(this.#chatId, {\n metadata: {\n ...freshChatData?.metadata,\n usage: updatedUsage,\n },\n });\n }\n\n /**\n * Consolidate context fragments (no-op for now).\n *\n * This is a placeholder for future functionality that merges context fragments\n * using specific rules. Currently, it does nothing.\n *\n * @experimental\n */\n public consolidate(): void {\n return void 0;\n }\n\n /**\n * Extract skill path mappings from available_skills fragments.\n * Returns array of { host, sandbox } for mounting in sandbox filesystem.\n *\n * Reads the original `paths` configuration stored in fragment metadata\n * by the skills() fragment helper.\n *\n * @example\n * ```ts\n * const context = new ContextEngine({ store, chatId, userId })\n * .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));\n *\n * const mounts = context.getSkillMounts();\n * // [{ host: './skills', sandbox: '/skills' }]\n * ```\n */\n public getSkillMounts(): SkillPathMapping[] {\n const mounts: SkillPathMapping[] = [];\n\n for (const fragment of this.#fragments) {\n if (\n fragment.name === 'available_skills' &&\n fragment.metadata &&\n Array.isArray(fragment.metadata.paths)\n ) {\n for (const mapping of fragment.metadata.paths) {\n if (\n typeof mapping === 'object' &&\n mapping !== null &&\n typeof mapping.host === 'string' &&\n typeof mapping.sandbox === 'string'\n ) {\n mounts.push({ host: mapping.host, sandbox: mapping.sandbox });\n }\n }\n }\n }\n\n return mounts;\n }\n\n /**\n * Inspect the full context state for debugging.\n * Returns a JSON-serializable object with context information.\n *\n * @param options - Inspection options (modelId and renderer required)\n * @returns Complete inspection data including estimates, rendered output, fragments, and graph\n *\n * @example\n * ```ts\n * const inspection = await context.inspect({\n * modelId: 'openai:gpt-4o',\n * renderer: new XmlRenderer(),\n * });\n * console.log(JSON.stringify(inspection, null, 2));\n *\n * // Or write to file for analysis\n * await fs.writeFile('context-debug.json', JSON.stringify(inspection, null, 2));\n * ```\n */\n public async inspect(options: InspectOptions): Promise<InspectResult> {\n await this.#ensureInitialized();\n\n const { renderer } = options;\n\n // Get token/cost estimation\n const estimateResult = await this.estimate(options.modelId, { renderer });\n\n // Render using provided renderer\n const rendered = renderer.render(this.#fragments);\n\n // Get persisted messages from store\n const persistedMessages: MessageData[] = [];\n if (this.#branch?.headMessageId) {\n const chain = await this.#store.getMessageChain(\n this.#branch.headMessageId,\n );\n persistedMessages.push(...chain);\n }\n\n // Get conversation graph\n const graph = await this.#store.getGraph(this.#chatId);\n\n return {\n estimate: estimateResult,\n rendered,\n fragments: {\n context: [...this.#fragments],\n pending: [...this.#pendingMessages],\n persisted: persistedMessages,\n },\n graph,\n meta: {\n chatId: this.#chatId,\n branch: this.#branchName,\n timestamp: Date.now(),\n },\n };\n }\n}\n", "import { encode } from 'gpt-tokenizer';\n\nimport type { ContextFragment } from './fragments.ts';\nimport type { Models } from './models.generated.ts';\nimport type { ContextRenderer } from './renderers/abstract.renderer.ts';\n\n/**\n * Cost information for a model (prices per 1M tokens)\n */\nexport interface ModelCost {\n input: number;\n output: number;\n cache_read?: number;\n cache_write?: number;\n reasoning?: number;\n}\n\n/**\n * Model information from models.dev\n */\nexport interface ModelInfo {\n id: string;\n name: string;\n family: string;\n cost: ModelCost;\n limit: {\n context: number;\n output: number;\n };\n provider: string;\n}\n\n/**\n * Estimate for a single fragment\n */\nexport interface FragmentEstimate {\n name: string;\n id?: string;\n tokens: number;\n cost: number;\n}\n\n/**\n * Estimate result returned by the estimate function\n */\nexport interface EstimateResult {\n model: string;\n provider: string;\n tokens: number;\n cost: number;\n limits: {\n context: number;\n output: number;\n exceedsContext: boolean;\n };\n fragments: FragmentEstimate[];\n}\n\n/**\n * Tokenizer interface for counting tokens\n */\nexport interface Tokenizer {\n encode(text: string): number[];\n count(text: string): number;\n}\n\n/**\n * Default tokenizer using gpt-tokenizer\n * Works reasonably well for most models (~5-10% variance)\n */\nexport const defaultTokenizer: Tokenizer = {\n encode(text: string): number[] {\n return encode(text);\n },\n count(text: string): number {\n return encode(text).length;\n },\n};\n\ntype ModelsDevResponse = Record<\n string,\n {\n id: string;\n name: string;\n models: Record<\n string,\n {\n id: string;\n name: string;\n family: string;\n cost: ModelCost;\n limit: { context: number; output: number };\n }\n >;\n }\n>;\n\n/**\n * Registry for AI model information from models.dev\n * Caches data and provides lookup by model ID\n */\nexport class ModelsRegistry {\n #cache: Map<string, ModelInfo> = new Map();\n #loaded = false;\n #tokenizers: Map<string, Tokenizer> = new Map();\n #defaultTokenizer: Tokenizer = defaultTokenizer;\n\n /**\n * Load models data from models.dev API\n */\n async load(): Promise<void> {\n if (this.#loaded) return;\n\n const response = await fetch('https://models.dev/api.json');\n if (!response.ok) {\n throw new Error(`Failed to fetch models: ${response.statusText}`);\n }\n\n const data = (await response.json()) as ModelsDevResponse;\n\n for (const [providerId, provider] of Object.entries(data)) {\n for (const [modelId, model] of Object.entries(provider.models)) {\n const info: ModelInfo = {\n id: model.id,\n name: model.name,\n family: model.family,\n cost: model.cost,\n limit: model.limit,\n provider: providerId,\n };\n\n // Store by full ID (provider:model)\n this.#cache.set(`${providerId}:${modelId}`, info);\n }\n }\n\n this.#loaded = true;\n }\n\n /**\n * Get model info by ID\n * @param modelId - Model ID (e.g., \"openai:gpt-4o\")\n */\n get(modelId: string): ModelInfo | undefined {\n return this.#cache.get(modelId);\n }\n\n /**\n * Check if a model exists in the registry\n */\n has(modelId: string): boolean {\n return this.#cache.has(modelId);\n }\n\n /**\n * List all available model IDs\n */\n list(): string[] {\n return [...this.#cache.keys()];\n }\n\n /**\n * Register a custom tokenizer for specific model families\n * @param family - Model family name (e.g., \"llama\", \"claude\")\n * @param tokenizer - Tokenizer implementation\n */\n registerTokenizer(family: string, tokenizer: Tokenizer): void {\n this.#tokenizers.set(family, tokenizer);\n }\n\n /**\n * Set the default tokenizer used when no family-specific tokenizer is registered\n */\n setDefaultTokenizer(tokenizer: Tokenizer): void {\n this.#defaultTokenizer = tokenizer;\n }\n\n /**\n * Get the appropriate tokenizer for a model\n */\n getTokenizer(modelId: string): Tokenizer {\n const model = this.get(modelId);\n if (model) {\n const familyTokenizer = this.#tokenizers.get(model.family);\n if (familyTokenizer) {\n return familyTokenizer;\n }\n }\n return this.#defaultTokenizer;\n }\n\n /**\n * Estimate token count and cost for given text and model\n * @param modelId - Model ID to use for pricing (e.g., \"openai:gpt-4o\")\n * @param input - Input text (prompt)\n */\n estimate(modelId: Models, input: string): EstimateResult {\n const model = this.get(modelId);\n if (!model) {\n throw new Error(\n `Model \"${modelId}\" not found. Call load() first or check model ID.`,\n );\n }\n\n const tokenizer = this.getTokenizer(modelId);\n const tokens = tokenizer.count(input);\n const cost = (tokens / 1_000_000) * model.cost.input;\n\n return {\n model: model.id,\n provider: model.provider,\n tokens,\n cost,\n limits: {\n context: model.limit.context,\n output: model.limit.output,\n exceedsContext: tokens > model.limit.context,\n },\n fragments: [],\n };\n }\n}\n\n// Singleton instance for convenience\nlet _registry: ModelsRegistry | null = null;\n\n/**\n * Get the shared ModelsRegistry instance\n */\nexport function getModelsRegistry(): ModelsRegistry {\n if (!_registry) {\n _registry = new ModelsRegistry();\n }\n return _registry;\n}\n\n/**\n * Convenience function to estimate cost for a model\n * Automatically loads the registry if not already loaded\n *\n * @param modelId - Model ID (e.g., \"openai:gpt-4o\", \"anthropic:claude-3-5-sonnet\")\n * @param renderer - Renderer to use for converting fragments to text\n * @param fragments - Context fragments to estimate\n */\nexport async function estimate(\n modelId: Models,\n renderer: ContextRenderer,\n ...fragments: ContextFragment[]\n): Promise<EstimateResult> {\n const registry = getModelsRegistry();\n await registry.load();\n\n // Calculate total (all fragments rendered together)\n const input = renderer.render(fragments);\n const model = registry.get(modelId);\n if (!model) {\n throw new Error(\n `Model \"${modelId}\" not found. Call load() first or check model ID.`,\n );\n }\n\n const tokenizer = registry.getTokenizer(modelId);\n const totalTokens = tokenizer.count(input);\n const totalCost = (totalTokens / 1_000_000) * model.cost.input;\n\n // Calculate per-fragment estimates\n const fragmentEstimates: FragmentEstimate[] = fragments.map((fragment) => {\n const rendered = renderer.render([fragment]);\n const tokens = tokenizer.count(rendered);\n const cost = (tokens / 1_000_000) * model.cost.input;\n return {\n id: fragment.id,\n name: fragment.name,\n tokens,\n cost,\n };\n });\n\n return {\n model: model.id,\n provider: model.provider,\n tokens: totalTokens,\n cost: totalCost,\n limits: {\n context: model.limit.context,\n output: model.limit.output,\n exceedsContext: totalTokens > model.limit.context,\n },\n fragments: fragmentEstimates,\n };\n}\n", "import { type UIMessage, generateId } from 'ai';\n\nimport type { FragmentCodec } from './codec.ts';\n\n/**\n * Fragment type identifier.\n * - 'fragment': Regular context fragment (default)\n * - 'message': Conversation message (user/assistant)\n */\nexport type FragmentType = 'fragment' | 'message';\n\n/**\n * A context fragment containing a name and associated data.\n */\nexport interface ContextFragment<T extends FragmentData = FragmentData> {\n /**\n * Unique identifier for this fragment.\n * Auto-generated for user/assistant messages, optional for other fragments.\n */\n id?: string;\n name: string;\n data: T;\n /**\n * Fragment type for categorization.\n * Messages use 'message' type and are handled separately during resolve().\n */\n type?: FragmentType;\n /**\n * When true, this fragment will be persisted to the store on save().\n */\n persist?: boolean;\n /**\n * Codec for encoding/decoding this fragment.\n * Used by resolve() to convert to AI SDK format.\n */\n codec?: FragmentCodec;\n /**\n * Optional metadata for internal tracking.\n * Not rendered to prompt, used for operational purposes like path remapping.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Fragment data can be a primitive, array, object, or nested fragment.\n */\nexport type FragmentData =\n | string\n | number\n | null\n | undefined\n | boolean\n | ContextFragment\n | FragmentData[]\n | { [key: string]: FragmentData };\n\n/**\n * Type guard to check if data is a ContextFragment.\n */\nexport function isFragment(data: unknown): data is ContextFragment {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'name' in data &&\n 'data' in data &&\n typeof (data as ContextFragment).name === 'string'\n );\n}\n\n/**\n * A plain object with string keys and FragmentData values.\n */\nexport type FragmentObject = Record<string, FragmentData>;\n\n/**\n * Type guard to check if data is a plain object (not array, not fragment, not primitive).\n */\nexport function isFragmentObject(data: unknown): data is FragmentObject {\n return (\n typeof data === 'object' &&\n data !== null &&\n !Array.isArray(data) &&\n !isFragment(data)\n );\n}\n\n/**\n * Type guard to check if a fragment is a message fragment.\n */\nexport function isMessageFragment(fragment: ContextFragment): boolean {\n return fragment.type === 'message';\n}\n\nexport function fragment(\n name: string,\n ...children: FragmentData[]\n): ContextFragment {\n return {\n name,\n data: children,\n };\n}\n\n/**\n * Create a user message fragment.\n * Message fragments are separated from regular fragments during resolve().\n *\n * @param content - The message content\n * @param options - Optional settings (id)\n *\n * @example\n * ```ts\n * context.set(user('Hello')); // Auto-generated ID\n * context.set(user('Hello', { id: 'msg-1' })); // Custom ID\n * ```\n */\nexport function user(content: string | UIMessage): ContextFragment {\n const message =\n typeof content === 'string'\n ? {\n id: generateId(),\n role: 'user',\n parts: [{ type: 'text', text: content }],\n }\n : content;\n return {\n id: message.id,\n name: 'user',\n data: 'content',\n type: 'message',\n persist: true,\n codec: {\n decode() {\n return message;\n },\n encode() {\n return message;\n },\n },\n };\n}\n\n/**\n * Create an assistant message fragment.\n * Message fragments are separated from regular fragments during resolve().\n *\n * @param message - The message content\n * @param options - Optional settings (id)\n *\n * @example\n * ```ts\n * context.set(assistant('Hi there!')); // Auto-generated ID\n * context.set(assistant('Hi there!', { id: 'resp-1' })); // Custom ID\n * ```\n */\nexport function assistant(message: UIMessage): ContextFragment {\n return {\n id: message.id,\n name: 'assistant',\n data: 'content',\n type: 'message',\n persist: true,\n codec: {\n decode() {\n return message;\n },\n encode() {\n return message;\n },\n },\n };\n}\nexport function message(content: string | UIMessage): ContextFragment {\n const message =\n typeof content === 'string'\n ? {\n id: generateId(),\n role: 'user' as const,\n parts: [{ type: 'text', text: content }],\n }\n : content;\n return {\n id: message.id,\n name: message.role,\n data: 'content',\n type: 'message',\n persist: true,\n codec: {\n decode() {\n return message;\n },\n encode() {\n return message;\n },\n },\n };\n}\n\n/**\n * Create an assistant message fragment from text content.\n * Convenience wrapper that creates a UIMessage internally.\n *\n * @param content - The message text content\n * @param options - Optional settings (id)\n *\n * @example\n * ```ts\n * context.set(assistantText('Hi there!')); // Auto-generated ID\n * context.set(assistantText('Hi there!', { id: 'resp-1' })); // Custom ID\n * ```\n */\nexport function assistantText(\n content: string,\n options?: { id?: string },\n): ContextFragment {\n const id = options?.id ?? crypto.randomUUID();\n return assistant({\n id,\n role: 'assistant',\n parts: [{ type: 'text', text: content }],\n });\n}\n\n/**\n * Symbol to mark fragments for lazy ID resolution.\n * @internal\n */\nexport const LAZY_ID = Symbol('lazy-id');\n\n/**\n * Lazy fragment configuration for ID resolution.\n */\nexport interface LazyConfig {\n type: 'last-assistant';\n content: string;\n}\n\n/**\n * Lazy fragment that gets its ID resolved during save().\n */\nexport interface LazyFragment extends ContextFragment {\n [LAZY_ID]?: LazyConfig;\n}\n\n/**\n * Check if a fragment needs lazy ID resolution.\n */\nexport function isLazyFragment(\n fragment: ContextFragment,\n): fragment is LazyFragment {\n return LAZY_ID in fragment;\n}\n\n/**\n * Create an assistant message fragment that uses the last assistant's ID.\n *\n * - If a pending/persisted assistant message exists, updates it\n * - If none exists, creates a new assistant message\n *\n * Useful for self-correction flows where retries should update\n * the same message instead of creating duplicates.\n *\n * @example\n * ```ts\n * // In guardrail retry loop:\n * context.set(lastAssistantMessage(correctedContent));\n * await context.save(); // ID resolved here\n * ```\n */\nexport function lastAssistantMessage(content: string): ContextFragment {\n return {\n name: 'assistant',\n type: 'message',\n persist: true,\n data: 'content',\n [LAZY_ID]: {\n type: 'last-assistant',\n content,\n },\n } as LazyFragment;\n}\n", "import pluralize from 'pluralize';\nimport { titlecase } from 'stringcase';\n\nimport {\n type ContextFragment,\n type FragmentData,\n type FragmentObject,\n isFragment,\n isFragmentObject,\n} from '../fragments.ts';\n\n/**\n * Render context passed through the template method.\n */\nexport interface RenderContext {\n depth: number;\n path: string[];\n}\n\n/**\n * Options for renderers.\n */\nexport interface RendererOptions {\n /**\n * When true, fragments with the same name are grouped under a pluralized parent tag.\n * e.g., multiple <hint> become <hints><hint>...</hint><hint>...</hint></hints>\n */\n groupFragments?: boolean;\n}\n\n/**\n * Base renderer implementing the Template Method pattern.\n * Subclasses implement the specific formatting hooks.\n */\nexport abstract class ContextRenderer {\n protected options: RendererOptions;\n\n constructor(options: RendererOptions = {}) {\n this.options = options;\n }\n\n abstract render(fragments: ContextFragment[]): string;\n\n /**\n * Check if data is a primitive (string, number, boolean).\n */\n protected isPrimitive(data: FragmentData): data is string | number | boolean {\n return (\n typeof data === 'string' ||\n typeof data === 'number' ||\n typeof data === 'boolean'\n );\n }\n\n /**\n * Group fragments by name for groupFragments option.\n */\n protected groupByName(\n fragments: ContextFragment[],\n ): Map<string, ContextFragment[]> {\n const groups = new Map<string, ContextFragment[]>();\n for (const fragment of fragments) {\n const existing = groups.get(fragment.name) ?? [];\n existing.push(fragment);\n groups.set(fragment.name, existing);\n }\n return groups;\n }\n\n /**\n * Remove null/undefined from fragments and fragment data recursively.\n * This protects renderers from nullish values and ensures they are ignored\n * consistently across all output formats.\n */\n protected sanitizeFragments(fragments: ContextFragment[]): ContextFragment[] {\n const sanitized: ContextFragment[] = [];\n for (const fragment of fragments) {\n const cleaned = this.sanitizeFragment(fragment, new WeakSet<object>());\n if (cleaned) {\n sanitized.push(cleaned);\n }\n }\n return sanitized;\n }\n\n protected sanitizeFragment(\n fragment: ContextFragment,\n seen: WeakSet<object>,\n ): ContextFragment | null {\n const data = this.sanitizeData(fragment.data, seen);\n if (data == null) {\n return null;\n }\n return {\n ...fragment,\n data,\n };\n }\n\n protected sanitizeData(\n data: FragmentData,\n seen: WeakSet<object>,\n ): FragmentData | undefined {\n if (data == null) {\n return undefined;\n }\n\n if (isFragment(data)) {\n return this.sanitizeFragment(data, seen) ?? undefined;\n }\n\n if (Array.isArray(data)) {\n if (seen.has(data)) {\n return undefined;\n }\n seen.add(data);\n\n const cleaned: FragmentData[] = [];\n for (const item of data) {\n const sanitizedItem = this.sanitizeData(item, seen);\n if (sanitizedItem != null) {\n cleaned.push(sanitizedItem);\n }\n }\n return cleaned;\n }\n\n if (isFragmentObject(data)) {\n if (seen.has(data)) {\n return undefined;\n }\n seen.add(data);\n\n const cleaned: FragmentObject = {};\n for (const [key, value] of Object.entries(data)) {\n const sanitizedValue = this.sanitizeData(value, seen);\n if (sanitizedValue != null) {\n cleaned[key] = sanitizedValue;\n }\n }\n return cleaned;\n }\n\n return data;\n }\n\n /**\n * Template method - dispatches value to appropriate handler.\n */\n protected renderValue(\n key: string,\n value: unknown,\n ctx: RenderContext,\n ): string {\n if (value == null) {\n return '';\n }\n if (isFragment(value)) {\n return this.renderFragment(value, ctx);\n }\n if (Array.isArray(value)) {\n return this.renderArray(key, value, ctx);\n }\n if (isFragmentObject(value)) {\n return this.renderObject(key, value, ctx);\n }\n return this.renderPrimitive(key, String(value), ctx);\n }\n\n /**\n * Render a nested fragment - subclasses implement this.\n */\n protected abstract renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string;\n\n /**\n * Render all entries of an object.\n */\n protected renderEntries(data: FragmentObject, ctx: RenderContext): string[] {\n return Object.entries(data)\n .map(([key, value]) => this.renderValue(key, value, ctx))\n .filter(Boolean);\n }\n\n // Hooks - subclasses implement these\n protected abstract renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string;\n protected abstract renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string;\n protected abstract renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string;\n}\n\n/**\n * Renders context fragments as XML.\n */\nexport class XmlRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n const sanitized = this.sanitizeFragments(fragments);\n return sanitized\n .map((f) => this.#renderTopLevel(f))\n .filter(Boolean)\n .join('\\n');\n }\n\n #renderTopLevel(fragment: ContextFragment): string {\n if (this.isPrimitive(fragment.data)) {\n return this.#leafRoot(fragment.name, String(fragment.data));\n }\n if (Array.isArray(fragment.data)) {\n return this.#renderArray(fragment.name, fragment.data, 0);\n }\n if (isFragment(fragment.data)) {\n const child = this.renderFragment(fragment.data, { depth: 1, path: [] });\n return this.#wrap(fragment.name, [child]);\n }\n if (isFragmentObject(fragment.data)) {\n return this.#wrap(\n fragment.name,\n this.renderEntries(fragment.data, { depth: 1, path: [] }),\n );\n }\n return '';\n }\n\n #renderArray(name: string, items: FragmentData[], depth: number): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter((item) => !isFragment(item));\n\n const children: string[] = [];\n\n // Render non-fragment items\n for (const item of nonFragmentItems) {\n if (item != null) {\n if (isFragmentObject(item)) {\n // Recursively render plain objects\n children.push(\n this.#wrapIndented(\n pluralize.singular(name),\n this.renderEntries(item, { depth: depth + 2, path: [] }),\n depth + 1,\n ),\n );\n } else {\n children.push(\n this.#leaf(pluralize.singular(name), String(item), depth + 1),\n );\n }\n }\n }\n\n // Render fragment items (possibly grouped)\n if (this.options.groupFragments && fragmentItems.length > 0) {\n const groups = this.groupByName(fragmentItems);\n for (const [groupName, groupFragments] of groups) {\n const groupChildren = groupFragments.map((frag) =>\n this.renderFragment(frag, { depth: depth + 2, path: [] }),\n );\n const pluralName = pluralize.plural(groupName);\n children.push(this.#wrapIndented(pluralName, groupChildren, depth + 1));\n }\n } else {\n for (const frag of fragmentItems) {\n children.push(\n this.renderFragment(frag, { depth: depth + 1, path: [] }),\n );\n }\n }\n\n return this.#wrap(name, children);\n }\n\n #leafRoot(tag: string, value: string): string {\n const safe = this.#escape(value);\n if (safe.includes('\\n')) {\n return `<${tag}>\\n${this.#indent(safe, 2)}\\n</${tag}>`;\n }\n return `<${tag}>${safe}</${tag}>`;\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n if (this.isPrimitive(data)) {\n return this.#leaf(name, String(data), ctx.depth);\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, { ...ctx, depth: ctx.depth + 1 });\n return this.#wrapIndented(name, [child], ctx.depth);\n }\n if (Array.isArray(data)) {\n return this.#renderArrayIndented(name, data, ctx.depth);\n }\n if (isFragmentObject(data)) {\n const children = this.renderEntries(data, {\n ...ctx,\n depth: ctx.depth + 1,\n });\n return this.#wrapIndented(name, children, ctx.depth);\n }\n return '';\n }\n\n #renderArrayIndented(\n name: string,\n items: FragmentData[],\n depth: number,\n ): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter((item) => !isFragment(item));\n\n const children: string[] = [];\n\n // Render non-fragment items\n for (const item of nonFragmentItems) {\n if (item != null) {\n if (isFragmentObject(item)) {\n // Recursively render plain objects\n children.push(\n this.#wrapIndented(\n pluralize.singular(name),\n this.renderEntries(item, { depth: depth + 2, path: [] }),\n depth + 1,\n ),\n );\n } else {\n children.push(\n this.#leaf(pluralize.singular(name), String(item), depth + 1),\n );\n }\n }\n }\n\n // Render fragment items (possibly grouped)\n if (this.options.groupFragments && fragmentItems.length > 0) {\n const groups = this.groupByName(fragmentItems);\n for (const [groupName, groupFragments] of groups) {\n const groupChildren = groupFragments.map((frag) =>\n this.renderFragment(frag, { depth: depth + 2, path: [] }),\n );\n const pluralName = pluralize.plural(groupName);\n children.push(this.#wrapIndented(pluralName, groupChildren, depth + 1));\n }\n } else {\n for (const frag of fragmentItems) {\n children.push(\n this.renderFragment(frag, { depth: depth + 1, path: [] }),\n );\n }\n }\n\n return this.#wrapIndented(name, children, depth);\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n return this.#leaf(key, value, ctx.depth);\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n if (!items.length) {\n return '';\n }\n const itemTag = pluralize.singular(key);\n const children = items\n .filter((item) => item != null)\n .map((item) => {\n // Check for ContextFragment first (has name + data properties)\n if (isFragment(item)) {\n return this.renderFragment(item, { ...ctx, depth: ctx.depth + 1 });\n }\n // Then check for plain objects\n if (isFragmentObject(item)) {\n return this.#wrapIndented(\n itemTag,\n this.renderEntries(item, { ...ctx, depth: ctx.depth + 2 }),\n ctx.depth + 1,\n );\n }\n // Primitives\n return this.#leaf(itemTag, String(item), ctx.depth + 1);\n });\n return this.#wrapIndented(key, children, ctx.depth);\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const children = this.renderEntries(obj, { ...ctx, depth: ctx.depth + 1 });\n return this.#wrapIndented(key, children, ctx.depth);\n }\n\n #escape(value: string): string {\n if (value == null) {\n return '';\n }\n return value\n .replaceAll(/&/g, '&')\n .replaceAll(/</g, '<')\n .replaceAll(/>/g, '>')\n .replaceAll(/\"/g, '"')\n .replaceAll(/'/g, ''');\n }\n\n #indent(text: string, spaces: number): string {\n if (!text.trim()) {\n return '';\n }\n const padding = ' '.repeat(spaces);\n return text\n .split('\\n')\n .map((line) => (line.length ? padding + line : padding))\n .join('\\n');\n }\n\n #leaf(tag: string, value: string, depth: number): string {\n const safe = this.#escape(value);\n const pad = ' '.repeat(depth);\n if (safe.includes('\\n')) {\n return `${pad}<${tag}>\\n${this.#indent(safe, (depth + 1) * 2)}\\n${pad}</${tag}>`;\n }\n return `${pad}<${tag}>${safe}</${tag}>`;\n }\n\n #wrap(tag: string, children: string[]): string {\n const content = children.filter(Boolean).join('\\n');\n if (!content) {\n return '';\n }\n return `<${tag}>\\n${content}\\n</${tag}>`;\n }\n\n #wrapIndented(tag: string, children: string[], depth: number): string {\n const content = children.filter(Boolean).join('\\n');\n if (!content) {\n return '';\n }\n const pad = ' '.repeat(depth);\n return `${pad}<${tag}>\\n${content}\\n${pad}</${tag}>`;\n }\n}\n\n/**\n * Renders context fragments as Markdown.\n */\nexport class MarkdownRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n return this.sanitizeFragments(fragments)\n .map((f) => {\n const title = `## ${titlecase(f.name)}`;\n if (this.isPrimitive(f.data)) {\n return `${title}\\n${String(f.data)}`;\n }\n if (Array.isArray(f.data)) {\n return `${title}\\n${this.#renderArray(f.data, 0)}`;\n }\n if (isFragment(f.data)) {\n return `${title}\\n${this.renderFragment(f.data, { depth: 0, path: [] })}`;\n }\n if (isFragmentObject(f.data)) {\n return `${title}\\n${this.renderEntries(f.data, { depth: 0, path: [] }).join('\\n')}`;\n }\n return `${title}\\n`;\n })\n .join('\\n\\n');\n }\n\n #renderArray(items: FragmentData[], depth: number): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter((item) => !isFragment(item));\n\n const lines: string[] = [];\n\n // Render non-fragment items\n for (const item of nonFragmentItems) {\n if (item != null) {\n lines.push(`${this.#pad(depth)}- ${String(item)}`);\n }\n }\n\n // Render fragment items (possibly grouped)\n if (this.options.groupFragments && fragmentItems.length > 0) {\n const groups = this.groupByName(fragmentItems);\n for (const [groupName, groupFragments] of groups) {\n const pluralName = pluralize.plural(groupName);\n lines.push(`${this.#pad(depth)}- **${titlecase(pluralName)}**:`);\n for (const frag of groupFragments) {\n lines.push(this.renderFragment(frag, { depth: depth + 1, path: [] }));\n }\n }\n } else {\n for (const frag of fragmentItems) {\n lines.push(this.renderFragment(frag, { depth, path: [] }));\n }\n }\n\n return lines.join('\\n');\n }\n\n #pad(depth: number): string {\n return ' '.repeat(depth);\n }\n\n #leaf(key: string, value: string, depth: number): string {\n return `${this.#pad(depth)}- **${key}**: ${value}`;\n }\n\n #arrayItem(item: unknown, depth: number): string {\n if (isFragment(item)) {\n return this.renderFragment(item, { depth, path: [] });\n }\n if (isFragmentObject(item)) {\n return this.renderEntries(item, {\n depth,\n path: [],\n }).join('\\n');\n }\n return `${this.#pad(depth)}- ${String(item)}`;\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n const header = `${this.#pad(ctx.depth)}- **${name}**:`;\n if (this.isPrimitive(data)) {\n return `${this.#pad(ctx.depth)}- **${name}**: ${String(data)}`;\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, { ...ctx, depth: ctx.depth + 1 });\n return [header, child].join('\\n');\n }\n if (Array.isArray(data)) {\n const children = data\n .filter((item) => item != null)\n .map((item) => this.#arrayItem(item, ctx.depth + 1));\n return [header, ...children].join('\\n');\n }\n if (isFragmentObject(data)) {\n const children = this.renderEntries(data, {\n ...ctx,\n depth: ctx.depth + 1,\n }).join('\\n');\n return [header, children].join('\\n');\n }\n return header;\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n return this.#leaf(key, value, ctx.depth);\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n const header = `${this.#pad(ctx.depth)}- **${key}**:`;\n const children = items\n .filter((item) => item != null)\n .map((item) => this.#arrayItem(item, ctx.depth + 1));\n return [header, ...children].join('\\n');\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const header = `${this.#pad(ctx.depth)}- **${key}**:`;\n const children = this.renderEntries(obj, {\n ...ctx,\n depth: ctx.depth + 1,\n }).join('\\n');\n return [header, children].join('\\n');\n }\n}\n\n/**\n * Renders context fragments as TOML.\n */\nexport class TomlRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n const rendered: string[] = [];\n for (const f of this.sanitizeFragments(fragments)) {\n if (this.isPrimitive(f.data)) {\n rendered.push(`${f.name} = ${this.#formatValue(f.data)}`);\n } else if (Array.isArray(f.data)) {\n rendered.push(this.#renderTopLevelArray(f.name, f.data));\n } else if (isFragment(f.data)) {\n rendered.push(\n [\n `[${f.name}]`,\n this.renderFragment(f.data, { depth: 0, path: [f.name] }),\n ].join('\\n'),\n );\n } else if (isFragmentObject(f.data)) {\n const entries = this.#renderObjectEntries(f.data, [f.name]);\n rendered.push([`[${f.name}]`, ...entries].join('\\n'));\n }\n }\n return rendered.join('\\n\\n');\n }\n\n #renderTopLevelArray(name: string, items: FragmentData[]): string {\n const fragmentItems = items.filter(isFragment);\n const nonFragmentItems = items.filter(\n (item) => !isFragment(item) && item != null,\n );\n\n // If array contains fragments, render as sections\n if (fragmentItems.length > 0) {\n const parts: string[] = [`[${name}]`];\n for (const frag of fragmentItems) {\n parts.push(this.renderFragment(frag, { depth: 0, path: [name] }));\n }\n return parts.join('\\n');\n }\n\n // Otherwise render as inline array\n const values = nonFragmentItems.map((item) => this.#formatValue(item));\n return `${name} = [${values.join(', ')}]`;\n }\n\n /**\n * Override renderValue to preserve type information for TOML formatting.\n */\n protected override renderValue(\n key: string,\n value: unknown,\n ctx: RenderContext,\n ): string {\n if (value == null) {\n return '';\n }\n if (isFragment(value)) {\n return this.renderFragment(value, ctx);\n }\n if (Array.isArray(value)) {\n return this.renderArray(key, value, ctx);\n }\n if (isFragmentObject(value)) {\n return this.renderObject(key, value, ctx);\n }\n // Preserve original type for TOML formatting\n return `${key} = ${this.#formatValue(value)}`;\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n void ctx;\n return `${key} = ${this.#formatValue(value)}`;\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n void ctx;\n const values = items\n .filter((item) => item != null)\n .map((item) => this.#formatValue(item));\n return `${key} = [${values.join(', ')}]`;\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const newPath = [...ctx.path, key];\n const entries = this.#renderObjectEntries(obj, newPath);\n return ['', `[${newPath.join('.')}]`, ...entries].join('\\n');\n }\n\n #renderObjectEntries(obj: FragmentObject, path: string[]): string[] {\n return Object.entries(obj)\n .map(([key, value]) => {\n if (value == null) {\n return '';\n }\n if (isFragmentObject(value)) {\n const newPath = [...path, key];\n const entries = this.#renderObjectEntries(value, newPath);\n return ['', `[${newPath.join('.')}]`, ...entries].join('\\n');\n }\n if (Array.isArray(value)) {\n const values = value\n .filter((item) => item != null)\n .map((item) => this.#formatValue(item));\n return `${key} = [${values.join(', ')}]`;\n }\n return `${key} = ${this.#formatValue(value)}`;\n })\n .filter(Boolean);\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n const newPath = [...ctx.path, name];\n if (this.isPrimitive(data)) {\n return `${name} = ${this.#formatValue(data)}`;\n }\n if (isFragment(data)) {\n return [\n '',\n `[${newPath.join('.')}]`,\n this.renderFragment(data, { ...ctx, path: newPath }),\n ].join('\\n');\n }\n if (Array.isArray(data)) {\n const fragmentItems = data.filter(isFragment);\n const nonFragmentItems = data.filter(\n (item) => !isFragment(item) && item != null,\n );\n\n if (fragmentItems.length > 0) {\n const parts: string[] = ['', `[${newPath.join('.')}]`];\n for (const frag of fragmentItems) {\n parts.push(this.renderFragment(frag, { ...ctx, path: newPath }));\n }\n return parts.join('\\n');\n }\n\n const values = nonFragmentItems.map((item) => this.#formatValue(item));\n return `${name} = [${values.join(', ')}]`;\n }\n if (isFragmentObject(data)) {\n const entries = this.#renderObjectEntries(data, newPath);\n return ['', `[${newPath.join('.')}]`, ...entries].join('\\n');\n }\n return '';\n }\n\n #escape(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n }\n\n #formatValue(value: unknown): string {\n if (typeof value === 'string') {\n return `\"${this.#escape(value)}\"`;\n }\n if (typeof value === 'boolean' || typeof value === 'number') {\n return String(value);\n }\n if (typeof value === 'object' && value !== null) {\n return JSON.stringify(value);\n }\n return `\"${String(value)}\"`;\n }\n}\n\n/**\n * Renders context fragments as TOON (Token-Oriented Object Notation).\n * TOON is a compact, token-efficient format for LLM prompts that combines\n * YAML-like indentation with CSV-like tabular arrays.\n */\nexport class ToonRenderer extends ContextRenderer {\n render(fragments: ContextFragment[]): string {\n const sanitized = this.sanitizeFragments(fragments);\n return sanitized\n .map((f) => this.#renderTopLevel(f))\n .filter(Boolean)\n .join('\\n');\n }\n\n #renderTopLevel(fragment: ContextFragment): string {\n const { name, data } = fragment;\n if (this.isPrimitive(data)) {\n return `${name}: ${this.#formatValue(data)}`;\n }\n if (Array.isArray(data)) {\n return this.#renderArrayField(name, data, 0);\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, { depth: 1, path: [] });\n return `${name}:\\n${child}`;\n }\n if (isFragmentObject(data)) {\n const entries = this.#renderObjectEntries(data, 1);\n if (!entries) {\n return `${name}:`;\n }\n return `${name}:\\n${entries}`;\n }\n return `${name}:`;\n }\n\n #renderArrayField(key: string, items: FragmentData[], depth: number): string {\n const filtered = items.filter((item) => item != null);\n if (filtered.length === 0) {\n return `${this.#pad(depth)}${key}[0]:`;\n }\n\n // Check for ContextFragment items\n const fragmentItems = filtered.filter(isFragment);\n if (fragmentItems.length > 0) {\n return this.#renderMixedArray(key, filtered, depth);\n }\n\n // Check if all items are primitives\n if (filtered.every((item) => this.#isPrimitiveValue(item))) {\n return this.#renderPrimitiveArray(key, filtered, depth);\n }\n\n // Check if tabular (uniform objects with primitive values)\n if (this.#isTabularArray(filtered)) {\n return this.#renderTabularArray(key, filtered, depth);\n }\n\n // Mixed array\n return this.#renderMixedArray(key, filtered, depth);\n }\n\n #isPrimitiveValue(value: unknown): boolean {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n );\n }\n\n #isTabularArray(items: FragmentData[]): items is FragmentObject[] {\n if (items.length === 0) return false;\n\n // All items must be objects (not arrays, not primitives, not fragments)\n const objects = items.filter(isFragmentObject);\n if (objects.length !== items.length) return false;\n\n // Determine if there is at least one shared field across all rows.\n // We treat null/undefined/missing as \"empty\" cells, but we still require\n // a non-empty key intersection so non-uniform objects are not\n // forced into a tabular shape.\n let intersection = new Set<string>(Object.keys(objects[0]));\n for (const obj of objects) {\n const keys = new Set(Object.keys(obj));\n intersection = new Set([...intersection].filter((k) => keys.has(k)));\n\n for (const value of Object.values(obj)) {\n if (value == null) continue;\n if (!this.#isPrimitiveValue(value)) {\n return false;\n }\n }\n }\n\n return intersection.size > 0;\n }\n\n #renderPrimitiveArray(\n key: string,\n items: FragmentData[],\n depth: number,\n ): string {\n const values = items.map((item) => this.#formatValue(item)).join(',');\n return `${this.#pad(depth)}${key}[${items.length}]: ${values}`;\n }\n\n #renderTabularArray(\n key: string,\n items: FragmentObject[],\n depth: number,\n ): string {\n if (items.length === 0) {\n return `${this.#pad(depth)}${key}[0]:`;\n }\n\n const fields = Array.from(\n new Set(items.flatMap((obj) => Object.keys(obj))),\n );\n const header = `${this.#pad(depth)}${key}[${items.length}]{${fields.join(',')}}:`;\n\n const rows = items.map((obj) => {\n const values = fields.map((f) => {\n const value = obj[f];\n if (value == null) return '';\n return this.#formatValue(value);\n });\n return `${this.#pad(depth + 1)}${values.join(',')}`;\n });\n\n return [header, ...rows].join('\\n');\n }\n\n #renderMixedArray(key: string, items: FragmentData[], depth: number): string {\n const header = `${this.#pad(depth)}${key}[${items.length}]:`;\n const lines = items.map((item) => this.#renderListItem(item, depth + 1));\n return [header, ...lines].join('\\n');\n }\n\n #renderListItem(item: FragmentData, depth: number): string {\n if (this.#isPrimitiveValue(item)) {\n return `${this.#pad(depth)}- ${this.#formatValue(item)}`;\n }\n if (isFragment(item)) {\n const rendered = this.renderFragment(item, {\n depth: depth + 1,\n path: [],\n });\n // For fragments, render key: value on same line as hyphen if primitive\n if (this.isPrimitive(item.data)) {\n return `${this.#pad(depth)}- ${item.name}: ${this.#formatValue(item.data)}`;\n }\n return `${this.#pad(depth)}- ${item.name}:\\n${rendered.split('\\n').slice(1).join('\\n')}`;\n }\n if (Array.isArray(item)) {\n // Nested array\n const content = this.#renderArrayField('', item, depth + 1);\n return `${this.#pad(depth)}-${content.trimStart()}`;\n }\n if (isFragmentObject(item)) {\n // Object in list\n const entries = this.#renderObjectEntries(item, depth + 1);\n if (!entries) {\n return `${this.#pad(depth)}-`;\n }\n // First line on same line as hyphen\n const lines = entries.split('\\n');\n const first = lines[0].trimStart();\n const rest = lines.slice(1).join('\\n');\n return rest\n ? `${this.#pad(depth)}- ${first}\\n${rest}`\n : `${this.#pad(depth)}- ${first}`;\n }\n return `${this.#pad(depth)}- ${this.#formatValue(item)}`;\n }\n\n #renderObjectEntries(obj: FragmentObject, depth: number): string {\n const lines: string[] = [];\n for (const [key, value] of Object.entries(obj)) {\n if (value == null) continue;\n\n if (this.#isPrimitiveValue(value)) {\n lines.push(`${this.#pad(depth)}${key}: ${this.#formatValue(value)}`);\n } else if (Array.isArray(value)) {\n lines.push(this.#renderArrayField(key, value, depth));\n } else if (isFragmentObject(value)) {\n const nested = this.#renderObjectEntries(value, depth + 1);\n if (nested) {\n lines.push(`${this.#pad(depth)}${key}:\\n${nested}`);\n } else {\n lines.push(`${this.#pad(depth)}${key}:`);\n }\n }\n }\n return lines.join('\\n');\n }\n\n protected renderFragment(\n fragment: ContextFragment,\n ctx: RenderContext,\n ): string {\n const { name, data } = fragment;\n if (this.isPrimitive(data)) {\n return `${this.#pad(ctx.depth)}${name}: ${this.#formatValue(data)}`;\n }\n if (isFragment(data)) {\n const child = this.renderFragment(data, {\n ...ctx,\n depth: ctx.depth + 1,\n });\n return `${this.#pad(ctx.depth)}${name}:\\n${child}`;\n }\n if (Array.isArray(data)) {\n return this.#renderArrayField(name, data, ctx.depth);\n }\n if (isFragmentObject(data)) {\n const entries = this.#renderObjectEntries(data, ctx.depth + 1);\n if (!entries) {\n return `${this.#pad(ctx.depth)}${name}:`;\n }\n return `${this.#pad(ctx.depth)}${name}:\\n${entries}`;\n }\n return `${this.#pad(ctx.depth)}${name}:`;\n }\n\n protected renderPrimitive(\n key: string,\n value: string,\n ctx: RenderContext,\n ): string {\n return `${this.#pad(ctx.depth)}${key}: ${this.#formatValue(value)}`;\n }\n\n protected renderArray(\n key: string,\n items: FragmentData[],\n ctx: RenderContext,\n ): string {\n return this.#renderArrayField(key, items, ctx.depth);\n }\n\n protected renderObject(\n key: string,\n obj: FragmentObject,\n ctx: RenderContext,\n ): string {\n const entries = this.#renderObjectEntries(obj, ctx.depth + 1);\n if (!entries) {\n return `${this.#pad(ctx.depth)}${key}:`;\n }\n return `${this.#pad(ctx.depth)}${key}:\\n${entries}`;\n }\n\n #pad(depth: number): string {\n return ' '.repeat(depth);\n }\n\n #needsQuoting(value: string): boolean {\n if (value === '') return true;\n if (value !== value.trim()) return true;\n if (['true', 'false', 'null'].includes(value.toLowerCase())) return true;\n if (/^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$/i.test(value)) return true;\n if (/[:\\\\\"'[\\]{}|,\\t\\n\\r]/.test(value)) return true;\n if (value.startsWith('-')) return true;\n return false;\n }\n\n #escape(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t');\n }\n\n #canonicalizeNumber(n: number): string {\n if (!Number.isFinite(n)) return 'null';\n if (Object.is(n, -0)) return '0';\n return String(n);\n }\n\n #formatValue(value: unknown): string {\n if (value === null) return 'null';\n if (typeof value === 'boolean') return String(value);\n if (typeof value === 'number') return this.#canonicalizeNumber(value);\n if (typeof value === 'string') {\n if (this.#needsQuoting(value)) {\n return `\"${this.#escape(value)}\"`;\n }\n return value;\n }\n // Fallback for objects/arrays in primitive context\n return `\"${this.#escape(JSON.stringify(value))}\"`;\n }\n}\n", "/**\n * Graph-based context store types and abstract interface.\n *\n * The storage model uses a DAG (Directed Acyclic Graph) for messages:\n * - Messages are immutable nodes with parentId forming the graph\n * - Branches are pointers to head (tip) messages\n * - Checkpoints are pointers to specific messages\n * - History is preserved through branching (rewind creates new branch)\n */\n\n// ============================================================================\n// Chat Types\n// ============================================================================\n\n/**\n * Data for creating/storing a chat.\n */\nexport interface ChatData {\n id: string;\n userId: string;\n title?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Stored chat data returned from database (includes timestamps).\n */\nexport interface StoredChatData extends ChatData {\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * Information about a chat for listing.\n */\nexport interface ChatInfo {\n id: string;\n userId: string;\n title?: string;\n metadata?: Record<string, unknown>;\n messageCount: number;\n branchCount: number;\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * Options for listing chats.\n */\nexport interface ListChatsOptions {\n /** Filter by user ID */\n userId?: string;\n /** Filter by metadata field (exact match on top-level primitive) */\n metadata?: { key: string; value: string | number | boolean };\n /** Maximum number of results to return */\n limit?: number;\n /** Number of results to skip (for pagination) */\n offset?: number;\n}\n\n/**\n * Options for deleting a chat.\n */\nexport interface DeleteChatOptions {\n /** If provided, only delete if chat belongs to this user */\n userId?: string;\n}\n\n// ============================================================================\n// Message Types (Graph Nodes)\n// ============================================================================\n\n/**\n * Data for creating/storing a message (graph node).\n */\nexport interface MessageData {\n id: string;\n chatId: string;\n parentId: string | null; // null for root messages\n name: string; // 'user', 'assistant', 'role', 'hint', etc.\n type?: string; // 'message', 'fragment'\n data: unknown; // JSON-serializable content\n createdAt: number;\n}\n\n/**\n * Message with computed properties for listing.\n */\nexport interface MessageInfo extends MessageData {\n hasChildren: boolean;\n}\n\n// ============================================================================\n// Branch Types\n// ============================================================================\n\n/**\n * Data for creating/storing a branch.\n * A branch is a pointer to a head message in the graph.\n */\nexport interface BranchData {\n id: string;\n chatId: string;\n name: string; // 'main', 'alt-1', etc.\n headMessageId: string | null; // null if branch is empty\n isActive: boolean;\n createdAt: number;\n}\n\n/**\n * Information about a branch for listing.\n */\nexport interface BranchInfo {\n id: string;\n name: string;\n headMessageId: string | null;\n isActive: boolean;\n messageCount: number; // count of messages in this branch's chain\n createdAt: number;\n}\n\n// ============================================================================\n// Checkpoint Types\n// ============================================================================\n\n/**\n * Data for creating/storing a checkpoint.\n * A checkpoint is a pointer to a specific message in the graph.\n */\nexport interface CheckpointData {\n id: string;\n chatId: string;\n name: string;\n messageId: string;\n createdAt: number;\n}\n\n/**\n * Information about a checkpoint for listing.\n */\nexport interface CheckpointInfo {\n id: string;\n name: string;\n messageId: string;\n createdAt: number;\n}\n\n// ============================================================================\n// Search Types\n// ============================================================================\n\n/**\n * Options for searching messages.\n */\nexport interface SearchOptions {\n /** Only search in specific roles (e.g., ['user', 'assistant']) */\n roles?: string[];\n /** Maximum results to return (default: 20) */\n limit?: number;\n}\n\n/**\n * Search result with relevance ranking.\n */\nexport interface SearchResult {\n /** The matched message */\n message: MessageData;\n /** BM25 relevance score (lower = more relevant) */\n rank: number;\n /** Highlighted snippet with matched terms */\n snippet?: string;\n}\n\n// ============================================================================\n// Graph Visualization Types\n// ============================================================================\n\n/**\n * A node in the visualization graph.\n */\nexport interface GraphNode {\n id: string;\n parentId: string | null;\n role: string; // 'user', 'assistant', etc.\n content: string; // Truncated preview of message content\n createdAt: number;\n}\n\n/**\n * A branch pointer for visualization.\n */\nexport interface GraphBranch {\n name: string;\n headMessageId: string | null;\n isActive: boolean;\n}\n\n/**\n * A checkpoint pointer for visualization.\n */\nexport interface GraphCheckpoint {\n name: string;\n messageId: string;\n}\n\n/**\n * Complete graph data for visualization.\n */\nexport interface GraphData {\n chatId: string;\n nodes: GraphNode[];\n branches: GraphBranch[];\n checkpoints: GraphCheckpoint[];\n}\n\n// ============================================================================\n// Abstract Store Interface\n// ============================================================================\n\n/**\n * Abstract base class for graph-based context storage.\n *\n * Implementations provide persistence for the message graph, branches,\n * and checkpoints. The graph model enables:\n * - Branching: rewind creates a new branch, original stays intact\n * - Checkpoints: pointers to specific messages for easy restore\n * - No data loss: soft delete only, all history preserved\n */\nexport abstract class ContextStore {\n // ==========================================================================\n // Chat Operations\n // ==========================================================================\n\n /**\n * Create a new chat.\n */\n abstract createChat(chat: ChatData): Promise<void>;\n\n /**\n * Create a chat if it doesn't exist, or return existing one.\n * Returns the stored chat data with timestamps.\n */\n abstract upsertChat(chat: ChatData): Promise<StoredChatData>;\n\n /**\n * Get a chat by ID.\n */\n abstract getChat(chatId: string): Promise<StoredChatData | undefined>;\n\n /**\n * Update chat metadata.\n * Note: updatedAt is automatically managed by database triggers.\n * Returns the updated chat data.\n */\n abstract updateChat(\n chatId: string,\n updates: Partial<Pick<ChatData, 'title' | 'metadata'>>,\n ): Promise<StoredChatData>;\n\n /**\n * List chats, sorted by updatedAt descending.\n * @param options - Optional filters for userId, limit, offset\n */\n abstract listChats(options?: ListChatsOptions): Promise<ChatInfo[]>;\n\n /**\n * Delete a chat and all associated data (messages, branches, checkpoints).\n * Returns true if deleted, false if not found or userId mismatch.\n */\n abstract deleteChat(\n chatId: string,\n options?: DeleteChatOptions,\n ): Promise<boolean>;\n\n // ==========================================================================\n // Message Operations (Graph Nodes)\n // ==========================================================================\n\n /**\n * Add a message to the graph.\n */\n abstract addMessage(message: MessageData): Promise<void>;\n\n /**\n * Get a message by ID.\n */\n abstract getMessage(messageId: string): Promise<MessageData | undefined>;\n\n /**\n * Walk up the parent chain from a head message, returning messages in\n * chronological order (root first).\n */\n abstract getMessageChain(headId: string): Promise<MessageData[]>;\n\n /**\n * Get all messages for a chat from the active branch.\n * Returns messages in chronological order (oldest first).\n *\n * @throws Error if chat doesn't exist\n * @returns Empty array if chat has no active branch or branch has no messages\n */\n abstract getMessages(chatId: string): Promise<MessageData[]>;\n\n /**\n * Check if a message has children (is a fork point).\n */\n abstract hasChildren(messageId: string): Promise<boolean>;\n\n // ==========================================================================\n // Branch Operations\n // ==========================================================================\n\n /**\n * Create a new branch.\n */\n abstract createBranch(branch: BranchData): Promise<void>;\n\n /**\n * Get a branch by chat ID and name.\n */\n abstract getBranch(\n chatId: string,\n name: string,\n ): Promise<BranchData | undefined>;\n\n /**\n * Get the active branch for a chat.\n */\n abstract getActiveBranch(chatId: string): Promise<BranchData | undefined>;\n\n /**\n * Set a branch as active (and deactivate others).\n */\n abstract setActiveBranch(chatId: string, branchId: string): Promise<void>;\n\n /**\n * Update a branch's head message.\n */\n abstract updateBranchHead(\n branchId: string,\n messageId: string | null,\n ): Promise<void>;\n\n /**\n * List all branches for a chat.\n */\n abstract listBranches(chatId: string): Promise<BranchInfo[]>;\n\n // ==========================================================================\n // Checkpoint Operations\n // ==========================================================================\n\n /**\n * Create a checkpoint.\n */\n abstract createCheckpoint(checkpoint: CheckpointData): Promise<void>;\n\n /**\n * Get a checkpoint by chat ID and name.\n */\n abstract getCheckpoint(\n chatId: string,\n name: string,\n ): Promise<CheckpointData | undefined>;\n\n /**\n * List all checkpoints for a chat.\n */\n abstract listCheckpoints(chatId: string): Promise<CheckpointInfo[]>;\n\n /**\n * Delete a checkpoint.\n */\n abstract deleteCheckpoint(chatId: string, name: string): Promise<void>;\n\n // ==========================================================================\n // Search Operations\n // ==========================================================================\n\n /**\n * Search messages using full-text search.\n *\n * @param chatId - The chat to search in\n * @param query - FTS5 query string (supports AND, OR, NOT, phrases, prefix*)\n * @param options - Search options\n * @returns Search results ordered by relevance (lower rank = more relevant)\n */\n abstract searchMessages(\n chatId: string,\n query: string,\n options?: SearchOptions,\n ): Promise<SearchResult[]>;\n\n // ==========================================================================\n // Visualization Operations\n // ==========================================================================\n\n /**\n * Get the complete graph data for a chat.\n * Returns all messages, branches, and checkpoints.\n */\n abstract getGraph(chatId: string): Promise<GraphData>;\n}\n", "import type { ContextFragment, FragmentData } from '../fragments.ts';\n\n/**\n * Domain knowledge fragment builders.\n *\n * These fragments capture domain-specific knowledge that can be injected\n * into AI prompts. Use with renderers (XML, Markdown, TOML, TOON) to format.\n *\n * @example\n * ```ts\n * import { term, hint, guardrail } from '@deepagents/context';\n *\n * context.set(\n * term('NPL', 'non-performing loan'),\n * hint('Always filter by status'),\n * guardrail({ rule: 'Never expose PII' }),\n * );\n * ```\n */\n\n/**\n * Define domain-specific vocabulary and business terminology.\n *\n * Use this to define simple, direct mappings between business terms and their meanings.\n * The system will understand these terms when users mention them in queries.\n *\n * @param name - The business term or acronym to define\n * @param definition - What the term means in your domain\n *\n * @example\n * // Logistics/Transportation dataset\n * term(\"deadhead miles\", \"distance driven with empty truck between deliveries\")\n * term(\"dwell time\", \"total time a truck spends at a loading dock or warehouse\")\n * term(\"LTL\", \"less than truckload - shipment that doesn't fill entire truck\")\n *\n * @example\n * // Education/University dataset\n * term(\"matriculation\", \"students who completed enrollment and started classes\")\n * term(\"DFW rate\", \"percentage of students receiving D, F, or Withdrawal in a course\")\n * term(\"cohort\", \"group of students who entered the same semester or academic year\")\n *\n * @example\n * // Finance/Banking dataset\n * term(\"NPL\", \"non-performing loan - loan past due 90+ days\")\n * term(\"basis points\", \"one hundredth of a percentage point (1% = 100 bps)\")\n * term(\"AUM\", \"assets under management - total market value of client investments\")\n */\nexport function term(name: string, definition: string): ContextFragment {\n return {\n name: 'term',\n data: { name, definition },\n };\n}\n\n/**\n * Define behavioral rules and constraints that should always apply.\n *\n * Use this for business logic, data quality rules, or query preferences that should\n * be automatically applied to all relevant queries.\n *\n * @param text - The rule or constraint to follow (use imperative language)\n *\n * @example\n * // Manufacturing/Supply Chain dataset\n * hint(\"Always exclude work orders with status = 'simulation' from production metrics\")\n * hint(\"When calculating OEE (overall equipment effectiveness), only count scheduled production time\")\n * hint(\"Defect rates should be calculated per batch, not per individual unit, for consistency\")\n *\n * @example\n * // Real Estate/Property dataset\n * hint(\"Never include properties with listing_status = 'draft' in market analysis\")\n * hint(\"Always filter out duplicate MLS listings - use the earliest listing_date for each property_id\")\n * hint(\"Square footage comparisons must specify if including or excluding basement/garage\")\n *\n * @example\n * // Social Media/Content Platform dataset\n * hint(\"Engagement metrics should exclude bot accounts identified by is_verified_human = false\")\n * hint(\"View counts reset daily - always use cumulative_views for historical analysis\")\n * hint(\"Default content filters to published_status = 'public' unless analyzing drafts\")\n */\nexport function hint(text: string): ContextFragment {\n return {\n name: 'hint',\n data: text,\n };\n}\n\n/**\n * Define hard guardrails, safety rules, and compliance boundaries.\n *\n * Use this for \"never do\" rules, sensitive data handling, and required behaviors when\n * certain conditions occur. Guardrails should be explicit and action-oriented.\n *\n * @param input.rule - The guardrail or restriction to enforce\n * @param input.reason - Why this guardrail exists (compliance, security, performance)\n * @param input.action - What to do when this guardrail is triggered\n *\n * @example\n * // Healthcare dataset\n * guardrail({\n * rule: \"Never return PHI like SSN, MRN, or full address in query results\",\n * reason: \"HIPAA compliance\",\n * action: \"If asked, state that identifiable patient data cannot be shared; offer de-identified aggregates instead\"\n * })\n *\n * @example\n * // Finance dataset\n * guardrail({\n * rule: \"Block any query exposing employee-level compensation by name\",\n * reason: \"Confidential payroll data\",\n * action: \"Provide ranges grouped by department or level instead of individual salaries\"\n * })\n *\n * @example\n * // E-commerce dataset\n * guardrail({\n * rule: \"Warn when a query would scan more than 10 million rows; require a narrower date range\",\n * reason: \"Performance and cost control\",\n * action: \"Ask the user to add filters (recent timeframe, specific categories) before proceeding\"\n * })\n */\nexport function guardrail(input: {\n rule: string;\n reason?: string;\n action?: string;\n}): ContextFragment {\n return {\n name: 'guardrail',\n data: {\n rule: input.rule,\n ...(input.reason && { reason: input.reason }),\n ...(input.action && { action: input.action }),\n },\n };\n}\n\n/**\n * Define a rich understanding of a single concept using metaphors and explanations.\n *\n * Use this when a simple term definition isn't enough - when you need to convey deeper\n * understanding about how to think about and calculate a metric or concept.\n *\n * @param input.concept - The concept being explained\n * @param input.explanation - A metaphor or detailed explanation\n * @param input.therefore - Optional actionable instruction based on this understanding\n *\n * @example\n * // Gaming/Entertainment dataset\n * explain({\n * concept: \"daily active users to monthly active users ratio\",\n * explanation: \"like measuring how many club members visit daily vs just once a month - shows stickiness\",\n * therefore: \"Calculate as DAU / MAU, where higher ratio (closer to 1) means more engaged user base\"\n * })\n *\n * @example\n * // HR/Employee Management dataset\n * explain({\n * concept: \"time to fill\",\n * explanation: \"like measuring how long a house sits on the market - from posting job to accepting offer\",\n * therefore: \"Calculate as days between job_posted_date and offer_accepted_date, exclude cancelled requisitions\"\n * })\n *\n * @example\n * // Telecommunications dataset\n * explain({\n * concept: \"network congestion ratio\",\n * explanation: \"like rush hour traffic density - measures actual usage vs total capacity at peak times\",\n * therefore: \"Calculate as (peak_hour_bandwidth_used / total_bandwidth_capacity) during busiest hour of day\"\n * })\n */\nexport function explain(input: {\n concept: string;\n explanation: string;\n therefore?: string;\n}): ContextFragment {\n return {\n name: 'explain',\n data: {\n concept: input.concept,\n explanation: input.explanation,\n ...(input.therefore && { therefore: input.therefore }),\n },\n };\n}\n\n/**\n * Define concrete examples of question \u2192 answer pairs.\n *\n * Use this for few-shot learning - show the system exactly how to translate\n * specific types of questions. Great for establishing patterns.\n *\n * @param input.question - The natural language question or request\n * @param input.answer - The correct answer that responds to the question\n * @param input.note - Optional note or explanation about the example\n *\n * @example\n * // Energy/Utilities dataset\n * example({\n * question: \"show me peak demand hours for the last week\",\n * answer: \"SELECT DATE_TRUNC('hour', reading_timestamp) as hour, MAX(consumption_kwh) as peak_demand FROM meter_readings WHERE reading_timestamp >= CURRENT_DATE - INTERVAL '7 days' GROUP BY hour ORDER BY peak_demand DESC LIMIT 10\"\n * })\n *\n * @example\n * // Agriculture/Farm Management dataset\n * example({\n * question: \"what is the average yield per acre by crop type this season\",\n * answer: \"SELECT crop_type, AVG(harvest_quantity / field_acres) as yield_per_acre FROM harvests WHERE harvest_date >= '2024-01-01' GROUP BY crop_type ORDER BY yield_per_acre DESC\"\n * })\n *\n * @example\n * // Travel/Hospitality dataset\n * example({\n * question: \"show me hotel occupancy rate for this month\",\n * answer: \"SELECT hotel_name, (SUM(occupied_rooms) / SUM(total_rooms)) * 100 as occupancy_rate FROM daily_occupancy WHERE date >= DATE_TRUNC('month', CURRENT_DATE) GROUP BY hotel_id, hotel_name ORDER BY occupancy_rate DESC\",\n * note: \"Occupancy rate is a percentage - multiply by 100 for readable output\"\n * })\n */\nexport function example(input: {\n question: string;\n answer: string;\n note?: string;\n}): ContextFragment {\n return {\n name: 'example',\n data: {\n question: input.question,\n answer: input.answer,\n ...(input.note && { note: input.note }),\n },\n };\n}\n\n/**\n * Define when and what to ask for clarification.\n *\n * Use this to handle ambiguous terms or situations where the system should\n * proactively ask the user for more information.\n *\n * @param input.when - The condition or trigger that should prompt clarification\n * @param input.ask - The question to ask the user\n * @param input.reason - Why this clarification is necessary\n *\n * @example\n * // Marketing/Advertising dataset\n * clarification({\n * when: \"user asks for 'conversion rate'\",\n * ask: \"Which conversion: click-to-lead, lead-to-opportunity, or opportunity-to-customer?\",\n * reason: \"Conversion rate means different things at each funnel stage - need to specify which metric\"\n * })\n *\n * @example\n * // Food Delivery dataset\n * clarification({\n * when: \"user asks about 'delivery time'\",\n * ask: \"Do you mean estimated time at order, actual delivery time, or time from kitchen to door?\",\n * reason: \"Multiple time metrics exist - estimated vs actual impacts customer satisfaction differently\"\n * })\n *\n * @example\n * // Fitness/Gym Management dataset\n * clarification({\n * when: \"user mentions 'active members'\",\n * ask: \"Do you mean paid memberships or members who actually visited in last 30 days?\",\n * reason: \"Many paid members don't use facilities - different metrics for revenue vs utilization\"\n * })\n */\nexport function clarification(input: {\n when: string;\n ask: string;\n reason: string;\n}): ContextFragment {\n return {\n name: 'clarification',\n data: {\n when: input.when,\n ask: input.ask,\n reason: input.reason,\n },\n };\n}\n\n/**\n * Define multi-step analytical processes that require sequential logic.\n *\n * Use this for complex analytical tasks that require multiple steps or specific\n * methodologies. Workflows teach the system HOW to approach a type of analysis.\n *\n * @param input.task - Name of the analytical task\n * @param input.steps - Sequential steps to execute\n * @param input.triggers - Optional phrases that should activate this workflow\n * @param input.notes - Optional additional context, warnings, or guidance\n *\n * @example\n * // Insurance dataset\n * workflow({\n * task: \"Claims Loss Ratio Analysis\",\n * triggers: [\"loss ratio\", \"claims ratio\", \"underwriting performance\"],\n * steps: [\n * \"Calculate total claims paid for each policy period\",\n * \"Calculate total premiums earned for same period\",\n * \"Compute loss ratio as (claims_paid / premiums_earned) * 100\",\n * \"Segment by policy type, geography, and underwriter\",\n * \"Identify policies with loss ratio > 100% (losing money)\",\n * \"Calculate trend over time using rolling 12-month windows\"\n * ],\n * notes: \"Use incurred date for claims, not paid date. Exclude reinsurance recoveries from claims total.\"\n * })\n *\n * @example\n * // Media/Publishing dataset\n * workflow({\n * task: \"Content Performance Funnel\",\n * triggers: [\"content funnel\", \"engagement funnel\", \"content performance\"],\n * steps: [\n * \"Count total impressions (articles shown) per content piece\",\n * \"Count click-throughs (articles opened)\",\n * \"Count scroll depth > 50% (meaningful engagement)\",\n * \"Count shares, comments, or saves (viral actions)\",\n * \"Calculate conversion rate at each funnel stage\",\n * \"Identify top-performing content by final conversion rate\"\n * ],\n * notes: \"Requires multiple event types. Join events table multiple times or use conditional aggregation.\"\n * })\n *\n * @example\n * // Sports Analytics dataset\n * workflow({\n * task: \"Player Performance Rating Calculation\",\n * triggers: [\"player rating\", \"performance score\", \"player analytics\"],\n * steps: [\n * \"Aggregate per-game stats: points, assists, rebounds, turnovers\",\n * \"Calculate efficiency metrics: shooting percentage, plus/minus\",\n * \"Normalize each metric using z-scores vs league average\",\n * \"Apply position-specific weights to each metric\",\n * \"Combine weighted scores into overall performance rating (0-100)\",\n * \"Rank players within position group and overall\"\n * ],\n * notes: \"Requires league-wide statistics for normalization. Update weights each season based on game trends.\"\n * })\n */\nexport function workflow(input: {\n task: string;\n steps: string[];\n triggers?: string[];\n notes?: string;\n}): ContextFragment {\n return {\n name: 'workflow',\n data: {\n task: input.task,\n steps: input.steps,\n ...(input.triggers?.length && { triggers: input.triggers }),\n ...(input.notes && { notes: input.notes }),\n },\n };\n}\n\n/**\n * Define data quirks, edge cases, or database-specific issues and their workarounds.\n *\n * Use this to document weird data patterns, database limitations, or special handling\n * required for specific scenarios.\n *\n * @param input.issue - Description of the quirk, edge case, or problem\n * @param input.workaround - How to handle or work around this issue\n *\n * @example\n * // Government/Public Services dataset\n * quirk({\n * issue: \"Citizen IDs contain leading zeros but are stored as integers, losing the zeros\",\n * workaround: \"Always cast to VARCHAR and use LPAD(citizen_id::VARCHAR, 10, '0') to restore leading zeros\"\n * })\n *\n * @example\n * // Aviation dataset\n * quirk({\n * issue: \"Flight times crossing midnight show as negative duration (landing before takeoff)\",\n * workaround: \"Add 24 hours when calculated duration < 0: CASE WHEN duration < 0 THEN duration + INTERVAL '24 hours' ELSE duration END\"\n * })\n *\n * @example\n * // Automotive/Dealership dataset\n * quirk({\n * issue: \"VIN numbers with letter 'O' were incorrectly entered as zero '0' in legacy data\",\n * workaround: \"When searching by VIN, use REPLACE(vin, '0', 'O') or fuzzy matching to handle both cases\"\n * })\n */\nexport function quirk(input: {\n issue: string;\n workaround: string;\n}): ContextFragment {\n return {\n name: 'quirk',\n data: {\n issue: input.issue,\n workaround: input.workaround,\n },\n };\n}\n\n/**\n * Define style preferences and coding standards.\n *\n * Use this to enforce consistent formatting, naming conventions, and best practices\n * specific to your team or organization.\n *\n * @param input.prefer - Preferred style or pattern\n * @param input.never - Optional anti-pattern to avoid\n * @param input.always - Optional rule that must always be followed\n *\n * @example\n * // Non-profit/Charity dataset\n * styleGuide({\n * prefer: \"Use donor-centric language in column aliases: 'donor_name' not 'customer_name'\",\n * never: \"Never expose internal donor IDs in external reports - use public gift IDs\",\n * always: \"Always include fiscal year in date-based aggregations (FY starts July 1)\"\n * })\n *\n * @example\n * // Legal/Law Firm dataset\n * styleGuide({\n * prefer: \"Use billable_hours with 2 decimal precision for accurate client billing\",\n * never: \"Never include attorney_rate in queries visible to paralegals - confidential data\",\n * always: \"Always filter by matter_status = 'open' unless specifically analyzing closed cases\"\n * })\n *\n * @example\n * // Inventory/Warehouse dataset\n * styleGuide({\n * prefer: \"Use location_id in joins rather than location_name (duplicates exist across warehouses)\",\n * never: \"Never aggregate inventory without grouping by warehouse_id first\",\n * always: \"Always use inventory_on_hand - inventory_reserved for available stock calculations\"\n * })\n */\nexport function styleGuide(input: {\n prefer: string;\n never?: string;\n always?: string;\n}): ContextFragment {\n return {\n name: 'styleGuide',\n data: {\n prefer: input.prefer,\n ...(input.never && { never: input.never }),\n ...(input.always && { always: input.always }),\n },\n };\n}\n\n/**\n * Define comparisons between related concepts through real-world analogies.\n *\n * Use this to teach relational understanding between concepts by drawing comparisons\n * to familiar real-world scenarios.\n *\n * @param input.concepts - Array of related concepts to compare\n * @param input.relationship - The comparison/analogy using real-world examples\n * @param input.insight - Optional key insight the analogy reveals\n * @param input.therefore - Optional actionable instruction\n * @param input.pitfall - Optional common mistake to avoid\n *\n * @example\n * // E-commerce dataset\n * analogy({\n * concepts: [\"cart abandonment\", \"browse abandonment\"],\n * relationship: \"Cart abandonment is like leaving items at a checkout counter, browse abandonment is like window shopping without picking anything up\",\n * insight: \"Cart abandonment shows purchase intent (added to cart), browse abandonment shows only interest\",\n * therefore: \"Prioritize cart abandonment recovery campaigns - higher conversion potential than browse\",\n * pitfall: \"Don't combine both into generic 'abandonment rate' - they need different marketing strategies\"\n * })\n *\n * @example\n * // SaaS dataset\n * analogy({\n * concepts: [\"logo churn\", \"revenue churn\"],\n * relationship: \"Logo churn is like counting how many customers left the store, revenue churn is how much money walked out\",\n * insight: \"Losing 10 small customers (high logo churn) might hurt less than losing 1 enterprise customer (high revenue churn)\",\n * therefore: \"Always report both metrics - logo churn for customer satisfaction, revenue churn for financial health\",\n * pitfall: \"Don't use logo churn to predict revenue impact - customer size distribution matters\"\n * })\n *\n * @example\n * // Healthcare dataset\n * analogy({\n * concepts: [\"incidence\", \"prevalence\"],\n * relationship: \"Incidence is like new house sales this month, prevalence is total houses currently occupied\",\n * insight: \"Incidence measures new cases over time, prevalence measures all existing cases at a point in time\",\n * therefore: \"For tracking disease outbreaks use incidence rate, for resource planning use prevalence\",\n * pitfall: \"Don't sum incidence rates across time periods - it's a rate not a count\"\n * })\n */\nexport function analogy(input: {\n concepts: string[];\n relationship: string;\n insight?: string;\n therefore?: string;\n pitfall?: string;\n}): ContextFragment {\n return {\n name: 'analogy',\n data: {\n concepts: input.concepts,\n relationship: input.relationship,\n ...(input.insight && { insight: input.insight }),\n ...(input.therefore && { therefore: input.therefore }),\n ...(input.pitfall && { pitfall: input.pitfall }),\n },\n };\n}\n\n/**\n * Map business terms directly to expressions or fragments.\n *\n * Use this to teach the system how to CALCULATE or QUERY specific business concepts.\n * The system will substitute these patterns when users mention the term.\n *\n * **Glossary vs Alias:**\n * - `alias` = user vocabulary \u2192 table/column name (\"the big table\" \u2192 \"orders table\")\n * - `glossary` = business term \u2192 SQL expression (\"revenue\" \u2192 \"SUM(orders.total_amount)\")\n *\n * In short: alias renames, glossary computes.\n *\n * @param entries - Record mapping business terms to their expressions\n *\n * @example\n * glossary({\n * \"revenue\": \"SUM(orders.total_amount)\",\n * \"average order value\": \"AVG(orders.total_amount)\",\n * \"active user\": \"last_login > NOW() - INTERVAL '30 days'\",\n * \"churned\": \"status = 'churned'\",\n * \"power user\": \"order_count > 10\",\n * \"net revenue\": \"SUM(orders.total_amount) - SUM(refunds.amount)\",\n * })\n */\nexport function glossary(entries: Record<string, string>): ContextFragment {\n return {\n name: 'glossary',\n data: Object.entries(entries).map(([term, expression]) => ({\n term,\n expression,\n })),\n };\n}\n\n/**\n * Create a role fragment for system prompt instructions.\n */\nexport function role(content: string): ContextFragment {\n return {\n name: 'role',\n data: content,\n };\n}\n\n/**\n * Define a guiding principle that shapes agent behavior.\n *\n * Use this to establish high-level rules for decision-making, reasoning, or domain behavior.\n * Principles can contain policies (specific rules that implement the principle).\n *\n * @param input.title - Name/title of the principle\n * @param input.description - What this principle means and why it matters\n * @param input.policies - Optional specific rules that implement this principle\n *\n * @example\n * // Logical dependencies principle\n * principle({\n * title: \"Logical dependencies and constraints\",\n * description: \"Analyze intended actions against factors in order of importance\",\n * policies: [\n * \"Policy-based rules, mandatory prerequisites, and constraints\",\n * \"Order of operations: Ensure actions don't prevent subsequent necessary actions\",\n * \"Other prerequisites (information and/or actions needed)\",\n * \"Explicit user constraints or preferences\"\n * ]\n * })\n *\n * @example\n * // Risk assessment principle\n * principle({\n * title: \"Risk assessment\",\n * description: \"Evaluate consequences before taking action\",\n * policies: [\n * \"For exploratory tasks, missing optional parameters is LOW risk\",\n * \"Prefer calling tools with available information over asking the user\"\n * ]\n * })\n *\n * @example\n * // Design principle\n * principle({\n * title: \"Separation of concerns\",\n * description: \"Each module should have a single, well-defined responsibility\",\n * policies: [\n * \"Data access logic stays in repository layer\",\n * \"Business rules stay in service layer\",\n * \"Presentation logic stays in controller/view layer\"\n * ]\n * })\n */\nexport function principle(input: {\n title: string;\n description: string;\n policies?: FragmentData[];\n}): ContextFragment {\n return {\n name: 'principle',\n data: {\n title: input.title,\n description: input.description,\n ...(input.policies?.length && { policies: input.policies }),\n },\n };\n}\n\n/**\n * Define a policy rule, optionally with prerequisites or nested sub-policies.\n *\n * Policies can be used in two ways:\n * 1. Prerequisite rules: \"must do X before Y\" using the `before` parameter\n * 2. Sub-policies: nested rules within a principle using the `policies` parameter\n *\n * Policies differ from guardrails: policies are prerequisites (do this first),\n * guardrails are prohibitions (never do this).\n *\n * @param input.rule - The policy rule to enforce\n * @param input.before - What action this is a prerequisite for (optional for sub-policies)\n * @param input.reason - Why this rule matters\n * @param input.policies - Nested sub-policies for hierarchical structure\n *\n * @example\n * // Prerequisite rule with \"before\"\n * policy({\n * rule: \"Validate SQL syntax\",\n * before: \"executing any query against the database\",\n * reason: \"Catches errors early and allows correction before execution\"\n * })\n *\n * @example\n * // Sub-policy within a principle (no \"before\" needed)\n * policy({ rule: \"Policy-based rules, mandatory prerequisites, and constraints.\" })\n *\n * @example\n * // Nested sub-policies (hierarchical structure like 1.2 \u2192 1.2.1)\n * policy({\n * rule: \"Order of operations: Ensure taking an action does not prevent a subsequent necessary action.\",\n * policies: [\n * \"The user may request actions in a random order, but you may need to reorder operations.\",\n * ],\n * })\n */\nexport function policy(input: {\n rule: string;\n before?: string;\n reason?: string;\n policies?: FragmentData[];\n}): ContextFragment {\n return {\n name: 'policy',\n data: {\n rule: input.rule,\n ...(input.before && { before: input.before }),\n ...(input.reason && { reason: input.reason }),\n ...(input.policies?.length && { policies: input.policies }),\n },\n };\n}\n", "import type { ContextFragment } from '../fragments.ts';\n\n/**\n * User-specific fragment builders.\n *\n * These fragments capture user context, preferences, and personalization data\n * that can be injected into AI prompts to tailor responses.\n *\n * @example\n * ```ts\n * import { identity, persona, preference } from '@deepagents/context';\n *\n * context.set(\n * identity({ name: 'John', role: 'VP of Sales' }),\n * persona({ name: 'Freya', role: 'Data Assistant', tone: 'professional' }),\n * preference('date format', 'YYYY-MM-DD'),\n * );\n * ```\n */\n\n/**\n * Define the user's identity including name and/or role.\n *\n * Use this to capture who the user is and what lens they view data through.\n * Helps tailor explanations, terminology, and focus areas.\n *\n * @param input.name - The user's name (optional)\n * @param input.role - The user's role or position (optional)\n *\n * @example\n * identity({ name: \"John\", role: \"VP of Sales\" })\n * identity({ role: \"Data analyst in the marketing team\" })\n * identity({ name: \"Sarah\" })\n * identity({ role: \"Finance manager focused on cost optimization\" })\n */\nexport function identity(input: {\n name?: string;\n role?: string;\n}): ContextFragment {\n return {\n name: 'identity',\n data: {\n ...(input.name && { name: input.name }),\n ...(input.role && { role: input.role }),\n },\n };\n}\n\n/**\n * Define an AI persona with a name, role, objective, and communication tone.\n *\n * Use this to customize the assistant's identity and what it should accomplish.\n *\n * @param input.name - The persona's name\n * @param input.role - The persona's expertise/identity (what they are)\n * @param input.objective - What the persona should accomplish (the goal)\n * @param input.tone - The communication style (e.g., friendly, professional, concise)\n *\n * @example\n * persona({ name: \"DataBot\", role: \"SQL Expert\", objective: \"Generate accurate SQL queries from natural language\" })\n * persona({ name: \"QueryMaster\", role: \"Database Analyst\", objective: \"Help users explore database schemas\" })\n */\nexport function persona(input: {\n name: string;\n role?: string;\n objective?: string;\n tone?: string;\n}): ContextFragment {\n return {\n name: 'persona',\n data: {\n name: input.name,\n ...(input.role && { role: input.role }),\n ...(input.objective && { objective: input.objective }),\n ...(input.tone && { tone: input.tone }),\n },\n };\n}\n\n/**\n * Define user-specific term meanings and vocabulary.\n *\n * Use this when the user has their own definitions for terms that might\n * differ from standard or domain definitions. Like `term()` but personal.\n *\n * @param term - The term the user uses\n * @param meaning - What the user means by this term\n *\n * @example\n * alias(\"revenue\", \"gross revenue before deductions, not net\")\n * alias(\"active users\", \"users who logged in within the last 30 days\")\n * alias(\"the big table\", \"the orders table\")\n * alias(\"Q4\", \"October through December, not fiscal Q4\")\n */\nexport function alias(term: string, meaning: string): ContextFragment {\n return {\n name: 'alias',\n data: { term, meaning },\n };\n}\n\n/**\n * Define how the user prefers results presented.\n *\n * Use this to capture output formatting, style, and behavioral preferences\n * that should apply to all interactions with this user.\n *\n * @param aspect - What aspect of output this preference applies to\n * @param value - The user's preference\n *\n * @example\n * preference(\"date format\", \"YYYY-MM-DD\")\n * preference(\"output style\", \"tables over charts unless trend data\")\n * preference(\"detail level\", \"always show the SQL query in responses\")\n * preference(\"row limit\", \"default to 50 rows unless I ask for more\")\n * preference(\"explanation style\", \"brief and to the point\")\n */\nexport function preference(aspect: string, value: string): ContextFragment {\n return {\n name: 'preference',\n data: { aspect, value },\n };\n}\n\n/**\n * Define the user's current working focus or project.\n *\n * Use this to capture temporary context that helps inform defaults,\n * assumptions, and suggestions. Should be updated as focus changes.\n *\n * @param description - What the user is currently working on\n *\n * @example\n * userContext(\"Preparing Q4 board presentation\")\n * userContext(\"Investigating drop in signups last week\")\n * userContext(\"Working on EMEA regional analysis for strategy meeting\")\n * userContext(\"Debugging discrepancy in revenue numbers\")\n */\nexport function userContext(description: string): ContextFragment {\n return {\n name: 'userContext',\n data: description,\n };\n}\n\n/**\n * Record a correction the user made to previous understanding.\n *\n * Use this when the user corrects a misunderstanding about data, columns,\n * or business logic. Prevents repeating the same mistake.\n *\n * @param subject - What was misunderstood\n * @param clarification - The correct understanding\n *\n * @example\n * correction(\"status column\", \"1 = active, 0 = inactive, not boolean true/false\")\n * correction(\"orders table\", \"Use orders_v2, not the deprecated legacy_orders table\")\n * correction(\"date field\", \"order_date is when order was placed, ship_date is when shipped\")\n * correction(\"revenue calculation\", \"Must exclude refunds and chargebacks\")\n */\nexport function correction(\n subject: string,\n clarification: string,\n): ContextFragment {\n return {\n name: 'correction',\n data: { subject, clarification },\n };\n}\n", "/**\n * Guardrail system for real-time stream interception and self-correction.\n *\n * Guardrails inspect streaming parts and can either:\n * - `pass(part)`: Allow the part through (optionally modified)\n * - `fail(feedback)`: Abort the stream and retry with self-correction feedback\n *\n * When a guardrail fails, the accumulated text is combined with the feedback\n * to create a \"self-correction\" that appears as if the agent caught itself.\n *\n * @example\n * ```typescript\n * const safetyGuardrail: Guardrail = {\n * id: 'safety',\n * name: 'Safety Filter',\n * handle: (part, context) => {\n * if (part.type === 'text-delta' && part.delta.includes('unsafe')) {\n * return fail('I should not provide this information. Let me help differently.');\n * }\n * if (part.type === 'error' && context.availableTools.length > 0) {\n * return fail(`Try using: ${context.availableTools.join(', ')}`);\n * }\n * return pass(part);\n * },\n * };\n *\n * const agent = agent({\n * name: 'safe_assistant',\n * context,\n * model,\n * guardrails: [safetyGuardrail],\n * });\n * ```\n */\nimport type { InferUIMessageChunk, UIDataTypes, UIMessage } from 'ai';\n\n/**\n * Type alias for stream parts from the AI SDK's UI message stream.\n * This is the full chunk type that includes text-delta, error, reasoning-delta, etc.\n */\nexport type StreamPart = InferUIMessageChunk<\n UIMessage<unknown, UIDataTypes, Record<string, never>>\n>;\n\n/**\n * Result of a guardrail check.\n * - `pass`: The part is allowed through (optionally modified)\n * - `fail`: The stream should abort and retry with feedback\n */\nexport type GuardrailResult =\n | { type: 'pass'; part: StreamPart }\n | { type: 'fail'; feedback: string };\n\n/**\n * Context passed to guardrails during stream processing.\n * Provides information about the agent's capabilities.\n */\nexport interface GuardrailContext {\n /** Names of tools available to the agent */\n availableTools: string[];\n}\n\n/**\n * A guardrail that inspects streaming parts.\n */\nexport interface Guardrail {\n /** Unique identifier for this guardrail */\n id: string;\n /** Human-readable name for logging/debugging */\n name: string;\n /**\n * Handle a stream part.\n *\n * @param part - The full stream part to inspect (text-delta, error, etc.)\n * @param context - Context with agent capabilities (available tools, etc.)\n * @returns Either `pass(part)` to allow or `fail(feedback)` to abort and retry\n */\n handle: (part: StreamPart, context: GuardrailContext) => GuardrailResult;\n}\n\n/**\n * Configuration for guardrail behavior.\n */\nexport interface GuardrailConfig {\n /** Maximum number of retry attempts when guardrails fail (default: 3) */\n maxRetries?: number;\n}\n\n/**\n * Allow a part to pass through the guardrail.\n *\n * @param part - The part to pass (can be modified from original)\n * @returns A pass result\n *\n * @example\n * ```typescript\n * handle: (part) => {\n * // Pass through unchanged\n * return pass(part);\n *\n * // Or modify text-delta before passing\n * if (part.type === 'text-delta') {\n * return pass({ ...part, delta: part.delta.replace('bad', 'good') });\n * }\n * return pass(part);\n * }\n * ```\n */\nexport function pass(part: StreamPart): GuardrailResult {\n return { type: 'pass', part };\n}\n\n/**\n * Fail the guardrail check and trigger a retry with feedback.\n *\n * The feedback will be appended to the accumulated assistant text,\n * making it appear as if the agent \"caught itself\" and self-corrected.\n *\n * @param feedback - The self-correction feedback to append\n * @returns A fail result\n *\n * @example\n * ```typescript\n * handle: (part) => {\n * if (part.type === 'text-delta' && part.delta.includes('hack')) {\n * return fail('I should not provide hacking instructions. Let me suggest ethical alternatives.');\n * }\n * if (part.type === 'error') {\n * return fail('An error occurred. Let me try a different approach.');\n * }\n * return pass(part);\n * }\n * ```\n */\nexport function fail(feedback: string): GuardrailResult {\n return { type: 'fail', feedback };\n}\n\n/**\n * Run a part through a chain of guardrails sequentially.\n *\n * @param part - The stream part to check\n * @param guardrails - Array of guardrails to run in order\n * @param context - Context with agent capabilities (available tools, etc.)\n * @returns The final result after all guardrails pass, or the first failure\n */\nexport function runGuardrailChain(\n part: StreamPart,\n guardrails: Guardrail[],\n context: GuardrailContext,\n): GuardrailResult {\n let currentPart = part;\n\n for (const guardrail of guardrails) {\n const result = guardrail.handle(currentPart, context);\n\n if (result.type === 'fail') {\n return result;\n }\n\n // Pass the (possibly modified) part to the next guardrail\n currentPart = result.part;\n }\n\n return pass(currentPart);\n}\n", "/**\n * Error Recovery Guardrail\n *\n * Intercepts API-level errors (like tool validation failures) and triggers\n * self-correction retries. This is essential for models like gpt-oss-20b\n * that may hallucinate tools that don't exist.\n *\n * Catches errors like:\n * - \"Tool choice is none, but model called a tool\"\n * - \"attempted to call tool 'X' which was not in request.tools\"\n * - \"Failed to parse tool call arguments as JSON\" (malformed JSON)\n * - Parsing failures\n *\n * @example\n * ```typescript\n * const myAgent = agent({\n * name: 'my_agent',\n * model: groq('openai/gpt-oss-20b'),\n * tools: { bash, sql },\n * guardrails: [errorRecoveryGuardrail],\n * maxGuardrailRetries: 3,\n * });\n * ```\n */\nimport chalk from 'chalk';\n\nimport type { Guardrail } from '../guardrail.ts';\nimport { fail, pass } from '../guardrail.ts';\n\nexport const errorRecoveryGuardrail: Guardrail = {\n id: 'error-recovery',\n name: 'API Error Recovery',\n handle: (part, context) => {\n // Only handle error parts\n if (part.type !== 'error') {\n return pass(part);\n }\n\n const errorText = part.errorText || '';\n const prefix = chalk.bold.magenta('[ErrorRecovery]');\n\n console.log(\n `${prefix} ${chalk.red('Caught error:')} ${chalk.dim(errorText.slice(0, 150))}`,\n );\n\n // Helper to log and return fail\n const logAndFail = (pattern: string, feedback: string) => {\n console.log(\n `${prefix} ${chalk.yellow('Pattern:')} ${chalk.cyan(pattern)}`,\n );\n console.log(\n `${prefix} ${chalk.green('Feedback:')} ${chalk.dim(feedback.slice(0, 80))}...`,\n );\n return fail(feedback);\n };\n\n // Pattern: No tools available but model tried to call one\n if (errorText.includes('Tool choice is none')) {\n if (context.availableTools.length > 0) {\n return logAndFail(\n 'Tool choice is none',\n `I tried to call a tool that doesn't exist. Available tools: ${context.availableTools.join(', ')}. Let me use one of these instead.`,\n );\n }\n return logAndFail(\n 'Tool choice is none (no tools)',\n 'I tried to call a tool, but no tools are available. Let me respond with plain text instead.',\n );\n }\n\n // Pattern: Tool not found in request.tools\n if (\n errorText.includes('not in request.tools') ||\n (errorText.includes('tool') && errorText.includes('not found'))\n ) {\n const toolMatch = errorText.match(/tool '([^']+)'/);\n const toolName = toolMatch ? toolMatch[1] : 'unknown';\n if (context.availableTools.length > 0) {\n return logAndFail(\n `Unregistered tool: ${toolName}`,\n `I tried to call \"${toolName}\" but it doesn't exist. Available tools: ${context.availableTools.join(', ')}. Let me use one of these instead.`,\n );\n }\n return logAndFail(\n `Unregistered tool: ${toolName} (no tools)`,\n `I tried to call \"${toolName}\" but no tools are available. Let me respond with plain text instead.`,\n );\n }\n\n // Pattern: Failed to parse tool arguments as JSON\n if (\n errorText.includes('Failed to parse tool call arguments') ||\n errorText.includes('parse tool call') ||\n errorText.includes('invalid JSON')\n ) {\n return logAndFail(\n 'Malformed JSON arguments',\n 'I generated malformed JSON for the tool arguments. Let me format my tool call properly with valid JSON.',\n );\n }\n\n // Pattern: Parsing failed (generic)\n if (errorText.includes('Parsing failed')) {\n return logAndFail(\n 'Parsing failed',\n 'My response format was invalid. Let me try again with a properly formatted response.',\n );\n }\n console.dir({ part }, { depth: null });\n // Unknown error - still try to recover\n return logAndFail(\n 'Unknown error',\n `An error occurred: ${errorText}. Let me try a different approach.`,\n );\n },\n};\n", "import { existsSync } from 'fs';\nimport { type CustomCommand, defineCommand } from 'just-bash';\nimport spawn from 'nano-spawn';\nimport * as path from 'path';\n\nexport interface BinaryBridgeConfig {\n /** Command name in the sandbox (what the agent types) */\n name: string;\n /** Actual binary path on the host system (defaults to name) */\n binaryPath?: string;\n /** Optional regex to restrict allowed arguments for security */\n allowedArgs?: RegExp;\n}\n\nexport type BinaryBridgeInput = string | BinaryBridgeConfig;\n\n/**\n * Creates custom commands that bridge to real system binaries.\n *\n * This allows just-bash sandboxed environments to execute specific\n * host system binaries while maintaining control over which binaries\n * are accessible.\n *\n * @example\n * // Simple - just strings (name === binaryPath)\n * createBinaryBridges('presenterm', 'node', 'cargo')\n *\n * @example\n * // Mixed - strings and config objects\n * createBinaryBridges(\n * 'presenterm',\n * { name: 'python', binaryPath: 'python3' },\n * { name: 'git', allowedArgs: /^(status|log|diff)/ }\n * )\n */\nexport function createBinaryBridges(\n ...binaries: BinaryBridgeInput[]\n): CustomCommand[] {\n return binaries.map((input) => {\n const config: BinaryBridgeConfig =\n typeof input === 'string' ? { name: input } : input;\n\n const { name, binaryPath = name, allowedArgs } = config;\n\n return defineCommand(name, async (args, ctx) => {\n // Validate args against pattern if specified\n if (allowedArgs) {\n const invalidArg = args.find((arg) => !allowedArgs.test(arg));\n if (invalidArg) {\n return {\n stdout: '',\n stderr: `${name}: argument '${invalidArg}' not allowed by security policy`,\n exitCode: 1,\n };\n }\n }\n\n try {\n // Resolve the real working directory from the virtual filesystem\n // just-bash uses virtual paths like /home/user, we need the real host path\n const realCwd = resolveRealCwd(ctx);\n\n // Resolve file paths in arguments relative to the real cwd\n const resolvedArgs = args.map((arg) => {\n // Skip flags and options\n if (arg.startsWith('-')) {\n return arg;\n }\n\n // Check if arg looks like a path:\n // 1. Has a file extension (e.g., file.md, script.py)\n // 2. Contains path separator (e.g., src/file, dir\\file)\n // 3. Is a relative path starting with . (e.g., ., .., ./foo)\n const hasExtension = path.extname(arg) !== '';\n const hasPathSep = arg.includes(path.sep) || arg.includes('/');\n const isRelative = arg.startsWith('.');\n\n if (hasExtension || hasPathSep || isRelative) {\n // Resolve relative to the real cwd\n return path.resolve(realCwd, arg);\n }\n\n return arg;\n });\n\n // Merge environments but preserve process.env.PATH for binary resolution\n // ctx.env.PATH is the virtual PATH (/bin:/usr/bin) which doesn't include host binaries\n const mergedEnv = {\n ...process.env,\n ...ctx.env,\n PATH: process.env.PATH, // Always use host PATH for binary bridges\n };\n\n const result = await spawn(binaryPath, resolvedArgs, {\n cwd: realCwd,\n env: mergedEnv,\n });\n\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: 0,\n };\n } catch (error) {\n // nano-spawn wraps ENOENT (missing binary) into a SubprocessError\n // with exitCode undefined and the real cause on error.cause.\n if (error && typeof error === 'object') {\n const err = error as { cause?: unknown; message?: string };\n const cause = err.cause as\n | { code?: string; path?: string; syscall?: string }\n | undefined;\n\n if (cause?.code === 'ENOENT') {\n return {\n stdout: '',\n stderr: `${name}: ${binaryPath} not found`,\n exitCode: 127,\n };\n }\n }\n\n // nano-spawn throws SubprocessError for non-zero exits\n if (error && typeof error === 'object' && 'exitCode' in error) {\n const subprocessError = error as {\n exitCode?: number;\n stdout: string;\n stderr: string;\n };\n return {\n stdout: subprocessError.stdout ?? '',\n stderr: subprocessError.stderr ?? '',\n exitCode: subprocessError.exitCode ?? 1,\n };\n }\n\n // Unknown error (e.g., binary not found)\n return {\n stdout: '',\n stderr: `${name}: ${error instanceof Error ? error.message : String(error)}`,\n exitCode: 127,\n };\n }\n });\n });\n}\n\n/**\n * Resolves the real filesystem path from a just-bash virtual path.\n *\n * just-bash filesystems (ReadWriteFs, OverlayFs) use virtual paths like /home/user\n * but we need the actual host filesystem path for spawning processes.\n */\nfunction resolveRealCwd(ctx: { cwd: string; fs: unknown }): string {\n const fs = ctx.fs as {\n toRealPath?: (p: string) => string | null;\n root?: string;\n getMountPoint?: () => string;\n };\n\n let realCwd: string;\n\n if (fs.root) {\n // ReadWriteFs - virtual paths are relative to root\n // e.g., root=/Users/x/project, cwd=/ -> /Users/x/project\n realCwd = path.join(fs.root, ctx.cwd);\n } else if (\n typeof fs.getMountPoint === 'function' &&\n typeof fs.toRealPath === 'function'\n ) {\n // OverlayFs - use toRealPath for proper path mapping\n const real = fs.toRealPath(ctx.cwd);\n realCwd = real ?? process.cwd();\n } else {\n // Fallback for InMemoryFs or unknown filesystems\n realCwd = process.cwd();\n }\n\n // Verify the path exists, fall back to process.cwd() if not\n if (!existsSync(realCwd)) {\n realCwd = process.cwd();\n }\n\n return realCwd;\n}\n", "import { type CommandResult, type Sandbox } from 'bash-tool';\nimport spawn from 'nano-spawn';\nimport { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\n\n// Re-export types from bash-tool for convenience\nexport type { CommandResult as ExecResult, Sandbox } from 'bash-tool';\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Error Classes\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Base error for all Docker sandbox operations.\n */\nexport class DockerSandboxError extends Error {\n readonly containerId?: string;\n\n constructor(message: string, containerId?: string) {\n super(message);\n this.name = 'DockerSandboxError';\n this.containerId = containerId;\n }\n}\n\n/**\n * Thrown when Docker daemon is not available.\n */\nexport class DockerNotAvailableError extends DockerSandboxError {\n constructor() {\n super('Docker is not available. Ensure Docker daemon is running.');\n this.name = 'DockerNotAvailableError';\n }\n}\n\n/**\n * Thrown when container creation fails.\n */\nexport class ContainerCreationError extends DockerSandboxError {\n readonly image: string;\n override cause?: Error;\n\n constructor(message: string, image: string, cause?: Error) {\n super(`Failed to create container from image \"${image}\": ${message}`);\n this.name = 'ContainerCreationError';\n this.image = image;\n this.cause = cause;\n }\n}\n\n/**\n * Thrown when package installation fails.\n */\nexport class PackageInstallError extends DockerSandboxError {\n readonly packages: string[];\n readonly image: string;\n readonly packageManager: 'apk' | 'apt-get';\n readonly stderr: string;\n\n constructor(\n packages: string[],\n image: string,\n packageManager: 'apk' | 'apt-get',\n stderr: string,\n containerId?: string,\n ) {\n super(\n `Package installation failed for [${packages.join(', ')}] ` +\n `using ${packageManager} on ${image}: ${stderr}`,\n containerId,\n );\n this.name = 'PackageInstallError';\n this.packages = packages;\n this.image = image;\n this.packageManager = packageManager;\n this.stderr = stderr;\n }\n}\n\n/**\n * Thrown when a binary installation from URL fails.\n */\nexport class BinaryInstallError extends DockerSandboxError {\n readonly binaryName: string;\n readonly url: string;\n readonly reason: string;\n\n constructor(\n binaryName: string,\n url: string,\n reason: string,\n containerId?: string,\n ) {\n super(\n `Failed to install binary \"${binaryName}\" from ${url}: ${reason}`,\n containerId,\n );\n this.name = 'BinaryInstallError';\n this.binaryName = binaryName;\n this.url = url;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown when a mount path doesn't exist on the host.\n */\nexport class MountPathError extends DockerSandboxError {\n readonly hostPath: string;\n readonly containerPath: string;\n\n constructor(hostPath: string, containerPath: string) {\n super(\n `Mount path does not exist on host: \"${hostPath}\" -> \"${containerPath}\"`,\n );\n this.name = 'MountPathError';\n this.hostPath = hostPath;\n this.containerPath = containerPath;\n }\n}\n\n/**\n * Thrown when Dockerfile build fails.\n */\nexport class DockerfileBuildError extends DockerSandboxError {\n readonly stderr: string;\n\n constructor(stderr: string) {\n super(`Dockerfile build failed: ${stderr}`);\n this.name = 'DockerfileBuildError';\n this.stderr = stderr;\n }\n}\n\n/**\n * Thrown when docker compose up fails.\n */\nexport class ComposeStartError extends DockerSandboxError {\n readonly composeFile: string;\n readonly stderr: string;\n\n constructor(composeFile: string, stderr: string) {\n super(`Docker Compose failed to start: ${stderr}`);\n this.name = 'ComposeStartError';\n this.composeFile = composeFile;\n this.stderr = stderr;\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Interfaces\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Configuration for mounting a host directory into the container.\n */\nexport interface DockerMount {\n /** Absolute path on the host machine */\n hostPath: string;\n /** Path inside the container */\n containerPath: string;\n /** Whether the mount is read-only (default: true) */\n readOnly?: boolean;\n}\n\n/**\n * Resource limits for the container.\n */\nexport interface DockerResources {\n /** Memory limit (e.g., '1g', '512m') */\n memory?: string;\n /** CPU limit (number of CPUs) */\n cpus?: number;\n}\n\n/**\n * Architecture-specific URL mapping for binary downloads.\n * Maps container architecture (from `uname -m`) to download URLs.\n */\nexport interface ArchitectureUrls {\n /** URL for x86_64 architecture (amd64) */\n x86_64?: string;\n /** URL for ARM64 architecture (aarch64) */\n aarch64?: string;\n /** URL for ARMv7 architecture */\n armv7l?: string;\n}\n\n/**\n * Configuration for installing a binary from a URL.\n *\n * Binaries are downloaded, extracted (if tar.gz), and installed to /usr/local/bin.\n */\nexport interface BinaryInstall {\n /** Name of the binary (used for the final executable name) */\n name: string;\n /**\n * URL or architecture-specific URLs.\n * - If a string, used for all architectures\n * - If ArchitectureUrls, selects based on container architecture\n */\n url: string | ArchitectureUrls;\n /**\n * Optional: The binary filename inside the archive if different from `name`.\n * Useful when the archive contains versioned binaries like \"presenterm-0.15.1\".\n */\n binaryPath?: string;\n}\n\n/**\n * Options for RuntimeStrategy - installs packages/binaries at container runtime.\n */\nexport interface RuntimeSandboxOptions {\n /** Docker image to use (default: 'alpine:latest') */\n image?: string;\n /** Packages to install in the container via package manager (apk/apt) */\n packages?: string[];\n /** Binaries to install from URLs (for tools not in package managers) */\n binaries?: BinaryInstall[];\n /** Directories to mount from host */\n mounts?: DockerMount[];\n /** Resource limits */\n resources?: DockerResources;\n}\n\n/**\n * Options for DockerfileStrategy - builds custom image from Dockerfile.\n */\nexport interface DockerfileSandboxOptions {\n /** Dockerfile content (if contains newlines) or path to Dockerfile */\n dockerfile: string;\n /** Build context directory (default: '.') */\n context?: string;\n /** Directories to mount from host */\n mounts?: DockerMount[];\n /** Resource limits */\n resources?: DockerResources;\n}\n\n/**\n * Options for ComposeStrategy - manages multi-container environments.\n */\nexport interface ComposeSandboxOptions {\n /** Path to docker-compose.yml file */\n compose: string;\n /** Service name to execute commands in (required) */\n service: string;\n /** Resource limits (applied to target service only) */\n resources?: DockerResources;\n // Note: mounts must be defined in compose file, not here\n}\n\n/**\n * Union type for Docker sandbox options.\n * - RuntimeSandboxOptions: Runtime package/binary installation\n * - DockerfileSandboxOptions: Pre-built images from Dockerfile\n * - ComposeSandboxOptions: Multi-container environments via Docker Compose\n */\nexport type DockerSandboxOptions =\n | RuntimeSandboxOptions\n | DockerfileSandboxOptions\n | ComposeSandboxOptions;\n\n/**\n * Extended sandbox interface with disposal method.\n */\nexport interface DockerSandbox extends Sandbox {\n /** Stop and remove the container */\n dispose(): Promise<void>;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Helper Functions\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Detects if the image is Debian-based (uses apt-get) or Alpine-based (uses apk).\n */\nfunction isDebianBased(image: string): boolean {\n const debianPatterns = ['debian', 'ubuntu', 'node', 'python'];\n return debianPatterns.some((pattern) =>\n image.toLowerCase().includes(pattern),\n );\n}\n\n/**\n * Type guard to determine if options are for DockerfileStrategy.\n */\nexport function isDockerfileOptions(\n opts: DockerSandboxOptions,\n): opts is DockerfileSandboxOptions {\n return 'dockerfile' in opts;\n}\n\n/**\n * Type guard to determine if options are for ComposeStrategy.\n */\nexport function isComposeOptions(\n opts: DockerSandboxOptions,\n): opts is ComposeSandboxOptions {\n return 'compose' in opts;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Strategy Pattern - Base Class\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Internal context shared across strategy methods.\n */\ninterface StrategyContext {\n containerId: string;\n image: string;\n}\n\n/**\n * Abstract base class for Docker sandbox creation strategies.\n *\n * Uses the Template Method pattern to define the skeleton of the sandbox\n * creation algorithm, deferring specific steps to subclasses.\n *\n * @example Extending the strategy\n * ```typescript\n * class CustomStrategy extends DockerSandboxStrategy {\n * protected async getImage(): Promise<string> {\n * // Custom image resolution logic\n * return 'my-custom-image:latest';\n * }\n *\n * protected async configure(): Promise<void> {\n * // Custom configuration after container starts\n * }\n * }\n * ```\n */\nexport abstract class DockerSandboxStrategy {\n protected context!: StrategyContext;\n protected mounts: DockerMount[];\n protected resources: DockerResources;\n\n constructor(mounts: DockerMount[] = [], resources: DockerResources = {}) {\n this.mounts = mounts;\n this.resources = resources;\n }\n\n /**\n * Template method - defines the algorithm skeleton for creating a sandbox.\n *\n * Steps:\n * 1. Validate mount paths exist on host\n * 2. Get/build the Docker image (strategy-specific)\n * 3. Start the container\n * 4. Configure the container (strategy-specific)\n * 5. Create and return sandbox methods\n */\n async create(): Promise<DockerSandbox> {\n this.validateMounts();\n const image = await this.getImage();\n const containerId = await this.startContainer(image);\n this.context = { containerId, image };\n\n try {\n await this.configure();\n } catch (error) {\n // Clean up container if configuration fails\n await this.stopContainer(containerId);\n throw error;\n }\n\n return this.createSandboxMethods();\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Common implementations (shared by all strategies)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Validates that all mount paths exist on the host filesystem.\n */\n protected validateMounts(): void {\n for (const mount of this.mounts) {\n if (!existsSync(mount.hostPath)) {\n throw new MountPathError(mount.hostPath, mount.containerPath);\n }\n }\n }\n\n /**\n * Builds the docker run command arguments.\n */\n protected buildDockerArgs(image: string, containerId: string): string[] {\n const { memory = '1g', cpus = 2 } = this.resources;\n\n const args: string[] = [\n 'run',\n '-d', // Detached mode\n '--rm', // Remove container when stopped\n '--name',\n containerId,\n `--memory=${memory}`,\n `--cpus=${cpus}`,\n '-w',\n '/workspace', // Set working directory\n ];\n\n // Add mounts\n for (const mount of this.mounts) {\n const mode = mount.readOnly !== false ? 'ro' : 'rw';\n args.push('-v', `${mount.hostPath}:${mount.containerPath}:${mode}`);\n }\n\n // Add image and command to keep container alive\n args.push(image, 'tail', '-f', '/dev/null');\n\n return args;\n }\n\n /**\n * Starts a Docker container with the given image.\n */\n protected async startContainer(image: string): Promise<string> {\n const containerId = `sandbox-${crypto.randomUUID().slice(0, 8)}`;\n const args = this.buildDockerArgs(image, containerId);\n\n try {\n await spawn('docker', args);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n if (\n err.message?.includes('Cannot connect') ||\n err.message?.includes('docker daemon') ||\n err.stderr?.includes('Cannot connect')\n ) {\n throw new DockerNotAvailableError();\n }\n throw new ContainerCreationError(err.message || String(err), image, err);\n }\n\n return containerId;\n }\n\n /**\n * Stops a Docker container.\n */\n protected async stopContainer(containerId: string): Promise<void> {\n try {\n await spawn('docker', ['stop', containerId]);\n } catch {\n // Container may already be stopped, ignore errors\n }\n }\n\n /**\n * Executes a command in the container.\n */\n protected async exec(command: string): Promise<CommandResult> {\n try {\n const result = await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n command,\n ]);\n return {\n stdout: result.stdout,\n stderr: result.stderr,\n exitCode: 0,\n };\n } catch (error) {\n const err = error as Error & {\n stdout?: string;\n stderr?: string;\n exitCode?: number;\n };\n return {\n stdout: err.stdout || '',\n stderr: err.stderr || err.message || '',\n exitCode: err.exitCode ?? 1,\n };\n }\n }\n\n /**\n * Creates the DockerSandbox interface with all methods.\n */\n protected createSandboxMethods(): DockerSandbox {\n const { containerId } = this.context;\n\n const sandbox: DockerSandbox = {\n executeCommand: async (command: string): Promise<CommandResult> => {\n return this.exec(command);\n },\n\n readFile: async (path: string): Promise<string> => {\n // Use base64 encoding to preserve exact content (including trailing newlines)\n // nano-spawn strips trailing newlines from stdout, so we encode/decode\n const result = await sandbox.executeCommand(`base64 \"${path}\"`);\n if (result.exitCode !== 0) {\n throw new Error(`Failed to read file \"${path}\": ${result.stderr}`);\n }\n return Buffer.from(result.stdout, 'base64').toString('utf-8');\n },\n\n writeFiles: async (\n files: Array<{ path: string; content: string }>,\n ): Promise<void> => {\n for (const file of files) {\n // Create parent directories\n const dir = file.path.substring(0, file.path.lastIndexOf('/'));\n if (dir) {\n await sandbox.executeCommand(`mkdir -p \"${dir}\"`);\n }\n\n // Use base64 encoding for binary-safe file writes\n const base64Content = Buffer.from(file.content).toString('base64');\n const result = await sandbox.executeCommand(\n `echo \"${base64Content}\" | base64 -d > \"${file.path}\"`,\n );\n\n if (result.exitCode !== 0) {\n throw new Error(\n `Failed to write file \"${file.path}\": ${result.stderr}`,\n );\n }\n }\n },\n\n dispose: async (): Promise<void> => {\n await this.stopContainer(containerId);\n },\n };\n\n return sandbox;\n }\n\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Strategy-specific hooks (to be implemented by subclasses)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Returns the Docker image to use for the container.\n * For RuntimeStrategy: returns the image name directly.\n * For DockerfileStrategy: builds the image and returns the tag.\n */\n protected abstract getImage(): Promise<string>;\n\n /**\n * Configures the container after it starts.\n * For RuntimeStrategy: installs packages and binaries.\n * For DockerfileStrategy: no-op (Dockerfile already configured).\n */\n protected abstract configure(): Promise<void>;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// RuntimeStrategy - Installs packages/binaries at container runtime\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Strategy that uses an existing Docker image and installs packages/binaries\n * at container runtime.\n *\n * This is the \"configure-on-demand\" approach - starts a vanilla image and\n * customizes it by executing installation commands.\n *\n * @example\n * ```typescript\n * const strategy = new RuntimeStrategy(\n * 'alpine:latest',\n * ['curl', 'jq'],\n * [{ name: 'presenterm', url: {...} }],\n * );\n * const sandbox = await strategy.create();\n * ```\n */\nexport class RuntimeStrategy extends DockerSandboxStrategy {\n private image: string;\n private packages: string[];\n private binaries: BinaryInstall[];\n\n constructor(\n image = 'alpine:latest',\n packages: string[] = [],\n binaries: BinaryInstall[] = [],\n mounts?: DockerMount[],\n resources?: DockerResources,\n ) {\n super(mounts, resources);\n this.image = image;\n this.packages = packages;\n this.binaries = binaries;\n }\n\n protected async getImage(): Promise<string> {\n return this.image;\n }\n\n protected async configure(): Promise<void> {\n await this.installPackages();\n await this.installBinaries();\n }\n\n /**\n * Installs packages using the appropriate package manager (apk/apt-get).\n */\n private async installPackages(): Promise<void> {\n if (this.packages.length === 0) return;\n\n const useApt = isDebianBased(this.image);\n const installCmd = useApt\n ? `apt-get update && apt-get install -y ${this.packages.join(' ')}`\n : `apk add --no-cache ${this.packages.join(' ')}`;\n\n try {\n await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n installCmd,\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new PackageInstallError(\n this.packages,\n this.image,\n useApt ? 'apt-get' : 'apk',\n err.stderr || err.message,\n this.context.containerId,\n );\n }\n }\n\n /**\n * Installs binaries from URLs.\n */\n private async installBinaries(): Promise<void> {\n if (this.binaries.length === 0) return;\n\n // Ensure curl is available for downloading\n await this.ensureCurl();\n\n // Detect container architecture\n const arch = await this.detectArchitecture();\n\n // Install each binary\n for (const binary of this.binaries) {\n await this.installBinary(binary, arch);\n }\n }\n\n /**\n * Ensures curl is installed in the container.\n */\n private async ensureCurl(): Promise<void> {\n const checkResult = await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'which',\n 'curl',\n ]).catch(() => null);\n\n if (checkResult) return; // curl already installed\n\n const useApt = isDebianBased(this.image);\n const curlInstallCmd = useApt\n ? 'apt-get update && apt-get install -y curl'\n : 'apk add --no-cache curl';\n\n try {\n await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n curlInstallCmd,\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new BinaryInstallError(\n 'curl',\n 'package-manager',\n `Required for binary downloads: ${err.stderr || err.message}`,\n this.context.containerId,\n );\n }\n }\n\n /**\n * Detects the container's CPU architecture.\n */\n private async detectArchitecture(): Promise<string> {\n try {\n const result = await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'uname',\n '-m',\n ]);\n return result.stdout.trim();\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new DockerSandboxError(\n `Failed to detect container architecture: ${err.stderr || err.message}`,\n this.context.containerId,\n );\n }\n }\n\n /**\n * Installs a single binary from URL.\n */\n private async installBinary(\n binary: BinaryInstall,\n arch: string,\n ): Promise<void> {\n // Resolve URL based on architecture\n let url: string;\n if (typeof binary.url === 'string') {\n url = binary.url;\n } else {\n const archUrl = binary.url[arch as keyof ArchitectureUrls];\n if (!archUrl) {\n throw new BinaryInstallError(\n binary.name,\n `arch:${arch}`,\n `No URL provided for architecture \"${arch}\". Available: ${Object.keys(binary.url).join(', ')}`,\n this.context.containerId,\n );\n }\n url = archUrl;\n }\n\n // Download and install the binary\n const isTarGz = url.endsWith('.tar.gz') || url.endsWith('.tgz');\n let installCmd: string;\n\n if (isTarGz) {\n const binaryPathInArchive = binary.binaryPath || binary.name;\n installCmd = `\n set -e\n TMPDIR=$(mktemp -d)\n cd \"$TMPDIR\"\n curl -fsSL \"${url}\" -o archive.tar.gz\n tar -xzf archive.tar.gz\n BINARY_FILE=$(find . -name \"${binaryPathInArchive}\" -o -name \"${binary.name}\" | head -1)\n if [ -z \"$BINARY_FILE\" ]; then\n echo \"Binary not found in archive. Contents:\" >&2\n find . -type f >&2\n exit 1\n fi\n chmod +x \"$BINARY_FILE\"\n mv \"$BINARY_FILE\" /usr/local/bin/${binary.name}\n cd /\n rm -rf \"$TMPDIR\"\n `;\n } else {\n installCmd = `\n curl -fsSL \"${url}\" -o /usr/local/bin/${binary.name}\n chmod +x /usr/local/bin/${binary.name}\n `;\n }\n\n try {\n await spawn('docker', [\n 'exec',\n this.context.containerId,\n 'sh',\n '-c',\n installCmd,\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new BinaryInstallError(\n binary.name,\n url,\n err.stderr || err.message,\n this.context.containerId,\n );\n }\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// DockerfileStrategy - Builds image from Dockerfile\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Strategy that builds a custom Docker image from a Dockerfile.\n *\n * This is the \"build-once, run-many\" approach - builds the image upfront\n * (with caching) and runs containers from the pre-configured image.\n *\n * Image caching: Uses a deterministic tag based on Dockerfile content hash.\n * If the same Dockerfile is used, the existing image is reused (cache hit).\n *\n * @example Inline Dockerfile\n * ```typescript\n * const strategy = new DockerfileStrategy(`\n * FROM alpine:latest\n * RUN apk add --no-cache curl jq\n * `);\n * const sandbox = await strategy.create();\n * ```\n *\n * @example Dockerfile path\n * ```typescript\n * const strategy = new DockerfileStrategy(\n * './Dockerfile.sandbox',\n * './docker', // build context\n * );\n * const sandbox = await strategy.create();\n * ```\n */\nexport class DockerfileStrategy extends DockerSandboxStrategy {\n private imageTag: string;\n private dockerfile: string;\n private dockerContext: string;\n\n constructor(\n dockerfile: string,\n dockerContext = '.',\n mounts?: DockerMount[],\n resources?: DockerResources,\n ) {\n super(mounts, resources);\n this.dockerfile = dockerfile;\n this.dockerContext = dockerContext;\n this.imageTag = this.computeImageTag();\n }\n\n /**\n * Computes a deterministic image tag based on Dockerfile content.\n * Same Dockerfile \u2192 same tag \u2192 Docker skips rebuild if image exists.\n */\n private computeImageTag(): string {\n const content = this.isInlineDockerfile()\n ? this.dockerfile\n : readFileSync(this.dockerfile, 'utf-8');\n const hash = createHash('sha256')\n .update(content)\n .digest('hex')\n .slice(0, 12);\n return `sandbox-${hash}`;\n }\n\n /**\n * Checks if the dockerfile property is inline content or a file path.\n */\n private isInlineDockerfile(): boolean {\n return this.dockerfile.includes('\\n');\n }\n\n protected async getImage(): Promise<string> {\n // Check if image already exists (cache hit)\n const exists = await this.imageExists();\n if (!exists) {\n await this.buildImage();\n }\n return this.imageTag;\n }\n\n protected async configure(): Promise<void> {\n // No-op - Dockerfile already configured the image\n }\n\n /**\n * Checks if the image already exists locally.\n */\n private async imageExists(): Promise<boolean> {\n try {\n await spawn('docker', ['image', 'inspect', this.imageTag]);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Builds the Docker image from the Dockerfile.\n */\n private async buildImage(): Promise<void> {\n try {\n if (this.isInlineDockerfile()) {\n // Inline Dockerfile - use heredoc via shell\n const buildCmd = `echo '${this.dockerfile.replace(/'/g, \"'\\\\''\")}' | docker build -t ${this.imageTag} -f - ${this.dockerContext}`;\n await spawn('sh', ['-c', buildCmd]);\n } else {\n // Path to Dockerfile\n await spawn('docker', [\n 'build',\n '-t',\n this.imageTag,\n '-f',\n this.dockerfile,\n this.dockerContext,\n ]);\n }\n } catch (error) {\n const err = error as Error & { stderr?: string };\n throw new DockerfileBuildError(err.stderr || err.message);\n }\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// ComposeStrategy - Multi-container environments via Docker Compose\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Strategy that manages multi-container environments using Docker Compose.\n *\n * Unlike other strategies that manage a single container, ComposeStrategy\n * orchestrates multiple services as a unit using docker compose commands.\n *\n * @example\n * ```typescript\n * const strategy = new ComposeStrategy(\n * './docker-compose.yml',\n * 'app', // Service to execute commands in\n * );\n * const sandbox = await strategy.create();\n *\n * // Commands run in the 'app' service\n * await sandbox.executeCommand('node --version');\n *\n * // Can communicate with other services via service names\n * await sandbox.executeCommand('curl http://api:3000/health');\n *\n * // Stops ALL services\n * await sandbox.dispose();\n * ```\n */\nexport class ComposeStrategy extends DockerSandboxStrategy {\n private projectName: string;\n private composeFile: string;\n private service: string;\n\n constructor(\n composeFile: string,\n service: string,\n resources?: DockerResources,\n ) {\n // Pass empty mounts - compose handles its own volumes\n super([], resources);\n this.composeFile = composeFile;\n this.service = service;\n this.projectName = this.computeProjectName();\n }\n\n /**\n * Deterministic project name based on compose file content for caching.\n * Same compose file \u2192 same project name \u2192 faster subsequent startups.\n */\n private computeProjectName(): string {\n const content = readFileSync(this.composeFile, 'utf-8');\n const hash = createHash('sha256').update(content).digest('hex').slice(0, 8);\n return `sandbox-${hash}`;\n }\n\n /**\n * Override: No image to get - compose manages its own images.\n */\n protected async getImage(): Promise<string> {\n return ''; // Not used for compose\n }\n\n /**\n * Override: Start all services with docker compose up.\n */\n protected override async startContainer(_image: string): Promise<string> {\n try {\n await spawn('docker', [\n 'compose',\n '-f',\n this.composeFile,\n '-p',\n this.projectName,\n 'up',\n '-d',\n ]);\n } catch (error) {\n const err = error as Error & { stderr?: string };\n if (err.stderr?.includes('Cannot connect')) {\n throw new DockerNotAvailableError();\n }\n throw new ComposeStartError(this.composeFile, err.stderr || err.message);\n }\n\n // Return project name as the \"container ID\" for context\n return this.projectName;\n }\n\n protected async configure(): Promise<void> {\n // No additional configuration - compose file defines everything\n }\n\n /**\n * Override: Execute commands in the target service.\n */\n protected override async exec(command: string): Promise<CommandResult> {\n try {\n const result = await spawn('docker', [\n 'compose',\n '-f',\n this.composeFile,\n '-p',\n this.projectName,\n 'exec',\n '-T', // -T disables pseudo-TTY\n this.service,\n 'sh',\n '-c',\n command,\n ]);\n return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };\n } catch (error) {\n const err = error as Error & {\n stdout?: string;\n stderr?: string;\n exitCode?: number;\n };\n return {\n stdout: err.stdout || '',\n stderr: err.stderr || err.message || '',\n exitCode: err.exitCode ?? 1,\n };\n }\n }\n\n /**\n * Override: Stop all services with docker compose down.\n */\n protected override async stopContainer(_containerId: string): Promise<void> {\n try {\n await spawn('docker', [\n 'compose',\n '-f',\n this.composeFile,\n '-p',\n this.projectName,\n 'down',\n ]);\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Factory Function\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Creates a Docker-based sandbox for executing commands in an isolated container.\n *\n * Supports three strategies:\n * - **RuntimeStrategy**: Uses existing image, installs packages/binaries at runtime\n * - **DockerfileStrategy**: Builds custom image from Dockerfile (with caching)\n * - **ComposeStrategy**: Multi-container environments via Docker Compose\n *\n * @example RuntimeStrategy (default)\n * ```typescript\n * const sandbox = await createDockerSandbox({\n * image: 'alpine:latest',\n * packages: ['curl', 'jq'],\n * binaries: [{ name: 'presenterm', url: {...} }],\n * });\n * await sandbox.executeCommand('curl --version');\n * await sandbox.dispose();\n * ```\n *\n * @example DockerfileStrategy\n * ```typescript\n * const sandbox = await createDockerSandbox({\n * dockerfile: `\n * FROM alpine:latest\n * RUN apk add --no-cache curl jq\n * `,\n * context: '.',\n * });\n * await sandbox.executeCommand('curl --version');\n * await sandbox.dispose();\n * ```\n *\n * @example ComposeStrategy\n * ```typescript\n * const sandbox = await createDockerSandbox({\n * compose: './docker-compose.yml',\n * service: 'app',\n * });\n * // Commands run in the 'app' service\n * await sandbox.executeCommand('node --version');\n * // Can reach other services by name\n * await sandbox.executeCommand('curl http://db:5432');\n * await sandbox.dispose(); // Stops ALL services\n * ```\n */\nexport async function createDockerSandbox(\n options: DockerSandboxOptions = {},\n): Promise<DockerSandbox> {\n let strategy: DockerSandboxStrategy;\n\n if (isComposeOptions(options)) {\n strategy = new ComposeStrategy(\n options.compose,\n options.service,\n options.resources,\n );\n } else if (isDockerfileOptions(options)) {\n strategy = new DockerfileStrategy(\n options.dockerfile,\n options.context,\n options.mounts,\n options.resources,\n );\n } else {\n strategy = new RuntimeStrategy(\n options.image,\n options.packages,\n options.binaries,\n options.mounts,\n options.resources,\n );\n }\n\n return strategy.create();\n}\n\n/**\n * Execute a function with a Docker sandbox that auto-disposes on completion.\n * Ensures cleanup even if the function throws.\n *\n * @example\n * ```typescript\n * const output = await useSandbox(\n * { packages: ['curl', 'jq'] },\n * async (sandbox) => {\n * const result = await sandbox.executeCommand('curl --version');\n * return result.stdout;\n * },\n * );\n * // Container is automatically disposed - no try/finally needed\n * ```\n */\nexport async function useSandbox<T>(\n options: DockerSandboxOptions,\n fn: (sandbox: DockerSandbox) => Promise<T>,\n): Promise<T> {\n const sandbox = await createDockerSandbox(options);\n try {\n return await fn(sandbox);\n } finally {\n await sandbox.dispose();\n }\n}\n", "import {\n createBashTool,\n type CreateBashToolOptions,\n type BashToolkit,\n} from 'bash-tool';\n\nimport {\n createDockerSandbox,\n isComposeOptions,\n isDockerfileOptions,\n type BinaryInstall,\n type DockerMount,\n type DockerResources,\n type DockerSandbox,\n type DockerSandboxOptions,\n} from './docker-sandbox.ts';\n\n/**\n * Base options shared by RuntimeContainerToolOptions and DockerfileContainerToolOptions.\n */\ninterface BaseContainerToolOptions\n extends Omit<CreateBashToolOptions, 'sandbox' | 'uploadDirectory'> {\n /** Directories to mount from host into the container */\n mounts?: DockerMount[];\n /** Resource limits for the container */\n resources?: DockerResources;\n}\n\n/**\n * Options for container tool using RuntimeStrategy.\n * Installs packages/binaries at container runtime.\n */\nexport interface RuntimeContainerToolOptions extends BaseContainerToolOptions {\n /** Docker image to use (default: 'alpine:latest') */\n image?: string;\n /** Packages to install in the container via package manager (apk/apt) */\n packages?: string[];\n /** Binaries to install from URLs (for tools not in package managers) */\n binaries?: BinaryInstall[];\n}\n\n/**\n * Options for container tool using DockerfileStrategy.\n * Builds custom image from Dockerfile (with caching).\n */\nexport interface DockerfileContainerToolOptions extends BaseContainerToolOptions {\n /** Dockerfile content (if contains newlines) or path to Dockerfile */\n dockerfile: string;\n /** Build context directory (default: '.') */\n context?: string;\n}\n\n/**\n * Options for container tool using ComposeStrategy.\n * Manages multi-container environments via Docker Compose.\n */\nexport interface ComposeContainerToolOptions\n extends Omit<CreateBashToolOptions, 'sandbox' | 'uploadDirectory'> {\n /** Path to docker-compose.yml file */\n compose: string;\n /** Service name to execute commands in (required) */\n service: string;\n /** Resource limits for the container */\n resources?: DockerResources;\n // Note: mounts must be defined in compose file, not here\n}\n\n/**\n * Union type for container tool options.\n * - RuntimeContainerToolOptions: Runtime package/binary installation\n * - DockerfileContainerToolOptions: Pre-built images from Dockerfile\n * - ComposeContainerToolOptions: Multi-container environments via Docker Compose\n */\nexport type ContainerToolOptions =\n | RuntimeContainerToolOptions\n | DockerfileContainerToolOptions\n | ComposeContainerToolOptions;\n\n/**\n * Result of creating a container tool.\n * Extends BashToolkit but with DockerSandbox (which has dispose()) instead of base Sandbox.\n */\nexport type ContainerToolResult = Omit<BashToolkit, 'sandbox'> & {\n sandbox: DockerSandbox;\n};\n\n/**\n * Creates a bash tool that runs in a Docker container.\n *\n * This is a high-level wrapper that combines `createDockerSandbox()` and\n * `createBashTool()` into a single call. It provides a convenient way to\n * get a bash tool that executes real binaries in an isolated container.\n *\n * Supports three strategies:\n * - **RuntimeStrategy**: Uses existing image, installs packages/binaries at runtime\n * - **DockerfileStrategy**: Builds custom image from Dockerfile (with caching)\n * - **ComposeStrategy**: Multi-container environments via Docker Compose\n *\n * @example RuntimeStrategy (default)\n * ```typescript\n * const { bash, tools, sandbox } = await createContainerTool({\n * packages: ['curl', 'jq'],\n * mounts: [{\n * hostPath: process.cwd(),\n * containerPath: '/workspace',\n * readOnly: false,\n * }],\n * });\n *\n * // Use with AI SDK\n * const response = await generateText({\n * model: yourModel,\n * tools,\n * prompt: 'Fetch the weather data and parse it with jq',\n * });\n *\n * // Clean up when done\n * await sandbox.dispose();\n * ```\n *\n * @example DockerfileStrategy\n * ```typescript\n * const { bash, tools, sandbox } = await createContainerTool({\n * dockerfile: `\n * FROM python:3.11-slim\n * RUN pip install pandas numpy\n * `,\n * context: '.',\n * mounts: [{\n * hostPath: process.cwd(),\n * containerPath: '/workspace',\n * }],\n * });\n * ```\n *\n * @example ComposeStrategy\n * ```typescript\n * const { bash, tools, sandbox } = await createContainerTool({\n * compose: './docker-compose.yml',\n * service: 'app',\n * });\n * // Commands run in the 'app' service, can reach other services by name\n * await sandbox.dispose(); // Stops ALL services\n * ```\n *\n * @example With hooks for logging\n * ```typescript\n * const { bash, sandbox } = await createContainerTool({\n * packages: ['python3'],\n * onBeforeBashCall: ({ command }) => {\n * console.log('Running:', command);\n * },\n * onAfterBashCall: ({ command, result }) => {\n * console.log(`Exit code: ${result.exitCode}`);\n * },\n * });\n * ```\n */\nexport async function createContainerTool(\n options: ContainerToolOptions = {},\n): Promise<ContainerToolResult> {\n // Extract sandbox options from bash tool options\n let sandboxOptions: DockerSandboxOptions;\n let bashOptions: Omit<CreateBashToolOptions, 'sandbox' | 'uploadDirectory'>;\n\n if (isComposeOptions(options)) {\n const { compose, service, resources, ...rest } = options;\n sandboxOptions = { compose, service, resources };\n bashOptions = rest;\n } else if (isDockerfileOptions(options)) {\n const { dockerfile, context, mounts, resources, ...rest } = options;\n sandboxOptions = { dockerfile, context, mounts, resources };\n bashOptions = rest;\n } else {\n const { image, packages, binaries, mounts, resources, ...rest } = options;\n sandboxOptions = { image, packages, binaries, mounts, resources };\n bashOptions = rest;\n }\n\n // Create the Docker sandbox\n const sandbox = await createDockerSandbox(sandboxOptions);\n\n // Create the bash tool with our Docker sandbox\n const toolkit = await createBashTool({\n ...bashOptions,\n sandbox,\n });\n\n return {\n bash: toolkit.bash,\n tools: toolkit.tools,\n sandbox,\n };\n}\n", "import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport YAML from 'yaml';\n\nimport type { ParsedSkillMd, SkillMetadata } from './types.ts';\n\n/**\n * Parse YAML frontmatter from a SKILL.md file content.\n *\n * Frontmatter format:\n * ```\n * ---\n * name: skill-name\n * description: Skill description here\n * ---\n *\n * # Markdown body\n * ```\n */\nexport function parseFrontmatter(content: string): ParsedSkillMd {\n const frontmatterRegex = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?([\\s\\S]*)$/;\n const match = content.match(frontmatterRegex);\n\n if (!match) {\n throw new Error('Invalid SKILL.md: missing or malformed frontmatter');\n }\n\n const [, yamlContent, body] = match;\n const frontmatter = YAML.parse(yamlContent) as Record<string, unknown>;\n\n if (!frontmatter.name || typeof frontmatter.name !== 'string') {\n throw new Error('Invalid SKILL.md: frontmatter must have a \"name\" field');\n }\n\n if (!frontmatter.description || typeof frontmatter.description !== 'string') {\n throw new Error(\n 'Invalid SKILL.md: frontmatter must have a \"description\" field',\n );\n }\n\n return {\n frontmatter: frontmatter as ParsedSkillMd['frontmatter'],\n body: body.trim(),\n };\n}\n\n/**\n * Load skill metadata from a SKILL.md file.\n * Only parses frontmatter, does not load full body into memory.\n * This is the core of progressive disclosure - metadata only at startup.\n */\nexport function loadSkillMetadata(skillMdPath: string): SkillMetadata {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n const parsed = parseFrontmatter(content);\n const skillDir = path.dirname(skillMdPath);\n\n return {\n name: parsed.frontmatter.name,\n description: parsed.frontmatter.description,\n path: skillDir,\n skillMdPath,\n };\n}\n\n/**\n * Discover all skills in a directory.\n * Looks for subdirectories containing SKILL.md files.\n * Only loads metadata - full content is read by LLM when needed.\n */\nexport function discoverSkillsInDirectory(directory: string): SkillMetadata[] {\n const skills: SkillMetadata[] = [];\n\n // Expand ~ to home directory\n const expandedDir = directory.startsWith('~')\n ? path.join(process.env.HOME || '', directory.slice(1))\n : directory;\n if (!fs.existsSync(expandedDir)) {\n return skills;\n }\n\n const entries = fs.readdirSync(expandedDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const skillMdPath = path.join(expandedDir, entry.name, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) continue;\n\n try {\n const metadata = loadSkillMetadata(skillMdPath);\n skills.push(metadata);\n } catch (error) {\n // Skip invalid skills, log warning\n console.warn(`Warning: Failed to load skill at ${skillMdPath}:`, error);\n }\n }\n\n return skills;\n}\n", "import type { ContextFragment } from '../fragments.ts';\nimport { discoverSkillsInDirectory } from './loader.ts';\nimport type { SkillMetadata, SkillsFragmentOptions } from './types.ts';\n\n/**\n * Create a context fragment containing available skills metadata.\n *\n * Follows Anthropic's progressive disclosure pattern:\n * - At startup: only skill metadata (name, description, path) is injected\n * - At runtime: LLM reads full SKILL.md using file tools when relevant\n *\n * @param options - Configuration including paths to scan and optional filtering\n *\n * @example\n * ```ts\n * const context = new ContextEngine({ userId: 'demo-user', store, chatId: 'demo' })\n * .set(\n * role('You are a helpful assistant.'),\n * skills({\n * paths: [\n * { host: './skills', sandbox: '/skills/skills' }\n * ]\n * }),\n * );\n *\n * // LLM now sees skill metadata with sandbox paths and can read full SKILL.md\n * ```\n */\nexport function skills(options: SkillsFragmentOptions): ContextFragment {\n // Build host-to-sandbox mapping for path rewriting\n const pathMapping = new Map<string, string>();\n for (const { host, sandbox } of options.paths) {\n pathMapping.set(host, sandbox);\n }\n\n // Discover skills from all host paths (later paths override earlier ones)\n const skillsMap = new Map<string, SkillMetadata>();\n for (const { host } of options.paths) {\n const discovered = discoverSkillsInDirectory(host);\n for (const skill of discovered) {\n skillsMap.set(skill.name, skill);\n }\n }\n const allSkills = Array.from(skillsMap.values());\n\n // Apply filtering\n let filteredSkills = allSkills;\n if (options.include) {\n filteredSkills = allSkills.filter((s) => options.include!.includes(s.name));\n }\n if (options.exclude) {\n filteredSkills = filteredSkills.filter(\n (s) => !options.exclude!.includes(s.name),\n );\n }\n\n // Convert skills to ContextFragments with sandbox paths\n const skillFragments: ContextFragment[] = filteredSkills.map((skill) => {\n const originalPath = skill.skillMdPath;\n let sandboxPath = originalPath;\n\n // Rewrite path from host to sandbox\n for (const [host, sandbox] of pathMapping) {\n if (originalPath.startsWith(host)) {\n const relativePath = originalPath.slice(host.length);\n sandboxPath = sandbox + relativePath;\n break;\n }\n }\n\n return {\n name: 'skill',\n data: {\n name: skill.name,\n path: sandboxPath,\n description: skill.description,\n },\n metadata: { originalPath },\n };\n });\n\n return {\n name: 'available_skills',\n data: [\n {\n name: 'instructions',\n data: SKILLS_INSTRUCTIONS,\n } as ContextFragment,\n ...skillFragments,\n ],\n metadata: {\n paths: options.paths, // Store original path mappings for getSkillMounts()\n },\n };\n}\n\n/**\n * Instructions for the LLM on how to use available skills.\n * Follows Anthropic's progressive disclosure - LLM reads files when needed.\n */\nconst SKILLS_INSTRUCTIONS = `When a user's request matches one of the skills listed below, read the skill's SKILL.md file to get detailed instructions before proceeding. Skills provide specialized knowledge and workflows for specific tasks.\n\nTo use a skill:\n1. Identify if the user's request matches a skill's description\n2. Read the SKILL.md file at the skill's path to load full instructions\n3. Follow the skill's guidance to complete the task\n\nSkills are only loaded when relevant - don't read skill files unless needed.`;\n", "import { DatabaseSync, type SQLInputValue } from 'node:sqlite';\n\nimport type {\n BranchData,\n BranchInfo,\n ChatData,\n ChatInfo,\n CheckpointData,\n CheckpointInfo,\n DeleteChatOptions,\n GraphBranch,\n GraphCheckpoint,\n GraphData,\n GraphNode,\n ListChatsOptions,\n MessageData,\n SearchOptions,\n SearchResult,\n StoredChatData,\n} from './store.ts';\nimport { ContextStore } from './store.ts';\n\nconst STORE_DDL = `\n-- Chats table\n-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates\nCREATE TABLE IF NOT EXISTS chats (\n id TEXT PRIMARY KEY,\n userId TEXT NOT NULL,\n title TEXT,\n metadata TEXT,\n createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),\n updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);\nCREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);\n\n-- Messages table (nodes in the DAG)\nCREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n parentId TEXT,\n name TEXT NOT NULL,\n type TEXT,\n data TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (parentId) REFERENCES messages(id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);\nCREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);\n\n-- Branches table (pointers to head messages)\nCREATE TABLE IF NOT EXISTS branches (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n headMessageId TEXT,\n isActive INTEGER NOT NULL DEFAULT 0,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (headMessageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);\n\n-- Checkpoints table (pointers to message nodes)\nCREATE TABLE IF NOT EXISTS checkpoints (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n messageId TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (messageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);\n\n-- FTS5 virtual table for full-text search\n-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)\n-- Only 'content' is indexed for full-text search\nCREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(\n messageId UNINDEXED,\n chatId UNINDEXED,\n name UNINDEXED,\n content,\n tokenize='porter unicode61'\n);\n`;\n\n/**\n * SQLite-based context store using graph model.\n *\n * Uses node:sqlite's synchronous DatabaseSync for persistence.\n * Messages are stored as nodes in a DAG with parentId links.\n */\nexport class SqliteContextStore extends ContextStore {\n #db: DatabaseSync;\n\n constructor(path: string) {\n super();\n this.#db = new DatabaseSync(path);\n this.#db.exec('PRAGMA foreign_keys = ON');\n this.#db.exec(STORE_DDL);\n }\n\n /**\n * Execute a function within a transaction.\n * Automatically commits on success or rolls back on error.\n */\n #useTransaction<T>(fn: () => T): T {\n this.#db.exec('BEGIN TRANSACTION');\n try {\n const result = fn();\n this.#db.exec('COMMIT');\n return result;\n } catch (error) {\n this.#db.exec('ROLLBACK');\n throw error;\n }\n }\n\n // ==========================================================================\n // Chat Operations\n // ==========================================================================\n\n async createChat(chat: ChatData): Promise<void> {\n this.#useTransaction(() => {\n // Create chat (createdAt and updatedAt are auto-set by SQLite DEFAULT)\n this.#db\n .prepare(\n `INSERT INTO chats (id, userId, title, metadata)\n VALUES (?, ?, ?, ?)`,\n )\n .run(\n chat.id,\n chat.userId,\n chat.title ?? null,\n chat.metadata ? JSON.stringify(chat.metadata) : null,\n );\n\n // Create \"main\" branch\n this.#db\n .prepare(\n `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)\n VALUES (?, ?, 'main', NULL, 1, ?)`,\n )\n .run(crypto.randomUUID(), chat.id, Date.now());\n });\n }\n\n async upsertChat(chat: ChatData): Promise<StoredChatData> {\n return this.#useTransaction(() => {\n // Insert if not exists, no-op update if exists (to trigger RETURNING)\n const row = this.#db\n .prepare(\n `INSERT INTO chats (id, userId, title, metadata)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET id = excluded.id\n RETURNING *`,\n )\n .get(\n chat.id,\n chat.userId,\n chat.title ?? null,\n chat.metadata ? JSON.stringify(chat.metadata) : null,\n ) as {\n id: string;\n userId: string;\n title: string | null;\n metadata: string | null;\n createdAt: number;\n updatedAt: number;\n };\n\n // Ensure \"main\" branch exists (INSERT OR IGNORE uses UNIQUE(chatId, name) constraint)\n this.#db\n .prepare(\n `INSERT OR IGNORE INTO branches (id, chatId, name, headMessageId, isActive, createdAt)\n VALUES (?, ?, 'main', NULL, 1, ?)`,\n )\n .run(crypto.randomUUID(), chat.id, Date.now());\n\n return {\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n });\n }\n\n async getChat(chatId: string): Promise<StoredChatData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM chats WHERE id = ?')\n .get(chatId) as\n | {\n id: string;\n userId: string;\n title: string | null;\n metadata: string | null;\n createdAt: number;\n updatedAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n }\n\n async updateChat(\n chatId: string,\n updates: Partial<Pick<ChatData, 'title' | 'metadata'>>,\n ): Promise<StoredChatData> {\n const setClauses: string[] = [\"updatedAt = strftime('%s', 'now') * 1000\"];\n const params: SQLInputValue[] = [];\n\n if (updates.title !== undefined) {\n setClauses.push('title = ?');\n params.push(updates.title ?? null);\n }\n if (updates.metadata !== undefined) {\n setClauses.push('metadata = ?');\n params.push(JSON.stringify(updates.metadata));\n }\n\n params.push(chatId);\n const row = this.#db\n .prepare(\n `UPDATE chats SET ${setClauses.join(', ')} WHERE id = ? RETURNING *`,\n )\n .get(...params) as {\n id: string;\n userId: string;\n title: string | null;\n metadata: string | null;\n createdAt: number;\n updatedAt: number;\n };\n\n return {\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n }\n\n async listChats(options?: ListChatsOptions): Promise<ChatInfo[]> {\n const params: SQLInputValue[] = [];\n const whereClauses: string[] = [];\n let limitClause = '';\n\n // Build WHERE clause for userId filter\n if (options?.userId) {\n whereClauses.push('c.userId = ?');\n params.push(options.userId);\n }\n\n // Build WHERE clause for metadata filter (exact match on top-level field)\n if (options?.metadata) {\n whereClauses.push(`json_extract(c.metadata, '$.' || ?) = ?`);\n params.push(options.metadata.key);\n params.push(\n typeof options.metadata.value === 'boolean'\n ? options.metadata.value\n ? 1\n : 0\n : options.metadata.value,\n );\n }\n\n const whereClause =\n whereClauses.length > 0 ? `WHERE ${whereClauses.join(' AND ')}` : '';\n\n // Build LIMIT/OFFSET clause\n if (options?.limit !== undefined) {\n limitClause = ' LIMIT ?';\n params.push(options.limit);\n if (options.offset !== undefined) {\n limitClause += ' OFFSET ?';\n params.push(options.offset);\n }\n }\n\n const rows = this.#db\n .prepare(\n `SELECT\n c.id,\n c.userId,\n c.title,\n c.metadata,\n c.createdAt,\n c.updatedAt,\n COUNT(DISTINCT m.id) as messageCount,\n COUNT(DISTINCT b.id) as branchCount\n FROM chats c\n LEFT JOIN messages m ON m.chatId = c.id\n LEFT JOIN branches b ON b.chatId = c.id\n ${whereClause}\n GROUP BY c.id\n ORDER BY c.updatedAt DESC${limitClause}`,\n )\n .all(...params) as {\n id: string;\n userId: string;\n title: string | null;\n metadata: string | null;\n createdAt: number;\n updatedAt: number;\n messageCount: number;\n branchCount: number;\n }[];\n\n return rows.map((row) => ({\n id: row.id,\n userId: row.userId,\n title: row.title ?? undefined,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n messageCount: row.messageCount,\n branchCount: row.branchCount,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n }));\n }\n\n async deleteChat(\n chatId: string,\n options?: DeleteChatOptions,\n ): Promise<boolean> {\n return this.#useTransaction(() => {\n // Get message IDs before deletion for FTS cleanup\n const messageIds = this.#db\n .prepare('SELECT id FROM messages WHERE chatId = ?')\n .all(chatId) as { id: string }[];\n\n // Build the delete query with optional userId check\n let sql = 'DELETE FROM chats WHERE id = ?';\n const params: SQLInputValue[] = [chatId];\n\n if (options?.userId !== undefined) {\n sql += ' AND userId = ?';\n params.push(options.userId);\n }\n\n const result = this.#db.prepare(sql).run(...params);\n\n // Clean up FTS entries (CASCADE handles messages, branches, checkpoints)\n if (result.changes > 0 && messageIds.length > 0) {\n const placeholders = messageIds.map(() => '?').join(', ');\n this.#db\n .prepare(\n `DELETE FROM messages_fts WHERE messageId IN (${placeholders})`,\n )\n .run(...messageIds.map((m) => m.id));\n }\n\n return result.changes > 0;\n });\n }\n\n // ==========================================================================\n // Message Operations (Graph Nodes)\n // ==========================================================================\n\n async addMessage(message: MessageData): Promise<void> {\n // Upsert message; CASE handles self-reference (parentId === id) by preserving existing parentId\n this.#db\n .prepare(\n `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)\n VALUES (\n ?1,\n ?2,\n CASE WHEN ?3 = ?1 THEN (SELECT parentId FROM messages WHERE id = ?1) ELSE ?3 END,\n ?4,\n ?5,\n ?6,\n ?7\n )\n ON CONFLICT(id) DO UPDATE SET\n name = excluded.name,\n type = excluded.type,\n data = excluded.data`,\n )\n .run(\n message.id,\n message.chatId,\n message.parentId,\n message.name,\n message.type ?? null,\n JSON.stringify(message.data),\n message.createdAt,\n );\n\n // Index in FTS for search\n const content =\n typeof message.data === 'string'\n ? message.data\n : JSON.stringify(message.data);\n\n // Delete existing FTS entry if any (for upsert), then insert new one\n this.#db\n .prepare(`DELETE FROM messages_fts WHERE messageId = ?`)\n .run(message.id);\n this.#db\n .prepare(\n `INSERT INTO messages_fts(messageId, chatId, name, content)\n VALUES (?, ?, ?, ?)`,\n )\n .run(message.id, message.chatId, message.name, content);\n }\n\n async getMessage(messageId: string): Promise<MessageData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM messages WHERE id = ?')\n .get(messageId) as\n | {\n id: string;\n chatId: string;\n parentId: string | null;\n name: string;\n type: string | null;\n data: string;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n parentId: row.parentId,\n name: row.name,\n type: row.type ?? undefined,\n data: JSON.parse(row.data),\n createdAt: row.createdAt,\n };\n }\n\n async getMessageChain(headId: string): Promise<MessageData[]> {\n // Walk up the parent chain using recursive CTE with depth tracking\n // The CTE walks from head (newest) to root (oldest), so we track depth\n // and order by depth DESC to get chronological order (root first)\n const rows = this.#db\n .prepare(\n `WITH RECURSIVE chain AS (\n SELECT *, 0 as depth FROM messages WHERE id = ?\n UNION ALL\n SELECT m.*, c.depth + 1 FROM messages m\n INNER JOIN chain c ON m.id = c.parentId\n )\n SELECT * FROM chain\n ORDER BY depth DESC`,\n )\n .all(headId) as {\n id: string;\n chatId: string;\n parentId: string | null;\n name: string;\n type: string | null;\n data: string;\n createdAt: number;\n depth: number;\n }[];\n\n return rows.map((row) => ({\n id: row.id,\n chatId: row.chatId,\n parentId: row.parentId,\n name: row.name,\n type: row.type ?? undefined,\n data: JSON.parse(row.data),\n createdAt: row.createdAt,\n }));\n }\n\n async hasChildren(messageId: string): Promise<boolean> {\n const row = this.#db\n .prepare(\n 'SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren',\n )\n .get(messageId) as { hasChildren: number };\n\n return row.hasChildren === 1;\n }\n\n async getMessages(chatId: string): Promise<MessageData[]> {\n const chat = await this.getChat(chatId);\n if (!chat) {\n throw new Error(`Chat \"${chatId}\" not found`);\n }\n\n const activeBranch = await this.getActiveBranch(chatId);\n if (!activeBranch?.headMessageId) {\n return [];\n }\n\n return this.getMessageChain(activeBranch.headMessageId);\n }\n\n // ==========================================================================\n // Branch Operations\n // ==========================================================================\n\n async createBranch(branch: BranchData): Promise<void> {\n this.#db\n .prepare(\n `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)\n VALUES (?, ?, ?, ?, ?, ?)`,\n )\n .run(\n branch.id,\n branch.chatId,\n branch.name,\n branch.headMessageId,\n branch.isActive ? 1 : 0,\n branch.createdAt,\n );\n }\n\n async getBranch(\n chatId: string,\n name: string,\n ): Promise<BranchData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM branches WHERE chatId = ? AND name = ?')\n .get(chatId, name) as\n | {\n id: string;\n chatId: string;\n name: string;\n headMessageId: string | null;\n isActive: number;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n name: row.name,\n headMessageId: row.headMessageId,\n isActive: row.isActive === 1,\n createdAt: row.createdAt,\n };\n }\n\n async getActiveBranch(chatId: string): Promise<BranchData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM branches WHERE chatId = ? AND isActive = 1')\n .get(chatId) as\n | {\n id: string;\n chatId: string;\n name: string;\n headMessageId: string | null;\n isActive: number;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n name: row.name,\n headMessageId: row.headMessageId,\n isActive: true,\n createdAt: row.createdAt,\n };\n }\n\n async setActiveBranch(chatId: string, branchId: string): Promise<void> {\n // Deactivate all branches for this chat\n this.#db\n .prepare('UPDATE branches SET isActive = 0 WHERE chatId = ?')\n .run(chatId);\n\n // Activate the specified branch\n this.#db\n .prepare('UPDATE branches SET isActive = 1 WHERE id = ?')\n .run(branchId);\n }\n\n async updateBranchHead(\n branchId: string,\n messageId: string | null,\n ): Promise<void> {\n this.#db\n .prepare('UPDATE branches SET headMessageId = ? WHERE id = ?')\n .run(messageId, branchId);\n }\n\n async listBranches(chatId: string): Promise<BranchInfo[]> {\n // Get branches with message count by walking the chain\n const branches = this.#db\n .prepare(\n `SELECT\n b.id,\n b.name,\n b.headMessageId,\n b.isActive,\n b.createdAt\n FROM branches b\n WHERE b.chatId = ?\n ORDER BY b.createdAt ASC`,\n )\n .all(chatId) as {\n id: string;\n name: string;\n headMessageId: string | null;\n isActive: number;\n createdAt: number;\n }[];\n\n // For each branch, count messages in the chain\n const result: BranchInfo[] = [];\n for (const branch of branches) {\n let messageCount = 0;\n if (branch.headMessageId) {\n const countRow = this.#db\n .prepare(\n `WITH RECURSIVE chain AS (\n SELECT id, parentId FROM messages WHERE id = ?\n UNION ALL\n SELECT m.id, m.parentId FROM messages m\n INNER JOIN chain c ON m.id = c.parentId\n )\n SELECT COUNT(*) as count FROM chain`,\n )\n .get(branch.headMessageId) as { count: number };\n messageCount = countRow.count;\n }\n\n result.push({\n id: branch.id,\n name: branch.name,\n headMessageId: branch.headMessageId,\n isActive: branch.isActive === 1,\n messageCount,\n createdAt: branch.createdAt,\n });\n }\n\n return result;\n }\n\n // ==========================================================================\n // Checkpoint Operations\n // ==========================================================================\n\n async createCheckpoint(checkpoint: CheckpointData): Promise<void> {\n this.#db\n .prepare(\n `INSERT INTO checkpoints (id, chatId, name, messageId, createdAt)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(chatId, name) DO UPDATE SET\n messageId = excluded.messageId,\n createdAt = excluded.createdAt`,\n )\n .run(\n checkpoint.id,\n checkpoint.chatId,\n checkpoint.name,\n checkpoint.messageId,\n checkpoint.createdAt,\n );\n }\n\n async getCheckpoint(\n chatId: string,\n name: string,\n ): Promise<CheckpointData | undefined> {\n const row = this.#db\n .prepare('SELECT * FROM checkpoints WHERE chatId = ? AND name = ?')\n .get(chatId, name) as\n | {\n id: string;\n chatId: string;\n name: string;\n messageId: string;\n createdAt: number;\n }\n | undefined;\n\n if (!row) {\n return undefined;\n }\n\n return {\n id: row.id,\n chatId: row.chatId,\n name: row.name,\n messageId: row.messageId,\n createdAt: row.createdAt,\n };\n }\n\n async listCheckpoints(chatId: string): Promise<CheckpointInfo[]> {\n const rows = this.#db\n .prepare(\n `SELECT id, name, messageId, createdAt\n FROM checkpoints\n WHERE chatId = ?\n ORDER BY createdAt DESC`,\n )\n .all(chatId) as {\n id: string;\n name: string;\n messageId: string;\n createdAt: number;\n }[];\n\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n messageId: row.messageId,\n createdAt: row.createdAt,\n }));\n }\n\n async deleteCheckpoint(chatId: string, name: string): Promise<void> {\n this.#db\n .prepare('DELETE FROM checkpoints WHERE chatId = ? AND name = ?')\n .run(chatId, name);\n }\n\n // ==========================================================================\n // Search Operations\n // ==========================================================================\n\n async searchMessages(\n chatId: string,\n query: string,\n options?: SearchOptions,\n ): Promise<SearchResult[]> {\n const limit = options?.limit ?? 20;\n const roles = options?.roles;\n\n // Build the query dynamically based on options\n let sql = `\n SELECT\n m.id,\n m.chatId,\n m.parentId,\n m.name,\n m.type,\n m.data,\n m.createdAt,\n fts.rank,\n snippet(messages_fts, 3, '<mark>', '</mark>', '...', 32) as snippet\n FROM messages_fts fts\n JOIN messages m ON m.id = fts.messageId\n WHERE messages_fts MATCH ?\n AND fts.chatId = ?\n `;\n\n const params: SQLInputValue[] = [query, chatId];\n\n if (roles && roles.length > 0) {\n const placeholders = roles.map(() => '?').join(', ');\n sql += ` AND fts.name IN (${placeholders})`;\n params.push(...roles);\n }\n\n sql += ' ORDER BY fts.rank LIMIT ?';\n params.push(limit);\n\n const rows = this.#db.prepare(sql).all(...params) as {\n id: string;\n chatId: string;\n parentId: string | null;\n name: string;\n type: string | null;\n data: string;\n createdAt: number;\n rank: number;\n snippet: string;\n }[];\n\n return rows.map((row) => ({\n message: {\n id: row.id,\n chatId: row.chatId,\n parentId: row.parentId,\n name: row.name,\n type: row.type ?? undefined,\n data: JSON.parse(row.data),\n createdAt: row.createdAt,\n },\n rank: row.rank,\n snippet: row.snippet,\n }));\n }\n\n // ==========================================================================\n // Visualization Operations\n // ==========================================================================\n\n async getGraph(chatId: string): Promise<GraphData> {\n // Get all messages for complete graph\n const messageRows = this.#db\n .prepare(\n `SELECT id, parentId, name, data, createdAt\n FROM messages\n WHERE chatId = ?\n ORDER BY createdAt ASC`,\n )\n .all(chatId) as {\n id: string;\n parentId: string | null;\n name: string;\n data: string;\n createdAt: number;\n }[];\n\n const nodes: GraphNode[] = messageRows.map((row) => {\n const data = JSON.parse(row.data);\n const content = typeof data === 'string' ? data : JSON.stringify(data);\n return {\n id: row.id,\n parentId: row.parentId,\n role: row.name,\n content: content.length > 50 ? content.slice(0, 50) + '...' : content,\n createdAt: row.createdAt,\n };\n });\n\n // Get all branches\n const branchRows = this.#db\n .prepare(\n `SELECT name, headMessageId, isActive\n FROM branches\n WHERE chatId = ?\n ORDER BY createdAt ASC`,\n )\n .all(chatId) as {\n name: string;\n headMessageId: string | null;\n isActive: number;\n }[];\n\n const branches: GraphBranch[] = branchRows.map((row) => ({\n name: row.name,\n headMessageId: row.headMessageId,\n isActive: row.isActive === 1,\n }));\n\n // Get all checkpoints\n const checkpointRows = this.#db\n .prepare(\n `SELECT name, messageId\n FROM checkpoints\n WHERE chatId = ?\n ORDER BY createdAt ASC`,\n )\n .all(chatId) as {\n name: string;\n messageId: string;\n }[];\n\n const checkpoints: GraphCheckpoint[] = checkpointRows.map((row) => ({\n name: row.name,\n messageId: row.messageId,\n }));\n\n return {\n chatId,\n nodes,\n branches,\n checkpoints,\n };\n }\n}\n", "import { SqliteContextStore } from './sqlite.store.ts';\n\n/**\n * In-memory context store.\n *\n * Uses SQLite's :memory: database for non-persistent storage.\n * Useful for testing and short-lived sessions.\n */\nexport class InMemoryContextStore extends SqliteContextStore {\n constructor() {\n super(':memory:');\n }\n}\n", "import type { GraphData, GraphNode } from './store/store.ts';\n\n/**\n * Render a graph as ASCII art.\n *\n * @param data - The graph data to visualize\n * @returns ASCII art representation of the graph\n *\n * @example\n * ```ts\n * const graph = await store.getGraph('my-chat');\n * console.log(visualizeGraph(graph));\n * ```\n */\nexport function visualizeGraph(data: GraphData): string {\n if (data.nodes.length === 0) {\n return `[chat: ${data.chatId}]\\n\\n(empty)`;\n }\n\n // Build lookup maps\n const childrenByParentId = new Map<string | null, GraphNode[]>();\n const branchHeads = new Map<string, string[]>(); // messageId -> branch names\n const checkpointsByMessageId = new Map<string, string[]>(); // messageId -> checkpoint names\n\n for (const node of data.nodes) {\n const children = childrenByParentId.get(node.parentId) ?? [];\n children.push(node);\n childrenByParentId.set(node.parentId, children);\n }\n\n for (const branch of data.branches) {\n if (branch.headMessageId) {\n const heads = branchHeads.get(branch.headMessageId) ?? [];\n heads.push(branch.isActive ? `${branch.name} *` : branch.name);\n branchHeads.set(branch.headMessageId, heads);\n }\n }\n\n for (const checkpoint of data.checkpoints) {\n const cps = checkpointsByMessageId.get(checkpoint.messageId) ?? [];\n cps.push(checkpoint.name);\n checkpointsByMessageId.set(checkpoint.messageId, cps);\n }\n\n // Find root nodes (parentId === null)\n const roots = childrenByParentId.get(null) ?? [];\n\n const lines: string[] = [`[chat: ${data.chatId}]`, ''];\n\n // Recursively render the tree\n function renderNode(\n node: GraphNode,\n prefix: string,\n isLast: boolean,\n isRoot: boolean,\n ): void {\n const connector = isRoot ? '' : isLast ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 ';\n const contentPreview = node.content.replace(/\\n/g, ' ');\n\n let line = `${prefix}${connector}${node.id.slice(0, 8)} (${node.role}): \"${contentPreview}\"`;\n\n // Add branch markers\n const branches = branchHeads.get(node.id);\n if (branches) {\n line += ` <- [${branches.join(', ')}]`;\n }\n\n // Add checkpoint markers\n const checkpoints = checkpointsByMessageId.get(node.id);\n if (checkpoints) {\n line += ` {${checkpoints.join(', ')}}`;\n }\n\n lines.push(line);\n\n // Render children\n const children = childrenByParentId.get(node.id) ?? [];\n const childPrefix = isRoot ? '' : prefix + (isLast ? ' ' : '\u2502 ');\n\n for (let i = 0; i < children.length; i++) {\n renderNode(children[i], childPrefix, i === children.length - 1, false);\n }\n }\n\n // Render each root\n for (let i = 0; i < roots.length; i++) {\n renderNode(roots[i], '', i === roots.length - 1, true);\n }\n\n // Add legend\n lines.push('');\n lines.push('Legend: * = active branch, {...} = checkpoint');\n\n return lines.join('\\n');\n}\n", "import { groq } from '@ai-sdk/groq';\nimport {\n type GenerateTextResult,\n NoSuchToolError,\n Output,\n type StreamTextResult,\n type StreamTextTransform,\n type ToolCallRepairFunction,\n type ToolChoice,\n type ToolSet,\n type UIMessageStreamWriter,\n convertToModelMessages,\n createUIMessageStream,\n generateId,\n generateText,\n smoothStream,\n stepCountIs,\n streamText,\n} from 'ai';\nimport chalk from 'chalk';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\n\nimport { type ContextEngine, XmlRenderer } from '../index.ts';\nimport { lastAssistantMessage } from './fragments.ts';\nimport {\n type Guardrail,\n type GuardrailContext,\n runGuardrailChain,\n} from './guardrail.ts';\n\nexport interface CreateAgent<CIn, COut = CIn> {\n name: string;\n context?: ContextEngine;\n tools?: ToolSet;\n model?: AgentModel;\n toolChoice?: ToolChoice<Record<string, COut>>;\n providerOptions?: Parameters<typeof generateText>[0]['providerOptions'];\n logging?: boolean;\n /**\n * Guardrails to apply during streaming.\n * Each guardrail inspects text chunks and can trigger self-correction retries.\n */\n guardrails?: Guardrail[];\n /**\n * Maximum number of retry attempts when guardrails fail (default: 3).\n */\n maxGuardrailRetries?: number;\n}\n\nclass Agent<CIn, COut = CIn> {\n #options: CreateAgent<CIn, COut>;\n #guardrails: Guardrail[] = [];\n readonly tools: ToolSet;\n constructor(options: CreateAgent<CIn, COut>) {\n this.#options = options;\n this.tools = options.tools || {};\n this.#guardrails = options.guardrails || [];\n }\n\n public async generate<COut, CIn = COut>(\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n },\n ): Promise<GenerateTextResult<ToolSet, Output.Output<string, string, any>>> {\n if (!this.#options.context) {\n throw new Error(`Agent ${this.#options.name} is missing a context.`);\n }\n if (!this.#options.model) {\n throw new Error(`Agent ${this.#options.name} is missing a model.`);\n }\n const { messages, systemPrompt } = await this.#options.context.resolve({\n renderer: new XmlRenderer(),\n });\n return generateText({\n abortSignal: config?.abortSignal,\n providerOptions: this.#options.providerOptions,\n model: this.#options.model,\n system: systemPrompt,\n messages: await convertToModelMessages(messages as never),\n stopWhen: stepCountIs(25),\n tools: this.#options.tools,\n experimental_context: contextVariables,\n experimental_repairToolCall: repairToolCall,\n toolChoice: this.#options.toolChoice,\n onStepFinish: (step) => {\n const toolCall = step.toolCalls.at(-1);\n if (toolCall) {\n console.log(\n `Debug: ${chalk.yellow('ToolCalled')}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`,\n );\n }\n },\n });\n }\n\n /**\n * Stream a response from the agent.\n *\n * When guardrails are configured, `toUIMessageStream()` is wrapped to provide\n * self-correction behavior. Direct access to fullStream/textStream bypasses guardrails.\n *\n * @example\n * ```typescript\n * const stream = await agent.stream({});\n *\n * // With guardrails - use toUIMessageStream for protection\n * await printer.readableStream(stream.toUIMessageStream());\n *\n * // Or use printer.stdout which uses toUIMessageStream internally\n * await printer.stdout(stream);\n * ```\n */\n public async stream<COut, CIn = COut>(\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n maxRetries?: number;\n },\n ): Promise<StreamTextResult<ToolSet, never>> {\n if (!this.#options.context) {\n throw new Error(`Agent ${this.#options.name} is missing a context.`);\n }\n if (!this.#options.model) {\n throw new Error(`Agent ${this.#options.name} is missing a model.`);\n }\n\n const result = await this.#createRawStream(contextVariables, config);\n\n if (this.#guardrails.length === 0) {\n return result;\n }\n\n return this.#wrapWithGuardrails(result, contextVariables, config);\n }\n\n /**\n * Create a raw stream without guardrail processing.\n */\n async #createRawStream<COut, CIn = COut>(\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n },\n ) {\n const { messages, systemPrompt } = await this.#options.context!.resolve({\n renderer: new XmlRenderer(),\n });\n\n const runId = generateId();\n return streamText({\n abortSignal: config?.abortSignal,\n providerOptions: this.#options.providerOptions,\n model: this.#options.model!,\n system: systemPrompt,\n messages: await convertToModelMessages(messages as never),\n experimental_repairToolCall: repairToolCall,\n stopWhen: stepCountIs(50),\n experimental_transform: config?.transform ?? smoothStream(),\n tools: this.#options.tools,\n experimental_context: contextVariables,\n toolChoice: this.#options.toolChoice,\n onStepFinish: (step) => {\n const toolCall = step.toolCalls.at(-1);\n if (toolCall) {\n console.log(\n `Debug: (${runId}) ${chalk.bold.yellow('ToolCalled')}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`,\n );\n }\n },\n });\n }\n\n /**\n * Wrap a StreamTextResult with guardrail protection on toUIMessageStream().\n *\n * When a guardrail fails:\n * 1. Accumulated text + feedback is appended as the assistant's self-correction\n * 2. The feedback is written to the output stream (user sees the correction)\n * 3. A new stream is started and the model continues from the correction\n */\n #wrapWithGuardrails<CIn>(\n result: StreamTextResult<ToolSet, never>,\n contextVariables: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n maxRetries?: number;\n },\n ): StreamTextResult<ToolSet, never> {\n const maxRetries =\n config?.maxRetries ?? this.#options.maxGuardrailRetries ?? 3;\n const context = this.#options.context!;\n\n // Save original method BEFORE override (prevents infinite recursion)\n const originalToUIMessageStream = result.toUIMessageStream.bind(result);\n\n // Override toUIMessageStream with guardrail logic\n result.toUIMessageStream = (options) => {\n return createUIMessageStream({\n generateId,\n execute: async ({ writer }) => {\n let currentResult: StreamTextResult<ToolSet, never> = result;\n let attempt = 0;\n\n // Create guardrail context with available tools\n const guardrailContext: GuardrailContext = {\n availableTools: Object.keys(this.tools),\n };\n\n while (attempt < maxRetries) {\n // Check if request was cancelled before starting new attempt\n if (config?.abortSignal?.aborted) {\n writer.write({ type: 'finish' });\n return;\n }\n\n attempt++;\n let accumulatedText = '';\n let guardrailFailed = false;\n let failureFeedback = '';\n\n // Use original method for first result (avoids recursion), new results have their own original\n const uiStream =\n currentResult === result\n ? originalToUIMessageStream(options)\n : currentResult.toUIMessageStream(options);\n\n for await (const part of uiStream) {\n // Run through guardrail chain - guardrails can handle any part type\n const checkResult = runGuardrailChain(\n part,\n this.#guardrails,\n guardrailContext,\n );\n\n if (checkResult.type === 'fail') {\n guardrailFailed = true;\n failureFeedback = checkResult.feedback;\n\n console.log(\n chalk.yellow(\n `[${this.#options.name}] Guardrail triggered (attempt ${attempt}/${maxRetries}): ${failureFeedback.slice(0, 50)}...`,\n ),\n );\n\n break; // Exit stream processing\n }\n\n // Guardrail passed - track text for self-correction context\n if (checkResult.part.type === 'text-delta') {\n accumulatedText += checkResult.part.delta;\n }\n\n // Write the (possibly modified) part to output\n writer.write(checkResult.part as typeof part);\n }\n\n if (!guardrailFailed) {\n // Stream completed successfully\n writer.write({ type: 'finish' });\n return;\n }\n\n // Check if we've exceeded max retries BEFORE writing feedback\n if (attempt >= maxRetries) {\n console.error(\n chalk.red(\n `[${this.#options.name}] Guardrail retry limit (${maxRetries}) exceeded.`,\n ),\n );\n writer.write({ type: 'finish' });\n return;\n }\n\n // Guardrail failed but we have retries left - prepare for retry\n // Write the self-correction feedback to the output stream\n writeText(writer, failureFeedback);\n\n // Add the partial assistant message + feedback to context\n // Uses lastAssistantMessage which finds/reuses the last assistant ID\n const selfCorrectionText = accumulatedText + ' ' + failureFeedback;\n context.set(lastAssistantMessage(selfCorrectionText));\n\n // Save to persist the self-correction (prevents duplicate messages on next resolve)\n await context.save();\n\n // Create new stream for retry\n currentResult = await this.#createRawStream(\n contextVariables,\n config,\n );\n }\n },\n onError: (error) => {\n const message =\n error instanceof Error ? error.message : String(error);\n return `Stream failed: ${message}`;\n },\n });\n };\n\n return result;\n }\n\n clone(overrides?: Partial<CreateAgent<CIn, COut>>): Agent<CIn, COut> {\n return new Agent<CIn, COut>({\n ...this.#options,\n ...overrides,\n });\n }\n}\n\nexport function agent<CIn, COut = CIn>(\n options: CreateAgent<CIn, COut>,\n): Agent<CIn, COut> {\n return new Agent(options);\n}\n\n/**\n * Options for creating a structured output handler.\n */\nexport interface StructuredOutputOptions<TSchema extends z.ZodType> {\n context?: ContextEngine;\n model?: AgentModel;\n schema: TSchema;\n providerOptions?: Parameters<typeof generateText>[0]['providerOptions'];\n tools?: ToolSet;\n}\n\n/**\n * Create a structured output handler that provides simplified access to structured output.\n *\n * @param options - Configuration options including schema\n * @returns Object with generate() and stream() methods\n *\n * @example\n * ```typescript\n * const output = structuredOutput({\n * name: 'extractor',\n * model: groq('...'),\n * context,\n * schema: z.object({\n * name: z.string(),\n * age: z.number(),\n * }),\n * });\n *\n * // Generate - returns only the structured output\n * const result = await output.generate({});\n * // result: { name: string, age: number }\n *\n * // Stream - returns the full stream\n * const stream = await output.stream({});\n * ```\n */\nexport interface StructuredOutputResult<TSchema extends z.ZodType> {\n generate<CIn>(\n contextVariables?: CIn,\n config?: { abortSignal?: AbortSignal },\n ): Promise<z.infer<TSchema>>;\n stream<CIn>(\n contextVariables?: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?: StreamTextTransform<ToolSet> | StreamTextTransform<ToolSet>[];\n },\n ): Promise<StreamTextResult<ToolSet, any>>;\n}\n\nexport function structuredOutput<TSchema extends z.ZodType>(\n options: StructuredOutputOptions<TSchema>,\n): StructuredOutputResult<TSchema> {\n return {\n async generate<CIn>(\n contextVariables?: CIn,\n config?: { abortSignal?: AbortSignal },\n ): Promise<z.infer<TSchema>> {\n if (!options.context) {\n throw new Error(`structuredOutput is missing a context.`);\n }\n if (!options.model) {\n throw new Error(`structuredOutput is missing a model.`);\n }\n\n const { messages, systemPrompt } = await options.context.resolve({\n renderer: new XmlRenderer(),\n });\n\n const result = await generateText({\n abortSignal: config?.abortSignal,\n providerOptions: options.providerOptions,\n model: options.model,\n system: systemPrompt,\n messages: await convertToModelMessages(messages as never),\n stopWhen: stepCountIs(25),\n experimental_repairToolCall: repairToolCall,\n experimental_context: contextVariables,\n output: Output.object({ schema: options.schema }),\n tools: options.tools,\n });\n\n return result.output as z.infer<TSchema>;\n },\n\n async stream<CIn>(\n contextVariables?: CIn,\n config?: {\n abortSignal?: AbortSignal;\n transform?:\n | StreamTextTransform<ToolSet>\n | StreamTextTransform<ToolSet>[];\n },\n ) {\n if (!options.context) {\n throw new Error(`structuredOutput is missing a context.`);\n }\n if (!options.model) {\n throw new Error(`structuredOutput is missing a model.`);\n }\n\n const { messages, systemPrompt } = await options.context.resolve({\n renderer: new XmlRenderer(),\n });\n\n return streamText({\n abortSignal: config?.abortSignal,\n providerOptions: options.providerOptions,\n model: options.model,\n system: systemPrompt,\n experimental_repairToolCall: repairToolCall,\n messages: await convertToModelMessages(messages as never),\n stopWhen: stepCountIs(50),\n experimental_transform: config?.transform ?? smoothStream(),\n experimental_context: contextVariables,\n output: Output.object({ schema: options.schema }),\n tools: options.tools,\n });\n },\n };\n}\n\nconst repairToolCall: ToolCallRepairFunction<ToolSet> = async ({\n toolCall,\n tools,\n inputSchema,\n error,\n}) => {\n console.log(\n `Debug: ${chalk.yellow('RepairingToolCall')}: ${toolCall.toolName}`,\n error.name,\n );\n if (NoSuchToolError.isInstance(error)) {\n return null; // do not attempt to fix invalid tool names\n }\n\n const tool = tools[toolCall.toolName as keyof typeof tools];\n\n const { output } = await generateText({\n model: groq('openai/gpt-oss-20b'),\n output: Output.object({ schema: tool.inputSchema }),\n prompt: [\n `The model tried to call the tool \"${toolCall.toolName}\"` +\n ` with the following inputs:`,\n JSON.stringify(toolCall.input),\n `The tool accepts the following schema:`,\n JSON.stringify(inputSchema(toolCall)),\n 'Please fix the inputs.',\n ].join('\\n'),\n });\n\n return { ...toolCall, input: JSON.stringify(output) };\n};\n\nfunction writeText(writer: UIMessageStreamWriter, text: string) {\n const feedbackPartId = generateId();\n writer.write({\n id: feedbackPartId,\n type: 'text-start',\n });\n writer.write({\n id: feedbackPartId,\n type: 'text-delta',\n delta: ` ${text}`,\n });\n writer.write({\n id: feedbackPartId,\n type: 'text-end',\n });\n}\n", "import { type ContextFragment, fragment } from './fragments.ts';\nimport { XmlRenderer } from './renderers/abstract.renderer.ts';\n\n/**\n * Render fragments to XML.\n *\n * Wraps fragments in a parent tag and renders using XmlRenderer.\n *\n * @param tag - Parent tag name (e.g., 'instructions')\n * @param fragments - Fragments to render\n * @returns XML string\n *\n * @example\n * ```ts\n * const xml = render(\n * 'instructions',\n * persona({ name: 'Freya', role: 'Data Assistant' }),\n * guardrail({ rule: 'Never expose PII' }),\n * );\n * ```\n */\nexport function render(tag: string, ...fragments: ContextFragment[]): string {\n if (fragments.length === 0) {\n return '';\n }\n\n const renderer = new XmlRenderer();\n const wrapped = fragment(tag, ...fragments);\n return renderer.render([wrapped]);\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\nexport interface SqlExtractorOptions {\n validateSql?: boolean;\n skipInvalid?: boolean;\n}\n\nconst outputSchema = z.object({\n question: z\n .string()\n .describe('A natural language question that the SQL query answers'),\n});\n/**\n * SqlExtractor - Generate questions for existing SQL queries.\n *\n * Given a list of SQL queries, uses an LLM to generate the natural\n * language questions they answer.\n */\nexport class SqlExtractor extends PairProducer {\n #sqls: string[];\n #adapter: Adapter;\n #options: SqlExtractorOptions;\n\n /**\n * @param sql - SQL query or queries to generate questions for\n * @param adapter - Database adapter for validation and schema introspection\n * @param options - Extraction configuration\n */\n constructor(\n sql: string[] | string,\n adapter: Adapter,\n options: SqlExtractorOptions = {},\n ) {\n super();\n this.#sqls = Array.isArray(sql) ? sql : [sql];\n this.#adapter = adapter;\n this.#options = options;\n }\n\n /**\n * Generates natural language questions for each SQL query using an LLM.\n * @returns Pairs with generated questions and original SQL\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n const { validateSql = true, skipInvalid = false } = this.#options;\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.#adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n\n for (const sql of this.#sqls) {\n let isValid = true;\n if (validateSql) {\n const error = await this.#adapter.validate(sql);\n isValid = error === undefined || error === null;\n\n if (!isValid && skipInvalid) {\n continue;\n }\n }\n\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `sql-to-question-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'sql_to_question',\n role: 'You are an expert at understanding SQL queries and generating clear, natural language questions that describe what the query retrieves.',\n objective:\n 'Generate clear, natural language questions that describe what SQL queries retrieve',\n }),\n fragment('database_schema', introspection),\n fragment('sql', sql),\n fragment(\n 'task',\n dedent`\n Given the database schema and the SQL query above, generate a single\n natural language question that:\n 1. Accurately describes what information the query retrieves\n 2. Uses natural business language (not SQL terminology)\n 3. Could be asked by a non-technical user\n 4. Is concise but complete\n `,\n ),\n fragment(\n 'examples',\n dedent`\n SQL: SELECT COUNT(*) FROM customers WHERE region = 'NY'\n Question: \"How many customers do we have in New York?\"\n\n SQL: SELECT product_name, SUM(quantity) as total FROM orders GROUP BY product_name ORDER BY total DESC LIMIT 10\n Question: \"What are our top 10 products by quantity sold?\"\n\n SQL: SELECT c.name, COUNT(o.id) FROM customers c LEFT JOIN orders o ON c.id = o.customer_id GROUP BY c.id HAVING COUNT(o.id) = 0\n Question: \"Which customers have never placed an order?\"\n `,\n ),\n user('Generate a natural language question for this SQL query.'),\n );\n\n const sqlToQuestionOutput = structuredOutput({\n model: groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n const output = await sqlToQuestionOutput.generate();\n\n yield [\n {\n question: output.question,\n sql,\n success: isValid,\n },\n ];\n }\n }\n}\n", "import type { UIMessage } from 'ai';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n} from './base-contextual-extractor.ts';\n\nexport type FullContextExtractorOptions = BaseContextualExtractorOptions;\n\n/**\n * Extracts SQL pairs with full conversation context.\n *\n * @example\n * ```typescript\n * const extractor = new FullContextExtractor(messages, adapter);\n * const pairs = await extractor.produce();\n * ```\n */\nexport class FullContextExtractor extends BaseContextualExtractor {\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: FullContextExtractorOptions = {},\n ) {\n super(messages, adapter, options);\n }\n\n /**\n * Add user message to context (keeps all messages).\n */\n protected async onUserMessage(text: string): Promise<void> {\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return all context accumulated so far.\n */\n protected getContextSnapshot(): string[] {\n return [...this.context];\n }\n}\n", "import type { UIMessage } from 'ai';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n} from './base-contextual-extractor.ts';\n\nexport interface WindowedContextExtractorOptions\n extends BaseContextualExtractorOptions {\n windowSize: number;\n}\n\n/**\n * Extracts SQL pairs with a sliding window of conversation context.\n *\n * @example\n * ```typescript\n * const extractor = new WindowedContextExtractor(messages, adapter, {\n * windowSize: 5,\n * });\n * const pairs = await extractor.produce();\n * ```\n */\nexport class WindowedContextExtractor extends BaseContextualExtractor {\n private windowSize: number;\n\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: WindowedContextExtractorOptions,\n ) {\n super(messages, adapter, options);\n this.windowSize = options.windowSize;\n }\n\n /**\n * Add user message to context (keeps all, windowing happens on snapshot).\n */\n protected async onUserMessage(text: string): Promise<void> {\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return only the last N messages based on window size.\n */\n protected getContextSnapshot(): string[] {\n if (this.context.length <= this.windowSize) {\n return [...this.context];\n }\n return this.context.slice(-this.windowSize);\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport type { UIMessage } from 'ai';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n formatConversation,\n resolveContext,\n} from './base-contextual-extractor.ts';\n\nexport type SegmentedContextExtractorOptions = BaseContextualExtractorOptions;\n\nconst topicChangeSchema = z.object({\n isTopicChange: z\n .boolean()\n .describe('Whether the new message represents a topic change'),\n reason: z.string().describe('Brief explanation for the decision'),\n});\n\n/**\n * Detects if a new message represents a topic change from the prior context.\n */\nasync function detectTopicChange(params: {\n context: string;\n newMessage: string;\n}): Promise<{ isTopicChange: boolean; reason: string }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `topic-change-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'topic_change_detector',\n role: 'You are an expert at understanding conversational flow and detecting topic changes.',\n objective: 'Detect significant topic changes in database conversations',\n }),\n fragment('conversation_context', params.context || '(no prior context)'),\n fragment('new_message', params.newMessage),\n fragment(\n 'task',\n dedent`\n Determine if the new message represents a significant topic change from the\n prior conversation context. A topic change occurs when:\n 1. The user asks about a completely different entity/table/domain\n 2. The user starts a new analytical question unrelated to prior discussion\n 3. There's a clear shift in what data or metrics are being discussed\n\n NOT a topic change:\n - Follow-up questions refining the same query (\"filter by...\", \"sort by...\")\n - Questions about the same entities with different conditions\n - Requests for more details on the same topic\n `,\n ),\n fragment(\n 'examples',\n dedent`\n Context: \"Show me customers in NY\" \u2192 \"Sort by revenue\"\n New: \"Filter to those with orders over $1000\"\n Decision: NOT a topic change (still refining customer query)\n\n Context: \"Show me customers in NY\" \u2192 \"Sort by revenue\"\n New: \"What were our total sales last quarter?\"\n Decision: Topic change (shifted from customers to sales metrics)\n\n Context: \"List all products\"\n New: \"How many orders did we have last month?\"\n Decision: Topic change (products \u2192 orders/sales)\n `,\n ),\n user('Determine if this is a topic change.'),\n );\n\n const topicOutput = structuredOutput({\n model: groq('openai/gpt-oss-20b'),\n context,\n schema: topicChangeSchema,\n });\n\n return topicOutput.generate();\n}\n\n/**\n * Extracts SQL pairs with topic-aware context segmentation.\n *\n * When a topic change is detected:\n * 1. The triggering message is resolved to standalone form using LLM\n * 2. Context is reset\n * 3. The resolved message becomes the start of the new context\n *\n * @example\n * ```typescript\n * const extractor = new SegmentedContextExtractor(messages, adapter);\n * const pairs = await extractor.produce();\n * ```\n */\nexport class SegmentedContextExtractor extends BaseContextualExtractor {\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: SegmentedContextExtractorOptions = {},\n ) {\n super(messages, adapter, options);\n }\n\n /**\n * Handle user message with topic change detection.\n * If topic changes, resolve the message to standalone form before resetting.\n *\n * Note: We capture context snapshot before async LLM calls to prevent race conditions\n * where context might be modified during the async operation.\n */\n protected async onUserMessage(text: string): Promise<void> {\n // Check for topic change if we have enough context\n if (this.context.length >= 2) {\n // Capture snapshot BEFORE async calls to prevent race conditions\n const contextSnapshot = [...this.context];\n const { isTopicChange } = await detectTopicChange({\n context: formatConversation(contextSnapshot),\n newMessage: text,\n });\n if (isTopicChange) {\n // Resolve the triggering message BEFORE resetting context\n const resolved = await this.resolveToStandalone(text, contextSnapshot);\n this.context = [`User: ${resolved}`];\n return;\n }\n }\n\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return all context in current topic segment.\n */\n protected getContextSnapshot(): string[] {\n return [...this.context];\n }\n\n /**\n * Resolve a context-dependent message into a standalone question.\n * Called when topic change is detected to preserve the meaning of\n * the triggering message before context is reset.\n * @param text - The user message to resolve\n * @param contextSnapshot - Snapshot of context captured before this async call\n */\n private async resolveToStandalone(\n text: string,\n contextSnapshot: string[],\n ): Promise<string> {\n const output = await resolveContext({\n conversation: formatConversation([...contextSnapshot, `User: ${text}`]),\n sql: '', // No SQL yet, just resolving the question\n });\n\n return output.question;\n }\n}\n", "import type { UIMessage } from 'ai';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport type { ExtractedPair } from '../types.ts';\nimport {\n BaseContextualExtractor,\n type BaseContextualExtractorOptions,\n formatConversation,\n resolveContext,\n} from './base-contextual-extractor.ts';\n\nexport type LastQueryExtractorOptions = BaseContextualExtractorOptions;\n\n/**\n * Extracts only the last SQL query with its resolved question.\n *\n * @example\n * ```typescript\n * const extractor = new LastQueryExtractor(messages, adapter);\n * const pairs = await extractor.toPairs(); // Returns array with at most 1 pair\n * ```\n */\nexport class LastQueryExtractor extends BaseContextualExtractor {\n constructor(\n messages: UIMessage[],\n adapter: Adapter,\n options: LastQueryExtractorOptions = {},\n ) {\n super(messages, adapter, options);\n }\n\n /**\n * Add user message to context (keeps all messages).\n */\n protected async onUserMessage(text: string): Promise<void> {\n this.context.push(`User: ${text}`);\n }\n\n /**\n * Return all context accumulated so far.\n */\n protected getContextSnapshot(): string[] {\n return [...this.context];\n }\n\n /**\n * Override to only resolve the LAST query instead of all queries.\n */\n protected override async *resolveQuestions(\n introspection: string,\n ): AsyncGenerator<ExtractedPair[]> {\n if (this.results.length === 0) {\n return;\n }\n\n const last = this.results.at(-1)!;\n const output = await resolveContext({\n conversation: formatConversation(last.conversationContext),\n sql: last.sql,\n introspection,\n });\n\n yield [\n {\n question: output.question,\n sql: last.sql,\n context: last.conversationContext,\n success: last.success,\n },\n ];\n }\n}\n", "import pLimit from 'p-limit';\n\nimport type { AgentModel } from '@deepagents/agent';\nimport type { ContextFragment } from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport {\n type QuestionComplexity,\n generateQuestions,\n} from '../../agents/question.agent.ts';\nimport {\n type ToSqlResult,\n UnanswerableSQLError,\n toSql,\n} from '../../agents/sql.agent.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\nimport type { Persona } from './persona-generator.ts';\n\nexport interface SchemaSynthesizerOptions {\n count: number;\n complexity?: QuestionComplexity | QuestionComplexity[];\n personas?: Persona[];\n teachings?: ContextFragment[];\n model: AgentModel;\n concurrency?: number;\n}\n/**\n * SchemaSynthesizer - Generate pairs from database schema.\n *\n * Fully synthetic: generates natural language questions at specified\n * complexity levels and personas, then generates SQL for each question.\n * Iterates through all persona \u00D7 complexity combinations.\n */\nexport class SchemaSynthesizer extends PairProducer {\n #complexities: QuestionComplexity[] = [];\n #personas: (Persona | undefined)[] = [];\n #limit: ReturnType<typeof pLimit>;\n\n /**\n * @param adapter - Database adapter for schema introspection and SQL validation\n * @param options - Synthesis configuration including count, complexity, and concurrency\n */\n constructor(\n private adapter: Adapter,\n private options: SchemaSynthesizerOptions,\n ) {\n super();\n this.#complexities = Array.isArray(this.options.complexity)\n ? this.options.complexity\n : [this.options.complexity ?? 'moderate'];\n\n this.#personas = this.options.personas ?? [undefined];\n this.#limit = pLimit(this.options.concurrency ?? 5);\n }\n\n /**\n * Generates question-SQL pairs by iterating through all persona \u00D7 complexity combinations.\n * Uses parallel processing bounded by the configured concurrency limit.\n * Yields results as each combination completes (streaming pattern).\n * @returns Generated pairs from all combinations\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n\n const combinations = this.#personas.flatMap((persona) =>\n this.#complexities.map((complexity) => ({ persona, complexity })),\n );\n\n // Process each combination and yield immediately as it completes\n // pLimit handles concurrency - no need to create all promises upfront\n for (const { persona, complexity } of combinations) {\n const pairs = await this.#processCombination(\n introspection,\n persona,\n complexity,\n );\n if (pairs.length) {\n yield pairs;\n }\n }\n }\n\n /**\n * Processes a single persona \u00D7 complexity combination by generating questions\n * and converting each to SQL in parallel.\n */\n async #processCombination(\n introspection: string, // TODO: This was Awaited<ReturnType<Adapter['introspect']>> - needs update when synthesis uses fragments\n persona: Persona | undefined,\n complexity: QuestionComplexity,\n ): Promise<ExtractedPair[]> {\n const personaContext = persona\n ? `As ${persona.role}, ${persona.perspective}\\n\\nGenerate questions this persona would ask.`\n : undefined;\n\n const prompt = personaContext\n ? `${personaContext}\\n\\nGenerate ${this.options.count} questions at ${complexity} complexity.`\n : undefined;\n\n const { questions } = await this.#limit(() =>\n generateQuestions({\n introspection,\n complexity,\n count: this.options.count,\n prompt,\n model: this.options.model,\n }),\n );\n\n const pairs = await Promise.all(\n questions.map(async (question) => {\n const result = await this.#limit(async () => {\n try {\n // TODO: Update to use schemaFragments instead of introspection string\n return await toSql({\n input: question,\n adapter: this.adapter,\n schemaFragments: [], // Placeholder - needs to pass actual fragments\n instructions: this.options.teachings ?? [],\n model: this.options.model,\n });\n } catch (error) {\n if (UnanswerableSQLError.isInstance(error)) {\n return {\n attempts: 0,\n sql: '',\n errors: [\n `Cannot answer the question ${question} because ${error.message}`,\n ],\n } satisfies ToSqlResult;\n }\n throw error;\n }\n });\n\n return {\n question,\n sql: result.sql,\n success: !result.errors || result.errors.length === 0,\n };\n }),\n );\n\n return pairs;\n }\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n guardrail,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nexport type QuestionComplexity =\n | 'simple'\n | 'moderate'\n | 'complex'\n | 'high complex';\n\nconst complexityInstructions: Record<QuestionComplexity, string> = {\n simple: dedent`\n Generate simple questions that require:\n - Basic SELECT with single table\n - Simple WHERE clauses with one condition\n - COUNT(*) or basic aggregations\n - No joins required\n Examples: \"How many customers do we have?\", \"List all products\", \"What is the total revenue?\"\n `,\n moderate: dedent`\n Generate moderate questions that require:\n - JOINs between 2-3 tables\n - Multiple WHERE conditions (AND/OR)\n - GROUP BY with HAVING clauses\n - ORDER BY with LIMIT\n - Basic subqueries\n Examples: \"What are the top 5 customers by total orders?\", \"Which products have never been ordered?\"\n `,\n complex: dedent`\n Generate complex questions that require:\n - Multiple JOINs (3+ tables)\n - Nested subqueries or CTEs\n - Complex aggregations with multiple GROUP BY columns\n - CASE expressions\n - Date/time calculations\n Examples: \"What is the month-over-month growth rate?\", \"Which customers have increased spending compared to last year?\"\n `,\n 'high complex': dedent`\n Generate highly complex questions that require advanced SQL features:\n - Window functions (ROW_NUMBER, RANK, DENSE_RANK)\n - LAG, LEAD for comparisons\n - Running totals (SUM OVER)\n - Moving averages\n - PARTITION BY clauses\n - Complex CTEs with multiple levels\n Examples: \"What is the running total of sales per month?\", \"Rank customers by their purchase frequency within each region\"\n `,\n};\n\nconst outputSchema = z.object({\n questions: z\n .array(z.string().describe('A natural language question about the data'))\n .min(1)\n .describe('List of natural language questions a user might ask'),\n});\n\nexport interface GenerateQuestionsParams {\n /** Database schema introspection */\n introspection: string;\n /** Complexity level for generated questions */\n complexity: QuestionComplexity;\n /** Number of questions to generate */\n count: number;\n /** Optional prompt to prepend (e.g., persona context) */\n prompt?: string;\n /** Optional model override */\n model?: AgentModel;\n}\n\nexport interface GenerateQuestionsResult {\n questions: string[];\n}\n\n/**\n * Generate natural language questions from database schema.\n * Used for creating synthetic training data for text-to-SQL models.\n */\nexport async function generateQuestions(\n params: GenerateQuestionsParams,\n): Promise<GenerateQuestionsResult> {\n const { introspection, complexity, count, prompt, model } = params;\n\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `question-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'question_generator',\n role: 'You are a synthetic data generator specializing in creating realistic natural language questions that users might ask about a database.',\n objective:\n 'Generate diverse, realistic natural language questions that match the specified complexity level',\n }),\n fragment('database_schema', introspection || ''),\n fragment(\n 'complexity',\n { level: complexity },\n complexityInstructions[complexity],\n ),\n fragment(\n 'task',\n dedent`\n Generate exactly ${count} natural language questions at the \"${complexity}\" complexity level.\n The questions should:\n 1. Match the complexity requirements above\n 2. Use natural business language, not technical SQL terms\n 3. Be realistic questions a non-technical user would actually ask\n 4. Cover different tables and relationships when possible\n `,\n ),\n guardrail({\n rule: 'Questions MUST ONLY reference tables and columns that exist in the schema above',\n }),\n guardrail({\n rule: 'Before generating each question, verify that ALL entities (tables, columns, relationships) you reference are explicitly listed in the schema',\n }),\n guardrail({\n rule: 'DO NOT invent or assume tables/columns that are not explicitly shown in the schema',\n }),\n guardrail({\n rule: 'Use natural language without SQL keywords like SELECT, WHERE, etc.',\n }),\n guardrail({\n rule: 'All questions must match the specified complexity level',\n }),\n user(\n prompt ??\n `Generate ${count} questions at ${complexity} complexity given db schema.`,\n ),\n );\n\n const questionOutput = structuredOutput({\n model: model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n return questionOutput.generate();\n}\n", "import { groq } from '@ai-sdk/groq';\nimport {\n APICallError,\n JSONParseError,\n NoContentGeneratedError,\n NoObjectGeneratedError,\n NoOutputGeneratedError,\n TypeValidationError,\n defaultSettingsMiddleware,\n wrapLanguageModel,\n} from 'ai';\nimport { Console } from 'node:console';\nimport { createWriteStream } from 'node:fs';\nimport pRetry from 'p-retry';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n type ContextFragment,\n InMemoryContextStore,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../adapters/adapter.ts';\n\nexport interface ToSqlOptions {\n /** The natural language input to convert to SQL */\n input: string;\n /** Database adapter for validation */\n adapter: Adapter;\n /** Schema fragments from adapter introspection */\n schemaFragments: ContextFragment[];\n /** Instructions/teachings to include */\n instructions: ContextFragment[];\n /** Optional model override */\n model: AgentModel;\n /** Maximum retry attempts on validation failure (default: 3) */\n maxRetries?: number;\n}\n\nexport interface ToSqlResult {\n /** The generated SQL query */\n sql: string;\n /** Number of attempts made */\n attempts: number;\n /** Validation errors encountered (if any retries occurred) */\n errors?: string[];\n}\n\nconst logger = new Console({\n stdout: createWriteStream('./sql-agent.log', { flags: 'a' }),\n stderr: createWriteStream('./sql-agent-error.log', { flags: 'a' }),\n inspectOptions: { depth: null },\n});\n\n/** Temperature progression for retries: deterministic first, then increasingly exploratory */\nconst RETRY_TEMPERATURES = [0, 0.2, 0.3];\n\n/** Extract SQL from markdown fenced code block if present */\nfunction extractSql(output: string): string {\n const match = output.match(/```sql\\n?([\\s\\S]*?)```/);\n return match ? match[1].trim() : output.trim();\n}\n\nconst marker = Symbol('SQLValidationError');\n/**\n * Error thrown when SQL validation fails.\n */\nexport class SQLValidationError extends Error {\n [marker]: true;\n constructor(message: string) {\n super(message);\n this.name = 'SQLValidationError';\n this[marker] = true;\n }\n static isInstance(error: unknown): error is SQLValidationError {\n return error instanceof SQLValidationError && error[marker] === true;\n }\n}\n\n/**\n * Error thrown when the question cannot be answered with the given schema.\n */\nexport class UnanswerableSQLError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UnanswerableSQLError';\n }\n static isInstance(error: unknown): error is UnanswerableSQLError {\n return error instanceof UnanswerableSQLError;\n }\n}\n\nexport async function toSql(options: ToSqlOptions): Promise<ToSqlResult> {\n const { maxRetries = 3 } = options;\n\n return withRetry(\n async (attemptNumber, errors, attempts) => {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `sql-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'Freya',\n role: 'You are an expert SQL query generator. You translate natural language questions into precise, efficient SQL queries based on the provided database schema.',\n objective:\n 'Translate natural language questions into precise, efficient SQL queries',\n }),\n ...options.instructions,\n ...options.schemaFragments,\n );\n\n // Add user message(s)\n if (errors.length) {\n context.set(\n user(options.input),\n user(\n `<validation_error>Your previous SQL query had the following error: ${errors.at(-1)?.message}. Please fix the query.</validation_error>`,\n ),\n );\n } else {\n context.set(user(options.input));\n }\n\n // Create structured output with schema\n const temperature =\n RETRY_TEMPERATURES[attemptNumber - 1] ??\n RETRY_TEMPERATURES[RETRY_TEMPERATURES.length - 1];\n const baseModel = options.model ?? groq('openai/gpt-oss-20b');\n const model = wrapLanguageModel({\n model: baseModel,\n middleware: defaultSettingsMiddleware({ settings: { temperature } }),\n });\n const sqlOutput = structuredOutput({\n model: model,\n context,\n schema: z.union([\n z.object({\n sql: z.string().describe('The SQL query that answers the question'),\n reasoning: z\n .string()\n .optional()\n .describe('The reasoning steps taken to generate the SQL'),\n }),\n z.object({\n error: z\n .string()\n .describe(\n 'Error message explaining why the question cannot be answered with the given schema',\n ),\n }),\n ]),\n });\n\n const output = await sqlOutput.generate();\n\n // Handle error responses (question is unanswerable with given schema)\n if ('error' in output) {\n throw new UnanswerableSQLError(output.error);\n }\n\n const sql = extractSql(output.sql);\n\n // Validate the generated SQL\n const validationError = await options.adapter.validate(sql);\n if (validationError) {\n throw new SQLValidationError(validationError);\n }\n\n return {\n attempts,\n sql,\n errors: errors.length ? errors.map(formatErrorMessage) : undefined,\n };\n },\n { retries: maxRetries - 1 },\n );\n}\n\nfunction formatErrorMessage(error: Error) {\n if (APICallError.isInstance(error)) {\n if (error.message.startsWith('Failed to validate JSON')) {\n return `Schema validation failed: ${error.message}`;\n }\n return error.message;\n }\n if (SQLValidationError.isInstance(error)) {\n return `SQL Validation Error: ${error.message}`;\n }\n return error.message;\n}\n\nasync function withRetry<T>(\n computation: (\n attemptNumber: number,\n errors: Error[],\n attempts: number,\n ) => Promise<T>,\n options: { retries: number } = { retries: 3 },\n) {\n const errors: Error[] = [];\n let attempts = 0;\n return pRetry(\n (attemptNumber) => {\n return computation(attemptNumber, errors, ++attempts);\n },\n {\n retries: options.retries,\n shouldRetry: (context) => {\n // Don't retry if unanswerable - it's intentional\n if (UnanswerableSQLError.isInstance(context.error)) {\n return false;\n }\n // Retry on validation errors\n if (SQLValidationError.isInstance(context.error)) {\n return true;\n }\n console.log({\n NoObjectGeneratedError: NoObjectGeneratedError.isInstance(\n context.error,\n ),\n NoOutputGeneratedError: NoOutputGeneratedError.isInstance(\n context.error,\n ),\n APICallError: APICallError.isInstance(context.error),\n JSONParseError: JSONParseError.isInstance(context.error),\n TypeValidationError: TypeValidationError.isInstance(context.error),\n NoContentGeneratedError: NoContentGeneratedError.isInstance(\n context.error,\n ),\n });\n // Retry on AI SDK errors\n return (\n APICallError.isInstance(context.error) ||\n JSONParseError.isInstance(context.error) ||\n TypeValidationError.isInstance(context.error) ||\n NoObjectGeneratedError.isInstance(context.error) ||\n NoOutputGeneratedError.isInstance(context.error) ||\n NoContentGeneratedError.isInstance(context.error)\n );\n },\n onFailedAttempt(context) {\n logger.error(`toSQL`, context.error);\n console.log(\n `Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`,\n );\n // console.dir(context.error, { depth: null });\n errors.push(context.error);\n },\n },\n );\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport pLimit from 'p-limit';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n guardrail,\n persona as personaFragment,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport { type ExtractedPair, PairProducer } from '../types.ts';\nimport type { Persona } from './persona-generator.ts';\nimport { styleInstructions } from './styles.ts';\n\nexport interface BreadthEvolverOptions {\n count: number;\n persona?: Persona;\n model?: AgentModel;\n concurrency?: number;\n}\n\nconst paraphraserOutputSchema = z.object({\n paraphrases: z\n .array(\n z.string().describe('A paraphrased version of the original question'),\n )\n .min(1)\n .describe('List of paraphrased questions that would produce the same SQL'),\n});\n\n/**\n * Generates paraphrased versions of a question while preserving SQL equivalence.\n */\nasync function paraphraseQuestion(params: {\n question: string;\n sql: string;\n count: number;\n persona?: Persona;\n model?: AgentModel;\n}): Promise<{ paraphrases: string[] }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `paraphraser-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n const personaInstruction = params.persona\n ? dedent`\n <persona role=\"${params.persona.role}\">\n ${params.persona.perspective}\n\n Paraphrase the question as this persona would naturally ask it.\n Use their vocabulary, priorities, and framing style.\n </persona>\n `\n : '';\n\n const styleInstruction =\n params.persona?.styles && params.persona.styles.length > 0\n ? dedent`\n <communication_styles>\n Generate paraphrases using these communication styles: ${params.persona.styles.join(', ')}\n\n Style definitions:\n ${params.persona.styles.map((s) => `- ${s}: ${styleInstructions[s]}`).join('\\n')}\n\n Distribute paraphrases across these styles for variety.\n </communication_styles>\n `\n : '';\n\n context.set(\n personaFragment({\n name: 'question_paraphraser',\n role: 'You are a linguistic expert specializing in paraphrasing database questions. Your task is to generate alternative phrasings of questions that preserve the exact same semantic meaning - they must all produce the identical SQL query.',\n objective:\n 'Generate paraphrased versions of questions that preserve exact semantic meaning and produce identical SQL',\n }),\n fragment('original_question', params.question),\n fragment(\n 'reference_sql',\n params.sql,\n 'This SQL shows what the question is really asking - all paraphrases must ask for exactly this',\n ),\n ...(personaInstruction ? [fragment('persona', personaInstruction)] : []),\n ...(styleInstruction\n ? [fragment('communication_styles', styleInstruction)]\n : []),\n fragment(\n 'task',\n dedent`\n Generate exactly ${params.count} paraphrased versions of the original question.\n\n Requirements:\n 1. Each paraphrase must be semantically equivalent - it should produce the EXACT same SQL\n 2. Vary the sentence structure, word choice, and phrasing style\n 3. Use natural language without SQL keywords (SELECT, WHERE, JOIN, etc.)\n 4. Keep paraphrases realistic - how actual users would ask\n 5. Do not add or remove any conditions, filters, or requirements from the original\n ${params.persona?.styles?.length ? '6. Apply the specified communication styles to create diverse phrasings' : ''}\n `,\n ),\n guardrail({ rule: 'NEVER change what data is being requested' }),\n guardrail({\n rule: 'NEVER add filters, aggregations, or conditions not in the original',\n }),\n guardrail({\n rule: 'NEVER remove any specificity from the original question',\n }),\n guardrail({\n rule: 'All paraphrases must be answerable by the exact same SQL query',\n }),\n user(\n `Paraphrase this question ${params.count} times: \"${params.question}\"`,\n ),\n );\n\n const paraphraserOutput = structuredOutput({\n model: params.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: paraphraserOutputSchema,\n });\n\n return paraphraserOutput.generate();\n}\n/**\n * BreadthEvolver - Generate paraphrased variations of questions (in-breadth evolution).\n *\n * Takes existing question/SQL pairs and generates variations of the questions\n * while keeping the SQL identical. This creates training data diversity where\n * many different phrasings map to the same SQL query.\n *\n * Based on Microsoft's Evol-Instruct methodology for in-breadth evolution.\n */\nexport class BreadthEvolver extends PairProducer {\n #limit: ReturnType<typeof pLimit>;\n\n /**\n * @param source - Source pairs or producer to evolve\n * @param options - Evolution options including count, persona, and concurrency\n */\n constructor(\n private source: PairProducer | ExtractedPair[],\n private options: BreadthEvolverOptions,\n ) {\n super();\n this.#limit = pLimit(this.options.concurrency ?? 4);\n }\n\n /**\n * Batch pairs within each chunk for concurrent processing.\n * Uses pLimit for concurrency control, yields results per pair after chunk completes.\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n for await (const chunk of this.from(this.source)) {\n const tasks = chunk.map((pair) =>\n this.#limit(async () => {\n const result = await paraphraseQuestion({\n question: pair.question,\n sql: pair.sql,\n count: this.options.count,\n persona: this.options.persona,\n model: this.options.model,\n });\n\n return result.paraphrases.map((paraphrase: string) => ({\n question: paraphrase,\n sql: pair.sql,\n context: pair.context,\n success: pair.success,\n }));\n }),\n );\n\n const results = await Promise.all(tasks);\n yield results.flat();\n }\n }\n}\n", "/**\n * Natural language styles for text-to-SQL question generation.\n * Based on OmniSQL paper (March 2025): https://arxiv.org/html/2503.02240\n */\n\nexport const ALL_STYLES = [\n 'formal', // Professional business language\n 'colloquial', // Casual everyday speech\n 'imperative', // Commands: \"Show me...\", \"Get...\"\n 'interrogative', // Questions: \"What is...\", \"How many...\"\n 'descriptive', // Verbose, detailed\n 'concise', // Brief, minimal\n 'vague', // Ambiguous, hedging\n 'metaphorical', // Figurative language\n 'conversational', // Chat-like\n] as const;\n\nexport type NLStyle = (typeof ALL_STYLES)[number];\n\nexport const styleInstructions: Record<NLStyle, string> = {\n formal: 'Use professional business language, complete sentences, no slang',\n colloquial: 'Use casual everyday speech, contractions, informal tone',\n imperative: 'Phrase as commands: \"Show me...\", \"Get...\", \"List...\"',\n interrogative: 'Phrase as questions: \"What is...\", \"How many...\", \"Which...\"',\n descriptive: 'Use detailed, verbose phrasing with extra context',\n concise: 'Use minimal words, telegram-style brevity',\n vague: 'Be intentionally ambiguous, use hedging language',\n metaphorical: 'Use figurative language, analogies, creative phrasing',\n conversational: 'Chat-like tone, as if talking to a colleague',\n};\n", "import { groq } from '@ai-sdk/groq';\nimport { NoObjectGeneratedError, NoOutputGeneratedError } from 'ai';\nimport dedent from 'dedent';\nimport pLimit from 'p-limit';\nimport pRetry from 'p-retry';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n InMemoryContextStore,\n fragment,\n guardrail,\n persona,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport type { Adapter } from '../../adapters/adapter.ts';\nimport { UnanswerableSQLError, toSql } from '../../agents/sql.agent.ts';\nimport { type ExtractedPair, PairProducer } from '../types.ts';\n\n/**\n * Techniques for evolving questions into more complex versions.\n * Each technique transforms the question in a specific way.\n */\nexport type DepthTechnique =\n | 'add-aggregation'\n | 'add-filter'\n | 'add-join'\n | 'add-reasoning'\n | 'hypothetical';\n\nconst techniqueInstructions: Record<DepthTechnique, string> = {\n 'add-aggregation': dedent`\n Add aggregation requirements to the question.\n Transform it to require GROUP BY, COUNT, SUM, AVG, MIN, MAX, or similar operations.\n Examples:\n - \"Show orders\" \u2192 \"Show total order count by customer\"\n - \"List products\" \u2192 \"What is the average price per category?\"\n - \"Get employees\" \u2192 \"How many employees are in each department?\"\n `,\n 'add-filter': dedent`\n Add filtering conditions to the question.\n Transform it to require WHERE clauses with specific conditions.\n Examples:\n - \"Show orders\" \u2192 \"Show orders from the last 30 days\"\n - \"List customers\" \u2192 \"List customers who have made more than 5 purchases\"\n - \"Get products\" \u2192 \"Get products with price above $100\"\n `,\n 'add-join': dedent`\n Add requirements that need data from related tables.\n Transform it to require JOIN operations between multiple tables.\n Examples:\n - \"Show orders\" \u2192 \"Show orders with customer names and addresses\"\n - \"List products\" \u2192 \"List products with their supplier information\"\n - \"Get employees\" \u2192 \"Get employees with their department and manager names\"\n `,\n 'add-reasoning': dedent`\n Add multi-step reasoning requirements.\n Transform it to require logical deduction, comparisons, or derived calculations.\n Examples:\n - \"Show orders\" \u2192 \"Which customers have orders above the average order value?\"\n - \"List products\" \u2192 \"Which products are underperforming compared to their category average?\"\n - \"Get revenue\" \u2192 \"Which month had the highest growth compared to the previous month?\"\n `,\n hypothetical: dedent`\n Add a hypothetical or speculative scenario.\n Transform it to require applying calculations or projections.\n Examples:\n - \"Show revenue\" \u2192 \"What would revenue be if we increased all prices by 15%?\"\n - \"List inventory\" \u2192 \"How many days of stock remain at current sales rate?\"\n - \"Get costs\" \u2192 \"What would be the impact of a 10% discount on profit margins?\"\n `,\n};\n\nexport interface DepthEvolverOptions {\n techniques?: DepthTechnique[];\n count?: number;\n model: AgentModel;\n concurrency?: number;\n}\n\nconst evolverOutputSchema = z.object({\n evolvedQuestion: z\n .string()\n .describe('The evolved, more complex version of the original question'),\n});\n\n/**\n * Evolves a simple question into a more complex version using a specific technique.\n */\nasync function evolveQuestion(params: {\n question: string;\n sql: string;\n schema: string;\n technique: DepthTechnique;\n techniqueInstruction: string;\n model?: AgentModel;\n}): Promise<{ evolvedQuestion: string }> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `evolver-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'question_evolver',\n role: 'You are an expert at evolving simple database questions into more complex ones. Your task is to take a basic question and transform it into a more sophisticated version that requires advanced SQL techniques to answer.',\n objective:\n 'Transform simple questions into complex versions requiring advanced SQL techniques',\n }),\n fragment('original_question', params.question),\n fragment(\n 'original_sql',\n params.sql,\n '(This shows what the original question required)',\n ),\n fragment('database_schema', params.schema),\n fragment(\n 'technique',\n { name: params.technique },\n params.techniqueInstruction,\n ),\n fragment(\n 'task',\n dedent`\n Evolve the original question using the \"${params.technique}\" technique.\n\n Requirements:\n 1. The evolved question must be MORE COMPLEX than the original\n 2. Apply the specific technique described above\n 3. The evolved question must be answerable using the provided schema\n 4. Use natural language - no SQL keywords\n 5. Keep the question realistic and practical\n 6. The evolved question should build upon the original topic/domain\n `,\n ),\n guardrail({\n rule: 'The evolved question MUST require more complex SQL than the original',\n }),\n guardrail({\n rule: 'Do not ask for data that does not exist in the schema',\n }),\n guardrail({\n rule: 'Keep the question grounded in the same domain as the original',\n }),\n guardrail({ rule: 'Make sure the question is clear and unambiguous' }),\n user(\n `Evolve this question using \"${params.technique}\": \"${params.question}\"`,\n ),\n );\n\n const evolverOutput = structuredOutput({\n model: params.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: evolverOutputSchema,\n });\n\n return evolverOutput.generate();\n}\n\nconst ALL_TECHNIQUES: DepthTechnique[] = [\n 'add-aggregation',\n 'add-filter',\n 'add-join',\n 'add-reasoning',\n 'hypothetical',\n];\n/**\n * DepthEvolver - Evolve questions into more complex versions (in-depth evolution).\n *\n * Takes existing question/SQL pairs and evolves them into more complex versions\n * using specific techniques. Both the question AND SQL change - the evolved\n * question requires a more sophisticated query to answer.\n *\n * Based on Microsoft's Evol-Instruct methodology for in-depth evolution.\n */\nexport class DepthEvolver extends PairProducer {\n #limit: ReturnType<typeof pLimit>;\n\n /**\n * @param source - Source pairs or producer to evolve\n * @param adapter - Database adapter for SQL generation\n * @param options - Evolution options including techniques, count, and concurrency\n */\n constructor(\n private source: PairProducer | ExtractedPair[],\n private adapter: Adapter,\n private options: DepthEvolverOptions,\n ) {\n super();\n this.#limit = pLimit(this.options?.concurrency ?? 4);\n }\n\n /**\n * Yields evolved pairs as each completes (streaming pattern).\n * Removes batch barrier - no longer waits for all evolutions before yielding.\n */\n async *produce(): AsyncGenerator<ExtractedPair[]> {\n // TODO: Update to use fragments and render them\n // const schemaFragments = await this.adapter.introspect();\n // const introspection = new XmlRenderer().render(schemaFragments);\n const introspection = '' as any; // Placeholder - synthesis needs to be updated to use fragments\n const count = this.options?.count ?? 1;\n const techniques = this.options?.techniques ?? ALL_TECHNIQUES;\n\n let pairIndex = 0;\n for await (const chunk of this.from(this.source)) {\n for (const pair of chunk) {\n const tasks = Array.from({ length: count }, (_, i) => {\n const technique = this.options?.techniques\n ? techniques[i % techniques.length]\n : techniques[(pairIndex * count + i) % techniques.length];\n return this.#limit(() =>\n this.#processTask(pair, technique, introspection),\n );\n });\n\n const results = await Promise.all(tasks);\n yield results;\n pairIndex++;\n }\n }\n }\n\n async #processTask(\n pair: ExtractedPair,\n technique: DepthTechnique,\n introspection: string,\n ) {\n const output = await withRetry(() =>\n evolveQuestion({\n question: pair.question,\n sql: pair.sql,\n schema: introspection,\n technique,\n techniqueInstruction: techniqueInstructions[technique],\n model: this.options?.model,\n }),\n );\n\n const evolvedQuestion = output.evolvedQuestion;\n try {\n // TODO: Update to use schemaFragments instead of introspection string\n const sqlResult = await toSql({\n input: evolvedQuestion,\n adapter: this.adapter,\n schemaFragments: [], // Placeholder - needs to pass actual fragments\n instructions: [],\n model: this.options?.model,\n });\n\n return {\n question: evolvedQuestion,\n sql: sqlResult.sql,\n context: pair.context,\n success: !sqlResult.errors || sqlResult.errors.length === 0,\n };\n } catch (error) {\n if (UnanswerableSQLError.isInstance(error)) {\n return {\n question: evolvedQuestion,\n sql: '',\n context: pair.context,\n success: false,\n errors: [\n `Cannot answer the question ${evolvedQuestion} because ${error.message}`,\n ],\n };\n }\n throw error;\n }\n }\n}\n\nasync function withRetry<T>(computation: () => Promise<T>): Promise<T> {\n return pRetry(computation, {\n retries: 3,\n shouldRetry: (context) => {\n console.log({\n NoObjectGeneratedError: NoObjectGeneratedError.isInstance(\n context.error,\n ),\n NoOutputGeneratedError: NoOutputGeneratedError.isInstance(\n context.error,\n ),\n });\n return (\n NoObjectGeneratedError.isInstance(context.error) ||\n NoOutputGeneratedError.isInstance(context.error)\n );\n },\n onFailedAttempt(context) {\n console.log(\n `Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`,\n );\n console.dir(context.error, { depth: null });\n },\n });\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n type ContextFragment,\n InMemoryContextStore,\n XmlRenderer,\n fragment,\n guardrail,\n persona as personaFragment,\n structuredOutput,\n user,\n} from '@deepagents/context';\n\nimport { ALL_STYLES, type NLStyle } from './styles.ts';\n\nexport interface Persona {\n role: string;\n perspective: string;\n styles: NLStyle[];\n}\n\nexport interface PersonaGeneratorOptions {\n count?: number;\n model?: AgentModel;\n}\n\nconst outputSchema = z.object({\n personas: z\n .array(\n z.object({\n role: z.string().describe('The job title or role of this persona'),\n perspective: z\n .string()\n .describe(\n 'Rich description of what this persona cares about when querying the database',\n ),\n styles: z\n .array(z.enum(ALL_STYLES))\n .min(1)\n .max(3)\n .describe(\n 'Typical communication styles for this persona (1-3 styles)',\n ),\n }),\n )\n .min(1)\n .describe('List of personas who would query this database'),\n});\n\n/**\n * Generate personas by analyzing database schema.\n * Analyzes the schema to infer who would query this database and what\n * they care about. Generated personas can be used with BreadthEvolver\n * to create diverse question paraphrases from different perspectives.\n *\n * @param schemaFragments - Schema fragments from adapter.introspect()\n * @param options - Generation options including count and model\n * @returns Array of personas with roles and perspectives\n */\nexport async function generatePersonas(\n schemaFragments: ContextFragment[],\n options?: PersonaGeneratorOptions,\n): Promise<Persona[]> {\n const schema = new XmlRenderer().render(schemaFragments);\n const count = options?.count ?? 5;\n\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `persona-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n personaFragment({\n name: 'persona_generator',\n role: 'You are an expert at understanding database schemas and inferring who would use them.',\n objective:\n 'Generate realistic personas representing users who would query this database',\n }),\n fragment('database_schema', schema),\n fragment(\n 'task',\n dedent`\n Analyze the database schema and generate realistic personas representing\n the different types of users who would query this database.\n\n For each persona, provide:\n 1. **role**: Their job title or role (e.g., \"Financial Analyst\", \"Customer Support Rep\")\n 2. **perspective**: A rich description of what they care about, including:\n - What questions they typically ask\n - What metrics/data points matter to them\n - How they prefer data formatted or presented\n - Their priorities (speed vs accuracy, detail vs summary)\n - Domain-specific concerns relevant to their role\n 3. **styles**: 1-3 communication styles typical for this persona. Choose from:\n - formal: Professional business language, complete sentences\n - colloquial: Casual everyday speech, contractions\n - imperative: Commands like \"Show me...\", \"Get...\", \"List...\"\n - interrogative: Questions like \"What is...\", \"How many...\"\n - descriptive: Verbose, detailed phrasing\n - concise: Brief, minimal words\n - vague: Ambiguous, hedging language\n - metaphorical: Figurative language, analogies\n - conversational: Chat-like, casual tone\n\n Requirements:\n - Personas should be realistic for the given schema\n - Each persona should have distinct concerns and priorities\n - Perspectives should be detailed enough to guide question paraphrasing\n - Cover different levels of technical expertise (some technical, some business-focused)\n - Styles should match how this persona would naturally communicate\n `,\n ),\n fragment(\n 'example',\n dedent`\n For an e-commerce schema with orders, customers, products tables:\n\n {\n \"role\": \"Customer Support Rep\",\n \"perspective\": \"As customer support, I care about:\\\\n- Quick lookups by order ID or customer email\\\\n- Order status and shipping tracking\\\\n- Return and refund history\\\\n- Customer contact details and order history\\\\n- I need fast answers, not complex analysis\",\n \"styles\": [\"imperative\", \"concise\"]\n }\n\n {\n \"role\": \"Inventory Manager\",\n \"perspective\": \"As inventory manager, I care about:\\\\n- Current stock levels and reorder points\\\\n- Product availability across warehouses\\\\n- Slow-moving inventory identification\\\\n- Supplier lead times and pending orders\\\\n- I need accurate counts, often aggregated by location\",\n \"styles\": [\"formal\", \"interrogative\"]\n }\n `,\n ),\n guardrail({\n rule: 'Only generate personas relevant to the actual schema provided',\n }),\n guardrail({\n rule: 'Do not invent tables or data that do not exist in the schema',\n }),\n guardrail({\n rule: 'Ensure perspectives are specific to the domain, not generic',\n }),\n user(\n `Generate exactly ${count} distinct personas who would query this database.`,\n ),\n );\n\n const personaOutput = structuredOutput({\n model: options?.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n const output = await personaOutput.generate();\n return output.personas;\n}\n", "import { groq } from '@ai-sdk/groq';\nimport dedent from 'dedent';\nimport z from 'zod';\n\nimport { type AgentModel } from '@deepagents/agent';\nimport {\n ContextEngine,\n type ContextFragment,\n InMemoryContextStore,\n analogy,\n clarification,\n example,\n explain,\n fragment,\n guardrail,\n hint,\n persona,\n quirk,\n structuredOutput,\n styleGuide,\n term,\n user,\n workflow,\n} from '@deepagents/context';\n\nconst outputSchema = z.object({\n terms: z\n .array(z.object({ name: z.string(), definition: z.string() }))\n .optional()\n .describe('Domain terminology definitions'),\n hints: z\n .array(z.object({ text: z.string() }))\n .optional()\n .describe('Helpful hints for SQL generation'),\n guardrails: z\n .array(\n z.object({\n rule: z.string(),\n reason: z.string().optional(),\n action: z.string().optional(),\n }),\n )\n .optional()\n .describe('Safety rules and constraints'),\n explains: z\n .array(\n z.object({\n concept: z.string(),\n explanation: z.string(),\n therefore: z.string().optional(),\n }),\n )\n .optional()\n .describe('Concept explanations'),\n examples: z\n .array(\n z.object({\n question: z.string(),\n answer: z.string(),\n note: z.string().optional(),\n }),\n )\n .optional()\n .describe('Example question-answer pairs'),\n clarifications: z\n .array(z.object({ when: z.string(), ask: z.string(), reason: z.string() }))\n .optional()\n .describe('When to ask for clarification'),\n workflows: z\n .array(\n z.object({\n task: z.string(),\n steps: z.array(z.string()).min(1),\n triggers: z.array(z.string()).optional(),\n notes: z.string().optional(),\n }),\n )\n .optional()\n .describe('Multi-step workflows'),\n quirks: z\n .array(z.object({ issue: z.string(), workaround: z.string() }))\n .optional()\n .describe('Known issues and workarounds'),\n styleGuides: z\n .array(\n z.object({\n prefer: z.string(),\n never: z.string().optional(),\n always: z.string().optional(),\n }),\n )\n .optional()\n .describe('SQL style preferences'),\n analogies: z\n .array(\n z.object({\n concepts: z.array(z.string()).min(2),\n relationship: z.string(),\n insight: z.string().optional(),\n therefore: z.string().optional(),\n pitfall: z.string().optional(),\n }),\n )\n .optional()\n .describe('Concept analogies'),\n});\n\ntype TeachablesOutput = z.infer<typeof outputSchema>;\n\nexport interface GenerateToTeachingsOptions {\n model?: AgentModel;\n}\n\nexport async function toTeachings(\n input: { schema: string; context?: string },\n options?: GenerateToTeachingsOptions,\n): Promise<ContextFragment[]> {\n const context = new ContextEngine({\n store: new InMemoryContextStore(),\n chatId: `teachables-gen-${crypto.randomUUID()}`,\n userId: 'system',\n });\n\n context.set(\n persona({\n name: 'teachables-author',\n role: 'You design \"fragments\" for a Text2SQL system. Fragments become structured XML instructions.',\n objective:\n 'Choose only high-impact items that improve accuracy, safety, or clarity for this database',\n }),\n fragment('database_schema', input.schema),\n ...(input.context ? [fragment('additional_context', input.context)] : []),\n fragment(\n 'output_structure',\n dedent`\n Output a JSON object with these optional arrays (include only relevant ones):\n - terms: [{ name: string, definition: string }] - Domain terminology\n - hints: [{ text: string }] - Helpful SQL generation hints\n - guardrails: [{ rule: string, reason?: string, action?: string }] - Safety constraints\n - explains: [{ concept: string, explanation: string, therefore?: string }] - Concept explanations\n - examples: [{ question: string, answer: string, note?: string }] - Q&A examples\n - clarifications: [{ when: string, ask: string, reason: string }] - Clarification triggers\n - workflows: [{ task: string, steps: string[], triggers?: string[], notes?: string }] - Multi-step tasks\n - quirks: [{ issue: string, workaround: string }] - Known issues\n - styleGuides: [{ prefer: string, never?: string, always?: string }] - SQL style rules\n - analogies: [{ concepts: string[], relationship: string, insight?: string, therefore?: string, pitfall?: string }]\n `,\n ),\n fragment(\n 'task',\n dedent`\n 1. Analyze the schema to infer domain, relationships, and sensitive columns.\n 2. Generate 3-10 fragments total across all categories, prioritizing:\n - guardrails for PII columns (email, ssn, phone, etc)\n - hints for status/enum columns\n - clarifications for ambiguous terms\n 3. Ground everything in the schema - do not invent tables/columns.\n 4. Only include categories that are relevant to this schema.\n `,\n ),\n user(\n `Analyze this database schema and generate fragments that will help an AI generate accurate SQL queries.`,\n ),\n );\n\n const teachablesOutput = structuredOutput({\n model: options?.model ?? groq('openai/gpt-oss-20b'),\n context,\n schema: outputSchema,\n });\n\n const result = await teachablesOutput.generate();\n\n const fragments: ContextFragment[] = [];\n\n // Convert generated output to ContextFragments\n result.terms?.forEach((t) => fragments.push(term(t.name, t.definition)));\n result.hints?.forEach((h) => fragments.push(hint(h.text)));\n result.guardrails?.forEach((g) =>\n fragments.push(\n guardrail({ rule: g.rule, reason: g.reason, action: g.action }),\n ),\n );\n result.explains?.forEach((e) =>\n fragments.push(\n explain({\n concept: e.concept,\n explanation: e.explanation,\n therefore: e.therefore,\n }),\n ),\n );\n result.examples?.forEach((e) =>\n fragments.push(\n example({ question: e.question, answer: e.answer, note: e.note }),\n ),\n );\n result.clarifications?.forEach((c) =>\n fragments.push(\n clarification({ when: c.when, ask: c.ask, reason: c.reason }),\n ),\n );\n result.workflows?.forEach((w) =>\n fragments.push(\n workflow({\n task: w.task,\n steps: w.steps,\n triggers: w.triggers,\n notes: w.notes,\n }),\n ),\n );\n result.quirks?.forEach((q) =>\n fragments.push(quirk({ issue: q.issue, workaround: q.workaround })),\n );\n result.styleGuides?.forEach((s) =>\n fragments.push(\n styleGuide({ prefer: s.prefer, never: s.never, always: s.always }),\n ),\n );\n result.analogies?.forEach((a) =>\n fragments.push(\n analogy({\n concepts: a.concepts,\n relationship: a.relationship,\n insight: a.insight,\n therefore: a.therefore,\n pitfall: a.pitfall,\n }),\n ),\n );\n\n return fragments;\n}\n", "import type { AgentModel } from '@deepagents/agent';\nimport { type ContextFragment, XmlRenderer } from '@deepagents/context';\n\nimport { toTeachings } from '../../agents/teachables.agent.ts';\n\nexport interface TeachingsGeneratorOptions {\n context?: string;\n model?: AgentModel;\n maxRetries?: number;\n}\n\n/**\n * Generate domain-specific teachings from database schema.\n * Analyzes the schema to generate teachings that improve SQL generation accuracy.\n * Teachings include domain vocabulary, SQL patterns, guardrails, and examples\n * that help the SQL generator understand the domain and produce semantically\n * correct queries.\n *\n * @param schemaFragments - Schema fragments from adapter.introspect()\n * @param options - Generation options including context, model, and maxRetries\n * @returns Array of teachings including vocabulary, patterns, and guardrails\n */\nexport async function generateTeachings(\n schemaFragments: ContextFragment[],\n options?: TeachingsGeneratorOptions,\n): Promise<ContextFragment[]> {\n const schema = new XmlRenderer().render(schemaFragments);\n const maxRetries = options?.maxRetries ?? 3;\n\n let lastError: Error | undefined;\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await toTeachings(\n { schema, context: options?.context },\n { model: options?.model },\n );\n } catch (error) {\n lastError = error as Error;\n const isRetryable =\n lastError.message.includes('parse') ||\n lastError.message.includes('schema') ||\n lastError.message.includes('No object generated') ||\n lastError.name.includes('AI_');\n if (!isRetryable) {\n throw lastError;\n }\n }\n }\n\n throw lastError;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["getToolOrDynamicToolName", "isToolOrDynamicToolUIPart", "groq", "spawn", "generateId", "chalk", "fragment", "message", "fragment", "fragment", "message", "path", "message", "chalk", "groq", "message", "assistantText", "message", "isToolOrDynamicToolUIPart", "getToolOrDynamicToolName", "groq", "dedent", "z", "z", "dedent", "groq", "groq", "dedent", "z", "z", "dedent", "groq", "groq", "dedent", "z", "dedent", "outputSchema", "z", "groq", "groq", "z", "message", "groq", "z", "persona", "groq", "dedent", "pLimit", "z", "z", "dedent", "groq", "pLimit", "groq", "NoObjectGeneratedError", "NoOutputGeneratedError", "dedent", "pLimit", "pRetry", "z", "dedent", "z", "groq", "pLimit", "withRetry", "pRetry", "NoObjectGeneratedError", "NoOutputGeneratedError", "groq", "dedent", "z", "outputSchema", "z", "dedent", "groq", "groq", "dedent", "z", "outputSchema", "z", "dedent", "groq"]
|
|
7
7
|
}
|