@elizaos/plugin-local-inference 2.0.0-beta.1 → 2.0.3-beta.3

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.
Files changed (893) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/dist/actions/generate-media.d.ts +59 -0
  4. package/dist/actions/generate-media.d.ts.map +1 -0
  5. package/dist/actions/identify-speaker.d.ts +23 -0
  6. package/dist/actions/identify-speaker.d.ts.map +1 -0
  7. package/dist/actions/transcription-control.d.ts +29 -0
  8. package/dist/actions/transcription-control.d.ts.map +1 -0
  9. package/dist/adapters/capacitor-llama/environment.d.ts +12 -0
  10. package/dist/adapters/capacitor-llama/environment.d.ts.map +1 -0
  11. package/dist/adapters/capacitor-llama/index.browser.d.ts +9 -0
  12. package/dist/adapters/capacitor-llama/index.browser.d.ts.map +1 -0
  13. package/dist/adapters/capacitor-llama/index.d.ts +18 -0
  14. package/dist/adapters/capacitor-llama/index.d.ts.map +1 -0
  15. package/dist/adapters/capacitor-llama/loader.d.ts +35 -0
  16. package/dist/adapters/capacitor-llama/loader.d.ts.map +1 -0
  17. package/dist/adapters/capacitor-llama/native-voice-capture.d.ts +70 -0
  18. package/dist/adapters/capacitor-llama/native-voice-capture.d.ts.map +1 -0
  19. package/dist/adapters/capacitor-llama/structured-output.d.ts +62 -0
  20. package/dist/adapters/capacitor-llama/structured-output.d.ts.map +1 -0
  21. package/dist/adapters/capacitor-llama/text-streaming.d.ts +24 -0
  22. package/dist/adapters/capacitor-llama/text-streaming.d.ts.map +1 -0
  23. package/dist/adapters/capacitor-llama/types.d.ts +338 -0
  24. package/dist/adapters/capacitor-llama/types.d.ts.map +1 -0
  25. package/dist/adapters/capacitor-llama/voice-turn.d.ts +86 -0
  26. package/dist/adapters/capacitor-llama/voice-turn.d.ts.map +1 -0
  27. package/dist/backends/apple-foundation.d.ts +56 -0
  28. package/dist/backends/apple-foundation.d.ts.map +1 -0
  29. package/dist/index.d.ts +8 -37
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +38979 -430
  32. package/dist/index.js.map +217 -0
  33. package/dist/local-inference-routes.d.ts +47 -0
  34. package/dist/local-inference-routes.d.ts.map +1 -0
  35. package/dist/provider.d.ts +21 -0
  36. package/dist/provider.d.ts.map +1 -0
  37. package/dist/routes/compat-helpers.d.ts +18 -0
  38. package/dist/routes/compat-helpers.d.ts.map +1 -0
  39. package/dist/routes/family-member-route.d.ts +62 -0
  40. package/dist/routes/family-member-route.d.ts.map +1 -0
  41. package/dist/routes/index.d.ts +20 -0
  42. package/dist/routes/index.d.ts.map +1 -0
  43. package/dist/routes/index.js +42040 -0
  44. package/dist/routes/index.js.map +236 -0
  45. package/dist/routes/live-diarization-route.d.ts +33 -0
  46. package/dist/routes/live-diarization-route.d.ts.map +1 -0
  47. package/dist/routes/local-inference-asr-route.d.ts +4 -0
  48. package/dist/routes/local-inference-asr-route.d.ts.map +1 -0
  49. package/dist/routes/local-inference-asr-transcribe.d.ts +20 -0
  50. package/dist/routes/local-inference-asr-transcribe.d.ts.map +1 -0
  51. package/dist/routes/local-inference-compat-routes.d.ts +16 -0
  52. package/dist/routes/local-inference-compat-routes.d.ts.map +1 -0
  53. package/dist/routes/local-inference-tts-route.d.ts +7 -0
  54. package/dist/routes/local-inference-tts-route.d.ts.map +1 -0
  55. package/dist/routes/native-pcm-turn-route.d.ts +3 -0
  56. package/dist/routes/native-pcm-turn-route.d.ts.map +1 -0
  57. package/dist/routes/transcript-audio-store.d.ts +15 -0
  58. package/dist/routes/transcript-audio-store.d.ts.map +1 -0
  59. package/dist/routes/transcripts-routes.d.ts +44 -0
  60. package/dist/routes/transcripts-routes.d.ts.map +1 -0
  61. package/dist/routes/voice-first-run-routes.d.ts +62 -0
  62. package/dist/routes/voice-first-run-routes.d.ts.map +1 -0
  63. package/dist/routes/voice-models-routes.d.ts +62 -0
  64. package/dist/routes/voice-models-routes.d.ts.map +1 -0
  65. package/dist/routes/voice-profile-plugin-routes.d.ts +19 -0
  66. package/dist/routes/voice-profile-plugin-routes.d.ts.map +1 -0
  67. package/dist/routes/voice-profiles-management-routes.d.ts +52 -0
  68. package/dist/routes/voice-profiles-management-routes.d.ts.map +1 -0
  69. package/dist/routes/voice-speaker-profile-routes.d.ts +57 -0
  70. package/dist/routes/voice-speaker-profile-routes.d.ts.map +1 -0
  71. package/dist/runtime/embedding-manager-support.d.ts +77 -0
  72. package/dist/runtime/embedding-manager-support.d.ts.map +1 -0
  73. package/dist/runtime/embedding-presets.d.ts +16 -0
  74. package/dist/runtime/embedding-presets.d.ts.map +1 -0
  75. package/dist/runtime/embedding-warmup-policy.d.ts +14 -0
  76. package/dist/runtime/embedding-warmup-policy.d.ts.map +1 -0
  77. package/dist/runtime/ensure-local-inference-handler.d.ts +70 -0
  78. package/dist/runtime/ensure-local-inference-handler.d.ts.map +1 -0
  79. package/dist/runtime/index.d.ts +15 -0
  80. package/dist/runtime/index.d.ts.map +1 -0
  81. package/dist/runtime/index.js +38768 -0
  82. package/dist/runtime/index.js.map +217 -0
  83. package/dist/runtime/mobile-local-inference-gate.d.ts +63 -0
  84. package/dist/runtime/mobile-local-inference-gate.d.ts.map +1 -0
  85. package/dist/runtime/voice-entity-binding.d.ts +113 -0
  86. package/dist/runtime/voice-entity-binding.d.ts.map +1 -0
  87. package/dist/services/active-model.d.ts +310 -0
  88. package/dist/services/active-model.d.ts.map +1 -0
  89. package/dist/services/asr-provenance.d.ts +5 -0
  90. package/dist/services/asr-provenance.d.ts.map +1 -0
  91. package/dist/services/assignments.d.ts +84 -0
  92. package/dist/services/assignments.d.ts.map +1 -0
  93. package/dist/services/backend-selector.d.ts +55 -0
  94. package/dist/services/backend-selector.d.ts.map +1 -0
  95. package/dist/services/backend.d.ts +440 -0
  96. package/dist/services/backend.d.ts.map +1 -0
  97. package/dist/services/bionic-host-loader.d.ts +67 -0
  98. package/dist/services/bionic-host-loader.d.ts.map +1 -0
  99. package/dist/services/bundled-models.d.ts +34 -0
  100. package/dist/services/bundled-models.d.ts.map +1 -0
  101. package/dist/services/cache-bridge.d.ts +206 -0
  102. package/dist/services/cache-bridge.d.ts.map +1 -0
  103. package/dist/services/catalog.d.ts +10 -0
  104. package/dist/services/catalog.d.ts.map +1 -0
  105. package/dist/services/checkpoint-client.d.ts +109 -0
  106. package/dist/services/checkpoint-client.d.ts.map +1 -0
  107. package/dist/services/checkpoint-manager.d.ts +217 -0
  108. package/dist/services/checkpoint-manager.d.ts.map +1 -0
  109. package/dist/services/cloud-fallback.d.ts +102 -0
  110. package/dist/services/cloud-fallback.d.ts.map +1 -0
  111. package/dist/services/context-fit.d.ts +36 -0
  112. package/dist/services/context-fit.d.ts.map +1 -0
  113. package/dist/services/conversation-registry.d.ts +142 -0
  114. package/dist/services/conversation-registry.d.ts.map +1 -0
  115. package/dist/services/desktop-fused-ffi-backend-runtime.d.ts +111 -0
  116. package/dist/services/desktop-fused-ffi-backend-runtime.d.ts.map +1 -0
  117. package/dist/services/device-bridge.d.ts +188 -0
  118. package/dist/services/device-bridge.d.ts.map +1 -0
  119. package/dist/services/device-resource-metrics.d.ts +149 -0
  120. package/dist/services/device-resource-metrics.d.ts.map +1 -0
  121. package/dist/services/device-tier.d.ts +133 -0
  122. package/dist/services/device-tier.d.ts.map +1 -0
  123. package/dist/services/downloader.d.ts +94 -0
  124. package/dist/services/downloader.d.ts.map +1 -0
  125. package/dist/services/engine.d.ts +579 -0
  126. package/dist/services/engine.d.ts.map +1 -0
  127. package/dist/services/ensure-local-artifacts.d.ts +82 -0
  128. package/dist/services/ensure-local-artifacts.d.ts.map +1 -0
  129. package/dist/services/external-scanner.d.ts +17 -0
  130. package/dist/services/external-scanner.d.ts.map +1 -0
  131. package/dist/services/ffi-llm-mock.d.ts +90 -0
  132. package/dist/services/ffi-llm-mock.d.ts.map +1 -0
  133. package/dist/services/ffi-llm-streaming-abi.d.ts +318 -0
  134. package/dist/services/ffi-llm-streaming-abi.d.ts.map +1 -0
  135. package/dist/services/ffi-streaming-backend.d.ts +201 -0
  136. package/dist/services/ffi-streaming-backend.d.ts.map +1 -0
  137. package/dist/services/ffi-streaming-runner.d.ts +146 -0
  138. package/dist/services/ffi-streaming-runner.d.ts.map +1 -0
  139. package/dist/services/gpu-autotune.d.ts +150 -0
  140. package/dist/services/gpu-autotune.d.ts.map +1 -0
  141. package/dist/services/gpu-detect.d.ts +56 -0
  142. package/dist/services/gpu-detect.d.ts.map +1 -0
  143. package/dist/services/handler-registry.d.ts +72 -0
  144. package/dist/services/handler-registry.d.ts.map +1 -0
  145. package/dist/services/hardware.d.ts +63 -0
  146. package/dist/services/hardware.d.ts.map +1 -0
  147. package/dist/services/image-description-runtime.d.ts +14 -0
  148. package/dist/services/image-description-runtime.d.ts.map +1 -0
  149. package/dist/services/imagegen/aosp-unavailable.d.ts +134 -0
  150. package/dist/services/imagegen/aosp-unavailable.d.ts.map +1 -0
  151. package/dist/services/imagegen/backend-selector.d.ts +118 -0
  152. package/dist/services/imagegen/backend-selector.d.ts.map +1 -0
  153. package/dist/services/imagegen/coreml-unavailable.d.ts +105 -0
  154. package/dist/services/imagegen/coreml-unavailable.d.ts.map +1 -0
  155. package/dist/services/imagegen/errors.d.ts +16 -0
  156. package/dist/services/imagegen/errors.d.ts.map +1 -0
  157. package/dist/services/imagegen/index.d.ts +58 -0
  158. package/dist/services/imagegen/index.d.ts.map +1 -0
  159. package/dist/services/imagegen/mflux.d.ts +74 -0
  160. package/dist/services/imagegen/mflux.d.ts.map +1 -0
  161. package/dist/services/imagegen/sd-cpp.d.ts +181 -0
  162. package/dist/services/imagegen/sd-cpp.d.ts.map +1 -0
  163. package/dist/services/imagegen/tensorrt-unavailable.d.ts +83 -0
  164. package/dist/services/imagegen/tensorrt-unavailable.d.ts.map +1 -0
  165. package/dist/services/imagegen/types.d.ts +181 -0
  166. package/dist/services/imagegen/types.d.ts.map +1 -0
  167. package/dist/services/index.d.ts +31 -0
  168. package/dist/services/index.d.ts.map +1 -0
  169. package/dist/services/index.js +39453 -0
  170. package/dist/services/index.js.map +227 -0
  171. package/dist/services/inference-capabilities.d.ts +132 -0
  172. package/dist/services/inference-capabilities.d.ts.map +1 -0
  173. package/dist/services/inference-telemetry.d.ts +59 -0
  174. package/dist/services/inference-telemetry.d.ts.map +1 -0
  175. package/dist/services/ios-llama-streaming.d.ts +119 -0
  176. package/dist/services/ios-llama-streaming.d.ts.map +1 -0
  177. package/dist/services/kv-spill.d.ts +189 -0
  178. package/dist/services/kv-spill.d.ts.map +1 -0
  179. package/dist/services/latency-trace.d.ts +346 -0
  180. package/dist/services/latency-trace.d.ts.map +1 -0
  181. package/dist/services/lib-target.d.ts +55 -0
  182. package/dist/services/lib-target.d.ts.map +1 -0
  183. package/dist/services/live-signals.d.ts +86 -0
  184. package/dist/services/live-signals.d.ts.map +1 -0
  185. package/dist/services/llama-server-metrics.d.ts +114 -0
  186. package/dist/services/llama-server-metrics.d.ts.map +1 -0
  187. package/dist/services/llm-streaming-binding.d.ts +96 -0
  188. package/dist/services/llm-streaming-binding.d.ts.map +1 -0
  189. package/dist/services/load-args.d.ts +82 -0
  190. package/dist/services/load-args.d.ts.map +1 -0
  191. package/dist/services/manifest/index.d.ts +4 -0
  192. package/dist/services/manifest/index.d.ts.map +1 -0
  193. package/dist/services/manifest/schema.d.ts +903 -0
  194. package/dist/services/manifest/schema.d.ts.map +1 -0
  195. package/dist/services/manifest/types.d.ts +32 -0
  196. package/dist/services/manifest/types.d.ts.map +1 -0
  197. package/dist/services/manifest/validator.d.ts +66 -0
  198. package/dist/services/manifest/validator.d.ts.map +1 -0
  199. package/dist/services/memory-arbiter.d.ts +348 -0
  200. package/dist/services/memory-arbiter.d.ts.map +1 -0
  201. package/dist/services/memory-benchmark.d.ts +76 -0
  202. package/dist/services/memory-benchmark.d.ts.map +1 -0
  203. package/dist/services/memory-monitor.d.ts +128 -0
  204. package/dist/services/memory-monitor.d.ts.map +1 -0
  205. package/dist/services/memory-pressure.d.ts +130 -0
  206. package/dist/services/memory-pressure.d.ts.map +1 -0
  207. package/dist/services/mtp-doctor.d.ts +13 -0
  208. package/dist/services/mtp-doctor.d.ts.map +1 -0
  209. package/dist/services/network-policy.d.ts +127 -0
  210. package/dist/services/network-policy.d.ts.map +1 -0
  211. package/dist/services/paths.d.ts +6 -0
  212. package/dist/services/paths.d.ts.map +1 -0
  213. package/dist/services/planner-skeleton.d.ts +124 -0
  214. package/dist/services/planner-skeleton.d.ts.map +1 -0
  215. package/dist/services/providers.d.ts +38 -0
  216. package/dist/services/providers.d.ts.map +1 -0
  217. package/dist/services/ram-budget.d.ts +110 -0
  218. package/dist/services/ram-budget.d.ts.map +1 -0
  219. package/dist/services/readiness.d.ts +9 -0
  220. package/dist/services/readiness.d.ts.map +1 -0
  221. package/dist/services/recommendation.d.ts +111 -0
  222. package/dist/services/recommendation.d.ts.map +1 -0
  223. package/dist/services/registry.d.ts +33 -0
  224. package/dist/services/registry.d.ts.map +1 -0
  225. package/dist/services/router-handler.d.ts +92 -0
  226. package/dist/services/router-handler.d.ts.map +1 -0
  227. package/dist/services/routing-policy.d.ts +92 -0
  228. package/dist/services/routing-policy.d.ts.map +1 -0
  229. package/dist/services/routing-preferences.d.ts +8 -0
  230. package/dist/services/routing-preferences.d.ts.map +1 -0
  231. package/dist/services/runtime-target.d.ts +98 -0
  232. package/dist/services/runtime-target.d.ts.map +1 -0
  233. package/dist/services/service.d.ts +128 -0
  234. package/dist/services/service.d.ts.map +1 -0
  235. package/dist/services/session-pool.d.ts +72 -0
  236. package/dist/services/session-pool.d.ts.map +1 -0
  237. package/dist/services/structured-output/deterministic-repair.d.ts +23 -0
  238. package/dist/services/structured-output/deterministic-repair.d.ts.map +1 -0
  239. package/dist/services/structured-output/index.d.ts +2 -0
  240. package/dist/services/structured-output/index.d.ts.map +1 -0
  241. package/dist/services/structured-output.d.ts +311 -0
  242. package/dist/services/structured-output.d.ts.map +1 -0
  243. package/dist/services/system-memory.d.ts +33 -0
  244. package/dist/services/system-memory.d.ts.map +1 -0
  245. package/dist/services/types.d.ts +19 -0
  246. package/dist/services/types.d.ts.map +1 -0
  247. package/dist/services/verify-on-device.d.ts +34 -0
  248. package/dist/services/verify-on-device.d.ts.map +1 -0
  249. package/dist/services/verify.d.ts +8 -0
  250. package/dist/services/verify.d.ts.map +1 -0
  251. package/dist/services/vision/aosp-unavailable.d.ts +115 -0
  252. package/dist/services/vision/aosp-unavailable.d.ts.map +1 -0
  253. package/dist/services/vision/capacitor-llama.d.ts +99 -0
  254. package/dist/services/vision/capacitor-llama.d.ts.map +1 -0
  255. package/dist/services/vision/cloud-fallback.d.ts +47 -0
  256. package/dist/services/vision/cloud-fallback.d.ts.map +1 -0
  257. package/dist/services/vision/hash.d.ts +71 -0
  258. package/dist/services/vision/hash.d.ts.map +1 -0
  259. package/dist/services/vision/index.d.ts +95 -0
  260. package/dist/services/vision/index.d.ts.map +1 -0
  261. package/dist/services/vision/llama-server.d.ts +73 -0
  262. package/dist/services/vision/llama-server.d.ts.map +1 -0
  263. package/dist/services/vision/types.d.ts +162 -0
  264. package/dist/services/vision/types.d.ts.map +1 -0
  265. package/dist/services/vision/vast-fallback.d.ts +18 -0
  266. package/dist/services/vision/vast-fallback.d.ts.map +1 -0
  267. package/dist/services/vision-embedding-cache.d.ts +98 -0
  268. package/dist/services/vision-embedding-cache.d.ts.map +1 -0
  269. package/dist/services/voice/__test-helpers__/fake-ffi.d.ts +27 -0
  270. package/dist/services/voice/__test-helpers__/fake-ffi.d.ts.map +1 -0
  271. package/dist/services/voice/__test-helpers__/synthetic-speech.d.ts +66 -0
  272. package/dist/services/voice/__test-helpers__/synthetic-speech.d.ts.map +1 -0
  273. package/dist/services/voice/acoustic-speaker-attribution.d.ts +61 -0
  274. package/dist/services/voice/acoustic-speaker-attribution.d.ts.map +1 -0
  275. package/dist/services/voice/audio-frame-consumer.d.ts +294 -0
  276. package/dist/services/voice/audio-frame-consumer.d.ts.map +1 -0
  277. package/dist/services/voice/barge-in.d.ts +112 -0
  278. package/dist/services/voice/barge-in.d.ts.map +1 -0
  279. package/dist/services/voice/cancellation-coordinator.d.ts +127 -0
  280. package/dist/services/voice/cancellation-coordinator.d.ts.map +1 -0
  281. package/dist/services/voice/checkpoint-manager.d.ts +199 -0
  282. package/dist/services/voice/checkpoint-manager.d.ts.map +1 -0
  283. package/dist/services/voice/checkpoint-policy.d.ts +178 -0
  284. package/dist/services/voice/checkpoint-policy.d.ts.map +1 -0
  285. package/dist/services/voice/corpus-augment.d.ts +111 -0
  286. package/dist/services/voice/corpus-augment.d.ts.map +1 -0
  287. package/dist/services/voice/corpus-generator.d.ts +134 -0
  288. package/dist/services/voice/corpus-generator.d.ts.map +1 -0
  289. package/dist/services/voice/diarization-error-rate.d.ts +40 -0
  290. package/dist/services/voice/diarization-error-rate.d.ts.map +1 -0
  291. package/dist/services/voice/e2e-harness.d.ts +297 -0
  292. package/dist/services/voice/e2e-harness.d.ts.map +1 -0
  293. package/dist/services/voice/eager-context-builder.d.ts +170 -0
  294. package/dist/services/voice/eager-context-builder.d.ts.map +1 -0
  295. package/dist/services/voice/echo-delay.d.ts +67 -0
  296. package/dist/services/voice/echo-delay.d.ts.map +1 -0
  297. package/dist/services/voice/echo-metrics.d.ts +7 -0
  298. package/dist/services/voice/echo-metrics.d.ts.map +1 -0
  299. package/dist/services/voice/echo-reference-buffer.d.ts +65 -0
  300. package/dist/services/voice/echo-reference-buffer.d.ts.map +1 -0
  301. package/dist/services/voice/eliza1-eot-scorer.d.ts +124 -0
  302. package/dist/services/voice/eliza1-eot-scorer.d.ts.map +1 -0
  303. package/dist/services/voice/embedding-server.d.ts +37 -0
  304. package/dist/services/voice/embedding-server.d.ts.map +1 -0
  305. package/dist/services/voice/embedding.d.ts +132 -0
  306. package/dist/services/voice/embedding.d.ts.map +1 -0
  307. package/dist/services/voice/emotion-attribution.d.ts +68 -0
  308. package/dist/services/voice/emotion-attribution.d.ts.map +1 -0
  309. package/dist/services/voice/engine-bridge.d.ts +762 -0
  310. package/dist/services/voice/engine-bridge.d.ts.map +1 -0
  311. package/dist/services/voice/eot-classifier-ggml.d.ts +179 -0
  312. package/dist/services/voice/eot-classifier-ggml.d.ts.map +1 -0
  313. package/dist/services/voice/eot-classifier.d.ts +211 -0
  314. package/dist/services/voice/eot-classifier.d.ts.map +1 -0
  315. package/dist/services/voice/errors.d.ts +20 -0
  316. package/dist/services/voice/errors.d.ts.map +1 -0
  317. package/dist/services/voice/expressive-tags.d.ts +158 -0
  318. package/dist/services/voice/expressive-tags.d.ts.map +1 -0
  319. package/dist/services/voice/ffi-bindings.d.ts +696 -0
  320. package/dist/services/voice/ffi-bindings.d.ts.map +1 -0
  321. package/dist/services/voice/first-line-cache.d.ts +181 -0
  322. package/dist/services/voice/first-line-cache.d.ts.map +1 -0
  323. package/dist/services/voice/fused-eot-scorer.d.ts +51 -0
  324. package/dist/services/voice/fused-eot-scorer.d.ts.map +1 -0
  325. package/dist/services/voice/index.d.ts +96 -0
  326. package/dist/services/voice/index.d.ts.map +1 -0
  327. package/dist/services/voice/kokoro/index.d.ts +24 -0
  328. package/dist/services/voice/kokoro/index.d.ts.map +1 -0
  329. package/dist/services/voice/kokoro/kokoro-backend.d.ts +87 -0
  330. package/dist/services/voice/kokoro/kokoro-backend.d.ts.map +1 -0
  331. package/dist/services/voice/kokoro/kokoro-engine-discovery.d.ts +58 -0
  332. package/dist/services/voice/kokoro/kokoro-engine-discovery.d.ts.map +1 -0
  333. package/dist/services/voice/kokoro/kokoro-ffi-runtime.d.ts +75 -0
  334. package/dist/services/voice/kokoro/kokoro-ffi-runtime.d.ts.map +1 -0
  335. package/dist/services/voice/kokoro/kokoro-runtime.d.ts +100 -0
  336. package/dist/services/voice/kokoro/kokoro-runtime.d.ts.map +1 -0
  337. package/dist/services/voice/kokoro/phoneme-stream.d.ts +51 -0
  338. package/dist/services/voice/kokoro/phoneme-stream.d.ts.map +1 -0
  339. package/dist/services/voice/kokoro/phonemizer.d.ts +50 -0
  340. package/dist/services/voice/kokoro/phonemizer.d.ts.map +1 -0
  341. package/dist/services/voice/kokoro/pick-runtime.d.ts +61 -0
  342. package/dist/services/voice/kokoro/pick-runtime.d.ts.map +1 -0
  343. package/dist/services/voice/kokoro/runtime-selection.d.ts +31 -0
  344. package/dist/services/voice/kokoro/runtime-selection.d.ts.map +1 -0
  345. package/dist/services/voice/kokoro/types.d.ts +82 -0
  346. package/dist/services/voice/kokoro/types.d.ts.map +1 -0
  347. package/dist/services/voice/kokoro/voice-presets.d.ts +23 -0
  348. package/dist/services/voice/kokoro/voice-presets.d.ts.map +1 -0
  349. package/dist/services/voice/kokoro/voices.d.ts +30 -0
  350. package/dist/services/voice/kokoro/voices.d.ts.map +1 -0
  351. package/dist/services/voice/lifecycle.d.ts +135 -0
  352. package/dist/services/voice/lifecycle.d.ts.map +1 -0
  353. package/dist/services/voice/live-diarization-session.d.ts +196 -0
  354. package/dist/services/voice/live-diarization-session.d.ts.map +1 -0
  355. package/dist/services/voice/metric-math.d.ts +10 -0
  356. package/dist/services/voice/metric-math.d.ts.map +1 -0
  357. package/dist/services/voice/mic-source.d.ts +136 -0
  358. package/dist/services/voice/mic-source.d.ts.map +1 -0
  359. package/dist/services/voice/nlms-echo-canceller.d.ts +137 -0
  360. package/dist/services/voice/nlms-echo-canceller.d.ts.map +1 -0
  361. package/dist/services/voice/optimistic-policy.d.ts +109 -0
  362. package/dist/services/voice/optimistic-policy.d.ts.map +1 -0
  363. package/dist/services/voice/optimistic-rollback.d.ts +151 -0
  364. package/dist/services/voice/optimistic-rollback.d.ts.map +1 -0
  365. package/dist/services/voice/partial-stabilizer.d.ts +73 -0
  366. package/dist/services/voice/partial-stabilizer.d.ts.map +1 -0
  367. package/dist/services/voice/phoneme-tokenizer.d.ts +49 -0
  368. package/dist/services/voice/phoneme-tokenizer.d.ts.map +1 -0
  369. package/dist/services/voice/phrase-cache.d.ts +76 -0
  370. package/dist/services/voice/phrase-cache.d.ts.map +1 -0
  371. package/dist/services/voice/phrase-chunker.d.ts +62 -0
  372. package/dist/services/voice/phrase-chunker.d.ts.map +1 -0
  373. package/dist/services/voice/pipeline-impls.d.ts +151 -0
  374. package/dist/services/voice/pipeline-impls.d.ts.map +1 -0
  375. package/dist/services/voice/pipeline.d.ts +216 -0
  376. package/dist/services/voice/pipeline.d.ts.map +1 -0
  377. package/dist/services/voice/prefill-client.d.ts +123 -0
  378. package/dist/services/voice/prefill-client.d.ts.map +1 -0
  379. package/dist/services/voice/prefix-preserving-queue.d.ts +113 -0
  380. package/dist/services/voice/prefix-preserving-queue.d.ts.map +1 -0
  381. package/dist/services/voice/profile-store.d.ts +248 -0
  382. package/dist/services/voice/profile-store.d.ts.map +1 -0
  383. package/dist/services/voice/ring-buffer.d.ts +40 -0
  384. package/dist/services/voice/ring-buffer.d.ts.map +1 -0
  385. package/dist/services/voice/rollback-queue.d.ts +24 -0
  386. package/dist/services/voice/rollback-queue.d.ts.map +1 -0
  387. package/dist/services/voice/samantha-preset-placeholder.d.ts +67 -0
  388. package/dist/services/voice/samantha-preset-placeholder.d.ts.map +1 -0
  389. package/dist/services/voice/samantha-preset-regenerator.d.ts +87 -0
  390. package/dist/services/voice/samantha-preset-regenerator.d.ts.map +1 -0
  391. package/dist/services/voice/scheduler.d.ts +146 -0
  392. package/dist/services/voice/scheduler.d.ts.map +1 -0
  393. package/dist/services/voice/self-voice-imprint.d.ts +33 -0
  394. package/dist/services/voice/self-voice-imprint.d.ts.map +1 -0
  395. package/dist/services/voice/shared-resources.d.ts +204 -0
  396. package/dist/services/voice/shared-resources.d.ts.map +1 -0
  397. package/dist/services/voice/speaker/attribution-pipeline.d.ts +74 -0
  398. package/dist/services/voice/speaker/attribution-pipeline.d.ts.map +1 -0
  399. package/dist/services/voice/speaker/diarizer-fused.d.ts +59 -0
  400. package/dist/services/voice/speaker/diarizer-fused.d.ts.map +1 -0
  401. package/dist/services/voice/speaker/diarizer.d.ts +75 -0
  402. package/dist/services/voice/speaker/diarizer.d.ts.map +1 -0
  403. package/dist/services/voice/speaker/encoder-fused.d.ts +60 -0
  404. package/dist/services/voice/speaker/encoder-fused.d.ts.map +1 -0
  405. package/dist/services/voice/speaker/encoder-ggml.d.ts +33 -0
  406. package/dist/services/voice/speaker/encoder-ggml.d.ts.map +1 -0
  407. package/dist/services/voice/speaker/encoder.d.ts +37 -0
  408. package/dist/services/voice/speaker/encoder.d.ts.map +1 -0
  409. package/dist/services/voice/speaker-imprint.d.ts +83 -0
  410. package/dist/services/voice/speaker-imprint.d.ts.map +1 -0
  411. package/dist/services/voice/speaker-preset-cache.d.ts +77 -0
  412. package/dist/services/voice/speaker-preset-cache.d.ts.map +1 -0
  413. package/dist/services/voice/streaming-asr/streaming-pipeline-adapter.d.ts +160 -0
  414. package/dist/services/voice/streaming-asr/streaming-pipeline-adapter.d.ts.map +1 -0
  415. package/dist/services/voice/system-audio-sink.d.ts +73 -0
  416. package/dist/services/voice/system-audio-sink.d.ts.map +1 -0
  417. package/dist/services/voice/transcriber.d.ts +244 -0
  418. package/dist/services/voice/transcriber.d.ts.map +1 -0
  419. package/dist/services/voice/transcript-knowledge.d.ts +37 -0
  420. package/dist/services/voice/transcript-knowledge.d.ts.map +1 -0
  421. package/dist/services/voice/transcript-service.d.ts +60 -0
  422. package/dist/services/voice/transcript-service.d.ts.map +1 -0
  423. package/dist/services/voice/transcript-store.d.ts +64 -0
  424. package/dist/services/voice/transcript-store.d.ts.map +1 -0
  425. package/dist/services/voice/turn-controller.d.ts +183 -0
  426. package/dist/services/voice/turn-controller.d.ts.map +1 -0
  427. package/dist/services/voice/types.d.ts +643 -0
  428. package/dist/services/voice/types.d.ts.map +1 -0
  429. package/dist/services/voice/vad.d.ts +283 -0
  430. package/dist/services/voice/vad.d.ts.map +1 -0
  431. package/dist/services/voice/voice-budget.d.ts +241 -0
  432. package/dist/services/voice/voice-budget.d.ts.map +1 -0
  433. package/dist/services/voice/voice-emotion-classifier.d.ts +95 -0
  434. package/dist/services/voice/voice-emotion-classifier.d.ts.map +1 -0
  435. package/dist/services/voice/voice-preload-predictor.d.ts +76 -0
  436. package/dist/services/voice/voice-preload-predictor.d.ts.map +1 -0
  437. package/dist/services/voice/voice-preset-format.d.ts +158 -0
  438. package/dist/services/voice/voice-preset-format.d.ts.map +1 -0
  439. package/dist/services/voice/voice-profile-artifact.d.ts +116 -0
  440. package/dist/services/voice/voice-profile-artifact.d.ts.map +1 -0
  441. package/dist/services/voice/voice-profile-routes.d.ts +83 -0
  442. package/dist/services/voice/voice-profile-routes.d.ts.map +1 -0
  443. package/dist/services/voice/voice-scenario.d.ts +131 -0
  444. package/dist/services/voice/voice-scenario.d.ts.map +1 -0
  445. package/dist/services/voice/voice-state-machine.d.ts +364 -0
  446. package/dist/services/voice/voice-state-machine.d.ts.map +1 -0
  447. package/dist/services/voice/voice-workbench-report.d.ts +117 -0
  448. package/dist/services/voice/voice-workbench-report.d.ts.map +1 -0
  449. package/dist/services/voice/wake-word-ggml.d.ts +100 -0
  450. package/dist/services/voice/wake-word-ggml.d.ts.map +1 -0
  451. package/dist/services/voice/wake-word.d.ts +255 -0
  452. package/dist/services/voice/wake-word.d.ts.map +1 -0
  453. package/dist/services/voice/wav-codec.d.ts +11 -0
  454. package/dist/services/voice/wav-codec.d.ts.map +1 -0
  455. package/dist/services/voice/workbench-entrypoint.d.ts +42 -0
  456. package/dist/services/voice/workbench-entrypoint.d.ts.map +1 -0
  457. package/dist/services/voice/workbench-headless-runner.d.ts +102 -0
  458. package/dist/services/voice/workbench-headless-runner.d.ts.map +1 -0
  459. package/dist/services/voice/workbench-logic-services.d.ts +36 -0
  460. package/dist/services/voice/workbench-logic-services.d.ts.map +1 -0
  461. package/dist/services/voice/workbench-real-services.d.ts +17 -0
  462. package/dist/services/voice/workbench-real-services.d.ts.map +1 -0
  463. package/dist/services/voice/workbench-scenarios.d.ts +24 -0
  464. package/dist/services/voice/workbench-scenarios.d.ts.map +1 -0
  465. package/dist/services/voice/wrap-with-first-line-cache.d.ts +70 -0
  466. package/dist/services/voice/wrap-with-first-line-cache.d.ts.map +1 -0
  467. package/dist/services/voice-model-updater.d.ts +240 -0
  468. package/dist/services/voice-model-updater.d.ts.map +1 -0
  469. package/dist/services/voice-prewarm.d.ts +3 -0
  470. package/dist/services/voice-prewarm.d.ts.map +1 -0
  471. package/dist/voice-workbench.d.ts +18 -0
  472. package/dist/voice-workbench.d.ts.map +1 -0
  473. package/dist/voice-workbench.js +5259 -0
  474. package/dist/voice-workbench.js.map +34 -0
  475. package/package.json +101 -15
  476. package/registry-entry.json +137 -0
  477. package/src/actions/generate-media.ts +647 -0
  478. package/src/actions/identify-speaker.ts +171 -0
  479. package/src/actions/transcription-control.test.ts +100 -0
  480. package/src/actions/transcription-control.ts +127 -0
  481. package/src/adapters/capacitor-llama/__tests__/compat-behavior.test.ts +218 -0
  482. package/src/adapters/capacitor-llama/__tests__/index.test.ts +68 -0
  483. package/src/adapters/capacitor-llama/__tests__/structured-output.test.ts +215 -0
  484. package/src/adapters/capacitor-llama/__tests__/text-streaming.test.ts +174 -0
  485. package/src/adapters/capacitor-llama/__tests__/voice-turn.test.ts +293 -0
  486. package/src/adapters/capacitor-llama/environment.ts +71 -0
  487. package/src/adapters/capacitor-llama/index.browser.ts +83 -0
  488. package/src/adapters/capacitor-llama/index.ts +831 -0
  489. package/src/adapters/capacitor-llama/loader.ts +109 -0
  490. package/src/adapters/capacitor-llama/native-voice-capture.ts +140 -0
  491. package/src/adapters/capacitor-llama/structured-output.ts +165 -0
  492. package/src/adapters/capacitor-llama/text-streaming.ts +227 -0
  493. package/src/adapters/capacitor-llama/types.ts +374 -0
  494. package/src/adapters/capacitor-llama/voice-turn.ts +178 -0
  495. package/src/backends/apple-foundation.ts +127 -0
  496. package/src/index.ts +62 -0
  497. package/src/local-inference-routes.test.ts +390 -0
  498. package/src/local-inference-routes.ts +1625 -0
  499. package/src/provider.ts +1111 -0
  500. package/src/routes/compat-helpers.ts +275 -0
  501. package/src/routes/family-member-route.ts +353 -0
  502. package/src/routes/index.ts +61 -0
  503. package/src/routes/live-diarization-route.test.ts +347 -0
  504. package/src/routes/live-diarization-route.ts +198 -0
  505. package/src/routes/local-inference-asr-route.test.ts +246 -0
  506. package/src/routes/local-inference-asr-route.ts +166 -0
  507. package/src/routes/local-inference-asr-transcribe.test.ts +118 -0
  508. package/src/routes/local-inference-asr-transcribe.ts +97 -0
  509. package/src/routes/local-inference-compat-routes.test.ts +485 -0
  510. package/src/routes/local-inference-compat-routes.ts +775 -0
  511. package/src/routes/local-inference-tts-route.test.ts +179 -0
  512. package/src/routes/local-inference-tts-route.ts +230 -0
  513. package/src/routes/native-pcm-turn-route.test.ts +136 -0
  514. package/src/routes/native-pcm-turn-route.ts +121 -0
  515. package/src/routes/transcript-audio-store.ts +27 -0
  516. package/src/routes/transcripts-routes.test.ts +195 -0
  517. package/src/routes/transcripts-routes.ts +191 -0
  518. package/src/routes/voice-first-run-routes.ts +524 -0
  519. package/src/routes/voice-models-routes.ts +554 -0
  520. package/src/routes/voice-profile-plugin-routes.ts +138 -0
  521. package/src/routes/voice-profiles-management-routes.ts +476 -0
  522. package/src/routes/voice-speaker-profile-routes.ts +199 -0
  523. package/src/runtime/aosp-llama-loader-selection.test.ts +80 -0
  524. package/src/runtime/bionic-wire-encoding.test.ts +147 -0
  525. package/src/runtime/capacitor-llama.d.ts +25 -0
  526. package/src/runtime/embedding-manager-support.ts +497 -0
  527. package/src/runtime/embedding-presets.ts +81 -0
  528. package/src/runtime/embedding-warmup-policy.test.ts +53 -0
  529. package/src/runtime/embedding-warmup-policy.ts +48 -0
  530. package/src/runtime/ensure-local-inference-handler.test.ts +726 -0
  531. package/src/runtime/ensure-local-inference-handler.ts +1640 -0
  532. package/src/runtime/index.ts +36 -0
  533. package/src/runtime/mobile-local-inference-gate.test.ts +152 -0
  534. package/src/runtime/mobile-local-inference-gate.ts +99 -0
  535. package/src/runtime/voice-entity-binding.transcript.test.ts +98 -0
  536. package/src/runtime/voice-entity-binding.ts +368 -0
  537. package/src/runtime/voice-speaker-entity-contract.test.ts +149 -0
  538. package/src/services/README.md +71 -0
  539. package/src/services/__tests__/backend-selector.precedence.test.ts +333 -0
  540. package/src/services/__tests__/backend-selector.test.ts +101 -0
  541. package/src/services/__tests__/checkpoint-manager.test.ts +376 -0
  542. package/src/services/__tests__/gpu-autotune.test.ts +400 -0
  543. package/src/services/__tests__/llm-streaming-binding.test.ts +85 -0
  544. package/src/services/__tests__/planner-grammar.test.ts +372 -0
  545. package/src/services/__tests__/runtime-target.test.ts +176 -0
  546. package/src/services/active-model-context-fit.test.ts +125 -0
  547. package/src/services/active-model-switch-rollback.test.ts +183 -0
  548. package/src/services/active-model.ts +1416 -0
  549. package/src/services/asr-provenance.ts +68 -0
  550. package/src/services/assignment-validation.test.ts +118 -0
  551. package/src/services/assignments.test.ts +106 -0
  552. package/src/services/assignments.ts +278 -0
  553. package/src/services/backend-selector.ts +95 -0
  554. package/src/services/backend.test.ts +84 -0
  555. package/src/services/backend.ts +791 -0
  556. package/src/services/bionic-host-loader.test.ts +226 -0
  557. package/src/services/bionic-host-loader.ts +252 -0
  558. package/src/services/bundled-models.ts +129 -0
  559. package/src/services/cache-bridge.test.ts +516 -0
  560. package/src/services/cache-bridge.ts +423 -0
  561. package/src/services/catalog.test.ts +259 -0
  562. package/src/services/catalog.ts +33 -0
  563. package/src/services/checkpoint-client.ts +258 -0
  564. package/src/services/checkpoint-manager.ts +474 -0
  565. package/src/services/cloud-fallback.ts +230 -0
  566. package/src/services/context-fit.test.ts +121 -0
  567. package/src/services/context-fit.ts +113 -0
  568. package/src/services/conversation-registry.test.ts +235 -0
  569. package/src/services/conversation-registry.ts +264 -0
  570. package/src/services/desktop-fused-ffi-backend-runtime.ts +431 -0
  571. package/src/services/device-bridge.ts +1237 -0
  572. package/src/services/device-resource-metrics.test.ts +98 -0
  573. package/src/services/device-resource-metrics.ts +346 -0
  574. package/src/services/device-tier.test.ts +458 -0
  575. package/src/services/device-tier.ts +502 -0
  576. package/src/services/downloader.test.ts +888 -0
  577. package/src/services/downloader.ts +1039 -0
  578. package/src/services/engine-direct-bundle.test.ts +90 -0
  579. package/src/services/engine-streaming.test.ts +80 -0
  580. package/src/services/engine.ts +2096 -0
  581. package/src/services/ensure-local-artifacts.integration.test.ts +273 -0
  582. package/src/services/ensure-local-artifacts.test.ts +368 -0
  583. package/src/services/ensure-local-artifacts.ts +351 -0
  584. package/src/services/external-scanner.ts +312 -0
  585. package/src/services/ffi-llm-mock.ts +354 -0
  586. package/src/services/ffi-llm-streaming-abi.ts +445 -0
  587. package/src/services/ffi-streaming-backend.ts +418 -0
  588. package/src/services/ffi-streaming-runner.test.ts +220 -0
  589. package/src/services/ffi-streaming-runner.ts +407 -0
  590. package/src/services/ffi-unload-ordering.test.ts +166 -0
  591. package/src/services/fused-eliza1-no-regression.test.ts +144 -0
  592. package/src/services/gpu-autotune.ts +534 -0
  593. package/src/services/gpu-detect.ts +139 -0
  594. package/src/services/handler-registry.ts +240 -0
  595. package/src/services/hardware.test.ts +236 -0
  596. package/src/services/hardware.ts +438 -0
  597. package/src/services/image-description-runtime.test.ts +61 -0
  598. package/src/services/image-description-runtime.ts +118 -0
  599. package/src/services/imagegen/aosp-unavailable.ts +229 -0
  600. package/src/services/imagegen/backend-selector.test.ts +190 -0
  601. package/src/services/imagegen/backend-selector.ts +277 -0
  602. package/src/services/imagegen/coreml-unavailable.ts +237 -0
  603. package/src/services/imagegen/errors.ts +40 -0
  604. package/src/services/imagegen/index.ts +144 -0
  605. package/src/services/imagegen/mflux.ts +313 -0
  606. package/src/services/imagegen/sd-cpp.ts +715 -0
  607. package/src/services/imagegen/tensorrt-unavailable.ts +295 -0
  608. package/src/services/imagegen/types.ts +193 -0
  609. package/src/services/index.ts +229 -0
  610. package/src/services/inference-capabilities.test.ts +75 -0
  611. package/src/services/inference-capabilities.ts +204 -0
  612. package/src/services/inference-telemetry.ts +143 -0
  613. package/src/services/ios-llama-streaming.ts +248 -0
  614. package/src/services/kv-spill.test.ts +222 -0
  615. package/src/services/kv-spill.ts +357 -0
  616. package/src/services/latency-trace.test.ts +266 -0
  617. package/src/services/latency-trace.ts +844 -0
  618. package/src/services/lib-target.test.ts +145 -0
  619. package/src/services/lib-target.ts +102 -0
  620. package/src/services/live-signals.test.ts +132 -0
  621. package/src/services/live-signals.ts +177 -0
  622. package/src/services/llama-server-metrics.test.ts +168 -0
  623. package/src/services/llama-server-metrics.ts +304 -0
  624. package/src/services/llm-streaming-binding.ts +136 -0
  625. package/src/services/load-args.ts +81 -0
  626. package/src/services/manifest/eliza-1.manifest.v1.json +790 -0
  627. package/src/services/manifest/index.ts +72 -0
  628. package/src/services/manifest/manifest.test.ts +791 -0
  629. package/src/services/manifest/schema.ts +761 -0
  630. package/src/services/manifest/types.ts +61 -0
  631. package/src/services/manifest/validator.ts +633 -0
  632. package/src/services/memory-arbiter.test.ts +558 -0
  633. package/src/services/memory-arbiter.ts +991 -0
  634. package/src/services/memory-benchmark.test.ts +91 -0
  635. package/src/services/memory-benchmark.ts +354 -0
  636. package/src/services/memory-monitor.test.ts +232 -0
  637. package/src/services/memory-monitor.ts +309 -0
  638. package/src/services/memory-pressure.ts +414 -0
  639. package/src/services/mtp-doctor.ts +86 -0
  640. package/src/services/network-policy.ts +346 -0
  641. package/src/services/paths.ts +25 -0
  642. package/src/services/planner-skeleton.ts +175 -0
  643. package/src/services/providers.ts +507 -0
  644. package/src/services/ram-budget-cache.test.ts +164 -0
  645. package/src/services/ram-budget.ts +309 -0
  646. package/src/services/readiness.test.ts +87 -0
  647. package/src/services/readiness.ts +238 -0
  648. package/src/services/recommendation.test.ts +216 -0
  649. package/src/services/recommendation.ts +671 -0
  650. package/src/services/registry.ts +157 -0
  651. package/src/services/required-kernels-gate.test.ts +64 -0
  652. package/src/services/router-handler.test.ts +45 -0
  653. package/src/services/router-handler.ts +426 -0
  654. package/src/services/routing-policy.test.ts +352 -0
  655. package/src/services/routing-policy.ts +367 -0
  656. package/src/services/routing-preferences.ts +17 -0
  657. package/src/services/runtime-target.ts +154 -0
  658. package/src/services/service.test.ts +223 -0
  659. package/src/services/service.ts +750 -0
  660. package/src/services/session-pool.ts +153 -0
  661. package/src/services/structured-output/deterministic-repair.test.ts +169 -0
  662. package/src/services/structured-output/deterministic-repair.ts +443 -0
  663. package/src/services/structured-output/index.ts +4 -0
  664. package/src/services/structured-output.test.ts +483 -0
  665. package/src/services/structured-output.ts +712 -0
  666. package/src/services/system-memory.test.ts +47 -0
  667. package/src/services/system-memory.ts +67 -0
  668. package/src/services/transcription-priority.test.ts +211 -0
  669. package/src/services/types.ts +59 -0
  670. package/src/services/verify-on-device.test.ts +87 -0
  671. package/src/services/verify-on-device.ts +127 -0
  672. package/src/services/verify.ts +13 -0
  673. package/src/services/vision/aosp-unavailable.ts +163 -0
  674. package/src/services/vision/capacitor-llama.ts +255 -0
  675. package/src/services/vision/cloud-fallback.test.ts +243 -0
  676. package/src/services/vision/cloud-fallback.ts +268 -0
  677. package/src/services/vision/fallback-chain.test.ts +86 -0
  678. package/src/services/vision/hash.ts +157 -0
  679. package/src/services/vision/index.ts +251 -0
  680. package/src/services/vision/llama-server.ts +177 -0
  681. package/src/services/vision/types.ts +163 -0
  682. package/src/services/vision/vast-fallback.ts +127 -0
  683. package/src/services/vision-embedding-cache.ts +189 -0
  684. package/src/services/voice/VOICE_WORKBENCH.md +133 -0
  685. package/src/services/voice/__fixtures__/voice-workbench-logic-baseline.json +180 -0
  686. package/src/services/voice/__test-helpers__/fake-ffi.ts +94 -0
  687. package/src/services/voice/__test-helpers__/synthetic-speech.ts +194 -0
  688. package/src/services/voice/__tests__/checkpoint-manager.test.ts +241 -0
  689. package/src/services/voice/__tests__/checkpoint-policy.test.ts +270 -0
  690. package/src/services/voice/__tests__/eager-context-builder.test.ts +257 -0
  691. package/src/services/voice/__tests__/eliza1-eot-scorer.test.ts +288 -0
  692. package/src/services/voice/__tests__/eot-classifier.test.ts +431 -0
  693. package/src/services/voice/__tests__/optimistic-rollback.test.ts +312 -0
  694. package/src/services/voice/__tests__/prefill-client.test.ts +266 -0
  695. package/src/services/voice/__tests__/prefix-preserving-queue.test.ts +208 -0
  696. package/src/services/voice/__tests__/streaming-asr.test.ts +450 -0
  697. package/src/services/voice/__tests__/streaming-transcriber.test.ts +339 -0
  698. package/src/services/voice/__tests__/turn-detector-resolver.test.ts +195 -0
  699. package/src/services/voice/__tests__/voice-state-machine-prefill.test.ts +275 -0
  700. package/src/services/voice/__tests__/voice-state-machine.test.ts +354 -0
  701. package/src/services/voice/acoustic-speaker-attribution.test.ts +165 -0
  702. package/src/services/voice/acoustic-speaker-attribution.ts +336 -0
  703. package/src/services/voice/asr-timed.real.test.ts +139 -0
  704. package/src/services/voice/audio-frame-consumer.test.ts +669 -0
  705. package/src/services/voice/audio-frame-consumer.ts +651 -0
  706. package/src/services/voice/barge-in.test.ts +244 -0
  707. package/src/services/voice/barge-in.ts +335 -0
  708. package/src/services/voice/cancellation-coordinator.test.ts +196 -0
  709. package/src/services/voice/cancellation-coordinator.ts +269 -0
  710. package/src/services/voice/checkpoint-manager.ts +401 -0
  711. package/src/services/voice/checkpoint-policy.ts +336 -0
  712. package/src/services/voice/composite-eot-classifier.test.ts +59 -0
  713. package/src/services/voice/corpus-augment.test.ts +276 -0
  714. package/src/services/voice/corpus-augment.ts +451 -0
  715. package/src/services/voice/corpus-generator.test.ts +201 -0
  716. package/src/services/voice/corpus-generator.ts +413 -0
  717. package/src/services/voice/diarization-error-rate.greedy.test.ts +140 -0
  718. package/src/services/voice/diarization-error-rate.test.ts +100 -0
  719. package/src/services/voice/diarization-error-rate.ts +249 -0
  720. package/src/services/voice/e2e-harness.der.test.ts +94 -0
  721. package/src/services/voice/e2e-harness.respond-eot-entity.test.ts +277 -0
  722. package/src/services/voice/e2e-harness.security-echo.test.ts +103 -0
  723. package/src/services/voice/e2e-harness.test.ts +182 -0
  724. package/src/services/voice/e2e-harness.ts +902 -0
  725. package/src/services/voice/eager-context-builder.ts +262 -0
  726. package/src/services/voice/echo-delay.test.ts +118 -0
  727. package/src/services/voice/echo-delay.ts +135 -0
  728. package/src/services/voice/echo-metrics.test.ts +17 -0
  729. package/src/services/voice/echo-metrics.ts +20 -0
  730. package/src/services/voice/echo-reference-buffer.test.ts +86 -0
  731. package/src/services/voice/echo-reference-buffer.ts +165 -0
  732. package/src/services/voice/eliza1-eot-scorer.ts +242 -0
  733. package/src/services/voice/embedding-server.ts +200 -0
  734. package/src/services/voice/embedding.test.ts +131 -0
  735. package/src/services/voice/embedding.ts +242 -0
  736. package/src/services/voice/emotion-attribution.test.ts +129 -0
  737. package/src/services/voice/emotion-attribution.ts +361 -0
  738. package/src/services/voice/engine-bridge-cancellation.test.ts +422 -0
  739. package/src/services/voice/engine-bridge-transcript-join.test.ts +278 -0
  740. package/src/services/voice/engine-bridge.test.ts +384 -0
  741. package/src/services/voice/engine-bridge.ts +2343 -0
  742. package/src/services/voice/eot-classifier-ggml.ts +569 -0
  743. package/src/services/voice/eot-classifier.test.ts +98 -0
  744. package/src/services/voice/eot-classifier.ts +422 -0
  745. package/src/services/voice/errors.ts +34 -0
  746. package/src/services/voice/expressive-tags.asr.test.ts +77 -0
  747. package/src/services/voice/expressive-tags.test.ts +102 -0
  748. package/src/services/voice/expressive-tags.ts +405 -0
  749. package/src/services/voice/ffi-bindings.test.ts +735 -0
  750. package/src/services/voice/ffi-bindings.ts +3387 -0
  751. package/src/services/voice/first-line-cache.ts +725 -0
  752. package/src/services/voice/fused-eot-scorer.ts +139 -0
  753. package/src/services/voice/index.ts +502 -0
  754. package/src/services/voice/kokoro/__tests__/kokoro-backend.test.ts +262 -0
  755. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.real.test.ts +236 -0
  756. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.test.ts +60 -0
  757. package/src/services/voice/kokoro/__tests__/kokoro-engine-discovery.test.ts +277 -0
  758. package/src/services/voice/kokoro/__tests__/kokoro-ffi-runtime.test.ts +235 -0
  759. package/src/services/voice/kokoro/__tests__/kokoro-runtime.test.ts +95 -0
  760. package/src/services/voice/kokoro/__tests__/phonemizer.test.ts +53 -0
  761. package/src/services/voice/kokoro/__tests__/runtime-selection.test.ts +67 -0
  762. package/src/services/voice/kokoro/__tests__/voices.test.ts +57 -0
  763. package/src/services/voice/kokoro/index.ts +79 -0
  764. package/src/services/voice/kokoro/kokoro-backend.ts +223 -0
  765. package/src/services/voice/kokoro/kokoro-engine-discovery.ts +177 -0
  766. package/src/services/voice/kokoro/kokoro-ffi-runtime.ts +233 -0
  767. package/src/services/voice/kokoro/kokoro-runtime.ts +170 -0
  768. package/src/services/voice/kokoro/phoneme-stream.ts +123 -0
  769. package/src/services/voice/kokoro/phonemizer.ts +344 -0
  770. package/src/services/voice/kokoro/pick-runtime.test.ts +91 -0
  771. package/src/services/voice/kokoro/pick-runtime.ts +130 -0
  772. package/src/services/voice/kokoro/runtime-selection.ts +64 -0
  773. package/src/services/voice/kokoro/types.ts +95 -0
  774. package/src/services/voice/kokoro/voice-presets.ts +129 -0
  775. package/src/services/voice/kokoro/voices.ts +64 -0
  776. package/src/services/voice/lifecycle.test.ts +315 -0
  777. package/src/services/voice/lifecycle.ts +301 -0
  778. package/src/services/voice/live-diarization-session.echo.test.ts +232 -0
  779. package/src/services/voice/live-diarization-session.ts +622 -0
  780. package/src/services/voice/metric-math.test.ts +61 -0
  781. package/src/services/voice/metric-math.ts +25 -0
  782. package/src/services/voice/mic-source.test.ts +210 -0
  783. package/src/services/voice/mic-source.ts +503 -0
  784. package/src/services/voice/nlms-echo-canceller.test.ts +244 -0
  785. package/src/services/voice/nlms-echo-canceller.ts +317 -0
  786. package/src/services/voice/optimistic-policy.power-source.test.ts +36 -0
  787. package/src/services/voice/optimistic-policy.test.ts +101 -0
  788. package/src/services/voice/optimistic-policy.ts +192 -0
  789. package/src/services/voice/optimistic-rollback.ts +343 -0
  790. package/src/services/voice/partial-stabilizer.test.ts +68 -0
  791. package/src/services/voice/partial-stabilizer.ts +140 -0
  792. package/src/services/voice/phoneme-tokenizer.ts +158 -0
  793. package/src/services/voice/phrase-cache.test.ts +242 -0
  794. package/src/services/voice/phrase-cache.ts +186 -0
  795. package/src/services/voice/phrase-chunker.test.ts +239 -0
  796. package/src/services/voice/phrase-chunker.ts +281 -0
  797. package/src/services/voice/pipeline-impls.l6.test.ts +110 -0
  798. package/src/services/voice/pipeline-impls.test.ts +292 -0
  799. package/src/services/voice/pipeline-impls.ts +315 -0
  800. package/src/services/voice/pipeline.ts +504 -0
  801. package/src/services/voice/prefill-client.ts +316 -0
  802. package/src/services/voice/prefix-preserving-queue.ts +162 -0
  803. package/src/services/voice/profile-store.ts +887 -0
  804. package/src/services/voice/real-audio-decode.test.ts +148 -0
  805. package/src/services/voice/research/VOICE_8785_ASSESSMENT.md +141 -0
  806. package/src/services/voice/research/VOICE_PIPELINE_RESEARCH_2026.md +117 -0
  807. package/src/services/voice/research/VOICE_VALIDATION_RUNBOOK.md +135 -0
  808. package/src/services/voice/ring-buffer.test.ts +129 -0
  809. package/src/services/voice/ring-buffer.ts +123 -0
  810. package/src/services/voice/rollback-queue.ts +74 -0
  811. package/src/services/voice/samantha-preset-placeholder.test.ts +97 -0
  812. package/src/services/voice/samantha-preset-placeholder.ts +148 -0
  813. package/src/services/voice/samantha-preset-regenerator.ts +393 -0
  814. package/src/services/voice/samantha-preset-regenerator.wav.test.ts +90 -0
  815. package/src/services/voice/scheduler.t2.test.ts +141 -0
  816. package/src/services/voice/scheduler.ts +927 -0
  817. package/src/services/voice/self-voice-imprint.test.ts +59 -0
  818. package/src/services/voice/self-voice-imprint.ts +102 -0
  819. package/src/services/voice/shared-resources.ts +343 -0
  820. package/src/services/voice/speaker/attribution-pipeline.test.ts +221 -0
  821. package/src/services/voice/speaker/attribution-pipeline.ts +449 -0
  822. package/src/services/voice/speaker/diarizer-fused.real.test.ts +100 -0
  823. package/src/services/voice/speaker/diarizer-fused.ts +154 -0
  824. package/src/services/voice/speaker/diarizer.ts +218 -0
  825. package/src/services/voice/speaker/encoder-fused.real.test.ts +113 -0
  826. package/src/services/voice/speaker/encoder-fused.ts +138 -0
  827. package/src/services/voice/speaker/encoder-ggml.test.ts +59 -0
  828. package/src/services/voice/speaker/encoder-ggml.ts +79 -0
  829. package/src/services/voice/speaker/encoder.ts +105 -0
  830. package/src/services/voice/speaker-imprint.test.ts +185 -0
  831. package/src/services/voice/speaker-imprint.ts +312 -0
  832. package/src/services/voice/speaker-preset-cache.test.ts +154 -0
  833. package/src/services/voice/speaker-preset-cache.ts +195 -0
  834. package/src/services/voice/streaming-asr/streaming-pipeline-adapter.ts +292 -0
  835. package/src/services/voice/system-audio-sink.test.ts +29 -0
  836. package/src/services/voice/system-audio-sink.ts +366 -0
  837. package/src/services/voice/transcriber.asr-backend.test.ts +76 -0
  838. package/src/services/voice/transcriber.test.ts +392 -0
  839. package/src/services/voice/transcriber.ts +704 -0
  840. package/src/services/voice/transcript-knowledge.test.ts +68 -0
  841. package/src/services/voice/transcript-knowledge.ts +75 -0
  842. package/src/services/voice/transcript-service.test.ts +195 -0
  843. package/src/services/voice/transcript-service.ts +205 -0
  844. package/src/services/voice/transcript-store.test.ts +189 -0
  845. package/src/services/voice/transcript-store.ts +164 -0
  846. package/src/services/voice/turn-controller.test.ts +575 -0
  847. package/src/services/voice/turn-controller.ts +596 -0
  848. package/src/services/voice/types.ts +699 -0
  849. package/src/services/voice/vad.test.ts +498 -0
  850. package/src/services/voice/vad.ts +832 -0
  851. package/src/services/voice/vad.v1-v4.test.ts +222 -0
  852. package/src/services/voice/voice-budget.test.ts +415 -0
  853. package/src/services/voice/voice-budget.ts +635 -0
  854. package/src/services/voice/voice-duet.test.ts +375 -0
  855. package/src/services/voice/voice-emotion-classifier.test.ts +210 -0
  856. package/src/services/voice/voice-emotion-classifier.ts +273 -0
  857. package/src/services/voice/voice-hardening.fuzz.test.ts +116 -0
  858. package/src/services/voice/voice-preload-predictor.test.ts +130 -0
  859. package/src/services/voice/voice-preload-predictor.ts +113 -0
  860. package/src/services/voice/voice-preset-format.fuzz.test.ts +89 -0
  861. package/src/services/voice/voice-preset-format.test.ts +75 -0
  862. package/src/services/voice/voice-preset-format.ts +713 -0
  863. package/src/services/voice/voice-preset-generator.test.ts +89 -0
  864. package/src/services/voice/voice-profile-artifact.test.ts +138 -0
  865. package/src/services/voice/voice-profile-artifact.ts +518 -0
  866. package/src/services/voice/voice-profile-routes.test.ts +429 -0
  867. package/src/services/voice/voice-profile-routes.ts +425 -0
  868. package/src/services/voice/voice-scenario.test.ts +159 -0
  869. package/src/services/voice/voice-scenario.ts +280 -0
  870. package/src/services/voice/voice-scenario.turn-helpers.test.ts +77 -0
  871. package/src/services/voice/voice-state-machine.ts +727 -0
  872. package/src/services/voice/voice-workbench-report.test.ts +168 -0
  873. package/src/services/voice/voice-workbench-report.ts +367 -0
  874. package/src/services/voice/voice-workbench.test.ts +158 -0
  875. package/src/services/voice/voice.test.ts +1070 -0
  876. package/src/services/voice/wake-word-ggml.ts +319 -0
  877. package/src/services/voice/wake-word.test.ts +298 -0
  878. package/src/services/voice/wake-word.ts +554 -0
  879. package/src/services/voice/wav-codec.fuzz.test.ts +59 -0
  880. package/src/services/voice/wav-codec.test.ts +32 -0
  881. package/src/services/voice/wav-codec.ts +101 -0
  882. package/src/services/voice/workbench-entrypoint.test.ts +55 -0
  883. package/src/services/voice/workbench-entrypoint.ts +88 -0
  884. package/src/services/voice/workbench-headless-runner.test.ts +162 -0
  885. package/src/services/voice/workbench-headless-runner.ts +396 -0
  886. package/src/services/voice/workbench-logic-services.test.ts +225 -0
  887. package/src/services/voice/workbench-logic-services.ts +184 -0
  888. package/src/services/voice/workbench-real-services.ts +629 -0
  889. package/src/services/voice/workbench-scenarios.ts +407 -0
  890. package/src/services/voice/wrap-with-first-line-cache.ts +267 -0
  891. package/src/services/voice-model-updater.ts +724 -0
  892. package/src/services/voice-prewarm.ts +51 -0
  893. package/src/voice-workbench.ts +71 -0
@@ -0,0 +1,1039 @@
1
+ /**
2
+ * Resumable GGUF downloader.
3
+ *
4
+ * Streams directly from HuggingFace to a staging file under
5
+ * `$STATE_DIR/local-inference/downloads/<id>.part`, then atomically moves
6
+ * it into `models/<id>.gguf` on success. On restart the staging file is
7
+ * still there; `resumeIfPossible` sends a Range request starting at the
8
+ * current partial size.
9
+ *
10
+ * Concurrency model: at most one download per model id. Callers use
11
+ * `subscribe()` to receive progress events; the service facade wires that
12
+ * to SSE.
13
+ *
14
+ * The runtime `fetch` follows HuggingFace redirects and still gives us a body
15
+ * stream that can be piped into a Node WriteStream.
16
+ */
17
+
18
+ import { randomUUID } from "node:crypto";
19
+ import fs from "node:fs";
20
+ import fsp from "node:fs/promises";
21
+ import path from "node:path";
22
+ import { Readable, type Writable } from "node:stream";
23
+ import { pipeline } from "node:stream/promises";
24
+ import { logger } from "@elizaos/core";
25
+ import { ensureDefaultAssignment } from "./assignments";
26
+ import {
27
+ buildHuggingFaceResolveUrl,
28
+ buildHuggingFaceResolveUrlForPath,
29
+ findCatalogModel,
30
+ isDefaultEligibleId,
31
+ resolveHfDownloadBase,
32
+ } from "./catalog";
33
+ import { deviceCapsFromProbe, probeHardware } from "./hardware";
34
+ import {
35
+ libStagedName,
36
+ resolveHostLibTargets,
37
+ selectBundleLibFiles,
38
+ } from "./lib-target";
39
+ import {
40
+ type Eliza1DeviceCaps,
41
+ type Eliza1FileEntry,
42
+ type Eliza1Files,
43
+ type Eliza1Manifest,
44
+ parseManifestOrThrow,
45
+ SUPPORTED_BACKENDS_BY_TIER,
46
+ } from "./manifest";
47
+ import {
48
+ downloadsStagingDir,
49
+ elizaModelsDir,
50
+ localInferenceRoot,
51
+ } from "./paths";
52
+ import { upsertElizaModel } from "./registry";
53
+ import {
54
+ type CatalogModel,
55
+ classifyCatalogModelRuntimeClass,
56
+ type DownloadEvent,
57
+ type DownloadJob,
58
+ type DownloadState,
59
+ type HardwareProbe,
60
+ type InstalledModel,
61
+ } from "./types";
62
+ import { hashFile } from "./verify";
63
+
64
+ interface ActiveJob {
65
+ job: DownloadJob;
66
+ abortController: AbortController;
67
+ stagingPath: string;
68
+ finalPath: string;
69
+ }
70
+
71
+ type DownloadListener = (event: DownloadEvent) => void;
72
+ type BundleFileKind = keyof Eliza1Files;
73
+
74
+ /**
75
+ * Thrown before any weight byte is fetched when an Eliza-1 bundle's manifest
76
+ * is incompatible with this device — wrong schema version, no overlapping
77
+ * verified backend, or a RAM budget that exceeds the device's memory. Per
78
+ * `packages/inference/AGENTS.md` §7 there is no "download anyway" path.
79
+ */
80
+ export class BundleIncompatibleError extends Error {
81
+ readonly code = "ELIZA1_BUNDLE_INCOMPATIBLE" as const;
82
+ constructor(message: string) {
83
+ super(message);
84
+ this.name = "BundleIncompatibleError";
85
+ }
86
+ }
87
+
88
+ /**
89
+ * One-time verify-on-device pass per `packages/inference/AGENTS.md` §7:
90
+ * load → 1-token text generation → 1-phrase voice generation → barge-in
91
+ * cancel. The downloader stays decoupled from the engine — the service
92
+ * layer injects this; when absent the bundle is materialized and registered
93
+ * but its `bundleVerifiedAt` stays unset and it does NOT auto-fill an empty
94
+ * default slot (an unverified bundle must not become the recommended
95
+ * default).
96
+ */
97
+ export type VerifyBundleOnDevice = (args: {
98
+ modelId: string;
99
+ bundleRoot: string;
100
+ manifestPath: string;
101
+ textGgufPath: string;
102
+ }) => Promise<void>;
103
+
104
+ export interface DownloaderOptions {
105
+ /** Override the device-capability probe (tests / headless environments). */
106
+ probeDeviceCaps?: () => Promise<Eliza1DeviceCaps>;
107
+ /** Verify-on-device smoke run; see {@link VerifyBundleOnDevice}. */
108
+ verifyOnDevice?: VerifyBundleOnDevice;
109
+ /** Override the hardware probe used by the disk-space preflight (tests). */
110
+ probeHardware?: () => Promise<HardwareProbe>;
111
+ }
112
+
113
+ async function defaultProbeDeviceCaps(): Promise<Eliza1DeviceCaps> {
114
+ return deviceCapsFromProbe(await probeHardware());
115
+ }
116
+
117
+ /**
118
+ * Reject bundles this device cannot run — runs against the manifest before
119
+ * any weight byte is fetched. Mirrors the publish-side `canSetAsDefault`
120
+ * device check, minus the `defaultEligible` flag (a user may explicitly
121
+ * install a non-default bundle, but only one the device can actually load).
122
+ */
123
+ function assertBundleInstallable(
124
+ manifest: Eliza1Manifest,
125
+ device: Eliza1DeviceCaps,
126
+ ): void {
127
+ // Schema version is enforced upstream by `parseManifestOrThrow` — the Zod
128
+ // schema only accepts the current `$schema` URL, so a manifest with a
129
+ // non-current schema version is rejected before we get here.
130
+ if (manifest.ramBudgetMb.min > device.ramMb) {
131
+ throw new BundleIncompatibleError(
132
+ `Eliza-1 bundle ${manifest.id} needs at least ${manifest.ramBudgetMb.min} MB RAM; this device has ${device.ramMb} MB`,
133
+ );
134
+ }
135
+ const tierBackends = new Set(SUPPORTED_BACKENDS_BY_TIER[manifest.tier]);
136
+ const usable = device.availableBackends.filter(
137
+ (b) =>
138
+ tierBackends.has(b) &&
139
+ manifest.kernels.verifiedBackends[b].status === "pass",
140
+ );
141
+ if (usable.length === 0) {
142
+ const verified = Object.entries(manifest.kernels.verifiedBackends)
143
+ .filter(([, v]) => v.status === "pass")
144
+ .map(([b]) => b);
145
+ throw new BundleIncompatibleError(
146
+ `Eliza-1 bundle ${manifest.id}: no required-kernel backend is available on this device. ` +
147
+ `bundle verified [${verified.join(", ") || "none"}], device has [${device.availableBackends.join(", ")}], tier ${manifest.tier} supports [${[...tierBackends].join(", ")}]`,
148
+ );
149
+ }
150
+ }
151
+
152
+ interface DownloadedFile {
153
+ path: string;
154
+ sizeBytes: number;
155
+ sha256: string;
156
+ }
157
+
158
+ const PROGRESS_THROTTLE_MS = 250;
159
+ const TERMINAL_DOWNLOADS_FILENAME = "download-status.json";
160
+ const TERMINAL_DOWNLOAD_LIMIT = 32;
161
+ /** Headroom kept free above the download size for the disk-space preflight. */
162
+ const DISK_HEADROOM_GB = 0.5;
163
+
164
+ interface TerminalDownloadsFile {
165
+ version: 1;
166
+ jobs: DownloadJob[];
167
+ }
168
+
169
+ async function* readFetchBody(
170
+ body: ReadableStream<Uint8Array>,
171
+ ): AsyncIterable<Buffer> {
172
+ const reader = body.getReader();
173
+ try {
174
+ while (true) {
175
+ const { done, value } = await reader.read();
176
+ if (done) return;
177
+ if (value) yield Buffer.from(value);
178
+ }
179
+ } finally {
180
+ reader.releaseLock();
181
+ }
182
+ }
183
+
184
+ function stagingFilename(modelId: string): string {
185
+ // Filename is derived deterministically so repeated download attempts
186
+ // reuse the same partial file and actually resume.
187
+ const safe = modelId.replace(/[^a-zA-Z0-9._-]/g, "_");
188
+ return `${safe}.part`;
189
+ }
190
+
191
+ function finalFilename(model: CatalogModel): string {
192
+ const safe = model.id.replace(/[^a-zA-Z0-9._-]/g, "_");
193
+ return `${safe}.gguf`;
194
+ }
195
+
196
+ /**
197
+ * GGUF files begin with the ASCII magic `GGUF`. A non-GGUF body (e.g. an HTML
198
+ * auth/redirect page returned with HTTP 200 by a gated repo) must never be
199
+ * registered as an installed model.
200
+ */
201
+ async function hasGgufMagic(filePath: string): Promise<boolean> {
202
+ try {
203
+ const handle = await fsp.open(filePath, "r");
204
+ try {
205
+ const buffer = Buffer.alloc(4);
206
+ await handle.read(buffer, 0, 4, 0);
207
+ return buffer.toString("ascii") === "GGUF";
208
+ } finally {
209
+ await handle.close();
210
+ }
211
+ } catch {
212
+ return false;
213
+ }
214
+ }
215
+
216
+ function bundleDirname(modelId: string): string {
217
+ const safe = modelId.replace(/[^a-zA-Z0-9._-]/g, "_");
218
+ return `${safe}.bundle`;
219
+ }
220
+
221
+ function bundleStagingFilename(modelId: string, filePath: string): string {
222
+ const safePath = filePath.replace(/[^a-zA-Z0-9._-]/g, "_");
223
+ return stagingFilename(`${modelId}__${safePath}`);
224
+ }
225
+
226
+ function bundleTargetPath(root: string, filePath: string): string {
227
+ if (
228
+ !filePath ||
229
+ path.isAbsolute(filePath) ||
230
+ /^[a-zA-Z]:[\\/]/.test(filePath)
231
+ ) {
232
+ throw new Error(`Invalid bundle file path: ${filePath}`);
233
+ }
234
+ const resolvedRoot = path.resolve(root);
235
+ const target = path.resolve(resolvedRoot, filePath);
236
+ if (
237
+ target !== resolvedRoot &&
238
+ !target.startsWith(`${resolvedRoot}${path.sep}`)
239
+ ) {
240
+ throw new Error(`Bundle file escapes install root: ${filePath}`);
241
+ }
242
+ return target;
243
+ }
244
+
245
+ function parseBundleManifestOrThrow(
246
+ input: unknown,
247
+ catalogEntry: CatalogModel,
248
+ ): Eliza1Manifest {
249
+ const manifest = parseManifestOrThrow(input);
250
+ if (manifest.id !== catalogEntry.id) {
251
+ throw new Error(
252
+ `Invalid Eliza-1 manifest: id ${manifest.id} does not match ${catalogEntry.id}`,
253
+ );
254
+ }
255
+ if (
256
+ !manifest.files.text.some((entry) => entry.path === catalogEntry.ggufFile)
257
+ ) {
258
+ throw new Error(
259
+ `Invalid Eliza-1 manifest: primary text file ${catalogEntry.ggufFile} is missing`,
260
+ );
261
+ }
262
+
263
+ return manifest;
264
+ }
265
+
266
+ function collectBundleFiles(
267
+ manifest: Eliza1Manifest,
268
+ ): Array<{ kind: BundleFileKind; entry: Eliza1FileEntry }> {
269
+ const seen = new Map<
270
+ string,
271
+ { kind: BundleFileKind; entry: Eliza1FileEntry }
272
+ >();
273
+ for (const kind of [
274
+ "text",
275
+ "voice",
276
+ "asr",
277
+ "vision",
278
+ "mtp",
279
+ "cache",
280
+ "embedding",
281
+ "vad",
282
+ "wakeword",
283
+ ] as const) {
284
+ for (const entry of manifest.files[kind] ?? []) {
285
+ const current = seen.get(entry.path);
286
+ if (current && current.entry.sha256 !== entry.sha256) {
287
+ throw new Error(
288
+ `Conflicting sha256 entries for bundle file ${entry.path}`,
289
+ );
290
+ }
291
+ seen.set(entry.path, { kind, entry });
292
+ }
293
+ }
294
+ return [...seen.values()];
295
+ }
296
+
297
+ async function ensureDirs(): Promise<void> {
298
+ await fsp.mkdir(downloadsStagingDir(), { recursive: true });
299
+ await fsp.mkdir(elizaModelsDir(), { recursive: true });
300
+ }
301
+
302
+ function terminalDownloadsPath(): string {
303
+ return path.join(localInferenceRoot(), TERMINAL_DOWNLOADS_FILENAME);
304
+ }
305
+
306
+ async function partialSize(stagingPath: string): Promise<number> {
307
+ try {
308
+ const stat = await fsp.stat(stagingPath);
309
+ return stat.isFile() ? stat.size : 0;
310
+ } catch {
311
+ return 0;
312
+ }
313
+ }
314
+
315
+ export class Downloader {
316
+ private readonly active = new Map<string, ActiveJob>();
317
+ private readonly terminal = new Map<string, DownloadJob>();
318
+ private readonly listeners = new Set<DownloadListener>();
319
+ private readonly lastEmit = new Map<string, number>();
320
+ private readonly probeDeviceCaps: () => Promise<Eliza1DeviceCaps>;
321
+ private readonly verifyOnDevice?: VerifyBundleOnDevice;
322
+ private readonly probeHardware: () => Promise<HardwareProbe>;
323
+
324
+ constructor(options: DownloaderOptions = {}) {
325
+ this.probeDeviceCaps = options.probeDeviceCaps ?? defaultProbeDeviceCaps;
326
+ this.verifyOnDevice = options.verifyOnDevice;
327
+ this.probeHardware = options.probeHardware ?? probeHardware;
328
+ this.loadTerminalDownloads();
329
+ }
330
+
331
+ subscribe(listener: DownloadListener): () => void {
332
+ this.listeners.add(listener);
333
+ return () => {
334
+ this.listeners.delete(listener);
335
+ };
336
+ }
337
+
338
+ snapshot(): DownloadJob[] {
339
+ const active = [...this.active.values()].map((a) => ({ ...a.job }));
340
+ const activeIds = new Set(active.map((job) => job.modelId));
341
+ const terminal = [...this.terminal.values()]
342
+ .filter((job) => !activeIds.has(job.modelId))
343
+ .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))
344
+ .map((job) => ({ ...job }));
345
+ return [...active, ...terminal];
346
+ }
347
+
348
+ isActive(modelId: string): boolean {
349
+ const current = this.active.get(modelId);
350
+ return (
351
+ !!current &&
352
+ (current.job.state === "queued" || current.job.state === "downloading")
353
+ );
354
+ }
355
+
356
+ /**
357
+ * Start a download for a curated Eliza-1 catalog entry. Object specs are
358
+ * accepted only for internal tests that decorate a known Eliza-1 id; ad-hoc
359
+ * Hugging Face / ModelScope specs are rejected before any reservation or
360
+ * network I/O.
361
+ */
362
+ async start(modelIdOrSpec: string | CatalogModel): Promise<DownloadJob> {
363
+ const catalogEntry =
364
+ typeof modelIdOrSpec === "string"
365
+ ? findCatalogModel(modelIdOrSpec)
366
+ : modelIdOrSpec;
367
+ if (!catalogEntry) {
368
+ throw new Error(
369
+ `Unknown model id: ${typeof modelIdOrSpec === "string" ? modelIdOrSpec : "(no id)"}`,
370
+ );
371
+ }
372
+ const curated = findCatalogModel(catalogEntry.id);
373
+ if (!curated || !isDefaultEligibleId(curated.id)) {
374
+ throw new Error(
375
+ "Custom model downloads are disabled; choose an Eliza-1 tier from the curated catalog.",
376
+ );
377
+ }
378
+ const modelId = catalogEntry.id;
379
+ this.clearTerminalDownload(modelId);
380
+
381
+ const existing = this.active.get(modelId);
382
+ if (
383
+ existing &&
384
+ (existing.job.state === "queued" || existing.job.state === "downloading")
385
+ ) {
386
+ return { ...existing.job };
387
+ }
388
+
389
+ // Reserve the slot SYNCHRONOUSLY — before any await — so a second
390
+ // concurrent start(sameId) sees it at the check above and returns the same
391
+ // job instead of racing a second write stream onto the same .part file
392
+ // (which corrupts the GGUF). All path derivation is synchronous; the resume
393
+ // offset is filled in after the reservation is held.
394
+ const stagingPath = path.join(
395
+ downloadsStagingDir(),
396
+ stagingFilename(modelId),
397
+ );
398
+ const finalPath = path.join(elizaModelsDir(), finalFilename(catalogEntry));
399
+
400
+ const job: DownloadJob = {
401
+ jobId: randomUUID(),
402
+ modelId,
403
+ state: "queued",
404
+ received: 0,
405
+ total: Math.round(catalogEntry.sizeGb * 1024 ** 3),
406
+ bytesPerSec: 0,
407
+ etaMs: null,
408
+ startedAt: new Date().toISOString(),
409
+ updatedAt: new Date().toISOString(),
410
+ };
411
+
412
+ const abortController = new AbortController();
413
+ const record: ActiveJob = {
414
+ job,
415
+ abortController,
416
+ stagingPath,
417
+ finalPath,
418
+ };
419
+ this.active.set(modelId, record);
420
+
421
+ // Slot is held — now safe to await; a concurrent caller short-circuits above.
422
+ await ensureDirs();
423
+ job.received = await partialSize(stagingPath);
424
+
425
+ // Fire-and-forget; errors are captured and emitted as a "failed" event.
426
+ void this.runJob(catalogEntry, record).catch(() => {
427
+ // `runJob` handles its own failure telemetry; we only need to swallow
428
+ // the unhandled-rejection here.
429
+ });
430
+
431
+ this.emit({ type: "progress", job: { ...job } });
432
+ return { ...job };
433
+ }
434
+
435
+ cancel(modelId: string): boolean {
436
+ const record = this.active.get(modelId);
437
+ if (!record) return false;
438
+ if (record.job.state !== "downloading" && record.job.state !== "queued") {
439
+ return false;
440
+ }
441
+ record.abortController.abort();
442
+ this.updateState(record, "cancelled");
443
+ this.rememberTerminalDownload(record.job);
444
+ this.emit({ type: "cancelled", job: { ...record.job } });
445
+ this.active.delete(modelId);
446
+ return true;
447
+ }
448
+
449
+ private emit(event: DownloadEvent): void {
450
+ for (const listener of this.listeners) {
451
+ try {
452
+ listener(event);
453
+ } catch {
454
+ // A bad listener must not kill the downloader; drop it silently.
455
+ this.listeners.delete(listener);
456
+ }
457
+ }
458
+ }
459
+
460
+ private updateState(record: ActiveJob, state: DownloadState): void {
461
+ record.job.state = state;
462
+ record.job.updatedAt = new Date().toISOString();
463
+ }
464
+
465
+ private loadTerminalDownloads(): void {
466
+ try {
467
+ const raw = fs.readFileSync(terminalDownloadsPath(), "utf8");
468
+ const parsed = JSON.parse(raw) as TerminalDownloadsFile;
469
+ if (parsed?.version !== 1 || !Array.isArray(parsed.jobs)) {
470
+ return;
471
+ }
472
+ for (const job of parsed.jobs) {
473
+ if (
474
+ job &&
475
+ typeof job.modelId === "string" &&
476
+ (job.state === "completed" ||
477
+ job.state === "failed" ||
478
+ job.state === "cancelled")
479
+ ) {
480
+ this.terminal.set(job.modelId, { ...job });
481
+ }
482
+ }
483
+ } catch {
484
+ // Missing or malformed terminal-download state should not block
485
+ // local inference. New terminal states will rewrite the file.
486
+ }
487
+ }
488
+
489
+ private persistTerminalDownloads(): void {
490
+ try {
491
+ fs.mkdirSync(localInferenceRoot(), { recursive: true });
492
+ const jobs = [...this.terminal.values()]
493
+ .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))
494
+ .slice(0, TERMINAL_DOWNLOAD_LIMIT);
495
+ const payload: TerminalDownloadsFile = { version: 1, jobs };
496
+ fs.writeFileSync(
497
+ terminalDownloadsPath(),
498
+ JSON.stringify(payload, null, 2),
499
+ "utf8",
500
+ );
501
+ } catch {
502
+ // Terminal status is useful for chat/UI telemetry but is not allowed to
503
+ // fail the download path.
504
+ }
505
+ }
506
+
507
+ private rememberTerminalDownload(job: DownloadJob): void {
508
+ this.terminal.set(job.modelId, { ...job });
509
+ const ordered = [...this.terminal.values()].sort((left, right) =>
510
+ right.updatedAt.localeCompare(left.updatedAt),
511
+ );
512
+ this.terminal.clear();
513
+ for (const terminalJob of ordered.slice(0, TERMINAL_DOWNLOAD_LIMIT)) {
514
+ this.terminal.set(terminalJob.modelId, terminalJob);
515
+ }
516
+ this.persistTerminalDownloads();
517
+ }
518
+
519
+ private clearTerminalDownload(modelId: string): void {
520
+ if (!this.terminal.delete(modelId)) return;
521
+ this.persistTerminalDownloads();
522
+ }
523
+
524
+ private throttleEmit(record: ActiveJob): void {
525
+ const now = Date.now();
526
+ const last = this.lastEmit.get(record.job.modelId) ?? 0;
527
+ if (now - last < PROGRESS_THROTTLE_MS) return;
528
+ this.lastEmit.set(record.job.modelId, now);
529
+ this.emit({ type: "progress", job: { ...record.job } });
530
+ }
531
+
532
+ /**
533
+ * Disk-space preflight. The remaining download must fit on the models
534
+ * volume with a small headroom margin, or we fail the job up front with an
535
+ * actionable message instead of letting it stream gigabytes and die with
536
+ * ENOSPC near the end. Best-effort: when free disk can't be probed we let
537
+ * the download proceed (the post-hoc ENOSPC handling still catches it).
538
+ */
539
+ private async assertDiskSpaceForJob(record: ActiveJob): Promise<void> {
540
+ const remainingBytes = Math.max(0, record.job.total - record.job.received);
541
+ if (remainingBytes <= 0) return;
542
+ let freeDiskGb: number | undefined;
543
+ try {
544
+ const probe = await this.probeHardware();
545
+ freeDiskGb = probe.freeDiskGb ?? probe.mobile?.freeStorageGb ?? undefined;
546
+ } catch {
547
+ return; // probe failure must never block a download
548
+ }
549
+ if (freeDiskGb === undefined) return;
550
+ const requiredGb = remainingBytes / 1024 ** 3 + DISK_HEADROOM_GB;
551
+ if (freeDiskGb < requiredGb) {
552
+ throw new Error(
553
+ `Not enough disk space: this download needs ~${requiredGb.toFixed(1)} GB ` +
554
+ `but only ${freeDiskGb.toFixed(1)} GB is free on the models volume. ` +
555
+ "Free up space and retry.",
556
+ );
557
+ }
558
+ }
559
+
560
+ private async runJob(
561
+ catalogEntry: CatalogModel,
562
+ record: ActiveJob,
563
+ ): Promise<void> {
564
+ try {
565
+ this.updateState(record, "downloading");
566
+ await this.assertDiskSpaceForJob(record);
567
+ if (catalogEntry.bundleManifestFile) {
568
+ await this.runBundleJob(catalogEntry, record);
569
+ return;
570
+ }
571
+
572
+ const url = buildHuggingFaceResolveUrl(catalogEntry);
573
+
574
+ const httpClient = await this.loadHttpClient();
575
+ const startByte = record.job.received;
576
+
577
+ const headers: Record<string, string> = {
578
+ "user-agent": "Eliza-LocalInference/1.0",
579
+ ...resolveHfDownloadBase().authHeader,
580
+ };
581
+ if (startByte > 0) {
582
+ headers.range = `bytes=${startByte}-`;
583
+ }
584
+
585
+ const response = await httpClient.request(url, {
586
+ method: "GET",
587
+ headers,
588
+ signal: record.abortController.signal,
589
+ });
590
+
591
+ if (response.statusCode >= 400) {
592
+ throw new Error(
593
+ `HTTP ${response.statusCode} from model hub for ${catalogEntry.hfRepo}`,
594
+ );
595
+ }
596
+ let effectiveStartByte = startByte;
597
+ if (effectiveStartByte > 0 && response.statusCode !== 206) {
598
+ effectiveStartByte = 0;
599
+ record.job.received = 0;
600
+ }
601
+
602
+ const contentLengthHeader = response.headers["content-length"];
603
+ const contentLength = Array.isArray(contentLengthHeader)
604
+ ? Number.parseInt(contentLengthHeader[0] ?? "0", 10)
605
+ : Number.parseInt(contentLengthHeader ?? "0", 10);
606
+ if (Number.isFinite(contentLength) && contentLength > 0) {
607
+ record.job.total = effectiveStartByte + contentLength;
608
+ }
609
+
610
+ const writeStream: Writable = fs.createWriteStream(record.stagingPath, {
611
+ flags: effectiveStartByte > 0 ? "a" : "w",
612
+ });
613
+
614
+ let lastSampleBytes = record.job.received;
615
+ let lastSampleAt = Date.now();
616
+
617
+ const bodyStream = Readable.from(response.body);
618
+ bodyStream.on("data", (chunk: Buffer) => {
619
+ record.job.received += chunk.length;
620
+
621
+ const now = Date.now();
622
+ const elapsed = now - lastSampleAt;
623
+ if (elapsed >= 1000) {
624
+ record.job.bytesPerSec =
625
+ ((record.job.received - lastSampleBytes) * 1000) / elapsed;
626
+ record.job.etaMs =
627
+ record.job.bytesPerSec > 0
628
+ ? ((record.job.total - record.job.received) * 1000) /
629
+ record.job.bytesPerSec
630
+ : null;
631
+ lastSampleAt = now;
632
+ lastSampleBytes = record.job.received;
633
+ }
634
+
635
+ this.throttleEmit(record);
636
+ });
637
+
638
+ await pipeline(bodyStream, writeStream);
639
+
640
+ await fsp.rename(record.stagingPath, record.finalPath);
641
+
642
+ // Integrity gate: a gated/private repo can answer HTTP 200 with an
643
+ // HTML login/error body, which would otherwise be renamed `<id>.gguf`
644
+ // and registered as an installed model. Reject anything that is not a
645
+ // real GGUF before it enters the registry, and point the user at the
646
+ // likely cause (gated bundles resolve through the Eliza Cloud HF
647
+ // proxy, so the device must be linked to Eliza Cloud).
648
+ if (!(await hasGgufMagic(record.finalPath))) {
649
+ await fsp.rm(record.finalPath, { force: true }).catch(() => undefined);
650
+ throw new Error(
651
+ `Downloaded file for ${catalogEntry.hfRepo ?? catalogEntry.id} is not a valid GGUF ` +
652
+ "(it looks like an auth/redirect page, not a model). If the bundle is gated, " +
653
+ "link this device to Eliza Cloud and retry — gated downloads route through " +
654
+ "the cloud HuggingFace proxy.",
655
+ );
656
+ }
657
+
658
+ const finalStat = await fsp.stat(record.finalPath);
659
+ // Compute SHA256 on commit so we have an integrity baseline. The
660
+ // chunk hasher we maintain during streaming gives the same result
661
+ // but would also have to handle resume-from-partial correctly; for
662
+ // a ~1-20 GB file a second disk pass at the end is simpler and
663
+ // robust. Measured at ~400 MB/s on an NVMe so even the 20 GB
664
+ // catalog entries finish in well under a minute.
665
+ const sha256 = await hashFile(record.finalPath);
666
+
667
+ const installed: InstalledModel = {
668
+ id: catalogEntry.id,
669
+ displayName: catalogEntry.displayName,
670
+ path: record.finalPath,
671
+ sizeBytes: finalStat.size,
672
+ hfRepo: catalogEntry.hfRepo,
673
+ installedAt: new Date().toISOString(),
674
+ lastUsedAt: null,
675
+ source: "eliza-download",
676
+ sha256,
677
+ lastVerifiedAt: new Date().toISOString(),
678
+ runtimeClass: classifyCatalogModelRuntimeClass(catalogEntry),
679
+ ...(catalogEntry.runtimeRole
680
+ ? { runtimeRole: catalogEntry.runtimeRole }
681
+ : {}),
682
+ };
683
+ await upsertElizaModel(installed);
684
+
685
+ // First-light convenience: only default-eligible Eliza-1 downloads
686
+ // can fill empty slots.
687
+ if (isDefaultEligibleId(installed.id)) {
688
+ await ensureDefaultAssignment(installed.id);
689
+ }
690
+
691
+ this.updateState(record, "completed");
692
+ record.job.received = finalStat.size;
693
+ record.job.total = finalStat.size;
694
+ this.rememberTerminalDownload(record.job);
695
+ this.emit({ type: "completed", job: { ...record.job } });
696
+ } catch (err) {
697
+ if (record.abortController.signal.aborted) {
698
+ this.updateState(record, "cancelled");
699
+ this.rememberTerminalDownload(record.job);
700
+ this.emit({ type: "cancelled", job: { ...record.job } });
701
+ } else {
702
+ this.updateState(record, "failed");
703
+ record.job.error = err instanceof Error ? err.message : String(err);
704
+ this.rememberTerminalDownload(record.job);
705
+ this.emit({ type: "failed", job: { ...record.job } });
706
+ }
707
+ } finally {
708
+ this.active.delete(record.job.modelId);
709
+ }
710
+ }
711
+
712
+ private async runBundleJob(
713
+ catalogEntry: CatalogModel,
714
+ record: ActiveJob,
715
+ ): Promise<void> {
716
+ if (!catalogEntry.bundleManifestFile) {
717
+ throw new Error(
718
+ `[local-inference] ${catalogEntry.id} has no bundle manifest`,
719
+ );
720
+ }
721
+
722
+ const bundleRoot = path.join(
723
+ elizaModelsDir(),
724
+ bundleDirname(catalogEntry.id),
725
+ );
726
+ await fsp.mkdir(bundleRoot, { recursive: true });
727
+
728
+ const manifestPath = bundleTargetPath(
729
+ bundleRoot,
730
+ catalogEntry.bundleManifestFile,
731
+ );
732
+ const manifestDownloaded = await this.downloadRemotePath(
733
+ catalogEntry,
734
+ catalogEntry.bundleManifestFile,
735
+ path.join(
736
+ downloadsStagingDir(),
737
+ bundleStagingFilename(catalogEntry.id, catalogEntry.bundleManifestFile),
738
+ ),
739
+ manifestPath,
740
+ record,
741
+ 0,
742
+ catalogEntry.bundleManifestSha256,
743
+ );
744
+
745
+ const manifest = parseBundleManifestOrThrow(
746
+ JSON.parse(await fsp.readFile(manifestPath, "utf8")),
747
+ catalogEntry,
748
+ );
749
+
750
+ // §7: schema version, RAM budget, and kernel-backend availability are
751
+ // checked against this device BEFORE any weight byte is fetched. An
752
+ // incompatible bundle aborts here — there is no "download anyway" path.
753
+ const deviceCaps = await this.probeDeviceCaps();
754
+ assertBundleInstallable(manifest, deviceCaps);
755
+
756
+ let completedBytes = manifestDownloaded.sizeBytes;
757
+ const downloaded = new Map<string, DownloadedFile>();
758
+ for (const { entry } of collectBundleFiles(manifest)) {
759
+ const finalPath = bundleTargetPath(bundleRoot, entry.path);
760
+ const result = await this.downloadRemotePath(
761
+ catalogEntry,
762
+ entry.path,
763
+ path.join(
764
+ downloadsStagingDir(),
765
+ bundleStagingFilename(catalogEntry.id, entry.path),
766
+ ),
767
+ finalPath,
768
+ record,
769
+ completedBytes,
770
+ entry.sha256,
771
+ );
772
+ downloaded.set(entry.path, result);
773
+ completedBytes += result.sizeBytes;
774
+ record.job.received = completedBytes;
775
+ record.job.total = Math.max(record.job.total, completedBytes);
776
+ this.throttleEmit(record);
777
+ }
778
+
779
+ // Fused-lib bundle delivery (#9105): fetch the host-matching native-lib
780
+ // SET into `<bundleRoot>/lib/`, which the desktop FFI runtime resolves
781
+ // with no env wiring (`resolveFusedLibraryPath` path #2). Only entries
782
+ // whose `target` matches the host are fetched; no `lib[]` / no host match
783
+ // ⇒ skipped (the runtime falls back to a host-staged lib dir, else cloud).
784
+ // Mobile resolves to no targets — phones ship the lib natively.
785
+ // Prefer the GPU lib target when this device actually has a CUDA backend
786
+ // (NVIDIA), so a CUDA-capable host pulls the accelerated set when the
787
+ // bundle hosts one; everything else takes the CPU baseline. macOS arm64
788
+ // already resolves to the metal set (which carries the CPU fallback).
789
+ const preferGpu = deviceCaps.availableBackends.includes("cuda");
790
+ const selectedLib = selectBundleLibFiles(
791
+ manifest,
792
+ resolveHostLibTargets({ preferGpu }),
793
+ );
794
+ if (selectedLib) {
795
+ for (const libEntry of selectedLib.files) {
796
+ const relPath = `lib/${libStagedName(libEntry)}`;
797
+ const result = await this.downloadRemotePath(
798
+ catalogEntry,
799
+ libEntry.path,
800
+ path.join(
801
+ downloadsStagingDir(),
802
+ bundleStagingFilename(catalogEntry.id, relPath),
803
+ ),
804
+ bundleTargetPath(bundleRoot, relPath),
805
+ record,
806
+ completedBytes,
807
+ libEntry.sha256,
808
+ );
809
+ completedBytes += result.sizeBytes;
810
+ record.job.received = completedBytes;
811
+ record.job.total = Math.max(record.job.total, completedBytes);
812
+ this.throttleEmit(record);
813
+ }
814
+ logger.info(
815
+ `[local-inference] staged fused lib set for ${catalogEntry.id} ` +
816
+ `(target=${selectedLib.target}, ${selectedLib.files.length} file(s)) → ${bundleRoot}/lib`,
817
+ );
818
+ }
819
+
820
+ const textEntry = manifest.files.text.find(
821
+ (entry) => entry.path === catalogEntry.ggufFile,
822
+ );
823
+ if (!textEntry) {
824
+ throw new Error(
825
+ `[local-inference] Bundle missing primary text file ${catalogEntry.ggufFile}`,
826
+ );
827
+ }
828
+ const textFile = downloaded.get(textEntry.path);
829
+ if (!textFile) {
830
+ throw new Error(
831
+ `[local-inference] Bundle did not install text file ${textEntry.path}`,
832
+ );
833
+ }
834
+
835
+ // §7: materialize the bundle, then run the one-time verify-on-device
836
+ // pass before the bundle is treated as ready. The hook is injected by
837
+ // the service layer so the downloader stays decoupled from the engine.
838
+ // When no hook is wired, `bundleVerifiedAt` stays unset and the bundle
839
+ // is registered but does NOT auto-fill an empty default slot.
840
+ let bundleVerifiedAt: string | undefined;
841
+ if (this.verifyOnDevice) {
842
+ await this.verifyOnDevice({
843
+ modelId: catalogEntry.id,
844
+ bundleRoot,
845
+ manifestPath,
846
+ textGgufPath: textFile.path,
847
+ });
848
+ bundleVerifiedAt = new Date().toISOString();
849
+ }
850
+
851
+ const now = new Date().toISOString();
852
+ const bundleMeta = {
853
+ bundleRoot,
854
+ manifestPath,
855
+ manifestSha256: manifestDownloaded.sha256,
856
+ bundleVersion: manifest.version,
857
+ bundleSizeBytes: completedBytes,
858
+ ...(bundleVerifiedAt ? { bundleVerifiedAt } : {}),
859
+ };
860
+
861
+ const installed: InstalledModel = {
862
+ id: catalogEntry.id,
863
+ displayName: catalogEntry.displayName,
864
+ path: textFile.path,
865
+ sizeBytes: textFile.sizeBytes,
866
+ hfRepo: catalogEntry.hfRepo,
867
+ installedAt: now,
868
+ lastUsedAt: null,
869
+ source: "eliza-download",
870
+ sha256: textFile.sha256,
871
+ lastVerifiedAt: now,
872
+ runtimeClass: classifyCatalogModelRuntimeClass(catalogEntry),
873
+ ...bundleMeta,
874
+ };
875
+ await upsertElizaModel(installed);
876
+
877
+ // An empty default slot is filled only after the on-device verify pass
878
+ // succeeds. Without a verify hook the bundle is installed and visible,
879
+ // but it is not allowed to auto-fill defaults.
880
+ if (isDefaultEligibleId(installed.id) && bundleVerifiedAt !== undefined) {
881
+ await ensureDefaultAssignment(installed.id);
882
+ }
883
+
884
+ this.updateState(record, "completed");
885
+ record.job.received = completedBytes;
886
+ record.job.total = completedBytes;
887
+ this.rememberTerminalDownload(record.job);
888
+ this.emit({ type: "completed", job: { ...record.job } });
889
+ }
890
+
891
+ private async downloadRemotePath(
892
+ catalogEntry: CatalogModel,
893
+ remotePath: string,
894
+ stagingPath: string,
895
+ finalPath: string,
896
+ record: ActiveJob,
897
+ baseBytes: number,
898
+ expectedSha256?: string,
899
+ ): Promise<DownloadedFile> {
900
+ if (expectedSha256) {
901
+ try {
902
+ const stat = await fsp.stat(finalPath);
903
+ if (stat.isFile()) {
904
+ const currentSha256 = await hashFile(finalPath);
905
+ if (currentSha256 === expectedSha256) {
906
+ record.job.received = baseBytes + stat.size;
907
+ return {
908
+ path: finalPath,
909
+ sizeBytes: stat.size,
910
+ sha256: currentSha256,
911
+ };
912
+ }
913
+ await fsp.rm(finalPath, { force: true });
914
+ }
915
+ } catch {
916
+ // Missing files are downloaded below; unreadable stale files are
917
+ // treated as invalid and replaced by the fresh bundle artifact.
918
+ }
919
+ } else {
920
+ await fsp.rm(stagingPath, { force: true }).catch(() => undefined);
921
+ }
922
+
923
+ await fsp.mkdir(path.dirname(finalPath), { recursive: true });
924
+ await fsp.mkdir(path.dirname(stagingPath), { recursive: true });
925
+
926
+ let startByte = expectedSha256 ? await partialSize(stagingPath) : 0;
927
+ record.job.received = baseBytes + startByte;
928
+
929
+ const url = buildHuggingFaceResolveUrlForPath(catalogEntry, remotePath);
930
+ const headers: Record<string, string> = {
931
+ "user-agent": "Eliza-LocalInference/1.0",
932
+ ...resolveHfDownloadBase().authHeader,
933
+ };
934
+ if (startByte > 0) {
935
+ headers.range = `bytes=${startByte}-`;
936
+ }
937
+
938
+ const httpClient = await this.loadHttpClient();
939
+ const response = await httpClient.request(url, {
940
+ method: "GET",
941
+ headers,
942
+ signal: record.abortController.signal,
943
+ });
944
+
945
+ if (response.statusCode >= 400) {
946
+ throw new Error(
947
+ `HTTP ${response.statusCode} from model hub for ${catalogEntry.hfRepo}/${remotePath}`,
948
+ );
949
+ }
950
+ if (startByte > 0 && response.statusCode !== 206) {
951
+ startByte = 0;
952
+ record.job.received = baseBytes;
953
+ }
954
+
955
+ const contentLengthHeader = response.headers["content-length"];
956
+ const contentLength = Array.isArray(contentLengthHeader)
957
+ ? Number.parseInt(contentLengthHeader[0] ?? "0", 10)
958
+ : Number.parseInt(contentLengthHeader ?? "0", 10);
959
+ if (Number.isFinite(contentLength) && contentLength > 0) {
960
+ record.job.total = Math.max(
961
+ record.job.total,
962
+ baseBytes + startByte + contentLength,
963
+ );
964
+ }
965
+
966
+ const writeStream: Writable = fs.createWriteStream(stagingPath, {
967
+ flags: startByte > 0 ? "a" : "w",
968
+ });
969
+
970
+ let lastSampleBytes = record.job.received;
971
+ let lastSampleAt = Date.now();
972
+ const bodyStream = Readable.from(response.body);
973
+ bodyStream.on("data", (chunk: Buffer) => {
974
+ record.job.received += chunk.length;
975
+
976
+ const now = Date.now();
977
+ const elapsed = now - lastSampleAt;
978
+ if (elapsed >= 1000) {
979
+ record.job.bytesPerSec =
980
+ ((record.job.received - lastSampleBytes) * 1000) / elapsed;
981
+ record.job.etaMs =
982
+ record.job.bytesPerSec > 0
983
+ ? ((record.job.total - record.job.received) * 1000) /
984
+ record.job.bytesPerSec
985
+ : null;
986
+ lastSampleAt = now;
987
+ lastSampleBytes = record.job.received;
988
+ }
989
+
990
+ this.throttleEmit(record);
991
+ });
992
+
993
+ await pipeline(bodyStream, writeStream);
994
+ await fsp.rename(stagingPath, finalPath);
995
+
996
+ const stat = await fsp.stat(finalPath);
997
+ const sha256 = await hashFile(finalPath);
998
+ if (expectedSha256 && sha256 !== expectedSha256) {
999
+ await fsp.rm(finalPath, { force: true });
1000
+ throw new Error(`SHA256 mismatch for bundle file ${remotePath}`);
1001
+ }
1002
+ return { path: finalPath, sizeBytes: stat.size, sha256 };
1003
+ }
1004
+
1005
+ private async loadHttpClient(): Promise<{
1006
+ request: (
1007
+ url: string,
1008
+ options: {
1009
+ method: string;
1010
+ headers: Record<string, string>;
1011
+ signal: AbortSignal;
1012
+ },
1013
+ ) => Promise<{
1014
+ statusCode: number;
1015
+ headers: Record<string, string | string[] | undefined>;
1016
+ body: AsyncIterable<Buffer>;
1017
+ }>;
1018
+ }> {
1019
+ const fetchImpl = globalThis.fetch;
1020
+ return {
1021
+ request: async (url, options) => {
1022
+ const response = await fetchImpl(url, {
1023
+ method: options.method,
1024
+ headers: options.headers,
1025
+ signal: options.signal,
1026
+ redirect: "follow",
1027
+ });
1028
+ if (!response.body) {
1029
+ throw new Error(`Empty response body from ${url}`);
1030
+ }
1031
+ return {
1032
+ statusCode: response.status,
1033
+ headers: Object.fromEntries(response.headers.entries()),
1034
+ body: readFetchBody(response.body),
1035
+ };
1036
+ },
1037
+ };
1038
+ }
1039
+ }