@elizaos/plugin-local-inference 2.0.0-beta.1 → 2.0.11-beta.7

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 (676) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/package.json +81 -15
  4. package/src/actions/generate-media.d.ts +59 -0
  5. package/src/actions/generate-media.d.ts.map +1 -0
  6. package/src/actions/generate-media.ts +647 -0
  7. package/src/actions/identify-speaker.d.ts +23 -0
  8. package/src/actions/identify-speaker.d.ts.map +1 -0
  9. package/src/actions/identify-speaker.ts +171 -0
  10. package/src/adapters/capacitor-llama/__tests__/compat-behavior.test.ts +218 -0
  11. package/src/adapters/capacitor-llama/__tests__/index.test.ts +68 -0
  12. package/src/adapters/capacitor-llama/__tests__/structured-output.test.ts +215 -0
  13. package/src/adapters/capacitor-llama/__tests__/text-streaming.test.ts +174 -0
  14. package/src/adapters/capacitor-llama/environment.ts +71 -0
  15. package/src/adapters/capacitor-llama/index.browser.ts +83 -0
  16. package/src/adapters/capacitor-llama/index.ts +807 -0
  17. package/src/adapters/capacitor-llama/loader.ts +109 -0
  18. package/src/adapters/capacitor-llama/structured-output.ts +165 -0
  19. package/src/adapters/capacitor-llama/text-streaming.ts +227 -0
  20. package/src/adapters/capacitor-llama/types.ts +374 -0
  21. package/src/backends/apple-foundation.ts +127 -0
  22. package/src/index.d.ts +7 -0
  23. package/src/index.d.ts.map +1 -0
  24. package/src/index.ts +54 -0
  25. package/src/local-inference-routes.d.ts +38 -0
  26. package/src/local-inference-routes.d.ts.map +1 -0
  27. package/src/local-inference-routes.test.ts +344 -0
  28. package/src/local-inference-routes.ts +1543 -0
  29. package/src/provider.d.ts +21 -0
  30. package/src/provider.d.ts.map +1 -0
  31. package/src/provider.ts +1171 -0
  32. package/src/routes/compat-helpers.d.ts +18 -0
  33. package/src/routes/compat-helpers.d.ts.map +1 -0
  34. package/src/routes/compat-helpers.ts +274 -0
  35. package/src/routes/family-member-route.d.ts +62 -0
  36. package/src/routes/family-member-route.d.ts.map +1 -0
  37. package/src/routes/family-member-route.ts +353 -0
  38. package/src/routes/index.d.ts +19 -0
  39. package/src/routes/index.d.ts.map +1 -0
  40. package/src/routes/index.ts +60 -0
  41. package/src/routes/live-diarization-route.d.ts +26 -0
  42. package/src/routes/live-diarization-route.d.ts.map +1 -0
  43. package/src/routes/live-diarization-route.test.ts +213 -0
  44. package/src/routes/live-diarization-route.ts +122 -0
  45. package/src/routes/local-inference-asr-route.d.ts +4 -0
  46. package/src/routes/local-inference-asr-route.d.ts.map +1 -0
  47. package/src/routes/local-inference-asr-route.test.ts +190 -0
  48. package/src/routes/local-inference-asr-route.ts +213 -0
  49. package/src/routes/local-inference-compat-routes.d.ts +16 -0
  50. package/src/routes/local-inference-compat-routes.d.ts.map +1 -0
  51. package/src/routes/local-inference-compat-routes.test.ts +423 -0
  52. package/src/routes/local-inference-compat-routes.ts +782 -0
  53. package/src/routes/local-inference-tts-route.d.ts +7 -0
  54. package/src/routes/local-inference-tts-route.d.ts.map +1 -0
  55. package/src/routes/local-inference-tts-route.test.ts +179 -0
  56. package/src/routes/local-inference-tts-route.ts +230 -0
  57. package/src/routes/voice-first-run-routes.d.ts +62 -0
  58. package/src/routes/voice-first-run-routes.d.ts.map +1 -0
  59. package/src/routes/voice-first-run-routes.ts +524 -0
  60. package/src/routes/voice-models-routes.d.ts +62 -0
  61. package/src/routes/voice-models-routes.d.ts.map +1 -0
  62. package/src/routes/voice-models-routes.ts +554 -0
  63. package/src/routes/voice-profile-plugin-routes.d.ts +19 -0
  64. package/src/routes/voice-profile-plugin-routes.d.ts.map +1 -0
  65. package/src/routes/voice-profile-plugin-routes.ts +138 -0
  66. package/src/routes/voice-profiles-management-routes.d.ts +52 -0
  67. package/src/routes/voice-profiles-management-routes.d.ts.map +1 -0
  68. package/src/routes/voice-profiles-management-routes.ts +476 -0
  69. package/src/routes/voice-speaker-profile-routes.d.ts +57 -0
  70. package/src/routes/voice-speaker-profile-routes.d.ts.map +1 -0
  71. package/src/routes/voice-speaker-profile-routes.ts +199 -0
  72. package/src/runtime/aosp-llama-loader-selection.test.ts +80 -0
  73. package/src/runtime/capacitor-llama.d.ts +25 -0
  74. package/src/runtime/embedding-manager-support.d.ts +77 -0
  75. package/src/runtime/embedding-manager-support.d.ts.map +1 -0
  76. package/src/runtime/embedding-manager-support.ts +497 -0
  77. package/src/runtime/embedding-presets.d.ts +16 -0
  78. package/src/runtime/embedding-presets.d.ts.map +1 -0
  79. package/src/runtime/embedding-presets.ts +81 -0
  80. package/src/runtime/embedding-warmup-policy.d.ts +14 -0
  81. package/src/runtime/embedding-warmup-policy.d.ts.map +1 -0
  82. package/src/runtime/embedding-warmup-policy.test.ts +53 -0
  83. package/src/runtime/embedding-warmup-policy.ts +48 -0
  84. package/src/runtime/ensure-local-inference-handler.d.ts +53 -0
  85. package/src/runtime/ensure-local-inference-handler.d.ts.map +1 -0
  86. package/src/runtime/ensure-local-inference-handler.test.ts +528 -0
  87. package/src/runtime/ensure-local-inference-handler.ts +1398 -0
  88. package/src/runtime/index.d.ts +14 -0
  89. package/src/runtime/index.d.ts.map +1 -0
  90. package/src/runtime/index.ts +27 -0
  91. package/src/runtime/mobile-local-inference-gate.d.ts +31 -0
  92. package/src/runtime/mobile-local-inference-gate.d.ts.map +1 -0
  93. package/src/runtime/mobile-local-inference-gate.test.ts +69 -0
  94. package/src/runtime/mobile-local-inference-gate.ts +44 -0
  95. package/src/runtime/voice-entity-binding.d.ts +103 -0
  96. package/src/runtime/voice-entity-binding.d.ts.map +1 -0
  97. package/src/runtime/voice-entity-binding.transcript.test.ts +69 -0
  98. package/src/runtime/voice-entity-binding.ts +328 -0
  99. package/src/services/README.md +71 -0
  100. package/src/services/__tests__/backend-selector.test.ts +101 -0
  101. package/src/services/__tests__/checkpoint-manager.test.ts +376 -0
  102. package/src/services/__tests__/gpu-autotune.test.ts +400 -0
  103. package/src/services/__tests__/llm-streaming-binding.test.ts +85 -0
  104. package/src/services/__tests__/planner-grammar.test.ts +372 -0
  105. package/src/services/__tests__/runtime-target.test.ts +176 -0
  106. package/src/services/active-model-switch-rollback.test.ts +183 -0
  107. package/src/services/active-model.d.ts +282 -0
  108. package/src/services/active-model.d.ts.map +1 -0
  109. package/src/services/active-model.ts +1213 -0
  110. package/src/services/asr/errors.d.ts +21 -0
  111. package/src/services/asr/errors.d.ts.map +1 -0
  112. package/src/services/asr/errors.ts +50 -0
  113. package/src/services/asr/hash.d.ts +28 -0
  114. package/src/services/asr/hash.d.ts.map +1 -0
  115. package/src/services/asr/hash.ts +49 -0
  116. package/src/services/asr/index.d.ts +76 -0
  117. package/src/services/asr/index.d.ts.map +1 -0
  118. package/src/services/asr/index.ts +178 -0
  119. package/src/services/asr/types.d.ts +91 -0
  120. package/src/services/asr/types.d.ts.map +1 -0
  121. package/src/services/asr/types.ts +95 -0
  122. package/src/services/assignments.d.ts +71 -0
  123. package/src/services/assignments.d.ts.map +1 -0
  124. package/src/services/assignments.test.ts +80 -0
  125. package/src/services/assignments.ts +230 -0
  126. package/src/services/backend-selector.ts +95 -0
  127. package/src/services/backend.d.ts +346 -0
  128. package/src/services/backend.d.ts.map +1 -0
  129. package/src/services/backend.ts +612 -0
  130. package/src/services/bundled-models.d.ts +34 -0
  131. package/src/services/bundled-models.d.ts.map +1 -0
  132. package/src/services/bundled-models.ts +129 -0
  133. package/src/services/cache-bridge.d.ts +206 -0
  134. package/src/services/cache-bridge.d.ts.map +1 -0
  135. package/src/services/cache-bridge.test.ts +516 -0
  136. package/src/services/cache-bridge.ts +423 -0
  137. package/src/services/catalog.d.ts +10 -0
  138. package/src/services/catalog.d.ts.map +1 -0
  139. package/src/services/catalog.test.ts +240 -0
  140. package/src/services/catalog.ts +27 -0
  141. package/src/services/checkpoint-client.d.ts +109 -0
  142. package/src/services/checkpoint-client.d.ts.map +1 -0
  143. package/src/services/checkpoint-client.ts +258 -0
  144. package/src/services/checkpoint-manager.ts +474 -0
  145. package/src/services/cloud-fallback.d.ts +102 -0
  146. package/src/services/cloud-fallback.d.ts.map +1 -0
  147. package/src/services/cloud-fallback.ts +230 -0
  148. package/src/services/conversation-registry.d.ts +142 -0
  149. package/src/services/conversation-registry.d.ts.map +1 -0
  150. package/src/services/conversation-registry.test.ts +235 -0
  151. package/src/services/conversation-registry.ts +264 -0
  152. package/src/services/desktop-fused-ffi-backend-runtime.d.ts +92 -0
  153. package/src/services/desktop-fused-ffi-backend-runtime.d.ts.map +1 -0
  154. package/src/services/desktop-fused-ffi-backend-runtime.ts +333 -0
  155. package/src/services/device-bridge.d.ts +188 -0
  156. package/src/services/device-bridge.d.ts.map +1 -0
  157. package/src/services/device-bridge.ts +1237 -0
  158. package/src/services/device-resource-metrics.d.ts +149 -0
  159. package/src/services/device-resource-metrics.d.ts.map +1 -0
  160. package/src/services/device-resource-metrics.test.ts +98 -0
  161. package/src/services/device-resource-metrics.ts +346 -0
  162. package/src/services/device-tier.d.ts +115 -0
  163. package/src/services/device-tier.d.ts.map +1 -0
  164. package/src/services/device-tier.test.ts +371 -0
  165. package/src/services/device-tier.ts +410 -0
  166. package/src/services/downloader.d.ts +82 -0
  167. package/src/services/downloader.d.ts.map +1 -0
  168. package/src/services/downloader.test.ts +724 -0
  169. package/src/services/downloader.ts +899 -0
  170. package/src/services/engine-direct-bundle.test.ts +58 -0
  171. package/src/services/engine-streaming.test.ts +80 -0
  172. package/src/services/engine.d.ts +534 -0
  173. package/src/services/engine.d.ts.map +1 -0
  174. package/src/services/engine.ts +1891 -0
  175. package/src/services/ensure-local-artifacts.integration.test.ts +273 -0
  176. package/src/services/ensure-local-artifacts.test.ts +368 -0
  177. package/src/services/ensure-local-artifacts.ts +351 -0
  178. package/src/services/external-scanner.d.ts +17 -0
  179. package/src/services/external-scanner.d.ts.map +1 -0
  180. package/src/services/external-scanner.ts +312 -0
  181. package/src/services/ffi-llm-mock.ts +354 -0
  182. package/src/services/ffi-llm-streaming-abi.ts +442 -0
  183. package/src/services/ffi-streaming-backend.d.ts +180 -0
  184. package/src/services/ffi-streaming-backend.d.ts.map +1 -0
  185. package/src/services/ffi-streaming-backend.ts +382 -0
  186. package/src/services/ffi-streaming-runner.d.ts +122 -0
  187. package/src/services/ffi-streaming-runner.d.ts.map +1 -0
  188. package/src/services/ffi-streaming-runner.test.ts +60 -0
  189. package/src/services/ffi-streaming-runner.ts +354 -0
  190. package/src/services/ffi-unload-ordering.test.ts +162 -0
  191. package/src/services/gpu-autotune.ts +534 -0
  192. package/src/services/gpu-detect.ts +139 -0
  193. package/src/services/handler-registry.d.ts +72 -0
  194. package/src/services/handler-registry.d.ts.map +1 -0
  195. package/src/services/handler-registry.ts +240 -0
  196. package/src/services/hardware.d.ts +63 -0
  197. package/src/services/hardware.d.ts.map +1 -0
  198. package/src/services/hardware.test.ts +183 -0
  199. package/src/services/hardware.ts +404 -0
  200. package/src/services/hf-search.d.ts +26 -0
  201. package/src/services/hf-search.d.ts.map +1 -0
  202. package/src/services/hf-search.test.ts +69 -0
  203. package/src/services/hf-search.ts +420 -0
  204. package/src/services/image-description-runtime.d.ts +14 -0
  205. package/src/services/image-description-runtime.d.ts.map +1 -0
  206. package/src/services/image-description-runtime.test.ts +61 -0
  207. package/src/services/image-description-runtime.ts +118 -0
  208. package/src/services/imagegen/aosp-unavailable.d.ts +134 -0
  209. package/src/services/imagegen/aosp-unavailable.d.ts.map +1 -0
  210. package/src/services/imagegen/aosp-unavailable.ts +229 -0
  211. package/src/services/imagegen/backend-selector.d.ts +118 -0
  212. package/src/services/imagegen/backend-selector.d.ts.map +1 -0
  213. package/src/services/imagegen/backend-selector.ts +281 -0
  214. package/src/services/imagegen/coreml-unavailable.d.ts +105 -0
  215. package/src/services/imagegen/coreml-unavailable.d.ts.map +1 -0
  216. package/src/services/imagegen/coreml-unavailable.ts +237 -0
  217. package/src/services/imagegen/errors.d.ts +16 -0
  218. package/src/services/imagegen/errors.d.ts.map +1 -0
  219. package/src/services/imagegen/errors.ts +40 -0
  220. package/src/services/imagegen/index.d.ts +58 -0
  221. package/src/services/imagegen/index.d.ts.map +1 -0
  222. package/src/services/imagegen/index.ts +144 -0
  223. package/src/services/imagegen/mflux.d.ts +74 -0
  224. package/src/services/imagegen/mflux.d.ts.map +1 -0
  225. package/src/services/imagegen/mflux.ts +313 -0
  226. package/src/services/imagegen/sd-cpp.d.ts +180 -0
  227. package/src/services/imagegen/sd-cpp.d.ts.map +1 -0
  228. package/src/services/imagegen/sd-cpp.ts +718 -0
  229. package/src/services/imagegen/tensorrt-unavailable.d.ts +83 -0
  230. package/src/services/imagegen/tensorrt-unavailable.d.ts.map +1 -0
  231. package/src/services/imagegen/tensorrt-unavailable.ts +295 -0
  232. package/src/services/imagegen/types.d.ts +181 -0
  233. package/src/services/imagegen/types.d.ts.map +1 -0
  234. package/src/services/imagegen/types.ts +193 -0
  235. package/src/services/index.d.ts +30 -0
  236. package/src/services/index.d.ts.map +1 -0
  237. package/src/services/index.ts +225 -0
  238. package/src/services/inference-capabilities.d.ts +132 -0
  239. package/src/services/inference-capabilities.d.ts.map +1 -0
  240. package/src/services/inference-capabilities.test.ts +75 -0
  241. package/src/services/inference-capabilities.ts +204 -0
  242. package/src/services/inference-telemetry.d.ts +59 -0
  243. package/src/services/inference-telemetry.d.ts.map +1 -0
  244. package/src/services/inference-telemetry.ts +143 -0
  245. package/src/services/ios-llama-streaming.ts +248 -0
  246. package/src/services/kv-spill.d.ts +189 -0
  247. package/src/services/kv-spill.d.ts.map +1 -0
  248. package/src/services/kv-spill.test.ts +222 -0
  249. package/src/services/kv-spill.ts +356 -0
  250. package/src/services/latency-trace.d.ts +346 -0
  251. package/src/services/latency-trace.d.ts.map +1 -0
  252. package/src/services/latency-trace.test.ts +266 -0
  253. package/src/services/latency-trace.ts +844 -0
  254. package/src/services/llama-server-metrics.ts +304 -0
  255. package/src/services/llm-streaming-binding.d.ts +96 -0
  256. package/src/services/llm-streaming-binding.d.ts.map +1 -0
  257. package/src/services/llm-streaming-binding.ts +136 -0
  258. package/src/services/load-args.d.ts +82 -0
  259. package/src/services/load-args.d.ts.map +1 -0
  260. package/src/services/load-args.ts +81 -0
  261. package/src/services/manifest/eliza-1.manifest.v1.json +708 -0
  262. package/src/services/manifest/index.d.ts +4 -0
  263. package/src/services/manifest/index.d.ts.map +1 -0
  264. package/src/services/manifest/index.ts +66 -0
  265. package/src/services/manifest/manifest.test.ts +693 -0
  266. package/src/services/manifest/schema.d.ts +715 -0
  267. package/src/services/manifest/schema.d.ts.map +1 -0
  268. package/src/services/manifest/schema.ts +655 -0
  269. package/src/services/manifest/types.d.ts +30 -0
  270. package/src/services/manifest/types.d.ts.map +1 -0
  271. package/src/services/manifest/types.ts +55 -0
  272. package/src/services/manifest/validator.d.ts +66 -0
  273. package/src/services/manifest/validator.d.ts.map +1 -0
  274. package/src/services/manifest/validator.ts +569 -0
  275. package/src/services/memory-arbiter.d.ts +343 -0
  276. package/src/services/memory-arbiter.d.ts.map +1 -0
  277. package/src/services/memory-arbiter.test.ts +419 -0
  278. package/src/services/memory-arbiter.ts +1000 -0
  279. package/src/services/memory-monitor.d.ts +119 -0
  280. package/src/services/memory-monitor.d.ts.map +1 -0
  281. package/src/services/memory-monitor.test.ts +208 -0
  282. package/src/services/memory-monitor.ts +296 -0
  283. package/src/services/memory-pressure.d.ts +127 -0
  284. package/src/services/memory-pressure.d.ts.map +1 -0
  285. package/src/services/memory-pressure.ts +413 -0
  286. package/src/services/mtp-doctor.d.ts +13 -0
  287. package/src/services/mtp-doctor.d.ts.map +1 -0
  288. package/src/services/mtp-doctor.ts +78 -0
  289. package/src/services/network-policy.d.ts +127 -0
  290. package/src/services/network-policy.d.ts.map +1 -0
  291. package/src/services/network-policy.ts +346 -0
  292. package/src/services/paths.d.ts +6 -0
  293. package/src/services/paths.d.ts.map +1 -0
  294. package/src/services/paths.ts +25 -0
  295. package/src/services/planner-skeleton.d.ts +124 -0
  296. package/src/services/planner-skeleton.d.ts.map +1 -0
  297. package/src/services/planner-skeleton.ts +175 -0
  298. package/src/services/providers.d.ts +38 -0
  299. package/src/services/providers.d.ts.map +1 -0
  300. package/src/services/providers.ts +507 -0
  301. package/src/services/ram-budget-cache.test.ts +163 -0
  302. package/src/services/ram-budget.d.ts +110 -0
  303. package/src/services/ram-budget.d.ts.map +1 -0
  304. package/src/services/ram-budget.ts +0 -0
  305. package/src/services/readiness.d.ts +9 -0
  306. package/src/services/readiness.d.ts.map +1 -0
  307. package/src/services/readiness.test.ts +87 -0
  308. package/src/services/readiness.ts +238 -0
  309. package/src/services/recommendation.d.ts +111 -0
  310. package/src/services/recommendation.d.ts.map +1 -0
  311. package/src/services/recommendation.ts +672 -0
  312. package/src/services/registry.d.ts +35 -0
  313. package/src/services/registry.d.ts.map +1 -0
  314. package/src/services/registry.ts +151 -0
  315. package/src/services/router-handler.d.ts +92 -0
  316. package/src/services/router-handler.d.ts.map +1 -0
  317. package/src/services/router-handler.test.ts +45 -0
  318. package/src/services/router-handler.ts +376 -0
  319. package/src/services/routing-policy.d.ts +55 -0
  320. package/src/services/routing-policy.d.ts.map +1 -0
  321. package/src/services/routing-policy.ts +228 -0
  322. package/src/services/routing-preferences.d.ts +8 -0
  323. package/src/services/routing-preferences.d.ts.map +1 -0
  324. package/src/services/routing-preferences.ts +15 -0
  325. package/src/services/runtime-target.d.ts +98 -0
  326. package/src/services/runtime-target.d.ts.map +1 -0
  327. package/src/services/runtime-target.ts +154 -0
  328. package/src/services/service.d.ts +128 -0
  329. package/src/services/service.d.ts.map +1 -0
  330. package/src/services/service.test.ts +223 -0
  331. package/src/services/service.ts +735 -0
  332. package/src/services/session-pool.d.ts +72 -0
  333. package/src/services/session-pool.d.ts.map +1 -0
  334. package/src/services/session-pool.ts +153 -0
  335. package/src/services/structured-output/deterministic-repair.d.ts +23 -0
  336. package/src/services/structured-output/deterministic-repair.d.ts.map +1 -0
  337. package/src/services/structured-output/deterministic-repair.test.ts +169 -0
  338. package/src/services/structured-output/deterministic-repair.ts +443 -0
  339. package/src/services/structured-output/index.ts +4 -0
  340. package/src/services/structured-output.d.ts +311 -0
  341. package/src/services/structured-output.d.ts.map +1 -0
  342. package/src/services/structured-output.test.ts +483 -0
  343. package/src/services/structured-output.ts +712 -0
  344. package/src/services/transcription-priority.test.ts +211 -0
  345. package/src/services/tts/errors.ts +46 -0
  346. package/src/services/tts/index.ts +214 -0
  347. package/src/services/tts/tts-audio-cache.ts +235 -0
  348. package/src/services/tts/types.ts +157 -0
  349. package/src/services/types.d.ts +19 -0
  350. package/src/services/types.d.ts.map +1 -0
  351. package/src/services/types.ts +55 -0
  352. package/src/services/verify-on-device.d.ts +34 -0
  353. package/src/services/verify-on-device.d.ts.map +1 -0
  354. package/src/services/verify-on-device.test.ts +87 -0
  355. package/src/services/verify-on-device.ts +127 -0
  356. package/src/services/verify.d.ts +8 -0
  357. package/src/services/verify.d.ts.map +1 -0
  358. package/src/services/verify.ts +13 -0
  359. package/src/services/vision/aosp-unavailable.d.ts +115 -0
  360. package/src/services/vision/aosp-unavailable.d.ts.map +1 -0
  361. package/src/services/vision/aosp-unavailable.ts +163 -0
  362. package/src/services/vision/capacitor-llama.d.ts +99 -0
  363. package/src/services/vision/capacitor-llama.d.ts.map +1 -0
  364. package/src/services/vision/capacitor-llama.ts +255 -0
  365. package/src/services/vision/cloud-fallback.d.ts +47 -0
  366. package/src/services/vision/cloud-fallback.d.ts.map +1 -0
  367. package/src/services/vision/cloud-fallback.test.ts +243 -0
  368. package/src/services/vision/cloud-fallback.ts +268 -0
  369. package/src/services/vision/fallback-chain.test.ts +86 -0
  370. package/src/services/vision/hash.d.ts +71 -0
  371. package/src/services/vision/hash.d.ts.map +1 -0
  372. package/src/services/vision/hash.ts +157 -0
  373. package/src/services/vision/index.d.ts +95 -0
  374. package/src/services/vision/index.d.ts.map +1 -0
  375. package/src/services/vision/index.ts +251 -0
  376. package/src/services/vision/llama-server.d.ts +73 -0
  377. package/src/services/vision/llama-server.d.ts.map +1 -0
  378. package/src/services/vision/llama-server.ts +177 -0
  379. package/src/services/vision/types.d.ts +153 -0
  380. package/src/services/vision/types.d.ts.map +1 -0
  381. package/src/services/vision/types.ts +154 -0
  382. package/src/services/vision/vast-fallback.d.ts +18 -0
  383. package/src/services/vision/vast-fallback.d.ts.map +1 -0
  384. package/src/services/vision/vast-fallback.ts +127 -0
  385. package/src/services/vision-embedding-cache.d.ts +98 -0
  386. package/src/services/vision-embedding-cache.d.ts.map +1 -0
  387. package/src/services/vision-embedding-cache.ts +189 -0
  388. package/src/services/voice/VOICE_WORKBENCH.md +88 -0
  389. package/src/services/voice/__test-helpers__/fake-ffi.ts +92 -0
  390. package/src/services/voice/__test-helpers__/synthetic-speech.ts +124 -0
  391. package/src/services/voice/__tests__/checkpoint-manager.test.ts +241 -0
  392. package/src/services/voice/__tests__/checkpoint-policy.test.ts +270 -0
  393. package/src/services/voice/__tests__/eager-context-builder.test.ts +257 -0
  394. package/src/services/voice/__tests__/eliza1-eot-scorer.test.ts +288 -0
  395. package/src/services/voice/__tests__/eot-classifier.test.ts +431 -0
  396. package/src/services/voice/__tests__/optimistic-rollback.test.ts +312 -0
  397. package/src/services/voice/__tests__/prefill-client.test.ts +266 -0
  398. package/src/services/voice/__tests__/prefix-preserving-queue.test.ts +208 -0
  399. package/src/services/voice/__tests__/streaming-asr.test.ts +450 -0
  400. package/src/services/voice/__tests__/streaming-transcriber.test.ts +339 -0
  401. package/src/services/voice/__tests__/turn-detector-resolver.test.ts +197 -0
  402. package/src/services/voice/__tests__/voice-state-machine-prefill.test.ts +275 -0
  403. package/src/services/voice/__tests__/voice-state-machine.test.ts +354 -0
  404. package/src/services/voice/audio-frame-consumer.d.ts +212 -0
  405. package/src/services/voice/audio-frame-consumer.d.ts.map +1 -0
  406. package/src/services/voice/audio-frame-consumer.test.ts +343 -0
  407. package/src/services/voice/audio-frame-consumer.ts +491 -0
  408. package/src/services/voice/barge-in.d.ts +112 -0
  409. package/src/services/voice/barge-in.d.ts.map +1 -0
  410. package/src/services/voice/barge-in.test.ts +244 -0
  411. package/src/services/voice/barge-in.ts +336 -0
  412. package/src/services/voice/cancellation-coordinator.d.ts +127 -0
  413. package/src/services/voice/cancellation-coordinator.d.ts.map +1 -0
  414. package/src/services/voice/cancellation-coordinator.test.ts +196 -0
  415. package/src/services/voice/cancellation-coordinator.ts +269 -0
  416. package/src/services/voice/checkpoint-manager.d.ts +199 -0
  417. package/src/services/voice/checkpoint-manager.d.ts.map +1 -0
  418. package/src/services/voice/checkpoint-manager.ts +401 -0
  419. package/src/services/voice/checkpoint-policy.ts +336 -0
  420. package/src/services/voice/composite-eot-classifier.test.ts +59 -0
  421. package/src/services/voice/e2e-harness.test.ts +182 -0
  422. package/src/services/voice/e2e-harness.ts +743 -0
  423. package/src/services/voice/eager-context-builder.d.ts +170 -0
  424. package/src/services/voice/eager-context-builder.d.ts.map +1 -0
  425. package/src/services/voice/eager-context-builder.ts +262 -0
  426. package/src/services/voice/eliza1-eot-scorer.d.ts +124 -0
  427. package/src/services/voice/eliza1-eot-scorer.d.ts.map +1 -0
  428. package/src/services/voice/eliza1-eot-scorer.ts +242 -0
  429. package/src/services/voice/embedding-server.ts +200 -0
  430. package/src/services/voice/embedding.d.ts +133 -0
  431. package/src/services/voice/embedding.d.ts.map +1 -0
  432. package/src/services/voice/embedding.test.ts +148 -0
  433. package/src/services/voice/embedding.ts +244 -0
  434. package/src/services/voice/emotion-attribution.d.ts +68 -0
  435. package/src/services/voice/emotion-attribution.d.ts.map +1 -0
  436. package/src/services/voice/emotion-attribution.test.ts +129 -0
  437. package/src/services/voice/emotion-attribution.ts +361 -0
  438. package/src/services/voice/engine-bridge-cancellation.test.ts +422 -0
  439. package/src/services/voice/engine-bridge.d.ts +746 -0
  440. package/src/services/voice/engine-bridge.d.ts.map +1 -0
  441. package/src/services/voice/engine-bridge.test.ts +384 -0
  442. package/src/services/voice/engine-bridge.ts +2226 -0
  443. package/src/services/voice/eot-classifier-ggml.d.ts +179 -0
  444. package/src/services/voice/eot-classifier-ggml.d.ts.map +1 -0
  445. package/src/services/voice/eot-classifier-ggml.ts +566 -0
  446. package/src/services/voice/eot-classifier.d.ts +214 -0
  447. package/src/services/voice/eot-classifier.d.ts.map +1 -0
  448. package/src/services/voice/eot-classifier.ts +533 -0
  449. package/src/services/voice/errors.d.ts +20 -0
  450. package/src/services/voice/errors.d.ts.map +1 -0
  451. package/src/services/voice/errors.ts +32 -0
  452. package/src/services/voice/expressive-tags.d.ts +158 -0
  453. package/src/services/voice/expressive-tags.d.ts.map +1 -0
  454. package/src/services/voice/expressive-tags.ts +405 -0
  455. package/src/services/voice/ffi-bindings.d.ts +636 -0
  456. package/src/services/voice/ffi-bindings.d.ts.map +1 -0
  457. package/src/services/voice/ffi-bindings.test.ts +671 -0
  458. package/src/services/voice/ffi-bindings.ts +3050 -0
  459. package/src/services/voice/first-line-cache.d.ts +181 -0
  460. package/src/services/voice/first-line-cache.d.ts.map +1 -0
  461. package/src/services/voice/first-line-cache.ts +725 -0
  462. package/src/services/voice/fused-eot-scorer.d.ts +51 -0
  463. package/src/services/voice/fused-eot-scorer.d.ts.map +1 -0
  464. package/src/services/voice/fused-eot-scorer.ts +135 -0
  465. package/src/services/voice/index.d.ts +91 -0
  466. package/src/services/voice/index.d.ts.map +1 -0
  467. package/src/services/voice/index.ts +481 -0
  468. package/src/services/voice/kokoro/__tests__/kokoro-backend.test.ts +151 -0
  469. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.real.test.ts +151 -0
  470. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.test.ts +60 -0
  471. package/src/services/voice/kokoro/__tests__/kokoro-engine-discovery.test.ts +277 -0
  472. package/src/services/voice/kokoro/__tests__/kokoro-ffi-runtime.test.ts +235 -0
  473. package/src/services/voice/kokoro/__tests__/kokoro-runtime.test.ts +95 -0
  474. package/src/services/voice/kokoro/__tests__/phonemizer.test.ts +53 -0
  475. package/src/services/voice/kokoro/__tests__/runtime-selection.test.ts +231 -0
  476. package/src/services/voice/kokoro/__tests__/voices.test.ts +57 -0
  477. package/src/services/voice/kokoro/index.ts +79 -0
  478. package/src/services/voice/kokoro/kokoro-backend.d.ts +72 -0
  479. package/src/services/voice/kokoro/kokoro-backend.d.ts.map +1 -0
  480. package/src/services/voice/kokoro/kokoro-backend.ts +207 -0
  481. package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts +58 -0
  482. package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts.map +1 -0
  483. package/src/services/voice/kokoro/kokoro-engine-discovery.ts +177 -0
  484. package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts +75 -0
  485. package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts.map +1 -0
  486. package/src/services/voice/kokoro/kokoro-ffi-runtime.ts +233 -0
  487. package/src/services/voice/kokoro/kokoro-runtime.d.ts +100 -0
  488. package/src/services/voice/kokoro/kokoro-runtime.d.ts.map +1 -0
  489. package/src/services/voice/kokoro/kokoro-runtime.ts +170 -0
  490. package/src/services/voice/kokoro/phoneme-stream.ts +123 -0
  491. package/src/services/voice/kokoro/phonemizer.d.ts +50 -0
  492. package/src/services/voice/kokoro/phonemizer.d.ts.map +1 -0
  493. package/src/services/voice/kokoro/phonemizer.ts +344 -0
  494. package/src/services/voice/kokoro/pick-runtime.d.ts +61 -0
  495. package/src/services/voice/kokoro/pick-runtime.d.ts.map +1 -0
  496. package/src/services/voice/kokoro/pick-runtime.test.ts +91 -0
  497. package/src/services/voice/kokoro/pick-runtime.ts +130 -0
  498. package/src/services/voice/kokoro/runtime-selection.d.ts +92 -0
  499. package/src/services/voice/kokoro/runtime-selection.d.ts.map +1 -0
  500. package/src/services/voice/kokoro/runtime-selection.ts +237 -0
  501. package/src/services/voice/kokoro/types.d.ts +82 -0
  502. package/src/services/voice/kokoro/types.d.ts.map +1 -0
  503. package/src/services/voice/kokoro/types.ts +95 -0
  504. package/src/services/voice/kokoro/voice-presets.d.ts +23 -0
  505. package/src/services/voice/kokoro/voice-presets.d.ts.map +1 -0
  506. package/src/services/voice/kokoro/voice-presets.ts +129 -0
  507. package/src/services/voice/kokoro/voices.d.ts +30 -0
  508. package/src/services/voice/kokoro/voices.d.ts.map +1 -0
  509. package/src/services/voice/kokoro/voices.ts +64 -0
  510. package/src/services/voice/lifecycle.d.ts +135 -0
  511. package/src/services/voice/lifecycle.d.ts.map +1 -0
  512. package/src/services/voice/lifecycle.test.ts +315 -0
  513. package/src/services/voice/lifecycle.ts +301 -0
  514. package/src/services/voice/live-diarization-session.d.ts +96 -0
  515. package/src/services/voice/live-diarization-session.d.ts.map +1 -0
  516. package/src/services/voice/live-diarization-session.ts +289 -0
  517. package/src/services/voice/mic-source.d.ts +136 -0
  518. package/src/services/voice/mic-source.d.ts.map +1 -0
  519. package/src/services/voice/mic-source.test.ts +210 -0
  520. package/src/services/voice/mic-source.ts +503 -0
  521. package/src/services/voice/optimistic-policy.d.ts +109 -0
  522. package/src/services/voice/optimistic-policy.d.ts.map +1 -0
  523. package/src/services/voice/optimistic-policy.test.ts +101 -0
  524. package/src/services/voice/optimistic-policy.ts +192 -0
  525. package/src/services/voice/optimistic-rollback.ts +343 -0
  526. package/src/services/voice/partial-stabilizer.d.ts +73 -0
  527. package/src/services/voice/partial-stabilizer.d.ts.map +1 -0
  528. package/src/services/voice/partial-stabilizer.test.ts +68 -0
  529. package/src/services/voice/partial-stabilizer.ts +140 -0
  530. package/src/services/voice/phoneme-tokenizer.d.ts +49 -0
  531. package/src/services/voice/phoneme-tokenizer.d.ts.map +1 -0
  532. package/src/services/voice/phoneme-tokenizer.ts +158 -0
  533. package/src/services/voice/phrase-cache.d.ts +76 -0
  534. package/src/services/voice/phrase-cache.d.ts.map +1 -0
  535. package/src/services/voice/phrase-cache.test.ts +242 -0
  536. package/src/services/voice/phrase-cache.ts +186 -0
  537. package/src/services/voice/phrase-chunker.d.ts +62 -0
  538. package/src/services/voice/phrase-chunker.d.ts.map +1 -0
  539. package/src/services/voice/phrase-chunker.test.ts +239 -0
  540. package/src/services/voice/phrase-chunker.ts +281 -0
  541. package/src/services/voice/pipeline-impls.d.ts +151 -0
  542. package/src/services/voice/pipeline-impls.d.ts.map +1 -0
  543. package/src/services/voice/pipeline-impls.l6.test.ts +110 -0
  544. package/src/services/voice/pipeline-impls.test.ts +292 -0
  545. package/src/services/voice/pipeline-impls.ts +315 -0
  546. package/src/services/voice/pipeline.d.ts +216 -0
  547. package/src/services/voice/pipeline.d.ts.map +1 -0
  548. package/src/services/voice/pipeline.ts +505 -0
  549. package/src/services/voice/prefill-client.d.ts +123 -0
  550. package/src/services/voice/prefill-client.d.ts.map +1 -0
  551. package/src/services/voice/prefill-client.ts +316 -0
  552. package/src/services/voice/prefix-preserving-queue.d.ts +113 -0
  553. package/src/services/voice/prefix-preserving-queue.d.ts.map +1 -0
  554. package/src/services/voice/prefix-preserving-queue.ts +162 -0
  555. package/src/services/voice/profile-store.d.ts +248 -0
  556. package/src/services/voice/profile-store.d.ts.map +1 -0
  557. package/src/services/voice/profile-store.ts +887 -0
  558. package/src/services/voice/ring-buffer.d.ts +40 -0
  559. package/src/services/voice/ring-buffer.d.ts.map +1 -0
  560. package/src/services/voice/ring-buffer.ts +105 -0
  561. package/src/services/voice/rollback-queue.d.ts +24 -0
  562. package/src/services/voice/rollback-queue.d.ts.map +1 -0
  563. package/src/services/voice/rollback-queue.ts +74 -0
  564. package/src/services/voice/samantha-preset-placeholder.d.ts +67 -0
  565. package/src/services/voice/samantha-preset-placeholder.d.ts.map +1 -0
  566. package/src/services/voice/samantha-preset-placeholder.test.ts +97 -0
  567. package/src/services/voice/samantha-preset-placeholder.ts +148 -0
  568. package/src/services/voice/samantha-preset-regenerator.d.ts +87 -0
  569. package/src/services/voice/samantha-preset-regenerator.d.ts.map +1 -0
  570. package/src/services/voice/samantha-preset-regenerator.ts +393 -0
  571. package/src/services/voice/scheduler.d.ts +146 -0
  572. package/src/services/voice/scheduler.d.ts.map +1 -0
  573. package/src/services/voice/scheduler.t2.test.ts +141 -0
  574. package/src/services/voice/scheduler.ts +927 -0
  575. package/src/services/voice/shared-resources.d.ts +190 -0
  576. package/src/services/voice/shared-resources.d.ts.map +1 -0
  577. package/src/services/voice/shared-resources.ts +320 -0
  578. package/src/services/voice/speaker/attribution-pipeline.d.ts +74 -0
  579. package/src/services/voice/speaker/attribution-pipeline.d.ts.map +1 -0
  580. package/src/services/voice/speaker/attribution-pipeline.ts +386 -0
  581. package/src/services/voice/speaker/diarizer-fused.d.ts +59 -0
  582. package/src/services/voice/speaker/diarizer-fused.d.ts.map +1 -0
  583. package/src/services/voice/speaker/diarizer-fused.real.test.ts +100 -0
  584. package/src/services/voice/speaker/diarizer-fused.ts +154 -0
  585. package/src/services/voice/speaker/diarizer.d.ts +75 -0
  586. package/src/services/voice/speaker/diarizer.d.ts.map +1 -0
  587. package/src/services/voice/speaker/diarizer.ts +218 -0
  588. package/src/services/voice/speaker/encoder-fused.d.ts +60 -0
  589. package/src/services/voice/speaker/encoder-fused.d.ts.map +1 -0
  590. package/src/services/voice/speaker/encoder-fused.real.test.ts +113 -0
  591. package/src/services/voice/speaker/encoder-fused.ts +138 -0
  592. package/src/services/voice/speaker/encoder-ggml.d.ts +33 -0
  593. package/src/services/voice/speaker/encoder-ggml.d.ts.map +1 -0
  594. package/src/services/voice/speaker/encoder-ggml.ts +79 -0
  595. package/src/services/voice/speaker/encoder.d.ts +37 -0
  596. package/src/services/voice/speaker/encoder.d.ts.map +1 -0
  597. package/src/services/voice/speaker/encoder.ts +105 -0
  598. package/src/services/voice/speaker-imprint.d.ts +83 -0
  599. package/src/services/voice/speaker-imprint.d.ts.map +1 -0
  600. package/src/services/voice/speaker-imprint.test.ts +185 -0
  601. package/src/services/voice/speaker-imprint.ts +312 -0
  602. package/src/services/voice/speaker-preset-cache.d.ts +77 -0
  603. package/src/services/voice/speaker-preset-cache.d.ts.map +1 -0
  604. package/src/services/voice/speaker-preset-cache.test.ts +154 -0
  605. package/src/services/voice/speaker-preset-cache.ts +195 -0
  606. package/src/services/voice/streaming-asr/streaming-pipeline-adapter.ts +292 -0
  607. package/src/services/voice/system-audio-sink.d.ts +73 -0
  608. package/src/services/voice/system-audio-sink.d.ts.map +1 -0
  609. package/src/services/voice/system-audio-sink.test.ts +29 -0
  610. package/src/services/voice/system-audio-sink.ts +366 -0
  611. package/src/services/voice/transcriber.d.ts +244 -0
  612. package/src/services/voice/transcriber.d.ts.map +1 -0
  613. package/src/services/voice/transcriber.test.ts +392 -0
  614. package/src/services/voice/transcriber.ts +704 -0
  615. package/src/services/voice/turn-controller.d.ts +183 -0
  616. package/src/services/voice/turn-controller.d.ts.map +1 -0
  617. package/src/services/voice/turn-controller.test.ts +575 -0
  618. package/src/services/voice/turn-controller.ts +596 -0
  619. package/src/services/voice/types.d.ts +643 -0
  620. package/src/services/voice/types.d.ts.map +1 -0
  621. package/src/services/voice/types.ts +699 -0
  622. package/src/services/voice/vad.d.ts +282 -0
  623. package/src/services/voice/vad.d.ts.map +1 -0
  624. package/src/services/voice/vad.test.ts +480 -0
  625. package/src/services/voice/vad.ts +827 -0
  626. package/src/services/voice/vad.v1-v4.test.ts +222 -0
  627. package/src/services/voice/voice-budget.d.ts +241 -0
  628. package/src/services/voice/voice-budget.d.ts.map +1 -0
  629. package/src/services/voice/voice-budget.test.ts +420 -0
  630. package/src/services/voice/voice-budget.ts +656 -0
  631. package/src/services/voice/voice-duet.test.ts +375 -0
  632. package/src/services/voice/voice-emotion-classifier.d.ts +95 -0
  633. package/src/services/voice/voice-emotion-classifier.d.ts.map +1 -0
  634. package/src/services/voice/voice-emotion-classifier.test.ts +210 -0
  635. package/src/services/voice/voice-emotion-classifier.ts +273 -0
  636. package/src/services/voice/voice-preset-format.d.ts +158 -0
  637. package/src/services/voice/voice-preset-format.d.ts.map +1 -0
  638. package/src/services/voice/voice-preset-format.ts +700 -0
  639. package/src/services/voice/voice-preset-generator.test.ts +89 -0
  640. package/src/services/voice/voice-profile-artifact.d.ts +116 -0
  641. package/src/services/voice/voice-profile-artifact.d.ts.map +1 -0
  642. package/src/services/voice/voice-profile-artifact.test.ts +138 -0
  643. package/src/services/voice/voice-profile-artifact.ts +518 -0
  644. package/src/services/voice/voice-profile-routes.d.ts +83 -0
  645. package/src/services/voice/voice-profile-routes.d.ts.map +1 -0
  646. package/src/services/voice/voice-profile-routes.test.ts +429 -0
  647. package/src/services/voice/voice-profile-routes.ts +425 -0
  648. package/src/services/voice/voice-scenario.ts +154 -0
  649. package/src/services/voice/voice-settings.d.ts +82 -0
  650. package/src/services/voice/voice-settings.d.ts.map +1 -0
  651. package/src/services/voice/voice-settings.ts +172 -0
  652. package/src/services/voice/voice-state-machine.d.ts +364 -0
  653. package/src/services/voice/voice-state-machine.d.ts.map +1 -0
  654. package/src/services/voice/voice-state-machine.ts +727 -0
  655. package/src/services/voice/voice-workbench-report.test.ts +168 -0
  656. package/src/services/voice/voice-workbench-report.ts +326 -0
  657. package/src/services/voice/voice-workbench.test.ts +158 -0
  658. package/src/services/voice/voice.test.ts +1070 -0
  659. package/src/services/voice/wake-word-ggml.d.ts +101 -0
  660. package/src/services/voice/wake-word-ggml.d.ts.map +1 -0
  661. package/src/services/voice/wake-word-ggml.ts +320 -0
  662. package/src/services/voice/wake-word.d.ts +255 -0
  663. package/src/services/voice/wake-word.d.ts.map +1 -0
  664. package/src/services/voice/wake-word.test.ts +298 -0
  665. package/src/services/voice/wake-word.ts +554 -0
  666. package/src/services/voice/wrap-with-first-line-cache.d.ts +70 -0
  667. package/src/services/voice/wrap-with-first-line-cache.d.ts.map +1 -0
  668. package/src/services/voice/wrap-with-first-line-cache.ts +267 -0
  669. package/src/services/voice-model-updater.d.ts +240 -0
  670. package/src/services/voice-model-updater.d.ts.map +1 -0
  671. package/src/services/voice-model-updater.ts +724 -0
  672. package/src/services/voice-prewarm.d.ts +3 -0
  673. package/src/services/voice-prewarm.d.ts.map +1 -0
  674. package/src/services/voice-prewarm.ts +51 -0
  675. package/dist/index.d.ts +0 -37
  676. package/dist/index.js +0 -1098
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Tests for the `TRANSCRIPTION` model handler priority + provider hint
3
+ * semantics on `AgentRuntime`. These exercise the same `registerModel`
4
+ * machinery used by plugin-elizacloud (remote OpenAI Whisper API) and the
5
+ * local fused Qwen3-ASR handler so callers know exactly:
6
+ *
7
+ * - which handler wins when priorities tie (first-registered),
8
+ * - how an explicit `priority` overrides that,
9
+ * - that a `provider` hint to `useModel` overrides priority entirely,
10
+ * - that the runtime does NOT auto-fall-through on handler errors,
11
+ * - and how a caller can implement an explicit try/catch fallback.
12
+ *
13
+ * The test is intentionally provider-agnostic — it registers fake handlers
14
+ * named "local" and "openai" so the contract is observable without booting
15
+ * any real ASR backend.
16
+ */
17
+
18
+ import { describe, expect, it } from "vitest";
19
+
20
+ import { InMemoryDatabaseAdapter } from "../../../../packages/core/src/database/inMemoryAdapter";
21
+ import { AgentRuntime } from "../../../../packages/core/src/runtime";
22
+ import { ModelType } from "../../../../packages/core/src/types";
23
+
24
+ interface TranscriptionParams {
25
+ audio?: Float32Array | Uint8Array | Buffer | string;
26
+ }
27
+
28
+ interface TestRuntimeCtx {
29
+ runtime: AgentRuntime;
30
+ calls: string[];
31
+ }
32
+
33
+ function makeRuntime(): TestRuntimeCtx {
34
+ const runtime = new AgentRuntime({
35
+ character: {
36
+ name: "TranscriptionPriorityTest",
37
+ bio: "asr-priority test",
38
+ settings: {},
39
+ } as never,
40
+ adapter: new InMemoryDatabaseAdapter(),
41
+ logLevel: "fatal",
42
+ });
43
+ const calls: string[] = [];
44
+ return { runtime, calls };
45
+ }
46
+
47
+ function makeHandler(
48
+ calls: string[],
49
+ label: string,
50
+ result: string | (() => string),
51
+ options: { throws?: boolean } = {},
52
+ ) {
53
+ return async (_runtime: unknown, _params: unknown): Promise<string> => {
54
+ calls.push(label);
55
+ if (options.throws) {
56
+ throw new Error(`${label} handler failed`);
57
+ }
58
+ return typeof result === "function" ? result() : result;
59
+ };
60
+ }
61
+
62
+ describe("TRANSCRIPTION handler priority on AgentRuntime", () => {
63
+ it("local Qwen3-ASR wins over remote OpenAI Whisper by registration order when priorities tie", async () => {
64
+ const { runtime, calls } = makeRuntime();
65
+ runtime.registerModel(
66
+ ModelType.TRANSCRIPTION,
67
+ makeHandler(calls, "local", "local transcript"),
68
+ "eliza-local-inference",
69
+ 0,
70
+ );
71
+ runtime.registerModel(
72
+ ModelType.TRANSCRIPTION,
73
+ makeHandler(calls, "openai", "openai transcript"),
74
+ "openai",
75
+ 0,
76
+ );
77
+
78
+ const text = await runtime.useModel(ModelType.TRANSCRIPTION, {
79
+ audio: new Float32Array(160),
80
+ } as TranscriptionParams as never);
81
+
82
+ expect(text).toBe("local transcript");
83
+ expect(calls).toEqual(["local"]);
84
+ });
85
+
86
+ it("explicit higher priority always wins regardless of registration order", async () => {
87
+ const { runtime, calls } = makeRuntime();
88
+ runtime.registerModel(
89
+ ModelType.TRANSCRIPTION,
90
+ makeHandler(calls, "remote", "remote transcript"),
91
+ "openai",
92
+ 100,
93
+ );
94
+ runtime.registerModel(
95
+ ModelType.TRANSCRIPTION,
96
+ makeHandler(calls, "local", "local transcript"),
97
+ "eliza-local-inference",
98
+ 0,
99
+ );
100
+
101
+ // Remote wins because priority 100 > 0.
102
+ let text = await runtime.useModel(ModelType.TRANSCRIPTION, {
103
+ audio: new Float32Array(160),
104
+ } as TranscriptionParams as never);
105
+ expect(text).toBe("remote transcript");
106
+ expect(calls).toEqual(["remote"]);
107
+
108
+ // Re-register local with a higher priority — now local wins.
109
+ runtime.registerModel(
110
+ ModelType.TRANSCRIPTION,
111
+ makeHandler(calls, "local-priority", "local-priority transcript"),
112
+ "eliza-local-inference",
113
+ 200,
114
+ );
115
+
116
+ text = await runtime.useModel(ModelType.TRANSCRIPTION, {
117
+ audio: new Float32Array(160),
118
+ } as TranscriptionParams as never);
119
+ expect(text).toBe("local-priority transcript");
120
+ expect(calls).toEqual(["remote", "local-priority"]);
121
+ });
122
+
123
+ it("provider hint to useModel overrides priority", async () => {
124
+ const { runtime, calls } = makeRuntime();
125
+ runtime.registerModel(
126
+ ModelType.TRANSCRIPTION,
127
+ makeHandler(calls, "local", "local transcript"),
128
+ "eliza-local-inference",
129
+ 200,
130
+ );
131
+ runtime.registerModel(
132
+ ModelType.TRANSCRIPTION,
133
+ makeHandler(calls, "openai", "openai transcript"),
134
+ "openai",
135
+ 0,
136
+ );
137
+
138
+ // Default useModel: local wins (priority 200 > 0).
139
+ let text = await runtime.useModel(ModelType.TRANSCRIPTION, {
140
+ audio: new Float32Array(160),
141
+ } as TranscriptionParams as never);
142
+ expect(text).toBe("local transcript");
143
+
144
+ // Explicit provider hint flips it to openai despite the priority gap.
145
+ text = await runtime.useModel(
146
+ ModelType.TRANSCRIPTION,
147
+ { audio: new Float32Array(160) } as TranscriptionParams as never,
148
+ "openai",
149
+ );
150
+ expect(text).toBe("openai transcript");
151
+ expect(calls).toEqual(["local", "openai"]);
152
+ });
153
+
154
+ it("runtime does NOT auto-fall-through on handler errors", async () => {
155
+ const { runtime, calls } = makeRuntime();
156
+ runtime.registerModel(
157
+ ModelType.TRANSCRIPTION,
158
+ makeHandler(calls, "local", "should never reach this", { throws: true }),
159
+ "eliza-local-inference",
160
+ 200,
161
+ );
162
+ runtime.registerModel(
163
+ ModelType.TRANSCRIPTION,
164
+ makeHandler(calls, "openai", "openai transcript"),
165
+ "openai",
166
+ 0,
167
+ );
168
+
169
+ await expect(
170
+ runtime.useModel(ModelType.TRANSCRIPTION, {
171
+ audio: new Float32Array(160),
172
+ } as TranscriptionParams as never),
173
+ ).rejects.toThrow(/local handler failed/);
174
+ // The remote handler should NOT have been invoked — there is no
175
+ // implicit fallback chain on the runtime.
176
+ expect(calls).toEqual(["local"]);
177
+ });
178
+
179
+ it("caller can implement fallback via try/catch + provider hint", async () => {
180
+ const { runtime, calls } = makeRuntime();
181
+ runtime.registerModel(
182
+ ModelType.TRANSCRIPTION,
183
+ makeHandler(calls, "local", "boom", { throws: true }),
184
+ "eliza-local-inference",
185
+ 200,
186
+ );
187
+ runtime.registerModel(
188
+ ModelType.TRANSCRIPTION,
189
+ makeHandler(calls, "openai", "openai transcript"),
190
+ "openai",
191
+ 0,
192
+ );
193
+
194
+ let result: string;
195
+ try {
196
+ result = (await runtime.useModel(ModelType.TRANSCRIPTION, {
197
+ audio: new Float32Array(160),
198
+ } as TranscriptionParams as never)) as string;
199
+ } catch {
200
+ // Caller-controlled fallback: explicit retry with provider hint.
201
+ result = (await runtime.useModel(
202
+ ModelType.TRANSCRIPTION,
203
+ { audio: new Float32Array(160) } as TranscriptionParams as never,
204
+ "openai",
205
+ )) as string;
206
+ }
207
+
208
+ expect(result).toBe("openai transcript");
209
+ expect(calls).toEqual(["local", "openai"]);
210
+ });
211
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Structured error every TTS backend throws when it can't serve a
3
+ * request. Mirrors `ImageGenBackendUnavailableError` so the arbiter / WS5
4
+ * provider handler can surface a single typed failure mode upward.
5
+ *
6
+ * `unsupported_request` is the only reason callers retry against a
7
+ * different backend (e.g. unknown voice id on Kokoro → fall through to
8
+ * Edge TTS via the runtime model registry priority list). All other
9
+ * reasons indicate a missing local install and the caller should surface
10
+ * the actionable message verbatim — no silent fallback to a cloud
11
+ * provider for installer / weights issues (AGENTS.md §3).
12
+ */
13
+ export class TtsBackendUnavailableError extends Error {
14
+ readonly code = "TTS_BACKEND_UNAVAILABLE";
15
+ constructor(
16
+ readonly backendId: string,
17
+ readonly reason:
18
+ | "binary_missing"
19
+ | "binary_version_mismatch"
20
+ | "model_missing"
21
+ | "tokenizer_missing"
22
+ | "voice_preset_missing"
23
+ | "voice_unknown"
24
+ | "binding_unavailable"
25
+ | "unsupported_runtime"
26
+ | "unsupported_request"
27
+ | "subprocess_failed",
28
+ message: string,
29
+ options?: { cause?: unknown },
30
+ ) {
31
+ super(message, options);
32
+ this.name = "TtsBackendUnavailableError";
33
+ }
34
+ }
35
+
36
+ /** Tells callers whether a thrown error came from a backend availability check. */
37
+ export function isTtsUnavailable(
38
+ err: unknown,
39
+ ): err is TtsBackendUnavailableError {
40
+ return (
41
+ err instanceof TtsBackendUnavailableError ||
42
+ (typeof err === "object" &&
43
+ err !== null &&
44
+ (err as { code?: unknown }).code === "TTS_BACKEND_UNAVAILABLE")
45
+ );
46
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Local text-to-speech capability (WS5) — public entry point.
3
+ *
4
+ * This module is what `provider.ts` (`createTextToSpeechHandler`), the
5
+ * voice lifecycle service, and TTS skill imports use to register
6
+ * the capability with the WS1 `MemoryArbiter`.
7
+ *
8
+ * Wiring:
9
+ *
10
+ * const arbiter = service.getMemoryArbiter();
11
+ * const registration = createTtsCapabilityRegistration({
12
+ * loader: createDefaultTtsLoader({ ... }),
13
+ * cache: arbiterTtsCache,
14
+ * });
15
+ * arbiter.registerCapability(registration);
16
+ *
17
+ * `createTtsCapabilityRegistration` wraps the underlying backend so the
18
+ * arbiter's `run(request)` path:
19
+ *
20
+ * 1. Hashes the request (`hashTtsRequest`) against the loaded model id.
21
+ * 2. Checks the capability-level audio cache (`tts-audio` namespace).
22
+ * On hit it short-circuits the backend and returns the cached bytes
23
+ * with `metadata.cacheHit = true`.
24
+ * 3. On miss: calls `backend.supports(request)`. If false the arbiter
25
+ * throws `TtsBackendUnavailableError` with `unsupported_request` so
26
+ * the runtime model registry can fall through to the next provider
27
+ * in priority order (typically `plugin-edge-tts` as the cloud
28
+ * fallback). No silent fallback inside the capability — the runtime
29
+ * owns provider-cascade routing, AGENTS.md §3.
30
+ * 4. Calls `backend.synthesize(request)`, populates the cache, and
31
+ * returns the result.
32
+ *
33
+ * The capability registers with the default `"tts"` resident role
34
+ * (`memory-arbiter.ts#CAPABILITY_ROLE.speak === "tts"`). Co-evicts with
35
+ * NOTHING in particular — TTS has its own role in the `RESIDENT_ROLE_PRIORITY`
36
+ * table and is the first non-text bucket the arbiter drops under critical
37
+ * pressure. That ordering is deliberate: voice output is the cheapest
38
+ * audible feature to defer; vision/image-gen weights take longer to
39
+ * re-load.
40
+ */
41
+
42
+ export { isTtsUnavailable, TtsBackendUnavailableError } from "./errors";
43
+ export {
44
+ hashTtsRequest,
45
+ TtsAudioCache,
46
+ type TtsAudioCacheConfig,
47
+ type TtsAudioEntry,
48
+ } from "./tts-audio-cache";
49
+ export type {
50
+ TtsBackend,
51
+ TtsBackendLoader,
52
+ TtsLoadArgs,
53
+ TtsMimeType,
54
+ TtsRequest,
55
+ TtsResult,
56
+ } from "./types";
57
+
58
+ import type {
59
+ ArbiterCapability,
60
+ CapabilityRegistration,
61
+ } from "../memory-arbiter";
62
+ import { TtsBackendUnavailableError } from "./errors";
63
+ import { hashTtsRequest, type TtsAudioEntry } from "./tts-audio-cache";
64
+ import type {
65
+ TtsBackend,
66
+ TtsBackendLoader,
67
+ TtsMimeType,
68
+ TtsRequest,
69
+ TtsResult,
70
+ } from "./types";
71
+
72
+ /**
73
+ * Minimal cache shape the registration needs. Lets tests inject a fake
74
+ * cache without instantiating the full `TtsAudioCache`.
75
+ */
76
+ export interface TtsAudioCacheLike {
77
+ get(hash: string): TtsAudioEntry | null;
78
+ set(
79
+ hash: string,
80
+ entry: {
81
+ audio: Uint8Array;
82
+ mime: TtsMimeType;
83
+ sampleRate: number;
84
+ voice: string;
85
+ model: string;
86
+ },
87
+ ttlMs?: number,
88
+ ): void;
89
+ }
90
+
91
+ export interface CreateTtsCapabilityRegistrationOptions {
92
+ loader: TtsBackendLoader;
93
+ /**
94
+ * Optional audio cache. When provided the wrapper performs hash →
95
+ * cache lookup before calling the backend's `synthesize`. The cache
96
+ * is keyed on `(provider, model, voice, text, speed, sampleRate)` via
97
+ * {@link hashTtsRequest}; the provider name is fixed to
98
+ * `"eliza-local-inference"` so cache entries don't collide with a
99
+ * different runtime's TTS cache living in the same process.
100
+ */
101
+ cache?: TtsAudioCacheLike;
102
+ /**
103
+ * Default provider name used in the cache key. Defaults to
104
+ * `"eliza-local-inference"`. Tests may override to assert keying.
105
+ */
106
+ provider?: string;
107
+ /**
108
+ * Best-effort RAM footprint estimate for the loaded weights. The
109
+ * arbiter only uses this for telemetry; eviction is by priority.
110
+ * Defaults: Kokoro ≈ 350 MB int8 / 410 MB fp32; OmniVoice ranges
111
+ * 800–1700 MB across the Q3..Q8 ladder. The right call site is
112
+ * `loader.estimatedMb` once it's resolved against the active tier.
113
+ */
114
+ estimatedMb?: number;
115
+ /**
116
+ * Optional override for the cache TTL on insert. Defaults to the
117
+ * `TtsAudioCache` default (10 min).
118
+ */
119
+ cacheTtlMs?: number;
120
+ }
121
+
122
+ /**
123
+ * Build a `CapabilityRegistration` ready to feed to
124
+ * `arbiter.registerCapability()`. Mirrors
125
+ * `createVisionCapabilityRegistration` from WS2 and
126
+ * `createImageGenCapabilityRegistration` from WS3.
127
+ */
128
+ export function createTtsCapabilityRegistration(
129
+ opts: CreateTtsCapabilityRegistrationOptions,
130
+ ): CapabilityRegistration<TtsBackend, TtsRequest, TtsResult> {
131
+ const capability: ArbiterCapability = "speak";
132
+ const loader = opts.loader;
133
+ const cache = opts.cache;
134
+ const provider = opts.provider ?? "eliza-local-inference";
135
+ const cacheTtlMs = opts.cacheTtlMs;
136
+ return {
137
+ capability,
138
+ estimatedMb: opts.estimatedMb ?? 600,
139
+ async load(modelKey: string): Promise<TtsBackend> {
140
+ return await loader(modelKey);
141
+ },
142
+ async unload(backend: TtsBackend): Promise<void> {
143
+ await backend.dispose();
144
+ },
145
+ async run(backend: TtsBackend, request: TtsRequest): Promise<TtsResult> {
146
+ // 1. Reject unsupported requests cleanly — no silent fallback.
147
+ if (!backend.supports(request)) {
148
+ throw new TtsBackendUnavailableError(
149
+ backend.id,
150
+ "unsupported_request",
151
+ `[tts] backend "${backend.id}" does not support this request (voice=${request.voice ?? "default"} sampleRate=${request.sampleRate ?? "native"})`,
152
+ );
153
+ }
154
+
155
+ // 2. Cache lookup (if a cache is wired).
156
+ let cacheKey: string | null = null;
157
+ if (cache) {
158
+ cacheKey = hashTtsRequest({
159
+ provider,
160
+ model: backend.id,
161
+ voice: request.voice ?? "default",
162
+ text: request.text,
163
+ ...(typeof request.speed === "number"
164
+ ? { speed: request.speed }
165
+ : {}),
166
+ ...(typeof request.sampleRate === "number"
167
+ ? { sampleRate: request.sampleRate }
168
+ : {}),
169
+ });
170
+ const hit = cache.get(cacheKey);
171
+ if (hit && hit.live !== false) {
172
+ return {
173
+ audio: hit.audio,
174
+ mime: hit.mime,
175
+ sampleRate: hit.sampleRate,
176
+ metadata: {
177
+ model: hit.model,
178
+ voice: hit.voice,
179
+ text: request.text,
180
+ inferenceTimeMs: 0,
181
+ cacheHit: true,
182
+ },
183
+ };
184
+ }
185
+ }
186
+
187
+ // 3. Miss — synthesize through the backend.
188
+ const result = await backend.synthesize(request);
189
+
190
+ // 4. Populate cache (best-effort).
191
+ if (cache && cacheKey) {
192
+ cache.set(
193
+ cacheKey,
194
+ {
195
+ audio: result.audio,
196
+ mime: result.mime,
197
+ sampleRate: result.sampleRate,
198
+ voice: result.metadata.voice,
199
+ model: result.metadata.model,
200
+ },
201
+ cacheTtlMs,
202
+ );
203
+ }
204
+
205
+ return {
206
+ ...result,
207
+ metadata: {
208
+ ...result.metadata,
209
+ cacheHit: false,
210
+ },
211
+ };
212
+ },
213
+ };
214
+ }
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Content-hashed cache for synthesized TTS audio. WS5 capability-level
3
+ * cache that sits between the WS1 `MemoryArbiter` and the concrete TTS
4
+ * backend (OmniVoice / Kokoro / fake).
5
+ *
6
+ * Cache namespace: `tts-audio`.
7
+ *
8
+ * Why a capability-level cache (in addition to the existing
9
+ * `wrap-with-first-line-cache.ts`):
10
+ *
11
+ * - `wrap-with-first-line-cache.ts` is a *first-sentence* cache. It
12
+ * accelerates the audible TTFA on the planner's typical "short
13
+ * acknowledgement" leading sentence. It's keyed on `(provider,
14
+ * voiceId, voiceRevision, codec, voiceSettingsFingerprint,
15
+ * normalizedFirstSentence)`.
16
+ *
17
+ * - This cache is a *full-utterance* cache. It accelerates repeated
18
+ * synthesis of identical inputs across turns (system prompts, idle
19
+ * fillers, the user replaying a previously-spoken sentence). It's
20
+ * keyed on `sha256(provider || model || voice || text)`. Hits short-
21
+ * circuit `backend.synthesize` entirely; the arbiter returns the
22
+ * cached bytes and records `metadata.cacheHit = true`.
23
+ *
24
+ * The two layers are independent. A request can hit the first-line cache,
25
+ * a partially-matching prefix can hit this cache, both can miss — and the
26
+ * keying spaces don't collide because the first-line cache keys on the
27
+ * *first sentence's* normalized text, while this one keys on the full
28
+ * text. Wire both at boot for maximum hit rate.
29
+ *
30
+ * Contract:
31
+ * - Caller computes a stable hash from a deterministic key (the request
32
+ * plus the active model). The hash is the cache key.
33
+ * - Caller pairs the hash with the synthesized bytes AND the
34
+ * `(mime, sampleRate, voice, model)` quadruple so a reader can
35
+ * reproduce a full `TtsResult` on hit.
36
+ * - `get(hash)` returns `null` on miss or expiry, the entry on hit. A
37
+ * hit also "touches" the entry to keep it warm under LRU.
38
+ * - `set(hash, entry, ttlMs?)` inserts with a TTL (default 10 min);
39
+ * if the LRU is full, the coldest entry is evicted.
40
+ */
41
+
42
+ import { createHash } from "node:crypto";
43
+ import type { TtsMimeType } from "./types";
44
+
45
+ interface CacheEntry {
46
+ audio: Uint8Array;
47
+ mime: TtsMimeType;
48
+ sampleRate: number;
49
+ voice: string;
50
+ model: string;
51
+ expiresAtMs: number;
52
+ }
53
+
54
+ export interface TtsAudioEntry {
55
+ audio: Uint8Array;
56
+ mime: TtsMimeType;
57
+ sampleRate: number;
58
+ voice: string;
59
+ model: string;
60
+ /** True when this entry is still within its TTL. */
61
+ live: boolean;
62
+ }
63
+
64
+ export interface TtsAudioCacheConfig {
65
+ /** Max entries retained. LRU evicts beyond this. Default 64. */
66
+ maxEntries: number;
67
+ /** Default TTL when `set()` is called without one. Default 10 min. */
68
+ defaultTtlMs: number;
69
+ /** Optional max total bytes; an LRU sweep enforces this on insert. Default 64 MB. */
70
+ maxBytes: number;
71
+ }
72
+
73
+ const DEFAULTS: TtsAudioCacheConfig = {
74
+ maxEntries: 64,
75
+ defaultTtlMs: 10 * 60_000,
76
+ maxBytes: 64 * 1024 * 1024,
77
+ };
78
+
79
+ /**
80
+ * Build the canonical cache key for a TTS request + active model. The
81
+ * fields are joined with a separator that can't appear in any of them
82
+ * (a NUL byte) and SHA-256'd. The model id participates so a tier swap
83
+ * doesn't yield phantom hits from a different voice family.
84
+ */
85
+ export function hashTtsRequest(args: {
86
+ provider: string;
87
+ model: string;
88
+ voice: string;
89
+ text: string;
90
+ speed?: number;
91
+ sampleRate?: number;
92
+ }): string {
93
+ const h = createHash("sha256");
94
+ h.update(args.provider);
95
+ h.update("\0");
96
+ h.update(args.model);
97
+ h.update("\0");
98
+ h.update(args.voice);
99
+ h.update("\0");
100
+ h.update(args.text);
101
+ h.update("\0");
102
+ h.update(typeof args.speed === "number" ? String(args.speed) : "");
103
+ h.update("\0");
104
+ h.update(typeof args.sampleRate === "number" ? String(args.sampleRate) : "");
105
+ return h.digest("hex");
106
+ }
107
+
108
+ export class TtsAudioCache {
109
+ private readonly config: TtsAudioCacheConfig;
110
+ /**
111
+ * `Map` preserves insertion order; we re-insert on hit to bubble
112
+ * entries to the back, so the first key in iteration order is the
113
+ * LRU candidate.
114
+ */
115
+ private readonly entries = new Map<string, CacheEntry>();
116
+ private readonly now: () => number;
117
+ private totalBytes = 0;
118
+
119
+ constructor(
120
+ opts: {
121
+ config?: Partial<TtsAudioCacheConfig>;
122
+ now?: () => number;
123
+ } = {},
124
+ ) {
125
+ this.config = {
126
+ maxEntries: Math.max(1, opts.config?.maxEntries ?? DEFAULTS.maxEntries),
127
+ defaultTtlMs: Math.max(
128
+ 0,
129
+ opts.config?.defaultTtlMs ?? DEFAULTS.defaultTtlMs,
130
+ ),
131
+ maxBytes: Math.max(1024, opts.config?.maxBytes ?? DEFAULTS.maxBytes),
132
+ };
133
+ this.now = opts.now ?? (() => Date.now());
134
+ }
135
+
136
+ get(hash: string): TtsAudioEntry | null {
137
+ const found = this.entries.get(hash);
138
+ if (!found) return null;
139
+ const live = found.expiresAtMs > this.now();
140
+ if (!live) {
141
+ this.entries.delete(hash);
142
+ this.totalBytes -= found.audio.byteLength;
143
+ return null;
144
+ }
145
+ // Bubble to MRU position.
146
+ this.entries.delete(hash);
147
+ this.entries.set(hash, found);
148
+ return {
149
+ audio: found.audio,
150
+ mime: found.mime,
151
+ sampleRate: found.sampleRate,
152
+ voice: found.voice,
153
+ model: found.model,
154
+ live: true,
155
+ };
156
+ }
157
+
158
+ set(
159
+ hash: string,
160
+ entry: {
161
+ audio: Uint8Array;
162
+ mime: TtsMimeType;
163
+ sampleRate: number;
164
+ voice: string;
165
+ model: string;
166
+ },
167
+ ttlMs?: number,
168
+ ): void {
169
+ const ttl = typeof ttlMs === "number" ? ttlMs : this.config.defaultTtlMs;
170
+ if (ttl <= 0) return;
171
+ const existing = this.entries.get(hash);
172
+ if (existing) this.totalBytes -= existing.audio.byteLength;
173
+ const record: CacheEntry = {
174
+ audio: entry.audio,
175
+ mime: entry.mime,
176
+ sampleRate: entry.sampleRate,
177
+ voice: entry.voice,
178
+ model: entry.model,
179
+ expiresAtMs: this.now() + ttl,
180
+ };
181
+ this.entries.set(hash, record);
182
+ this.totalBytes += entry.audio.byteLength;
183
+ this.enforceBudget();
184
+ }
185
+
186
+ has(hash: string): boolean {
187
+ const found = this.entries.get(hash);
188
+ if (!found) return false;
189
+ if (found.expiresAtMs <= this.now()) {
190
+ this.entries.delete(hash);
191
+ this.totalBytes -= found.audio.byteLength;
192
+ return false;
193
+ }
194
+ return true;
195
+ }
196
+
197
+ purgeExpired(nowMs: number = this.now()): number {
198
+ let removed = 0;
199
+ for (const [key, value] of this.entries) {
200
+ if (value.expiresAtMs <= nowMs) {
201
+ this.entries.delete(key);
202
+ this.totalBytes -= value.audio.byteLength;
203
+ removed += 1;
204
+ }
205
+ }
206
+ return removed;
207
+ }
208
+
209
+ clear(): void {
210
+ this.entries.clear();
211
+ this.totalBytes = 0;
212
+ }
213
+
214
+ size(): number {
215
+ return this.entries.size;
216
+ }
217
+
218
+ bytes(): number {
219
+ return this.totalBytes;
220
+ }
221
+
222
+ private enforceBudget(): void {
223
+ while (
224
+ this.entries.size > this.config.maxEntries ||
225
+ this.totalBytes > this.config.maxBytes
226
+ ) {
227
+ const oldest = this.entries.keys().next();
228
+ if (oldest.done) break;
229
+ const key = oldest.value;
230
+ const removed = this.entries.get(key);
231
+ this.entries.delete(key);
232
+ if (removed) this.totalBytes -= removed.audio.byteLength;
233
+ }
234
+ }
235
+ }