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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (701) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/package.json +82 -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/actions/transcription-control.d.ts +29 -0
  11. package/src/actions/transcription-control.d.ts.map +1 -0
  12. package/src/actions/transcription-control.test.ts +100 -0
  13. package/src/actions/transcription-control.ts +127 -0
  14. package/src/adapters/capacitor-llama/__tests__/compat-behavior.test.ts +218 -0
  15. package/src/adapters/capacitor-llama/__tests__/index.test.ts +68 -0
  16. package/src/adapters/capacitor-llama/__tests__/structured-output.test.ts +215 -0
  17. package/src/adapters/capacitor-llama/__tests__/text-streaming.test.ts +174 -0
  18. package/src/adapters/capacitor-llama/environment.ts +71 -0
  19. package/src/adapters/capacitor-llama/index.browser.ts +83 -0
  20. package/src/adapters/capacitor-llama/index.ts +807 -0
  21. package/src/adapters/capacitor-llama/loader.ts +109 -0
  22. package/src/adapters/capacitor-llama/structured-output.ts +165 -0
  23. package/src/adapters/capacitor-llama/text-streaming.ts +227 -0
  24. package/src/adapters/capacitor-llama/types.ts +374 -0
  25. package/src/backends/apple-foundation.ts +127 -0
  26. package/src/index.d.ts +8 -0
  27. package/src/index.d.ts.map +1 -0
  28. package/src/index.ts +62 -0
  29. package/src/local-inference-routes.d.ts +38 -0
  30. package/src/local-inference-routes.d.ts.map +1 -0
  31. package/src/local-inference-routes.test.ts +344 -0
  32. package/src/local-inference-routes.ts +1543 -0
  33. package/src/provider.d.ts +21 -0
  34. package/src/provider.d.ts.map +1 -0
  35. package/src/provider.ts +1082 -0
  36. package/src/routes/compat-helpers.d.ts +18 -0
  37. package/src/routes/compat-helpers.d.ts.map +1 -0
  38. package/src/routes/compat-helpers.ts +274 -0
  39. package/src/routes/family-member-route.d.ts +62 -0
  40. package/src/routes/family-member-route.d.ts.map +1 -0
  41. package/src/routes/family-member-route.ts +353 -0
  42. package/src/routes/index.d.ts +19 -0
  43. package/src/routes/index.d.ts.map +1 -0
  44. package/src/routes/index.ts +60 -0
  45. package/src/routes/live-diarization-route.d.ts +26 -0
  46. package/src/routes/live-diarization-route.d.ts.map +1 -0
  47. package/src/routes/live-diarization-route.test.ts +213 -0
  48. package/src/routes/live-diarization-route.ts +122 -0
  49. package/src/routes/local-inference-asr-route.d.ts +4 -0
  50. package/src/routes/local-inference-asr-route.d.ts.map +1 -0
  51. package/src/routes/local-inference-asr-route.test.ts +205 -0
  52. package/src/routes/local-inference-asr-route.ts +163 -0
  53. package/src/routes/local-inference-asr-transcribe.d.ts +20 -0
  54. package/src/routes/local-inference-asr-transcribe.d.ts.map +1 -0
  55. package/src/routes/local-inference-asr-transcribe.test.ts +118 -0
  56. package/src/routes/local-inference-asr-transcribe.ts +97 -0
  57. package/src/routes/local-inference-compat-routes.d.ts +16 -0
  58. package/src/routes/local-inference-compat-routes.d.ts.map +1 -0
  59. package/src/routes/local-inference-compat-routes.test.ts +485 -0
  60. package/src/routes/local-inference-compat-routes.ts +808 -0
  61. package/src/routes/local-inference-tts-route.d.ts +7 -0
  62. package/src/routes/local-inference-tts-route.d.ts.map +1 -0
  63. package/src/routes/local-inference-tts-route.test.ts +179 -0
  64. package/src/routes/local-inference-tts-route.ts +230 -0
  65. package/src/routes/transcript-audio-store.d.ts +15 -0
  66. package/src/routes/transcript-audio-store.d.ts.map +1 -0
  67. package/src/routes/transcript-audio-store.ts +27 -0
  68. package/src/routes/transcripts-routes.d.ts +36 -0
  69. package/src/routes/transcripts-routes.d.ts.map +1 -0
  70. package/src/routes/transcripts-routes.test.ts +144 -0
  71. package/src/routes/transcripts-routes.ts +159 -0
  72. package/src/routes/voice-first-run-routes.d.ts +62 -0
  73. package/src/routes/voice-first-run-routes.d.ts.map +1 -0
  74. package/src/routes/voice-first-run-routes.ts +524 -0
  75. package/src/routes/voice-models-routes.d.ts +62 -0
  76. package/src/routes/voice-models-routes.d.ts.map +1 -0
  77. package/src/routes/voice-models-routes.ts +554 -0
  78. package/src/routes/voice-profile-plugin-routes.d.ts +19 -0
  79. package/src/routes/voice-profile-plugin-routes.d.ts.map +1 -0
  80. package/src/routes/voice-profile-plugin-routes.ts +138 -0
  81. package/src/routes/voice-profiles-management-routes.d.ts +52 -0
  82. package/src/routes/voice-profiles-management-routes.d.ts.map +1 -0
  83. package/src/routes/voice-profiles-management-routes.ts +476 -0
  84. package/src/routes/voice-speaker-profile-routes.d.ts +57 -0
  85. package/src/routes/voice-speaker-profile-routes.d.ts.map +1 -0
  86. package/src/routes/voice-speaker-profile-routes.ts +199 -0
  87. package/src/runtime/aosp-llama-loader-selection.test.ts +80 -0
  88. package/src/runtime/capacitor-llama.d.ts +25 -0
  89. package/src/runtime/embedding-manager-support.d.ts +77 -0
  90. package/src/runtime/embedding-manager-support.d.ts.map +1 -0
  91. package/src/runtime/embedding-manager-support.ts +497 -0
  92. package/src/runtime/embedding-presets.d.ts +16 -0
  93. package/src/runtime/embedding-presets.d.ts.map +1 -0
  94. package/src/runtime/embedding-presets.ts +81 -0
  95. package/src/runtime/embedding-warmup-policy.d.ts +14 -0
  96. package/src/runtime/embedding-warmup-policy.d.ts.map +1 -0
  97. package/src/runtime/embedding-warmup-policy.test.ts +53 -0
  98. package/src/runtime/embedding-warmup-policy.ts +48 -0
  99. package/src/runtime/ensure-local-inference-handler.d.ts +62 -0
  100. package/src/runtime/ensure-local-inference-handler.d.ts.map +1 -0
  101. package/src/runtime/ensure-local-inference-handler.test.ts +528 -0
  102. package/src/runtime/ensure-local-inference-handler.ts +1448 -0
  103. package/src/runtime/index.d.ts +15 -0
  104. package/src/runtime/index.d.ts.map +1 -0
  105. package/src/runtime/index.ts +33 -0
  106. package/src/runtime/mobile-local-inference-gate.d.ts +31 -0
  107. package/src/runtime/mobile-local-inference-gate.d.ts.map +1 -0
  108. package/src/runtime/mobile-local-inference-gate.test.ts +69 -0
  109. package/src/runtime/mobile-local-inference-gate.ts +44 -0
  110. package/src/runtime/voice-entity-binding.d.ts +103 -0
  111. package/src/runtime/voice-entity-binding.d.ts.map +1 -0
  112. package/src/runtime/voice-entity-binding.transcript.test.ts +69 -0
  113. package/src/runtime/voice-entity-binding.ts +328 -0
  114. package/src/services/README.md +71 -0
  115. package/src/services/__tests__/backend-selector.test.ts +101 -0
  116. package/src/services/__tests__/checkpoint-manager.test.ts +376 -0
  117. package/src/services/__tests__/gpu-autotune.test.ts +400 -0
  118. package/src/services/__tests__/llm-streaming-binding.test.ts +85 -0
  119. package/src/services/__tests__/planner-grammar.test.ts +372 -0
  120. package/src/services/__tests__/runtime-target.test.ts +176 -0
  121. package/src/services/active-model-switch-rollback.test.ts +183 -0
  122. package/src/services/active-model.d.ts +282 -0
  123. package/src/services/active-model.d.ts.map +1 -0
  124. package/src/services/active-model.ts +1213 -0
  125. package/src/services/assignments.d.ts +71 -0
  126. package/src/services/assignments.d.ts.map +1 -0
  127. package/src/services/assignments.test.ts +80 -0
  128. package/src/services/assignments.ts +230 -0
  129. package/src/services/backend-selector.ts +95 -0
  130. package/src/services/backend.d.ts +346 -0
  131. package/src/services/backend.d.ts.map +1 -0
  132. package/src/services/backend.ts +612 -0
  133. package/src/services/bionic-host-loader.d.ts +46 -0
  134. package/src/services/bionic-host-loader.d.ts.map +1 -0
  135. package/src/services/bionic-host-loader.test.ts +133 -0
  136. package/src/services/bionic-host-loader.ts +180 -0
  137. package/src/services/bundled-models.d.ts +34 -0
  138. package/src/services/bundled-models.d.ts.map +1 -0
  139. package/src/services/bundled-models.ts +129 -0
  140. package/src/services/cache-bridge.d.ts +206 -0
  141. package/src/services/cache-bridge.d.ts.map +1 -0
  142. package/src/services/cache-bridge.test.ts +516 -0
  143. package/src/services/cache-bridge.ts +423 -0
  144. package/src/services/catalog.d.ts +10 -0
  145. package/src/services/catalog.d.ts.map +1 -0
  146. package/src/services/catalog.test.ts +238 -0
  147. package/src/services/catalog.ts +27 -0
  148. package/src/services/checkpoint-client.d.ts +109 -0
  149. package/src/services/checkpoint-client.d.ts.map +1 -0
  150. package/src/services/checkpoint-client.ts +258 -0
  151. package/src/services/checkpoint-manager.ts +474 -0
  152. package/src/services/cloud-fallback.d.ts +102 -0
  153. package/src/services/cloud-fallback.d.ts.map +1 -0
  154. package/src/services/cloud-fallback.ts +230 -0
  155. package/src/services/conversation-registry.d.ts +142 -0
  156. package/src/services/conversation-registry.d.ts.map +1 -0
  157. package/src/services/conversation-registry.test.ts +235 -0
  158. package/src/services/conversation-registry.ts +264 -0
  159. package/src/services/desktop-fused-ffi-backend-runtime.d.ts +95 -0
  160. package/src/services/desktop-fused-ffi-backend-runtime.d.ts.map +1 -0
  161. package/src/services/desktop-fused-ffi-backend-runtime.ts +339 -0
  162. package/src/services/device-bridge.d.ts +188 -0
  163. package/src/services/device-bridge.d.ts.map +1 -0
  164. package/src/services/device-bridge.ts +1237 -0
  165. package/src/services/device-resource-metrics.d.ts +149 -0
  166. package/src/services/device-resource-metrics.d.ts.map +1 -0
  167. package/src/services/device-resource-metrics.test.ts +98 -0
  168. package/src/services/device-resource-metrics.ts +346 -0
  169. package/src/services/device-tier.d.ts +115 -0
  170. package/src/services/device-tier.d.ts.map +1 -0
  171. package/src/services/device-tier.test.ts +371 -0
  172. package/src/services/device-tier.ts +410 -0
  173. package/src/services/downloader.d.ts +82 -0
  174. package/src/services/downloader.d.ts.map +1 -0
  175. package/src/services/downloader.test.ts +747 -0
  176. package/src/services/downloader.ts +925 -0
  177. package/src/services/engine-direct-bundle.test.ts +58 -0
  178. package/src/services/engine-streaming.test.ts +80 -0
  179. package/src/services/engine.d.ts +540 -0
  180. package/src/services/engine.d.ts.map +1 -0
  181. package/src/services/engine.ts +1909 -0
  182. package/src/services/ensure-local-artifacts.integration.test.ts +273 -0
  183. package/src/services/ensure-local-artifacts.test.ts +368 -0
  184. package/src/services/ensure-local-artifacts.ts +351 -0
  185. package/src/services/external-scanner.d.ts +17 -0
  186. package/src/services/external-scanner.d.ts.map +1 -0
  187. package/src/services/external-scanner.ts +312 -0
  188. package/src/services/ffi-llm-mock.ts +354 -0
  189. package/src/services/ffi-llm-streaming-abi.ts +442 -0
  190. package/src/services/ffi-streaming-backend.d.ts +180 -0
  191. package/src/services/ffi-streaming-backend.d.ts.map +1 -0
  192. package/src/services/ffi-streaming-backend.ts +382 -0
  193. package/src/services/ffi-streaming-runner.d.ts +122 -0
  194. package/src/services/ffi-streaming-runner.d.ts.map +1 -0
  195. package/src/services/ffi-streaming-runner.test.ts +60 -0
  196. package/src/services/ffi-streaming-runner.ts +354 -0
  197. package/src/services/ffi-unload-ordering.test.ts +162 -0
  198. package/src/services/gpu-autotune.ts +534 -0
  199. package/src/services/gpu-detect.d.ts +56 -0
  200. package/src/services/gpu-detect.d.ts.map +1 -0
  201. package/src/services/gpu-detect.ts +139 -0
  202. package/src/services/handler-registry.d.ts +72 -0
  203. package/src/services/handler-registry.d.ts.map +1 -0
  204. package/src/services/handler-registry.ts +240 -0
  205. package/src/services/hardware.d.ts +63 -0
  206. package/src/services/hardware.d.ts.map +1 -0
  207. package/src/services/hardware.test.ts +231 -0
  208. package/src/services/hardware.ts +410 -0
  209. package/src/services/hf-search.d.ts +26 -0
  210. package/src/services/hf-search.d.ts.map +1 -0
  211. package/src/services/hf-search.test.ts +69 -0
  212. package/src/services/hf-search.ts +420 -0
  213. package/src/services/image-description-runtime.d.ts +14 -0
  214. package/src/services/image-description-runtime.d.ts.map +1 -0
  215. package/src/services/image-description-runtime.test.ts +61 -0
  216. package/src/services/image-description-runtime.ts +118 -0
  217. package/src/services/imagegen/aosp-unavailable.d.ts +134 -0
  218. package/src/services/imagegen/aosp-unavailable.d.ts.map +1 -0
  219. package/src/services/imagegen/aosp-unavailable.ts +229 -0
  220. package/src/services/imagegen/backend-selector.d.ts +118 -0
  221. package/src/services/imagegen/backend-selector.d.ts.map +1 -0
  222. package/src/services/imagegen/backend-selector.ts +277 -0
  223. package/src/services/imagegen/coreml-unavailable.d.ts +105 -0
  224. package/src/services/imagegen/coreml-unavailable.d.ts.map +1 -0
  225. package/src/services/imagegen/coreml-unavailable.ts +237 -0
  226. package/src/services/imagegen/errors.d.ts +16 -0
  227. package/src/services/imagegen/errors.d.ts.map +1 -0
  228. package/src/services/imagegen/errors.ts +40 -0
  229. package/src/services/imagegen/index.d.ts +58 -0
  230. package/src/services/imagegen/index.d.ts.map +1 -0
  231. package/src/services/imagegen/index.ts +144 -0
  232. package/src/services/imagegen/mflux.d.ts +74 -0
  233. package/src/services/imagegen/mflux.d.ts.map +1 -0
  234. package/src/services/imagegen/mflux.ts +313 -0
  235. package/src/services/imagegen/sd-cpp.d.ts +180 -0
  236. package/src/services/imagegen/sd-cpp.d.ts.map +1 -0
  237. package/src/services/imagegen/sd-cpp.ts +718 -0
  238. package/src/services/imagegen/tensorrt-unavailable.d.ts +83 -0
  239. package/src/services/imagegen/tensorrt-unavailable.d.ts.map +1 -0
  240. package/src/services/imagegen/tensorrt-unavailable.ts +295 -0
  241. package/src/services/imagegen/types.d.ts +181 -0
  242. package/src/services/imagegen/types.d.ts.map +1 -0
  243. package/src/services/imagegen/types.ts +193 -0
  244. package/src/services/index.d.ts +29 -0
  245. package/src/services/index.d.ts.map +1 -0
  246. package/src/services/index.ts +211 -0
  247. package/src/services/inference-capabilities.d.ts +132 -0
  248. package/src/services/inference-capabilities.d.ts.map +1 -0
  249. package/src/services/inference-capabilities.test.ts +75 -0
  250. package/src/services/inference-capabilities.ts +204 -0
  251. package/src/services/inference-telemetry.d.ts +59 -0
  252. package/src/services/inference-telemetry.d.ts.map +1 -0
  253. package/src/services/inference-telemetry.ts +143 -0
  254. package/src/services/ios-llama-streaming.ts +248 -0
  255. package/src/services/kv-spill.d.ts +189 -0
  256. package/src/services/kv-spill.d.ts.map +1 -0
  257. package/src/services/kv-spill.test.ts +222 -0
  258. package/src/services/kv-spill.ts +356 -0
  259. package/src/services/latency-trace.d.ts +346 -0
  260. package/src/services/latency-trace.d.ts.map +1 -0
  261. package/src/services/latency-trace.test.ts +266 -0
  262. package/src/services/latency-trace.ts +844 -0
  263. package/src/services/llama-server-metrics.ts +304 -0
  264. package/src/services/llm-streaming-binding.d.ts +96 -0
  265. package/src/services/llm-streaming-binding.d.ts.map +1 -0
  266. package/src/services/llm-streaming-binding.ts +136 -0
  267. package/src/services/load-args.d.ts +82 -0
  268. package/src/services/load-args.d.ts.map +1 -0
  269. package/src/services/load-args.ts +81 -0
  270. package/src/services/manifest/eliza-1.manifest.v1.json +708 -0
  271. package/src/services/manifest/index.d.ts +4 -0
  272. package/src/services/manifest/index.d.ts.map +1 -0
  273. package/src/services/manifest/index.ts +66 -0
  274. package/src/services/manifest/manifest.test.ts +689 -0
  275. package/src/services/manifest/schema.d.ts +713 -0
  276. package/src/services/manifest/schema.d.ts.map +1 -0
  277. package/src/services/manifest/schema.ts +653 -0
  278. package/src/services/manifest/types.d.ts +30 -0
  279. package/src/services/manifest/types.d.ts.map +1 -0
  280. package/src/services/manifest/types.ts +55 -0
  281. package/src/services/manifest/validator.d.ts +66 -0
  282. package/src/services/manifest/validator.d.ts.map +1 -0
  283. package/src/services/manifest/validator.ts +567 -0
  284. package/src/services/memory-arbiter.d.ts +318 -0
  285. package/src/services/memory-arbiter.d.ts.map +1 -0
  286. package/src/services/memory-arbiter.test.ts +419 -0
  287. package/src/services/memory-arbiter.ts +925 -0
  288. package/src/services/memory-monitor.d.ts +122 -0
  289. package/src/services/memory-monitor.d.ts.map +1 -0
  290. package/src/services/memory-monitor.test.ts +208 -0
  291. package/src/services/memory-monitor.ts +297 -0
  292. package/src/services/memory-pressure.d.ts +130 -0
  293. package/src/services/memory-pressure.d.ts.map +1 -0
  294. package/src/services/memory-pressure.ts +414 -0
  295. package/src/services/mtp-doctor.d.ts +13 -0
  296. package/src/services/mtp-doctor.d.ts.map +1 -0
  297. package/src/services/mtp-doctor.ts +78 -0
  298. package/src/services/network-policy.d.ts +127 -0
  299. package/src/services/network-policy.d.ts.map +1 -0
  300. package/src/services/network-policy.ts +346 -0
  301. package/src/services/paths.d.ts +6 -0
  302. package/src/services/paths.d.ts.map +1 -0
  303. package/src/services/paths.ts +25 -0
  304. package/src/services/planner-skeleton.d.ts +124 -0
  305. package/src/services/planner-skeleton.d.ts.map +1 -0
  306. package/src/services/planner-skeleton.ts +175 -0
  307. package/src/services/providers.d.ts +38 -0
  308. package/src/services/providers.d.ts.map +1 -0
  309. package/src/services/providers.ts +507 -0
  310. package/src/services/ram-budget-cache.test.ts +163 -0
  311. package/src/services/ram-budget.d.ts +110 -0
  312. package/src/services/ram-budget.d.ts.map +1 -0
  313. package/src/services/ram-budget.ts +0 -0
  314. package/src/services/readiness.d.ts +9 -0
  315. package/src/services/readiness.d.ts.map +1 -0
  316. package/src/services/readiness.test.ts +87 -0
  317. package/src/services/readiness.ts +238 -0
  318. package/src/services/recommendation.d.ts +111 -0
  319. package/src/services/recommendation.d.ts.map +1 -0
  320. package/src/services/recommendation.ts +671 -0
  321. package/src/services/registry.d.ts +35 -0
  322. package/src/services/registry.d.ts.map +1 -0
  323. package/src/services/registry.ts +151 -0
  324. package/src/services/router-handler.d.ts +92 -0
  325. package/src/services/router-handler.d.ts.map +1 -0
  326. package/src/services/router-handler.test.ts +45 -0
  327. package/src/services/router-handler.ts +407 -0
  328. package/src/services/routing-policy.d.ts +69 -0
  329. package/src/services/routing-policy.d.ts.map +1 -0
  330. package/src/services/routing-policy.test.ts +164 -0
  331. package/src/services/routing-policy.ts +297 -0
  332. package/src/services/routing-preferences.d.ts +8 -0
  333. package/src/services/routing-preferences.d.ts.map +1 -0
  334. package/src/services/routing-preferences.ts +17 -0
  335. package/src/services/runtime-target.d.ts +98 -0
  336. package/src/services/runtime-target.d.ts.map +1 -0
  337. package/src/services/runtime-target.ts +154 -0
  338. package/src/services/service.d.ts +128 -0
  339. package/src/services/service.d.ts.map +1 -0
  340. package/src/services/service.test.ts +223 -0
  341. package/src/services/service.ts +735 -0
  342. package/src/services/session-pool.d.ts +72 -0
  343. package/src/services/session-pool.d.ts.map +1 -0
  344. package/src/services/session-pool.ts +153 -0
  345. package/src/services/structured-output/deterministic-repair.d.ts +23 -0
  346. package/src/services/structured-output/deterministic-repair.d.ts.map +1 -0
  347. package/src/services/structured-output/deterministic-repair.test.ts +169 -0
  348. package/src/services/structured-output/deterministic-repair.ts +443 -0
  349. package/src/services/structured-output/index.ts +4 -0
  350. package/src/services/structured-output.d.ts +311 -0
  351. package/src/services/structured-output.d.ts.map +1 -0
  352. package/src/services/structured-output.test.ts +483 -0
  353. package/src/services/structured-output.ts +712 -0
  354. package/src/services/system-memory.d.ts +33 -0
  355. package/src/services/system-memory.d.ts.map +1 -0
  356. package/src/services/system-memory.test.ts +47 -0
  357. package/src/services/system-memory.ts +67 -0
  358. package/src/services/transcription-priority.test.ts +211 -0
  359. package/src/services/types.d.ts +19 -0
  360. package/src/services/types.d.ts.map +1 -0
  361. package/src/services/types.ts +55 -0
  362. package/src/services/verify-on-device.d.ts +34 -0
  363. package/src/services/verify-on-device.d.ts.map +1 -0
  364. package/src/services/verify-on-device.test.ts +87 -0
  365. package/src/services/verify-on-device.ts +127 -0
  366. package/src/services/verify.d.ts +8 -0
  367. package/src/services/verify.d.ts.map +1 -0
  368. package/src/services/verify.ts +13 -0
  369. package/src/services/vision/aosp-unavailable.d.ts +115 -0
  370. package/src/services/vision/aosp-unavailable.d.ts.map +1 -0
  371. package/src/services/vision/aosp-unavailable.ts +163 -0
  372. package/src/services/vision/capacitor-llama.d.ts +99 -0
  373. package/src/services/vision/capacitor-llama.d.ts.map +1 -0
  374. package/src/services/vision/capacitor-llama.ts +255 -0
  375. package/src/services/vision/cloud-fallback.d.ts +47 -0
  376. package/src/services/vision/cloud-fallback.d.ts.map +1 -0
  377. package/src/services/vision/cloud-fallback.test.ts +243 -0
  378. package/src/services/vision/cloud-fallback.ts +268 -0
  379. package/src/services/vision/fallback-chain.test.ts +86 -0
  380. package/src/services/vision/hash.d.ts +71 -0
  381. package/src/services/vision/hash.d.ts.map +1 -0
  382. package/src/services/vision/hash.ts +157 -0
  383. package/src/services/vision/index.d.ts +95 -0
  384. package/src/services/vision/index.d.ts.map +1 -0
  385. package/src/services/vision/index.ts +251 -0
  386. package/src/services/vision/llama-server.d.ts +73 -0
  387. package/src/services/vision/llama-server.d.ts.map +1 -0
  388. package/src/services/vision/llama-server.ts +177 -0
  389. package/src/services/vision/types.d.ts +153 -0
  390. package/src/services/vision/types.d.ts.map +1 -0
  391. package/src/services/vision/types.ts +154 -0
  392. package/src/services/vision/vast-fallback.d.ts +18 -0
  393. package/src/services/vision/vast-fallback.d.ts.map +1 -0
  394. package/src/services/vision/vast-fallback.ts +127 -0
  395. package/src/services/vision-embedding-cache.d.ts +98 -0
  396. package/src/services/vision-embedding-cache.d.ts.map +1 -0
  397. package/src/services/vision-embedding-cache.ts +189 -0
  398. package/src/services/voice/VOICE_WORKBENCH.md +88 -0
  399. package/src/services/voice/__test-helpers__/fake-ffi.ts +94 -0
  400. package/src/services/voice/__test-helpers__/synthetic-speech.ts +124 -0
  401. package/src/services/voice/__tests__/checkpoint-manager.test.ts +241 -0
  402. package/src/services/voice/__tests__/checkpoint-policy.test.ts +270 -0
  403. package/src/services/voice/__tests__/eager-context-builder.test.ts +257 -0
  404. package/src/services/voice/__tests__/eliza1-eot-scorer.test.ts +288 -0
  405. package/src/services/voice/__tests__/eot-classifier.test.ts +431 -0
  406. package/src/services/voice/__tests__/optimistic-rollback.test.ts +312 -0
  407. package/src/services/voice/__tests__/prefill-client.test.ts +266 -0
  408. package/src/services/voice/__tests__/prefix-preserving-queue.test.ts +208 -0
  409. package/src/services/voice/__tests__/streaming-asr.test.ts +450 -0
  410. package/src/services/voice/__tests__/streaming-transcriber.test.ts +339 -0
  411. package/src/services/voice/__tests__/turn-detector-resolver.test.ts +195 -0
  412. package/src/services/voice/__tests__/voice-state-machine-prefill.test.ts +275 -0
  413. package/src/services/voice/__tests__/voice-state-machine.test.ts +354 -0
  414. package/src/services/voice/asr-timed.real.test.ts +141 -0
  415. package/src/services/voice/audio-frame-consumer.d.ts +212 -0
  416. package/src/services/voice/audio-frame-consumer.d.ts.map +1 -0
  417. package/src/services/voice/audio-frame-consumer.test.ts +343 -0
  418. package/src/services/voice/audio-frame-consumer.ts +491 -0
  419. package/src/services/voice/barge-in.d.ts +112 -0
  420. package/src/services/voice/barge-in.d.ts.map +1 -0
  421. package/src/services/voice/barge-in.test.ts +244 -0
  422. package/src/services/voice/barge-in.ts +336 -0
  423. package/src/services/voice/cancellation-coordinator.d.ts +127 -0
  424. package/src/services/voice/cancellation-coordinator.d.ts.map +1 -0
  425. package/src/services/voice/cancellation-coordinator.test.ts +196 -0
  426. package/src/services/voice/cancellation-coordinator.ts +269 -0
  427. package/src/services/voice/checkpoint-manager.d.ts +199 -0
  428. package/src/services/voice/checkpoint-manager.d.ts.map +1 -0
  429. package/src/services/voice/checkpoint-manager.ts +401 -0
  430. package/src/services/voice/checkpoint-policy.ts +336 -0
  431. package/src/services/voice/composite-eot-classifier.test.ts +59 -0
  432. package/src/services/voice/e2e-harness.test.ts +182 -0
  433. package/src/services/voice/e2e-harness.ts +743 -0
  434. package/src/services/voice/eager-context-builder.d.ts +170 -0
  435. package/src/services/voice/eager-context-builder.d.ts.map +1 -0
  436. package/src/services/voice/eager-context-builder.ts +262 -0
  437. package/src/services/voice/eliza1-eot-scorer.d.ts +124 -0
  438. package/src/services/voice/eliza1-eot-scorer.d.ts.map +1 -0
  439. package/src/services/voice/eliza1-eot-scorer.ts +242 -0
  440. package/src/services/voice/embedding-server.ts +200 -0
  441. package/src/services/voice/embedding.d.ts +133 -0
  442. package/src/services/voice/embedding.d.ts.map +1 -0
  443. package/src/services/voice/embedding.test.ts +131 -0
  444. package/src/services/voice/embedding.ts +243 -0
  445. package/src/services/voice/emotion-attribution.d.ts +68 -0
  446. package/src/services/voice/emotion-attribution.d.ts.map +1 -0
  447. package/src/services/voice/emotion-attribution.test.ts +129 -0
  448. package/src/services/voice/emotion-attribution.ts +361 -0
  449. package/src/services/voice/engine-bridge-cancellation.test.ts +422 -0
  450. package/src/services/voice/engine-bridge.d.ts +759 -0
  451. package/src/services/voice/engine-bridge.d.ts.map +1 -0
  452. package/src/services/voice/engine-bridge.test.ts +384 -0
  453. package/src/services/voice/engine-bridge.ts +2302 -0
  454. package/src/services/voice/eot-classifier-ggml.d.ts +179 -0
  455. package/src/services/voice/eot-classifier-ggml.d.ts.map +1 -0
  456. package/src/services/voice/eot-classifier-ggml.ts +566 -0
  457. package/src/services/voice/eot-classifier.d.ts +214 -0
  458. package/src/services/voice/eot-classifier.d.ts.map +1 -0
  459. package/src/services/voice/eot-classifier.ts +533 -0
  460. package/src/services/voice/errors.d.ts +20 -0
  461. package/src/services/voice/errors.d.ts.map +1 -0
  462. package/src/services/voice/errors.ts +32 -0
  463. package/src/services/voice/expressive-tags.d.ts +158 -0
  464. package/src/services/voice/expressive-tags.d.ts.map +1 -0
  465. package/src/services/voice/expressive-tags.ts +405 -0
  466. package/src/services/voice/ffi-bindings.d.ts +674 -0
  467. package/src/services/voice/ffi-bindings.d.ts.map +1 -0
  468. package/src/services/voice/ffi-bindings.test.ts +728 -0
  469. package/src/services/voice/ffi-bindings.ts +3225 -0
  470. package/src/services/voice/first-line-cache.d.ts +181 -0
  471. package/src/services/voice/first-line-cache.d.ts.map +1 -0
  472. package/src/services/voice/first-line-cache.ts +725 -0
  473. package/src/services/voice/fused-eot-scorer.d.ts +51 -0
  474. package/src/services/voice/fused-eot-scorer.d.ts.map +1 -0
  475. package/src/services/voice/fused-eot-scorer.ts +135 -0
  476. package/src/services/voice/index.d.ts +91 -0
  477. package/src/services/voice/index.d.ts.map +1 -0
  478. package/src/services/voice/index.ts +481 -0
  479. package/src/services/voice/kokoro/__tests__/kokoro-backend.test.ts +151 -0
  480. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.real.test.ts +151 -0
  481. package/src/services/voice/kokoro/__tests__/kokoro-engine-bridge.test.ts +60 -0
  482. package/src/services/voice/kokoro/__tests__/kokoro-engine-discovery.test.ts +277 -0
  483. package/src/services/voice/kokoro/__tests__/kokoro-ffi-runtime.test.ts +235 -0
  484. package/src/services/voice/kokoro/__tests__/kokoro-runtime.test.ts +95 -0
  485. package/src/services/voice/kokoro/__tests__/phonemizer.test.ts +53 -0
  486. package/src/services/voice/kokoro/__tests__/runtime-selection.test.ts +231 -0
  487. package/src/services/voice/kokoro/__tests__/voices.test.ts +57 -0
  488. package/src/services/voice/kokoro/index.ts +79 -0
  489. package/src/services/voice/kokoro/kokoro-backend.d.ts +72 -0
  490. package/src/services/voice/kokoro/kokoro-backend.d.ts.map +1 -0
  491. package/src/services/voice/kokoro/kokoro-backend.ts +207 -0
  492. package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts +58 -0
  493. package/src/services/voice/kokoro/kokoro-engine-discovery.d.ts.map +1 -0
  494. package/src/services/voice/kokoro/kokoro-engine-discovery.ts +177 -0
  495. package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts +75 -0
  496. package/src/services/voice/kokoro/kokoro-ffi-runtime.d.ts.map +1 -0
  497. package/src/services/voice/kokoro/kokoro-ffi-runtime.ts +233 -0
  498. package/src/services/voice/kokoro/kokoro-runtime.d.ts +100 -0
  499. package/src/services/voice/kokoro/kokoro-runtime.d.ts.map +1 -0
  500. package/src/services/voice/kokoro/kokoro-runtime.ts +170 -0
  501. package/src/services/voice/kokoro/phoneme-stream.ts +123 -0
  502. package/src/services/voice/kokoro/phonemizer.d.ts +50 -0
  503. package/src/services/voice/kokoro/phonemizer.d.ts.map +1 -0
  504. package/src/services/voice/kokoro/phonemizer.ts +344 -0
  505. package/src/services/voice/kokoro/pick-runtime.d.ts +61 -0
  506. package/src/services/voice/kokoro/pick-runtime.d.ts.map +1 -0
  507. package/src/services/voice/kokoro/pick-runtime.test.ts +91 -0
  508. package/src/services/voice/kokoro/pick-runtime.ts +130 -0
  509. package/src/services/voice/kokoro/runtime-selection.d.ts +92 -0
  510. package/src/services/voice/kokoro/runtime-selection.d.ts.map +1 -0
  511. package/src/services/voice/kokoro/runtime-selection.ts +237 -0
  512. package/src/services/voice/kokoro/types.d.ts +82 -0
  513. package/src/services/voice/kokoro/types.d.ts.map +1 -0
  514. package/src/services/voice/kokoro/types.ts +95 -0
  515. package/src/services/voice/kokoro/voice-presets.d.ts +23 -0
  516. package/src/services/voice/kokoro/voice-presets.d.ts.map +1 -0
  517. package/src/services/voice/kokoro/voice-presets.ts +129 -0
  518. package/src/services/voice/kokoro/voices.d.ts +30 -0
  519. package/src/services/voice/kokoro/voices.d.ts.map +1 -0
  520. package/src/services/voice/kokoro/voices.ts +64 -0
  521. package/src/services/voice/lifecycle.d.ts +135 -0
  522. package/src/services/voice/lifecycle.d.ts.map +1 -0
  523. package/src/services/voice/lifecycle.test.ts +315 -0
  524. package/src/services/voice/lifecycle.ts +301 -0
  525. package/src/services/voice/live-diarization-session.d.ts +96 -0
  526. package/src/services/voice/live-diarization-session.d.ts.map +1 -0
  527. package/src/services/voice/live-diarization-session.ts +289 -0
  528. package/src/services/voice/mic-source.d.ts +136 -0
  529. package/src/services/voice/mic-source.d.ts.map +1 -0
  530. package/src/services/voice/mic-source.test.ts +210 -0
  531. package/src/services/voice/mic-source.ts +503 -0
  532. package/src/services/voice/optimistic-policy.d.ts +109 -0
  533. package/src/services/voice/optimistic-policy.d.ts.map +1 -0
  534. package/src/services/voice/optimistic-policy.test.ts +101 -0
  535. package/src/services/voice/optimistic-policy.ts +192 -0
  536. package/src/services/voice/optimistic-rollback.ts +343 -0
  537. package/src/services/voice/partial-stabilizer.d.ts +73 -0
  538. package/src/services/voice/partial-stabilizer.d.ts.map +1 -0
  539. package/src/services/voice/partial-stabilizer.test.ts +68 -0
  540. package/src/services/voice/partial-stabilizer.ts +140 -0
  541. package/src/services/voice/phoneme-tokenizer.d.ts +49 -0
  542. package/src/services/voice/phoneme-tokenizer.d.ts.map +1 -0
  543. package/src/services/voice/phoneme-tokenizer.ts +158 -0
  544. package/src/services/voice/phrase-cache.d.ts +76 -0
  545. package/src/services/voice/phrase-cache.d.ts.map +1 -0
  546. package/src/services/voice/phrase-cache.test.ts +242 -0
  547. package/src/services/voice/phrase-cache.ts +186 -0
  548. package/src/services/voice/phrase-chunker.d.ts +62 -0
  549. package/src/services/voice/phrase-chunker.d.ts.map +1 -0
  550. package/src/services/voice/phrase-chunker.test.ts +239 -0
  551. package/src/services/voice/phrase-chunker.ts +281 -0
  552. package/src/services/voice/pipeline-impls.d.ts +151 -0
  553. package/src/services/voice/pipeline-impls.d.ts.map +1 -0
  554. package/src/services/voice/pipeline-impls.l6.test.ts +110 -0
  555. package/src/services/voice/pipeline-impls.test.ts +292 -0
  556. package/src/services/voice/pipeline-impls.ts +315 -0
  557. package/src/services/voice/pipeline.d.ts +216 -0
  558. package/src/services/voice/pipeline.d.ts.map +1 -0
  559. package/src/services/voice/pipeline.ts +505 -0
  560. package/src/services/voice/prefill-client.d.ts +123 -0
  561. package/src/services/voice/prefill-client.d.ts.map +1 -0
  562. package/src/services/voice/prefill-client.ts +316 -0
  563. package/src/services/voice/prefix-preserving-queue.d.ts +113 -0
  564. package/src/services/voice/prefix-preserving-queue.d.ts.map +1 -0
  565. package/src/services/voice/prefix-preserving-queue.ts +162 -0
  566. package/src/services/voice/profile-store.d.ts +248 -0
  567. package/src/services/voice/profile-store.d.ts.map +1 -0
  568. package/src/services/voice/profile-store.ts +887 -0
  569. package/src/services/voice/real-audio-decode.test.ts +148 -0
  570. package/src/services/voice/ring-buffer.d.ts +40 -0
  571. package/src/services/voice/ring-buffer.d.ts.map +1 -0
  572. package/src/services/voice/ring-buffer.test.ts +129 -0
  573. package/src/services/voice/ring-buffer.ts +123 -0
  574. package/src/services/voice/rollback-queue.d.ts +24 -0
  575. package/src/services/voice/rollback-queue.d.ts.map +1 -0
  576. package/src/services/voice/rollback-queue.ts +74 -0
  577. package/src/services/voice/samantha-preset-placeholder.d.ts +67 -0
  578. package/src/services/voice/samantha-preset-placeholder.d.ts.map +1 -0
  579. package/src/services/voice/samantha-preset-placeholder.test.ts +97 -0
  580. package/src/services/voice/samantha-preset-placeholder.ts +148 -0
  581. package/src/services/voice/samantha-preset-regenerator.d.ts +87 -0
  582. package/src/services/voice/samantha-preset-regenerator.d.ts.map +1 -0
  583. package/src/services/voice/samantha-preset-regenerator.ts +393 -0
  584. package/src/services/voice/scheduler.d.ts +146 -0
  585. package/src/services/voice/scheduler.d.ts.map +1 -0
  586. package/src/services/voice/scheduler.t2.test.ts +141 -0
  587. package/src/services/voice/scheduler.ts +927 -0
  588. package/src/services/voice/shared-resources.d.ts +190 -0
  589. package/src/services/voice/shared-resources.d.ts.map +1 -0
  590. package/src/services/voice/shared-resources.ts +320 -0
  591. package/src/services/voice/speaker/attribution-pipeline.d.ts +74 -0
  592. package/src/services/voice/speaker/attribution-pipeline.d.ts.map +1 -0
  593. package/src/services/voice/speaker/attribution-pipeline.ts +386 -0
  594. package/src/services/voice/speaker/diarizer-fused.d.ts +59 -0
  595. package/src/services/voice/speaker/diarizer-fused.d.ts.map +1 -0
  596. package/src/services/voice/speaker/diarizer-fused.real.test.ts +100 -0
  597. package/src/services/voice/speaker/diarizer-fused.ts +154 -0
  598. package/src/services/voice/speaker/diarizer.d.ts +75 -0
  599. package/src/services/voice/speaker/diarizer.d.ts.map +1 -0
  600. package/src/services/voice/speaker/diarizer.ts +218 -0
  601. package/src/services/voice/speaker/encoder-fused.d.ts +60 -0
  602. package/src/services/voice/speaker/encoder-fused.d.ts.map +1 -0
  603. package/src/services/voice/speaker/encoder-fused.real.test.ts +113 -0
  604. package/src/services/voice/speaker/encoder-fused.ts +138 -0
  605. package/src/services/voice/speaker/encoder-ggml.d.ts +33 -0
  606. package/src/services/voice/speaker/encoder-ggml.d.ts.map +1 -0
  607. package/src/services/voice/speaker/encoder-ggml.ts +79 -0
  608. package/src/services/voice/speaker/encoder.d.ts +37 -0
  609. package/src/services/voice/speaker/encoder.d.ts.map +1 -0
  610. package/src/services/voice/speaker/encoder.ts +105 -0
  611. package/src/services/voice/speaker-imprint.d.ts +83 -0
  612. package/src/services/voice/speaker-imprint.d.ts.map +1 -0
  613. package/src/services/voice/speaker-imprint.test.ts +185 -0
  614. package/src/services/voice/speaker-imprint.ts +312 -0
  615. package/src/services/voice/speaker-preset-cache.d.ts +77 -0
  616. package/src/services/voice/speaker-preset-cache.d.ts.map +1 -0
  617. package/src/services/voice/speaker-preset-cache.test.ts +154 -0
  618. package/src/services/voice/speaker-preset-cache.ts +195 -0
  619. package/src/services/voice/streaming-asr/streaming-pipeline-adapter.ts +292 -0
  620. package/src/services/voice/system-audio-sink.d.ts +73 -0
  621. package/src/services/voice/system-audio-sink.d.ts.map +1 -0
  622. package/src/services/voice/system-audio-sink.test.ts +29 -0
  623. package/src/services/voice/system-audio-sink.ts +366 -0
  624. package/src/services/voice/transcriber.d.ts +244 -0
  625. package/src/services/voice/transcriber.d.ts.map +1 -0
  626. package/src/services/voice/transcriber.test.ts +392 -0
  627. package/src/services/voice/transcriber.ts +704 -0
  628. package/src/services/voice/transcript-knowledge.d.ts +37 -0
  629. package/src/services/voice/transcript-knowledge.d.ts.map +1 -0
  630. package/src/services/voice/transcript-knowledge.test.ts +68 -0
  631. package/src/services/voice/transcript-knowledge.ts +75 -0
  632. package/src/services/voice/transcript-service.d.ts +41 -0
  633. package/src/services/voice/transcript-service.d.ts.map +1 -0
  634. package/src/services/voice/transcript-service.test.ts +137 -0
  635. package/src/services/voice/transcript-service.ts +141 -0
  636. package/src/services/voice/transcript-store.d.ts +53 -0
  637. package/src/services/voice/transcript-store.d.ts.map +1 -0
  638. package/src/services/voice/transcript-store.test.ts +153 -0
  639. package/src/services/voice/transcript-store.ts +132 -0
  640. package/src/services/voice/turn-controller.d.ts +183 -0
  641. package/src/services/voice/turn-controller.d.ts.map +1 -0
  642. package/src/services/voice/turn-controller.test.ts +575 -0
  643. package/src/services/voice/turn-controller.ts +596 -0
  644. package/src/services/voice/types.d.ts +643 -0
  645. package/src/services/voice/types.d.ts.map +1 -0
  646. package/src/services/voice/types.ts +699 -0
  647. package/src/services/voice/vad.d.ts +282 -0
  648. package/src/services/voice/vad.d.ts.map +1 -0
  649. package/src/services/voice/vad.test.ts +480 -0
  650. package/src/services/voice/vad.ts +827 -0
  651. package/src/services/voice/vad.v1-v4.test.ts +222 -0
  652. package/src/services/voice/voice-budget.d.ts +241 -0
  653. package/src/services/voice/voice-budget.d.ts.map +1 -0
  654. package/src/services/voice/voice-budget.test.ts +418 -0
  655. package/src/services/voice/voice-budget.ts +635 -0
  656. package/src/services/voice/voice-duet.test.ts +375 -0
  657. package/src/services/voice/voice-emotion-classifier.d.ts +95 -0
  658. package/src/services/voice/voice-emotion-classifier.d.ts.map +1 -0
  659. package/src/services/voice/voice-emotion-classifier.test.ts +210 -0
  660. package/src/services/voice/voice-emotion-classifier.ts +273 -0
  661. package/src/services/voice/voice-preset-format.d.ts +158 -0
  662. package/src/services/voice/voice-preset-format.d.ts.map +1 -0
  663. package/src/services/voice/voice-preset-format.ts +700 -0
  664. package/src/services/voice/voice-preset-generator.test.ts +89 -0
  665. package/src/services/voice/voice-profile-artifact.d.ts +116 -0
  666. package/src/services/voice/voice-profile-artifact.d.ts.map +1 -0
  667. package/src/services/voice/voice-profile-artifact.test.ts +138 -0
  668. package/src/services/voice/voice-profile-artifact.ts +518 -0
  669. package/src/services/voice/voice-profile-routes.d.ts +83 -0
  670. package/src/services/voice/voice-profile-routes.d.ts.map +1 -0
  671. package/src/services/voice/voice-profile-routes.test.ts +429 -0
  672. package/src/services/voice/voice-profile-routes.ts +425 -0
  673. package/src/services/voice/voice-scenario.ts +154 -0
  674. package/src/services/voice/voice-settings.d.ts +82 -0
  675. package/src/services/voice/voice-settings.d.ts.map +1 -0
  676. package/src/services/voice/voice-settings.ts +172 -0
  677. package/src/services/voice/voice-state-machine.d.ts +364 -0
  678. package/src/services/voice/voice-state-machine.d.ts.map +1 -0
  679. package/src/services/voice/voice-state-machine.ts +727 -0
  680. package/src/services/voice/voice-workbench-report.test.ts +168 -0
  681. package/src/services/voice/voice-workbench-report.ts +326 -0
  682. package/src/services/voice/voice-workbench.test.ts +158 -0
  683. package/src/services/voice/voice.test.ts +1070 -0
  684. package/src/services/voice/wake-word-ggml.d.ts +101 -0
  685. package/src/services/voice/wake-word-ggml.d.ts.map +1 -0
  686. package/src/services/voice/wake-word-ggml.ts +320 -0
  687. package/src/services/voice/wake-word.d.ts +255 -0
  688. package/src/services/voice/wake-word.d.ts.map +1 -0
  689. package/src/services/voice/wake-word.test.ts +298 -0
  690. package/src/services/voice/wake-word.ts +554 -0
  691. package/src/services/voice/wrap-with-first-line-cache.d.ts +70 -0
  692. package/src/services/voice/wrap-with-first-line-cache.d.ts.map +1 -0
  693. package/src/services/voice/wrap-with-first-line-cache.ts +267 -0
  694. package/src/services/voice-model-updater.d.ts +240 -0
  695. package/src/services/voice-model-updater.d.ts.map +1 -0
  696. package/src/services/voice-model-updater.ts +724 -0
  697. package/src/services/voice-prewarm.d.ts +3 -0
  698. package/src/services/voice-prewarm.d.ts.map +1 -0
  699. package/src/services/voice-prewarm.ts +51 -0
  700. package/dist/index.d.ts +0 -37
  701. package/dist/index.js +0 -1098
@@ -0,0 +1,101 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ DEFAULT_OPTIMISTIC_EOT_THRESHOLD,
4
+ OptimisticGenerationPolicy,
5
+ resolveOptimisticPolicyEnabled,
6
+ } from "./optimistic-policy";
7
+
8
+ describe("resolveOptimisticPolicyEnabled — pure", () => {
9
+ it("override:true wins regardless of power source", () => {
10
+ expect(
11
+ resolveOptimisticPolicyEnabled({
12
+ powerSource: "battery",
13
+ override: true,
14
+ }),
15
+ ).toBe(true);
16
+ });
17
+
18
+ it("override:false wins regardless of power source", () => {
19
+ expect(
20
+ resolveOptimisticPolicyEnabled({
21
+ powerSource: "plugged-in",
22
+ override: false,
23
+ }),
24
+ ).toBe(false);
25
+ });
26
+
27
+ it("battery without override → false", () => {
28
+ expect(resolveOptimisticPolicyEnabled({ powerSource: "battery" })).toBe(
29
+ false,
30
+ );
31
+ });
32
+
33
+ it("plugged-in without override → true", () => {
34
+ expect(resolveOptimisticPolicyEnabled({ powerSource: "plugged-in" })).toBe(
35
+ true,
36
+ );
37
+ });
38
+
39
+ it("unknown power source defaults to true", () => {
40
+ expect(resolveOptimisticPolicyEnabled({ powerSource: "unknown" })).toBe(
41
+ true,
42
+ );
43
+ expect(resolveOptimisticPolicyEnabled({})).toBe(true);
44
+ });
45
+ });
46
+
47
+ describe("OptimisticGenerationPolicy", () => {
48
+ it("defaults: enabled, threshold = DEFAULT", () => {
49
+ const p = new OptimisticGenerationPolicy();
50
+ expect(p.enabled()).toBe(true);
51
+ expect(p.threshold).toBe(DEFAULT_OPTIMISTIC_EOT_THRESHOLD);
52
+ });
53
+
54
+ it("battery flips enabled() to false", () => {
55
+ const p = new OptimisticGenerationPolicy();
56
+ p.setPowerSource("battery");
57
+ expect(p.enabled()).toBe(false);
58
+ });
59
+
60
+ it("explicit override true wins on battery", () => {
61
+ const p = new OptimisticGenerationPolicy();
62
+ p.setPowerSource("battery");
63
+ p.setOverride(true);
64
+ expect(p.enabled()).toBe(true);
65
+ });
66
+
67
+ it("override:false wins on plugged-in", () => {
68
+ const p = new OptimisticGenerationPolicy();
69
+ p.setPowerSource("plugged-in");
70
+ p.setOverride(false);
71
+ expect(p.enabled()).toBe(false);
72
+ });
73
+
74
+ it("shouldStartOptimisticLm gates on EOT threshold", () => {
75
+ const p = new OptimisticGenerationPolicy({ eotThreshold: 0.7 });
76
+ expect(p.shouldStartOptimisticLm(0.5)).toBe(false);
77
+ expect(p.shouldStartOptimisticLm(0.7)).toBe(true);
78
+ expect(p.shouldStartOptimisticLm(0.95)).toBe(true);
79
+ });
80
+
81
+ it("shouldStartOptimisticLm returns false when disabled regardless of probability", () => {
82
+ const p = new OptimisticGenerationPolicy();
83
+ p.setPowerSource("battery");
84
+ expect(p.shouldStartOptimisticLm(0.99)).toBe(false);
85
+ });
86
+
87
+ it("override clears back to default with undefined", () => {
88
+ const p = new OptimisticGenerationPolicy();
89
+ p.setOverride(false);
90
+ expect(p.enabled()).toBe(false);
91
+ p.setOverride(undefined);
92
+ expect(p.enabled()).toBe(true);
93
+ });
94
+
95
+ it("constructor defaultEnabled=false flips the base policy", () => {
96
+ const p = new OptimisticGenerationPolicy({ defaultEnabled: false });
97
+ expect(p.enabled()).toBe(false);
98
+ p.setOverride(true);
99
+ expect(p.enabled()).toBe(true);
100
+ });
101
+ });
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Optimistic-generation policy — Wave 3 W3-9.
3
+ *
4
+ * Decides whether the LM should fire optimistically the moment the turn
5
+ * detector crosses the EOT threshold (before the audio buffer is fully
6
+ * drained), or wait for the full hangover. Per the user spec:
7
+ *
8
+ * > "We want to optimize to start running and checking basically the
9
+ * second there is no audio / voice detected, and then we start
10
+ * optimistically processing, and if the user starts talking again we
11
+ * kill and add to the conversation and start reprocessing."
12
+ *
13
+ * Default heuristic:
14
+ * - Plugged-in (or power state unknown): optimistic = true.
15
+ * - On battery: optimistic = false (the extra forward pass burns
16
+ * joules every false-positive EOT).
17
+ *
18
+ * Callers (notably `VoiceStateMachine`) read `shouldStartOptimisticLm()`
19
+ * each time they consider firing the speculative drafter. The policy can be
20
+ * mutated at runtime via `setOptimisticOverride()` so a Settings UI toggle
21
+ * or a per-session env var can pin the value without restarting the engine.
22
+ */
23
+
24
+ /** Power-source signal. `unknown` is treated as "plugged in" for the
25
+ * default policy (a desktop without battery telemetry). */
26
+ export type PowerSourceState = "plugged-in" | "battery" | "unknown";
27
+
28
+ /**
29
+ * Resolve the current power-source state.
30
+ *
31
+ * Production callers (engine bridge) prime the optimistic policy with this
32
+ * once at session start. There is no canonical power-source accessor in
33
+ * `device-tier.ts` (the hardware probe records RAM / GPU / cores but no
34
+ * battery telemetry), so this resolver consults two cheap signals:
35
+ *
36
+ * 1. `ELIZA_VOICE_POWER_SOURCE` env var (`"plugged-in" | "battery" |
37
+ * "unknown"`). Wins outright when set — operators / Settings can pin
38
+ * the policy without restarting the engine.
39
+ * 2. Linux `/sys/class/power_supply` AC online status when available
40
+ * synchronously. Returns `"plugged-in"` when any AC adapter reports
41
+ * `online`, `"battery"` when only batteries are present and none
42
+ * report online, `"unknown"` otherwise.
43
+ *
44
+ * macOS / Windows / mobile go through `"unknown"` here — the desktop probe
45
+ * surfaces those via the Electrobun side and Settings overrides it through
46
+ * (1). The default heuristic (`unknown → enabled`) treats those as
47
+ * plugged-in for the optimistic gate, which is the right default for
48
+ * desktop / dev (battery-aware mobile builds set the override explicitly).
49
+ */
50
+ export function resolvePowerSourceState(): PowerSourceState {
51
+ const fromEnv = process.env.ELIZA_VOICE_POWER_SOURCE?.trim().toLowerCase();
52
+ if (
53
+ fromEnv === "plugged-in" ||
54
+ fromEnv === "battery" ||
55
+ fromEnv === "unknown"
56
+ ) {
57
+ return fromEnv;
58
+ }
59
+ if (process.platform === "linux") {
60
+ try {
61
+ // Lazy require so this module stays free of node-fs imports for
62
+ // non-linux platforms / browser bundlers.
63
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
64
+ const fs = require("node:fs") as typeof import("node:fs");
65
+ const base = "/sys/class/power_supply";
66
+ if (!fs.existsSync(base)) return "unknown";
67
+ const entries = fs.readdirSync(base);
68
+ let sawBattery = false;
69
+ for (const entry of entries) {
70
+ const typePath = `${base}/${entry}/type`;
71
+ if (!fs.existsSync(typePath)) continue;
72
+ const type = fs.readFileSync(typePath, "utf8").trim();
73
+ if (type === "Mains") {
74
+ const onlinePath = `${base}/${entry}/online`;
75
+ if (
76
+ fs.existsSync(onlinePath) &&
77
+ fs.readFileSync(onlinePath, "utf8").trim() === "1"
78
+ ) {
79
+ return "plugged-in";
80
+ }
81
+ } else if (type === "Battery") {
82
+ sawBattery = true;
83
+ }
84
+ }
85
+ return sawBattery ? "battery" : "unknown";
86
+ } catch {
87
+ return "unknown";
88
+ }
89
+ }
90
+ return "unknown";
91
+ }
92
+
93
+ export interface OptimisticPolicyOptions {
94
+ /**
95
+ * Default value when no override is set. Resolved from the device tier
96
+ * at construction time (per the user spec: true on plugged-in,
97
+ * false on battery). Defaults to `true` when no power signal is given.
98
+ */
99
+ defaultEnabled?: boolean;
100
+ /**
101
+ * The threshold the turn detector's EOT probability must clear before
102
+ * the optimistic LM start fires. Mirrors `EOT_TENTATIVE_THRESHOLD`
103
+ * (the existing default) but is configurable per-policy so tier-aware
104
+ * deployments can tighten it on slower devices.
105
+ */
106
+ eotThreshold?: number;
107
+ }
108
+
109
+ export interface ResolveOptimisticPolicyArgs {
110
+ powerSource?: PowerSourceState;
111
+ /**
112
+ * Explicit override. When set, wins over both the default and the
113
+ * power-source heuristic. Sourced from `voice.optimisticGenerationOnEot`
114
+ * in user settings (Wave 3C).
115
+ */
116
+ override?: boolean;
117
+ }
118
+
119
+ export const DEFAULT_OPTIMISTIC_EOT_THRESHOLD = 0.6;
120
+
121
+ /**
122
+ * Pure resolver. Takes the power source + an optional override and returns
123
+ * whether optimistic LM start should fire.
124
+ */
125
+ export function resolveOptimisticPolicyEnabled(
126
+ args: ResolveOptimisticPolicyArgs,
127
+ ): boolean {
128
+ if (typeof args.override === "boolean") return args.override;
129
+ if (args.powerSource === "battery") return false;
130
+ // plugged-in OR unknown OR undefined → enabled (safer default for
131
+ // desktop / dev where power telemetry isn't surfaced).
132
+ return true;
133
+ }
134
+
135
+ /**
136
+ * Mutable runtime policy. The voice state machine asks
137
+ * `policy.shouldStartOptimisticLm(eotProb)` before firing the drafter.
138
+ *
139
+ * Reasoning for a class rather than a free function: at runtime we need
140
+ * the override + the power source to be hot-swappable from Settings or a
141
+ * device-event listener (battery state change) without re-plumbing the
142
+ * machine.
143
+ */
144
+ export class OptimisticGenerationPolicy {
145
+ private overrideValue: boolean | undefined;
146
+ private powerSource: PowerSourceState;
147
+ private readonly defaultEnabled: boolean;
148
+ private readonly eotThreshold: number;
149
+
150
+ constructor(opts: OptimisticPolicyOptions = {}) {
151
+ this.defaultEnabled = opts.defaultEnabled ?? true;
152
+ this.eotThreshold = opts.eotThreshold ?? DEFAULT_OPTIMISTIC_EOT_THRESHOLD;
153
+ this.powerSource = "unknown";
154
+ }
155
+
156
+ /** Update the power source (called by the device-tier observer). */
157
+ setPowerSource(state: PowerSourceState): void {
158
+ this.powerSource = state;
159
+ }
160
+
161
+ /** Set / clear the user override. */
162
+ setOverride(value: boolean | undefined): void {
163
+ this.overrideValue = value;
164
+ }
165
+
166
+ /** Resolve "should we be optimistic right now?". */
167
+ enabled(): boolean {
168
+ // Explicit user override wins outright.
169
+ if (typeof this.overrideValue === "boolean") return this.overrideValue;
170
+ // No override: apply the battery heuristic on top of `defaultEnabled`.
171
+ // `defaultEnabled=false` pins the policy off regardless of power source;
172
+ // `defaultEnabled=true` enables it on plugged-in / unknown and disables
173
+ // it on battery.
174
+ if (!this.defaultEnabled) return false;
175
+ return resolveOptimisticPolicyEnabled({ powerSource: this.powerSource });
176
+ }
177
+
178
+ /**
179
+ * Combined gate: the policy must be enabled AND the EOT probability
180
+ * must clear the policy's threshold. This is the canonical check the
181
+ * voice state machine calls before firing the drafter on a partial
182
+ * transcript.
183
+ */
184
+ shouldStartOptimisticLm(eotProb: number): boolean {
185
+ if (!this.enabled()) return false;
186
+ return eotProb >= this.eotThreshold;
187
+ }
188
+
189
+ get threshold(): number {
190
+ return this.eotThreshold;
191
+ }
192
+ }
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Optimistic-decode-with-rollback controller for the voice loop.
3
+ *
4
+ * IDLE
5
+ * │ speech-start
6
+ * ▼
7
+ * LISTENING
8
+ * │ speech-pause
9
+ * ▼
10
+ * PAUSE_TENTATIVE ── speech-active (within rollback window) ──▶ LISTENING
11
+ * │ ▲
12
+ * │ speech-end (or pause > rollback window) │
13
+ * ▼ │
14
+ * DRAFT_RESPONSE ──── speech-active (within rollback window) ──────┘
15
+ * │ (restore checkpoint, abort drafter)
16
+ * │ commit
17
+ * ▼
18
+ * COMMITTED → IDLE
19
+ *
20
+ * On `speech-pause` we snapshot the slot's KV state (`C1-<turn-id>`) and
21
+ * speculatively start the drafter against the partial transcript so that
22
+ * when the user IS done, the response is already underway. If they resume
23
+ * within the rollback window (~2× pause hangover), we restore the snapshot
24
+ * and abort the speculative draft — net cost is one checkpoint write/read.
25
+ *
26
+ * This module deliberately does NOT modify `turn-controller.ts`,
27
+ * `vad.ts`, `scheduler.ts`, `phrase-chunker.ts`, `barge-in.ts`, `pipeline.ts`,
28
+ * `pipeline-impls.ts`, or `transcriber.ts` — those are owned elsewhere.
29
+ * Instead, the turn controller composes this controller after the upstream
30
+ * llama.cpp merge lands and the feature flag flips.
31
+ */
32
+
33
+ import type { CheckpointClient, CheckpointHandle } from "../checkpoint-client";
34
+ import type { VadEvent, VadEventSource } from "./types";
35
+
36
+ /**
37
+ * State of the optimistic-rollback controller. Mirrors the comment-block
38
+ * state machine above; exported so tests and telemetry can assert on it.
39
+ */
40
+ export type OptimisticRollbackState =
41
+ | "idle"
42
+ | "listening"
43
+ | "pause-tentative"
44
+ | "draft-response"
45
+ | "committed";
46
+
47
+ /**
48
+ * Telemetry event stream. Stays narrow — the controller emits exactly four
49
+ * lifecycle events per turn (save, restore-or-commit, draft-start,
50
+ * draft-abort). Consumers wire these into the existing voice-bench
51
+ * trajectory captures.
52
+ */
53
+ export interface OptimisticRollbackTelemetry {
54
+ onCheckpointSaved?: (handle: CheckpointHandle, turnId: string) => void;
55
+ onCheckpointRestored?: (handle: CheckpointHandle, turnId: string) => void;
56
+ onSpeculativeDraftStarted?: (turnId: string) => void;
57
+ onSpeculativeDraftAborted?: (turnId: string, reason: AbortReason) => void;
58
+ /**
59
+ * Fired when the controller silently swallows an error from the
60
+ * checkpoint client — surfacing it via telemetry rather than rethrowing
61
+ * keeps a failing checkpoint endpoint from breaking the voice loop. The
62
+ * caller decides whether to flip the feature flag off.
63
+ */
64
+ onCheckpointError?: (
65
+ op: "save" | "restore" | "cancel",
66
+ error: unknown,
67
+ turnId: string,
68
+ ) => void;
69
+ }
70
+
71
+ export type AbortReason = "resumed" | "committed" | "shutdown";
72
+
73
+ /**
74
+ * Speculative drafter handle. The voice turn controller hands one of these
75
+ * in when starting the drafter so this module can abort it without
76
+ * importing the drafter's internals. `abort()` MUST be idempotent — the
77
+ * controller may call it again on shutdown.
78
+ */
79
+ export interface SpeculativeDraftHandle {
80
+ abort(): void;
81
+ }
82
+
83
+ /**
84
+ * Caller-supplied drafter starter. Returns a handle the controller can
85
+ * abort. Called on entry to `pause-tentative`. The promise must resolve
86
+ * synchronously (or near-synchronously) — the speculative draft runs in
87
+ * the background.
88
+ */
89
+ export type StartSpeculativeDraft = (
90
+ partialTranscript: string,
91
+ turnId: string,
92
+ ) => SpeculativeDraftHandle;
93
+
94
+ /**
95
+ * Caller-supplied source for the partial transcript captured at the
96
+ * `speech-pause` instant. Kept as a function so the controller doesn't
97
+ * have to hold a reference to the transcriber.
98
+ */
99
+ export type ReadPartialTranscript = () => string;
100
+
101
+ export interface OptimisticRollbackControllerOptions {
102
+ /** The slot id the voice loop is pinned to for this turn. */
103
+ slotId: number;
104
+ /**
105
+ * Per-process feature flag. Defaults to `false` — flip on once the
106
+ * upstream `--ctx-checkpoints` merge lands AND the rollout plan in
107
+ * `docs/eliza-1-optimistic-rollback.md` reaches the desired bucket.
108
+ * Forwards every VAD event to the wrapped state machine when off but
109
+ * never makes a checkpoint REST call.
110
+ */
111
+ enableOptimisticRollback?: boolean;
112
+ /**
113
+ * VAD pause hangover (ms). Default 100 ms (lowered from 220ms; further
114
+ * reduction gated on semantic EOT classifier V2) — matches the voice
115
+ * loop's standard hangover. The rollback window is `2 ×` this value.
116
+ */
117
+ pauseHangoverMs?: number;
118
+ /** Source of VAD events; usually `VadDetector`. */
119
+ vadSource: VadEventSource;
120
+ client: CheckpointClient;
121
+ startSpeculativeDraft: StartSpeculativeDraft;
122
+ readPartialTranscript: ReadPartialTranscript;
123
+ telemetry?: OptimisticRollbackTelemetry;
124
+ /**
125
+ * Wall-clock function. Injected for tests; defaults to `Date.now`. The
126
+ * controller uses this to enforce the rollback window — if a
127
+ * `speech-active` arrives more than `2 × pauseHangoverMs` after the
128
+ * `speech-pause`, the controller commits rather than restores.
129
+ */
130
+ now?: () => number;
131
+ }
132
+
133
+ // Lowered from 220ms; further reduction gated on semantic EOT classifier (V2).
134
+ const DEFAULT_PAUSE_HANGOVER_MS = 100;
135
+ /** Rollback window = ROLLBACK_WINDOW_MULTIPLIER × pauseHangoverMs. */
136
+ const ROLLBACK_WINDOW_MULTIPLIER = 2;
137
+
138
+ /**
139
+ * Optimistic-rollback controller. Subscribes to a `VadEventSource` and
140
+ * drives the checkpoint REST client + a caller-supplied speculative
141
+ * drafter. Idempotent `dispose()` for clean shutdown.
142
+ */
143
+ export class OptimisticRollbackController {
144
+ private state: OptimisticRollbackState = "idle";
145
+ private currentTurnId = 0;
146
+ private pauseTimestampMs: number | null = null;
147
+ private currentCheckpoint: CheckpointHandle | null = null;
148
+ private currentDraft: SpeculativeDraftHandle | null = null;
149
+ private readonly unsubscribe: () => void;
150
+ private readonly enabled: boolean;
151
+ private readonly pauseHangoverMs: number;
152
+ private readonly slotId: number;
153
+ private readonly client: CheckpointClient;
154
+ private readonly startSpeculativeDraft: StartSpeculativeDraft;
155
+ private readonly readPartialTranscript: ReadPartialTranscript;
156
+ private readonly telemetry: OptimisticRollbackTelemetry;
157
+ private readonly now: () => number;
158
+ private disposed = false;
159
+
160
+ constructor(opts: OptimisticRollbackControllerOptions) {
161
+ this.slotId = opts.slotId;
162
+ this.enabled = opts.enableOptimisticRollback ?? false;
163
+ this.pauseHangoverMs = opts.pauseHangoverMs ?? DEFAULT_PAUSE_HANGOVER_MS;
164
+ this.client = opts.client;
165
+ this.startSpeculativeDraft = opts.startSpeculativeDraft;
166
+ this.readPartialTranscript = opts.readPartialTranscript;
167
+ this.telemetry = opts.telemetry ?? {};
168
+ this.now = opts.now ?? Date.now;
169
+ this.unsubscribe = opts.vadSource.onVadEvent((event) =>
170
+ this.handleVadEvent(event),
171
+ );
172
+ }
173
+
174
+ /** Current state — read-only view for tests / telemetry. */
175
+ getState(): OptimisticRollbackState {
176
+ return this.state;
177
+ }
178
+
179
+ /**
180
+ * Detach from the VAD source and abort any in-flight speculative draft.
181
+ * Safe to call multiple times.
182
+ */
183
+ dispose(): void {
184
+ if (this.disposed) return;
185
+ this.disposed = true;
186
+ this.unsubscribe();
187
+ if (this.currentDraft) {
188
+ this.currentDraft.abort();
189
+ this.telemetry.onSpeculativeDraftAborted?.(
190
+ this.turnIdString(),
191
+ "shutdown",
192
+ );
193
+ this.currentDraft = null;
194
+ }
195
+ if (this.currentCheckpoint && this.enabled) {
196
+ // Best-effort cancellation of any in-flight decode on the slot. We do
197
+ // NOT delete the snapshot file — upstream evicts on slot reuse, and a
198
+ // dangling snapshot is cheap. Errors here are swallowed (the loop is
199
+ // shutting down) but surfaced via telemetry.
200
+ this.client
201
+ .cancelSlot(this.slotId)
202
+ .catch((error: unknown) =>
203
+ this.telemetry.onCheckpointError?.(
204
+ "cancel",
205
+ error,
206
+ this.turnIdString(),
207
+ ),
208
+ );
209
+ }
210
+ this.currentCheckpoint = null;
211
+ this.state = "idle";
212
+ }
213
+
214
+ private handleVadEvent(event: VadEvent): void {
215
+ if (this.disposed) return;
216
+ switch (event.type) {
217
+ case "speech-start":
218
+ this.transitionToListening();
219
+ return;
220
+ case "speech-pause":
221
+ this.handleSpeechPause(event.timestampMs);
222
+ return;
223
+ case "speech-active":
224
+ this.handleSpeechActive(event.timestampMs);
225
+ return;
226
+ case "speech-end":
227
+ this.handleSpeechEnd();
228
+ return;
229
+ case "blip":
230
+ return;
231
+ default: {
232
+ // exhaustive — VadEvent is a closed union; if it gains a variant
233
+ // the compiler will flag this cast.
234
+ const _exhaustive: never = event;
235
+ void _exhaustive;
236
+ }
237
+ }
238
+ }
239
+
240
+ private transitionToListening(): void {
241
+ if (this.state === "idle") {
242
+ this.currentTurnId += 1;
243
+ }
244
+ this.state = "listening";
245
+ }
246
+
247
+ private handleSpeechPause(timestampMs: number): void {
248
+ if (this.state !== "listening") return;
249
+ this.pauseTimestampMs = timestampMs;
250
+ if (!this.enabled) {
251
+ this.state = "pause-tentative";
252
+ return;
253
+ }
254
+ const turnId = this.turnIdString();
255
+ const filename = `C1-${turnId}`;
256
+ this.state = "pause-tentative";
257
+ void this.client
258
+ .saveCheckpoint(this.slotId, filename)
259
+ .then((handle) => {
260
+ if (this.state !== "pause-tentative") return;
261
+ this.currentCheckpoint = handle;
262
+ this.telemetry.onCheckpointSaved?.(handle, turnId);
263
+ this.startDraftIfStillPaused(turnId);
264
+ })
265
+ .catch((error: unknown) => {
266
+ this.telemetry.onCheckpointError?.("save", error, turnId);
267
+ });
268
+ }
269
+
270
+ private startDraftIfStillPaused(turnId: string): void {
271
+ if (this.state !== "pause-tentative") return;
272
+ const partial = this.readPartialTranscript();
273
+ this.currentDraft = this.startSpeculativeDraft(partial, turnId);
274
+ this.state = "draft-response";
275
+ this.telemetry.onSpeculativeDraftStarted?.(turnId);
276
+ }
277
+
278
+ private handleSpeechActive(timestampMs: number): void {
279
+ if (this.state !== "pause-tentative" && this.state !== "draft-response") {
280
+ // `listening → speech-active` is a heartbeat; ignore.
281
+ return;
282
+ }
283
+ const pauseAt = this.pauseTimestampMs;
284
+ if (pauseAt === null) {
285
+ this.state = "listening";
286
+ return;
287
+ }
288
+ const elapsed = timestampMs - pauseAt;
289
+ const rollbackWindowMs = this.pauseHangoverMs * ROLLBACK_WINDOW_MULTIPLIER;
290
+ if (elapsed > rollbackWindowMs) {
291
+ // Resumed too late to roll back — commit and let the drafter's
292
+ // output flow as the response.
293
+ this.commit();
294
+ return;
295
+ }
296
+ this.rollback("resumed");
297
+ }
298
+
299
+ private handleSpeechEnd(): void {
300
+ if (this.state === "pause-tentative" || this.state === "draft-response") {
301
+ this.commit();
302
+ return;
303
+ }
304
+ this.state = "idle";
305
+ }
306
+
307
+ private rollback(reason: AbortReason): void {
308
+ const turnId = this.turnIdString();
309
+ if (this.currentDraft) {
310
+ this.currentDraft.abort();
311
+ this.telemetry.onSpeculativeDraftAborted?.(turnId, reason);
312
+ this.currentDraft = null;
313
+ }
314
+ if (this.enabled && this.currentCheckpoint) {
315
+ const handle = this.currentCheckpoint;
316
+ void this.client
317
+ .restoreCheckpoint(this.slotId, handle.filename)
318
+ .then(() => this.telemetry.onCheckpointRestored?.(handle, turnId))
319
+ .catch((error: unknown) => {
320
+ this.telemetry.onCheckpointError?.("restore", error, turnId);
321
+ });
322
+ }
323
+ this.currentCheckpoint = null;
324
+ this.pauseTimestampMs = null;
325
+ this.state = "listening";
326
+ }
327
+
328
+ private commit(): void {
329
+ // Commit drops the snapshot reference and leaves the drafter's
330
+ // in-flight decode to flow into the response. We do NOT abort the
331
+ // drafter — its output IS the response.
332
+ this.currentCheckpoint = null;
333
+ this.currentDraft = null;
334
+ this.pauseTimestampMs = null;
335
+ this.state = "committed";
336
+ // Auto-return to idle so the next `speech-start` opens a new turn.
337
+ this.state = "idle";
338
+ }
339
+
340
+ private turnIdString(): string {
341
+ return `turn-${this.currentTurnId.toString(36)}`;
342
+ }
343
+ }