@hawkeyexl/inference 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +33 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -468,26 +468,46 @@ function defaultLlamaModelsDirectory() {
|
|
|
468
468
|
var LLAMA_TIERS = ["fast", "balanced", "quality"];
|
|
469
469
|
var LLAMA_SELECTORS = ["auto", ...LLAMA_TIERS];
|
|
470
470
|
var LLAMA_MODELS = deepFreezeEntries({
|
|
471
|
+
"granite-4.1-3b-q2": {
|
|
472
|
+
uri: "hf:unsloth/granite-4.1-3b-GGUF/granite-4.1-3b-UD-Q2_K_XL.gguf",
|
|
473
|
+
sizeBytes: 1414548800,
|
|
474
|
+
license: "Apache-2.0",
|
|
475
|
+
tier: "fast",
|
|
476
|
+
notes: "Smallest tier and the quickest measured (4.8s/page). Scores level with models three times its size on schema-constrained extraction."
|
|
477
|
+
},
|
|
478
|
+
"qwen3.5-4b": {
|
|
479
|
+
uri: "hf:unsloth/Qwen3.5-4B-GGUF/Qwen3.5-4B-UD-Q4_K_XL.gguf",
|
|
480
|
+
sizeBytes: 2912109728,
|
|
481
|
+
license: "Apache-2.0",
|
|
482
|
+
notes: "The default for most machines. Smaller and faster than the Gemma 4 E4B it replaces, at indistinguishable measured quality.",
|
|
483
|
+
tier: "balanced"
|
|
484
|
+
},
|
|
485
|
+
"qwen3.5-9b": {
|
|
486
|
+
uri: "hf:unsloth/Qwen3.5-9B-GGUF/Qwen3.5-9B-UD-Q4_K_XL.gguf",
|
|
487
|
+
sizeBytes: 5966095584,
|
|
488
|
+
license: "Apache-2.0",
|
|
489
|
+
tier: "quality",
|
|
490
|
+
notes: "Best measured of everything tried, and still smaller and faster than the Gemma 4 12B it replaces. Wants a GPU or plenty of RAM."
|
|
491
|
+
},
|
|
492
|
+
// --- Superseded, kept resolvable by name. Untiered: nothing selects these
|
|
493
|
+
// unless a caller asks for one outright.
|
|
471
494
|
"gemma-4-e2b": {
|
|
472
495
|
uri: "hf:unsloth/gemma-4-E2B-it-qat-GGUF/gemma-4-E2B-it-qat-UD-Q4_K_XL.gguf",
|
|
473
496
|
sizeBytes: 2620370976,
|
|
474
497
|
license: "Apache-2.0",
|
|
475
|
-
|
|
476
|
-
notes: "IFEval 94.6. Smallest Gemma 4; the floor for this family."
|
|
498
|
+
notes: "IFEval 94.6. Former `fast` tier; sound, but larger than Granite."
|
|
477
499
|
},
|
|
478
500
|
"gemma-4-e4b": {
|
|
479
501
|
uri: "hf:unsloth/gemma-4-E4B-it-qat-GGUF/gemma-4-E4B-it-qat-UD-Q4_K_XL.gguf",
|
|
480
502
|
sizeBytes: 4215695776,
|
|
481
503
|
license: "Apache-2.0",
|
|
482
|
-
|
|
483
|
-
notes: "IFEval 96.7. The default for most machines."
|
|
504
|
+
notes: "IFEval 96.7. Former `balanced` tier; sound, but larger."
|
|
484
505
|
},
|
|
485
506
|
"gemma-4-12b": {
|
|
486
507
|
uri: "hf:unsloth/gemma-4-12B-it-qat-GGUF/gemma-4-12B-it-qat-UD-Q4_K_XL.gguf",
|
|
487
508
|
sizeBytes: 6716356800,
|
|
488
509
|
license: "Apache-2.0",
|
|
489
|
-
|
|
490
|
-
notes: "IFEval 97.2. Dense 12B; wants a GPU or plenty of RAM."
|
|
510
|
+
notes: "IFEval 97.2. Former `quality` tier. Dense 12B; wants a GPU."
|
|
491
511
|
},
|
|
492
512
|
"gemma-4-26b-a4b": {
|
|
493
513
|
uri: "hf:unsloth/gemma-4-26B-A4B-it-qat-GGUF/gemma-4-26B-A4B-it-qat-UD-Q4_K_XL.gguf",
|
|
@@ -499,7 +519,7 @@ var LLAMA_MODELS = deepFreezeEntries({
|
|
|
499
519
|
uri: "hf:unsloth/gemma-4-E2B-it-qat-GGUF/gemma-4-E2B-it-qat-UD-Q2_K_XL.gguf",
|
|
500
520
|
sizeBytes: 2186186784,
|
|
501
521
|
license: "Apache-2.0",
|
|
502
|
-
notes: "
|
|
522
|
+
notes: "AVOID. Smallest download, but it does not reliably terminate: 6 of 12 pages unfinished at 120s, one still running at 400s, and the pages that did finish proposed identifiers as prose. Kept only so existing pins still resolve."
|
|
503
523
|
}
|
|
504
524
|
});
|
|
505
525
|
function deepFreezeEntries(catalog) {
|
|
@@ -507,9 +527,9 @@ function deepFreezeEntries(catalog) {
|
|
|
507
527
|
return Object.freeze(catalog);
|
|
508
528
|
}
|
|
509
529
|
var TIER_ALIAS = {
|
|
510
|
-
fast: "
|
|
511
|
-
balanced: "
|
|
512
|
-
quality: "
|
|
530
|
+
fast: "granite-4.1-3b-q2",
|
|
531
|
+
balanced: "qwen3.5-4b",
|
|
532
|
+
quality: "qwen3.5-9b"
|
|
513
533
|
};
|
|
514
534
|
function isLlamaSelector(model) {
|
|
515
535
|
return LLAMA_SELECTORS.includes(model);
|
|
@@ -532,7 +552,7 @@ function uriForTier(tier) {
|
|
|
532
552
|
function resolveLlamaModelRef(model) {
|
|
533
553
|
if (isLlamaSelector(model)) {
|
|
534
554
|
throw new InferenceError(
|
|
535
|
-
`llama-cpp model "${model}" is a selector and needs a hardware probe to resolve. Use resolveProviderIdentityAsync/makeProviderAsync, or name a concrete model (e.g. "
|
|
555
|
+
`llama-cpp model "${model}" is a selector and needs a hardware probe to resolve. Use resolveProviderIdentityAsync/makeProviderAsync, or name a concrete model (e.g. "${TIER_ALIAS.balanced}").`
|
|
536
556
|
);
|
|
537
557
|
}
|
|
538
558
|
const entry = LLAMA_MODELS[model];
|
|
@@ -780,7 +800,7 @@ var LlamaCppProvider = class {
|
|
|
780
800
|
this.model = model;
|
|
781
801
|
if (isLlamaSelector(model)) {
|
|
782
802
|
throw new InferenceError(
|
|
783
|
-
`llama-cpp model "${model}" is a selector. Constructing a provider directly needs a concrete model (e.g. "
|
|
803
|
+
`llama-cpp model "${model}" is a selector. Constructing a provider directly needs a concrete model (e.g. "${aliasForTier("balanced")}") \u2014 use makeProviderAsync to resolve a selector against this machine.`
|
|
784
804
|
);
|
|
785
805
|
}
|
|
786
806
|
this.uri = resolveLlamaModelRef(model);
|
|
@@ -1088,7 +1108,7 @@ function resolveProviderIdentity(spec) {
|
|
|
1088
1108
|
const model = spec.model ?? DEFAULT_MODELS[spec.provider] ?? "unknown";
|
|
1089
1109
|
if (spec.provider === "llama-cpp" && isLlamaSelector(model)) {
|
|
1090
1110
|
throw new InferenceError(
|
|
1091
|
-
`llama-cpp model "${model}" is a selector and cannot be resolved synchronously \u2014 picking a tier probes GPU memory. Use resolveProviderIdentityAsync/makeProviderAsync, or name a concrete model (e.g. "
|
|
1111
|
+
`llama-cpp model "${model}" is a selector and cannot be resolved synchronously \u2014 picking a tier probes GPU memory. Use resolveProviderIdentityAsync/makeProviderAsync, or name a concrete model (e.g. "${aliasForTier("balanced")}").`
|
|
1092
1112
|
);
|
|
1093
1113
|
}
|
|
1094
1114
|
return { provider: spec.provider, model };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/runtime.ts","../src/providers/anthropic.ts","../src/providers/openai-compat.ts","../src/exec.ts","../src/providers/claude-cli.ts","../src/providers/mock.ts","../src/cache.ts","../src/providers/llama-models.ts","../src/providers/llama-install.ts","../src/providers/llama-cpp.ts","../src/providers/detect.ts","../src/providers/index.ts","../src/providers/llama-clean.ts","../src/complete.ts","../src/cost.ts","../src/judge/verdict-schema.json","../src/judge/types.ts","../src/judge/consensus.ts","../src/judge/zones.ts","../src/judge/ensemble.ts"],"sourcesContent":["/**\n * Shared error type. Consumers catch this to distinguish an inference-layer\n * operational failure (missing API key, unknown provider) from their own\n * domain errors, and typically map it to their own exit code.\n */\nexport class InferenceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InferenceError\";\n }\n}\n","/**\n * The one thing this library says about the runtime it was loaded into.\n *\n * `engines.node` is `>=24`, but npm treats an `engines` mismatch as an\n * `EBADENGINE` **warning**, not a refusal — scrolled past in CI, invisible in a\n * transitive install. So an older Node installs cleanly, runs, and then fails\n * somewhere unrelated with an error that never mentions the Node version.\n *\n * This is a warning rather than a throw on purpose. Nothing in `src/` uses a\n * Node-24-only API — the imports are `node:crypto`, `node:fs`, `node:os` and\n * `node:path`, the newest globals are `fetch` (18+) and `structuredClone`\n * (17+), and `tsconfig` targets ES2022 — and the strictest dependency floor is\n * `node-llama-cpp` at `>=20`. Node 24 is this package's *support* policy, not a\n * technical impossibility, and a library has no business refusing to run on a\n * consumer's behalf when it can still do the work. It says so once and\n * continues, the same way the four existing warn-once paths do.\n */\n\n/**\n * The major version `engines.node` declares. `test/unit/runtime.test.ts` pins\n * this against `package.json`, so the two cannot drift apart.\n */\nexport const MINIMUM_NODE_MAJOR = 24;\n\nlet warnedNodeVersion = false;\n\n/**\n * Warn once if the running Node is older than the declared minimum.\n *\n * The version is a parameter so this is testable in-process — no subprocess on\n * a second Node install just to see the string.\n */\nexport function warnIfUnsupportedNode(\n version: string = process.versions.node,\n): void {\n if (warnedNodeVersion) return;\n const major = Number.parseInt(version, 10);\n // An unreadable version is not evidence of an old one. Same rule as an\n // unknown model price: never guess.\n if (!Number.isInteger(major) || major >= MINIMUM_NODE_MAJOR) return;\n warnedNodeVersion = true;\n console.warn(\n `inference: running on Node ${version}, older than the Node ` +\n `${MINIMUM_NODE_MAJOR} this package requires. npm only warns about that ` +\n `at install time (EBADENGINE), so nothing has stopped you yet — upgrade ` +\n `Node, or expect failures this library cannot explain.`,\n );\n}\n\n/** Test seam: reset the once-per-process Node version warning. */\nexport function resetNodeVersionWarning(): void {\n warnedNodeVersion = false;\n}\n","/**\n * Anthropic provider: structured output via a single forced tool call whose\n * input schema is the caller's schema — the model cannot answer any other way.\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { InferenceError } from \"../types.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n} from \"./types.js\";\n\nconst DEFAULT_TOOL_NAME = \"record_result\";\n\nexport interface AnthropicProviderOptions {\n /**\n * Name of the forced tool. Purely cosmetic to the model, but a descriptive\n * name (\"record_verdict\", \"record_proposal\") measurably steers output, so\n * consumers may set their own.\n */\n toolName?: string;\n /** Tool description shown to the model. */\n toolDescription?: string;\n maxTokens?: number;\n}\n\nexport class AnthropicProvider implements InferenceProvider {\n private readonly client: Anthropic;\n private readonly toolName: string;\n private readonly toolDescription: string;\n private readonly maxTokens: number;\n\n constructor(\n private readonly model: string,\n apiKeyEnv: string,\n options: AnthropicProviderOptions = {},\n ) {\n const apiKey = process.env[apiKeyEnv];\n if (!apiKey) {\n throw new InferenceError(\n `Anthropic provider needs ${apiKeyEnv} set (or choose another provider)`,\n );\n }\n this.client = new Anthropic({ apiKey });\n this.toolName = options.toolName ?? DEFAULT_TOOL_NAME;\n this.toolDescription =\n options.toolDescription ?? \"Record the structured result.\";\n this.maxTokens = options.maxTokens ?? 1024;\n }\n\n provider(): string {\n return \"anthropic\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: req.temperature,\n system: req.system,\n messages: [{ role: \"user\", content: req.user }],\n tools: [\n {\n name: this.toolName,\n description: this.toolDescription,\n input_schema: req.schema as Anthropic.Tool[\"input_schema\"],\n },\n ],\n tool_choice: { type: \"tool\", name: this.toolName },\n });\n\n // A truncated tool call still arrives as a tool_use block, just with\n // partial input. Without this check it fails schema validation instead,\n // burning the retry and reporting a misleading validation error rather\n // than the actionable \"raise maxTokens\".\n if (response.stop_reason === \"max_tokens\") {\n throw new Error(\n `Anthropic response hit max_tokens (${this.maxTokens}) before completing the tool call — raise the anthropic.maxTokens option.`,\n );\n }\n\n const toolUse = response.content.find(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n if (!toolUse) {\n throw new Error(\"Anthropic response contained no tool_use block\");\n }\n return {\n json: toolUse.input,\n usage: {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n },\n };\n }\n}\n","/**\n * OpenAI-compatible provider: works against OpenAI, Azure, Ollama, Groq,\n * Together, or any server speaking /chat/completions. Prefers strict\n * json_schema response_format; falls back to json_object + schema-in-prompt\n * when the server rejects it (older Ollama, some proxies).\n */\nimport { InferenceError } from \"../types.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n} from \"./types.js\";\n\ninterface ChatResponse {\n choices?: { message?: { content?: string } }[];\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n error?: { message?: string };\n}\n\n/** Extract a JSON object from content that may carry markdown fences. */\nexport function extractJson(content: string): unknown {\n const trimmed = content\n .replace(/^\\s*```(?:json)?\\s*/i, \"\")\n .replace(/\\s*```\\s*$/, \"\")\n .trim();\n try {\n return JSON.parse(trimmed);\n } catch {\n const start = trimmed.indexOf(\"{\");\n const end = trimmed.lastIndexOf(\"}\");\n if (start >= 0 && end > start) {\n return JSON.parse(trimmed.slice(start, end + 1));\n }\n throw new Error(\"Response contained no parseable JSON object\");\n }\n}\n\n/**\n * OpenAI strict mode requires `required` to list EVERY property (optionality\n * is expressed as a `null` type union) and rejects keywords outside its\n * subset (minLength, uniqueItems). Transform an all-optional schema into a\n * strict-compatible equivalent; null values are stripped from the response.\n */\nexport function toStrictSchema(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n const clone = structuredClone(schema);\n const walk = (node: unknown): void => {\n if (node === null || typeof node !== \"object\") return;\n if (Array.isArray(node)) {\n for (const item of node) walk(item);\n return;\n }\n const obj = node as Record<string, unknown>;\n delete obj[\"minLength\"];\n delete obj[\"uniqueItems\"];\n const properties = obj[\"properties\"];\n if (properties && typeof properties === \"object\") {\n obj[\"required\"] = Object.keys(properties);\n // Strict mode requires additionalProperties:false on EVERY object, not\n // just the root. A nested object without it is rejected as a schema\n // error, which permanently downgrades this provider to the weaker\n // json_object fallback for the rest of its life.\n obj[\"additionalProperties\"] = false;\n for (const prop of Object.values(properties as Record<string, unknown>)) {\n walk(prop);\n if (prop && typeof prop === \"object\" && !Array.isArray(prop)) {\n const p = prop as Record<string, unknown>;\n if (typeof p[\"type\"] === \"string\" && p[\"type\"] !== \"null\") {\n p[\"type\"] = [p[\"type\"], \"null\"];\n }\n }\n }\n }\n walk(obj[\"items\"]);\n };\n walk(clone);\n return clone;\n}\n\n/** Remove null-valued keys (strict-mode \"omitted\" marker) from a response object. */\nexport function stripNulls(value: unknown): unknown {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n return value;\n }\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).filter(\n ([, v]) => v !== null,\n ),\n );\n}\n\nexport interface OpenAICompatProviderOptions {\n /** `json_schema.name` sent to the server. Cosmetic; steers some models. */\n schemaName?: string;\n}\n\nexport class OpenAICompatProvider implements InferenceProvider {\n private supportsJsonSchema = true;\n private readonly schemaName: string;\n\n constructor(\n private readonly baseUrl: string,\n private readonly model: string,\n apiKeyEnv: string,\n private readonly apiKey: string | undefined = process.env[apiKeyEnv],\n options: OpenAICompatProviderOptions = {},\n ) {\n // Local servers (Ollama) often need no key; only insist for api.openai.com.\n if (!this.apiKey && baseUrl.includes(\"api.openai.com\")) {\n throw new InferenceError(\n `OpenAI provider needs ${apiKeyEnv} set (or point baseUrl at a local server)`,\n );\n }\n this.schemaName = options.schemaName ?? \"result\";\n }\n\n provider(): string {\n return \"openai\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n private async chat(body: Record<string, unknown>): Promise<ChatResponse> {\n const response = await fetch(\n `${this.baseUrl.replace(/\\/$/, \"\")}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(this.apiKey ? { authorization: `Bearer ${this.apiKey}` } : {}),\n },\n body: JSON.stringify(body),\n },\n );\n const json = (await response.json().catch(() => ({}))) as ChatResponse;\n if (!response.ok) {\n const message = json.error?.message ?? `HTTP ${response.status}`;\n throw new Error(`${message}`);\n }\n return json;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const base = {\n model: this.model,\n temperature: req.temperature,\n messages: [\n { role: \"system\", content: req.system },\n { role: \"user\", content: req.user },\n ],\n };\n\n let response: ChatResponse;\n if (this.supportsJsonSchema) {\n try {\n response = await this.chat({\n ...base,\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: this.schemaName,\n strict: true,\n schema: toStrictSchema(req.schema),\n },\n },\n });\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n // Fall back on schema/format complaints AND on opaque 400s (gateways\n // that reject response_format without a parseable error body).\n if (\n !/response_format|json_schema|schema/i.test(message) &&\n message !== \"HTTP 400\"\n ) {\n throw e;\n }\n this.supportsJsonSchema = false;\n response = await this.jsonObjectFallback(base, req);\n }\n } else {\n response = await this.jsonObjectFallback(base, req);\n }\n\n const content = response.choices?.[0]?.message?.content;\n if (!content) throw new Error(\"Empty completion response\");\n return {\n json: stripNulls(extractJson(content)),\n usage:\n response.usage?.prompt_tokens != null\n ? {\n inputTokens: response.usage.prompt_tokens ?? 0,\n outputTokens: response.usage.completion_tokens ?? 0,\n }\n : undefined,\n };\n }\n\n private jsonObjectFallback(\n base: Record<string, unknown>,\n req: CompleteJSONRequest,\n ): Promise<ChatResponse> {\n return this.chat({\n ...base,\n messages: [\n {\n role: \"system\",\n content: `${req.system}\\n\\nRespond with ONLY a JSON object conforming to this JSON Schema:\\n${JSON.stringify(req.schema)}`,\n },\n { role: \"user\", content: req.user },\n ],\n response_format: { type: \"json_object\" },\n });\n }\n}\n","/**\n * Process execution wrapper for subprocess-backed providers. Uses cross-spawn\n * so npm shims resolve on Windows without `shell: true` and its quoting\n * hazards. Large payloads go through `input` (piped stdin) rather than argv —\n * Windows caps the command line at ~32K characters.\n */\nimport spawn from \"cross-spawn\";\nimport type { ExecFn, ExecResult } from \"./providers/types.js\";\n\nexport const realExec: ExecFn = (cmd, opts = {}) => {\n const [bin, ...args] = cmd;\n if (!bin) {\n return Promise.resolve<ExecResult>({\n code: null,\n stdout: \"\",\n stderr: \"\",\n timedOut: false,\n spawnError: \"Empty command\",\n });\n }\n return new Promise<ExecResult>((resolvePromise) => {\n const child = spawn(bin, args, {\n cwd: opts.cwd,\n env: { ...process.env, ...(opts.env ?? {}) },\n stdio: [opts.input != null ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n let settled = false;\n\n const timeoutMs = opts.timeoutMs ?? 60000;\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill();\n // Settle on the timeout itself rather than waiting for 'close'. On POSIX\n // a child that handles SIGTERM survives kill(), so 'close' would never\n // fire and the caller would hang forever with no timeout error. (On\n // Windows kill() is forceful, so this is belt-and-braces there.)\n settle({ code: null, stdout, stderr, timedOut: true });\n }, timeoutMs);\n\n const settle = (result: ExecResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolvePromise(result);\n };\n\n if (opts.input != null && child.stdin) {\n // EPIPE from a child that exits before reading is not our failure.\n child.stdin.on(\"error\", () => {});\n child.stdin.end(opts.input);\n }\n\n // setEncoding routes chunks through a StringDecoder, so multi-byte UTF-8\n // characters straddling pipe-chunk boundaries decode correctly; a raw\n // per-chunk Buffer.toString() would corrupt them nondeterministically.\n child.stdout?.setEncoding(\"utf8\");\n child.stderr?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (d: string) => (stdout += d));\n child.stderr?.on(\"data\", (d: string) => (stderr += d));\n child.on(\"error\", (e) =>\n settle({ code: null, stdout, stderr, timedOut, spawnError: e.message }),\n );\n child.on(\"close\", (code) => settle({ code, stdout, stderr, timedOut }));\n });\n};\n","/**\n * Claude CLI provider: shells out to the `claude` CLI using its local\n * authentication — no API key needed. Token usage is not reported, so cost\n * shows as unknown.\n */\nimport { realExec } from \"../exec.js\";\nimport { extractJson } from \"./openai-compat.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n ExecFn,\n InferenceProvider,\n} from \"./types.js\";\n\nexport class ClaudeCliProvider implements InferenceProvider {\n constructor(\n private readonly model: string,\n private readonly command: string = \"claude\",\n private readonly exec: ExecFn = realExec,\n private readonly timeoutMs: number = 180000,\n ) {}\n\n provider(): string {\n return \"claude-cli\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const prompt = [\n req.user,\n \"\",\n \"Respond with ONLY a JSON object conforming to this JSON Schema — no\",\n \"prose, no markdown fences:\",\n JSON.stringify(req.schema),\n ].join(\"\\n\");\n\n // The prompt is piped via stdin: user content routinely exceeds the ~32K\n // Windows command-line limit when passed as an argument.\n const result = await this.exec(\n [\n this.command,\n \"-p\",\n \"--append-system-prompt\",\n req.system,\n \"--output-format\",\n \"json\",\n \"--model\",\n this.model,\n ],\n { timeoutMs: this.timeoutMs, input: prompt },\n );\n\n if (result.spawnError) {\n throw new Error(\n `Failed to run ${this.command}: ${result.spawnError} (is the Claude CLI installed?)`,\n );\n }\n if (result.timedOut) throw new Error(\"Claude CLI timed out\");\n if (result.code !== 0) {\n throw new Error(\n `Claude CLI exited ${result.code}: ${result.stderr.trim().slice(-300)}`,\n );\n }\n\n // --output-format json wraps the answer: { result: \"...\", ... }\n //\n // When it isn't JSON at all the CLI is talking to a human, not to us — an\n // update banner, a login prompt, a proxy interception page. A bare\n // `SyntaxError: Unexpected token 'W'` would reach the end user of a\n // consuming CLI naming neither the culprit nor the fix, so say who printed\n // it and quote enough for a login prompt to be recognisable on sight.\n // Collapsed to one line and capped, like the stderr tail above, so a\n // megabyte of HTML cannot become the error message.\n let wrapper: { result?: string };\n try {\n wrapper = JSON.parse(result.stdout) as { result?: string };\n } catch {\n const excerpt = result.stdout.trim().replace(/\\s+/g, \" \").slice(0, 200);\n throw new Error(\n `Claude CLI printed non-JSON output (is it logged in?): ${excerpt || \"(no output)\"}`,\n );\n }\n if (typeof wrapper.result !== \"string\") {\n throw new Error(\"Claude CLI returned no result field\");\n }\n return { json: extractJson(wrapper.result) };\n }\n}\n","/**\n * Mock provider for tests and offline development. Responds with scripted\n * results in order, cycling when exhausted. Exported from the public API so\n * downstream consumers can test their own pipelines without a live provider.\n */\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n TokenUsage,\n} from \"./types.js\";\n\nexport type MockResponse =\n | { json: unknown; usage?: TokenUsage }\n | { error: string };\n\nexport class MockProvider implements InferenceProvider {\n private calls = 0;\n /** Every request seen, in order — assert against this in tests. */\n public readonly requests: CompleteJSONRequest[] = [];\n\n constructor(\n private readonly responses: MockResponse[],\n private readonly model = \"mock-model\",\n ) {\n if (responses.length === 0) {\n throw new Error(\"MockProvider needs at least one scripted response\");\n }\n }\n\n provider(): string {\n return \"mock\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n this.requests.push(req);\n const response = this.responses[this.calls % this.responses.length]!;\n this.calls += 1;\n if (\"error\" in response) {\n return Promise.reject(new Error(response.error));\n }\n return Promise.resolve({\n json: response.json,\n usage: response.usage ?? { inputTokens: 500, outputTokens: 100 },\n });\n }\n}\n\n/** Convenience: a scripted response shaped like the canonical judge verdict. */\nexport function mockVerdict(\n match: \"pass\" | \"fail\" | \"partial\",\n confidence: number,\n overrides: Partial<{\n claim: string;\n observed: string;\n reasoning: string;\n }> = {},\n): { json: unknown } {\n return {\n json: {\n claim: overrides.claim ?? \"The assertion under test\",\n observed: overrides.observed ?? \"Observed content\",\n match,\n confidence,\n reasoning: overrides.reasoning ?? \"Scripted mock reasoning\",\n },\n };\n}\n","/**\n * Content-addressed JSON cache for inference results, so a cached run replays\n * identically instead of re-billing a nondeterministic call.\n *\n * Key composition stays with the caller: each consumer has a different notion\n * of what should invalidate an entry (page body, prompt version, ensemble\n * size, requested fields). `buildCacheKey` just hashes the parts you name.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport function sha256(text: string): string {\n return createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Hash an ordered list of key parts into a cache key. Long parts (page bodies,\n * rendered traces) should be pre-hashed with `sha256` by the caller so the\n * joined string stays small.\n */\nexport function buildCacheKey(parts: string[]): string {\n // Length-prefixed, not plain-joined: a part that itself contains the\n // separator would otherwise let two different compositions — [\"a|b\", \"c\"]\n // and [\"a\", \"b|c\"] — share one cached result.\n return sha256(parts.map((p) => `${p.length}:${p}`).join(\"|\"));\n}\n\nexport class JsonCache<T> {\n /** Cache-write failures warn once per process, not once per entry. */\n private warned = false;\n\n constructor(\n private readonly dir: string,\n private readonly enabled: boolean = true,\n /** Prefix for the one-time write-failure warning. */\n private readonly label: string = \"inference\",\n ) {}\n\n get(key: string): T | undefined {\n if (!this.enabled) return undefined;\n const path = join(this.dir, `${key}.json`);\n if (!existsSync(path)) return undefined;\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as T;\n } catch {\n return undefined; // Corrupt cache entry — treat as a miss.\n }\n }\n\n set(key: string, value: T): void {\n if (!this.enabled) return;\n // The cache is an optimization: a write failure (read-only workspace, full\n // disk, long path) must never abort a run whose inference already\n // succeeded and was already paid for.\n try {\n mkdirSync(this.dir, { recursive: true });\n writeFileSync(\n join(this.dir, `${key}.json`),\n JSON.stringify(value, null, 2),\n );\n } catch (e) {\n if (!this.warned) {\n this.warned = true;\n console.warn(\n `${this.label}: could not write the cache at ${this.dir} (${\n e instanceof Error ? e.message : String(e)\n }). Continuing without caching.`,\n );\n }\n }\n }\n}\n","/**\n * Curated GGUF models for the in-process `llama-cpp` provider, plus the\n * selector-to-model resolution that sits in front of them.\n *\n * Every entry is an unsloth Gemma 4 QAT build. One family keeps the chat\n * template identical across tiers, and QAT (quantization-aware training) is\n * built for 4-bit deployment, so it beats a standard Q4 quant of the same\n * model at a smaller file size (E4B: QAT UD-Q4_K_XL 4.22 GB vs stock\n * Q4_K_M 4.98 GB). Gemma 4's IFEval scores — 94.6 / 96.7 / 97.2 across the\n * three tiers — are what actually predicts schema-conforming output.\n *\n * Entries pin an exact blob path rather than a `:QUANT` tag. That is a hard\n * requirement, not a style preference: the QAT repos ship NO Q4_K_M — only\n * UD-Q4_K_XL and UD-Q2_K_XL — so a tag would fail to resolve outright. They\n * also carry `mmproj-*.gguf` (the ~1 GB vision projector; Gemma 4 is natively\n * multimodal) and `mtp-*.gguf` next to the weights, which text-only judging\n * must not download. A pinned path also cannot silently re-point underneath a\n * cache key that already claims it.\n */\nimport { readdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { InferenceError } from \"../types.js\";\n\n/**\n * Where this library downloads weights — its OWN directory, not\n * node-llama-cpp's global `~/.node-llama-cpp/models`.\n *\n * That default is shared: node-llama-cpp's CLI writes there, as does anything\n * else on the machine using it. Owning a directory outright means clearing it\n * can never destroy a model this library did not download, and it keeps one\n * copy shared across every consumer of this package on the machine.\n *\n * `INFERENCE_MODELS_DIR` overrides it — useful for CI or a shared volume.\n */\nexport function defaultLlamaModelsDirectory(): string {\n return (\n process.env[\"INFERENCE_MODELS_DIR\"] ||\n join(homedir(), \".hawkeyexl-inference\", \"models\")\n );\n}\n\n/** Size tiers, smallest first. Order is load-bearing for `tierForBudget`. */\nexport const LLAMA_TIERS = [\"fast\", \"balanced\", \"quality\"] as const;\nexport type LlamaTier = (typeof LLAMA_TIERS)[number];\n\n/** Model selectors — resolved against hardware, never used as a cache key. */\nexport const LLAMA_SELECTORS = [\"auto\", ...LLAMA_TIERS] as const;\nexport type LlamaSelector = (typeof LLAMA_SELECTORS)[number];\n\nexport interface LlamaModelEntry {\n /** `hf:` URI pinned to one blob, handed to `resolveModelFile` as-is. */\n readonly uri: string;\n /** Size of that blob in bytes, as reported by the Hugging Face API. */\n readonly sizeBytes: number;\n readonly license: string;\n /** Absent for entries that are selectable by alias but not by tier. */\n readonly tier?: LlamaTier;\n /** Human note for `LLAMA_MODELS` readers deciding what to download. */\n readonly notes: string;\n}\n\n/**\n * Frozen per entry, not just at the top level.\n *\n * A shallow freeze leaves the entries writable, and this catalog is exported\n * for consumers to read: a stray write to `sizeBytes` silently re-points\n * `tierForBudget` process-wide, and a write to `uri` defeats the pinned-blob\n * invariant the whole catalog exists to hold (ADR 01003).\n */\nexport const LLAMA_MODELS: Readonly<Record<string, LlamaModelEntry>> =\n deepFreezeEntries({\n \"gemma-4-e2b\": {\n uri: \"hf:unsloth/gemma-4-E2B-it-qat-GGUF/gemma-4-E2B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 2_620_370_976,\n license: \"Apache-2.0\",\n tier: \"fast\",\n notes: \"IFEval 94.6. Smallest Gemma 4; the floor for this family.\",\n },\n \"gemma-4-e4b\": {\n uri: \"hf:unsloth/gemma-4-E4B-it-qat-GGUF/gemma-4-E4B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 4_215_695_776,\n license: \"Apache-2.0\",\n tier: \"balanced\",\n notes: \"IFEval 96.7. The default for most machines.\",\n },\n \"gemma-4-12b\": {\n uri: \"hf:unsloth/gemma-4-12B-it-qat-GGUF/gemma-4-12B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 6_716_356_800,\n license: \"Apache-2.0\",\n tier: \"quality\",\n notes: \"IFEval 97.2. Dense 12B; wants a GPU or plenty of RAM.\",\n },\n \"gemma-4-26b-a4b\": {\n uri: \"hf:unsloth/gemma-4-26B-A4B-it-qat-GGUF/gemma-4-26B-A4B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 14_249_047_104,\n license: \"Apache-2.0\",\n notes:\n \"MoE: 25.2B total, 3.8B active — infers near E4B speed if it fits in memory.\",\n },\n \"gemma-4-e2b-q2\": {\n uri: \"hf:unsloth/gemma-4-E2B-it-qat-GGUF/gemma-4-E2B-it-qat-UD-Q2_K_XL.gguf\",\n sizeBytes: 2_186_186_784,\n license: \"Apache-2.0\",\n notes: \"Q2 build of the fast tier; smallest download, lowest fidelity.\",\n },\n });\n\nfunction deepFreezeEntries<T extends Record<string, object>>(\n catalog: T,\n): Readonly<T> {\n for (const entry of Object.values(catalog)) Object.freeze(entry);\n return Object.freeze(catalog);\n}\n\n/** The alias backing each tier, used by `auto` and the tier keywords. */\nconst TIER_ALIAS: Record<LlamaTier, string> = {\n fast: \"gemma-4-e2b\",\n balanced: \"gemma-4-e4b\",\n quality: \"gemma-4-12b\",\n};\n\nexport function isLlamaSelector(model: string): model is LlamaSelector {\n return (LLAMA_SELECTORS as readonly string[]).includes(model);\n}\n\n/**\n * Weights are only part of the cost — the KV cache at a real context length,\n * the OS, and whatever else the machine is doing all want memory too. Requiring\n * several times the file size keeps `auto` from picking a model that technically\n * loads and then thrashes.\n */\nconst MEMORY_HEADROOM = 3.5;\n\n/**\n * Largest tier whose weights fit the memory budget with headroom. Lands at\n * roughly: >=24 GB -> quality, >=15 GB -> balanced, else fast.\n *\n * Sized off the catalog's recorded bytes rather than parameter counts: Gemma\n * 4's E-series are per-layer-embedding models whose footprint does not track\n * \"effective params\" (E4B is 4.5B effective but 15 GB at BF16).\n */\nexport function tierForBudget(budgetBytes: number): LlamaTier {\n let chosen: LlamaTier = \"fast\";\n for (const tier of LLAMA_TIERS) {\n const entry = LLAMA_MODELS[TIER_ALIAS[tier]]!;\n if (entry.sizeBytes * MEMORY_HEADROOM <= budgetBytes) chosen = tier;\n }\n // Falls back to the smallest tier rather than refusing: a machine too small\n // for `fast` will thrash, but that is the caller's call to make, not ours.\n return chosen;\n}\n\n/**\n * Catalog alias backing a tier. Selectors resolve to this rather than to a raw\n * URI so the identity — and therefore the cache key — stays human-readable.\n */\nexport function aliasForTier(tier: LlamaTier): string {\n return TIER_ALIAS[tier];\n}\n\n/** The pinned URI backing a tier keyword. */\nexport function uriForTier(tier: LlamaTier): string {\n return LLAMA_MODELS[TIER_ALIAS[tier]]!.uri;\n}\n\n/**\n * Turn a concrete model reference — a curated alias, an `hf:` URI, or a local\n * path — into something `resolveModelFile` accepts.\n *\n * Selectors are rejected rather than guessed at: they need a hardware probe,\n * which is async, and this runs on the synchronous cache-key path. Resolving\n * one here from RAM alone would emit a key naming a model the provider then\n * did not load.\n */\nexport function resolveLlamaModelRef(model: string): string {\n if (isLlamaSelector(model)) {\n throw new InferenceError(\n `llama-cpp model \"${model}\" is a selector and needs a hardware probe to ` +\n `resolve. Use resolveProviderIdentityAsync/makeProviderAsync, or name a ` +\n `concrete model (e.g. \"gemma-4-e4b\").`,\n );\n }\n const entry = LLAMA_MODELS[model];\n if (entry) return entry.uri;\n if (isModelPathOrUri(model)) return model;\n throw new InferenceError(\n `Unknown llama-cpp model \"${model}\". Use a selector (${LLAMA_SELECTORS.join(\n \", \",\n )}), a curated alias (${Object.keys(LLAMA_MODELS).join(\n \", \",\n )}), an hf: URI, or a path to a .gguf file.`,\n );\n}\n\n/**\n * The catalog's pinned filename for a model reference.\n *\n * Strips a `#branch` fragment (node-llama-cpp accepts\n * `hf:user/repo/file.gguf#branch`) and splits on both separators, so a Windows\n * path resolves too. Getting either wrong makes callers silently match nothing.\n */\nexport function blobNameFor(model: string): string {\n const ref = resolveLlamaModelRef(model).split(\"#\")[0]!;\n return ref.split(/[/\\\\]/).pop()!;\n}\n\n/**\n * Does an on-disk entry belong to this model?\n *\n * node-llama-cpp prefixes downloads with `hf_<user>_`, so match by SUFFIX\n * rather than equality — that survives a change to their naming scheme.\n */\nexport function matchesModelBlob(entry: string, blobName: string): boolean {\n const base = entry.replace(/\\.ipull$/, \"\");\n if (base.endsWith(blobName)) return true;\n // Split models land as `<stem>-00001-of-00003.gguf`; every part belongs to\n // the same model, so matching the stem covers the whole set.\n const stem = blobName.replace(/\\.gguf$/, \"\");\n return new RegExp(`${escapeRegExp(stem)}-\\\\d{5}-of-\\\\d{5}\\\\.gguf$`).test(base);\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Are this model's weights already on disk?\n *\n * A `.ipull` partial counts as NOT downloaded: it cannot be loaded, so\n * treating it as present would skip the download warning and then stall on a\n * download anyway.\n */\nexport function isModelDownloaded(model: string, directory: string): boolean {\n const blobName = blobNameFor(model);\n return listModelDirectory(directory).some(\n (entry) => !entry.endsWith(\".ipull\") && matchesModelBlob(entry, blobName),\n );\n}\n\n/** Top level only — a nested directory is not ours to walk. */\nexport function listModelDirectory(directory: string): string[] {\n try {\n return readdirSync(directory, { withFileTypes: true })\n .filter((entry) => entry.isFile())\n .map((entry) => entry.name);\n } catch {\n // No directory means nothing was ever downloaded — not an error.\n return [];\n }\n}\n\n/**\n * A bare unknown word is a typo'd alias, not a model — catch it early rather\n * than letting it reach the downloader as a doomed repo name.\n *\n * Accepts exactly what the error message in `resolveLlamaModelRef` promises: a\n * recognised URI scheme, or something that names a `.gguf` file. A bare\n * `user/repo` is deliberately NOT a model reference — it would otherwise slip\n * past this guard and fail deep inside the downloader with a far worse\n * message. Any real path to weights ends in `.gguf`, on every platform.\n */\nfunction isModelPathOrUri(model: string): boolean {\n return (\n /^(hf|huggingface):/i.test(model) ||\n /^https?:\\/\\//i.test(model) ||\n /^(hf|huggingface)\\.co\\//i.test(model) ||\n model.endsWith(\".gguf\")\n );\n}\n","/**\n * Installing the optional `node-llama-cpp` peer on demand.\n *\n * Detection ends at `llama-cpp` precisely because it needs no key and no\n * account — but that rung is only reachable if the native binding is present,\n * and npm does not install optional peers. Without this module a machine with\n * no credentials and no binding falls off the end of the chain, which is the\n * one case auto-detection exists to serve.\n *\n * The install goes into a directory this library OWNS — never the consumer's\n * `node_modules`, `package.json` or lockfile. That is the same reasoning ADR\n * 01003 applied to weights: owning a directory removes the hazard instead of\n * defending against it. A consumer's dependency manifest is theirs, and a\n * library that edits it has broken reproducible installs for everyone\n * downstream.\n */\nimport {\n existsSync,\n mkdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { InferenceError } from \"../types.js\";\nimport { realExec } from \"../exec.js\";\nimport type { ExecFn } from \"./types.js\";\n\n/** Pinned to the peer range: below 3.19.0 nothing in the catalog loads. */\nconst PACKAGE_SPEC = \"node-llama-cpp@^3.19.0\";\n\n/**\n * The shim doubles as the readiness marker — it is written only after npm\n * exits 0, so its presence means \"this prefix is complete\".\n */\nconst SHIM = \"loader.mjs\";\nconst LOCK = \".install.lock\";\n\n/**\n * Generous by necessity. Prebuilt binaries cover win32-x64, darwin-arm64 and\n * linux-x64; anything else falls back to a CMake build that genuinely takes\n * minutes. `realExec` defaults to 60s, which would kill it half-built.\n */\nconst INSTALL_TIMEOUT_MS = 900_000;\n\n/** How long to wait for another process's install before giving up. */\nconst LOCK_WAIT_MS = INSTALL_TIMEOUT_MS;\n/** A lock older than this belonged to a process that died holding it. */\nconst LOCK_STALE_MS = INSTALL_TIMEOUT_MS + 60_000;\n\nexport interface RuntimeInstallOptions {\n /** Defaults to this library's own runtime directory. */\n directory?: string;\n /** Injected for tests; defaults to the real process runner. */\n exec?: ExecFn;\n /** Injected for tests; defaults to `process.env`. */\n env?: Record<string, string | undefined>;\n timeoutMs?: number;\n /** Test seam: how the shim is imported once it exists. */\n importShim?: (url: string) => Promise<unknown>;\n /**\n * Test seam: how the consumer's own copy is looked for.\n *\n * Whether an optional peer is installed is a property of the machine, and the\n * behaviour that matters most here — that probing installs nothing — is\n * unobservable on a machine that already has it. Injecting the lookup is the\n * only way to assert it deterministically.\n */\n probeImport?: () => Promise<unknown>;\n}\n\n/**\n * Where this library installs the binding — its OWN directory, beside the\n * models directory and for the same reason.\n *\n * `INFERENCE_RUNTIME_DIR` overrides it, mirroring `INFERENCE_MODELS_DIR`.\n */\nexport function defaultLlamaRuntimeDirectory(\n env: Record<string, string | undefined> = process.env,\n): string {\n return (\n env[\"INFERENCE_RUNTIME_DIR\"] ||\n join(homedir(), \".hawkeyexl-inference\", \"runtime\")\n );\n}\n\n/**\n * Is this import failure \"the package is not here\", as opposed to \"the package\n * is here and broken\"?\n *\n * The distinction decides whether installing can possibly help. `ERR_MODULE_NOT_FOUND`\n * is what Node raises for a missing bare specifier; `MODULE_NOT_FOUND` is its\n * CJS spelling. Anything else — a failed `dlopen`, an unsupported Node, a\n * package whose `exports` do not match — means the package resolved and then\n * failed, and no amount of reinstalling changes that.\n */\nexport function isModuleNotFound(e: unknown): boolean {\n const code = (e as NodeJS.ErrnoException | undefined)?.code;\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n}\n\nfunction describe(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n/** Whether the binding can be used, and at what cost, WITHOUT installing it. */\nexport type RuntimeStatus =\n /** Importable right now — either the consumer's own copy or a filled prefix. */\n | { state: \"present\" }\n /** Absent, but an install is permitted. Using it will fetch a native module. */\n | { state: \"installable\"; directory: string }\n /** Absent and installing is refused, so this provider cannot be used. */\n | { state: \"refused\"; reason: string };\n\n/**\n * Can the local runtime be used, and would using it install anything?\n *\n * Detection needs this rather than simply calling `getMemoryBudgetBytes`:\n * that goes through `importNodeLlamaCpp` and would install, which turns\n * `availableProviders()` — a function whose entire job is to REPORT what is\n * usable — into one that changes what is usable. Probing must not have the side\n * effect it is probing for.\n */\nexport async function nodeLlamaCppStatus(\n options: RuntimeInstallOptions = {},\n): Promise<RuntimeStatus> {\n const env = options.env ?? process.env;\n const directory = options.directory ?? defaultLlamaRuntimeDirectory(env);\n\n const probeImport =\n options.probeImport ?? ((): Promise<unknown> => import(\"node-llama-cpp\"));\n try {\n await probeImport();\n return { state: \"present\" };\n } catch (e) {\n // Only a genuine \"no such package\" means absent. An ABI mismatch, a missing\n // system library, or an unsupported Node all fail here too, and installing\n // over them would fetch the same broken package again while burying the\n // real cause under a download.\n if (!isModuleNotFound(e)) {\n return {\n state: \"refused\",\n reason: `node-llama-cpp is installed but failed to load (${describe(e)})`,\n };\n }\n // Genuinely absent — fall through to our own prefix.\n }\n if (existsSync(join(directory, SHIM))) return { state: \"present\" };\n\n if ((env[\"INFERENCE_NO_AUTO_INSTALL\"] ?? \"\") !== \"\") {\n return {\n state: \"refused\",\n reason: `node-llama-cpp is not installed and INFERENCE_NO_AUTO_INSTALL is set`,\n };\n }\n return { state: \"installable\", directory };\n}\n\n/**\n * In-flight installs, keyed by directory. docmeta and the ensemble runner both\n * work several items at once, so without this every worker that misses the\n * binding would spawn its own npm against one prefix.\n */\nconst installs = new Map<string, Promise<unknown>>();\n\nlet warnedInstall = false;\n\n/** Test seam: forget in-flight installs and re-arm the one-time warning. */\nexport function resetRuntimeInstall(): void {\n installs.clear();\n warnedInstall = false;\n}\n\n/**\n * Import `node-llama-cpp` from the library-owned prefix, installing it first if\n * it is not there.\n *\n * Callers reach this only after a plain `import(\"node-llama-cpp\")` has already\n * failed — a consumer who installed the peer themselves never gets here.\n */\nexport function importNodeLlamaCpp(\n options: RuntimeInstallOptions = {},\n): Promise<unknown> {\n const env = options.env ?? process.env;\n const directory = options.directory ?? defaultLlamaRuntimeDirectory(env);\n\n const existing = installs.get(directory);\n if (existing) return existing;\n\n const pending = fromPrefix(directory, env, options);\n // Drop a failed attempt so the next call retries: a download killed by a\n // flaky network must not poison the runtime for the rest of the process —\n // the same rule `load()` applies to weights.\n const guarded = pending.catch((e: unknown) => {\n if (installs.get(directory) === guarded) installs.delete(directory);\n throw e;\n });\n installs.set(directory, guarded);\n return guarded;\n}\n\nasync function fromPrefix(\n directory: string,\n env: Record<string, string | undefined>,\n options: RuntimeInstallOptions,\n): Promise<unknown> {\n const importShim =\n options.importShim ?? ((url: string): Promise<unknown> => import(url));\n const shim = join(directory, SHIM);\n\n if (existsSync(shim)) return importShim(pathToFileURL(shim).href);\n\n if ((env[\"INFERENCE_NO_AUTO_INSTALL\"] ?? \"\") !== \"\") {\n throw new InferenceError(\n `The llama-cpp provider needs node-llama-cpp, and INFERENCE_NO_AUTO_INSTALL ` +\n `is set. Install it yourself (npm i ${PACKAGE_SPEC}), unset ` +\n `INFERENCE_NO_AUTO_INSTALL to allow installing into ${directory}, or name ` +\n `a different provider.`,\n );\n }\n\n mkdirSync(directory, { recursive: true });\n await withLock(directory, async () => {\n // Another process may have finished while we waited for the lock.\n if (existsSync(shim)) return;\n warnInstalling(directory);\n await runInstall(directory, env, options);\n // Only now is the prefix complete, so only now does it get its marker.\n writeFileSync(shim, `export * from \"node-llama-cpp\";\\n`, \"utf8\");\n });\n\n return importShim(pathToFileURL(shim).href);\n}\n\nasync function runInstall(\n directory: string,\n env: Record<string, string | undefined>,\n options: RuntimeInstallOptions,\n): Promise<void> {\n // Anchor npm to this directory. Without a manifest here npm walks UP looking\n // for one, and would install into whatever project happens to be above us —\n // the exact mutation this module exists to avoid.\n const manifest = join(directory, \"package.json\");\n if (!existsSync(manifest)) {\n writeFileSync(\n manifest,\n `${JSON.stringify(\n {\n name: \"hawkeyexl-inference-runtime\",\n version: \"0.0.0\",\n private: true,\n description:\n \"Auto-installed runtime for @hawkeyexl/inference. Safe to delete.\",\n },\n null,\n 2,\n )}\\n`,\n \"utf8\",\n );\n }\n\n const exec = options.exec ?? realExec;\n const result = await exec(\n [\n \"npm\",\n \"install\",\n \"--prefix\",\n directory,\n PACKAGE_SPEC,\n \"--no-audit\",\n \"--no-fund\",\n ],\n { timeoutMs: options.timeoutMs ?? INSTALL_TIMEOUT_MS, env },\n );\n\n if (result.spawnError != null) {\n throw new InferenceError(\n `Could not run npm to install node-llama-cpp (${result.spawnError}). ` +\n `Install it yourself with: npm i ${PACKAGE_SPEC}, or name a provider ` +\n `that does not need it.`,\n );\n }\n if (result.timedOut) {\n throw new InferenceError(\n `Installing node-llama-cpp into ${directory} timed out. A source build ` +\n `can take a while — retry, raise the timeout, or install it yourself ` +\n `with: npm i ${PACKAGE_SPEC}.`,\n );\n }\n if (result.code !== 0) {\n throw new InferenceError(\n `Installing node-llama-cpp into ${directory} failed (exit ${String(\n result.code,\n )}).\\n${tail(result.stderr || result.stdout)}\\n` +\n `Install it yourself with: npm i ${PACKAGE_SPEC}, or name a provider ` +\n `that does not need it.`,\n );\n }\n}\n\n/** Enough npm output to diagnose the failure, not enough to bury the advice. */\nfunction tail(output: string, lines = 12): string {\n return output.trimEnd().split(/\\r?\\n/).slice(-lines).join(\"\\n\");\n}\n\n/**\n * Pulling a native module is not free, and the weights that follow are far less\n * free. Say so before it starts, not after a build has already stalled on it.\n */\nfunction warnInstalling(directory: string): void {\n if (warnedInstall) return;\n warnedInstall = true;\n console.warn(\n `inference: node-llama-cpp is not installed — fetching it into ${directory} ` +\n `so the local model can run. This is a one-time native install; set ` +\n `INFERENCE_NO_AUTO_INSTALL=1 to refuse it, or name a provider that does ` +\n `not need it.`,\n );\n}\n\n/**\n * Cross-process guard around one prefix.\n *\n * The in-process memo covers a worker pool inside one run; this covers two runs\n * started at once, where two npm processes writing one `node_modules` is how a\n * prefix ends up half-written.\n */\nasync function withLock(\n directory: string,\n fn: () => Promise<void>,\n): Promise<void> {\n const lock = join(directory, LOCK);\n const deadline = Date.now() + LOCK_WAIT_MS;\n\n for (;;) {\n try {\n writeFileSync(lock, String(process.pid), { flag: \"wx\" });\n break;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== \"EEXIST\") throw e;\n if (ageOf(lock) > LOCK_STALE_MS) {\n // Whoever held this died. Reclaiming a stale lock is safer than\n // blocking forever on a process that will never return.\n rmSync(lock, { force: true });\n continue;\n }\n if (existsSync(join(directory, SHIM))) return;\n if (Date.now() > deadline) {\n throw new InferenceError(\n `Timed out waiting for another process to install node-llama-cpp into ` +\n `${directory}. If nothing else is running, remove ${lock} and retry.`,\n );\n }\n await delay(250);\n }\n }\n\n try {\n await fn();\n } finally {\n rmSync(lock, { force: true });\n }\n}\n\n/** Infinity for a lock that vanished mid-check, so the caller retries. */\nfunction ageOf(path: string): number {\n try {\n return Date.now() - statSync(path).mtimeMs;\n } catch {\n return Number.POSITIVE_INFINITY;\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * In-process local inference over GGUF weights via `node-llama-cpp`.\n *\n * Unlike every other provider here, this one owns weights: it downloads them\n * from Hugging Face on first use and holds gigabytes of RAM once loaded. Two\n * consequences shape the design.\n *\n * First, `node-llama-cpp` is a native module with prebuilt binaries per\n * platform and a CMake fallback. It is an OPTIONAL peer dependency reached\n * through a dynamic `import()`, so the four repos consuming this library pay\n * nothing — install cost or toolchain risk — unless they ask for local models.\n *\n * Second, everything real happens behind `LlamaRuntime`. Tests inject a fake\n * and never touch the network, the filesystem, or a GPU (the same seam as\n * `ExecFn` for the Claude CLI provider).\n */\nimport { InferenceError } from \"../types.js\";\nimport { buildCacheKey } from \"../cache.js\";\nimport { extractJson } from \"./openai-compat.js\";\nimport {\n defaultLlamaModelsDirectory,\n isLlamaSelector,\n resolveLlamaModelRef,\n} from \"./llama-models.js\";\nimport { importNodeLlamaCpp, isModuleNotFound } from \"./llama-install.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n TokenUsage,\n} from \"./types.js\";\n\nexport interface LlamaPromptOptions {\n /** JSON Schema converted to a GBNF grammar by the runtime. */\n schema: Record<string, unknown>;\n temperature: number;\n /** Thinking budget; 0 disables it. See the note in `completeJSON`. */\n thoughtTokens: number;\n maxTokens?: number;\n}\n\nexport interface LlamaPromptResult {\n text: string;\n usage?: TokenUsage;\n /**\n * Why generation stopped. `\"maxTokens\"` means the output was cut off, so the\n * text is almost certainly truncated JSON — see the guard in `completeJSON`.\n */\n stopReason?: string;\n}\n\nexport interface LlamaSession {\n prompt(text: string, options: LlamaPromptOptions): Promise<LlamaPromptResult>;\n dispose(): Promise<void>;\n}\n\nexport interface LlamaLoadedModel {\n createSession(systemPrompt: string): Promise<LlamaSession>;\n dispose(): Promise<void>;\n}\n\n/**\n * The whole of `node-llama-cpp` that this provider uses. Kept this narrow so a\n * test fake is a few lines and so the real adapter is the only place that\n * knows the upstream API shape.\n */\nexport interface LlamaRuntime {\n /**\n * Resolve an `hf:` URI or path to a local file inside `directory`,\n * downloading if needed.\n */\n resolveModelFile(uri: string, directory: string): Promise<string>;\n loadModel(path: string): Promise<LlamaLoadedModel>;\n /** Memory available for weights, in bytes — VRAM if there is a GPU, else RAM. */\n getMemoryBudgetBytes(): Promise<number>;\n}\n\nexport interface LlamaCppProviderOptions {\n /** Injected for tests; defaults to the real `node-llama-cpp` adapter. */\n runtime?: LlamaRuntime;\n /**\n * Thinking budget in tokens, default 0.\n *\n * Gemma 4 has a thinking mode, but a grammar constrains generation from\n * token 0 — so an unbudgeted model starts reasoning and gets cut off\n * mid-thought. Zero is the deterministic choice for judging; raise it if you\n * want reasoning before the JSON.\n */\n thoughtTokens?: number;\n maxTokens?: number;\n /**\n * Where to download and look for weights. Defaults to this library's own\n * directory — see `defaultLlamaModelsDirectory`.\n */\n modelsDirectory?: string;\n}\n\n/**\n * Loaded weights, keyed by directory + URI.\n *\n * `runEnsemble` issues N sequential calls and a load costs seconds and\n * gigabytes, so this is process-wide rather than per-instance: two providers\n * naming the same model share one copy. Values are the in-flight promise so\n * concurrent first calls coalesce instead of loading twice. The directory is\n * part of the key because the same URI in two directories is two files.\n */\nconst loadedModels = new Map<string, Promise<LlamaLoadedModel>>();\n\n/**\n * Free every loaded model.\n *\n * A standalone function rather than a `dispose()` on `InferenceProvider`:\n * adding one to the contract would make all five providers carry a lifecycle\n * only this one has. Short-lived processes can skip it.\n */\nexport async function disposeLlamaModels(): Promise<void> {\n const pending = [...loadedModels.values()];\n loadedModels.clear();\n await Promise.all(\n pending.map((p) => p.then((m) => m.dispose()).catch(() => undefined)),\n );\n}\n\nexport class LlamaCppProvider implements InferenceProvider {\n private readonly uri: string;\n private readonly runtime: LlamaRuntime;\n private readonly thoughtTokens: number;\n private readonly maxTokens: number | undefined;\n private readonly modelsDirectory: string;\n /**\n * Loaded-model key: the same URI in two directories is two different files.\n * Built with `buildCacheKey` so its parts are length-prefixed — a plain join\n * would let two different (directory, uri) pairs collide and hand a provider\n * back the wrong weights.\n */\n private readonly cacheKey: string;\n\n constructor(\n private readonly model: string,\n options: LlamaCppProviderOptions = {},\n ) {\n if (isLlamaSelector(model)) {\n throw new InferenceError(\n `llama-cpp model \"${model}\" is a selector. Constructing a provider ` +\n `directly needs a concrete model (e.g. \"gemma-4-e4b\") — use ` +\n `makeProviderAsync to resolve a selector against this machine.`,\n );\n }\n this.uri = resolveLlamaModelRef(model);\n this.runtime = options.runtime ?? defaultLlamaRuntime();\n this.thoughtTokens = options.thoughtTokens ?? 0;\n this.maxTokens = options.maxTokens;\n this.modelsDirectory =\n options.modelsDirectory ?? defaultLlamaModelsDirectory();\n this.cacheKey = buildCacheKey([this.modelsDirectory, this.uri]);\n }\n\n provider(): string {\n return \"llama-cpp\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const model = await this.load();\n // A fresh session per call: the contract is single-shot, and reusing one\n // would leak the previous run's turns into this one's context.\n const session = await model.createSession(systemPromptFor(req));\n try {\n const result = await session.prompt(req.user, {\n schema: req.schema,\n temperature: req.temperature,\n thoughtTokens: this.thoughtTokens,\n ...(this.maxTokens != null ? { maxTokens: this.maxTokens } : {}),\n });\n // A run cut off at the token or context limit leaves truncated JSON.\n // Without this it surfaces as \"failed schema validation\" — or worse,\n // extractJson's brace-slicing fallback salvages a wrong-but-parseable\n // object — and the retry burns another full local inference to fail the\n // same way. Same guard as the Anthropic provider's max_tokens check.\n if (result.stopReason === \"maxTokens\") {\n throw new Error(\n `llama-cpp generation hit the token limit before completing the JSON` +\n `${this.maxTokens != null ? ` (maxTokens: ${this.maxTokens})` : \"\"}` +\n ` — raise llamaCpp.maxTokens, or shorten the prompt if the context is full.`,\n );\n }\n return { json: extractJson(result.text), usage: result.usage };\n } finally {\n await session.dispose().catch(() => undefined);\n }\n }\n\n private load(): Promise<LlamaLoadedModel> {\n const existing = loadedModels.get(this.cacheKey);\n if (existing) return existing;\n const pending = (async () => {\n const path = await this.runtime.resolveModelFile(\n this.uri,\n this.modelsDirectory,\n );\n return this.runtime.loadModel(path);\n })();\n // Drop a failed load so the next call retries — a download interrupted by\n // a flaky network must not poison the model for the rest of the process.\n // Only evict our OWN entry: a dispose plus a re-load between the failure\n // and this handler would otherwise orphan the newer model, leaking it.\n const guarded = pending.catch((e: unknown) => {\n if (loadedModels.get(this.cacheKey) === guarded) {\n loadedModels.delete(this.cacheKey);\n }\n throw e;\n });\n loadedModels.set(this.cacheKey, guarded);\n return guarded;\n }\n}\n\n/**\n * The grammar constrains the SHAPE of the output, but `node-llama-cpp` never\n * shows the schema to the model — so `description` fields, which is where\n * consumers put their domain instructions (ADR 01001), would be invisible.\n * Restating the schema is the same fix the Claude CLI provider and the\n * OpenAI json_object fallback already use.\n */\nfunction systemPromptFor(req: CompleteJSONRequest): string {\n return `${req.system}\\n\\nRespond with ONLY a JSON object conforming to this JSON Schema:\\n${JSON.stringify(\n req.schema,\n )}`;\n}\n\n/** Cached so repeated provider construction imports the native module once. */\nlet runtimePromise: Promise<LlamaRuntime> | undefined;\n\n/**\n * Lazy adapter over the real `node-llama-cpp`. Every method defers to the\n * dynamic import, so constructing a provider for a fully-cached run never\n * loads the native binary.\n */\nexport function defaultLlamaRuntime(): LlamaRuntime {\n const real = (): Promise<LlamaRuntime> =>\n // Drop a failed init so the next call retries. A GPU that failed to\n // initialise, or a binary still being extracted by a concurrent install,\n // must not poison the runtime for the rest of the process — the same rule\n // `load()` applies to weights.\n (runtimePromise ??= loadNodeLlamaCpp().catch((e: unknown) => {\n runtimePromise = undefined;\n throw e;\n }));\n return {\n resolveModelFile: (uri, directory) =>\n real().then((r) => r.resolveModelFile(uri, directory)),\n loadModel: (path) => real().then((r) => r.loadModel(path)),\n getMemoryBudgetBytes: () => real().then((r) => r.getMemoryBudgetBytes()),\n };\n}\n\nasync function loadNodeLlamaCpp(): Promise<LlamaRuntime> {\n let mod: typeof import(\"node-llama-cpp\");\n try {\n mod = await import(\"node-llama-cpp\");\n } catch (e) {\n // A package that resolved and then failed to load — ABI mismatch, missing\n // system library, unsupported Node — is not a missing package. Installing\n // over it would fetch the same broken thing again and replace a precise\n // error with a download.\n if (!isModuleNotFound(e)) {\n throw new InferenceError(\n `node-llama-cpp is installed but failed to load (${\n e instanceof Error ? e.message : String(e)\n }). This is the copy resolved from your own node_modules, so ` +\n `reinstalling it here will not help — check the Node version and the ` +\n `platform build.`,\n );\n }\n // Genuinely absent, so fall back to the library's own prefix — installing\n // it there if needed. npm does not install optional peers, and detection\n // ends at this provider precisely because it needs no credentials, so\n // refusing here would strand the one machine `auto` exists to serve.\n // Resetting `runtimePromise` is the caller's job — see `defaultLlamaRuntime`.\n mod = (await importNodeLlamaCpp()) as typeof import(\"node-llama-cpp\");\n }\n\n const { getLlama, resolveModelFile, LlamaChatSession, TokenMeter } = mod;\n const llama = await getLlama();\n\n return {\n // `directory` is this library's own, not node-llama-cpp's global default —\n // owning it is what makes `clearLlamaModels` safe.\n resolveModelFile: (uri, directory) => resolveModelFile(uri, { directory }),\n\n async loadModel(path) {\n const model = await llama.loadModel({ modelPath: path });\n return {\n async createSession(systemPrompt) {\n const context = await model.createContext();\n const sequence = context.getSequence();\n const session = new LlamaChatSession({\n contextSequence: sequence,\n systemPrompt,\n });\n return {\n async prompt(text, options) {\n const grammar = await llama.createGrammarForJsonSchema(\n options.schema as Parameters<\n typeof llama.createGrammarForJsonSchema\n >[0],\n );\n const before = sequence.tokenMeter.getState();\n const result = await session.promptWithMeta(text, {\n grammar,\n temperature: options.temperature,\n budgets: { thoughtTokens: options.thoughtTokens },\n ...(options.maxTokens != null\n ? { maxTokens: options.maxTokens }\n : {}),\n });\n // promptWithMeta does not report usage; the sequence's meter does.\n const diff = TokenMeter.diff(sequence.tokenMeter, before);\n return {\n text: result.responseText,\n stopReason: result.stopReason,\n usage: {\n inputTokens: diff.usedInputTokens,\n outputTokens: diff.usedOutputTokens,\n },\n };\n },\n async dispose() {\n await context.dispose();\n },\n };\n },\n async dispose() {\n await model.dispose();\n },\n };\n },\n\n async getMemoryBudgetBytes() {\n const { totalmem } = await import(\"node:os\");\n // Half of RAM is what a judge can reasonably claim on a shared machine;\n // a GPU's free VRAM is usable outright.\n const ramBudget = totalmem() / 2;\n try {\n const vram = await llama.getVramState();\n // The LARGER of the two, not VRAM in preference to RAM: llama.cpp\n // offloads the layers that fit onto the GPU and keeps the rest in\n // system RAM, so a small GPU beside plenty of RAM still runs a big\n // model. Sizing off VRAM alone would idle most of such a machine.\n return Math.max(vram.free, ramBudget);\n } catch {\n // CPU-only builds and probe failures are normal, never fatal.\n return ramBudget;\n }\n },\n };\n}\n","/**\n * Which providers can this machine actually use, and which should it pick?\n *\n * Every probe goes through a seam that already exists — environment variables,\n * `ExecFn`, `LlamaRuntime` — so the whole matrix is exercisable offline with no\n * network, no subprocess, and no weights.\n *\n * Detection is async because two of the four probes are: running the Claude CLI\n * and loading the optional `node-llama-cpp` binding. That is why only\n * `resolveProviderIdentityAsync`/`makeProviderAsync` can resolve an `auto`\n * provider, and the synchronous twins throw instead of guessing.\n */\nimport { InferenceError } from \"../types.js\";\nimport { realExec } from \"../exec.js\";\nimport { defaultLlamaRuntime } from \"./llama-cpp.js\";\nimport { nodeLlamaCppStatus } from \"./llama-install.js\";\nimport type { LlamaRuntime } from \"./llama-cpp.js\";\nimport type { ProviderName, ProviderSpec } from \"./index.js\";\n\n/**\n * Priority order. `mock` is deliberately absent: it answers `{ json: {} }`\n * unless scripted, which would sail through as a non-error result — the exact\n * opposite of the \"an errored run is recorded, never coerced\" invariant the\n * consuming eval tools depend on. It must always be asked for by name.\n */\nexport const DETECTION_ORDER: readonly ProviderName[] = [\n \"anthropic\",\n \"openai\",\n \"claude-cli\",\n \"llama-cpp\",\n];\n\nconst DEFAULT_KEY_ENV: Partial<Record<ProviderName, string>> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n};\n\ninterface Probe {\n available: boolean;\n /** Why not, phrased as advice — this is what the aggregate error prints. */\n reason?: string;\n}\n\n/**\n * Probes the provider's DEFAULT key variable, deliberately ignoring\n * `spec.apiKeyEnv`.\n *\n * `apiKeyEnv` is one field shared by both API providers, and detection only\n * runs when no provider was named — so a custom name cannot say which provider\n * it belongs to. Honouring it here made a single custom variable satisfy both\n * probes, and anthropic then won on priority: a spec carrying an OpenAI key\n * under a custom name selected `anthropic` and 401'd at call time.\n *\n * A custom `apiKeyEnv` still applies in full once a provider is named — it just\n * cannot be what chooses one.\n */\nfunction hasKey(provider: \"anthropic\" | \"openai\"): boolean {\n // An empty string is not a key; treating it as one produces a 401 later.\n return (process.env[DEFAULT_KEY_ENV[provider]!] ?? \"\") !== \"\";\n}\n\n/**\n * Memoised **per command**: spawning a process costs ~150ms and detection may\n * run on every provider construction, but a spec naming a different executable\n * is a different question — memoising on one key would make a fallback to an\n * absolute path silently inherit the bare command's failure.\n *\n * Environment probes stay unmemoised: they are free, and a consumer may\n * legitimately set a key part-way through a process.\n */\nconst cliProbes = new Map<string, Promise<boolean>>();\n\n/** Test seam: forget the memoised Claude CLI probes. */\nexport function resetClaudeCliProbe(): void {\n cliProbes.clear();\n}\n\nfunction probeClaudeCli(spec: ProviderSpec): Promise<boolean> {\n const exec = spec.exec ?? realExec;\n const command = spec.command ?? \"claude\";\n const cached = cliProbes.get(command);\n if (cached) return cached;\n const probing = exec([command, \"--version\"], { timeoutMs: 10_000 })\n .then((r) => r.code === 0 && !r.timedOut && r.spawnError == null)\n .catch(() => false);\n cliProbes.set(command, probing);\n return probing;\n}\n\nasync function probeLlamaCpp(spec: ProviderSpec): Promise<Probe> {\n const injected = spec.llamaRuntime ?? spec.llamaCpp?.runtime;\n if (injected) return budgetProbe(injected);\n\n // Ask whether the binding is usable BEFORE touching it. Going straight to\n // `getMemoryBudgetBytes` would route through the auto-installer, so merely\n // asking `availableProviders()` what this machine can do would fetch a native\n // module — a query with a side effect, and a slow one.\n const status = await nodeLlamaCppStatus();\n if (status.state === \"refused\") {\n return { available: false, reason: status.reason };\n }\n if (status.state === \"installable\") {\n // Usable, at the cost of an install that happens when it is actually\n // needed. `warnInstalling` announces it at that point.\n return { available: true };\n }\n // Present: load it for real, which is also what catches a binding that is\n // installed but whose backend fails to start.\n return budgetProbe(defaultLlamaRuntime());\n}\n\n/**\n * The same call the `auto` MODEL selector makes, so choosing llama-cpp here\n * costs nothing extra: it is loaded either way.\n */\nfunction budgetProbe(runtime: LlamaRuntime): Promise<Probe> {\n return runtime.getMemoryBudgetBytes().then(\n () => ({ available: true }),\n (e: unknown) => ({\n available: false,\n reason:\n e instanceof Error && /node-llama-cpp/.test(e.message)\n ? \"node-llama-cpp is not installed (npm i node-llama-cpp)\"\n : `node-llama-cpp could not start (${\n e instanceof Error ? e.message : String(e)\n })`,\n }),\n );\n}\n\nasync function probe(\n provider: ProviderName,\n spec: ProviderSpec,\n): Promise<Probe> {\n switch (provider) {\n case \"anthropic\":\n return hasKey(\"anthropic\")\n ? { available: true }\n : {\n available: false,\n reason: `ANTHROPIC_API_KEY is not set`,\n };\n case \"openai\":\n // A local OpenAI-compatible server needs no key — same rule the\n // OpenAICompatProvider constructor applies.\n return hasKey(\"openai\") || spec.baseUrl\n ? { available: true }\n : {\n available: false,\n reason: `OPENAI_API_KEY is not set and no baseUrl was given`,\n };\n case \"claude-cli\":\n return (await probeClaudeCli(spec))\n ? { available: true }\n : {\n available: false,\n reason: `could not run \\`${spec.command ?? \"claude\"}\\` (is the Claude CLI installed?)`,\n };\n case \"llama-cpp\":\n return probeLlamaCpp(spec);\n default:\n return { available: false, reason: \"not auto-selectable\" };\n }\n}\n\n/**\n * Every provider this machine could use right now, in priority order.\n *\n * Useful for showing a picker or explaining a fallback; `detectProvider` is\n * the same sweep with the first hit returned.\n */\nexport async function availableProviders(\n spec: ProviderSpec = {},\n): Promise<ProviderName[]> {\n const probes = await Promise.all(\n DETECTION_ORDER.map((name) => probe(name, spec)),\n );\n return DETECTION_ORDER.filter((_, i) => probes[i]!.available);\n}\n\n/**\n * The highest-priority provider this machine can use.\n *\n * Throws an `InferenceError` naming every provider and why each was\n * unavailable — far more actionable than the `Unknown provider \"undefined\"`\n * this replaces.\n */\nexport async function detectProvider(\n spec: ProviderSpec = {},\n): Promise<ProviderName> {\n // Sequential, not Promise.all: the probes get dramatically more expensive\n // down the list, and the cheapest usually wins. Reading an environment\n // variable costs microseconds, spawning the Claude CLI ~150ms, and loading\n // the node-llama-cpp binding ~850ms — the last of which also initialises the\n // llama backend and allocates GPU context. Probing eagerly would pay all of\n // that on every construction just to pick `anthropic` off an env var, and\n // would touch the GPU for a provider that is never used.\n const reasons: string[] = [];\n for (const name of DETECTION_ORDER) {\n const result = await probe(name, spec);\n if (result.available) {\n warnSelected(name, spec.provider === \"auto\");\n return name;\n }\n reasons.push(` ${name.padEnd(10)} — ${result.reason}`);\n }\n throw new InferenceError(\n `No inference provider is available. Tried:\\n${reasons.join(\"\\n\")}\\n` +\n `Pass an explicit \\`provider\\`, set one of the keys above, or install node-llama-cpp.`,\n );\n}\n\nlet warnedSelection = false;\nlet warnedDownload = false;\n\n/** Test seam: reset the once-per-process auto-detection warnings. */\nexport function resetProviderDetectionWarning(): void {\n warnedSelection = false;\n warnedDownload = false;\n}\n\n/**\n * These are eval tools: a run whose provider silently changed because an\n * environment variable moved is a run whose verdicts and cache are no longer\n * comparable to the last one. Say which provider was picked, once.\n */\nfunction warnSelected(provider: ProviderName, wasExplicitAuto: boolean): void {\n if (warnedSelection) return;\n warnedSelection = true;\n // `provider: \"auto\"` IS a specification — saying otherwise sends someone\n // hunting their config for a field they did set.\n const because = wasExplicitAuto ? `provider \"auto\"` : \"no provider specified\";\n console.warn(\n `inference: ${because} — auto-selected \"${provider}\". ` +\n `Pass an explicit \\`provider\\` to pin it.`,\n );\n}\n\n/**\n * Falling back to a local model can mean pulling gigabytes. Say so before it\n * starts, not after a CI job has already stalled on it.\n */\nexport function warnPendingDownload(model: string, sizeBytes: number): void {\n if (warnedDownload) return;\n warnedDownload = true;\n console.warn(\n `inference: \"${model}\" is not downloaded yet — the first run will fetch ` +\n `~${(sizeBytes / 1e9).toFixed(2)} GB. Pre-fetch it, or pass an explicit ` +\n `\\`provider\\` to avoid the local model entirely.`,\n );\n}\n","/**\n * Provider factory over a library-owned `ProviderSpec`.\n *\n * The spec is deliberately NOT any consumer's config type. Every consumer maps\n * its own config into this flat shape, so adding a provider here does not\n * require touching four config schemas, and no consumer has to model its\n * config on another's to reuse this layer (ADR 01000).\n */\nimport { InferenceError } from \"../types.js\";\nimport { warnIfUnsupportedNode } from \"../runtime.js\";\nimport type { Pricing } from \"../cost.js\";\nimport { AnthropicProvider } from \"./anthropic.js\";\nimport { OpenAICompatProvider } from \"./openai-compat.js\";\nimport { ClaudeCliProvider } from \"./claude-cli.js\";\nimport { MockProvider } from \"./mock.js\";\nimport { LlamaCppProvider, defaultLlamaRuntime } from \"./llama-cpp.js\";\nimport {\n LLAMA_MODELS,\n aliasForTier,\n defaultLlamaModelsDirectory,\n isLlamaSelector,\n isModelDownloaded,\n tierForBudget,\n} from \"./llama-models.js\";\nimport { detectProvider, warnPendingDownload } from \"./detect.js\";\nimport type { AnthropicProviderOptions } from \"./anthropic.js\";\nimport type { OpenAICompatProviderOptions } from \"./openai-compat.js\";\nimport type { MockResponse } from \"./mock.js\";\nimport type { LlamaCppProviderOptions, LlamaRuntime } from \"./llama-cpp.js\";\nimport type { LlamaTier } from \"./llama-models.js\";\nimport type { ExecFn, InferenceProvider } from \"./types.js\";\n\nexport type ProviderName =\n | \"anthropic\"\n | \"openai\"\n | \"claude-cli\"\n | \"mock\"\n | \"llama-cpp\";\n\n/** A concrete provider, or `\"auto\"` to detect one. */\nexport type ProviderSelector = ProviderName | \"auto\";\n\nexport interface ProviderSpec {\n /**\n * Omitting this is identical to `\"auto\"`: the highest-priority provider this\n * machine can actually use is detected, ending at the free local model.\n * Resolving it needs `makeProviderAsync`/`resolveProviderIdentityAsync`.\n */\n provider?: ProviderSelector;\n /** null/undefined selects the per-provider default. */\n model?: string | null;\n /** Env var NAME holding the API key; null/undefined selects the default. */\n apiKeyEnv?: string | null;\n /** openai only. */\n baseUrl?: string;\n /** claude-cli only: the executable to run. */\n command?: string;\n /** claude-cli only: subprocess timeout. */\n timeoutMs?: number;\n /**\n * Pricing override for this model. Not used to construct the provider —\n * carried here so a consumer passes one object to both `makeProvider` and\n * `pricingFor`.\n */\n pricing?: Pricing;\n /** Provider-specific tuning, ignored by the other providers. */\n anthropic?: AnthropicProviderOptions;\n openai?: OpenAICompatProviderOptions;\n llamaCpp?: LlamaCppProviderOptions;\n /** Test seam for the claude-cli provider. */\n exec?: ExecFn;\n /** Test seam for the llama-cpp provider. */\n llamaRuntime?: LlamaRuntime;\n /** Scripted responses for the mock provider; defaults to a single empty object. */\n mockResponses?: MockResponse[];\n}\n\nexport const DEFAULT_MODELS: Record<ProviderName, string> = {\n anthropic: \"claude-sonnet-4-5\",\n openai: \"gpt-4o-mini\",\n \"claude-cli\": \"claude-sonnet-4-5\",\n mock: \"mock-model\",\n // A selector, not a pinned model: which weights a tier points at is then a\n // catalog change rather than an API change. Resolving it needs the async\n // factory — see `resolveProviderIdentityAsync`.\n \"llama-cpp\": \"auto\",\n};\n\nconst DEFAULT_API_KEY_ENV: Record<string, string> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n};\n\nexport const DEFAULT_OPENAI_BASE_URL = \"https://api.openai.com/v1\";\n\nexport interface ProviderIdentity {\n /** Always concrete — never `\"auto\"`, so it is safe as cache-key material. */\n provider: ProviderName;\n model: string;\n}\n\n/**\n * Resolve the provider name and model WITHOUT constructing the provider —\n * cache keys and pricing need the identity, but construction may require an\n * API key that a fully-cached run never uses.\n *\n * Throws for an unresolved `llama-cpp` selector. That is deliberate: picking a\n * tier weighs GPU VRAM, which needs an await, and returning the literal\n * \"auto\" as cache-key material would let a 2 GB and a 12 GB model share cached\n * verdicts — and give different results per machine under one key. Use\n * `resolveProviderIdentityAsync` for selectors.\n */\nexport function resolveProviderIdentity(spec: ProviderSpec): ProviderIdentity {\n if (spec.provider == null || spec.provider === \"auto\") {\n throw new InferenceError(\n `No provider specified. Detecting one probes the environment, the Claude ` +\n `CLI and the local model runtime, which cannot be done synchronously — ` +\n `use resolveProviderIdentityAsync/makeProviderAsync, or name a provider ` +\n `(${Object.keys(DEFAULT_MODELS).join(\", \")}).`,\n );\n }\n const model = spec.model ?? DEFAULT_MODELS[spec.provider] ?? \"unknown\";\n if (spec.provider === \"llama-cpp\" && isLlamaSelector(model)) {\n throw new InferenceError(\n `llama-cpp model \"${model}\" is a selector and cannot be resolved ` +\n `synchronously — picking a tier probes GPU memory. Use ` +\n `resolveProviderIdentityAsync/makeProviderAsync, or name a concrete ` +\n `model (e.g. \"gemma-4-e4b\").`,\n );\n }\n return { provider: spec.provider, model };\n}\n\n/**\n * Selector-aware identity resolution. Returns the CONCRETE model a selector\n * resolved to, so the cache key names the weights that actually ran.\n *\n * Every other provider delegates to the synchronous form, so a consumer can\n * switch to this wholesale.\n */\nexport async function resolveProviderIdentityAsync(\n spec: ProviderSpec,\n): Promise<ProviderIdentity> {\n // A model name belongs to exactly one provider, so it cannot be carried into\n // whichever provider detection happens to pick: `{ model: \"gpt-4o-mini\" }` on\n // a machine with an Anthropic key selected `anthropic` and then 404'd at call\n // time, after the caller had already paid for detection. `null` still means\n // \"use the default\" — only a real name is ambiguous.\n if (\n (spec.provider == null || spec.provider === \"auto\") &&\n spec.model != null\n ) {\n throw new InferenceError(\n `Model \"${spec.model}\" was given without a provider, and a model name ` +\n `does not say which provider owns it. Name the provider too ` +\n `(${Object.keys(DEFAULT_MODELS).join(\", \")}), or drop the model to ` +\n `take the detected provider's default.`,\n );\n }\n\n // Provider first, then the model logic for whichever provider won.\n const provider =\n spec.provider == null || spec.provider === \"auto\"\n ? await detectProvider(spec)\n : spec.provider;\n const resolved: ProviderSpec = { ...spec, provider };\n\n const model = spec.model ?? DEFAULT_MODELS[provider] ?? \"unknown\";\n if (provider !== \"llama-cpp\" || !isLlamaSelector(model)) {\n return resolveProviderIdentity(resolved);\n }\n const tier: LlamaTier =\n model === \"auto\" ? await probeTier(llamaRuntimeFor(spec)) : model;\n return { provider, model: aliasForTier(tier) };\n}\n\n/**\n * Warn before a multi-gigabyte download, but only once the caller has actually\n * committed to running.\n *\n * Deliberately NOT in `resolveProviderIdentityAsync`: that resolves an identity\n * *without* constructing anything, which is exactly what a fully-cached run\n * does — and such a run downloads nothing, so warning there announces gigabytes\n * that never move.\n */\nfunction warnIfDownloadPending(spec: ProviderSpec, model: string): void {\n const entry = LLAMA_MODELS[model];\n if (!entry) return;\n const directory =\n spec.llamaCpp?.modelsDirectory ?? defaultLlamaModelsDirectory();\n if (!isModelDownloaded(model, directory)) {\n warnPendingDownload(model, entry.sizeBytes);\n }\n}\n\n/**\n * A runtime can be injected either as `spec.llamaRuntime` or inside\n * `spec.llamaCpp` — `makeProvider` honours both, so selector resolution must\n * too. Missing one sends the probe to the real native module and throws for a\n * consumer whose whole point was to avoid it.\n */\nfunction llamaRuntimeFor(spec: ProviderSpec): LlamaRuntime | undefined {\n return spec.llamaRuntime ?? spec.llamaCpp?.runtime;\n}\n\nasync function probeTier(runtime: LlamaRuntime | undefined): Promise<LlamaTier> {\n const source = runtime ?? defaultLlamaRuntime();\n return tierForBudget(await source.getMemoryBudgetBytes());\n}\n\nexport function makeProvider(spec: ProviderSpec): InferenceProvider {\n // First use of the library, for anyone who goes through the factory — before\n // the spec is even validated, so an unsupported Node is named ahead of any\n // error it might be the real cause of.\n warnIfUnsupportedNode();\n const { model } = resolveProviderIdentity(spec);\n\n switch (spec.provider) {\n case \"anthropic\":\n return new AnthropicProvider(\n model,\n spec.apiKeyEnv ?? DEFAULT_API_KEY_ENV[\"anthropic\"]!,\n spec.anthropic ?? {},\n );\n case \"openai\":\n return new OpenAICompatProvider(\n spec.baseUrl ?? DEFAULT_OPENAI_BASE_URL,\n model,\n spec.apiKeyEnv ?? DEFAULT_API_KEY_ENV[\"openai\"]!,\n undefined,\n spec.openai ?? {},\n );\n case \"claude-cli\":\n return new ClaudeCliProvider(\n model,\n spec.command ?? \"claude\",\n spec.exec,\n spec.timeoutMs,\n );\n case \"mock\":\n // Offline smoke-testing seam: proposes nothing unless scripted.\n return new MockProvider(spec.mockResponses ?? [{ json: {} }], model);\n case \"llama-cpp\":\n return new LlamaCppProvider(model, {\n ...(spec.llamaCpp ?? {}),\n ...(spec.llamaRuntime ? { runtime: spec.llamaRuntime } : {}),\n });\n default:\n throw new InferenceError(\n `Unknown provider \"${String(spec.provider)}\". Available: ${Object.keys(\n DEFAULT_MODELS,\n ).join(\", \")}.`,\n );\n }\n}\n\n/**\n * Selector-aware provider construction. Resolves a `llama-cpp` selector\n * against this machine first, so the returned provider's `modelName()` — and\n * therefore the cache key — names the weights it will actually load.\n *\n * Every other provider delegates to `makeProvider`.\n */\nexport async function makeProviderAsync(\n spec: ProviderSpec,\n): Promise<InferenceProvider> {\n // Both halves must be threaded through: passing only the model would leave a\n // detected provider as `undefined` and throw in `makeProvider`.\n const { provider, model } = await resolveProviderIdentityAsync(spec);\n if (provider === \"llama-cpp\") warnIfDownloadPending(spec, model);\n return makeProvider({ ...spec, provider, model });\n}\n","/**\n * Reclaiming disk space from downloaded GGUF weights.\n *\n * This is straightforward because the models directory belongs to this library\n * alone (see `defaultLlamaModelsDirectory`). node-llama-cpp's own global\n * directory is shared with its CLI and anything else on the machine, so\n * clearing THAT would destroy models this library never downloaded; owning a\n * directory removes the hazard rather than defending against it.\n */\nimport { rmSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n blobNameFor,\n defaultLlamaModelsDirectory,\n listModelDirectory,\n matchesModelBlob,\n} from \"./llama-models.js\";\nimport { disposeLlamaModels } from \"./llama-cpp.js\";\n\nexport interface ClearedModelFile {\n path: string;\n sizeBytes: number;\n}\n\nexport interface ClearLlamaModelsResult {\n /** Files removed, or that would be removed under `dryRun`. */\n files: ClearedModelFile[];\n freedBytes: number;\n directory: string;\n dryRun: boolean;\n}\n\nexport interface ClearLlamaModelsOptions {\n /** Defaults to this library's own models directory. */\n directory?: string;\n /** Clear only these models (alias or `hf:` URI); default clears all. */\n models?: string[];\n /** Report what would be removed without deleting anything. */\n dryRun?: boolean;\n}\n\n/**\n * Delete downloaded GGUF weights and interrupted partial downloads.\n *\n * Loaded models are disposed first: on Windows the weights are memory-mapped\n * while loaded, and deleting an open file fails with EBUSY/EPERM.\n */\nexport async function clearLlamaModels(\n options: ClearLlamaModelsOptions = {},\n): Promise<ClearLlamaModelsResult> {\n const directory = options.directory ?? defaultLlamaModelsDirectory();\n const dryRun = options.dryRun ?? false;\n // Resolve BEFORE touching disk so an unknown name fails without having\n // deleted half the set.\n const wanted = options.models?.map(blobNameFor);\n\n if (!dryRun) await disposeLlamaModels();\n\n const files: ClearedModelFile[] = [];\n for (const entry of listModelDirectory(directory)) {\n // Only ever weights — a stray config or log in this directory is safe, and\n // so is anything else if `directory` was pointed somewhere shared.\n if (!isModelBlob(entry)) continue;\n if (wanted && !wanted.some((name) => matchesModelBlob(entry, name))) {\n continue;\n }\n const path = join(directory, entry);\n const sizeBytes = sizeOf(path);\n if (sizeBytes === undefined) continue;\n if (!dryRun) {\n try {\n rmSync(path);\n } catch {\n // A file held open by another process is not this call's business to\n // force; report only what actually went away.\n continue;\n }\n }\n files.push({ path, sizeBytes });\n }\n\n return {\n files,\n freedBytes: files.reduce((total, file) => total + file.sizeBytes, 0),\n directory,\n dryRun,\n };\n}\n\nfunction isModelBlob(entry: string): boolean {\n return entry.endsWith(\".gguf\") || entry.endsWith(\".gguf.ipull\");\n}\n\nfunction sizeOf(path: string): number | undefined {\n try {\n return statSync(path).size;\n } catch {\n return undefined;\n }\n}\n","/**\n * One schema-validated completion, with a single retry.\n *\n * The invariant every consumer depends on: a run that cannot produce\n * schema-valid JSON after the retry is recorded as an ERROR, not dropped and\n * not coerced. Downstream, an errored run counts against consensus — it can\n * push a result toward human review, but it can never produce a silent pass.\n */\nimport { Ajv2020 } from \"ajv/dist/2020.js\";\nimport type { ValidateFunction } from \"ajv\";\nimport { warnIfUnsupportedNode } from \"./runtime.js\";\nimport type { InferenceProvider, TokenUsage } from \"./providers/types.js\";\n\n/** One attempt at a schema-constrained completion. */\nexport interface InferenceRun<T = unknown> {\n /** Absent when the run errored (invalid JSON after retry, API failure). */\n result?: T;\n error?: string;\n provider: string;\n model: string;\n cached: boolean;\n usage?: TokenUsage;\n durationMs: number;\n}\n\nexport interface CompleteValidatedOptions {\n provider: InferenceProvider;\n system: string;\n user: string;\n schema: Record<string, unknown>;\n temperature?: number;\n /**\n * Attempts before recording an error. Default 2 (one initial call plus one\n * retry) — matches the behavior all three source projects shipped.\n */\n attempts?: number;\n /**\n * Pre-compiled validator. Compiling Ajv per call is wasteful in an ensemble\n * loop, so `runEnsemble` compiles once and passes it down.\n */\n validate?: ValidateFunction;\n}\n\nconst validatorCache = new WeakMap<object, ValidateFunction>();\n\n/** Compile once per schema object identity — Ajv compilation is not cheap. */\nexport function validatorFor(\n schema: Record<string, unknown>,\n): ValidateFunction {\n const cached = validatorCache.get(schema);\n if (cached) return cached;\n // A fresh Ajv per distinct schema object, not one shared instance: Ajv keeps\n // a registry keyed by `$id`, so a caller that rebuilds an equal schema object\n // per call (spreading VERDICT_SCHEMA to override descriptions, say) misses\n // the identity cache above and would hit \"schema with key or id ... already\n // exists\" on the second compile. Instances are held only by this WeakMap, so\n // they are collected with the schemas that own them.\n const compiled = new Ajv2020({ allErrors: true }).compile(schema);\n validatorCache.set(schema, compiled);\n return compiled;\n}\n\nexport async function completeValidatedJSON<T = unknown>(\n options: CompleteValidatedOptions,\n): Promise<InferenceRun<T>> {\n // The other half of \"first use\": a consumer that constructs a provider\n // directly never touches `makeProvider`, but everything still funnels here.\n warnIfUnsupportedNode();\n const {\n provider,\n system,\n user,\n schema,\n temperature = 0,\n attempts = 2,\n } = options;\n const validate = options.validate ?? validatorFor(schema);\n\n const start = Date.now();\n const base = {\n provider: provider.provider(),\n model: provider.modelName(),\n cached: false,\n };\n\n let lastError = \"unknown error\";\n for (let attempt = 0; attempt < attempts; attempt++) {\n try {\n const response = await provider.completeJSON({\n system,\n user,\n schema,\n temperature,\n });\n if (validate(response.json)) {\n return {\n ...base,\n result: response.json as T,\n usage: response.usage,\n durationMs: Date.now() - start,\n };\n }\n lastError = `Response failed schema validation: ${(validate.errors ?? [])\n .map((e) => `${e.instancePath} ${e.message}`)\n .join(\"; \")}`;\n } catch (e) {\n lastError = e instanceof Error ? e.message : String(e);\n }\n }\n return { ...base, error: lastError, durationMs: Date.now() - start };\n}\n","/**\n * Cost tracking: token usage priced from a small static table, overridable per\n * model by the caller. Unknown models cost 0 (unknown), never a guess — a\n * fabricated price is worse than an absent one when a budget gate depends on it.\n */\nimport type { TokenUsage } from \"./providers/types.js\";\n\nexport interface Pricing {\n inputPerMTok: number;\n outputPerMTok: number;\n}\n\n/**\n * USD per million tokens. Entries are base names; pinned variants\n * (`claude-sonnet-4-5-20250929`) resolve by prefix.\n */\nexport const PRICE_TABLE: Record<string, Pricing> = {\n \"claude-sonnet-4-5\": { inputPerMTok: 3, outputPerMTok: 15 },\n \"claude-sonnet-4-6\": { inputPerMTok: 3, outputPerMTok: 15 },\n \"claude-haiku-4-5\": { inputPerMTok: 1, outputPerMTok: 5 },\n \"claude-opus-4-8\": { inputPerMTok: 15, outputPerMTok: 75 },\n \"gpt-4o-mini\": { inputPerMTok: 0.15, outputPerMTok: 0.6 },\n \"gpt-4o\": { inputPerMTok: 2.5, outputPerMTok: 10 },\n};\n\nexport function pricingFor(\n model: string,\n override?: Pricing,\n): Pricing | undefined {\n if (override) return override;\n if (PRICE_TABLE[model]) return PRICE_TABLE[model];\n // Match pinned variants like claude-sonnet-4-5-20250929. Longest prefix\n // wins so `claude-sonnet-4-5` never shadows a more specific future entry.\n const base = Object.keys(PRICE_TABLE)\n .filter((k) => model.startsWith(k))\n .sort((a, b) => b.length - a.length)[0];\n return base ? PRICE_TABLE[base] : undefined;\n}\n\nexport function costOfUsage(\n usage: TokenUsage | undefined,\n pricing: Pricing | undefined,\n): number {\n if (!usage || !pricing) return 0;\n return (\n (usage.inputTokens / 1_000_000) * pricing.inputPerMTok +\n (usage.outputTokens / 1_000_000) * pricing.outputPerMTok\n );\n}\n\n/** Sum the cost of a set of runs. Cached runs cost nothing — they made no call. */\nexport function costOfRuns(\n runs: { usage?: TokenUsage; cached?: boolean }[],\n pricing: Pricing | undefined,\n): number {\n if (!pricing) return 0;\n let usd = 0;\n for (const run of runs) {\n if (!run.usage || run.cached) continue;\n usd += costOfUsage(run.usage, pricing);\n }\n return usd;\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"inference:verdict:0.1\",\n \"title\": \"LLM-as-judge verdict\",\n \"type\": \"object\",\n \"required\": [\"claim\", \"observed\", \"match\", \"confidence\", \"reasoning\"],\n \"properties\": {\n \"claim\": {\n \"type\": \"string\",\n \"description\": \"The specific assertion under evaluation.\"\n },\n \"observed\": {\n \"type\": \"string\",\n \"description\": \"What was actually observed in the subject under evaluation, quoting evidence where possible.\"\n },\n \"match\": {\n \"enum\": [\"pass\", \"fail\", \"partial\"],\n \"description\": \"pass only if the assertion is fully satisfied; partial for partial compliance.\"\n },\n \"confidence\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1,\n \"description\": \"Self-reported confidence in this verdict.\"\n },\n \"reasoning\": {\n \"type\": \"string\",\n \"description\": \"Why this conclusion was reached.\"\n }\n },\n \"additionalProperties\": false\n}\n","/**\n * LLM-as-judge types. The verdict shape is the one all consuming projects\n * already share; the canonical schema lives in verdict-schema.json and is\n * overridable per consumer so domain-specific field descriptions survive\n * (ADR 01001).\n */\nimport verdictSchemaJson from \"./verdict-schema.json\" with { type: \"json\" };\nimport type { TokenUsage } from \"../providers/types.js\";\n\nexport const VERDICT_SCHEMA = verdictSchemaJson as Record<string, unknown>;\n\nexport type Match = \"pass\" | \"fail\" | \"partial\";\n\n/** Confidence-zone routing for LLM-judged evals. */\nexport type Zone = \"auto-pass\" | \"auto-fail\" | \"human-review\";\n\nexport interface JudgeVerdict {\n /** The specific assertion under evaluation. */\n claim: string;\n /** What the judge actually observed. */\n observed: string;\n match: Match;\n /** 0.0–1.0 self-reported confidence. */\n confidence: number;\n reasoning: string;\n}\n\n/** One run within an ensemble. */\nexport interface JudgeRun {\n /** Absent when the run errored (invalid JSON after retry, API failure). */\n verdict?: JudgeVerdict;\n error?: string;\n provider: string;\n model: string;\n cached: boolean;\n usage?: TokenUsage;\n durationMs: number;\n}\n\n/** Aggregated outcome of an ensemble of judge runs for one subject. */\nexport interface ConsensusResult {\n runs: JudgeRun[];\n votes: { pass: number; fail: number; partial: number; error: number };\n /** Majority verdict; `partial` counts as fail for the binary outcome. */\n verdict: Match;\n /** Fraction of non-errored runs agreeing with the majority verdict. */\n agreement: number;\n /** Mean confidence across non-errored runs. */\n meanConfidence: number;\n zone: Zone;\n}\n","/**\n * Ensemble consensus math. `partial` counts as fail for the binary outcome\n * but stays visible in the vote counts. Errored runs count against consensus —\n * they can only push a result toward human review, never toward a silent pass.\n */\nimport type { ConsensusResult, JudgeRun, Match } from \"./types.js\";\n\nexport function computeConsensus(\n runs: JudgeRun[],\n): Omit<ConsensusResult, \"zone\"> {\n const votes = { pass: 0, fail: 0, partial: 0, error: 0 };\n let confidenceSum = 0;\n let confidenceCount = 0;\n for (const run of runs) {\n if (!run.verdict) {\n votes.error += 1;\n continue;\n }\n votes[run.verdict.match] += 1;\n confidenceSum += run.verdict.confidence;\n confidenceCount += 1;\n }\n\n const passVotes = votes.pass;\n const failVotes = votes.fail + votes.partial;\n // Binary majority; a tie is not a pass.\n const verdict: Match = passVotes > failVotes ? \"pass\" : \"fail\";\n const graded = passVotes + failVotes;\n const agreement = graded > 0 ? Math.max(passVotes, failVotes) / graded : 0;\n const meanConfidence =\n confidenceCount > 0 ? confidenceSum / confidenceCount : 0;\n\n return { runs, votes, verdict, agreement, meanConfidence };\n}\n","/**\n * Confidence-zone routing: only unanimous, high-confidence ensembles\n * auto-resolve; everything else goes to a human.\n */\nimport type { ConsensusResult, Zone } from \"./types.js\";\n\nexport interface ZoneThresholds {\n autoPass: number;\n autoFail: number;\n}\n\nexport const DEFAULT_ZONES: ZoneThresholds = { autoPass: 0.8, autoFail: 0.8 };\n\nexport function zoneFor(\n consensus: Omit<ConsensusResult, \"zone\">,\n thresholds: ZoneThresholds = DEFAULT_ZONES,\n): Zone {\n const { votes, meanConfidence } = consensus;\n const unanimousPass =\n votes.pass > 0 &&\n votes.fail === 0 &&\n votes.partial === 0 &&\n votes.error === 0;\n const unanimousFail =\n votes.pass === 0 && votes.error === 0 && votes.fail + votes.partial > 0;\n\n if (unanimousPass && meanConfidence >= thresholds.autoPass) return \"auto-pass\";\n if (unanimousFail && meanConfidence >= thresholds.autoFail) return \"auto-fail\";\n return \"human-review\";\n}\n","/**\n * The ensemble judge: N independent runs for one subject, each a fresh request\n * with no shared context (eval isolation), aggregated by consensus and routed\n * through confidence zones.\n *\n * Runs within one ensemble stay sequential on purpose — they are meant to be\n * independent samples, and interleaving them buys nothing. Concurrency belongs\n * one level up, across subjects, where the consumer owns the pool.\n */\nimport { completeValidatedJSON, validatorFor } from \"../complete.js\";\nimport type { JsonCache } from \"../cache.js\";\nimport type { InferenceProvider } from \"../providers/types.js\";\nimport { computeConsensus } from \"./consensus.js\";\nimport { DEFAULT_ZONES, zoneFor, type ZoneThresholds } from \"./zones.js\";\nimport {\n VERDICT_SCHEMA,\n type ConsensusResult,\n type JudgeRun,\n type JudgeVerdict,\n} from \"./types.js\";\n\nexport interface EnsembleOptions {\n provider: InferenceProvider;\n system: string;\n user: string;\n /** Ensemble size; default 3. */\n runs?: number;\n /** Default 0. Nonzero adds noise to verdicts and warns once. */\n temperature?: number;\n /**\n * Verdict schema. Defaults to the canonical one; pass your own to keep\n * domain-specific field descriptions (they measurably steer the model).\n * Must still produce objects matching `JudgeVerdict`.\n */\n schema?: Record<string, unknown>;\n /** Optional result cache. Requires `cacheKey`. */\n cache?: JsonCache<JudgeRun[]>;\n /** Content-addressed key; build it with `buildCacheKey`. */\n cacheKey?: string;\n /** Prefix for warnings, e.g. your tool's name. */\n label?: string;\n}\n\nlet warnedTemperature = false;\n\n/**\n * Run the ensemble and return every run. Cached ensembles replay identically,\n * with each run flagged `cached: true` so cost accounting skips them.\n */\nexport async function runEnsemble(\n options: EnsembleOptions,\n): Promise<JudgeRun[]> {\n const {\n provider,\n system,\n user,\n runs: runCount = 3,\n temperature = 0,\n schema = VERDICT_SCHEMA,\n cache,\n cacheKey,\n label = \"inference\",\n } = options;\n\n if (temperature > 0 && !warnedTemperature) {\n warnedTemperature = true;\n console.warn(\n `${label}: judge temperature is ${temperature} — nonzero temperature adds noise to verdicts; 0 is strongly recommended.`,\n );\n }\n\n if (cache && cacheKey) {\n const hit = cache.get(cacheKey);\n // A cache file can be valid JSON and still be the wrong shape — a\n // truncated write, or an entry from an older cache generation. JsonCache\n // only guards parse failures, so the shape check belongs here; a bad entry\n // is a miss, never a crash.\n if (Array.isArray(hit)) return hit.map((r) => ({ ...r, cached: true }));\n }\n\n // Compile the schema once for the whole ensemble rather than per run.\n const validate = validatorFor(schema);\n\n const results: JudgeRun[] = [];\n for (let i = 0; i < runCount; i++) {\n const run = await completeValidatedJSON<JudgeVerdict>({\n provider,\n system,\n user,\n schema,\n temperature,\n validate,\n });\n // `result` is the generic name at the completion layer; the judge layer\n // calls it `verdict`, which is what consumers persist in their caches.\n const { result, ...rest } = run;\n results.push(result === undefined ? rest : { ...rest, verdict: result });\n }\n\n if (cache && cacheKey) cache.set(cacheKey, results);\n return results;\n}\n\n/** Run the ensemble and aggregate it into a zoned consensus. */\nexport async function judge(\n options: EnsembleOptions & { zones?: ZoneThresholds },\n): Promise<ConsensusResult> {\n const runs = await runEnsemble(options);\n const base = computeConsensus(runs);\n return { ...base, zone: zoneFor(base, options.zones ?? DEFAULT_ZONES) };\n}\n\n/** Test seam: reset the once-per-process temperature warning. */\nexport function resetTemperatureWarning(): void {\n warnedTemperature = false;\n}\n"],"mappings":"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","names":["join","join","existsSync","mkdirSync","writeFileSync","homedir","join","join","homedir","existsSync","mkdirSync","writeFileSync","rmSync","statSync","join","join","rmSync","statSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/runtime.ts","../src/providers/anthropic.ts","../src/providers/openai-compat.ts","../src/exec.ts","../src/providers/claude-cli.ts","../src/providers/mock.ts","../src/cache.ts","../src/providers/llama-models.ts","../src/providers/llama-install.ts","../src/providers/llama-cpp.ts","../src/providers/detect.ts","../src/providers/index.ts","../src/providers/llama-clean.ts","../src/complete.ts","../src/cost.ts","../src/judge/verdict-schema.json","../src/judge/types.ts","../src/judge/consensus.ts","../src/judge/zones.ts","../src/judge/ensemble.ts"],"sourcesContent":["/**\n * Shared error type. Consumers catch this to distinguish an inference-layer\n * operational failure (missing API key, unknown provider) from their own\n * domain errors, and typically map it to their own exit code.\n */\nexport class InferenceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"InferenceError\";\n }\n}\n","/**\n * The one thing this library says about the runtime it was loaded into.\n *\n * `engines.node` is `>=24`, but npm treats an `engines` mismatch as an\n * `EBADENGINE` **warning**, not a refusal — scrolled past in CI, invisible in a\n * transitive install. So an older Node installs cleanly, runs, and then fails\n * somewhere unrelated with an error that never mentions the Node version.\n *\n * This is a warning rather than a throw on purpose. Nothing in `src/` uses a\n * Node-24-only API — the imports are `node:crypto`, `node:fs`, `node:os` and\n * `node:path`, the newest globals are `fetch` (18+) and `structuredClone`\n * (17+), and `tsconfig` targets ES2022 — and the strictest dependency floor is\n * `node-llama-cpp` at `>=20`. Node 24 is this package's *support* policy, not a\n * technical impossibility, and a library has no business refusing to run on a\n * consumer's behalf when it can still do the work. It says so once and\n * continues, the same way the four existing warn-once paths do.\n */\n\n/**\n * The major version `engines.node` declares. `test/unit/runtime.test.ts` pins\n * this against `package.json`, so the two cannot drift apart.\n */\nexport const MINIMUM_NODE_MAJOR = 24;\n\nlet warnedNodeVersion = false;\n\n/**\n * Warn once if the running Node is older than the declared minimum.\n *\n * The version is a parameter so this is testable in-process — no subprocess on\n * a second Node install just to see the string.\n */\nexport function warnIfUnsupportedNode(\n version: string = process.versions.node,\n): void {\n if (warnedNodeVersion) return;\n const major = Number.parseInt(version, 10);\n // An unreadable version is not evidence of an old one. Same rule as an\n // unknown model price: never guess.\n if (!Number.isInteger(major) || major >= MINIMUM_NODE_MAJOR) return;\n warnedNodeVersion = true;\n console.warn(\n `inference: running on Node ${version}, older than the Node ` +\n `${MINIMUM_NODE_MAJOR} this package requires. npm only warns about that ` +\n `at install time (EBADENGINE), so nothing has stopped you yet — upgrade ` +\n `Node, or expect failures this library cannot explain.`,\n );\n}\n\n/** Test seam: reset the once-per-process Node version warning. */\nexport function resetNodeVersionWarning(): void {\n warnedNodeVersion = false;\n}\n","/**\n * Anthropic provider: structured output via a single forced tool call whose\n * input schema is the caller's schema — the model cannot answer any other way.\n */\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport { InferenceError } from \"../types.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n} from \"./types.js\";\n\nconst DEFAULT_TOOL_NAME = \"record_result\";\n\nexport interface AnthropicProviderOptions {\n /**\n * Name of the forced tool. Purely cosmetic to the model, but a descriptive\n * name (\"record_verdict\", \"record_proposal\") measurably steers output, so\n * consumers may set their own.\n */\n toolName?: string;\n /** Tool description shown to the model. */\n toolDescription?: string;\n maxTokens?: number;\n}\n\nexport class AnthropicProvider implements InferenceProvider {\n private readonly client: Anthropic;\n private readonly toolName: string;\n private readonly toolDescription: string;\n private readonly maxTokens: number;\n\n constructor(\n private readonly model: string,\n apiKeyEnv: string,\n options: AnthropicProviderOptions = {},\n ) {\n const apiKey = process.env[apiKeyEnv];\n if (!apiKey) {\n throw new InferenceError(\n `Anthropic provider needs ${apiKeyEnv} set (or choose another provider)`,\n );\n }\n this.client = new Anthropic({ apiKey });\n this.toolName = options.toolName ?? DEFAULT_TOOL_NAME;\n this.toolDescription =\n options.toolDescription ?? \"Record the structured result.\";\n this.maxTokens = options.maxTokens ?? 1024;\n }\n\n provider(): string {\n return \"anthropic\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: this.maxTokens,\n temperature: req.temperature,\n system: req.system,\n messages: [{ role: \"user\", content: req.user }],\n tools: [\n {\n name: this.toolName,\n description: this.toolDescription,\n input_schema: req.schema as Anthropic.Tool[\"input_schema\"],\n },\n ],\n tool_choice: { type: \"tool\", name: this.toolName },\n });\n\n // A truncated tool call still arrives as a tool_use block, just with\n // partial input. Without this check it fails schema validation instead,\n // burning the retry and reporting a misleading validation error rather\n // than the actionable \"raise maxTokens\".\n if (response.stop_reason === \"max_tokens\") {\n throw new Error(\n `Anthropic response hit max_tokens (${this.maxTokens}) before completing the tool call — raise the anthropic.maxTokens option.`,\n );\n }\n\n const toolUse = response.content.find(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n if (!toolUse) {\n throw new Error(\"Anthropic response contained no tool_use block\");\n }\n return {\n json: toolUse.input,\n usage: {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n },\n };\n }\n}\n","/**\n * OpenAI-compatible provider: works against OpenAI, Azure, Ollama, Groq,\n * Together, or any server speaking /chat/completions. Prefers strict\n * json_schema response_format; falls back to json_object + schema-in-prompt\n * when the server rejects it (older Ollama, some proxies).\n */\nimport { InferenceError } from \"../types.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n} from \"./types.js\";\n\ninterface ChatResponse {\n choices?: { message?: { content?: string } }[];\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n error?: { message?: string };\n}\n\n/** Extract a JSON object from content that may carry markdown fences. */\nexport function extractJson(content: string): unknown {\n const trimmed = content\n .replace(/^\\s*```(?:json)?\\s*/i, \"\")\n .replace(/\\s*```\\s*$/, \"\")\n .trim();\n try {\n return JSON.parse(trimmed);\n } catch {\n const start = trimmed.indexOf(\"{\");\n const end = trimmed.lastIndexOf(\"}\");\n if (start >= 0 && end > start) {\n return JSON.parse(trimmed.slice(start, end + 1));\n }\n throw new Error(\"Response contained no parseable JSON object\");\n }\n}\n\n/**\n * OpenAI strict mode requires `required` to list EVERY property (optionality\n * is expressed as a `null` type union) and rejects keywords outside its\n * subset (minLength, uniqueItems). Transform an all-optional schema into a\n * strict-compatible equivalent; null values are stripped from the response.\n */\nexport function toStrictSchema(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n const clone = structuredClone(schema);\n const walk = (node: unknown): void => {\n if (node === null || typeof node !== \"object\") return;\n if (Array.isArray(node)) {\n for (const item of node) walk(item);\n return;\n }\n const obj = node as Record<string, unknown>;\n delete obj[\"minLength\"];\n delete obj[\"uniqueItems\"];\n const properties = obj[\"properties\"];\n if (properties && typeof properties === \"object\") {\n obj[\"required\"] = Object.keys(properties);\n // Strict mode requires additionalProperties:false on EVERY object, not\n // just the root. A nested object without it is rejected as a schema\n // error, which permanently downgrades this provider to the weaker\n // json_object fallback for the rest of its life.\n obj[\"additionalProperties\"] = false;\n for (const prop of Object.values(properties as Record<string, unknown>)) {\n walk(prop);\n if (prop && typeof prop === \"object\" && !Array.isArray(prop)) {\n const p = prop as Record<string, unknown>;\n if (typeof p[\"type\"] === \"string\" && p[\"type\"] !== \"null\") {\n p[\"type\"] = [p[\"type\"], \"null\"];\n }\n }\n }\n }\n walk(obj[\"items\"]);\n };\n walk(clone);\n return clone;\n}\n\n/** Remove null-valued keys (strict-mode \"omitted\" marker) from a response object. */\nexport function stripNulls(value: unknown): unknown {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n return value;\n }\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).filter(\n ([, v]) => v !== null,\n ),\n );\n}\n\nexport interface OpenAICompatProviderOptions {\n /** `json_schema.name` sent to the server. Cosmetic; steers some models. */\n schemaName?: string;\n}\n\nexport class OpenAICompatProvider implements InferenceProvider {\n private supportsJsonSchema = true;\n private readonly schemaName: string;\n\n constructor(\n private readonly baseUrl: string,\n private readonly model: string,\n apiKeyEnv: string,\n private readonly apiKey: string | undefined = process.env[apiKeyEnv],\n options: OpenAICompatProviderOptions = {},\n ) {\n // Local servers (Ollama) often need no key; only insist for api.openai.com.\n if (!this.apiKey && baseUrl.includes(\"api.openai.com\")) {\n throw new InferenceError(\n `OpenAI provider needs ${apiKeyEnv} set (or point baseUrl at a local server)`,\n );\n }\n this.schemaName = options.schemaName ?? \"result\";\n }\n\n provider(): string {\n return \"openai\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n private async chat(body: Record<string, unknown>): Promise<ChatResponse> {\n const response = await fetch(\n `${this.baseUrl.replace(/\\/$/, \"\")}/chat/completions`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(this.apiKey ? { authorization: `Bearer ${this.apiKey}` } : {}),\n },\n body: JSON.stringify(body),\n },\n );\n const json = (await response.json().catch(() => ({}))) as ChatResponse;\n if (!response.ok) {\n const message = json.error?.message ?? `HTTP ${response.status}`;\n throw new Error(`${message}`);\n }\n return json;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const base = {\n model: this.model,\n temperature: req.temperature,\n messages: [\n { role: \"system\", content: req.system },\n { role: \"user\", content: req.user },\n ],\n };\n\n let response: ChatResponse;\n if (this.supportsJsonSchema) {\n try {\n response = await this.chat({\n ...base,\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: this.schemaName,\n strict: true,\n schema: toStrictSchema(req.schema),\n },\n },\n });\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n // Fall back on schema/format complaints AND on opaque 400s (gateways\n // that reject response_format without a parseable error body).\n if (\n !/response_format|json_schema|schema/i.test(message) &&\n message !== \"HTTP 400\"\n ) {\n throw e;\n }\n this.supportsJsonSchema = false;\n response = await this.jsonObjectFallback(base, req);\n }\n } else {\n response = await this.jsonObjectFallback(base, req);\n }\n\n const content = response.choices?.[0]?.message?.content;\n if (!content) throw new Error(\"Empty completion response\");\n return {\n json: stripNulls(extractJson(content)),\n usage:\n response.usage?.prompt_tokens != null\n ? {\n inputTokens: response.usage.prompt_tokens ?? 0,\n outputTokens: response.usage.completion_tokens ?? 0,\n }\n : undefined,\n };\n }\n\n private jsonObjectFallback(\n base: Record<string, unknown>,\n req: CompleteJSONRequest,\n ): Promise<ChatResponse> {\n return this.chat({\n ...base,\n messages: [\n {\n role: \"system\",\n content: `${req.system}\\n\\nRespond with ONLY a JSON object conforming to this JSON Schema:\\n${JSON.stringify(req.schema)}`,\n },\n { role: \"user\", content: req.user },\n ],\n response_format: { type: \"json_object\" },\n });\n }\n}\n","/**\n * Process execution wrapper for subprocess-backed providers. Uses cross-spawn\n * so npm shims resolve on Windows without `shell: true` and its quoting\n * hazards. Large payloads go through `input` (piped stdin) rather than argv —\n * Windows caps the command line at ~32K characters.\n */\nimport spawn from \"cross-spawn\";\nimport type { ExecFn, ExecResult } from \"./providers/types.js\";\n\nexport const realExec: ExecFn = (cmd, opts = {}) => {\n const [bin, ...args] = cmd;\n if (!bin) {\n return Promise.resolve<ExecResult>({\n code: null,\n stdout: \"\",\n stderr: \"\",\n timedOut: false,\n spawnError: \"Empty command\",\n });\n }\n return new Promise<ExecResult>((resolvePromise) => {\n const child = spawn(bin, args, {\n cwd: opts.cwd,\n env: { ...process.env, ...(opts.env ?? {}) },\n stdio: [opts.input != null ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n let settled = false;\n\n const timeoutMs = opts.timeoutMs ?? 60000;\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill();\n // Settle on the timeout itself rather than waiting for 'close'. On POSIX\n // a child that handles SIGTERM survives kill(), so 'close' would never\n // fire and the caller would hang forever with no timeout error. (On\n // Windows kill() is forceful, so this is belt-and-braces there.)\n settle({ code: null, stdout, stderr, timedOut: true });\n }, timeoutMs);\n\n const settle = (result: ExecResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolvePromise(result);\n };\n\n if (opts.input != null && child.stdin) {\n // EPIPE from a child that exits before reading is not our failure.\n child.stdin.on(\"error\", () => {});\n child.stdin.end(opts.input);\n }\n\n // setEncoding routes chunks through a StringDecoder, so multi-byte UTF-8\n // characters straddling pipe-chunk boundaries decode correctly; a raw\n // per-chunk Buffer.toString() would corrupt them nondeterministically.\n child.stdout?.setEncoding(\"utf8\");\n child.stderr?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (d: string) => (stdout += d));\n child.stderr?.on(\"data\", (d: string) => (stderr += d));\n child.on(\"error\", (e) =>\n settle({ code: null, stdout, stderr, timedOut, spawnError: e.message }),\n );\n child.on(\"close\", (code) => settle({ code, stdout, stderr, timedOut }));\n });\n};\n","/**\n * Claude CLI provider: shells out to the `claude` CLI using its local\n * authentication — no API key needed. Token usage is not reported, so cost\n * shows as unknown.\n */\nimport { realExec } from \"../exec.js\";\nimport { extractJson } from \"./openai-compat.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n ExecFn,\n InferenceProvider,\n} from \"./types.js\";\n\nexport class ClaudeCliProvider implements InferenceProvider {\n constructor(\n private readonly model: string,\n private readonly command: string = \"claude\",\n private readonly exec: ExecFn = realExec,\n private readonly timeoutMs: number = 180000,\n ) {}\n\n provider(): string {\n return \"claude-cli\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const prompt = [\n req.user,\n \"\",\n \"Respond with ONLY a JSON object conforming to this JSON Schema — no\",\n \"prose, no markdown fences:\",\n JSON.stringify(req.schema),\n ].join(\"\\n\");\n\n // The prompt is piped via stdin: user content routinely exceeds the ~32K\n // Windows command-line limit when passed as an argument.\n const result = await this.exec(\n [\n this.command,\n \"-p\",\n \"--append-system-prompt\",\n req.system,\n \"--output-format\",\n \"json\",\n \"--model\",\n this.model,\n ],\n { timeoutMs: this.timeoutMs, input: prompt },\n );\n\n if (result.spawnError) {\n throw new Error(\n `Failed to run ${this.command}: ${result.spawnError} (is the Claude CLI installed?)`,\n );\n }\n if (result.timedOut) throw new Error(\"Claude CLI timed out\");\n if (result.code !== 0) {\n throw new Error(\n `Claude CLI exited ${result.code}: ${result.stderr.trim().slice(-300)}`,\n );\n }\n\n // --output-format json wraps the answer: { result: \"...\", ... }\n //\n // When it isn't JSON at all the CLI is talking to a human, not to us — an\n // update banner, a login prompt, a proxy interception page. A bare\n // `SyntaxError: Unexpected token 'W'` would reach the end user of a\n // consuming CLI naming neither the culprit nor the fix, so say who printed\n // it and quote enough for a login prompt to be recognisable on sight.\n // Collapsed to one line and capped, like the stderr tail above, so a\n // megabyte of HTML cannot become the error message.\n let wrapper: { result?: string };\n try {\n wrapper = JSON.parse(result.stdout) as { result?: string };\n } catch {\n const excerpt = result.stdout.trim().replace(/\\s+/g, \" \").slice(0, 200);\n throw new Error(\n `Claude CLI printed non-JSON output (is it logged in?): ${excerpt || \"(no output)\"}`,\n );\n }\n if (typeof wrapper.result !== \"string\") {\n throw new Error(\"Claude CLI returned no result field\");\n }\n return { json: extractJson(wrapper.result) };\n }\n}\n","/**\n * Mock provider for tests and offline development. Responds with scripted\n * results in order, cycling when exhausted. Exported from the public API so\n * downstream consumers can test their own pipelines without a live provider.\n */\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n TokenUsage,\n} from \"./types.js\";\n\nexport type MockResponse =\n | { json: unknown; usage?: TokenUsage }\n | { error: string };\n\nexport class MockProvider implements InferenceProvider {\n private calls = 0;\n /** Every request seen, in order — assert against this in tests. */\n public readonly requests: CompleteJSONRequest[] = [];\n\n constructor(\n private readonly responses: MockResponse[],\n private readonly model = \"mock-model\",\n ) {\n if (responses.length === 0) {\n throw new Error(\"MockProvider needs at least one scripted response\");\n }\n }\n\n provider(): string {\n return \"mock\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n this.requests.push(req);\n const response = this.responses[this.calls % this.responses.length]!;\n this.calls += 1;\n if (\"error\" in response) {\n return Promise.reject(new Error(response.error));\n }\n return Promise.resolve({\n json: response.json,\n usage: response.usage ?? { inputTokens: 500, outputTokens: 100 },\n });\n }\n}\n\n/** Convenience: a scripted response shaped like the canonical judge verdict. */\nexport function mockVerdict(\n match: \"pass\" | \"fail\" | \"partial\",\n confidence: number,\n overrides: Partial<{\n claim: string;\n observed: string;\n reasoning: string;\n }> = {},\n): { json: unknown } {\n return {\n json: {\n claim: overrides.claim ?? \"The assertion under test\",\n observed: overrides.observed ?? \"Observed content\",\n match,\n confidence,\n reasoning: overrides.reasoning ?? \"Scripted mock reasoning\",\n },\n };\n}\n","/**\n * Content-addressed JSON cache for inference results, so a cached run replays\n * identically instead of re-billing a nondeterministic call.\n *\n * Key composition stays with the caller: each consumer has a different notion\n * of what should invalidate an entry (page body, prompt version, ensemble\n * size, requested fields). `buildCacheKey` just hashes the parts you name.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport function sha256(text: string): string {\n return createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Hash an ordered list of key parts into a cache key. Long parts (page bodies,\n * rendered traces) should be pre-hashed with `sha256` by the caller so the\n * joined string stays small.\n */\nexport function buildCacheKey(parts: string[]): string {\n // Length-prefixed, not plain-joined: a part that itself contains the\n // separator would otherwise let two different compositions — [\"a|b\", \"c\"]\n // and [\"a\", \"b|c\"] — share one cached result.\n return sha256(parts.map((p) => `${p.length}:${p}`).join(\"|\"));\n}\n\nexport class JsonCache<T> {\n /** Cache-write failures warn once per process, not once per entry. */\n private warned = false;\n\n constructor(\n private readonly dir: string,\n private readonly enabled: boolean = true,\n /** Prefix for the one-time write-failure warning. */\n private readonly label: string = \"inference\",\n ) {}\n\n get(key: string): T | undefined {\n if (!this.enabled) return undefined;\n const path = join(this.dir, `${key}.json`);\n if (!existsSync(path)) return undefined;\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as T;\n } catch {\n return undefined; // Corrupt cache entry — treat as a miss.\n }\n }\n\n set(key: string, value: T): void {\n if (!this.enabled) return;\n // The cache is an optimization: a write failure (read-only workspace, full\n // disk, long path) must never abort a run whose inference already\n // succeeded and was already paid for.\n try {\n mkdirSync(this.dir, { recursive: true });\n writeFileSync(\n join(this.dir, `${key}.json`),\n JSON.stringify(value, null, 2),\n );\n } catch (e) {\n if (!this.warned) {\n this.warned = true;\n console.warn(\n `${this.label}: could not write the cache at ${this.dir} (${\n e instanceof Error ? e.message : String(e)\n }). Continuing without caching.`,\n );\n }\n }\n }\n}\n","/**\n * Curated GGUF models for the in-process `llama-cpp` provider, plus the\n * selector-to-model resolution that sits in front of them.\n *\n * The three tiers were chosen by measurement, not by published benchmark:\n * 26 real documentation pages with their `title` and `description` removed,\n * refilled through this library under a JSON Schema, and scored against the\n * human-written originals. See ADR 01009 for the numbers and the method.\n *\n * Two results from that run shape this catalog:\n *\n * 1. **Quality barely tracks size on schema-constrained extraction.** Every\n * model between 1.4 GB and 5.5 GB scored within one standard error of the\n * others. The tiers therefore buy latency and headroom for richer schemas\n * than that corpus exercises — they do not buy a linear quality gain, and\n * should not be described as if they do.\n * 2. **Latency stability does not track size either, and matters more.** Two\n * builds ran away on long pages, generating until they hit a timeout rather\n * than closing the JSON: Gemma 4 E2B at Q2 (6 of 12 pages unfinished at\n * 120s, one still going at 400s) and Granite 4.1 8B at Q4 (6 of 26 pages\n * over 20s, three over 115s). Both are excluded from the tiers for that\n * reason alone. A tier entry has to terminate.\n *\n * That is why the tiers are no longer one family. Chat templates now differ\n * across tiers, which is fine — node-llama-cpp reads the template out of each\n * GGUF — but it does mean a prompt tuned against one tier is not automatically\n * tuned against the next.\n *\n * The superseded Gemma aliases are kept, untiered, because consumers pin them\n * by name; dropping an alias is a hard failure rather than a slower download.\n *\n * Entries pin an exact blob path rather than a `:QUANT` tag. That is a hard\n * requirement, not a style preference, and it holds for every entry:\n *\n * - A tag can be re-pointed upstream, which silently changes the weights\n * behind a cache key that already names the model. A pinned path cannot.\n * This reason alone is sufficient, and it applies to all repos.\n * - Most of these repos carry `mmproj-*.gguf` (the ~1 GB vision projector)\n * and some carry `mtp-*.gguf` beside the weights, which text-only judging\n * must not download.\n * - On the Gemma QAT repos a tag would not resolve at all: they ship NO\n * Q4_K_M, only UD-Q4_K_XL and UD-Q2_K_XL.\n *\n * Note the last point is specific to the Gemma QAT repos, which now back only\n * untiered entries. The Qwen3.5 and Granite repos behind the three tiers DO\n * publish Q4_K_M, so a tag would resolve there — and would still be wrong, for\n * the first reason. Do not read \"the tag resolves\" as \"the tag is allowed\".\n */\nimport { readdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { InferenceError } from \"../types.js\";\n\n/**\n * Where this library downloads weights — its OWN directory, not\n * node-llama-cpp's global `~/.node-llama-cpp/models`.\n *\n * That default is shared: node-llama-cpp's CLI writes there, as does anything\n * else on the machine using it. Owning a directory outright means clearing it\n * can never destroy a model this library did not download, and it keeps one\n * copy shared across every consumer of this package on the machine.\n *\n * `INFERENCE_MODELS_DIR` overrides it — useful for CI or a shared volume.\n */\nexport function defaultLlamaModelsDirectory(): string {\n return (\n process.env[\"INFERENCE_MODELS_DIR\"] ||\n join(homedir(), \".hawkeyexl-inference\", \"models\")\n );\n}\n\n/** Size tiers, smallest first. Order is load-bearing for `tierForBudget`. */\nexport const LLAMA_TIERS = [\"fast\", \"balanced\", \"quality\"] as const;\nexport type LlamaTier = (typeof LLAMA_TIERS)[number];\n\n/** Model selectors — resolved against hardware, never used as a cache key. */\nexport const LLAMA_SELECTORS = [\"auto\", ...LLAMA_TIERS] as const;\nexport type LlamaSelector = (typeof LLAMA_SELECTORS)[number];\n\nexport interface LlamaModelEntry {\n /** `hf:` URI pinned to one blob, handed to `resolveModelFile` as-is. */\n readonly uri: string;\n /** Size of that blob in bytes, as reported by the Hugging Face API. */\n readonly sizeBytes: number;\n readonly license: string;\n /** Absent for entries that are selectable by alias but not by tier. */\n readonly tier?: LlamaTier;\n /** Human note for `LLAMA_MODELS` readers deciding what to download. */\n readonly notes: string;\n}\n\n/**\n * Frozen per entry, not just at the top level.\n *\n * A shallow freeze leaves the entries writable, and this catalog is exported\n * for consumers to read: a stray write to `sizeBytes` silently re-points\n * `tierForBudget` process-wide, and a write to `uri` defeats the pinned-blob\n * invariant the whole catalog exists to hold (ADR 01003).\n */\nexport const LLAMA_MODELS: Readonly<Record<string, LlamaModelEntry>> =\n deepFreezeEntries({\n \"granite-4.1-3b-q2\": {\n uri: \"hf:unsloth/granite-4.1-3b-GGUF/granite-4.1-3b-UD-Q2_K_XL.gguf\",\n sizeBytes: 1_414_548_800,\n license: \"Apache-2.0\",\n tier: \"fast\",\n notes:\n \"Smallest tier and the quickest measured (4.8s/page). Scores level \" +\n \"with models three times its size on schema-constrained extraction.\",\n },\n \"qwen3.5-4b\": {\n uri: \"hf:unsloth/Qwen3.5-4B-GGUF/Qwen3.5-4B-UD-Q4_K_XL.gguf\",\n sizeBytes: 2_912_109_728,\n license: \"Apache-2.0\",\n notes:\n \"The default for most machines. Smaller and faster than the Gemma 4 \" +\n \"E4B it replaces, at indistinguishable measured quality.\",\n tier: \"balanced\",\n },\n \"qwen3.5-9b\": {\n uri: \"hf:unsloth/Qwen3.5-9B-GGUF/Qwen3.5-9B-UD-Q4_K_XL.gguf\",\n sizeBytes: 5_966_095_584,\n license: \"Apache-2.0\",\n tier: \"quality\",\n notes:\n \"Best measured of everything tried, and still smaller and faster \" +\n \"than the Gemma 4 12B it replaces. Wants a GPU or plenty of RAM.\",\n },\n // --- Superseded, kept resolvable by name. Untiered: nothing selects these\n // unless a caller asks for one outright.\n \"gemma-4-e2b\": {\n uri: \"hf:unsloth/gemma-4-E2B-it-qat-GGUF/gemma-4-E2B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 2_620_370_976,\n license: \"Apache-2.0\",\n notes: \"IFEval 94.6. Former `fast` tier; sound, but larger than Granite.\",\n },\n \"gemma-4-e4b\": {\n uri: \"hf:unsloth/gemma-4-E4B-it-qat-GGUF/gemma-4-E4B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 4_215_695_776,\n license: \"Apache-2.0\",\n notes: \"IFEval 96.7. Former `balanced` tier; sound, but larger.\",\n },\n \"gemma-4-12b\": {\n uri: \"hf:unsloth/gemma-4-12B-it-qat-GGUF/gemma-4-12B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 6_716_356_800,\n license: \"Apache-2.0\",\n notes: \"IFEval 97.2. Former `quality` tier. Dense 12B; wants a GPU.\",\n },\n \"gemma-4-26b-a4b\": {\n uri: \"hf:unsloth/gemma-4-26B-A4B-it-qat-GGUF/gemma-4-26B-A4B-it-qat-UD-Q4_K_XL.gguf\",\n sizeBytes: 14_249_047_104,\n license: \"Apache-2.0\",\n notes:\n \"MoE: 25.2B total, 3.8B active — infers near E4B speed if it fits in memory.\",\n },\n \"gemma-4-e2b-q2\": {\n uri: \"hf:unsloth/gemma-4-E2B-it-qat-GGUF/gemma-4-E2B-it-qat-UD-Q2_K_XL.gguf\",\n sizeBytes: 2_186_186_784,\n license: \"Apache-2.0\",\n notes:\n \"AVOID. Smallest download, but it does not reliably terminate: 6 of \" +\n \"12 pages unfinished at 120s, one still running at 400s, and the \" +\n \"pages that did finish proposed identifiers as prose. Kept only so \" +\n \"existing pins still resolve.\",\n },\n });\n\nfunction deepFreezeEntries<T extends Record<string, object>>(\n catalog: T,\n): Readonly<T> {\n for (const entry of Object.values(catalog)) Object.freeze(entry);\n return Object.freeze(catalog);\n}\n\n/** The alias backing each tier, used by `auto` and the tier keywords. */\nconst TIER_ALIAS: Record<LlamaTier, string> = {\n fast: \"granite-4.1-3b-q2\",\n balanced: \"qwen3.5-4b\",\n quality: \"qwen3.5-9b\",\n};\n\nexport function isLlamaSelector(model: string): model is LlamaSelector {\n return (LLAMA_SELECTORS as readonly string[]).includes(model);\n}\n\n/**\n * Weights are only part of the cost — the KV cache at a real context length,\n * the OS, and whatever else the machine is doing all want memory too. Requiring\n * several times the file size keeps `auto` from picking a model that technically\n * loads and then thrashes.\n */\nconst MEMORY_HEADROOM = 3.5;\n\n/**\n * Largest tier whose weights fit the memory budget with headroom. Lands at\n * roughly: >=24 GB -> quality, >=15 GB -> balanced, else fast.\n *\n * Sized off the catalog's recorded bytes rather than parameter counts: Gemma\n * 4's E-series are per-layer-embedding models whose footprint does not track\n * \"effective params\" (E4B is 4.5B effective but 15 GB at BF16).\n */\nexport function tierForBudget(budgetBytes: number): LlamaTier {\n let chosen: LlamaTier = \"fast\";\n for (const tier of LLAMA_TIERS) {\n const entry = LLAMA_MODELS[TIER_ALIAS[tier]]!;\n if (entry.sizeBytes * MEMORY_HEADROOM <= budgetBytes) chosen = tier;\n }\n // Falls back to the smallest tier rather than refusing: a machine too small\n // for `fast` will thrash, but that is the caller's call to make, not ours.\n return chosen;\n}\n\n/**\n * Catalog alias backing a tier. Selectors resolve to this rather than to a raw\n * URI so the identity — and therefore the cache key — stays human-readable.\n */\nexport function aliasForTier(tier: LlamaTier): string {\n return TIER_ALIAS[tier];\n}\n\n/** The pinned URI backing a tier keyword. */\nexport function uriForTier(tier: LlamaTier): string {\n return LLAMA_MODELS[TIER_ALIAS[tier]]!.uri;\n}\n\n/**\n * Turn a concrete model reference — a curated alias, an `hf:` URI, or a local\n * path — into something `resolveModelFile` accepts.\n *\n * Selectors are rejected rather than guessed at: they need a hardware probe,\n * which is async, and this runs on the synchronous cache-key path. Resolving\n * one here from RAM alone would emit a key naming a model the provider then\n * did not load.\n */\nexport function resolveLlamaModelRef(model: string): string {\n if (isLlamaSelector(model)) {\n throw new InferenceError(\n `llama-cpp model \"${model}\" is a selector and needs a hardware probe to ` +\n `resolve. Use resolveProviderIdentityAsync/makeProviderAsync, or name a ` +\n `concrete model (e.g. \"${TIER_ALIAS.balanced}\").`,\n );\n }\n const entry = LLAMA_MODELS[model];\n if (entry) return entry.uri;\n if (isModelPathOrUri(model)) return model;\n throw new InferenceError(\n `Unknown llama-cpp model \"${model}\". Use a selector (${LLAMA_SELECTORS.join(\n \", \",\n )}), a curated alias (${Object.keys(LLAMA_MODELS).join(\n \", \",\n )}), an hf: URI, or a path to a .gguf file.`,\n );\n}\n\n/**\n * The catalog's pinned filename for a model reference.\n *\n * Strips a `#branch` fragment (node-llama-cpp accepts\n * `hf:user/repo/file.gguf#branch`) and splits on both separators, so a Windows\n * path resolves too. Getting either wrong makes callers silently match nothing.\n */\nexport function blobNameFor(model: string): string {\n const ref = resolveLlamaModelRef(model).split(\"#\")[0]!;\n return ref.split(/[/\\\\]/).pop()!;\n}\n\n/**\n * Does an on-disk entry belong to this model?\n *\n * node-llama-cpp prefixes downloads with `hf_<user>_`, so match by SUFFIX\n * rather than equality — that survives a change to their naming scheme.\n */\nexport function matchesModelBlob(entry: string, blobName: string): boolean {\n const base = entry.replace(/\\.ipull$/, \"\");\n if (base.endsWith(blobName)) return true;\n // Split models land as `<stem>-00001-of-00003.gguf`; every part belongs to\n // the same model, so matching the stem covers the whole set.\n const stem = blobName.replace(/\\.gguf$/, \"\");\n return new RegExp(`${escapeRegExp(stem)}-\\\\d{5}-of-\\\\d{5}\\\\.gguf$`).test(base);\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Are this model's weights already on disk?\n *\n * A `.ipull` partial counts as NOT downloaded: it cannot be loaded, so\n * treating it as present would skip the download warning and then stall on a\n * download anyway.\n */\nexport function isModelDownloaded(model: string, directory: string): boolean {\n const blobName = blobNameFor(model);\n return listModelDirectory(directory).some(\n (entry) => !entry.endsWith(\".ipull\") && matchesModelBlob(entry, blobName),\n );\n}\n\n/** Top level only — a nested directory is not ours to walk. */\nexport function listModelDirectory(directory: string): string[] {\n try {\n return readdirSync(directory, { withFileTypes: true })\n .filter((entry) => entry.isFile())\n .map((entry) => entry.name);\n } catch {\n // No directory means nothing was ever downloaded — not an error.\n return [];\n }\n}\n\n/**\n * A bare unknown word is a typo'd alias, not a model — catch it early rather\n * than letting it reach the downloader as a doomed repo name.\n *\n * Accepts exactly what the error message in `resolveLlamaModelRef` promises: a\n * recognised URI scheme, or something that names a `.gguf` file. A bare\n * `user/repo` is deliberately NOT a model reference — it would otherwise slip\n * past this guard and fail deep inside the downloader with a far worse\n * message. Any real path to weights ends in `.gguf`, on every platform.\n */\nfunction isModelPathOrUri(model: string): boolean {\n return (\n /^(hf|huggingface):/i.test(model) ||\n /^https?:\\/\\//i.test(model) ||\n /^(hf|huggingface)\\.co\\//i.test(model) ||\n model.endsWith(\".gguf\")\n );\n}\n","/**\n * Installing the optional `node-llama-cpp` peer on demand.\n *\n * Detection ends at `llama-cpp` precisely because it needs no key and no\n * account — but that rung is only reachable if the native binding is present,\n * and npm does not install optional peers. Without this module a machine with\n * no credentials and no binding falls off the end of the chain, which is the\n * one case auto-detection exists to serve.\n *\n * The install goes into a directory this library OWNS — never the consumer's\n * `node_modules`, `package.json` or lockfile. That is the same reasoning ADR\n * 01003 applied to weights: owning a directory removes the hazard instead of\n * defending against it. A consumer's dependency manifest is theirs, and a\n * library that edits it has broken reproducible installs for everyone\n * downstream.\n */\nimport {\n existsSync,\n mkdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { InferenceError } from \"../types.js\";\nimport { realExec } from \"../exec.js\";\nimport type { ExecFn } from \"./types.js\";\n\n/** Pinned to the peer range: below 3.19.0 nothing in the catalog loads. */\nconst PACKAGE_SPEC = \"node-llama-cpp@^3.19.0\";\n\n/**\n * The shim doubles as the readiness marker — it is written only after npm\n * exits 0, so its presence means \"this prefix is complete\".\n */\nconst SHIM = \"loader.mjs\";\nconst LOCK = \".install.lock\";\n\n/**\n * Generous by necessity. Prebuilt binaries cover win32-x64, darwin-arm64 and\n * linux-x64; anything else falls back to a CMake build that genuinely takes\n * minutes. `realExec` defaults to 60s, which would kill it half-built.\n */\nconst INSTALL_TIMEOUT_MS = 900_000;\n\n/** How long to wait for another process's install before giving up. */\nconst LOCK_WAIT_MS = INSTALL_TIMEOUT_MS;\n/** A lock older than this belonged to a process that died holding it. */\nconst LOCK_STALE_MS = INSTALL_TIMEOUT_MS + 60_000;\n\nexport interface RuntimeInstallOptions {\n /** Defaults to this library's own runtime directory. */\n directory?: string;\n /** Injected for tests; defaults to the real process runner. */\n exec?: ExecFn;\n /** Injected for tests; defaults to `process.env`. */\n env?: Record<string, string | undefined>;\n timeoutMs?: number;\n /** Test seam: how the shim is imported once it exists. */\n importShim?: (url: string) => Promise<unknown>;\n /**\n * Test seam: how the consumer's own copy is looked for.\n *\n * Whether an optional peer is installed is a property of the machine, and the\n * behaviour that matters most here — that probing installs nothing — is\n * unobservable on a machine that already has it. Injecting the lookup is the\n * only way to assert it deterministically.\n */\n probeImport?: () => Promise<unknown>;\n}\n\n/**\n * Where this library installs the binding — its OWN directory, beside the\n * models directory and for the same reason.\n *\n * `INFERENCE_RUNTIME_DIR` overrides it, mirroring `INFERENCE_MODELS_DIR`.\n */\nexport function defaultLlamaRuntimeDirectory(\n env: Record<string, string | undefined> = process.env,\n): string {\n return (\n env[\"INFERENCE_RUNTIME_DIR\"] ||\n join(homedir(), \".hawkeyexl-inference\", \"runtime\")\n );\n}\n\n/**\n * Is this import failure \"the package is not here\", as opposed to \"the package\n * is here and broken\"?\n *\n * The distinction decides whether installing can possibly help. `ERR_MODULE_NOT_FOUND`\n * is what Node raises for a missing bare specifier; `MODULE_NOT_FOUND` is its\n * CJS spelling. Anything else — a failed `dlopen`, an unsupported Node, a\n * package whose `exports` do not match — means the package resolved and then\n * failed, and no amount of reinstalling changes that.\n */\nexport function isModuleNotFound(e: unknown): boolean {\n const code = (e as NodeJS.ErrnoException | undefined)?.code;\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n}\n\nfunction describe(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n/** Whether the binding can be used, and at what cost, WITHOUT installing it. */\nexport type RuntimeStatus =\n /** Importable right now — either the consumer's own copy or a filled prefix. */\n | { state: \"present\" }\n /** Absent, but an install is permitted. Using it will fetch a native module. */\n | { state: \"installable\"; directory: string }\n /** Absent and installing is refused, so this provider cannot be used. */\n | { state: \"refused\"; reason: string };\n\n/**\n * Can the local runtime be used, and would using it install anything?\n *\n * Detection needs this rather than simply calling `getMemoryBudgetBytes`:\n * that goes through `importNodeLlamaCpp` and would install, which turns\n * `availableProviders()` — a function whose entire job is to REPORT what is\n * usable — into one that changes what is usable. Probing must not have the side\n * effect it is probing for.\n */\nexport async function nodeLlamaCppStatus(\n options: RuntimeInstallOptions = {},\n): Promise<RuntimeStatus> {\n const env = options.env ?? process.env;\n const directory = options.directory ?? defaultLlamaRuntimeDirectory(env);\n\n const probeImport =\n options.probeImport ?? ((): Promise<unknown> => import(\"node-llama-cpp\"));\n try {\n await probeImport();\n return { state: \"present\" };\n } catch (e) {\n // Only a genuine \"no such package\" means absent. An ABI mismatch, a missing\n // system library, or an unsupported Node all fail here too, and installing\n // over them would fetch the same broken package again while burying the\n // real cause under a download.\n if (!isModuleNotFound(e)) {\n return {\n state: \"refused\",\n reason: `node-llama-cpp is installed but failed to load (${describe(e)})`,\n };\n }\n // Genuinely absent — fall through to our own prefix.\n }\n if (existsSync(join(directory, SHIM))) return { state: \"present\" };\n\n if ((env[\"INFERENCE_NO_AUTO_INSTALL\"] ?? \"\") !== \"\") {\n return {\n state: \"refused\",\n reason: `node-llama-cpp is not installed and INFERENCE_NO_AUTO_INSTALL is set`,\n };\n }\n return { state: \"installable\", directory };\n}\n\n/**\n * In-flight installs, keyed by directory. docmeta and the ensemble runner both\n * work several items at once, so without this every worker that misses the\n * binding would spawn its own npm against one prefix.\n */\nconst installs = new Map<string, Promise<unknown>>();\n\nlet warnedInstall = false;\n\n/** Test seam: forget in-flight installs and re-arm the one-time warning. */\nexport function resetRuntimeInstall(): void {\n installs.clear();\n warnedInstall = false;\n}\n\n/**\n * Import `node-llama-cpp` from the library-owned prefix, installing it first if\n * it is not there.\n *\n * Callers reach this only after a plain `import(\"node-llama-cpp\")` has already\n * failed — a consumer who installed the peer themselves never gets here.\n */\nexport function importNodeLlamaCpp(\n options: RuntimeInstallOptions = {},\n): Promise<unknown> {\n const env = options.env ?? process.env;\n const directory = options.directory ?? defaultLlamaRuntimeDirectory(env);\n\n const existing = installs.get(directory);\n if (existing) return existing;\n\n const pending = fromPrefix(directory, env, options);\n // Drop a failed attempt so the next call retries: a download killed by a\n // flaky network must not poison the runtime for the rest of the process —\n // the same rule `load()` applies to weights.\n const guarded = pending.catch((e: unknown) => {\n if (installs.get(directory) === guarded) installs.delete(directory);\n throw e;\n });\n installs.set(directory, guarded);\n return guarded;\n}\n\nasync function fromPrefix(\n directory: string,\n env: Record<string, string | undefined>,\n options: RuntimeInstallOptions,\n): Promise<unknown> {\n const importShim =\n options.importShim ?? ((url: string): Promise<unknown> => import(url));\n const shim = join(directory, SHIM);\n\n if (existsSync(shim)) return importShim(pathToFileURL(shim).href);\n\n if ((env[\"INFERENCE_NO_AUTO_INSTALL\"] ?? \"\") !== \"\") {\n throw new InferenceError(\n `The llama-cpp provider needs node-llama-cpp, and INFERENCE_NO_AUTO_INSTALL ` +\n `is set. Install it yourself (npm i ${PACKAGE_SPEC}), unset ` +\n `INFERENCE_NO_AUTO_INSTALL to allow installing into ${directory}, or name ` +\n `a different provider.`,\n );\n }\n\n mkdirSync(directory, { recursive: true });\n await withLock(directory, async () => {\n // Another process may have finished while we waited for the lock.\n if (existsSync(shim)) return;\n warnInstalling(directory);\n await runInstall(directory, env, options);\n // Only now is the prefix complete, so only now does it get its marker.\n writeFileSync(shim, `export * from \"node-llama-cpp\";\\n`, \"utf8\");\n });\n\n return importShim(pathToFileURL(shim).href);\n}\n\nasync function runInstall(\n directory: string,\n env: Record<string, string | undefined>,\n options: RuntimeInstallOptions,\n): Promise<void> {\n // Anchor npm to this directory. Without a manifest here npm walks UP looking\n // for one, and would install into whatever project happens to be above us —\n // the exact mutation this module exists to avoid.\n const manifest = join(directory, \"package.json\");\n if (!existsSync(manifest)) {\n writeFileSync(\n manifest,\n `${JSON.stringify(\n {\n name: \"hawkeyexl-inference-runtime\",\n version: \"0.0.0\",\n private: true,\n description:\n \"Auto-installed runtime for @hawkeyexl/inference. Safe to delete.\",\n },\n null,\n 2,\n )}\\n`,\n \"utf8\",\n );\n }\n\n const exec = options.exec ?? realExec;\n const result = await exec(\n [\n \"npm\",\n \"install\",\n \"--prefix\",\n directory,\n PACKAGE_SPEC,\n \"--no-audit\",\n \"--no-fund\",\n ],\n { timeoutMs: options.timeoutMs ?? INSTALL_TIMEOUT_MS, env },\n );\n\n if (result.spawnError != null) {\n throw new InferenceError(\n `Could not run npm to install node-llama-cpp (${result.spawnError}). ` +\n `Install it yourself with: npm i ${PACKAGE_SPEC}, or name a provider ` +\n `that does not need it.`,\n );\n }\n if (result.timedOut) {\n throw new InferenceError(\n `Installing node-llama-cpp into ${directory} timed out. A source build ` +\n `can take a while — retry, raise the timeout, or install it yourself ` +\n `with: npm i ${PACKAGE_SPEC}.`,\n );\n }\n if (result.code !== 0) {\n throw new InferenceError(\n `Installing node-llama-cpp into ${directory} failed (exit ${String(\n result.code,\n )}).\\n${tail(result.stderr || result.stdout)}\\n` +\n `Install it yourself with: npm i ${PACKAGE_SPEC}, or name a provider ` +\n `that does not need it.`,\n );\n }\n}\n\n/** Enough npm output to diagnose the failure, not enough to bury the advice. */\nfunction tail(output: string, lines = 12): string {\n return output.trimEnd().split(/\\r?\\n/).slice(-lines).join(\"\\n\");\n}\n\n/**\n * Pulling a native module is not free, and the weights that follow are far less\n * free. Say so before it starts, not after a build has already stalled on it.\n */\nfunction warnInstalling(directory: string): void {\n if (warnedInstall) return;\n warnedInstall = true;\n console.warn(\n `inference: node-llama-cpp is not installed — fetching it into ${directory} ` +\n `so the local model can run. This is a one-time native install; set ` +\n `INFERENCE_NO_AUTO_INSTALL=1 to refuse it, or name a provider that does ` +\n `not need it.`,\n );\n}\n\n/**\n * Cross-process guard around one prefix.\n *\n * The in-process memo covers a worker pool inside one run; this covers two runs\n * started at once, where two npm processes writing one `node_modules` is how a\n * prefix ends up half-written.\n */\nasync function withLock(\n directory: string,\n fn: () => Promise<void>,\n): Promise<void> {\n const lock = join(directory, LOCK);\n const deadline = Date.now() + LOCK_WAIT_MS;\n\n for (;;) {\n try {\n writeFileSync(lock, String(process.pid), { flag: \"wx\" });\n break;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== \"EEXIST\") throw e;\n if (ageOf(lock) > LOCK_STALE_MS) {\n // Whoever held this died. Reclaiming a stale lock is safer than\n // blocking forever on a process that will never return.\n rmSync(lock, { force: true });\n continue;\n }\n if (existsSync(join(directory, SHIM))) return;\n if (Date.now() > deadline) {\n throw new InferenceError(\n `Timed out waiting for another process to install node-llama-cpp into ` +\n `${directory}. If nothing else is running, remove ${lock} and retry.`,\n );\n }\n await delay(250);\n }\n }\n\n try {\n await fn();\n } finally {\n rmSync(lock, { force: true });\n }\n}\n\n/** Infinity for a lock that vanished mid-check, so the caller retries. */\nfunction ageOf(path: string): number {\n try {\n return Date.now() - statSync(path).mtimeMs;\n } catch {\n return Number.POSITIVE_INFINITY;\n }\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * In-process local inference over GGUF weights via `node-llama-cpp`.\n *\n * Unlike every other provider here, this one owns weights: it downloads them\n * from Hugging Face on first use and holds gigabytes of RAM once loaded. Two\n * consequences shape the design.\n *\n * First, `node-llama-cpp` is a native module with prebuilt binaries per\n * platform and a CMake fallback. It is an OPTIONAL peer dependency reached\n * through a dynamic `import()`, so the four repos consuming this library pay\n * nothing — install cost or toolchain risk — unless they ask for local models.\n *\n * Second, everything real happens behind `LlamaRuntime`. Tests inject a fake\n * and never touch the network, the filesystem, or a GPU (the same seam as\n * `ExecFn` for the Claude CLI provider).\n */\nimport { InferenceError } from \"../types.js\";\nimport { buildCacheKey } from \"../cache.js\";\nimport { extractJson } from \"./openai-compat.js\";\nimport {\n aliasForTier,\n defaultLlamaModelsDirectory,\n isLlamaSelector,\n resolveLlamaModelRef,\n} from \"./llama-models.js\";\nimport { importNodeLlamaCpp, isModuleNotFound } from \"./llama-install.js\";\nimport type {\n CompleteJSONRequest,\n CompleteJSONResponse,\n InferenceProvider,\n TokenUsage,\n} from \"./types.js\";\n\nexport interface LlamaPromptOptions {\n /** JSON Schema converted to a GBNF grammar by the runtime. */\n schema: Record<string, unknown>;\n temperature: number;\n /** Thinking budget; 0 disables it. See the note in `completeJSON`. */\n thoughtTokens: number;\n maxTokens?: number;\n}\n\nexport interface LlamaPromptResult {\n text: string;\n usage?: TokenUsage;\n /**\n * Why generation stopped. `\"maxTokens\"` means the output was cut off, so the\n * text is almost certainly truncated JSON — see the guard in `completeJSON`.\n */\n stopReason?: string;\n}\n\nexport interface LlamaSession {\n prompt(text: string, options: LlamaPromptOptions): Promise<LlamaPromptResult>;\n dispose(): Promise<void>;\n}\n\nexport interface LlamaLoadedModel {\n createSession(systemPrompt: string): Promise<LlamaSession>;\n dispose(): Promise<void>;\n}\n\n/**\n * The whole of `node-llama-cpp` that this provider uses. Kept this narrow so a\n * test fake is a few lines and so the real adapter is the only place that\n * knows the upstream API shape.\n */\nexport interface LlamaRuntime {\n /**\n * Resolve an `hf:` URI or path to a local file inside `directory`,\n * downloading if needed.\n */\n resolveModelFile(uri: string, directory: string): Promise<string>;\n loadModel(path: string): Promise<LlamaLoadedModel>;\n /** Memory available for weights, in bytes — VRAM if there is a GPU, else RAM. */\n getMemoryBudgetBytes(): Promise<number>;\n}\n\nexport interface LlamaCppProviderOptions {\n /** Injected for tests; defaults to the real `node-llama-cpp` adapter. */\n runtime?: LlamaRuntime;\n /**\n * Thinking budget in tokens, default 0.\n *\n * Gemma 4 has a thinking mode, but a grammar constrains generation from\n * token 0 — so an unbudgeted model starts reasoning and gets cut off\n * mid-thought. Zero is the deterministic choice for judging; raise it if you\n * want reasoning before the JSON.\n */\n thoughtTokens?: number;\n maxTokens?: number;\n /**\n * Where to download and look for weights. Defaults to this library's own\n * directory — see `defaultLlamaModelsDirectory`.\n */\n modelsDirectory?: string;\n}\n\n/**\n * Loaded weights, keyed by directory + URI.\n *\n * `runEnsemble` issues N sequential calls and a load costs seconds and\n * gigabytes, so this is process-wide rather than per-instance: two providers\n * naming the same model share one copy. Values are the in-flight promise so\n * concurrent first calls coalesce instead of loading twice. The directory is\n * part of the key because the same URI in two directories is two files.\n */\nconst loadedModels = new Map<string, Promise<LlamaLoadedModel>>();\n\n/**\n * Free every loaded model.\n *\n * A standalone function rather than a `dispose()` on `InferenceProvider`:\n * adding one to the contract would make all five providers carry a lifecycle\n * only this one has. Short-lived processes can skip it.\n */\nexport async function disposeLlamaModels(): Promise<void> {\n const pending = [...loadedModels.values()];\n loadedModels.clear();\n await Promise.all(\n pending.map((p) => p.then((m) => m.dispose()).catch(() => undefined)),\n );\n}\n\nexport class LlamaCppProvider implements InferenceProvider {\n private readonly uri: string;\n private readonly runtime: LlamaRuntime;\n private readonly thoughtTokens: number;\n private readonly maxTokens: number | undefined;\n private readonly modelsDirectory: string;\n /**\n * Loaded-model key: the same URI in two directories is two different files.\n * Built with `buildCacheKey` so its parts are length-prefixed — a plain join\n * would let two different (directory, uri) pairs collide and hand a provider\n * back the wrong weights.\n */\n private readonly cacheKey: string;\n\n constructor(\n private readonly model: string,\n options: LlamaCppProviderOptions = {},\n ) {\n if (isLlamaSelector(model)) {\n throw new InferenceError(\n `llama-cpp model \"${model}\" is a selector. Constructing a provider ` +\n `directly needs a concrete model (e.g. \"${aliasForTier(\"balanced\")}\") — use ` +\n `makeProviderAsync to resolve a selector against this machine.`,\n );\n }\n this.uri = resolveLlamaModelRef(model);\n this.runtime = options.runtime ?? defaultLlamaRuntime();\n this.thoughtTokens = options.thoughtTokens ?? 0;\n this.maxTokens = options.maxTokens;\n this.modelsDirectory =\n options.modelsDirectory ?? defaultLlamaModelsDirectory();\n this.cacheKey = buildCacheKey([this.modelsDirectory, this.uri]);\n }\n\n provider(): string {\n return \"llama-cpp\";\n }\n\n modelName(): string {\n return this.model;\n }\n\n async completeJSON(req: CompleteJSONRequest): Promise<CompleteJSONResponse> {\n const model = await this.load();\n // A fresh session per call: the contract is single-shot, and reusing one\n // would leak the previous run's turns into this one's context.\n const session = await model.createSession(systemPromptFor(req));\n try {\n const result = await session.prompt(req.user, {\n schema: req.schema,\n temperature: req.temperature,\n thoughtTokens: this.thoughtTokens,\n ...(this.maxTokens != null ? { maxTokens: this.maxTokens } : {}),\n });\n // A run cut off at the token or context limit leaves truncated JSON.\n // Without this it surfaces as \"failed schema validation\" — or worse,\n // extractJson's brace-slicing fallback salvages a wrong-but-parseable\n // object — and the retry burns another full local inference to fail the\n // same way. Same guard as the Anthropic provider's max_tokens check.\n if (result.stopReason === \"maxTokens\") {\n throw new Error(\n `llama-cpp generation hit the token limit before completing the JSON` +\n `${this.maxTokens != null ? ` (maxTokens: ${this.maxTokens})` : \"\"}` +\n ` — raise llamaCpp.maxTokens, or shorten the prompt if the context is full.`,\n );\n }\n return { json: extractJson(result.text), usage: result.usage };\n } finally {\n await session.dispose().catch(() => undefined);\n }\n }\n\n private load(): Promise<LlamaLoadedModel> {\n const existing = loadedModels.get(this.cacheKey);\n if (existing) return existing;\n const pending = (async () => {\n const path = await this.runtime.resolveModelFile(\n this.uri,\n this.modelsDirectory,\n );\n return this.runtime.loadModel(path);\n })();\n // Drop a failed load so the next call retries — a download interrupted by\n // a flaky network must not poison the model for the rest of the process.\n // Only evict our OWN entry: a dispose plus a re-load between the failure\n // and this handler would otherwise orphan the newer model, leaking it.\n const guarded = pending.catch((e: unknown) => {\n if (loadedModels.get(this.cacheKey) === guarded) {\n loadedModels.delete(this.cacheKey);\n }\n throw e;\n });\n loadedModels.set(this.cacheKey, guarded);\n return guarded;\n }\n}\n\n/**\n * The grammar constrains the SHAPE of the output, but `node-llama-cpp` never\n * shows the schema to the model — so `description` fields, which is where\n * consumers put their domain instructions (ADR 01001), would be invisible.\n * Restating the schema is the same fix the Claude CLI provider and the\n * OpenAI json_object fallback already use.\n */\nfunction systemPromptFor(req: CompleteJSONRequest): string {\n return `${req.system}\\n\\nRespond with ONLY a JSON object conforming to this JSON Schema:\\n${JSON.stringify(\n req.schema,\n )}`;\n}\n\n/** Cached so repeated provider construction imports the native module once. */\nlet runtimePromise: Promise<LlamaRuntime> | undefined;\n\n/**\n * Lazy adapter over the real `node-llama-cpp`. Every method defers to the\n * dynamic import, so constructing a provider for a fully-cached run never\n * loads the native binary.\n */\nexport function defaultLlamaRuntime(): LlamaRuntime {\n const real = (): Promise<LlamaRuntime> =>\n // Drop a failed init so the next call retries. A GPU that failed to\n // initialise, or a binary still being extracted by a concurrent install,\n // must not poison the runtime for the rest of the process — the same rule\n // `load()` applies to weights.\n (runtimePromise ??= loadNodeLlamaCpp().catch((e: unknown) => {\n runtimePromise = undefined;\n throw e;\n }));\n return {\n resolveModelFile: (uri, directory) =>\n real().then((r) => r.resolveModelFile(uri, directory)),\n loadModel: (path) => real().then((r) => r.loadModel(path)),\n getMemoryBudgetBytes: () => real().then((r) => r.getMemoryBudgetBytes()),\n };\n}\n\nasync function loadNodeLlamaCpp(): Promise<LlamaRuntime> {\n let mod: typeof import(\"node-llama-cpp\");\n try {\n mod = await import(\"node-llama-cpp\");\n } catch (e) {\n // A package that resolved and then failed to load — ABI mismatch, missing\n // system library, unsupported Node — is not a missing package. Installing\n // over it would fetch the same broken thing again and replace a precise\n // error with a download.\n if (!isModuleNotFound(e)) {\n throw new InferenceError(\n `node-llama-cpp is installed but failed to load (${\n e instanceof Error ? e.message : String(e)\n }). This is the copy resolved from your own node_modules, so ` +\n `reinstalling it here will not help — check the Node version and the ` +\n `platform build.`,\n );\n }\n // Genuinely absent, so fall back to the library's own prefix — installing\n // it there if needed. npm does not install optional peers, and detection\n // ends at this provider precisely because it needs no credentials, so\n // refusing here would strand the one machine `auto` exists to serve.\n // Resetting `runtimePromise` is the caller's job — see `defaultLlamaRuntime`.\n mod = (await importNodeLlamaCpp()) as typeof import(\"node-llama-cpp\");\n }\n\n const { getLlama, resolveModelFile, LlamaChatSession, TokenMeter } = mod;\n const llama = await getLlama();\n\n return {\n // `directory` is this library's own, not node-llama-cpp's global default —\n // owning it is what makes `clearLlamaModels` safe.\n resolveModelFile: (uri, directory) => resolveModelFile(uri, { directory }),\n\n async loadModel(path) {\n const model = await llama.loadModel({ modelPath: path });\n return {\n async createSession(systemPrompt) {\n const context = await model.createContext();\n const sequence = context.getSequence();\n const session = new LlamaChatSession({\n contextSequence: sequence,\n systemPrompt,\n });\n return {\n async prompt(text, options) {\n const grammar = await llama.createGrammarForJsonSchema(\n options.schema as Parameters<\n typeof llama.createGrammarForJsonSchema\n >[0],\n );\n const before = sequence.tokenMeter.getState();\n const result = await session.promptWithMeta(text, {\n grammar,\n temperature: options.temperature,\n budgets: { thoughtTokens: options.thoughtTokens },\n ...(options.maxTokens != null\n ? { maxTokens: options.maxTokens }\n : {}),\n });\n // promptWithMeta does not report usage; the sequence's meter does.\n const diff = TokenMeter.diff(sequence.tokenMeter, before);\n return {\n text: result.responseText,\n stopReason: result.stopReason,\n usage: {\n inputTokens: diff.usedInputTokens,\n outputTokens: diff.usedOutputTokens,\n },\n };\n },\n async dispose() {\n await context.dispose();\n },\n };\n },\n async dispose() {\n await model.dispose();\n },\n };\n },\n\n async getMemoryBudgetBytes() {\n const { totalmem } = await import(\"node:os\");\n // Half of RAM is what a judge can reasonably claim on a shared machine;\n // a GPU's free VRAM is usable outright.\n const ramBudget = totalmem() / 2;\n try {\n const vram = await llama.getVramState();\n // The LARGER of the two, not VRAM in preference to RAM: llama.cpp\n // offloads the layers that fit onto the GPU and keeps the rest in\n // system RAM, so a small GPU beside plenty of RAM still runs a big\n // model. Sizing off VRAM alone would idle most of such a machine.\n return Math.max(vram.free, ramBudget);\n } catch {\n // CPU-only builds and probe failures are normal, never fatal.\n return ramBudget;\n }\n },\n };\n}\n","/**\n * Which providers can this machine actually use, and which should it pick?\n *\n * Every probe goes through a seam that already exists — environment variables,\n * `ExecFn`, `LlamaRuntime` — so the whole matrix is exercisable offline with no\n * network, no subprocess, and no weights.\n *\n * Detection is async because two of the four probes are: running the Claude CLI\n * and loading the optional `node-llama-cpp` binding. That is why only\n * `resolveProviderIdentityAsync`/`makeProviderAsync` can resolve an `auto`\n * provider, and the synchronous twins throw instead of guessing.\n */\nimport { InferenceError } from \"../types.js\";\nimport { realExec } from \"../exec.js\";\nimport { defaultLlamaRuntime } from \"./llama-cpp.js\";\nimport { nodeLlamaCppStatus } from \"./llama-install.js\";\nimport type { LlamaRuntime } from \"./llama-cpp.js\";\nimport type { ProviderName, ProviderSpec } from \"./index.js\";\n\n/**\n * Priority order. `mock` is deliberately absent: it answers `{ json: {} }`\n * unless scripted, which would sail through as a non-error result — the exact\n * opposite of the \"an errored run is recorded, never coerced\" invariant the\n * consuming eval tools depend on. It must always be asked for by name.\n */\nexport const DETECTION_ORDER: readonly ProviderName[] = [\n \"anthropic\",\n \"openai\",\n \"claude-cli\",\n \"llama-cpp\",\n];\n\nconst DEFAULT_KEY_ENV: Partial<Record<ProviderName, string>> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n};\n\ninterface Probe {\n available: boolean;\n /** Why not, phrased as advice — this is what the aggregate error prints. */\n reason?: string;\n}\n\n/**\n * Probes the provider's DEFAULT key variable, deliberately ignoring\n * `spec.apiKeyEnv`.\n *\n * `apiKeyEnv` is one field shared by both API providers, and detection only\n * runs when no provider was named — so a custom name cannot say which provider\n * it belongs to. Honouring it here made a single custom variable satisfy both\n * probes, and anthropic then won on priority: a spec carrying an OpenAI key\n * under a custom name selected `anthropic` and 401'd at call time.\n *\n * A custom `apiKeyEnv` still applies in full once a provider is named — it just\n * cannot be what chooses one.\n */\nfunction hasKey(provider: \"anthropic\" | \"openai\"): boolean {\n // An empty string is not a key; treating it as one produces a 401 later.\n return (process.env[DEFAULT_KEY_ENV[provider]!] ?? \"\") !== \"\";\n}\n\n/**\n * Memoised **per command**: spawning a process costs ~150ms and detection may\n * run on every provider construction, but a spec naming a different executable\n * is a different question — memoising on one key would make a fallback to an\n * absolute path silently inherit the bare command's failure.\n *\n * Environment probes stay unmemoised: they are free, and a consumer may\n * legitimately set a key part-way through a process.\n */\nconst cliProbes = new Map<string, Promise<boolean>>();\n\n/** Test seam: forget the memoised Claude CLI probes. */\nexport function resetClaudeCliProbe(): void {\n cliProbes.clear();\n}\n\nfunction probeClaudeCli(spec: ProviderSpec): Promise<boolean> {\n const exec = spec.exec ?? realExec;\n const command = spec.command ?? \"claude\";\n const cached = cliProbes.get(command);\n if (cached) return cached;\n const probing = exec([command, \"--version\"], { timeoutMs: 10_000 })\n .then((r) => r.code === 0 && !r.timedOut && r.spawnError == null)\n .catch(() => false);\n cliProbes.set(command, probing);\n return probing;\n}\n\nasync function probeLlamaCpp(spec: ProviderSpec): Promise<Probe> {\n const injected = spec.llamaRuntime ?? spec.llamaCpp?.runtime;\n if (injected) return budgetProbe(injected);\n\n // Ask whether the binding is usable BEFORE touching it. Going straight to\n // `getMemoryBudgetBytes` would route through the auto-installer, so merely\n // asking `availableProviders()` what this machine can do would fetch a native\n // module — a query with a side effect, and a slow one.\n const status = await nodeLlamaCppStatus();\n if (status.state === \"refused\") {\n return { available: false, reason: status.reason };\n }\n if (status.state === \"installable\") {\n // Usable, at the cost of an install that happens when it is actually\n // needed. `warnInstalling` announces it at that point.\n return { available: true };\n }\n // Present: load it for real, which is also what catches a binding that is\n // installed but whose backend fails to start.\n return budgetProbe(defaultLlamaRuntime());\n}\n\n/**\n * The same call the `auto` MODEL selector makes, so choosing llama-cpp here\n * costs nothing extra: it is loaded either way.\n */\nfunction budgetProbe(runtime: LlamaRuntime): Promise<Probe> {\n return runtime.getMemoryBudgetBytes().then(\n () => ({ available: true }),\n (e: unknown) => ({\n available: false,\n reason:\n e instanceof Error && /node-llama-cpp/.test(e.message)\n ? \"node-llama-cpp is not installed (npm i node-llama-cpp)\"\n : `node-llama-cpp could not start (${\n e instanceof Error ? e.message : String(e)\n })`,\n }),\n );\n}\n\nasync function probe(\n provider: ProviderName,\n spec: ProviderSpec,\n): Promise<Probe> {\n switch (provider) {\n case \"anthropic\":\n return hasKey(\"anthropic\")\n ? { available: true }\n : {\n available: false,\n reason: `ANTHROPIC_API_KEY is not set`,\n };\n case \"openai\":\n // A local OpenAI-compatible server needs no key — same rule the\n // OpenAICompatProvider constructor applies.\n return hasKey(\"openai\") || spec.baseUrl\n ? { available: true }\n : {\n available: false,\n reason: `OPENAI_API_KEY is not set and no baseUrl was given`,\n };\n case \"claude-cli\":\n return (await probeClaudeCli(spec))\n ? { available: true }\n : {\n available: false,\n reason: `could not run \\`${spec.command ?? \"claude\"}\\` (is the Claude CLI installed?)`,\n };\n case \"llama-cpp\":\n return probeLlamaCpp(spec);\n default:\n return { available: false, reason: \"not auto-selectable\" };\n }\n}\n\n/**\n * Every provider this machine could use right now, in priority order.\n *\n * Useful for showing a picker or explaining a fallback; `detectProvider` is\n * the same sweep with the first hit returned.\n */\nexport async function availableProviders(\n spec: ProviderSpec = {},\n): Promise<ProviderName[]> {\n const probes = await Promise.all(\n DETECTION_ORDER.map((name) => probe(name, spec)),\n );\n return DETECTION_ORDER.filter((_, i) => probes[i]!.available);\n}\n\n/**\n * The highest-priority provider this machine can use.\n *\n * Throws an `InferenceError` naming every provider and why each was\n * unavailable — far more actionable than the `Unknown provider \"undefined\"`\n * this replaces.\n */\nexport async function detectProvider(\n spec: ProviderSpec = {},\n): Promise<ProviderName> {\n // Sequential, not Promise.all: the probes get dramatically more expensive\n // down the list, and the cheapest usually wins. Reading an environment\n // variable costs microseconds, spawning the Claude CLI ~150ms, and loading\n // the node-llama-cpp binding ~850ms — the last of which also initialises the\n // llama backend and allocates GPU context. Probing eagerly would pay all of\n // that on every construction just to pick `anthropic` off an env var, and\n // would touch the GPU for a provider that is never used.\n const reasons: string[] = [];\n for (const name of DETECTION_ORDER) {\n const result = await probe(name, spec);\n if (result.available) {\n warnSelected(name, spec.provider === \"auto\");\n return name;\n }\n reasons.push(` ${name.padEnd(10)} — ${result.reason}`);\n }\n throw new InferenceError(\n `No inference provider is available. Tried:\\n${reasons.join(\"\\n\")}\\n` +\n `Pass an explicit \\`provider\\`, set one of the keys above, or install node-llama-cpp.`,\n );\n}\n\nlet warnedSelection = false;\nlet warnedDownload = false;\n\n/** Test seam: reset the once-per-process auto-detection warnings. */\nexport function resetProviderDetectionWarning(): void {\n warnedSelection = false;\n warnedDownload = false;\n}\n\n/**\n * These are eval tools: a run whose provider silently changed because an\n * environment variable moved is a run whose verdicts and cache are no longer\n * comparable to the last one. Say which provider was picked, once.\n */\nfunction warnSelected(provider: ProviderName, wasExplicitAuto: boolean): void {\n if (warnedSelection) return;\n warnedSelection = true;\n // `provider: \"auto\"` IS a specification — saying otherwise sends someone\n // hunting their config for a field they did set.\n const because = wasExplicitAuto ? `provider \"auto\"` : \"no provider specified\";\n console.warn(\n `inference: ${because} — auto-selected \"${provider}\". ` +\n `Pass an explicit \\`provider\\` to pin it.`,\n );\n}\n\n/**\n * Falling back to a local model can mean pulling gigabytes. Say so before it\n * starts, not after a CI job has already stalled on it.\n */\nexport function warnPendingDownload(model: string, sizeBytes: number): void {\n if (warnedDownload) return;\n warnedDownload = true;\n console.warn(\n `inference: \"${model}\" is not downloaded yet — the first run will fetch ` +\n `~${(sizeBytes / 1e9).toFixed(2)} GB. Pre-fetch it, or pass an explicit ` +\n `\\`provider\\` to avoid the local model entirely.`,\n );\n}\n","/**\n * Provider factory over a library-owned `ProviderSpec`.\n *\n * The spec is deliberately NOT any consumer's config type. Every consumer maps\n * its own config into this flat shape, so adding a provider here does not\n * require touching four config schemas, and no consumer has to model its\n * config on another's to reuse this layer (ADR 01000).\n */\nimport { InferenceError } from \"../types.js\";\nimport { warnIfUnsupportedNode } from \"../runtime.js\";\nimport type { Pricing } from \"../cost.js\";\nimport { AnthropicProvider } from \"./anthropic.js\";\nimport { OpenAICompatProvider } from \"./openai-compat.js\";\nimport { ClaudeCliProvider } from \"./claude-cli.js\";\nimport { MockProvider } from \"./mock.js\";\nimport { LlamaCppProvider, defaultLlamaRuntime } from \"./llama-cpp.js\";\nimport {\n LLAMA_MODELS,\n aliasForTier,\n defaultLlamaModelsDirectory,\n isLlamaSelector,\n isModelDownloaded,\n tierForBudget,\n} from \"./llama-models.js\";\nimport { detectProvider, warnPendingDownload } from \"./detect.js\";\nimport type { AnthropicProviderOptions } from \"./anthropic.js\";\nimport type { OpenAICompatProviderOptions } from \"./openai-compat.js\";\nimport type { MockResponse } from \"./mock.js\";\nimport type { LlamaCppProviderOptions, LlamaRuntime } from \"./llama-cpp.js\";\nimport type { LlamaTier } from \"./llama-models.js\";\nimport type { ExecFn, InferenceProvider } from \"./types.js\";\n\nexport type ProviderName =\n | \"anthropic\"\n | \"openai\"\n | \"claude-cli\"\n | \"mock\"\n | \"llama-cpp\";\n\n/** A concrete provider, or `\"auto\"` to detect one. */\nexport type ProviderSelector = ProviderName | \"auto\";\n\nexport interface ProviderSpec {\n /**\n * Omitting this is identical to `\"auto\"`: the highest-priority provider this\n * machine can actually use is detected, ending at the free local model.\n * Resolving it needs `makeProviderAsync`/`resolveProviderIdentityAsync`.\n */\n provider?: ProviderSelector;\n /** null/undefined selects the per-provider default. */\n model?: string | null;\n /** Env var NAME holding the API key; null/undefined selects the default. */\n apiKeyEnv?: string | null;\n /** openai only. */\n baseUrl?: string;\n /** claude-cli only: the executable to run. */\n command?: string;\n /** claude-cli only: subprocess timeout. */\n timeoutMs?: number;\n /**\n * Pricing override for this model. Not used to construct the provider —\n * carried here so a consumer passes one object to both `makeProvider` and\n * `pricingFor`.\n */\n pricing?: Pricing;\n /** Provider-specific tuning, ignored by the other providers. */\n anthropic?: AnthropicProviderOptions;\n openai?: OpenAICompatProviderOptions;\n llamaCpp?: LlamaCppProviderOptions;\n /** Test seam for the claude-cli provider. */\n exec?: ExecFn;\n /** Test seam for the llama-cpp provider. */\n llamaRuntime?: LlamaRuntime;\n /** Scripted responses for the mock provider; defaults to a single empty object. */\n mockResponses?: MockResponse[];\n}\n\nexport const DEFAULT_MODELS: Record<ProviderName, string> = {\n anthropic: \"claude-sonnet-4-5\",\n openai: \"gpt-4o-mini\",\n \"claude-cli\": \"claude-sonnet-4-5\",\n mock: \"mock-model\",\n // A selector, not a pinned model: which weights a tier points at is then a\n // catalog change rather than an API change. Resolving it needs the async\n // factory — see `resolveProviderIdentityAsync`.\n \"llama-cpp\": \"auto\",\n};\n\nconst DEFAULT_API_KEY_ENV: Record<string, string> = {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n};\n\nexport const DEFAULT_OPENAI_BASE_URL = \"https://api.openai.com/v1\";\n\nexport interface ProviderIdentity {\n /** Always concrete — never `\"auto\"`, so it is safe as cache-key material. */\n provider: ProviderName;\n model: string;\n}\n\n/**\n * Resolve the provider name and model WITHOUT constructing the provider —\n * cache keys and pricing need the identity, but construction may require an\n * API key that a fully-cached run never uses.\n *\n * Throws for an unresolved `llama-cpp` selector. That is deliberate: picking a\n * tier weighs GPU VRAM, which needs an await, and returning the literal\n * \"auto\" as cache-key material would let a 2 GB and a 12 GB model share cached\n * verdicts — and give different results per machine under one key. Use\n * `resolveProviderIdentityAsync` for selectors.\n */\nexport function resolveProviderIdentity(spec: ProviderSpec): ProviderIdentity {\n if (spec.provider == null || spec.provider === \"auto\") {\n throw new InferenceError(\n `No provider specified. Detecting one probes the environment, the Claude ` +\n `CLI and the local model runtime, which cannot be done synchronously — ` +\n `use resolveProviderIdentityAsync/makeProviderAsync, or name a provider ` +\n `(${Object.keys(DEFAULT_MODELS).join(\", \")}).`,\n );\n }\n const model = spec.model ?? DEFAULT_MODELS[spec.provider] ?? \"unknown\";\n if (spec.provider === \"llama-cpp\" && isLlamaSelector(model)) {\n throw new InferenceError(\n `llama-cpp model \"${model}\" is a selector and cannot be resolved ` +\n `synchronously — picking a tier probes GPU memory. Use ` +\n `resolveProviderIdentityAsync/makeProviderAsync, or name a concrete ` +\n `model (e.g. \"${aliasForTier(\"balanced\")}\").`,\n );\n }\n return { provider: spec.provider, model };\n}\n\n/**\n * Selector-aware identity resolution. Returns the CONCRETE model a selector\n * resolved to, so the cache key names the weights that actually ran.\n *\n * Every other provider delegates to the synchronous form, so a consumer can\n * switch to this wholesale.\n */\nexport async function resolveProviderIdentityAsync(\n spec: ProviderSpec,\n): Promise<ProviderIdentity> {\n // A model name belongs to exactly one provider, so it cannot be carried into\n // whichever provider detection happens to pick: `{ model: \"gpt-4o-mini\" }` on\n // a machine with an Anthropic key selected `anthropic` and then 404'd at call\n // time, after the caller had already paid for detection. `null` still means\n // \"use the default\" — only a real name is ambiguous.\n if (\n (spec.provider == null || spec.provider === \"auto\") &&\n spec.model != null\n ) {\n throw new InferenceError(\n `Model \"${spec.model}\" was given without a provider, and a model name ` +\n `does not say which provider owns it. Name the provider too ` +\n `(${Object.keys(DEFAULT_MODELS).join(\", \")}), or drop the model to ` +\n `take the detected provider's default.`,\n );\n }\n\n // Provider first, then the model logic for whichever provider won.\n const provider =\n spec.provider == null || spec.provider === \"auto\"\n ? await detectProvider(spec)\n : spec.provider;\n const resolved: ProviderSpec = { ...spec, provider };\n\n const model = spec.model ?? DEFAULT_MODELS[provider] ?? \"unknown\";\n if (provider !== \"llama-cpp\" || !isLlamaSelector(model)) {\n return resolveProviderIdentity(resolved);\n }\n const tier: LlamaTier =\n model === \"auto\" ? await probeTier(llamaRuntimeFor(spec)) : model;\n return { provider, model: aliasForTier(tier) };\n}\n\n/**\n * Warn before a multi-gigabyte download, but only once the caller has actually\n * committed to running.\n *\n * Deliberately NOT in `resolveProviderIdentityAsync`: that resolves an identity\n * *without* constructing anything, which is exactly what a fully-cached run\n * does — and such a run downloads nothing, so warning there announces gigabytes\n * that never move.\n */\nfunction warnIfDownloadPending(spec: ProviderSpec, model: string): void {\n const entry = LLAMA_MODELS[model];\n if (!entry) return;\n const directory =\n spec.llamaCpp?.modelsDirectory ?? defaultLlamaModelsDirectory();\n if (!isModelDownloaded(model, directory)) {\n warnPendingDownload(model, entry.sizeBytes);\n }\n}\n\n/**\n * A runtime can be injected either as `spec.llamaRuntime` or inside\n * `spec.llamaCpp` — `makeProvider` honours both, so selector resolution must\n * too. Missing one sends the probe to the real native module and throws for a\n * consumer whose whole point was to avoid it.\n */\nfunction llamaRuntimeFor(spec: ProviderSpec): LlamaRuntime | undefined {\n return spec.llamaRuntime ?? spec.llamaCpp?.runtime;\n}\n\nasync function probeTier(runtime: LlamaRuntime | undefined): Promise<LlamaTier> {\n const source = runtime ?? defaultLlamaRuntime();\n return tierForBudget(await source.getMemoryBudgetBytes());\n}\n\nexport function makeProvider(spec: ProviderSpec): InferenceProvider {\n // First use of the library, for anyone who goes through the factory — before\n // the spec is even validated, so an unsupported Node is named ahead of any\n // error it might be the real cause of.\n warnIfUnsupportedNode();\n const { model } = resolveProviderIdentity(spec);\n\n switch (spec.provider) {\n case \"anthropic\":\n return new AnthropicProvider(\n model,\n spec.apiKeyEnv ?? DEFAULT_API_KEY_ENV[\"anthropic\"]!,\n spec.anthropic ?? {},\n );\n case \"openai\":\n return new OpenAICompatProvider(\n spec.baseUrl ?? DEFAULT_OPENAI_BASE_URL,\n model,\n spec.apiKeyEnv ?? DEFAULT_API_KEY_ENV[\"openai\"]!,\n undefined,\n spec.openai ?? {},\n );\n case \"claude-cli\":\n return new ClaudeCliProvider(\n model,\n spec.command ?? \"claude\",\n spec.exec,\n spec.timeoutMs,\n );\n case \"mock\":\n // Offline smoke-testing seam: proposes nothing unless scripted.\n return new MockProvider(spec.mockResponses ?? [{ json: {} }], model);\n case \"llama-cpp\":\n return new LlamaCppProvider(model, {\n ...(spec.llamaCpp ?? {}),\n ...(spec.llamaRuntime ? { runtime: spec.llamaRuntime } : {}),\n });\n default:\n throw new InferenceError(\n `Unknown provider \"${String(spec.provider)}\". Available: ${Object.keys(\n DEFAULT_MODELS,\n ).join(\", \")}.`,\n );\n }\n}\n\n/**\n * Selector-aware provider construction. Resolves a `llama-cpp` selector\n * against this machine first, so the returned provider's `modelName()` — and\n * therefore the cache key — names the weights it will actually load.\n *\n * Every other provider delegates to `makeProvider`.\n */\nexport async function makeProviderAsync(\n spec: ProviderSpec,\n): Promise<InferenceProvider> {\n // Both halves must be threaded through: passing only the model would leave a\n // detected provider as `undefined` and throw in `makeProvider`.\n const { provider, model } = await resolveProviderIdentityAsync(spec);\n if (provider === \"llama-cpp\") warnIfDownloadPending(spec, model);\n return makeProvider({ ...spec, provider, model });\n}\n","/**\n * Reclaiming disk space from downloaded GGUF weights.\n *\n * This is straightforward because the models directory belongs to this library\n * alone (see `defaultLlamaModelsDirectory`). node-llama-cpp's own global\n * directory is shared with its CLI and anything else on the machine, so\n * clearing THAT would destroy models this library never downloaded; owning a\n * directory removes the hazard rather than defending against it.\n */\nimport { rmSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n blobNameFor,\n defaultLlamaModelsDirectory,\n listModelDirectory,\n matchesModelBlob,\n} from \"./llama-models.js\";\nimport { disposeLlamaModels } from \"./llama-cpp.js\";\n\nexport interface ClearedModelFile {\n path: string;\n sizeBytes: number;\n}\n\nexport interface ClearLlamaModelsResult {\n /** Files removed, or that would be removed under `dryRun`. */\n files: ClearedModelFile[];\n freedBytes: number;\n directory: string;\n dryRun: boolean;\n}\n\nexport interface ClearLlamaModelsOptions {\n /** Defaults to this library's own models directory. */\n directory?: string;\n /** Clear only these models (alias or `hf:` URI); default clears all. */\n models?: string[];\n /** Report what would be removed without deleting anything. */\n dryRun?: boolean;\n}\n\n/**\n * Delete downloaded GGUF weights and interrupted partial downloads.\n *\n * Loaded models are disposed first: on Windows the weights are memory-mapped\n * while loaded, and deleting an open file fails with EBUSY/EPERM.\n */\nexport async function clearLlamaModels(\n options: ClearLlamaModelsOptions = {},\n): Promise<ClearLlamaModelsResult> {\n const directory = options.directory ?? defaultLlamaModelsDirectory();\n const dryRun = options.dryRun ?? false;\n // Resolve BEFORE touching disk so an unknown name fails without having\n // deleted half the set.\n const wanted = options.models?.map(blobNameFor);\n\n if (!dryRun) await disposeLlamaModels();\n\n const files: ClearedModelFile[] = [];\n for (const entry of listModelDirectory(directory)) {\n // Only ever weights — a stray config or log in this directory is safe, and\n // so is anything else if `directory` was pointed somewhere shared.\n if (!isModelBlob(entry)) continue;\n if (wanted && !wanted.some((name) => matchesModelBlob(entry, name))) {\n continue;\n }\n const path = join(directory, entry);\n const sizeBytes = sizeOf(path);\n if (sizeBytes === undefined) continue;\n if (!dryRun) {\n try {\n rmSync(path);\n } catch {\n // A file held open by another process is not this call's business to\n // force; report only what actually went away.\n continue;\n }\n }\n files.push({ path, sizeBytes });\n }\n\n return {\n files,\n freedBytes: files.reduce((total, file) => total + file.sizeBytes, 0),\n directory,\n dryRun,\n };\n}\n\nfunction isModelBlob(entry: string): boolean {\n return entry.endsWith(\".gguf\") || entry.endsWith(\".gguf.ipull\");\n}\n\nfunction sizeOf(path: string): number | undefined {\n try {\n return statSync(path).size;\n } catch {\n return undefined;\n }\n}\n","/**\n * One schema-validated completion, with a single retry.\n *\n * The invariant every consumer depends on: a run that cannot produce\n * schema-valid JSON after the retry is recorded as an ERROR, not dropped and\n * not coerced. Downstream, an errored run counts against consensus — it can\n * push a result toward human review, but it can never produce a silent pass.\n */\nimport { Ajv2020 } from \"ajv/dist/2020.js\";\nimport type { ValidateFunction } from \"ajv\";\nimport { warnIfUnsupportedNode } from \"./runtime.js\";\nimport type { InferenceProvider, TokenUsage } from \"./providers/types.js\";\n\n/** One attempt at a schema-constrained completion. */\nexport interface InferenceRun<T = unknown> {\n /** Absent when the run errored (invalid JSON after retry, API failure). */\n result?: T;\n error?: string;\n provider: string;\n model: string;\n cached: boolean;\n usage?: TokenUsage;\n durationMs: number;\n}\n\nexport interface CompleteValidatedOptions {\n provider: InferenceProvider;\n system: string;\n user: string;\n schema: Record<string, unknown>;\n temperature?: number;\n /**\n * Attempts before recording an error. Default 2 (one initial call plus one\n * retry) — matches the behavior all three source projects shipped.\n */\n attempts?: number;\n /**\n * Pre-compiled validator. Compiling Ajv per call is wasteful in an ensemble\n * loop, so `runEnsemble` compiles once and passes it down.\n */\n validate?: ValidateFunction;\n}\n\nconst validatorCache = new WeakMap<object, ValidateFunction>();\n\n/** Compile once per schema object identity — Ajv compilation is not cheap. */\nexport function validatorFor(\n schema: Record<string, unknown>,\n): ValidateFunction {\n const cached = validatorCache.get(schema);\n if (cached) return cached;\n // A fresh Ajv per distinct schema object, not one shared instance: Ajv keeps\n // a registry keyed by `$id`, so a caller that rebuilds an equal schema object\n // per call (spreading VERDICT_SCHEMA to override descriptions, say) misses\n // the identity cache above and would hit \"schema with key or id ... already\n // exists\" on the second compile. Instances are held only by this WeakMap, so\n // they are collected with the schemas that own them.\n const compiled = new Ajv2020({ allErrors: true }).compile(schema);\n validatorCache.set(schema, compiled);\n return compiled;\n}\n\nexport async function completeValidatedJSON<T = unknown>(\n options: CompleteValidatedOptions,\n): Promise<InferenceRun<T>> {\n // The other half of \"first use\": a consumer that constructs a provider\n // directly never touches `makeProvider`, but everything still funnels here.\n warnIfUnsupportedNode();\n const {\n provider,\n system,\n user,\n schema,\n temperature = 0,\n attempts = 2,\n } = options;\n const validate = options.validate ?? validatorFor(schema);\n\n const start = Date.now();\n const base = {\n provider: provider.provider(),\n model: provider.modelName(),\n cached: false,\n };\n\n let lastError = \"unknown error\";\n for (let attempt = 0; attempt < attempts; attempt++) {\n try {\n const response = await provider.completeJSON({\n system,\n user,\n schema,\n temperature,\n });\n if (validate(response.json)) {\n return {\n ...base,\n result: response.json as T,\n usage: response.usage,\n durationMs: Date.now() - start,\n };\n }\n lastError = `Response failed schema validation: ${(validate.errors ?? [])\n .map((e) => `${e.instancePath} ${e.message}`)\n .join(\"; \")}`;\n } catch (e) {\n lastError = e instanceof Error ? e.message : String(e);\n }\n }\n return { ...base, error: lastError, durationMs: Date.now() - start };\n}\n","/**\n * Cost tracking: token usage priced from a small static table, overridable per\n * model by the caller. Unknown models cost 0 (unknown), never a guess — a\n * fabricated price is worse than an absent one when a budget gate depends on it.\n */\nimport type { TokenUsage } from \"./providers/types.js\";\n\nexport interface Pricing {\n inputPerMTok: number;\n outputPerMTok: number;\n}\n\n/**\n * USD per million tokens. Entries are base names; pinned variants\n * (`claude-sonnet-4-5-20250929`) resolve by prefix.\n */\nexport const PRICE_TABLE: Record<string, Pricing> = {\n \"claude-sonnet-4-5\": { inputPerMTok: 3, outputPerMTok: 15 },\n \"claude-sonnet-4-6\": { inputPerMTok: 3, outputPerMTok: 15 },\n \"claude-haiku-4-5\": { inputPerMTok: 1, outputPerMTok: 5 },\n \"claude-opus-4-8\": { inputPerMTok: 15, outputPerMTok: 75 },\n \"gpt-4o-mini\": { inputPerMTok: 0.15, outputPerMTok: 0.6 },\n \"gpt-4o\": { inputPerMTok: 2.5, outputPerMTok: 10 },\n};\n\nexport function pricingFor(\n model: string,\n override?: Pricing,\n): Pricing | undefined {\n if (override) return override;\n if (PRICE_TABLE[model]) return PRICE_TABLE[model];\n // Match pinned variants like claude-sonnet-4-5-20250929. Longest prefix\n // wins so `claude-sonnet-4-5` never shadows a more specific future entry.\n const base = Object.keys(PRICE_TABLE)\n .filter((k) => model.startsWith(k))\n .sort((a, b) => b.length - a.length)[0];\n return base ? PRICE_TABLE[base] : undefined;\n}\n\nexport function costOfUsage(\n usage: TokenUsage | undefined,\n pricing: Pricing | undefined,\n): number {\n if (!usage || !pricing) return 0;\n return (\n (usage.inputTokens / 1_000_000) * pricing.inputPerMTok +\n (usage.outputTokens / 1_000_000) * pricing.outputPerMTok\n );\n}\n\n/** Sum the cost of a set of runs. Cached runs cost nothing — they made no call. */\nexport function costOfRuns(\n runs: { usage?: TokenUsage; cached?: boolean }[],\n pricing: Pricing | undefined,\n): number {\n if (!pricing) return 0;\n let usd = 0;\n for (const run of runs) {\n if (!run.usage || run.cached) continue;\n usd += costOfUsage(run.usage, pricing);\n }\n return usd;\n}\n","{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"inference:verdict:0.1\",\n \"title\": \"LLM-as-judge verdict\",\n \"type\": \"object\",\n \"required\": [\"claim\", \"observed\", \"match\", \"confidence\", \"reasoning\"],\n \"properties\": {\n \"claim\": {\n \"type\": \"string\",\n \"description\": \"The specific assertion under evaluation.\"\n },\n \"observed\": {\n \"type\": \"string\",\n \"description\": \"What was actually observed in the subject under evaluation, quoting evidence where possible.\"\n },\n \"match\": {\n \"enum\": [\"pass\", \"fail\", \"partial\"],\n \"description\": \"pass only if the assertion is fully satisfied; partial for partial compliance.\"\n },\n \"confidence\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1,\n \"description\": \"Self-reported confidence in this verdict.\"\n },\n \"reasoning\": {\n \"type\": \"string\",\n \"description\": \"Why this conclusion was reached.\"\n }\n },\n \"additionalProperties\": false\n}\n","/**\n * LLM-as-judge types. The verdict shape is the one all consuming projects\n * already share; the canonical schema lives in verdict-schema.json and is\n * overridable per consumer so domain-specific field descriptions survive\n * (ADR 01001).\n */\nimport verdictSchemaJson from \"./verdict-schema.json\" with { type: \"json\" };\nimport type { TokenUsage } from \"../providers/types.js\";\n\nexport const VERDICT_SCHEMA = verdictSchemaJson as Record<string, unknown>;\n\nexport type Match = \"pass\" | \"fail\" | \"partial\";\n\n/** Confidence-zone routing for LLM-judged evals. */\nexport type Zone = \"auto-pass\" | \"auto-fail\" | \"human-review\";\n\nexport interface JudgeVerdict {\n /** The specific assertion under evaluation. */\n claim: string;\n /** What the judge actually observed. */\n observed: string;\n match: Match;\n /** 0.0–1.0 self-reported confidence. */\n confidence: number;\n reasoning: string;\n}\n\n/** One run within an ensemble. */\nexport interface JudgeRun {\n /** Absent when the run errored (invalid JSON after retry, API failure). */\n verdict?: JudgeVerdict;\n error?: string;\n provider: string;\n model: string;\n cached: boolean;\n usage?: TokenUsage;\n durationMs: number;\n}\n\n/** Aggregated outcome of an ensemble of judge runs for one subject. */\nexport interface ConsensusResult {\n runs: JudgeRun[];\n votes: { pass: number; fail: number; partial: number; error: number };\n /** Majority verdict; `partial` counts as fail for the binary outcome. */\n verdict: Match;\n /** Fraction of non-errored runs agreeing with the majority verdict. */\n agreement: number;\n /** Mean confidence across non-errored runs. */\n meanConfidence: number;\n zone: Zone;\n}\n","/**\n * Ensemble consensus math. `partial` counts as fail for the binary outcome\n * but stays visible in the vote counts. Errored runs count against consensus —\n * they can only push a result toward human review, never toward a silent pass.\n */\nimport type { ConsensusResult, JudgeRun, Match } from \"./types.js\";\n\nexport function computeConsensus(\n runs: JudgeRun[],\n): Omit<ConsensusResult, \"zone\"> {\n const votes = { pass: 0, fail: 0, partial: 0, error: 0 };\n let confidenceSum = 0;\n let confidenceCount = 0;\n for (const run of runs) {\n if (!run.verdict) {\n votes.error += 1;\n continue;\n }\n votes[run.verdict.match] += 1;\n confidenceSum += run.verdict.confidence;\n confidenceCount += 1;\n }\n\n const passVotes = votes.pass;\n const failVotes = votes.fail + votes.partial;\n // Binary majority; a tie is not a pass.\n const verdict: Match = passVotes > failVotes ? \"pass\" : \"fail\";\n const graded = passVotes + failVotes;\n const agreement = graded > 0 ? Math.max(passVotes, failVotes) / graded : 0;\n const meanConfidence =\n confidenceCount > 0 ? confidenceSum / confidenceCount : 0;\n\n return { runs, votes, verdict, agreement, meanConfidence };\n}\n","/**\n * Confidence-zone routing: only unanimous, high-confidence ensembles\n * auto-resolve; everything else goes to a human.\n */\nimport type { ConsensusResult, Zone } from \"./types.js\";\n\nexport interface ZoneThresholds {\n autoPass: number;\n autoFail: number;\n}\n\nexport const DEFAULT_ZONES: ZoneThresholds = { autoPass: 0.8, autoFail: 0.8 };\n\nexport function zoneFor(\n consensus: Omit<ConsensusResult, \"zone\">,\n thresholds: ZoneThresholds = DEFAULT_ZONES,\n): Zone {\n const { votes, meanConfidence } = consensus;\n const unanimousPass =\n votes.pass > 0 &&\n votes.fail === 0 &&\n votes.partial === 0 &&\n votes.error === 0;\n const unanimousFail =\n votes.pass === 0 && votes.error === 0 && votes.fail + votes.partial > 0;\n\n if (unanimousPass && meanConfidence >= thresholds.autoPass) return \"auto-pass\";\n if (unanimousFail && meanConfidence >= thresholds.autoFail) return \"auto-fail\";\n return \"human-review\";\n}\n","/**\n * The ensemble judge: N independent runs for one subject, each a fresh request\n * with no shared context (eval isolation), aggregated by consensus and routed\n * through confidence zones.\n *\n * Runs within one ensemble stay sequential on purpose — they are meant to be\n * independent samples, and interleaving them buys nothing. Concurrency belongs\n * one level up, across subjects, where the consumer owns the pool.\n */\nimport { completeValidatedJSON, validatorFor } from \"../complete.js\";\nimport type { JsonCache } from \"../cache.js\";\nimport type { InferenceProvider } from \"../providers/types.js\";\nimport { computeConsensus } from \"./consensus.js\";\nimport { DEFAULT_ZONES, zoneFor, type ZoneThresholds } from \"./zones.js\";\nimport {\n VERDICT_SCHEMA,\n type ConsensusResult,\n type JudgeRun,\n type JudgeVerdict,\n} from \"./types.js\";\n\nexport interface EnsembleOptions {\n provider: InferenceProvider;\n system: string;\n user: string;\n /** Ensemble size; default 3. */\n runs?: number;\n /** Default 0. Nonzero adds noise to verdicts and warns once. */\n temperature?: number;\n /**\n * Verdict schema. Defaults to the canonical one; pass your own to keep\n * domain-specific field descriptions (they measurably steer the model).\n * Must still produce objects matching `JudgeVerdict`.\n */\n schema?: Record<string, unknown>;\n /** Optional result cache. Requires `cacheKey`. */\n cache?: JsonCache<JudgeRun[]>;\n /** Content-addressed key; build it with `buildCacheKey`. */\n cacheKey?: string;\n /** Prefix for warnings, e.g. your tool's name. */\n label?: string;\n}\n\nlet warnedTemperature = false;\n\n/**\n * Run the ensemble and return every run. Cached ensembles replay identically,\n * with each run flagged `cached: true` so cost accounting skips them.\n */\nexport async function runEnsemble(\n options: EnsembleOptions,\n): Promise<JudgeRun[]> {\n const {\n provider,\n system,\n user,\n runs: runCount = 3,\n temperature = 0,\n schema = VERDICT_SCHEMA,\n cache,\n cacheKey,\n label = \"inference\",\n } = options;\n\n if (temperature > 0 && !warnedTemperature) {\n warnedTemperature = true;\n console.warn(\n `${label}: judge temperature is ${temperature} — nonzero temperature adds noise to verdicts; 0 is strongly recommended.`,\n );\n }\n\n if (cache && cacheKey) {\n const hit = cache.get(cacheKey);\n // A cache file can be valid JSON and still be the wrong shape — a\n // truncated write, or an entry from an older cache generation. JsonCache\n // only guards parse failures, so the shape check belongs here; a bad entry\n // is a miss, never a crash.\n if (Array.isArray(hit)) return hit.map((r) => ({ ...r, cached: true }));\n }\n\n // Compile the schema once for the whole ensemble rather than per run.\n const validate = validatorFor(schema);\n\n const results: JudgeRun[] = [];\n for (let i = 0; i < runCount; i++) {\n const run = await completeValidatedJSON<JudgeVerdict>({\n provider,\n system,\n user,\n schema,\n temperature,\n validate,\n });\n // `result` is the generic name at the completion layer; the judge layer\n // calls it `verdict`, which is what consumers persist in their caches.\n const { result, ...rest } = run;\n results.push(result === undefined ? rest : { ...rest, verdict: result });\n }\n\n if (cache && cacheKey) cache.set(cacheKey, results);\n return results;\n}\n\n/** Run the ensemble and aggregate it into a zoned consensus. */\nexport async function judge(\n options: EnsembleOptions & { zones?: ZoneThresholds },\n): Promise<ConsensusResult> {\n const runs = await runEnsemble(options);\n const base = computeConsensus(runs);\n return { ...base, zone: zoneFor(base, options.zones ?? DEFAULT_ZONES) };\n}\n\n/** Test seam: reset the once-per-process temperature warning. */\nexport function resetTemperatureWarning(): void {\n warnedTemperature = false;\n}\n"],"mappings":";AAKO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ACYO,IAAM,qBAAqB;AAElC,IAAI,oBAAoB;AAQjB,SAAS,sBACd,UAAkB,QAAQ,SAAS,MAC7B;AACN,MAAI,kBAAmB;AACvB,QAAM,QAAQ,OAAO,SAAS,SAAS,EAAE;AAGzC,MAAI,CAAC,OAAO,UAAU,KAAK,KAAK,SAAS,mBAAoB;AAC7D,sBAAoB;AACpB,UAAQ;AAAA,IACN,8BAA8B,OAAO,yBAChC,kBAAkB;AAAA,EAGzB;AACF;AAGO,SAAS,0BAAgC;AAC9C,sBAAoB;AACtB;;;AChDA,OAAO,eAAe;AAQtB,IAAM,oBAAoB;AAcnB,IAAM,oBAAN,MAAqD;AAAA,EAM1D,YACmB,OACjB,WACA,UAAoC,CAAC,GACrC;AAHiB;AAIjB,UAAM,SAAS,QAAQ,IAAI,SAAS;AACpC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,4BAA4B,SAAS;AAAA,MACvC;AAAA,IACF;AACA,SAAK,SAAS,IAAI,UAAU,EAAE,OAAO,CAAC;AACtC,SAAK,WAAW,QAAQ,YAAY;AACpC,SAAK,kBACH,QAAQ,mBAAmB;AAC7B,SAAK,YAAY,QAAQ,aAAa;AAAA,EACxC;AAAA,EAfmB;AAAA,EANF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAoBjB,WAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,aAAa,KAAyD;AAC1E,UAAM,WAAW,MAAM,KAAK,OAAO,SAAS,OAAO;AAAA,MACjD,OAAO,KAAK;AAAA,MACZ,YAAY,KAAK;AAAA,MACjB,aAAa,IAAI;AAAA,MACjB,QAAQ,IAAI;AAAA,MACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,IAAI,KAAK,CAAC;AAAA,MAC9C,OAAO;AAAA,QACL;AAAA,UACE,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,cAAc,IAAI;AAAA,QACpB;AAAA,MACF;AAAA,MACA,aAAa,EAAE,MAAM,QAAQ,MAAM,KAAK,SAAS;AAAA,IACnD,CAAC;AAMD,QAAI,SAAS,gBAAgB,cAAc;AACzC,YAAM,IAAI;AAAA,QACR,sCAAsC,KAAK,SAAS;AAAA,MACtD;AAAA,IACF;AAEA,UAAM,UAAU,SAAS,QAAQ;AAAA,MAC/B,CAAC,UAA2C,MAAM,SAAS;AAAA,IAC7D;AACA,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AACA,WAAO;AAAA,MACL,MAAM,QAAQ;AAAA,MACd,OAAO;AAAA,QACL,aAAa,SAAS,MAAM;AAAA,QAC5B,cAAc,SAAS,MAAM;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AACF;;;AC/EO,SAAS,YAAY,SAA0B;AACpD,QAAM,UAAU,QACb,QAAQ,wBAAwB,EAAE,EAClC,QAAQ,cAAc,EAAE,EACxB,KAAK;AACR,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,UAAM,QAAQ,QAAQ,QAAQ,GAAG;AACjC,UAAM,MAAM,QAAQ,YAAY,GAAG;AACnC,QAAI,SAAS,KAAK,MAAM,OAAO;AAC7B,aAAO,KAAK,MAAM,QAAQ,MAAM,OAAO,MAAM,CAAC,CAAC;AAAA,IACjD;AACA,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACF;AAQO,SAAS,eACd,QACyB;AACzB,QAAM,QAAQ,gBAAgB,MAAM;AACpC,QAAM,OAAO,CAAC,SAAwB;AACpC,QAAI,SAAS,QAAQ,OAAO,SAAS,SAAU;AAC/C,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,iBAAW,QAAQ,KAAM,MAAK,IAAI;AAClC;AAAA,IACF;AACA,UAAM,MAAM;AACZ,WAAO,IAAI,WAAW;AACtB,WAAO,IAAI,aAAa;AACxB,UAAM,aAAa,IAAI,YAAY;AACnC,QAAI,cAAc,OAAO,eAAe,UAAU;AAChD,UAAI,UAAU,IAAI,OAAO,KAAK,UAAU;AAKxC,UAAI,sBAAsB,IAAI;AAC9B,iBAAW,QAAQ,OAAO,OAAO,UAAqC,GAAG;AACvE,aAAK,IAAI;AACT,YAAI,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;AAC5D,gBAAM,IAAI;AACV,cAAI,OAAO,EAAE,MAAM,MAAM,YAAY,EAAE,MAAM,MAAM,QAAQ;AACzD,cAAE,MAAM,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;AAAA,UAChC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,SAAK,IAAI,OAAO,CAAC;AAAA,EACnB;AACA,OAAK,KAAK;AACV,SAAO;AACT;AAGO,SAAS,WAAW,OAAyB;AAClD,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO;AAAA,EACT;AACA,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,KAAgC,EAAE;AAAA,MAC/C,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM;AAAA,IACnB;AAAA,EACF;AACF;AAOO,IAAM,uBAAN,MAAwD;AAAA,EAI7D,YACmB,SACA,OACjB,WACiB,SAA6B,QAAQ,IAAI,SAAS,GACnE,UAAuC,CAAC,GACxC;AALiB;AACA;AAEA;AAIjB,QAAI,CAAC,KAAK,UAAU,QAAQ,SAAS,gBAAgB,GAAG;AACtD,YAAM,IAAI;AAAA,QACR,yBAAyB,SAAS;AAAA,MACpC;AAAA,IACF;AACA,SAAK,aAAa,QAAQ,cAAc;AAAA,EAC1C;AAAA,EAbmB;AAAA,EACA;AAAA,EAEA;AAAA,EAPX,qBAAqB;AAAA,EACZ;AAAA,EAkBjB,WAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,KAAK,MAAsD;AACvE,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,KAAK,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MAClC;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAI,KAAK,SAAS,EAAE,eAAe,UAAU,KAAK,MAAM,GAAG,IAAI,CAAC;AAAA,QAClE;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B;AAAA,IACF;AACA,UAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACpD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,UAAU,KAAK,OAAO,WAAW,QAAQ,SAAS,MAAM;AAC9D,YAAM,IAAI,MAAM,GAAG,OAAO,EAAE;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,KAAyD;AAC1E,UAAM,OAAO;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,aAAa,IAAI;AAAA,MACjB,UAAU;AAAA,QACR,EAAE,MAAM,UAAU,SAAS,IAAI,OAAO;AAAA,QACtC,EAAE,MAAM,QAAQ,SAAS,IAAI,KAAK;AAAA,MACpC;AAAA,IACF;AAEA,QAAI;AACJ,QAAI,KAAK,oBAAoB;AAC3B,UAAI;AACF,mBAAW,MAAM,KAAK,KAAK;AAAA,UACzB,GAAG;AAAA,UACH,iBAAiB;AAAA,YACf,MAAM;AAAA,YACN,aAAa;AAAA,cACX,MAAM,KAAK;AAAA,cACX,QAAQ;AAAA,cACR,QAAQ,eAAe,IAAI,MAAM;AAAA,YACnC;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AACV,cAAM,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAGzD,YACE,CAAC,sCAAsC,KAAK,OAAO,KACnD,YAAY,YACZ;AACA,gBAAM;AAAA,QACR;AACA,aAAK,qBAAqB;AAC1B,mBAAW,MAAM,KAAK,mBAAmB,MAAM,GAAG;AAAA,MACpD;AAAA,IACF,OAAO;AACL,iBAAW,MAAM,KAAK,mBAAmB,MAAM,GAAG;AAAA,IACpD;AAEA,UAAM,UAAU,SAAS,UAAU,CAAC,GAAG,SAAS;AAChD,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,2BAA2B;AACzD,WAAO;AAAA,MACL,MAAM,WAAW,YAAY,OAAO,CAAC;AAAA,MACrC,OACE,SAAS,OAAO,iBAAiB,OAC7B;AAAA,QACE,aAAa,SAAS,MAAM,iBAAiB;AAAA,QAC7C,cAAc,SAAS,MAAM,qBAAqB;AAAA,MACpD,IACA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,mBACN,MACA,KACuB;AACvB,WAAO,KAAK,KAAK;AAAA,MACf,GAAG;AAAA,MACH,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,SAAS,GAAG,IAAI,MAAM;AAAA;AAAA;AAAA,EAAwE,KAAK,UAAU,IAAI,MAAM,CAAC;AAAA,QAC1H;AAAA,QACA,EAAE,MAAM,QAAQ,SAAS,IAAI,KAAK;AAAA,MACpC;AAAA,MACA,iBAAiB,EAAE,MAAM,cAAc;AAAA,IACzC,CAAC;AAAA,EACH;AACF;;;AClNA,OAAO,WAAW;AAGX,IAAM,WAAmB,CAAC,KAAK,OAAO,CAAC,MAAM;AAClD,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI;AACvB,MAAI,CAAC,KAAK;AACR,WAAO,QAAQ,QAAoB;AAAA,MACjC,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AACA,SAAO,IAAI,QAAoB,CAAC,mBAAmB;AACjD,UAAM,QAAQ,MAAM,KAAK,MAAM;AAAA,MAC7B,KAAK,KAAK;AAAA,MACV,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAI,KAAK,OAAO,CAAC,EAAG;AAAA,MAC3C,OAAO,CAAC,KAAK,SAAS,OAAO,SAAS,UAAU,QAAQ,MAAM;AAAA,IAChE,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,WAAW;AACf,QAAI,UAAU;AAEd,UAAM,YAAY,KAAK,aAAa;AACpC,UAAM,QAAQ,WAAW,MAAM;AAC7B,iBAAW;AACX,YAAM,KAAK;AAKX,aAAO,EAAE,MAAM,MAAM,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,IACvD,GAAG,SAAS;AAEZ,UAAM,SAAS,CAAC,WAA6B;AAC3C,UAAI,QAAS;AACb,gBAAU;AACV,mBAAa,KAAK;AAClB,qBAAe,MAAM;AAAA,IACvB;AAEA,QAAI,KAAK,SAAS,QAAQ,MAAM,OAAO;AAErC,YAAM,MAAM,GAAG,SAAS,MAAM;AAAA,MAAC,CAAC;AAChC,YAAM,MAAM,IAAI,KAAK,KAAK;AAAA,IAC5B;AAKA,UAAM,QAAQ,YAAY,MAAM;AAChC,UAAM,QAAQ,YAAY,MAAM;AAChC,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAe,UAAU,CAAE;AACrD,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAe,UAAU,CAAE;AACrD,UAAM;AAAA,MAAG;AAAA,MAAS,CAAC,MACjB,OAAO,EAAE,MAAM,MAAM,QAAQ,QAAQ,UAAU,YAAY,EAAE,QAAQ,CAAC;AAAA,IACxE;AACA,UAAM,GAAG,SAAS,CAAC,SAAS,OAAO,EAAE,MAAM,QAAQ,QAAQ,SAAS,CAAC,CAAC;AAAA,EACxE,CAAC;AACH;;;ACtDO,IAAM,oBAAN,MAAqD;AAAA,EAC1D,YACmB,OACA,UAAkB,UAClB,OAAe,UACf,YAAoB,MACrC;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGnB,WAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,aAAa,KAAyD;AAC1E,UAAM,SAAS;AAAA,MACb,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,UAAU,IAAI,MAAM;AAAA,IAC3B,EAAE,KAAK,IAAI;AAIX,UAAM,SAAS,MAAM,KAAK;AAAA,MACxB;AAAA,QACE,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK;AAAA,MACP;AAAA,MACA,EAAE,WAAW,KAAK,WAAW,OAAO,OAAO;AAAA,IAC7C;AAEA,QAAI,OAAO,YAAY;AACrB,YAAM,IAAI;AAAA,QACR,iBAAiB,KAAK,OAAO,KAAK,OAAO,UAAU;AAAA,MACrD;AAAA,IACF;AACA,QAAI,OAAO,SAAU,OAAM,IAAI,MAAM,sBAAsB;AAC3D,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,IAAI;AAAA,QACR,qBAAqB,OAAO,IAAI,KAAK,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAAA,MACvE;AAAA,IACF;AAWA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,OAAO,MAAM;AAAA,IACpC,QAAQ;AACN,YAAM,UAAU,OAAO,OAAO,KAAK,EAAE,QAAQ,QAAQ,GAAG,EAAE,MAAM,GAAG,GAAG;AACtE,YAAM,IAAI;AAAA,QACR,0DAA0D,WAAW,aAAa;AAAA,MACpF;AAAA,IACF;AACA,QAAI,OAAO,QAAQ,WAAW,UAAU;AACtC,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,WAAO,EAAE,MAAM,YAAY,QAAQ,MAAM,EAAE;AAAA,EAC7C;AACF;;;AC1EO,IAAM,eAAN,MAAgD;AAAA,EAKrD,YACmB,WACA,QAAQ,cACzB;AAFiB;AACA;AAEjB,QAAI,UAAU,WAAW,GAAG;AAC1B,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAAA,EACF;AAAA,EANmB;AAAA,EACA;AAAA,EANX,QAAQ;AAAA;AAAA,EAEA,WAAkC,CAAC;AAAA,EAWnD,WAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,aAAa,KAAyD;AACpE,SAAK,SAAS,KAAK,GAAG;AACtB,UAAM,WAAW,KAAK,UAAU,KAAK,QAAQ,KAAK,UAAU,MAAM;AAClE,SAAK,SAAS;AACd,QAAI,WAAW,UAAU;AACvB,aAAO,QAAQ,OAAO,IAAI,MAAM,SAAS,KAAK,CAAC;AAAA,IACjD;AACA,WAAO,QAAQ,QAAQ;AAAA,MACrB,MAAM,SAAS;AAAA,MACf,OAAO,SAAS,SAAS,EAAE,aAAa,KAAK,cAAc,IAAI;AAAA,IACjE,CAAC;AAAA,EACH;AACF;AAGO,SAAS,YACd,OACA,YACA,YAIK,CAAC,GACa;AACnB,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,OAAO,UAAU,SAAS;AAAA,MAC1B,UAAU,UAAU,YAAY;AAAA,MAChC;AAAA,MACA;AAAA,MACA,WAAW,UAAU,aAAa;AAAA,IACpC;AAAA,EACF;AACF;;;AC/DA,SAAS,kBAAkB;AAC3B,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,YAAY;AAEd,SAAS,OAAO,MAAsB;AAC3C,SAAO,WAAW,QAAQ,EAAE,OAAO,MAAM,MAAM,EAAE,OAAO,KAAK;AAC/D;AAOO,SAAS,cAAc,OAAyB;AAIrD,SAAO,OAAO,MAAM,IAAI,CAAC,MAAM,GAAG,EAAE,MAAM,IAAI,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC;AAC9D;AAEO,IAAM,YAAN,MAAmB;AAAA,EAIxB,YACmB,KACA,UAAmB,MAEnB,QAAgB,aACjC;AAJiB;AACA;AAEA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EAEA;AAAA;AAAA,EANX,SAAS;AAAA,EASjB,IAAI,KAA4B;AAC9B,QAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,UAAM,OAAO,KAAK,KAAK,KAAK,GAAG,GAAG,OAAO;AACzC,QAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,QAAI;AACF,aAAO,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AAAA,IAC9C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,IAAI,KAAa,OAAgB;AAC/B,QAAI,CAAC,KAAK,QAAS;AAInB,QAAI;AACF,gBAAU,KAAK,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC;AAAA,QACE,KAAK,KAAK,KAAK,GAAG,GAAG,OAAO;AAAA,QAC5B,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,MAC/B;AAAA,IACF,SAAS,GAAG;AACV,UAAI,CAAC,KAAK,QAAQ;AAChB,aAAK,SAAS;AACd,gBAAQ;AAAA,UACN,GAAG,KAAK,KAAK,kCAAkC,KAAK,GAAG,KACrD,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAC3C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACxBA,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AACxB,SAAS,QAAAA,aAAY;AAcd,SAAS,8BAAsC;AACpD,SACE,QAAQ,IAAI,sBAAsB,KAClCC,MAAK,QAAQ,GAAG,wBAAwB,QAAQ;AAEpD;AAGO,IAAM,cAAc,CAAC,QAAQ,YAAY,SAAS;AAIlD,IAAM,kBAAkB,CAAC,QAAQ,GAAG,WAAW;AAuB/C,IAAM,eACX,kBAAkB;AAAA,EAChB,qBAAqB;AAAA,IACnB,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OACE;AAAA,EAEJ;AAAA,EACA,cAAc;AAAA,IACZ,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OACE;AAAA,IAEF,MAAM;AAAA,EACR;AAAA,EACA,cAAc;AAAA,IACZ,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OACE;AAAA,EAEJ;AAAA;AAAA;AAAA,EAGA,eAAe;AAAA,IACb,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,eAAe;AAAA,IACb,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,eAAe;AAAA,IACb,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,mBAAmB;AAAA,IACjB,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OACE;AAAA,EACJ;AAAA,EACA,kBAAkB;AAAA,IAChB,KAAK;AAAA,IACL,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OACE;AAAA,EAIJ;AACF,CAAC;AAEH,SAAS,kBACP,SACa;AACb,aAAW,SAAS,OAAO,OAAO,OAAO,EAAG,QAAO,OAAO,KAAK;AAC/D,SAAO,OAAO,OAAO,OAAO;AAC9B;AAGA,IAAM,aAAwC;AAAA,EAC5C,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AACX;AAEO,SAAS,gBAAgB,OAAuC;AACrE,SAAQ,gBAAsC,SAAS,KAAK;AAC9D;AAQA,IAAM,kBAAkB;AAUjB,SAAS,cAAc,aAAgC;AAC5D,MAAI,SAAoB;AACxB,aAAW,QAAQ,aAAa;AAC9B,UAAM,QAAQ,aAAa,WAAW,IAAI,CAAC;AAC3C,QAAI,MAAM,YAAY,mBAAmB,YAAa,UAAS;AAAA,EACjE;AAGA,SAAO;AACT;AAMO,SAAS,aAAa,MAAyB;AACpD,SAAO,WAAW,IAAI;AACxB;AAGO,SAAS,WAAW,MAAyB;AAClD,SAAO,aAAa,WAAW,IAAI,CAAC,EAAG;AACzC;AAWO,SAAS,qBAAqB,OAAuB;AAC1D,MAAI,gBAAgB,KAAK,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR,oBAAoB,KAAK,8IAEE,WAAW,QAAQ;AAAA,IAChD;AAAA,EACF;AACA,QAAM,QAAQ,aAAa,KAAK;AAChC,MAAI,MAAO,QAAO,MAAM;AACxB,MAAI,iBAAiB,KAAK,EAAG,QAAO;AACpC,QAAM,IAAI;AAAA,IACR,4BAA4B,KAAK,sBAAsB,gBAAgB;AAAA,MACrE;AAAA,IACF,CAAC,uBAAuB,OAAO,KAAK,YAAY,EAAE;AAAA,MAChD;AAAA,IACF,CAAC;AAAA,EACH;AACF;AASO,SAAS,YAAY,OAAuB;AACjD,QAAM,MAAM,qBAAqB,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;AACpD,SAAO,IAAI,MAAM,OAAO,EAAE,IAAI;AAChC;AAQO,SAAS,iBAAiB,OAAe,UAA2B;AACzE,QAAM,OAAO,MAAM,QAAQ,YAAY,EAAE;AACzC,MAAI,KAAK,SAAS,QAAQ,EAAG,QAAO;AAGpC,QAAM,OAAO,SAAS,QAAQ,WAAW,EAAE;AAC3C,SAAO,IAAI,OAAO,GAAG,aAAa,IAAI,CAAC,2BAA2B,EAAE,KAAK,IAAI;AAC/E;AAEA,SAAS,aAAa,MAAsB;AAC1C,SAAO,KAAK,QAAQ,uBAAuB,MAAM;AACnD;AASO,SAAS,kBAAkB,OAAe,WAA4B;AAC3E,QAAM,WAAW,YAAY,KAAK;AAClC,SAAO,mBAAmB,SAAS,EAAE;AAAA,IACnC,CAAC,UAAU,CAAC,MAAM,SAAS,QAAQ,KAAK,iBAAiB,OAAO,QAAQ;AAAA,EAC1E;AACF;AAGO,SAAS,mBAAmB,WAA6B;AAC9D,MAAI;AACF,WAAO,YAAY,WAAW,EAAE,eAAe,KAAK,CAAC,EAClD,OAAO,CAAC,UAAU,MAAM,OAAO,CAAC,EAChC,IAAI,CAAC,UAAU,MAAM,IAAI;AAAA,EAC9B,QAAQ;AAEN,WAAO,CAAC;AAAA,EACV;AACF;AAYA,SAAS,iBAAiB,OAAwB;AAChD,SACE,sBAAsB,KAAK,KAAK,KAChC,gBAAgB,KAAK,KAAK,KAC1B,2BAA2B,KAAK,KAAK,KACrC,MAAM,SAAS,OAAO;AAE1B;;;ACxTA;AAAA,EACE,cAAAC;AAAA,EACA,aAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAAC;AAAA,OACK;AACP,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;AACrB,SAAS,qBAAqB;AAM9B,IAAM,eAAe;AAMrB,IAAM,OAAO;AACb,IAAM,OAAO;AAOb,IAAM,qBAAqB;AAG3B,IAAM,eAAe;AAErB,IAAM,gBAAgB,qBAAqB;AA6BpC,SAAS,6BACd,MAA0C,QAAQ,KAC1C;AACR,SACE,IAAI,uBAAuB,KAC3BC,MAAKC,SAAQ,GAAG,wBAAwB,SAAS;AAErD;AAYO,SAAS,iBAAiB,GAAqB;AACpD,QAAM,OAAQ,GAAyC;AACvD,SAAO,SAAS,0BAA0B,SAAS;AACrD;AAEA,SAAS,SAAS,GAAoB;AACpC,SAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAClD;AAoBA,eAAsB,mBACpB,UAAiC,CAAC,GACV;AACxB,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,YAAY,QAAQ,aAAa,6BAA6B,GAAG;AAEvE,QAAM,cACJ,QAAQ,gBAAgB,MAAwB,OAAO,gBAAgB;AACzE,MAAI;AACF,UAAM,YAAY;AAClB,WAAO,EAAE,OAAO,UAAU;AAAA,EAC5B,SAAS,GAAG;AAKV,QAAI,CAAC,iBAAiB,CAAC,GAAG;AACxB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,mDAAmD,SAAS,CAAC,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EAEF;AACA,MAAIC,YAAWF,MAAK,WAAW,IAAI,CAAC,EAAG,QAAO,EAAE,OAAO,UAAU;AAEjE,OAAK,IAAI,2BAA2B,KAAK,QAAQ,IAAI;AACnD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AACA,SAAO,EAAE,OAAO,eAAe,UAAU;AAC3C;AAOA,IAAM,WAAW,oBAAI,IAA8B;AAEnD,IAAI,gBAAgB;AAGb,SAAS,sBAA4B;AAC1C,WAAS,MAAM;AACf,kBAAgB;AAClB;AASO,SAAS,mBACd,UAAiC,CAAC,GAChB;AAClB,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,YAAY,QAAQ,aAAa,6BAA6B,GAAG;AAEvE,QAAM,WAAW,SAAS,IAAI,SAAS;AACvC,MAAI,SAAU,QAAO;AAErB,QAAM,UAAU,WAAW,WAAW,KAAK,OAAO;AAIlD,QAAM,UAAU,QAAQ,MAAM,CAAC,MAAe;AAC5C,QAAI,SAAS,IAAI,SAAS,MAAM,QAAS,UAAS,OAAO,SAAS;AAClE,UAAM;AAAA,EACR,CAAC;AACD,WAAS,IAAI,WAAW,OAAO;AAC/B,SAAO;AACT;AAEA,eAAe,WACb,WACA,KACA,SACkB;AAClB,QAAM,aACJ,QAAQ,eAAe,CAAC,QAAkC,OAAO;AACnE,QAAM,OAAOA,MAAK,WAAW,IAAI;AAEjC,MAAIE,YAAW,IAAI,EAAG,QAAO,WAAW,cAAc,IAAI,EAAE,IAAI;AAEhE,OAAK,IAAI,2BAA2B,KAAK,QAAQ,IAAI;AACnD,UAAM,IAAI;AAAA,MACR,iHACwC,YAAY,+DACI,SAAS;AAAA,IAEnE;AAAA,EACF;AAEA,EAAAC,WAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,SAAS,WAAW,YAAY;AAEpC,QAAID,YAAW,IAAI,EAAG;AACtB,mBAAe,SAAS;AACxB,UAAM,WAAW,WAAW,KAAK,OAAO;AAExC,IAAAE,eAAc,MAAM;AAAA,GAAqC,MAAM;AAAA,EACjE,CAAC;AAED,SAAO,WAAW,cAAc,IAAI,EAAE,IAAI;AAC5C;AAEA,eAAe,WACb,WACA,KACA,SACe;AAIf,QAAM,WAAWJ,MAAK,WAAW,cAAc;AAC/C,MAAI,CAACE,YAAW,QAAQ,GAAG;AACzB,IAAAE;AAAA,MACE;AAAA,MACA,GAAG,KAAK;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,UACT,aACE;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,EAAE,WAAW,QAAQ,aAAa,oBAAoB,IAAI;AAAA,EAC5D;AAEA,MAAI,OAAO,cAAc,MAAM;AAC7B,UAAM,IAAI;AAAA,MACR,gDAAgD,OAAO,UAAU,sCAC5B,YAAY;AAAA,IAEnD;AAAA,EACF;AACA,MAAI,OAAO,UAAU;AACnB,UAAM,IAAI;AAAA,MACR,kCAAkC,SAAS,mHAE1B,YAAY;AAAA,IAC/B;AAAA,EACF;AACA,MAAI,OAAO,SAAS,GAAG;AACrB,UAAM,IAAI;AAAA,MACR,kCAAkC,SAAS,iBAAiB;AAAA,QAC1D,OAAO;AAAA,MACT,CAAC;AAAA,EAAO,KAAK,OAAO,UAAU,OAAO,MAAM,CAAC;AAAA,kCACP,YAAY;AAAA,IAEnD;AAAA,EACF;AACF;AAGA,SAAS,KAAK,QAAgB,QAAQ,IAAY;AAChD,SAAO,OAAO,QAAQ,EAAE,MAAM,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,IAAI;AAChE;AAMA,SAAS,eAAe,WAAyB;AAC/C,MAAI,cAAe;AACnB,kBAAgB;AAChB,UAAQ;AAAA,IACN,sEAAiE,SAAS;AAAA,EAI5E;AACF;AASA,eAAe,SACb,WACA,IACe;AACf,QAAM,OAAOJ,MAAK,WAAW,IAAI;AACjC,QAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,aAAS;AACP,QAAI;AACF,MAAAI,eAAc,MAAM,OAAO,QAAQ,GAAG,GAAG,EAAE,MAAM,KAAK,CAAC;AACvD;AAAA,IACF,SAAS,GAAG;AACV,UAAK,EAA4B,SAAS,SAAU,OAAM;AAC1D,UAAI,MAAM,IAAI,IAAI,eAAe;AAG/B,eAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAC5B;AAAA,MACF;AACA,UAAIF,YAAWF,MAAK,WAAW,IAAI,CAAC,EAAG;AACvC,UAAI,KAAK,IAAI,IAAI,UAAU;AACzB,cAAM,IAAI;AAAA,UACR,wEACK,SAAS,wCAAwC,IAAI;AAAA,QAC5D;AAAA,MACF;AACA,YAAM,MAAM,GAAG;AAAA,IACjB;AAAA,EACF;AAEA,MAAI;AACF,UAAM,GAAG;AAAA,EACX,UAAE;AACA,WAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,EAC9B;AACF;AAGA,SAAS,MAAM,MAAsB;AACnC,MAAI;AACF,WAAO,KAAK,IAAI,IAAI,SAAS,IAAI,EAAE;AAAA,EACrC,QAAQ;AACN,WAAO,OAAO;AAAA,EAChB;AACF;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;;;AC9QA,IAAM,eAAe,oBAAI,IAAuC;AAShE,eAAsB,qBAAoC;AACxD,QAAM,UAAU,CAAC,GAAG,aAAa,OAAO,CAAC;AACzC,eAAa,MAAM;AACnB,QAAM,QAAQ;AAAA,IACZ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,MAAM,MAAS,CAAC;AAAA,EACtE;AACF;AAEO,IAAM,mBAAN,MAAoD;AAAA,EAczD,YACmB,OACjB,UAAmC,CAAC,GACpC;AAFiB;AAGjB,QAAI,gBAAgB,KAAK,GAAG;AAC1B,YAAM,IAAI;AAAA,QACR,oBAAoB,KAAK,mFACmB,aAAa,UAAU,CAAC;AAAA,MAEtE;AAAA,IACF;AACA,SAAK,MAAM,qBAAqB,KAAK;AACrC,SAAK,UAAU,QAAQ,WAAW,oBAAoB;AACtD,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,YAAY,QAAQ;AACzB,SAAK,kBACH,QAAQ,mBAAmB,4BAA4B;AACzD,SAAK,WAAW,cAAc,CAAC,KAAK,iBAAiB,KAAK,GAAG,CAAC;AAAA,EAChE;AAAA,EAjBmB;AAAA,EAdF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,EAsBjB,WAAmB;AACjB,WAAO;AAAA,EACT;AAAA,EAEA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,aAAa,KAAyD;AAC1E,UAAM,QAAQ,MAAM,KAAK,KAAK;AAG9B,UAAM,UAAU,MAAM,MAAM,cAAc,gBAAgB,GAAG,CAAC;AAC9D,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,OAAO,IAAI,MAAM;AAAA,QAC5C,QAAQ,IAAI;AAAA,QACZ,aAAa,IAAI;AAAA,QACjB,eAAe,KAAK;AAAA,QACpB,GAAI,KAAK,aAAa,OAAO,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,MAChE,CAAC;AAMD,UAAI,OAAO,eAAe,aAAa;AACrC,cAAM,IAAI;AAAA,UACR,sEACK,KAAK,aAAa,OAAO,gBAAgB,KAAK,SAAS,MAAM,EAAE;AAAA,QAEtE;AAAA,MACF;AACA,aAAO,EAAE,MAAM,YAAY,OAAO,IAAI,GAAG,OAAO,OAAO,MAAM;AAAA,IAC/D,UAAE;AACA,YAAM,QAAQ,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,IAC/C;AAAA,EACF;AAAA,EAEQ,OAAkC;AACxC,UAAM,WAAW,aAAa,IAAI,KAAK,QAAQ;AAC/C,QAAI,SAAU,QAAO;AACrB,UAAM,WAAW,YAAY;AAC3B,YAAM,OAAO,MAAM,KAAK,QAAQ;AAAA,QAC9B,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,aAAO,KAAK,QAAQ,UAAU,IAAI;AAAA,IACpC,GAAG;AAKH,UAAM,UAAU,QAAQ,MAAM,CAAC,MAAe;AAC5C,UAAI,aAAa,IAAI,KAAK,QAAQ,MAAM,SAAS;AAC/C,qBAAa,OAAO,KAAK,QAAQ;AAAA,MACnC;AACA,YAAM;AAAA,IACR,CAAC;AACD,iBAAa,IAAI,KAAK,UAAU,OAAO;AACvC,WAAO;AAAA,EACT;AACF;AASA,SAAS,gBAAgB,KAAkC;AACzD,SAAO,GAAG,IAAI,MAAM;AAAA;AAAA;AAAA,EAAwE,KAAK;AAAA,IAC/F,IAAI;AAAA,EACN,CAAC;AACH;AAGA,IAAI;AAOG,SAAS,sBAAoC;AAClD,QAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,mBAAmB,iBAAiB,EAAE,MAAM,CAAC,MAAe;AAC3D,uBAAiB;AACjB,YAAM;AAAA,IACR,CAAC;AAAA;AACH,SAAO;AAAA,IACL,kBAAkB,CAAC,KAAK,cACtB,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,iBAAiB,KAAK,SAAS,CAAC;AAAA,IACvD,WAAW,CAAC,SAAS,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,IAAI,CAAC;AAAA,IACzD,sBAAsB,MAAM,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,qBAAqB,CAAC;AAAA,EACzE;AACF;AAEA,eAAe,mBAA0C;AACvD,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,OAAO,gBAAgB;AAAA,EACrC,SAAS,GAAG;AAKV,QAAI,CAAC,iBAAiB,CAAC,GAAG;AACxB,YAAM,IAAI;AAAA,QACR,mDACE,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAC3C;AAAA,MAGF;AAAA,IACF;AAMA,UAAO,MAAM,mBAAmB;AAAA,EAClC;AAEA,QAAM,EAAE,UAAU,kBAAkB,kBAAkB,WAAW,IAAI;AACrE,QAAM,QAAQ,MAAM,SAAS;AAE7B,SAAO;AAAA;AAAA;AAAA,IAGL,kBAAkB,CAAC,KAAK,cAAc,iBAAiB,KAAK,EAAE,UAAU,CAAC;AAAA,IAEzE,MAAM,UAAU,MAAM;AACpB,YAAM,QAAQ,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AACvD,aAAO;AAAA,QACL,MAAM,cAAc,cAAc;AAChC,gBAAM,UAAU,MAAM,MAAM,cAAc;AAC1C,gBAAM,WAAW,QAAQ,YAAY;AACrC,gBAAM,UAAU,IAAI,iBAAiB;AAAA,YACnC,iBAAiB;AAAA,YACjB;AAAA,UACF,CAAC;AACD,iBAAO;AAAA,YACL,MAAM,OAAO,MAAM,SAAS;AAC1B,oBAAM,UAAU,MAAM,MAAM;AAAA,gBAC1B,QAAQ;AAAA,cAGV;AACA,oBAAM,SAAS,SAAS,WAAW,SAAS;AAC5C,oBAAM,SAAS,MAAM,QAAQ,eAAe,MAAM;AAAA,gBAChD;AAAA,gBACA,aAAa,QAAQ;AAAA,gBACrB,SAAS,EAAE,eAAe,QAAQ,cAAc;AAAA,gBAChD,GAAI,QAAQ,aAAa,OACrB,EAAE,WAAW,QAAQ,UAAU,IAC/B,CAAC;AAAA,cACP,CAAC;AAED,oBAAM,OAAO,WAAW,KAAK,SAAS,YAAY,MAAM;AACxD,qBAAO;AAAA,gBACL,MAAM,OAAO;AAAA,gBACb,YAAY,OAAO;AAAA,gBACnB,OAAO;AAAA,kBACL,aAAa,KAAK;AAAA,kBAClB,cAAc,KAAK;AAAA,gBACrB;AAAA,cACF;AAAA,YACF;AAAA,YACA,MAAM,UAAU;AACd,oBAAM,QAAQ,QAAQ;AAAA,YACxB;AAAA,UACF;AAAA,QACF;AAAA,QACA,MAAM,UAAU;AACd,gBAAM,MAAM,QAAQ;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,uBAAuB;AAC3B,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,IAAS;AAG3C,YAAM,YAAY,SAAS,IAAI;AAC/B,UAAI;AACF,cAAM,OAAO,MAAM,MAAM,aAAa;AAKtC,eAAO,KAAK,IAAI,KAAK,MAAM,SAAS;AAAA,MACtC,QAAQ;AAEN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;AC/UO,IAAM,kBAA2C;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,kBAAyD;AAAA,EAC7D,WAAW;AAAA,EACX,QAAQ;AACV;AAqBA,SAAS,OAAO,UAA2C;AAEzD,UAAQ,QAAQ,IAAI,gBAAgB,QAAQ,CAAE,KAAK,QAAQ;AAC7D;AAWA,IAAM,YAAY,oBAAI,IAA8B;AAG7C,SAAS,sBAA4B;AAC1C,YAAU,MAAM;AAClB;AAEA,SAAS,eAAe,MAAsC;AAC5D,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,SAAS,UAAU,IAAI,OAAO;AACpC,MAAI,OAAQ,QAAO;AACnB,QAAM,UAAU,KAAK,CAAC,SAAS,WAAW,GAAG,EAAE,WAAW,IAAO,CAAC,EAC/D,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,EAAE,YAAY,EAAE,cAAc,IAAI,EAC/D,MAAM,MAAM,KAAK;AACpB,YAAU,IAAI,SAAS,OAAO;AAC9B,SAAO;AACT;AAEA,eAAe,cAAc,MAAoC;AAC/D,QAAM,WAAW,KAAK,gBAAgB,KAAK,UAAU;AACrD,MAAI,SAAU,QAAO,YAAY,QAAQ;AAMzC,QAAM,SAAS,MAAM,mBAAmB;AACxC,MAAI,OAAO,UAAU,WAAW;AAC9B,WAAO,EAAE,WAAW,OAAO,QAAQ,OAAO,OAAO;AAAA,EACnD;AACA,MAAI,OAAO,UAAU,eAAe;AAGlC,WAAO,EAAE,WAAW,KAAK;AAAA,EAC3B;AAGA,SAAO,YAAY,oBAAoB,CAAC;AAC1C;AAMA,SAAS,YAAY,SAAuC;AAC1D,SAAO,QAAQ,qBAAqB,EAAE;AAAA,IACpC,OAAO,EAAE,WAAW,KAAK;AAAA,IACzB,CAAC,OAAgB;AAAA,MACf,WAAW;AAAA,MACX,QACE,aAAa,SAAS,iBAAiB,KAAK,EAAE,OAAO,IACjD,2DACA,mCACE,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAC3C;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAe,MACb,UACA,MACgB;AAChB,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,OAAO,WAAW,IACrB,EAAE,WAAW,KAAK,IAClB;AAAA,QACE,WAAW;AAAA,QACX,QAAQ;AAAA,MACV;AAAA,IACN,KAAK;AAGH,aAAO,OAAO,QAAQ,KAAK,KAAK,UAC5B,EAAE,WAAW,KAAK,IAClB;AAAA,QACE,WAAW;AAAA,QACX,QAAQ;AAAA,MACV;AAAA,IACN,KAAK;AACH,aAAQ,MAAM,eAAe,IAAI,IAC7B,EAAE,WAAW,KAAK,IAClB;AAAA,QACE,WAAW;AAAA,QACX,QAAQ,mBAAmB,KAAK,WAAW,QAAQ;AAAA,MACrD;AAAA,IACN,KAAK;AACH,aAAO,cAAc,IAAI;AAAA,IAC3B;AACE,aAAO,EAAE,WAAW,OAAO,QAAQ,sBAAsB;AAAA,EAC7D;AACF;AAQA,eAAsB,mBACpB,OAAqB,CAAC,GACG;AACzB,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,gBAAgB,IAAI,CAAC,SAAS,MAAM,MAAM,IAAI,CAAC;AAAA,EACjD;AACA,SAAO,gBAAgB,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,EAAG,SAAS;AAC9D;AASA,eAAsB,eACpB,OAAqB,CAAC,GACC;AAQvB,QAAM,UAAoB,CAAC;AAC3B,aAAW,QAAQ,iBAAiB;AAClC,UAAM,SAAS,MAAM,MAAM,MAAM,IAAI;AACrC,QAAI,OAAO,WAAW;AACpB,mBAAa,MAAM,KAAK,aAAa,MAAM;AAC3C,aAAO;AAAA,IACT;AACA,YAAQ,KAAK,KAAK,KAAK,OAAO,EAAE,CAAC,WAAM,OAAO,MAAM,EAAE;AAAA,EACxD;AACA,QAAM,IAAI;AAAA,IACR;AAAA,EAA+C,QAAQ,KAAK,IAAI,CAAC;AAAA;AAAA,EAEnE;AACF;AAEA,IAAI,kBAAkB;AACtB,IAAI,iBAAiB;AAGd,SAAS,gCAAsC;AACpD,oBAAkB;AAClB,mBAAiB;AACnB;AAOA,SAAS,aAAa,UAAwB,iBAAgC;AAC5E,MAAI,gBAAiB;AACrB,oBAAkB;AAGlB,QAAM,UAAU,kBAAkB,oBAAoB;AACtD,UAAQ;AAAA,IACN,cAAc,OAAO,0BAAqB,QAAQ;AAAA,EAEpD;AACF;AAMO,SAAS,oBAAoB,OAAe,WAAyB;AAC1E,MAAI,eAAgB;AACpB,mBAAiB;AACjB,UAAQ;AAAA,IACN,eAAe,KAAK,6DACb,YAAY,KAAK,QAAQ,CAAC,CAAC;AAAA,EAEpC;AACF;;;AC7KO,IAAM,iBAA+C;AAAA,EAC1D,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,MAAM;AAAA;AAAA;AAAA;AAAA,EAIN,aAAa;AACf;AAEA,IAAM,sBAA8C;AAAA,EAClD,WAAW;AAAA,EACX,QAAQ;AACV;AAEO,IAAM,0BAA0B;AAmBhC,SAAS,wBAAwB,MAAsC;AAC5E,MAAI,KAAK,YAAY,QAAQ,KAAK,aAAa,QAAQ;AACrD,UAAM,IAAI;AAAA,MACR,8NAGM,OAAO,KAAK,cAAc,EAAE,KAAK,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AACA,QAAM,QAAQ,KAAK,SAAS,eAAe,KAAK,QAAQ,KAAK;AAC7D,MAAI,KAAK,aAAa,eAAe,gBAAgB,KAAK,GAAG;AAC3D,UAAM,IAAI;AAAA,MACR,oBAAoB,KAAK,qLAGP,aAAa,UAAU,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,SAAO,EAAE,UAAU,KAAK,UAAU,MAAM;AAC1C;AASA,eAAsB,6BACpB,MAC2B;AAM3B,OACG,KAAK,YAAY,QAAQ,KAAK,aAAa,WAC5C,KAAK,SAAS,MACd;AACA,UAAM,IAAI;AAAA,MACR,UAAU,KAAK,KAAK,gHAEd,OAAO,KAAK,cAAc,EAAE,KAAK,IAAI,CAAC;AAAA,IAE9C;AAAA,EACF;AAGA,QAAM,WACJ,KAAK,YAAY,QAAQ,KAAK,aAAa,SACvC,MAAM,eAAe,IAAI,IACzB,KAAK;AACX,QAAM,WAAyB,EAAE,GAAG,MAAM,SAAS;AAEnD,QAAM,QAAQ,KAAK,SAAS,eAAe,QAAQ,KAAK;AACxD,MAAI,aAAa,eAAe,CAAC,gBAAgB,KAAK,GAAG;AACvD,WAAO,wBAAwB,QAAQ;AAAA,EACzC;AACA,QAAM,OACJ,UAAU,SAAS,MAAM,UAAU,gBAAgB,IAAI,CAAC,IAAI;AAC9D,SAAO,EAAE,UAAU,OAAO,aAAa,IAAI,EAAE;AAC/C;AAWA,SAAS,sBAAsB,MAAoB,OAAqB;AACtE,QAAM,QAAQ,aAAa,KAAK;AAChC,MAAI,CAAC,MAAO;AACZ,QAAM,YACJ,KAAK,UAAU,mBAAmB,4BAA4B;AAChE,MAAI,CAAC,kBAAkB,OAAO,SAAS,GAAG;AACxC,wBAAoB,OAAO,MAAM,SAAS;AAAA,EAC5C;AACF;AAQA,SAAS,gBAAgB,MAA8C;AACrE,SAAO,KAAK,gBAAgB,KAAK,UAAU;AAC7C;AAEA,eAAe,UAAU,SAAuD;AAC9E,QAAM,SAAS,WAAW,oBAAoB;AAC9C,SAAO,cAAc,MAAM,OAAO,qBAAqB,CAAC;AAC1D;AAEO,SAAS,aAAa,MAAuC;AAIlE,wBAAsB;AACtB,QAAM,EAAE,MAAM,IAAI,wBAAwB,IAAI;AAE9C,UAAQ,KAAK,UAAU;AAAA,IACrB,KAAK;AACH,aAAO,IAAI;AAAA,QACT;AAAA,QACA,KAAK,aAAa,oBAAoB,WAAW;AAAA,QACjD,KAAK,aAAa,CAAC;AAAA,MACrB;AAAA,IACF,KAAK;AACH,aAAO,IAAI;AAAA,QACT,KAAK,WAAW;AAAA,QAChB;AAAA,QACA,KAAK,aAAa,oBAAoB,QAAQ;AAAA,QAC9C;AAAA,QACA,KAAK,UAAU,CAAC;AAAA,MAClB;AAAA,IACF,KAAK;AACH,aAAO,IAAI;AAAA,QACT;AAAA,QACA,KAAK,WAAW;AAAA,QAChB,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AAAA,IACF,KAAK;AAEH,aAAO,IAAI,aAAa,KAAK,iBAAiB,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,KAAK;AAAA,IACrE,KAAK;AACH,aAAO,IAAI,iBAAiB,OAAO;AAAA,QACjC,GAAI,KAAK,YAAY,CAAC;AAAA,QACtB,GAAI,KAAK,eAAe,EAAE,SAAS,KAAK,aAAa,IAAI,CAAC;AAAA,MAC5D,CAAC;AAAA,IACH;AACE,YAAM,IAAI;AAAA,QACR,qBAAqB,OAAO,KAAK,QAAQ,CAAC,iBAAiB,OAAO;AAAA,UAChE;AAAA,QACF,EAAE,KAAK,IAAI,CAAC;AAAA,MACd;AAAA,EACJ;AACF;AASA,eAAsB,kBACpB,MAC4B;AAG5B,QAAM,EAAE,UAAU,MAAM,IAAI,MAAM,6BAA6B,IAAI;AACnE,MAAI,aAAa,YAAa,uBAAsB,MAAM,KAAK;AAC/D,SAAO,aAAa,EAAE,GAAG,MAAM,UAAU,MAAM,CAAC;AAClD;;;ACtQA,SAAS,UAAAK,SAAQ,YAAAC,iBAAgB;AACjC,SAAS,QAAAC,aAAY;AAqCrB,eAAsB,iBACpB,UAAmC,CAAC,GACH;AACjC,QAAM,YAAY,QAAQ,aAAa,4BAA4B;AACnE,QAAM,SAAS,QAAQ,UAAU;AAGjC,QAAM,SAAS,QAAQ,QAAQ,IAAI,WAAW;AAE9C,MAAI,CAAC,OAAQ,OAAM,mBAAmB;AAEtC,QAAM,QAA4B,CAAC;AACnC,aAAW,SAAS,mBAAmB,SAAS,GAAG;AAGjD,QAAI,CAAC,YAAY,KAAK,EAAG;AACzB,QAAI,UAAU,CAAC,OAAO,KAAK,CAAC,SAAS,iBAAiB,OAAO,IAAI,CAAC,GAAG;AACnE;AAAA,IACF;AACA,UAAM,OAAOC,MAAK,WAAW,KAAK;AAClC,UAAM,YAAY,OAAO,IAAI;AAC7B,QAAI,cAAc,OAAW;AAC7B,QAAI,CAAC,QAAQ;AACX,UAAI;AACF,QAAAC,QAAO,IAAI;AAAA,MACb,QAAQ;AAGN;AAAA,MACF;AAAA,IACF;AACA,UAAM,KAAK,EAAE,MAAM,UAAU,CAAC;AAAA,EAChC;AAEA,SAAO;AAAA,IACL;AAAA,IACA,YAAY,MAAM,OAAO,CAAC,OAAO,SAAS,QAAQ,KAAK,WAAW,CAAC;AAAA,IACnE;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,YAAY,OAAwB;AAC3C,SAAO,MAAM,SAAS,OAAO,KAAK,MAAM,SAAS,aAAa;AAChE;AAEA,SAAS,OAAO,MAAkC;AAChD,MAAI;AACF,WAAOC,UAAS,IAAI,EAAE;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC3FA,SAAS,eAAe;AAmCxB,IAAM,iBAAiB,oBAAI,QAAkC;AAGtD,SAAS,aACd,QACkB;AAClB,QAAM,SAAS,eAAe,IAAI,MAAM;AACxC,MAAI,OAAQ,QAAO;AAOnB,QAAM,WAAW,IAAI,QAAQ,EAAE,WAAW,KAAK,CAAC,EAAE,QAAQ,MAAM;AAChE,iBAAe,IAAI,QAAQ,QAAQ;AACnC,SAAO;AACT;AAEA,eAAsB,sBACpB,SAC0B;AAG1B,wBAAsB;AACtB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,WAAW;AAAA,EACb,IAAI;AACJ,QAAM,WAAW,QAAQ,YAAY,aAAa,MAAM;AAExD,QAAM,QAAQ,KAAK,IAAI;AACvB,QAAM,OAAO;AAAA,IACX,UAAU,SAAS,SAAS;AAAA,IAC5B,OAAO,SAAS,UAAU;AAAA,IAC1B,QAAQ;AAAA,EACV;AAEA,MAAI,YAAY;AAChB,WAAS,UAAU,GAAG,UAAU,UAAU,WAAW;AACnD,QAAI;AACF,YAAM,WAAW,MAAM,SAAS,aAAa;AAAA,QAC3C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,UAAI,SAAS,SAAS,IAAI,GAAG;AAC3B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,QAAQ,SAAS;AAAA,UACjB,OAAO,SAAS;AAAA,UAChB,YAAY,KAAK,IAAI,IAAI;AAAA,QAC3B;AAAA,MACF;AACA,kBAAY,uCAAuC,SAAS,UAAU,CAAC,GACpE,IAAI,CAAC,MAAM,GAAG,EAAE,YAAY,IAAI,EAAE,OAAO,EAAE,EAC3C,KAAK,IAAI,CAAC;AAAA,IACf,SAAS,GAAG;AACV,kBAAY,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AAAA,IACvD;AAAA,EACF;AACA,SAAO,EAAE,GAAG,MAAM,OAAO,WAAW,YAAY,KAAK,IAAI,IAAI,MAAM;AACrE;;;AC9FO,IAAM,cAAuC;AAAA,EAClD,qBAAqB,EAAE,cAAc,GAAG,eAAe,GAAG;AAAA,EAC1D,qBAAqB,EAAE,cAAc,GAAG,eAAe,GAAG;AAAA,EAC1D,oBAAoB,EAAE,cAAc,GAAG,eAAe,EAAE;AAAA,EACxD,mBAAmB,EAAE,cAAc,IAAI,eAAe,GAAG;AAAA,EACzD,eAAe,EAAE,cAAc,MAAM,eAAe,IAAI;AAAA,EACxD,UAAU,EAAE,cAAc,KAAK,eAAe,GAAG;AACnD;AAEO,SAAS,WACd,OACA,UACqB;AACrB,MAAI,SAAU,QAAO;AACrB,MAAI,YAAY,KAAK,EAAG,QAAO,YAAY,KAAK;AAGhD,QAAM,OAAO,OAAO,KAAK,WAAW,EACjC,OAAO,CAAC,MAAM,MAAM,WAAW,CAAC,CAAC,EACjC,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACxC,SAAO,OAAO,YAAY,IAAI,IAAI;AACpC;AAEO,SAAS,YACd,OACA,SACQ;AACR,MAAI,CAAC,SAAS,CAAC,QAAS,QAAO;AAC/B,SACG,MAAM,cAAc,MAAa,QAAQ,eACzC,MAAM,eAAe,MAAa,QAAQ;AAE/C;AAGO,SAAS,WACd,MACA,SACQ;AACR,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,MAAM;AACV,aAAW,OAAO,MAAM;AACtB,QAAI,CAAC,IAAI,SAAS,IAAI,OAAQ;AAC9B,WAAO,YAAY,IAAI,OAAO,OAAO;AAAA,EACvC;AACA,SAAO;AACT;;;AC9DA;AAAA,EACE,SAAW;AAAA,EACX,KAAO;AAAA,EACP,OAAS;AAAA,EACT,MAAQ;AAAA,EACR,UAAY,CAAC,SAAS,YAAY,SAAS,cAAc,WAAW;AAAA,EACpE,YAAc;AAAA,IACZ,OAAS;AAAA,MACP,MAAQ;AAAA,MACR,aAAe;AAAA,IACjB;AAAA,IACA,UAAY;AAAA,MACV,MAAQ;AAAA,MACR,aAAe;AAAA,IACjB;AAAA,IACA,OAAS;AAAA,MACP,MAAQ,CAAC,QAAQ,QAAQ,SAAS;AAAA,MAClC,aAAe;AAAA,IACjB;AAAA,IACA,YAAc;AAAA,MACZ,MAAQ;AAAA,MACR,SAAW;AAAA,MACX,SAAW;AAAA,MACX,aAAe;AAAA,IACjB;AAAA,IACA,WAAa;AAAA,MACX,MAAQ;AAAA,MACR,aAAe;AAAA,IACjB;AAAA,EACF;AAAA,EACA,sBAAwB;AAC1B;;;ACtBO,IAAM,iBAAiB;;;ACFvB,SAAS,iBACd,MAC+B;AAC/B,QAAM,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,EAAE;AACvD,MAAI,gBAAgB;AACpB,MAAI,kBAAkB;AACtB,aAAW,OAAO,MAAM;AACtB,QAAI,CAAC,IAAI,SAAS;AAChB,YAAM,SAAS;AACf;AAAA,IACF;AACA,UAAM,IAAI,QAAQ,KAAK,KAAK;AAC5B,qBAAiB,IAAI,QAAQ;AAC7B,uBAAmB;AAAA,EACrB;AAEA,QAAM,YAAY,MAAM;AACxB,QAAM,YAAY,MAAM,OAAO,MAAM;AAErC,QAAM,UAAiB,YAAY,YAAY,SAAS;AACxD,QAAM,SAAS,YAAY;AAC3B,QAAM,YAAY,SAAS,IAAI,KAAK,IAAI,WAAW,SAAS,IAAI,SAAS;AACzE,QAAM,iBACJ,kBAAkB,IAAI,gBAAgB,kBAAkB;AAE1D,SAAO,EAAE,MAAM,OAAO,SAAS,WAAW,eAAe;AAC3D;;;ACtBO,IAAM,gBAAgC,EAAE,UAAU,KAAK,UAAU,IAAI;AAErE,SAAS,QACd,WACA,aAA6B,eACvB;AACN,QAAM,EAAE,OAAO,eAAe,IAAI;AAClC,QAAM,gBACJ,MAAM,OAAO,KACb,MAAM,SAAS,KACf,MAAM,YAAY,KAClB,MAAM,UAAU;AAClB,QAAM,gBACJ,MAAM,SAAS,KAAK,MAAM,UAAU,KAAK,MAAM,OAAO,MAAM,UAAU;AAExE,MAAI,iBAAiB,kBAAkB,WAAW,SAAU,QAAO;AACnE,MAAI,iBAAiB,kBAAkB,WAAW,SAAU,QAAO;AACnE,SAAO;AACT;;;ACcA,IAAI,oBAAoB;AAMxB,eAAsB,YACpB,SACqB;AACrB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,WAAW;AAAA,IACjB,cAAc;AAAA,IACd,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,IAAI;AAEJ,MAAI,cAAc,KAAK,CAAC,mBAAmB;AACzC,wBAAoB;AACpB,YAAQ;AAAA,MACN,GAAG,KAAK,0BAA0B,WAAW;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,SAAS,UAAU;AACrB,UAAM,MAAM,MAAM,IAAI,QAAQ;AAK9B,QAAI,MAAM,QAAQ,GAAG,EAAG,QAAO,IAAI,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,KAAK,EAAE;AAAA,EACxE;AAGA,QAAM,WAAW,aAAa,MAAM;AAEpC,QAAM,UAAsB,CAAC;AAC7B,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,UAAM,MAAM,MAAM,sBAAoC;AAAA,MACpD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAGD,UAAM,EAAE,QAAQ,GAAG,KAAK,IAAI;AAC5B,YAAQ,KAAK,WAAW,SAAY,OAAO,EAAE,GAAG,MAAM,SAAS,OAAO,CAAC;AAAA,EACzE;AAEA,MAAI,SAAS,SAAU,OAAM,IAAI,UAAU,OAAO;AAClD,SAAO;AACT;AAGA,eAAsB,MACpB,SAC0B;AAC1B,QAAM,OAAO,MAAM,YAAY,OAAO;AACtC,QAAM,OAAO,iBAAiB,IAAI;AAClC,SAAO,EAAE,GAAG,MAAM,MAAM,QAAQ,MAAM,QAAQ,SAAS,aAAa,EAAE;AACxE;AAGO,SAAS,0BAAgC;AAC9C,sBAAoB;AACtB;","names":["join","join","existsSync","mkdirSync","writeFileSync","homedir","join","join","homedir","existsSync","mkdirSync","writeFileSync","rmSync","statSync","join","join","rmSync","statSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hawkeyexl/inference",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared TypeScript LLM inference layer: schema-constrained completion across Anthropic, OpenAI-compatible, Claude CLI, and in-process local llama.cpp providers, with caching, cost accounting, and an LLM-as-judge ensemble.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|