@odla-ai/ai 0.1.4 → 0.2.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/README.md +27 -0
- package/dist/index.cjs +72 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +71 -3
- package/dist/index.js.map +1 -1
- package/llms.txt +21 -0
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shape/capabilities.ts","../src/catalog/anthropic.ts","../src/catalog/openai.ts","../src/catalog/google.ts","../src/catalog/index.ts","../src/providers/registry.ts","../src/client/keys.ts","../src/client/init.ts","../src/doc/llms.ts","../src/agent/tools.ts","../src/agent/skill.ts","../src/agent/taint.ts","../src/agent/loop.ts","../src/agent/memory.ts","../src/eval/harness.ts","../src/eval/graders.ts","../src/store/schema.ts","../src/store/memory.ts","../src/store/runs.ts","../src/store/keys.ts","../src/store/provision.ts","../src/store/crud.ts"],"sourcesContent":["// Model capabilities + request validation. This is the \"config is data\"\n// doctrine from odla-kg: what a model can do is a data descriptor, and every\n// request is checked against it *before* dispatch so a mismatch fails fast with\n// a clear CapabilityError instead of a confusing provider 400 (the canonical\n// example: audio input to a Claude model — Claude has no audio input).\n\nimport {\n CapabilityError,\n blocksOf,\n type OracleRequest,\n type ProviderId,\n} from \"./index\";\n\n/** The kind of model — chat/multimodal is the v1 focus; the others are modeled\n * so embeddings, transcription, TTS, and image generation slot in later. */\nexport type ModelKind = \"chat\" | \"embed\" | \"transcribe\" | \"tts\" | \"image\";\n\nexport interface Capabilities {\n kind: ModelKind;\n /** Accepts text input. */\n textIn: boolean;\n /** Accepts image input (ImageBlock). */\n imageIn: boolean;\n /** Accepts audio input (AudioBlock). */\n audioIn: boolean;\n /** Accepts document/PDF input (DocumentBlock). */\n documentIn: boolean;\n /** Supports tool / function calling. */\n toolUse: boolean;\n /** Supports a native reasoning/thinking mode. */\n thinking: boolean;\n /** Supports an effort/reasoning-depth knob. */\n effort: boolean;\n /** Supports streaming responses. */\n streaming: boolean;\n /** Supports constrained JSON / structured output. */\n structuredOutput: boolean;\n /** Supports a provider server-side web-search tool. */\n webSearch: boolean;\n}\n\n/** A catalog entry: a canonical model id mapped to a provider, that provider's\n * native model id, and what the model can do. */\nexport interface ModelSpec {\n /** Canonical id used across odla-ai (e.g. \"claude-opus-4-8\", \"gpt-5\"). */\n id: string;\n provider: ProviderId;\n /** The id passed to the provider SDK. Often equal to `id`. */\n nativeId: string;\n capabilities: Capabilities;\n contextWindow?: number;\n maxOutput?: number;\n}\n\n/** Sensible chat/multimodal defaults; per-model tables override what differs. */\nexport function chatCapabilities(overrides: Partial<Capabilities> = {}): Capabilities {\n return {\n kind: \"chat\",\n textIn: true,\n imageIn: true,\n audioIn: false,\n documentIn: false,\n toolUse: true,\n thinking: false,\n effort: false,\n streaming: true,\n structuredOutput: true,\n webSearch: false,\n ...overrides,\n };\n}\n\n/**\n * Validate a request against a model's capabilities. Throws CapabilityError on\n * the first violation. Runs before any provider call — the single choke point\n * that turns \"audio to Claude\" from an opaque upstream 400 into a clear error.\n */\nexport function validateRequest(spec: ModelSpec, req: OracleRequest): void {\n const caps = spec.capabilities;\n const fail = (what: string): never => {\n throw new CapabilityError(\n `Model \"${spec.id}\" (${spec.provider}) does not support ${what}.`,\n );\n };\n\n if (caps.kind !== \"chat\") fail(`chat requests (it is a \"${caps.kind}\" model)`);\n\n for (const msg of req.messages) {\n for (const block of blocksOf(msg.content)) {\n if (block.type === \"image\" && !caps.imageIn) fail(\"image input\");\n if (block.type === \"audio\" && !caps.audioIn) fail(\"audio input\");\n if (block.type === \"document\" && !caps.documentIn) fail(\"document (PDF) input\");\n }\n }\n\n if (req.tools && req.tools.length > 0 && !caps.toolUse) fail(\"tool use\");\n if (req.webSearch && !caps.webSearch) fail(\"web search\");\n if (req.responseFormat && !caps.structuredOutput) fail(\"structured output\");\n if (req.effort && !caps.effort) fail(\"the effort parameter\");\n if (req.thinking === \"adaptive\" && !caps.thinking) fail(\"thinking\");\n}\n","// Anthropic (Claude) model catalog — config as data.\n//\n// Model ids + capabilities VERIFIED against the `claude-api` skill (this build):\n// current chat ids are claude-opus-4-8 (default), claude-sonnet-5,\n// claude-haiku-4-5, claude-fable-5; thinking is adaptive-only; forced tool-call\n// is tool_choice:{type:\"tool\"}; web search is web_search_20260209 and needs\n// Sonnet-4.6+/Opus (not Haiku); Claude has NO audio input.\n// Re-verify via the skill before shipping if time has passed — never hardcode\n// model ids from memory.\n\nimport { chatCapabilities, type ModelSpec } from \"../shape/capabilities\";\n\nconst FRONTIER = () =>\n chatCapabilities({\n documentIn: true, // PDF\n thinking: true, // adaptive\n effort: true, // output_config.effort\n webSearch: true, // web_search_20260209\n audioIn: false, // Claude has no audio input\n });\n\nexport const ANTHROPIC_MODELS: ModelSpec[] = [\n {\n id: \"claude-opus-4-8\",\n provider: \"anthropic\",\n nativeId: \"claude-opus-4-8\",\n capabilities: FRONTIER(),\n contextWindow: 1_000_000,\n maxOutput: 128_000,\n },\n {\n id: \"claude-sonnet-5\",\n provider: \"anthropic\",\n nativeId: \"claude-sonnet-5\",\n capabilities: FRONTIER(),\n contextWindow: 1_000_000,\n maxOutput: 128_000,\n },\n {\n id: \"claude-fable-5\",\n provider: \"anthropic\",\n nativeId: \"claude-fable-5\",\n capabilities: FRONTIER(),\n contextWindow: 1_000_000,\n maxOutput: 128_000,\n },\n {\n // Haiku 4.5: no effort param (errors), web search needs Sonnet-4.6+, so both off.\n id: \"claude-haiku-4-5\",\n provider: \"anthropic\",\n nativeId: \"claude-haiku-4-5\",\n capabilities: chatCapabilities({\n documentIn: true,\n thinking: false,\n effort: false,\n webSearch: false,\n audioIn: false,\n }),\n contextWindow: 200_000,\n maxOutput: 64_000,\n },\n];\n","// OpenAI model catalog — config as data.\n//\n// Model ids VERIFIED against the account's live /v1/models list at build time\n// (2026-07). Canonical id == native id. The GPT-5 line are reasoning models\n// (reasoning-effort knob → effort: true) and take image but not audio input;\n// audio input is the separate `gpt-audio` line. Re-verify against /v1/models as\n// the lineup drifts — do NOT trust these from memory. Pass a custom catalog to\n// init() to add models without a release.\n\nimport { chatCapabilities, type ModelSpec } from \"../shape/capabilities\";\n\nexport const OPENAI_MODELS: ModelSpec[] = [\n {\n id: \"gpt-5.5\",\n provider: \"openai\",\n nativeId: \"gpt-5.5\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n id: \"gpt-5\",\n provider: \"openai\",\n nativeId: \"gpt-5\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n id: \"gpt-5-mini\",\n provider: \"openai\",\n nativeId: \"gpt-5-mini\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n id: \"gpt-5-nano\",\n provider: \"openai\",\n nativeId: \"gpt-5-nano\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n // Search-tuned chat model: the adapter sends web_search_options when a\n // request sets webSearch. No reasoning-effort knob.\n id: \"gpt-5-search-api\",\n provider: \"openai\",\n nativeId: \"gpt-5-search-api\",\n capabilities: chatCapabilities({ webSearch: true }),\n contextWindow: 400_000,\n },\n {\n // Non-reasoning multimodal chat (image input, no effort knob).\n id: \"gpt-4o\",\n provider: \"openai\",\n nativeId: \"gpt-4o\",\n capabilities: chatCapabilities(),\n contextWindow: 128_000,\n },\n {\n // Audio-input model line — accepts audio, not images.\n id: \"gpt-audio\",\n provider: \"openai\",\n nativeId: \"gpt-audio\",\n capabilities: chatCapabilities({ imageIn: false, audioIn: true }),\n contextWindow: 128_000,\n },\n];\n","// Google (Gemini) model catalog — config as data.\n//\n// ⚠️ VERIFY these ids + capabilities against current Google GenAI docs before\n// shipping — do NOT trust them from memory. Canonical id == native id here.\n//\n// Gemini is natively multimodal: text + image + audio + PDF input on the current\n// generation, tool calling via functionDeclarations, thinking via thinkingConfig,\n// structured output via responseSchema, and web grounding via the googleSearch\n// tool — hence the broad default caps below.\n\nimport { chatCapabilities, type ModelSpec } from \"../shape/capabilities\";\n\nconst GEMINI = () =>\n chatCapabilities({\n audioIn: true,\n documentIn: true,\n thinking: true,\n effort: true, // mapped onto thinkingConfig\n webSearch: true, // googleSearch grounding\n });\n\nexport const GOOGLE_MODELS: ModelSpec[] = [\n {\n id: \"gemini-2.5-pro\",\n provider: \"google\",\n nativeId: \"gemini-2.5-pro\",\n capabilities: GEMINI(),\n contextWindow: 1_000_000,\n },\n {\n id: \"gemini-2.5-flash\",\n provider: \"google\",\n nativeId: \"gemini-2.5-flash\",\n capabilities: GEMINI(),\n contextWindow: 1_000_000,\n },\n {\n id: \"gemini-2.0-flash\",\n provider: \"google\",\n nativeId: \"gemini-2.0-flash\",\n capabilities: chatCapabilities({\n audioIn: true,\n documentIn: true,\n thinking: false,\n effort: false,\n webSearch: true,\n }),\n contextWindow: 1_000_000,\n },\n];\n","// The model catalog: canonical model id → ModelSpec (provider, native id,\n// capabilities). The default catalog merges the three per-provider tables;\n// init() accepts a custom or extended catalog so apps can add/override models\n// without waiting on a release.\n\nimport { ConfigError, type ProviderId } from \"../shape/index\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport { ANTHROPIC_MODELS } from \"./anthropic\";\nimport { OPENAI_MODELS } from \"./openai\";\nimport { GOOGLE_MODELS } from \"./google\";\n\n/** Canonical id → spec. */\nexport type Catalog = Record<string, ModelSpec>;\n\nfunction index(specs: ModelSpec[]): Catalog {\n const out: Catalog = {};\n for (const spec of specs) out[spec.id] = spec;\n return out;\n}\n\n/** The built-in catalog across Anthropic, OpenAI, and Google. */\nexport const DEFAULT_CATALOG: Catalog = index([\n ...ANTHROPIC_MODELS,\n ...OPENAI_MODELS,\n ...GOOGLE_MODELS,\n]);\n\n/** Merge extra specs over a base catalog (later entries win by id). */\nexport function buildCatalog(base: Catalog, extra: ModelSpec[] = []): Catalog {\n return { ...base, ...index(extra) };\n}\n\n/** Look up a model, throwing a ConfigError (with the known ids) if unknown. */\nexport function resolveModel(catalog: Catalog, id: string): ModelSpec {\n const spec = catalog[id];\n if (!spec) {\n const known = Object.keys(catalog).sort().join(\", \");\n throw new ConfigError(`Unknown model \"${id}\". Known models: ${known || \"(none)\"}.`);\n }\n return spec;\n}\n\n/** List every provider the catalog can route to. */\nexport function providersInCatalog(catalog: Catalog): ProviderId[] {\n const set = new Set<ProviderId>();\n for (const spec of Object.values(catalog)) set.add(spec.provider);\n return [...set];\n}\n\nexport { ANTHROPIC_MODELS, OPENAI_MODELS, GOOGLE_MODELS };\n","// Provider registry. `getProvider` lazy-`import()`s the adapter module — and,\n// transitively, only that provider's SDK — so a Claude-only consumer never loads\n// `openai` or `@google/genai` at runtime. Adapters are cached per (provider, key)\n// so repeated calls reuse one SDK client.\n\nimport type { ProviderId } from \"../shape/index\";\nimport type { Provider } from \"./types\";\n\nconst cache = new Map<string, Provider>();\n\nexport async function getProvider(id: ProviderId, apiKey: string): Promise<Provider> {\n const cacheKey = `${id}:${apiKey}`;\n const existing = cache.get(cacheKey);\n if (existing) return existing;\n\n let provider: Provider;\n switch (id) {\n case \"anthropic\": {\n const { AnthropicProvider } = await import(\"./anthropic\");\n provider = new AnthropicProvider(apiKey);\n break;\n }\n case \"openai\": {\n const { OpenAIProvider } = await import(\"./openai\");\n provider = new OpenAIProvider(apiKey);\n break;\n }\n case \"google\": {\n const { GoogleProvider } = await import(\"./google\");\n provider = new GoogleProvider(apiKey);\n break;\n }\n }\n cache.set(cacheKey, provider);\n return provider;\n}\n\n/** Test seam: register a provider instance directly (used to inject mocks). */\nexport function registerProvider(id: ProviderId, apiKey: string, provider: Provider): void {\n cache.set(`${id}:${apiKey}`, provider);\n}\n\n/** Test seam: clear the adapter cache. */\nexport function clearProviderCache(): void {\n cache.clear();\n}\n","// API-key resolution + redaction. Keys are BYO (caller-supplied): a static map\n// per provider and/or a dynamic resolver (for multi-tenant / per-call selection).\n// A provider with no resolvable key is simply unavailable — requesting one of its\n// models throws a ConfigError, the library analog of odla-db's config-presence\n// gating. Redaction patterns are lifted from odla-ai-old's publish-safety scanner.\n\nimport { ConfigError, type ProviderId } from \"../shape/index\";\n\n/** Static per-provider keys. */\nexport type KeyMap = Partial<Record<ProviderId, string>>;\n\n/** Dynamic key lookup — return undefined to signal \"no key for this provider\". */\nexport type KeyResolver = (provider: ProviderId) => string | undefined | Promise<string | undefined>;\n\nexport interface KeyOptions {\n keys?: KeyMap;\n /** Consulted first; falls back to `keys` when it returns undefined. */\n resolveKey?: KeyResolver;\n}\n\n/** Resolve the API key for a provider, or throw a ConfigError naming it. */\nexport async function resolveApiKey(\n provider: ProviderId,\n opts: KeyOptions,\n forModel: string,\n): Promise<string> {\n let key: string | undefined;\n if (opts.resolveKey) key = await opts.resolveKey(provider);\n if (!key) key = opts.keys?.[provider];\n if (!key) {\n throw new ConfigError(\n `No API key configured for provider \"${provider}\" (required by model \"${forModel}\"). ` +\n `Pass keys: { ${provider}: \"...\" } or a resolveKey() to init().`,\n );\n }\n return key;\n}\n\n/** Best-effort key-shape check (used by smoke/diagnostics, never for gating —\n * formats drift). */\nexport function looksLikeKey(provider: ProviderId, key: string): boolean {\n switch (provider) {\n case \"anthropic\":\n return /^sk-ant-[a-zA-Z0-9_-]{20,}/.test(key);\n case \"openai\":\n return /^sk-(?!ant-)(?:proj-)?[a-zA-Z0-9_-]{20,}/.test(key);\n case \"google\":\n return /^AIza[0-9A-Za-z_-]{35}$/.test(key) || key.length >= 20;\n }\n}\n\n/** Redact anything that looks like a provider secret from a string (logs/errors). */\nexport function redact(text: string): string {\n return text\n .replace(/sk-ant-[a-zA-Z0-9_-]{6,}/g, \"sk-ant-…\")\n .replace(/sk-(?!ant-)(?:proj-)?[a-zA-Z0-9_-]{10,}/g, \"sk-…\")\n .replace(/\\bAIza[0-9A-Za-z_-]{6,}/g, \"AIza…\");\n}\n","// The client facade. `init()` returns an `Ai` object (the odla convention —\n// init-returns-a-facade, cf. odla-db's `init()` → Db/AdminDb). It owns model\n// resolution (canonical id → provider), BYO key resolution, capability gating,\n// and the two convenience calls (`extract`, `search`) built generically on top\n// of the normalized `create` — so they work for any provider.\n\nimport {\n CapabilityError,\n ConfigError,\n ProviderError,\n extractText,\n type OracleContentBlock,\n type OracleEvent,\n type OracleRequest,\n type OracleResponse,\n type OracleUsage,\n type ProviderId,\n type TextBlock,\n} from \"../shape/index\";\nimport { validateRequest, type ModelSpec } from \"../shape/capabilities\";\nimport { DEFAULT_CATALOG, resolveModel, type Catalog } from \"../catalog/index\";\nimport { getProvider } from \"../providers/registry\";\nimport type { Provider } from \"../providers/types\";\nimport { resolveApiKey, type KeyMap, type KeyResolver } from \"./keys\";\n\nexport interface InitOptions {\n /** Static per-provider API keys (BYO). */\n keys?: KeyMap;\n /** Dynamic per-provider key lookup (multi-tenant / per-call). Consulted first. */\n resolveKey?: KeyResolver;\n /** Model used when a call omits `model`. */\n defaultModel?: string;\n /** Override / extend the built-in model catalog. */\n catalog?: Catalog;\n /** Inject provider adapters directly (tests, custom transports). When present\n * for a provider, it is used verbatim and no key is required. */\n providers?: Partial<Record<ProviderId, Provider>>;\n}\n\n/** Any turn's input — a canonical request with `model` optional (defaultModel fills). */\nexport type ChatInput = Omit<OracleRequest, \"model\"> & { model?: string };\n\nexport interface ExtractTool {\n name: string;\n description?: string;\n /** JSON Schema for the forced tool's arguments. */\n parameters: Record<string, unknown>;\n}\n\n/** A forced structured extraction: schema in, validated object out. */\nexport interface ExtractCall {\n model?: string;\n system?: string | TextBlock[];\n user: string | OracleContentBlock[];\n tool: ExtractTool;\n maxTokens?: number;\n}\n\n/** A web-search-enabled call: the model searches and returns prose. */\nexport interface SearchCall {\n model?: string;\n system?: string | TextBlock[];\n user: string | OracleContentBlock[];\n maxTokens?: number;\n}\n\nexport interface Ai {\n /** The active model catalog. */\n readonly catalog: Catalog;\n /** One assistant turn (non-streaming). */\n chat(input: ChatInput): Promise<OracleResponse>;\n /** One assistant turn as a stream of normalized OracleEvents. */\n stream(input: ChatInput): AsyncIterable<OracleEvent>;\n /** Forced single-tool extraction → validated object. */\n extract<T = unknown>(c: ExtractCall): Promise<{ value: T; usage: OracleUsage }>;\n /** Web-search-grounded prose answer. */\n search(c: SearchCall): Promise<{ text: string; usage: OracleUsage }>;\n}\n\n/** The minimal inference surface the agent loop and eval harness depend on. */\nexport type Inference = Pick<Ai, \"chat\" | \"stream\" | \"catalog\">;\n\nexport function init(opts: InitOptions = {}): Ai {\n const catalog = opts.catalog ?? DEFAULT_CATALOG;\n\n const pickModel = (model?: string): string => {\n const chosen = model ?? opts.defaultModel;\n if (!chosen) throw new ConfigError(\"No model specified and no defaultModel set in init().\");\n return chosen;\n };\n\n const resolveProviderFor = async (model: string): Promise<{ spec: ModelSpec; provider: Provider }> => {\n const spec = resolveModel(catalog, model);\n const injected = opts.providers?.[spec.provider];\n if (injected) return { spec, provider: injected };\n const key = await resolveApiKey(spec.provider, opts, model);\n const provider = await getProvider(spec.provider, key);\n return { spec, provider };\n };\n\n const chat: Ai[\"chat\"] = async (input) => {\n const model = pickModel(input.model);\n const req: OracleRequest = { ...input, model };\n const { spec, provider } = await resolveProviderFor(model);\n validateRequest(spec, req);\n return provider.create(spec, req);\n };\n\n async function* stream(input: ChatInput): AsyncIterable<OracleEvent> {\n const model = pickModel(input.model);\n const req: OracleRequest = { ...input, model };\n const { spec, provider } = await resolveProviderFor(model);\n if (!spec.capabilities.streaming) {\n throw new CapabilityError(`Model \"${model}\" (${spec.provider}) does not support streaming.`);\n }\n validateRequest(spec, req);\n yield* provider.stream(spec, req);\n }\n\n const extract: Ai[\"extract\"] = async <T = unknown>(c: ExtractCall) => {\n const model = pickModel(c.model);\n const req: OracleRequest = {\n model,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n tools: [{ name: c.tool.name, description: c.tool.description ?? \"\", inputSchema: c.tool.parameters }],\n toolChoice: { type: \"tool\", name: c.tool.name },\n maxTokens: c.maxTokens ?? 4096,\n };\n const { spec, provider } = await resolveProviderFor(model);\n validateRequest(spec, req);\n const res = await provider.create(spec, req);\n const block = res.content.find((b) => b.type === \"tool_use\" && b.name === c.tool.name);\n if (!block || block.type !== \"tool_use\") {\n throw new ProviderError(`Model \"${model}\" did not return the forced tool call \"${c.tool.name}\".`);\n }\n return { value: block.input as T, usage: res.usage };\n };\n\n const search: Ai[\"search\"] = async (c) => {\n const model = pickModel(c.model);\n const req: OracleRequest = {\n model,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n webSearch: true,\n maxTokens: c.maxTokens ?? 8192,\n };\n const { spec, provider } = await resolveProviderFor(model);\n validateRequest(spec, req);\n const res = await provider.create(spec, req);\n return { text: extractText(res.content), usage: res.usage };\n };\n\n return { catalog, chat, stream, extract, search };\n}\n","// Machine-readable context codegen — the odla-db pattern (generateLlmsTxt), but\n// for an inference/agent library the source is the model catalog rather than a\n// per-app schema. Emits an `llms.txt` giving an LLM everything it needs to build,\n// run, test, and persist agents with @odla-ai/ai, including the odla-db\n// handshake / storage / secrets flow. The shipped repo-root `llms.txt` is this\n// function's output over DEFAULT_CATALOG.\n\nimport { DEFAULT_CATALOG, type Catalog } from \"../catalog/index\";\nimport type { Capabilities } from \"../shape/capabilities\";\n\nfunction capsSummary(c: Capabilities): string {\n const flags: string[] = [\"text\"];\n if (c.imageIn) flags.push(\"image\");\n if (c.audioIn) flags.push(\"audio\");\n if (c.toolUse) flags.push(\"tools\");\n if (c.thinking) flags.push(\"thinking\");\n if (c.effort) flags.push(\"effort\");\n if (c.webSearch) flags.push(\"web-search\");\n if (c.structuredOutput) flags.push(\"structured\");\n return flags.join(\", \");\n}\n\n/** Generate the `llms.txt` context document for a given model catalog. */\nexport function generateLlmsTxt(catalog: Catalog = DEFAULT_CATALOG): string {\n const models = Object.values(catalog)\n .map((s) => `- \\`${s.id}\\` (${s.provider}) — ${capsSummary(s.capabilities)}`)\n .join(\"\\n\");\n\n return `# @odla-ai/ai — LLM context\n\nOne general-purpose interface for AI inference across Anthropic (Claude), OpenAI\n(GPT), and Google (Gemini) — text, image, and audio — plus a tool-use agent\nengine and an eval harness. Library-only: import in-process, bring your own keys.\nIsomorphic (Node 20+, Cloudflare Workers, browser). Provider SDKs are lazy-loaded.\n\n## Install\n\n npm i @odla-ai/ai\n # provider SDKs are peer-lazy-loaded; install those you use:\n # @anthropic-ai/sdk openai @google/genai\n # optional, for odla-db-backed storage + secrets:\n # @odla-ai/db\n\n## Core call\n\n import { init } from \"@odla-ai/ai\";\n const ai = init({ keys: { anthropic: KEY, openai: KEY, google: KEY }, defaultModel: \"claude-opus-4-8\" });\n const res = await ai.chat({ model: \"gpt-5\", messages: [{ role: \"user\", content: \"hi\" }], maxTokens: 256 });\n // res.content: OracleContentBlock[] (text/tool_use/thinking)\n\n- \\`ai.stream(input)\\` → async iterable of normalized events: message_start →\n content_block_start → content_block_delta {text_delta|thinking_delta|input_json_delta}\n → content_block_stop → message_delta → message_stop (plus \\`error\\`).\n- \\`ai.extract<T>({ model, user, tool })\\` → forced tool call; returns a parsed object.\n- \\`ai.search({ model, user })\\` → web-search-grounded prose.\n\n## Content blocks (multimodal)\n\ntext; image { source: base64|url }; audio { source: base64|url }; document (PDF);\ntool_use { id, name, input }; tool_result { toolUseId, content }; thinking.\nAudio input is accepted by OpenAI (\\`gpt-audio\\`) and Google, NOT by Claude — an\naudio block to a Claude model throws CapabilityError before any network call.\n\n## Agents\n\n import { runAgent, type Persona, type ToolDef } from \"@odla-ai/ai\";\n const add: ToolDef = { name: \"add\", description: \"add\", inputSchema: {...}, handler: (i) => ({ content: String(i.a + i.b) }) };\n const persona: Persona = { name: \"calc\", model: \"claude-opus-4-8\", system: \"...\", tools: [add], maxSteps: 4 };\n const run = await runAgent(ai, persona, { input: \"what is 21+21?\" });\n // run.finalText, run.toolCalls, run.usage, run.steps, run.stoppedReason\n\nTools may carry taint labels (outputTaint / acceptsTaint) for a CaMeL-style\nprompt-injection gate. Personas may carry a MemoryScope.\n\n## Skills\n\nA Skill = { name, instructions?, tools: ToolDef[] } is a bundle you attach to a\npersona (persona.skills); its tools merge into the model's tool list and its\ninstructions into the system prompt. Turn an odla-db entity into CRUD tools:\n\n import { entityCrudSkill } from \"@odla-ai/ai\";\n const todos = entityCrudSkill({ db, entity: \"todos\", fields: { text: { type: \"string\" }, done: { type: \"boolean\" } }, required: [\"text\"] });\n // → tools: list_todos, create_todo, update_todo (partial), delete_todo — handlers write to odla-db\n const run = await runAgent(ai, { name: \"todo-bot\", model: \"gpt-5\", skills: [todos] }, { input: \"add buy milk\" });\n\n## Evals\n\n import { evaluate, exactMatch, llmJudge } from \"@odla-ai/ai\";\n const report = await evaluate({ inference: ai, model: \"claude-opus-4-8\", grader: exactMatch(), cases: [{ input, expected }] });\n // graders: exactMatch, includes, structuredMatch, llmJudge; pass a persona to eval an agent.\n\n## Storage + BYO keys via odla-db (@odla-ai/db) — the handshake\n\nFollows odla-db's model. An agent never takes a platform secret; it does a\ndevice-authorization handshake for a scoped, revocable token, then provisions.\n\n import { requestToken, init as odlaInit } from \"@odla-ai/db\";\n import { provisionAgentApp, odlaDbKeyResolver, OdlaDbMemory, persistRun, init } from \"@odla-ai/ai\";\n\n // 1. Handshake: the human supplies the endpoint and approves a short code in odla-db Studio.\n const { token } = await requestToken({ endpoint: ODLA_URL, onCode: ({ userCode }) => show(userCode) });\n\n // 2. Provision: create app, mint app key, push agent schema, store BYO provider keys as secrets.\n const { appId, appKey } = await provisionAgentApp({ endpoint: ODLA_URL, token, providerKeys: { openai: OPENAI_KEY } });\n\n // 3. Runtime: read keys from odla-db secrets; persist memory + run traces.\n const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_URL });\n const ai = init({ resolveKey: odlaDbKeyResolver(db) });\n const persona = { name: \"assistant\", model: \"gpt-5\", memory: new OdlaDbMemory({ db, sessionId: \"user-42\" }) };\n const run = await runAgent(ai, persona, { input: \"hello\" });\n await persistRun(run, { db, sessionId: \"user-42\" });\n\nSecrets are read-only from the app key (odlaDbKeyResolver → db.secrets.get); they\nare WRITTEN only with the operator/dev token via provisionAgentApp/putSecret. Keys\nare AES-GCM-encrypted at rest in odla-db and never returned to browser clients.\n\n## Errors\n\nEvery error is an OdlaAIError subclass with a stable \\`code\\` — branch on err.code:\nconfig, auth, rate_limit, capability_unsupported, context_window, invalid_request,\ntool_input_invalid, provider_error.\n\n## Models\n\n${models}\n\nModel ids live in a data catalog and drift — verify against provider docs; extend\nvia buildCatalog / init({ catalog }).\n`;\n}\n","// Tool definitions for the agent loop. A ToolDef is config-as-data: a name, a\n// description, a JSON-Schema for its input, and an optional local `handler`. A\n// tool without a handler is a provider server tool (e.g. web search) that\n// resolves inside the adapter; a tool with a handler is executed in-process by\n// the loop.\n\nimport type { OracleContentBlock, OracleTool } from \"../shape/index\";\nimport type { TaintLabel, TaintSet } from \"./taint\";\n\n/** What a tool returns after running. */\nexport interface ToolOutput {\n content: string | OracleContentBlock[];\n isError?: boolean;\n}\n\n/** Context handed to a tool handler when it runs. */\nexport interface ToolContext {\n /** The taint currently on the conversation feeding this call. */\n taint: TaintSet;\n /** Optional cancellation signal, threaded from the run. */\n signal?: AbortSignal;\n}\n\nexport type ToolHandler = (\n input: Record<string, unknown>,\n ctx: ToolContext,\n) => Promise<ToolOutput> | ToolOutput;\n\nexport interface ToolDef {\n name: string;\n description: string;\n /** JSON Schema for the tool arguments. */\n inputSchema: Record<string, unknown>;\n /** Local executor. Omit for provider server tools resolved inside the adapter. */\n handler?: ToolHandler;\n /** Taint labels this tool's output introduces into the conversation. */\n outputTaint?: TaintLabel[];\n /** If set, the tool only runs when the conversation taint ⊆ this allowlist. */\n acceptsTaint?: TaintLabel[];\n}\n\n/** Project a ToolDef to the wire-level OracleTool the model sees. */\nexport function toOracleTool(t: ToolDef): OracleTool {\n return { name: t.name, description: t.description, inputSchema: t.inputSchema };\n}\n","// A Skill is a named, reusable bundle of tools plus the instructions that tell a\n// persona how and when to use them. Attaching a skill to a persona is how you\n// \"add a skill and call it as tools\": the skill's tools are merged into the\n// model's tool list, and its instructions are appended to the system prompt.\n// A skill whose tool handlers write to odla-db is how an agent changes data.\n\nimport type { ToolDef } from \"./tools\";\n\nexport interface Skill {\n /** Skill name — used as the heading for its instructions in the system prompt. */\n name: string;\n /** Guidance appended to the persona's system prompt (when/how to use the tools). */\n instructions?: string;\n /** The tools this skill contributes to the agent. */\n tools: ToolDef[];\n}\n\nexport interface ComposedPersona {\n system: string | undefined;\n tools: ToolDef[];\n}\n\n/** Merge a persona's own tools/system with its attached skills: skill tools are\n * appended to the tool list, and each skill's instructions become a titled\n * section of the system prompt. Order is preserved (persona first, then skills). */\nexport function composeSkills(\n system: string | undefined,\n tools: ToolDef[] | undefined,\n skills: Skill[] | undefined,\n): ComposedPersona {\n const composedTools: ToolDef[] = [...(tools ?? [])];\n const sections: string[] = system ? [system] : [];\n for (const skill of skills ?? []) {\n composedTools.push(...skill.tools);\n if (skill.instructions) sections.push(`## ${skill.name}\\n${skill.instructions}`);\n }\n return { system: sections.length > 0 ? sections.join(\"\\n\\n\") : undefined, tools: composedTools };\n}\n","// A lightweight CaMeL-style taint layer (after arXiv:2503.18813, the model used\n// by odla-ai-old's chat-engine). Values that entered the conversation from an\n// untrusted source carry a label; a privileged tool (\"sink\") declares which\n// taints it will accept, and the agent loop refuses to run it when the current\n// conversation taint is not a subset of that allowlist. This is the seed of a\n// prompt-injection defense — deliberately minimal in v1 (label + assert), not a\n// full information-flow tracker.\n\nimport { OdlaAIError } from \"../shape/index\";\n\nexport type TaintLabel =\n | \"web_untrusted\"\n | \"operator_pasted_untrusted\"\n | \"llm_inherited\"\n | `tool_untrusted:${string}`;\n\nexport type TaintSet = Set<TaintLabel>;\n\nexport function taint(...labels: TaintLabel[]): TaintSet {\n return new Set(labels);\n}\n\n/** Raised when a tool's taint allowlist doesn't cover the conversation's taint. */\nexport class TaintError extends OdlaAIError {\n constructor(message: string) {\n super(\"invalid_request\", message);\n }\n}\n\n/**\n * Throw unless every label currently on the conversation is in the sink's\n * allowlist. `llm_inherited` is intentionally excludable: a tool that lists it as\n * *not* accepted is one where model-derived instructions must never reach the\n * sink (the classic injected-instruction propagation case).\n */\nexport function assertSinkAcceptsTaint(sink: string, allowed: TaintLabel[], actual: TaintSet): void {\n const allow = new Set(allowed);\n for (const label of actual) {\n if (!allow.has(label)) {\n throw new TaintError(\n `Tool \"${sink}\" refused: conversation carries taint \"${label}\" which is not in its allowlist [${allowed.join(\", \")}].`,\n );\n }\n }\n}\n","// The tool-use agent loop — the \"running\" half of the engine. Given a Persona\n// and an input, it drives model turn → tool execution → model turn until the\n// model stops asking for tools (or a step/refusal limit is hit), and returns the\n// full conversation, an accumulated usage tally, and a trace of tool calls.\n//\n// Provider-agnostic: it speaks only the canonical shape via the Inference facade,\n// so the same persona runs on Claude, GPT, or Gemini unchanged.\n\nimport {\n addUsage,\n emptyUsage,\n extractText,\n extractToolUses,\n type OracleContentBlock,\n type OracleMessage,\n type OracleResponse,\n type OracleUsage,\n type ToolResultBlock,\n type ToolUseBlock,\n} from \"../shape/index\";\nimport type { Inference } from \"../client/init\";\nimport type { Persona } from \"./persona\";\nimport type { ToolDef, ToolOutput } from \"./tools\";\nimport { toOracleTool } from \"./tools\";\nimport { composeSkills } from \"./skill\";\nimport { assertSinkAcceptsTaint, type TaintLabel, type TaintSet } from \"./taint\";\n\n/** One resolved tool call within a run. */\nexport interface ToolCallRecord {\n toolUse: ToolUseBlock;\n output: ToolOutput;\n}\n\nexport interface AgentRunInput {\n /** A single user turn (convenience). */\n input?: string | OracleContentBlock[];\n /** Or an explicit list of messages (takes precedence when both are given). */\n messages?: OracleMessage[];\n signal?: AbortSignal;\n}\n\nexport type StoppedReason = \"end_turn\" | \"max_steps\" | \"refusal\";\n\nexport interface AgentRun {\n /** Concatenated text of the final assistant turn. */\n finalText: string;\n /** The last assistant response. */\n response: OracleResponse;\n /** The full conversation (loaded memory + input + assistant/tool turns). */\n messages: OracleMessage[];\n /** Accumulated usage across every model turn. */\n usage: OracleUsage;\n /** Number of model turns taken. */\n steps: number;\n /** Every tool call executed, in order. */\n toolCalls: ToolCallRecord[];\n stoppedReason: StoppedReason;\n}\n\nexport async function runAgent(inference: Inference, persona: Persona, input: AgentRunInput): Promise<AgentRun> {\n // Fold attached skills into the tool list + system prompt.\n const { system, tools } = composeSkills(persona.system, persona.tools, persona.skills);\n const handlerByName = new Map<string, ToolDef>();\n for (const t of tools) if (t.handler) handlerByName.set(t.name, t);\n\n // Assemble the starting conversation: memory, then the caller's input.\n const priorFromMemory = persona.memory ? await persona.memory.load() : [];\n const startingInput: OracleMessage[] = input.messages\n ? input.messages\n : input.input !== undefined\n ? [{ role: \"user\", content: input.input }]\n : [];\n const messages: OracleMessage[] = [...priorFromMemory, ...startingInput];\n const runStartIndex = messages.length - startingInput.length; // memory boundary for append()\n\n const usage = emptyUsage();\n const toolCalls: ToolCallRecord[] = [];\n const accumulatedTaint: TaintSet = new Set<TaintLabel>([\"llm_inherited\"]);\n const maxSteps = persona.maxSteps ?? 8;\n\n let response: OracleResponse | undefined;\n let stoppedReason: StoppedReason = \"max_steps\";\n let steps = 0;\n\n while (steps < maxSteps) {\n steps++;\n response = await inference.chat({\n model: persona.model,\n system,\n messages,\n tools: tools.length > 0 ? tools.map(toOracleTool) : undefined,\n maxTokens: persona.maxTokens ?? 4096,\n effort: persona.effort,\n thinking: persona.thinking,\n temperature: persona.temperature,\n webSearch: persona.webSearch,\n });\n addUsage(usage, response.usage);\n messages.push({ role: \"assistant\", content: response.content });\n\n if (response.stopReason === \"refusal\") {\n stoppedReason = \"refusal\";\n break;\n }\n\n const toolUses = extractToolUses(response.content);\n if (toolUses.length === 0) {\n stoppedReason = \"end_turn\";\n break;\n }\n\n const resultBlocks: ToolResultBlock[] = [];\n for (const toolUse of toolUses) {\n const def = handlerByName.get(toolUse.name);\n if (!def || !def.handler) {\n resultBlocks.push(errorResult(toolUse.id, `No local handler for tool \"${toolUse.name}\".`));\n continue;\n }\n if (def.acceptsTaint) assertSinkAcceptsTaint(def.name, def.acceptsTaint, accumulatedTaint);\n\n let output: ToolOutput;\n try {\n output = await def.handler(toolUse.input, { taint: new Set(accumulatedTaint), signal: input.signal });\n } catch (err) {\n output = { content: `Tool \"${toolUse.name}\" threw: ${(err as Error).message}`, isError: true };\n }\n toolCalls.push({ toolUse, output });\n resultBlocks.push({ type: \"tool_result\", toolUseId: toolUse.id, content: output.content, isError: output.isError });\n for (const label of def.outputTaint ?? []) accumulatedTaint.add(label);\n }\n messages.push({ role: \"user\", content: resultBlocks });\n }\n\n if (!response) throw new Error(\"runAgent: maxSteps must be at least 1\");\n\n if (persona.memory) await persona.memory.append(messages.slice(runStartIndex));\n\n return {\n finalText: extractText(response.content),\n response,\n messages,\n usage,\n steps,\n toolCalls,\n stoppedReason,\n };\n}\n\nfunction errorResult(toolUseId: string, message: string): ToolResultBlock {\n return { type: \"tool_result\", toolUseId, content: message, isError: true };\n}\n","// Pluggable persona memory. A MemoryScope supplies prior conversation context\n// before a run and receives the new turns after it. The default is in-process;\n// an app that wants durable memory (e.g. persisted to odla-db) implements this\n// interface — deliberately kept out of the core so the library has no infra deps.\n\nimport type { OracleMessage } from \"../shape/index\";\n\nexport interface MemoryScope {\n /** Messages to prepend before this run's input. */\n load(): Promise<OracleMessage[]> | OracleMessage[];\n /** Persist the messages produced during this run (excludes what load() returned). */\n append(messages: OracleMessage[]): Promise<void> | void;\n}\n\n/** A simple process-lifetime memory scope. */\nexport class InMemoryScope implements MemoryScope {\n private messages: OracleMessage[] = [];\n\n load(): OracleMessage[] {\n return [...this.messages];\n }\n\n append(messages: OracleMessage[]): void {\n this.messages.push(...messages);\n }\n\n clear(): void {\n this.messages = [];\n }\n}\n","// The eval harness: run a set of cases through either a Persona (via the agent\n// loop) or a plain model chat, grade each with a Grader, and return a report.\n// A thin, honest harness — run → grade → report — that is the seed of a real\n// conformance/eval suite, not a full framework.\n\nimport {\n addUsage,\n emptyUsage,\n extractText,\n type OracleContentBlock,\n type OracleMessage,\n type OracleResponse,\n type OracleUsage,\n} from \"../shape/index\";\nimport type { Inference } from \"../client/init\";\nimport { runAgent } from \"../agent/loop\";\nimport type { Persona } from \"../agent/persona\";\nimport type { EvalCase, GradeResult, Grader } from \"./types\";\n\nexport interface EvalResult {\n case: EvalCase;\n output: string;\n grade: GradeResult;\n response: OracleResponse;\n usage: OracleUsage;\n}\n\nexport interface EvalReport {\n total: number;\n passed: number;\n failed: number;\n passRate: number;\n results: EvalResult[];\n usage: OracleUsage;\n}\n\nexport interface EvaluateOptions {\n inference: Inference;\n cases: EvalCase[];\n grader: Grader;\n /** Run each case through this persona (agent loop). Mutually exclusive with model. */\n persona?: Persona;\n /** Or run each case as a single chat with this model + system. */\n model?: string;\n system?: string;\n maxTokens?: number;\n /** Max cases run concurrently (default 5). */\n concurrency?: number;\n}\n\nexport async function evaluate(opts: EvaluateOptions): Promise<EvalReport> {\n if (!opts.persona && !opts.model) {\n throw new Error(\"evaluate() requires either a persona or a model.\");\n }\n const results = await runPool(opts.cases, opts.concurrency ?? 5, (c) => runCase(opts, c));\n\n const usage = emptyUsage();\n let passed = 0;\n for (const r of results) {\n addUsage(usage, r.usage);\n if (r.grade.pass) passed++;\n }\n const total = results.length;\n return { total, passed, failed: total - passed, passRate: total ? passed / total : 0, results, usage };\n}\n\nasync function runCase(opts: EvaluateOptions, c: EvalCase): Promise<EvalResult> {\n const messages = toMessages(c.input);\n\n let output: string;\n let response: OracleResponse;\n let usage: OracleUsage;\n\n if (opts.persona) {\n const run = await runAgent(opts.inference, opts.persona, { messages });\n output = run.finalText;\n response = run.response;\n usage = run.usage;\n } else {\n response = await opts.inference.chat({\n model: opts.model!,\n system: opts.system,\n messages,\n maxTokens: opts.maxTokens ?? 1024,\n });\n output = extractText(response.content);\n usage = response.usage;\n }\n\n const grade = await opts.grader({ case: c, output, response });\n return { case: c, output, grade, response, usage };\n}\n\nfunction toMessages(input: EvalCase[\"input\"]): OracleMessage[] {\n if (Array.isArray(input) && input.length > 0 && \"role\" in (input[0] as object)) {\n return input as OracleMessage[];\n }\n return [{ role: \"user\", content: input as string | OracleContentBlock[] }];\n}\n\n/** Run `fn` over `items` with at most `limit` in flight, preserving input order. */\nasync function runPool<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {\n for (;;) {\n const i = next++;\n if (i >= items.length) return;\n results[i] = await fn(items[i]!);\n }\n });\n await Promise.all(workers);\n return results;\n}\n","// Built-in graders. `exactMatch` / `includes` / `structuredMatch` are pure and\n// offline; `llmJudge` dogfoods the inference core (an Inference facade) to grade\n// open-ended output against `case.expected` treated as a rubric.\n\nimport { extractText } from \"../shape/index\";\nimport type { Inference } from \"../client/init\";\nimport type { Grader } from \"./types\";\n\n/** Pass when trimmed output equals `case.expected` (stringified). */\nexport function exactMatch(): Grader {\n return ({ case: c, output }) => {\n const expected = String(c.expected ?? \"\").trim();\n const pass = output.trim() === expected;\n return { pass, reason: pass ? \"exact match\" : `expected \"${expected}\", got \"${output.trim()}\"` };\n };\n}\n\n/** Pass when output contains `case.expected` (case-insensitive by default). */\nexport function includes(opts: { caseInsensitive?: boolean } = {}): Grader {\n const ci = opts.caseInsensitive ?? true;\n return ({ case: c, output }) => {\n const needle = String(c.expected ?? \"\");\n const hay = ci ? output.toLowerCase() : output;\n const pass = hay.includes(ci ? needle.toLowerCase() : needle);\n return { pass, reason: pass ? `contains \"${needle}\"` : `missing \"${needle}\"` };\n };\n}\n\n/** Pass when output parses as JSON and contains every key/value in\n * `case.expected` (a recursive subset match). */\nexport function structuredMatch(): Grader {\n return ({ case: c, output }) => {\n let actual: unknown;\n try {\n actual = JSON.parse(stripFences(output));\n } catch {\n return { pass: false, reason: \"output is not valid JSON\" };\n }\n const ok = subset(c.expected, actual);\n return { pass: ok, reason: ok ? \"structural subset match\" : \"output is missing expected fields/values\" };\n };\n}\n\n/** Grade open-ended output with a judge model. `case.expected` is the rubric.\n * The judge is asked to return a strict JSON verdict which is tolerantly parsed. */\nexport function llmJudge(opts: { inference: Inference; model: string; extraGuidance?: string }): Grader {\n return async ({ case: c, output }) => {\n const rubric = typeof c.expected === \"string\" ? c.expected : JSON.stringify(c.expected);\n const system =\n \"You are a strict evaluation judge. Decide whether the CANDIDATE output satisfies the RUBRIC. \" +\n \"Respond with ONLY a JSON object: {\\\"pass\\\": boolean, \\\"reason\\\": string}. No prose, no code fences.\" +\n (opts.extraGuidance ? ` ${opts.extraGuidance}` : \"\");\n const user = `RUBRIC:\\n${rubric}\\n\\nCANDIDATE:\\n${output}`;\n const res = await opts.inference.chat({\n model: opts.model,\n system,\n messages: [{ role: \"user\", content: user }],\n maxTokens: 512,\n });\n const text = extractText(res.content);\n try {\n const verdict = JSON.parse(stripFences(text)) as { pass?: unknown; reason?: unknown };\n return { pass: Boolean(verdict.pass), reason: typeof verdict.reason === \"string\" ? verdict.reason : undefined };\n } catch {\n // Fall back to a lenient keyword read if the judge didn't return clean JSON.\n const pass = /\\bpass(ed)?\\b|\\btrue\\b|\\byes\\b/i.test(text) && !/\\bfail(ed)?\\b|\\bfalse\\b|\\bno\\b/i.test(text);\n return { pass, reason: `unparseable judge output: ${text.slice(0, 200)}` };\n }\n };\n}\n\nfunction stripFences(s: string): string {\n return s.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\").trim();\n}\n\n/** Recursive \"is `expected` a subset of `actual`\" check. */\nfunction subset(expected: unknown, actual: unknown): boolean {\n if (expected === null || typeof expected !== \"object\") return expected === actual;\n if (Array.isArray(expected)) {\n if (!Array.isArray(actual) || actual.length < expected.length) return false;\n return expected.every((e, i) => subset(e, actual[i]));\n }\n if (actual === null || typeof actual !== \"object\" || Array.isArray(actual)) return false;\n const a = actual as Record<string, unknown>;\n return Object.entries(expected as Record<string, unknown>).every(([k, v]) => subset(v, a[k]));\n}\n","// The odla-db schema odla-ai persists into: three flat, natural-key-keyed\n// entities for conversation memory and agent-run traces. odla-db is\n// schema-optional (attrs are auto-created on write), but pushing this schema\n// makes the `key` attr `unique` so `Lookup` upserts bind to exactly one row.\n//\n// AGENT_SCHEMA is the already-serialized wire shape (generated with @odla-ai/db's\n// `i.schema(...).serialize()`), embedded so provisioning can push it without the\n// consumer needing the schema builder.\n\n/** Entity namespaces odla-ai owns in odla-db (all non-`$`, so no platform clash). */\nexport const NS = {\n conversation: \"agent_conversation\",\n message: \"agent_message\",\n run: \"agent_run\",\n} as const;\n\n/** Serialized odla-db schema (POST to `/app/:id/schema` as `{ schema: AGENT_SCHEMA }`). */\nexport const AGENT_SCHEMA = {\n entities: {\n agent_conversation: {\n attrs: {\n key: { type: \"string\", unique: true, indexed: true, optional: false },\n sessionId: { type: \"string\", unique: false, indexed: true, optional: false },\n title: { type: \"string\", unique: false, indexed: false, optional: true },\n createdAt: { type: \"date\", unique: false, indexed: true, optional: false },\n updatedAt: { type: \"date\", unique: false, indexed: true, optional: false },\n },\n },\n agent_message: {\n attrs: {\n key: { type: \"string\", unique: true, indexed: true, optional: false },\n sessionId: { type: \"string\", unique: false, indexed: true, optional: false },\n seq: { type: \"number\", unique: false, indexed: true, optional: false },\n role: { type: \"string\", unique: false, indexed: false, optional: false },\n content: { type: \"string\", unique: false, indexed: false, optional: false },\n createdAt: { type: \"date\", unique: false, indexed: true, optional: false },\n },\n },\n agent_run: {\n attrs: {\n key: { type: \"string\", unique: true, indexed: true, optional: false },\n sessionId: { type: \"string\", unique: false, indexed: true, optional: false },\n persona: { type: \"string\", unique: false, indexed: false, optional: true },\n status: { type: \"string\", unique: false, indexed: true, optional: false },\n steps: { type: \"number\", unique: false, indexed: false, optional: false },\n inputTokens: { type: \"number\", unique: false, indexed: false, optional: false },\n outputTokens: { type: \"number\", unique: false, indexed: false, optional: false },\n finalText: { type: \"string\", unique: false, indexed: false, optional: true },\n trace: { type: \"json\", unique: false, indexed: false, optional: false },\n createdAt: { type: \"date\", unique: false, indexed: true, optional: false },\n },\n },\n },\n links: {},\n} as const;\n","// An odla-db-backed MemoryScope: persona conversation memory persisted as\n// odla-db records. load() replays prior turns for a session; append() upserts\n// the run's new turns by natural key with a deterministic mutationId (so a\n// retried run doesn't duplicate). Injected with an odla-db client — odla-ai keeps\n// zero runtime dependency on @odla-ai/db.\n\nimport type { OracleMessage, OracleRole } from \"../shape/index\";\nimport type { MemoryScope } from \"../agent/memory\";\nimport type { OdlaDbClient, OdlaOp } from \"./types\";\nimport { NS } from \"./schema\";\n\nexport interface OdlaDbMemoryOptions {\n db: OdlaDbClient;\n /** Groups a conversation's turns; stable across runs to accumulate history. */\n sessionId: string;\n /** Optional human-readable title stored on the conversation node. */\n title?: string;\n /** Override entity namespaces (default NS.message / NS.conversation). */\n namespaces?: { message?: string; conversation?: string };\n}\n\nexport class OdlaDbMemory implements MemoryScope {\n private readonly db: OdlaDbClient;\n private readonly sessionId: string;\n private readonly title: string | undefined;\n private readonly msgNs: string;\n private readonly convNs: string;\n private loadedCount = 0;\n\n constructor(opts: OdlaDbMemoryOptions) {\n this.db = opts.db;\n this.sessionId = opts.sessionId;\n this.title = opts.title;\n this.msgNs = opts.namespaces?.message ?? NS.message;\n this.convNs = opts.namespaces?.conversation ?? NS.conversation;\n }\n\n async load(): Promise<OracleMessage[]> {\n const res = await this.db.query({\n [this.msgNs]: { $: { where: { sessionId: this.sessionId }, order: { seq: \"asc\" }, limit: 100_000 } },\n });\n const rows = (res[this.msgNs] ?? []) as { seq?: number; role?: string; content?: string }[];\n this.loadedCount = rows.length;\n return rows.map((r) => ({ role: (r.role ?? \"user\") as OracleRole, content: parseContent(r.content) }));\n }\n\n async append(messages: OracleMessage[]): Promise<void> {\n if (messages.length === 0) return;\n const base = this.loadedCount;\n const now = Date.now();\n\n const ops: OdlaOp[] = [\n {\n t: \"update\",\n ns: this.convNs,\n id: { ns: this.convNs, attr: \"key\", value: this.sessionId },\n attrs: {\n key: this.sessionId,\n sessionId: this.sessionId,\n ...(this.title ? { title: this.title } : {}),\n ...(base === 0 ? { createdAt: now } : {}),\n updatedAt: now,\n },\n },\n ];\n\n messages.forEach((m, i) => {\n const seq = base + i;\n const key = `${this.sessionId}:${seq}`;\n ops.push({\n t: \"update\",\n ns: this.msgNs,\n id: { ns: this.msgNs, attr: \"key\", value: key },\n attrs: { key, sessionId: this.sessionId, seq, role: m.role, content: JSON.stringify(m.content), createdAt: now },\n });\n });\n\n await this.db.transact(ops, {\n mutationId: `${this.sessionId}:append:${base}:${base + messages.length}`,\n });\n this.loadedCount += messages.length;\n }\n}\n\n/** Message content was stored as JSON (string | OracleContentBlock[]); restore it. */\nfunction parseContent(raw: string | undefined): OracleMessage[\"content\"] {\n if (raw === undefined) return \"\";\n try {\n return JSON.parse(raw) as OracleMessage[\"content\"];\n } catch {\n return raw; // tolerate a plain string that wasn't JSON-encoded\n }\n}\n","// Persist agent-run traces to odla-db for observability. Each run becomes one\n// `agent_run` record (status, steps, usage, final text, and a JSON trace of tool\n// calls + full message log), upserted by a natural key so a retried write is\n// idempotent.\n\nimport type { AgentRun } from \"../agent/loop\";\nimport type { OdlaDbClient } from \"./types\";\nimport { NS } from \"./schema\";\n\nexport interface PersistRunOptions {\n db: OdlaDbClient;\n /** Groups runs (typically the same session as the memory scope). */\n sessionId: string;\n personaName?: string;\n /** Stable id for exactly-once persistence; defaults to `${sessionId}:${now}`. */\n runId?: string;\n namespace?: string;\n}\n\n/** Write a run trace; returns the record key. */\nexport async function persistRun(run: AgentRun, opts: PersistRunOptions): Promise<string> {\n const ns = opts.namespace ?? NS.run;\n const key = opts.runId ?? `${opts.sessionId}:${Date.now()}`;\n await opts.db.transact(\n [\n {\n t: \"update\",\n ns,\n id: { ns, attr: \"key\", value: key },\n attrs: {\n key,\n sessionId: opts.sessionId,\n ...(opts.personaName ? { persona: opts.personaName } : {}),\n status: run.stoppedReason,\n steps: run.steps,\n inputTokens: run.usage.inputTokens,\n outputTokens: run.usage.outputTokens,\n finalText: run.finalText,\n trace: {\n toolCalls: run.toolCalls.map((t) => ({ name: t.toolUse.name, input: t.toolUse.input, output: t.output })),\n messages: run.messages,\n },\n createdAt: Date.now(),\n },\n },\n ],\n { mutationId: `run:${key}` },\n );\n return key;\n}\n\nexport interface QueryRunsOptions {\n db: OdlaDbClient;\n sessionId: string;\n limit?: number;\n namespace?: string;\n}\n\n/** Read persisted runs for a session, newest first. */\nexport async function queryRuns(opts: QueryRunsOptions): Promise<Record<string, unknown>[]> {\n const ns = opts.namespace ?? NS.run;\n const res = await opts.db.query({\n [ns]: { $: { where: { sessionId: opts.sessionId }, order: { createdAt: \"desc\" }, limit: opts.limit ?? 50 } },\n });\n return res[ns] ?? [];\n}\n","// An odla-db-secrets-backed key resolver. BYO provider keys are stored as odla-db\n// secrets (AES-GCM-encrypted at rest, never returned to browser clients) and read\n// at request time with the app key via the read-only `secrets.get`. Wire it in\n// with `init({ resolveKey: odlaDbKeyResolver(db) })`.\n//\n// Storing the keys is a separate, privileged step (an operator/dev token, not the\n// app key) — see `provisionAgentApp` / `putSecret` in ./provision.\n\nimport type { ProviderId } from \"../shape/index\";\nimport type { KeyResolver } from \"../client/keys\";\nimport type { OdlaDbClient } from \"./types\";\n\n/** Default secret name per provider. */\nexport const DEFAULT_SECRET_NAMES: Record<ProviderId, string> = {\n anthropic: \"anthropic_api_key\",\n openai: \"openai_api_key\",\n google: \"google_api_key\",\n};\n\nexport interface OdlaDbKeyResolverOptions {\n /** Map a provider to its secret name (default `<provider>_api_key`). Use this\n * to scope per-end-user keys, e.g. `(p) => \\`${p}_api_key__${userSub}\\``. */\n secretName?: (provider: ProviderId) => string;\n /** Cache resolved keys in memory (default true). */\n cache?: boolean;\n}\n\n/** Build a KeyResolver that reads provider keys from odla-db secrets. A missing\n * secret resolves to `undefined` (→ that provider is simply unavailable, and\n * odla-ai throws its own ConfigError) rather than surfacing odla-db's 404. */\nexport function odlaDbKeyResolver(db: OdlaDbClient, opts: OdlaDbKeyResolverOptions = {}): KeyResolver {\n const nameFor = opts.secretName ?? ((p: ProviderId) => DEFAULT_SECRET_NAMES[p]);\n const useCache = opts.cache ?? true;\n const cache = new Map<ProviderId, string>();\n\n return async (provider: ProviderId): Promise<string | undefined> => {\n if (useCache) {\n const hit = cache.get(provider);\n if (hit !== undefined) return hit;\n }\n try {\n const key = await db.secrets.get(nameFor(provider));\n if (useCache) cache.set(provider, key);\n return key;\n } catch {\n return undefined; // absent secret → provider unavailable\n }\n };\n}\n","// Provisioning helpers — the \"stand up a new agent app\" flow, mirroring odla-db's\n// handshake model. These are plain `fetch` calls (no @odla-ai/db dependency)\n// against a human-supplied odla-db endpoint, authenticated with an **operator or\n// developer token** (the platform `ADMIN_SECRET`, or an `odla_dev_...` token from\n// `@odla-ai/db`'s `requestToken(...)` device handshake) — NOT an app key.\n//\n// The typical agent flow:\n// 1. `const { token } = await requestToken({ endpoint, onCode })` // from @odla-ai/db\n// 2. `const { appId, appKey } = await provisionAgentApp({ endpoint, token, providerKeys })`\n// 3. `const db = init({ appId, adminToken: appKey, endpoint })` // @odla-ai/db client\n// 4. `const ai = init({ resolveKey: odlaDbKeyResolver(db) })` // keys from secrets\n// `persona.memory = new OdlaDbMemory({ db, sessionId })`\n//\n// Storing a secret needs the operator/dev token; reading it back at request time\n// needs only the app key (via odlaDbKeyResolver).\n\nimport { AuthError, ConfigError, ProviderError, type ProviderId } from \"../shape/index\";\nimport { redact } from \"../client/keys\";\nimport { AGENT_SCHEMA } from \"./schema\";\nimport { DEFAULT_SECRET_NAMES } from \"./keys\";\n\ntype FetchLike = typeof fetch;\n\nexport interface ProvisionContext {\n /** The odla-db worker URL — always human-supplied, never hardcoded. */\n endpoint: string;\n /** Operator `ADMIN_SECRET` or an `odla_dev_...` developer token (owns the app). */\n token: string;\n fetch?: FetchLike;\n}\n\nasync function post(ctx: ProvisionContext, path: string, body: unknown): Promise<Record<string, unknown>> {\n const f = ctx.fetch ?? fetch;\n const res = await f(`${ctx.endpoint.replace(/\\/$/, \"\")}${path}`, {\n method: \"POST\",\n headers: { authorization: `Bearer ${ctx.token}`, \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw errorFor(res.status, await safeText(res), path);\n return (await res.json().catch(() => ({}))) as Record<string, unknown>;\n}\n\nasync function postWithKey(ctx: { endpoint: string; fetch?: FetchLike }, path: string, appKey: string, body: unknown): Promise<Record<string, unknown>> {\n const f = ctx.fetch ?? fetch;\n const res = await f(`${ctx.endpoint.replace(/\\/$/, \"\")}${path}`, {\n method: \"POST\",\n headers: { authorization: `Bearer ${appKey}`, \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw errorFor(res.status, await safeText(res), path);\n return (await res.json().catch(() => ({}))) as Record<string, unknown>;\n}\n\n/** Create an app (owned by the token's developer). Returns its id. */\nexport async function createApp(ctx: ProvisionContext, opts: { appId?: string; name?: string } = {}): Promise<string> {\n const body: Record<string, unknown> = {};\n if (opts.appId) body.appId = opts.appId;\n if (opts.name) body.name = opts.name;\n const json = await post(ctx, \"/admin/apps\", body);\n const appId = (json.appId ?? json.id ?? (json.app as { appId?: string } | undefined)?.appId ?? opts.appId) as string | undefined;\n if (!appId) throw new ProviderError(\"[odla-db] createApp: response did not include an appId\");\n return appId;\n}\n\n/** Mint a per-app `odla_sk_...` key (shown once). */\nexport async function mintAppKey(ctx: ProvisionContext, appId: string): Promise<string> {\n const json = await post(ctx, `/admin/apps/${encodeURIComponent(appId)}/keys`, {});\n const key = json.key as string | undefined;\n if (!key) throw new ProviderError(\"[odla-db] mintAppKey: response did not include a key\");\n return key;\n}\n\n/** Push odla-ai's agent memory/run schema (authenticated with the app key). */\nexport async function pushAgentSchema(ctx: { endpoint: string; fetch?: FetchLike }, appId: string, appKey: string): Promise<void> {\n await postWithKey(ctx, `/app/${encodeURIComponent(appId)}/schema`, appKey, { schema: AGENT_SCHEMA });\n}\n\n/** Store a named secret (operator/dev token). Value must be a non-`$` string. */\nexport async function putSecret(ctx: ProvisionContext, appId: string, name: string, value: string): Promise<void> {\n await post(ctx, `/admin/apps/${encodeURIComponent(appId)}/secrets`, { name, value });\n}\n\n/** Per-namespace CEL permission rules (odla-db is DEFAULT-DENY: end-users can't\n * read or write any namespace until its rules are set; the app key bypasses\n * rules, so agent-only apps work without them). */\nexport type AppRules = Record<string, { view?: string; create?: string; update?: string; delete?: string }>;\n\n/** Install permission rules (operator/dev token). Replaces the app's rule set. */\nexport async function putRules(ctx: ProvisionContext, appId: string, rules: AppRules): Promise<void> {\n await post(ctx, `/app/${encodeURIComponent(appId)}/admin/rules`, rules);\n}\n\nexport interface ProvisionAgentAppOptions extends ProvisionContext {\n appId?: string;\n appName?: string;\n /** Provider keys to store as odla-db secrets (read later via odlaDbKeyResolver). */\n providerKeys?: Partial<Record<ProviderId, string>>;\n /** Map a provider to its secret name (default `<provider>_api_key`). */\n secretName?: (provider: ProviderId) => string;\n /** Push the agent schema (default true). */\n pushSchema?: boolean;\n /** Permission rules to install. odla-db is default-deny, so set these for any\n * namespace end-users should reach (e.g. own-rows-only:\n * `{ todos: { view: \"auth.id == data.ownerId\", create: \"auth.id == data.ownerId\" } }`).\n * Omit for agent-only apps — the app key bypasses rules. */\n rules?: AppRules;\n}\n\nexport interface ProvisionedApp {\n appId: string;\n /** The minted `odla_sk_...` app key — pass as `adminToken` to `@odla-ai/db`'s init. */\n appKey: string;\n}\n\n/** One call to stand up an agent app: create → mint key → push schema → store\n * provider keys as secrets. Returns the appId + app key for the runtime client. */\nexport async function provisionAgentApp(opts: ProvisionAgentAppOptions): Promise<ProvisionedApp> {\n const ctx: ProvisionContext = { endpoint: opts.endpoint, token: opts.token, fetch: opts.fetch };\n const appId = await createApp(ctx, { appId: opts.appId, name: opts.appName });\n const appKey = await mintAppKey(ctx, appId);\n\n if (opts.pushSchema !== false) {\n await pushAgentSchema({ endpoint: opts.endpoint, fetch: opts.fetch }, appId, appKey);\n }\n\n if (opts.rules) {\n await putRules(ctx, appId, opts.rules);\n }\n\n const nameFor = opts.secretName ?? ((p: ProviderId) => DEFAULT_SECRET_NAMES[p]);\n for (const [provider, value] of Object.entries(opts.providerKeys ?? {})) {\n if (!value) continue;\n await putSecret(ctx, appId, nameFor(provider as ProviderId), value);\n }\n\n return { appId, appKey };\n}\n\nfunction errorFor(status: number, text: string, path: string): Error {\n const msg = redact(`[odla-db] ${path} → ${status} ${text}`.trim());\n if (status === 401 || status === 403) return new AuthError(msg, { providerStatus: status });\n if (status === 501) return new ConfigError(`${msg} (secrets/feature not enabled on the odla-db worker)`);\n if (status === 400) return new ConfigError(msg);\n return new ProviderError(msg, { providerStatus: status });\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return (await res.text()).slice(0, 300);\n } catch {\n return \"\";\n }\n}\n","// Turn any odla-db entity into an agent Skill: list / create / update / delete\n// tools whose handlers call db.query / db.transact. This is the ergonomic hook\n// for \"add a skill and call it as tools that change data in odla-db\" — a todos\n// skill is a few lines (see the example at the bottom of this file's doc).\n//\n// Generic over the entity and its fields; the tool JSON-Schemas are built from\n// the `fields` you declare, so the model gets a precise contract.\n\nimport type { Skill } from \"../agent/skill\";\nimport type { ToolDef, ToolOutput } from \"../agent/tools\";\nimport type { OdlaDbClient, OdlaOp } from \"./types\";\n\nexport interface EntityCrudSkillOptions {\n /** The injected odla-db client (from @odla-ai/db's init). */\n db: OdlaDbClient;\n /** Entity namespace, e.g. \"todos\". */\n entity: string;\n /** JSON-Schema `properties` for the create/update fields (e.g. { text: { type: \"string\" }, done: { type: \"boolean\" } }). */\n fields: Record<string, unknown>;\n /** Field names required to create (default: none). */\n required?: string[];\n /** Skill name (default `${entity} management`). */\n name?: string;\n /** Extra instructions appended to the generated guidance. */\n instructions?: string;\n /** Singular noun for tool names (default: `entity` with a trailing \"s\" stripped). */\n singular?: string;\n /** Plural noun for the list tool (default: `entity`). */\n plural?: string;\n /** Input field carrying the entity id in update/delete (default \"id\"). */\n idField?: string;\n /** Generate a new id on create (default crypto.randomUUID). */\n genId?: () => string;\n /** Attr stamped with Date.now() on create; pass false to disable (default \"createdAt\"). */\n stampCreatedAt?: string | false;\n /** Default ordering for the list tool (default { createdAt: \"asc\" }). */\n order?: Record<string, \"asc\" | \"desc\">;\n}\n\n/** Build a CRUD Skill for one odla-db entity. */\nexport function entityCrudSkill(opts: EntityCrudSkillOptions): Skill {\n const { db, entity, fields } = opts;\n const singular = opts.singular ?? entity.replace(/s$/, \"\");\n const plural = opts.plural ?? entity;\n const idField = opts.idField ?? \"id\";\n const genId = opts.genId ?? (() => globalThis.crypto.randomUUID());\n const stampAttr = opts.stampCreatedAt === undefined ? \"createdAt\" : opts.stampCreatedAt;\n const order = opts.order ?? { createdAt: \"asc\" };\n const fieldNames = Object.keys(fields);\n\n const pickFields = (input: Record<string, unknown>): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const k of fieldNames) if (k in input && input[k] !== undefined) out[k] = input[k];\n return out;\n };\n const ok = (obj: Record<string, unknown>): ToolOutput => ({ content: JSON.stringify(obj) });\n\n // odla-db's `update` op is an UPSERT (creates the entity if the id is missing),\n // so update/delete must verify the id exists first — otherwise a model that\n // passes a wrong/hallucinated id silently spawns a phantom empty row instead of\n // failing. Membership is checked client-side (rows carry `id`), which works the\n // same on the real client and on test doubles.\n const existsById = async (id: string): Promise<boolean> => {\n const res = await db.query({ [entity]: { $: {} } });\n return (res[entity] ?? []).some((r) => String(r.id ?? \"\") === id);\n };\n\n const listTool: ToolDef = {\n name: `list_${plural}`,\n description: `List ${plural}. Optionally filter by exact field values and/or limit the count.`,\n inputSchema: {\n type: \"object\",\n properties: {\n filter: { type: \"object\", description: `Exact-match filters, e.g. { \"done\": false }`, additionalProperties: true },\n limit: { type: \"number\", description: \"Max rows to return.\" },\n },\n },\n handler: async (input) => {\n const filter = (input.filter as Record<string, unknown> | undefined) ?? undefined;\n const limit = typeof input.limit === \"number\" ? input.limit : undefined;\n const $: Record<string, unknown> = { order };\n if (filter && Object.keys(filter).length > 0) $.where = filter;\n if (limit !== undefined) $.limit = limit;\n const res = await db.query({ [entity]: { $ } });\n return ok({ [plural]: res[entity] ?? [] });\n },\n };\n\n const createTool: ToolDef = {\n name: `create_${singular}`,\n description: `Create a new ${singular}.`,\n inputSchema: { type: \"object\", properties: fields, ...(opts.required ? { required: opts.required } : {}) },\n handler: async (input) => {\n for (const req of opts.required ?? []) {\n const v = input[req];\n if (v === undefined || v === null || v === \"\") {\n return { content: `Cannot create ${singular}: field \"${req}\" is required.`, isError: true };\n }\n }\n const id = genId();\n const attrs: Record<string, unknown> = pickFields(input);\n if (stampAttr) attrs[stampAttr] = Date.now();\n await db.transact([{ t: \"update\", ns: entity, id, attrs }]);\n return ok({ id, created: true });\n },\n };\n\n const updateTool: ToolDef = {\n name: `update_${singular}`,\n description: `Update fields of an existing ${singular} by ${idField}. Only the fields you pass change.`,\n inputSchema: {\n type: \"object\",\n properties: {\n [idField]: { type: \"string\", description: `The exact ${idField} string from list_${plural} — NOT the item's position in a numbered list.` },\n ...fields,\n },\n required: [idField],\n },\n handler: async (input) => {\n const id = String(input[idField] ?? \"\");\n if (!id) return { content: `Missing \"${idField}\".`, isError: true };\n const attrs = pickFields(input);\n if (Object.keys(attrs).length === 0) return { content: \"No fields to update.\", isError: true };\n if (!(await existsById(id))) {\n return { content: `No ${singular} with ${idField} \"${id}\". Call list_${plural} for valid ids, then retry.`, isError: true };\n }\n await db.transact([{ t: \"update\", ns: entity, id, attrs }]);\n return ok({ id, updated: true });\n },\n };\n\n const deleteTool: ToolDef = {\n name: `delete_${singular}`,\n description: `Delete a ${singular} by ${idField}.`,\n inputSchema: {\n type: \"object\",\n properties: { [idField]: { type: \"string\", description: `The exact ${idField} string from list_${plural} — NOT the item's position in a numbered list.` } },\n required: [idField],\n },\n handler: async (input) => {\n const id = String(input[idField] ?? \"\");\n if (!id) return { content: `Missing \"${idField}\".`, isError: true };\n if (!(await existsById(id))) {\n return { content: `No ${singular} with ${idField} \"${id}\". Call list_${plural} for valid ids.`, isError: true };\n }\n const op: OdlaOp = { t: \"delete\", ns: entity, id };\n await db.transact([op]);\n return ok({ id, deleted: true });\n },\n };\n\n const guidance =\n `Manage ${plural} in the database. Use list_${plural} to read (optionally filtered), ` +\n `create_${singular} to add, update_${singular} to change fields (e.g. mark done), and ` +\n `delete_${singular} to remove. Always confirm the current list with list_${plural} before ` +\n `updating or deleting so you use the right id.` +\n (opts.instructions ? `\\n${opts.instructions}` : \"\");\n\n return {\n name: opts.name ?? `${entity} management`,\n instructions: guidance,\n tools: [listTool, createTool, updateTool, deleteTool],\n };\n}\n"],"mappings":"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","names":["cache"]}
|
|
1
|
+
{"version":3,"sources":["../src/shape/capabilities.ts","../src/catalog/anthropic.ts","../src/catalog/openai.ts","../src/catalog/google.ts","../src/catalog/index.ts","../src/providers/registry.ts","../src/client/keys.ts","../src/client/init.ts","../src/doc/llms.ts","../src/agent/tools.ts","../src/agent/skill.ts","../src/agent/taint.ts","../src/agent/loop.ts","../src/agent/memory.ts","../src/eval/harness.ts","../src/eval/graders.ts","../src/store/schema.ts","../src/store/memory.ts","../src/store/runs.ts","../src/store/keys.ts","../src/store/platform.ts","../src/store/provision.ts","../src/store/crud.ts"],"sourcesContent":["// Model capabilities + request validation. This is the \"config is data\"\n// doctrine from odla-kg: what a model can do is a data descriptor, and every\n// request is checked against it *before* dispatch so a mismatch fails fast with\n// a clear CapabilityError instead of a confusing provider 400 (the canonical\n// example: audio input to a Claude model — Claude has no audio input).\n\nimport {\n CapabilityError,\n blocksOf,\n type OracleRequest,\n type ProviderId,\n} from \"./index\";\n\n/** The kind of model — chat/multimodal is the v1 focus; the others are modeled\n * so embeddings, transcription, TTS, and image generation slot in later. */\nexport type ModelKind = \"chat\" | \"embed\" | \"transcribe\" | \"tts\" | \"image\";\n\nexport interface Capabilities {\n kind: ModelKind;\n /** Accepts text input. */\n textIn: boolean;\n /** Accepts image input (ImageBlock). */\n imageIn: boolean;\n /** Accepts audio input (AudioBlock). */\n audioIn: boolean;\n /** Accepts document/PDF input (DocumentBlock). */\n documentIn: boolean;\n /** Supports tool / function calling. */\n toolUse: boolean;\n /** Supports a native reasoning/thinking mode. */\n thinking: boolean;\n /** Supports an effort/reasoning-depth knob. */\n effort: boolean;\n /** Supports streaming responses. */\n streaming: boolean;\n /** Supports constrained JSON / structured output. */\n structuredOutput: boolean;\n /** Supports a provider server-side web-search tool. */\n webSearch: boolean;\n}\n\n/** A catalog entry: a canonical model id mapped to a provider, that provider's\n * native model id, and what the model can do. */\nexport interface ModelSpec {\n /** Canonical id used across odla-ai (e.g. \"claude-opus-4-8\", \"gpt-5\"). */\n id: string;\n provider: ProviderId;\n /** The id passed to the provider SDK. Often equal to `id`. */\n nativeId: string;\n capabilities: Capabilities;\n contextWindow?: number;\n maxOutput?: number;\n}\n\n/** Sensible chat/multimodal defaults; per-model tables override what differs. */\nexport function chatCapabilities(overrides: Partial<Capabilities> = {}): Capabilities {\n return {\n kind: \"chat\",\n textIn: true,\n imageIn: true,\n audioIn: false,\n documentIn: false,\n toolUse: true,\n thinking: false,\n effort: false,\n streaming: true,\n structuredOutput: true,\n webSearch: false,\n ...overrides,\n };\n}\n\n/**\n * Validate a request against a model's capabilities. Throws CapabilityError on\n * the first violation. Runs before any provider call — the single choke point\n * that turns \"audio to Claude\" from an opaque upstream 400 into a clear error.\n */\nexport function validateRequest(spec: ModelSpec, req: OracleRequest): void {\n const caps = spec.capabilities;\n const fail = (what: string): never => {\n throw new CapabilityError(\n `Model \"${spec.id}\" (${spec.provider}) does not support ${what}.`,\n );\n };\n\n if (caps.kind !== \"chat\") fail(`chat requests (it is a \"${caps.kind}\" model)`);\n\n for (const msg of req.messages) {\n for (const block of blocksOf(msg.content)) {\n if (block.type === \"image\" && !caps.imageIn) fail(\"image input\");\n if (block.type === \"audio\" && !caps.audioIn) fail(\"audio input\");\n if (block.type === \"document\" && !caps.documentIn) fail(\"document (PDF) input\");\n }\n }\n\n if (req.tools && req.tools.length > 0 && !caps.toolUse) fail(\"tool use\");\n if (req.webSearch && !caps.webSearch) fail(\"web search\");\n if (req.responseFormat && !caps.structuredOutput) fail(\"structured output\");\n if (req.effort && !caps.effort) fail(\"the effort parameter\");\n if (req.thinking === \"adaptive\" && !caps.thinking) fail(\"thinking\");\n}\n","// Anthropic (Claude) model catalog — config as data.\n//\n// Model ids + capabilities VERIFIED against the `claude-api` skill (this build):\n// current chat ids are claude-opus-4-8 (default), claude-sonnet-5,\n// claude-haiku-4-5, claude-fable-5; thinking is adaptive-only; forced tool-call\n// is tool_choice:{type:\"tool\"}; web search is web_search_20260209 and needs\n// Sonnet-4.6+/Opus (not Haiku); Claude has NO audio input.\n// Re-verify via the skill before shipping if time has passed — never hardcode\n// model ids from memory.\n\nimport { chatCapabilities, type ModelSpec } from \"../shape/capabilities\";\n\nconst FRONTIER = () =>\n chatCapabilities({\n documentIn: true, // PDF\n thinking: true, // adaptive\n effort: true, // output_config.effort\n webSearch: true, // web_search_20260209\n audioIn: false, // Claude has no audio input\n });\n\nexport const ANTHROPIC_MODELS: ModelSpec[] = [\n {\n id: \"claude-opus-4-8\",\n provider: \"anthropic\",\n nativeId: \"claude-opus-4-8\",\n capabilities: FRONTIER(),\n contextWindow: 1_000_000,\n maxOutput: 128_000,\n },\n {\n id: \"claude-sonnet-5\",\n provider: \"anthropic\",\n nativeId: \"claude-sonnet-5\",\n capabilities: FRONTIER(),\n contextWindow: 1_000_000,\n maxOutput: 128_000,\n },\n {\n id: \"claude-fable-5\",\n provider: \"anthropic\",\n nativeId: \"claude-fable-5\",\n capabilities: FRONTIER(),\n contextWindow: 1_000_000,\n maxOutput: 128_000,\n },\n {\n // Haiku 4.5: no effort param (errors), web search needs Sonnet-4.6+, so both off.\n id: \"claude-haiku-4-5\",\n provider: \"anthropic\",\n nativeId: \"claude-haiku-4-5\",\n capabilities: chatCapabilities({\n documentIn: true,\n thinking: false,\n effort: false,\n webSearch: false,\n audioIn: false,\n }),\n contextWindow: 200_000,\n maxOutput: 64_000,\n },\n];\n","// OpenAI model catalog — config as data.\n//\n// Model ids VERIFIED against the account's live /v1/models list at build time\n// (2026-07). Canonical id == native id. The GPT-5 line are reasoning models\n// (reasoning-effort knob → effort: true) and take image but not audio input;\n// audio input is the separate `gpt-audio` line. Re-verify against /v1/models as\n// the lineup drifts — do NOT trust these from memory. Pass a custom catalog to\n// init() to add models without a release.\n\nimport { chatCapabilities, type ModelSpec } from \"../shape/capabilities\";\n\nexport const OPENAI_MODELS: ModelSpec[] = [\n {\n id: \"gpt-5.5\",\n provider: \"openai\",\n nativeId: \"gpt-5.5\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n id: \"gpt-5\",\n provider: \"openai\",\n nativeId: \"gpt-5\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n id: \"gpt-5-mini\",\n provider: \"openai\",\n nativeId: \"gpt-5-mini\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n id: \"gpt-5-nano\",\n provider: \"openai\",\n nativeId: \"gpt-5-nano\",\n capabilities: chatCapabilities({ effort: true }),\n contextWindow: 400_000,\n },\n {\n // Search-tuned chat model: the adapter sends web_search_options when a\n // request sets webSearch. No reasoning-effort knob.\n id: \"gpt-5-search-api\",\n provider: \"openai\",\n nativeId: \"gpt-5-search-api\",\n capabilities: chatCapabilities({ webSearch: true }),\n contextWindow: 400_000,\n },\n {\n // Non-reasoning multimodal chat (image input, no effort knob).\n id: \"gpt-4o\",\n provider: \"openai\",\n nativeId: \"gpt-4o\",\n capabilities: chatCapabilities(),\n contextWindow: 128_000,\n },\n {\n // Audio-input model line — accepts audio, not images.\n id: \"gpt-audio\",\n provider: \"openai\",\n nativeId: \"gpt-audio\",\n capabilities: chatCapabilities({ imageIn: false, audioIn: true }),\n contextWindow: 128_000,\n },\n];\n","// Google (Gemini) model catalog — config as data.\n//\n// ⚠️ VERIFY these ids + capabilities against current Google GenAI docs before\n// shipping — do NOT trust them from memory. Canonical id == native id here.\n//\n// Gemini is natively multimodal: text + image + audio + PDF input on the current\n// generation, tool calling via functionDeclarations, thinking via thinkingConfig,\n// structured output via responseSchema, and web grounding via the googleSearch\n// tool — hence the broad default caps below.\n\nimport { chatCapabilities, type ModelSpec } from \"../shape/capabilities\";\n\nconst GEMINI = () =>\n chatCapabilities({\n audioIn: true,\n documentIn: true,\n thinking: true,\n effort: true, // mapped onto thinkingConfig\n webSearch: true, // googleSearch grounding\n });\n\nexport const GOOGLE_MODELS: ModelSpec[] = [\n {\n id: \"gemini-2.5-pro\",\n provider: \"google\",\n nativeId: \"gemini-2.5-pro\",\n capabilities: GEMINI(),\n contextWindow: 1_000_000,\n },\n {\n id: \"gemini-2.5-flash\",\n provider: \"google\",\n nativeId: \"gemini-2.5-flash\",\n capabilities: GEMINI(),\n contextWindow: 1_000_000,\n },\n {\n id: \"gemini-2.0-flash\",\n provider: \"google\",\n nativeId: \"gemini-2.0-flash\",\n capabilities: chatCapabilities({\n audioIn: true,\n documentIn: true,\n thinking: false,\n effort: false,\n webSearch: true,\n }),\n contextWindow: 1_000_000,\n },\n];\n","// The model catalog: canonical model id → ModelSpec (provider, native id,\n// capabilities). The default catalog merges the three per-provider tables;\n// init() accepts a custom or extended catalog so apps can add/override models\n// without waiting on a release.\n\nimport { ConfigError, type ProviderId } from \"../shape/index\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport { ANTHROPIC_MODELS } from \"./anthropic\";\nimport { OPENAI_MODELS } from \"./openai\";\nimport { GOOGLE_MODELS } from \"./google\";\n\n/** Canonical id → spec. */\nexport type Catalog = Record<string, ModelSpec>;\n\nfunction index(specs: ModelSpec[]): Catalog {\n const out: Catalog = {};\n for (const spec of specs) out[spec.id] = spec;\n return out;\n}\n\n/** The built-in catalog across Anthropic, OpenAI, and Google. */\nexport const DEFAULT_CATALOG: Catalog = index([\n ...ANTHROPIC_MODELS,\n ...OPENAI_MODELS,\n ...GOOGLE_MODELS,\n]);\n\n/** Merge extra specs over a base catalog (later entries win by id). */\nexport function buildCatalog(base: Catalog, extra: ModelSpec[] = []): Catalog {\n return { ...base, ...index(extra) };\n}\n\n/** Look up a model, throwing a ConfigError (with the known ids) if unknown. */\nexport function resolveModel(catalog: Catalog, id: string): ModelSpec {\n const spec = catalog[id];\n if (!spec) {\n const known = Object.keys(catalog).sort().join(\", \");\n throw new ConfigError(`Unknown model \"${id}\". Known models: ${known || \"(none)\"}.`);\n }\n return spec;\n}\n\n/** List every provider the catalog can route to. */\nexport function providersInCatalog(catalog: Catalog): ProviderId[] {\n const set = new Set<ProviderId>();\n for (const spec of Object.values(catalog)) set.add(spec.provider);\n return [...set];\n}\n\nexport { ANTHROPIC_MODELS, OPENAI_MODELS, GOOGLE_MODELS };\n","// Provider registry. `getProvider` lazy-`import()`s the adapter module — and,\n// transitively, only that provider's SDK — so a Claude-only consumer never loads\n// `openai` or `@google/genai` at runtime. Adapters are cached per (provider, key)\n// so repeated calls reuse one SDK client.\n\nimport type { ProviderId } from \"../shape/index\";\nimport type { Provider } from \"./types\";\n\nconst cache = new Map<string, Provider>();\n\nexport async function getProvider(id: ProviderId, apiKey: string): Promise<Provider> {\n const cacheKey = `${id}:${apiKey}`;\n const existing = cache.get(cacheKey);\n if (existing) return existing;\n\n let provider: Provider;\n switch (id) {\n case \"anthropic\": {\n const { AnthropicProvider } = await import(\"./anthropic\");\n provider = new AnthropicProvider(apiKey);\n break;\n }\n case \"openai\": {\n const { OpenAIProvider } = await import(\"./openai\");\n provider = new OpenAIProvider(apiKey);\n break;\n }\n case \"google\": {\n const { GoogleProvider } = await import(\"./google\");\n provider = new GoogleProvider(apiKey);\n break;\n }\n }\n cache.set(cacheKey, provider);\n return provider;\n}\n\n/** Test seam: register a provider instance directly (used to inject mocks). */\nexport function registerProvider(id: ProviderId, apiKey: string, provider: Provider): void {\n cache.set(`${id}:${apiKey}`, provider);\n}\n\n/** Test seam: clear the adapter cache. */\nexport function clearProviderCache(): void {\n cache.clear();\n}\n","// API-key resolution + redaction. Keys are BYO (caller-supplied): a static map\n// per provider and/or a dynamic resolver (for multi-tenant / per-call selection).\n// A provider with no resolvable key is simply unavailable — requesting one of its\n// models throws a ConfigError, the library analog of odla-db's config-presence\n// gating. Redaction patterns are lifted from odla-ai-old's publish-safety scanner.\n\nimport { ConfigError, type ProviderId } from \"../shape/index\";\n\n/** Static per-provider keys. */\nexport type KeyMap = Partial<Record<ProviderId, string>>;\n\n/** Dynamic key lookup — return undefined to signal \"no key for this provider\". */\nexport type KeyResolver = (provider: ProviderId) => string | undefined | Promise<string | undefined>;\n\nexport interface KeyOptions {\n keys?: KeyMap;\n /** Consulted first; falls back to `keys` when it returns undefined. */\n resolveKey?: KeyResolver;\n}\n\n/** Resolve the API key for a provider, or throw a ConfigError naming it. */\nexport async function resolveApiKey(\n provider: ProviderId,\n opts: KeyOptions,\n forModel: string,\n): Promise<string> {\n let key: string | undefined;\n if (opts.resolveKey) key = await opts.resolveKey(provider);\n if (!key) key = opts.keys?.[provider];\n if (!key) {\n throw new ConfigError(\n `No API key configured for provider \"${provider}\" (required by model \"${forModel}\"). ` +\n `Pass keys: { ${provider}: \"...\" } or a resolveKey() to init().`,\n );\n }\n return key;\n}\n\n/** Best-effort key-shape check (used by smoke/diagnostics, never for gating —\n * formats drift). */\nexport function looksLikeKey(provider: ProviderId, key: string): boolean {\n switch (provider) {\n case \"anthropic\":\n return /^sk-ant-[a-zA-Z0-9_-]{20,}/.test(key);\n case \"openai\":\n return /^sk-(?!ant-)(?:proj-)?[a-zA-Z0-9_-]{20,}/.test(key);\n case \"google\":\n return /^AIza[0-9A-Za-z_-]{35}$/.test(key) || key.length >= 20;\n }\n}\n\n/** Redact anything that looks like a provider secret from a string (logs/errors). */\nexport function redact(text: string): string {\n return text\n .replace(/sk-ant-[a-zA-Z0-9_-]{6,}/g, \"sk-ant-…\")\n .replace(/sk-(?!ant-)(?:proj-)?[a-zA-Z0-9_-]{10,}/g, \"sk-…\")\n .replace(/\\bAIza[0-9A-Za-z_-]{6,}/g, \"AIza…\");\n}\n","// The client facade. `init()` returns an `Ai` object (the odla convention —\n// init-returns-a-facade, cf. odla-db's `init()` → Db/AdminDb). It owns model\n// resolution (canonical id → provider), BYO key resolution, capability gating,\n// and the two convenience calls (`extract`, `search`) built generically on top\n// of the normalized `create` — so they work for any provider.\n\nimport {\n CapabilityError,\n ConfigError,\n ProviderError,\n extractText,\n type OracleContentBlock,\n type OracleEvent,\n type OracleRequest,\n type OracleResponse,\n type OracleUsage,\n type ProviderId,\n type TextBlock,\n} from \"../shape/index\";\nimport { validateRequest, type ModelSpec } from \"../shape/capabilities\";\nimport { DEFAULT_CATALOG, resolveModel, type Catalog } from \"../catalog/index\";\nimport { getProvider } from \"../providers/registry\";\nimport type { Provider } from \"../providers/types\";\nimport { resolveApiKey, type KeyMap, type KeyResolver } from \"./keys\";\n\nexport interface InitOptions {\n /** Static per-provider API keys (BYO). */\n keys?: KeyMap;\n /** Dynamic per-provider key lookup (multi-tenant / per-call). Consulted first. */\n resolveKey?: KeyResolver;\n /** Model used when a call omits `model`. */\n defaultModel?: string;\n /** Override / extend the built-in model catalog. */\n catalog?: Catalog;\n /** Inject provider adapters directly (tests, custom transports). When present\n * for a provider, it is used verbatim and no key is required. */\n providers?: Partial<Record<ProviderId, Provider>>;\n}\n\n/** Any turn's input — a canonical request with `model` optional (defaultModel fills). */\nexport type ChatInput = Omit<OracleRequest, \"model\"> & { model?: string };\n\nexport interface ExtractTool {\n name: string;\n description?: string;\n /** JSON Schema for the forced tool's arguments. */\n parameters: Record<string, unknown>;\n}\n\n/** A forced structured extraction: schema in, validated object out. */\nexport interface ExtractCall {\n model?: string;\n system?: string | TextBlock[];\n user: string | OracleContentBlock[];\n tool: ExtractTool;\n maxTokens?: number;\n}\n\n/** A web-search-enabled call: the model searches and returns prose. */\nexport interface SearchCall {\n model?: string;\n system?: string | TextBlock[];\n user: string | OracleContentBlock[];\n maxTokens?: number;\n}\n\nexport interface Ai {\n /** The active model catalog. */\n readonly catalog: Catalog;\n /** One assistant turn (non-streaming). */\n chat(input: ChatInput): Promise<OracleResponse>;\n /** One assistant turn as a stream of normalized OracleEvents. */\n stream(input: ChatInput): AsyncIterable<OracleEvent>;\n /** Forced single-tool extraction → validated object. */\n extract<T = unknown>(c: ExtractCall): Promise<{ value: T; usage: OracleUsage }>;\n /** Web-search-grounded prose answer. */\n search(c: SearchCall): Promise<{ text: string; usage: OracleUsage }>;\n}\n\n/** The minimal inference surface the agent loop and eval harness depend on. */\nexport type Inference = Pick<Ai, \"chat\" | \"stream\" | \"catalog\">;\n\nexport function init(opts: InitOptions = {}): Ai {\n const catalog = opts.catalog ?? DEFAULT_CATALOG;\n\n const pickModel = (model?: string): string => {\n const chosen = model ?? opts.defaultModel;\n if (!chosen) throw new ConfigError(\"No model specified and no defaultModel set in init().\");\n return chosen;\n };\n\n const resolveProviderFor = async (model: string): Promise<{ spec: ModelSpec; provider: Provider }> => {\n const spec = resolveModel(catalog, model);\n const injected = opts.providers?.[spec.provider];\n if (injected) return { spec, provider: injected };\n const key = await resolveApiKey(spec.provider, opts, model);\n const provider = await getProvider(spec.provider, key);\n return { spec, provider };\n };\n\n const chat: Ai[\"chat\"] = async (input) => {\n const model = pickModel(input.model);\n const req: OracleRequest = { ...input, model };\n const { spec, provider } = await resolveProviderFor(model);\n validateRequest(spec, req);\n return provider.create(spec, req);\n };\n\n async function* stream(input: ChatInput): AsyncIterable<OracleEvent> {\n const model = pickModel(input.model);\n const req: OracleRequest = { ...input, model };\n const { spec, provider } = await resolveProviderFor(model);\n if (!spec.capabilities.streaming) {\n throw new CapabilityError(`Model \"${model}\" (${spec.provider}) does not support streaming.`);\n }\n validateRequest(spec, req);\n yield* provider.stream(spec, req);\n }\n\n const extract: Ai[\"extract\"] = async <T = unknown>(c: ExtractCall) => {\n const model = pickModel(c.model);\n const req: OracleRequest = {\n model,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n tools: [{ name: c.tool.name, description: c.tool.description ?? \"\", inputSchema: c.tool.parameters }],\n toolChoice: { type: \"tool\", name: c.tool.name },\n maxTokens: c.maxTokens ?? 4096,\n };\n const { spec, provider } = await resolveProviderFor(model);\n validateRequest(spec, req);\n const res = await provider.create(spec, req);\n const block = res.content.find((b) => b.type === \"tool_use\" && b.name === c.tool.name);\n if (!block || block.type !== \"tool_use\") {\n throw new ProviderError(`Model \"${model}\" did not return the forced tool call \"${c.tool.name}\".`);\n }\n return { value: block.input as T, usage: res.usage };\n };\n\n const search: Ai[\"search\"] = async (c) => {\n const model = pickModel(c.model);\n const req: OracleRequest = {\n model,\n system: c.system,\n messages: [{ role: \"user\", content: c.user }],\n webSearch: true,\n maxTokens: c.maxTokens ?? 8192,\n };\n const { spec, provider } = await resolveProviderFor(model);\n validateRequest(spec, req);\n const res = await provider.create(spec, req);\n return { text: extractText(res.content), usage: res.usage };\n };\n\n return { catalog, chat, stream, extract, search };\n}\n","// Machine-readable context codegen — the odla-db pattern (generateLlmsTxt), but\n// for an inference/agent library the source is the model catalog rather than a\n// per-app schema. Emits an `llms.txt` giving an LLM everything it needs to build,\n// run, test, and persist agents with @odla-ai/ai, including the odla-db\n// handshake / storage / secrets flow. The shipped repo-root `llms.txt` is this\n// function's output over DEFAULT_CATALOG.\n\nimport { DEFAULT_CATALOG, type Catalog } from \"../catalog/index\";\nimport type { Capabilities } from \"../shape/capabilities\";\n\nfunction capsSummary(c: Capabilities): string {\n const flags: string[] = [\"text\"];\n if (c.imageIn) flags.push(\"image\");\n if (c.audioIn) flags.push(\"audio\");\n if (c.toolUse) flags.push(\"tools\");\n if (c.thinking) flags.push(\"thinking\");\n if (c.effort) flags.push(\"effort\");\n if (c.webSearch) flags.push(\"web-search\");\n if (c.structuredOutput) flags.push(\"structured\");\n return flags.join(\", \");\n}\n\n/** Generate the `llms.txt` context document for a given model catalog. */\nexport function generateLlmsTxt(catalog: Catalog = DEFAULT_CATALOG): string {\n const models = Object.values(catalog)\n .map((s) => `- \\`${s.id}\\` (${s.provider}) — ${capsSummary(s.capabilities)}`)\n .join(\"\\n\");\n\n return `# @odla-ai/ai — LLM context\n\nOne general-purpose interface for AI inference across Anthropic (Claude), OpenAI\n(GPT), and Google (Gemini) — text, image, and audio — plus a tool-use agent\nengine and an eval harness. Library-only: import in-process, bring your own keys.\nIsomorphic (Node 20+, Cloudflare Workers, browser). Provider SDKs are lazy-loaded.\n\n## Install\n\n npm i @odla-ai/ai\n # provider SDKs are peer-lazy-loaded; install those you use:\n # @anthropic-ai/sdk openai @google/genai\n # optional, for odla-db-backed storage + secrets:\n # @odla-ai/db\n\n## Core call\n\n import { init } from \"@odla-ai/ai\";\n const ai = init({ keys: { anthropic: KEY, openai: KEY, google: KEY }, defaultModel: \"claude-opus-4-8\" });\n const res = await ai.chat({ model: \"gpt-5\", messages: [{ role: \"user\", content: \"hi\" }], maxTokens: 256 });\n // res.content: OracleContentBlock[] (text/tool_use/thinking)\n\n- \\`ai.stream(input)\\` → async iterable of normalized events: message_start →\n content_block_start → content_block_delta {text_delta|thinking_delta|input_json_delta}\n → content_block_stop → message_delta → message_stop (plus \\`error\\`).\n- \\`ai.extract<T>({ model, user, tool })\\` → forced tool call; returns a parsed object.\n- \\`ai.search({ model, user })\\` → web-search-grounded prose.\n\n## Content blocks (multimodal)\n\ntext; image { source: base64|url }; audio { source: base64|url }; document (PDF);\ntool_use { id, name, input }; tool_result { toolUseId, content }; thinking.\nAudio input is accepted by OpenAI (\\`gpt-audio\\`) and Google, NOT by Claude — an\naudio block to a Claude model throws CapabilityError before any network call.\n\n## Agents\n\n import { runAgent, type Persona, type ToolDef } from \"@odla-ai/ai\";\n const add: ToolDef = { name: \"add\", description: \"add\", inputSchema: {...}, handler: (i) => ({ content: String(i.a + i.b) }) };\n const persona: Persona = { name: \"calc\", model: \"claude-opus-4-8\", system: \"...\", tools: [add], maxSteps: 4 };\n const run = await runAgent(ai, persona, { input: \"what is 21+21?\" });\n // run.finalText, run.toolCalls, run.usage, run.steps, run.stoppedReason\n\nTools may carry taint labels (outputTaint / acceptsTaint) for a CaMeL-style\nprompt-injection gate. Personas may carry a MemoryScope.\n\n## Skills\n\nA Skill = { name, instructions?, tools: ToolDef[] } is a bundle you attach to a\npersona (persona.skills); its tools merge into the model's tool list and its\ninstructions into the system prompt. Turn an odla-db entity into CRUD tools:\n\n import { entityCrudSkill } from \"@odla-ai/ai\";\n const todos = entityCrudSkill({ db, entity: \"todos\", fields: { text: { type: \"string\" }, done: { type: \"boolean\" } }, required: [\"text\"] });\n // → tools: list_todos, create_todo, update_todo (partial), delete_todo — handlers write to odla-db\n const run = await runAgent(ai, { name: \"todo-bot\", model: \"gpt-5\", skills: [todos] }, { input: \"add buy milk\" });\n\n## Evals\n\n import { evaluate, exactMatch, llmJudge } from \"@odla-ai/ai\";\n const report = await evaluate({ inference: ai, model: \"claude-opus-4-8\", grader: exactMatch(), cases: [{ input, expected }] });\n // graders: exactMatch, includes, structuredMatch, llmJudge; pass a persona to eval an agent.\n\n## Storage + BYO keys via odla-db (@odla-ai/db) — the handshake\n\nFollows odla-db's model. An agent never takes a platform secret; it does a\ndevice-authorization handshake for a scoped, revocable token, then provisions.\n\n import { requestToken, init as odlaInit } from \"@odla-ai/db\";\n import { provisionAgentApp, odlaDbKeyResolver, OdlaDbMemory, persistRun, init } from \"@odla-ai/ai\";\n\n // 1. Handshake: the human supplies the endpoint and approves a short code in odla-db Studio.\n const { token } = await requestToken({ endpoint: ODLA_URL, onCode: ({ userCode }) => show(userCode) });\n\n // 2. Provision: create app, mint app key, push agent schema, store BYO provider keys as secrets.\n const { appId, appKey } = await provisionAgentApp({ endpoint: ODLA_URL, token, providerKeys: { openai: OPENAI_KEY } });\n\n // 3. Runtime: read keys from odla-db secrets; persist memory + run traces.\n const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_URL });\n const ai = init({ resolveKey: odlaDbKeyResolver(db) });\n const persona = { name: \"assistant\", model: \"gpt-5\", memory: new OdlaDbMemory({ db, sessionId: \"user-42\" }) };\n const run = await runAgent(ai, persona, { input: \"hello\" });\n await persistRun(run, { db, sessionId: \"user-42\" });\n\nSecrets are read-only from the app key (odlaDbKeyResolver → db.secrets.get); they\nare WRITTEN only with the operator/dev token via provisionAgentApp/putSecret. Keys\nare AES-GCM-encrypted at rest in odla-db and never returned to browser clients.\n\n## Platform wiring (odla.ai apps registry)\n\nApps registered on the odla platform get LLM access as an APP SETTING —\nnever put provider keys in your Worker's wrangler vars/secrets. Provider +\ndefault model are per-environment registry config (set in Studio's AI card\nor \\`@odla-ai/apps\\` setAi); the API key lives in the platform vault (the\nenv's odla-db tenant secrets, write-only for operators). One call reads both:\n\n import { initFromPlatform } from \"@odla-ai/ai\";\n import { init as odlaInit } from \"@odla-ai/db\";\n\n const db = odlaInit({ appId: TENANT, adminToken: env.ODLA_API_KEY, endpoint: \"https://db.odla.ai\" });\n const { ai, provider, model } = await initFromPlatform({\n platform: \"https://odla.ai\", appId: APP_ID, env: \"prod\", db });\n // ai.chat / ai.stream / ai.extract — provider/model from public-config\n // (cached ~60s, so Studio changes apply without redeploys); the key is\n // fetched from the vault at call time via odlaDbKeyResolver.\n\nThe only secret the Worker carries is its odla-db app key. Rotating the LLM\nkey = writing the secret again; switching provider/model = a Studio edit.\n\n## Errors\n\nEvery error is an OdlaAIError subclass with a stable \\`code\\` — branch on err.code:\nconfig, auth, rate_limit, capability_unsupported, context_window, invalid_request,\ntool_input_invalid, provider_error.\n\n## Models\n\n${models}\n\nModel ids live in a data catalog and drift — verify against provider docs; extend\nvia buildCatalog / init({ catalog }).\n`;\n}\n","// Tool definitions for the agent loop. A ToolDef is config-as-data: a name, a\n// description, a JSON-Schema for its input, and an optional local `handler`. A\n// tool without a handler is a provider server tool (e.g. web search) that\n// resolves inside the adapter; a tool with a handler is executed in-process by\n// the loop.\n\nimport type { OracleContentBlock, OracleTool } from \"../shape/index\";\nimport type { TaintLabel, TaintSet } from \"./taint\";\n\n/** What a tool returns after running. */\nexport interface ToolOutput {\n content: string | OracleContentBlock[];\n isError?: boolean;\n}\n\n/** Context handed to a tool handler when it runs. */\nexport interface ToolContext {\n /** The taint currently on the conversation feeding this call. */\n taint: TaintSet;\n /** Optional cancellation signal, threaded from the run. */\n signal?: AbortSignal;\n}\n\nexport type ToolHandler = (\n input: Record<string, unknown>,\n ctx: ToolContext,\n) => Promise<ToolOutput> | ToolOutput;\n\nexport interface ToolDef {\n name: string;\n description: string;\n /** JSON Schema for the tool arguments. */\n inputSchema: Record<string, unknown>;\n /** Local executor. Omit for provider server tools resolved inside the adapter. */\n handler?: ToolHandler;\n /** Taint labels this tool's output introduces into the conversation. */\n outputTaint?: TaintLabel[];\n /** If set, the tool only runs when the conversation taint ⊆ this allowlist. */\n acceptsTaint?: TaintLabel[];\n}\n\n/** Project a ToolDef to the wire-level OracleTool the model sees. */\nexport function toOracleTool(t: ToolDef): OracleTool {\n return { name: t.name, description: t.description, inputSchema: t.inputSchema };\n}\n","// A Skill is a named, reusable bundle of tools plus the instructions that tell a\n// persona how and when to use them. Attaching a skill to a persona is how you\n// \"add a skill and call it as tools\": the skill's tools are merged into the\n// model's tool list, and its instructions are appended to the system prompt.\n// A skill whose tool handlers write to odla-db is how an agent changes data.\n\nimport type { ToolDef } from \"./tools\";\n\nexport interface Skill {\n /** Skill name — used as the heading for its instructions in the system prompt. */\n name: string;\n /** Guidance appended to the persona's system prompt (when/how to use the tools). */\n instructions?: string;\n /** The tools this skill contributes to the agent. */\n tools: ToolDef[];\n}\n\nexport interface ComposedPersona {\n system: string | undefined;\n tools: ToolDef[];\n}\n\n/** Merge a persona's own tools/system with its attached skills: skill tools are\n * appended to the tool list, and each skill's instructions become a titled\n * section of the system prompt. Order is preserved (persona first, then skills). */\nexport function composeSkills(\n system: string | undefined,\n tools: ToolDef[] | undefined,\n skills: Skill[] | undefined,\n): ComposedPersona {\n const composedTools: ToolDef[] = [...(tools ?? [])];\n const sections: string[] = system ? [system] : [];\n for (const skill of skills ?? []) {\n composedTools.push(...skill.tools);\n if (skill.instructions) sections.push(`## ${skill.name}\\n${skill.instructions}`);\n }\n return { system: sections.length > 0 ? sections.join(\"\\n\\n\") : undefined, tools: composedTools };\n}\n","// A lightweight CaMeL-style taint layer (after arXiv:2503.18813, the model used\n// by odla-ai-old's chat-engine). Values that entered the conversation from an\n// untrusted source carry a label; a privileged tool (\"sink\") declares which\n// taints it will accept, and the agent loop refuses to run it when the current\n// conversation taint is not a subset of that allowlist. This is the seed of a\n// prompt-injection defense — deliberately minimal in v1 (label + assert), not a\n// full information-flow tracker.\n\nimport { OdlaAIError } from \"../shape/index\";\n\nexport type TaintLabel =\n | \"web_untrusted\"\n | \"operator_pasted_untrusted\"\n | \"llm_inherited\"\n | `tool_untrusted:${string}`;\n\nexport type TaintSet = Set<TaintLabel>;\n\nexport function taint(...labels: TaintLabel[]): TaintSet {\n return new Set(labels);\n}\n\n/** Raised when a tool's taint allowlist doesn't cover the conversation's taint. */\nexport class TaintError extends OdlaAIError {\n constructor(message: string) {\n super(\"invalid_request\", message);\n }\n}\n\n/**\n * Throw unless every label currently on the conversation is in the sink's\n * allowlist. `llm_inherited` is intentionally excludable: a tool that lists it as\n * *not* accepted is one where model-derived instructions must never reach the\n * sink (the classic injected-instruction propagation case).\n */\nexport function assertSinkAcceptsTaint(sink: string, allowed: TaintLabel[], actual: TaintSet): void {\n const allow = new Set(allowed);\n for (const label of actual) {\n if (!allow.has(label)) {\n throw new TaintError(\n `Tool \"${sink}\" refused: conversation carries taint \"${label}\" which is not in its allowlist [${allowed.join(\", \")}].`,\n );\n }\n }\n}\n","// The tool-use agent loop — the \"running\" half of the engine. Given a Persona\n// and an input, it drives model turn → tool execution → model turn until the\n// model stops asking for tools (or a step/refusal limit is hit), and returns the\n// full conversation, an accumulated usage tally, and a trace of tool calls.\n//\n// Provider-agnostic: it speaks only the canonical shape via the Inference facade,\n// so the same persona runs on Claude, GPT, or Gemini unchanged.\n\nimport {\n addUsage,\n emptyUsage,\n extractText,\n extractToolUses,\n type OracleContentBlock,\n type OracleMessage,\n type OracleResponse,\n type OracleUsage,\n type ToolResultBlock,\n type ToolUseBlock,\n} from \"../shape/index\";\nimport type { Inference } from \"../client/init\";\nimport type { Persona } from \"./persona\";\nimport type { ToolDef, ToolOutput } from \"./tools\";\nimport { toOracleTool } from \"./tools\";\nimport { composeSkills } from \"./skill\";\nimport { assertSinkAcceptsTaint, type TaintLabel, type TaintSet } from \"./taint\";\n\n/** One resolved tool call within a run. */\nexport interface ToolCallRecord {\n toolUse: ToolUseBlock;\n output: ToolOutput;\n}\n\nexport interface AgentRunInput {\n /** A single user turn (convenience). */\n input?: string | OracleContentBlock[];\n /** Or an explicit list of messages (takes precedence when both are given). */\n messages?: OracleMessage[];\n signal?: AbortSignal;\n}\n\nexport type StoppedReason = \"end_turn\" | \"max_steps\" | \"refusal\";\n\nexport interface AgentRun {\n /** Concatenated text of the final assistant turn. */\n finalText: string;\n /** The last assistant response. */\n response: OracleResponse;\n /** The full conversation (loaded memory + input + assistant/tool turns). */\n messages: OracleMessage[];\n /** Accumulated usage across every model turn. */\n usage: OracleUsage;\n /** Number of model turns taken. */\n steps: number;\n /** Every tool call executed, in order. */\n toolCalls: ToolCallRecord[];\n stoppedReason: StoppedReason;\n}\n\nexport async function runAgent(inference: Inference, persona: Persona, input: AgentRunInput): Promise<AgentRun> {\n // Fold attached skills into the tool list + system prompt.\n const { system, tools } = composeSkills(persona.system, persona.tools, persona.skills);\n const handlerByName = new Map<string, ToolDef>();\n for (const t of tools) if (t.handler) handlerByName.set(t.name, t);\n\n // Assemble the starting conversation: memory, then the caller's input.\n const priorFromMemory = persona.memory ? await persona.memory.load() : [];\n const startingInput: OracleMessage[] = input.messages\n ? input.messages\n : input.input !== undefined\n ? [{ role: \"user\", content: input.input }]\n : [];\n const messages: OracleMessage[] = [...priorFromMemory, ...startingInput];\n const runStartIndex = messages.length - startingInput.length; // memory boundary for append()\n\n const usage = emptyUsage();\n const toolCalls: ToolCallRecord[] = [];\n const accumulatedTaint: TaintSet = new Set<TaintLabel>([\"llm_inherited\"]);\n const maxSteps = persona.maxSteps ?? 8;\n\n let response: OracleResponse | undefined;\n let stoppedReason: StoppedReason = \"max_steps\";\n let steps = 0;\n\n while (steps < maxSteps) {\n steps++;\n response = await inference.chat({\n model: persona.model,\n system,\n messages,\n tools: tools.length > 0 ? tools.map(toOracleTool) : undefined,\n maxTokens: persona.maxTokens ?? 4096,\n effort: persona.effort,\n thinking: persona.thinking,\n temperature: persona.temperature,\n webSearch: persona.webSearch,\n });\n addUsage(usage, response.usage);\n messages.push({ role: \"assistant\", content: response.content });\n\n if (response.stopReason === \"refusal\") {\n stoppedReason = \"refusal\";\n break;\n }\n\n const toolUses = extractToolUses(response.content);\n if (toolUses.length === 0) {\n stoppedReason = \"end_turn\";\n break;\n }\n\n const resultBlocks: ToolResultBlock[] = [];\n for (const toolUse of toolUses) {\n const def = handlerByName.get(toolUse.name);\n if (!def || !def.handler) {\n resultBlocks.push(errorResult(toolUse.id, `No local handler for tool \"${toolUse.name}\".`));\n continue;\n }\n if (def.acceptsTaint) assertSinkAcceptsTaint(def.name, def.acceptsTaint, accumulatedTaint);\n\n let output: ToolOutput;\n try {\n output = await def.handler(toolUse.input, { taint: new Set(accumulatedTaint), signal: input.signal });\n } catch (err) {\n output = { content: `Tool \"${toolUse.name}\" threw: ${(err as Error).message}`, isError: true };\n }\n toolCalls.push({ toolUse, output });\n resultBlocks.push({ type: \"tool_result\", toolUseId: toolUse.id, content: output.content, isError: output.isError });\n for (const label of def.outputTaint ?? []) accumulatedTaint.add(label);\n }\n messages.push({ role: \"user\", content: resultBlocks });\n }\n\n if (!response) throw new Error(\"runAgent: maxSteps must be at least 1\");\n\n if (persona.memory) await persona.memory.append(messages.slice(runStartIndex));\n\n return {\n finalText: extractText(response.content),\n response,\n messages,\n usage,\n steps,\n toolCalls,\n stoppedReason,\n };\n}\n\nfunction errorResult(toolUseId: string, message: string): ToolResultBlock {\n return { type: \"tool_result\", toolUseId, content: message, isError: true };\n}\n","// Pluggable persona memory. A MemoryScope supplies prior conversation context\n// before a run and receives the new turns after it. The default is in-process;\n// an app that wants durable memory (e.g. persisted to odla-db) implements this\n// interface — deliberately kept out of the core so the library has no infra deps.\n\nimport type { OracleMessage } from \"../shape/index\";\n\nexport interface MemoryScope {\n /** Messages to prepend before this run's input. */\n load(): Promise<OracleMessage[]> | OracleMessage[];\n /** Persist the messages produced during this run (excludes what load() returned). */\n append(messages: OracleMessage[]): Promise<void> | void;\n}\n\n/** A simple process-lifetime memory scope. */\nexport class InMemoryScope implements MemoryScope {\n private messages: OracleMessage[] = [];\n\n load(): OracleMessage[] {\n return [...this.messages];\n }\n\n append(messages: OracleMessage[]): void {\n this.messages.push(...messages);\n }\n\n clear(): void {\n this.messages = [];\n }\n}\n","// The eval harness: run a set of cases through either a Persona (via the agent\n// loop) or a plain model chat, grade each with a Grader, and return a report.\n// A thin, honest harness — run → grade → report — that is the seed of a real\n// conformance/eval suite, not a full framework.\n\nimport {\n addUsage,\n emptyUsage,\n extractText,\n type OracleContentBlock,\n type OracleMessage,\n type OracleResponse,\n type OracleUsage,\n} from \"../shape/index\";\nimport type { Inference } from \"../client/init\";\nimport { runAgent } from \"../agent/loop\";\nimport type { Persona } from \"../agent/persona\";\nimport type { EvalCase, GradeResult, Grader } from \"./types\";\n\nexport interface EvalResult {\n case: EvalCase;\n output: string;\n grade: GradeResult;\n response: OracleResponse;\n usage: OracleUsage;\n}\n\nexport interface EvalReport {\n total: number;\n passed: number;\n failed: number;\n passRate: number;\n results: EvalResult[];\n usage: OracleUsage;\n}\n\nexport interface EvaluateOptions {\n inference: Inference;\n cases: EvalCase[];\n grader: Grader;\n /** Run each case through this persona (agent loop). Mutually exclusive with model. */\n persona?: Persona;\n /** Or run each case as a single chat with this model + system. */\n model?: string;\n system?: string;\n maxTokens?: number;\n /** Max cases run concurrently (default 5). */\n concurrency?: number;\n}\n\nexport async function evaluate(opts: EvaluateOptions): Promise<EvalReport> {\n if (!opts.persona && !opts.model) {\n throw new Error(\"evaluate() requires either a persona or a model.\");\n }\n const results = await runPool(opts.cases, opts.concurrency ?? 5, (c) => runCase(opts, c));\n\n const usage = emptyUsage();\n let passed = 0;\n for (const r of results) {\n addUsage(usage, r.usage);\n if (r.grade.pass) passed++;\n }\n const total = results.length;\n return { total, passed, failed: total - passed, passRate: total ? passed / total : 0, results, usage };\n}\n\nasync function runCase(opts: EvaluateOptions, c: EvalCase): Promise<EvalResult> {\n const messages = toMessages(c.input);\n\n let output: string;\n let response: OracleResponse;\n let usage: OracleUsage;\n\n if (opts.persona) {\n const run = await runAgent(opts.inference, opts.persona, { messages });\n output = run.finalText;\n response = run.response;\n usage = run.usage;\n } else {\n response = await opts.inference.chat({\n model: opts.model!,\n system: opts.system,\n messages,\n maxTokens: opts.maxTokens ?? 1024,\n });\n output = extractText(response.content);\n usage = response.usage;\n }\n\n const grade = await opts.grader({ case: c, output, response });\n return { case: c, output, grade, response, usage };\n}\n\nfunction toMessages(input: EvalCase[\"input\"]): OracleMessage[] {\n if (Array.isArray(input) && input.length > 0 && \"role\" in (input[0] as object)) {\n return input as OracleMessage[];\n }\n return [{ role: \"user\", content: input as string | OracleContentBlock[] }];\n}\n\n/** Run `fn` over `items` with at most `limit` in flight, preserving input order. */\nasync function runPool<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {\n for (;;) {\n const i = next++;\n if (i >= items.length) return;\n results[i] = await fn(items[i]!);\n }\n });\n await Promise.all(workers);\n return results;\n}\n","// Built-in graders. `exactMatch` / `includes` / `structuredMatch` are pure and\n// offline; `llmJudge` dogfoods the inference core (an Inference facade) to grade\n// open-ended output against `case.expected` treated as a rubric.\n\nimport { extractText } from \"../shape/index\";\nimport type { Inference } from \"../client/init\";\nimport type { Grader } from \"./types\";\n\n/** Pass when trimmed output equals `case.expected` (stringified). */\nexport function exactMatch(): Grader {\n return ({ case: c, output }) => {\n const expected = String(c.expected ?? \"\").trim();\n const pass = output.trim() === expected;\n return { pass, reason: pass ? \"exact match\" : `expected \"${expected}\", got \"${output.trim()}\"` };\n };\n}\n\n/** Pass when output contains `case.expected` (case-insensitive by default). */\nexport function includes(opts: { caseInsensitive?: boolean } = {}): Grader {\n const ci = opts.caseInsensitive ?? true;\n return ({ case: c, output }) => {\n const needle = String(c.expected ?? \"\");\n const hay = ci ? output.toLowerCase() : output;\n const pass = hay.includes(ci ? needle.toLowerCase() : needle);\n return { pass, reason: pass ? `contains \"${needle}\"` : `missing \"${needle}\"` };\n };\n}\n\n/** Pass when output parses as JSON and contains every key/value in\n * `case.expected` (a recursive subset match). */\nexport function structuredMatch(): Grader {\n return ({ case: c, output }) => {\n let actual: unknown;\n try {\n actual = JSON.parse(stripFences(output));\n } catch {\n return { pass: false, reason: \"output is not valid JSON\" };\n }\n const ok = subset(c.expected, actual);\n return { pass: ok, reason: ok ? \"structural subset match\" : \"output is missing expected fields/values\" };\n };\n}\n\n/** Grade open-ended output with a judge model. `case.expected` is the rubric.\n * The judge is asked to return a strict JSON verdict which is tolerantly parsed. */\nexport function llmJudge(opts: { inference: Inference; model: string; extraGuidance?: string }): Grader {\n return async ({ case: c, output }) => {\n const rubric = typeof c.expected === \"string\" ? c.expected : JSON.stringify(c.expected);\n const system =\n \"You are a strict evaluation judge. Decide whether the CANDIDATE output satisfies the RUBRIC. \" +\n \"Respond with ONLY a JSON object: {\\\"pass\\\": boolean, \\\"reason\\\": string}. No prose, no code fences.\" +\n (opts.extraGuidance ? ` ${opts.extraGuidance}` : \"\");\n const user = `RUBRIC:\\n${rubric}\\n\\nCANDIDATE:\\n${output}`;\n const res = await opts.inference.chat({\n model: opts.model,\n system,\n messages: [{ role: \"user\", content: user }],\n maxTokens: 512,\n });\n const text = extractText(res.content);\n try {\n const verdict = JSON.parse(stripFences(text)) as { pass?: unknown; reason?: unknown };\n return { pass: Boolean(verdict.pass), reason: typeof verdict.reason === \"string\" ? verdict.reason : undefined };\n } catch {\n // Fall back to a lenient keyword read if the judge didn't return clean JSON.\n const pass = /\\bpass(ed)?\\b|\\btrue\\b|\\byes\\b/i.test(text) && !/\\bfail(ed)?\\b|\\bfalse\\b|\\bno\\b/i.test(text);\n return { pass, reason: `unparseable judge output: ${text.slice(0, 200)}` };\n }\n };\n}\n\nfunction stripFences(s: string): string {\n return s.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\").trim();\n}\n\n/** Recursive \"is `expected` a subset of `actual`\" check. */\nfunction subset(expected: unknown, actual: unknown): boolean {\n if (expected === null || typeof expected !== \"object\") return expected === actual;\n if (Array.isArray(expected)) {\n if (!Array.isArray(actual) || actual.length < expected.length) return false;\n return expected.every((e, i) => subset(e, actual[i]));\n }\n if (actual === null || typeof actual !== \"object\" || Array.isArray(actual)) return false;\n const a = actual as Record<string, unknown>;\n return Object.entries(expected as Record<string, unknown>).every(([k, v]) => subset(v, a[k]));\n}\n","// The odla-db schema odla-ai persists into: three flat, natural-key-keyed\n// entities for conversation memory and agent-run traces. odla-db is\n// schema-optional (attrs are auto-created on write), but pushing this schema\n// makes the `key` attr `unique` so `Lookup` upserts bind to exactly one row.\n//\n// AGENT_SCHEMA is the already-serialized wire shape (generated with @odla-ai/db's\n// `i.schema(...).serialize()`), embedded so provisioning can push it without the\n// consumer needing the schema builder.\n\n/** Entity namespaces odla-ai owns in odla-db (all non-`$`, so no platform clash). */\nexport const NS = {\n conversation: \"agent_conversation\",\n message: \"agent_message\",\n run: \"agent_run\",\n} as const;\n\n/** Serialized odla-db schema (POST to `/app/:id/schema` as `{ schema: AGENT_SCHEMA }`). */\nexport const AGENT_SCHEMA = {\n entities: {\n agent_conversation: {\n attrs: {\n key: { type: \"string\", unique: true, indexed: true, optional: false },\n sessionId: { type: \"string\", unique: false, indexed: true, optional: false },\n title: { type: \"string\", unique: false, indexed: false, optional: true },\n createdAt: { type: \"date\", unique: false, indexed: true, optional: false },\n updatedAt: { type: \"date\", unique: false, indexed: true, optional: false },\n },\n },\n agent_message: {\n attrs: {\n key: { type: \"string\", unique: true, indexed: true, optional: false },\n sessionId: { type: \"string\", unique: false, indexed: true, optional: false },\n seq: { type: \"number\", unique: false, indexed: true, optional: false },\n role: { type: \"string\", unique: false, indexed: false, optional: false },\n content: { type: \"string\", unique: false, indexed: false, optional: false },\n createdAt: { type: \"date\", unique: false, indexed: true, optional: false },\n },\n },\n agent_run: {\n attrs: {\n key: { type: \"string\", unique: true, indexed: true, optional: false },\n sessionId: { type: \"string\", unique: false, indexed: true, optional: false },\n persona: { type: \"string\", unique: false, indexed: false, optional: true },\n status: { type: \"string\", unique: false, indexed: true, optional: false },\n steps: { type: \"number\", unique: false, indexed: false, optional: false },\n inputTokens: { type: \"number\", unique: false, indexed: false, optional: false },\n outputTokens: { type: \"number\", unique: false, indexed: false, optional: false },\n finalText: { type: \"string\", unique: false, indexed: false, optional: true },\n trace: { type: \"json\", unique: false, indexed: false, optional: false },\n createdAt: { type: \"date\", unique: false, indexed: true, optional: false },\n },\n },\n },\n links: {},\n} as const;\n","// An odla-db-backed MemoryScope: persona conversation memory persisted as\n// odla-db records. load() replays prior turns for a session; append() upserts\n// the run's new turns by natural key with a deterministic mutationId (so a\n// retried run doesn't duplicate). Injected with an odla-db client — odla-ai keeps\n// zero runtime dependency on @odla-ai/db.\n\nimport type { OracleMessage, OracleRole } from \"../shape/index\";\nimport type { MemoryScope } from \"../agent/memory\";\nimport type { OdlaDbClient, OdlaOp } from \"./types\";\nimport { NS } from \"./schema\";\n\nexport interface OdlaDbMemoryOptions {\n db: OdlaDbClient;\n /** Groups a conversation's turns; stable across runs to accumulate history. */\n sessionId: string;\n /** Optional human-readable title stored on the conversation node. */\n title?: string;\n /** Override entity namespaces (default NS.message / NS.conversation). */\n namespaces?: { message?: string; conversation?: string };\n}\n\nexport class OdlaDbMemory implements MemoryScope {\n private readonly db: OdlaDbClient;\n private readonly sessionId: string;\n private readonly title: string | undefined;\n private readonly msgNs: string;\n private readonly convNs: string;\n private loadedCount = 0;\n\n constructor(opts: OdlaDbMemoryOptions) {\n this.db = opts.db;\n this.sessionId = opts.sessionId;\n this.title = opts.title;\n this.msgNs = opts.namespaces?.message ?? NS.message;\n this.convNs = opts.namespaces?.conversation ?? NS.conversation;\n }\n\n async load(): Promise<OracleMessage[]> {\n const res = await this.db.query({\n [this.msgNs]: { $: { where: { sessionId: this.sessionId }, order: { seq: \"asc\" }, limit: 100_000 } },\n });\n const rows = (res[this.msgNs] ?? []) as { seq?: number; role?: string; content?: string }[];\n this.loadedCount = rows.length;\n return rows.map((r) => ({ role: (r.role ?? \"user\") as OracleRole, content: parseContent(r.content) }));\n }\n\n async append(messages: OracleMessage[]): Promise<void> {\n if (messages.length === 0) return;\n const base = this.loadedCount;\n const now = Date.now();\n\n const ops: OdlaOp[] = [\n {\n t: \"update\",\n ns: this.convNs,\n id: { ns: this.convNs, attr: \"key\", value: this.sessionId },\n attrs: {\n key: this.sessionId,\n sessionId: this.sessionId,\n ...(this.title ? { title: this.title } : {}),\n ...(base === 0 ? { createdAt: now } : {}),\n updatedAt: now,\n },\n },\n ];\n\n messages.forEach((m, i) => {\n const seq = base + i;\n const key = `${this.sessionId}:${seq}`;\n ops.push({\n t: \"update\",\n ns: this.msgNs,\n id: { ns: this.msgNs, attr: \"key\", value: key },\n attrs: { key, sessionId: this.sessionId, seq, role: m.role, content: JSON.stringify(m.content), createdAt: now },\n });\n });\n\n await this.db.transact(ops, {\n mutationId: `${this.sessionId}:append:${base}:${base + messages.length}`,\n });\n this.loadedCount += messages.length;\n }\n}\n\n/** Message content was stored as JSON (string | OracleContentBlock[]); restore it. */\nfunction parseContent(raw: string | undefined): OracleMessage[\"content\"] {\n if (raw === undefined) return \"\";\n try {\n return JSON.parse(raw) as OracleMessage[\"content\"];\n } catch {\n return raw; // tolerate a plain string that wasn't JSON-encoded\n }\n}\n","// Persist agent-run traces to odla-db for observability. Each run becomes one\n// `agent_run` record (status, steps, usage, final text, and a JSON trace of tool\n// calls + full message log), upserted by a natural key so a retried write is\n// idempotent.\n\nimport type { AgentRun } from \"../agent/loop\";\nimport type { OdlaDbClient } from \"./types\";\nimport { NS } from \"./schema\";\n\nexport interface PersistRunOptions {\n db: OdlaDbClient;\n /** Groups runs (typically the same session as the memory scope). */\n sessionId: string;\n personaName?: string;\n /** Stable id for exactly-once persistence; defaults to `${sessionId}:${now}`. */\n runId?: string;\n namespace?: string;\n}\n\n/** Write a run trace; returns the record key. */\nexport async function persistRun(run: AgentRun, opts: PersistRunOptions): Promise<string> {\n const ns = opts.namespace ?? NS.run;\n const key = opts.runId ?? `${opts.sessionId}:${Date.now()}`;\n await opts.db.transact(\n [\n {\n t: \"update\",\n ns,\n id: { ns, attr: \"key\", value: key },\n attrs: {\n key,\n sessionId: opts.sessionId,\n ...(opts.personaName ? { persona: opts.personaName } : {}),\n status: run.stoppedReason,\n steps: run.steps,\n inputTokens: run.usage.inputTokens,\n outputTokens: run.usage.outputTokens,\n finalText: run.finalText,\n trace: {\n toolCalls: run.toolCalls.map((t) => ({ name: t.toolUse.name, input: t.toolUse.input, output: t.output })),\n messages: run.messages,\n },\n createdAt: Date.now(),\n },\n },\n ],\n { mutationId: `run:${key}` },\n );\n return key;\n}\n\nexport interface QueryRunsOptions {\n db: OdlaDbClient;\n sessionId: string;\n limit?: number;\n namespace?: string;\n}\n\n/** Read persisted runs for a session, newest first. */\nexport async function queryRuns(opts: QueryRunsOptions): Promise<Record<string, unknown>[]> {\n const ns = opts.namespace ?? NS.run;\n const res = await opts.db.query({\n [ns]: { $: { where: { sessionId: opts.sessionId }, order: { createdAt: \"desc\" }, limit: opts.limit ?? 50 } },\n });\n return res[ns] ?? [];\n}\n","// An odla-db-secrets-backed key resolver. BYO provider keys are stored as odla-db\n// secrets (AES-GCM-encrypted at rest, never returned to browser clients) and read\n// at request time with the app key via the read-only `secrets.get`. Wire it in\n// with `init({ resolveKey: odlaDbKeyResolver(db) })`.\n//\n// Storing the keys is a separate, privileged step (an operator/dev token, not the\n// app key) — see `provisionAgentApp` / `putSecret` in ./provision.\n\nimport type { ProviderId } from \"../shape/index\";\nimport type { KeyResolver } from \"../client/keys\";\nimport type { OdlaDbClient } from \"./types\";\n\n/** Default secret name per provider. */\nexport const DEFAULT_SECRET_NAMES: Record<ProviderId, string> = {\n anthropic: \"anthropic_api_key\",\n openai: \"openai_api_key\",\n google: \"google_api_key\",\n};\n\nexport interface OdlaDbKeyResolverOptions {\n /** Map a provider to its secret name (default `<provider>_api_key`). Use this\n * to scope per-end-user keys, e.g. `(p) => \\`${p}_api_key__${userSub}\\``. */\n secretName?: (provider: ProviderId) => string;\n /** Cache resolved keys in memory (default true). */\n cache?: boolean;\n}\n\n/** Build a KeyResolver that reads provider keys from odla-db secrets. A missing\n * secret resolves to `undefined` (→ that provider is simply unavailable, and\n * odla-ai throws its own ConfigError) rather than surfacing odla-db's 404. */\nexport function odlaDbKeyResolver(db: OdlaDbClient, opts: OdlaDbKeyResolverOptions = {}): KeyResolver {\n const nameFor = opts.secretName ?? ((p: ProviderId) => DEFAULT_SECRET_NAMES[p]);\n const useCache = opts.cache ?? true;\n const cache = new Map<ProviderId, string>();\n\n return async (provider: ProviderId): Promise<string | undefined> => {\n if (useCache) {\n const hit = cache.get(provider);\n if (hit !== undefined) return hit;\n }\n try {\n const key = await db.secrets.get(nameFor(provider));\n if (useCache) cache.set(provider, key);\n return key;\n } catch {\n return undefined; // absent secret → provider unavailable\n }\n };\n}\n","// Platform wiring: one call turns an odla.ai app registration into a ready\n// `Ai`. The NON-SECRET half ({provider, model?}) comes from the registry's\n// anonymous public-config endpoint — so switching provider/model in Studio\n// needs no redeploy; the SECRET half (the provider API key) never travels\n// through the registry at all: it is read from the app's own odla-db tenant\n// secrets at call time via `odlaDbKeyResolver`. The only credential the\n// caller holds is its odla-db app key (inside `db`).\n\nimport { ConfigError, ProviderError, type ProviderId } from \"../shape/index\";\nimport { init, type Ai, type InitOptions } from \"../client/init\";\nimport { odlaDbKeyResolver, DEFAULT_SECRET_NAMES } from \"./keys\";\nimport type { OdlaDbClient } from \"./types\";\n\nexport interface InitFromPlatformOptions {\n /** Platform base URL (e.g. \"https://odla.ai\"). */\n platform: string;\n /** The REGISTRY app id — not the env's tenant id. */\n appId: string;\n /** Which environment's config to use (\"dev\", \"prod\", …). */\n env: string;\n /** The app's own tenant client — key reads go through `db.secrets.get`. */\n db: OdlaDbClient;\n /** Transport override (tests / non-global fetch). */\n fetch?: typeof fetch;\n /** public-config cache TTL in ms (default 60s; 0 disables caching). */\n ttlMs?: number;\n /** Extra init() options (catalog, injected providers, …). `resolveKey` is\n * owned by this helper; `defaultModel` is used only when the platform\n * config sets no model. */\n initOptions?: Omit<InitOptions, \"resolveKey\">;\n}\n\nexport interface PlatformAi {\n ai: Ai;\n provider: ProviderId;\n model?: string;\n}\n\ninterface CacheEntry {\n value: PlatformAi;\n expiresAt: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/** Drop all cached platform configs (tests, or to force an immediate refetch). */\nexport function clearPlatformAiCache(): void {\n cache.clear();\n}\n\ninterface PublicConfig {\n ai?: { provider?: unknown; model?: unknown } | null;\n}\n\nasync function fetchAiConfig(opts: InitFromPlatformOptions): Promise<{ provider: ProviderId; model?: string }> {\n const doFetch = opts.fetch ?? fetch;\n const base = opts.platform.replace(/\\/$/, \"\");\n const url = `${base}/registry/apps/${encodeURIComponent(opts.appId)}/public-config?env=${encodeURIComponent(opts.env)}`;\n const res = await doFetch(url);\n if (!res.ok) throw new ProviderError(`platform public-config fetch failed: ${res.status} for ${opts.appId}/${opts.env}`);\n const cfg = (await res.json()) as PublicConfig;\n const ai = cfg.ai;\n if (!ai || typeof ai.provider !== \"string\") {\n throw new ConfigError(\n `ai service is not enabled/configured for \"${opts.appId}\" (${opts.env}) — ` +\n `enable it in Studio or via @odla-ai/apps setAi(appId, env, { provider }).`,\n );\n }\n if (!(ai.provider in DEFAULT_SECRET_NAMES)) {\n throw new ConfigError(`platform ai config names unknown provider \"${ai.provider}\" for \"${opts.appId}\" (${opts.env}).`);\n }\n const model = typeof ai.model === \"string\" && ai.model ? ai.model : undefined;\n return { provider: ai.provider as ProviderId, ...(model ? { model } : {}) };\n}\n\n/** Build an `Ai` from the app's platform registration: provider/model from\n * public-config (cached ~60s), API key from the tenant's odla-db secrets at\n * call time. On cache refresh the existing `Ai` (and its warm key-resolver\n * cache) is reused unless provider/model changed. Note the first call's\n * `db`/`initOptions` are captured for the cache entry's lifetime. */\nexport async function initFromPlatform(opts: InitFromPlatformOptions): Promise<PlatformAi> {\n const ttl = opts.ttlMs ?? 60_000;\n const key = `${opts.platform}|${opts.appId}|${opts.env}`;\n const now = Date.now();\n const hit = ttl > 0 ? cache.get(key) : undefined;\n if (hit && hit.expiresAt > now) return hit.value;\n\n const { provider, model } = await fetchAiConfig(opts);\n\n // Same config as the (expired) entry → keep the Ai and its warm key cache.\n if (hit && hit.value.provider === provider && hit.value.model === model) {\n hit.expiresAt = now + ttl;\n return hit.value;\n }\n\n const ai = init({\n ...opts.initOptions,\n resolveKey: odlaDbKeyResolver(opts.db),\n defaultModel: model ?? opts.initOptions?.defaultModel,\n });\n const value: PlatformAi = { ai, provider, ...(model ? { model } : {}) };\n if (ttl > 0) cache.set(key, { value, expiresAt: now + ttl });\n return value;\n}\n","// Provisioning helpers — the \"stand up a new agent app\" flow, mirroring odla-db's\n// handshake model. These are plain `fetch` calls (no @odla-ai/db dependency)\n// against a human-supplied odla-db endpoint, authenticated with an **operator or\n// developer token** (the platform `ADMIN_SECRET`, or an `odla_dev_...` token from\n// `@odla-ai/db`'s `requestToken(...)` device handshake) — NOT an app key.\n//\n// The typical agent flow:\n// 1. `const { token } = await requestToken({ endpoint, onCode })` // from @odla-ai/db\n// 2. `const { appId, appKey } = await provisionAgentApp({ endpoint, token, providerKeys })`\n// 3. `const db = init({ appId, adminToken: appKey, endpoint })` // @odla-ai/db client\n// 4. `const ai = init({ resolveKey: odlaDbKeyResolver(db) })` // keys from secrets\n// `persona.memory = new OdlaDbMemory({ db, sessionId })`\n//\n// Storing a secret needs the operator/dev token; reading it back at request time\n// needs only the app key (via odlaDbKeyResolver).\n\nimport { AuthError, ConfigError, ProviderError, type ProviderId } from \"../shape/index\";\nimport { redact } from \"../client/keys\";\nimport { AGENT_SCHEMA } from \"./schema\";\nimport { DEFAULT_SECRET_NAMES } from \"./keys\";\n\ntype FetchLike = typeof fetch;\n\nexport interface ProvisionContext {\n /** The odla-db worker URL — always human-supplied, never hardcoded. */\n endpoint: string;\n /** Operator `ADMIN_SECRET` or an `odla_dev_...` developer token (owns the app). */\n token: string;\n fetch?: FetchLike;\n}\n\nasync function post(ctx: ProvisionContext, path: string, body: unknown): Promise<Record<string, unknown>> {\n const f = ctx.fetch ?? fetch;\n const res = await f(`${ctx.endpoint.replace(/\\/$/, \"\")}${path}`, {\n method: \"POST\",\n headers: { authorization: `Bearer ${ctx.token}`, \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw errorFor(res.status, await safeText(res), path);\n return (await res.json().catch(() => ({}))) as Record<string, unknown>;\n}\n\nasync function postWithKey(ctx: { endpoint: string; fetch?: FetchLike }, path: string, appKey: string, body: unknown): Promise<Record<string, unknown>> {\n const f = ctx.fetch ?? fetch;\n const res = await f(`${ctx.endpoint.replace(/\\/$/, \"\")}${path}`, {\n method: \"POST\",\n headers: { authorization: `Bearer ${appKey}`, \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw errorFor(res.status, await safeText(res), path);\n return (await res.json().catch(() => ({}))) as Record<string, unknown>;\n}\n\n/** Create an app (owned by the token's developer). Returns its id. */\nexport async function createApp(ctx: ProvisionContext, opts: { appId?: string; name?: string } = {}): Promise<string> {\n const body: Record<string, unknown> = {};\n if (opts.appId) body.appId = opts.appId;\n if (opts.name) body.name = opts.name;\n const json = await post(ctx, \"/admin/apps\", body);\n const appId = (json.appId ?? json.id ?? (json.app as { appId?: string } | undefined)?.appId ?? opts.appId) as string | undefined;\n if (!appId) throw new ProviderError(\"[odla-db] createApp: response did not include an appId\");\n return appId;\n}\n\n/** Mint a per-app `odla_sk_...` key (shown once). */\nexport async function mintAppKey(ctx: ProvisionContext, appId: string): Promise<string> {\n const json = await post(ctx, `/admin/apps/${encodeURIComponent(appId)}/keys`, {});\n const key = json.key as string | undefined;\n if (!key) throw new ProviderError(\"[odla-db] mintAppKey: response did not include a key\");\n return key;\n}\n\n/** Push odla-ai's agent memory/run schema (authenticated with the app key). */\nexport async function pushAgentSchema(ctx: { endpoint: string; fetch?: FetchLike }, appId: string, appKey: string): Promise<void> {\n await postWithKey(ctx, `/app/${encodeURIComponent(appId)}/schema`, appKey, { schema: AGENT_SCHEMA });\n}\n\n/** Store a named secret (operator/dev token). Value must be a non-`$` string. */\nexport async function putSecret(ctx: ProvisionContext, appId: string, name: string, value: string): Promise<void> {\n await post(ctx, `/admin/apps/${encodeURIComponent(appId)}/secrets`, { name, value });\n}\n\n/** Per-namespace CEL permission rules (odla-db is DEFAULT-DENY: end-users can't\n * read or write any namespace until its rules are set; the app key bypasses\n * rules, so agent-only apps work without them). */\nexport type AppRules = Record<string, { view?: string; create?: string; update?: string; delete?: string }>;\n\n/** Install permission rules (operator/dev token). Replaces the app's rule set. */\nexport async function putRules(ctx: ProvisionContext, appId: string, rules: AppRules): Promise<void> {\n await post(ctx, `/app/${encodeURIComponent(appId)}/admin/rules`, rules);\n}\n\nexport interface ProvisionAgentAppOptions extends ProvisionContext {\n appId?: string;\n appName?: string;\n /** Provider keys to store as odla-db secrets (read later via odlaDbKeyResolver). */\n providerKeys?: Partial<Record<ProviderId, string>>;\n /** Map a provider to its secret name (default `<provider>_api_key`). */\n secretName?: (provider: ProviderId) => string;\n /** Push the agent schema (default true). */\n pushSchema?: boolean;\n /** Permission rules to install. odla-db is default-deny, so set these for any\n * namespace end-users should reach (e.g. own-rows-only:\n * `{ todos: { view: \"auth.id == data.ownerId\", create: \"auth.id == data.ownerId\" } }`).\n * Omit for agent-only apps — the app key bypasses rules. */\n rules?: AppRules;\n}\n\nexport interface ProvisionedApp {\n appId: string;\n /** The minted `odla_sk_...` app key — pass as `adminToken` to `@odla-ai/db`'s init. */\n appKey: string;\n}\n\n/** One call to stand up an agent app: create → mint key → push schema → store\n * provider keys as secrets. Returns the appId + app key for the runtime client. */\nexport async function provisionAgentApp(opts: ProvisionAgentAppOptions): Promise<ProvisionedApp> {\n const ctx: ProvisionContext = { endpoint: opts.endpoint, token: opts.token, fetch: opts.fetch };\n const appId = await createApp(ctx, { appId: opts.appId, name: opts.appName });\n const appKey = await mintAppKey(ctx, appId);\n\n if (opts.pushSchema !== false) {\n await pushAgentSchema({ endpoint: opts.endpoint, fetch: opts.fetch }, appId, appKey);\n }\n\n if (opts.rules) {\n await putRules(ctx, appId, opts.rules);\n }\n\n const nameFor = opts.secretName ?? ((p: ProviderId) => DEFAULT_SECRET_NAMES[p]);\n for (const [provider, value] of Object.entries(opts.providerKeys ?? {})) {\n if (!value) continue;\n await putSecret(ctx, appId, nameFor(provider as ProviderId), value);\n }\n\n return { appId, appKey };\n}\n\nfunction errorFor(status: number, text: string, path: string): Error {\n const msg = redact(`[odla-db] ${path} → ${status} ${text}`.trim());\n if (status === 401 || status === 403) return new AuthError(msg, { providerStatus: status });\n if (status === 501) return new ConfigError(`${msg} (secrets/feature not enabled on the odla-db worker)`);\n if (status === 400) return new ConfigError(msg);\n return new ProviderError(msg, { providerStatus: status });\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return (await res.text()).slice(0, 300);\n } catch {\n return \"\";\n }\n}\n","// Turn any odla-db entity into an agent Skill: list / create / update / delete\n// tools whose handlers call db.query / db.transact. This is the ergonomic hook\n// for \"add a skill and call it as tools that change data in odla-db\" — a todos\n// skill is a few lines (see the example at the bottom of this file's doc).\n//\n// Generic over the entity and its fields; the tool JSON-Schemas are built from\n// the `fields` you declare, so the model gets a precise contract.\n\nimport type { Skill } from \"../agent/skill\";\nimport type { ToolDef, ToolOutput } from \"../agent/tools\";\nimport type { OdlaDbClient, OdlaOp } from \"./types\";\n\nexport interface EntityCrudSkillOptions {\n /** The injected odla-db client (from @odla-ai/db's init). */\n db: OdlaDbClient;\n /** Entity namespace, e.g. \"todos\". */\n entity: string;\n /** JSON-Schema `properties` for the create/update fields (e.g. { text: { type: \"string\" }, done: { type: \"boolean\" } }). */\n fields: Record<string, unknown>;\n /** Field names required to create (default: none). */\n required?: string[];\n /** Skill name (default `${entity} management`). */\n name?: string;\n /** Extra instructions appended to the generated guidance. */\n instructions?: string;\n /** Singular noun for tool names (default: `entity` with a trailing \"s\" stripped). */\n singular?: string;\n /** Plural noun for the list tool (default: `entity`). */\n plural?: string;\n /** Input field carrying the entity id in update/delete (default \"id\"). */\n idField?: string;\n /** Generate a new id on create (default crypto.randomUUID). */\n genId?: () => string;\n /** Attr stamped with Date.now() on create; pass false to disable (default \"createdAt\"). */\n stampCreatedAt?: string | false;\n /** Default ordering for the list tool (default { createdAt: \"asc\" }). */\n order?: Record<string, \"asc\" | \"desc\">;\n}\n\n/** Build a CRUD Skill for one odla-db entity. */\nexport function entityCrudSkill(opts: EntityCrudSkillOptions): Skill {\n const { db, entity, fields } = opts;\n const singular = opts.singular ?? entity.replace(/s$/, \"\");\n const plural = opts.plural ?? entity;\n const idField = opts.idField ?? \"id\";\n const genId = opts.genId ?? (() => globalThis.crypto.randomUUID());\n const stampAttr = opts.stampCreatedAt === undefined ? \"createdAt\" : opts.stampCreatedAt;\n const order = opts.order ?? { createdAt: \"asc\" };\n const fieldNames = Object.keys(fields);\n\n const pickFields = (input: Record<string, unknown>): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const k of fieldNames) if (k in input && input[k] !== undefined) out[k] = input[k];\n return out;\n };\n const ok = (obj: Record<string, unknown>): ToolOutput => ({ content: JSON.stringify(obj) });\n\n // odla-db's `update` op is an UPSERT (creates the entity if the id is missing),\n // so update/delete must verify the id exists first — otherwise a model that\n // passes a wrong/hallucinated id silently spawns a phantom empty row instead of\n // failing. Membership is checked client-side (rows carry `id`), which works the\n // same on the real client and on test doubles.\n const existsById = async (id: string): Promise<boolean> => {\n const res = await db.query({ [entity]: { $: {} } });\n return (res[entity] ?? []).some((r) => String(r.id ?? \"\") === id);\n };\n\n const listTool: ToolDef = {\n name: `list_${plural}`,\n description: `List ${plural}. Optionally filter by exact field values and/or limit the count.`,\n inputSchema: {\n type: \"object\",\n properties: {\n filter: { type: \"object\", description: `Exact-match filters, e.g. { \"done\": false }`, additionalProperties: true },\n limit: { type: \"number\", description: \"Max rows to return.\" },\n },\n },\n handler: async (input) => {\n const filter = (input.filter as Record<string, unknown> | undefined) ?? undefined;\n const limit = typeof input.limit === \"number\" ? input.limit : undefined;\n const $: Record<string, unknown> = { order };\n if (filter && Object.keys(filter).length > 0) $.where = filter;\n if (limit !== undefined) $.limit = limit;\n const res = await db.query({ [entity]: { $ } });\n return ok({ [plural]: res[entity] ?? [] });\n },\n };\n\n const createTool: ToolDef = {\n name: `create_${singular}`,\n description: `Create a new ${singular}.`,\n inputSchema: { type: \"object\", properties: fields, ...(opts.required ? { required: opts.required } : {}) },\n handler: async (input) => {\n for (const req of opts.required ?? []) {\n const v = input[req];\n if (v === undefined || v === null || v === \"\") {\n return { content: `Cannot create ${singular}: field \"${req}\" is required.`, isError: true };\n }\n }\n const id = genId();\n const attrs: Record<string, unknown> = pickFields(input);\n if (stampAttr) attrs[stampAttr] = Date.now();\n await db.transact([{ t: \"update\", ns: entity, id, attrs }]);\n return ok({ id, created: true });\n },\n };\n\n const updateTool: ToolDef = {\n name: `update_${singular}`,\n description: `Update fields of an existing ${singular} by ${idField}. Only the fields you pass change.`,\n inputSchema: {\n type: \"object\",\n properties: {\n [idField]: { type: \"string\", description: `The exact ${idField} string from list_${plural} — NOT the item's position in a numbered list.` },\n ...fields,\n },\n required: [idField],\n },\n handler: async (input) => {\n const id = String(input[idField] ?? \"\");\n if (!id) return { content: `Missing \"${idField}\".`, isError: true };\n const attrs = pickFields(input);\n if (Object.keys(attrs).length === 0) return { content: \"No fields to update.\", isError: true };\n if (!(await existsById(id))) {\n return { content: `No ${singular} with ${idField} \"${id}\". Call list_${plural} for valid ids, then retry.`, isError: true };\n }\n await db.transact([{ t: \"update\", ns: entity, id, attrs }]);\n return ok({ id, updated: true });\n },\n };\n\n const deleteTool: ToolDef = {\n name: `delete_${singular}`,\n description: `Delete a ${singular} by ${idField}.`,\n inputSchema: {\n type: \"object\",\n properties: { [idField]: { type: \"string\", description: `The exact ${idField} string from list_${plural} — NOT the item's position in a numbered list.` } },\n required: [idField],\n },\n handler: async (input) => {\n const id = String(input[idField] ?? \"\");\n if (!id) return { content: `Missing \"${idField}\".`, isError: true };\n if (!(await existsById(id))) {\n return { content: `No ${singular} with ${idField} \"${id}\". Call list_${plural} for valid ids.`, isError: true };\n }\n const op: OdlaOp = { t: \"delete\", ns: entity, id };\n await db.transact([op]);\n return ok({ id, deleted: true });\n },\n };\n\n const guidance =\n `Manage ${plural} in the database. Use list_${plural} to read (optionally filtered), ` +\n `create_${singular} to add, update_${singular} to change fields (e.g. mark done), and ` +\n `delete_${singular} to remove. Always confirm the current list with list_${plural} before ` +\n `updating or deleting so you use the right id.` +\n (opts.instructions ? `\\n${opts.instructions}` : \"\");\n\n return {\n name: opts.name ?? `${entity} management`,\n instructions: guidance,\n tools: [listTool, createTool, updateTool, deleteTool],\n };\n}\n"],"mappings":"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","names":["cache","cache"]}
|
package/llms.txt
CHANGED
|
@@ -86,6 +86,27 @@ Secrets are read-only from the app key (odlaDbKeyResolver → db.secrets.get); t
|
|
|
86
86
|
are WRITTEN only with the operator/dev token via provisionAgentApp/putSecret. Keys
|
|
87
87
|
are AES-GCM-encrypted at rest in odla-db and never returned to browser clients.
|
|
88
88
|
|
|
89
|
+
## Platform wiring (odla.ai apps registry)
|
|
90
|
+
|
|
91
|
+
Apps registered on the odla platform get LLM access as an APP SETTING —
|
|
92
|
+
never put provider keys in your Worker's wrangler vars/secrets. Provider +
|
|
93
|
+
default model are per-environment registry config (set in Studio's AI card
|
|
94
|
+
or `@odla-ai/apps` setAi); the API key lives in the platform vault (the
|
|
95
|
+
env's odla-db tenant secrets, write-only for operators). One call reads both:
|
|
96
|
+
|
|
97
|
+
import { initFromPlatform } from "@odla-ai/ai";
|
|
98
|
+
import { init as odlaInit } from "@odla-ai/db";
|
|
99
|
+
|
|
100
|
+
const db = odlaInit({ appId: TENANT, adminToken: env.ODLA_API_KEY, endpoint: "https://db.odla.ai" });
|
|
101
|
+
const { ai, provider, model } = await initFromPlatform({
|
|
102
|
+
platform: "https://odla.ai", appId: APP_ID, env: "prod", db });
|
|
103
|
+
// ai.chat / ai.stream / ai.extract — provider/model from public-config
|
|
104
|
+
// (cached ~60s, so Studio changes apply without redeploys); the key is
|
|
105
|
+
// fetched from the vault at call time via odlaDbKeyResolver.
|
|
106
|
+
|
|
107
|
+
The only secret the Worker carries is its odla-db app key. Rotating the LLM
|
|
108
|
+
key = writing the secret again; switching provider/model = a Studio edit.
|
|
109
|
+
|
|
89
110
|
## Errors
|
|
90
111
|
|
|
91
112
|
Every error is an OdlaAIError subclass with a stable `code` — branch on err.code:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@odla-ai/ai",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "One general-purpose interface for AI inference across Anthropic (Claude), OpenAI, and Google (Gemini)
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "One general-purpose interface for AI inference across Anthropic (Claude), OpenAI, and Google (Gemini) — text, image, and audio — plus a tool-use agent engine and eval harness. The place every odla app reaches for when it needs AI. Isomorphic: Node 20+, Cloudflare Workers, the browser.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|