@dvai-bridge/core 4.0.1 → 4.0.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/multimodalCallable.ts","../src/TransformersBackend.ts","../src/handlers/completions.ts","../src/rendezvous/keys.ts","../src/pairing/handshake.ts","../src/capability/heuristic.ts","../src/capability/precheck.ts","../src/handlers/chat.ts","../src/handlers/embeddings.ts","../src/handlers/models.ts","../src/handlers/index.ts","../src/transports/msw.ts","../src/transports/port-fallback.ts","../src/transports/http.ts","../src/transports/capacitor.ts","../src/transports/index.ts","../src/offload/decide.ts","../src/offload/error.ts","../src/offload/policy.ts","../src/offload/proxy.ts","../src/offload/forwarder.ts","../src/offload/index.ts","../src/capability/cache.ts","../src/capability/deviceId.ts","../src/capability/probe.ts","../src/capability/index.ts","../src/discovery/types.ts","../src/discovery/static.ts","../src/discovery/mdns-node.ts","../src/discovery/mdns-browser.ts","../src/discovery/composite.ts","../src/discovery/index.ts","../src/pairing/store.ts","../src/pairing/policy.ts","../src/pairing/index.ts","../src/handlers/dvai/index.ts","../src/NodeLlamaCppBackend.ts","../src/WebLLMBackend.ts","../src/index.ts","../src/license/LicenseValidator.ts","../src/license/publicKeys.ts","../src/license/audience.ts","../src/license/discovery.ts","../src/license/types.ts"],"sourcesContent":["/**\n * Shared helpers for the declarative multimodal loader path (used by both\n * the main-thread `TransformersBackend` and the worker-side init handler).\n *\n * The idea: a host app tells dvai-bridge \"load model `X` via class `Y` with\n * processor `Z`, and null fields `F1, F2` after load\". dvai-bridge does it\n * — no model-family knowledge baked into the library. If tomorrow a host\n * wants to run a Qwen-VL or an Idefics model, it's the same config surface;\n * if transformers.js exports the class, this path loads it.\n *\n * Limitation: the generic callable assumes the common `processor(prompt,\n * images, audio, options)` call signature. Most HuggingFace multimodal\n * processors follow this shape; ones that don't (e.g. processors taking\n * kwargs like `{ images, audios, videos }`) should fall back to the\n * main-thread `createPipeline` factory, which gives the host full control.\n * That path is still supported.\n */\n\n/**\n * Extract the media content parts (audio / images) from the last user\n * message in a chat-messages array. Returns `null` for a modality that\n * isn't present, so the processor call can pass-through.\n *\n * Content shape assumed per the OpenAI-compatible convention:\n * { role, content: string | Array<{ type: 'text'|'audio'|'image', ... }> }\n * with dvai-bridge's extension: audio parts carry `{ type: 'audio', data: Float32Array }`.\n */\nexport function extractMediaParts(messages: any[]): {\n\taudio: Float32Array | null;\n\timages: unknown[] | null;\n} {\n\tconst last = messages[messages.length - 1];\n\tif (!last || !Array.isArray(last.content)) {\n\t\treturn { audio: null, images: null };\n\t}\n\tlet audio: Float32Array | null = null;\n\tconst images: unknown[] = [];\n\tfor (const part of last.content) {\n\t\tif (!part) continue;\n\t\tif (part.type === \"audio\" && part.data) {\n\t\t\taudio = part.data as Float32Array;\n\t\t} else if (part.type === \"image\" && (part.image || part.data || part.url)) {\n\t\t\t// Push whichever field the host used; downstream processor accepts\n\t\t\t// various shapes (URL, RawImage, tensor). dvai-bridge stays\n\t\t\t// opinion-free here.\n\t\t\timages.push(part.image ?? part.data ?? part.url);\n\t\t}\n\t}\n\treturn { audio, images: images.length > 0 ? images : null };\n}\n\nexport interface MultimodalCallableOptions {\n\t/** Default max_new_tokens if caller doesn't specify. */\n\tdefaultMaxNewTokens?: number;\n}\n\n/**\n * Build a pipeline-shaped callable `(messages, options) => [{ generated_text }]`\n * that wraps a `(model, processor)` pair loaded by the declarative path.\n * Matches the contract of transformers.js's `pipeline()` output so the rest\n * of the backend code (chat completion, streaming, runPipeline) can treat\n * it interchangeably.\n *\n * The returned function also carries `.tokenizer` (for TextStreamer) and\n * `.dispose()` (for VRAM release during unload) as instance properties.\n */\nexport function buildMultimodalCallable(\n\tmodel: any,\n\tprocessor: any,\n\topts: MultimodalCallableOptions = {},\n): any {\n\tconst defaultMaxNewTokens = opts.defaultMaxNewTokens ?? 1024;\n\n\tconst callable: any = async (messages: any, options: any) => {\n\t\tconst prompt = processor.apply_chat_template(messages, {\n\t\t\tenable_thinking: false,\n\t\t\tadd_generation_prompt: true,\n\t\t});\n\t\tconst { audio, images } = extractMediaParts(messages);\n\t\tconst inputs = await processor(prompt, images, audio, {\n\t\t\tadd_special_tokens: false,\n\t\t});\n\t\tconst genArgs: Record<string, unknown> = {\n\t\t\t...inputs,\n\t\t\tmax_new_tokens: options?.max_new_tokens ?? defaultMaxNewTokens,\n\t\t\ttemperature: options?.temperature ?? 0,\n\t\t\tdo_sample: options?.do_sample ?? false,\n\t\t\ttop_p: options?.top_p ?? 1,\n\t\t};\n\t\tif (options?.streamer) genArgs.streamer = options.streamer;\n\t\tconst outputs = await model.generate(genArgs);\n\t\tconst promptLen = inputs.input_ids.dims.at(-1);\n\t\tconst generated = outputs.slice(null, [promptLen, null]);\n\t\tconst decoded = processor.batch_decode(generated, {\n\t\t\tskip_special_tokens: true,\n\t\t});\n\t\treturn [{ generated_text: decoded[0] ?? \"\" }];\n\t};\n\n\t// TextStreamer uses this to tokenize partial outputs during streaming.\n\tcallable.tokenizer = processor.tokenizer;\n\n\t// dvai-bridge calls this on unload to release VRAM held by the ONNX\n\t// session(s) behind the model. AutoProcessor has no dispose() in current\n\t// transformers.js, so we only drop the model's sessions.\n\tcallable.dispose = async () => {\n\t\ttry {\n\t\t\tawait model.dispose?.();\n\t\t} catch {\n\t\t\t/* ignore */\n\t\t}\n\t};\n\n\treturn callable;\n}\n\n/**\n * Null out named submodules on a loaded model to reclaim memory. Host apps\n * pass a list of field names (e.g., `['vision_encoder']`) based on which\n * modalities they don't use. dvai-bridge treats this as purely declarative —\n * it walks the list and nulls each field if present.\n */\nexport function disableModelEncoders(model: any, names: string[] | undefined): void {\n\tif (!names || names.length === 0) return;\n\tfor (const name of names) {\n\t\ttry {\n\t\t\tif (model && (model as any)[name]) {\n\t\t\t\t(model as any)[name] = null;\n\t\t\t}\n\t\t} catch {\n\t\t\t/* ignore — name didn't exist or was non-writable */\n\t\t}\n\t}\n}\n","/**\n * TransformersBackend: Wraps @huggingface/transformers for local inference.\n * - Multi-modal: supports any Transformers.js pipeline task (text-generation,\n * text-to-image, automatic-speech-recognition, image-to-text, etc.)\n * - Runs inference in a Web Worker to keep the main thread unblocked\n * - Falls back to main-thread pipeline if worker URL is not available\n * - Auto-detects WebGPU, falls back to CPU\n * - Provides OpenAI-compatible chat completion API for text-generation tasks\n * - Supports streaming responses for text tasks\n * - Direct pipeline access via runPipeline() for all modalities\n * - Timeout protection on all operations\n * - Three loader paths, in priority order:\n * 1. Main-thread `createPipeline` factory (when provided) — gives the\n * host complete control; used for models that need a custom\n * processor call signature that the declarative path doesn't cover.\n * 2. Declarative `modelClass` + `processorClass` + `disableEncoders`\n * — works in the worker AND on the main thread. Use this for any\n * multimodal model that follows the common processor signature.\n * 3. Generic `pipeline(task, modelId)` — the default, good for 99% of\n * transformers.js tasks.\n */\n\nimport {\n\tbuildMultimodalCallable,\n\tdisableModelEncoders,\n} from \"./multimodalCallable.js\";\n\nexport type DeviceType = \"webgpu\" | \"cpu\" | \"auto\";\n\n/**\n * Convert a Transformers.js Tensor (or plain array) into a nested number[][].\n * Handles both single-input and batched feature-extraction outputs.\n */\nfunction tensorToArray(t: any): number[][] {\n\tif (!t) return [];\n\tif (Array.isArray(t)) {\n\t\tif (t.length > 0 && Array.isArray(t[0])) return t as number[][];\n\t\treturn [t as number[]];\n\t}\n\tif (typeof t.tolist === \"function\") {\n\t\tconst arr = t.tolist();\n\t\tif (Array.isArray(arr) && arr.length > 0 && Array.isArray(arr[0]))\n\t\t\treturn arr;\n\t\treturn [arr];\n\t}\n\tif (t.data && t.dims) {\n\t\tconst [batch, hidden] =\n\t\t\tt.dims.length === 2 ? t.dims : [1, t.dims[t.dims.length - 1]];\n\t\tconst flat = Array.from(t.data as Iterable<number>);\n\t\tconst out: number[][] = [];\n\t\tfor (let i = 0; i < batch; i++) {\n\t\t\tout.push(flat.slice(i * hidden, (i + 1) * hidden));\n\t\t}\n\t\treturn out;\n\t}\n\treturn [];\n}\n\n/**\n * Aggressive content flattening to satisfy Jinja2 templates (like Llama 3)\n * that expect 'content' to be a string and use filters like '| trim'.\n */\nfunction flattenContent(content: any): string {\n\tif (typeof content === \"string\") return content;\n\tif (Array.isArray(content)) {\n\t\treturn content.map((c) => flattenContent(c)).join(\"\");\n\t}\n\tif (content && typeof content === \"object\") {\n\t\treturn content.text || content.content || JSON.stringify(content);\n\t}\n\treturn String(content || \"\");\n}\n\n/**\n * Supported pipeline tasks from Transformers.js.\n * Common tasks include:\n * - \"text-generation\" (default) — LLM chat/text generation\n * - \"text2text-generation\" — encoder-decoder text models\n * - \"text-to-image\" — image generation from text prompts\n * - \"image-to-text\" — image captioning\n * - \"automatic-speech-recognition\" — audio/speech to text\n * - \"text-to-speech\" — text to audio\n * - \"zero-shot-classification\" — classify without training\n * - \"feature-extraction\" — embeddings\n * - \"translation\" — language translation\n * - \"summarization\" — text summarization\n * - And many more: see https://huggingface.co/docs/transformers.js\n */\nexport type PipelineTask = string;\n\n/**\n * A pipeline-compatible callable function.\n * Accepts messages (chat format) and generation options,\n * returns results in the same shape as a Transformers.js pipeline:\n * [{ generated_text: string }]\n */\nexport type PipelineCallable = (messages: any, options?: any) => Promise<any>;\n\n/**\n * Factory function that the client can supply to customize model loading.\n * Receives the dynamically-imported @huggingface/transformers module and\n * config details; must return a PipelineCallable.\n *\n * This lets the client control *how* the model is loaded and how inference\n * is run, while DVAI handles everything else (MSW, OpenAI endpoint, etc.).\n */\nexport type CreatePipelineFn = (\n\ttransformers: any,\n\tctx: {\n\t\tmodelId: string;\n\t\tdevice: \"webgpu\" | \"wasm\" | \"cpu\";\n\t\tdtype?: string;\n\t\tonProgress?: (info: any) => void;\n\t},\n) => Promise<PipelineCallable>;\n\nexport interface TransformersBackendConfig {\n\tmodelId: string;\n\tdevice: DeviceType;\n\tgenerationTimeout: number;\n\tworkerUrl?: string;\n\t/** The pipeline task to use. Default: \"text-generation\" */\n\tpipelineTask?: PipelineTask;\n\t/** Quantization/DType for the model (e.g. 'q4', 'q8', 'f16'). Default: undefined */\n\tdtype?: string;\n\t/**\n\t * Optional custom pipeline factory. Main-thread only (function closures\n\t * don't cross the Worker boundary — use `modelClass`/`processorClass`\n\t * for the worker path instead). Replaces the default `pipeline()` call.\n\t * Use this when your model's processor takes a non-standard call\n\t * signature that the declarative multimodal callable can't express.\n\t */\n\tcreatePipeline?: CreatePipelineFn;\n\t/**\n\t * Name of a transformers.js export to use as the model class, loaded via\n\t * `ClassName.from_pretrained(modelId)`. Enables the declarative\n\t * multimodal loader path (works in the worker and on main thread).\n\t * Examples: \"Gemma4ForConditionalGeneration\", \"LlavaForConditionalGeneration\",\n\t * \"AutoModelForCausalLM\". When unset, falls back to `pipeline()`.\n\t */\n\tmodelClass?: string;\n\t/**\n\t * Name of a transformers.js export to use as the processor class.\n\t * Only used when `modelClass` is set. Default: \"AutoProcessor\".\n\t */\n\tprocessorClass?: string;\n\t/**\n\t * Model submodule fields to null out after `from_pretrained`, e.g.\n\t * `[\"vision_encoder\"]` for a voice-only host app using a multimodal\n\t * checkpoint. Purely declarative — backend walks the list and nulls\n\t * each named field if present. Unknown/absent names are ignored.\n\t */\n\tdisableEncoders?: string[];\n}\n\nexport interface TransformersProgressInfo {\n\tstatus: string;\n\tname?: string;\n\tfile?: string;\n\tprogress?: number;\n\tloaded?: number;\n\ttotal?: number;\n}\n\n/**\n * Detects whether WebGPU is available in the current environment.\n */\nexport async function detectWebGPU(): Promise<boolean> {\n\tif (typeof navigator === \"undefined\") return false;\n\tif (!(\"gpu\" in navigator)) return false;\n\ttry {\n\t\tconst adapter = await (navigator as any).gpu.requestAdapter();\n\t\treturn adapter !== null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport class TransformersBackend {\n\tprivate pipeline: any = null;\n\tprivate worker: Worker | null = null;\n\tprivate modelId: string;\n\tprivate device: DeviceType;\n\tprivate resolvedDevice: \"webgpu\" | \"wasm\" | \"cpu\" = \"wasm\";\n\tprivate generationTimeout: number;\n\tprivate workerUrl?: string;\n\tprivate pipelineTask: PipelineTask;\n\tprivate dtype?: string;\n\tprivate createPipelineFn?: CreatePipelineFn;\n\tprivate modelClass?: string;\n\tprivate processorClass?: string;\n\tprivate disableEncoders?: string[];\n\tprivate usingWorker: boolean = false;\n\tprivate pendingRequests: Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (value: any) => void;\n\t\t\treject: (error: any) => void;\n\t\t}\n\t> = new Map();\n\tprivate pendingStreams: Map<\n\t\tstring,\n\t\t{\n\t\t\tonChunk: (text: string) => void;\n\t\t\tonComplete: () => void;\n\t\t\tonError: (error: any) => void;\n\t\t}\n\t> = new Map();\n\n\tconstructor(config: TransformersBackendConfig) {\n\t\tthis.modelId = config.modelId;\n\t\tthis.device = config.device;\n\t\tthis.generationTimeout = config.generationTimeout;\n\t\tthis.workerUrl = config.workerUrl;\n\t\tthis.pipelineTask = config.pipelineTask || \"text-generation\";\n\t\tthis.dtype = config.dtype;\n\t\tthis.createPipelineFn = config.createPipeline;\n\t\tthis.modelClass = config.modelClass;\n\t\tthis.processorClass = config.processorClass;\n\t\tthis.disableEncoders = config.disableEncoders;\n\t}\n\n\tasync initialize(onProgress?: (info: any) => void): Promise<void> {\n\t\t// Resolve device.\n\t\t//\n\t\t// Runtime split:\n\t\t// - Browser → onnxruntime-web → accepts \"wasm\" + \"webgpu\". CPU is \"wasm\".\n\t\t// - Node → onnxruntime-node → accepts \"cpu\" + \"dml\" + \"webgpu\" (experimental).\n\t\t// Throws on \"wasm\".\n\t\t//\n\t\t// Detect once and route accordingly. Without this branch, Node hosts\n\t\t// (the v3.1 Hub, the node-langchain example, etc.) fail to initialize\n\t\t// with \"Unsupported device: \\\"wasm\\\"\".\n\t\tconst isNode =\n\t\t\ttypeof window === \"undefined\" &&\n\t\t\ttypeof process !== \"undefined\" &&\n\t\t\t(process as any).versions?.node !== undefined;\n\n\t\tif (this.device === \"auto\") {\n\t\t\tconst hasWebGPU = await detectWebGPU();\n\t\t\tthis.resolvedDevice = hasWebGPU ? \"webgpu\" : isNode ? \"cpu\" : \"wasm\";\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI/Transformers] Auto-detected device: ${this.resolvedDevice}`,\n\t\t\t);\n\t\t} else if (this.device === \"cpu\") {\n\t\t\tthis.resolvedDevice = isNode ? \"cpu\" : \"wasm\";\n\t\t} else {\n\t\t\tthis.resolvedDevice = this.device as \"webgpu\" | \"wasm\" | \"cpu\";\n\t\t}\n\n\t\t// Worker-based initialization is the DEFAULT path. Running inference\n\t\t// off the main thread is a baseline guarantee dvai-bridge makes to\n\t\t// every host app — no host should have to worry about Gemma / Llama /\n\t\t// whisper forward-passes stalling their UI. The Worker constructor\n\t\t// auto-falls back to main-thread only in truly broken environments\n\t\t// (Worker unavailable, or the bundled worker file failed to load).\n\t\tif (this.workerUrl && typeof Worker !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tawait this.initializeWithWorker(onProgress);\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\t// Raised to error level + remediation tip. Silent main-thread\n\t\t\t\t// fallback was hiding deployment bugs where the worker file\n\t\t\t\t// wasn't copied to public/.\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[DVAI/Transformers] Worker initialization FAILED — falling back to main thread. \" +\n\t\t\t\t\t\t\"This WILL block the UI during inference. Check that the worker file is \" +\n\t\t\t\t\t\t`deployed at \"${this.workerUrl}\" (run \\`dvai-bridge init\\` to copy it).`,\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t\tthis.worker = null;\n\t\t\t}\n\t\t} else if (!this.workerUrl) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[DVAI/Transformers] No workerUrl configured — running on main thread. \" +\n\t\t\t\t\t\"This blocks the UI during inference. Set `transformersWorkerUrl` \" +\n\t\t\t\t\t\"(defaults to '/dvai-transformers.worker.js') to enable the worker path.\",\n\t\t\t);\n\t\t}\n\n\t\t// Fallback: main-thread pipeline\n\t\tawait this.initializeMainThread(onProgress);\n\t}\n\n\tprivate async initializeWithWorker(\n\t\tonProgress?: (info: any) => void,\n\t): Promise<void> {\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst worker = new Worker(this.workerUrl!, { type: \"module\" });\n\t\t\tconst requestId = this.generateRequestId();\n\n\t\t\tconst handleMessage = (event: MessageEvent) => {\n\t\t\t\tconst msg = event.data;\n\t\t\t\tif (msg.id !== requestId) return;\n\n\t\t\t\tswitch (msg.type) {\n\t\t\t\t\tcase \"progress\":\n\t\t\t\t\t\tif (onProgress) {\n\t\t\t\t\t\t\tconst info = msg.data;\n\t\t\t\t\t\t\tconst progressValue = info.progress ?? 0;\n\t\t\t\t\t\t\tconst text =\n\t\t\t\t\t\t\t\tinfo.status === \"progress\"\n\t\t\t\t\t\t\t\t\t? `Downloading ${info.file}: ${Math.round(progressValue)}%`\n\t\t\t\t\t\t\t\t\t: info.status === \"ready\"\n\t\t\t\t\t\t\t\t\t\t? \"Model ready\"\n\t\t\t\t\t\t\t\t\t\t: `${info.status}${info.file ? `: ${info.file}` : \"\"}`;\n\t\t\t\t\t\t\tonProgress({\n\t\t\t\t\t\t\t\ttext,\n\t\t\t\t\t\t\t\tprogress: progressValue / 100,\n\t\t\t\t\t\t\t\ttimeElapsed: 0,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"init_complete\":\n\t\t\t\t\t\tworker.removeEventListener(\"message\", handleMessage);\n\t\t\t\t\t\tthis.worker = worker;\n\t\t\t\t\t\tthis.usingWorker = true;\n\n\t\t\t\t\t\t// Set up persistent message handler for future requests\n\t\t\t\t\t\tworker.addEventListener(\"message\", (e: MessageEvent) =>\n\t\t\t\t\t\t\tthis.handleWorkerMessage(e),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"[DVAI/Transformers] Initialized with Web Worker (main thread unblocked)\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\tworker.removeEventListener(\"message\", handleMessage);\n\t\t\t\t\t\tworker.terminate();\n\t\t\t\t\t\treject(new Error(msg.error));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tworker.addEventListener(\"message\", handleMessage);\n\t\t\tworker.addEventListener(\"error\", (err: any) => {\n\t\t\t\tworker.removeEventListener(\"message\", handleMessage);\n\t\t\t\tconst errorMessage =\n\t\t\t\t\terr.message ||\n\t\t\t\t\t(err.error ? err.error.message : \"Unknown worker error\");\n\t\t\t\treject(new Error(`Worker error: ${errorMessage}`));\n\t\t\t});\n\n\t\t\tconst initParams = {\n\t\t\t\ttype: \"init\",\n\t\t\t\tid: requestId,\n\t\t\t\tpipelineTask: this.pipelineTask,\n\t\t\t\tmodelId: this.modelId,\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tdtype: this.dtype,\n\t\t\t\t// Declarative loader config — worker uses these to pick the\n\t\t\t\t// model+processor path when modelClass is set, otherwise it\n\t\t\t\t// falls back to pipeline(task, modelId). Host app controls\n\t\t\t\t// which class to load, which processor to pair with it, and\n\t\t\t\t// which encoder submodules to null after load.\n\t\t\t\tmodelClass: this.modelClass,\n\t\t\t\tprocessorClass: this.processorClass,\n\t\t\t\tdisableEncoders: this.disableEncoders,\n\t\t\t};\n\t\t\tconsole.log(\"[DVAI/Transformers] Sending init to worker:\", initParams);\n\t\t\tworker.postMessage(initParams);\n\t\t});\n\t}\n\n\tprivate handleWorkerMessage(event: MessageEvent) {\n\t\tconst msg = event.data;\n\t\tif (!msg.id) return;\n\n\t\t// Streaming messages first — they share an id with pendingStreams\n\t\tconst stream = this.pendingStreams.get(msg.id);\n\t\tif (stream) {\n\t\t\tswitch (msg.type) {\n\t\t\t\tcase \"stream_chunk\":\n\t\t\t\t\tstream.onChunk(msg.text);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"stream_complete\":\n\t\t\t\t\tthis.pendingStreams.delete(msg.id);\n\t\t\t\t\tstream.onComplete();\n\t\t\t\t\treturn;\n\t\t\t\tcase \"error\":\n\t\t\t\t\tthis.pendingStreams.delete(msg.id);\n\t\t\t\t\tstream.onError(\n\t\t\t\t\t\tnew Error(msg.error || \"Unknown worker internal error\"),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tconst pending = this.pendingRequests.get(msg.id);\n\t\tif (!pending) return;\n\n\t\tswitch (msg.type) {\n\t\t\tcase \"generate_complete\":\n\t\t\tcase \"embed_complete\":\n\t\t\t\tthis.pendingRequests.delete(msg.id);\n\t\t\t\tpending.resolve(msg.data);\n\t\t\t\tbreak;\n\t\t\tcase \"unload_complete\":\n\t\t\t\tthis.pendingRequests.delete(msg.id);\n\t\t\t\tpending.resolve(undefined);\n\t\t\t\tbreak;\n\t\t\tcase \"error\":\n\t\t\t\tthis.pendingRequests.delete(msg.id);\n\t\t\t\tpending.reject(new Error(msg.error || \"Unknown worker internal error\"));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate sendWorkerRequest(\n\t\ttype: string,\n\t\tdata: Record<string, any> = {},\n\t): Promise<any> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst id = this.generateRequestId();\n\t\t\tthis.pendingRequests.set(id, { resolve, reject });\n\t\t\tthis.worker!.postMessage({ type, id, ...data });\n\t\t});\n\t}\n\n\tprivate async initializeMainThread(\n\t\tonProgress?: (info: any) => void,\n\t): Promise<void> {\n\t\t// @ts-ignore - module resolved at runtime after pnpm install\n\t\tconst transformers = await import(\"@huggingface/transformers\");\n\t\tconst { pipeline: pipelineFn, env } = transformers;\n\n\t\t// Only allow local models if explicitly requested or if we're in a specific environment.\n\t\t// Defaulting to false ensures CDN fallback when local models are not provided.\n\t\t// Force remote models from Hugging Face CDN\n\t\tenv.allowLocalModels = false;\n\t\tenv.allowRemoteModels = true;\n\t\tenv.remoteHost = \"https://huggingface.co\";\n\t\tenv.remotePathTemplate = \"{model}/resolve/{revision}/\";\n\n\t\tconst progressCallback = onProgress\n\t\t\t? (info: TransformersProgressInfo) => {\n\t\t\t\t\tconst progressValue = info.progress ?? 0;\n\t\t\t\t\tconst text =\n\t\t\t\t\t\tinfo.status === \"progress\"\n\t\t\t\t\t\t\t? `Downloading ${info.file}: ${Math.round(progressValue)}%`\n\t\t\t\t\t\t\t: info.status === \"ready\"\n\t\t\t\t\t\t\t\t? `Model ready (${this.pipelineTask})`\n\t\t\t\t\t\t\t\t: `${info.status}${info.file ? `: ${info.file}` : \"\"}`;\n\t\t\t\t\tonProgress({ text, progress: progressValue / 100, timeElapsed: 0 });\n\t\t\t\t}\n\t\t\t: undefined;\n\n\t\tif (this.createPipelineFn) {\n\t\t\t// Client-supplied custom pipeline factory — use it instead of pipeline().\n\t\t\tconsole.log(\"[DVAI/Transformers] Using custom createPipeline factory.\");\n\t\t\tthis.pipeline = await this.createPipelineFn(transformers, {\n\t\t\t\tmodelId: this.modelId,\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tdtype: this.dtype,\n\t\t\t\tonProgress: progressCallback,\n\t\t\t});\n\t\t} else if (this.modelClass) {\n\t\t\t// Declarative model+processor loader — same contract as the\n\t\t\t// worker path. Keeps behavior consistent regardless of which\n\t\t\t// path the env ends up on (e.g., a dev running without the\n\t\t\t// worker file deployed yet).\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI/Transformers] Using declarative loader: ${this.modelClass} + ${this.processorClass || \"AutoProcessor\"}`,\n\t\t\t);\n\t\t\tconst ModelClass = (transformers as any)[this.modelClass];\n\t\t\tif (!ModelClass) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`transformers.js has no export named \"${this.modelClass}\". Check your modelClass config.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst processorName = this.processorClass || \"AutoProcessor\";\n\t\t\tconst ProcessorClass = (transformers as any)[processorName];\n\t\t\tif (!ProcessorClass) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`transformers.js has no export named \"${processorName}\". Check your processorClass config.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst processor = await ProcessorClass.from_pretrained(this.modelId, {\n\t\t\t\tprogress_callback: progressCallback,\n\t\t\t});\n\t\t\tconst model = await ModelClass.from_pretrained(this.modelId, {\n\t\t\t\tdtype: this.dtype as any,\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tprogress_callback: progressCallback,\n\t\t\t});\n\t\t\tdisableModelEncoders(model, this.disableEncoders);\n\t\t\tthis.pipeline = buildMultimodalCallable(model, processor);\n\t\t} else {\n\t\t\tthis.pipeline = await pipelineFn(this.pipelineTask as any, this.modelId, {\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tprogress_callback: progressCallback,\n\t\t\t\tdtype: this.dtype as any,\n\t\t\t});\n\t\t}\n\t\tthis.usingWorker = false;\n\n\t\tif (this.resolvedDevice === \"wasm\" || this.resolvedDevice === \"cpu\") {\n\t\t\tconsole.warn(\n\t\t\t\t`[DVAI/Transformers] Running on main thread with ${this.resolvedDevice.toUpperCase()} (CPU). ` +\n\t\t\t\t\t(this.resolvedDevice === \"wasm\"\n\t\t\t\t\t\t? \"Inference may block the UI. Set `workerUrl` to a deployed transformers worker for better performance.\"\n\t\t\t\t\t\t: \"(Node host — no UI thread to block.)\"),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\t\"[DVAI/Transformers] Initialized on main thread (WebGPU compute is async)\",\n\t\t\t);\n\t\t}\n\t}\n\n\tgetPipelineTask(): PipelineTask {\n\t\treturn this.pipelineTask;\n\t}\n\n\tgetResolvedDevice(): \"webgpu\" | \"wasm\" | \"cpu\" {\n\t\treturn this.resolvedDevice;\n\t}\n\n\tisWorkerBased(): boolean {\n\t\treturn this.usingWorker;\n\t}\n\n\tgetPipeline(): any {\n\t\treturn this.pipeline;\n\t}\n\n\t/**\n\t * Returns whether the current task is a text generation task\n\t * (supports OpenAI-compatible chat completion API).\n\t */\n\tisTextTask(): boolean {\n\t\treturn [\n\t\t\t\"text-generation\",\n\t\t\t\"text2text-generation\",\n\t\t\t\"summarization\",\n\t\t\t\"translation\",\n\t\t\t\"image-text-to-text\",\n\t\t\t\"any-to-any\",\n\t\t].includes(this.pipelineTask);\n\t}\n\n\t/**\n\t * Run the pipeline directly with arbitrary inputs.\n\t * Use this for non-text-generation tasks (text-to-image, STT, etc.)\n\t * or when you need full control over the pipeline output.\n\t *\n\t * @param inputs - Input data (text, audio buffer, image URL, etc.)\n\t * @param options - Pipeline-specific options\n\t * @returns Raw pipeline output (varies by task)\n\t */\n\tasync runPipeline(inputs: any, options?: Record<string, any>): Promise<any> {\n\t\tif (this.usingWorker && this.worker) {\n\t\t\treturn this.withTimeout(\n\t\t\t\tthis.sendWorkerRequest(\"generate\", {\n\t\t\t\t\trequestBody: { raw: true, inputs, options },\n\t\t\t\t}),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t}\n\t\tif (!this.pipeline)\n\t\t\tthrow new Error(\"Transformers.js pipeline not initialized\");\n\t\treturn this.withTimeout(\n\t\t\tthis.pipeline(inputs, options),\n\t\t\tthis.generationTimeout,\n\t\t);\n\t}\n\n\t/**\n\t * OpenAI-compatible non-streaming chat completion.\n\t * Only works for text-generation / text2text-generation tasks.\n\t */\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.isTextTask()) {\n\t\t\tthrow new Error(\n\t\t\t\t`chatCompletion() is only available for text-generation tasks. ` +\n\t\t\t\t\t`Current task: \"${this.pipelineTask}\". Use runPipeline() instead.`,\n\t\t\t);\n\t\t}\n\n\t\t// Aggressive sanitization: ensure content is ALWAYS a string\n\t\tconst messages = (requestBody.messages || []).map((m: any) => ({\n\t\t\t...m,\n\t\t\tcontent: flattenContent(m.content),\n\t\t}));\n\n\t\tconst maxNewTokens =\n\t\t\trequestBody.max_tokens ?? requestBody.max_completion_tokens ?? 256;\n\t\tconst temperature = requestBody.temperature ?? 0.7;\n\t\tconst topP = requestBody.top_p ?? 1.0;\n\n\t\tconst options: any = {\n\t\t\tmax_new_tokens: maxNewTokens,\n\t\t\ttemperature,\n\t\t\ttop_p: topP,\n\t\t\tdo_sample: temperature > 0,\n\t\t\treturn_full_text: false,\n\t\t};\n\n\t\tlet result: any;\n\t\tif (this.usingWorker && this.worker) {\n\t\t\t// Run inference in worker\n\t\t\tresult = await this.withTimeout(\n\t\t\t\tthis.sendWorkerRequest(\"generate\", {\n\t\t\t\t\trequestBody: { ...requestBody, messages },\n\t\t\t\t}),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t} else if (this.pipeline) {\n\t\t\t// Run inference on main thread\n\t\t\tconsole.log(\"[DVAI/Transformers] Running local inference:\", messages);\n\t\t\tresult = await this.withTimeout(\n\t\t\t\tthis.pipeline(messages, options),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(\"Transformers.js backend not initialized\");\n\t\t}\n\n\t\t// Extract generated text\n\t\tconst generatedText = (result as any)?.[0]?.generated_text ?? \"\";\n\t\tconst content =\n\t\t\ttypeof generatedText === \"string\"\n\t\t\t\t? generatedText\n\t\t\t\t: Array.isArray(generatedText)\n\t\t\t\t\t? (generatedText[generatedText.length - 1]?.content ?? \"\")\n\t\t\t\t\t: String(generatedText);\n\n\t\treturn {\n\t\t\tid: `chatcmpl-${Date.now()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel: this.modelId,\n\t\t\tchoices: [\n\t\t\t\t{\n\t\t\t\t\tindex: 0,\n\t\t\t\t\tmessage: { role: \"assistant\", content },\n\t\t\t\t\tfinish_reason: \"stop\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tusage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n\t\t};\n\t}\n\n\t/**\n\t * OpenAI-compatible streaming chat completion using real token-level streaming\n\t * via Transformers.js TextStreamer. Returns a ReadableStream of SSE-formatted data.\n\t */\n\tcreateStreamingResponse(requestBody: any): ReadableStream<Uint8Array> {\n\t\tif (!this.isTextTask()) {\n\t\t\tthrow new Error(\n\t\t\t\t`Streaming chat completion is only available for text-generation tasks. ` +\n\t\t\t\t\t`Current task: \"${this.pipelineTask}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst modelId = this.modelId;\n\t\tconst backend = this;\n\t\tconst generationTimeout = this.generationTimeout;\n\n\t\treturn new ReadableStream<Uint8Array>({\n\t\t\tasync start(controller) {\n\t\t\t\tconst completionId = `chatcmpl-${Date.now()}`;\n\t\t\t\tconst created = Math.floor(Date.now() / 1000);\n\t\t\t\tconst encoder = new TextEncoder();\n\n\t\t\t\tconst enqueueChunk = (text: string) => {\n\t\t\t\t\tconst chunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{ index: 0, delta: { content: text }, finish_reason: null },\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tconst enqueueFinal = (finishReason: string = \"stop\") => {\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [{ index: 0, delta: {}, finish_reason: finishReason }],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst streamPromise = new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\tif (backend.usingWorker && backend.worker) {\n\t\t\t\t\t\t\tconst id = backend.generateRequestId();\n\t\t\t\t\t\t\tbackend.pendingStreams.set(id, {\n\t\t\t\t\t\t\t\tonChunk: enqueueChunk,\n\t\t\t\t\t\t\t\tonComplete: resolve,\n\t\t\t\t\t\t\t\tonError: reject,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tbackend.worker.postMessage({\n\t\t\t\t\t\t\t\ttype: \"generate_stream\",\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\trequestBody,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (backend.pipeline) {\n\t\t\t\t\t\t\t// Main-thread streaming via TextStreamer.\n\t\t\t\t\t\t\t// Lazy-load @huggingface/transformers to access TextStreamer without\n\t\t\t\t\t\t\t// importing it at top-level (keeps the main bundle lean).\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\t\t\t\t\t\t(async () => {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t// @ts-ignore - module resolved at runtime\n\t\t\t\t\t\t\t\t\tconst { TextStreamer } =\n\t\t\t\t\t\t\t\t\t\tawait import(\"@huggingface/transformers\");\n\t\t\t\t\t\t\t\t\tconst tokenizer = (backend.pipeline as any).tokenizer;\n\t\t\t\t\t\t\t\t\tif (!tokenizer) {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t\t\t\"Streaming requires a tokenizer on the pipeline.\",\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tconst messages = (requestBody.messages || []).map(\n\t\t\t\t\t\t\t\t\t\t(m: any) => ({\n\t\t\t\t\t\t\t\t\t\t\t...m,\n\t\t\t\t\t\t\t\t\t\t\tcontent: flattenContent(m.content),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconst options: any = {\n\t\t\t\t\t\t\t\t\t\tmax_new_tokens:\n\t\t\t\t\t\t\t\t\t\t\trequestBody.max_tokens ??\n\t\t\t\t\t\t\t\t\t\t\trequestBody.max_completion_tokens ??\n\t\t\t\t\t\t\t\t\t\t\t256,\n\t\t\t\t\t\t\t\t\t\ttemperature: requestBody.temperature ?? 0.7,\n\t\t\t\t\t\t\t\t\t\ttop_p: requestBody.top_p ?? 1.0,\n\t\t\t\t\t\t\t\t\t\tdo_sample: (requestBody.temperature ?? 0.7) > 0,\n\t\t\t\t\t\t\t\t\t\treturn_full_text: false,\n\t\t\t\t\t\t\t\t\t\tstreamer: new TextStreamer(tokenizer, {\n\t\t\t\t\t\t\t\t\t\t\tskip_prompt: true,\n\t\t\t\t\t\t\t\t\t\t\tskip_special_tokens: true,\n\t\t\t\t\t\t\t\t\t\t\tcallback_function: (text: string) => {\n\t\t\t\t\t\t\t\t\t\t\t\tif (text) enqueueChunk(text);\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\tawait backend.pipeline(messages, options);\n\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\t\treject(e);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(new Error(\"Transformers.js backend not initialized\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\t\t\t\ttimeoutId = setTimeout(\n\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t`Generation timed out after ${generationTimeout}ms`,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tgenerationTimeout,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tawait Promise.race([streamPromise, timeoutPromise]);\n\t\t\t\t\tenqueueFinal(\"stop\");\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tconsole.error(\"[DVAI/Transformers] Stream error:\", error.message);\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(\n\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: error.message })}\\n\\n`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Generate embeddings for one or more text inputs.\n\t * Requires the pipeline to have been initialized with pipelineTask=\"feature-extraction\".\n\t *\n\t * @param inputs - A single string or array of strings to embed\n\t * @returns An array of embedding vectors (one per input)\n\t */\n\tasync embedding(inputs: string | string[]): Promise<number[][]> {\n\t\tif (this.pipelineTask !== \"feature-extraction\") {\n\t\t\tthrow new Error(\n\t\t\t\t`embedding() requires pipelineTask=\"feature-extraction\". Current task: \"${this.pipelineTask}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst inputArray = Array.isArray(inputs) ? inputs : [inputs];\n\n\t\tif (this.usingWorker && this.worker) {\n\t\t\tconst result = await this.withTimeout(\n\t\t\t\tthis.sendWorkerRequest(\"embed\", {\n\t\t\t\t\tinputs: inputArray,\n\t\t\t\t\tpooling: \"mean\",\n\t\t\t\t\tnormalize: true,\n\t\t\t\t}),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t\treturn result as number[][];\n\t\t}\n\n\t\tif (!this.pipeline) {\n\t\t\tthrow new Error(\"Transformers.js pipeline not initialized\");\n\t\t}\n\n\t\tconst raw = await this.withTimeout(\n\t\t\tthis.pipeline(inputArray, { pooling: \"mean\", normalize: true }),\n\t\t\tthis.generationTimeout,\n\t\t);\n\t\treturn tensorToArray(raw);\n\t}\n\n\tasync unload(): Promise<void> {\n\t\tif (this.usingWorker && this.worker) {\n\t\t\ttry {\n\t\t\t\tawait this.sendWorkerRequest(\"unload\");\n\t\t\t} catch (_) {\n\t\t\t\t/* best effort */\n\t\t\t}\n\t\t\tthis.worker.terminate();\n\t\t\tthis.worker = null;\n\t\t}\n\n\t\tif (this.pipeline) {\n\t\t\tif (typeof this.pipeline.dispose === \"function\") {\n\t\t\t\tawait this.pipeline.dispose();\n\t\t\t}\n\t\t\tthis.pipeline = null;\n\t\t}\n\n\t\tthis.pendingRequests.clear();\n\t\tthis.usingWorker = false;\n\t}\n\n\t/** Wraps a promise with a timeout. */\n\tprivate withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() => reject(new Error(`Generation timed out after ${ms}ms`)),\n\t\t\t\tms,\n\t\t\t);\n\t\t\tpromise\n\t\t\t\t.then((val) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tresolve(val);\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(err);\n\t\t\t\t});\n\t\t});\n\t}\n\n\tprivate generateRequestId(): string {\n\t\treturn `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n\t}\n}\n","import type { HandlerContext } from \"./context\";\n\n/**\n * Convert an OpenAI chat.completion response body into the legacy\n * text_completion shape used by POST /v1/completions.\n */\nexport function chatToLegacyCompletion(chatResp: any): any {\n return {\n id:\n (chatResp.id || \"\").replace(\"chatcmpl-\", \"cmpl-\") || `cmpl-${Date.now()}`,\n object: \"text_completion\",\n created: chatResp.created ?? Math.floor(Date.now() / 1000),\n model: chatResp.model,\n choices: (chatResp.choices || []).map((c: any) => ({\n text: c.message?.content ?? \"\",\n index: c.index ?? 0,\n finish_reason: c.finish_reason ?? \"stop\",\n logprobs: null,\n })),\n usage: chatResp.usage ?? {\n prompt_tokens: 0,\n completion_tokens: 0,\n total_tokens: 0,\n },\n };\n}\n\n/**\n * Wraps an SSE stream of chat.completion.chunk events and rewrites each\n * event as a legacy text_completion chunk. Preserves event boundaries.\n */\nexport function legacyCompletionStreamAdapter(\n chatStream: ReadableStream<Uint8Array>,\n model: string,\n): ReadableStream<Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let buffer = \"\";\n\n return new ReadableStream<Uint8Array>({\n async start(controller) {\n const reader = chatStream.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n let idx: number;\n while ((idx = buffer.indexOf(\"\\n\\n\")) !== -1) {\n const rawEvent = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 2);\n const dataLine = rawEvent\n .split(\"\\n\")\n .find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n const payload = dataLine.slice(\"data:\".length).trim();\n if (payload === \"[DONE]\") {\n controller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n continue;\n }\n try {\n const chunk = JSON.parse(payload);\n const legacyChunk = {\n id: (chunk.id || \"\").replace(\"chatcmpl-\", \"cmpl-\"),\n object: \"text_completion.chunk\",\n created: chunk.created,\n model: chunk.model || model,\n choices: (chunk.choices || []).map((c: any) => ({\n text: c.delta?.content ?? \"\",\n index: c.index ?? 0,\n finish_reason: c.finish_reason ?? null,\n logprobs: null,\n })),\n };\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(legacyChunk)}\\n\\n`),\n );\n } catch {\n controller.enqueue(encoder.encode(`data: ${payload}\\n\\n`));\n }\n }\n }\n } finally {\n controller.close();\n }\n },\n });\n}\n\nexport async function handleCompletion(\n body: any,\n ctx: HandlerContext,\n): Promise<Response> {\n if (!ctx.backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n\n const promptField = body.prompt;\n const prompt = Array.isArray(promptField)\n ? promptField.join(\"\\n\")\n : (promptField ?? \"\");\n const chatBody = {\n ...body,\n messages: [{ role: \"user\", content: prompt }],\n };\n delete chatBody.prompt;\n\n try {\n if (chatBody.stream) {\n const chatStream = ctx.backend.createStreamingResponse(chatBody);\n const legacyStream = legacyCompletionStreamAdapter(\n chatStream,\n body.model || ctx.modelId,\n );\n return new Response(legacyStream, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n }\n const chatResp = await ctx.backend.chatCompletion(chatBody);\n return Response.json(chatToLegacyCompletion(chatResp));\n } catch (error: any) {\n return Response.json({ error: error.message }, { status: 500 });\n }\n}\n","/**\n * X25519 ephemeral key generation + shared-secret derivation.\n *\n * Both pairing devices generate an ephemeral X25519 keypair, exchange\n * public keys via the rendezvous server, and derive an identical\n * shared secret independently. The server only relays the public\n * keys — it cannot derive the secret.\n *\n * Implementation note: WebCrypto's `subtle.deriveKey` doesn't support\n * X25519 in all runtimes yet (Node has it as of 22; Safari is still\n * catching up). We use `@noble/curves/ed25519` (which exports x25519)\n * because it's small (~5 KB), audited, and works in every JS runtime\n * we support without a native dep.\n */\n\nimport { x25519 } from \"@noble/curves/ed25519\";\n\nexport interface KeyPair {\n publicKey: Uint8Array;\n secretKey: Uint8Array;\n}\n\n/** Generate a fresh ephemeral X25519 keypair. */\nexport function generateEphemeralKeyPair(): KeyPair {\n const secretKey = x25519.utils.randomPrivateKey();\n const publicKey = x25519.getPublicKey(secretKey);\n return { publicKey, secretKey };\n}\n\n/** Derive the 32-byte shared secret. Inputs are raw byte arrays. */\nexport function deriveSharedSecret(\n ourSecretKey: Uint8Array,\n theirPublicKey: Uint8Array,\n): Uint8Array {\n return x25519.getSharedSecret(ourSecretKey, theirPublicKey);\n}\n\n/** Encode bytes as URL-safe base64 (no padding). */\nexport function encodeBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (const b of bytes) binary += String.fromCharCode(b);\n const b64 =\n typeof btoa !== \"undefined\"\n ? btoa(binary)\n : Buffer.from(binary, \"binary\").toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Decode URL-safe base64 → bytes. Throws on invalid input. */\nexport function decodeBase64Url(s: string): Uint8Array {\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const binary =\n typeof atob !== \"undefined\"\n ? atob(padded)\n : Buffer.from(padded, \"base64\").toString(\"binary\");\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n","/**\n * LAN-pairing handshake. The first time Device A wants to offload to\n * Device B over the LAN, A POSTs /v1/dvai/handshake to B with its\n * identity + a nonce. B surfaces a UI prompt to the user; on approve,\n * B generates a 256-bit pairing key and returns it. From then on, A\n * includes `X-DVAI-Pairing: HMAC-SHA256(pairingKey, body)` on every\n * offload request to B.\n */\n\nimport { encodeBase64Url } from \"../rendezvous/keys.js\";\nimport type { HandshakeRequest, HandshakeResponse } from \"./types.js\";\n\n/** Generate a fresh 256-bit pairing key (base64-url encoded). */\nexport function generatePairingKey(): string {\n const cryptoApi = typeof globalThis.crypto !== \"undefined\" ? globalThis.crypto : undefined;\n if (!cryptoApi || typeof cryptoApi.getRandomValues !== \"function\") {\n throw new Error(\"[DVAI/pairing] no secure random source\");\n }\n const bytes = new Uint8Array(32);\n cryptoApi.getRandomValues(bytes);\n return encodeBase64Url(bytes);\n}\n\n/** Generate a fresh nonce for a handshake request. */\nexport function generateNonce(): string {\n const cryptoApi = typeof globalThis.crypto !== \"undefined\" ? globalThis.crypto : undefined;\n if (!cryptoApi || typeof cryptoApi.getRandomValues !== \"function\") {\n throw new Error(\"[DVAI/pairing] no secure random source\");\n }\n const bytes = new Uint8Array(16);\n cryptoApi.getRandomValues(bytes);\n return encodeBase64Url(bytes);\n}\n\n/**\n * HMAC-SHA256(key, message). Used to sign offload requests so the\n * peer can verify they came from a paired device.\n */\nexport async function signHmac(\n pairingKey: string,\n message: string,\n): Promise<string> {\n const cryptoApi = globalThis.crypto;\n if (!cryptoApi?.subtle) {\n throw new Error(\"[DVAI/pairing] WebCrypto subtle not available\");\n }\n const keyBytes = decodeBase64UrlBytes(pairingKey);\n const cryptoKey = await cryptoApi.subtle.importKey(\n \"raw\",\n keyBytes as unknown as ArrayBuffer,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const sig = await cryptoApi.subtle.sign(\n \"HMAC\",\n cryptoKey,\n new TextEncoder().encode(message) as unknown as ArrayBuffer,\n );\n return encodeBase64Url(new Uint8Array(sig));\n}\n\n/** Verify an HMAC. Returns true on match, false otherwise (constant-time-ish). */\nexport async function verifyHmac(\n pairingKey: string,\n message: string,\n signature: string,\n): Promise<boolean> {\n const expected = await signHmac(pairingKey, message);\n return constantTimeEquals(expected, signature);\n}\n\nfunction constantTimeEquals(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\nfunction decodeBase64UrlBytes(s: string): Uint8Array {\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const binary =\n typeof atob !== \"undefined\"\n ? atob(padded)\n : Buffer.from(padded, \"base64\").toString(\"binary\");\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n/**\n * Compose the canonical message that gets HMAC-signed for a peer-to-peer\n * offload request. The peer recomputes the same string and verifies.\n *\n * Format: `${nonce}\\n${method}\\n${path}\\n${bodyHash}` — bodyHash is the\n * hex-encoded SHA-256 of the request body bytes.\n */\nexport async function composeSignedMessage(\n nonce: string,\n method: string,\n path: string,\n body: string | undefined,\n): Promise<string> {\n const bodyHash = body\n ? await sha256Hex(body)\n : \"0000000000000000000000000000000000000000000000000000000000000000\";\n return `${nonce}\\n${method.toUpperCase()}\\n${path}\\n${bodyHash}`;\n}\n\nasync function sha256Hex(input: string): Promise<string> {\n const cryptoApi = globalThis.crypto;\n if (!cryptoApi?.subtle) {\n throw new Error(\"[DVAI/pairing] WebCrypto subtle not available\");\n }\n const buf = await cryptoApi.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(input) as unknown as ArrayBuffer,\n );\n return Array.from(new Uint8Array(buf))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nexport type { HandshakeRequest, HandshakeResponse };\n","/**\n * Coarse static fallback for the capability score before any cold-run\n * probe has run. The numbers below are deliberately conservative —\n * we'd rather under-promise local capability and offload more often\n * than over-promise and serve a slow stream.\n *\n * Refinement: the first real probe (capability/probe.ts) replaces\n * the heuristic with a measured value cached for that (model, version).\n */\n\nimport type { DeviceCapabilityHints } from \"./types.js\";\n\n/**\n * Returns an estimated tok/s for the given hints. Pick the lowest\n * factor across the four dimensions — the bottleneck is what\n * actually limits inference speed.\n */\nexport function heuristicTokPerSec(hints: DeviceCapabilityHints): number {\n // Base score by GPU class. These numbers are based on observed\n // floors for common 1-3B GGUF q4 models in the wild.\n const gpuBase: Record<DeviceCapabilityHints[\"gpuClass\"], number> = {\n \"none\": 3, // CPU-only — very slow\n \"integrated\": 8, // basic iGPU\n \"discrete\": 35, // mid-range discrete (RTX 4060-class)\n \"apple-silicon\": 40, // M-series unified memory + Metal\n };\n\n // CPU class multiplier — affects prompt-processing more than\n // decode but still part of the picture.\n const cpuMul: Record<DeviceCapabilityHints[\"cpuClass\"], number> = {\n \"low\": 0.6,\n \"mid\": 1.0,\n \"high\": 1.3,\n };\n\n // RAM penalty — under 4 GB you're probably swapping; under 8 GB\n // you can't run a 3B q4 model comfortably.\n let ramMul: number;\n if (hints.ramGb < 4) ramMul = 0.3;\n else if (hints.ramGb < 8) ramMul = 0.7;\n else ramMul = 1.0;\n\n // NPU bonus — only meaningful when the *backend* uses it (Foundation\n // Models on iOS, MediaPipe with QNN on Android). Conservative bonus\n // because not every backend benefits.\n const npuBonus = hints.hasNpu ? 1.4 : 1.0;\n\n const estimate = gpuBase[hints.gpuClass] * cpuMul[hints.cpuClass] * ramMul * npuBonus;\n // Round to one decimal to avoid spurious-precision noise.\n return Math.round(estimate * 10) / 10;\n}\n\n/**\n * Detect device hints from the current runtime. JS-side runs in\n * Node / browser; native SDKs supply richer hints via their own\n * platform-bridges.\n */\nexport function detectDeviceHints(): DeviceCapabilityHints {\n // Browser: rough guess from navigator (deviceMemory is in GB,\n // hardwareConcurrency is logical-core count). WebGPU presence is\n // a \"discrete-ish\" hint.\n if (typeof navigator !== \"undefined\") {\n const nav = navigator as Navigator & { deviceMemory?: number };\n const ramGb = nav.deviceMemory ?? 4;\n const cores = nav.hardwareConcurrency ?? 4;\n const hasGpu = typeof (nav as Navigator & { gpu?: unknown }).gpu !== \"undefined\";\n\n return {\n hasNpu: false, // browsers can't introspect NPU presence today\n ramGb,\n gpuClass: hasGpu ? \"discrete\" : \"none\",\n cpuClass: cores >= 8 ? \"high\" : cores >= 4 ? \"mid\" : \"low\",\n };\n }\n\n // Node / Electron-main: query os.totalmem + os.cpus().\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n // Dynamic import to avoid pulling node:os into browser bundles.\n // We can't await at module top level here, so return a\n // pessimistic default and let detectDeviceHintsAsync() refine.\n }\n\n // Default conservative bucket.\n return {\n hasNpu: false,\n ramGb: 4,\n gpuClass: \"integrated\",\n cpuClass: \"mid\",\n };\n}\n\n/**\n * Async variant — uses node:os when available. Prefer this in Node\n * contexts; the sync version is for browser fallback.\n */\nexport async function detectDeviceHintsAsync(): Promise<DeviceCapabilityHints> {\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n try {\n const os = await import(\"node:os\");\n const totalMem = os.totalmem();\n const ramGb = Math.round(totalMem / (1024 ** 3));\n const cores = os.cpus().length;\n // Node can't introspect GPU class portably; default to\n // \"integrated\" unless the consumer overrides via config.\n return {\n hasNpu: false,\n ramGb,\n gpuClass: \"integrated\",\n cpuClass: cores >= 12 ? \"high\" : cores >= 6 ? \"mid\" : \"low\",\n };\n } catch {\n // node:os not available; fall through.\n }\n }\n return detectDeviceHints();\n}\n","/**\n * v3.2 — pre-init capability gate.\n *\n * Runs BEFORE any model download or backend init. Decides whether the\n * device should:\n *\n * 1. Refuse to run inference at all (hardware too weak).\n * Triggers a host-supplied `onHardwareTooWeak` callback so the\n * consumer can show a system popup, then throws so initialize()\n * aborts cleanly.\n *\n * 2. Run in offload-only mode (capable enough to bridge but not to\n * run the model itself comfortably). The SDK skips the model\n * download/load entirely and only brings up the proxy + discovery\n * + pairing layer. Every request gets forwarded to a paired peer;\n * 503 + Retry-After if no peer is available.\n *\n * 3. Run normally (load the model locally + run discovery/pairing\n * so this device can also serve other peers if it's strong).\n *\n * The decision uses ONLY the heuristic (CPU/GPU/RAM hints) — no model\n * is needed at this stage. Refinement via a real probe happens later,\n * after the model has been loaded and a request has actually completed.\n */\n\nimport {\n detectDeviceHintsAsync,\n heuristicTokPerSec,\n} from \"./heuristic.js\";\nimport type { DeviceCapabilityHints } from \"./types.js\";\n\n/** Result of a pre-init capability assessment. */\nexport type PrecheckResult =\n | { mode: \"ok\"; tokPerSec: number; hints: DeviceCapabilityHints; reason: string }\n | { mode: \"offload-only\"; tokPerSec: number; hints: DeviceCapabilityHints; reason: string }\n | { mode: \"too-weak\"; tokPerSec: number; hints: DeviceCapabilityHints; reason: string };\n\nexport interface PrecheckOptions {\n /**\n * Hard floor for any local inference, in tok/s. Below this, the\n * device is too weak to be useful at all and the precheck returns\n * `mode: \"too-weak\"`. Default: 3.\n *\n * Apps that want to allow even slower inference (e.g. for\n * long-prompt summarization where latency is acceptable) can pass\n * a smaller value. Apps targeting interactive chat should leave\n * the default — anything below ~3 tok/s feels broken.\n */\n hardwareMinimum?: number;\n\n /**\n * Below this tok/s but above `hardwareMinimum`, run in offload-only\n * mode (skip model load; route every request to a paired peer).\n * Above this, load locally. Default: pulled from\n * `OffloadConfig.minLocalCapability` if not set.\n */\n minLocalCapability?: number;\n\n /**\n * Pre-detected hints. Tests pass synthetic values here. In\n * production, leave undefined and let the precheck call\n * `detectDeviceHintsAsync()` itself.\n */\n hints?: DeviceCapabilityHints;\n}\n\nconst DEFAULT_HARDWARE_MINIMUM = 3;\nconst DEFAULT_MIN_LOCAL_CAPABILITY = 10;\n\nexport async function assessCapability(\n opts: PrecheckOptions = {},\n): Promise<PrecheckResult> {\n const hardwareMinimum = opts.hardwareMinimum ?? DEFAULT_HARDWARE_MINIMUM;\n const minLocalCapability = opts.minLocalCapability ?? DEFAULT_MIN_LOCAL_CAPABILITY;\n\n if (hardwareMinimum > minLocalCapability) {\n // Allowed but odd — the offload-only band collapses to zero.\n // Useful for \"load locally if you can or refuse\" deployments.\n }\n\n const hints = opts.hints ?? (await detectDeviceHintsAsync());\n const tokPerSec = heuristicTokPerSec(hints);\n\n if (tokPerSec < hardwareMinimum) {\n return {\n mode: \"too-weak\",\n tokPerSec,\n hints,\n reason:\n `estimated ${tokPerSec} tok/s, below the ${hardwareMinimum} tok/s ` +\n `hardware floor — local inference would be unusable, no peer to ` +\n `offload to either (a peer-only mode still requires the host to ` +\n `bring up discovery + pairing).`,\n };\n }\n\n if (tokPerSec < minLocalCapability) {\n return {\n mode: \"offload-only\",\n tokPerSec,\n hints,\n reason:\n `estimated ${tokPerSec} tok/s, below the ${minLocalCapability} tok/s ` +\n `comfort threshold — model will not be loaded locally; every ` +\n `request will be forwarded to a paired peer.`,\n };\n }\n\n return {\n mode: \"ok\",\n tokPerSec,\n hints,\n reason:\n `estimated ${tokPerSec} tok/s, above the ${minLocalCapability} tok/s ` +\n `local-capability threshold — running normally.`,\n };\n}\n\n/**\n * @deprecated v3.2 — kept only to avoid breaking type imports. The\n * SDK no longer throws on too-weak hardware; consumers call\n * `dvai.assessHardware()` and decide their own UI. start() in a\n * too-weak case enters offload-only mode silently (no model\n * download/load).\n *\n * Will be removed in v4.0.\n */\nexport class HardwareTooWeakError extends Error {\n readonly tokPerSec: number;\n readonly hardwareMinimum: number;\n readonly hints: DeviceCapabilityHints;\n\n constructor(opts: {\n tokPerSec: number;\n hardwareMinimum: number;\n hints: DeviceCapabilityHints;\n reason: string;\n }) {\n super(`DVAI: ${opts.reason}`);\n this.name = \"HardwareTooWeakError\";\n this.tokPerSec = opts.tokPerSec;\n this.hardwareMinimum = opts.hardwareMinimum;\n this.hints = opts.hints;\n }\n}\n","import type { HandlerContext } from \"./context\";\n\nconst SSE_HEADERS = {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n};\n\nexport async function handleChatCompletion(\n body: any,\n ctx: HandlerContext,\n headers?: Record<string, string>,\n): Promise<Response> {\n // Phase 4 — first-chance interceptor (used by the Hub to enforce\n // substitution policy + route through external engines). If it\n // returns a Response, we're done; null falls through to the local\n // backend path below.\n if (ctx.chatCompletionInterceptor) {\n try {\n const intercepted = await ctx.chatCompletionInterceptor(body, ctx, headers);\n if (intercepted !== null) return intercepted;\n } catch (err: any) {\n return Response.json(\n { error: err?.message ?? \"interceptor failed\" },\n { status: 500 },\n );\n }\n }\n\n if (!ctx.backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n\n // Always read ctx.backend inside runOnce so that if onRecovery replaces\n // the underlying backend instance, the retry hits the new one.\n const runOnce = async (): Promise<Response> => {\n const backend = ctx.backend;\n if (!backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n if (body.stream) {\n const stream = backend.createStreamingResponse(body);\n return new Response(stream, { headers: SSE_HEADERS });\n }\n const response = await backend.chatCompletion(body);\n return Response.json(response);\n };\n\n try {\n // Proactive recovery: if the backend is flagged with a prior fatal error,\n // ask DVAI to recover before the attempt.\n if (ctx.backend.lastFatalError && ctx.onRecovery) {\n await ctx.onRecovery();\n }\n return await runOnce();\n } catch (error: any) {\n // Reactive recovery: if the backend flags a fatal error during the attempt,\n // recover and retry once. DVAI's onRecovery throws when exhausted, which\n // falls through to the 500 response below.\n if (ctx.backend?.lastFatalError && ctx.onRecovery) {\n try {\n await ctx.onRecovery();\n return await runOnce();\n } catch {\n /* fall through to 500 */\n }\n }\n return Response.json({ error: error.message }, { status: 500 });\n }\n}\n","import type { HandlerContext } from \"./context\";\n\nexport async function handleEmbeddings(\n body: any,\n ctx: HandlerContext,\n): Promise<Response> {\n if (!ctx.backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n if (ctx.resolvedBackend === \"webllm\") {\n return Response.json(\n {\n error:\n \"Embeddings are not supported on the WebLLM backend. \" +\n \"Use backend: 'transformers' with pipelineTask: 'feature-extraction'.\",\n },\n { status: 400 },\n );\n }\n if (typeof ctx.backend.embedding !== \"function\") {\n return Response.json(\n {\n error:\n \"The current backend does not support embeddings. \" +\n \"For transformers: use pipelineTask: 'feature-extraction'.\",\n },\n { status: 400 },\n );\n }\n\n const input = body?.input;\n if (input === undefined || input === null) {\n return Response.json(\n { error: \"Missing 'input' field.\" },\n { status: 400 },\n );\n }\n\n try {\n const vectors: number[][] = await ctx.backend.embedding(input);\n return Response.json({\n object: \"list\",\n data: vectors.map((v, i) => ({\n object: \"embedding\",\n embedding: v,\n index: i,\n })),\n model: body.model || ctx.modelId,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n });\n } catch (error: any) {\n return Response.json({ error: error.message }, { status: 500 });\n }\n}\n","import type { HandlerContext } from \"./context\";\n\nexport async function handleModels(ctx: HandlerContext): Promise<Response> {\n return Response.json({\n object: \"list\",\n data: [\n {\n id: ctx.modelId,\n object: \"model\",\n created: Math.floor(Date.now() / 1000),\n owned_by: \"dvai-bridge\",\n },\n ],\n });\n}\n","export type { BackendInterface, HandlerContext } from \"./context.js\";\nexport { handleChatCompletion } from \"./chat.js\";\nexport {\n handleCompletion,\n chatToLegacyCompletion,\n legacyCompletionStreamAdapter,\n} from \"./completions.js\";\nexport { handleEmbeddings } from \"./embeddings.js\";\nexport { handleModels } from \"./models.js\";\n","import { setupWorker, type SetupWorker } from \"msw/browser\";\nimport { http } from \"msw\";\nimport type { HandlerContext } from \"../handlers/context.js\";\nimport {\n handleChatCompletion,\n handleCompletion,\n handleEmbeddings,\n handleModels,\n} from \"../handlers/index.js\";\nimport type {\n MswTransportOptions,\n Transport,\n TransportStartResult,\n} from \"./types.js\";\n\nfunction getEndpoints(mockUrl: string): {\n chat: string;\n completions: string;\n embeddings: string;\n models: string;\n base: string;\n} {\n const chat = mockUrl;\n let base = chat;\n const chatSuffix = \"/chat/completions\";\n if (chat.endsWith(chatSuffix)) {\n base = chat.slice(0, -chatSuffix.length);\n } else {\n try {\n const u = new URL(chat);\n const parts = u.pathname.split(\"/\").filter(Boolean);\n parts.pop();\n u.pathname = \"/\" + parts.join(\"/\");\n base = u.toString().replace(/\\/$/, \"\");\n } catch {\n /* keep base = chat */\n }\n }\n return {\n chat,\n completions: `${base}/completions`,\n embeddings: `${base}/embeddings`,\n models: `${base}/models`,\n base,\n };\n}\n\nexport class MswTransport implements Transport {\n readonly kind = \"msw\" as const;\n private worker: SetupWorker | null = null;\n\n constructor(private readonly opts: MswTransportOptions) {}\n\n async start(ctx: HandlerContext): Promise<TransportStartResult> {\n const urls = getEndpoints(this.opts.mockUrl);\n\n // Empty serviceWorkerUrl means \"don't register\" — preserves the\n // direct-inference escape hatch while still reporting a baseUrl.\n if (this.opts.serviceWorkerUrl) {\n const handlers = [\n http.post(urls.chat, async ({ request }) =>\n handleChatCompletion(await request.json(), ctx),\n ),\n http.post(urls.completions, async ({ request }) =>\n handleCompletion(await request.json(), ctx),\n ),\n http.post(urls.embeddings, async ({ request }) =>\n handleEmbeddings(await request.json(), ctx),\n ),\n http.get(urls.models, async () => handleModels(ctx)),\n ];\n this.worker = setupWorker(...handlers);\n await this.worker.start({\n onUnhandledRequest: \"bypass\",\n serviceWorker: { url: this.opts.serviceWorkerUrl },\n } as any);\n }\n\n return { baseUrl: urls.base };\n }\n\n async stop(): Promise<void> {\n if (this.worker) {\n this.worker.stop();\n this.worker = null;\n }\n }\n}\n","import type { Server } from \"node:http\";\n\n/** DVAI-reserved base port. Deliberately high to avoid clashes with Ollama/Postgres/etc. */\nexport const BASE_PORT = 38883;\n\n/** Maximum port-fallback attempts before giving up. */\nexport const MAX_PORT_ATTEMPTS = 16;\n\n/**\n * Attempt to bind `server` to `basePort`, falling back to basePort+1,\n * basePort+2, ... on EADDRINUSE up to `maxAttempts` times.\n *\n * Default host is `127.0.0.1` (loopback-only) — safe default for any\n * single-device DVAI deployment. v3.0 LAN-target deployments (the\n * v3.1 Hub, native SDKs running in target mode) override to `0.0.0.0`\n * so peers on the same Wi-Fi can reach the server.\n *\n * Throws a loud, actionable error listing the tried range if all are in use.\n * Re-throws non-EADDRINUSE errors immediately (e.g. EACCES on privileged ports).\n *\n * @returns the port that was successfully bound\n */\nexport async function tryBind(\n server: Server,\n basePort: number = BASE_PORT,\n maxAttempts: number = MAX_PORT_ATTEMPTS,\n host: string = \"127.0.0.1\",\n): Promise<number> {\n for (let i = 0; i < maxAttempts; i++) {\n const port = basePort + i;\n try {\n await new Promise<void>((resolve, reject) => {\n const onError = (err: any) => {\n server.off(\"error\", onError);\n reject(err);\n };\n server.once(\"error\", onError);\n server.listen(port, host, () => {\n server.off(\"error\", onError);\n resolve();\n });\n });\n return port;\n } catch (err: any) {\n if (err.code !== \"EADDRINUSE\") throw err;\n }\n }\n throw new Error(\n `[DVAI] Could not bind HTTP transport to any port in range ` +\n `${basePort}..${basePort + maxAttempts - 1} (all in use). ` +\n `Another local AI server may already be running.`,\n );\n}\n","import type { HandlerContext } from \"../handlers/context.js\";\nimport {\n handleChatCompletion,\n handleCompletion,\n handleEmbeddings,\n handleModels,\n} from \"../handlers/index.js\";\nimport type { DvaiHandler } from \"../handlers/dvai/index.js\";\nimport type {\n HttpTransportOptions,\n Transport,\n TransportStartResult,\n} from \"./types.js\";\nimport { tryBind } from \"./port-fallback.js\";\n\ntype NodeReq = import(\"node:http\").IncomingMessage;\ntype NodeRes = import(\"node:http\").ServerResponse;\ntype NodeServer = import(\"node:http\").Server;\n\nfunction pickOrigin(origin: string | undefined, cfg: string | string[]): string | null {\n if (cfg === \"*\") return \"*\";\n if (typeof cfg === \"string\") return cfg;\n if (!origin) return null;\n return cfg.includes(origin) ? origin : null;\n}\n\nfunction corsHeaders(\n reqOrigin: string | undefined,\n cfg: string | string[],\n): Record<string, string> {\n const allow = pickOrigin(reqOrigin, cfg);\n const headers: Record<string, string> = {\n \"Access-Control-Allow-Methods\": \"POST, GET, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n \"Access-Control-Allow-Private-Network\": \"true\",\n };\n if (allow) headers[\"Access-Control-Allow-Origin\"] = allow;\n return headers;\n}\n\nasync function readJsonBody(req: NodeReq): Promise<any> {\n const chunks: Buffer[] = [];\n for await (const c of req) chunks.push(c as Buffer);\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (!raw) return {};\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(\"Invalid JSON body\");\n }\n}\n\nasync function writeWhatwgResponse(\n res: NodeRes,\n response: Response,\n extraHeaders: Record<string, string>,\n): Promise<void> {\n const headers: Record<string, string> = { ...extraHeaders };\n response.headers.forEach((v, k) => {\n headers[k] = v;\n });\n\n if (response.body) {\n res.writeHead(response.status, headers);\n const reader = response.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(Buffer.from(value));\n }\n } finally {\n res.end();\n }\n return;\n }\n const text = await response.text();\n res.writeHead(response.status, headers);\n res.end(text);\n}\n\nasync function route(\n req: NodeReq,\n res: NodeRes,\n ctx: HandlerContext,\n opts: HttpTransportOptions,\n): Promise<void> {\n const reqOrigin = req.headers.origin as string | undefined;\n const cors = corsHeaders(reqOrigin, opts.corsOrigin);\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204, cors);\n res.end();\n return;\n }\n\n const url = new URL(req.url || \"/\", \"http://127.0.0.1\");\n const path = url.pathname;\n\n try {\n if (req.method === \"POST\" && path === \"/v1/chat/completions\") {\n const body = await readJsonBody(req);\n const reqHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(req.headers)) {\n if (typeof v === \"string\") reqHeaders[k.toLowerCase()] = v;\n else if (Array.isArray(v) && v.length > 0) reqHeaders[k.toLowerCase()] = v.join(\", \");\n }\n const r = await handleChatCompletion(body, ctx, reqHeaders);\n return writeWhatwgResponse(res, r, cors);\n }\n if (req.method === \"POST\" && path === \"/v1/completions\") {\n const body = await readJsonBody(req);\n const r = await handleCompletion(body, ctx);\n return writeWhatwgResponse(res, r, cors);\n }\n if (req.method === \"POST\" && path === \"/v1/embeddings\") {\n const body = await readJsonBody(req);\n const r = await handleEmbeddings(body, ctx);\n return writeWhatwgResponse(res, r, cors);\n }\n if (req.method === \"GET\" && path === \"/v1/models\") {\n const r = await handleModels(ctx);\n return writeWhatwgResponse(res, r, cors);\n }\n // /v1/dvai/* — Phase 3 distributed-inference plane. Routes are\n // registered by DVAI when offload.enabled. Absent map → 404 (no\n // offload state) which matches the pre-Phase-3 behaviour.\n const dvaiRoutes = ctx.dvaiRoutes;\n if (dvaiRoutes) {\n const key = `${req.method ?? \"GET\"} ${path}`;\n const dvaiHandler = dvaiRoutes[key];\n if (dvaiHandler) {\n let body: unknown = undefined;\n if (req.method === \"POST\" || req.method === \"PUT\" || req.method === \"PATCH\") {\n body = await readJsonBody(req);\n }\n const r = await dvaiHandler({ body });\n const respHeaders: Record<string, string> = {\n ...cors,\n \"Content-Type\": \"application/json\",\n };\n res.writeHead(r.status, respHeaders);\n res.end(JSON.stringify(r.body));\n return;\n }\n }\n res.writeHead(404, { ...cors, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"not found\" }));\n } catch (err: any) {\n res.writeHead(500, { ...cors, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: err?.message ?? \"unknown error\" }));\n }\n}\n\nexport class HttpTransport implements Transport {\n readonly kind = \"http\" as const;\n private server: NodeServer | null = null;\n private boundPort: number | undefined;\n\n constructor(private readonly opts: HttpTransportOptions) {}\n\n async start(ctx: HandlerContext): Promise<TransportStartResult> {\n const { createServer } = await import(\"node:http\");\n const server = createServer((req, res) => {\n route(req, res, ctx, this.opts).catch((err) => {\n try {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (err as Error).message }));\n } catch {\n /* socket already closed */\n }\n });\n });\n const bindHost = this.opts.bindHost ?? \"127.0.0.1\";\n const port = await tryBind(\n server,\n this.opts.httpBasePort,\n this.opts.httpMaxPortAttempts,\n bindHost,\n );\n this.server = server;\n this.boundPort = port;\n // baseUrl reports the bind host so downstream consumers can choose\n // a sensible advertise URL. 0.0.0.0 isn't directly callable from\n // peers — they should use the host's actual LAN IP — but this URL\n // accurately reflects what the server is listening on.\n return { baseUrl: `http://${bindHost}:${port}/v1`, port };\n }\n\n async stop(): Promise<void> {\n if (this.server) {\n await new Promise<void>((r) => this.server!.close(() => r()));\n this.server = null;\n this.boundPort = undefined;\n }\n }\n}\n","import type { HandlerContext } from \"../handlers/context.js\";\nimport type { Transport, TransportStartResult } from \"./types.js\";\n\nexport interface CapacitorTransportOptions {\n capacitorBackend: \"llama\" | \"foundation\" | \"mediapipe\";\n nativeModelPath?: string;\n nativeMmprojPath?: string;\n nativeGpuLayers?: number;\n nativeContextSize?: number;\n nativeThreads?: number;\n nativeEmbeddingMode?: boolean;\n httpBasePort: number;\n httpMaxPortAttempts: number;\n corsOrigin: string | string[];\n autoUnloadOnLowMemory?: boolean;\n logLevel?: \"silent\" | \"info\" | \"debug\";\n}\n\nexport class CapacitorTransport implements Transport {\n readonly kind = \"capacitor\" as const;\n\n constructor(private readonly opts: CapacitorTransportOptions) {}\n\n async start(_ctx: HandlerContext): Promise<TransportStartResult> {\n const { DVAIBridge } = await import(\"@dvai-bridge/capacitor\");\n const result = await DVAIBridge.start({\n backend: this.opts.capacitorBackend,\n modelPath: this.opts.nativeModelPath,\n mmprojPath: this.opts.nativeMmprojPath,\n gpuLayers: this.opts.nativeGpuLayers,\n contextSize: this.opts.nativeContextSize,\n threads: this.opts.nativeThreads,\n embeddingMode: this.opts.nativeEmbeddingMode,\n httpBasePort: this.opts.httpBasePort,\n httpMaxPortAttempts: this.opts.httpMaxPortAttempts,\n corsOrigin: this.opts.corsOrigin,\n autoUnloadOnLowMemory: this.opts.autoUnloadOnLowMemory,\n logLevel: this.opts.logLevel,\n });\n return { baseUrl: result.baseUrl, port: result.port };\n }\n\n async stop(): Promise<void> {\n const { DVAIBridge } = await import(\"@dvai-bridge/capacitor\");\n await DVAIBridge.stop();\n }\n}\n","export type {\n Transport,\n TransportKind,\n TransportStartResult,\n HttpTransportOptions,\n MswTransportOptions,\n} from \"./types.js\";\nexport { MswTransport } from \"./msw.js\";\nexport { HttpTransport } from \"./http.js\";\nexport { CapacitorTransport } from \"./capacitor.js\";\nexport { BASE_PORT, MAX_PORT_ATTEMPTS, tryBind } from \"./port-fallback.js\";\n\nexport interface SelectTransportInput {\n /** Raw config: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\", or undefined. */\n transport?: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\";\n /** Back-compat signal: \"\" disables transport when transport is not explicit. */\n serviceWorkerUrl?: string;\n}\n\n/** Resolve \"auto\" based on the runtime environment. */\nexport function selectTransport(\n input: SelectTransportInput,\n): \"msw\" | \"http\" | \"none\" | \"capacitor\" {\n // Back-compat escape hatch: empty serviceWorkerUrl with no explicit transport → none\n if (input.serviceWorkerUrl === \"\" && input.transport == null) return \"none\";\n const requested = input.transport ?? \"auto\";\n if (requested !== \"auto\") return requested;\n if (isCapacitorContext()) return \"capacitor\";\n if (isBrowserLike()) return \"msw\";\n if (isNode()) return \"http\";\n return \"none\";\n}\n\nfunction isCapacitorContext(): boolean {\n return (\n typeof window !== \"undefined\" &&\n !!(window as any).Capacitor?.isNativePlatform?.()\n );\n}\n\nfunction isBrowserLike(): boolean {\n return (\n typeof window !== \"undefined\" &&\n typeof document !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n typeof (navigator as any).serviceWorker !== \"undefined\"\n );\n}\n\nfunction isNode(): boolean {\n return (\n typeof process !== \"undefined\" &&\n process.versions != null &&\n process.versions.node != null\n );\n}\n","/**\n * Pure offload decision function. Tested in isolation; called by the\n * proxy on every chat-completion request.\n */\n\nimport type { Peer } from \"../discovery/types.js\";\nimport type { Decision, OffloadConfig, OffloadHeader, PeerCheckResult } from \"./types.js\";\n\nexport interface DecideInput {\n config: OffloadConfig;\n modelId: string;\n /** This device's capability for `modelId`. */\n localCapability: number;\n /** All known peers. */\n peers: Peer[];\n /** Per-request override (X-DVAI-Offload header). */\n header: OffloadHeader;\n}\n\nexport function decide(input: DecideInput): Decision {\n if (!input.config.enabled) return { kind: \"local\" };\n if (input.header === \"never\") return { kind: \"local\" };\n\n // Filter to peers that have this model loaded AND report a usable score.\n // LAN peers preferred over rendezvous peers when scores are comparable\n // (lower latency, no relay overhead) — sort key: (modelHasLoaded desc,\n // score desc, LAN-first).\n const eligible = input.peers\n .map((p): { peer: Peer; score: number; hasModel: boolean } => ({\n peer: p,\n score: p.capability[input.modelId] ?? 0,\n hasModel: p.loadedModels.includes(input.modelId),\n }))\n .filter((x) => x.score > 0)\n .sort((a, b) => {\n if (a.hasModel !== b.hasModel) return a.hasModel ? -1 : 1;\n if (a.score !== b.score) return b.score - a.score;\n // Same score, same load state: prefer mDNS over rendezvous over static.\n const order: Record<Peer[\"via\"], number> = { mdns: 0, static: 1, custom: 2, rendezvous: 3 };\n return order[a.peer.via] - order[b.peer.via];\n });\n\n const bestPeer = eligible[0]?.peer;\n const bestScore = eligible[0]?.score ?? 0;\n const threshold = input.config.minLocalCapability;\n\n if (input.header === \"require\") {\n if (bestPeer && bestScore >= threshold) {\n return { kind: \"offload\", peer: bestPeer };\n }\n return {\n kind: \"no_capable_device\",\n checked: buildCheckedList(input),\n localCapability: input.localCapability,\n required: threshold,\n };\n }\n\n // Default: \"prefer\".\n if (input.localCapability >= threshold) {\n return { kind: \"local\" };\n }\n if (bestPeer && bestScore > input.localCapability) {\n return { kind: \"offload\", peer: bestPeer };\n }\n // Local is below threshold but no better peer.\n if (input.localCapability > 0) {\n return { kind: \"local\" }; // best we have\n }\n return {\n kind: \"no_capable_device\",\n checked: buildCheckedList(input),\n localCapability: input.localCapability,\n required: threshold,\n };\n}\n\nfunction buildCheckedList(input: DecideInput): PeerCheckResult[] {\n const list: PeerCheckResult[] = [\n {\n deviceId: \"self\",\n capabilityScore: input.localCapability,\n reason:\n input.localCapability < input.config.minLocalCapability\n ? \"below threshold\"\n : \"no eligible peer found\",\n },\n ];\n for (const peer of input.peers) {\n const score = peer.capability[input.modelId] ?? 0;\n list.push({\n deviceId: peer.deviceId,\n deviceName: peer.deviceName,\n capabilityScore: score,\n reason: score === 0 ? \"model not advertised\" : score < input.config.minLocalCapability ? \"below threshold\" : \"checked\",\n });\n }\n return list;\n}\n","/**\n * Constructs the structured `no_capable_device` HTTP error response.\n * Returned with HTTP 503 + Retry-After: 30. Body is OpenAI-error-shaped\n * so existing OpenAI clients surface it naturally.\n */\n\nimport type { Decision } from \"./types.js\";\nimport type { NoCapableDeviceErrorBody } from \"./types.js\";\n\nexport function buildNoCapableDeviceResponse(\n decision: Extract<Decision, { kind: \"no_capable_device\" }>,\n ctx: { rendezvousConfigured: boolean; pairedRemotePeers: number; requestId?: string; modelId: string },\n): { status: number; headers: Record<string, string>; body: NoCapableDeviceErrorBody } {\n const message =\n `No device with capability ≥ ${decision.required} tok/s for model ` +\n `${ctx.modelId} was reachable. Local: ${decision.localCapability} tok/s; ` +\n `${decision.checked.length - 1} peer(s) checked.`;\n\n return {\n status: 503,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Retry-After\": \"30\",\n },\n body: {\n error: {\n type: \"no_capable_device\",\n code: 503,\n message,\n checked: decision.checked,\n localCapability: decision.localCapability,\n requiredAtLeast: decision.required,\n rendezvousConfigured: ctx.rendezvousConfigured,\n pairedRemotePeers: ctx.pairedRemotePeers,\n requestId: ctx.requestId,\n },\n },\n };\n}\n","/**\n * Per-request offload policy parsing. Reads X-DVAI-Offload header,\n * defaults to \"prefer\".\n */\n\nimport type { OffloadHeader } from \"./types.js\";\n\nconst VALID: ReadonlySet<OffloadHeader> = new Set([\"never\", \"prefer\", \"require\"]);\n\nexport function parseOffloadHeader(headers: Headers | Record<string, string | undefined>): OffloadHeader {\n let raw: string | null | undefined;\n if (headers instanceof Headers) {\n raw = headers.get(\"x-dvai-offload\");\n } else {\n // Case-insensitive lookup for the plain-object case.\n for (const [k, v] of Object.entries(headers)) {\n if (k.toLowerCase() === \"x-dvai-offload\") {\n raw = v;\n break;\n }\n }\n }\n if (!raw) return \"prefer\";\n const lc = raw.toLowerCase().trim() as OffloadHeader;\n return VALID.has(lc) ? lc : \"prefer\";\n}\n","/**\n * Offload proxy. Given a Decision.offload, forward the OpenAI HTTP\n * request to the peer's baseUrl and stream the response back to the\n * caller.\n *\n * For LAN peers: direct HTTP. For rendezvous-paired peers: the same\n * HTTP shape but tunneled through the rendezvous WebSocket relay.\n * (Rendezvous tunneling is a v3.0+ extension; this initial impl\n * supports the LAN HTTP path; rendezvous support is wired in once\n * Task 5+6 land the handshake + endpoint glue.)\n */\n\nimport type { Peer } from \"../discovery/types.js\";\n\nexport interface ProxyRequest {\n method: \"POST\" | \"GET\";\n path: string; // e.g. \"/chat/completions\"\n body?: unknown;\n headers?: Record<string, string>;\n /** Whether to expect SSE-style streaming response. */\n stream: boolean;\n /** Caller's AbortSignal — propagated to the upstream fetch. */\n signal?: AbortSignal;\n}\n\nexport interface ProxyResponse {\n status: number;\n headers: Record<string, string>;\n /** For non-streaming: the parsed body. */\n body?: unknown;\n /** For streaming: a ReadableStream of SSE-format chunks. */\n stream?: ReadableStream<Uint8Array>;\n}\n\n/**\n * Proxy a single request to a peer. The peer's baseUrl is the OpenAI\n * v1 root (e.g. http://192.168.1.10:38883/v1).\n */\nexport async function proxyToPeer(\n peer: Peer,\n req: ProxyRequest,\n): Promise<ProxyResponse> {\n if (peer.via === \"rendezvous\") {\n // TODO(v3.0): wire up the WebSocket relay path. For now, fall\n // through to direct HTTP if the peer also has a baseUrl (which\n // it shouldn't for pure-rendezvous peers; left as a guard).\n if (!peer.baseUrl || !peer.baseUrl.startsWith(\"http\")) {\n throw new Error(\n \"[DVAI/offload] rendezvous-paired peers require WebSocket relay \" +\n \"(not yet implemented in v3.0.0-rc1; expected in v3.0.0 final).\",\n );\n }\n }\n\n const url = `${peer.baseUrl.replace(/\\/$/, \"\")}${req.path}`;\n const upstreamHeaders: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-DVAI-Forwarded\": \"1\",\n ...(req.headers ?? {}),\n };\n\n const response = await fetch(url, {\n method: req.method,\n headers: upstreamHeaders,\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\n signal: req.signal,\n });\n\n const headers: Record<string, string> = {};\n response.headers.forEach((v, k) => {\n headers[k] = v;\n });\n\n if (req.stream && response.body) {\n return {\n status: response.status,\n headers,\n stream: response.body,\n };\n }\n\n return {\n status: response.status,\n headers,\n body: await response.json().catch(() => undefined),\n };\n}\n","/**\n * v3.2 — auto-wired offload forwarder.\n *\n * Returns a `chatCompletionInterceptor` (the same shape that the Hub\n * uses for its substitution-policy hook) which:\n *\n * 1. On every /v1/chat/completions request, runs `decide()` to pick\n * a route.\n * 2. If decide → \"offload\": calls `proxyToPeer` and returns a\n * Response with the peer's reply.\n * 3. If decide → \"no_capable_device\": returns a structured 503\n * using `buildNoCapableDeviceResponse()`.\n * 4. If decide → \"local\": returns null. The default handler runs:\n * - When the local backend exists, it serves the request.\n * - When the SDK is in offload-only mode (no backend), the\n * default handler returns 503 itself (\"not initialized\").\n * That second 503 indicates a peer was claimed available\n * but isn't usable for this model — operationally rare.\n *\n * The forwarder reads dynamic state (current peers, current\n * capability score) every call so changes to either don't require\n * re-wiring. Pass dynamic getters in `opts`.\n */\n\nimport { decide } from \"./decide.js\";\nimport { buildNoCapableDeviceResponse } from \"./error.js\";\nimport { proxyToPeer } from \"./proxy.js\";\nimport { parseOffloadHeader } from \"./policy.js\";\nimport type { OffloadConfig, OffloadHeader } from \"./types.js\";\nimport type { Peer } from \"../discovery/types.js\";\nimport type { HandlerContext } from \"../handlers/context.js\";\n\nexport interface ForwarderOptions {\n /** Static OffloadConfig (the one supplied to the SDK). */\n config: OffloadConfig;\n /** Live snapshot of discovered peers. Re-read on each request. */\n getPeers: () => Peer[];\n /** Live capability score for `modelId` on this device. */\n getLocalCapability: (modelId: string) => number;\n /** When true, every \"local\" decision is forced to a 503 because\n * no local backend exists to serve it (offload-only mode). */\n offloadOnlyMode: boolean;\n}\n\ntype Interceptor = NonNullable<HandlerContext[\"chatCompletionInterceptor\"]>;\n\nexport function buildOffloadInterceptor(opts: ForwarderOptions): Interceptor {\n return async function offloadInterceptor(\n body: any,\n ctx: HandlerContext,\n headers?: Record<string, string>,\n ): Promise<Response | null> {\n if (!opts.config.enabled) return null;\n\n // Per-request override (X-DVAI-Offload header). Defaults to \"prefer\".\n const headerValue = readOffloadHeader(headers);\n\n const peers = opts.getPeers();\n const modelId = ctx.modelId;\n const localCapability = opts.getLocalCapability(modelId);\n\n const decision = decide({\n config: opts.config,\n modelId,\n localCapability,\n peers,\n header: headerValue,\n });\n\n if (decision.kind === \"local\") {\n // In offload-only mode the default handler will 503 because\n // ctx.backend is null. We could short-circuit here with a\n // friendlier message, but returning null preserves the\n // backend-not-initialized error path the existing code already\n // handles.\n if (opts.offloadOnlyMode) {\n // Offload-only + decide-said-local: typically means\n // header=never (caller insisted on local). Surface that\n // explicitly so the caller knows their override was the cause.\n return jsonResponse(503, {\n error: {\n type: \"no_local_backend\",\n code: 503,\n message:\n \"DVAI is running in offload-only mode (device below \" +\n \"minLocalCapability) and the request requested local \" +\n \"execution (X-DVAI-Offload: never). Either drop the \" +\n \"header or route to a peer.\",\n },\n });\n }\n return null;\n }\n\n if (decision.kind === \"offload\") {\n // Forward to the peer. Streaming is detected from the request body.\n const stream = body && typeof body === \"object\" && body.stream === true;\n try {\n const proxied = await proxyToPeer(decision.peer, {\n method: \"POST\",\n path: \"/chat/completions\",\n body,\n stream,\n headers: forwardableHeaders(headers),\n });\n\n // Best-effort callback (consumer-supplied diagnostics).\n try {\n opts.config.onOffload?.(decision.peer);\n } catch {\n // host-provided callback errors must not break the response\n }\n\n if (proxied.stream) {\n return new Response(proxied.stream, {\n status: proxied.status,\n headers: proxied.headers,\n });\n }\n return new Response(JSON.stringify(proxied.body), {\n status: proxied.status,\n headers: proxied.headers,\n });\n } catch (err) {\n // Proxy failed (peer unreachable, network drop, etc.). Surface\n // a 502 — the upstream peer was supposed to handle it but\n // didn't. Caller can retry; another peer might still be available.\n return jsonResponse(502, {\n error: {\n type: \"peer_unreachable\",\n code: 502,\n message: `Offload to peer ${decision.peer.deviceId} failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n peerId: decision.peer.deviceId,\n },\n });\n }\n }\n\n // decision.kind === \"no_capable_device\"\n const errResponse = buildNoCapableDeviceResponse(decision, {\n rendezvousConfigured: !!opts.config.rendezvousUrl,\n pairedRemotePeers: peers.filter((p) => p.via === \"rendezvous\").length,\n modelId,\n });\n return new Response(JSON.stringify(errResponse.body), {\n status: errResponse.status,\n headers: errResponse.headers,\n });\n };\n}\n\nfunction readOffloadHeader(\n headers?: Record<string, string>,\n): OffloadHeader {\n if (!headers) return \"prefer\";\n return parseOffloadHeader(headers as Record<string, string | undefined>);\n}\n\n/** Strip hop-by-hop / problematic headers; keep auth-relevant ones. */\nfunction forwardableHeaders(\n headers?: Record<string, string>,\n): Record<string, string> {\n if (!headers) return {};\n const out: Record<string, string> = {};\n const drop = new Set([\n \"host\",\n \"content-length\",\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n ]);\n for (const [k, v] of Object.entries(headers)) {\n if (drop.has(k.toLowerCase())) continue;\n out[k] = v;\n }\n return out;\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n","export type {\n Decision,\n OffloadConfig,\n OffloadHeader,\n PeerCheckResult,\n NoCapableDeviceErrorBody,\n} from \"./types.js\";\nexport { decide } from \"./decide.js\";\nexport { buildNoCapableDeviceResponse } from \"./error.js\";\nexport { parseOffloadHeader } from \"./policy.js\";\nexport { proxyToPeer, type ProxyRequest, type ProxyResponse } from \"./proxy.js\";\nexport {\n buildOffloadInterceptor,\n type ForwarderOptions,\n} from \"./forwarder.js\";\n","/**\n * Persistent storage of capability scores + the device identifier.\n * Concrete adapters per runtime — browser uses IndexedDB; Node uses\n * the filesystem under XDG_CACHE_HOME / %LOCALAPPDATA% / ~/.cache.\n *\n * Native SDKs (iOS / Android / .NET) supply their own platform-\n * appropriate adapter via the same interface.\n */\n\nimport type { CapabilityCache, CapabilityCacheKey, CapabilityScore } from \"./types.js\";\n\n/* -------------------------------------------------------------------------- */\n/* In-memory adapter — fallback / testing */\n/* -------------------------------------------------------------------------- */\n\nexport class InMemoryCapabilityCache implements CapabilityCache {\n private readonly map = new Map<string, CapabilityScore>();\n\n async get(key: CapabilityCacheKey): Promise<CapabilityScore | undefined> {\n return this.map.get(this.keyOf(key));\n }\n async set(score: CapabilityScore): Promise<void> {\n this.map.set(this.keyOf({ modelId: score.modelId, libraryVersion: score.libraryVersion }), score);\n }\n async list(): Promise<CapabilityScore[]> {\n return Array.from(this.map.values());\n }\n async clear(): Promise<void> {\n this.map.clear();\n }\n private keyOf(k: CapabilityCacheKey): string {\n return `${k.libraryVersion}::${k.modelId}`;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Browser adapter — IndexedDB */\n/* -------------------------------------------------------------------------- */\n\nconst DB_NAME = \"dvai-bridge\";\nconst STORE_NAME = \"capability-v1\";\nconst META_STORE_NAME = \"meta-v1\";\n\nexport class IndexedDBCapabilityCache implements CapabilityCache {\n private dbPromise?: Promise<IDBDatabase>;\n\n private openDb(): Promise<IDBDatabase> {\n if (!this.dbPromise) {\n this.dbPromise = new Promise<IDBDatabase>((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME);\n }\n if (!db.objectStoreNames.contains(META_STORE_NAME)) {\n db.createObjectStore(META_STORE_NAME);\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n }\n return this.dbPromise;\n }\n\n async get(key: CapabilityCacheKey): Promise<CapabilityScore | undefined> {\n const db = await this.openDb();\n return await new Promise<CapabilityScore | undefined>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readonly\");\n const req = tx.objectStore(STORE_NAME).get(this.keyOf(key));\n req.onsuccess = () => resolve(req.result as CapabilityScore | undefined);\n req.onerror = () => reject(req.error);\n });\n }\n\n async set(score: CapabilityScore): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).put(score, this.keyOf({ modelId: score.modelId, libraryVersion: score.libraryVersion }));\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async list(): Promise<CapabilityScore[]> {\n const db = await this.openDb();\n return await new Promise<CapabilityScore[]>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readonly\");\n const req = tx.objectStore(STORE_NAME).getAll();\n req.onsuccess = () => resolve(req.result as CapabilityScore[]);\n req.onerror = () => reject(req.error);\n });\n }\n\n async clear(): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).clear();\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /** Reads a small key/value blob (used for the persistent device ID). */\n async getMeta(key: string): Promise<string | undefined> {\n const db = await this.openDb();\n return await new Promise<string | undefined>((resolve, reject) => {\n const tx = db.transaction(META_STORE_NAME, \"readonly\");\n const req = tx.objectStore(META_STORE_NAME).get(key);\n req.onsuccess = () => resolve(req.result as string | undefined);\n req.onerror = () => reject(req.error);\n });\n }\n async setMeta(key: string, value: string): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(META_STORE_NAME, \"readwrite\");\n tx.objectStore(META_STORE_NAME).put(value, key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n private keyOf(k: CapabilityCacheKey): string {\n return `${k.libraryVersion}::${k.modelId}`;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Node FS adapter */\n/* -------------------------------------------------------------------------- */\n\ninterface CacheFile {\n deviceId: string;\n scores: Record<string, CapabilityScore>;\n}\n\nexport class NodeFsCapabilityCache implements CapabilityCache {\n private readonly cachePath: string;\n private cache?: CacheFile;\n\n constructor(cachePath?: string) {\n this.cachePath = cachePath ?? defaultCachePath();\n }\n\n private async load(): Promise<CacheFile> {\n if (this.cache) return this.cache;\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n try {\n const raw = await fs.readFile(this.cachePath, \"utf8\");\n this.cache = JSON.parse(raw) as CacheFile;\n } catch {\n // file doesn't exist or unparseable — start fresh.\n this.cache = { deviceId: \"\", scores: {} };\n }\n // Ensure parent dir exists for future writes.\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n return this.cache;\n }\n\n private async save(): Promise<void> {\n if (!this.cache) return;\n const fs = await import(\"node:fs/promises\");\n await fs.writeFile(this.cachePath, JSON.stringify(this.cache, null, 2), \"utf8\");\n }\n\n async get(key: CapabilityCacheKey): Promise<CapabilityScore | undefined> {\n const c = await this.load();\n return c.scores[this.keyOf(key)];\n }\n async set(score: CapabilityScore): Promise<void> {\n const c = await this.load();\n c.scores[this.keyOf({ modelId: score.modelId, libraryVersion: score.libraryVersion })] = score;\n await this.save();\n }\n async list(): Promise<CapabilityScore[]> {\n const c = await this.load();\n return Object.values(c.scores);\n }\n async clear(): Promise<void> {\n const c = await this.load();\n c.scores = {};\n await this.save();\n }\n async getDeviceId(): Promise<string> {\n const c = await this.load();\n return c.deviceId;\n }\n async setDeviceId(id: string): Promise<void> {\n const c = await this.load();\n c.deviceId = id;\n await this.save();\n }\n\n private keyOf(k: CapabilityCacheKey): string {\n return `${k.libraryVersion}::${k.modelId}`;\n }\n}\n\nfunction defaultCachePath(): string {\n // Cross-platform sane default. We can't use top-level await for\n // node:os; build the path inline using process.env at construction time.\n if (typeof globalThis.process !== \"undefined\") {\n const env = globalThis.process.env;\n const home = env.HOME ?? env.USERPROFILE ?? \".\";\n if (env.LOCALAPPDATA) {\n return `${env.LOCALAPPDATA}/dvai-bridge/capability.json`;\n }\n if (env.XDG_CACHE_HOME) {\n return `${env.XDG_CACHE_HOME}/dvai-bridge/capability.json`;\n }\n return `${home}/.cache/dvai-bridge/capability.json`;\n }\n return \"./.dvai-bridge-capability.json\";\n}\n\n/* -------------------------------------------------------------------------- */\n/* Factory */\n/* -------------------------------------------------------------------------- */\n\nexport function createCapabilityCache(): CapabilityCache {\n if (typeof indexedDB !== \"undefined\") {\n return new IndexedDBCapabilityCache();\n }\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n return new NodeFsCapabilityCache();\n }\n // Fallback for unknown runtimes (workers without IndexedDB, etc.) —\n // memory only. Capability scores won't survive a reload.\n return new InMemoryCapabilityCache();\n}\n","/**\n * Stable per-install device identifier. Generated once on first call,\n * persisted alongside the capability cache. Used for:\n * - identifying THIS device in mDNS TXT records (LAN discovery).\n * - identifying THIS device in rendezvous-server pairing payloads.\n * - keying the capability cache.\n *\n * NOT a privacy hazard: the ID is per-install and per-device-storage,\n * never tied to user identity. Reinstalling the app or wiping app\n * storage produces a fresh ID — that's the right behaviour.\n */\n\n/** Generate a 22-char URL-safe random ID. */\nexport function generateDeviceId(): string {\n // Pull from the platform's secure random source. WebCrypto is\n // available in browsers, Node 19+, Bun, Deno.\n const cryptoApi: Crypto | undefined =\n typeof globalThis.crypto !== \"undefined\" ? globalThis.crypto : undefined;\n\n if (!cryptoApi || typeof cryptoApi.getRandomValues !== \"function\") {\n throw new Error(\n \"[DVAI/capability] No secure random source available. \" +\n \"globalThis.crypto.getRandomValues required (Node ≥19, modern browsers, Bun, Deno).\",\n );\n }\n\n const bytes = new Uint8Array(16);\n cryptoApi.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n // Browser + Node-compatible: convert to base64, then URL-safe.\n let binary = \"\";\n for (const b of bytes) {\n binary += String.fromCharCode(b);\n }\n const b64 = typeof btoa !== \"undefined\"\n ? btoa(binary)\n : Buffer.from(binary, \"binary\").toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n","/**\n * Cold-run capability probe. Runs a fixed-size completion against the\n * active backend and measures decode tok/s. Result is cached per\n * (modelId, libraryVersion) so we only pay this cost once per\n * (model, version) pair.\n */\n\nimport type { CapabilityScore } from \"./types.js\";\n\nconst PROBE_PROMPT = \"Generate a single short sentence about clouds.\";\nconst PROBE_MAX_TOKENS = 50;\n\n/**\n * Generic backend interface the probe needs. The full BackendInterface\n * in DVAI is wider; we only need chatCompletion here.\n */\nexport interface ProbableBackend {\n chatCompletion(req: {\n messages: Array<{ role: string; content: string }>;\n max_tokens?: number;\n stream?: boolean;\n }): Promise<{\n choices: Array<{ message?: { content?: string }; text?: string }>;\n usage?: { completion_tokens?: number };\n }>;\n}\n\nexport async function probeCapability(opts: {\n backend: ProbableBackend;\n modelId: string;\n libraryVersion: string;\n deviceId: string;\n}): Promise<CapabilityScore> {\n const t0 = performance.now();\n const response = await opts.backend.chatCompletion({\n messages: [{ role: \"user\", content: PROBE_PROMPT }],\n max_tokens: PROBE_MAX_TOKENS,\n stream: false,\n });\n const elapsedSec = (performance.now() - t0) / 1000;\n\n // Token count: prefer the backend's own count (many backends report\n // it in usage.completion_tokens), fall back to a coarse word-count\n // approximation.\n const tokensFromUsage = response.usage?.completion_tokens;\n const text =\n response.choices[0]?.message?.content ??\n response.choices[0]?.text ??\n \"\";\n const tokens = tokensFromUsage ?? approximateTokenCount(text);\n\n // Guard against zero-time and zero-token edge cases.\n const tokPerSec = tokens > 0 && elapsedSec > 0\n ? Math.round((tokens / elapsedSec) * 10) / 10\n : 0;\n\n return {\n modelId: opts.modelId,\n deviceId: opts.deviceId,\n libraryVersion: opts.libraryVersion,\n tokPerSec,\n source: \"probe\",\n measuredAt: Date.now(),\n };\n}\n\n/**\n * Crude token-count approximation when the backend doesn't report\n * usage. Underestimates by ~25% for English text; that's fine for\n * capacity-band purposes — we'd rather under-report tok/s and offload\n * more often than over-report.\n */\nfunction approximateTokenCount(text: string): number {\n // ~4 chars per token for English; whitespace + punctuation counted.\n return Math.max(1, Math.round(text.length / 4));\n}\n","/**\n * Phase 3 — capability assessment surface.\n *\n * Public facade glued together from the building blocks in this dir:\n * - probe.ts: cold-run measurement\n * - heuristic.ts: static fallback before first probe\n * - cache.ts: persistent storage adapters\n * - deviceId.ts: stable per-install ID\n *\n * Wired into DVAI in src/index.ts as `dvai.probeCapability()` and\n * `dvai.getCapability(modelId)`. The offload decider in src/offload/\n * is the primary consumer.\n */\n\nimport {\n IndexedDBCapabilityCache,\n NodeFsCapabilityCache,\n createCapabilityCache,\n InMemoryCapabilityCache,\n} from \"./cache.js\";\nimport { detectDeviceHints, detectDeviceHintsAsync, heuristicTokPerSec } from \"./heuristic.js\";\nimport { generateDeviceId } from \"./deviceId.js\";\nimport { probeCapability, type ProbableBackend } from \"./probe.js\";\nimport {\n assessCapability,\n HardwareTooWeakError,\n type PrecheckOptions,\n type PrecheckResult,\n} from \"./precheck.js\";\nimport type { CapabilityCache, CapabilityScore, DeviceCapabilityHints } from \"./types.js\";\n\nexport type {\n CapabilityScore,\n CapabilityCache,\n DeviceCapabilityHints,\n ProbableBackend,\n PrecheckOptions,\n PrecheckResult,\n};\n\nexport {\n IndexedDBCapabilityCache,\n NodeFsCapabilityCache,\n InMemoryCapabilityCache,\n createCapabilityCache,\n heuristicTokPerSec,\n detectDeviceHints,\n detectDeviceHintsAsync,\n generateDeviceId,\n probeCapability,\n assessCapability,\n HardwareTooWeakError,\n};\n\nconst DEVICE_ID_META_KEY = \"dvai.deviceId\";\n\n/**\n * Resolve (or generate-on-first-call + persist) the per-install\n * device ID. The cache adapter is responsible for the persistence;\n * this function unifies the IndexedDB / Node-FS access patterns.\n */\nexport async function ensureDeviceId(cache: CapabilityCache): Promise<string> {\n // IndexedDB adapter has its own getMeta/setMeta for the meta store.\n if (cache instanceof IndexedDBCapabilityCache) {\n const existing = await cache.getMeta(DEVICE_ID_META_KEY);\n if (existing) return existing;\n const fresh = generateDeviceId();\n await cache.setMeta(DEVICE_ID_META_KEY, fresh);\n return fresh;\n }\n // Node FS adapter has dedicated getDeviceId/setDeviceId on the\n // backing JSON file.\n if (cache instanceof NodeFsCapabilityCache) {\n const existing = await cache.getDeviceId();\n if (existing) return existing;\n const fresh = generateDeviceId();\n await cache.setDeviceId(fresh);\n return fresh;\n }\n // In-memory adapter — generate but don't persist (ephemeral).\n const fresh = generateDeviceId();\n return fresh;\n}\n\n/**\n * Get the capability score for a model on this device. Returns the\n * cached probe result if available; otherwise the static heuristic\n * estimate (marked source: \"heuristic\"). Does NOT trigger a probe —\n * call probeAndCache separately to measure.\n */\nexport async function getCapability(opts: {\n cache: CapabilityCache;\n modelId: string;\n libraryVersion: string;\n hints?: DeviceCapabilityHints;\n}): Promise<CapabilityScore> {\n const cached = await opts.cache.get({\n modelId: opts.modelId,\n libraryVersion: opts.libraryVersion,\n });\n if (cached) return cached;\n\n const hints = opts.hints ?? (await detectDeviceHintsAsync());\n const deviceId = await ensureDeviceId(opts.cache);\n\n return {\n modelId: opts.modelId,\n deviceId,\n libraryVersion: opts.libraryVersion,\n tokPerSec: heuristicTokPerSec(hints),\n source: \"heuristic\",\n measuredAt: Date.now(),\n };\n}\n\n/**\n * Run a cold-run probe against the given backend, persist the result,\n * and return the new score.\n */\nexport async function probeAndCache(opts: {\n cache: CapabilityCache;\n backend: ProbableBackend;\n modelId: string;\n libraryVersion: string;\n}): Promise<CapabilityScore> {\n const deviceId = await ensureDeviceId(opts.cache);\n const score = await probeCapability({\n backend: opts.backend,\n modelId: opts.modelId,\n libraryVersion: opts.libraryVersion,\n deviceId,\n });\n await opts.cache.set(score);\n return score;\n}\n\n/** Drop all cached scores. Useful for diagnostics + tests. */\nexport async function clearCapabilityCache(cache: CapabilityCache): Promise<void> {\n await cache.clear();\n}\n","/**\n * Phase 3 — peer discovery types.\n *\n * A \"peer\" is another device running dvai-bridge that this device can\n * (potentially) offload inference requests to. Peers are surfaced by\n * one or more `IDiscovery` impls — LAN mDNS, app-supplied static list,\n * rendezvous-paired (internet), or a host-app-provided custom source.\n */\n\nexport interface Peer {\n /** Stable per-install device ID of the peer. */\n deviceId: string;\n /** Human-readable hint (iOS device name, hostname, etc.). */\n deviceName: string;\n /** Library SemVer the peer is running. */\n dvaiVersion: string;\n /** OpenAI-compatible base URL the peer's local server exposes. */\n baseUrl: string;\n /**\n * v3.1 wire-protocol extension. Identifies which application on the\n * peer device is making the request — used by multi-tenant targets\n * (the Hub) to isolate per-app state. Optional for backwards compat\n * with v3.0 SDKs that don't send this field.\n */\n appId?: string;\n /**\n * Models the peer claims to have loaded right now. Used to filter\n * peer eligibility — we only offload model X to a peer that already\n * has model X loaded (loading from scratch on the peer is fine but\n * defeats the latency win).\n */\n loadedModels: string[];\n /**\n * Peer-reported capability map: { modelId → tok/s }. Treat as\n * advisory only; the offload decider re-probes a peer with a small\n * reachability+decode test before its first real offload request.\n */\n capability: Record<string, number>;\n /** Discovery source — useful for diagnostics and the structured-error response. */\n via: \"mdns\" | \"static\" | \"rendezvous\" | \"custom\";\n /** Whether the peer's URL uses TLS. */\n secure: boolean;\n /** Last-seen unix ms — discovery sources update this. */\n lastSeenAt: number;\n}\n\nexport type DiscoveryEvent =\n | { type: \"peer-up\"; peer: Peer }\n | { type: \"peer-down\"; deviceId: string }\n | { type: \"error\"; message: string };\n\n/**\n * The contract every discovery source implements. Used by the\n * composite discovery layer.\n */\nexport interface IDiscovery {\n /** Begin discovering. Idempotent. */\n start(): Promise<void>;\n /** Stop and release resources. Idempotent. */\n stop(): Promise<void>;\n /** Snapshot of currently-known peers. */\n peers(): Peer[];\n /** Subscribe to discovery events. Returns unsubscribe fn. */\n subscribe(listener: (e: DiscoveryEvent) => void): () => void;\n}\n\n/** Service-type advertised on mDNS for dvai-bridge instances. */\nexport const MDNS_SERVICE_TYPE = \"_dvai-bridge._tcp.local\";\n","/**\n * Static-list discovery source. Apps that already know about peer\n * devices (e.g. a corporate device registry, hard-coded testbed\n * peers, persistent pairings restored across restart) supply a static\n * Peer[] at config time.\n *\n * The list is treated as authoritative — we don't health-check the\n * URLs here. The offload decider runs reachability checks before\n * actually using a peer.\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\n\nexport class StaticDiscovery implements IDiscovery {\n private readonly listeners = new Set<(e: DiscoveryEvent) => void>();\n private readonly peerList: Peer[];\n private started = false;\n\n constructor(peers: Peer[]) {\n // Normalize lastSeenAt to \"now\" if not supplied (treat 0 as\n // \"no value, use now\"). Preserve explicit non-zero values so\n // tests + custom merges that care about ordering can pass them.\n const now = Date.now();\n this.peerList = peers.map((p) => ({\n ...p,\n lastSeenAt: p.lastSeenAt && p.lastSeenAt > 0 ? p.lastSeenAt : now,\n via: \"static\",\n }));\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n // Emit peer-up for each immediately so consumers see them on\n // first poll.\n for (const peer of this.peerList) {\n this.emit({ type: \"peer-up\", peer });\n }\n }\n\n async stop(): Promise<void> {\n this.started = false;\n for (const peer of this.peerList) {\n this.emit({ type: \"peer-down\", deviceId: peer.deviceId });\n }\n }\n\n peers(): Peer[] {\n return this.started ? [...this.peerList] : [];\n }\n\n subscribe(listener: (e: DiscoveryEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private emit(e: DiscoveryEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(e);\n } catch (err) {\n // Don't let one buggy listener break the others.\n console.error(\"[DVAI/discovery] listener threw:\", err);\n }\n }\n }\n}\n","/**\n * Node mDNS / DNS-SD discovery + advertisement.\n *\n * Backed by `multicast-dns` (optional dep — if not installed, this\n * module logs a warning and degrades to no-op). The decision to keep\n * the dep optional avoids forcing a transitive native dep on every\n * dvai-bridge-core consumer; the offload feature itself is opt-in.\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\nimport { MDNS_SERVICE_TYPE } from \"./types.js\";\n\n/**\n * Loose typing for the multicast-dns module — we don't take a hard\n * dep on its types (would force the install).\n */\ninterface MdnsInstance {\n on(event: \"response\", handler: (response: MdnsResponse) => void): void;\n query(query: { questions: Array<{ name: string; type: string }> }): void;\n respond(response: { answers: MdnsAnswer[] }): void;\n destroy(): void;\n}\ninterface MdnsResponse {\n answers: MdnsAnswer[];\n additionals?: MdnsAnswer[];\n}\ninterface MdnsAnswer {\n name: string;\n type: string;\n data: unknown;\n ttl?: number;\n}\n\n/** Fields we advertise in the TXT record per the spec §4.2. */\nexport interface AdvertisedTxt {\n dvaiVersion: string;\n deviceId: string;\n deviceName: string;\n models: string[];\n capability: Record<string, number>;\n port: number;\n secure: boolean;\n}\n\nexport class NodeMdnsDiscovery implements IDiscovery {\n private mdns?: MdnsInstance;\n private readonly peerMap = new Map<string, Peer>();\n private readonly listeners = new Set<(e: DiscoveryEvent) => void>();\n private queryTimer?: NodeJS.Timeout;\n private gcTimer?: NodeJS.Timeout;\n private started = false;\n\n constructor(\n private readonly opts: {\n /** Our own deviceId — filter so we don't return ourselves. */\n selfDeviceId: string;\n /** Optional: TXT we should advertise. If omitted, we discover-only. */\n advertise?: AdvertisedTxt;\n /** How often to re-broadcast queries. Default 30s. */\n queryIntervalMs?: number;\n /** GC interval for stale peers (no recent advertisement). Default 60s. */\n gcIntervalMs?: number;\n /** TTL after which a peer is considered down (no recent ad). Default 90s. */\n peerTtlMs?: number;\n },\n ) {}\n\n async start(): Promise<void> {\n if (this.started) return;\n\n let mod: { default?: (opts?: unknown) => MdnsInstance } | ((opts?: unknown) => MdnsInstance);\n try {\n // @ts-expect-error — optional dep; consumers install only if they want LAN discovery.\n mod = await import(\"multicast-dns\");\n } catch {\n this.emit({\n type: \"error\",\n message:\n \"[DVAI/discovery] `multicast-dns` not installed; LAN discovery disabled. \" +\n \"Install with `npm i multicast-dns` (optional dep) to enable peer discovery.\",\n });\n return;\n }\n const factory = (typeof mod === \"function\" ? mod : mod.default) as (\n opts?: unknown,\n ) => MdnsInstance;\n this.mdns = factory();\n this.started = true;\n\n this.mdns.on(\"response\", (response) => this.onResponse(response));\n\n // Initial query + periodic re-query.\n this.broadcastQuery();\n const queryInterval = this.opts.queryIntervalMs ?? 30_000;\n this.queryTimer = setInterval(() => this.broadcastQuery(), queryInterval);\n\n // Periodic GC for stale peers.\n const gcInterval = this.opts.gcIntervalMs ?? 60_000;\n this.gcTimer = setInterval(() => this.gc(), gcInterval);\n }\n\n async stop(): Promise<void> {\n if (!this.started) return;\n this.started = false;\n if (this.queryTimer) clearInterval(this.queryTimer);\n if (this.gcTimer) clearInterval(this.gcTimer);\n this.mdns?.destroy();\n this.mdns = undefined;\n for (const peer of this.peerMap.values()) {\n this.emit({ type: \"peer-down\", deviceId: peer.deviceId });\n }\n this.peerMap.clear();\n }\n\n peers(): Peer[] {\n return Array.from(this.peerMap.values());\n }\n\n subscribe(listener: (e: DiscoveryEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private broadcastQuery(): void {\n if (!this.mdns) return;\n try {\n this.mdns.query({\n questions: [{ name: MDNS_SERVICE_TYPE, type: \"PTR\" }],\n });\n } catch (err) {\n this.emit({ type: \"error\", message: `mDNS query failed: ${String(err)}` });\n }\n\n // If we're advertising too, respond unsolicited so peers see us\n // sooner than their next query interval.\n if (this.opts.advertise) {\n try {\n this.mdns.respond({\n answers: this.buildAdvertisementAnswers(this.opts.advertise),\n });\n } catch (err) {\n this.emit({ type: \"error\", message: `mDNS respond failed: ${String(err)}` });\n }\n }\n }\n\n private buildAdvertisementAnswers(txt: AdvertisedTxt): MdnsAnswer[] {\n const instanceName = `${txt.deviceId}.${MDNS_SERVICE_TYPE}`;\n return [\n // PTR: service-type → instance-name\n { name: MDNS_SERVICE_TYPE, type: \"PTR\", data: instanceName, ttl: 120 },\n // SRV: instance-name → host/port\n {\n name: instanceName,\n type: \"SRV\",\n data: { port: txt.port, target: `${txt.deviceId}.local` },\n ttl: 120,\n },\n // TXT: instance-name → key/value pairs\n {\n name: instanceName,\n type: \"TXT\",\n data: this.encodeTxt(txt),\n ttl: 120,\n },\n ];\n }\n\n private encodeTxt(txt: AdvertisedTxt): string[] {\n return [\n `dvaiVersion=${txt.dvaiVersion}`,\n `deviceId=${txt.deviceId}`,\n `deviceName=${txt.deviceName}`,\n `models=${txt.models.join(\",\")}`,\n `capability=${JSON.stringify(txt.capability)}`,\n `port=${txt.port}`,\n `secure=${txt.secure ? \"1\" : \"0\"}`,\n ];\n }\n\n private onResponse(response: MdnsResponse): void {\n const all = [...response.answers, ...(response.additionals ?? [])];\n let srv: MdnsAnswer | undefined;\n let txt: MdnsAnswer | undefined;\n for (const a of all) {\n if (a.type === \"SRV\" && String(a.name).endsWith(MDNS_SERVICE_TYPE)) srv = a;\n if (a.type === \"TXT\" && String(a.name).endsWith(MDNS_SERVICE_TYPE)) txt = a;\n }\n if (!srv || !txt) return;\n\n const decoded = this.decodeTxt(txt.data);\n if (!decoded || decoded.deviceId === this.opts.selfDeviceId) return;\n\n const srvData = srv.data as { port: number; target: string };\n const baseUrl = `${decoded.secure ? \"https\" : \"http\"}://${srvData.target}:${srvData.port}/v1`;\n\n const peer: Peer = {\n deviceId: decoded.deviceId,\n deviceName: decoded.deviceName,\n dvaiVersion: decoded.dvaiVersion,\n baseUrl,\n loadedModels: decoded.models,\n capability: decoded.capability,\n via: \"mdns\",\n secure: decoded.secure,\n lastSeenAt: Date.now(),\n };\n\n const existing = this.peerMap.get(peer.deviceId);\n this.peerMap.set(peer.deviceId, peer);\n if (!existing) {\n this.emit({ type: \"peer-up\", peer });\n }\n }\n\n private decodeTxt(raw: unknown): AdvertisedTxt | undefined {\n let lines: string[];\n if (Array.isArray(raw)) {\n lines = raw.map((b) => (Buffer.isBuffer(b) ? b.toString(\"utf8\") : String(b)));\n } else if (Buffer.isBuffer(raw)) {\n lines = raw.toString(\"utf8\").split(\"\\n\");\n } else if (typeof raw === \"string\") {\n lines = raw.split(\"\\n\");\n } else {\n return undefined;\n }\n const map: Record<string, string> = {};\n for (const line of lines) {\n const eq = line.indexOf(\"=\");\n if (eq < 0) continue;\n map[line.slice(0, eq)] = line.slice(eq + 1);\n }\n if (!map.deviceId || !map.dvaiVersion) return undefined;\n let capability: Record<string, number> = {};\n try {\n capability = JSON.parse(map.capability ?? \"{}\");\n } catch {\n // bad TXT — keep going with empty capability map\n }\n return {\n dvaiVersion: map.dvaiVersion,\n deviceId: map.deviceId,\n deviceName: map.deviceName ?? map.deviceId,\n models: map.models ? map.models.split(\",\").filter(Boolean) : [],\n capability,\n port: Number.parseInt(map.port ?? \"0\", 10),\n secure: map.secure === \"1\",\n };\n }\n\n private gc(): void {\n const ttl = this.opts.peerTtlMs ?? 90_000;\n const cutoff = Date.now() - ttl;\n for (const [id, peer] of this.peerMap) {\n if (peer.lastSeenAt < cutoff) {\n this.peerMap.delete(id);\n this.emit({ type: \"peer-down\", deviceId: id });\n }\n }\n }\n\n private emit(e: DiscoveryEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(e);\n } catch (err) {\n console.error(\"[DVAI/discovery] listener threw:\", err);\n }\n }\n }\n}\n","/**\n * Browser-side mDNS adapter — a no-op.\n *\n * Browsers don't speak mDNS. They CAN'T accept inbound HTTP requests\n * across origins reliably either, so even if we could discover peers\n * on the LAN, the browser couldn't act as an offload TARGET. Browser\n * is offload-source-only; native devices are the offload targets it\n * pairs with via the rendezvous server.\n *\n * This file exists so the composite discovery layer can pick a\n * runtime-appropriate impl without an `if (browser)` check.\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\n\nexport class BrowserMdnsDiscovery implements IDiscovery {\n // Empty — browsers can't speak mDNS, but this slot keeps the\n // composite discovery layer's typing clean.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(_opts: unknown = {}) {}\n\n async start(): Promise<void> {\n // No-op. Diagnostic log is at TRACE level only — this isn't an\n // error condition; it's an architectural fact.\n }\n async stop(): Promise<void> {\n // No-op.\n }\n peers(): Peer[] {\n return [];\n }\n subscribe(_listener: (e: DiscoveryEvent) => void): () => void {\n return () => {};\n }\n}\n","/**\n * Composite discovery — combines mDNS + static + rendezvous-paired\n * peers into a single unified peer stream. The offload decider only\n * talks to this layer; it doesn't know which source produced a peer.\n *\n * Per-source peers are deduplicated by `deviceId` — the most-recent\n * `lastSeenAt` wins, with mDNS preferred over static when both\n * exist (same device, but mDNS gives us a fresher TXT).\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\n\nexport class CompositeDiscovery implements IDiscovery {\n private readonly listeners = new Set<(e: DiscoveryEvent) => void>();\n private readonly subscriptions: Array<() => void> = [];\n private started = false;\n\n constructor(private readonly sources: IDiscovery[]) {}\n\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n for (const source of this.sources) {\n const unsub = source.subscribe((e) => this.onEvent(e));\n this.subscriptions.push(unsub);\n await source.start();\n }\n }\n\n async stop(): Promise<void> {\n if (!this.started) return;\n this.started = false;\n for (const unsub of this.subscriptions) unsub();\n this.subscriptions.length = 0;\n for (const source of this.sources) {\n await source.stop();\n }\n }\n\n peers(): Peer[] {\n const merged = new Map<string, Peer>();\n for (const source of this.sources) {\n for (const peer of source.peers()) {\n const existing = merged.get(peer.deviceId);\n if (!existing || peer.lastSeenAt > existing.lastSeenAt) {\n merged.set(peer.deviceId, peer);\n }\n }\n }\n return Array.from(merged.values());\n }\n\n subscribe(listener: (e: DiscoveryEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private onEvent(e: DiscoveryEvent): void {\n // Forward all events. Consumers handle duplicates via the merged\n // peer set rather than us trying to dedupe events here (an event\n // for one source might still be informative — e.g. the rendezvous\n // peer dropped but the LAN peer is still up).\n for (const listener of this.listeners) {\n try {\n listener(e);\n } catch (err) {\n console.error(\"[DVAI/discovery] listener threw:\", err);\n }\n }\n }\n}\n\n/** Factory: pick the right mDNS adapter for the runtime. */\nexport async function createMdnsDiscovery(opts: {\n selfDeviceId: string;\n advertise?: import(\"./mdns-node.js\").AdvertisedTxt;\n}): Promise<IDiscovery> {\n if (\n typeof globalThis.process !== \"undefined\" &&\n globalThis.process.versions?.node\n ) {\n const { NodeMdnsDiscovery } = await import(\"./mdns-node.js\");\n return new NodeMdnsDiscovery(opts);\n }\n const { BrowserMdnsDiscovery } = await import(\"./mdns-browser.js\");\n return new BrowserMdnsDiscovery();\n}\n","/**\n * Public surface for the discovery module.\n */\n\nexport type { Peer, DiscoveryEvent, IDiscovery } from \"./types.js\";\nexport { MDNS_SERVICE_TYPE } from \"./types.js\";\nexport { StaticDiscovery } from \"./static.js\";\nexport { CompositeDiscovery, createMdnsDiscovery } from \"./composite.js\";\nexport { BrowserMdnsDiscovery } from \"./mdns-browser.js\";\n// Node-only — re-exported lazily; importing this in a browser bundle\n// will tree-shake away cleanly when discovery isn't used.\nexport type { AdvertisedTxt } from \"./mdns-node.js\";\n","/**\n * Pairing storage adapters. Mirrors capability/cache.ts shape — same\n * runtimes, same per-runtime impls, same default-cache-path resolution.\n */\n\nimport type { Pairing, PairingStore } from \"./types.js\";\n\n/** In-memory adapter (testing + browser-without-IndexedDB fallback). */\nexport class InMemoryPairingStore implements PairingStore {\n private readonly map = new Map<string, Pairing>();\n\n async get(peerDeviceId: string): Promise<Pairing | undefined> {\n return this.map.get(peerDeviceId);\n }\n async set(p: Pairing): Promise<void> {\n this.map.set(p.peerDeviceId, p);\n }\n async list(): Promise<Pairing[]> {\n return Array.from(this.map.values());\n }\n async remove(peerDeviceId: string): Promise<void> {\n this.map.delete(peerDeviceId);\n }\n async clear(): Promise<void> {\n this.map.clear();\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Browser — IndexedDB */\n/* -------------------------------------------------------------------------- */\n\nconst DB_NAME = \"dvai-bridge\";\nconst STORE_NAME = \"pairings-v1\";\n\nexport class IndexedDBPairingStore implements PairingStore {\n private dbPromise?: Promise<IDBDatabase>;\n\n private openDb(): Promise<IDBDatabase> {\n if (!this.dbPromise) {\n this.dbPromise = new Promise<IDBDatabase>((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: \"peerDeviceId\" });\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n }\n return this.dbPromise;\n }\n\n async get(peerDeviceId: string): Promise<Pairing | undefined> {\n const db = await this.openDb();\n return await new Promise((resolve, reject) => {\n const req = db.transaction(STORE_NAME, \"readonly\").objectStore(STORE_NAME).get(peerDeviceId);\n req.onsuccess = () => resolve(req.result as Pairing | undefined);\n req.onerror = () => reject(req.error);\n });\n }\n async set(p: Pairing): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).put(p);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n async list(): Promise<Pairing[]> {\n const db = await this.openDb();\n return await new Promise((resolve, reject) => {\n const req = db.transaction(STORE_NAME, \"readonly\").objectStore(STORE_NAME).getAll();\n req.onsuccess = () => resolve(req.result as Pairing[]);\n req.onerror = () => reject(req.error);\n });\n }\n async remove(peerDeviceId: string): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).delete(peerDeviceId);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n async clear(): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).clear();\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Node FS adapter */\n/* -------------------------------------------------------------------------- */\n\nexport class NodeFsPairingStore implements PairingStore {\n private readonly cachePath: string;\n private cache?: Record<string, Pairing>;\n\n constructor(cachePath?: string) {\n this.cachePath = cachePath ?? defaultPairingsPath();\n }\n\n private async load(): Promise<Record<string, Pairing>> {\n if (this.cache) return this.cache;\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n try {\n const raw = await fs.readFile(this.cachePath, \"utf8\");\n this.cache = JSON.parse(raw) as Record<string, Pairing>;\n } catch {\n this.cache = {};\n }\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n return this.cache;\n }\n\n private async save(): Promise<void> {\n if (!this.cache) return;\n const fs = await import(\"node:fs/promises\");\n await fs.writeFile(this.cachePath, JSON.stringify(this.cache, null, 2), \"utf8\");\n }\n\n async get(peerDeviceId: string): Promise<Pairing | undefined> {\n const c = await this.load();\n return c[peerDeviceId];\n }\n async set(p: Pairing): Promise<void> {\n const c = await this.load();\n c[p.peerDeviceId] = p;\n await this.save();\n }\n async list(): Promise<Pairing[]> {\n const c = await this.load();\n return Object.values(c);\n }\n async remove(peerDeviceId: string): Promise<void> {\n const c = await this.load();\n delete c[peerDeviceId];\n await this.save();\n }\n async clear(): Promise<void> {\n this.cache = {};\n await this.save();\n }\n}\n\nfunction defaultPairingsPath(): string {\n if (typeof globalThis.process !== \"undefined\") {\n const env = globalThis.process.env;\n const home = env.HOME ?? env.USERPROFILE ?? \".\";\n if (env.LOCALAPPDATA) return `${env.LOCALAPPDATA}/dvai-bridge/pairings.json`;\n if (env.XDG_CACHE_HOME) return `${env.XDG_CACHE_HOME}/dvai-bridge/pairings.json`;\n return `${home}/.cache/dvai-bridge/pairings.json`;\n }\n return \"./.dvai-bridge-pairings.json\";\n}\n\nexport function createPairingStore(): PairingStore {\n if (typeof indexedDB !== \"undefined\") return new IndexedDBPairingStore();\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n return new NodeFsPairingStore();\n }\n return new InMemoryPairingStore();\n}\n","/**\n * Pairing decision flow. Coordinates the host-app `onPairingRequest`\n * callback (which surfaces a UI prompt) with the persistent store.\n *\n * Default behaviour (no callback supplied): deny all incoming pairings.\n * That's the safe fallback — apps that haven't wired UI shouldn't\n * accidentally accept random LAN devices.\n */\n\nimport { generatePairingKey } from \"./handshake.js\";\nimport type { Pairing, PairingStore } from \"./types.js\";\n\nexport interface PairingPolicyOptions {\n store: PairingStore;\n /**\n * Host-app callback that returns the user's approve/deny decision.\n *\n * Return value:\n * - `true` / `false` — backwards-compat boolean. PairingPolicy\n * generates a fresh pairingKey when approved.\n * - `{ approved: true, pairingKey }` — host has its OWN pairing\n * state (e.g. v3.1 Hub's MultiTenantPairing) and wants\n * PairingPolicy to use the host-supplied key. Avoids two\n * parallel stores generating divergent keys for the same peer.\n * - `{ approved: false }` — denied.\n */\n onPairingRequest?: (\n peerDeviceId: string,\n peerDeviceName: string,\n appId?: string,\n ) => Promise<\n | boolean\n | { approved: true; pairingKey: string }\n | { approved: false }\n >;\n /** Pairing TTL in days. Default 30. */\n expireAfterDays?: number;\n}\n\nexport interface IncomingHandshake {\n peerDeviceId: string;\n peerDeviceName: string;\n via: \"lan-handshake\" | \"rendezvous-qr\";\n /**\n * v3.1 wire-protocol extension. Identifies which application on the\n * peer device is requesting pairing, so multi-tenant targets (the\n * v3.1 Hub) can isolate per-app pairings, capability caches, and\n * audit logs. Optional for backwards compat — v3.0 SDKs that don't\n * send this field still pair, just under the deviceId-as-appId\n * fallback the Hub uses today.\n */\n appId?: string;\n}\n\nexport class PairingPolicy {\n constructor(private readonly opts: PairingPolicyOptions) {}\n\n /**\n * Get an existing pairing, applying TTL expiry. Returns undefined\n * if the pairing is missing or expired.\n */\n async getActive(peerDeviceId: string): Promise<Pairing | undefined> {\n const existing = await this.opts.store.get(peerDeviceId);\n if (!existing) return undefined;\n const ttlMs = (this.opts.expireAfterDays ?? 30) * 24 * 60 * 60 * 1000;\n if (Date.now() - existing.lastUsedAt > ttlMs) {\n await this.opts.store.remove(peerDeviceId);\n return undefined;\n }\n return existing;\n }\n\n /**\n * Process an incoming pairing request. If we already have an active\n * pairing for this peer, reuse it (and bump lastUsedAt). Otherwise\n * call the host-app's onPairingRequest hook to ask for approval.\n *\n * Returns the pairing key (base64-url) on approval; throws otherwise.\n */\n async approveOrFetch(req: IncomingHandshake): Promise<Pairing> {\n const existing = await this.getActive(req.peerDeviceId);\n if (existing) {\n existing.lastUsedAt = Date.now();\n await this.opts.store.set(existing);\n return existing;\n }\n\n const callback = this.opts.onPairingRequest;\n const result = callback\n ? await callback(req.peerDeviceId, req.peerDeviceName, req.appId)\n : false;\n const approved =\n typeof result === \"boolean\" ? result : result.approved;\n if (!approved) {\n throw new Error(\n `[DVAI/pairing] denied: peer ${req.peerDeviceId} (${req.peerDeviceName})${callback ? \"\" : \" (no onPairingRequest callback supplied)\"}`,\n );\n }\n // If the host returned a pairingKey (v3.1 Hub provides one from\n // its own MultiTenantPairing store), use it. Otherwise generate\n // a fresh one.\n const hostKey =\n typeof result === \"object\" && \"pairingKey\" in result\n ? result.pairingKey\n : undefined;\n const fresh: Pairing = {\n peerDeviceId: req.peerDeviceId,\n peerDeviceName: req.peerDeviceName,\n pairingKey: hostKey ?? generatePairingKey(),\n pairedAt: Date.now(),\n lastUsedAt: Date.now(),\n via: req.via,\n };\n await this.opts.store.set(fresh);\n return fresh;\n }\n\n /** Mark a pairing as used (bumps lastUsedAt). */\n async touch(peerDeviceId: string): Promise<void> {\n const existing = await this.opts.store.get(peerDeviceId);\n if (!existing) return;\n existing.lastUsedAt = Date.now();\n await this.opts.store.set(existing);\n }\n\n async revoke(peerDeviceId: string): Promise<void> {\n await this.opts.store.remove(peerDeviceId);\n }\n}\n","export type { Pairing, PairingStore, HandshakeRequest, HandshakeResponse } from \"./types.js\";\nexport {\n generatePairingKey,\n generateNonce,\n signHmac,\n verifyHmac,\n composeSignedMessage,\n} from \"./handshake.js\";\nexport {\n InMemoryPairingStore,\n IndexedDBPairingStore,\n NodeFsPairingStore,\n createPairingStore,\n} from \"./store.js\";\nexport { PairingPolicy, type PairingPolicyOptions, type IncomingHandshake } from \"./policy.js\";\n","/**\n * Phase 3 — `/v1/dvai/*` handlers.\n *\n * Hosted by the same in-process HTTP server (or MSW intercept in\n * browser) that already serves the OpenAI surface. Routes:\n *\n * GET /v1/dvai/health — liveness, capacity, version\n * GET /v1/dvai/capability — this device's capability map\n * GET /v1/dvai/peers — discovered peer list\n * POST /v1/dvai/probe — manually trigger a capability probe\n * POST /v1/dvai/handshake — LAN-pairing handshake\n * POST /v1/dvai/pair-qr — start a rendezvous session, return QR payload\n * POST /v1/dvai/pair-scan — submit a scanned QR payload, complete the join\n *\n * These handlers are pure-ish — they take a context object with the\n * relevant collaborators (capability cache, discovery, pairing policy,\n * rendezvous client, etc.) and return JSON responses. Wired into the\n * core's existing handler pipeline by src/index.ts.\n */\n\nimport type { CapabilityCache, CapabilityScore, ProbableBackend } from \"../../capability/index.js\";\nimport { getCapability, probeAndCache, ensureDeviceId } from \"../../capability/index.js\";\nimport type { Peer, IDiscovery } from \"../../discovery/index.js\";\nimport type { PairingPolicy, IncomingHandshake } from \"../../pairing/index.js\";\n\nexport interface DvaiHandlerContext {\n /** Library SemVer — used in cache keys + responses. */\n libraryVersion: string;\n /** Currently-loaded model ID (for capability lookup). */\n currentModelId?: string;\n /** Capability cache. */\n capabilityCache: CapabilityCache;\n /** Backend reference for the probe endpoint. */\n backend?: ProbableBackend;\n /** Discovery layer. */\n discovery?: IDiscovery;\n /** Pairing policy. */\n pairingPolicy?: PairingPolicy;\n /** Server-uptime epoch. */\n startedAt: number;\n}\n\n/** Generic handler shape: takes parsed request body, returns JSON-stringifiable. */\nexport type DvaiHandler = (req: { body: unknown }) => Promise<{\n status: number;\n body: unknown;\n}>;\n\n/* -------------------------------------------------------------------------- */\n/* Handlers */\n/* -------------------------------------------------------------------------- */\n\nexport function handleHealth(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => ({\n status: 200,\n body: {\n status: \"ok\",\n version: ctx.libraryVersion,\n uptimeSec: Math.floor((Date.now() - ctx.startedAt) / 1000),\n currentModelId: ctx.currentModelId ?? null,\n },\n });\n}\n\nexport function handleCapability(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n const all: CapabilityScore[] = await ctx.capabilityCache.list();\n return { status: 200, body: { scores: all } };\n };\n}\n\nexport function handlePeers(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n const peers: Peer[] = ctx.discovery?.peers() ?? [];\n return { status: 200, body: { peers } };\n };\n}\n\nexport function handleProbe(ctx: DvaiHandlerContext): DvaiHandler {\n return async (req) => {\n if (!ctx.backend) {\n return {\n status: 503,\n body: { error: { type: \"no_backend\", message: \"no backend currently loaded\" } },\n };\n }\n const body = (req.body ?? {}) as { modelId?: string };\n const modelId = body.modelId ?? ctx.currentModelId;\n if (!modelId) {\n return {\n status: 400,\n body: {\n error: {\n type: \"missing_model_id\",\n message: \"supply `modelId` in request body or call after a model is loaded\",\n },\n },\n };\n }\n const score = await probeAndCache({\n cache: ctx.capabilityCache,\n backend: ctx.backend,\n modelId,\n libraryVersion: ctx.libraryVersion,\n });\n return { status: 200, body: { score } };\n };\n}\n\nexport function handleHandshake(ctx: DvaiHandlerContext): DvaiHandler {\n return async (req) => {\n if (!ctx.pairingPolicy) {\n return {\n status: 503,\n body: { error: { type: \"pairing_disabled\", message: \"pairing not configured\" } },\n };\n }\n const body = req.body as Partial<IncomingHandshake> | undefined;\n if (!body?.peerDeviceId || !body?.peerDeviceName) {\n return {\n status: 400,\n body: { error: { type: \"malformed_handshake\", message: \"missing peerDeviceId / peerDeviceName\" } },\n };\n }\n try {\n const pairing = await ctx.pairingPolicy.approveOrFetch({\n peerDeviceId: body.peerDeviceId,\n peerDeviceName: body.peerDeviceName,\n via: body.via ?? \"lan-handshake\",\n ...(body.appId !== undefined ? { appId: body.appId } : {}),\n });\n // v3.1 wire protocol: echo the pairing key in the response so\n // the requester can HMAC-sign subsequent calls. LAN trust model\n // — the response only crosses the same Wi-Fi the handshake did,\n // and the protocol is opt-in (offload.enabled defaults to false).\n // The rendezvous-QR flow uses ECDH key agreement instead and\n // doesn't reach this handler.\n return {\n status: 200,\n body: {\n paired: true,\n pairedAt: pairing.pairedAt,\n via: pairing.via,\n pairingKey: pairing.pairingKey,\n peerDeviceId: pairing.peerDeviceId,\n },\n };\n } catch (err) {\n return {\n status: 403,\n body: { error: { type: \"pairing_denied\", message: String(err) } },\n };\n }\n };\n}\n\nexport function handlePairQr(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n // The actual rendezvous-WS dance lives in src/rendezvous/client.ts;\n // this endpoint is a thin shim that returns a 501 here in v3.0.0-rc1\n // because the in-DVAI integration (creating a session + holding it\n // open until the target scans) needs the per-platform glue from\n // Task 8 (per-SDK integration). The endpoint shape is locked in\n // for forward-compat; the implementation lights up in 8a–8f.\n return {\n status: 501,\n body: {\n error: {\n type: \"not_implemented_yet\",\n message:\n \"POST /v1/dvai/pair-qr requires per-SDK integration (Task 8); \" +\n \"the rendezvous client surface (rendezvous/client.ts) is callable \" +\n \"directly until then.\",\n },\n },\n };\n };\n}\n\nexport function handlePairScan(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n return {\n status: 501,\n body: {\n error: {\n type: \"not_implemented_yet\",\n message: \"POST /v1/dvai/pair-scan requires per-SDK integration (Task 8).\",\n },\n },\n };\n };\n}\n\n/**\n * Build the route → handler map. The transport layer (src/transports/)\n * dispatches incoming requests to these.\n */\nexport function buildDvaiRoutes(\n ctx: DvaiHandlerContext,\n): Record<string, DvaiHandler> {\n return {\n \"GET /v1/dvai/health\": handleHealth(ctx),\n \"GET /v1/dvai/capability\": handleCapability(ctx),\n \"GET /v1/dvai/peers\": handlePeers(ctx),\n \"POST /v1/dvai/probe\": handleProbe(ctx),\n \"POST /v1/dvai/handshake\": handleHandshake(ctx),\n \"POST /v1/dvai/pair-qr\": handlePairQr(ctx),\n \"POST /v1/dvai/pair-scan\": handlePairScan(ctx),\n };\n}\n","/**\n * NodeLlamaCppBackend: Wraps `node-llama-cpp` for local inference in Node.\n *\n * - Loads a GGUF model file from disk via `getLlama()` + `loadModel()`.\n * - Exposes the same duck-typed surface as TransformersBackend / WebLLMBackend\n * (`chatCompletion`, `createStreamingResponse`, `unload`) so the\n * transport-agnostic handlers in `handlers/` work without changes.\n * - Streams via the LlamaChatSession's `prompt({ onTextChunk })` callback,\n * re-shaped into OpenAI-style SSE chunks.\n * - Honors `generationTimeout` for both the blocking and streaming paths.\n *\n * Native peer dependency. `node-llama-cpp` ships its own prebuilt llama.cpp\n * binaries; consumers add it to their own `package.json`. We keep it as an\n * **optional** peer dep so the package still installs cleanly in browser-only\n * projects.\n */\n\nexport interface NodeLlamaCppBackendConfig {\n\t/** Absolute or relative path to a GGUF model file. Required. */\n\tmodelPath: string;\n\t/** Number of GPU layers to offload (Metal on macOS, CUDA on Linux/Win). Default: 99 (max). */\n\tgpuLayers?: number;\n\t/** Number of CPU threads. Default: undefined (let node-llama-cpp pick). */\n\tthreads?: number;\n\t/** Context window in tokens. Default: 2048. */\n\tcontextSize?: number;\n\t/** Generation timeout in ms. Default: 60000. */\n\tgenerationTimeout?: number;\n\t/** Logical model identifier echoed back in OpenAI responses. Default: basename of modelPath. */\n\tmodelId?: string;\n}\n\nexport class NodeLlamaCppBackend {\n\tprivate modelPath: string;\n\tprivate gpuLayers: number;\n\tprivate threads?: number;\n\tprivate contextSize: number;\n\tprivate generationTimeout: number;\n\tprivate modelId: string;\n\n\tprivate llama: any = null;\n\tprivate model: any = null;\n\tprivate context: any = null;\n\tprivate session: any = null;\n\n\t/** Mirrors WebLLMBackend; not used by this backend but keeps the duck-type stable. */\n\tpublic lastFatalError: string | null = null;\n\n\tconstructor(config: NodeLlamaCppBackendConfig) {\n\t\tif (!config.modelPath) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI/NodeLlamaCpp] modelPath is required (path to a GGUF file).\",\n\t\t\t);\n\t\t}\n\t\tthis.modelPath = config.modelPath;\n\t\tthis.gpuLayers = config.gpuLayers ?? 99;\n\t\tthis.threads = config.threads;\n\t\tthis.contextSize = config.contextSize ?? 2048;\n\t\tthis.generationTimeout = config.generationTimeout ?? 60000;\n\t\tthis.modelId =\n\t\t\tconfig.modelId ||\n\t\t\tthis.modelPath.split(/[\\\\/]/).pop()?.replace(/\\.gguf$/i, \"\") ||\n\t\t\t\"node-llama-cpp\";\n\t}\n\n\tclearFatalError(): void {\n\t\tthis.lastFatalError = null;\n\t}\n\n\tasync initialize(onProgress?: (info: any) => void): Promise<void> {\n\t\tlet llamaModule: any;\n\t\ttry {\n\t\t\tllamaModule = await import(\"node-llama-cpp\");\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t'[DVAI/NodeLlamaCpp] backend selected but \"node-llama-cpp\" is not installed.\\n' +\n\t\t\t\t\t\"Install it with: npm install node-llama-cpp\",\n\t\t\t);\n\t\t}\n\n\t\tonProgress?.({ text: \"Loading llama.cpp runtime...\", progress: 0 });\n\t\tthis.llama = await llamaModule.getLlama();\n\n\t\tonProgress?.({\n\t\t\ttext: `Loading model: ${this.modelId}`,\n\t\t\tprogress: 0.1,\n\t\t});\n\t\tthis.model = await this.llama.loadModel({\n\t\t\tmodelPath: this.modelPath,\n\t\t\tgpuLayers: this.gpuLayers,\n\t\t});\n\n\t\tonProgress?.({ text: \"Creating context...\", progress: 0.9 });\n\t\tthis.context = await this.model.createContext({\n\t\t\tcontextSize: this.contextSize,\n\t\t\tthreads: this.threads,\n\t\t});\n\n\t\tthis.session = new llamaModule.LlamaChatSession({\n\t\t\tcontextSequence: this.context.getSequence(),\n\t\t});\n\n\t\tonProgress?.({ text: \"Ready\", progress: 1 });\n\t\tconsole.log(\n\t\t\t`[DVAI/NodeLlamaCpp] Loaded \"${this.modelId}\" (gpuLayers=${this.gpuLayers}, contextSize=${this.contextSize})`,\n\t\t);\n\t}\n\n\tisWorkerBased(): boolean {\n\t\treturn false;\n\t}\n\n\tgetModelId(): string {\n\t\treturn this.modelId;\n\t}\n\n\tprivate withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst t = setTimeout(\n\t\t\t\t() => reject(new Error(`Generation timed out after ${ms}ms`)),\n\t\t\t\tms,\n\t\t\t);\n\t\t\tp.then(\n\t\t\t\t(v) => {\n\t\t\t\t\tclearTimeout(t);\n\t\t\t\t\tresolve(v);\n\t\t\t\t},\n\t\t\t\t(e) => {\n\t\t\t\t\tclearTimeout(t);\n\t\t\t\t\treject(e);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Build a single prompt string from an OpenAI-style messages array.\n\t * The session keeps its own chat history, so we collapse the *current*\n\t * user-turn into one prompt and rely on node-llama-cpp's templating\n\t * for everything else.\n\t */\n\tprivate extractUserPrompt(messages: any[]): {\n\t\tsystemPrompt?: string;\n\t\tuserPrompt: string;\n\t} {\n\t\tconst sys = messages.find((m: any) => m.role === \"system\");\n\t\tconst lastUser = [...messages].reverse().find((m: any) => m.role === \"user\");\n\t\tconst flatten = (c: any): string => {\n\t\t\tif (typeof c === \"string\") return c;\n\t\t\tif (Array.isArray(c)) return c.map(flatten).join(\"\");\n\t\t\tif (c && typeof c === \"object\")\n\t\t\t\treturn c.text || c.content || JSON.stringify(c);\n\t\t\treturn String(c || \"\");\n\t\t};\n\t\treturn {\n\t\t\tsystemPrompt: sys ? flatten(sys.content) : undefined,\n\t\t\tuserPrompt: lastUser ? flatten(lastUser.content) : \"\",\n\t\t};\n\t}\n\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.session) {\n\t\t\tthrow new Error(\"[DVAI/NodeLlamaCpp] Backend not initialized.\");\n\t\t}\n\t\tconst messages = requestBody.messages || [];\n\t\tconst { systemPrompt, userPrompt } = this.extractUserPrompt(messages);\n\n\t\tconst maxTokens =\n\t\t\trequestBody.max_tokens ?? requestBody.max_completion_tokens ?? 256;\n\t\tconst temperature = requestBody.temperature ?? 0.7;\n\t\tconst topP = requestBody.top_p ?? 1.0;\n\n\t\tconst promptOpts: any = {\n\t\t\tmaxTokens,\n\t\t\ttemperature,\n\t\t\ttopP,\n\t\t};\n\t\tif (systemPrompt) promptOpts.systemPrompt = systemPrompt;\n\n\t\tconst text: string = await this.withTimeout(\n\t\t\tthis.session.prompt(userPrompt, promptOpts),\n\t\t\tthis.generationTimeout,\n\t\t);\n\n\t\treturn {\n\t\t\tid: `chatcmpl-${Date.now()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel: this.modelId,\n\t\t\tchoices: [\n\t\t\t\t{\n\t\t\t\t\tindex: 0,\n\t\t\t\t\tmessage: { role: \"assistant\", content: text },\n\t\t\t\t\tfinish_reason: \"stop\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tusage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n\t\t};\n\t}\n\n\tcreateStreamingResponse(requestBody: any): ReadableStream<Uint8Array> {\n\t\tif (!this.session) {\n\t\t\tthrow new Error(\"[DVAI/NodeLlamaCpp] Backend not initialized.\");\n\t\t}\n\t\tconst session = this.session;\n\t\tconst modelId = this.modelId;\n\t\tconst generationTimeout = this.generationTimeout;\n\t\tconst messages = requestBody.messages || [];\n\t\tconst { systemPrompt, userPrompt } = this.extractUserPrompt(messages);\n\t\tconst maxTokens =\n\t\t\trequestBody.max_tokens ?? requestBody.max_completion_tokens ?? 256;\n\t\tconst temperature = requestBody.temperature ?? 0.7;\n\t\tconst topP = requestBody.top_p ?? 1.0;\n\n\t\treturn new ReadableStream<Uint8Array>({\n\t\t\tasync start(controller) {\n\t\t\t\tconst completionId = `chatcmpl-${Date.now()}`;\n\t\t\t\tconst created = Math.floor(Date.now() / 1000);\n\t\t\t\tconst encoder = new TextEncoder();\n\n\t\t\t\tconst enqueueChunk = (text: string) => {\n\t\t\t\t\tconst chunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{ index: 0, delta: { content: text }, finish_reason: null },\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tconst enqueueFinal = (finishReason = \"stop\") => {\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [{ index: 0, delta: {}, finish_reason: finishReason }],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst promptOpts: any = {\n\t\t\t\t\t\tmaxTokens,\n\t\t\t\t\t\ttemperature,\n\t\t\t\t\t\ttopP,\n\t\t\t\t\t\tonTextChunk: (text: string) => {\n\t\t\t\t\t\t\tif (text) enqueueChunk(text);\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tif (systemPrompt) promptOpts.systemPrompt = systemPrompt;\n\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\ttimeoutId = setTimeout(\n\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t`Generation timed out after ${generationTimeout}ms`,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tgenerationTimeout,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tsession.prompt(userPrompt, promptOpts).then(\n\t\t\t\t\t\t\t() => resolve(),\n\t\t\t\t\t\t\t(e: any) => reject(e),\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tenqueueFinal(\"stop\");\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"[DVAI/NodeLlamaCpp] Stream error:\",\n\t\t\t\t\t\terror?.message ?? error,\n\t\t\t\t\t);\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(\n\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: error?.message ?? String(error) })}\\n\\n`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync unload(): Promise<void> {\n\t\ttry {\n\t\t\tif (this.session && typeof this.session.dispose === \"function\") {\n\t\t\t\tawait this.session.dispose();\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t/* best effort */\n\t\t}\n\t\ttry {\n\t\t\tif (this.context && typeof this.context.dispose === \"function\") {\n\t\t\t\tawait this.context.dispose();\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t/* best effort */\n\t\t}\n\t\ttry {\n\t\t\tif (this.model && typeof this.model.dispose === \"function\") {\n\t\t\t\tawait this.model.dispose();\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t/* best effort */\n\t\t}\n\t\tthis.session = null;\n\t\tthis.context = null;\n\t\tthis.model = null;\n\t\tthis.llama = null;\n\t}\n}\n","/**\n * WebLLMBackend: Wraps @mlc-ai/web-llm with robustness improvements.\n * - Runs inference in a Web Worker to keep main thread unblocked\n * - Falls back to main-thread engine if worker URL is not available\n * - Blank-chunk detection to abort runaway streams\n * - Generation timeout to prevent infinite loops\n * - finish_reason checks to properly terminate streams\n * - Engine state cleanup after failures\n */\n\nexport interface WebLLMBackendConfig {\n\tmodelId: string;\n\tgenerationTimeout: number;\n\tmaxBlankChunks: number;\n\tworkerUrl?: string;\n\tonProgress?: (info: any) => void;\n}\n\nexport class WebLLMBackend {\n\tprivate engine: any = null; // MLCEngine | WebWorkerMLCEngine\n\tprivate modelId: string;\n\tprivate generationTimeout: number;\n\tprivate maxBlankChunks: number;\n\tprivate workerUrl?: string;\n\tprivate usingWorker: boolean = false;\n\n\t/**\n\t * Set to a non-null string when a fatal error occurs (blank output, timeout)\n\t * that requires a full unload → reload cycle at the orchestrator level.\n\t */\n\tpublic lastFatalError: string | null = null;\n\n\tconstructor(config: WebLLMBackendConfig) {\n\t\tthis.modelId = config.modelId;\n\t\tthis.generationTimeout = config.generationTimeout;\n\t\tthis.maxBlankChunks = config.maxBlankChunks;\n\t\tthis.workerUrl = config.workerUrl;\n\t}\n\n\t/** Clear the fatal error flag after a successful recovery. */\n\tclearFatalError(): void {\n\t\tthis.lastFatalError = null;\n\t}\n\n\tasync initialize(onProgress?: (info: any) => void): Promise<void> {\n\t\tconst webllm = await import(\"@mlc-ai/web-llm\");\n\n\t\t// Try worker-based engine first (fully offloads inference from main thread)\n\t\tif (this.workerUrl && typeof Worker !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tconst worker = new Worker(this.workerUrl, { type: \"module\" });\n\t\t\t\tthis.engine = new webllm.WebWorkerMLCEngine(worker, {\n\t\t\t\t\tinitProgressCallback: onProgress,\n\t\t\t\t});\n\t\t\t\tawait this.engine.reload(this.modelId);\n\t\t\t\tthis.usingWorker = true;\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[DVAI/WebLLM] Initialized with Web Worker (main thread unblocked)\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[DVAI/WebLLM] Worker initialization failed, falling back to main thread:\",\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: main-thread engine (WebGPU compute is still async/non-blocking)\n\t\tthis.engine = await webllm.CreateMLCEngine(this.modelId, {\n\t\t\tinitProgressCallback: onProgress,\n\t\t});\n\t\tthis.usingWorker = false;\n\t\tconsole.log(\n\t\t\t\"[DVAI/WebLLM] Initialized on main thread (WebGPU compute is async)\",\n\t\t);\n\t}\n\n\tisWorkerBased(): boolean {\n\t\treturn this.usingWorker;\n\t}\n\n\tgetEngine(): any {\n\t\treturn this.engine;\n\t}\n\n\t/**\n\t * Non-streaming chat completion with timeout protection.\n\t */\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.engine) throw new Error(\"WebLLM engine not initialized\");\n\n\t\tconst result: any = await this.withTimeout(\n\t\t\tthis.engine.chat.completions.create({\n\t\t\t\t...requestBody,\n\t\t\t\tstream: false,\n\t\t\t}),\n\t\t\tthis.generationTimeout,\n\t\t);\n\n\t\t// Validate response has actual content\n\t\tconst content = result?.choices?.[0]?.message?.content;\n\t\tif (content === undefined || content === null || content === \"\") {\n\t\t\tconsole.warn(\n\t\t\t\t\"[DVAI/WebLLM] Warning: Engine returned blank content — flagging for full restart.\",\n\t\t\t);\n\t\t\tthis.lastFatalError = \"blank_output\";\n\t\t\ttry {\n\t\t\t\tawait this.engine.resetChat();\n\t\t\t} catch (_) {\n\t\t\t\t/* best effort */\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t\"WebLLM engine returned blank content. A full engine restart is required.\",\n\t\t\t);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Streaming chat completion with blank-chunk detection, timeout, and finish_reason termination.\n\t * Returns a ReadableStream of SSE-formatted data.\n\t */\n\tcreateStreamingResponse(requestBody: any): ReadableStream<Uint8Array> {\n\t\tconst engine = this.engine;\n\t\tif (!engine) throw new Error(\"WebLLM engine not initialized\");\n\t\tconst maxBlankChunks = this.maxBlankChunks;\n\t\tconst generationTimeout = this.generationTimeout;\n\t\t// Capture class reference for use inside ReadableStream closure\n\t\tconst backend = this;\n\n\t\treturn new ReadableStream<Uint8Array>({\n\t\t\tasync start(controller) {\n\t\t\t\tlet consecutiveBlanks = 0;\n\t\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\t\t\t\ttry {\n\t\t\t\t\tconst asyncChunkGenerator = (await engine.chat.completions.create({\n\t\t\t\t\t\t...requestBody,\n\t\t\t\t\t\tstream: true,\n\t\t\t\t\t})) as unknown as AsyncIterable<any>;\n\n\t\t\t\t\t// Set overall generation timeout\n\t\t\t\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(`Generation timed out after ${generationTimeout}ms`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}, generationTimeout);\n\t\t\t\t\t});\n\n\t\t\t\t\tconst streamPromise = (async () => {\n\t\t\t\t\t\tfor await (const chunk of asyncChunkGenerator) {\n\t\t\t\t\t\t\tconst delta = chunk?.choices?.[0]?.delta;\n\t\t\t\t\t\t\tconst finishReason = chunk?.choices?.[0]?.finish_reason;\n\n\t\t\t\t\t\t\t// Check for blank chunks\n\t\t\t\t\t\t\tif (!delta?.content && delta?.content !== undefined) {\n\t\t\t\t\t\t\t\tconsecutiveBlanks++;\n\t\t\t\t\t\t\t\tif (consecutiveBlanks >= maxBlankChunks) {\n\t\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t\t`[DVAI/WebLLM] ${maxBlankChunks} consecutive blank chunks detected — flagging for full restart.`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tbackend.lastFatalError = \"blank_stream\";\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tengine.interruptGenerate();\n\t\t\t\t\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tawait engine.resetChat();\n\t\t\t\t\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: \"Stream aborted: too many blank chunks. Engine restart required.\" })}\\n\\n`,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconsecutiveBlanks = 0;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Emit the chunk\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tnew TextEncoder().encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// Check if generation is complete\n\t\t\t\t\t\t\tif (finishReason === \"stop\" || finishReason === \"length\") {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\n\t\t\t\t\t// Race: stream vs timeout\n\t\t\t\t\tawait Promise.race([streamPromise, timeoutPromise]);\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tconsole.error(\"[DVAI/WebLLM] Stream error:\", error.message);\n\t\t\t\t\t// Flag timeout errors for full restart\n\t\t\t\t\tif (error.message?.includes(\"timed out\")) {\n\t\t\t\t\t\tbackend.lastFatalError = \"timeout\";\n\t\t\t\t\t}\n\t\t\t\t\t// Try to interrupt and reset on failure\n\t\t\t\t\ttry {\n\t\t\t\t\t\tengine.interruptGenerate();\n\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait engine.resetChat();\n\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t}\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: error.message })}\\n\\n`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\t\tcontroller.enqueue(new TextEncoder().encode(\"data: [DONE]\\n\\n\"));\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync unload(): Promise<void> {\n\t\tif (this.engine) {\n\t\t\tawait this.engine.unload();\n\t\t\tthis.engine = null;\n\t\t}\n\t}\n\n\t/** Wraps a promise with a timeout. */\n\tprivate withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() => reject(new Error(`Generation timed out after ${ms}ms`)),\n\t\t\t\tms,\n\t\t\t);\n\t\t\tpromise\n\t\t\t\t.then((val) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tresolve(val);\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(err);\n\t\t\t\t});\n\t\t});\n\t}\n}\n","import { LicenseValidator } from \"./license/index.js\";\nimport type { LicenseStatus } from \"./license/index.js\";\nimport { type HandlerContext } from \"./handlers/index.js\";\n\n// Re-export types from backends\nexport type { TransformersProgressInfo } from \"./TransformersBackend.js\";\nexport { detectWebGPU } from \"./TransformersBackend.js\";\n\n// Re-export InitProgressReport from web-llm for backward compatibility\nexport type { InitProgressReport } from \"@mlc-ai/web-llm\";\n\n// Re-export legacy helpers from the handlers module for backward compat.\n// Existing tests/consumers import these from \"@dvai-bridge/core\".\nexport {\n\tchatToLegacyCompletion,\n\tlegacyCompletionStreamAdapter,\n} from \"./handlers/completions.js\";\n\n// v3.1 — re-export HMAC primitives so consumers (the Hub, native SDKs,\n// rendezvous clients) can compose + verify signed messages without\n// reaching into deep package paths.\nexport {\n\tcomposeSignedMessage,\n\tverifyHmac,\n\tsignHmac,\n\tgenerateNonce,\n\tgeneratePairingKey,\n} from \"./pairing/handshake.js\";\n\n// v4.0 — re-export the license module so host apps (dashboards, dev\n// tools, license-aware UI shells) can run validation independently of\n// the heavy DVAI.initialize() boot sequence. Useful for things like\n// \"show a license-status pill in the title bar\" without paying the\n// model-load cost of a full init.\nexport {\n\tLicenseValidator,\n\tLicenseRequiredError,\n\tisPaidTier,\n\tDEFAULT_LICENSE_FILENAME,\n\tDVAI_PUBLIC_KEYS,\n\tPLACEHOLDER_KID,\n} from \"./license/index.js\";\nexport type {\n\tLicenseStatus,\n\tLicenseTier,\n\tLicenseValidatorOptions,\n\tLicenseDiscoveryOptions,\n\tDvaiLicensePayload,\n\tDvaiPlatform,\n\tDvaiPublicKeyJwk,\n} from \"./license/index.js\";\n\nexport type BackendType = \"webllm\" | \"transformers\" | \"native\" | \"auto\";\nexport type DeviceType = \"webgpu\" | \"cpu\" | \"auto\";\nexport type {\n\tPipelineTask,\n\tCreatePipelineFn,\n\tPipelineCallable,\n} from \"./TransformersBackend.js\";\n\nexport interface DVAIConfig {\n\t/** The model ID for web-llm backend. Default: \"gemma-2-2b-it-q4f16_1-MLC\" */\n\tmodelId?: string;\n\t/**\n\t * The backend engine to use. Default: \"webllm\". Set to \"auto\" to auto-detect.\n\t * - \"webllm\" → @mlc-ai/web-llm (browser, WebGPU)\n\t * - \"transformers\" → @huggingface/transformers (browser or Node)\n\t * - \"native\" → node-llama-cpp (Node only; loads a GGUF file)\n\t * - \"auto\" → resolved at runtime\n\t */\n\tbackend?: BackendType;\n\t/** HuggingFace model ID for Transformers.js backend. Default: \"onnx-community/gemma-3n-E2B-it-ONNX\" */\n\ttransformersModelId?: string;\n\t/** Pipeline task for Transformers.js (e.g. \"text-generation\", \"text-to-image\", \"automatic-speech-recognition\"). Default: \"text-generation\" */\n\tpipelineTask?: string;\n\t/** Device for Transformers.js - \"webgpu\", \"cpu\", or \"auto\" (detect). Default: \"auto\" */\n\tdevice?: DeviceType;\n\t/** Quantization for Transformers.js (e.g. \"q4\", \"q8\", \"f16\"). Default: undefined */\n\tdtype?: string;\n\t/** Generation timeout in ms. Default: 60000 (60s) */\n\tgenerationTimeout?: number;\n\t/** Maximum consecutive blank chunks before aborting stream (WebLLM). Default: 20 */\n\tmaxBlankChunks?: number;\n\t/** Maximum auto-recovery retries on fatal WebLLM errors (blank output/timeout). Default: 2 */\n\tmaxRetries?: number;\n\t/** Mock URL for MSW interception. Default: \"https://api.openai.local/v1/chat/completions\" */\n\tmockUrl?: string;\n\t/** Path to the MSW service worker script. Default: \"/mockServiceWorker.js\" */\n\tserviceWorkerUrl?: string;\n\t/** URL to the WebLLM worker script (for offloading inference). Default: \"/dvai-webllm.worker.js\" */\n\twebllmWorkerUrl?: string;\n\t/** URL to the Transformers.js worker script (for offloading inference). Default: \"/dvai-transformers.worker.js\" */\n\ttransformersWorkerUrl?: string;\n\t/**\n\t * Custom pipeline factory for Transformers.js backend.\n\t * MAIN-THREAD ONLY — function closures don't cross the Worker boundary.\n\t * When provided, replaces the default pipeline() call with your own\n\t * model loading and inference logic. Must return a callable that accepts\n\t * (messages, options) and returns [{ generated_text: string }].\n\t *\n\t * For multimodal models that should run in the worker (recommended),\n\t * use the declarative `transformersModelClass` / `transformersProcessorClass`\n\t * / `transformersDisableEncoders` config instead.\n\t */\n\tcreatePipeline?: import(\"./TransformersBackend.js\").CreatePipelineFn;\n\t/**\n\t * Name of a transformers.js export to use as the model class (loaded via\n\t * `ClassName.from_pretrained(modelId)`). Enables the declarative\n\t * multimodal loader — works in the worker AND on main thread so the\n\t * same config ships correctly regardless of path.\n\t *\n\t * Examples: \"Gemma4ForConditionalGeneration\", \"LlavaForConditionalGeneration\",\n\t * \"AutoModelForCausalLM\". Leave unset to use the generic `pipeline()` factory.\n\t */\n\ttransformersModelClass?: string;\n\t/**\n\t * Processor class name for the declarative loader. Default: \"AutoProcessor\".\n\t * Only used when `transformersModelClass` is set.\n\t */\n\ttransformersProcessorClass?: string;\n\t/**\n\t * Model submodule fields to null out after load, e.g. `[\"vision_encoder\"]`\n\t * for a voice-only host app using a multimodal checkpoint. Purely\n\t * declarative — dvai-bridge walks the list and nulls each named field\n\t * if present; unknown/absent names are silently ignored. Host apps\n\t * control this based on their own criteria.\n\t */\n\ttransformersDisableEncoders?: string[];\n\n\t// --- Capacitor native runtime options (forwarded by CapacitorTransport) ---\n\t/** Path to the GGUF model file for the Capacitor llama backend. */\n\tnativeModelPath?: string;\n\t/** Number of GPU layers for the Capacitor llama backend (iOS Metal). Default: 99 (max) */\n\tnativeGpuLayers?: number;\n\t/** Number of CPU threads for the Capacitor llama backend. Default: 4 */\n\tnativeThreads?: number;\n\t/** Context window size for the Capacitor llama backend. Default: 2048 */\n\tnativeContextSize?: number;\n\t/**\n\t * Initialize the Capacitor llama context in embedding mode. Required for\n\t * `/v1/embeddings` to work natively. When true, the context should be a\n\t * dedicated embedding model and will typically not be usable for\n\t * chat/completion. Default: false.\n\t */\n\tnativeEmbeddingMode?: boolean;\n\n\t/**\n\t * Path (or fetchable URL) to your DVAI-Bridge license JWT file.\n\t *\n\t * Default behaviour when this is unset: the SDK looks for\n\t * `dvai-license.jwt` at platform-conventional locations:\n\t * - Node: `process.cwd()/dvai-license.jwt` (and one level up for\n\t * monorepos)\n\t * - Browser / Capacitor: same-origin `/dvai-license.jwt`\n\t *\n\t * Override mechanisms in priority order:\n\t * 1. `licenseToken` (below) — inline JWT string, highest priority\n\t * 2. `licenseKeyPath` (this field) — explicit path or URL\n\t * 3. `DVAI_LICENSE_PATH` env var\n\t * 4. `DVAI_LICENSE_TOKEN` env var — inline JWT\n\t * 5. Auto-discovery\n\t *\n\t * If no license is found OR validation fails, the SDK falls back to\n\t * the free tier (with the \"Powered by DVAI Bridge\" attribution\n\t * badge in browser/Capacitor contexts). The SDK never refuses to\n\t * start because of a license problem — license issues surface as a\n\t * `licenseStatus` value with `kind: \"free-prod\"` and a\n\t * human-readable `reason`.\n\t */\n\tlicenseKeyPath?: string;\n\n\t/**\n\t * Inline DVAI-Bridge license JWT (the full token string). Use this\n\t * when you'd rather inject the license via env var / config than\n\t * ship a file — typical in serverless / containerised deployments\n\t * where filesystem state is awkward.\n\t *\n\t * If both `licenseToken` and `licenseKeyPath` are set, `licenseToken`\n\t * wins.\n\t */\n\tlicenseToken?: string;\n\t/** Auto-initialize on creation (React/Vanilla). Default: true */\n\tautoInit?: boolean;\n\n\t/**\n\t * Phase 3 (v3.0+) — distributed inference / device offload.\n\t *\n\t * If unset OR `enabled: false`, the library behaves exactly like\n\t * v2.x: every request runs locally. When enabled, the library\n\t * discovers peer devices on the LAN (via mDNS) and / or via a\n\t * self-hosted rendezvous server (if `rendezvousUrl` is set), and\n\t * routes inference requests to the most-capable peer when local\n\t * tok/s falls below `minLocalCapability`.\n\t *\n\t * See `docs/guide/distributed-inference.md` for the full design,\n\t * `docs/guide/self-hosting-rendezvous.md` for the rendezvous\n\t * server self-hosting flow, and `src/offload/types.ts` for the\n\t * full `OffloadConfig` shape.\n\t */\n\toffload?: import(\"./offload/index.js\").OffloadConfig;\n\n\t/**\n\t * Which transport to use for the OpenAI-compatible surface.\n\t * - \"auto\" (default) → capacitor on Capacitor, msw in browser,\n\t * http in Node, none in workers\n\t * - \"msw\" → force MSW (browser only; errors elsewhere)\n\t * - \"http\" → force HTTP server (Node only; errors elsewhere)\n\t * - \"capacitor\" → force native Capacitor HTTP server (requires\n\t * @dvai-bridge/capacitor + a Capacitor backend plugin)\n\t * - \"none\" → no transport; use dvai.chatCompletion() directly\n\t */\n\ttransport?: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\";\n\n\t/** HTTP-only. Base port. Default: 38883. */\n\thttpBasePort?: number;\n\n\t/** HTTP-only. Max port-fallback attempts. Default: 16. */\n\thttpMaxPortAttempts?: number;\n\n\t/**\n\t * HTTP-only. Network interface to bind. Default `127.0.0.1`\n\t * (loopback only). Set to `0.0.0.0` for LAN-target deployments\n\t * (the v3.1 Hub, native SDKs running in target mode) so peers on\n\t * the same Wi-Fi can reach the embedded server. Phone-as-source /\n\t * single-device deployments should leave this default — a\n\t * 0.0.0.0 bind on a developer laptop without pairing protection\n\t * exposes the OpenAI surface.\n\t */\n\thttpBindHost?: string;\n\n\t/**\n\t * Phase 4 — first-chance interceptor for /v1/chat/completions. The\n\t * v3.1 Hub uses this to apply substitution-policy + engine-bridge\n\t * routing before falling through to the default local-backend\n\t * handler. Return a Response → that's what the client gets;\n\t * return null → fall through to the local backend.\n\t *\n\t * Receives request headers (lower-cased keys) so the interceptor\n\t * can read the v3.1 identity fields (X-DVAI-Peer-Device-Id,\n\t * X-DVAI-App-Id, X-DVAI-Nonce, X-DVAI-Signature) for HMAC verify.\n\t */\n\tchatCompletionInterceptor?: (\n\t\tbody: any,\n\t\tctx: import(\"./handlers/context.js\").HandlerContext,\n\t\theaders?: Record<string, string>,\n\t) => Promise<Response | null>;\n\n\t/**\n\t * HTTP-only. Controls the Access-Control-Allow-Origin response header.\n\t * - \"*\" → echo \"*\" (default; dev-friendly)\n\t * - \"https://x.com\" → echo that exact origin\n\t * - [\"a.com\",\"b.com\"] → match the request's Origin header against the\n\t * list; echo the matched value. Requests from\n\t * unlisted origins get ACAO omitted.\n\t */\n\tcorsOrigin?: string | string[];\n\n\t/**\n\t * Capacitor-backend selection (when transport resolves to \"capacitor\").\n\t * Default: \"llama\".\n\t */\n\tcapacitorBackend?: \"llama\" | \"foundation\" | \"mediapipe\";\n\n\t/**\n\t * Path to the mmproj (vision projector) file when using a multimodal\n\t * llama.cpp model. Optional; only required for vision-capable models.\n\t */\n\tnativeMmprojPath?: string;\n}\n\n/**\n * DVAI: Local AI Orchestration\n * Orchestrates WebLLM or Transformers.js for local inference and selects\n * an MSW, HTTP, or Capacitor transport (auto-detected from environment)\n * to expose the OpenAI-compatible endpoint. On Capacitor, the native\n * runtime runs in a first-party plugin behind the \"capacitor\" transport.\n * Read `dvai.baseUrl` after initialize() to get the URL to point any\n * OpenAI SDK at.\n */\nexport class DVAI {\n\tpublic modelId: string;\n\tpublic mockUrl: string;\n\tpublic serviceWorkerUrl: string;\n\tpublic licenseKeyPath?: string;\n\tpublic licenseToken?: string;\n\t/**\n\t * Result of the most recent license validation. Populated by\n\t * `initialize()`; consult before promoting paid-tier UI affordances\n\t * (e.g. hiding the attribution badge). Null before initialization.\n\t */\n\tpublic licenseStatus: LicenseStatus | null = null;\n\tpublic backend: BackendType;\n\tpublic transformersModelId: string;\n\tpublic pipelineTask: string;\n\tpublic device: DeviceType;\n\tpublic generationTimeout: number;\n\tpublic maxBlankChunks: number;\n\tpublic maxRetries: number;\n\tpublic webllmWorkerUrl: string;\n\tpublic transformersWorkerUrl: string;\n\tpublic dtype?: string;\n\tpublic createPipeline?: import(\"./TransformersBackend.js\").CreatePipelineFn;\n\tpublic transformersModelClass?: string;\n\tpublic transformersProcessorClass?: string;\n\tpublic transformersDisableEncoders?: string[];\n\n\t// Native backend options\n\tpublic nativeModelPath: string;\n\tpublic nativeGpuLayers: number;\n\tpublic nativeThreads: number;\n\tpublic nativeContextSize: number;\n\tpublic nativeEmbeddingMode: boolean;\n\tpublic capacitorBackend: \"llama\" | \"foundation\" | \"mediapipe\";\n\tpublic nativeMmprojPath?: string;\n\n\t/** Raw transport config (e.g., \"auto\"). */\n\tpublic transport: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\";\n\tpublic httpBasePort: number;\n\tpublic httpMaxPortAttempts: number;\n\tpublic corsOrigin: string | string[];\n\tpublic httpBindHost: string | undefined;\n\tpublic chatCompletionInterceptor:\n\t\t| ((\n\t\t\t\tbody: any,\n\t\t\t\tctx: import(\"./handlers/context.js\").HandlerContext,\n\t\t\t\theaders?: Record<string, string>,\n\t\t\t) => Promise<Response | null>)\n\t\t| undefined;\n\n\t/** Resolved transport kind after selectTransport() runs. */\n\tprivate resolvedTransport: \"msw\" | \"http\" | \"none\" | \"capacitor\" = \"none\";\n\n\t/** Populated after transport.start(). Undefined on \"none\". */\n\tpublic baseUrl?: string;\n\tpublic port?: number;\n\n\t/** Active transport instance; null before initialize() / after unload(). */\n\tprivate activeTransport: import(\"./transports/index.js\").Transport | null =\n\t\tnull;\n\n\tprivate validator: LicenseValidator;\n\tprivate backendInstance: any = null; // WebLLMBackend | TransformersBackend\n\tpublic isReady: boolean = false;\n\t/** Tracks how many consecutive recovery attempts have been made. */\n\tprivate recoveryAttempts: number = 0;\n\n\t/** The resolved backend type (after \"auto\" resolution). */\n\tprivate resolvedBackend: \"webllm\" | \"transformers\" | \"native\" = \"webllm\";\n\n\t/* ----- Phase 3 (v3.0+) — distributed-inference state ----- */\n\n\t/** OffloadConfig as supplied by the consumer (or undefined). */\n\tpublic offload?: import(\"./offload/index.js\").OffloadConfig;\n\t/**\n\t * v3.2 — set true when the pre-init capability gate decides this\n\t * device is below `OffloadConfig.minLocalCapability`. In this mode\n\t * `initialize()` skips backend init entirely (no model download /\n\t * load) and only brings up discovery + pairing. Every request is\n\t * expected to be forwarded to a paired peer; without one, requests\n\t * 503.\n\t */\n\tpublic offloadOnlyMode: boolean = false;\n\t/** Capability cache (persistent storage of probe scores). */\n\tprivate capabilityCache?: import(\"./capability/index.js\").CapabilityCache;\n\t/** Phase 3 — built when offload.enabled; mounted on the HTTP transport via the handler context. */\n\tprivate dvaiRoutes?: Record<string, import(\"./handlers/dvai/index.js\").DvaiHandler>;\n\t/** Used by the dvai/health endpoint to report uptime. */\n\tprivate startedAt: number = Date.now();\n\t/** Discovery layer — composite of LAN mDNS + static + custom. */\n\tprivate discovery?: import(\"./discovery/index.js\").IDiscovery;\n\t/** Pairing policy (LAN-handshake auth + persistent store). */\n\tprivate pairingPolicy?: import(\"./pairing/index.js\").PairingPolicy;\n\t/** Stable per-install device ID (cached after first call). */\n\tprivate deviceId?: string;\n\n\tconstructor(config: DVAIConfig = {}) {\n\t\tthis.modelId = config.modelId || \"gemma-2-2b-it-q4f16_1-MLC\";\n\t\tthis.backend = config.backend || \"webllm\";\n\t\tthis.transformersModelId =\n\t\t\tconfig.transformersModelId ||\n\t\t\tconfig.modelId ||\n\t\t\t\"onnx-community/gemma-3n-E2B-it-ONNX\";\n\t\tthis.pipelineTask = config.pipelineTask || \"text-generation\";\n\t\tthis.device = config.device || \"auto\";\n\t\tthis.dtype = config.dtype;\n\t\tthis.createPipeline = config.createPipeline;\n\t\tthis.transformersModelClass = config.transformersModelClass;\n\t\tthis.transformersProcessorClass = config.transformersProcessorClass;\n\t\tthis.transformersDisableEncoders = config.transformersDisableEncoders;\n\t\tthis.generationTimeout = config.generationTimeout ?? 60000;\n\t\tthis.maxBlankChunks = config.maxBlankChunks ?? 20;\n\t\tthis.maxRetries = config.maxRetries ?? 2;\n\t\tthis.webllmWorkerUrl = config.webllmWorkerUrl ?? \"/dvai-webllm.worker.js\";\n\t\tthis.transformersWorkerUrl =\n\t\t\tconfig.transformersWorkerUrl ?? \"/dvai-transformers.worker.js\";\n\t\tthis.mockUrl =\n\t\t\tconfig.mockUrl ?? \"https://api.openai.local/v1/chat/completions\";\n\t\tthis.serviceWorkerUrl = config.serviceWorkerUrl ?? \"/mockServiceWorker.js\";\n\t\tthis.licenseKeyPath = config.licenseKeyPath;\n\t\tthis.licenseToken = config.licenseToken;\n\t\tthis.validator = new LicenseValidator({\n\t\t\t...(this.licenseKeyPath !== undefined ? { path: this.licenseKeyPath } : {}),\n\t\t\t...(this.licenseToken !== undefined ? { token: this.licenseToken } : {}),\n\t\t});\n\n\t\t// Native backend options\n\t\tthis.nativeModelPath = config.nativeModelPath || \"\";\n\t\tthis.nativeGpuLayers = config.nativeGpuLayers ?? 99;\n\t\tthis.nativeThreads = config.nativeThreads ?? 4;\n\t\tthis.nativeContextSize = config.nativeContextSize ?? 2048;\n\t\tthis.nativeEmbeddingMode = config.nativeEmbeddingMode ?? false;\n\t\tthis.capacitorBackend = config.capacitorBackend ?? \"llama\";\n\t\tthis.nativeMmprojPath = config.nativeMmprojPath;\n\n\t\t// Transport options\n\t\tthis.transport = config.transport ?? \"auto\";\n\t\tthis.httpBasePort = config.httpBasePort ?? 38883;\n\t\tthis.httpMaxPortAttempts = config.httpMaxPortAttempts ?? 16;\n\t\tthis.corsOrigin = config.corsOrigin ?? \"*\";\n\t\tthis.httpBindHost = config.httpBindHost;\n\t\tthis.chatCompletionInterceptor = config.chatCompletionInterceptor;\n\n\t\t// Resolve explicit backends immediately so getActiveBackend() is correct\n\t\t// before initialize(). \"auto\" defers to initialize() for runtime env detection.\n\t\tif (this.backend !== \"auto\") {\n\t\t\tthis.resolvedBackend = this.backend as \"webllm\" | \"transformers\" | \"native\";\n\t\t}\n\n\t\t// Phase 3 — capture offload config (lifecycle wiring lights up\n\t\t// in initialize() so we don't pay the cost on cold-construct).\n\t\tthis.offload = config.offload;\n\t}\n\n\t/**\n\t * Returns the active backend type (resolved from \"auto\" if applicable).\n\t */\n\tgetActiveBackend(): \"webllm\" | \"transformers\" | \"native\" {\n\t\treturn this.resolvedBackend;\n\t}\n\n\t/** Returns the resolved transport kind (after \"auto\" resolution). */\n\tgetActiveTransport(): \"msw\" | \"http\" | \"none\" | \"capacitor\" {\n\t\treturn this.resolvedTransport;\n\t}\n\n\t/** Returns the base URL a host app hands to an OpenAI SDK. */\n\tgetBaseUrl(): string | undefined {\n\t\treturn this.baseUrl;\n\t}\n\n\t/** Returns the HTTP port bound (http transport only). */\n\tgetPort(): number | undefined {\n\t\treturn this.port;\n\t}\n\n\t/**\n\t * Resolves the \"auto\" backend to a concrete type based on environment.\n\t *\n\t * On Capacitor, the native runtime is selected via `transport: \"capacitor\"`\n\t * (which delegates to a native HTTP server in the Capacitor plugin), not\n\t * via the backend. The backend stays in the webview as a thin client.\n\t */\n\tprivate resolveBackend(): \"webllm\" | \"transformers\" | \"native\" {\n\t\tif (this.backend === \"auto\") {\n\t\t\tconsole.log(\n\t\t\t\t\"[DVAI] Auto-detected web environment → using webllm backend\",\n\t\t\t);\n\t\t\treturn \"webllm\";\n\t\t}\n\t\treturn this.backend as \"webllm\" | \"transformers\" | \"native\";\n\t}\n\n\t/**\n\t * Initializes the selected backend engine and starts the resolved\n\t * transport (MSW in browsers, HTTP server in Node, or none).\n\t * @param onProgress - Callback for model download progress\n\t */\n\tasync initialize(\n\t\tonProgress: (info: any) => void = console.log,\n\t): Promise<boolean> {\n\t\tif (this.isReady) return true;\n\n\t\t// Resolve \"auto\" backend\n\t\tthis.resolvedBackend = this.resolveBackend();\n\n\t\t// 0. Validate license (offline JWT verification). THROWS\n\t\t// `LicenseRequiredError` in production / release contexts\n\t\t// when no valid commercial/trial license is found — that's\n\t\t// the BSL 1.1 enforcement point. Dev-mode environments\n\t\t// (localhost, NODE_ENV=test, DVAI_FORCE_DEV=1, etc.) bypass\n\t\t// the assert and return a `free-dev` status so developers\n\t\t// can iterate without a key. Surface the resolved status\n\t\t// through `this.licenseStatus` for host-app dashboards.\n\t\tthis.licenseStatus = await this.validator.validateAndAssert();\n\n\t\t// Detect Web Worker context — MSW (service workers) are unavailable inside Web Workers.\n\t\tconst isWorkerContext =\n\t\t\ttypeof window === \"undefined\" &&\n\t\t\ttypeof self !== \"undefined\" &&\n\t\t\ttypeof (self as any).importScripts === \"function\";\n\n\t\t// 0.1 Verify Service Worker Reachability (Quality of Life) — skip when\n\t\t// inside a Worker context, and skip when serviceWorkerUrl is empty (MSW disabled).\n\t\tif (!isWorkerContext && this.serviceWorkerUrl) {\n\t\t\ttry {\n\t\t\t\tconst swRes = await fetch(this.serviceWorkerUrl, { method: \"HEAD\" });\n\t\t\t\tif (!swRes.ok) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[DVAI] Warning: Service Worker not found at \"${this.serviceWorkerUrl}\". ` +\n\t\t\t\t\t\t\t`Please run \"dvai-bridge init\" or \"npx msw init <public_dir>\" to generate it.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[DVAI] Could not verify Service Worker existence at \"${this.serviceWorkerUrl}\".`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// 0.5 v3.2 — pre-init capability gate. Runs the heuristic\n\t\t\t// (CPU/GPU/RAM hints, no model required) and decides\n\t\t\t// internally whether to load a model locally:\n\t\t\t//\n\t\t\t// - too-weak → enter offload-only mode silently\n\t\t\t// (no model download/load). The SDK\n\t\t\t// does NOT throw and does NOT show any\n\t\t\t// UI — consumers query\n\t\t\t// `dvai.assessHardware()` ahead of\n\t\t\t// initialize() if they want to refuse\n\t\t\t// to start on too-weak devices.\n\t\t\t// - offload-only → same internal treatment: skip backend\n\t\t\t// init; bring up only transport + offload.\n\t\t\t// - ok → proceed to full initialization.\n\t\t\t//\n\t\t\t// Both too-weak and offload-only collapse to \"skip backend\n\t\t\t// init\" from the SDK's perspective; the *informational*\n\t\t\t// distinction lives in assessHardware()'s return value for\n\t\t\t// consumers to react to.\n\t\t\t//\n\t\t\t// Skipped entirely when offload is not configured or when\n\t\t\t// `offload.enabled` is false — there's no point gating a\n\t\t\t// device that has no offload path.\n\t\t\tif (this.offload?.enabled) {\n\t\t\t\tconst { assessCapability } =\n\t\t\t\t\tawait import(\"./capability/precheck.js\");\n\t\t\t\tconst result = await assessCapability({\n\t\t\t\t\thardwareMinimum: this.offload.hardwareMinimum,\n\t\t\t\t\tminLocalCapability: this.offload.minLocalCapability,\n\t\t\t\t});\n\t\t\t\tthis.offloadOnlyMode =\n\t\t\t\t\tresult.mode === \"offload-only\" || result.mode === \"too-weak\";\n\t\t\t\tonProgress({\n\t\t\t\t\tphase: \"precheck\",\n\t\t\t\t\tmode: result.mode,\n\t\t\t\t\ttokPerSec: result.tokPerSec,\n\t\t\t\t\treason: result.reason,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// 1. Initialize the selected backend (lazy import). Skipped\n\t\t\t// when the precheck put us in offload-only mode (or\n\t\t\t// too-weak) — the consumer won't be running inference\n\t\t\t// locally, so we don't pay the model download/load cost.\n\t\t\tif (!this.offloadOnlyMode) {\n\t\t\t\tawait this.initializeBackend(onProgress);\n\t\t\t} else {\n\t\t\t\tonProgress({\n\t\t\t\t\tphase: \"backend\",\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: \"offload-only mode (device below minLocalCapability or below hardwareMinimum)\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// 2. Select transport based on env + config\n\t\t\tconst { selectTransport, MswTransport, HttpTransport, CapacitorTransport } =\n\t\t\t\tawait import(\"./transports/index.js\");\n\t\t\tthis.resolvedTransport = selectTransport({\n\t\t\t\ttransport: this.transport === \"auto\" ? undefined : this.transport,\n\t\t\t\tserviceWorkerUrl: this.serviceWorkerUrl,\n\t\t\t});\n\n\t\t\t// Warn if mockUrl was explicitly customized under HTTP (will be ignored).\n\t\t\t// The default value is used as the sentinel for \"user did not customize\".\n\t\t\tif (\n\t\t\t\tthis.resolvedTransport === \"http\" &&\n\t\t\t\tthis.mockUrl !== \"https://api.openai.local/v1/chat/completions\"\n\t\t\t) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[DVAI] mockUrl config is ignored under transport=\\\"http\\\". \" +\n\t\t\t\t\t\t\"The HTTP server always serves at /v1/*. Use dvai.baseUrl \" +\n\t\t\t\t\t\t\"to get the exact endpoint.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Warn if serviceWorkerUrl is empty but transport=\"msw\" was forced\n\t\t\tif (\n\t\t\t\tthis.resolvedTransport === \"msw\" &&\n\t\t\t\tthis.serviceWorkerUrl === \"\" &&\n\t\t\t\tthis.transport === \"msw\"\n\t\t\t) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[DVAI] serviceWorkerUrl is empty but transport='msw' was requested; MSW will fail to register.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Worker-context informational message\n\t\t\tif (\n\t\t\t\tthis.resolvedTransport === \"none\" &&\n\t\t\t\ttypeof window === \"undefined\" &&\n\t\t\t\ttypeof self !== \"undefined\"\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[DVAI] Running in a Web Worker — no transport started. \" +\n\t\t\t\t\t\t\"Use dvai.chatCompletion() directly, or register MSW on the main thread.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Construct + start the transport\n\t\t\tif (this.resolvedTransport === \"msw\") {\n\t\t\t\tthis.activeTransport = new MswTransport({\n\t\t\t\t\tmockUrl: this.mockUrl,\n\t\t\t\t\tserviceWorkerUrl: this.serviceWorkerUrl,\n\t\t\t\t});\n\t\t\t} else if (this.resolvedTransport === \"http\") {\n\t\t\t\tthis.activeTransport = new HttpTransport({\n\t\t\t\t\thttpBasePort: this.httpBasePort,\n\t\t\t\t\thttpMaxPortAttempts: this.httpMaxPortAttempts,\n\t\t\t\t\tcorsOrigin: this.corsOrigin,\n\t\t\t\t\t...(this.httpBindHost !== undefined ? { bindHost: this.httpBindHost } : {}),\n\t\t\t\t});\n\t\t\t} else if (this.resolvedTransport === \"capacitor\") {\n\t\t\t\tthis.activeTransport = new CapacitorTransport({\n\t\t\t\t\tcapacitorBackend: this.capacitorBackend,\n\t\t\t\t\tnativeModelPath: this.nativeModelPath || undefined,\n\t\t\t\t\tnativeMmprojPath: this.nativeMmprojPath,\n\t\t\t\t\tnativeGpuLayers: this.nativeGpuLayers,\n\t\t\t\t\tnativeContextSize: this.nativeContextSize,\n\t\t\t\t\tnativeThreads: this.nativeThreads,\n\t\t\t\t\tnativeEmbeddingMode: this.nativeEmbeddingMode,\n\t\t\t\t\thttpBasePort: this.httpBasePort,\n\t\t\t\t\thttpMaxPortAttempts: this.httpMaxPortAttempts,\n\t\t\t\t\tcorsOrigin: this.corsOrigin,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.activeTransport = null;\n\t\t\t}\n\n\t\t\tif (this.activeTransport) {\n\t\t\t\tconst ctx = this.getHandlerContext(onProgress);\n\t\t\t\tconst started = await this.activeTransport.start(ctx);\n\t\t\t\tthis.baseUrl = started.baseUrl;\n\t\t\t\tthis.port = started.port;\n\t\t\t} else {\n\t\t\t\tthis.baseUrl = undefined;\n\t\t\t\tthis.port = undefined;\n\t\t\t}\n\n\t\t\tthis.isReady = true;\n\t\t\tthis.recoveryAttempts = 0;\n\n\t\t\t// Phase 3 — bring up offload-related state if the consumer\n\t\t\t// opted in. Errors here are logged but do not fail\n\t\t\t// initialize() — local inference still works without offload.\n\t\t\tif (this.offload?.enabled) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.initializeOffload();\n\t\t\t\t\t// v3.2 — auto-attach the offload forwarder so requests\n\t\t\t\t\t// route to peers without the consumer wiring an\n\t\t\t\t\t// interceptor manually. Skipped when:\n\t\t\t\t\t// 1. The consumer already supplied their own\n\t\t\t\t\t// chatCompletionInterceptor (e.g. the Hub uses\n\t\t\t\t\t// one for substitution-policy routing — they're\n\t\t\t\t\t// responsible for composing offload themselves).\n\t\t\t\t\t// 2. We're not in offload-only mode AND a local\n\t\t\t\t\t// backend is available — for v3.2.0 we keep the\n\t\t\t\t\t// auto-attach scoped to the offload-only path\n\t\t\t\t\t// to avoid changing local-first behavior. The\n\t\t\t\t\t// richer \"decide per request even when local\n\t\t\t\t\t// works\" wiring follows in a future patch.\n\t\t\t\t\tif (\n\t\t\t\t\t\tthis.chatCompletionInterceptor === undefined &&\n\t\t\t\t\t\tthis.offloadOnlyMode\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst { buildOffloadInterceptor } = await import(\n\t\t\t\t\t\t\t\"./offload/index.js\"\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst offloadConfig = this.offload;\n\t\t\t\t\t\tthis.chatCompletionInterceptor = buildOffloadInterceptor({\n\t\t\t\t\t\t\tconfig: offloadConfig,\n\t\t\t\t\t\t\tgetPeers: () => this.discovery?.peers() ?? [],\n\t\t\t\t\t\t\tgetLocalCapability: () => 0, // offload-only ⇒ no local backend\n\t\t\t\t\t\t\toffloadOnlyMode: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"[DVAI/offload] failed to initialize offload state; \" +\n\t\t\t\t\t\t\t\"local inference still works. Cause:\",\n\t\t\t\t\t\terr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[DVAI] Failed to initialize:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Phase 3 — bring up the capability cache, discovery layer, and\n\t * pairing policy on top of an already-running DVAI instance.\n\t * Called from initialize() when `offload.enabled` is true.\n\t */\n\tprivate async initializeOffload(): Promise<void> {\n\t\tconst { createCapabilityCache, ensureDeviceId } = await import(\n\t\t\t\"./capability/index.js\"\n\t\t);\n\t\tconst { CompositeDiscovery, StaticDiscovery, createMdnsDiscovery } = await import(\n\t\t\t\"./discovery/index.js\"\n\t\t);\n\t\tconst { PairingPolicy, createPairingStore } = await import(\n\t\t\t\"./pairing/index.js\"\n\t\t);\n\n\t\tthis.capabilityCache = createCapabilityCache();\n\t\tthis.deviceId = await ensureDeviceId(this.capabilityCache);\n\n\t\tconst sources: Array<import(\"./discovery/index.js\").IDiscovery> = [];\n\t\tif (this.offload?.discoverLAN !== false) {\n\t\t\t// v3.2.1 — when `offload.advertiseLAN` is true, register an\n\t\t\t// `_dvai-bridge._tcp` mDNS advertisement so remote peers\n\t\t\t// (mobile SDKs, other Hubs) can discover this instance via\n\t\t\t// NWBrowser without manual URL entry. The native SDKs\n\t\t\t// already do this themselves; the JS-side core (Hub, Node\n\t\t\t// CLI) opts in here. Port defaults to the bound DVAI server\n\t\t\t// port; `models` is empty until the Hub feeds enumeration\n\t\t\t// in (loadedModels via setLoadedModels at runtime).\n\t\t\t//\n\t\t\t// On macOS, the Hub's wrapper (`hub/peer-mode/server.ts`)\n\t\t\t// uses a `dns-sd -R` subprocess that goes through the\n\t\t\t// system mDNSResponder daemon — that's the canonical path\n\t\t\t// and Bonjour clients see a properly-named service\n\t\t\t// (`<DVAI Hub>` vs the generic `<hostname>.local`\n\t\t\t// `multicast-dns` produces). To avoid emitting duplicate\n\t\t\t// `_dvai-bridge._tcp` records, the JS-core's advertise\n\t\t\t// silently no-ops on Darwin — leave the macOS path to the\n\t\t\t// dns-sd subprocess. On Linux / Windows the npm lib's\n\t\t\t// advertise actually works (no system-daemon conflict),\n\t\t\t// so we keep it there.\n\t\t\tconst skipAdvertiseOnDarwin =\n\t\t\t\ttypeof globalThis.process !== \"undefined\" &&\n\t\t\t\tglobalThis.process.platform === \"darwin\";\n\t\t\tconst advertise: import(\"./discovery/mdns-node.js\").AdvertisedTxt | undefined =\n\t\t\t\tthis.offload?.advertiseLAN && !skipAdvertiseOnDarwin\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tdeviceId: this.deviceId,\n\t\t\t\t\t\t\tdeviceName:\n\t\t\t\t\t\t\t\t(typeof globalThis.process !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\t(globalThis.process.env.DVAI_DEVICE_NAME ?? \"\")) ||\n\t\t\t\t\t\t\t\t\"DVAI\",\n\t\t\t\t\t\t\tdvaiVersion: \"3.2.1\",\n\t\t\t\t\t\t\t// Default to the bound DVAI server port (set in\n\t\t\t\t\t\t\t// `start()` after the transport binds). If the\n\t\t\t\t\t\t\t// transport is `none` (rare for advertising), fall\n\t\t\t\t\t\t\t// back to the legacy default 38883.\n\t\t\t\t\t\t\tport:\n\t\t\t\t\t\t\t\tthis.offload.advertisePort ??\n\t\t\t\t\t\t\t\tthis.port ??\n\t\t\t\t\t\t\t\t38883,\n\t\t\t\t\t\t\tsecure: false,\n\t\t\t\t\t\t\t// Field name matches the AdvertisedTxt schema\n\t\t\t\t\t\t\t// (NOT `loadedModels` — that's the iOS-side\n\t\t\t\t\t\t\t// struct name, not the wire encoding).\n\t\t\t\t\t\t\tmodels: [],\n\t\t\t\t\t\t\tcapability: {},\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined;\n\t\t\tsources.push(\n\t\t\t\tawait createMdnsDiscovery({\n\t\t\t\t\tselfDeviceId: this.deviceId,\n\t\t\t\t\t...(advertise ? { advertise } : {}),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (this.offload?.knownPeers && this.offload.knownPeers.length > 0) {\n\t\t\tsources.push(new StaticDiscovery(this.offload.knownPeers));\n\t\t}\n\t\tif (sources.length > 0) {\n\t\t\tthis.discovery = new CompositeDiscovery(sources);\n\t\t\tawait this.discovery.start();\n\t\t}\n\n\t\tthis.pairingPolicy = new PairingPolicy({\n\t\t\tstore: createPairingStore(),\n\t\t\tonPairingRequest: this.offload?.onPairingRequest\n\t\t\t\t? async (peerDeviceId, peerDeviceName, appId) => {\n\t\t\t\t\t\tconst cb = this.offload?.onPairingRequest;\n\t\t\t\t\t\tif (!cb) return false;\n\t\t\t\t\t\treturn cb({\n\t\t\t\t\t\t\tdeviceId: peerDeviceId,\n\t\t\t\t\t\t\tdeviceName: peerDeviceName,\n\t\t\t\t\t\t\tdvaiVersion: \"unknown\",\n\t\t\t\t\t\t\tbaseUrl: \"\",\n\t\t\t\t\t\t\tloadedModels: [],\n\t\t\t\t\t\t\tcapability: {},\n\t\t\t\t\t\t\tvia: \"static\",\n\t\t\t\t\t\t\tsecure: false,\n\t\t\t\t\t\t\tlastSeenAt: Date.now(),\n\t\t\t\t\t\t\t...(appId !== undefined ? { appId } : {}),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t});\n\n\t\t// Build the /v1/dvai/* route map. The HTTP transport reads this\n\t\t// late via the handler-context getter (initializeOffload runs\n\t\t// AFTER the transport has started in the current lifecycle).\n\t\tconst { buildDvaiRoutes } = await import(\"./handlers/dvai/index.js\");\n\t\tconst self = this;\n\t\tthis.dvaiRoutes = buildDvaiRoutes({\n\t\t\tlibraryVersion: \"3.0.0\",\n\t\t\tget currentModelId() {\n\t\t\t\treturn self.resolvedBackend === \"transformers\"\n\t\t\t\t\t? self.transformersModelId\n\t\t\t\t\t: self.modelId;\n\t\t\t},\n\t\t\tcapabilityCache: this.capabilityCache,\n\t\t\tget backend() {\n\t\t\t\tconst b = self.backendInstance;\n\t\t\t\tif (!b) return undefined;\n\t\t\t\t// ProbableBackend just needs `chatCompletion` — duck-typed.\n\t\t\t\treturn b as unknown as import(\"./capability/index.js\").ProbableBackend;\n\t\t\t},\n\t\t\tdiscovery: this.discovery,\n\t\t\tpairingPolicy: this.pairingPolicy,\n\t\t\tstartedAt: this.startedAt,\n\t\t} as Parameters<typeof buildDvaiRoutes>[0]);\n\t}\n\n\t/** Phase 3 — release offload state (LAN advertiser, discovery sockets, etc). */\n\tprivate async shutdownOffload(): Promise<void> {\n\t\tif (this.discovery) {\n\t\t\ttry {\n\t\t\t\tawait this.discovery.stop();\n\t\t\t} catch {\n\t\t\t\t/* swallow — best-effort cleanup */\n\t\t\t}\n\t\t\tthis.discovery = undefined;\n\t\t}\n\t\tthis.capabilityCache = undefined;\n\t\tthis.pairingPolicy = undefined;\n\t\tthis.dvaiRoutes = undefined;\n\t\tthis.deviceId = undefined;\n\t}\n\n\t/**\n\t * Builds a HandlerContext consumed by the transport-agnostic handlers.\n\t * `backend` is exposed via a getter so that when recovery replaces\n\t * `this.backendInstance` mid-request, the handler's subsequent reads of\n\t * `ctx.backend` see the new instance (critical for the reactive-recovery\n\t * retry path in handleChatCompletion).\n\t */\n\tprivate getHandlerContext(\n\t\tonProgress: (info: any) => void,\n\t): HandlerContext {\n\t\tconst self = this;\n\t\treturn {\n\t\t\tget backend() {\n\t\t\t\treturn self.backendInstance;\n\t\t\t},\n\t\t\tresolvedBackend: this.resolvedBackend,\n\t\t\tmodelId:\n\t\t\t\tthis.resolvedBackend === \"transformers\"\n\t\t\t\t\t? this.transformersModelId\n\t\t\t\t\t: this.modelId,\n\t\t\tonRecovery:\n\t\t\t\tthis.resolvedBackend === \"webllm\"\n\t\t\t\t\t? async () => {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tthis.backendInstance?.lastFatalError &&\n\t\t\t\t\t\t\t\tthis.recoveryAttempts < this.maxRetries\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tawait this.attemptRecovery(onProgress);\n\t\t\t\t\t\t\t} else if (this.recoveryAttempts >= this.maxRetries) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Recovery exhausted\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t// Phase 3 — late-bound getter so the HTTP transport sees\n\t\t\t// the routes once initializeOffload() finishes (which runs\n\t\t\t// AFTER the transport starts in the current lifecycle).\n\t\t\tget dvaiRoutes() {\n\t\t\t\treturn self.dvaiRoutes;\n\t\t\t},\n\t\t\t// Phase 4 — Hub injects this to enforce substitution policy\n\t\t\t// + engine-bridge routing.\n\t\t\t...(this.chatCompletionInterceptor !== undefined\n\t\t\t\t? { chatCompletionInterceptor: this.chatCompletionInterceptor }\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\t/**\n\t * Attempts to recover from a fatal WebLLM error by unloading and reloading the backend.\n\t */\n\tprivate async attemptRecovery(\n\t\tonProgress: (info: any) => void,\n\t): Promise<void> {\n\t\tthis.recoveryAttempts++;\n\t\tconst fatalError = this.backendInstance?.lastFatalError;\n\t\tconsole.log(\n\t\t\t`[DVAI] Auto-recovery: unloading engine due to \"${fatalError}\" (attempt ${this.recoveryAttempts}/${this.maxRetries})`,\n\t\t);\n\n\t\t// Unload the backend\n\t\tif (this.backendInstance) {\n\t\t\ttry {\n\t\t\t\tawait this.backendInstance.unload();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(\"[DVAI] Error during recovery unload:\", e);\n\t\t\t}\n\t\t\tthis.backendInstance = null;\n\t\t}\n\n\t\t// Reload\n\t\tawait this.initializeBackend(onProgress);\n\t\tif (this.backendInstance?.clearFatalError) {\n\t\t\tthis.backendInstance.clearFatalError();\n\t\t}\n\t\tconsole.log(\"[DVAI] Auto-recovery: engine reloaded successfully\");\n\t}\n\n\t/**\n\t * Lazy-imports and initializes the selected backend.\n\t */\n\tprivate async initializeBackend(\n\t\tonProgress: (info: any) => void,\n\t): Promise<void> {\n\t\tif (this.resolvedBackend === \"native\") {\n\t\t\tlet NodeLlamaCppBackend: any;\n\t\t\ttry {\n\t\t\t\tconst mod = await import(\"./NodeLlamaCppBackend.js\");\n\t\t\t\tNodeLlamaCppBackend = mod.NodeLlamaCppBackend;\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] native backend selected but the NodeLlamaCppBackend module failed to load.',\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!this.nativeModelPath) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] backend: \"native\" requires `nativeModelPath` (path to a GGUF file).',\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Use modelId only if the consumer customized it; otherwise let\n\t\t\t// the backend derive it from the GGUF basename. The default\n\t\t\t// WebLLM model id is meaningless for the native backend.\n\t\t\tconst customModelId =\n\t\t\t\tthis.modelId !== \"gemma-2-2b-it-q4f16_1-MLC\" ? this.modelId : undefined;\n\t\t\tconst backend = new NodeLlamaCppBackend({\n\t\t\t\tmodelPath: this.nativeModelPath,\n\t\t\t\tgpuLayers: this.nativeGpuLayers,\n\t\t\t\tthreads: this.nativeThreads,\n\t\t\t\tcontextSize: this.nativeContextSize,\n\t\t\t\tgenerationTimeout: this.generationTimeout,\n\t\t\t\tmodelId: customModelId,\n\t\t\t});\n\t\t\tawait backend.initialize(onProgress);\n\t\t\tthis.backendInstance = backend;\n\t\t\t// Echo the resolved model identifier (basename if not provided)\n\t\t\tthis.modelId = backend.getModelId();\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI] node-llama-cpp backend ready (modelId=\"${this.modelId}\", gpuLayers=${this.nativeGpuLayers}, contextSize=${this.nativeContextSize})`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (this.resolvedBackend === \"transformers\") {\n\t\t\tlet TransformersBackend: any;\n\t\t\ttry {\n\t\t\t\tconst mod = await import(\"./TransformersBackend.js\");\n\t\t\t\tTransformersBackend = mod.TransformersBackend;\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] Transformers.js backend selected but \"@huggingface/transformers\" is not installed.\\n' +\n\t\t\t\t\t\t\"Install it with: npm install @huggingface/transformers\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst backend = new TransformersBackend({\n\t\t\t\tmodelId: this.transformersModelId,\n\t\t\t\tdevice: this.device,\n\t\t\t\tgenerationTimeout: this.generationTimeout,\n\t\t\t\tworkerUrl: this.transformersWorkerUrl,\n\t\t\t\tpipelineTask: this.pipelineTask,\n\t\t\t\tdtype: this.dtype,\n\t\t\t\tcreatePipeline: this.createPipeline,\n\t\t\t\tmodelClass: this.transformersModelClass,\n\t\t\t\tprocessorClass: this.transformersProcessorClass,\n\t\t\t\tdisableEncoders: this.transformersDisableEncoders,\n\t\t\t});\n\t\t\tawait backend.initialize(onProgress);\n\t\t\tthis.backendInstance = backend;\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI] Transformers.js backend ready (task: ${this.pipelineTask}, device: ${backend.getResolvedDevice()}, worker: ${backend.isWorkerBased()})`,\n\t\t\t);\n\t\t} else {\n\t\t\tlet WebLLMBackend: any;\n\t\t\ttry {\n\t\t\t\tconst mod = await import(\"./WebLLMBackend.js\");\n\t\t\t\tWebLLMBackend = mod.WebLLMBackend;\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] WebLLM backend selected but \"@mlc-ai/web-llm\" is not installed.\\n' +\n\t\t\t\t\t\t\"Install it with: npm install @mlc-ai/web-llm\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst backend = new WebLLMBackend({\n\t\t\t\tmodelId: this.modelId,\n\t\t\t\tgenerationTimeout: this.generationTimeout,\n\t\t\t\tmaxBlankChunks: this.maxBlankChunks,\n\t\t\t\tworkerUrl: this.webllmWorkerUrl,\n\t\t\t});\n\t\t\tawait backend.initialize(onProgress);\n\t\t\tthis.backendInstance = backend;\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI] WebLLM backend ready (worker: ${backend.isWorkerBased()})`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Gets the underlying engine instance directly.\n\t * - For WebLLM: returns the MLCEngine\n\t * - For Transformers.js: returns the pipeline\n\t */\n\tgetEngine(): any {\n\t\tif (!this.backendInstance) return null;\n\t\tif (this.resolvedBackend === \"webllm\") {\n\t\t\treturn this.backendInstance.getEngine?.() ?? null;\n\t\t}\n\t\tif (this.resolvedBackend === \"native\") {\n\t\t\t// node-llama-cpp doesn't expose a single \"engine\" — return the\n\t\t\t// chat session, which is the closest analogue.\n\t\t\treturn this.backendInstance ?? null;\n\t\t}\n\t\treturn this.backendInstance.getPipeline?.() ?? null;\n\t}\n\n\t/**\n\t * Perform a direct chat completion (bypasses MSW, calls backend directly).\n\t * Useful for programmatic usage without going through the fetch mock.\n\t */\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.backendInstance)\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Backend not initialized. Call initialize() first.\",\n\t\t\t);\n\t\treturn this.backendInstance.chatCompletion(requestBody);\n\t}\n\n\t/**\n\t * Generate embeddings for one or more text inputs.\n\t *\n\t * Supported when backend is \"transformers\" with\n\t * pipelineTask: \"feature-extraction\".\n\t *\n\t * Throws when called on the WebLLM backend.\n\t *\n\t * @param inputs - A single string or array of strings to embed\n\t * @returns An array of embedding vectors (one per input)\n\t */\n\tasync embedding(inputs: string | string[]): Promise<number[][]> {\n\t\tif (!this.backendInstance)\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Backend not initialized. Call initialize() first.\",\n\t\t\t);\n\t\tif (this.resolvedBackend === \"webllm\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Embeddings are not supported on the WebLLM backend. \" +\n\t\t\t\t\t\"Use backend: 'transformers' with pipelineTask: 'feature-extraction'.\",\n\t\t\t);\n\t\t}\n\t\tif (this.resolvedBackend === \"native\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Embeddings are not yet supported on the node-llama-cpp backend. \" +\n\t\t\t\t\t\"Use backend: 'transformers' with pipelineTask: 'feature-extraction'.\",\n\t\t\t);\n\t\t}\n\t\tif (typeof this.backendInstance.embedding !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] The current backend does not expose an embedding() method.\",\n\t\t\t);\n\t\t}\n\t\treturn this.backendInstance.embedding(inputs);\n\t}\n\n\t/**\n\t * Run the pipeline directly (Transformers.js only).\n\t * Use this for non-text tasks like text-to-image, ASR, text-to-speech, etc.\n\t * @param inputs - Input data appropriate for the pipeline task\n\t * @param options - Pipeline-specific options\n\t */\n\tasync runPipeline(inputs: any, options?: Record<string, any>): Promise<any> {\n\t\tif (!this.backendInstance)\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Backend not initialized. Call initialize() first.\",\n\t\t\t);\n\t\tif (\n\t\t\tthis.resolvedBackend !== \"transformers\" ||\n\t\t\t!this.backendInstance.runPipeline\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] runPipeline() is only available with the Transformers.js backend.\",\n\t\t\t);\n\t\t}\n\t\treturn this.backendInstance.runPipeline(inputs, options);\n\t}\n\n\t/**\n\t * Unloads the AI engine and stops the active transport to free up resources.\n\t */\n\tasync unload(): Promise<void> {\n\t\t// Phase 3 — tear down offload state first so we stop advertising\n\t\t// before the underlying server disappears.\n\t\tawait this.shutdownOffload();\n\n\t\tif (this.backendInstance) {\n\t\t\tawait this.backendInstance.unload();\n\t\t\tthis.backendInstance = null;\n\t\t}\n\t\tif (this.activeTransport) {\n\t\t\tawait this.activeTransport.stop();\n\t\t\tthis.activeTransport = null;\n\t\t}\n\t\tthis.baseUrl = undefined;\n\t\tthis.port = undefined;\n\t\tthis.isReady = false;\n\t\tthis.recoveryAttempts = 0;\n\t\tconsole.log(\"[DVAI] Unloaded model and transport.\");\n\t}\n\n\t/* ----- Phase 3 — public surface for offload diagnostics ----- */\n\n\t/**\n\t * v3.2 — pre-init hardware assessment.\n\t *\n\t * Returns a JSON-serializable description of how this device\n\t * would handle local inference, BEFORE any model download/load.\n\t *\n\t * Consumers should call this before `initialize()` if they want\n\t * to refuse to start on too-weak devices. The SDK itself never\n\t * shows UI — it's the consumer app's job to decide what (if\n\t * anything) to surface based on the result.\n\t *\n\t * Result `mode` values:\n\t * - `ok` → device can comfortably run the model\n\t * locally; initialize() will proceed normally.\n\t * - `offload-only` → device can run but slowly (below\n\t * `minLocalCapability`); initialize() will\n\t * skip the model load and route every\n\t * request to a paired peer.\n\t * - `too-weak` → device is below the hardware floor (3\n\t * tok/s by default); initialize() will\n\t * ALSO skip the model load — the consumer\n\t * should typically bail rather than even\n\t * calling initialize().\n\t *\n\t * Pass `hardwareMinimum` / `minLocalCapability` to override the\n\t * defaults (matches `OffloadConfig`).\n\t *\n\t * @returns a serializable assessment (safe to JSON.stringify and\n\t * ship over a Pigeon / Capacitor channel).\n\t */\n\tasync assessHardware(opts: {\n\t\thardwareMinimum?: number;\n\t\tminLocalCapability?: number;\n\t} = {}): Promise<{\n\t\tmode: \"ok\" | \"offload-only\" | \"too-weak\";\n\t\ttokPerSec: number;\n\t\treason: string;\n\t\thints: import(\"./capability/index.js\").DeviceCapabilityHints;\n\t}> {\n\t\tconst { assessCapability } = await import(\"./capability/precheck.js\");\n\t\tconst result = await assessCapability({\n\t\t\thardwareMinimum: opts.hardwareMinimum,\n\t\t\tminLocalCapability: opts.minLocalCapability,\n\t\t});\n\t\treturn {\n\t\t\tmode: result.mode,\n\t\t\ttokPerSec: result.tokPerSec,\n\t\t\treason: result.reason,\n\t\t\thints: result.hints,\n\t\t};\n\t}\n\n\t/**\n\t * Run a cold-run capability probe against the active backend +\n\t * model. Persists the result for future getCapability() calls.\n\t * Requires offload.enabled.\n\t */\n\tasync probeCapability(): Promise<\n\t\timport(\"./capability/index.js\").CapabilityScore | undefined\n\t> {\n\t\tif (!this.capabilityCache || !this.backendInstance) return undefined;\n\t\tconst { probeAndCache } = await import(\"./capability/index.js\");\n\t\tconst modelId =\n\t\t\tthis.resolvedBackend === \"transformers\"\n\t\t\t\t? this.transformersModelId\n\t\t\t\t: this.modelId;\n\t\treturn probeAndCache({\n\t\t\tcache: this.capabilityCache,\n\t\t\tbackend: this.backendInstance,\n\t\t\tmodelId,\n\t\t\tlibraryVersion: \"3.0.0\",\n\t\t});\n\t}\n\n\t/**\n\t * Get the cached capability score for a model on this device, or\n\t * compute a heuristic estimate if no probe has run yet.\n\t */\n\tasync getCapability(\n\t\tmodelId?: string,\n\t): Promise<import(\"./capability/index.js\").CapabilityScore | undefined> {\n\t\tif (!this.capabilityCache) return undefined;\n\t\tconst { getCapability } = await import(\"./capability/index.js\");\n\t\tconst id =\n\t\t\tmodelId ??\n\t\t\t(this.resolvedBackend === \"transformers\"\n\t\t\t\t? this.transformersModelId\n\t\t\t\t: this.modelId);\n\t\treturn getCapability({\n\t\t\tcache: this.capabilityCache,\n\t\t\tmodelId: id,\n\t\t\tlibraryVersion: \"3.0.0\",\n\t\t});\n\t}\n\n\t/** Snapshot of currently-known peers via the discovery layer. */\n\tgetPeers(): import(\"./discovery/index.js\").Peer[] {\n\t\treturn this.discovery?.peers() ?? [];\n\t}\n}\n\n// Export a singleton instance by default, or the class for advanced usage\nexport const dvai: DVAI = new DVAI();\n","/**\n * DVAI-Bridge license validator — offline JWT verification.\n *\n * This file replaces the v3.2.x `LicenseValidator` that did checksum-\n * style validation on a `dvai-...` plaintext key. The new model:\n *\n * 1. The license is a signed JWT (header + payload + ECDSA P-256\n * signature), issued by the operator's own license-generator\n * service from a private key they hold.\n * 2. The SDK ships only with public keys (see `publicKeys.ts`) and\n * cannot itself produce valid licenses — so reverse-engineering\n * the bundled SDK gains nothing.\n * 3. At runtime, the validator does signature + expiry + audience +\n * platform binding checks. Failure of any check collapses to\n * free-tier (with attribution badge), not a hard error — the SDK\n * stays usable for hobbyists / community use.\n *\n * Network calls: zero. The whole flow is offline by design — there's\n * no \"phone home\" step, no license server polling, no DRM beacon. The\n * private-key holder is the only party that can mint tokens, and any\n * deployment that has a valid file activates without contacting us.\n */\nimport {\n importJWK,\n jwtVerify,\n errors as joseErrors,\n type JWTPayload,\n} from \"jose\";\nimport {\n DVAI_PUBLIC_KEYS,\n PLACEHOLDER_KID,\n type DvaiPublicKeyJwk,\n} from \"./publicKeys.js\";\nimport {\n detectAudience,\n detectDevMode,\n detectPlatform,\n matchAudience,\n} from \"./audience.js\";\nimport {\n discoverLicenseToken,\n type LicenseDiscoveryOptions,\n} from \"./discovery.js\";\nimport {\n LicenseRequiredError,\n type DvaiLicensePayload,\n type DvaiPlatform,\n type LicenseStatus,\n} from \"./types.js\";\n\nexport interface LicenseValidatorOptions extends LicenseDiscoveryOptions {\n /**\n * Override the public-key registry. Defaults to `DVAI_PUBLIC_KEYS`\n * from `./publicKeys.ts`. Tests inject their own keypair via this\n * option so they can sign + verify against a deterministic key\n * without polluting the production registry.\n */\n publicKeys?: Record<string, DvaiPublicKeyJwk>;\n /**\n * If true, accept tokens signed under `PLACEHOLDER_KID` (i.e. the\n * built-in placeholder public key). Off by default — a real\n * production build must replace the placeholder with a generated\n * key. Tests set this to true.\n */\n allowPlaceholderKey?: boolean;\n}\n\n/**\n * Validate a DVAI-Bridge license once at SDK startup. The returned\n * `LicenseStatus` is the discriminated value the rest of the SDK\n * dispatches on. Never throws on validation failure — it logs a\n * console.warn and returns a `free-prod` / `free-expired` status.\n */\nexport class LicenseValidator {\n private readonly opts: LicenseValidatorOptions;\n\n constructor(opts: LicenseValidatorOptions = {}) {\n this.opts = opts;\n }\n\n /**\n * Validate WITHOUT throwing. Returns a `LicenseStatus` describing what\n * the validator determined; never throws on missing / invalid /\n * expired licenses. Useful for host-app dashboards that want to\n * display the licensee / expiry / fallback reason without halting\n * SDK startup, and for tests.\n *\n * The SDK's `initialize()` calls `validateAndAssert()` instead — that\n * throws `LicenseRequiredError` for `free-prod` / `free-expired`,\n * which is how the BSL 1.1 commercial-only-in-production policy is\n * actually enforced at runtime.\n *\n * Idempotent; safe to call multiple times.\n */\n async validate(): Promise<LicenseStatus> {\n // 1. Dev-mode bypass — license required only in production.\n const dev = detectDevMode();\n if (dev.isDev) {\n return { kind: \"free-dev\", reason: dev.reason };\n }\n\n // 2. Discover the token. Returns null when no license source is\n // configured AND auto-discovery fails — fall through to free-prod\n // so the SDK still works for community / hobbyist users.\n const discovered = await discoverLicenseToken({\n ...(this.opts.token !== undefined ? { token: this.opts.token } : {}),\n ...(this.opts.path !== undefined ? { path: this.opts.path } : {}),\n });\n if (discovered === null) {\n return {\n kind: \"free-prod\",\n reason:\n \"no license token found; checked config.licenseToken, \" +\n \"config.licenseKeyPath, DVAI_LICENSE_PATH env, \" +\n \"DVAI_LICENSE_TOKEN env, and platform-default paths\",\n };\n }\n\n // 3. Verify signature + claims with jose.\n const platform = detectPlatform();\n const audience = detectAudience();\n return await this.verifyToken(discovered.token, platform, audience);\n }\n\n /**\n * Strict validation entry point used by the SDK at startup. Returns\n * `LicenseStatus` on success (`commercial`, `trial`, `free-dev`) and\n * THROWS `LicenseRequiredError` on `free-prod` / `free-expired`.\n *\n * This is the BSL 1.1 enforcement point: in production / release\n * builds (any non-dev-mode environment), the SDK refuses to operate\n * without a valid commercial or trial license. Developers running on\n * localhost / debug builds / explicit DVAI_FORCE_DEV are unaffected\n * — those return a `free-dev` status and the SDK proceeds normally.\n *\n * Use `validate()` instead when you want to inspect the status\n * without halting startup (host-app dashboards, test fixtures).\n */\n async validateAndAssert(): Promise<LicenseStatus> {\n const status = await this.validate();\n if (status.kind === \"free-prod\") {\n throw new LicenseRequiredError(buildRequiredErrorMessage(status), status);\n }\n if (status.kind === \"free-expired\") {\n throw new LicenseRequiredError(buildRequiredErrorMessage(status), status);\n }\n return status;\n }\n\n private async verifyToken(\n token: string,\n platform: DvaiPlatform,\n runtimeAudience: string | null,\n ): Promise<LicenseStatus> {\n const registry = this.opts.publicKeys ?? DVAI_PUBLIC_KEYS;\n\n // Read the kid out of the JWT header to pick the right public key.\n // We could let jose iterate but specifying the key up-front gives\n // clearer error messages on misses.\n let header: { alg?: string; kid?: string };\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[0]) {\n return {\n kind: \"free-prod\",\n reason: \"license token is not a well-formed JWT (need 3 segments)\",\n };\n }\n const headerJson = base64UrlDecodeUtf8(parts[0]);\n header = JSON.parse(headerJson) as { alg?: string; kid?: string };\n } catch (err) {\n return {\n kind: \"free-prod\",\n reason: `license token header is not parseable JSON: ${asMessage(err)}`,\n };\n }\n\n if (header.alg !== \"ES256\") {\n // Refuse `alg: none` and any non-ES256 algorithm. Critical defense\n // against the classic JWT algorithm-confusion vulnerability.\n return {\n kind: \"free-prod\",\n reason: `license token uses unsupported alg \"${header.alg ?? \"(missing)\"}\", expected ES256`,\n };\n }\n\n if (typeof header.kid !== \"string\" || header.kid.length === 0) {\n return {\n kind: \"free-prod\",\n reason: \"license token header missing kid; cannot select verification key\",\n };\n }\n\n const jwk = registry[header.kid];\n if (jwk === undefined) {\n return {\n kind: \"free-prod\",\n reason:\n `license token kid \"${header.kid}\" is not in the SDK's public-key ` +\n `registry; either the key was rotated and you're on an old SDK, ` +\n `or the token was signed with a key we don't recognise`,\n };\n }\n\n if (header.kid === PLACEHOLDER_KID && this.opts.allowPlaceholderKey !== true) {\n return {\n kind: \"free-prod\",\n reason:\n `license token signed with the placeholder key (kid \"${PLACEHOLDER_KID}\"); ` +\n `replace the placeholder in publicKeys.ts with a real key generated ` +\n `via scripts/license/generate-keypair.mjs before issuing real licenses`,\n };\n }\n\n let payload: JWTPayload;\n try {\n const key = await importJWK(jwk, \"ES256\");\n const result = await jwtVerify(token, key, {\n algorithms: [\"ES256\"],\n issuer: \"DVAI-Bridge\",\n // Audience and expiry are checked manually below so we can\n // surface specific failure reasons rather than generic\n // jose error codes.\n });\n payload = result.payload;\n } catch (err) {\n // joseErrors gives us typed failure modes — surface the most\n // useful diagnostic per category so the developer's console\n // warning is actionable.\n if (err instanceof joseErrors.JWTExpired) {\n // Expired but otherwise valid — surface the licensee/expiry\n // so the developer knows whose renewal to chase.\n const exp = (err.payload?.exp as number | undefined) ?? 0;\n const licensee = (err.payload?.licensee as string | undefined) ?? \"(unknown)\";\n return {\n kind: \"free-expired\",\n licensee,\n expiredAt: exp,\n };\n }\n if (err instanceof joseErrors.JWSSignatureVerificationFailed) {\n return {\n kind: \"free-prod\",\n reason:\n `license token signature did not verify against kid \"${header.kid}\"; ` +\n `the token may have been tampered with or was signed by a different key`,\n };\n }\n if (err instanceof joseErrors.JWTClaimValidationFailed) {\n return {\n kind: \"free-prod\",\n reason: `license token claim \"${err.claim}\" failed: ${err.reason}`,\n };\n }\n return {\n kind: \"free-prod\",\n reason: `license token verification failed: ${asMessage(err)}`,\n };\n }\n\n // Coerce + validate the payload shape ourselves (jose only checks\n // the standard claims). Each branch below provides a specific\n // free-prod reason so the developer can fix exactly what's wrong.\n if (!isLicensePayload(payload)) {\n return {\n kind: \"free-prod\",\n reason: \"license token payload missing required DVAI fields (tier/platforms/aud/licensee)\",\n };\n }\n\n if (!payload.platforms.includes(platform)) {\n return {\n kind: \"free-prod\",\n reason:\n `license token does not authorise platform \"${platform}\"; ` +\n `the token covers [${payload.platforms.join(\", \")}]`,\n };\n }\n\n const matched = matchAudience(runtimeAudience, payload.aud);\n if (matched === null) {\n return {\n kind: \"free-prod\",\n reason:\n `license token's audience entries [${payload.aud.join(\", \")}] ` +\n `do not match the current runtime audience \"${runtimeAudience ?? \"(none)\"}\"` +\n (runtimeAudience === null\n ? ` — set DVAI_AUDIENCE in your environment, or use a \"*\" aud entry for any-domain licenses`\n : \"\"),\n };\n }\n\n return {\n kind: payload.tier,\n licensee: payload.licensee,\n expiresAt: payload.exp,\n platform,\n audienceMatched: matched,\n };\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------------- */\n\nfunction isLicensePayload(p: JWTPayload): p is DvaiLicensePayload & JWTPayload {\n return (\n typeof p.iss === \"string\" &&\n typeof p.sub === \"string\" &&\n Array.isArray(p.aud) &&\n p.aud.every((a) => typeof a === \"string\") &&\n (p.tier === \"commercial\" || p.tier === \"trial\") &&\n Array.isArray((p as { platforms?: unknown }).platforms) &&\n ((p as { platforms: unknown[] }).platforms).every((x) => typeof x === \"string\") &&\n typeof (p as { licensee?: unknown }).licensee === \"string\" &&\n typeof p.iat === \"number\" &&\n typeof p.exp === \"number\"\n );\n}\n\nfunction base64UrlDecodeUtf8(s: string): string {\n // Convert base64url → base64, pad, then decode.\n const pad = s.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (s.length % 4));\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\") + pad;\n // Browsers: atob → binary string → UTF-8 via TextDecoder.\n if (typeof atob === \"function\") {\n const binary = atob(b64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n // Node fallback (Buffer is available).\n return Buffer.from(b64, \"base64\").toString(\"utf-8\");\n}\n\nfunction asMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Build the developer-facing error message for `LicenseRequiredError`.\n * Intentionally verbose: it tells the developer exactly what failed,\n * how to resolve it, where to put the license file, and how to bypass\n * for local development. This message will be printed to a terminal /\n * a browser console / a crash log somewhere — make it readable in all\n * three contexts.\n */\nfunction buildRequiredErrorMessage(status: LicenseStatus): string {\n const header =\n \"\\n\" +\n \"DVAI-Bridge Commercial License Required\\n\" +\n \"=======================================\\n\";\n\n const reason =\n status.kind === \"free-expired\"\n ? `License for \"${status.licensee}\" expired at ${new Date(\n status.expiredAt * 1000,\n ).toISOString()}.`\n : status.kind === \"free-prod\"\n ? status.reason\n : \"(unknown status)\";\n\n const remediation =\n \"\\n\" +\n \"This SDK is licensed under BSL 1.1 and requires a valid commercial\\n\" +\n \"or trial license to run in production / release builds.\\n\" +\n \"\\n\" +\n \"To resolve:\\n\" +\n \" 1. Obtain a license at https://deepvoiceai.com/dvai-bridge/license\\n\" +\n \" 2. Place the file at one of these locations (any will work):\\n\" +\n \" - <project-root>/dvai-license.jwt (auto-discovered)\\n\" +\n \" - the path you pass as DVAIConfig.licenseKeyPath\\n\" +\n \" - the path in $DVAI_LICENSE_PATH\\n\" +\n \" - inline JWT in DVAIConfig.licenseToken or $DVAI_LICENSE_TOKEN\\n\" +\n \" 3. Re-run.\\n\" +\n \"\\n\" +\n \"Developing locally? The SDK auto-detects dev mode on:\\n\" +\n \" - localhost / 127.0.0.1 / *.local hostnames in the browser\\n\" +\n \" - NODE_ENV=test or NODE_ENV=development in Node\\n\" +\n \" - DVAI_FORCE_DEV=1 environment variable (explicit override)\\n\" +\n \" - Capacitor.DEBUG=true on hybrid mobile builds\\n\" +\n \"Any of these silences this error and lets the SDK run without a\\n\" +\n \"license.\\n\";\n\n return header + \"\\n\" + reason + \"\\n\" + remediation;\n}\n","/**\n * Public-key registry for DVAI-Bridge license JWT verification.\n *\n * Each entry is keyed by `kid` (key id, written by the license generator\n * into the JWT header). The SDK looks up the matching entry by kid when\n * verifying a license token. Multiple entries can coexist so that key\n * rotation is non-disruptive: ship the new key in a release alongside\n * the old, leave the old in place for ~12 months while previously-\n * issued licenses naturally expire or get re-issued, then prune.\n *\n * THE PRIVATE KEY DOES NOT LIVE HERE. It belongs in your secrets\n * manager (1Password / AWS Secrets Manager / Vault), accessible only\n * to the license-generator service that produces signed JWTs. The\n * mathematics of ECDSA P-256 guarantee that a holder of the public\n * key alone cannot forge a signature.\n *\n * To populate this registry:\n * 1. Run `node scripts/license/generate-keypair.mjs` (see that\n * script's comment for full instructions)\n * 2. Paste the printed PUBLIC key JWK as an entry below\n * 3. Move the printed PRIVATE key into your secrets store\n * 4. Wire your license-generator backend to use the private key\n */\n\n/** ES256 (P-256 ECDSA) public key in JWK form. */\nexport interface DvaiPublicKeyJwk {\n kty: \"EC\";\n crv: \"P-256\";\n x: string;\n y: string;\n alg?: \"ES256\";\n use?: \"sig\";\n kid?: string;\n}\n\n/**\n * Registry mapping `kid` → public key JWK.\n *\n * ⚠️ The entry below is a **placeholder** — it is a published, well-known\n * test keypair and DOES NOT verify any real production license. Before\n * shipping licenses to customers, replace it with the output of\n * `scripts/license/generate-keypair.mjs`. The SDK refuses to validate\n * licenses against the placeholder kid `\"placeholder-do-not-ship\"`\n * unless DVAI_LICENSE_ALLOW_PLACEHOLDER=1 is set (test-only escape hatch).\n *\n * Adding a new key for rotation:\n *\n * export const DVAI_PUBLIC_KEYS: Record<string, DvaiPublicKeyJwk> = {\n * \"2026-05\": { kty: \"EC\", crv: \"P-256\", x: \"...\", y: \"...\", alg: \"ES256\", use: \"sig\", kid: \"2026-05\" },\n * \"2027-01\": { kty: \"EC\", crv: \"P-256\", x: \"...\", y: \"...\", alg: \"ES256\", use: \"sig\", kid: \"2027-01\" },\n * };\n */\nexport const DVAI_PUBLIC_KEYS: Record<string, DvaiPublicKeyJwk> = {\n // Production key, kid `2026-05`. Generated 2026-05-15 by\n // scripts/license/generate-keypair.mjs. The matching private key\n // lives in the operator's secrets manager.\n \"2026-05\": {\n kty: \"EC\",\n crv: \"P-256\",\n x: \"2Y8TuhnlE4tiVDtliozYTgc1TAqi4_TBTI6FHe1p_Vw\",\n y: \"pyxMJHj10HPe2hnpJvMpnZ4AzpYZRfqGEMhpBr1-Oto\",\n alg: \"ES256\",\n use: \"sig\",\n kid: \"2026-05\",\n },\n // PLACEHOLDER — used by the SDK's own unit tests and by the sample\n // license printed by `generate-keypair.mjs`. The validator REFUSES to\n // accept tokens signed under this kid unless allowPlaceholderKey is\n // explicitly set (DVAI_LICENSE_ALLOW_PLACEHOLDER=1 env var, or the\n // allowPlaceholderKey constructor option). Safe to keep in production\n // builds; remove only if you want test fixtures to stop working.\n \"placeholder-do-not-ship\": {\n kty: \"EC\",\n crv: \"P-256\",\n x: \"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\",\n y: \"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\",\n alg: \"ES256\",\n use: \"sig\",\n kid: \"placeholder-do-not-ship\",\n },\n};\n\n/**\n * `kid` reserved for the placeholder key above. The validator refuses to\n * accept tokens signed with this kid unless the caller explicitly opts\n * in (DVAI_LICENSE_ALLOW_PLACEHOLDER=1 or `allowPlaceholderKey: true`\n * passed to the validator constructor). Used by tests and by the\n * sample license printed by `generate-keypair.mjs`.\n */\nexport const PLACEHOLDER_KID = \"placeholder-do-not-ship\";\n","/**\n * Runtime audience + platform + dev-mode detection for the JS/TS SDK.\n *\n * Native SDKs (iOS, Android, .NET, Flutter) re-implement this in their\n * own languages — the semantics are the same but the platform APIs\n * differ. Each native validator detects its own audience (bundle id /\n * package name / assembly name) and its own platform identifier.\n *\n * For the JS/TS SDK, \"audience\" means:\n * - in a browser: window.location.hostname\n * - under Capacitor: the native platform's bundle id (read via the\n * Capacitor bridge if available, otherwise falls back to hostname)\n * - under React Native: deferred — the RN SDK's validator handles this\n * - in Node: process.env.DVAI_AUDIENCE (operator-supplied), else null\n * (Node deployments without an explicit audience can't be domain-\n * bound, so the license aud check is permissive — see validator)\n *\n * \"Dev mode\" detection bypasses license enforcement entirely so\n * developers don't need a license to run the SDK on localhost or in a\n * debug build. This matches the prior LicenseValidator behaviour and\n * keeps the developer-experience curve gentle.\n */\nimport type { DvaiPlatform } from \"./types.js\";\n\n/** Detect the current SDK platform identifier. Best-effort; returns\n * the most specific known platform that matches the runtime. */\nexport function detectPlatform(): DvaiPlatform {\n // Capacitor is detected before browser because a Capacitor app *is*\n // a browser environment with a Capacitor global attached. Order matters.\n if (typeof globalThis !== \"undefined\") {\n const g = globalThis as unknown as {\n Capacitor?: { isNativePlatform?: () => boolean };\n process?: { versions?: { node?: string } };\n window?: unknown;\n };\n if (g.Capacitor?.isNativePlatform?.()) return \"capacitor\";\n if (g.process?.versions?.node && typeof g.window === \"undefined\") {\n return \"node\";\n }\n }\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") {\n return \"web\";\n }\n // Fall back to \"node\" — workers and exotic JS hosts get folded in here.\n // The audience binding is the load-bearing check; platform is a coarse\n // filter, so over-permissiveness here is acceptable.\n return \"node\";\n}\n\n/** Detect the current audience string the license must bind. Returns\n * null when no determinable audience exists (e.g. headless Node) —\n * the validator handles null by accepting any aud entry, since binding\n * enforcement requires a concrete runtime identifier to match against. */\nexport function detectAudience(): string | null {\n // Browser-like environments report the hostname. Capacitor reports\n // `localhost` (the bundled-content origin) which is intentionally\n // matched against the license's aud entries — Capacitor apps that\n // want native-bundle-id binding should use the native SDK's validator\n // (running below the bridge) rather than the JS-side one.\n if (typeof window !== \"undefined\") {\n const w = window as unknown as { location?: { hostname?: string } };\n const host = w.location?.hostname;\n if (typeof host === \"string\" && host.length > 0) return host;\n }\n // Node.js explicit override — operators set this on the process so\n // server-side deployments can opt in to license binding.\n if (typeof process !== \"undefined\" && process.env?.DVAI_AUDIENCE) {\n return process.env.DVAI_AUDIENCE;\n }\n return null;\n}\n\n/**\n * Detect whether the SDK is running in a developer environment where\n * license enforcement should be bypassed. The bypass list is intentionally\n * generous: blocking a developer mid-`pnpm dev` with a license-not-found\n * error would be hostile. The cost is that a malicious actor pointing\n * their build at `localhost` could bypass — but they could equally fork\n * the SDK and remove the check, so the dev-mode bypass adds no real\n * attack surface.\n */\nexport function detectDevMode(): { isDev: boolean; reason: string } {\n // 1. Explicit env-var override wins.\n if (typeof process !== \"undefined\" && process.env) {\n if (process.env.DVAI_FORCE_PROD === \"1\" || process.env.DVAI_FORCE_PROD === \"true\") {\n return { isDev: false, reason: \"DVAI_FORCE_PROD set\" };\n }\n if (process.env.DVAI_FORCE_DEV === \"1\" || process.env.DVAI_FORCE_DEV === \"true\") {\n return { isDev: true, reason: \"DVAI_FORCE_DEV set\" };\n }\n if (process.env.NODE_ENV === \"test\") {\n return { isDev: true, reason: \"NODE_ENV=test\" };\n }\n }\n\n // 2. Capacitor / Cordova debug flags.\n const g = (typeof globalThis !== \"undefined\" ? globalThis : {}) as Record<string, unknown>;\n const cap = g[\"Capacitor\"] as { DEBUG?: boolean } | undefined;\n if (cap?.DEBUG === true) {\n return { isDev: true, reason: \"Capacitor.DEBUG=true\" };\n }\n\n // 3. Localhost / private-network / .local mDNS hostnames in the browser.\n // Matches the prior LicenseValidator's heuristic so v3.2.x apps that\n // used to bypass licensing on dev URLs continue to bypass.\n if (typeof window !== \"undefined\") {\n const w = window as unknown as { location?: { hostname?: string } };\n const host = w.location?.hostname ?? \"\";\n if (\n host === \"localhost\" ||\n host === \"127.0.0.1\" ||\n host === \"::1\" ||\n host.endsWith(\".local\") ||\n host.startsWith(\"192.168.\") ||\n host.startsWith(\"10.\") ||\n host.startsWith(\"172.\")\n ) {\n return { isDev: true, reason: `localhost-class hostname: ${host}` };\n }\n // 4. localStorage override (browser-only test hook).\n try {\n const ls = (window as unknown as { localStorage?: Storage }).localStorage;\n if (ls?.getItem(\"DVAI_FORCE_PROD\") === \"true\") {\n return { isDev: false, reason: \"localStorage DVAI_FORCE_PROD=true\" };\n }\n if (ls?.getItem(\"DVAI_FORCE_DEV\") === \"true\") {\n return { isDev: true, reason: \"localStorage DVAI_FORCE_DEV=true\" };\n }\n } catch {\n /* sandboxed contexts (some iframes) throw on storage access */\n }\n }\n\n return { isDev: false, reason: \"production-class environment\" };\n}\n\n/**\n * Decide whether a license-payload `aud` entry matches the current\n * runtime audience. Supports exact match and `*.example.com` wildcard\n * matching for subdomain binding. Returns the matched `aud` pattern\n * on success so it can be recorded for audit, or null on miss.\n *\n * Match rules:\n * - \"foo\" matches \"foo\" exactly\n * - \"*.example.com\" matches \"example.com\" AND any \"<sub>.example.com\"\n * - \"*\" matches any non-empty audience (intentionally permissive; use\n * for trial/site licenses that span all of a customer's deployments)\n *\n * Runtime audience of `null` matches `\"*\"` only — a Node deployment\n * without DVAI_AUDIENCE set can activate \"any-domain\" licenses but\n * not domain-bound ones. This is the safe default; operators that\n * want stricter binding set DVAI_AUDIENCE explicitly.\n */\nexport function matchAudience(\n runtimeAudience: string | null,\n audClaim: string[],\n): string | null {\n if (runtimeAudience === null) {\n return audClaim.includes(\"*\") ? \"*\" : null;\n }\n const runtime = runtimeAudience.toLowerCase();\n for (const pattern of audClaim) {\n const p = pattern.toLowerCase();\n if (p === \"*\") return pattern; // permissive wildcard\n if (p === runtime) return pattern; // exact match\n if (p.startsWith(\"*.\")) {\n const suffix = p.slice(2);\n if (runtime === suffix || runtime.endsWith(\".\" + suffix)) {\n return pattern;\n }\n }\n }\n return null;\n}\n","/**\n * License-file discovery for the JS/TS SDK.\n *\n * The SDK reads the license JWT from (in priority order):\n *\n * 1. An explicit string literal passed as `licenseToken` in DVAIConfig\n * — useful for CI / serverless / contexts where reading a file isn't\n * practical and the operator wants to inject via env var instead.\n *\n * 2. A path passed as `licenseKeyPath` in DVAIConfig — the developer\n * points the SDK at a file they've placed somewhere non-default.\n *\n * 3. The `DVAI_LICENSE_PATH` env var — same as (2) but driven by\n * process environment, helpful for containerised deployments.\n *\n * 4. Auto-discovery from platform-default locations (see below) —\n * the dev-friendly happy path. Drop the file at the convention\n * location and forget about it.\n *\n * Default discovery paths per JS-side platform:\n *\n * - **Node.js**: looks for `dvai-license.jwt` in `process.cwd()` and\n * in `<package-root>/dvai-license.jwt` (one level up). Mirrors how\n * `.env` files are discovered.\n *\n * - **Browser**: fetches `/dvai-license.jwt` from the same origin. The\n * file must be served alongside `mockServiceWorker.js` — typically\n * in `public/` for Vite/Webpack apps. The HTTP fetch is cached by\n * the browser so this is one round-trip on startup, not per request.\n *\n * - **Capacitor**: fetches `/dvai-license.jwt` from the bundled web\n * assets (Capacitor.convertFileSrc on the public/ folder). The\n * native-side validator (in DVAIBridge.iOS / .Android) is the\n * authoritative binding for native bundle ids; this JS-side check\n * is a soft signal only.\n *\n * Returning `null` means \"no license file found\"; the validator treats\n * that as the free-tier case (after dev-mode bypass).\n */\n\n/**\n * Default filename the SDK looks for. Chosen to be self-documenting and\n * to encourage commit-to-vcs (so the license travels with the code,\n * audited and reviewable by the team).\n */\nexport const DEFAULT_LICENSE_FILENAME = \"dvai-license.jwt\";\n\nexport interface LicenseDiscoveryOptions {\n /** Pre-loaded JWT string (skips all filesystem / fetch lookups). */\n token?: string;\n /** Explicit path or URL to load from. Overrides auto-discovery. */\n path?: string;\n}\n\n/**\n * Best-effort load of a license JWT. Returns the raw token string on\n * success or null on miss. Errors during loading (file not found,\n * network timeout) collapse to null — the validator's responsibility\n * is to handle the no-license case gracefully, not the discovery\n * layer's.\n */\nexport async function discoverLicenseToken(\n opts: LicenseDiscoveryOptions = {},\n): Promise<{ token: string; source: string } | null> {\n // 1. Explicit token wins.\n if (typeof opts.token === \"string\" && opts.token.length > 0) {\n return { token: opts.token.trim(), source: \"config.licenseToken\" };\n }\n\n // 2. Explicit path (config option).\n if (typeof opts.path === \"string\" && opts.path.length > 0) {\n const loaded = await tryLoadFromPath(opts.path);\n if (loaded !== null) return { token: loaded, source: opts.path };\n return null; // explicit path that didn't load is a real miss, not a silent fallthrough\n }\n\n // 3. Env-var path.\n if (typeof process !== \"undefined\" && process.env?.DVAI_LICENSE_PATH) {\n const envPath = process.env.DVAI_LICENSE_PATH;\n const loaded = await tryLoadFromPath(envPath);\n if (loaded !== null) return { token: loaded, source: `DVAI_LICENSE_PATH=${envPath}` };\n }\n\n // 4. Env-var inline token (alternative to file for serverless).\n if (typeof process !== \"undefined\" && process.env?.DVAI_LICENSE_TOKEN) {\n return {\n token: process.env.DVAI_LICENSE_TOKEN.trim(),\n source: \"DVAI_LICENSE_TOKEN env var\",\n };\n }\n\n // 5. Platform default-location auto-discovery.\n return await tryAutoDiscover();\n}\n\nasync function tryLoadFromPath(p: string): Promise<string | null> {\n // URLs (browser + Node 18+ fetch) — anything with a scheme.\n if (/^https?:\\/\\//i.test(p)) {\n return await tryFetch(p);\n }\n // Otherwise treat as filesystem path. Use a dynamic import so this\n // module stays browser-safe; `fs/promises` is only imported when we're\n // actually about to read a path.\n return await tryFsRead(p);\n}\n\nasync function tryFetch(url: string): Promise<string | null> {\n try {\n const res = await fetch(url, { method: \"GET\" });\n if (!res.ok) return null;\n const text = (await res.text()).trim();\n return text.length > 0 ? text : null;\n } catch {\n return null;\n }\n}\n\nasync function tryFsRead(path: string): Promise<string | null> {\n try {\n // Dynamic import keeps `fs/promises` out of the browser bundle.\n // tsup/vite will tree-shake this out when bundling for web.\n const fs = await import(\"node:fs/promises\");\n const buf = await fs.readFile(path, \"utf-8\");\n const text = buf.trim();\n return text.length > 0 ? text : null;\n } catch {\n return null;\n }\n}\n\nasync function tryAutoDiscover(): Promise<{ token: string; source: string } | null> {\n // Browser / Capacitor: try same-origin /dvai-license.jwt.\n if (typeof window !== \"undefined\") {\n const sameOriginUrl = `/${DEFAULT_LICENSE_FILENAME}`;\n const loaded = await tryFetch(sameOriginUrl);\n if (loaded !== null) return { token: loaded, source: sameOriginUrl };\n return null;\n }\n\n // Node: try cwd, then one level up (monorepo root case).\n if (typeof process !== \"undefined\" && typeof process.cwd === \"function\") {\n const path = await import(\"node:path\").catch(() => null);\n if (path === null) return null;\n const candidates = [\n path.join(process.cwd(), DEFAULT_LICENSE_FILENAME),\n path.join(process.cwd(), \"..\", DEFAULT_LICENSE_FILENAME),\n ];\n for (const c of candidates) {\n const loaded = await tryFsRead(c);\n if (loaded !== null) return { token: loaded, source: c };\n }\n }\n return null;\n}\n","/**\n * Type surface for the DVAI-Bridge offline JWT license system.\n *\n * The whole license flow is deliberately small:\n * 1. A signed JWT (produced server-side by your license generator) is\n * either dropped at a platform-default path, pointed at via the\n * `licenseKeyPath` config option, or pasted directly into the\n * `licenseToken` config option.\n * 2. The SDK reads it, verifies the ECDSA P-256 signature against the\n * key registry in `publicKeys.ts`, and checks four runtime claims:\n * - signature must verify against a known kid\n * - `exp` must be in the future\n * - `aud` must include the current audience (hostname / bundleId)\n * - `platforms` must include the current SDK platform\n * 3. The outcome is summarised in a `LicenseStatus` value that the\n * rest of the SDK can dispatch on (commercial/trial → premium\n * behaviour; everything else → free-tier behaviour with the\n * \"Powered by DVAI Bridge\" attribution badge).\n *\n * Nothing in this file makes network calls. The entire flow is offline.\n */\n\n/** Recognised license tiers. Free-tier values are produced internally by\n * the validator; commercial / trial come from the signed token's `tier`\n * claim. Anything unknown collapses to \"free-prod\" defensively. */\nexport type LicenseTier =\n | \"commercial\"\n | \"trial\"\n | \"free-dev\" // running on localhost / debug build — no badge required\n | \"free-prod\" // production deploy with no valid license — badge required\n | \"free-expired\"; // had a valid license but `exp` is past — badge required + warn\n\n/** Payload shape we issue (subset; extra claims tolerated). */\nexport interface DvaiLicensePayload {\n /** Standard JWT issuer claim. Must be `\"DVAI-Bridge\"`. */\n iss: string;\n /** Standard subject — our internal license id. Surfaced in audit logs. */\n sub: string;\n /** Audience binding — array of domains and/or bundle ids permitted to\n * activate this license. Each entry is either an exact string match\n * (e.g. `\"com.acme.app\"`) or a wildcard subdomain pattern\n * (e.g. `\"*.acme.com\"` matches both `acme.com` and `app.acme.com`). */\n aud: string[];\n /** Tier the license grants. `commercial` and `trial` are the live tiers;\n * the validator never produces `free-*` here (those are computed). */\n tier: \"commercial\" | \"trial\";\n /** Which DVAI-Bridge SDK platforms this license activates. The current\n * runtime platform must appear here for the license to apply. */\n platforms: DvaiPlatform[];\n /** Display name of the licensee, for audit logs + user-facing messaging. */\n licensee: string;\n /** Standard JWT issued-at (seconds since Unix epoch). */\n iat: number;\n /** Standard JWT expiry (seconds since Unix epoch). */\n exp: number;\n}\n\n/** Platform identifiers the SDK recognises in license `platforms` claims. */\nexport type DvaiPlatform =\n | \"web\"\n | \"node\"\n | \"ios\"\n | \"android\"\n | \"dotnet\"\n | \"flutter\"\n | \"react-native\"\n | \"capacitor\";\n\n/**\n * Result of license validation. Discriminated union so the consumer's\n * decision tree is exhaustive (\"commercial\" or \"trial\" → premium;\n * everything else → free).\n */\nexport type LicenseStatus =\n | {\n kind: \"commercial\";\n licensee: string;\n expiresAt: number;\n platform: DvaiPlatform;\n audienceMatched: string;\n }\n | {\n kind: \"trial\";\n licensee: string;\n expiresAt: number;\n platform: DvaiPlatform;\n audienceMatched: string;\n }\n | {\n kind: \"free-dev\";\n /** Why dev mode was detected (for logging / dashboard surfacing). */\n reason: string;\n }\n | {\n kind: \"free-prod\";\n /** Why a license could not be loaded or validated. Surfaced via a\n * console warning so the developer can debug. Does NOT throw — the\n * SDK falls back to free tier rather than refusing to start. */\n reason: string;\n }\n | {\n kind: \"free-expired\";\n licensee: string;\n expiredAt: number;\n };\n\n/** Returns true iff `tier` represents a paid / unwatermarked status. */\nexport function isPaidTier(status: LicenseStatus): boolean {\n return status.kind === \"commercial\" || status.kind === \"trial\";\n}\n\n/**\n * Thrown by `LicenseValidator.validateAndAssert()` (and propagated from\n * `DVAI.initialize()`) when an SDK consumer attempts to run the library\n * in a production / release context without a valid commercial or trial\n * license.\n *\n * The error message is intentionally verbose: it tells the developer\n * exactly which check failed (missing file, expired, audience mismatch,\n * etc.), how to resolve it, and where to put the license file once\n * they have one. This is the front line of the BSL 1.1 commercial\n * enforcement story — surface it clearly enough that a developer can\n * unblock themselves without a support ticket.\n *\n * The `status` field carries the underlying `LicenseStatus` so\n * programmatic callers can dispatch on `err.status.kind` if they\n * want to handle \"expired\" differently from \"missing\".\n */\nexport class LicenseRequiredError extends Error {\n /** Stable name set so `err.name === \"LicenseRequiredError\"` works\n * across module-boundary serialisation (e.g. Vite SSR). */\n override readonly name = \"LicenseRequiredError\";\n\n constructor(\n message: string,\n /** The underlying validator status that triggered the throw. */\n public readonly status: LicenseStatus,\n ) {\n super(message);\n // Restore the prototype chain for native-builtin Error in environments\n // (older transpiled CJS targets, some sandboxed iframes) where it\n // gets clobbered. Cheap insurance against `instanceof` surprises.\n Object.setPrototypeOf(this, LicenseRequiredError.prototype);\n }\n}\n"],"mappings":"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","names":["extractMediaParts","messages","last","audio","images","part","buildMultimodalCallable","model","processor","opts","defaultMaxNewTokens","callable","options","prompt","inputs","genArgs","outputs","promptLen","generated","disableModelEncoders","names","name","init_multimodalCallable","__esmMin","TransformersBackend_exports","__export","TransformersBackend","detectWebGPU","tensorToArray","arr","batch","hidden","flat","out","i","flattenContent","content","c","init_TransformersBackend","__esmMin","init_multimodalCallable","config","onProgress","isNode","hasWebGPU","err","resolve","reject","worker","requestId","handleMessage","event","msg","info","progressValue","text","e","errorMessage","initParams","stream","pending","type","data","id","transformers","pipelineFn","env","progressCallback","ModelClass","processorName","ProcessorClass","processor","model","disableModelEncoders","buildMultimodalCallable","inputs","options","requestBody","messages","m","maxNewTokens","temperature","topP","result","generatedText","modelId","backend","generationTimeout","controller","completionId","created","encoder","enqueueChunk","chunk","enqueueFinal","finishReason","finalChunk","timeoutId","streamPromise","TextStreamer","tokenizer","timeoutPromise","_","error","inputArray","raw","promise","ms","timer","val","chatToLegacyCompletion","chatResp","c","legacyCompletionStreamAdapter","chatStream","model","decoder","encoder","buffer","controller","reader","done","value","idx","rawEvent","dataLine","l","payload","chunk","legacyChunk","handleCompletion","body","ctx","promptField","prompt","chatBody","legacyStream","error","init_completions","__esmMin","encodeBase64Url","bytes","binary","b","import_ed25519","init_keys","__esmMin","generatePairingKey","cryptoApi","bytes","encodeBase64Url","generateNonce","signHmac","pairingKey","message","keyBytes","decodeBase64UrlBytes","cryptoKey","sig","verifyHmac","signature","expected","constantTimeEquals","a","b","diff","i","s","b64","padded","binary","composeSignedMessage","nonce","method","path","body","bodyHash","sha256Hex","input","buf","init_handshake","__esmMin","init_keys","heuristicTokPerSec","hints","gpuBase","cpuMul","ramMul","npuBonus","estimate","detectDeviceHints","nav","ramGb","cores","hasGpu","detectDeviceHintsAsync","os","totalMem","init_heuristic","__esmMin","precheck_exports","__export","HardwareTooWeakError","assessCapability","opts","hardwareMinimum","DEFAULT_HARDWARE_MINIMUM","minLocalCapability","DEFAULT_MIN_LOCAL_CAPABILITY","hints","detectDeviceHintsAsync","tokPerSec","heuristicTokPerSec","init_precheck","__esmMin","init_heuristic","handleChatCompletion","body","ctx","headers","intercepted","err","runOnce","backend","stream","SSE_HEADERS","response","error","init_chat","__esmMin","handleEmbeddings","body","ctx","input","vectors","v","i","error","init_embeddings","__esmMin","handleModels","ctx","init_models","__esmMin","init_handlers","__esmMin","init_chat","init_completions","init_embeddings","init_models","getEndpoints","mockUrl","chat","base","chatSuffix","u","parts","import_browser","import_msw","MswTransport","init_msw","__esmMin","init_handlers","opts","ctx","urls","handlers","request","handleChatCompletion","handleCompletion","handleEmbeddings","handleModels","tryBind","server","basePort","maxAttempts","host","port","resolve","reject","onError","err","BASE_PORT","MAX_PORT_ATTEMPTS","init_port_fallback","__esmMin","pickOrigin","origin","cfg","corsHeaders","reqOrigin","allow","headers","readJsonBody","req","chunks","c","raw","writeWhatwgResponse","res","response","extraHeaders","v","k","reader","done","value","text","route","ctx","opts","cors","path","body","reqHeaders","r","handleChatCompletion","handleCompletion","handleEmbeddings","handleModels","dvaiRoutes","key","dvaiHandler","respHeaders","err","HttpTransport","init_http","__esmMin","init_handlers","init_port_fallback","createServer","server","bindHost","port","tryBind","CapacitorTransport","init_capacitor","__esmMin","opts","_ctx","DVAIBridge","result","transports_exports","__export","BASE_PORT","CapacitorTransport","HttpTransport","MAX_PORT_ATTEMPTS","MswTransport","selectTransport","tryBind","input","requested","isCapacitorContext","isBrowserLike","isNode","init_transports","__esmMin","init_msw","init_http","init_capacitor","init_port_fallback","decide","input","eligible","p","x","a","b","order","bestPeer","bestScore","threshold","buildCheckedList","list","peer","score","init_decide","__esmMin","buildNoCapableDeviceResponse","decision","ctx","message","init_error","__esmMin","parseOffloadHeader","headers","raw","k","v","lc","VALID","init_policy","__esmMin","proxyToPeer","peer","req","url","upstreamHeaders","response","headers","v","k","init_proxy","__esmMin","buildOffloadInterceptor","opts","body","ctx","headers","headerValue","readOffloadHeader","peers","modelId","localCapability","decision","decide","jsonResponse","stream","proxied","proxyToPeer","forwardableHeaders","err","errResponse","buildNoCapableDeviceResponse","p","parseOffloadHeader","out","drop","k","v","status","init_forwarder","__esmMin","init_decide","init_error","init_proxy","init_policy","offload_exports","__export","buildNoCapableDeviceResponse","buildOffloadInterceptor","decide","parseOffloadHeader","proxyToPeer","init_offload","__esmMin","init_decide","init_error","init_policy","init_proxy","init_forwarder","defaultCachePath","env","home","createCapabilityCache","IndexedDBCapabilityCache","NodeFsCapabilityCache","InMemoryCapabilityCache","DB_NAME","STORE_NAME","META_STORE_NAME","init_cache","__esmMin","key","score","k","resolve","reject","req","db","tx","value","cachePath","fs","path","raw","c","id","generateDeviceId","cryptoApi","bytes","base64UrlEncode","binary","b","init_deviceId","__esmMin","probeCapability","opts","t0","response","PROBE_PROMPT","elapsedSec","tokensFromUsage","text","tokens","approximateTokenCount","tokPerSec","init_probe","__esmMin","capability_exports","__export","HardwareTooWeakError","InMemoryCapabilityCache","IndexedDBCapabilityCache","NodeFsCapabilityCache","assessCapability","clearCapabilityCache","createCapabilityCache","detectDeviceHints","detectDeviceHintsAsync","ensureDeviceId","generateDeviceId","getCapability","heuristicTokPerSec","probeAndCache","probeCapability","cache","existing","DEVICE_ID_META_KEY","fresh","opts","cached","hints","deviceId","score","init_capability","__esmMin","init_cache","init_heuristic","init_deviceId","init_probe","init_precheck","MDNS_SERVICE_TYPE","init_types","__esmMin","StaticDiscovery","init_static","__esmMin","peers","now","p","peer","listener","err","mdns_node_exports","__export","NodeMdnsDiscovery","init_mdns_node","__esmMin","init_types","opts","mod","factory","response","queryInterval","gcInterval","peer","listener","MDNS_SERVICE_TYPE","err","txt","instanceName","all","srv","a","decoded","srvData","baseUrl","existing","raw","lines","b","map","line","eq","capability","ttl","cutoff","id","mdns_browser_exports","__export","BrowserMdnsDiscovery","init_mdns_browser","__esmMin","_opts","_listener","createMdnsDiscovery","opts","NodeMdnsDiscovery","BrowserMdnsDiscovery","CompositeDiscovery","init_composite","__esmMin","sources","source","unsub","e","merged","peer","existing","listener","err","discovery_exports","__export","BrowserMdnsDiscovery","CompositeDiscovery","MDNS_SERVICE_TYPE","StaticDiscovery","createMdnsDiscovery","init_discovery","__esmMin","init_types","init_static","init_composite","init_mdns_browser","defaultPairingsPath","env","home","createPairingStore","IndexedDBPairingStore","NodeFsPairingStore","InMemoryPairingStore","DB_NAME","STORE_NAME","init_store","__esmMin","peerDeviceId","p","resolve","reject","req","db","tx","cachePath","fs","path","raw","c","PairingPolicy","init_policy","__esmMin","init_handshake","opts","peerDeviceId","existing","ttlMs","req","callback","result","hostKey","fresh","generatePairingKey","pairing_exports","__export","InMemoryPairingStore","IndexedDBPairingStore","NodeFsPairingStore","PairingPolicy","composeSignedMessage","createPairingStore","generateNonce","generatePairingKey","signHmac","verifyHmac","init_pairing","__esmMin","init_handshake","init_store","init_policy","dvai_exports","__export","buildDvaiRoutes","handleCapability","handleHandshake","handleHealth","handlePairQr","handlePairScan","handlePeers","handleProbe","ctx","req","modelId","probeAndCache","body","pairing","err","init_dvai","__esmMin","init_capability","NodeLlamaCppBackend_exports","__export","NodeLlamaCppBackend","init_NodeLlamaCppBackend","__esmMin","config","onProgress","llamaModule","p","ms","resolve","reject","t","v","e","messages","sys","m","lastUser","flatten","c","requestBody","systemPrompt","userPrompt","maxTokens","temperature","topP","promptOpts","text","session","modelId","generationTimeout","controller","completionId","created","encoder","enqueueChunk","chunk","enqueueFinal","finishReason","finalChunk","timeoutId","error","WebLLMBackend_exports","__export","WebLLMBackend","init_WebLLMBackend","__esmMin","config","onProgress","webllm","worker","err","requestBody","result","content","engine","maxBlankChunks","generationTimeout","backend","controller","consecutiveBlanks","timeoutId","asyncChunkGenerator","timeoutPromise","_","reject","streamPromise","chunk","delta","finishReason","error","promise","ms","resolve","timer","val","index_exports","__export","DEFAULT_LICENSE_FILENAME","DVAI","DVAI_PUBLIC_KEYS","LicenseRequiredError","LicenseValidator","PLACEHOLDER_KID","chatToLegacyCompletion","composeSignedMessage","detectWebGPU","dvai","generateNonce","generatePairingKey","isPaidTier","legacyCompletionStreamAdapter","signHmac","verifyHmac","__toCommonJS","import_jose","DVAI_PUBLIC_KEYS","PLACEHOLDER_KID","detectPlatform","g","detectAudience","host","detectDevMode","ls","matchAudience","runtimeAudience","audClaim","runtime","pattern","p","suffix","DEFAULT_LICENSE_FILENAME","discoverLicenseToken","opts","loaded","tryLoadFromPath","envPath","tryAutoDiscover","p","tryFetch","tryFsRead","url","res","text","path","sameOriginUrl","candidates","c","isPaidTier","status","LicenseRequiredError","_LicenseRequiredError","message","LicenseValidator","opts","dev","detectDevMode","discovered","discoverLicenseToken","platform","detectPlatform","audience","detectAudience","status","LicenseRequiredError","buildRequiredErrorMessage","token","runtimeAudience","registry","DVAI_PUBLIC_KEYS","header","parts","headerJson","base64UrlDecodeUtf8","err","asMessage","jwk","PLACEHOLDER_KID","payload","key","joseErrors","exp","isLicensePayload","matched","matchAudience","p","a","x","s","pad","b64","binary","bytes","i","reason","init_TransformersBackend","init_completions","init_handshake","DVAI","config","LicenseValidator","onProgress","assessCapability","result","selectTransport","MswTransport","HttpTransport","CapacitorTransport","ctx","started","buildOffloadInterceptor","offloadConfig","err","error","createCapabilityCache","ensureDeviceId","CompositeDiscovery","StaticDiscovery","createMdnsDiscovery","PairingPolicy","createPairingStore","sources","skipAdvertiseOnDarwin","advertise","peerDeviceId","peerDeviceName","appId","cb","buildDvaiRoutes","self","b","fatalError","e","NodeLlamaCppBackend","customModelId","backend","TransformersBackend","WebLLMBackend","requestBody","inputs","options","opts","probeAndCache","modelId","getCapability","id","dvai"]}
1
+ {"version":3,"sources":["../src/multimodalCallable.ts","../src/TransformersBackend.ts","../src/handlers/completions.ts","../src/rendezvous/keys.ts","../src/pairing/handshake.ts","../src/capability/heuristic.ts","../src/capability/precheck.ts","../src/handlers/chat.ts","../src/handlers/embeddings.ts","../src/handlers/models.ts","../src/handlers/index.ts","../src/transports/msw.ts","../src/transports/port-fallback.ts","../src/transports/http.ts","../src/transports/capacitor.ts","../src/transports/index.ts","../src/offload/decide.ts","../src/offload/error.ts","../src/offload/policy.ts","../src/offload/proxy.ts","../src/offload/forwarder.ts","../src/offload/index.ts","../src/capability/cache.ts","../src/capability/deviceId.ts","../src/capability/probe.ts","../src/capability/index.ts","../src/discovery/types.ts","../src/discovery/static.ts","../src/discovery/mdns-node.ts","../src/discovery/mdns-browser.ts","../src/discovery/composite.ts","../src/discovery/index.ts","../src/pairing/store.ts","../src/pairing/policy.ts","../src/pairing/index.ts","../src/handlers/dvai/index.ts","../src/NodeLlamaCppBackend.ts","../src/WebLLMBackend.ts","../src/index.ts","../src/license/LicenseValidator.ts","../src/license/publicKeys.ts","../src/license/audience.ts","../src/license/discovery.ts","../src/license/types.ts"],"sourcesContent":["/**\n * Shared helpers for the declarative multimodal loader path (used by both\n * the main-thread `TransformersBackend` and the worker-side init handler).\n *\n * The idea: a host app tells dvai-bridge \"load model `X` via class `Y` with\n * processor `Z`, and null fields `F1, F2` after load\". dvai-bridge does it\n * — no model-family knowledge baked into the library. If tomorrow a host\n * wants to run a Qwen-VL or an Idefics model, it's the same config surface;\n * if transformers.js exports the class, this path loads it.\n *\n * Limitation: the generic callable assumes the common `processor(prompt,\n * images, audio, options)` call signature. Most HuggingFace multimodal\n * processors follow this shape; ones that don't (e.g. processors taking\n * kwargs like `{ images, audios, videos }`) should fall back to the\n * main-thread `createPipeline` factory, which gives the host full control.\n * That path is still supported.\n */\n\n/**\n * Extract the media content parts (audio / images) from the last user\n * message in a chat-messages array. Returns `null` for a modality that\n * isn't present, so the processor call can pass-through.\n *\n * Content shape assumed per the OpenAI-compatible convention:\n * { role, content: string | Array<{ type: 'text'|'audio'|'image', ... }> }\n * with dvai-bridge's extension: audio parts carry `{ type: 'audio', data: Float32Array }`.\n */\nexport function extractMediaParts(messages: any[]): {\n\taudio: Float32Array | null;\n\timages: unknown[] | null;\n} {\n\tconst last = messages[messages.length - 1];\n\tif (!last || !Array.isArray(last.content)) {\n\t\treturn { audio: null, images: null };\n\t}\n\tlet audio: Float32Array | null = null;\n\tconst images: unknown[] = [];\n\tfor (const part of last.content) {\n\t\tif (!part) continue;\n\t\tif (part.type === \"audio\" && part.data) {\n\t\t\taudio = part.data as Float32Array;\n\t\t} else if (part.type === \"image\" && (part.image || part.data || part.url)) {\n\t\t\t// Push whichever field the host used; downstream processor accepts\n\t\t\t// various shapes (URL, RawImage, tensor). dvai-bridge stays\n\t\t\t// opinion-free here.\n\t\t\timages.push(part.image ?? part.data ?? part.url);\n\t\t}\n\t}\n\treturn { audio, images: images.length > 0 ? images : null };\n}\n\nexport interface MultimodalCallableOptions {\n\t/** Default max_new_tokens if caller doesn't specify. */\n\tdefaultMaxNewTokens?: number;\n}\n\n/**\n * Build a pipeline-shaped callable `(messages, options) => [{ generated_text }]`\n * that wraps a `(model, processor)` pair loaded by the declarative path.\n * Matches the contract of transformers.js's `pipeline()` output so the rest\n * of the backend code (chat completion, streaming, runPipeline) can treat\n * it interchangeably.\n *\n * The returned function also carries `.tokenizer` (for TextStreamer) and\n * `.dispose()` (for VRAM release during unload) as instance properties.\n */\nexport function buildMultimodalCallable(\n\tmodel: any,\n\tprocessor: any,\n\topts: MultimodalCallableOptions = {},\n): any {\n\tconst defaultMaxNewTokens = opts.defaultMaxNewTokens ?? 1024;\n\n\tconst callable: any = async (messages: any, options: any) => {\n\t\tconst prompt = processor.apply_chat_template(messages, {\n\t\t\tenable_thinking: false,\n\t\t\tadd_generation_prompt: true,\n\t\t});\n\t\tconst { audio, images } = extractMediaParts(messages);\n\t\tconst inputs = await processor(prompt, images, audio, {\n\t\t\tadd_special_tokens: false,\n\t\t});\n\t\tconst genArgs: Record<string, unknown> = {\n\t\t\t...inputs,\n\t\t\tmax_new_tokens: options?.max_new_tokens ?? defaultMaxNewTokens,\n\t\t\ttemperature: options?.temperature ?? 0,\n\t\t\tdo_sample: options?.do_sample ?? false,\n\t\t\ttop_p: options?.top_p ?? 1,\n\t\t};\n\t\tif (options?.streamer) genArgs.streamer = options.streamer;\n\t\tconst outputs = await model.generate(genArgs);\n\t\tconst promptLen = inputs.input_ids.dims.at(-1);\n\t\tconst generated = outputs.slice(null, [promptLen, null]);\n\t\tconst decoded = processor.batch_decode(generated, {\n\t\t\tskip_special_tokens: true,\n\t\t});\n\t\treturn [{ generated_text: decoded[0] ?? \"\" }];\n\t};\n\n\t// TextStreamer uses this to tokenize partial outputs during streaming.\n\tcallable.tokenizer = processor.tokenizer;\n\n\t// dvai-bridge calls this on unload to release VRAM held by the ONNX\n\t// session(s) behind the model. AutoProcessor has no dispose() in current\n\t// transformers.js, so we only drop the model's sessions.\n\tcallable.dispose = async () => {\n\t\ttry {\n\t\t\tawait model.dispose?.();\n\t\t} catch {\n\t\t\t/* ignore */\n\t\t}\n\t};\n\n\treturn callable;\n}\n\n/**\n * Null out named submodules on a loaded model to reclaim memory. Host apps\n * pass a list of field names (e.g., `['vision_encoder']`) based on which\n * modalities they don't use. dvai-bridge treats this as purely declarative —\n * it walks the list and nulls each field if present.\n */\nexport function disableModelEncoders(model: any, names: string[] | undefined): void {\n\tif (!names || names.length === 0) return;\n\tfor (const name of names) {\n\t\ttry {\n\t\t\tif (model && (model as any)[name]) {\n\t\t\t\t(model as any)[name] = null;\n\t\t\t}\n\t\t} catch {\n\t\t\t/* ignore — name didn't exist or was non-writable */\n\t\t}\n\t}\n}\n","/**\n * TransformersBackend: Wraps @huggingface/transformers for local inference.\n * - Multi-modal: supports any Transformers.js pipeline task (text-generation,\n * text-to-image, automatic-speech-recognition, image-to-text, etc.)\n * - Runs inference in a Web Worker to keep the main thread unblocked\n * - Falls back to main-thread pipeline if worker URL is not available\n * - Auto-detects WebGPU, falls back to CPU\n * - Provides OpenAI-compatible chat completion API for text-generation tasks\n * - Supports streaming responses for text tasks\n * - Direct pipeline access via runPipeline() for all modalities\n * - Timeout protection on all operations\n * - Three loader paths, in priority order:\n * 1. Main-thread `createPipeline` factory (when provided) — gives the\n * host complete control; used for models that need a custom\n * processor call signature that the declarative path doesn't cover.\n * 2. Declarative `modelClass` + `processorClass` + `disableEncoders`\n * — works in the worker AND on the main thread. Use this for any\n * multimodal model that follows the common processor signature.\n * 3. Generic `pipeline(task, modelId)` — the default, good for 99% of\n * transformers.js tasks.\n */\n\nimport {\n\tbuildMultimodalCallable,\n\tdisableModelEncoders,\n} from \"./multimodalCallable.js\";\n\nexport type DeviceType = \"webgpu\" | \"cpu\" | \"auto\";\n\n/**\n * Convert a Transformers.js Tensor (or plain array) into a nested number[][].\n * Handles both single-input and batched feature-extraction outputs.\n */\nfunction tensorToArray(t: any): number[][] {\n\tif (!t) return [];\n\tif (Array.isArray(t)) {\n\t\tif (t.length > 0 && Array.isArray(t[0])) return t as number[][];\n\t\treturn [t as number[]];\n\t}\n\tif (typeof t.tolist === \"function\") {\n\t\tconst arr = t.tolist();\n\t\tif (Array.isArray(arr) && arr.length > 0 && Array.isArray(arr[0]))\n\t\t\treturn arr;\n\t\treturn [arr];\n\t}\n\tif (t.data && t.dims) {\n\t\tconst [batch, hidden] =\n\t\t\tt.dims.length === 2 ? t.dims : [1, t.dims[t.dims.length - 1]];\n\t\tconst flat = Array.from(t.data as Iterable<number>);\n\t\tconst out: number[][] = [];\n\t\tfor (let i = 0; i < batch; i++) {\n\t\t\tout.push(flat.slice(i * hidden, (i + 1) * hidden));\n\t\t}\n\t\treturn out;\n\t}\n\treturn [];\n}\n\n/**\n * Aggressive content flattening to satisfy Jinja2 templates (like Llama 3)\n * that expect 'content' to be a string and use filters like '| trim'.\n */\nfunction flattenContent(content: any): string {\n\tif (typeof content === \"string\") return content;\n\tif (Array.isArray(content)) {\n\t\treturn content.map((c) => flattenContent(c)).join(\"\");\n\t}\n\tif (content && typeof content === \"object\") {\n\t\treturn content.text || content.content || JSON.stringify(content);\n\t}\n\treturn String(content || \"\");\n}\n\n/**\n * Supported pipeline tasks from Transformers.js.\n * Common tasks include:\n * - \"text-generation\" (default) — LLM chat/text generation\n * - \"text2text-generation\" — encoder-decoder text models\n * - \"text-to-image\" — image generation from text prompts\n * - \"image-to-text\" — image captioning\n * - \"automatic-speech-recognition\" — audio/speech to text\n * - \"text-to-speech\" — text to audio\n * - \"zero-shot-classification\" — classify without training\n * - \"feature-extraction\" — embeddings\n * - \"translation\" — language translation\n * - \"summarization\" — text summarization\n * - And many more: see https://huggingface.co/docs/transformers.js\n */\nexport type PipelineTask = string;\n\n/**\n * A pipeline-compatible callable function.\n * Accepts messages (chat format) and generation options,\n * returns results in the same shape as a Transformers.js pipeline:\n * [{ generated_text: string }]\n */\nexport type PipelineCallable = (messages: any, options?: any) => Promise<any>;\n\n/**\n * Factory function that the client can supply to customize model loading.\n * Receives the dynamically-imported @huggingface/transformers module and\n * config details; must return a PipelineCallable.\n *\n * This lets the client control *how* the model is loaded and how inference\n * is run, while DVAI handles everything else (MSW, OpenAI endpoint, etc.).\n */\nexport type CreatePipelineFn = (\n\ttransformers: any,\n\tctx: {\n\t\tmodelId: string;\n\t\tdevice: \"webgpu\" | \"wasm\" | \"cpu\";\n\t\tdtype?: string;\n\t\tonProgress?: (info: any) => void;\n\t},\n) => Promise<PipelineCallable>;\n\nexport interface TransformersBackendConfig {\n\tmodelId: string;\n\tdevice: DeviceType;\n\tgenerationTimeout: number;\n\tworkerUrl?: string;\n\t/** The pipeline task to use. Default: \"text-generation\" */\n\tpipelineTask?: PipelineTask;\n\t/** Quantization/DType for the model (e.g. 'q4', 'q8', 'f16'). Default: undefined */\n\tdtype?: string;\n\t/**\n\t * Optional custom pipeline factory. Main-thread only (function closures\n\t * don't cross the Worker boundary — use `modelClass`/`processorClass`\n\t * for the worker path instead). Replaces the default `pipeline()` call.\n\t * Use this when your model's processor takes a non-standard call\n\t * signature that the declarative multimodal callable can't express.\n\t */\n\tcreatePipeline?: CreatePipelineFn;\n\t/**\n\t * Name of a transformers.js export to use as the model class, loaded via\n\t * `ClassName.from_pretrained(modelId)`. Enables the declarative\n\t * multimodal loader path (works in the worker and on main thread).\n\t * Examples: \"Gemma4ForConditionalGeneration\", \"LlavaForConditionalGeneration\",\n\t * \"AutoModelForCausalLM\". When unset, falls back to `pipeline()`.\n\t */\n\tmodelClass?: string;\n\t/**\n\t * Name of a transformers.js export to use as the processor class.\n\t * Only used when `modelClass` is set. Default: \"AutoProcessor\".\n\t */\n\tprocessorClass?: string;\n\t/**\n\t * Model submodule fields to null out after `from_pretrained`, e.g.\n\t * `[\"vision_encoder\"]` for a voice-only host app using a multimodal\n\t * checkpoint. Purely declarative — backend walks the list and nulls\n\t * each named field if present. Unknown/absent names are ignored.\n\t */\n\tdisableEncoders?: string[];\n}\n\nexport interface TransformersProgressInfo {\n\tstatus: string;\n\tname?: string;\n\tfile?: string;\n\tprogress?: number;\n\tloaded?: number;\n\ttotal?: number;\n}\n\n/**\n * Detects whether WebGPU is available in the current environment.\n */\nexport async function detectWebGPU(): Promise<boolean> {\n\tif (typeof navigator === \"undefined\") return false;\n\tif (!(\"gpu\" in navigator)) return false;\n\ttry {\n\t\tconst adapter = await (navigator as any).gpu.requestAdapter();\n\t\treturn adapter !== null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport class TransformersBackend {\n\tprivate pipeline: any = null;\n\tprivate worker: Worker | null = null;\n\tprivate modelId: string;\n\tprivate device: DeviceType;\n\tprivate resolvedDevice: \"webgpu\" | \"wasm\" | \"cpu\" = \"wasm\";\n\tprivate generationTimeout: number;\n\tprivate workerUrl?: string;\n\tprivate pipelineTask: PipelineTask;\n\tprivate dtype?: string;\n\tprivate createPipelineFn?: CreatePipelineFn;\n\tprivate modelClass?: string;\n\tprivate processorClass?: string;\n\tprivate disableEncoders?: string[];\n\tprivate usingWorker: boolean = false;\n\tprivate pendingRequests: Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (value: any) => void;\n\t\t\treject: (error: any) => void;\n\t\t}\n\t> = new Map();\n\tprivate pendingStreams: Map<\n\t\tstring,\n\t\t{\n\t\t\tonChunk: (text: string) => void;\n\t\t\tonComplete: () => void;\n\t\t\tonError: (error: any) => void;\n\t\t}\n\t> = new Map();\n\n\tconstructor(config: TransformersBackendConfig) {\n\t\tthis.modelId = config.modelId;\n\t\tthis.device = config.device;\n\t\tthis.generationTimeout = config.generationTimeout;\n\t\tthis.workerUrl = config.workerUrl;\n\t\tthis.pipelineTask = config.pipelineTask || \"text-generation\";\n\t\tthis.dtype = config.dtype;\n\t\tthis.createPipelineFn = config.createPipeline;\n\t\tthis.modelClass = config.modelClass;\n\t\tthis.processorClass = config.processorClass;\n\t\tthis.disableEncoders = config.disableEncoders;\n\t}\n\n\tasync initialize(onProgress?: (info: any) => void): Promise<void> {\n\t\t// Resolve device.\n\t\t//\n\t\t// Runtime split:\n\t\t// - Browser → onnxruntime-web → accepts \"wasm\" + \"webgpu\". CPU is \"wasm\".\n\t\t// - Node → onnxruntime-node → accepts \"cpu\" + \"dml\" + \"webgpu\" (experimental).\n\t\t// Throws on \"wasm\".\n\t\t//\n\t\t// Detect once and route accordingly. Without this branch, Node hosts\n\t\t// (the v3.1 Hub, the node-langchain example, etc.) fail to initialize\n\t\t// with \"Unsupported device: \\\"wasm\\\"\".\n\t\tconst isNode =\n\t\t\ttypeof window === \"undefined\" &&\n\t\t\ttypeof process !== \"undefined\" &&\n\t\t\t(process as any).versions?.node !== undefined;\n\n\t\tif (this.device === \"auto\") {\n\t\t\tconst hasWebGPU = await detectWebGPU();\n\t\t\tthis.resolvedDevice = hasWebGPU ? \"webgpu\" : isNode ? \"cpu\" : \"wasm\";\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI/Transformers] Auto-detected device: ${this.resolvedDevice}`,\n\t\t\t);\n\t\t} else if (this.device === \"cpu\") {\n\t\t\tthis.resolvedDevice = isNode ? \"cpu\" : \"wasm\";\n\t\t} else {\n\t\t\tthis.resolvedDevice = this.device as \"webgpu\" | \"wasm\" | \"cpu\";\n\t\t}\n\n\t\t// Worker-based initialization is the DEFAULT path. Running inference\n\t\t// off the main thread is a baseline guarantee dvai-bridge makes to\n\t\t// every host app — no host should have to worry about Gemma / Llama /\n\t\t// whisper forward-passes stalling their UI. The Worker constructor\n\t\t// auto-falls back to main-thread only in truly broken environments\n\t\t// (Worker unavailable, or the bundled worker file failed to load).\n\t\tif (this.workerUrl && typeof Worker !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tawait this.initializeWithWorker(onProgress);\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\t// Raised to error level + remediation tip. Silent main-thread\n\t\t\t\t// fallback was hiding deployment bugs where the worker file\n\t\t\t\t// wasn't copied to public/.\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[DVAI/Transformers] Worker initialization FAILED — falling back to main thread. \" +\n\t\t\t\t\t\t\"This WILL block the UI during inference. Check that the worker file is \" +\n\t\t\t\t\t\t`deployed at \"${this.workerUrl}\" (run \\`dvai-bridge init\\` to copy it).`,\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t\tthis.worker = null;\n\t\t\t}\n\t\t} else if (!this.workerUrl) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[DVAI/Transformers] No workerUrl configured — running on main thread. \" +\n\t\t\t\t\t\"This blocks the UI during inference. Set `transformersWorkerUrl` \" +\n\t\t\t\t\t\"(defaults to '/dvai-transformers.worker.js') to enable the worker path.\",\n\t\t\t);\n\t\t}\n\n\t\t// Fallback: main-thread pipeline\n\t\tawait this.initializeMainThread(onProgress);\n\t}\n\n\tprivate async initializeWithWorker(\n\t\tonProgress?: (info: any) => void,\n\t): Promise<void> {\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst worker = new Worker(this.workerUrl!, { type: \"module\" });\n\t\t\tconst requestId = this.generateRequestId();\n\n\t\t\tconst handleMessage = (event: MessageEvent) => {\n\t\t\t\tconst msg = event.data;\n\t\t\t\tif (msg.id !== requestId) return;\n\n\t\t\t\tswitch (msg.type) {\n\t\t\t\t\tcase \"progress\":\n\t\t\t\t\t\tif (onProgress) {\n\t\t\t\t\t\t\tconst info = msg.data;\n\t\t\t\t\t\t\tconst progressValue = info.progress ?? 0;\n\t\t\t\t\t\t\tconst text =\n\t\t\t\t\t\t\t\tinfo.status === \"progress\"\n\t\t\t\t\t\t\t\t\t? `Downloading ${info.file}: ${Math.round(progressValue)}%`\n\t\t\t\t\t\t\t\t\t: info.status === \"ready\"\n\t\t\t\t\t\t\t\t\t\t? \"Model ready\"\n\t\t\t\t\t\t\t\t\t\t: `${info.status}${info.file ? `: ${info.file}` : \"\"}`;\n\t\t\t\t\t\t\tonProgress({\n\t\t\t\t\t\t\t\ttext,\n\t\t\t\t\t\t\t\tprogress: progressValue / 100,\n\t\t\t\t\t\t\t\ttimeElapsed: 0,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"init_complete\":\n\t\t\t\t\t\tworker.removeEventListener(\"message\", handleMessage);\n\t\t\t\t\t\tthis.worker = worker;\n\t\t\t\t\t\tthis.usingWorker = true;\n\n\t\t\t\t\t\t// Set up persistent message handler for future requests\n\t\t\t\t\t\tworker.addEventListener(\"message\", (e: MessageEvent) =>\n\t\t\t\t\t\t\tthis.handleWorkerMessage(e),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"[DVAI/Transformers] Initialized with Web Worker (main thread unblocked)\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\tworker.removeEventListener(\"message\", handleMessage);\n\t\t\t\t\t\tworker.terminate();\n\t\t\t\t\t\treject(new Error(msg.error));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tworker.addEventListener(\"message\", handleMessage);\n\t\t\tworker.addEventListener(\"error\", (err: any) => {\n\t\t\t\tworker.removeEventListener(\"message\", handleMessage);\n\t\t\t\tconst errorMessage =\n\t\t\t\t\terr.message ||\n\t\t\t\t\t(err.error ? err.error.message : \"Unknown worker error\");\n\t\t\t\treject(new Error(`Worker error: ${errorMessage}`));\n\t\t\t});\n\n\t\t\tconst initParams = {\n\t\t\t\ttype: \"init\",\n\t\t\t\tid: requestId,\n\t\t\t\tpipelineTask: this.pipelineTask,\n\t\t\t\tmodelId: this.modelId,\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tdtype: this.dtype,\n\t\t\t\t// Declarative loader config — worker uses these to pick the\n\t\t\t\t// model+processor path when modelClass is set, otherwise it\n\t\t\t\t// falls back to pipeline(task, modelId). Host app controls\n\t\t\t\t// which class to load, which processor to pair with it, and\n\t\t\t\t// which encoder submodules to null after load.\n\t\t\t\tmodelClass: this.modelClass,\n\t\t\t\tprocessorClass: this.processorClass,\n\t\t\t\tdisableEncoders: this.disableEncoders,\n\t\t\t};\n\t\t\tconsole.log(\"[DVAI/Transformers] Sending init to worker:\", initParams);\n\t\t\tworker.postMessage(initParams);\n\t\t});\n\t}\n\n\tprivate handleWorkerMessage(event: MessageEvent) {\n\t\tconst msg = event.data;\n\t\tif (!msg.id) return;\n\n\t\t// Streaming messages first — they share an id with pendingStreams\n\t\tconst stream = this.pendingStreams.get(msg.id);\n\t\tif (stream) {\n\t\t\tswitch (msg.type) {\n\t\t\t\tcase \"stream_chunk\":\n\t\t\t\t\tstream.onChunk(msg.text);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"stream_complete\":\n\t\t\t\t\tthis.pendingStreams.delete(msg.id);\n\t\t\t\t\tstream.onComplete();\n\t\t\t\t\treturn;\n\t\t\t\tcase \"error\":\n\t\t\t\t\tthis.pendingStreams.delete(msg.id);\n\t\t\t\t\tstream.onError(\n\t\t\t\t\t\tnew Error(msg.error || \"Unknown worker internal error\"),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tconst pending = this.pendingRequests.get(msg.id);\n\t\tif (!pending) return;\n\n\t\tswitch (msg.type) {\n\t\t\tcase \"generate_complete\":\n\t\t\tcase \"embed_complete\":\n\t\t\t\tthis.pendingRequests.delete(msg.id);\n\t\t\t\tpending.resolve(msg.data);\n\t\t\t\tbreak;\n\t\t\tcase \"unload_complete\":\n\t\t\t\tthis.pendingRequests.delete(msg.id);\n\t\t\t\tpending.resolve(undefined);\n\t\t\t\tbreak;\n\t\t\tcase \"error\":\n\t\t\t\tthis.pendingRequests.delete(msg.id);\n\t\t\t\tpending.reject(new Error(msg.error || \"Unknown worker internal error\"));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate sendWorkerRequest(\n\t\ttype: string,\n\t\tdata: Record<string, any> = {},\n\t): Promise<any> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst id = this.generateRequestId();\n\t\t\tthis.pendingRequests.set(id, { resolve, reject });\n\t\t\tthis.worker!.postMessage({ type, id, ...data });\n\t\t});\n\t}\n\n\tprivate async initializeMainThread(\n\t\tonProgress?: (info: any) => void,\n\t): Promise<void> {\n\t\t// @ts-ignore - module resolved at runtime after pnpm install\n\t\tconst transformers = await import(\"@huggingface/transformers\");\n\t\tconst { pipeline: pipelineFn, env } = transformers;\n\n\t\t// Only allow local models if explicitly requested or if we're in a specific environment.\n\t\t// Defaulting to false ensures CDN fallback when local models are not provided.\n\t\t// Force remote models from Hugging Face CDN\n\t\tenv.allowLocalModels = false;\n\t\tenv.allowRemoteModels = true;\n\t\tenv.remoteHost = \"https://huggingface.co\";\n\t\tenv.remotePathTemplate = \"{model}/resolve/{revision}/\";\n\n\t\tconst progressCallback = onProgress\n\t\t\t? (info: TransformersProgressInfo) => {\n\t\t\t\t\tconst progressValue = info.progress ?? 0;\n\t\t\t\t\tconst text =\n\t\t\t\t\t\tinfo.status === \"progress\"\n\t\t\t\t\t\t\t? `Downloading ${info.file}: ${Math.round(progressValue)}%`\n\t\t\t\t\t\t\t: info.status === \"ready\"\n\t\t\t\t\t\t\t\t? `Model ready (${this.pipelineTask})`\n\t\t\t\t\t\t\t\t: `${info.status}${info.file ? `: ${info.file}` : \"\"}`;\n\t\t\t\t\tonProgress({ text, progress: progressValue / 100, timeElapsed: 0 });\n\t\t\t\t}\n\t\t\t: undefined;\n\n\t\tif (this.createPipelineFn) {\n\t\t\t// Client-supplied custom pipeline factory — use it instead of pipeline().\n\t\t\tconsole.log(\"[DVAI/Transformers] Using custom createPipeline factory.\");\n\t\t\tthis.pipeline = await this.createPipelineFn(transformers, {\n\t\t\t\tmodelId: this.modelId,\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tdtype: this.dtype,\n\t\t\t\tonProgress: progressCallback,\n\t\t\t});\n\t\t} else if (this.modelClass) {\n\t\t\t// Declarative model+processor loader — same contract as the\n\t\t\t// worker path. Keeps behavior consistent regardless of which\n\t\t\t// path the env ends up on (e.g., a dev running without the\n\t\t\t// worker file deployed yet).\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI/Transformers] Using declarative loader: ${this.modelClass} + ${this.processorClass || \"AutoProcessor\"}`,\n\t\t\t);\n\t\t\tconst ModelClass = (transformers as any)[this.modelClass];\n\t\t\tif (!ModelClass) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`transformers.js has no export named \"${this.modelClass}\". Check your modelClass config.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst processorName = this.processorClass || \"AutoProcessor\";\n\t\t\tconst ProcessorClass = (transformers as any)[processorName];\n\t\t\tif (!ProcessorClass) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`transformers.js has no export named \"${processorName}\". Check your processorClass config.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst processor = await ProcessorClass.from_pretrained(this.modelId, {\n\t\t\t\tprogress_callback: progressCallback,\n\t\t\t});\n\t\t\tconst model = await ModelClass.from_pretrained(this.modelId, {\n\t\t\t\tdtype: this.dtype as any,\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tprogress_callback: progressCallback,\n\t\t\t});\n\t\t\tdisableModelEncoders(model, this.disableEncoders);\n\t\t\tthis.pipeline = buildMultimodalCallable(model, processor);\n\t\t} else {\n\t\t\tthis.pipeline = await pipelineFn(this.pipelineTask as any, this.modelId, {\n\t\t\t\tdevice: this.resolvedDevice,\n\t\t\t\tprogress_callback: progressCallback,\n\t\t\t\tdtype: this.dtype as any,\n\t\t\t});\n\t\t}\n\t\tthis.usingWorker = false;\n\n\t\tif (this.resolvedDevice === \"wasm\" || this.resolvedDevice === \"cpu\") {\n\t\t\tconsole.warn(\n\t\t\t\t`[DVAI/Transformers] Running on main thread with ${this.resolvedDevice.toUpperCase()} (CPU). ` +\n\t\t\t\t\t(this.resolvedDevice === \"wasm\"\n\t\t\t\t\t\t? \"Inference may block the UI. Set `workerUrl` to a deployed transformers worker for better performance.\"\n\t\t\t\t\t\t: \"(Node host — no UI thread to block.)\"),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\t\"[DVAI/Transformers] Initialized on main thread (WebGPU compute is async)\",\n\t\t\t);\n\t\t}\n\t}\n\n\tgetPipelineTask(): PipelineTask {\n\t\treturn this.pipelineTask;\n\t}\n\n\tgetResolvedDevice(): \"webgpu\" | \"wasm\" | \"cpu\" {\n\t\treturn this.resolvedDevice;\n\t}\n\n\tisWorkerBased(): boolean {\n\t\treturn this.usingWorker;\n\t}\n\n\tgetPipeline(): any {\n\t\treturn this.pipeline;\n\t}\n\n\t/**\n\t * Returns whether the current task is a text generation task\n\t * (supports OpenAI-compatible chat completion API).\n\t */\n\tisTextTask(): boolean {\n\t\treturn [\n\t\t\t\"text-generation\",\n\t\t\t\"text2text-generation\",\n\t\t\t\"summarization\",\n\t\t\t\"translation\",\n\t\t\t\"image-text-to-text\",\n\t\t\t\"any-to-any\",\n\t\t].includes(this.pipelineTask);\n\t}\n\n\t/**\n\t * Run the pipeline directly with arbitrary inputs.\n\t * Use this for non-text-generation tasks (text-to-image, STT, etc.)\n\t * or when you need full control over the pipeline output.\n\t *\n\t * @param inputs - Input data (text, audio buffer, image URL, etc.)\n\t * @param options - Pipeline-specific options\n\t * @returns Raw pipeline output (varies by task)\n\t */\n\tasync runPipeline(inputs: any, options?: Record<string, any>): Promise<any> {\n\t\tif (this.usingWorker && this.worker) {\n\t\t\treturn this.withTimeout(\n\t\t\t\tthis.sendWorkerRequest(\"generate\", {\n\t\t\t\t\trequestBody: { raw: true, inputs, options },\n\t\t\t\t}),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t}\n\t\tif (!this.pipeline)\n\t\t\tthrow new Error(\"Transformers.js pipeline not initialized\");\n\t\treturn this.withTimeout(\n\t\t\tthis.pipeline(inputs, options),\n\t\t\tthis.generationTimeout,\n\t\t);\n\t}\n\n\t/**\n\t * OpenAI-compatible non-streaming chat completion.\n\t * Only works for text-generation / text2text-generation tasks.\n\t */\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.isTextTask()) {\n\t\t\tthrow new Error(\n\t\t\t\t`chatCompletion() is only available for text-generation tasks. ` +\n\t\t\t\t\t`Current task: \"${this.pipelineTask}\". Use runPipeline() instead.`,\n\t\t\t);\n\t\t}\n\n\t\t// Aggressive sanitization: ensure content is ALWAYS a string\n\t\tconst messages = (requestBody.messages || []).map((m: any) => ({\n\t\t\t...m,\n\t\t\tcontent: flattenContent(m.content),\n\t\t}));\n\n\t\tconst maxNewTokens =\n\t\t\trequestBody.max_tokens ?? requestBody.max_completion_tokens ?? 256;\n\t\tconst temperature = requestBody.temperature ?? 0.7;\n\t\tconst topP = requestBody.top_p ?? 1.0;\n\n\t\tconst options: any = {\n\t\t\tmax_new_tokens: maxNewTokens,\n\t\t\ttemperature,\n\t\t\ttop_p: topP,\n\t\t\tdo_sample: temperature > 0,\n\t\t\treturn_full_text: false,\n\t\t};\n\n\t\tlet result: any;\n\t\tif (this.usingWorker && this.worker) {\n\t\t\t// Run inference in worker\n\t\t\tresult = await this.withTimeout(\n\t\t\t\tthis.sendWorkerRequest(\"generate\", {\n\t\t\t\t\trequestBody: { ...requestBody, messages },\n\t\t\t\t}),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t} else if (this.pipeline) {\n\t\t\t// Run inference on main thread\n\t\t\tconsole.log(\"[DVAI/Transformers] Running local inference:\", messages);\n\t\t\tresult = await this.withTimeout(\n\t\t\t\tthis.pipeline(messages, options),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(\"Transformers.js backend not initialized\");\n\t\t}\n\n\t\t// Extract generated text\n\t\tconst generatedText = (result as any)?.[0]?.generated_text ?? \"\";\n\t\tconst content =\n\t\t\ttypeof generatedText === \"string\"\n\t\t\t\t? generatedText\n\t\t\t\t: Array.isArray(generatedText)\n\t\t\t\t\t? (generatedText[generatedText.length - 1]?.content ?? \"\")\n\t\t\t\t\t: String(generatedText);\n\n\t\treturn {\n\t\t\tid: `chatcmpl-${Date.now()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel: this.modelId,\n\t\t\tchoices: [\n\t\t\t\t{\n\t\t\t\t\tindex: 0,\n\t\t\t\t\tmessage: { role: \"assistant\", content },\n\t\t\t\t\tfinish_reason: \"stop\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tusage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n\t\t};\n\t}\n\n\t/**\n\t * OpenAI-compatible streaming chat completion using real token-level streaming\n\t * via Transformers.js TextStreamer. Returns a ReadableStream of SSE-formatted data.\n\t */\n\tcreateStreamingResponse(requestBody: any): ReadableStream<Uint8Array> {\n\t\tif (!this.isTextTask()) {\n\t\t\tthrow new Error(\n\t\t\t\t`Streaming chat completion is only available for text-generation tasks. ` +\n\t\t\t\t\t`Current task: \"${this.pipelineTask}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst modelId = this.modelId;\n\t\tconst backend = this;\n\t\tconst generationTimeout = this.generationTimeout;\n\n\t\treturn new ReadableStream<Uint8Array>({\n\t\t\tasync start(controller) {\n\t\t\t\tconst completionId = `chatcmpl-${Date.now()}`;\n\t\t\t\tconst created = Math.floor(Date.now() / 1000);\n\t\t\t\tconst encoder = new TextEncoder();\n\n\t\t\t\tconst enqueueChunk = (text: string) => {\n\t\t\t\t\tconst chunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{ index: 0, delta: { content: text }, finish_reason: null },\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tconst enqueueFinal = (finishReason: string = \"stop\") => {\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [{ index: 0, delta: {}, finish_reason: finishReason }],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst streamPromise = new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\tif (backend.usingWorker && backend.worker) {\n\t\t\t\t\t\t\tconst id = backend.generateRequestId();\n\t\t\t\t\t\t\tbackend.pendingStreams.set(id, {\n\t\t\t\t\t\t\t\tonChunk: enqueueChunk,\n\t\t\t\t\t\t\t\tonComplete: resolve,\n\t\t\t\t\t\t\t\tonError: reject,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tbackend.worker.postMessage({\n\t\t\t\t\t\t\t\ttype: \"generate_stream\",\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\trequestBody,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else if (backend.pipeline) {\n\t\t\t\t\t\t\t// Main-thread streaming via TextStreamer.\n\t\t\t\t\t\t\t// Lazy-load @huggingface/transformers to access TextStreamer without\n\t\t\t\t\t\t\t// importing it at top-level (keeps the main bundle lean).\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\t\t\t\t\t\t(async () => {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t// @ts-ignore - module resolved at runtime\n\t\t\t\t\t\t\t\t\tconst { TextStreamer } =\n\t\t\t\t\t\t\t\t\t\tawait import(\"@huggingface/transformers\");\n\t\t\t\t\t\t\t\t\tconst tokenizer = (backend.pipeline as any).tokenizer;\n\t\t\t\t\t\t\t\t\tif (!tokenizer) {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t\t\t\"Streaming requires a tokenizer on the pipeline.\",\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tconst messages = (requestBody.messages || []).map(\n\t\t\t\t\t\t\t\t\t\t(m: any) => ({\n\t\t\t\t\t\t\t\t\t\t\t...m,\n\t\t\t\t\t\t\t\t\t\t\tcontent: flattenContent(m.content),\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tconst options: any = {\n\t\t\t\t\t\t\t\t\t\tmax_new_tokens:\n\t\t\t\t\t\t\t\t\t\t\trequestBody.max_tokens ??\n\t\t\t\t\t\t\t\t\t\t\trequestBody.max_completion_tokens ??\n\t\t\t\t\t\t\t\t\t\t\t256,\n\t\t\t\t\t\t\t\t\t\ttemperature: requestBody.temperature ?? 0.7,\n\t\t\t\t\t\t\t\t\t\ttop_p: requestBody.top_p ?? 1.0,\n\t\t\t\t\t\t\t\t\t\tdo_sample: (requestBody.temperature ?? 0.7) > 0,\n\t\t\t\t\t\t\t\t\t\treturn_full_text: false,\n\t\t\t\t\t\t\t\t\t\tstreamer: new TextStreamer(tokenizer, {\n\t\t\t\t\t\t\t\t\t\t\tskip_prompt: true,\n\t\t\t\t\t\t\t\t\t\t\tskip_special_tokens: true,\n\t\t\t\t\t\t\t\t\t\t\tcallback_function: (text: string) => {\n\t\t\t\t\t\t\t\t\t\t\t\tif (text) enqueueChunk(text);\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t\tawait backend.pipeline(messages, options);\n\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\t\treject(e);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(new Error(\"Transformers.js backend not initialized\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\t\t\t\ttimeoutId = setTimeout(\n\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t`Generation timed out after ${generationTimeout}ms`,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tgenerationTimeout,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tawait Promise.race([streamPromise, timeoutPromise]);\n\t\t\t\t\tenqueueFinal(\"stop\");\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tconsole.error(\"[DVAI/Transformers] Stream error:\", error.message);\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(\n\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: error.message })}\\n\\n`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Generate embeddings for one or more text inputs.\n\t * Requires the pipeline to have been initialized with pipelineTask=\"feature-extraction\".\n\t *\n\t * @param inputs - A single string or array of strings to embed\n\t * @returns An array of embedding vectors (one per input)\n\t */\n\tasync embedding(inputs: string | string[]): Promise<number[][]> {\n\t\tif (this.pipelineTask !== \"feature-extraction\") {\n\t\t\tthrow new Error(\n\t\t\t\t`embedding() requires pipelineTask=\"feature-extraction\". Current task: \"${this.pipelineTask}\".`,\n\t\t\t);\n\t\t}\n\n\t\tconst inputArray = Array.isArray(inputs) ? inputs : [inputs];\n\n\t\tif (this.usingWorker && this.worker) {\n\t\t\tconst result = await this.withTimeout(\n\t\t\t\tthis.sendWorkerRequest(\"embed\", {\n\t\t\t\t\tinputs: inputArray,\n\t\t\t\t\tpooling: \"mean\",\n\t\t\t\t\tnormalize: true,\n\t\t\t\t}),\n\t\t\t\tthis.generationTimeout,\n\t\t\t);\n\t\t\treturn result as number[][];\n\t\t}\n\n\t\tif (!this.pipeline) {\n\t\t\tthrow new Error(\"Transformers.js pipeline not initialized\");\n\t\t}\n\n\t\tconst raw = await this.withTimeout(\n\t\t\tthis.pipeline(inputArray, { pooling: \"mean\", normalize: true }),\n\t\t\tthis.generationTimeout,\n\t\t);\n\t\treturn tensorToArray(raw);\n\t}\n\n\tasync unload(): Promise<void> {\n\t\tif (this.usingWorker && this.worker) {\n\t\t\ttry {\n\t\t\t\tawait this.sendWorkerRequest(\"unload\");\n\t\t\t} catch (_) {\n\t\t\t\t/* best effort */\n\t\t\t}\n\t\t\tthis.worker.terminate();\n\t\t\tthis.worker = null;\n\t\t}\n\n\t\tif (this.pipeline) {\n\t\t\tif (typeof this.pipeline.dispose === \"function\") {\n\t\t\t\tawait this.pipeline.dispose();\n\t\t\t}\n\t\t\tthis.pipeline = null;\n\t\t}\n\n\t\tthis.pendingRequests.clear();\n\t\tthis.usingWorker = false;\n\t}\n\n\t/** Wraps a promise with a timeout. */\n\tprivate withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() => reject(new Error(`Generation timed out after ${ms}ms`)),\n\t\t\t\tms,\n\t\t\t);\n\t\t\tpromise\n\t\t\t\t.then((val) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tresolve(val);\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(err);\n\t\t\t\t});\n\t\t});\n\t}\n\n\tprivate generateRequestId(): string {\n\t\treturn `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n\t}\n}\n","import type { HandlerContext } from \"./context\";\n\n/**\n * Convert an OpenAI chat.completion response body into the legacy\n * text_completion shape used by POST /v1/completions.\n */\nexport function chatToLegacyCompletion(chatResp: any): any {\n return {\n id:\n (chatResp.id || \"\").replace(\"chatcmpl-\", \"cmpl-\") || `cmpl-${Date.now()}`,\n object: \"text_completion\",\n created: chatResp.created ?? Math.floor(Date.now() / 1000),\n model: chatResp.model,\n choices: (chatResp.choices || []).map((c: any) => ({\n text: c.message?.content ?? \"\",\n index: c.index ?? 0,\n finish_reason: c.finish_reason ?? \"stop\",\n logprobs: null,\n })),\n usage: chatResp.usage ?? {\n prompt_tokens: 0,\n completion_tokens: 0,\n total_tokens: 0,\n },\n };\n}\n\n/**\n * Wraps an SSE stream of chat.completion.chunk events and rewrites each\n * event as a legacy text_completion chunk. Preserves event boundaries.\n */\nexport function legacyCompletionStreamAdapter(\n chatStream: ReadableStream<Uint8Array>,\n model: string,\n): ReadableStream<Uint8Array> {\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n let buffer = \"\";\n\n return new ReadableStream<Uint8Array>({\n async start(controller) {\n const reader = chatStream.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n let idx: number;\n while ((idx = buffer.indexOf(\"\\n\\n\")) !== -1) {\n const rawEvent = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 2);\n const dataLine = rawEvent\n .split(\"\\n\")\n .find((l) => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n const payload = dataLine.slice(\"data:\".length).trim();\n if (payload === \"[DONE]\") {\n controller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n continue;\n }\n try {\n const chunk = JSON.parse(payload);\n const legacyChunk = {\n id: (chunk.id || \"\").replace(\"chatcmpl-\", \"cmpl-\"),\n object: \"text_completion.chunk\",\n created: chunk.created,\n model: chunk.model || model,\n choices: (chunk.choices || []).map((c: any) => ({\n text: c.delta?.content ?? \"\",\n index: c.index ?? 0,\n finish_reason: c.finish_reason ?? null,\n logprobs: null,\n })),\n };\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(legacyChunk)}\\n\\n`),\n );\n } catch {\n controller.enqueue(encoder.encode(`data: ${payload}\\n\\n`));\n }\n }\n }\n } finally {\n controller.close();\n }\n },\n });\n}\n\nexport async function handleCompletion(\n body: any,\n ctx: HandlerContext,\n): Promise<Response> {\n if (!ctx.backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n\n const promptField = body.prompt;\n const prompt = Array.isArray(promptField)\n ? promptField.join(\"\\n\")\n : (promptField ?? \"\");\n const chatBody = {\n ...body,\n messages: [{ role: \"user\", content: prompt }],\n };\n delete chatBody.prompt;\n\n try {\n if (chatBody.stream) {\n const chatStream = ctx.backend.createStreamingResponse(chatBody);\n const legacyStream = legacyCompletionStreamAdapter(\n chatStream,\n body.model || ctx.modelId,\n );\n return new Response(legacyStream, {\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n }\n const chatResp = await ctx.backend.chatCompletion(chatBody);\n return Response.json(chatToLegacyCompletion(chatResp));\n } catch (error: any) {\n return Response.json({ error: error.message }, { status: 500 });\n }\n}\n","/**\n * X25519 ephemeral key generation + shared-secret derivation.\n *\n * Both pairing devices generate an ephemeral X25519 keypair, exchange\n * public keys via the rendezvous server, and derive an identical\n * shared secret independently. The server only relays the public\n * keys — it cannot derive the secret.\n *\n * Implementation note: WebCrypto's `subtle.deriveKey` doesn't support\n * X25519 in all runtimes yet (Node has it as of 22; Safari is still\n * catching up). We use `@noble/curves/ed25519.js` (which exports x25519)\n * because it's small (~5 KB), audited, and works in every JS runtime\n * we support without a native dep.\n *\n * @noble/curves v2 note: the export path now requires the `.js` suffix\n * (v1's extensionless `@noble/curves/ed25519` was dropped from the\n * package `exports` map), and `utils.randomPrivateKey()` was renamed to\n * `utils.randomSecretKey()`. `getPublicKey` / `getSharedSecret` are\n * unchanged.\n */\n\nimport { x25519 } from \"@noble/curves/ed25519.js\";\n\nexport interface KeyPair {\n publicKey: Uint8Array;\n secretKey: Uint8Array;\n}\n\n/** Generate a fresh ephemeral X25519 keypair. */\nexport function generateEphemeralKeyPair(): KeyPair {\n const secretKey = x25519.utils.randomSecretKey();\n const publicKey = x25519.getPublicKey(secretKey);\n return { publicKey, secretKey };\n}\n\n/** Derive the 32-byte shared secret. Inputs are raw byte arrays. */\nexport function deriveSharedSecret(\n ourSecretKey: Uint8Array,\n theirPublicKey: Uint8Array,\n): Uint8Array {\n return x25519.getSharedSecret(ourSecretKey, theirPublicKey);\n}\n\n/** Encode bytes as URL-safe base64 (no padding). */\nexport function encodeBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (const b of bytes) binary += String.fromCharCode(b);\n const b64 =\n typeof btoa !== \"undefined\"\n ? btoa(binary)\n : Buffer.from(binary, \"binary\").toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Decode URL-safe base64 → bytes. Throws on invalid input. */\nexport function decodeBase64Url(s: string): Uint8Array {\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const binary =\n typeof atob !== \"undefined\"\n ? atob(padded)\n : Buffer.from(padded, \"base64\").toString(\"binary\");\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n","/**\n * LAN-pairing handshake. The first time Device A wants to offload to\n * Device B over the LAN, A POSTs /v1/dvai/handshake to B with its\n * identity + a nonce. B surfaces a UI prompt to the user; on approve,\n * B generates a 256-bit pairing key and returns it. From then on, A\n * includes `X-DVAI-Pairing: HMAC-SHA256(pairingKey, body)` on every\n * offload request to B.\n */\n\nimport { encodeBase64Url } from \"../rendezvous/keys.js\";\nimport type { HandshakeRequest, HandshakeResponse } from \"./types.js\";\n\n/** Generate a fresh 256-bit pairing key (base64-url encoded). */\nexport function generatePairingKey(): string {\n const cryptoApi = typeof globalThis.crypto !== \"undefined\" ? globalThis.crypto : undefined;\n if (!cryptoApi || typeof cryptoApi.getRandomValues !== \"function\") {\n throw new Error(\"[DVAI/pairing] no secure random source\");\n }\n const bytes = new Uint8Array(32);\n cryptoApi.getRandomValues(bytes);\n return encodeBase64Url(bytes);\n}\n\n/** Generate a fresh nonce for a handshake request. */\nexport function generateNonce(): string {\n const cryptoApi = typeof globalThis.crypto !== \"undefined\" ? globalThis.crypto : undefined;\n if (!cryptoApi || typeof cryptoApi.getRandomValues !== \"function\") {\n throw new Error(\"[DVAI/pairing] no secure random source\");\n }\n const bytes = new Uint8Array(16);\n cryptoApi.getRandomValues(bytes);\n return encodeBase64Url(bytes);\n}\n\n/**\n * HMAC-SHA256(key, message). Used to sign offload requests so the\n * peer can verify they came from a paired device.\n */\nexport async function signHmac(\n pairingKey: string,\n message: string,\n): Promise<string> {\n const cryptoApi = globalThis.crypto;\n if (!cryptoApi?.subtle) {\n throw new Error(\"[DVAI/pairing] WebCrypto subtle not available\");\n }\n const keyBytes = decodeBase64UrlBytes(pairingKey);\n const cryptoKey = await cryptoApi.subtle.importKey(\n \"raw\",\n keyBytes as unknown as ArrayBuffer,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const sig = await cryptoApi.subtle.sign(\n \"HMAC\",\n cryptoKey,\n new TextEncoder().encode(message) as unknown as ArrayBuffer,\n );\n return encodeBase64Url(new Uint8Array(sig));\n}\n\n/** Verify an HMAC. Returns true on match, false otherwise (constant-time-ish). */\nexport async function verifyHmac(\n pairingKey: string,\n message: string,\n signature: string,\n): Promise<boolean> {\n const expected = await signHmac(pairingKey, message);\n return constantTimeEquals(expected, signature);\n}\n\nfunction constantTimeEquals(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\nfunction decodeBase64UrlBytes(s: string): Uint8Array {\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const binary =\n typeof atob !== \"undefined\"\n ? atob(padded)\n : Buffer.from(padded, \"base64\").toString(\"binary\");\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n/**\n * Compose the canonical message that gets HMAC-signed for a peer-to-peer\n * offload request. The peer recomputes the same string and verifies.\n *\n * Format: `${nonce}\\n${method}\\n${path}\\n${bodyHash}` — bodyHash is the\n * hex-encoded SHA-256 of the request body bytes.\n */\nexport async function composeSignedMessage(\n nonce: string,\n method: string,\n path: string,\n body: string | undefined,\n): Promise<string> {\n const bodyHash = body\n ? await sha256Hex(body)\n : \"0000000000000000000000000000000000000000000000000000000000000000\";\n return `${nonce}\\n${method.toUpperCase()}\\n${path}\\n${bodyHash}`;\n}\n\nasync function sha256Hex(input: string): Promise<string> {\n const cryptoApi = globalThis.crypto;\n if (!cryptoApi?.subtle) {\n throw new Error(\"[DVAI/pairing] WebCrypto subtle not available\");\n }\n const buf = await cryptoApi.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(input) as unknown as ArrayBuffer,\n );\n return Array.from(new Uint8Array(buf))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nexport type { HandshakeRequest, HandshakeResponse };\n","/**\n * Coarse static fallback for the capability score before any cold-run\n * probe has run. The numbers below are deliberately conservative —\n * we'd rather under-promise local capability and offload more often\n * than over-promise and serve a slow stream.\n *\n * Refinement: the first real probe (capability/probe.ts) replaces\n * the heuristic with a measured value cached for that (model, version).\n */\n\nimport type { DeviceCapabilityHints } from \"./types.js\";\n\n/**\n * Returns an estimated tok/s for the given hints. Pick the lowest\n * factor across the four dimensions — the bottleneck is what\n * actually limits inference speed.\n */\nexport function heuristicTokPerSec(hints: DeviceCapabilityHints): number {\n // Base score by GPU class. These numbers are based on observed\n // floors for common 1-3B GGUF q4 models in the wild.\n const gpuBase: Record<DeviceCapabilityHints[\"gpuClass\"], number> = {\n \"none\": 3, // CPU-only — very slow\n \"integrated\": 8, // basic iGPU\n \"discrete\": 35, // mid-range discrete (RTX 4060-class)\n \"apple-silicon\": 40, // M-series unified memory + Metal\n };\n\n // CPU class multiplier — affects prompt-processing more than\n // decode but still part of the picture.\n const cpuMul: Record<DeviceCapabilityHints[\"cpuClass\"], number> = {\n \"low\": 0.6,\n \"mid\": 1.0,\n \"high\": 1.3,\n };\n\n // RAM penalty — under 4 GB you're probably swapping; under 8 GB\n // you can't run a 3B q4 model comfortably.\n let ramMul: number;\n if (hints.ramGb < 4) ramMul = 0.3;\n else if (hints.ramGb < 8) ramMul = 0.7;\n else ramMul = 1.0;\n\n // NPU bonus — only meaningful when the *backend* uses it (Foundation\n // Models on iOS, MediaPipe with QNN on Android). Conservative bonus\n // because not every backend benefits.\n const npuBonus = hints.hasNpu ? 1.4 : 1.0;\n\n const estimate = gpuBase[hints.gpuClass] * cpuMul[hints.cpuClass] * ramMul * npuBonus;\n // Round to one decimal to avoid spurious-precision noise.\n return Math.round(estimate * 10) / 10;\n}\n\n/**\n * Detect device hints from the current runtime. JS-side runs in\n * Node / browser; native SDKs supply richer hints via their own\n * platform-bridges.\n */\nexport function detectDeviceHints(): DeviceCapabilityHints {\n // Browser: rough guess from navigator (deviceMemory is in GB,\n // hardwareConcurrency is logical-core count). WebGPU presence is\n // a \"discrete-ish\" hint.\n if (typeof navigator !== \"undefined\") {\n const nav = navigator as Navigator & { deviceMemory?: number };\n const ramGb = nav.deviceMemory ?? 4;\n const cores = nav.hardwareConcurrency ?? 4;\n const hasGpu = typeof (nav as Navigator & { gpu?: unknown }).gpu !== \"undefined\";\n\n return {\n hasNpu: false, // browsers can't introspect NPU presence today\n ramGb,\n gpuClass: hasGpu ? \"discrete\" : \"none\",\n cpuClass: cores >= 8 ? \"high\" : cores >= 4 ? \"mid\" : \"low\",\n };\n }\n\n // Node / Electron-main: query os.totalmem + os.cpus().\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n // Dynamic import to avoid pulling node:os into browser bundles.\n // We can't await at module top level here, so return a\n // pessimistic default and let detectDeviceHintsAsync() refine.\n }\n\n // Default conservative bucket.\n return {\n hasNpu: false,\n ramGb: 4,\n gpuClass: \"integrated\",\n cpuClass: \"mid\",\n };\n}\n\n/**\n * Async variant — uses node:os when available. Prefer this in Node\n * contexts; the sync version is for browser fallback.\n */\nexport async function detectDeviceHintsAsync(): Promise<DeviceCapabilityHints> {\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n try {\n const os = await import(\"node:os\");\n const totalMem = os.totalmem();\n const ramGb = Math.round(totalMem / (1024 ** 3));\n const cores = os.cpus().length;\n // Node can't introspect GPU class portably; default to\n // \"integrated\" unless the consumer overrides via config.\n return {\n hasNpu: false,\n ramGb,\n gpuClass: \"integrated\",\n cpuClass: cores >= 12 ? \"high\" : cores >= 6 ? \"mid\" : \"low\",\n };\n } catch {\n // node:os not available; fall through.\n }\n }\n return detectDeviceHints();\n}\n","/**\n * v3.2 — pre-init capability gate.\n *\n * Runs BEFORE any model download or backend init. Decides whether the\n * device should:\n *\n * 1. Refuse to run inference at all (hardware too weak).\n * Triggers a host-supplied `onHardwareTooWeak` callback so the\n * consumer can show a system popup, then throws so initialize()\n * aborts cleanly.\n *\n * 2. Run in offload-only mode (capable enough to bridge but not to\n * run the model itself comfortably). The SDK skips the model\n * download/load entirely and only brings up the proxy + discovery\n * + pairing layer. Every request gets forwarded to a paired peer;\n * 503 + Retry-After if no peer is available.\n *\n * 3. Run normally (load the model locally + run discovery/pairing\n * so this device can also serve other peers if it's strong).\n *\n * The decision uses ONLY the heuristic (CPU/GPU/RAM hints) — no model\n * is needed at this stage. Refinement via a real probe happens later,\n * after the model has been loaded and a request has actually completed.\n */\n\nimport {\n detectDeviceHintsAsync,\n heuristicTokPerSec,\n} from \"./heuristic.js\";\nimport type { DeviceCapabilityHints } from \"./types.js\";\n\n/** Result of a pre-init capability assessment. */\nexport type PrecheckResult =\n | { mode: \"ok\"; tokPerSec: number; hints: DeviceCapabilityHints; reason: string }\n | { mode: \"offload-only\"; tokPerSec: number; hints: DeviceCapabilityHints; reason: string }\n | { mode: \"too-weak\"; tokPerSec: number; hints: DeviceCapabilityHints; reason: string };\n\nexport interface PrecheckOptions {\n /**\n * Hard floor for any local inference, in tok/s. Below this, the\n * device is too weak to be useful at all and the precheck returns\n * `mode: \"too-weak\"`. Default: 3.\n *\n * Apps that want to allow even slower inference (e.g. for\n * long-prompt summarization where latency is acceptable) can pass\n * a smaller value. Apps targeting interactive chat should leave\n * the default — anything below ~3 tok/s feels broken.\n */\n hardwareMinimum?: number;\n\n /**\n * Below this tok/s but above `hardwareMinimum`, run in offload-only\n * mode (skip model load; route every request to a paired peer).\n * Above this, load locally. Default: pulled from\n * `OffloadConfig.minLocalCapability` if not set.\n */\n minLocalCapability?: number;\n\n /**\n * Pre-detected hints. Tests pass synthetic values here. In\n * production, leave undefined and let the precheck call\n * `detectDeviceHintsAsync()` itself.\n */\n hints?: DeviceCapabilityHints;\n}\n\nconst DEFAULT_HARDWARE_MINIMUM = 3;\nconst DEFAULT_MIN_LOCAL_CAPABILITY = 10;\n\nexport async function assessCapability(\n opts: PrecheckOptions = {},\n): Promise<PrecheckResult> {\n const hardwareMinimum = opts.hardwareMinimum ?? DEFAULT_HARDWARE_MINIMUM;\n const minLocalCapability = opts.minLocalCapability ?? DEFAULT_MIN_LOCAL_CAPABILITY;\n\n if (hardwareMinimum > minLocalCapability) {\n // Allowed but odd — the offload-only band collapses to zero.\n // Useful for \"load locally if you can or refuse\" deployments.\n }\n\n const hints = opts.hints ?? (await detectDeviceHintsAsync());\n const tokPerSec = heuristicTokPerSec(hints);\n\n if (tokPerSec < hardwareMinimum) {\n return {\n mode: \"too-weak\",\n tokPerSec,\n hints,\n reason:\n `estimated ${tokPerSec} tok/s, below the ${hardwareMinimum} tok/s ` +\n `hardware floor — local inference would be unusable, no peer to ` +\n `offload to either (a peer-only mode still requires the host to ` +\n `bring up discovery + pairing).`,\n };\n }\n\n if (tokPerSec < minLocalCapability) {\n return {\n mode: \"offload-only\",\n tokPerSec,\n hints,\n reason:\n `estimated ${tokPerSec} tok/s, below the ${minLocalCapability} tok/s ` +\n `comfort threshold — model will not be loaded locally; every ` +\n `request will be forwarded to a paired peer.`,\n };\n }\n\n return {\n mode: \"ok\",\n tokPerSec,\n hints,\n reason:\n `estimated ${tokPerSec} tok/s, above the ${minLocalCapability} tok/s ` +\n `local-capability threshold — running normally.`,\n };\n}\n\n/**\n * @deprecated v3.2 — kept only to avoid breaking type imports. The\n * SDK no longer throws on too-weak hardware; consumers call\n * `dvai.assessHardware()` and decide their own UI. start() in a\n * too-weak case enters offload-only mode silently (no model\n * download/load).\n *\n * Will be removed in v4.0.\n */\nexport class HardwareTooWeakError extends Error {\n readonly tokPerSec: number;\n readonly hardwareMinimum: number;\n readonly hints: DeviceCapabilityHints;\n\n constructor(opts: {\n tokPerSec: number;\n hardwareMinimum: number;\n hints: DeviceCapabilityHints;\n reason: string;\n }) {\n super(`DVAI: ${opts.reason}`);\n this.name = \"HardwareTooWeakError\";\n this.tokPerSec = opts.tokPerSec;\n this.hardwareMinimum = opts.hardwareMinimum;\n this.hints = opts.hints;\n }\n}\n","import type { HandlerContext } from \"./context\";\n\nconst SSE_HEADERS = {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n};\n\nexport async function handleChatCompletion(\n body: any,\n ctx: HandlerContext,\n headers?: Record<string, string>,\n): Promise<Response> {\n // Phase 4 — first-chance interceptor (used by the Hub to enforce\n // substitution policy + route through external engines). If it\n // returns a Response, we're done; null falls through to the local\n // backend path below.\n if (ctx.chatCompletionInterceptor) {\n try {\n const intercepted = await ctx.chatCompletionInterceptor(body, ctx, headers);\n if (intercepted !== null) return intercepted;\n } catch (err: any) {\n return Response.json(\n { error: err?.message ?? \"interceptor failed\" },\n { status: 500 },\n );\n }\n }\n\n if (!ctx.backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n\n // Always read ctx.backend inside runOnce so that if onRecovery replaces\n // the underlying backend instance, the retry hits the new one.\n const runOnce = async (): Promise<Response> => {\n const backend = ctx.backend;\n if (!backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n if (body.stream) {\n const stream = backend.createStreamingResponse(body);\n return new Response(stream, { headers: SSE_HEADERS });\n }\n const response = await backend.chatCompletion(body);\n return Response.json(response);\n };\n\n try {\n // Proactive recovery: if the backend is flagged with a prior fatal error,\n // ask DVAI to recover before the attempt.\n if (ctx.backend.lastFatalError && ctx.onRecovery) {\n await ctx.onRecovery();\n }\n return await runOnce();\n } catch (error: any) {\n // Reactive recovery: if the backend flags a fatal error during the attempt,\n // recover and retry once. DVAI's onRecovery throws when exhausted, which\n // falls through to the 500 response below.\n if (ctx.backend?.lastFatalError && ctx.onRecovery) {\n try {\n await ctx.onRecovery();\n return await runOnce();\n } catch {\n /* fall through to 500 */\n }\n }\n return Response.json({ error: error.message }, { status: 500 });\n }\n}\n","import type { HandlerContext } from \"./context\";\n\nexport async function handleEmbeddings(\n body: any,\n ctx: HandlerContext,\n): Promise<Response> {\n if (!ctx.backend) {\n return Response.json(\n { error: \"AI engine not initialized\" },\n { status: 503 },\n );\n }\n if (ctx.resolvedBackend === \"webllm\") {\n return Response.json(\n {\n error:\n \"Embeddings are not supported on the WebLLM backend. \" +\n \"Use backend: 'transformers' with pipelineTask: 'feature-extraction'.\",\n },\n { status: 400 },\n );\n }\n if (typeof ctx.backend.embedding !== \"function\") {\n return Response.json(\n {\n error:\n \"The current backend does not support embeddings. \" +\n \"For transformers: use pipelineTask: 'feature-extraction'.\",\n },\n { status: 400 },\n );\n }\n\n const input = body?.input;\n if (input === undefined || input === null) {\n return Response.json(\n { error: \"Missing 'input' field.\" },\n { status: 400 },\n );\n }\n\n try {\n const vectors: number[][] = await ctx.backend.embedding(input);\n return Response.json({\n object: \"list\",\n data: vectors.map((v, i) => ({\n object: \"embedding\",\n embedding: v,\n index: i,\n })),\n model: body.model || ctx.modelId,\n usage: { prompt_tokens: 0, total_tokens: 0 },\n });\n } catch (error: any) {\n return Response.json({ error: error.message }, { status: 500 });\n }\n}\n","import type { HandlerContext } from \"./context\";\n\nexport async function handleModels(ctx: HandlerContext): Promise<Response> {\n return Response.json({\n object: \"list\",\n data: [\n {\n id: ctx.modelId,\n object: \"model\",\n created: Math.floor(Date.now() / 1000),\n owned_by: \"dvai-bridge\",\n },\n ],\n });\n}\n","export type { BackendInterface, HandlerContext } from \"./context.js\";\nexport { handleChatCompletion } from \"./chat.js\";\nexport {\n handleCompletion,\n chatToLegacyCompletion,\n legacyCompletionStreamAdapter,\n} from \"./completions.js\";\nexport { handleEmbeddings } from \"./embeddings.js\";\nexport { handleModels } from \"./models.js\";\n","import { setupWorker, type SetupWorker } from \"msw/browser\";\nimport { http } from \"msw\";\nimport type { HandlerContext } from \"../handlers/context.js\";\nimport {\n handleChatCompletion,\n handleCompletion,\n handleEmbeddings,\n handleModels,\n} from \"../handlers/index.js\";\nimport type {\n MswTransportOptions,\n Transport,\n TransportStartResult,\n} from \"./types.js\";\n\nfunction getEndpoints(mockUrl: string): {\n chat: string;\n completions: string;\n embeddings: string;\n models: string;\n base: string;\n} {\n const chat = mockUrl;\n let base = chat;\n const chatSuffix = \"/chat/completions\";\n if (chat.endsWith(chatSuffix)) {\n base = chat.slice(0, -chatSuffix.length);\n } else {\n try {\n const u = new URL(chat);\n const parts = u.pathname.split(\"/\").filter(Boolean);\n parts.pop();\n u.pathname = \"/\" + parts.join(\"/\");\n base = u.toString().replace(/\\/$/, \"\");\n } catch {\n /* keep base = chat */\n }\n }\n return {\n chat,\n completions: `${base}/completions`,\n embeddings: `${base}/embeddings`,\n models: `${base}/models`,\n base,\n };\n}\n\nexport class MswTransport implements Transport {\n readonly kind = \"msw\" as const;\n private worker: SetupWorker | null = null;\n\n constructor(private readonly opts: MswTransportOptions) {}\n\n async start(ctx: HandlerContext): Promise<TransportStartResult> {\n const urls = getEndpoints(this.opts.mockUrl);\n\n // Empty serviceWorkerUrl means \"don't register\" — preserves the\n // direct-inference escape hatch while still reporting a baseUrl.\n if (this.opts.serviceWorkerUrl) {\n const handlers = [\n http.post(urls.chat, async ({ request }) =>\n handleChatCompletion(await request.json(), ctx),\n ),\n http.post(urls.completions, async ({ request }) =>\n handleCompletion(await request.json(), ctx),\n ),\n http.post(urls.embeddings, async ({ request }) =>\n handleEmbeddings(await request.json(), ctx),\n ),\n http.get(urls.models, async () => handleModels(ctx)),\n ];\n this.worker = setupWorker(...handlers);\n await this.worker.start({\n onUnhandledRequest: \"bypass\",\n serviceWorker: { url: this.opts.serviceWorkerUrl },\n } as any);\n }\n\n return { baseUrl: urls.base };\n }\n\n async stop(): Promise<void> {\n if (this.worker) {\n this.worker.stop();\n this.worker = null;\n }\n }\n}\n","import type { Server } from \"node:http\";\n\n/** DVAI-reserved base port. Deliberately high to avoid clashes with Ollama/Postgres/etc. */\nexport const BASE_PORT = 38883;\n\n/** Maximum port-fallback attempts before giving up. */\nexport const MAX_PORT_ATTEMPTS = 16;\n\n/**\n * Attempt to bind `server` to `basePort`, falling back to basePort+1,\n * basePort+2, ... on EADDRINUSE up to `maxAttempts` times.\n *\n * Default host is `127.0.0.1` (loopback-only) — safe default for any\n * single-device DVAI deployment. v3.0 LAN-target deployments (the\n * v3.1 Hub, native SDKs running in target mode) override to `0.0.0.0`\n * so peers on the same Wi-Fi can reach the server.\n *\n * Throws a loud, actionable error listing the tried range if all are in use.\n * Re-throws non-EADDRINUSE errors immediately (e.g. EACCES on privileged ports).\n *\n * @returns the port that was successfully bound\n */\nexport async function tryBind(\n server: Server,\n basePort: number = BASE_PORT,\n maxAttempts: number = MAX_PORT_ATTEMPTS,\n host: string = \"127.0.0.1\",\n): Promise<number> {\n for (let i = 0; i < maxAttempts; i++) {\n const port = basePort + i;\n try {\n await new Promise<void>((resolve, reject) => {\n const onError = (err: any) => {\n server.off(\"error\", onError);\n reject(err);\n };\n server.once(\"error\", onError);\n server.listen(port, host, () => {\n server.off(\"error\", onError);\n resolve();\n });\n });\n return port;\n } catch (err: any) {\n if (err.code !== \"EADDRINUSE\") throw err;\n }\n }\n throw new Error(\n `[DVAI] Could not bind HTTP transport to any port in range ` +\n `${basePort}..${basePort + maxAttempts - 1} (all in use). ` +\n `Another local AI server may already be running.`,\n );\n}\n","import type { HandlerContext } from \"../handlers/context.js\";\nimport {\n handleChatCompletion,\n handleCompletion,\n handleEmbeddings,\n handleModels,\n} from \"../handlers/index.js\";\nimport type { DvaiHandler } from \"../handlers/dvai/index.js\";\nimport type {\n HttpTransportOptions,\n Transport,\n TransportStartResult,\n} from \"./types.js\";\nimport { tryBind } from \"./port-fallback.js\";\n\ntype NodeReq = import(\"node:http\").IncomingMessage;\ntype NodeRes = import(\"node:http\").ServerResponse;\ntype NodeServer = import(\"node:http\").Server;\n\nfunction pickOrigin(origin: string | undefined, cfg: string | string[]): string | null {\n if (cfg === \"*\") return \"*\";\n if (typeof cfg === \"string\") return cfg;\n if (!origin) return null;\n return cfg.includes(origin) ? origin : null;\n}\n\nfunction corsHeaders(\n reqOrigin: string | undefined,\n cfg: string | string[],\n): Record<string, string> {\n const allow = pickOrigin(reqOrigin, cfg);\n const headers: Record<string, string> = {\n \"Access-Control-Allow-Methods\": \"POST, GET, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n \"Access-Control-Allow-Private-Network\": \"true\",\n };\n if (allow) headers[\"Access-Control-Allow-Origin\"] = allow;\n return headers;\n}\n\nasync function readJsonBody(req: NodeReq): Promise<any> {\n const chunks: Buffer[] = [];\n for await (const c of req) chunks.push(c as Buffer);\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (!raw) return {};\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(\"Invalid JSON body\");\n }\n}\n\nasync function writeWhatwgResponse(\n res: NodeRes,\n response: Response,\n extraHeaders: Record<string, string>,\n): Promise<void> {\n const headers: Record<string, string> = { ...extraHeaders };\n response.headers.forEach((v, k) => {\n headers[k] = v;\n });\n\n if (response.body) {\n res.writeHead(response.status, headers);\n const reader = response.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(Buffer.from(value));\n }\n } finally {\n res.end();\n }\n return;\n }\n const text = await response.text();\n res.writeHead(response.status, headers);\n res.end(text);\n}\n\nasync function route(\n req: NodeReq,\n res: NodeRes,\n ctx: HandlerContext,\n opts: HttpTransportOptions,\n): Promise<void> {\n const reqOrigin = req.headers.origin as string | undefined;\n const cors = corsHeaders(reqOrigin, opts.corsOrigin);\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204, cors);\n res.end();\n return;\n }\n\n const url = new URL(req.url || \"/\", \"http://127.0.0.1\");\n const path = url.pathname;\n\n try {\n if (req.method === \"POST\" && path === \"/v1/chat/completions\") {\n const body = await readJsonBody(req);\n const reqHeaders: Record<string, string> = {};\n for (const [k, v] of Object.entries(req.headers)) {\n if (typeof v === \"string\") reqHeaders[k.toLowerCase()] = v;\n else if (Array.isArray(v) && v.length > 0) reqHeaders[k.toLowerCase()] = v.join(\", \");\n }\n const r = await handleChatCompletion(body, ctx, reqHeaders);\n return writeWhatwgResponse(res, r, cors);\n }\n if (req.method === \"POST\" && path === \"/v1/completions\") {\n const body = await readJsonBody(req);\n const r = await handleCompletion(body, ctx);\n return writeWhatwgResponse(res, r, cors);\n }\n if (req.method === \"POST\" && path === \"/v1/embeddings\") {\n const body = await readJsonBody(req);\n const r = await handleEmbeddings(body, ctx);\n return writeWhatwgResponse(res, r, cors);\n }\n if (req.method === \"GET\" && path === \"/v1/models\") {\n const r = await handleModels(ctx);\n return writeWhatwgResponse(res, r, cors);\n }\n // /v1/dvai/* — Phase 3 distributed-inference plane. Routes are\n // registered by DVAI when offload.enabled. Absent map → 404 (no\n // offload state) which matches the pre-Phase-3 behaviour.\n const dvaiRoutes = ctx.dvaiRoutes;\n if (dvaiRoutes) {\n const key = `${req.method ?? \"GET\"} ${path}`;\n const dvaiHandler = dvaiRoutes[key];\n if (dvaiHandler) {\n let body: unknown = undefined;\n if (req.method === \"POST\" || req.method === \"PUT\" || req.method === \"PATCH\") {\n body = await readJsonBody(req);\n }\n const r = await dvaiHandler({ body });\n const respHeaders: Record<string, string> = {\n ...cors,\n \"Content-Type\": \"application/json\",\n };\n res.writeHead(r.status, respHeaders);\n res.end(JSON.stringify(r.body));\n return;\n }\n }\n res.writeHead(404, { ...cors, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"not found\" }));\n } catch (err: any) {\n res.writeHead(500, { ...cors, \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: err?.message ?? \"unknown error\" }));\n }\n}\n\nexport class HttpTransport implements Transport {\n readonly kind = \"http\" as const;\n private server: NodeServer | null = null;\n private boundPort: number | undefined;\n\n constructor(private readonly opts: HttpTransportOptions) {}\n\n async start(ctx: HandlerContext): Promise<TransportStartResult> {\n const { createServer } = await import(\"node:http\");\n const server = createServer((req, res) => {\n route(req, res, ctx, this.opts).catch((err) => {\n try {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (err as Error).message }));\n } catch {\n /* socket already closed */\n }\n });\n });\n const bindHost = this.opts.bindHost ?? \"127.0.0.1\";\n const port = await tryBind(\n server,\n this.opts.httpBasePort,\n this.opts.httpMaxPortAttempts,\n bindHost,\n );\n this.server = server;\n this.boundPort = port;\n // baseUrl reports the bind host so downstream consumers can choose\n // a sensible advertise URL. 0.0.0.0 isn't directly callable from\n // peers — they should use the host's actual LAN IP — but this URL\n // accurately reflects what the server is listening on.\n return { baseUrl: `http://${bindHost}:${port}/v1`, port };\n }\n\n async stop(): Promise<void> {\n if (this.server) {\n await new Promise<void>((r) => this.server!.close(() => r()));\n this.server = null;\n this.boundPort = undefined;\n }\n }\n}\n","import type { HandlerContext } from \"../handlers/context.js\";\nimport type { Transport, TransportStartResult } from \"./types.js\";\n\nexport interface CapacitorTransportOptions {\n capacitorBackend: \"llama\" | \"foundation\" | \"mediapipe\";\n nativeModelPath?: string;\n nativeMmprojPath?: string;\n nativeGpuLayers?: number;\n nativeContextSize?: number;\n nativeThreads?: number;\n nativeEmbeddingMode?: boolean;\n httpBasePort: number;\n httpMaxPortAttempts: number;\n corsOrigin: string | string[];\n autoUnloadOnLowMemory?: boolean;\n logLevel?: \"silent\" | \"info\" | \"debug\";\n}\n\nexport class CapacitorTransport implements Transport {\n readonly kind = \"capacitor\" as const;\n\n constructor(private readonly opts: CapacitorTransportOptions) {}\n\n async start(_ctx: HandlerContext): Promise<TransportStartResult> {\n const { DVAIBridge } = await import(\"@dvai-bridge/capacitor\");\n const result = await DVAIBridge.start({\n backend: this.opts.capacitorBackend,\n modelPath: this.opts.nativeModelPath,\n mmprojPath: this.opts.nativeMmprojPath,\n gpuLayers: this.opts.nativeGpuLayers,\n contextSize: this.opts.nativeContextSize,\n threads: this.opts.nativeThreads,\n embeddingMode: this.opts.nativeEmbeddingMode,\n httpBasePort: this.opts.httpBasePort,\n httpMaxPortAttempts: this.opts.httpMaxPortAttempts,\n corsOrigin: this.opts.corsOrigin,\n autoUnloadOnLowMemory: this.opts.autoUnloadOnLowMemory,\n logLevel: this.opts.logLevel,\n });\n return { baseUrl: result.baseUrl, port: result.port };\n }\n\n async stop(): Promise<void> {\n const { DVAIBridge } = await import(\"@dvai-bridge/capacitor\");\n await DVAIBridge.stop();\n }\n}\n","export type {\n Transport,\n TransportKind,\n TransportStartResult,\n HttpTransportOptions,\n MswTransportOptions,\n} from \"./types.js\";\nexport { MswTransport } from \"./msw.js\";\nexport { HttpTransport } from \"./http.js\";\nexport { CapacitorTransport } from \"./capacitor.js\";\nexport { BASE_PORT, MAX_PORT_ATTEMPTS, tryBind } from \"./port-fallback.js\";\n\nexport interface SelectTransportInput {\n /** Raw config: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\", or undefined. */\n transport?: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\";\n /** Back-compat signal: \"\" disables transport when transport is not explicit. */\n serviceWorkerUrl?: string;\n}\n\n/** Resolve \"auto\" based on the runtime environment. */\nexport function selectTransport(\n input: SelectTransportInput,\n): \"msw\" | \"http\" | \"none\" | \"capacitor\" {\n // Back-compat escape hatch: empty serviceWorkerUrl with no explicit transport → none\n if (input.serviceWorkerUrl === \"\" && input.transport == null) return \"none\";\n const requested = input.transport ?? \"auto\";\n if (requested !== \"auto\") return requested;\n if (isCapacitorContext()) return \"capacitor\";\n if (isBrowserLike()) return \"msw\";\n if (isNode()) return \"http\";\n return \"none\";\n}\n\nfunction isCapacitorContext(): boolean {\n return (\n typeof window !== \"undefined\" &&\n !!(window as any).Capacitor?.isNativePlatform?.()\n );\n}\n\nfunction isBrowserLike(): boolean {\n return (\n typeof window !== \"undefined\" &&\n typeof document !== \"undefined\" &&\n typeof navigator !== \"undefined\" &&\n typeof (navigator as any).serviceWorker !== \"undefined\"\n );\n}\n\nfunction isNode(): boolean {\n return (\n typeof process !== \"undefined\" &&\n process.versions != null &&\n process.versions.node != null\n );\n}\n","/**\n * Pure offload decision function. Tested in isolation; called by the\n * proxy on every chat-completion request.\n */\n\nimport type { Peer } from \"../discovery/types.js\";\nimport type { Decision, OffloadConfig, OffloadHeader, PeerCheckResult } from \"./types.js\";\n\nexport interface DecideInput {\n config: OffloadConfig;\n modelId: string;\n /** This device's capability for `modelId`. */\n localCapability: number;\n /** All known peers. */\n peers: Peer[];\n /** Per-request override (X-DVAI-Offload header). */\n header: OffloadHeader;\n}\n\nexport function decide(input: DecideInput): Decision {\n if (!input.config.enabled) return { kind: \"local\" };\n if (input.header === \"never\") return { kind: \"local\" };\n\n // Filter to peers that have this model loaded AND report a usable score.\n // LAN peers preferred over rendezvous peers when scores are comparable\n // (lower latency, no relay overhead) — sort key: (modelHasLoaded desc,\n // score desc, LAN-first).\n const eligible = input.peers\n .map((p): { peer: Peer; score: number; hasModel: boolean } => ({\n peer: p,\n score: p.capability[input.modelId] ?? 0,\n hasModel: p.loadedModels.includes(input.modelId),\n }))\n .filter((x) => x.score > 0)\n .sort((a, b) => {\n if (a.hasModel !== b.hasModel) return a.hasModel ? -1 : 1;\n if (a.score !== b.score) return b.score - a.score;\n // Same score, same load state: prefer mDNS over rendezvous over static.\n const order: Record<Peer[\"via\"], number> = { mdns: 0, static: 1, custom: 2, rendezvous: 3 };\n return order[a.peer.via] - order[b.peer.via];\n });\n\n const bestPeer = eligible[0]?.peer;\n const bestScore = eligible[0]?.score ?? 0;\n const threshold = input.config.minLocalCapability;\n\n if (input.header === \"require\") {\n if (bestPeer && bestScore >= threshold) {\n return { kind: \"offload\", peer: bestPeer };\n }\n return {\n kind: \"no_capable_device\",\n checked: buildCheckedList(input),\n localCapability: input.localCapability,\n required: threshold,\n };\n }\n\n // Default: \"prefer\".\n if (input.localCapability >= threshold) {\n return { kind: \"local\" };\n }\n if (bestPeer && bestScore > input.localCapability) {\n return { kind: \"offload\", peer: bestPeer };\n }\n // Local is below threshold but no better peer.\n if (input.localCapability > 0) {\n return { kind: \"local\" }; // best we have\n }\n return {\n kind: \"no_capable_device\",\n checked: buildCheckedList(input),\n localCapability: input.localCapability,\n required: threshold,\n };\n}\n\nfunction buildCheckedList(input: DecideInput): PeerCheckResult[] {\n const list: PeerCheckResult[] = [\n {\n deviceId: \"self\",\n capabilityScore: input.localCapability,\n reason:\n input.localCapability < input.config.minLocalCapability\n ? \"below threshold\"\n : \"no eligible peer found\",\n },\n ];\n for (const peer of input.peers) {\n const score = peer.capability[input.modelId] ?? 0;\n list.push({\n deviceId: peer.deviceId,\n deviceName: peer.deviceName,\n capabilityScore: score,\n reason: score === 0 ? \"model not advertised\" : score < input.config.minLocalCapability ? \"below threshold\" : \"checked\",\n });\n }\n return list;\n}\n","/**\n * Constructs the structured `no_capable_device` HTTP error response.\n * Returned with HTTP 503 + Retry-After: 30. Body is OpenAI-error-shaped\n * so existing OpenAI clients surface it naturally.\n */\n\nimport type { Decision } from \"./types.js\";\nimport type { NoCapableDeviceErrorBody } from \"./types.js\";\n\nexport function buildNoCapableDeviceResponse(\n decision: Extract<Decision, { kind: \"no_capable_device\" }>,\n ctx: { rendezvousConfigured: boolean; pairedRemotePeers: number; requestId?: string; modelId: string },\n): { status: number; headers: Record<string, string>; body: NoCapableDeviceErrorBody } {\n const message =\n `No device with capability ≥ ${decision.required} tok/s for model ` +\n `${ctx.modelId} was reachable. Local: ${decision.localCapability} tok/s; ` +\n `${decision.checked.length - 1} peer(s) checked.`;\n\n return {\n status: 503,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Retry-After\": \"30\",\n },\n body: {\n error: {\n type: \"no_capable_device\",\n code: 503,\n message,\n checked: decision.checked,\n localCapability: decision.localCapability,\n requiredAtLeast: decision.required,\n rendezvousConfigured: ctx.rendezvousConfigured,\n pairedRemotePeers: ctx.pairedRemotePeers,\n requestId: ctx.requestId,\n },\n },\n };\n}\n","/**\n * Per-request offload policy parsing. Reads X-DVAI-Offload header,\n * defaults to \"prefer\".\n */\n\nimport type { OffloadHeader } from \"./types.js\";\n\nconst VALID: ReadonlySet<OffloadHeader> = new Set([\"never\", \"prefer\", \"require\"]);\n\nexport function parseOffloadHeader(headers: Headers | Record<string, string | undefined>): OffloadHeader {\n let raw: string | null | undefined;\n if (headers instanceof Headers) {\n raw = headers.get(\"x-dvai-offload\");\n } else {\n // Case-insensitive lookup for the plain-object case.\n for (const [k, v] of Object.entries(headers)) {\n if (k.toLowerCase() === \"x-dvai-offload\") {\n raw = v;\n break;\n }\n }\n }\n if (!raw) return \"prefer\";\n const lc = raw.toLowerCase().trim() as OffloadHeader;\n return VALID.has(lc) ? lc : \"prefer\";\n}\n","/**\n * Offload proxy. Given a Decision.offload, forward the OpenAI HTTP\n * request to the peer's baseUrl and stream the response back to the\n * caller.\n *\n * For LAN peers: direct HTTP. For rendezvous-paired peers: the same\n * HTTP shape but tunneled through the rendezvous WebSocket relay.\n * (Rendezvous tunneling is a v3.0+ extension; this initial impl\n * supports the LAN HTTP path; rendezvous support is wired in once\n * Task 5+6 land the handshake + endpoint glue.)\n */\n\nimport type { Peer } from \"../discovery/types.js\";\n\nexport interface ProxyRequest {\n method: \"POST\" | \"GET\";\n path: string; // e.g. \"/chat/completions\"\n body?: unknown;\n headers?: Record<string, string>;\n /** Whether to expect SSE-style streaming response. */\n stream: boolean;\n /** Caller's AbortSignal — propagated to the upstream fetch. */\n signal?: AbortSignal;\n}\n\nexport interface ProxyResponse {\n status: number;\n headers: Record<string, string>;\n /** For non-streaming: the parsed body. */\n body?: unknown;\n /** For streaming: a ReadableStream of SSE-format chunks. */\n stream?: ReadableStream<Uint8Array>;\n}\n\n/**\n * Proxy a single request to a peer. The peer's baseUrl is the OpenAI\n * v1 root (e.g. http://192.168.1.10:38883/v1).\n */\nexport async function proxyToPeer(\n peer: Peer,\n req: ProxyRequest,\n): Promise<ProxyResponse> {\n if (peer.via === \"rendezvous\") {\n // TODO(v3.0): wire up the WebSocket relay path. For now, fall\n // through to direct HTTP if the peer also has a baseUrl (which\n // it shouldn't for pure-rendezvous peers; left as a guard).\n if (!peer.baseUrl || !peer.baseUrl.startsWith(\"http\")) {\n throw new Error(\n \"[DVAI/offload] rendezvous-paired peers require WebSocket relay \" +\n \"(not yet implemented in v3.0.0-rc1; expected in v3.0.0 final).\",\n );\n }\n }\n\n const url = `${peer.baseUrl.replace(/\\/$/, \"\")}${req.path}`;\n const upstreamHeaders: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-DVAI-Forwarded\": \"1\",\n ...(req.headers ?? {}),\n };\n\n const response = await fetch(url, {\n method: req.method,\n headers: upstreamHeaders,\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\n signal: req.signal,\n });\n\n const headers: Record<string, string> = {};\n response.headers.forEach((v, k) => {\n headers[k] = v;\n });\n\n if (req.stream && response.body) {\n return {\n status: response.status,\n headers,\n stream: response.body,\n };\n }\n\n return {\n status: response.status,\n headers,\n body: await response.json().catch(() => undefined),\n };\n}\n","/**\n * v3.2 — auto-wired offload forwarder.\n *\n * Returns a `chatCompletionInterceptor` (the same shape that the Hub\n * uses for its substitution-policy hook) which:\n *\n * 1. On every /v1/chat/completions request, runs `decide()` to pick\n * a route.\n * 2. If decide → \"offload\": calls `proxyToPeer` and returns a\n * Response with the peer's reply.\n * 3. If decide → \"no_capable_device\": returns a structured 503\n * using `buildNoCapableDeviceResponse()`.\n * 4. If decide → \"local\": returns null. The default handler runs:\n * - When the local backend exists, it serves the request.\n * - When the SDK is in offload-only mode (no backend), the\n * default handler returns 503 itself (\"not initialized\").\n * That second 503 indicates a peer was claimed available\n * but isn't usable for this model — operationally rare.\n *\n * The forwarder reads dynamic state (current peers, current\n * capability score) every call so changes to either don't require\n * re-wiring. Pass dynamic getters in `opts`.\n */\n\nimport { decide } from \"./decide.js\";\nimport { buildNoCapableDeviceResponse } from \"./error.js\";\nimport { proxyToPeer } from \"./proxy.js\";\nimport { parseOffloadHeader } from \"./policy.js\";\nimport type { OffloadConfig, OffloadHeader } from \"./types.js\";\nimport type { Peer } from \"../discovery/types.js\";\nimport type { HandlerContext } from \"../handlers/context.js\";\n\nexport interface ForwarderOptions {\n /** Static OffloadConfig (the one supplied to the SDK). */\n config: OffloadConfig;\n /** Live snapshot of discovered peers. Re-read on each request. */\n getPeers: () => Peer[];\n /** Live capability score for `modelId` on this device. */\n getLocalCapability: (modelId: string) => number;\n /** When true, every \"local\" decision is forced to a 503 because\n * no local backend exists to serve it (offload-only mode). */\n offloadOnlyMode: boolean;\n}\n\ntype Interceptor = NonNullable<HandlerContext[\"chatCompletionInterceptor\"]>;\n\nexport function buildOffloadInterceptor(opts: ForwarderOptions): Interceptor {\n return async function offloadInterceptor(\n body: any,\n ctx: HandlerContext,\n headers?: Record<string, string>,\n ): Promise<Response | null> {\n if (!opts.config.enabled) return null;\n\n // Per-request override (X-DVAI-Offload header). Defaults to \"prefer\".\n const headerValue = readOffloadHeader(headers);\n\n const peers = opts.getPeers();\n const modelId = ctx.modelId;\n const localCapability = opts.getLocalCapability(modelId);\n\n const decision = decide({\n config: opts.config,\n modelId,\n localCapability,\n peers,\n header: headerValue,\n });\n\n if (decision.kind === \"local\") {\n // In offload-only mode the default handler will 503 because\n // ctx.backend is null. We could short-circuit here with a\n // friendlier message, but returning null preserves the\n // backend-not-initialized error path the existing code already\n // handles.\n if (opts.offloadOnlyMode) {\n // Offload-only + decide-said-local: typically means\n // header=never (caller insisted on local). Surface that\n // explicitly so the caller knows their override was the cause.\n return jsonResponse(503, {\n error: {\n type: \"no_local_backend\",\n code: 503,\n message:\n \"DVAI is running in offload-only mode (device below \" +\n \"minLocalCapability) and the request requested local \" +\n \"execution (X-DVAI-Offload: never). Either drop the \" +\n \"header or route to a peer.\",\n },\n });\n }\n return null;\n }\n\n if (decision.kind === \"offload\") {\n // Forward to the peer. Streaming is detected from the request body.\n const stream = body && typeof body === \"object\" && body.stream === true;\n try {\n const proxied = await proxyToPeer(decision.peer, {\n method: \"POST\",\n path: \"/chat/completions\",\n body,\n stream,\n headers: forwardableHeaders(headers),\n });\n\n // Best-effort callback (consumer-supplied diagnostics).\n try {\n opts.config.onOffload?.(decision.peer);\n } catch {\n // host-provided callback errors must not break the response\n }\n\n if (proxied.stream) {\n return new Response(proxied.stream, {\n status: proxied.status,\n headers: proxied.headers,\n });\n }\n return new Response(JSON.stringify(proxied.body), {\n status: proxied.status,\n headers: proxied.headers,\n });\n } catch (err) {\n // Proxy failed (peer unreachable, network drop, etc.). Surface\n // a 502 — the upstream peer was supposed to handle it but\n // didn't. Caller can retry; another peer might still be available.\n return jsonResponse(502, {\n error: {\n type: \"peer_unreachable\",\n code: 502,\n message: `Offload to peer ${decision.peer.deviceId} failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n peerId: decision.peer.deviceId,\n },\n });\n }\n }\n\n // decision.kind === \"no_capable_device\"\n const errResponse = buildNoCapableDeviceResponse(decision, {\n rendezvousConfigured: !!opts.config.rendezvousUrl,\n pairedRemotePeers: peers.filter((p) => p.via === \"rendezvous\").length,\n modelId,\n });\n return new Response(JSON.stringify(errResponse.body), {\n status: errResponse.status,\n headers: errResponse.headers,\n });\n };\n}\n\nfunction readOffloadHeader(\n headers?: Record<string, string>,\n): OffloadHeader {\n if (!headers) return \"prefer\";\n return parseOffloadHeader(headers as Record<string, string | undefined>);\n}\n\n/** Strip hop-by-hop / problematic headers; keep auth-relevant ones. */\nfunction forwardableHeaders(\n headers?: Record<string, string>,\n): Record<string, string> {\n if (!headers) return {};\n const out: Record<string, string> = {};\n const drop = new Set([\n \"host\",\n \"content-length\",\n \"connection\",\n \"keep-alive\",\n \"transfer-encoding\",\n ]);\n for (const [k, v] of Object.entries(headers)) {\n if (drop.has(k.toLowerCase())) continue;\n out[k] = v;\n }\n return out;\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n}\n","export type {\n Decision,\n OffloadConfig,\n OffloadHeader,\n PeerCheckResult,\n NoCapableDeviceErrorBody,\n} from \"./types.js\";\nexport { decide } from \"./decide.js\";\nexport { buildNoCapableDeviceResponse } from \"./error.js\";\nexport { parseOffloadHeader } from \"./policy.js\";\nexport { proxyToPeer, type ProxyRequest, type ProxyResponse } from \"./proxy.js\";\nexport {\n buildOffloadInterceptor,\n type ForwarderOptions,\n} from \"./forwarder.js\";\n","/**\n * Persistent storage of capability scores + the device identifier.\n * Concrete adapters per runtime — browser uses IndexedDB; Node uses\n * the filesystem under XDG_CACHE_HOME / %LOCALAPPDATA% / ~/.cache.\n *\n * Native SDKs (iOS / Android / .NET) supply their own platform-\n * appropriate adapter via the same interface.\n */\n\nimport type { CapabilityCache, CapabilityCacheKey, CapabilityScore } from \"./types.js\";\n\n/* -------------------------------------------------------------------------- */\n/* In-memory adapter — fallback / testing */\n/* -------------------------------------------------------------------------- */\n\nexport class InMemoryCapabilityCache implements CapabilityCache {\n private readonly map = new Map<string, CapabilityScore>();\n\n async get(key: CapabilityCacheKey): Promise<CapabilityScore | undefined> {\n return this.map.get(this.keyOf(key));\n }\n async set(score: CapabilityScore): Promise<void> {\n this.map.set(this.keyOf({ modelId: score.modelId, libraryVersion: score.libraryVersion }), score);\n }\n async list(): Promise<CapabilityScore[]> {\n return Array.from(this.map.values());\n }\n async clear(): Promise<void> {\n this.map.clear();\n }\n private keyOf(k: CapabilityCacheKey): string {\n return `${k.libraryVersion}::${k.modelId}`;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Browser adapter — IndexedDB */\n/* -------------------------------------------------------------------------- */\n\nconst DB_NAME = \"dvai-bridge\";\nconst STORE_NAME = \"capability-v1\";\nconst META_STORE_NAME = \"meta-v1\";\n\nexport class IndexedDBCapabilityCache implements CapabilityCache {\n private dbPromise?: Promise<IDBDatabase>;\n\n private openDb(): Promise<IDBDatabase> {\n if (!this.dbPromise) {\n this.dbPromise = new Promise<IDBDatabase>((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME);\n }\n if (!db.objectStoreNames.contains(META_STORE_NAME)) {\n db.createObjectStore(META_STORE_NAME);\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n }\n return this.dbPromise;\n }\n\n async get(key: CapabilityCacheKey): Promise<CapabilityScore | undefined> {\n const db = await this.openDb();\n return await new Promise<CapabilityScore | undefined>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readonly\");\n const req = tx.objectStore(STORE_NAME).get(this.keyOf(key));\n req.onsuccess = () => resolve(req.result as CapabilityScore | undefined);\n req.onerror = () => reject(req.error);\n });\n }\n\n async set(score: CapabilityScore): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).put(score, this.keyOf({ modelId: score.modelId, libraryVersion: score.libraryVersion }));\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async list(): Promise<CapabilityScore[]> {\n const db = await this.openDb();\n return await new Promise<CapabilityScore[]>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readonly\");\n const req = tx.objectStore(STORE_NAME).getAll();\n req.onsuccess = () => resolve(req.result as CapabilityScore[]);\n req.onerror = () => reject(req.error);\n });\n }\n\n async clear(): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).clear();\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /** Reads a small key/value blob (used for the persistent device ID). */\n async getMeta(key: string): Promise<string | undefined> {\n const db = await this.openDb();\n return await new Promise<string | undefined>((resolve, reject) => {\n const tx = db.transaction(META_STORE_NAME, \"readonly\");\n const req = tx.objectStore(META_STORE_NAME).get(key);\n req.onsuccess = () => resolve(req.result as string | undefined);\n req.onerror = () => reject(req.error);\n });\n }\n async setMeta(key: string, value: string): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(META_STORE_NAME, \"readwrite\");\n tx.objectStore(META_STORE_NAME).put(value, key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n private keyOf(k: CapabilityCacheKey): string {\n return `${k.libraryVersion}::${k.modelId}`;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Node FS adapter */\n/* -------------------------------------------------------------------------- */\n\ninterface CacheFile {\n deviceId: string;\n scores: Record<string, CapabilityScore>;\n}\n\nexport class NodeFsCapabilityCache implements CapabilityCache {\n private readonly cachePath: string;\n private cache?: CacheFile;\n\n constructor(cachePath?: string) {\n this.cachePath = cachePath ?? defaultCachePath();\n }\n\n private async load(): Promise<CacheFile> {\n if (this.cache) return this.cache;\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n try {\n const raw = await fs.readFile(this.cachePath, \"utf8\");\n this.cache = JSON.parse(raw) as CacheFile;\n } catch {\n // file doesn't exist or unparseable — start fresh.\n this.cache = { deviceId: \"\", scores: {} };\n }\n // Ensure parent dir exists for future writes.\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n return this.cache;\n }\n\n private async save(): Promise<void> {\n if (!this.cache) return;\n const fs = await import(\"node:fs/promises\");\n await fs.writeFile(this.cachePath, JSON.stringify(this.cache, null, 2), \"utf8\");\n }\n\n async get(key: CapabilityCacheKey): Promise<CapabilityScore | undefined> {\n const c = await this.load();\n return c.scores[this.keyOf(key)];\n }\n async set(score: CapabilityScore): Promise<void> {\n const c = await this.load();\n c.scores[this.keyOf({ modelId: score.modelId, libraryVersion: score.libraryVersion })] = score;\n await this.save();\n }\n async list(): Promise<CapabilityScore[]> {\n const c = await this.load();\n return Object.values(c.scores);\n }\n async clear(): Promise<void> {\n const c = await this.load();\n c.scores = {};\n await this.save();\n }\n async getDeviceId(): Promise<string> {\n const c = await this.load();\n return c.deviceId;\n }\n async setDeviceId(id: string): Promise<void> {\n const c = await this.load();\n c.deviceId = id;\n await this.save();\n }\n\n private keyOf(k: CapabilityCacheKey): string {\n return `${k.libraryVersion}::${k.modelId}`;\n }\n}\n\nfunction defaultCachePath(): string {\n // Cross-platform sane default. We can't use top-level await for\n // node:os; build the path inline using process.env at construction time.\n if (typeof globalThis.process !== \"undefined\") {\n const env = globalThis.process.env;\n const home = env.HOME ?? env.USERPROFILE ?? \".\";\n if (env.LOCALAPPDATA) {\n return `${env.LOCALAPPDATA}/dvai-bridge/capability.json`;\n }\n if (env.XDG_CACHE_HOME) {\n return `${env.XDG_CACHE_HOME}/dvai-bridge/capability.json`;\n }\n return `${home}/.cache/dvai-bridge/capability.json`;\n }\n return \"./.dvai-bridge-capability.json\";\n}\n\n/* -------------------------------------------------------------------------- */\n/* Factory */\n/* -------------------------------------------------------------------------- */\n\nexport function createCapabilityCache(): CapabilityCache {\n if (typeof indexedDB !== \"undefined\") {\n return new IndexedDBCapabilityCache();\n }\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n return new NodeFsCapabilityCache();\n }\n // Fallback for unknown runtimes (workers without IndexedDB, etc.) —\n // memory only. Capability scores won't survive a reload.\n return new InMemoryCapabilityCache();\n}\n","/**\n * Stable per-install device identifier. Generated once on first call,\n * persisted alongside the capability cache. Used for:\n * - identifying THIS device in mDNS TXT records (LAN discovery).\n * - identifying THIS device in rendezvous-server pairing payloads.\n * - keying the capability cache.\n *\n * NOT a privacy hazard: the ID is per-install and per-device-storage,\n * never tied to user identity. Reinstalling the app or wiping app\n * storage produces a fresh ID — that's the right behaviour.\n */\n\n/** Generate a 22-char URL-safe random ID. */\nexport function generateDeviceId(): string {\n // Pull from the platform's secure random source. WebCrypto is\n // available in browsers, Node 19+, Bun, Deno.\n const cryptoApi: Crypto | undefined =\n typeof globalThis.crypto !== \"undefined\" ? globalThis.crypto : undefined;\n\n if (!cryptoApi || typeof cryptoApi.getRandomValues !== \"function\") {\n throw new Error(\n \"[DVAI/capability] No secure random source available. \" +\n \"globalThis.crypto.getRandomValues required (Node ≥19, modern browsers, Bun, Deno).\",\n );\n }\n\n const bytes = new Uint8Array(16);\n cryptoApi.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\nfunction base64UrlEncode(bytes: Uint8Array): string {\n // Browser + Node-compatible: convert to base64, then URL-safe.\n let binary = \"\";\n for (const b of bytes) {\n binary += String.fromCharCode(b);\n }\n const b64 = typeof btoa !== \"undefined\"\n ? btoa(binary)\n : Buffer.from(binary, \"binary\").toString(\"base64\");\n return b64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n","/**\n * Cold-run capability probe. Runs a fixed-size completion against the\n * active backend and measures decode tok/s. Result is cached per\n * (modelId, libraryVersion) so we only pay this cost once per\n * (model, version) pair.\n */\n\nimport type { CapabilityScore } from \"./types.js\";\n\nconst PROBE_PROMPT = \"Generate a single short sentence about clouds.\";\nconst PROBE_MAX_TOKENS = 50;\n\n/**\n * Generic backend interface the probe needs. The full BackendInterface\n * in DVAI is wider; we only need chatCompletion here.\n */\nexport interface ProbableBackend {\n chatCompletion(req: {\n messages: Array<{ role: string; content: string }>;\n max_tokens?: number;\n stream?: boolean;\n }): Promise<{\n choices: Array<{ message?: { content?: string }; text?: string }>;\n usage?: { completion_tokens?: number };\n }>;\n}\n\nexport async function probeCapability(opts: {\n backend: ProbableBackend;\n modelId: string;\n libraryVersion: string;\n deviceId: string;\n}): Promise<CapabilityScore> {\n const t0 = performance.now();\n const response = await opts.backend.chatCompletion({\n messages: [{ role: \"user\", content: PROBE_PROMPT }],\n max_tokens: PROBE_MAX_TOKENS,\n stream: false,\n });\n const elapsedSec = (performance.now() - t0) / 1000;\n\n // Token count: prefer the backend's own count (many backends report\n // it in usage.completion_tokens), fall back to a coarse word-count\n // approximation.\n const tokensFromUsage = response.usage?.completion_tokens;\n const text =\n response.choices[0]?.message?.content ??\n response.choices[0]?.text ??\n \"\";\n const tokens = tokensFromUsage ?? approximateTokenCount(text);\n\n // Guard against zero-time and zero-token edge cases.\n const tokPerSec = tokens > 0 && elapsedSec > 0\n ? Math.round((tokens / elapsedSec) * 10) / 10\n : 0;\n\n return {\n modelId: opts.modelId,\n deviceId: opts.deviceId,\n libraryVersion: opts.libraryVersion,\n tokPerSec,\n source: \"probe\",\n measuredAt: Date.now(),\n };\n}\n\n/**\n * Crude token-count approximation when the backend doesn't report\n * usage. Underestimates by ~25% for English text; that's fine for\n * capacity-band purposes — we'd rather under-report tok/s and offload\n * more often than over-report.\n */\nfunction approximateTokenCount(text: string): number {\n // ~4 chars per token for English; whitespace + punctuation counted.\n return Math.max(1, Math.round(text.length / 4));\n}\n","/**\n * Phase 3 — capability assessment surface.\n *\n * Public facade glued together from the building blocks in this dir:\n * - probe.ts: cold-run measurement\n * - heuristic.ts: static fallback before first probe\n * - cache.ts: persistent storage adapters\n * - deviceId.ts: stable per-install ID\n *\n * Wired into DVAI in src/index.ts as `dvai.probeCapability()` and\n * `dvai.getCapability(modelId)`. The offload decider in src/offload/\n * is the primary consumer.\n */\n\nimport {\n IndexedDBCapabilityCache,\n NodeFsCapabilityCache,\n createCapabilityCache,\n InMemoryCapabilityCache,\n} from \"./cache.js\";\nimport { detectDeviceHints, detectDeviceHintsAsync, heuristicTokPerSec } from \"./heuristic.js\";\nimport { generateDeviceId } from \"./deviceId.js\";\nimport { probeCapability, type ProbableBackend } from \"./probe.js\";\nimport {\n assessCapability,\n HardwareTooWeakError,\n type PrecheckOptions,\n type PrecheckResult,\n} from \"./precheck.js\";\nimport type { CapabilityCache, CapabilityScore, DeviceCapabilityHints } from \"./types.js\";\n\nexport type {\n CapabilityScore,\n CapabilityCache,\n DeviceCapabilityHints,\n ProbableBackend,\n PrecheckOptions,\n PrecheckResult,\n};\n\nexport {\n IndexedDBCapabilityCache,\n NodeFsCapabilityCache,\n InMemoryCapabilityCache,\n createCapabilityCache,\n heuristicTokPerSec,\n detectDeviceHints,\n detectDeviceHintsAsync,\n generateDeviceId,\n probeCapability,\n assessCapability,\n HardwareTooWeakError,\n};\n\nconst DEVICE_ID_META_KEY = \"dvai.deviceId\";\n\n/**\n * Resolve (or generate-on-first-call + persist) the per-install\n * device ID. The cache adapter is responsible for the persistence;\n * this function unifies the IndexedDB / Node-FS access patterns.\n */\nexport async function ensureDeviceId(cache: CapabilityCache): Promise<string> {\n // IndexedDB adapter has its own getMeta/setMeta for the meta store.\n if (cache instanceof IndexedDBCapabilityCache) {\n const existing = await cache.getMeta(DEVICE_ID_META_KEY);\n if (existing) return existing;\n const fresh = generateDeviceId();\n await cache.setMeta(DEVICE_ID_META_KEY, fresh);\n return fresh;\n }\n // Node FS adapter has dedicated getDeviceId/setDeviceId on the\n // backing JSON file.\n if (cache instanceof NodeFsCapabilityCache) {\n const existing = await cache.getDeviceId();\n if (existing) return existing;\n const fresh = generateDeviceId();\n await cache.setDeviceId(fresh);\n return fresh;\n }\n // In-memory adapter — generate but don't persist (ephemeral).\n const fresh = generateDeviceId();\n return fresh;\n}\n\n/**\n * Get the capability score for a model on this device. Returns the\n * cached probe result if available; otherwise the static heuristic\n * estimate (marked source: \"heuristic\"). Does NOT trigger a probe —\n * call probeAndCache separately to measure.\n */\nexport async function getCapability(opts: {\n cache: CapabilityCache;\n modelId: string;\n libraryVersion: string;\n hints?: DeviceCapabilityHints;\n}): Promise<CapabilityScore> {\n const cached = await opts.cache.get({\n modelId: opts.modelId,\n libraryVersion: opts.libraryVersion,\n });\n if (cached) return cached;\n\n const hints = opts.hints ?? (await detectDeviceHintsAsync());\n const deviceId = await ensureDeviceId(opts.cache);\n\n return {\n modelId: opts.modelId,\n deviceId,\n libraryVersion: opts.libraryVersion,\n tokPerSec: heuristicTokPerSec(hints),\n source: \"heuristic\",\n measuredAt: Date.now(),\n };\n}\n\n/**\n * Run a cold-run probe against the given backend, persist the result,\n * and return the new score.\n */\nexport async function probeAndCache(opts: {\n cache: CapabilityCache;\n backend: ProbableBackend;\n modelId: string;\n libraryVersion: string;\n}): Promise<CapabilityScore> {\n const deviceId = await ensureDeviceId(opts.cache);\n const score = await probeCapability({\n backend: opts.backend,\n modelId: opts.modelId,\n libraryVersion: opts.libraryVersion,\n deviceId,\n });\n await opts.cache.set(score);\n return score;\n}\n\n/** Drop all cached scores. Useful for diagnostics + tests. */\nexport async function clearCapabilityCache(cache: CapabilityCache): Promise<void> {\n await cache.clear();\n}\n","/**\n * Phase 3 — peer discovery types.\n *\n * A \"peer\" is another device running dvai-bridge that this device can\n * (potentially) offload inference requests to. Peers are surfaced by\n * one or more `IDiscovery` impls — LAN mDNS, app-supplied static list,\n * rendezvous-paired (internet), or a host-app-provided custom source.\n */\n\nexport interface Peer {\n /** Stable per-install device ID of the peer. */\n deviceId: string;\n /** Human-readable hint (iOS device name, hostname, etc.). */\n deviceName: string;\n /** Library SemVer the peer is running. */\n dvaiVersion: string;\n /** OpenAI-compatible base URL the peer's local server exposes. */\n baseUrl: string;\n /**\n * v3.1 wire-protocol extension. Identifies which application on the\n * peer device is making the request — used by multi-tenant targets\n * (the Hub) to isolate per-app state. Optional for backwards compat\n * with v3.0 SDKs that don't send this field.\n */\n appId?: string;\n /**\n * Models the peer claims to have loaded right now. Used to filter\n * peer eligibility — we only offload model X to a peer that already\n * has model X loaded (loading from scratch on the peer is fine but\n * defeats the latency win).\n */\n loadedModels: string[];\n /**\n * Peer-reported capability map: { modelId → tok/s }. Treat as\n * advisory only; the offload decider re-probes a peer with a small\n * reachability+decode test before its first real offload request.\n */\n capability: Record<string, number>;\n /** Discovery source — useful for diagnostics and the structured-error response. */\n via: \"mdns\" | \"static\" | \"rendezvous\" | \"custom\";\n /** Whether the peer's URL uses TLS. */\n secure: boolean;\n /** Last-seen unix ms — discovery sources update this. */\n lastSeenAt: number;\n}\n\nexport type DiscoveryEvent =\n | { type: \"peer-up\"; peer: Peer }\n | { type: \"peer-down\"; deviceId: string }\n | { type: \"error\"; message: string };\n\n/**\n * The contract every discovery source implements. Used by the\n * composite discovery layer.\n */\nexport interface IDiscovery {\n /** Begin discovering. Idempotent. */\n start(): Promise<void>;\n /** Stop and release resources. Idempotent. */\n stop(): Promise<void>;\n /** Snapshot of currently-known peers. */\n peers(): Peer[];\n /** Subscribe to discovery events. Returns unsubscribe fn. */\n subscribe(listener: (e: DiscoveryEvent) => void): () => void;\n}\n\n/** Service-type advertised on mDNS for dvai-bridge instances. */\nexport const MDNS_SERVICE_TYPE = \"_dvai-bridge._tcp.local\";\n","/**\n * Static-list discovery source. Apps that already know about peer\n * devices (e.g. a corporate device registry, hard-coded testbed\n * peers, persistent pairings restored across restart) supply a static\n * Peer[] at config time.\n *\n * The list is treated as authoritative — we don't health-check the\n * URLs here. The offload decider runs reachability checks before\n * actually using a peer.\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\n\nexport class StaticDiscovery implements IDiscovery {\n private readonly listeners = new Set<(e: DiscoveryEvent) => void>();\n private readonly peerList: Peer[];\n private started = false;\n\n constructor(peers: Peer[]) {\n // Normalize lastSeenAt to \"now\" if not supplied (treat 0 as\n // \"no value, use now\"). Preserve explicit non-zero values so\n // tests + custom merges that care about ordering can pass them.\n const now = Date.now();\n this.peerList = peers.map((p) => ({\n ...p,\n lastSeenAt: p.lastSeenAt && p.lastSeenAt > 0 ? p.lastSeenAt : now,\n via: \"static\",\n }));\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n // Emit peer-up for each immediately so consumers see them on\n // first poll.\n for (const peer of this.peerList) {\n this.emit({ type: \"peer-up\", peer });\n }\n }\n\n async stop(): Promise<void> {\n this.started = false;\n for (const peer of this.peerList) {\n this.emit({ type: \"peer-down\", deviceId: peer.deviceId });\n }\n }\n\n peers(): Peer[] {\n return this.started ? [...this.peerList] : [];\n }\n\n subscribe(listener: (e: DiscoveryEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private emit(e: DiscoveryEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(e);\n } catch (err) {\n // Don't let one buggy listener break the others.\n console.error(\"[DVAI/discovery] listener threw:\", err);\n }\n }\n }\n}\n","/**\n * Node mDNS / DNS-SD discovery + advertisement.\n *\n * Backed by `multicast-dns` (optional dep — if not installed, this\n * module logs a warning and degrades to no-op). The decision to keep\n * the dep optional avoids forcing a transitive native dep on every\n * dvai-bridge-core consumer; the offload feature itself is opt-in.\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\nimport { MDNS_SERVICE_TYPE } from \"./types.js\";\n\n/**\n * Loose typing for the multicast-dns module — we don't take a hard\n * dep on its types (would force the install).\n */\ninterface MdnsInstance {\n on(event: \"response\", handler: (response: MdnsResponse) => void): void;\n query(query: { questions: Array<{ name: string; type: string }> }): void;\n respond(response: { answers: MdnsAnswer[] }): void;\n destroy(): void;\n}\ninterface MdnsResponse {\n answers: MdnsAnswer[];\n additionals?: MdnsAnswer[];\n}\ninterface MdnsAnswer {\n name: string;\n type: string;\n data: unknown;\n ttl?: number;\n}\n\n/** Fields we advertise in the TXT record per the spec §4.2. */\nexport interface AdvertisedTxt {\n dvaiVersion: string;\n deviceId: string;\n deviceName: string;\n models: string[];\n capability: Record<string, number>;\n port: number;\n secure: boolean;\n}\n\nexport class NodeMdnsDiscovery implements IDiscovery {\n private mdns?: MdnsInstance;\n private readonly peerMap = new Map<string, Peer>();\n private readonly listeners = new Set<(e: DiscoveryEvent) => void>();\n private queryTimer?: NodeJS.Timeout;\n private gcTimer?: NodeJS.Timeout;\n private started = false;\n\n constructor(\n private readonly opts: {\n /** Our own deviceId — filter so we don't return ourselves. */\n selfDeviceId: string;\n /** Optional: TXT we should advertise. If omitted, we discover-only. */\n advertise?: AdvertisedTxt;\n /** How often to re-broadcast queries. Default 30s. */\n queryIntervalMs?: number;\n /** GC interval for stale peers (no recent advertisement). Default 60s. */\n gcIntervalMs?: number;\n /** TTL after which a peer is considered down (no recent ad). Default 90s. */\n peerTtlMs?: number;\n },\n ) {}\n\n async start(): Promise<void> {\n if (this.started) return;\n\n let mod: { default?: (opts?: unknown) => MdnsInstance } | ((opts?: unknown) => MdnsInstance);\n try {\n // @ts-expect-error — optional dep; consumers install only if they want LAN discovery.\n mod = await import(\"multicast-dns\");\n } catch {\n this.emit({\n type: \"error\",\n message:\n \"[DVAI/discovery] `multicast-dns` not installed; LAN discovery disabled. \" +\n \"Install with `npm i multicast-dns` (optional dep) to enable peer discovery.\",\n });\n return;\n }\n const factory = (typeof mod === \"function\" ? mod : mod.default) as (\n opts?: unknown,\n ) => MdnsInstance;\n this.mdns = factory();\n this.started = true;\n\n this.mdns.on(\"response\", (response) => this.onResponse(response));\n\n // Initial query + periodic re-query.\n this.broadcastQuery();\n const queryInterval = this.opts.queryIntervalMs ?? 30_000;\n this.queryTimer = setInterval(() => this.broadcastQuery(), queryInterval);\n\n // Periodic GC for stale peers.\n const gcInterval = this.opts.gcIntervalMs ?? 60_000;\n this.gcTimer = setInterval(() => this.gc(), gcInterval);\n }\n\n async stop(): Promise<void> {\n if (!this.started) return;\n this.started = false;\n if (this.queryTimer) clearInterval(this.queryTimer);\n if (this.gcTimer) clearInterval(this.gcTimer);\n this.mdns?.destroy();\n this.mdns = undefined;\n for (const peer of this.peerMap.values()) {\n this.emit({ type: \"peer-down\", deviceId: peer.deviceId });\n }\n this.peerMap.clear();\n }\n\n peers(): Peer[] {\n return Array.from(this.peerMap.values());\n }\n\n subscribe(listener: (e: DiscoveryEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private broadcastQuery(): void {\n if (!this.mdns) return;\n try {\n this.mdns.query({\n questions: [{ name: MDNS_SERVICE_TYPE, type: \"PTR\" }],\n });\n } catch (err) {\n this.emit({ type: \"error\", message: `mDNS query failed: ${String(err)}` });\n }\n\n // If we're advertising too, respond unsolicited so peers see us\n // sooner than their next query interval.\n if (this.opts.advertise) {\n try {\n this.mdns.respond({\n answers: this.buildAdvertisementAnswers(this.opts.advertise),\n });\n } catch (err) {\n this.emit({ type: \"error\", message: `mDNS respond failed: ${String(err)}` });\n }\n }\n }\n\n private buildAdvertisementAnswers(txt: AdvertisedTxt): MdnsAnswer[] {\n const instanceName = `${txt.deviceId}.${MDNS_SERVICE_TYPE}`;\n return [\n // PTR: service-type → instance-name\n { name: MDNS_SERVICE_TYPE, type: \"PTR\", data: instanceName, ttl: 120 },\n // SRV: instance-name → host/port\n {\n name: instanceName,\n type: \"SRV\",\n data: { port: txt.port, target: `${txt.deviceId}.local` },\n ttl: 120,\n },\n // TXT: instance-name → key/value pairs\n {\n name: instanceName,\n type: \"TXT\",\n data: this.encodeTxt(txt),\n ttl: 120,\n },\n ];\n }\n\n private encodeTxt(txt: AdvertisedTxt): string[] {\n return [\n `dvaiVersion=${txt.dvaiVersion}`,\n `deviceId=${txt.deviceId}`,\n `deviceName=${txt.deviceName}`,\n `models=${txt.models.join(\",\")}`,\n `capability=${JSON.stringify(txt.capability)}`,\n `port=${txt.port}`,\n `secure=${txt.secure ? \"1\" : \"0\"}`,\n ];\n }\n\n private onResponse(response: MdnsResponse): void {\n const all = [...response.answers, ...(response.additionals ?? [])];\n let srv: MdnsAnswer | undefined;\n let txt: MdnsAnswer | undefined;\n for (const a of all) {\n if (a.type === \"SRV\" && String(a.name).endsWith(MDNS_SERVICE_TYPE)) srv = a;\n if (a.type === \"TXT\" && String(a.name).endsWith(MDNS_SERVICE_TYPE)) txt = a;\n }\n if (!srv || !txt) return;\n\n const decoded = this.decodeTxt(txt.data);\n if (!decoded || decoded.deviceId === this.opts.selfDeviceId) return;\n\n const srvData = srv.data as { port: number; target: string };\n const baseUrl = `${decoded.secure ? \"https\" : \"http\"}://${srvData.target}:${srvData.port}/v1`;\n\n const peer: Peer = {\n deviceId: decoded.deviceId,\n deviceName: decoded.deviceName,\n dvaiVersion: decoded.dvaiVersion,\n baseUrl,\n loadedModels: decoded.models,\n capability: decoded.capability,\n via: \"mdns\",\n secure: decoded.secure,\n lastSeenAt: Date.now(),\n };\n\n const existing = this.peerMap.get(peer.deviceId);\n this.peerMap.set(peer.deviceId, peer);\n if (!existing) {\n this.emit({ type: \"peer-up\", peer });\n }\n }\n\n private decodeTxt(raw: unknown): AdvertisedTxt | undefined {\n let lines: string[];\n if (Array.isArray(raw)) {\n lines = raw.map((b) => (Buffer.isBuffer(b) ? b.toString(\"utf8\") : String(b)));\n } else if (Buffer.isBuffer(raw)) {\n lines = raw.toString(\"utf8\").split(\"\\n\");\n } else if (typeof raw === \"string\") {\n lines = raw.split(\"\\n\");\n } else {\n return undefined;\n }\n const map: Record<string, string> = {};\n for (const line of lines) {\n const eq = line.indexOf(\"=\");\n if (eq < 0) continue;\n map[line.slice(0, eq)] = line.slice(eq + 1);\n }\n if (!map.deviceId || !map.dvaiVersion) return undefined;\n let capability: Record<string, number> = {};\n try {\n capability = JSON.parse(map.capability ?? \"{}\");\n } catch {\n // bad TXT — keep going with empty capability map\n }\n return {\n dvaiVersion: map.dvaiVersion,\n deviceId: map.deviceId,\n deviceName: map.deviceName ?? map.deviceId,\n models: map.models ? map.models.split(\",\").filter(Boolean) : [],\n capability,\n port: Number.parseInt(map.port ?? \"0\", 10),\n secure: map.secure === \"1\",\n };\n }\n\n private gc(): void {\n const ttl = this.opts.peerTtlMs ?? 90_000;\n const cutoff = Date.now() - ttl;\n for (const [id, peer] of this.peerMap) {\n if (peer.lastSeenAt < cutoff) {\n this.peerMap.delete(id);\n this.emit({ type: \"peer-down\", deviceId: id });\n }\n }\n }\n\n private emit(e: DiscoveryEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(e);\n } catch (err) {\n console.error(\"[DVAI/discovery] listener threw:\", err);\n }\n }\n }\n}\n","/**\n * Browser-side mDNS adapter — a no-op.\n *\n * Browsers don't speak mDNS. They CAN'T accept inbound HTTP requests\n * across origins reliably either, so even if we could discover peers\n * on the LAN, the browser couldn't act as an offload TARGET. Browser\n * is offload-source-only; native devices are the offload targets it\n * pairs with via the rendezvous server.\n *\n * This file exists so the composite discovery layer can pick a\n * runtime-appropriate impl without an `if (browser)` check.\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\n\nexport class BrowserMdnsDiscovery implements IDiscovery {\n // Empty — browsers can't speak mDNS, but this slot keeps the\n // composite discovery layer's typing clean.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n constructor(_opts: unknown = {}) {}\n\n async start(): Promise<void> {\n // No-op. Diagnostic log is at TRACE level only — this isn't an\n // error condition; it's an architectural fact.\n }\n async stop(): Promise<void> {\n // No-op.\n }\n peers(): Peer[] {\n return [];\n }\n subscribe(_listener: (e: DiscoveryEvent) => void): () => void {\n return () => {};\n }\n}\n","/**\n * Composite discovery — combines mDNS + static + rendezvous-paired\n * peers into a single unified peer stream. The offload decider only\n * talks to this layer; it doesn't know which source produced a peer.\n *\n * Per-source peers are deduplicated by `deviceId` — the most-recent\n * `lastSeenAt` wins, with mDNS preferred over static when both\n * exist (same device, but mDNS gives us a fresher TXT).\n */\n\nimport type { DiscoveryEvent, IDiscovery, Peer } from \"./types.js\";\n\nexport class CompositeDiscovery implements IDiscovery {\n private readonly listeners = new Set<(e: DiscoveryEvent) => void>();\n private readonly subscriptions: Array<() => void> = [];\n private started = false;\n\n constructor(private readonly sources: IDiscovery[]) {}\n\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n for (const source of this.sources) {\n const unsub = source.subscribe((e) => this.onEvent(e));\n this.subscriptions.push(unsub);\n await source.start();\n }\n }\n\n async stop(): Promise<void> {\n if (!this.started) return;\n this.started = false;\n for (const unsub of this.subscriptions) unsub();\n this.subscriptions.length = 0;\n for (const source of this.sources) {\n await source.stop();\n }\n }\n\n peers(): Peer[] {\n const merged = new Map<string, Peer>();\n for (const source of this.sources) {\n for (const peer of source.peers()) {\n const existing = merged.get(peer.deviceId);\n if (!existing || peer.lastSeenAt > existing.lastSeenAt) {\n merged.set(peer.deviceId, peer);\n }\n }\n }\n return Array.from(merged.values());\n }\n\n subscribe(listener: (e: DiscoveryEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private onEvent(e: DiscoveryEvent): void {\n // Forward all events. Consumers handle duplicates via the merged\n // peer set rather than us trying to dedupe events here (an event\n // for one source might still be informative — e.g. the rendezvous\n // peer dropped but the LAN peer is still up).\n for (const listener of this.listeners) {\n try {\n listener(e);\n } catch (err) {\n console.error(\"[DVAI/discovery] listener threw:\", err);\n }\n }\n }\n}\n\n/** Factory: pick the right mDNS adapter for the runtime. */\nexport async function createMdnsDiscovery(opts: {\n selfDeviceId: string;\n advertise?: import(\"./mdns-node.js\").AdvertisedTxt;\n}): Promise<IDiscovery> {\n if (\n typeof globalThis.process !== \"undefined\" &&\n globalThis.process.versions?.node\n ) {\n const { NodeMdnsDiscovery } = await import(\"./mdns-node.js\");\n return new NodeMdnsDiscovery(opts);\n }\n const { BrowserMdnsDiscovery } = await import(\"./mdns-browser.js\");\n return new BrowserMdnsDiscovery();\n}\n","/**\n * Public surface for the discovery module.\n */\n\nexport type { Peer, DiscoveryEvent, IDiscovery } from \"./types.js\";\nexport { MDNS_SERVICE_TYPE } from \"./types.js\";\nexport { StaticDiscovery } from \"./static.js\";\nexport { CompositeDiscovery, createMdnsDiscovery } from \"./composite.js\";\nexport { BrowserMdnsDiscovery } from \"./mdns-browser.js\";\n// Node-only — re-exported lazily; importing this in a browser bundle\n// will tree-shake away cleanly when discovery isn't used.\nexport type { AdvertisedTxt } from \"./mdns-node.js\";\n","/**\n * Pairing storage adapters. Mirrors capability/cache.ts shape — same\n * runtimes, same per-runtime impls, same default-cache-path resolution.\n */\n\nimport type { Pairing, PairingStore } from \"./types.js\";\n\n/** In-memory adapter (testing + browser-without-IndexedDB fallback). */\nexport class InMemoryPairingStore implements PairingStore {\n private readonly map = new Map<string, Pairing>();\n\n async get(peerDeviceId: string): Promise<Pairing | undefined> {\n return this.map.get(peerDeviceId);\n }\n async set(p: Pairing): Promise<void> {\n this.map.set(p.peerDeviceId, p);\n }\n async list(): Promise<Pairing[]> {\n return Array.from(this.map.values());\n }\n async remove(peerDeviceId: string): Promise<void> {\n this.map.delete(peerDeviceId);\n }\n async clear(): Promise<void> {\n this.map.clear();\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Browser — IndexedDB */\n/* -------------------------------------------------------------------------- */\n\nconst DB_NAME = \"dvai-bridge\";\nconst STORE_NAME = \"pairings-v1\";\n\nexport class IndexedDBPairingStore implements PairingStore {\n private dbPromise?: Promise<IDBDatabase>;\n\n private openDb(): Promise<IDBDatabase> {\n if (!this.dbPromise) {\n this.dbPromise = new Promise<IDBDatabase>((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, 1);\n req.onupgradeneeded = () => {\n const db = req.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: \"peerDeviceId\" });\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n }\n return this.dbPromise;\n }\n\n async get(peerDeviceId: string): Promise<Pairing | undefined> {\n const db = await this.openDb();\n return await new Promise((resolve, reject) => {\n const req = db.transaction(STORE_NAME, \"readonly\").objectStore(STORE_NAME).get(peerDeviceId);\n req.onsuccess = () => resolve(req.result as Pairing | undefined);\n req.onerror = () => reject(req.error);\n });\n }\n async set(p: Pairing): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).put(p);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n async list(): Promise<Pairing[]> {\n const db = await this.openDb();\n return await new Promise((resolve, reject) => {\n const req = db.transaction(STORE_NAME, \"readonly\").objectStore(STORE_NAME).getAll();\n req.onsuccess = () => resolve(req.result as Pairing[]);\n req.onerror = () => reject(req.error);\n });\n }\n async remove(peerDeviceId: string): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).delete(peerDeviceId);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n async clear(): Promise<void> {\n const db = await this.openDb();\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).clear();\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Node FS adapter */\n/* -------------------------------------------------------------------------- */\n\nexport class NodeFsPairingStore implements PairingStore {\n private readonly cachePath: string;\n private cache?: Record<string, Pairing>;\n\n constructor(cachePath?: string) {\n this.cachePath = cachePath ?? defaultPairingsPath();\n }\n\n private async load(): Promise<Record<string, Pairing>> {\n if (this.cache) return this.cache;\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n try {\n const raw = await fs.readFile(this.cachePath, \"utf8\");\n this.cache = JSON.parse(raw) as Record<string, Pairing>;\n } catch {\n this.cache = {};\n }\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n return this.cache;\n }\n\n private async save(): Promise<void> {\n if (!this.cache) return;\n const fs = await import(\"node:fs/promises\");\n await fs.writeFile(this.cachePath, JSON.stringify(this.cache, null, 2), \"utf8\");\n }\n\n async get(peerDeviceId: string): Promise<Pairing | undefined> {\n const c = await this.load();\n return c[peerDeviceId];\n }\n async set(p: Pairing): Promise<void> {\n const c = await this.load();\n c[p.peerDeviceId] = p;\n await this.save();\n }\n async list(): Promise<Pairing[]> {\n const c = await this.load();\n return Object.values(c);\n }\n async remove(peerDeviceId: string): Promise<void> {\n const c = await this.load();\n delete c[peerDeviceId];\n await this.save();\n }\n async clear(): Promise<void> {\n this.cache = {};\n await this.save();\n }\n}\n\nfunction defaultPairingsPath(): string {\n if (typeof globalThis.process !== \"undefined\") {\n const env = globalThis.process.env;\n const home = env.HOME ?? env.USERPROFILE ?? \".\";\n if (env.LOCALAPPDATA) return `${env.LOCALAPPDATA}/dvai-bridge/pairings.json`;\n if (env.XDG_CACHE_HOME) return `${env.XDG_CACHE_HOME}/dvai-bridge/pairings.json`;\n return `${home}/.cache/dvai-bridge/pairings.json`;\n }\n return \"./.dvai-bridge-pairings.json\";\n}\n\nexport function createPairingStore(): PairingStore {\n if (typeof indexedDB !== \"undefined\") return new IndexedDBPairingStore();\n if (typeof globalThis.process !== \"undefined\" && globalThis.process.versions?.node) {\n return new NodeFsPairingStore();\n }\n return new InMemoryPairingStore();\n}\n","/**\n * Pairing decision flow. Coordinates the host-app `onPairingRequest`\n * callback (which surfaces a UI prompt) with the persistent store.\n *\n * Default behaviour (no callback supplied): deny all incoming pairings.\n * That's the safe fallback — apps that haven't wired UI shouldn't\n * accidentally accept random LAN devices.\n */\n\nimport { generatePairingKey } from \"./handshake.js\";\nimport type { Pairing, PairingStore } from \"./types.js\";\n\nexport interface PairingPolicyOptions {\n store: PairingStore;\n /**\n * Host-app callback that returns the user's approve/deny decision.\n *\n * Return value:\n * - `true` / `false` — backwards-compat boolean. PairingPolicy\n * generates a fresh pairingKey when approved.\n * - `{ approved: true, pairingKey }` — host has its OWN pairing\n * state (e.g. v3.1 Hub's MultiTenantPairing) and wants\n * PairingPolicy to use the host-supplied key. Avoids two\n * parallel stores generating divergent keys for the same peer.\n * - `{ approved: false }` — denied.\n */\n onPairingRequest?: (\n peerDeviceId: string,\n peerDeviceName: string,\n appId?: string,\n ) => Promise<\n | boolean\n | { approved: true; pairingKey: string }\n | { approved: false }\n >;\n /** Pairing TTL in days. Default 30. */\n expireAfterDays?: number;\n}\n\nexport interface IncomingHandshake {\n peerDeviceId: string;\n peerDeviceName: string;\n via: \"lan-handshake\" | \"rendezvous-qr\";\n /**\n * v3.1 wire-protocol extension. Identifies which application on the\n * peer device is requesting pairing, so multi-tenant targets (the\n * v3.1 Hub) can isolate per-app pairings, capability caches, and\n * audit logs. Optional for backwards compat — v3.0 SDKs that don't\n * send this field still pair, just under the deviceId-as-appId\n * fallback the Hub uses today.\n */\n appId?: string;\n}\n\nexport class PairingPolicy {\n constructor(private readonly opts: PairingPolicyOptions) {}\n\n /**\n * Get an existing pairing, applying TTL expiry. Returns undefined\n * if the pairing is missing or expired.\n */\n async getActive(peerDeviceId: string): Promise<Pairing | undefined> {\n const existing = await this.opts.store.get(peerDeviceId);\n if (!existing) return undefined;\n const ttlMs = (this.opts.expireAfterDays ?? 30) * 24 * 60 * 60 * 1000;\n if (Date.now() - existing.lastUsedAt > ttlMs) {\n await this.opts.store.remove(peerDeviceId);\n return undefined;\n }\n return existing;\n }\n\n /**\n * Process an incoming pairing request. If we already have an active\n * pairing for this peer, reuse it (and bump lastUsedAt). Otherwise\n * call the host-app's onPairingRequest hook to ask for approval.\n *\n * Returns the pairing key (base64-url) on approval; throws otherwise.\n */\n async approveOrFetch(req: IncomingHandshake): Promise<Pairing> {\n const existing = await this.getActive(req.peerDeviceId);\n if (existing) {\n existing.lastUsedAt = Date.now();\n await this.opts.store.set(existing);\n return existing;\n }\n\n const callback = this.opts.onPairingRequest;\n const result = callback\n ? await callback(req.peerDeviceId, req.peerDeviceName, req.appId)\n : false;\n const approved =\n typeof result === \"boolean\" ? result : result.approved;\n if (!approved) {\n throw new Error(\n `[DVAI/pairing] denied: peer ${req.peerDeviceId} (${req.peerDeviceName})${callback ? \"\" : \" (no onPairingRequest callback supplied)\"}`,\n );\n }\n // If the host returned a pairingKey (v3.1 Hub provides one from\n // its own MultiTenantPairing store), use it. Otherwise generate\n // a fresh one.\n const hostKey =\n typeof result === \"object\" && \"pairingKey\" in result\n ? result.pairingKey\n : undefined;\n const fresh: Pairing = {\n peerDeviceId: req.peerDeviceId,\n peerDeviceName: req.peerDeviceName,\n pairingKey: hostKey ?? generatePairingKey(),\n pairedAt: Date.now(),\n lastUsedAt: Date.now(),\n via: req.via,\n };\n await this.opts.store.set(fresh);\n return fresh;\n }\n\n /** Mark a pairing as used (bumps lastUsedAt). */\n async touch(peerDeviceId: string): Promise<void> {\n const existing = await this.opts.store.get(peerDeviceId);\n if (!existing) return;\n existing.lastUsedAt = Date.now();\n await this.opts.store.set(existing);\n }\n\n async revoke(peerDeviceId: string): Promise<void> {\n await this.opts.store.remove(peerDeviceId);\n }\n}\n","export type { Pairing, PairingStore, HandshakeRequest, HandshakeResponse } from \"./types.js\";\nexport {\n generatePairingKey,\n generateNonce,\n signHmac,\n verifyHmac,\n composeSignedMessage,\n} from \"./handshake.js\";\nexport {\n InMemoryPairingStore,\n IndexedDBPairingStore,\n NodeFsPairingStore,\n createPairingStore,\n} from \"./store.js\";\nexport { PairingPolicy, type PairingPolicyOptions, type IncomingHandshake } from \"./policy.js\";\n","/**\n * Phase 3 — `/v1/dvai/*` handlers.\n *\n * Hosted by the same in-process HTTP server (or MSW intercept in\n * browser) that already serves the OpenAI surface. Routes:\n *\n * GET /v1/dvai/health — liveness, capacity, version\n * GET /v1/dvai/capability — this device's capability map\n * GET /v1/dvai/peers — discovered peer list\n * POST /v1/dvai/probe — manually trigger a capability probe\n * POST /v1/dvai/handshake — LAN-pairing handshake\n * POST /v1/dvai/pair-qr — start a rendezvous session, return QR payload\n * POST /v1/dvai/pair-scan — submit a scanned QR payload, complete the join\n *\n * These handlers are pure-ish — they take a context object with the\n * relevant collaborators (capability cache, discovery, pairing policy,\n * rendezvous client, etc.) and return JSON responses. Wired into the\n * core's existing handler pipeline by src/index.ts.\n */\n\nimport type { CapabilityCache, CapabilityScore, ProbableBackend } from \"../../capability/index.js\";\nimport { getCapability, probeAndCache, ensureDeviceId } from \"../../capability/index.js\";\nimport type { Peer, IDiscovery } from \"../../discovery/index.js\";\nimport type { PairingPolicy, IncomingHandshake } from \"../../pairing/index.js\";\n\nexport interface DvaiHandlerContext {\n /** Library SemVer — used in cache keys + responses. */\n libraryVersion: string;\n /** Currently-loaded model ID (for capability lookup). */\n currentModelId?: string;\n /** Capability cache. */\n capabilityCache: CapabilityCache;\n /** Backend reference for the probe endpoint. */\n backend?: ProbableBackend;\n /** Discovery layer. */\n discovery?: IDiscovery;\n /** Pairing policy. */\n pairingPolicy?: PairingPolicy;\n /** Server-uptime epoch. */\n startedAt: number;\n}\n\n/** Generic handler shape: takes parsed request body, returns JSON-stringifiable. */\nexport type DvaiHandler = (req: { body: unknown }) => Promise<{\n status: number;\n body: unknown;\n}>;\n\n/* -------------------------------------------------------------------------- */\n/* Handlers */\n/* -------------------------------------------------------------------------- */\n\nexport function handleHealth(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => ({\n status: 200,\n body: {\n status: \"ok\",\n version: ctx.libraryVersion,\n uptimeSec: Math.floor((Date.now() - ctx.startedAt) / 1000),\n currentModelId: ctx.currentModelId ?? null,\n },\n });\n}\n\nexport function handleCapability(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n const all: CapabilityScore[] = await ctx.capabilityCache.list();\n return { status: 200, body: { scores: all } };\n };\n}\n\nexport function handlePeers(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n const peers: Peer[] = ctx.discovery?.peers() ?? [];\n return { status: 200, body: { peers } };\n };\n}\n\nexport function handleProbe(ctx: DvaiHandlerContext): DvaiHandler {\n return async (req) => {\n if (!ctx.backend) {\n return {\n status: 503,\n body: { error: { type: \"no_backend\", message: \"no backend currently loaded\" } },\n };\n }\n const body = (req.body ?? {}) as { modelId?: string };\n const modelId = body.modelId ?? ctx.currentModelId;\n if (!modelId) {\n return {\n status: 400,\n body: {\n error: {\n type: \"missing_model_id\",\n message: \"supply `modelId` in request body or call after a model is loaded\",\n },\n },\n };\n }\n const score = await probeAndCache({\n cache: ctx.capabilityCache,\n backend: ctx.backend,\n modelId,\n libraryVersion: ctx.libraryVersion,\n });\n return { status: 200, body: { score } };\n };\n}\n\nexport function handleHandshake(ctx: DvaiHandlerContext): DvaiHandler {\n return async (req) => {\n if (!ctx.pairingPolicy) {\n return {\n status: 503,\n body: { error: { type: \"pairing_disabled\", message: \"pairing not configured\" } },\n };\n }\n const body = req.body as Partial<IncomingHandshake> | undefined;\n if (!body?.peerDeviceId || !body?.peerDeviceName) {\n return {\n status: 400,\n body: { error: { type: \"malformed_handshake\", message: \"missing peerDeviceId / peerDeviceName\" } },\n };\n }\n try {\n const pairing = await ctx.pairingPolicy.approveOrFetch({\n peerDeviceId: body.peerDeviceId,\n peerDeviceName: body.peerDeviceName,\n via: body.via ?? \"lan-handshake\",\n ...(body.appId !== undefined ? { appId: body.appId } : {}),\n });\n // v3.1 wire protocol: echo the pairing key in the response so\n // the requester can HMAC-sign subsequent calls. LAN trust model\n // — the response only crosses the same Wi-Fi the handshake did,\n // and the protocol is opt-in (offload.enabled defaults to false).\n // The rendezvous-QR flow uses ECDH key agreement instead and\n // doesn't reach this handler.\n return {\n status: 200,\n body: {\n paired: true,\n pairedAt: pairing.pairedAt,\n via: pairing.via,\n pairingKey: pairing.pairingKey,\n peerDeviceId: pairing.peerDeviceId,\n },\n };\n } catch (err) {\n return {\n status: 403,\n body: { error: { type: \"pairing_denied\", message: String(err) } },\n };\n }\n };\n}\n\nexport function handlePairQr(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n // The actual rendezvous-WS dance lives in src/rendezvous/client.ts;\n // this endpoint is a thin shim that returns a 501 here in v3.0.0-rc1\n // because the in-DVAI integration (creating a session + holding it\n // open until the target scans) needs the per-platform glue from\n // Task 8 (per-SDK integration). The endpoint shape is locked in\n // for forward-compat; the implementation lights up in 8a–8f.\n return {\n status: 501,\n body: {\n error: {\n type: \"not_implemented_yet\",\n message:\n \"POST /v1/dvai/pair-qr requires per-SDK integration (Task 8); \" +\n \"the rendezvous client surface (rendezvous/client.ts) is callable \" +\n \"directly until then.\",\n },\n },\n };\n };\n}\n\nexport function handlePairScan(ctx: DvaiHandlerContext): DvaiHandler {\n return async () => {\n return {\n status: 501,\n body: {\n error: {\n type: \"not_implemented_yet\",\n message: \"POST /v1/dvai/pair-scan requires per-SDK integration (Task 8).\",\n },\n },\n };\n };\n}\n\n/**\n * Build the route → handler map. The transport layer (src/transports/)\n * dispatches incoming requests to these.\n */\nexport function buildDvaiRoutes(\n ctx: DvaiHandlerContext,\n): Record<string, DvaiHandler> {\n return {\n \"GET /v1/dvai/health\": handleHealth(ctx),\n \"GET /v1/dvai/capability\": handleCapability(ctx),\n \"GET /v1/dvai/peers\": handlePeers(ctx),\n \"POST /v1/dvai/probe\": handleProbe(ctx),\n \"POST /v1/dvai/handshake\": handleHandshake(ctx),\n \"POST /v1/dvai/pair-qr\": handlePairQr(ctx),\n \"POST /v1/dvai/pair-scan\": handlePairScan(ctx),\n };\n}\n","/**\n * NodeLlamaCppBackend: Wraps `node-llama-cpp` for local inference in Node.\n *\n * - Loads a GGUF model file from disk via `getLlama()` + `loadModel()`.\n * - Exposes the same duck-typed surface as TransformersBackend / WebLLMBackend\n * (`chatCompletion`, `createStreamingResponse`, `unload`) so the\n * transport-agnostic handlers in `handlers/` work without changes.\n * - Streams via the LlamaChatSession's `prompt({ onTextChunk })` callback,\n * re-shaped into OpenAI-style SSE chunks.\n * - Honors `generationTimeout` for both the blocking and streaming paths.\n *\n * Native peer dependency. `node-llama-cpp` ships its own prebuilt llama.cpp\n * binaries; consumers add it to their own `package.json`. We keep it as an\n * **optional** peer dep so the package still installs cleanly in browser-only\n * projects.\n */\n\nexport interface NodeLlamaCppBackendConfig {\n\t/** Absolute or relative path to a GGUF model file. Required. */\n\tmodelPath: string;\n\t/** Number of GPU layers to offload (Metal on macOS, CUDA on Linux/Win). Default: 99 (max). */\n\tgpuLayers?: number;\n\t/** Number of CPU threads. Default: undefined (let node-llama-cpp pick). */\n\tthreads?: number;\n\t/** Context window in tokens. Default: 2048. */\n\tcontextSize?: number;\n\t/** Generation timeout in ms. Default: 60000. */\n\tgenerationTimeout?: number;\n\t/** Logical model identifier echoed back in OpenAI responses. Default: basename of modelPath. */\n\tmodelId?: string;\n}\n\nexport class NodeLlamaCppBackend {\n\tprivate modelPath: string;\n\tprivate gpuLayers: number;\n\tprivate threads?: number;\n\tprivate contextSize: number;\n\tprivate generationTimeout: number;\n\tprivate modelId: string;\n\n\tprivate llama: any = null;\n\tprivate model: any = null;\n\tprivate context: any = null;\n\tprivate session: any = null;\n\n\t/** Mirrors WebLLMBackend; not used by this backend but keeps the duck-type stable. */\n\tpublic lastFatalError: string | null = null;\n\n\tconstructor(config: NodeLlamaCppBackendConfig) {\n\t\tif (!config.modelPath) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI/NodeLlamaCpp] modelPath is required (path to a GGUF file).\",\n\t\t\t);\n\t\t}\n\t\tthis.modelPath = config.modelPath;\n\t\tthis.gpuLayers = config.gpuLayers ?? 99;\n\t\tthis.threads = config.threads;\n\t\tthis.contextSize = config.contextSize ?? 2048;\n\t\tthis.generationTimeout = config.generationTimeout ?? 60000;\n\t\tthis.modelId =\n\t\t\tconfig.modelId ||\n\t\t\tthis.modelPath.split(/[\\\\/]/).pop()?.replace(/\\.gguf$/i, \"\") ||\n\t\t\t\"node-llama-cpp\";\n\t}\n\n\tclearFatalError(): void {\n\t\tthis.lastFatalError = null;\n\t}\n\n\tasync initialize(onProgress?: (info: any) => void): Promise<void> {\n\t\tlet llamaModule: any;\n\t\ttry {\n\t\t\tllamaModule = await import(\"node-llama-cpp\");\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t'[DVAI/NodeLlamaCpp] backend selected but \"node-llama-cpp\" is not installed.\\n' +\n\t\t\t\t\t\"Install it with: npm install node-llama-cpp\",\n\t\t\t);\n\t\t}\n\n\t\tonProgress?.({ text: \"Loading llama.cpp runtime...\", progress: 0 });\n\t\tthis.llama = await llamaModule.getLlama();\n\n\t\tonProgress?.({\n\t\t\ttext: `Loading model: ${this.modelId}`,\n\t\t\tprogress: 0.1,\n\t\t});\n\t\tthis.model = await this.llama.loadModel({\n\t\t\tmodelPath: this.modelPath,\n\t\t\tgpuLayers: this.gpuLayers,\n\t\t});\n\n\t\tonProgress?.({ text: \"Creating context...\", progress: 0.9 });\n\t\tthis.context = await this.model.createContext({\n\t\t\tcontextSize: this.contextSize,\n\t\t\tthreads: this.threads,\n\t\t});\n\n\t\tthis.session = new llamaModule.LlamaChatSession({\n\t\t\tcontextSequence: this.context.getSequence(),\n\t\t});\n\n\t\tonProgress?.({ text: \"Ready\", progress: 1 });\n\t\tconsole.log(\n\t\t\t`[DVAI/NodeLlamaCpp] Loaded \"${this.modelId}\" (gpuLayers=${this.gpuLayers}, contextSize=${this.contextSize})`,\n\t\t);\n\t}\n\n\tisWorkerBased(): boolean {\n\t\treturn false;\n\t}\n\n\tgetModelId(): string {\n\t\treturn this.modelId;\n\t}\n\n\tprivate withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst t = setTimeout(\n\t\t\t\t() => reject(new Error(`Generation timed out after ${ms}ms`)),\n\t\t\t\tms,\n\t\t\t);\n\t\t\tp.then(\n\t\t\t\t(v) => {\n\t\t\t\t\tclearTimeout(t);\n\t\t\t\t\tresolve(v);\n\t\t\t\t},\n\t\t\t\t(e) => {\n\t\t\t\t\tclearTimeout(t);\n\t\t\t\t\treject(e);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Build a single prompt string from an OpenAI-style messages array.\n\t * The session keeps its own chat history, so we collapse the *current*\n\t * user-turn into one prompt and rely on node-llama-cpp's templating\n\t * for everything else.\n\t */\n\tprivate extractUserPrompt(messages: any[]): {\n\t\tsystemPrompt?: string;\n\t\tuserPrompt: string;\n\t} {\n\t\tconst sys = messages.find((m: any) => m.role === \"system\");\n\t\tconst lastUser = [...messages].reverse().find((m: any) => m.role === \"user\");\n\t\tconst flatten = (c: any): string => {\n\t\t\tif (typeof c === \"string\") return c;\n\t\t\tif (Array.isArray(c)) return c.map(flatten).join(\"\");\n\t\t\tif (c && typeof c === \"object\")\n\t\t\t\treturn c.text || c.content || JSON.stringify(c);\n\t\t\treturn String(c || \"\");\n\t\t};\n\t\treturn {\n\t\t\tsystemPrompt: sys ? flatten(sys.content) : undefined,\n\t\t\tuserPrompt: lastUser ? flatten(lastUser.content) : \"\",\n\t\t};\n\t}\n\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.session) {\n\t\t\tthrow new Error(\"[DVAI/NodeLlamaCpp] Backend not initialized.\");\n\t\t}\n\t\tconst messages = requestBody.messages || [];\n\t\tconst { systemPrompt, userPrompt } = this.extractUserPrompt(messages);\n\n\t\tconst maxTokens =\n\t\t\trequestBody.max_tokens ?? requestBody.max_completion_tokens ?? 256;\n\t\tconst temperature = requestBody.temperature ?? 0.7;\n\t\tconst topP = requestBody.top_p ?? 1.0;\n\n\t\tconst promptOpts: any = {\n\t\t\tmaxTokens,\n\t\t\ttemperature,\n\t\t\ttopP,\n\t\t};\n\t\tif (systemPrompt) promptOpts.systemPrompt = systemPrompt;\n\n\t\tconst text: string = await this.withTimeout(\n\t\t\tthis.session.prompt(userPrompt, promptOpts),\n\t\t\tthis.generationTimeout,\n\t\t);\n\n\t\treturn {\n\t\t\tid: `chatcmpl-${Date.now()}`,\n\t\t\tobject: \"chat.completion\",\n\t\t\tcreated: Math.floor(Date.now() / 1000),\n\t\t\tmodel: this.modelId,\n\t\t\tchoices: [\n\t\t\t\t{\n\t\t\t\t\tindex: 0,\n\t\t\t\t\tmessage: { role: \"assistant\", content: text },\n\t\t\t\t\tfinish_reason: \"stop\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tusage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n\t\t};\n\t}\n\n\tcreateStreamingResponse(requestBody: any): ReadableStream<Uint8Array> {\n\t\tif (!this.session) {\n\t\t\tthrow new Error(\"[DVAI/NodeLlamaCpp] Backend not initialized.\");\n\t\t}\n\t\tconst session = this.session;\n\t\tconst modelId = this.modelId;\n\t\tconst generationTimeout = this.generationTimeout;\n\t\tconst messages = requestBody.messages || [];\n\t\tconst { systemPrompt, userPrompt } = this.extractUserPrompt(messages);\n\t\tconst maxTokens =\n\t\t\trequestBody.max_tokens ?? requestBody.max_completion_tokens ?? 256;\n\t\tconst temperature = requestBody.temperature ?? 0.7;\n\t\tconst topP = requestBody.top_p ?? 1.0;\n\n\t\treturn new ReadableStream<Uint8Array>({\n\t\t\tasync start(controller) {\n\t\t\t\tconst completionId = `chatcmpl-${Date.now()}`;\n\t\t\t\tconst created = Math.floor(Date.now() / 1000);\n\t\t\t\tconst encoder = new TextEncoder();\n\n\t\t\t\tconst enqueueChunk = (text: string) => {\n\t\t\t\t\tconst chunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [\n\t\t\t\t\t\t\t{ index: 0, delta: { content: text }, finish_reason: null },\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tconst enqueueFinal = (finishReason = \"stop\") => {\n\t\t\t\t\tconst finalChunk = {\n\t\t\t\t\t\tid: completionId,\n\t\t\t\t\t\tobject: \"chat.completion.chunk\",\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t\tmodel: modelId,\n\t\t\t\t\t\tchoices: [{ index: 0, delta: {}, finish_reason: finishReason }],\n\t\t\t\t\t};\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(`data: ${JSON.stringify(finalChunk)}\\n\\n`),\n\t\t\t\t\t);\n\t\t\t\t};\n\n\t\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst promptOpts: any = {\n\t\t\t\t\t\tmaxTokens,\n\t\t\t\t\t\ttemperature,\n\t\t\t\t\t\ttopP,\n\t\t\t\t\t\tonTextChunk: (text: string) => {\n\t\t\t\t\t\t\tif (text) enqueueChunk(text);\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tif (systemPrompt) promptOpts.systemPrompt = systemPrompt;\n\n\t\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\t\ttimeoutId = setTimeout(\n\t\t\t\t\t\t\t() =>\n\t\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t`Generation timed out after ${generationTimeout}ms`,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tgenerationTimeout,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tsession.prompt(userPrompt, promptOpts).then(\n\t\t\t\t\t\t\t() => resolve(),\n\t\t\t\t\t\t\t(e: any) => reject(e),\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tenqueueFinal(\"stop\");\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"[DVAI/NodeLlamaCpp] Stream error:\",\n\t\t\t\t\t\terror?.message ?? error,\n\t\t\t\t\t);\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tencoder.encode(\n\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: error?.message ?? String(error) })}\\n\\n`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\t\tcontroller.enqueue(encoder.encode(\"data: [DONE]\\n\\n\"));\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync unload(): Promise<void> {\n\t\ttry {\n\t\t\tif (this.session && typeof this.session.dispose === \"function\") {\n\t\t\t\tawait this.session.dispose();\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t/* best effort */\n\t\t}\n\t\ttry {\n\t\t\tif (this.context && typeof this.context.dispose === \"function\") {\n\t\t\t\tawait this.context.dispose();\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t/* best effort */\n\t\t}\n\t\ttry {\n\t\t\tif (this.model && typeof this.model.dispose === \"function\") {\n\t\t\t\tawait this.model.dispose();\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t/* best effort */\n\t\t}\n\t\tthis.session = null;\n\t\tthis.context = null;\n\t\tthis.model = null;\n\t\tthis.llama = null;\n\t}\n}\n","/**\n * WebLLMBackend: Wraps @mlc-ai/web-llm with robustness improvements.\n * - Runs inference in a Web Worker to keep main thread unblocked\n * - Falls back to main-thread engine if worker URL is not available\n * - Blank-chunk detection to abort runaway streams\n * - Generation timeout to prevent infinite loops\n * - finish_reason checks to properly terminate streams\n * - Engine state cleanup after failures\n */\n\nexport interface WebLLMBackendConfig {\n\tmodelId: string;\n\tgenerationTimeout: number;\n\tmaxBlankChunks: number;\n\tworkerUrl?: string;\n\tonProgress?: (info: any) => void;\n}\n\nexport class WebLLMBackend {\n\tprivate engine: any = null; // MLCEngine | WebWorkerMLCEngine\n\tprivate modelId: string;\n\tprivate generationTimeout: number;\n\tprivate maxBlankChunks: number;\n\tprivate workerUrl?: string;\n\tprivate usingWorker: boolean = false;\n\n\t/**\n\t * Set to a non-null string when a fatal error occurs (blank output, timeout)\n\t * that requires a full unload → reload cycle at the orchestrator level.\n\t */\n\tpublic lastFatalError: string | null = null;\n\n\tconstructor(config: WebLLMBackendConfig) {\n\t\tthis.modelId = config.modelId;\n\t\tthis.generationTimeout = config.generationTimeout;\n\t\tthis.maxBlankChunks = config.maxBlankChunks;\n\t\tthis.workerUrl = config.workerUrl;\n\t}\n\n\t/** Clear the fatal error flag after a successful recovery. */\n\tclearFatalError(): void {\n\t\tthis.lastFatalError = null;\n\t}\n\n\tasync initialize(onProgress?: (info: any) => void): Promise<void> {\n\t\tconst webllm = await import(\"@mlc-ai/web-llm\");\n\n\t\t// Try worker-based engine first (fully offloads inference from main thread)\n\t\tif (this.workerUrl && typeof Worker !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tconst worker = new Worker(this.workerUrl, { type: \"module\" });\n\t\t\t\tthis.engine = new webllm.WebWorkerMLCEngine(worker, {\n\t\t\t\t\tinitProgressCallback: onProgress,\n\t\t\t\t});\n\t\t\t\tawait this.engine.reload(this.modelId);\n\t\t\t\tthis.usingWorker = true;\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[DVAI/WebLLM] Initialized with Web Worker (main thread unblocked)\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[DVAI/WebLLM] Worker initialization failed, falling back to main thread:\",\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: main-thread engine (WebGPU compute is still async/non-blocking)\n\t\tthis.engine = await webllm.CreateMLCEngine(this.modelId, {\n\t\t\tinitProgressCallback: onProgress,\n\t\t});\n\t\tthis.usingWorker = false;\n\t\tconsole.log(\n\t\t\t\"[DVAI/WebLLM] Initialized on main thread (WebGPU compute is async)\",\n\t\t);\n\t}\n\n\tisWorkerBased(): boolean {\n\t\treturn this.usingWorker;\n\t}\n\n\tgetEngine(): any {\n\t\treturn this.engine;\n\t}\n\n\t/**\n\t * Non-streaming chat completion with timeout protection.\n\t */\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.engine) throw new Error(\"WebLLM engine not initialized\");\n\n\t\tconst result: any = await this.withTimeout(\n\t\t\tthis.engine.chat.completions.create({\n\t\t\t\t...requestBody,\n\t\t\t\tstream: false,\n\t\t\t}),\n\t\t\tthis.generationTimeout,\n\t\t);\n\n\t\t// Validate response has actual content\n\t\tconst content = result?.choices?.[0]?.message?.content;\n\t\tif (content === undefined || content === null || content === \"\") {\n\t\t\tconsole.warn(\n\t\t\t\t\"[DVAI/WebLLM] Warning: Engine returned blank content — flagging for full restart.\",\n\t\t\t);\n\t\t\tthis.lastFatalError = \"blank_output\";\n\t\t\ttry {\n\t\t\t\tawait this.engine.resetChat();\n\t\t\t} catch (_) {\n\t\t\t\t/* best effort */\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t\"WebLLM engine returned blank content. A full engine restart is required.\",\n\t\t\t);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Streaming chat completion with blank-chunk detection, timeout, and finish_reason termination.\n\t * Returns a ReadableStream of SSE-formatted data.\n\t */\n\tcreateStreamingResponse(requestBody: any): ReadableStream<Uint8Array> {\n\t\tconst engine = this.engine;\n\t\tif (!engine) throw new Error(\"WebLLM engine not initialized\");\n\t\tconst maxBlankChunks = this.maxBlankChunks;\n\t\tconst generationTimeout = this.generationTimeout;\n\t\t// Capture class reference for use inside ReadableStream closure\n\t\tconst backend = this;\n\n\t\treturn new ReadableStream<Uint8Array>({\n\t\t\tasync start(controller) {\n\t\t\t\tlet consecutiveBlanks = 0;\n\t\t\t\tlet timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\t\t\t\ttry {\n\t\t\t\t\tconst asyncChunkGenerator = (await engine.chat.completions.create({\n\t\t\t\t\t\t...requestBody,\n\t\t\t\t\t\tstream: true,\n\t\t\t\t\t})) as unknown as AsyncIterable<any>;\n\n\t\t\t\t\t// Set overall generation timeout\n\t\t\t\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\t\t\treject(\n\t\t\t\t\t\t\t\tnew Error(`Generation timed out after ${generationTimeout}ms`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}, generationTimeout);\n\t\t\t\t\t});\n\n\t\t\t\t\tconst streamPromise = (async () => {\n\t\t\t\t\t\tfor await (const chunk of asyncChunkGenerator) {\n\t\t\t\t\t\t\tconst delta = chunk?.choices?.[0]?.delta;\n\t\t\t\t\t\t\tconst finishReason = chunk?.choices?.[0]?.finish_reason;\n\n\t\t\t\t\t\t\t// Check for blank chunks\n\t\t\t\t\t\t\tif (!delta?.content && delta?.content !== undefined) {\n\t\t\t\t\t\t\t\tconsecutiveBlanks++;\n\t\t\t\t\t\t\t\tif (consecutiveBlanks >= maxBlankChunks) {\n\t\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t\t`[DVAI/WebLLM] ${maxBlankChunks} consecutive blank chunks detected — flagging for full restart.`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tbackend.lastFatalError = \"blank_stream\";\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tengine.interruptGenerate();\n\t\t\t\t\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tawait engine.resetChat();\n\t\t\t\t\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: \"Stream aborted: too many blank chunks. Engine restart required.\" })}\\n\\n`,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconsecutiveBlanks = 0;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Emit the chunk\n\t\t\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\t\t\tnew TextEncoder().encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// Check if generation is complete\n\t\t\t\t\t\t\tif (finishReason === \"stop\" || finishReason === \"length\") {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\n\t\t\t\t\t// Race: stream vs timeout\n\t\t\t\t\tawait Promise.race([streamPromise, timeoutPromise]);\n\t\t\t\t} catch (error: any) {\n\t\t\t\t\tconsole.error(\"[DVAI/WebLLM] Stream error:\", error.message);\n\t\t\t\t\t// Flag timeout errors for full restart\n\t\t\t\t\tif (error.message?.includes(\"timed out\")) {\n\t\t\t\t\t\tbackend.lastFatalError = \"timeout\";\n\t\t\t\t\t}\n\t\t\t\t\t// Try to interrupt and reset on failure\n\t\t\t\t\ttry {\n\t\t\t\t\t\tengine.interruptGenerate();\n\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait engine.resetChat();\n\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\t/* best effort */\n\t\t\t\t\t}\n\t\t\t\t\tcontroller.enqueue(\n\t\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\t\t`data: ${JSON.stringify({ error: error.message })}\\n\\n`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} finally {\n\t\t\t\t\tif (timeoutId) clearTimeout(timeoutId);\n\t\t\t\t\tcontroller.enqueue(new TextEncoder().encode(\"data: [DONE]\\n\\n\"));\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync unload(): Promise<void> {\n\t\tif (this.engine) {\n\t\t\tawait this.engine.unload();\n\t\t\tthis.engine = null;\n\t\t}\n\t}\n\n\t/** Wraps a promise with a timeout. */\n\tprivate withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() => reject(new Error(`Generation timed out after ${ms}ms`)),\n\t\t\t\tms,\n\t\t\t);\n\t\t\tpromise\n\t\t\t\t.then((val) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tresolve(val);\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(err);\n\t\t\t\t});\n\t\t});\n\t}\n}\n","import { LicenseValidator } from \"./license/index.js\";\nimport type { LicenseStatus } from \"./license/index.js\";\nimport { type HandlerContext } from \"./handlers/index.js\";\n\n// Re-export types from backends\nexport type { TransformersProgressInfo } from \"./TransformersBackend.js\";\nexport { detectWebGPU } from \"./TransformersBackend.js\";\n\n// Re-export InitProgressReport from web-llm for backward compatibility\nexport type { InitProgressReport } from \"@mlc-ai/web-llm\";\n\n// Re-export legacy helpers from the handlers module for backward compat.\n// Existing tests/consumers import these from \"@dvai-bridge/core\".\nexport {\n\tchatToLegacyCompletion,\n\tlegacyCompletionStreamAdapter,\n} from \"./handlers/completions.js\";\n\n// v3.1 — re-export HMAC primitives so consumers (the Hub, native SDKs,\n// rendezvous clients) can compose + verify signed messages without\n// reaching into deep package paths.\nexport {\n\tcomposeSignedMessage,\n\tverifyHmac,\n\tsignHmac,\n\tgenerateNonce,\n\tgeneratePairingKey,\n} from \"./pairing/handshake.js\";\n\n// v4.0 — re-export the license module so host apps (dashboards, dev\n// tools, license-aware UI shells) can run validation independently of\n// the heavy DVAI.initialize() boot sequence. Useful for things like\n// \"show a license-status pill in the title bar\" without paying the\n// model-load cost of a full init.\nexport {\n\tLicenseValidator,\n\tLicenseRequiredError,\n\tisPaidTier,\n\tDEFAULT_LICENSE_FILENAME,\n\tDVAI_PUBLIC_KEYS,\n\tPLACEHOLDER_KID,\n} from \"./license/index.js\";\nexport type {\n\tLicenseStatus,\n\tLicenseTier,\n\tLicenseValidatorOptions,\n\tLicenseDiscoveryOptions,\n\tDvaiLicensePayload,\n\tDvaiPlatform,\n\tDvaiPublicKeyJwk,\n} from \"./license/index.js\";\n\nexport type BackendType = \"webllm\" | \"transformers\" | \"native\" | \"auto\";\nexport type DeviceType = \"webgpu\" | \"cpu\" | \"auto\";\nexport type {\n\tPipelineTask,\n\tCreatePipelineFn,\n\tPipelineCallable,\n} from \"./TransformersBackend.js\";\n\nexport interface DVAIConfig {\n\t/** The model ID for web-llm backend. Default: \"gemma-2-2b-it-q4f16_1-MLC\" */\n\tmodelId?: string;\n\t/**\n\t * The backend engine to use. Default: \"webllm\". Set to \"auto\" to auto-detect.\n\t * - \"webllm\" → @mlc-ai/web-llm (browser, WebGPU)\n\t * - \"transformers\" → @huggingface/transformers (browser or Node)\n\t * - \"native\" → node-llama-cpp (Node only; loads a GGUF file)\n\t * - \"auto\" → resolved at runtime\n\t */\n\tbackend?: BackendType;\n\t/** HuggingFace model ID for Transformers.js backend. Default: \"onnx-community/gemma-3n-E2B-it-ONNX\" */\n\ttransformersModelId?: string;\n\t/** Pipeline task for Transformers.js (e.g. \"text-generation\", \"text-to-image\", \"automatic-speech-recognition\"). Default: \"text-generation\" */\n\tpipelineTask?: string;\n\t/** Device for Transformers.js - \"webgpu\", \"cpu\", or \"auto\" (detect). Default: \"auto\" */\n\tdevice?: DeviceType;\n\t/** Quantization for Transformers.js (e.g. \"q4\", \"q8\", \"f16\"). Default: undefined */\n\tdtype?: string;\n\t/** Generation timeout in ms. Default: 60000 (60s) */\n\tgenerationTimeout?: number;\n\t/** Maximum consecutive blank chunks before aborting stream (WebLLM). Default: 20 */\n\tmaxBlankChunks?: number;\n\t/** Maximum auto-recovery retries on fatal WebLLM errors (blank output/timeout). Default: 2 */\n\tmaxRetries?: number;\n\t/** Mock URL for MSW interception. Default: \"https://api.openai.local/v1/chat/completions\" */\n\tmockUrl?: string;\n\t/** Path to the MSW service worker script. Default: \"/mockServiceWorker.js\" */\n\tserviceWorkerUrl?: string;\n\t/** URL to the WebLLM worker script (for offloading inference). Default: \"/dvai-webllm.worker.js\" */\n\twebllmWorkerUrl?: string;\n\t/** URL to the Transformers.js worker script (for offloading inference). Default: \"/dvai-transformers.worker.js\" */\n\ttransformersWorkerUrl?: string;\n\t/**\n\t * Custom pipeline factory for Transformers.js backend.\n\t * MAIN-THREAD ONLY — function closures don't cross the Worker boundary.\n\t * When provided, replaces the default pipeline() call with your own\n\t * model loading and inference logic. Must return a callable that accepts\n\t * (messages, options) and returns [{ generated_text: string }].\n\t *\n\t * For multimodal models that should run in the worker (recommended),\n\t * use the declarative `transformersModelClass` / `transformersProcessorClass`\n\t * / `transformersDisableEncoders` config instead.\n\t */\n\tcreatePipeline?: import(\"./TransformersBackend.js\").CreatePipelineFn;\n\t/**\n\t * Name of a transformers.js export to use as the model class (loaded via\n\t * `ClassName.from_pretrained(modelId)`). Enables the declarative\n\t * multimodal loader — works in the worker AND on main thread so the\n\t * same config ships correctly regardless of path.\n\t *\n\t * Examples: \"Gemma4ForConditionalGeneration\", \"LlavaForConditionalGeneration\",\n\t * \"AutoModelForCausalLM\". Leave unset to use the generic `pipeline()` factory.\n\t */\n\ttransformersModelClass?: string;\n\t/**\n\t * Processor class name for the declarative loader. Default: \"AutoProcessor\".\n\t * Only used when `transformersModelClass` is set.\n\t */\n\ttransformersProcessorClass?: string;\n\t/**\n\t * Model submodule fields to null out after load, e.g. `[\"vision_encoder\"]`\n\t * for a voice-only host app using a multimodal checkpoint. Purely\n\t * declarative — dvai-bridge walks the list and nulls each named field\n\t * if present; unknown/absent names are silently ignored. Host apps\n\t * control this based on their own criteria.\n\t */\n\ttransformersDisableEncoders?: string[];\n\n\t// --- Capacitor native runtime options (forwarded by CapacitorTransport) ---\n\t/** Path to the GGUF model file for the Capacitor llama backend. */\n\tnativeModelPath?: string;\n\t/** Number of GPU layers for the Capacitor llama backend (iOS Metal). Default: 99 (max) */\n\tnativeGpuLayers?: number;\n\t/** Number of CPU threads for the Capacitor llama backend. Default: 4 */\n\tnativeThreads?: number;\n\t/** Context window size for the Capacitor llama backend. Default: 2048 */\n\tnativeContextSize?: number;\n\t/**\n\t * Initialize the Capacitor llama context in embedding mode. Required for\n\t * `/v1/embeddings` to work natively. When true, the context should be a\n\t * dedicated embedding model and will typically not be usable for\n\t * chat/completion. Default: false.\n\t */\n\tnativeEmbeddingMode?: boolean;\n\n\t/**\n\t * Path (or fetchable URL) to your DVAI-Bridge license JWT file.\n\t *\n\t * Default behaviour when this is unset: the SDK looks for\n\t * `dvai-license.jwt` at platform-conventional locations:\n\t * - Node: `process.cwd()/dvai-license.jwt` (and one level up for\n\t * monorepos)\n\t * - Browser / Capacitor: same-origin `/dvai-license.jwt`\n\t *\n\t * Override mechanisms in priority order:\n\t * 1. `licenseToken` (below) — inline JWT string, highest priority\n\t * 2. `licenseKeyPath` (this field) — explicit path or URL\n\t * 3. `DVAI_LICENSE_PATH` env var\n\t * 4. `DVAI_LICENSE_TOKEN` env var — inline JWT\n\t * 5. Auto-discovery\n\t *\n\t * If no license is found OR validation fails, the SDK falls back to\n\t * the free tier (with the \"Powered by DVAI Bridge\" attribution\n\t * badge in browser/Capacitor contexts). The SDK never refuses to\n\t * start because of a license problem — license issues surface as a\n\t * `licenseStatus` value with `kind: \"free-prod\"` and a\n\t * human-readable `reason`.\n\t */\n\tlicenseKeyPath?: string;\n\n\t/**\n\t * Inline DVAI-Bridge license JWT (the full token string). Use this\n\t * when you'd rather inject the license via env var / config than\n\t * ship a file — typical in serverless / containerised deployments\n\t * where filesystem state is awkward.\n\t *\n\t * If both `licenseToken` and `licenseKeyPath` are set, `licenseToken`\n\t * wins.\n\t */\n\tlicenseToken?: string;\n\t/** Auto-initialize on creation (React/Vanilla). Default: true */\n\tautoInit?: boolean;\n\n\t/**\n\t * Phase 3 (v3.0+) — distributed inference / device offload.\n\t *\n\t * If unset OR `enabled: false`, the library behaves exactly like\n\t * v2.x: every request runs locally. When enabled, the library\n\t * discovers peer devices on the LAN (via mDNS) and / or via a\n\t * self-hosted rendezvous server (if `rendezvousUrl` is set), and\n\t * routes inference requests to the most-capable peer when local\n\t * tok/s falls below `minLocalCapability`.\n\t *\n\t * See `docs/guide/distributed-inference.md` for the full design,\n\t * `docs/guide/self-hosting-rendezvous.md` for the rendezvous\n\t * server self-hosting flow, and `src/offload/types.ts` for the\n\t * full `OffloadConfig` shape.\n\t */\n\toffload?: import(\"./offload/index.js\").OffloadConfig;\n\n\t/**\n\t * Which transport to use for the OpenAI-compatible surface.\n\t * - \"auto\" (default) → capacitor on Capacitor, msw in browser,\n\t * http in Node, none in workers\n\t * - \"msw\" → force MSW (browser only; errors elsewhere)\n\t * - \"http\" → force HTTP server (Node only; errors elsewhere)\n\t * - \"capacitor\" → force native Capacitor HTTP server (requires\n\t * @dvai-bridge/capacitor + a Capacitor backend plugin)\n\t * - \"none\" → no transport; use dvai.chatCompletion() directly\n\t */\n\ttransport?: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\";\n\n\t/** HTTP-only. Base port. Default: 38883. */\n\thttpBasePort?: number;\n\n\t/** HTTP-only. Max port-fallback attempts. Default: 16. */\n\thttpMaxPortAttempts?: number;\n\n\t/**\n\t * HTTP-only. Network interface to bind. Default `127.0.0.1`\n\t * (loopback only). Set to `0.0.0.0` for LAN-target deployments\n\t * (the v3.1 Hub, native SDKs running in target mode) so peers on\n\t * the same Wi-Fi can reach the embedded server. Phone-as-source /\n\t * single-device deployments should leave this default — a\n\t * 0.0.0.0 bind on a developer laptop without pairing protection\n\t * exposes the OpenAI surface.\n\t */\n\thttpBindHost?: string;\n\n\t/**\n\t * Phase 4 — first-chance interceptor for /v1/chat/completions. The\n\t * v3.1 Hub uses this to apply substitution-policy + engine-bridge\n\t * routing before falling through to the default local-backend\n\t * handler. Return a Response → that's what the client gets;\n\t * return null → fall through to the local backend.\n\t *\n\t * Receives request headers (lower-cased keys) so the interceptor\n\t * can read the v3.1 identity fields (X-DVAI-Peer-Device-Id,\n\t * X-DVAI-App-Id, X-DVAI-Nonce, X-DVAI-Signature) for HMAC verify.\n\t */\n\tchatCompletionInterceptor?: (\n\t\tbody: any,\n\t\tctx: import(\"./handlers/context.js\").HandlerContext,\n\t\theaders?: Record<string, string>,\n\t) => Promise<Response | null>;\n\n\t/**\n\t * HTTP-only. Controls the Access-Control-Allow-Origin response header.\n\t * - \"*\" → echo \"*\" (default; dev-friendly)\n\t * - \"https://x.com\" → echo that exact origin\n\t * - [\"a.com\",\"b.com\"] → match the request's Origin header against the\n\t * list; echo the matched value. Requests from\n\t * unlisted origins get ACAO omitted.\n\t */\n\tcorsOrigin?: string | string[];\n\n\t/**\n\t * Capacitor-backend selection (when transport resolves to \"capacitor\").\n\t * Default: \"llama\".\n\t */\n\tcapacitorBackend?: \"llama\" | \"foundation\" | \"mediapipe\";\n\n\t/**\n\t * Path to the mmproj (vision projector) file when using a multimodal\n\t * llama.cpp model. Optional; only required for vision-capable models.\n\t */\n\tnativeMmprojPath?: string;\n}\n\n/**\n * DVAI: Local AI Orchestration\n * Orchestrates WebLLM or Transformers.js for local inference and selects\n * an MSW, HTTP, or Capacitor transport (auto-detected from environment)\n * to expose the OpenAI-compatible endpoint. On Capacitor, the native\n * runtime runs in a first-party plugin behind the \"capacitor\" transport.\n * Read `dvai.baseUrl` after initialize() to get the URL to point any\n * OpenAI SDK at.\n */\nexport class DVAI {\n\tpublic modelId: string;\n\tpublic mockUrl: string;\n\tpublic serviceWorkerUrl: string;\n\tpublic licenseKeyPath?: string;\n\tpublic licenseToken?: string;\n\t/**\n\t * Result of the most recent license validation. Populated by\n\t * `initialize()`; consult before promoting paid-tier UI affordances\n\t * (e.g. hiding the attribution badge). Null before initialization.\n\t */\n\tpublic licenseStatus: LicenseStatus | null = null;\n\tpublic backend: BackendType;\n\tpublic transformersModelId: string;\n\tpublic pipelineTask: string;\n\tpublic device: DeviceType;\n\tpublic generationTimeout: number;\n\tpublic maxBlankChunks: number;\n\tpublic maxRetries: number;\n\tpublic webllmWorkerUrl: string;\n\tpublic transformersWorkerUrl: string;\n\tpublic dtype?: string;\n\tpublic createPipeline?: import(\"./TransformersBackend.js\").CreatePipelineFn;\n\tpublic transformersModelClass?: string;\n\tpublic transformersProcessorClass?: string;\n\tpublic transformersDisableEncoders?: string[];\n\n\t// Native backend options\n\tpublic nativeModelPath: string;\n\tpublic nativeGpuLayers: number;\n\tpublic nativeThreads: number;\n\tpublic nativeContextSize: number;\n\tpublic nativeEmbeddingMode: boolean;\n\tpublic capacitorBackend: \"llama\" | \"foundation\" | \"mediapipe\";\n\tpublic nativeMmprojPath?: string;\n\n\t/** Raw transport config (e.g., \"auto\"). */\n\tpublic transport: \"auto\" | \"msw\" | \"http\" | \"none\" | \"capacitor\";\n\tpublic httpBasePort: number;\n\tpublic httpMaxPortAttempts: number;\n\tpublic corsOrigin: string | string[];\n\tpublic httpBindHost: string | undefined;\n\tpublic chatCompletionInterceptor:\n\t\t| ((\n\t\t\t\tbody: any,\n\t\t\t\tctx: import(\"./handlers/context.js\").HandlerContext,\n\t\t\t\theaders?: Record<string, string>,\n\t\t\t) => Promise<Response | null>)\n\t\t| undefined;\n\n\t/** Resolved transport kind after selectTransport() runs. */\n\tprivate resolvedTransport: \"msw\" | \"http\" | \"none\" | \"capacitor\" = \"none\";\n\n\t/** Populated after transport.start(). Undefined on \"none\". */\n\tpublic baseUrl?: string;\n\tpublic port?: number;\n\n\t/** Active transport instance; null before initialize() / after unload(). */\n\tprivate activeTransport: import(\"./transports/index.js\").Transport | null =\n\t\tnull;\n\n\tprivate validator: LicenseValidator;\n\tprivate backendInstance: any = null; // WebLLMBackend | TransformersBackend\n\tpublic isReady: boolean = false;\n\t/** Tracks how many consecutive recovery attempts have been made. */\n\tprivate recoveryAttempts: number = 0;\n\n\t/** The resolved backend type (after \"auto\" resolution). */\n\tprivate resolvedBackend: \"webllm\" | \"transformers\" | \"native\" = \"webllm\";\n\n\t/* ----- Phase 3 (v3.0+) — distributed-inference state ----- */\n\n\t/** OffloadConfig as supplied by the consumer (or undefined). */\n\tpublic offload?: import(\"./offload/index.js\").OffloadConfig;\n\t/**\n\t * v3.2 — set true when the pre-init capability gate decides this\n\t * device is below `OffloadConfig.minLocalCapability`. In this mode\n\t * `initialize()` skips backend init entirely (no model download /\n\t * load) and only brings up discovery + pairing. Every request is\n\t * expected to be forwarded to a paired peer; without one, requests\n\t * 503.\n\t */\n\tpublic offloadOnlyMode: boolean = false;\n\t/** Capability cache (persistent storage of probe scores). */\n\tprivate capabilityCache?: import(\"./capability/index.js\").CapabilityCache;\n\t/** Phase 3 — built when offload.enabled; mounted on the HTTP transport via the handler context. */\n\tprivate dvaiRoutes?: Record<string, import(\"./handlers/dvai/index.js\").DvaiHandler>;\n\t/** Used by the dvai/health endpoint to report uptime. */\n\tprivate startedAt: number = Date.now();\n\t/** Discovery layer — composite of LAN mDNS + static + custom. */\n\tprivate discovery?: import(\"./discovery/index.js\").IDiscovery;\n\t/** Pairing policy (LAN-handshake auth + persistent store). */\n\tprivate pairingPolicy?: import(\"./pairing/index.js\").PairingPolicy;\n\t/** Stable per-install device ID (cached after first call). */\n\tprivate deviceId?: string;\n\n\tconstructor(config: DVAIConfig = {}) {\n\t\tthis.modelId = config.modelId || \"gemma-2-2b-it-q4f16_1-MLC\";\n\t\tthis.backend = config.backend || \"webllm\";\n\t\tthis.transformersModelId =\n\t\t\tconfig.transformersModelId ||\n\t\t\tconfig.modelId ||\n\t\t\t\"onnx-community/gemma-3n-E2B-it-ONNX\";\n\t\tthis.pipelineTask = config.pipelineTask || \"text-generation\";\n\t\tthis.device = config.device || \"auto\";\n\t\tthis.dtype = config.dtype;\n\t\tthis.createPipeline = config.createPipeline;\n\t\tthis.transformersModelClass = config.transformersModelClass;\n\t\tthis.transformersProcessorClass = config.transformersProcessorClass;\n\t\tthis.transformersDisableEncoders = config.transformersDisableEncoders;\n\t\tthis.generationTimeout = config.generationTimeout ?? 60000;\n\t\tthis.maxBlankChunks = config.maxBlankChunks ?? 20;\n\t\tthis.maxRetries = config.maxRetries ?? 2;\n\t\tthis.webllmWorkerUrl = config.webllmWorkerUrl ?? \"/dvai-webllm.worker.js\";\n\t\tthis.transformersWorkerUrl =\n\t\t\tconfig.transformersWorkerUrl ?? \"/dvai-transformers.worker.js\";\n\t\tthis.mockUrl =\n\t\t\tconfig.mockUrl ?? \"https://api.openai.local/v1/chat/completions\";\n\t\tthis.serviceWorkerUrl = config.serviceWorkerUrl ?? \"/mockServiceWorker.js\";\n\t\tthis.licenseKeyPath = config.licenseKeyPath;\n\t\tthis.licenseToken = config.licenseToken;\n\t\tthis.validator = new LicenseValidator({\n\t\t\t...(this.licenseKeyPath !== undefined ? { path: this.licenseKeyPath } : {}),\n\t\t\t...(this.licenseToken !== undefined ? { token: this.licenseToken } : {}),\n\t\t});\n\n\t\t// Native backend options\n\t\tthis.nativeModelPath = config.nativeModelPath || \"\";\n\t\tthis.nativeGpuLayers = config.nativeGpuLayers ?? 99;\n\t\tthis.nativeThreads = config.nativeThreads ?? 4;\n\t\tthis.nativeContextSize = config.nativeContextSize ?? 2048;\n\t\tthis.nativeEmbeddingMode = config.nativeEmbeddingMode ?? false;\n\t\tthis.capacitorBackend = config.capacitorBackend ?? \"llama\";\n\t\tthis.nativeMmprojPath = config.nativeMmprojPath;\n\n\t\t// Transport options\n\t\tthis.transport = config.transport ?? \"auto\";\n\t\tthis.httpBasePort = config.httpBasePort ?? 38883;\n\t\tthis.httpMaxPortAttempts = config.httpMaxPortAttempts ?? 16;\n\t\tthis.corsOrigin = config.corsOrigin ?? \"*\";\n\t\tthis.httpBindHost = config.httpBindHost;\n\t\tthis.chatCompletionInterceptor = config.chatCompletionInterceptor;\n\n\t\t// Resolve explicit backends immediately so getActiveBackend() is correct\n\t\t// before initialize(). \"auto\" defers to initialize() for runtime env detection.\n\t\tif (this.backend !== \"auto\") {\n\t\t\tthis.resolvedBackend = this.backend as \"webllm\" | \"transformers\" | \"native\";\n\t\t}\n\n\t\t// Phase 3 — capture offload config (lifecycle wiring lights up\n\t\t// in initialize() so we don't pay the cost on cold-construct).\n\t\tthis.offload = config.offload;\n\t}\n\n\t/**\n\t * Returns the active backend type (resolved from \"auto\" if applicable).\n\t */\n\tgetActiveBackend(): \"webllm\" | \"transformers\" | \"native\" {\n\t\treturn this.resolvedBackend;\n\t}\n\n\t/** Returns the resolved transport kind (after \"auto\" resolution). */\n\tgetActiveTransport(): \"msw\" | \"http\" | \"none\" | \"capacitor\" {\n\t\treturn this.resolvedTransport;\n\t}\n\n\t/** Returns the base URL a host app hands to an OpenAI SDK. */\n\tgetBaseUrl(): string | undefined {\n\t\treturn this.baseUrl;\n\t}\n\n\t/** Returns the HTTP port bound (http transport only). */\n\tgetPort(): number | undefined {\n\t\treturn this.port;\n\t}\n\n\t/**\n\t * Resolves the \"auto\" backend to a concrete type based on environment.\n\t *\n\t * On Capacitor, the native runtime is selected via `transport: \"capacitor\"`\n\t * (which delegates to a native HTTP server in the Capacitor plugin), not\n\t * via the backend. The backend stays in the webview as a thin client.\n\t */\n\tprivate resolveBackend(): \"webllm\" | \"transformers\" | \"native\" {\n\t\tif (this.backend === \"auto\") {\n\t\t\tconsole.log(\n\t\t\t\t\"[DVAI] Auto-detected web environment → using webllm backend\",\n\t\t\t);\n\t\t\treturn \"webllm\";\n\t\t}\n\t\treturn this.backend as \"webllm\" | \"transformers\" | \"native\";\n\t}\n\n\t/**\n\t * Initializes the selected backend engine and starts the resolved\n\t * transport (MSW in browsers, HTTP server in Node, or none).\n\t * @param onProgress - Callback for model download progress\n\t */\n\tasync initialize(\n\t\tonProgress: (info: any) => void = console.log,\n\t): Promise<boolean> {\n\t\tif (this.isReady) return true;\n\n\t\t// Resolve \"auto\" backend\n\t\tthis.resolvedBackend = this.resolveBackend();\n\n\t\t// 0. Validate license (offline JWT verification). THROWS\n\t\t// `LicenseRequiredError` in production / release contexts\n\t\t// when no valid commercial/trial license is found — that's\n\t\t// the BSL 1.1 enforcement point. Dev-mode environments\n\t\t// (localhost, NODE_ENV=test, DVAI_FORCE_DEV=1, etc.) bypass\n\t\t// the assert and return a `free-dev` status so developers\n\t\t// can iterate without a key. Surface the resolved status\n\t\t// through `this.licenseStatus` for host-app dashboards.\n\t\tthis.licenseStatus = await this.validator.validateAndAssert();\n\n\t\t// Detect Web Worker context — MSW (service workers) are unavailable inside Web Workers.\n\t\tconst isWorkerContext =\n\t\t\ttypeof window === \"undefined\" &&\n\t\t\ttypeof self !== \"undefined\" &&\n\t\t\ttypeof (self as any).importScripts === \"function\";\n\n\t\t// 0.1 Verify Service Worker Reachability (Quality of Life) — skip when\n\t\t// inside a Worker context, and skip when serviceWorkerUrl is empty (MSW disabled).\n\t\tif (!isWorkerContext && this.serviceWorkerUrl) {\n\t\t\ttry {\n\t\t\t\tconst swRes = await fetch(this.serviceWorkerUrl, { method: \"HEAD\" });\n\t\t\t\tif (!swRes.ok) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[DVAI] Warning: Service Worker not found at \"${this.serviceWorkerUrl}\". ` +\n\t\t\t\t\t\t\t`Please run \"dvai-bridge init\" or \"npx msw init <public_dir>\" to generate it.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[DVAI] Could not verify Service Worker existence at \"${this.serviceWorkerUrl}\".`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// 0.5 v3.2 — pre-init capability gate. Runs the heuristic\n\t\t\t// (CPU/GPU/RAM hints, no model required) and decides\n\t\t\t// internally whether to load a model locally:\n\t\t\t//\n\t\t\t// - too-weak → enter offload-only mode silently\n\t\t\t// (no model download/load). The SDK\n\t\t\t// does NOT throw and does NOT show any\n\t\t\t// UI — consumers query\n\t\t\t// `dvai.assessHardware()` ahead of\n\t\t\t// initialize() if they want to refuse\n\t\t\t// to start on too-weak devices.\n\t\t\t// - offload-only → same internal treatment: skip backend\n\t\t\t// init; bring up only transport + offload.\n\t\t\t// - ok → proceed to full initialization.\n\t\t\t//\n\t\t\t// Both too-weak and offload-only collapse to \"skip backend\n\t\t\t// init\" from the SDK's perspective; the *informational*\n\t\t\t// distinction lives in assessHardware()'s return value for\n\t\t\t// consumers to react to.\n\t\t\t//\n\t\t\t// Skipped entirely when offload is not configured or when\n\t\t\t// `offload.enabled` is false — there's no point gating a\n\t\t\t// device that has no offload path.\n\t\t\tif (this.offload?.enabled) {\n\t\t\t\tconst { assessCapability } =\n\t\t\t\t\tawait import(\"./capability/precheck.js\");\n\t\t\t\tconst result = await assessCapability({\n\t\t\t\t\thardwareMinimum: this.offload.hardwareMinimum,\n\t\t\t\t\tminLocalCapability: this.offload.minLocalCapability,\n\t\t\t\t});\n\t\t\t\tthis.offloadOnlyMode =\n\t\t\t\t\tresult.mode === \"offload-only\" || result.mode === \"too-weak\";\n\t\t\t\tonProgress({\n\t\t\t\t\tphase: \"precheck\",\n\t\t\t\t\tmode: result.mode,\n\t\t\t\t\ttokPerSec: result.tokPerSec,\n\t\t\t\t\treason: result.reason,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// 1. Initialize the selected backend (lazy import). Skipped\n\t\t\t// when the precheck put us in offload-only mode (or\n\t\t\t// too-weak) — the consumer won't be running inference\n\t\t\t// locally, so we don't pay the model download/load cost.\n\t\t\tif (!this.offloadOnlyMode) {\n\t\t\t\tawait this.initializeBackend(onProgress);\n\t\t\t} else {\n\t\t\t\tonProgress({\n\t\t\t\t\tphase: \"backend\",\n\t\t\t\t\tskipped: true,\n\t\t\t\t\treason: \"offload-only mode (device below minLocalCapability or below hardwareMinimum)\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// 2. Select transport based on env + config\n\t\t\tconst { selectTransport, MswTransport, HttpTransport, CapacitorTransport } =\n\t\t\t\tawait import(\"./transports/index.js\");\n\t\t\tthis.resolvedTransport = selectTransport({\n\t\t\t\ttransport: this.transport === \"auto\" ? undefined : this.transport,\n\t\t\t\tserviceWorkerUrl: this.serviceWorkerUrl,\n\t\t\t});\n\n\t\t\t// Warn if mockUrl was explicitly customized under HTTP (will be ignored).\n\t\t\t// The default value is used as the sentinel for \"user did not customize\".\n\t\t\tif (\n\t\t\t\tthis.resolvedTransport === \"http\" &&\n\t\t\t\tthis.mockUrl !== \"https://api.openai.local/v1/chat/completions\"\n\t\t\t) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[DVAI] mockUrl config is ignored under transport=\\\"http\\\". \" +\n\t\t\t\t\t\t\"The HTTP server always serves at /v1/*. Use dvai.baseUrl \" +\n\t\t\t\t\t\t\"to get the exact endpoint.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Warn if serviceWorkerUrl is empty but transport=\"msw\" was forced\n\t\t\tif (\n\t\t\t\tthis.resolvedTransport === \"msw\" &&\n\t\t\t\tthis.serviceWorkerUrl === \"\" &&\n\t\t\t\tthis.transport === \"msw\"\n\t\t\t) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[DVAI] serviceWorkerUrl is empty but transport='msw' was requested; MSW will fail to register.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Worker-context informational message\n\t\t\tif (\n\t\t\t\tthis.resolvedTransport === \"none\" &&\n\t\t\t\ttypeof window === \"undefined\" &&\n\t\t\t\ttypeof self !== \"undefined\"\n\t\t\t) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[DVAI] Running in a Web Worker — no transport started. \" +\n\t\t\t\t\t\t\"Use dvai.chatCompletion() directly, or register MSW on the main thread.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Construct + start the transport\n\t\t\tif (this.resolvedTransport === \"msw\") {\n\t\t\t\tthis.activeTransport = new MswTransport({\n\t\t\t\t\tmockUrl: this.mockUrl,\n\t\t\t\t\tserviceWorkerUrl: this.serviceWorkerUrl,\n\t\t\t\t});\n\t\t\t} else if (this.resolvedTransport === \"http\") {\n\t\t\t\tthis.activeTransport = new HttpTransport({\n\t\t\t\t\thttpBasePort: this.httpBasePort,\n\t\t\t\t\thttpMaxPortAttempts: this.httpMaxPortAttempts,\n\t\t\t\t\tcorsOrigin: this.corsOrigin,\n\t\t\t\t\t...(this.httpBindHost !== undefined ? { bindHost: this.httpBindHost } : {}),\n\t\t\t\t});\n\t\t\t} else if (this.resolvedTransport === \"capacitor\") {\n\t\t\t\tthis.activeTransport = new CapacitorTransport({\n\t\t\t\t\tcapacitorBackend: this.capacitorBackend,\n\t\t\t\t\tnativeModelPath: this.nativeModelPath || undefined,\n\t\t\t\t\tnativeMmprojPath: this.nativeMmprojPath,\n\t\t\t\t\tnativeGpuLayers: this.nativeGpuLayers,\n\t\t\t\t\tnativeContextSize: this.nativeContextSize,\n\t\t\t\t\tnativeThreads: this.nativeThreads,\n\t\t\t\t\tnativeEmbeddingMode: this.nativeEmbeddingMode,\n\t\t\t\t\thttpBasePort: this.httpBasePort,\n\t\t\t\t\thttpMaxPortAttempts: this.httpMaxPortAttempts,\n\t\t\t\t\tcorsOrigin: this.corsOrigin,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.activeTransport = null;\n\t\t\t}\n\n\t\t\tif (this.activeTransport) {\n\t\t\t\tconst ctx = this.getHandlerContext(onProgress);\n\t\t\t\tconst started = await this.activeTransport.start(ctx);\n\t\t\t\tthis.baseUrl = started.baseUrl;\n\t\t\t\tthis.port = started.port;\n\t\t\t} else {\n\t\t\t\tthis.baseUrl = undefined;\n\t\t\t\tthis.port = undefined;\n\t\t\t}\n\n\t\t\tthis.isReady = true;\n\t\t\tthis.recoveryAttempts = 0;\n\n\t\t\t// Phase 3 — bring up offload-related state if the consumer\n\t\t\t// opted in. Errors here are logged but do not fail\n\t\t\t// initialize() — local inference still works without offload.\n\t\t\tif (this.offload?.enabled) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.initializeOffload();\n\t\t\t\t\t// v3.2 — auto-attach the offload forwarder so requests\n\t\t\t\t\t// route to peers without the consumer wiring an\n\t\t\t\t\t// interceptor manually. Skipped when:\n\t\t\t\t\t// 1. The consumer already supplied their own\n\t\t\t\t\t// chatCompletionInterceptor (e.g. the Hub uses\n\t\t\t\t\t// one for substitution-policy routing — they're\n\t\t\t\t\t// responsible for composing offload themselves).\n\t\t\t\t\t// 2. We're not in offload-only mode AND a local\n\t\t\t\t\t// backend is available — for v3.2.0 we keep the\n\t\t\t\t\t// auto-attach scoped to the offload-only path\n\t\t\t\t\t// to avoid changing local-first behavior. The\n\t\t\t\t\t// richer \"decide per request even when local\n\t\t\t\t\t// works\" wiring follows in a future patch.\n\t\t\t\t\tif (\n\t\t\t\t\t\tthis.chatCompletionInterceptor === undefined &&\n\t\t\t\t\t\tthis.offloadOnlyMode\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst { buildOffloadInterceptor } = await import(\n\t\t\t\t\t\t\t\"./offload/index.js\"\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst offloadConfig = this.offload;\n\t\t\t\t\t\tthis.chatCompletionInterceptor = buildOffloadInterceptor({\n\t\t\t\t\t\t\tconfig: offloadConfig,\n\t\t\t\t\t\t\tgetPeers: () => this.discovery?.peers() ?? [],\n\t\t\t\t\t\t\tgetLocalCapability: () => 0, // offload-only ⇒ no local backend\n\t\t\t\t\t\t\toffloadOnlyMode: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"[DVAI/offload] failed to initialize offload state; \" +\n\t\t\t\t\t\t\t\"local inference still works. Cause:\",\n\t\t\t\t\t\terr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[DVAI] Failed to initialize:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Phase 3 — bring up the capability cache, discovery layer, and\n\t * pairing policy on top of an already-running DVAI instance.\n\t * Called from initialize() when `offload.enabled` is true.\n\t */\n\tprivate async initializeOffload(): Promise<void> {\n\t\tconst { createCapabilityCache, ensureDeviceId } = await import(\n\t\t\t\"./capability/index.js\"\n\t\t);\n\t\tconst { CompositeDiscovery, StaticDiscovery, createMdnsDiscovery } = await import(\n\t\t\t\"./discovery/index.js\"\n\t\t);\n\t\tconst { PairingPolicy, createPairingStore } = await import(\n\t\t\t\"./pairing/index.js\"\n\t\t);\n\n\t\tthis.capabilityCache = createCapabilityCache();\n\t\tthis.deviceId = await ensureDeviceId(this.capabilityCache);\n\n\t\tconst sources: Array<import(\"./discovery/index.js\").IDiscovery> = [];\n\t\tif (this.offload?.discoverLAN !== false) {\n\t\t\t// v3.2.1 — when `offload.advertiseLAN` is true, register an\n\t\t\t// `_dvai-bridge._tcp` mDNS advertisement so remote peers\n\t\t\t// (mobile SDKs, other Hubs) can discover this instance via\n\t\t\t// NWBrowser without manual URL entry. The native SDKs\n\t\t\t// already do this themselves; the JS-side core (Hub, Node\n\t\t\t// CLI) opts in here. Port defaults to the bound DVAI server\n\t\t\t// port; `models` is empty until the Hub feeds enumeration\n\t\t\t// in (loadedModels via setLoadedModels at runtime).\n\t\t\t//\n\t\t\t// On macOS, the Hub's wrapper (`hub/peer-mode/server.ts`)\n\t\t\t// uses a `dns-sd -R` subprocess that goes through the\n\t\t\t// system mDNSResponder daemon — that's the canonical path\n\t\t\t// and Bonjour clients see a properly-named service\n\t\t\t// (`<DVAI Hub>` vs the generic `<hostname>.local`\n\t\t\t// `multicast-dns` produces). To avoid emitting duplicate\n\t\t\t// `_dvai-bridge._tcp` records, the JS-core's advertise\n\t\t\t// silently no-ops on Darwin — leave the macOS path to the\n\t\t\t// dns-sd subprocess. On Linux / Windows the npm lib's\n\t\t\t// advertise actually works (no system-daemon conflict),\n\t\t\t// so we keep it there.\n\t\t\tconst skipAdvertiseOnDarwin =\n\t\t\t\ttypeof globalThis.process !== \"undefined\" &&\n\t\t\t\tglobalThis.process.platform === \"darwin\";\n\t\t\tconst advertise: import(\"./discovery/mdns-node.js\").AdvertisedTxt | undefined =\n\t\t\t\tthis.offload?.advertiseLAN && !skipAdvertiseOnDarwin\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tdeviceId: this.deviceId,\n\t\t\t\t\t\t\tdeviceName:\n\t\t\t\t\t\t\t\t(typeof globalThis.process !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\t(globalThis.process.env.DVAI_DEVICE_NAME ?? \"\")) ||\n\t\t\t\t\t\t\t\t\"DVAI\",\n\t\t\t\t\t\t\tdvaiVersion: \"3.2.1\",\n\t\t\t\t\t\t\t// Default to the bound DVAI server port (set in\n\t\t\t\t\t\t\t// `start()` after the transport binds). If the\n\t\t\t\t\t\t\t// transport is `none` (rare for advertising), fall\n\t\t\t\t\t\t\t// back to the legacy default 38883.\n\t\t\t\t\t\t\tport:\n\t\t\t\t\t\t\t\tthis.offload.advertisePort ??\n\t\t\t\t\t\t\t\tthis.port ??\n\t\t\t\t\t\t\t\t38883,\n\t\t\t\t\t\t\tsecure: false,\n\t\t\t\t\t\t\t// Field name matches the AdvertisedTxt schema\n\t\t\t\t\t\t\t// (NOT `loadedModels` — that's the iOS-side\n\t\t\t\t\t\t\t// struct name, not the wire encoding).\n\t\t\t\t\t\t\tmodels: [],\n\t\t\t\t\t\t\tcapability: {},\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined;\n\t\t\tsources.push(\n\t\t\t\tawait createMdnsDiscovery({\n\t\t\t\t\tselfDeviceId: this.deviceId,\n\t\t\t\t\t...(advertise ? { advertise } : {}),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (this.offload?.knownPeers && this.offload.knownPeers.length > 0) {\n\t\t\tsources.push(new StaticDiscovery(this.offload.knownPeers));\n\t\t}\n\t\tif (sources.length > 0) {\n\t\t\tthis.discovery = new CompositeDiscovery(sources);\n\t\t\tawait this.discovery.start();\n\t\t}\n\n\t\tthis.pairingPolicy = new PairingPolicy({\n\t\t\tstore: createPairingStore(),\n\t\t\tonPairingRequest: this.offload?.onPairingRequest\n\t\t\t\t? async (peerDeviceId, peerDeviceName, appId) => {\n\t\t\t\t\t\tconst cb = this.offload?.onPairingRequest;\n\t\t\t\t\t\tif (!cb) return false;\n\t\t\t\t\t\treturn cb({\n\t\t\t\t\t\t\tdeviceId: peerDeviceId,\n\t\t\t\t\t\t\tdeviceName: peerDeviceName,\n\t\t\t\t\t\t\tdvaiVersion: \"unknown\",\n\t\t\t\t\t\t\tbaseUrl: \"\",\n\t\t\t\t\t\t\tloadedModels: [],\n\t\t\t\t\t\t\tcapability: {},\n\t\t\t\t\t\t\tvia: \"static\",\n\t\t\t\t\t\t\tsecure: false,\n\t\t\t\t\t\t\tlastSeenAt: Date.now(),\n\t\t\t\t\t\t\t...(appId !== undefined ? { appId } : {}),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t});\n\n\t\t// Build the /v1/dvai/* route map. The HTTP transport reads this\n\t\t// late via the handler-context getter (initializeOffload runs\n\t\t// AFTER the transport has started in the current lifecycle).\n\t\tconst { buildDvaiRoutes } = await import(\"./handlers/dvai/index.js\");\n\t\tconst self = this;\n\t\tthis.dvaiRoutes = buildDvaiRoutes({\n\t\t\tlibraryVersion: \"3.0.0\",\n\t\t\tget currentModelId() {\n\t\t\t\treturn self.resolvedBackend === \"transformers\"\n\t\t\t\t\t? self.transformersModelId\n\t\t\t\t\t: self.modelId;\n\t\t\t},\n\t\t\tcapabilityCache: this.capabilityCache,\n\t\t\tget backend() {\n\t\t\t\tconst b = self.backendInstance;\n\t\t\t\tif (!b) return undefined;\n\t\t\t\t// ProbableBackend just needs `chatCompletion` — duck-typed.\n\t\t\t\treturn b as unknown as import(\"./capability/index.js\").ProbableBackend;\n\t\t\t},\n\t\t\tdiscovery: this.discovery,\n\t\t\tpairingPolicy: this.pairingPolicy,\n\t\t\tstartedAt: this.startedAt,\n\t\t} as Parameters<typeof buildDvaiRoutes>[0]);\n\t}\n\n\t/** Phase 3 — release offload state (LAN advertiser, discovery sockets, etc). */\n\tprivate async shutdownOffload(): Promise<void> {\n\t\tif (this.discovery) {\n\t\t\ttry {\n\t\t\t\tawait this.discovery.stop();\n\t\t\t} catch {\n\t\t\t\t/* swallow — best-effort cleanup */\n\t\t\t}\n\t\t\tthis.discovery = undefined;\n\t\t}\n\t\tthis.capabilityCache = undefined;\n\t\tthis.pairingPolicy = undefined;\n\t\tthis.dvaiRoutes = undefined;\n\t\tthis.deviceId = undefined;\n\t}\n\n\t/**\n\t * Builds a HandlerContext consumed by the transport-agnostic handlers.\n\t * `backend` is exposed via a getter so that when recovery replaces\n\t * `this.backendInstance` mid-request, the handler's subsequent reads of\n\t * `ctx.backend` see the new instance (critical for the reactive-recovery\n\t * retry path in handleChatCompletion).\n\t */\n\tprivate getHandlerContext(\n\t\tonProgress: (info: any) => void,\n\t): HandlerContext {\n\t\tconst self = this;\n\t\treturn {\n\t\t\tget backend() {\n\t\t\t\treturn self.backendInstance;\n\t\t\t},\n\t\t\tresolvedBackend: this.resolvedBackend,\n\t\t\tmodelId:\n\t\t\t\tthis.resolvedBackend === \"transformers\"\n\t\t\t\t\t? this.transformersModelId\n\t\t\t\t\t: this.modelId,\n\t\t\tonRecovery:\n\t\t\t\tthis.resolvedBackend === \"webllm\"\n\t\t\t\t\t? async () => {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tthis.backendInstance?.lastFatalError &&\n\t\t\t\t\t\t\t\tthis.recoveryAttempts < this.maxRetries\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tawait this.attemptRecovery(onProgress);\n\t\t\t\t\t\t\t} else if (this.recoveryAttempts >= this.maxRetries) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Recovery exhausted\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t// Phase 3 — late-bound getter so the HTTP transport sees\n\t\t\t// the routes once initializeOffload() finishes (which runs\n\t\t\t// AFTER the transport starts in the current lifecycle).\n\t\t\tget dvaiRoutes() {\n\t\t\t\treturn self.dvaiRoutes;\n\t\t\t},\n\t\t\t// Phase 4 — Hub injects this to enforce substitution policy\n\t\t\t// + engine-bridge routing.\n\t\t\t...(this.chatCompletionInterceptor !== undefined\n\t\t\t\t? { chatCompletionInterceptor: this.chatCompletionInterceptor }\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\t/**\n\t * Attempts to recover from a fatal WebLLM error by unloading and reloading the backend.\n\t */\n\tprivate async attemptRecovery(\n\t\tonProgress: (info: any) => void,\n\t): Promise<void> {\n\t\tthis.recoveryAttempts++;\n\t\tconst fatalError = this.backendInstance?.lastFatalError;\n\t\tconsole.log(\n\t\t\t`[DVAI] Auto-recovery: unloading engine due to \"${fatalError}\" (attempt ${this.recoveryAttempts}/${this.maxRetries})`,\n\t\t);\n\n\t\t// Unload the backend\n\t\tif (this.backendInstance) {\n\t\t\ttry {\n\t\t\t\tawait this.backendInstance.unload();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(\"[DVAI] Error during recovery unload:\", e);\n\t\t\t}\n\t\t\tthis.backendInstance = null;\n\t\t}\n\n\t\t// Reload\n\t\tawait this.initializeBackend(onProgress);\n\t\tif (this.backendInstance?.clearFatalError) {\n\t\t\tthis.backendInstance.clearFatalError();\n\t\t}\n\t\tconsole.log(\"[DVAI] Auto-recovery: engine reloaded successfully\");\n\t}\n\n\t/**\n\t * Lazy-imports and initializes the selected backend.\n\t */\n\tprivate async initializeBackend(\n\t\tonProgress: (info: any) => void,\n\t): Promise<void> {\n\t\tif (this.resolvedBackend === \"native\") {\n\t\t\tlet NodeLlamaCppBackend: any;\n\t\t\ttry {\n\t\t\t\tconst mod = await import(\"./NodeLlamaCppBackend.js\");\n\t\t\t\tNodeLlamaCppBackend = mod.NodeLlamaCppBackend;\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] native backend selected but the NodeLlamaCppBackend module failed to load.',\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!this.nativeModelPath) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] backend: \"native\" requires `nativeModelPath` (path to a GGUF file).',\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Use modelId only if the consumer customized it; otherwise let\n\t\t\t// the backend derive it from the GGUF basename. The default\n\t\t\t// WebLLM model id is meaningless for the native backend.\n\t\t\tconst customModelId =\n\t\t\t\tthis.modelId !== \"gemma-2-2b-it-q4f16_1-MLC\" ? this.modelId : undefined;\n\t\t\tconst backend = new NodeLlamaCppBackend({\n\t\t\t\tmodelPath: this.nativeModelPath,\n\t\t\t\tgpuLayers: this.nativeGpuLayers,\n\t\t\t\tthreads: this.nativeThreads,\n\t\t\t\tcontextSize: this.nativeContextSize,\n\t\t\t\tgenerationTimeout: this.generationTimeout,\n\t\t\t\tmodelId: customModelId,\n\t\t\t});\n\t\t\tawait backend.initialize(onProgress);\n\t\t\tthis.backendInstance = backend;\n\t\t\t// Echo the resolved model identifier (basename if not provided)\n\t\t\tthis.modelId = backend.getModelId();\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI] node-llama-cpp backend ready (modelId=\"${this.modelId}\", gpuLayers=${this.nativeGpuLayers}, contextSize=${this.nativeContextSize})`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (this.resolvedBackend === \"transformers\") {\n\t\t\tlet TransformersBackend: any;\n\t\t\ttry {\n\t\t\t\tconst mod = await import(\"./TransformersBackend.js\");\n\t\t\t\tTransformersBackend = mod.TransformersBackend;\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] Transformers.js backend selected but \"@huggingface/transformers\" is not installed.\\n' +\n\t\t\t\t\t\t\"Install it with: npm install @huggingface/transformers\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst backend = new TransformersBackend({\n\t\t\t\tmodelId: this.transformersModelId,\n\t\t\t\tdevice: this.device,\n\t\t\t\tgenerationTimeout: this.generationTimeout,\n\t\t\t\tworkerUrl: this.transformersWorkerUrl,\n\t\t\t\tpipelineTask: this.pipelineTask,\n\t\t\t\tdtype: this.dtype,\n\t\t\t\tcreatePipeline: this.createPipeline,\n\t\t\t\tmodelClass: this.transformersModelClass,\n\t\t\t\tprocessorClass: this.transformersProcessorClass,\n\t\t\t\tdisableEncoders: this.transformersDisableEncoders,\n\t\t\t});\n\t\t\tawait backend.initialize(onProgress);\n\t\t\tthis.backendInstance = backend;\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI] Transformers.js backend ready (task: ${this.pipelineTask}, device: ${backend.getResolvedDevice()}, worker: ${backend.isWorkerBased()})`,\n\t\t\t);\n\t\t} else {\n\t\t\tlet WebLLMBackend: any;\n\t\t\ttry {\n\t\t\t\tconst mod = await import(\"./WebLLMBackend.js\");\n\t\t\t\tWebLLMBackend = mod.WebLLMBackend;\n\t\t\t} catch {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[DVAI] WebLLM backend selected but \"@mlc-ai/web-llm\" is not installed.\\n' +\n\t\t\t\t\t\t\"Install it with: npm install @mlc-ai/web-llm\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst backend = new WebLLMBackend({\n\t\t\t\tmodelId: this.modelId,\n\t\t\t\tgenerationTimeout: this.generationTimeout,\n\t\t\t\tmaxBlankChunks: this.maxBlankChunks,\n\t\t\t\tworkerUrl: this.webllmWorkerUrl,\n\t\t\t});\n\t\t\tawait backend.initialize(onProgress);\n\t\t\tthis.backendInstance = backend;\n\t\t\tconsole.log(\n\t\t\t\t`[DVAI] WebLLM backend ready (worker: ${backend.isWorkerBased()})`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Gets the underlying engine instance directly.\n\t * - For WebLLM: returns the MLCEngine\n\t * - For Transformers.js: returns the pipeline\n\t */\n\tgetEngine(): any {\n\t\tif (!this.backendInstance) return null;\n\t\tif (this.resolvedBackend === \"webllm\") {\n\t\t\treturn this.backendInstance.getEngine?.() ?? null;\n\t\t}\n\t\tif (this.resolvedBackend === \"native\") {\n\t\t\t// node-llama-cpp doesn't expose a single \"engine\" — return the\n\t\t\t// chat session, which is the closest analogue.\n\t\t\treturn this.backendInstance ?? null;\n\t\t}\n\t\treturn this.backendInstance.getPipeline?.() ?? null;\n\t}\n\n\t/**\n\t * Perform a direct chat completion (bypasses MSW, calls backend directly).\n\t * Useful for programmatic usage without going through the fetch mock.\n\t */\n\tasync chatCompletion(requestBody: any): Promise<any> {\n\t\tif (!this.backendInstance)\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Backend not initialized. Call initialize() first.\",\n\t\t\t);\n\t\treturn this.backendInstance.chatCompletion(requestBody);\n\t}\n\n\t/**\n\t * Generate embeddings for one or more text inputs.\n\t *\n\t * Supported when backend is \"transformers\" with\n\t * pipelineTask: \"feature-extraction\".\n\t *\n\t * Throws when called on the WebLLM backend.\n\t *\n\t * @param inputs - A single string or array of strings to embed\n\t * @returns An array of embedding vectors (one per input)\n\t */\n\tasync embedding(inputs: string | string[]): Promise<number[][]> {\n\t\tif (!this.backendInstance)\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Backend not initialized. Call initialize() first.\",\n\t\t\t);\n\t\tif (this.resolvedBackend === \"webllm\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Embeddings are not supported on the WebLLM backend. \" +\n\t\t\t\t\t\"Use backend: 'transformers' with pipelineTask: 'feature-extraction'.\",\n\t\t\t);\n\t\t}\n\t\tif (this.resolvedBackend === \"native\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Embeddings are not yet supported on the node-llama-cpp backend. \" +\n\t\t\t\t\t\"Use backend: 'transformers' with pipelineTask: 'feature-extraction'.\",\n\t\t\t);\n\t\t}\n\t\tif (typeof this.backendInstance.embedding !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] The current backend does not expose an embedding() method.\",\n\t\t\t);\n\t\t}\n\t\treturn this.backendInstance.embedding(inputs);\n\t}\n\n\t/**\n\t * Run the pipeline directly (Transformers.js only).\n\t * Use this for non-text tasks like text-to-image, ASR, text-to-speech, etc.\n\t * @param inputs - Input data appropriate for the pipeline task\n\t * @param options - Pipeline-specific options\n\t */\n\tasync runPipeline(inputs: any, options?: Record<string, any>): Promise<any> {\n\t\tif (!this.backendInstance)\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] Backend not initialized. Call initialize() first.\",\n\t\t\t);\n\t\tif (\n\t\t\tthis.resolvedBackend !== \"transformers\" ||\n\t\t\t!this.backendInstance.runPipeline\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DVAI] runPipeline() is only available with the Transformers.js backend.\",\n\t\t\t);\n\t\t}\n\t\treturn this.backendInstance.runPipeline(inputs, options);\n\t}\n\n\t/**\n\t * Unloads the AI engine and stops the active transport to free up resources.\n\t */\n\tasync unload(): Promise<void> {\n\t\t// Phase 3 — tear down offload state first so we stop advertising\n\t\t// before the underlying server disappears.\n\t\tawait this.shutdownOffload();\n\n\t\tif (this.backendInstance) {\n\t\t\tawait this.backendInstance.unload();\n\t\t\tthis.backendInstance = null;\n\t\t}\n\t\tif (this.activeTransport) {\n\t\t\tawait this.activeTransport.stop();\n\t\t\tthis.activeTransport = null;\n\t\t}\n\t\tthis.baseUrl = undefined;\n\t\tthis.port = undefined;\n\t\tthis.isReady = false;\n\t\tthis.recoveryAttempts = 0;\n\t\tconsole.log(\"[DVAI] Unloaded model and transport.\");\n\t}\n\n\t/* ----- Phase 3 — public surface for offload diagnostics ----- */\n\n\t/**\n\t * v3.2 — pre-init hardware assessment.\n\t *\n\t * Returns a JSON-serializable description of how this device\n\t * would handle local inference, BEFORE any model download/load.\n\t *\n\t * Consumers should call this before `initialize()` if they want\n\t * to refuse to start on too-weak devices. The SDK itself never\n\t * shows UI — it's the consumer app's job to decide what (if\n\t * anything) to surface based on the result.\n\t *\n\t * Result `mode` values:\n\t * - `ok` → device can comfortably run the model\n\t * locally; initialize() will proceed normally.\n\t * - `offload-only` → device can run but slowly (below\n\t * `minLocalCapability`); initialize() will\n\t * skip the model load and route every\n\t * request to a paired peer.\n\t * - `too-weak` → device is below the hardware floor (3\n\t * tok/s by default); initialize() will\n\t * ALSO skip the model load — the consumer\n\t * should typically bail rather than even\n\t * calling initialize().\n\t *\n\t * Pass `hardwareMinimum` / `minLocalCapability` to override the\n\t * defaults (matches `OffloadConfig`).\n\t *\n\t * @returns a serializable assessment (safe to JSON.stringify and\n\t * ship over a Pigeon / Capacitor channel).\n\t */\n\tasync assessHardware(opts: {\n\t\thardwareMinimum?: number;\n\t\tminLocalCapability?: number;\n\t} = {}): Promise<{\n\t\tmode: \"ok\" | \"offload-only\" | \"too-weak\";\n\t\ttokPerSec: number;\n\t\treason: string;\n\t\thints: import(\"./capability/index.js\").DeviceCapabilityHints;\n\t}> {\n\t\tconst { assessCapability } = await import(\"./capability/precheck.js\");\n\t\tconst result = await assessCapability({\n\t\t\thardwareMinimum: opts.hardwareMinimum,\n\t\t\tminLocalCapability: opts.minLocalCapability,\n\t\t});\n\t\treturn {\n\t\t\tmode: result.mode,\n\t\t\ttokPerSec: result.tokPerSec,\n\t\t\treason: result.reason,\n\t\t\thints: result.hints,\n\t\t};\n\t}\n\n\t/**\n\t * Run a cold-run capability probe against the active backend +\n\t * model. Persists the result for future getCapability() calls.\n\t * Requires offload.enabled.\n\t */\n\tasync probeCapability(): Promise<\n\t\timport(\"./capability/index.js\").CapabilityScore | undefined\n\t> {\n\t\tif (!this.capabilityCache || !this.backendInstance) return undefined;\n\t\tconst { probeAndCache } = await import(\"./capability/index.js\");\n\t\tconst modelId =\n\t\t\tthis.resolvedBackend === \"transformers\"\n\t\t\t\t? this.transformersModelId\n\t\t\t\t: this.modelId;\n\t\treturn probeAndCache({\n\t\t\tcache: this.capabilityCache,\n\t\t\tbackend: this.backendInstance,\n\t\t\tmodelId,\n\t\t\tlibraryVersion: \"3.0.0\",\n\t\t});\n\t}\n\n\t/**\n\t * Get the cached capability score for a model on this device, or\n\t * compute a heuristic estimate if no probe has run yet.\n\t */\n\tasync getCapability(\n\t\tmodelId?: string,\n\t): Promise<import(\"./capability/index.js\").CapabilityScore | undefined> {\n\t\tif (!this.capabilityCache) return undefined;\n\t\tconst { getCapability } = await import(\"./capability/index.js\");\n\t\tconst id =\n\t\t\tmodelId ??\n\t\t\t(this.resolvedBackend === \"transformers\"\n\t\t\t\t? this.transformersModelId\n\t\t\t\t: this.modelId);\n\t\treturn getCapability({\n\t\t\tcache: this.capabilityCache,\n\t\t\tmodelId: id,\n\t\t\tlibraryVersion: \"3.0.0\",\n\t\t});\n\t}\n\n\t/** Snapshot of currently-known peers via the discovery layer. */\n\tgetPeers(): import(\"./discovery/index.js\").Peer[] {\n\t\treturn this.discovery?.peers() ?? [];\n\t}\n}\n\n// Export a singleton instance by default, or the class for advanced usage\nexport const dvai: DVAI = new DVAI();\n","/**\n * DVAI-Bridge license validator — offline JWT verification.\n *\n * This file replaces the v3.2.x `LicenseValidator` that did checksum-\n * style validation on a `dvai-...` plaintext key. The new model:\n *\n * 1. The license is a signed JWT (header + payload + ECDSA P-256\n * signature), issued by the operator's own license-generator\n * service from a private key they hold.\n * 2. The SDK ships only with public keys (see `publicKeys.ts`) and\n * cannot itself produce valid licenses — so reverse-engineering\n * the bundled SDK gains nothing.\n * 3. At runtime, the validator does signature + expiry + audience +\n * platform binding checks. Failure of any check collapses to\n * free-tier (with attribution badge), not a hard error — the SDK\n * stays usable for hobbyists / community use.\n *\n * Network calls: zero. The whole flow is offline by design — there's\n * no \"phone home\" step, no license server polling, no DRM beacon. The\n * private-key holder is the only party that can mint tokens, and any\n * deployment that has a valid file activates without contacting us.\n */\nimport {\n importJWK,\n jwtVerify,\n errors as joseErrors,\n type JWTPayload,\n} from \"jose\";\nimport {\n DVAI_PUBLIC_KEYS,\n PLACEHOLDER_KID,\n type DvaiPublicKeyJwk,\n} from \"./publicKeys.js\";\nimport {\n detectAudience,\n detectDevMode,\n detectPlatform,\n matchAudience,\n} from \"./audience.js\";\nimport {\n discoverLicenseToken,\n type LicenseDiscoveryOptions,\n} from \"./discovery.js\";\nimport {\n LicenseRequiredError,\n type DvaiLicensePayload,\n type DvaiPlatform,\n type LicenseStatus,\n} from \"./types.js\";\n\nexport interface LicenseValidatorOptions extends LicenseDiscoveryOptions {\n /**\n * Override the public-key registry. Defaults to `DVAI_PUBLIC_KEYS`\n * from `./publicKeys.ts`. Tests inject their own keypair via this\n * option so they can sign + verify against a deterministic key\n * without polluting the production registry.\n */\n publicKeys?: Record<string, DvaiPublicKeyJwk>;\n /**\n * If true, accept tokens signed under `PLACEHOLDER_KID` (i.e. the\n * built-in placeholder public key). Off by default — a real\n * production build must replace the placeholder with a generated\n * key. Tests set this to true.\n */\n allowPlaceholderKey?: boolean;\n}\n\n/**\n * Validate a DVAI-Bridge license once at SDK startup. The returned\n * `LicenseStatus` is the discriminated value the rest of the SDK\n * dispatches on. Never throws on validation failure — it logs a\n * console.warn and returns a `free-prod` / `free-expired` status.\n */\nexport class LicenseValidator {\n private readonly opts: LicenseValidatorOptions;\n\n constructor(opts: LicenseValidatorOptions = {}) {\n this.opts = opts;\n }\n\n /**\n * Validate WITHOUT throwing. Returns a `LicenseStatus` describing what\n * the validator determined; never throws on missing / invalid /\n * expired licenses. Useful for host-app dashboards that want to\n * display the licensee / expiry / fallback reason without halting\n * SDK startup, and for tests.\n *\n * The SDK's `initialize()` calls `validateAndAssert()` instead — that\n * throws `LicenseRequiredError` for `free-prod` / `free-expired`,\n * which is how the BSL 1.1 commercial-only-in-production policy is\n * actually enforced at runtime.\n *\n * Idempotent; safe to call multiple times.\n */\n async validate(): Promise<LicenseStatus> {\n // 1. Dev-mode bypass — license required only in production.\n const dev = detectDevMode();\n if (dev.isDev) {\n return { kind: \"free-dev\", reason: dev.reason };\n }\n\n // 2. Discover the token. Returns null when no license source is\n // configured AND auto-discovery fails — fall through to free-prod\n // so the SDK still works for community / hobbyist users.\n const discovered = await discoverLicenseToken({\n ...(this.opts.token !== undefined ? { token: this.opts.token } : {}),\n ...(this.opts.path !== undefined ? { path: this.opts.path } : {}),\n });\n if (discovered === null) {\n return {\n kind: \"free-prod\",\n reason:\n \"no license token found; checked config.licenseToken, \" +\n \"config.licenseKeyPath, DVAI_LICENSE_PATH env, \" +\n \"DVAI_LICENSE_TOKEN env, and platform-default paths\",\n };\n }\n\n // 3. Verify signature + claims with jose.\n const platform = detectPlatform();\n const audience = detectAudience();\n return await this.verifyToken(discovered.token, platform, audience);\n }\n\n /**\n * Strict validation entry point used by the SDK at startup. Returns\n * `LicenseStatus` on success (`commercial`, `trial`, `free-dev`) and\n * THROWS `LicenseRequiredError` on `free-prod` / `free-expired`.\n *\n * This is the BSL 1.1 enforcement point: in production / release\n * builds (any non-dev-mode environment), the SDK refuses to operate\n * without a valid commercial or trial license. Developers running on\n * localhost / debug builds / explicit DVAI_FORCE_DEV are unaffected\n * — those return a `free-dev` status and the SDK proceeds normally.\n *\n * Use `validate()` instead when you want to inspect the status\n * without halting startup (host-app dashboards, test fixtures).\n */\n async validateAndAssert(): Promise<LicenseStatus> {\n const status = await this.validate();\n if (status.kind === \"free-prod\") {\n throw new LicenseRequiredError(buildRequiredErrorMessage(status), status);\n }\n if (status.kind === \"free-expired\") {\n throw new LicenseRequiredError(buildRequiredErrorMessage(status), status);\n }\n return status;\n }\n\n private async verifyToken(\n token: string,\n platform: DvaiPlatform,\n runtimeAudience: string | null,\n ): Promise<LicenseStatus> {\n const registry = this.opts.publicKeys ?? DVAI_PUBLIC_KEYS;\n\n // Read the kid out of the JWT header to pick the right public key.\n // We could let jose iterate but specifying the key up-front gives\n // clearer error messages on misses.\n let header: { alg?: string; kid?: string };\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[0]) {\n return {\n kind: \"free-prod\",\n reason: \"license token is not a well-formed JWT (need 3 segments)\",\n };\n }\n const headerJson = base64UrlDecodeUtf8(parts[0]);\n header = JSON.parse(headerJson) as { alg?: string; kid?: string };\n } catch (err) {\n return {\n kind: \"free-prod\",\n reason: `license token header is not parseable JSON: ${asMessage(err)}`,\n };\n }\n\n if (header.alg !== \"ES256\") {\n // Refuse `alg: none` and any non-ES256 algorithm. Critical defense\n // against the classic JWT algorithm-confusion vulnerability.\n return {\n kind: \"free-prod\",\n reason: `license token uses unsupported alg \"${header.alg ?? \"(missing)\"}\", expected ES256`,\n };\n }\n\n if (typeof header.kid !== \"string\" || header.kid.length === 0) {\n return {\n kind: \"free-prod\",\n reason: \"license token header missing kid; cannot select verification key\",\n };\n }\n\n const jwk = registry[header.kid];\n if (jwk === undefined) {\n return {\n kind: \"free-prod\",\n reason:\n `license token kid \"${header.kid}\" is not in the SDK's public-key ` +\n `registry; either the key was rotated and you're on an old SDK, ` +\n `or the token was signed with a key we don't recognise`,\n };\n }\n\n if (header.kid === PLACEHOLDER_KID && this.opts.allowPlaceholderKey !== true) {\n return {\n kind: \"free-prod\",\n reason:\n `license token signed with the placeholder key (kid \"${PLACEHOLDER_KID}\"); ` +\n `replace the placeholder in publicKeys.ts with a real key generated ` +\n `via scripts/license/generate-keypair.mjs before issuing real licenses`,\n };\n }\n\n let payload: JWTPayload;\n try {\n const key = await importJWK(jwk, \"ES256\");\n const result = await jwtVerify(token, key, {\n algorithms: [\"ES256\"],\n issuer: \"DVAI-Bridge\",\n // Audience and expiry are checked manually below so we can\n // surface specific failure reasons rather than generic\n // jose error codes.\n });\n payload = result.payload;\n } catch (err) {\n // joseErrors gives us typed failure modes — surface the most\n // useful diagnostic per category so the developer's console\n // warning is actionable.\n if (err instanceof joseErrors.JWTExpired) {\n // Expired but otherwise valid — surface the licensee/expiry\n // so the developer knows whose renewal to chase.\n const exp = (err.payload?.exp as number | undefined) ?? 0;\n const licensee = (err.payload?.licensee as string | undefined) ?? \"(unknown)\";\n return {\n kind: \"free-expired\",\n licensee,\n expiredAt: exp,\n };\n }\n if (err instanceof joseErrors.JWSSignatureVerificationFailed) {\n return {\n kind: \"free-prod\",\n reason:\n `license token signature did not verify against kid \"${header.kid}\"; ` +\n `the token may have been tampered with or was signed by a different key`,\n };\n }\n if (err instanceof joseErrors.JWTClaimValidationFailed) {\n return {\n kind: \"free-prod\",\n reason: `license token claim \"${err.claim}\" failed: ${err.reason}`,\n };\n }\n return {\n kind: \"free-prod\",\n reason: `license token verification failed: ${asMessage(err)}`,\n };\n }\n\n // Coerce + validate the payload shape ourselves (jose only checks\n // the standard claims). Each branch below provides a specific\n // free-prod reason so the developer can fix exactly what's wrong.\n if (!isLicensePayload(payload)) {\n return {\n kind: \"free-prod\",\n reason: \"license token payload missing required DVAI fields (tier/platforms/aud/licensee)\",\n };\n }\n\n if (!payload.platforms.includes(platform)) {\n return {\n kind: \"free-prod\",\n reason:\n `license token does not authorise platform \"${platform}\"; ` +\n `the token covers [${payload.platforms.join(\", \")}]`,\n };\n }\n\n const matched = matchAudience(runtimeAudience, payload.aud);\n if (matched === null) {\n return {\n kind: \"free-prod\",\n reason:\n `license token's audience entries [${payload.aud.join(\", \")}] ` +\n `do not match the current runtime audience \"${runtimeAudience ?? \"(none)\"}\"` +\n (runtimeAudience === null\n ? ` — set DVAI_AUDIENCE in your environment, or use a \"*\" aud entry for any-domain licenses`\n : \"\"),\n };\n }\n\n return {\n kind: payload.tier,\n licensee: payload.licensee,\n expiresAt: payload.exp,\n platform,\n audienceMatched: matched,\n };\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------------- */\n\nfunction isLicensePayload(p: JWTPayload): p is DvaiLicensePayload & JWTPayload {\n return (\n typeof p.iss === \"string\" &&\n typeof p.sub === \"string\" &&\n Array.isArray(p.aud) &&\n p.aud.every((a) => typeof a === \"string\") &&\n (p.tier === \"commercial\" || p.tier === \"trial\") &&\n Array.isArray((p as { platforms?: unknown }).platforms) &&\n ((p as { platforms: unknown[] }).platforms).every((x) => typeof x === \"string\") &&\n typeof (p as { licensee?: unknown }).licensee === \"string\" &&\n typeof p.iat === \"number\" &&\n typeof p.exp === \"number\"\n );\n}\n\nfunction base64UrlDecodeUtf8(s: string): string {\n // Convert base64url → base64, pad, then decode.\n const pad = s.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (s.length % 4));\n const b64 = s.replace(/-/g, \"+\").replace(/_/g, \"/\") + pad;\n // Browsers: atob → binary string → UTF-8 via TextDecoder.\n if (typeof atob === \"function\") {\n const binary = atob(b64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n // Node fallback (Buffer is available).\n return Buffer.from(b64, \"base64\").toString(\"utf-8\");\n}\n\nfunction asMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Build the developer-facing error message for `LicenseRequiredError`.\n * Intentionally verbose: it tells the developer exactly what failed,\n * how to resolve it, where to put the license file, and how to bypass\n * for local development. This message will be printed to a terminal /\n * a browser console / a crash log somewhere — make it readable in all\n * three contexts.\n */\nfunction buildRequiredErrorMessage(status: LicenseStatus): string {\n const header =\n \"\\n\" +\n \"DVAI-Bridge Commercial License Required\\n\" +\n \"=======================================\\n\";\n\n const reason =\n status.kind === \"free-expired\"\n ? `License for \"${status.licensee}\" expired at ${new Date(\n status.expiredAt * 1000,\n ).toISOString()}.`\n : status.kind === \"free-prod\"\n ? status.reason\n : \"(unknown status)\";\n\n const remediation =\n \"\\n\" +\n \"This SDK is licensed under BSL 1.1 and requires a valid commercial\\n\" +\n \"or trial license to run in production / release builds.\\n\" +\n \"\\n\" +\n \"To resolve:\\n\" +\n \" 1. Obtain a license at https://deepvoiceai.com/dvai-bridge/license\\n\" +\n \" 2. Place the file at one of these locations (any will work):\\n\" +\n \" - <project-root>/dvai-license.jwt (auto-discovered)\\n\" +\n \" - the path you pass as DVAIConfig.licenseKeyPath\\n\" +\n \" - the path in $DVAI_LICENSE_PATH\\n\" +\n \" - inline JWT in DVAIConfig.licenseToken or $DVAI_LICENSE_TOKEN\\n\" +\n \" 3. Re-run.\\n\" +\n \"\\n\" +\n \"Developing locally? The SDK auto-detects dev mode on:\\n\" +\n \" - localhost / 127.0.0.1 / *.local hostnames in the browser\\n\" +\n \" - NODE_ENV=test or NODE_ENV=development in Node\\n\" +\n \" - DVAI_FORCE_DEV=1 environment variable (explicit override)\\n\" +\n \" - Capacitor.DEBUG=true on hybrid mobile builds\\n\" +\n \"Any of these silences this error and lets the SDK run without a\\n\" +\n \"license.\\n\";\n\n return header + \"\\n\" + reason + \"\\n\" + remediation;\n}\n","/**\n * Public-key registry for DVAI-Bridge license JWT verification.\n *\n * Each entry is keyed by `kid` (key id, written by the license generator\n * into the JWT header). The SDK looks up the matching entry by kid when\n * verifying a license token. Multiple entries can coexist so that key\n * rotation is non-disruptive: ship the new key in a release alongside\n * the old, leave the old in place for ~12 months while previously-\n * issued licenses naturally expire or get re-issued, then prune.\n *\n * THE PRIVATE KEY DOES NOT LIVE HERE. It belongs in your secrets\n * manager (1Password / AWS Secrets Manager / Vault), accessible only\n * to the license-generator service that produces signed JWTs. The\n * mathematics of ECDSA P-256 guarantee that a holder of the public\n * key alone cannot forge a signature.\n *\n * To populate this registry:\n * 1. Run `node scripts/license/generate-keypair.mjs` (see that\n * script's comment for full instructions)\n * 2. Paste the printed PUBLIC key JWK as an entry below\n * 3. Move the printed PRIVATE key into your secrets store\n * 4. Wire your license-generator backend to use the private key\n */\n\n/** ES256 (P-256 ECDSA) public key in JWK form. */\nexport interface DvaiPublicKeyJwk {\n kty: \"EC\";\n crv: \"P-256\";\n x: string;\n y: string;\n alg?: \"ES256\";\n use?: \"sig\";\n kid?: string;\n}\n\n/**\n * Registry mapping `kid` → public key JWK.\n *\n * ⚠️ The entry below is a **placeholder** — it is a published, well-known\n * test keypair and DOES NOT verify any real production license. Before\n * shipping licenses to customers, replace it with the output of\n * `scripts/license/generate-keypair.mjs`. The SDK refuses to validate\n * licenses against the placeholder kid `\"placeholder-do-not-ship\"`\n * unless DVAI_LICENSE_ALLOW_PLACEHOLDER=1 is set (test-only escape hatch).\n *\n * Adding a new key for rotation:\n *\n * export const DVAI_PUBLIC_KEYS: Record<string, DvaiPublicKeyJwk> = {\n * \"2026-05\": { kty: \"EC\", crv: \"P-256\", x: \"...\", y: \"...\", alg: \"ES256\", use: \"sig\", kid: \"2026-05\" },\n * \"2027-01\": { kty: \"EC\", crv: \"P-256\", x: \"...\", y: \"...\", alg: \"ES256\", use: \"sig\", kid: \"2027-01\" },\n * };\n */\nexport const DVAI_PUBLIC_KEYS: Record<string, DvaiPublicKeyJwk> = {\n // Production key, kid `2026-05`. Generated 2026-05-15 by\n // scripts/license/generate-keypair.mjs. The matching private key\n // lives in the operator's secrets manager.\n \"2026-05\": {\n kty: \"EC\",\n crv: \"P-256\",\n x: \"2Y8TuhnlE4tiVDtliozYTgc1TAqi4_TBTI6FHe1p_Vw\",\n y: \"pyxMJHj10HPe2hnpJvMpnZ4AzpYZRfqGEMhpBr1-Oto\",\n alg: \"ES256\",\n use: \"sig\",\n kid: \"2026-05\",\n },\n // PLACEHOLDER — used by the SDK's own unit tests and by the sample\n // license printed by `generate-keypair.mjs`. The validator REFUSES to\n // accept tokens signed under this kid unless allowPlaceholderKey is\n // explicitly set (DVAI_LICENSE_ALLOW_PLACEHOLDER=1 env var, or the\n // allowPlaceholderKey constructor option). Safe to keep in production\n // builds; remove only if you want test fixtures to stop working.\n \"placeholder-do-not-ship\": {\n kty: \"EC\",\n crv: \"P-256\",\n x: \"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\",\n y: \"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\",\n alg: \"ES256\",\n use: \"sig\",\n kid: \"placeholder-do-not-ship\",\n },\n};\n\n/**\n * `kid` reserved for the placeholder key above. The validator refuses to\n * accept tokens signed with this kid unless the caller explicitly opts\n * in (DVAI_LICENSE_ALLOW_PLACEHOLDER=1 or `allowPlaceholderKey: true`\n * passed to the validator constructor). Used by tests and by the\n * sample license printed by `generate-keypair.mjs`.\n */\nexport const PLACEHOLDER_KID = \"placeholder-do-not-ship\";\n","/**\n * Runtime audience + platform + dev-mode detection for the JS/TS SDK.\n *\n * Native SDKs (iOS, Android, .NET, Flutter) re-implement this in their\n * own languages — the semantics are the same but the platform APIs\n * differ. Each native validator detects its own audience (bundle id /\n * package name / assembly name) and its own platform identifier.\n *\n * For the JS/TS SDK, \"audience\" means:\n * - in a browser: window.location.hostname\n * - under Capacitor: the native platform's bundle id (read via the\n * Capacitor bridge if available, otherwise falls back to hostname)\n * - under React Native: deferred — the RN SDK's validator handles this\n * - in Node: process.env.DVAI_AUDIENCE (operator-supplied), else null\n * (Node deployments without an explicit audience can't be domain-\n * bound, so the license aud check is permissive — see validator)\n *\n * \"Dev mode\" detection bypasses license enforcement entirely so\n * developers don't need a license to run the SDK on localhost or in a\n * debug build. This matches the prior LicenseValidator behaviour and\n * keeps the developer-experience curve gentle.\n */\nimport type { DvaiPlatform } from \"./types.js\";\n\n/** Detect the current SDK platform identifier. Best-effort; returns\n * the most specific known platform that matches the runtime. */\nexport function detectPlatform(): DvaiPlatform {\n // Capacitor is detected before browser because a Capacitor app *is*\n // a browser environment with a Capacitor global attached. Order matters.\n if (typeof globalThis !== \"undefined\") {\n const g = globalThis as unknown as {\n Capacitor?: { isNativePlatform?: () => boolean };\n process?: { versions?: { node?: string } };\n window?: unknown;\n };\n if (g.Capacitor?.isNativePlatform?.()) return \"capacitor\";\n if (g.process?.versions?.node && typeof g.window === \"undefined\") {\n return \"node\";\n }\n }\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") {\n return \"web\";\n }\n // Fall back to \"node\" — workers and exotic JS hosts get folded in here.\n // The audience binding is the load-bearing check; platform is a coarse\n // filter, so over-permissiveness here is acceptable.\n return \"node\";\n}\n\n/** Detect the current audience string the license must bind. Returns\n * null when no determinable audience exists (e.g. headless Node) —\n * the validator handles null by accepting any aud entry, since binding\n * enforcement requires a concrete runtime identifier to match against. */\nexport function detectAudience(): string | null {\n // Browser-like environments report the hostname. Capacitor reports\n // `localhost` (the bundled-content origin) which is intentionally\n // matched against the license's aud entries — Capacitor apps that\n // want native-bundle-id binding should use the native SDK's validator\n // (running below the bridge) rather than the JS-side one.\n if (typeof window !== \"undefined\") {\n const w = window as unknown as { location?: { hostname?: string } };\n const host = w.location?.hostname;\n if (typeof host === \"string\" && host.length > 0) return host;\n }\n // Node.js explicit override — operators set this on the process so\n // server-side deployments can opt in to license binding.\n if (typeof process !== \"undefined\" && process.env?.DVAI_AUDIENCE) {\n return process.env.DVAI_AUDIENCE;\n }\n return null;\n}\n\n/**\n * Detect whether the SDK is running in a developer environment where\n * license enforcement should be bypassed. The bypass list is intentionally\n * generous: blocking a developer mid-`pnpm dev` with a license-not-found\n * error would be hostile. The cost is that a malicious actor pointing\n * their build at `localhost` could bypass — but they could equally fork\n * the SDK and remove the check, so the dev-mode bypass adds no real\n * attack surface.\n */\nexport function detectDevMode(): { isDev: boolean; reason: string } {\n // 1. Explicit env-var override wins.\n if (typeof process !== \"undefined\" && process.env) {\n if (process.env.DVAI_FORCE_PROD === \"1\" || process.env.DVAI_FORCE_PROD === \"true\") {\n return { isDev: false, reason: \"DVAI_FORCE_PROD set\" };\n }\n if (process.env.DVAI_FORCE_DEV === \"1\" || process.env.DVAI_FORCE_DEV === \"true\") {\n return { isDev: true, reason: \"DVAI_FORCE_DEV set\" };\n }\n if (process.env.NODE_ENV === \"test\") {\n return { isDev: true, reason: \"NODE_ENV=test\" };\n }\n }\n\n // 2. Capacitor / Cordova debug flags.\n const g = (typeof globalThis !== \"undefined\" ? globalThis : {}) as Record<string, unknown>;\n const cap = g[\"Capacitor\"] as { DEBUG?: boolean } | undefined;\n if (cap?.DEBUG === true) {\n return { isDev: true, reason: \"Capacitor.DEBUG=true\" };\n }\n\n // 3. Localhost / private-network / .local mDNS hostnames in the browser.\n // Matches the prior LicenseValidator's heuristic so v3.2.x apps that\n // used to bypass licensing on dev URLs continue to bypass.\n if (typeof window !== \"undefined\") {\n const w = window as unknown as { location?: { hostname?: string } };\n const host = w.location?.hostname ?? \"\";\n if (\n host === \"localhost\" ||\n host === \"127.0.0.1\" ||\n host === \"::1\" ||\n host.endsWith(\".local\") ||\n host.startsWith(\"192.168.\") ||\n host.startsWith(\"10.\") ||\n host.startsWith(\"172.\")\n ) {\n return { isDev: true, reason: `localhost-class hostname: ${host}` };\n }\n // 4. localStorage override (browser-only test hook).\n try {\n const ls = (window as unknown as { localStorage?: Storage }).localStorage;\n if (ls?.getItem(\"DVAI_FORCE_PROD\") === \"true\") {\n return { isDev: false, reason: \"localStorage DVAI_FORCE_PROD=true\" };\n }\n if (ls?.getItem(\"DVAI_FORCE_DEV\") === \"true\") {\n return { isDev: true, reason: \"localStorage DVAI_FORCE_DEV=true\" };\n }\n } catch {\n /* sandboxed contexts (some iframes) throw on storage access */\n }\n }\n\n return { isDev: false, reason: \"production-class environment\" };\n}\n\n/**\n * Decide whether a license-payload `aud` entry matches the current\n * runtime audience. Supports exact match and `*.example.com` wildcard\n * matching for subdomain binding. Returns the matched `aud` pattern\n * on success so it can be recorded for audit, or null on miss.\n *\n * Match rules:\n * - \"foo\" matches \"foo\" exactly\n * - \"*.example.com\" matches \"example.com\" AND any \"<sub>.example.com\"\n * - \"*\" matches any non-empty audience (intentionally permissive; use\n * for trial/site licenses that span all of a customer's deployments)\n *\n * Runtime audience of `null` matches `\"*\"` only — a Node deployment\n * without DVAI_AUDIENCE set can activate \"any-domain\" licenses but\n * not domain-bound ones. This is the safe default; operators that\n * want stricter binding set DVAI_AUDIENCE explicitly.\n */\nexport function matchAudience(\n runtimeAudience: string | null,\n audClaim: string[],\n): string | null {\n if (runtimeAudience === null) {\n return audClaim.includes(\"*\") ? \"*\" : null;\n }\n const runtime = runtimeAudience.toLowerCase();\n for (const pattern of audClaim) {\n const p = pattern.toLowerCase();\n if (p === \"*\") return pattern; // permissive wildcard\n if (p === runtime) return pattern; // exact match\n if (p.startsWith(\"*.\")) {\n const suffix = p.slice(2);\n if (runtime === suffix || runtime.endsWith(\".\" + suffix)) {\n return pattern;\n }\n }\n }\n return null;\n}\n","/**\n * License-file discovery for the JS/TS SDK.\n *\n * The SDK reads the license JWT from (in priority order):\n *\n * 1. An explicit string literal passed as `licenseToken` in DVAIConfig\n * — useful for CI / serverless / contexts where reading a file isn't\n * practical and the operator wants to inject via env var instead.\n *\n * 2. A path passed as `licenseKeyPath` in DVAIConfig — the developer\n * points the SDK at a file they've placed somewhere non-default.\n *\n * 3. The `DVAI_LICENSE_PATH` env var — same as (2) but driven by\n * process environment, helpful for containerised deployments.\n *\n * 4. Auto-discovery from platform-default locations (see below) —\n * the dev-friendly happy path. Drop the file at the convention\n * location and forget about it.\n *\n * Default discovery paths per JS-side platform:\n *\n * - **Node.js**: looks for `dvai-license.jwt` in `process.cwd()` and\n * in `<package-root>/dvai-license.jwt` (one level up). Mirrors how\n * `.env` files are discovered.\n *\n * - **Browser**: fetches `/dvai-license.jwt` from the same origin. The\n * file must be served alongside `mockServiceWorker.js` — typically\n * in `public/` for Vite/Webpack apps. The HTTP fetch is cached by\n * the browser so this is one round-trip on startup, not per request.\n *\n * - **Capacitor**: fetches `/dvai-license.jwt` from the bundled web\n * assets (Capacitor.convertFileSrc on the public/ folder). The\n * native-side validator (in DVAIBridge.iOS / .Android) is the\n * authoritative binding for native bundle ids; this JS-side check\n * is a soft signal only.\n *\n * Returning `null` means \"no license file found\"; the validator treats\n * that as the free-tier case (after dev-mode bypass).\n */\n\n/**\n * Default filename the SDK looks for. Chosen to be self-documenting and\n * to encourage commit-to-vcs (so the license travels with the code,\n * audited and reviewable by the team).\n */\nexport const DEFAULT_LICENSE_FILENAME = \"dvai-license.jwt\";\n\nexport interface LicenseDiscoveryOptions {\n /** Pre-loaded JWT string (skips all filesystem / fetch lookups). */\n token?: string;\n /** Explicit path or URL to load from. Overrides auto-discovery. */\n path?: string;\n}\n\n/**\n * Best-effort load of a license JWT. Returns the raw token string on\n * success or null on miss. Errors during loading (file not found,\n * network timeout) collapse to null — the validator's responsibility\n * is to handle the no-license case gracefully, not the discovery\n * layer's.\n */\nexport async function discoverLicenseToken(\n opts: LicenseDiscoveryOptions = {},\n): Promise<{ token: string; source: string } | null> {\n // 1. Explicit token wins.\n if (typeof opts.token === \"string\" && opts.token.length > 0) {\n return { token: opts.token.trim(), source: \"config.licenseToken\" };\n }\n\n // 2. Explicit path (config option).\n if (typeof opts.path === \"string\" && opts.path.length > 0) {\n const loaded = await tryLoadFromPath(opts.path);\n if (loaded !== null) return { token: loaded, source: opts.path };\n return null; // explicit path that didn't load is a real miss, not a silent fallthrough\n }\n\n // 3. Env-var path.\n if (typeof process !== \"undefined\" && process.env?.DVAI_LICENSE_PATH) {\n const envPath = process.env.DVAI_LICENSE_PATH;\n const loaded = await tryLoadFromPath(envPath);\n if (loaded !== null) return { token: loaded, source: `DVAI_LICENSE_PATH=${envPath}` };\n }\n\n // 4. Env-var inline token (alternative to file for serverless).\n if (typeof process !== \"undefined\" && process.env?.DVAI_LICENSE_TOKEN) {\n return {\n token: process.env.DVAI_LICENSE_TOKEN.trim(),\n source: \"DVAI_LICENSE_TOKEN env var\",\n };\n }\n\n // 5. Platform default-location auto-discovery.\n return await tryAutoDiscover();\n}\n\nasync function tryLoadFromPath(p: string): Promise<string | null> {\n // URLs (browser + Node 18+ fetch) — anything with a scheme.\n if (/^https?:\\/\\//i.test(p)) {\n return await tryFetch(p);\n }\n // Otherwise treat as filesystem path. Use a dynamic import so this\n // module stays browser-safe; `fs/promises` is only imported when we're\n // actually about to read a path.\n return await tryFsRead(p);\n}\n\nasync function tryFetch(url: string): Promise<string | null> {\n try {\n const res = await fetch(url, { method: \"GET\" });\n if (!res.ok) return null;\n const text = (await res.text()).trim();\n return text.length > 0 ? text : null;\n } catch {\n return null;\n }\n}\n\nasync function tryFsRead(path: string): Promise<string | null> {\n try {\n // Dynamic import keeps `fs/promises` out of the browser bundle.\n // tsup/vite will tree-shake this out when bundling for web.\n const fs = await import(\"node:fs/promises\");\n const buf = await fs.readFile(path, \"utf-8\");\n const text = buf.trim();\n return text.length > 0 ? text : null;\n } catch {\n return null;\n }\n}\n\nasync function tryAutoDiscover(): Promise<{ token: string; source: string } | null> {\n // Browser / Capacitor: try same-origin /dvai-license.jwt.\n if (typeof window !== \"undefined\") {\n const sameOriginUrl = `/${DEFAULT_LICENSE_FILENAME}`;\n const loaded = await tryFetch(sameOriginUrl);\n if (loaded !== null) return { token: loaded, source: sameOriginUrl };\n return null;\n }\n\n // Node: try cwd, then one level up (monorepo root case).\n if (typeof process !== \"undefined\" && typeof process.cwd === \"function\") {\n const path = await import(\"node:path\").catch(() => null);\n if (path === null) return null;\n const candidates = [\n path.join(process.cwd(), DEFAULT_LICENSE_FILENAME),\n path.join(process.cwd(), \"..\", DEFAULT_LICENSE_FILENAME),\n ];\n for (const c of candidates) {\n const loaded = await tryFsRead(c);\n if (loaded !== null) return { token: loaded, source: c };\n }\n }\n return null;\n}\n","/**\n * Type surface for the DVAI-Bridge offline JWT license system.\n *\n * The whole license flow is deliberately small:\n * 1. A signed JWT (produced server-side by your license generator) is\n * either dropped at a platform-default path, pointed at via the\n * `licenseKeyPath` config option, or pasted directly into the\n * `licenseToken` config option.\n * 2. The SDK reads it, verifies the ECDSA P-256 signature against the\n * key registry in `publicKeys.ts`, and checks four runtime claims:\n * - signature must verify against a known kid\n * - `exp` must be in the future\n * - `aud` must include the current audience (hostname / bundleId)\n * - `platforms` must include the current SDK platform\n * 3. The outcome is summarised in a `LicenseStatus` value that the\n * rest of the SDK can dispatch on (commercial/trial → premium\n * behaviour; everything else → free-tier behaviour with the\n * \"Powered by DVAI Bridge\" attribution badge).\n *\n * Nothing in this file makes network calls. The entire flow is offline.\n */\n\n/** Recognised license tiers. Free-tier values are produced internally by\n * the validator; commercial / trial come from the signed token's `tier`\n * claim. Anything unknown collapses to \"free-prod\" defensively. */\nexport type LicenseTier =\n | \"commercial\"\n | \"trial\"\n | \"free-dev\" // running on localhost / debug build — no badge required\n | \"free-prod\" // production deploy with no valid license — badge required\n | \"free-expired\"; // had a valid license but `exp` is past — badge required + warn\n\n/** Payload shape we issue (subset; extra claims tolerated). */\nexport interface DvaiLicensePayload {\n /** Standard JWT issuer claim. Must be `\"DVAI-Bridge\"`. */\n iss: string;\n /** Standard subject — our internal license id. Surfaced in audit logs. */\n sub: string;\n /** Audience binding — array of domains and/or bundle ids permitted to\n * activate this license. Each entry is either an exact string match\n * (e.g. `\"com.acme.app\"`) or a wildcard subdomain pattern\n * (e.g. `\"*.acme.com\"` matches both `acme.com` and `app.acme.com`). */\n aud: string[];\n /** Tier the license grants. `commercial` and `trial` are the live tiers;\n * the validator never produces `free-*` here (those are computed). */\n tier: \"commercial\" | \"trial\";\n /** Which DVAI-Bridge SDK platforms this license activates. The current\n * runtime platform must appear here for the license to apply. */\n platforms: DvaiPlatform[];\n /** Display name of the licensee, for audit logs + user-facing messaging. */\n licensee: string;\n /** Standard JWT issued-at (seconds since Unix epoch). */\n iat: number;\n /** Standard JWT expiry (seconds since Unix epoch). */\n exp: number;\n}\n\n/** Platform identifiers the SDK recognises in license `platforms` claims. */\nexport type DvaiPlatform =\n | \"web\"\n | \"node\"\n | \"ios\"\n | \"android\"\n | \"dotnet\"\n | \"flutter\"\n | \"react-native\"\n | \"capacitor\";\n\n/**\n * Result of license validation. Discriminated union so the consumer's\n * decision tree is exhaustive (\"commercial\" or \"trial\" → premium;\n * everything else → free).\n */\nexport type LicenseStatus =\n | {\n kind: \"commercial\";\n licensee: string;\n expiresAt: number;\n platform: DvaiPlatform;\n audienceMatched: string;\n }\n | {\n kind: \"trial\";\n licensee: string;\n expiresAt: number;\n platform: DvaiPlatform;\n audienceMatched: string;\n }\n | {\n kind: \"free-dev\";\n /** Why dev mode was detected (for logging / dashboard surfacing). */\n reason: string;\n }\n | {\n kind: \"free-prod\";\n /** Why a license could not be loaded or validated. Surfaced via a\n * console warning so the developer can debug. Does NOT throw — the\n * SDK falls back to free tier rather than refusing to start. */\n reason: string;\n }\n | {\n kind: \"free-expired\";\n licensee: string;\n expiredAt: number;\n };\n\n/** Returns true iff `tier` represents a paid / unwatermarked status. */\nexport function isPaidTier(status: LicenseStatus): boolean {\n return status.kind === \"commercial\" || status.kind === \"trial\";\n}\n\n/**\n * Thrown by `LicenseValidator.validateAndAssert()` (and propagated from\n * `DVAI.initialize()`) when an SDK consumer attempts to run the library\n * in a production / release context without a valid commercial or trial\n * license.\n *\n * The error message is intentionally verbose: it tells the developer\n * exactly which check failed (missing file, expired, audience mismatch,\n * etc.), how to resolve it, and where to put the license file once\n * they have one. This is the front line of the BSL 1.1 commercial\n * enforcement story — surface it clearly enough that a developer can\n * unblock themselves without a support ticket.\n *\n * The `status` field carries the underlying `LicenseStatus` so\n * programmatic callers can dispatch on `err.status.kind` if they\n * want to handle \"expired\" differently from \"missing\".\n */\nexport class LicenseRequiredError extends Error {\n /** Stable name set so `err.name === \"LicenseRequiredError\"` works\n * across module-boundary serialisation (e.g. Vite SSR). */\n override readonly name = \"LicenseRequiredError\";\n\n constructor(\n message: string,\n /** The underlying validator status that triggered the throw. */\n public readonly status: LicenseStatus,\n ) {\n super(message);\n // Restore the prototype chain for native-builtin Error in environments\n // (older transpiled CJS targets, some sandboxed iframes) where it\n // gets clobbered. Cheap insurance against `instanceof` surprises.\n Object.setPrototypeOf(this, LicenseRequiredError.prototype);\n }\n}\n"],"mappings":"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","names":["extractMediaParts","messages","last","audio","images","part","buildMultimodalCallable","model","processor","opts","defaultMaxNewTokens","callable","options","prompt","inputs","genArgs","outputs","promptLen","generated","disableModelEncoders","names","name","init_multimodalCallable","__esmMin","TransformersBackend_exports","__export","TransformersBackend","detectWebGPU","tensorToArray","arr","batch","hidden","flat","out","i","flattenContent","content","c","init_TransformersBackend","__esmMin","init_multimodalCallable","config","onProgress","isNode","hasWebGPU","err","resolve","reject","worker","requestId","handleMessage","event","msg","info","progressValue","text","e","errorMessage","initParams","stream","pending","type","data","id","transformers","pipelineFn","env","progressCallback","ModelClass","processorName","ProcessorClass","processor","model","disableModelEncoders","buildMultimodalCallable","inputs","options","requestBody","messages","m","maxNewTokens","temperature","topP","result","generatedText","modelId","backend","generationTimeout","controller","completionId","created","encoder","enqueueChunk","chunk","enqueueFinal","finishReason","finalChunk","timeoutId","streamPromise","TextStreamer","tokenizer","timeoutPromise","_","error","inputArray","raw","promise","ms","timer","val","chatToLegacyCompletion","chatResp","c","legacyCompletionStreamAdapter","chatStream","model","decoder","encoder","buffer","controller","reader","done","value","idx","rawEvent","dataLine","l","payload","chunk","legacyChunk","handleCompletion","body","ctx","promptField","prompt","chatBody","legacyStream","error","init_completions","__esmMin","encodeBase64Url","bytes","binary","b","import_ed25519","init_keys","__esmMin","generatePairingKey","cryptoApi","bytes","encodeBase64Url","generateNonce","signHmac","pairingKey","message","keyBytes","decodeBase64UrlBytes","cryptoKey","sig","verifyHmac","signature","expected","constantTimeEquals","a","b","diff","i","s","b64","padded","binary","composeSignedMessage","nonce","method","path","body","bodyHash","sha256Hex","input","buf","init_handshake","__esmMin","init_keys","heuristicTokPerSec","hints","gpuBase","cpuMul","ramMul","npuBonus","estimate","detectDeviceHints","nav","ramGb","cores","hasGpu","detectDeviceHintsAsync","os","totalMem","init_heuristic","__esmMin","precheck_exports","__export","HardwareTooWeakError","assessCapability","opts","hardwareMinimum","DEFAULT_HARDWARE_MINIMUM","minLocalCapability","DEFAULT_MIN_LOCAL_CAPABILITY","hints","detectDeviceHintsAsync","tokPerSec","heuristicTokPerSec","init_precheck","__esmMin","init_heuristic","handleChatCompletion","body","ctx","headers","intercepted","err","runOnce","backend","stream","SSE_HEADERS","response","error","init_chat","__esmMin","handleEmbeddings","body","ctx","input","vectors","v","i","error","init_embeddings","__esmMin","handleModels","ctx","init_models","__esmMin","init_handlers","__esmMin","init_chat","init_completions","init_embeddings","init_models","getEndpoints","mockUrl","chat","base","chatSuffix","u","parts","import_browser","import_msw","MswTransport","init_msw","__esmMin","init_handlers","opts","ctx","urls","handlers","request","handleChatCompletion","handleCompletion","handleEmbeddings","handleModels","tryBind","server","basePort","maxAttempts","host","port","resolve","reject","onError","err","BASE_PORT","MAX_PORT_ATTEMPTS","init_port_fallback","__esmMin","pickOrigin","origin","cfg","corsHeaders","reqOrigin","allow","headers","readJsonBody","req","chunks","c","raw","writeWhatwgResponse","res","response","extraHeaders","v","k","reader","done","value","text","route","ctx","opts","cors","path","body","reqHeaders","r","handleChatCompletion","handleCompletion","handleEmbeddings","handleModels","dvaiRoutes","key","dvaiHandler","respHeaders","err","HttpTransport","init_http","__esmMin","init_handlers","init_port_fallback","createServer","server","bindHost","port","tryBind","CapacitorTransport","init_capacitor","__esmMin","opts","_ctx","DVAIBridge","result","transports_exports","__export","BASE_PORT","CapacitorTransport","HttpTransport","MAX_PORT_ATTEMPTS","MswTransport","selectTransport","tryBind","input","requested","isCapacitorContext","isBrowserLike","isNode","init_transports","__esmMin","init_msw","init_http","init_capacitor","init_port_fallback","decide","input","eligible","p","x","a","b","order","bestPeer","bestScore","threshold","buildCheckedList","list","peer","score","init_decide","__esmMin","buildNoCapableDeviceResponse","decision","ctx","message","init_error","__esmMin","parseOffloadHeader","headers","raw","k","v","lc","VALID","init_policy","__esmMin","proxyToPeer","peer","req","url","upstreamHeaders","response","headers","v","k","init_proxy","__esmMin","buildOffloadInterceptor","opts","body","ctx","headers","headerValue","readOffloadHeader","peers","modelId","localCapability","decision","decide","jsonResponse","stream","proxied","proxyToPeer","forwardableHeaders","err","errResponse","buildNoCapableDeviceResponse","p","parseOffloadHeader","out","drop","k","v","status","init_forwarder","__esmMin","init_decide","init_error","init_proxy","init_policy","offload_exports","__export","buildNoCapableDeviceResponse","buildOffloadInterceptor","decide","parseOffloadHeader","proxyToPeer","init_offload","__esmMin","init_decide","init_error","init_policy","init_proxy","init_forwarder","defaultCachePath","env","home","createCapabilityCache","IndexedDBCapabilityCache","NodeFsCapabilityCache","InMemoryCapabilityCache","DB_NAME","STORE_NAME","META_STORE_NAME","init_cache","__esmMin","key","score","k","resolve","reject","req","db","tx","value","cachePath","fs","path","raw","c","id","generateDeviceId","cryptoApi","bytes","base64UrlEncode","binary","b","init_deviceId","__esmMin","probeCapability","opts","t0","response","PROBE_PROMPT","elapsedSec","tokensFromUsage","text","tokens","approximateTokenCount","tokPerSec","init_probe","__esmMin","capability_exports","__export","HardwareTooWeakError","InMemoryCapabilityCache","IndexedDBCapabilityCache","NodeFsCapabilityCache","assessCapability","clearCapabilityCache","createCapabilityCache","detectDeviceHints","detectDeviceHintsAsync","ensureDeviceId","generateDeviceId","getCapability","heuristicTokPerSec","probeAndCache","probeCapability","cache","existing","DEVICE_ID_META_KEY","fresh","opts","cached","hints","deviceId","score","init_capability","__esmMin","init_cache","init_heuristic","init_deviceId","init_probe","init_precheck","MDNS_SERVICE_TYPE","init_types","__esmMin","StaticDiscovery","init_static","__esmMin","peers","now","p","peer","listener","err","mdns_node_exports","__export","NodeMdnsDiscovery","init_mdns_node","__esmMin","init_types","opts","mod","factory","response","queryInterval","gcInterval","peer","listener","MDNS_SERVICE_TYPE","err","txt","instanceName","all","srv","a","decoded","srvData","baseUrl","existing","raw","lines","b","map","line","eq","capability","ttl","cutoff","id","mdns_browser_exports","__export","BrowserMdnsDiscovery","init_mdns_browser","__esmMin","_opts","_listener","createMdnsDiscovery","opts","NodeMdnsDiscovery","BrowserMdnsDiscovery","CompositeDiscovery","init_composite","__esmMin","sources","source","unsub","e","merged","peer","existing","listener","err","discovery_exports","__export","BrowserMdnsDiscovery","CompositeDiscovery","MDNS_SERVICE_TYPE","StaticDiscovery","createMdnsDiscovery","init_discovery","__esmMin","init_types","init_static","init_composite","init_mdns_browser","defaultPairingsPath","env","home","createPairingStore","IndexedDBPairingStore","NodeFsPairingStore","InMemoryPairingStore","DB_NAME","STORE_NAME","init_store","__esmMin","peerDeviceId","p","resolve","reject","req","db","tx","cachePath","fs","path","raw","c","PairingPolicy","init_policy","__esmMin","init_handshake","opts","peerDeviceId","existing","ttlMs","req","callback","result","hostKey","fresh","generatePairingKey","pairing_exports","__export","InMemoryPairingStore","IndexedDBPairingStore","NodeFsPairingStore","PairingPolicy","composeSignedMessage","createPairingStore","generateNonce","generatePairingKey","signHmac","verifyHmac","init_pairing","__esmMin","init_handshake","init_store","init_policy","dvai_exports","__export","buildDvaiRoutes","handleCapability","handleHandshake","handleHealth","handlePairQr","handlePairScan","handlePeers","handleProbe","ctx","req","modelId","probeAndCache","body","pairing","err","init_dvai","__esmMin","init_capability","NodeLlamaCppBackend_exports","__export","NodeLlamaCppBackend","init_NodeLlamaCppBackend","__esmMin","config","onProgress","llamaModule","p","ms","resolve","reject","t","v","e","messages","sys","m","lastUser","flatten","c","requestBody","systemPrompt","userPrompt","maxTokens","temperature","topP","promptOpts","text","session","modelId","generationTimeout","controller","completionId","created","encoder","enqueueChunk","chunk","enqueueFinal","finishReason","finalChunk","timeoutId","error","WebLLMBackend_exports","__export","WebLLMBackend","init_WebLLMBackend","__esmMin","config","onProgress","webllm","worker","err","requestBody","result","content","engine","maxBlankChunks","generationTimeout","backend","controller","consecutiveBlanks","timeoutId","asyncChunkGenerator","timeoutPromise","_","reject","streamPromise","chunk","delta","finishReason","error","promise","ms","resolve","timer","val","index_exports","__export","DEFAULT_LICENSE_FILENAME","DVAI","DVAI_PUBLIC_KEYS","LicenseRequiredError","LicenseValidator","PLACEHOLDER_KID","chatToLegacyCompletion","composeSignedMessage","detectWebGPU","dvai","generateNonce","generatePairingKey","isPaidTier","legacyCompletionStreamAdapter","signHmac","verifyHmac","__toCommonJS","import_jose","DVAI_PUBLIC_KEYS","PLACEHOLDER_KID","detectPlatform","g","detectAudience","host","detectDevMode","ls","matchAudience","runtimeAudience","audClaim","runtime","pattern","p","suffix","DEFAULT_LICENSE_FILENAME","discoverLicenseToken","opts","loaded","tryLoadFromPath","envPath","tryAutoDiscover","p","tryFetch","tryFsRead","url","res","text","path","sameOriginUrl","candidates","c","isPaidTier","status","LicenseRequiredError","_LicenseRequiredError","message","LicenseValidator","opts","dev","detectDevMode","discovered","discoverLicenseToken","platform","detectPlatform","audience","detectAudience","status","LicenseRequiredError","buildRequiredErrorMessage","token","runtimeAudience","registry","DVAI_PUBLIC_KEYS","header","parts","headerJson","base64UrlDecodeUtf8","err","asMessage","jwk","PLACEHOLDER_KID","payload","key","joseErrors","exp","isLicensePayload","matched","matchAudience","p","a","x","s","pad","b64","binary","bytes","i","reason","init_TransformersBackend","init_completions","init_handshake","DVAI","config","LicenseValidator","onProgress","assessCapability","result","selectTransport","MswTransport","HttpTransport","CapacitorTransport","ctx","started","buildOffloadInterceptor","offloadConfig","err","error","createCapabilityCache","ensureDeviceId","CompositeDiscovery","StaticDiscovery","createMdnsDiscovery","PairingPolicy","createPairingStore","sources","skipAdvertiseOnDarwin","advertise","peerDeviceId","peerDeviceName","appId","cb","buildDvaiRoutes","self","b","fatalError","e","NodeLlamaCppBackend","customModelId","backend","TransformersBackend","WebLLMBackend","requestBody","inputs","options","opts","probeAndCache","modelId","getCapability","id","dvai"]}