@odla-ai/ai 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +25 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +25 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -816,17 +816,23 @@ interface OdlaLookup {
|
|
|
816
816
|
}
|
|
817
817
|
/** Entity reference: a raw id, or a natural-key `Lookup` for idempotent upsert. */
|
|
818
818
|
type OdlaEntityRef = string | OdlaLookup;
|
|
819
|
+
/** Attribute values for an op. Typed `any` on purpose: odla-db's real `transact`
|
|
820
|
+
* constrains attrs to its own `Value` type, while odla-ai builds attrs from
|
|
821
|
+
* mixed sources (message content, run traces). `any` is the one value type
|
|
822
|
+
* assignable in both directions, so a real `@odla-ai/db` `AdminDb` satisfies
|
|
823
|
+
* `OdlaDbClient` with no cast at the call site. */
|
|
824
|
+
type OdlaAttrs = Record<string, any>;
|
|
819
825
|
/** The odla-db transaction op union (subset we emit). */
|
|
820
826
|
type OdlaOp = {
|
|
821
827
|
t: "update";
|
|
822
828
|
ns: string;
|
|
823
829
|
id: OdlaEntityRef;
|
|
824
|
-
attrs:
|
|
830
|
+
attrs: OdlaAttrs;
|
|
825
831
|
} | {
|
|
826
832
|
t: "merge";
|
|
827
833
|
ns: string;
|
|
828
834
|
id: OdlaEntityRef;
|
|
829
|
-
attrs:
|
|
835
|
+
attrs: OdlaAttrs;
|
|
830
836
|
} | {
|
|
831
837
|
t: "delete";
|
|
832
838
|
ns: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -816,17 +816,23 @@ interface OdlaLookup {
|
|
|
816
816
|
}
|
|
817
817
|
/** Entity reference: a raw id, or a natural-key `Lookup` for idempotent upsert. */
|
|
818
818
|
type OdlaEntityRef = string | OdlaLookup;
|
|
819
|
+
/** Attribute values for an op. Typed `any` on purpose: odla-db's real `transact`
|
|
820
|
+
* constrains attrs to its own `Value` type, while odla-ai builds attrs from
|
|
821
|
+
* mixed sources (message content, run traces). `any` is the one value type
|
|
822
|
+
* assignable in both directions, so a real `@odla-ai/db` `AdminDb` satisfies
|
|
823
|
+
* `OdlaDbClient` with no cast at the call site. */
|
|
824
|
+
type OdlaAttrs = Record<string, any>;
|
|
819
825
|
/** The odla-db transaction op union (subset we emit). */
|
|
820
826
|
type OdlaOp = {
|
|
821
827
|
t: "update";
|
|
822
828
|
ns: string;
|
|
823
829
|
id: OdlaEntityRef;
|
|
824
|
-
attrs:
|
|
830
|
+
attrs: OdlaAttrs;
|
|
825
831
|
} | {
|
|
826
832
|
t: "merge";
|
|
827
833
|
ns: string;
|
|
828
834
|
id: OdlaEntityRef;
|
|
829
|
-
attrs:
|
|
835
|
+
attrs: OdlaAttrs;
|
|
830
836
|
} | {
|
|
831
837
|
t: "delete";
|
|
832
838
|
ns: string;
|
package/dist/index.js
CHANGED
|
@@ -986,6 +986,10 @@ function entityCrudSkill(opts) {
|
|
|
986
986
|
return out;
|
|
987
987
|
};
|
|
988
988
|
const ok = (obj) => ({ content: JSON.stringify(obj) });
|
|
989
|
+
const existsById = async (id) => {
|
|
990
|
+
const res = await db.query({ [entity]: { $: {} } });
|
|
991
|
+
return (res[entity] ?? []).some((r) => String(r.id ?? "") === id);
|
|
992
|
+
};
|
|
989
993
|
const listTool = {
|
|
990
994
|
name: `list_${plural}`,
|
|
991
995
|
description: `List ${plural}. Optionally filter by exact field values and/or limit the count.`,
|
|
@@ -1011,6 +1015,12 @@ function entityCrudSkill(opts) {
|
|
|
1011
1015
|
description: `Create a new ${singular}.`,
|
|
1012
1016
|
inputSchema: { type: "object", properties: fields, ...opts.required ? { required: opts.required } : {} },
|
|
1013
1017
|
handler: async (input) => {
|
|
1018
|
+
for (const req of opts.required ?? []) {
|
|
1019
|
+
const v = input[req];
|
|
1020
|
+
if (v === void 0 || v === null || v === "") {
|
|
1021
|
+
return { content: `Cannot create ${singular}: field "${req}" is required.`, isError: true };
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1014
1024
|
const id = genId();
|
|
1015
1025
|
const attrs = pickFields(input);
|
|
1016
1026
|
if (stampAttr) attrs[stampAttr] = Date.now();
|
|
@@ -1023,7 +1033,10 @@ function entityCrudSkill(opts) {
|
|
|
1023
1033
|
description: `Update fields of an existing ${singular} by ${idField}. Only the fields you pass change.`,
|
|
1024
1034
|
inputSchema: {
|
|
1025
1035
|
type: "object",
|
|
1026
|
-
properties: {
|
|
1036
|
+
properties: {
|
|
1037
|
+
[idField]: { type: "string", description: `The exact ${idField} string from list_${plural} \u2014 NOT the item's position in a numbered list.` },
|
|
1038
|
+
...fields
|
|
1039
|
+
},
|
|
1027
1040
|
required: [idField]
|
|
1028
1041
|
},
|
|
1029
1042
|
handler: async (input) => {
|
|
@@ -1031,6 +1044,9 @@ function entityCrudSkill(opts) {
|
|
|
1031
1044
|
if (!id) return { content: `Missing "${idField}".`, isError: true };
|
|
1032
1045
|
const attrs = pickFields(input);
|
|
1033
1046
|
if (Object.keys(attrs).length === 0) return { content: "No fields to update.", isError: true };
|
|
1047
|
+
if (!await existsById(id)) {
|
|
1048
|
+
return { content: `No ${singular} with ${idField} "${id}". Call list_${plural} for valid ids, then retry.`, isError: true };
|
|
1049
|
+
}
|
|
1034
1050
|
await db.transact([{ t: "update", ns: entity, id, attrs }]);
|
|
1035
1051
|
return ok({ id, updated: true });
|
|
1036
1052
|
}
|
|
@@ -1038,10 +1054,17 @@ function entityCrudSkill(opts) {
|
|
|
1038
1054
|
const deleteTool = {
|
|
1039
1055
|
name: `delete_${singular}`,
|
|
1040
1056
|
description: `Delete a ${singular} by ${idField}.`,
|
|
1041
|
-
inputSchema: {
|
|
1057
|
+
inputSchema: {
|
|
1058
|
+
type: "object",
|
|
1059
|
+
properties: { [idField]: { type: "string", description: `The exact ${idField} string from list_${plural} \u2014 NOT the item's position in a numbered list.` } },
|
|
1060
|
+
required: [idField]
|
|
1061
|
+
},
|
|
1042
1062
|
handler: async (input) => {
|
|
1043
1063
|
const id = String(input[idField] ?? "");
|
|
1044
1064
|
if (!id) return { content: `Missing "${idField}".`, isError: true };
|
|
1065
|
+
if (!await existsById(id)) {
|
|
1066
|
+
return { content: `No ${singular} with ${idField} "${id}". Call list_${plural} for valid ids.`, isError: true };
|
|
1067
|
+
}
|
|
1045
1068
|
const op = { t: "delete", ns: entity, id };
|
|
1046
1069
|
await db.transact([op]);
|
|
1047
1070
|
return ok({ id, deleted: true });
|
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 // 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\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}\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 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 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 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: { [idField]: { type: \"string\", description: `The ${singular}'s id.` }, ...fields },\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 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: { type: \"object\", properties: { [idField]: { type: \"string\" } }, required: [idField] },\n handler: async (input) => {\n const id = String(input[idField] ?? \"\");\n if (!id) return { content: `Missing \"${idField}\".`, isError: true };\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/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 // 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\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}\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 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"]}
|