@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,671 @@
1
+ /**
2
+ * Tests for the libelizainference FFI binding.
3
+ *
4
+ * Two layers of coverage:
5
+ *
6
+ * 1. Pure unit tests run in the vitest worker (Node 22 in this repo's
7
+ * CI). They exercise the runtime detection + structured error
8
+ * surface — calling `loadElizaInferenceFfi` from a non-Bun runtime
9
+ * must throw `VoiceLifecycleError({code:"kernel-missing"})` rather
10
+ * than crashing.
11
+ *
12
+ * 2. Integration tests spawn a `bun` subprocess that imports
13
+ * `ffi-bindings.ts` and exercises every entry point against the
14
+ * stub `libelizainference_stub.{dylib,so}` produced by
15
+ * `scripts/ffi-stub/Makefile`. This validates that:
16
+ * - `dlopen` succeeds against a real shared library,
17
+ * - the `create`/`destroy` round-trip works,
18
+ * - methods that need the fused build (e.g. `ttsSynthesize`)
19
+ * return ELIZA_ERR_NOT_IMPLEMENTED and the binding surfaces it
20
+ * as a structured `VoiceLifecycleError` — never a crash, never
21
+ * a fabricated successful response,
22
+ * - ABI version mismatch is caught at load time.
23
+ *
24
+ * Per `packages/inference/AGENTS.md` §3 + §9 every failure path is a
25
+ * structured error. The integration-test harness asserts on the JSON
26
+ * report the bun subprocess emits to stdout — a missing or malformed
27
+ * report fails the test.
28
+ */
29
+
30
+ import { spawnSync } from "node:child_process";
31
+ import {
32
+ existsSync,
33
+ mkdtempSync,
34
+ readFileSync,
35
+ rmSync,
36
+ statSync,
37
+ writeFileSync,
38
+ } from "node:fs";
39
+ import { tmpdir } from "node:os";
40
+ import path from "node:path";
41
+ import { fileURLToPath } from "node:url";
42
+ import { describe, expect, it } from "vitest";
43
+ import { fakeFfi } from "./__test-helpers__/fake-ffi";
44
+ import type { ElizaInferenceFfi, TtsStreamChunk } from "./ffi-bindings";
45
+ import {
46
+ ELIZA_ERR_CANCELLED,
47
+ ELIZA_ERR_NOT_IMPLEMENTED,
48
+ ELIZA_INFERENCE_ABI_VERSION,
49
+ ELIZA_OK,
50
+ loadElizaInferenceFfi,
51
+ } from "./ffi-bindings";
52
+ import { VoiceLifecycleError } from "./lifecycle";
53
+
54
+ /**
55
+ * The complete ABI v3 C symbol set declared in
56
+ * `scripts/ffi-stub/ffi.h` — kept here as the JS-side source of
57
+ * truth for both the fake-FFI surface check and the stub `nm` audit.
58
+ * Mirrors `REQUIRED_ELIZA_INFERENCE_SYMBOLS` in `verify-symbols.mjs`.
59
+ */
60
+ const ABI_V3_SYMBOLS = [
61
+ "eliza_inference_abi_version",
62
+ "eliza_inference_create",
63
+ "eliza_inference_destroy",
64
+ "eliza_inference_mmap_acquire",
65
+ "eliza_inference_mmap_evict",
66
+ "eliza_inference_tts_synthesize",
67
+ "eliza_inference_asr_transcribe",
68
+ "eliza_inference_asr_stream_supported",
69
+ "eliza_inference_asr_stream_open",
70
+ "eliza_inference_asr_stream_feed",
71
+ "eliza_inference_asr_stream_partial",
72
+ "eliza_inference_asr_stream_finish",
73
+ "eliza_inference_asr_stream_close",
74
+ "eliza_inference_tts_stream_supported",
75
+ "eliza_inference_tts_synthesize_stream",
76
+ "eliza_inference_cancel_tts",
77
+ "eliza_inference_set_verifier_callback",
78
+ "eliza_inference_encode_reference",
79
+ "eliza_inference_free_tokens",
80
+ "eliza_inference_vad_supported",
81
+ "eliza_inference_vad_open",
82
+ "eliza_inference_vad_process",
83
+ "eliza_inference_vad_reset",
84
+ "eliza_inference_vad_close",
85
+ "eliza_inference_free_string",
86
+ ] as const;
87
+
88
+ /** The TS-surface methods the binding must expose for the full ABI v3. */
89
+ const ELIZA_FFI_METHODS = [
90
+ "create",
91
+ "destroy",
92
+ "mmapAcquire",
93
+ "mmapEvict",
94
+ "ttsSynthesize",
95
+ "asrTranscribe",
96
+ "ttsStreamSupported",
97
+ "ttsSynthesizeStream",
98
+ "cancelTts",
99
+ "setVerifierCallback",
100
+ "vadSupported",
101
+ "vadOpen",
102
+ "vadProcess",
103
+ "vadReset",
104
+ "vadClose",
105
+ "asrStreamSupported",
106
+ "asrStreamOpen",
107
+ "asrStreamFeed",
108
+ "asrStreamPartial",
109
+ "asrStreamFinish",
110
+ "asrStreamClose",
111
+ "close",
112
+ ] as const satisfies ReadonlyArray<keyof ElizaInferenceFfi>;
113
+
114
+ const __filename = fileURLToPath(import.meta.url);
115
+ const __dirname = path.dirname(__filename);
116
+ // __dirname = plugins/plugin-local-inference/src/services/voice
117
+ // FFI_STUB_DIR = packages/app-core/scripts/ffi-stub
118
+ // (H2.c collapsed omnivoice-fuse/ — the FFI stub artifacts moved to ffi-stub/.)
119
+ const FFI_STUB_DIR = path.resolve(
120
+ __dirname,
121
+ "..",
122
+ "..",
123
+ "..",
124
+ "..",
125
+ "..",
126
+ "packages",
127
+ "app-core",
128
+ "scripts",
129
+ "ffi-stub",
130
+ );
131
+ const STUB_DYLIB = path.join(
132
+ FFI_STUB_DIR,
133
+ process.platform === "darwin"
134
+ ? "libelizainference_stub.dylib"
135
+ : "libelizainference_stub.so",
136
+ );
137
+
138
+ function bunOnPath(): string | null {
139
+ const direct = spawnSync("bun", ["--version"], { encoding: "utf8" });
140
+ if (direct.status === 0) return "bun";
141
+
142
+ const which = spawnSync("bash", ["-lc", "command -v bun"], {
143
+ encoding: "utf8",
144
+ });
145
+ if (which.status !== 0) return null;
146
+ const trimmed = which.stdout.trim();
147
+ if (trimmed.length > 0) return trimmed;
148
+
149
+ if (process.execPath && /(?:^|[/\\])bun(?:\.exe)?$/i.test(process.execPath)) {
150
+ const probe = spawnSync(process.execPath, ["--version"], {
151
+ encoding: "utf8",
152
+ });
153
+ if (probe.status === 0 && probe.stdout.trim().length > 0) {
154
+ return process.execPath;
155
+ }
156
+ }
157
+ return null;
158
+ }
159
+
160
+ describe("ffi-bindings — pure unit (no Bun, no dylib)", () => {
161
+ it("ELIZA_INFERENCE_ABI_VERSION is 11 (in-process end-of-turn scoring)", () => {
162
+ expect(ELIZA_INFERENCE_ABI_VERSION).toBe(11);
163
+ });
164
+
165
+ it("loadElizaInferenceFfi throws VoiceLifecycleError when FFI is unavailable", () => {
166
+ // Depending on the test runner this is either a non-Bun runtime or Bun
167
+ // with a deliberately missing dylib. Both must normalize to the same
168
+ // structured lifecycle error instead of leaking a raw runtime exception.
169
+ let thrown: unknown;
170
+ try {
171
+ loadElizaInferenceFfi("/nonexistent/path/libelizainference.dylib");
172
+ } catch (err) {
173
+ thrown = err;
174
+ }
175
+ expect(thrown).toBeInstanceOf(VoiceLifecycleError);
176
+ if (thrown instanceof VoiceLifecycleError) {
177
+ expect(thrown.code).toBe("kernel-missing");
178
+ expect(thrown.message).toMatch(
179
+ /runtime is not Bun|Failed to open libelizainference/,
180
+ );
181
+ }
182
+ });
183
+
184
+ it("loadElizaInferenceFfi throws on empty path even when Bun is unavailable", () => {
185
+ // Empty path should be rejected before any dlopen attempt — but the
186
+ // Bun-runtime guard fires first when running under Node, so the test
187
+ // checks both branches: the message must mention either reason.
188
+ let thrown: unknown;
189
+ try {
190
+ loadElizaInferenceFfi("");
191
+ } catch (err) {
192
+ thrown = err;
193
+ }
194
+ expect(thrown).toBeInstanceOf(VoiceLifecycleError);
195
+ if (thrown instanceof VoiceLifecycleError) {
196
+ expect(thrown.code).toBe("kernel-missing");
197
+ }
198
+ });
199
+
200
+ it("ELIZA_ERR_CANCELLED constant matches ffi.h (-7)", () => {
201
+ expect(ELIZA_ERR_CANCELLED).toBe(-7);
202
+ });
203
+ });
204
+
205
+ describe("ffi-bindings — ABI v3-compatible surface (fake FFI)", () => {
206
+ it("the test-helper fakeFfi exposes every required v3 method and disables reference encode", () => {
207
+ const ffi = fakeFfi("hello");
208
+ for (const method of ELIZA_FFI_METHODS) {
209
+ expect(typeof ffi[method]).toBe("function");
210
+ }
211
+ expect(ffi.libraryAbiVersion).toBe("3");
212
+ expect(ffi.encodeReferenceSupported?.()).toBe(false);
213
+ });
214
+
215
+ it("ttsSynthesizeStream delivers a body chunk then a final tail", () => {
216
+ const ffi = fakeFfi("hello", { ttsSamples: 12 });
217
+ const chunks: TtsStreamChunk[] = [];
218
+ const res = ffi.ttsSynthesizeStream({
219
+ ctx: 1n,
220
+ text: "hi there",
221
+ speakerPresetId: null,
222
+ onChunk: (c) => {
223
+ // Copy out — mirrors the production contract that `pcm` is a view.
224
+ chunks.push({ pcm: new Float32Array(c.pcm), isFinal: c.isFinal });
225
+ },
226
+ });
227
+ expect(res.cancelled).toBe(false);
228
+ expect(chunks.length).toBe(2);
229
+ expect(chunks[0]?.isFinal).toBe(false);
230
+ expect(chunks[0]?.pcm.length).toBe(12);
231
+ expect(chunks[1]?.isFinal).toBe(true);
232
+ expect(chunks[1]?.pcm.length).toBe(0);
233
+ });
234
+
235
+ it("ttsSynthesizeStream reports cancelled when onChunk returns true", () => {
236
+ const ffi = fakeFfi("hello");
237
+ const res = ffi.ttsSynthesizeStream({
238
+ ctx: 1n,
239
+ text: "hi",
240
+ speakerPresetId: null,
241
+ onChunk: () => true,
242
+ });
243
+ expect(res.cancelled).toBe(true);
244
+ });
245
+
246
+ it("ttsStreamSupported can be toggled off (non-streaming build)", () => {
247
+ expect(
248
+ fakeFfi("x", { ttsStreamSupported: true }).ttsStreamSupported(),
249
+ ).toBe(true);
250
+ expect(
251
+ fakeFfi("x", { ttsStreamSupported: false }).ttsStreamSupported(),
252
+ ).toBe(false);
253
+ });
254
+
255
+ it("setVerifierCallback returns a closeable handle; cancelTts is a no-op on the fake", () => {
256
+ const ffi = fakeFfi("x");
257
+ const handle = ffi.setVerifierCallback(1n, () => {});
258
+ expect(typeof handle.close).toBe("function");
259
+ handle.close();
260
+ // Clearing with null is also valid.
261
+ const cleared = ffi.setVerifierCallback(1n, null);
262
+ cleared.close();
263
+ expect(() => ffi.cancelTts(1n)).not.toThrow();
264
+ });
265
+
266
+ it("native VAD fake path advertises support and returns scripted probabilities", () => {
267
+ const ffi = fakeFfi("x", {
268
+ vadSupported: true,
269
+ vadProbs: [0.1, 0.8],
270
+ });
271
+ expect(ffi.vadSupported?.()).toBe(true);
272
+ const vad = ffi.vadOpen?.({ ctx: 1n, sampleRateHz: 16_000 });
273
+ if (!vad) throw new Error("fake native VAD did not open");
274
+ expect(ffi.vadProcess?.({ vad, pcm: new Float32Array(512) })).toBe(0.1);
275
+ expect(ffi.vadProcess?.({ vad, pcm: new Float32Array(512) })).toBe(0.8);
276
+ expect(() => ffi.vadReset?.(vad)).not.toThrow();
277
+ expect(() => ffi.vadClose?.(vad)).not.toThrow();
278
+ });
279
+ });
280
+
281
+ describe("ffi-stub stub library — ABI v3 symbol audit", () => {
282
+ // The committed macOS .dylib / built-on-Linux .so must export the full
283
+ // ABI v3 symbol set declared in ffi.h — same set verify-symbols.mjs
284
+ // requires of the real fused libelizainference. Skipped when the
285
+ // platform artifact isn't present (run `make -C scripts/ffi-stub`).
286
+ const haveDylib = existsSync(STUB_DYLIB);
287
+ if (!haveDylib) {
288
+ it.skip(`stub library missing at ${STUB_DYLIB} — run 'make -C scripts/ffi-stub' first`, () => {});
289
+ return;
290
+ }
291
+ it("exports every eliza_inference_* ABI v3 symbol", () => {
292
+ const symbols = readFileSync(STUB_DYLIB);
293
+ for (const name of ABI_V3_SYMBOLS) {
294
+ const exportedName = STUB_DYLIB.endsWith(".dylib") ? `_${name}` : name;
295
+ expect(symbols.includes(Buffer.from(exportedName))).toBe(true);
296
+ }
297
+ }, 30_000);
298
+ });
299
+
300
+ describe("ffi-bindings — integration via bun subprocess against stub dylib", () => {
301
+ const bun = bunOnPath();
302
+ const haveDylib = existsSync(STUB_DYLIB);
303
+
304
+ if (!bun) {
305
+ it.skip("bun not on PATH — skipping integration tests", () => {});
306
+ return;
307
+ }
308
+ if (!haveDylib) {
309
+ it.skip(`stub dylib missing at ${STUB_DYLIB} — run 'make -C scripts/ffi-stub' first`, () => {});
310
+ return;
311
+ }
312
+
313
+ it("stub dylib exists and is non-empty", () => {
314
+ expect(statSync(STUB_DYLIB).size).toBeGreaterThan(1024);
315
+ });
316
+
317
+ it("loads the committed v5 stub at degraded capability (no v6 speaker/diariz classifiers) and completes a create/destroy round-trip", () => {
318
+ const report = runBunHarness({ scenario: "create-destroy" });
319
+ expectHarnessOk(report);
320
+ // The committed stub predates the ABI-v6 speaker/diarizer fusion: it
321
+ // reports "5" and exports the v5 symbol set (no eliza_inference_speaker_*
322
+ // / _diariz_*). The binding accepts it at degraded capability — the v6
323
+ // classifier surfaces report unsupported — so older fused builds still
324
+ // load instead of hard-failing the ABI check.
325
+ expect(report.libraryAbiVersion).toBe("5");
326
+ expect(Number(report.libraryAbiVersion)).toBeLessThanOrEqual(
327
+ ELIZA_INFERENCE_ABI_VERSION,
328
+ );
329
+ expect(report.contextWasNonNull).toBe(true);
330
+ });
331
+
332
+ it("create surfaces a NULL C pointer as a structured lifecycle error", () => {
333
+ const report = runBunHarness({ scenario: "create-empty-fails" });
334
+ expectHarnessOk(report);
335
+ expect(report.threwLifecycleError).toBe(true);
336
+ expect(report.errorCode).toBe("kernel-missing");
337
+ expect(report.errorMessage).toMatch(/bundle_dir is required/);
338
+ });
339
+
340
+ it("ttsSynthesize against the stub returns ELIZA_ERR_NOT_IMPLEMENTED as a structured error (no crash)", () => {
341
+ const report = runBunHarness({ scenario: "tts-not-implemented" });
342
+ expectHarnessOk(report);
343
+ expect(report.threwLifecycleError).toBe(true);
344
+ expect(report.errorCode).toBe("kernel-missing");
345
+ // The C stub's diagnostic must surface verbatim.
346
+ expect(report.errorMessage).toMatch(/unsupported in ABI-only build/);
347
+ });
348
+
349
+ it("mmapEvict against the stub returns ELIZA_ERR_NOT_IMPLEMENTED as a structured error", () => {
350
+ const report = runBunHarness({ scenario: "mmap-evict-not-implemented" });
351
+ expectHarnessOk(report);
352
+ expect(report.threwLifecycleError).toBe(true);
353
+ expect(report.errorCode).toBe("kernel-missing");
354
+ expect(report.errorMessage).toMatch(/unsupported in ABI-only build/);
355
+ });
356
+
357
+ it("mmapAcquire against the stub returns ELIZA_ERR_NOT_IMPLEMENTED as a structured error", () => {
358
+ const report = runBunHarness({
359
+ scenario: "mmap-acquire-not-implemented",
360
+ });
361
+ expectHarnessOk(report);
362
+ expect(report.threwLifecycleError).toBe(true);
363
+ expect(report.errorCode).toBe("kernel-missing");
364
+ expect(report.errorMessage).toMatch(/unsupported in ABI-only build/);
365
+ });
366
+
367
+ it("stub advertises native VAD unsupported", () => {
368
+ const report = runBunHarness({ scenario: "vad-unsupported" });
369
+ expectHarnessOk(report);
370
+ expect(report.vadSupported).toBe(false);
371
+ });
372
+
373
+ it("ABI mismatch detection: when binding asserts wrong version, load fails structurally", () => {
374
+ // The harness exposes a dial that bumps the binding's expected ABI
375
+ // version BEFORE calling the loader, simulating a future binding
376
+ // loading an older library.
377
+ const report = runBunHarness({ scenario: "abi-mismatch" });
378
+ expectHarnessOk(report);
379
+ expect(report.threwLifecycleError).toBe(true);
380
+ expect(report.errorCode).toBe("kernel-missing");
381
+ expect(report.errorMessage).toMatch(/ABI mismatch/);
382
+ });
383
+
384
+ it("ELIZA_OK constant matches C side", () => {
385
+ // Sanity — the integration harness asserts the C stub returns
386
+ // ELIZA_OK for the create path; if this ever drifts, every other
387
+ // assertion above is suspect.
388
+ expect(ELIZA_OK).toBe(0);
389
+ expect(ELIZA_ERR_NOT_IMPLEMENTED).toBe(-1);
390
+ });
391
+ });
392
+
393
+ /* ----------------------------------------------------------------- */
394
+ /* Bun subprocess harness */
395
+ /* ----------------------------------------------------------------- */
396
+
397
+ interface HarnessReport {
398
+ ok: boolean;
399
+ scenario: string;
400
+ libraryAbiVersion?: string;
401
+ contextWasNonNull?: boolean;
402
+ threwLifecycleError?: boolean;
403
+ errorCode?: string;
404
+ errorMessage?: string;
405
+ vadSupported?: boolean;
406
+ unexpectedError?: string;
407
+ }
408
+
409
+ interface HarnessOptions {
410
+ scenario:
411
+ | "create-destroy"
412
+ | "create-empty-fails"
413
+ | "tts-not-implemented"
414
+ | "mmap-acquire-not-implemented"
415
+ | "mmap-evict-not-implemented"
416
+ | "vad-unsupported"
417
+ | "abi-mismatch";
418
+ }
419
+
420
+ function expectHarnessOk(report: HarnessReport): void {
421
+ if (!report.ok) {
422
+ throw new Error(
423
+ report.unexpectedError ??
424
+ `Bun FFI harness failed without diagnostic for ${report.scenario}`,
425
+ );
426
+ }
427
+ }
428
+
429
+ function runBunHarness(opts: HarnessOptions): HarnessReport {
430
+ const bindingsPath = path.join(__dirname, "ffi-bindings.ts");
431
+ const lifecyclePath = path.join(__dirname, "lifecycle.ts");
432
+ const dylibPath = STUB_DYLIB;
433
+ const tmp = mkdtempSync(path.join(tmpdir(), "eliza-ffi-harness-"));
434
+ const scriptPath = path.join(tmp, "harness.mjs");
435
+ const reportPath = path.join(tmp, "report.json");
436
+
437
+ // Inline ESM script the bun subprocess executes. Imports the binding
438
+ // and the lifecycle error class via absolute paths, runs the requested
439
+ // scenario, and writes one JSON report to a temp file. File output is
440
+ // intentional: Bun's test runner can swallow nested bun stdout on some
441
+ // hosts even when the child exits 0.
442
+ const script = `
443
+ import { writeFileSync } from "node:fs";
444
+ import { loadElizaInferenceFfi, ELIZA_INFERENCE_ABI_VERSION } from ${JSON.stringify(bindingsPath)};
445
+ import { VoiceLifecycleError } from ${JSON.stringify(lifecyclePath)};
446
+
447
+ const SCENARIO = ${JSON.stringify(opts.scenario)};
448
+ const DYLIB = ${JSON.stringify(dylibPath)};
449
+ const REPORT_PATH = ${JSON.stringify(reportPath)};
450
+
451
+ function emit(report) {
452
+ writeFileSync(REPORT_PATH, JSON.stringify(report));
453
+ }
454
+
455
+ function asLifecycleErr(e) {
456
+ if (!(e instanceof VoiceLifecycleError)) return null;
457
+ return { code: e.code, message: e.message };
458
+ }
459
+
460
+ (async () => {
461
+ if (SCENARIO === "create-destroy") {
462
+ const ffi = loadElizaInferenceFfi(DYLIB);
463
+ const ctx = ffi.create("/tmp/elizainference-test-bundle");
464
+ const ok = ctx !== null && ctx !== undefined && ctx !== 0n && ctx !== 0;
465
+ ffi.destroy(ctx);
466
+ ffi.close();
467
+ emit({
468
+ ok: true,
469
+ scenario: SCENARIO,
470
+ libraryAbiVersion: ffi.libraryAbiVersion,
471
+ contextWasNonNull: ok,
472
+ });
473
+ return;
474
+ }
475
+
476
+ if (SCENARIO === "create-empty-fails") {
477
+ const ffi = loadElizaInferenceFfi(DYLIB);
478
+ let thrown;
479
+ try {
480
+ ffi.create("");
481
+ } catch (e) {
482
+ thrown = e;
483
+ }
484
+ ffi.close();
485
+ const lc = asLifecycleErr(thrown);
486
+ emit({
487
+ ok: true,
488
+ scenario: SCENARIO,
489
+ threwLifecycleError: lc !== null,
490
+ errorCode: lc?.code,
491
+ errorMessage: lc?.message,
492
+ });
493
+ return;
494
+ }
495
+
496
+ if (SCENARIO === "tts-not-implemented") {
497
+ const ffi = loadElizaInferenceFfi(DYLIB);
498
+ const ctx = ffi.create("/tmp/elizainference-test-bundle");
499
+ let thrown;
500
+ try {
501
+ const out = new Float32Array(2400);
502
+ ffi.ttsSynthesize({ ctx, text: "hello world", speakerPresetId: null, out });
503
+ } catch (e) {
504
+ thrown = e;
505
+ }
506
+ ffi.destroy(ctx);
507
+ ffi.close();
508
+ const lc = asLifecycleErr(thrown);
509
+ emit({
510
+ ok: true,
511
+ scenario: SCENARIO,
512
+ threwLifecycleError: lc !== null,
513
+ errorCode: lc?.code,
514
+ errorMessage: lc?.message,
515
+ });
516
+ return;
517
+ }
518
+
519
+ if (SCENARIO === "mmap-evict-not-implemented") {
520
+ const ffi = loadElizaInferenceFfi(DYLIB);
521
+ const ctx = ffi.create("/tmp/elizainference-test-bundle");
522
+ let thrown;
523
+ try {
524
+ ffi.mmapEvict(ctx, "tts");
525
+ } catch (e) {
526
+ thrown = e;
527
+ }
528
+ ffi.destroy(ctx);
529
+ ffi.close();
530
+ const lc = asLifecycleErr(thrown);
531
+ emit({
532
+ ok: true,
533
+ scenario: SCENARIO,
534
+ threwLifecycleError: lc !== null,
535
+ errorCode: lc?.code,
536
+ errorMessage: lc?.message,
537
+ });
538
+ return;
539
+ }
540
+
541
+ if (SCENARIO === "mmap-acquire-not-implemented") {
542
+ const ffi = loadElizaInferenceFfi(DYLIB);
543
+ const ctx = ffi.create("/tmp/elizainference-test-bundle");
544
+ let thrown;
545
+ try {
546
+ ffi.mmapAcquire(ctx, "tts");
547
+ } catch (e) {
548
+ thrown = e;
549
+ }
550
+ ffi.destroy(ctx);
551
+ ffi.close();
552
+ const lc = asLifecycleErr(thrown);
553
+ emit({
554
+ ok: true,
555
+ scenario: SCENARIO,
556
+ threwLifecycleError: lc !== null,
557
+ errorCode: lc?.code,
558
+ errorMessage: lc?.message,
559
+ });
560
+ return;
561
+ }
562
+
563
+ if (SCENARIO === "vad-unsupported") {
564
+ const ffi = loadElizaInferenceFfi(DYLIB);
565
+ const supported = ffi.vadSupported();
566
+ ffi.close();
567
+ emit({
568
+ ok: true,
569
+ scenario: SCENARIO,
570
+ vadSupported: supported,
571
+ });
572
+ return;
573
+ }
574
+
575
+ if (SCENARIO === "abi-mismatch") {
576
+ // Force a version-mismatch by importing the binding fresh under a
577
+ // module that monkey-patches the exported constant. We can't mutate
578
+ // a const export, so instead we directly call the underlying bun:ffi
579
+ // dlopen with a wrong expected-version assertion. This mirrors the
580
+ // production guard.
581
+ const { dlopen, FFIType, CString } = (globalThis.Bun.__require ?
582
+ globalThis.Bun.__require("bun:ffi") : await import("bun:ffi"));
583
+ const lib = dlopen(DYLIB, {
584
+ eliza_inference_abi_version: { args: [], returns: FFIType.cstring },
585
+ });
586
+ const reported = lib.symbols.eliza_inference_abi_version();
587
+ const reportedStr = typeof reported === "string"
588
+ ? reported
589
+ : new CString(reported).toString();
590
+ lib.close();
591
+ // Simulate the mismatch path by throwing the same structured error
592
+ // the binding emits when versions disagree.
593
+ let thrown;
594
+ try {
595
+ const fakeExpected = ELIZA_INFERENCE_ABI_VERSION + 999;
596
+ if (reportedStr !== String(fakeExpected)) {
597
+ throw new VoiceLifecycleError(
598
+ "kernel-missing",
599
+ "[ffi-bindings] ABI mismatch: binding expected v" + fakeExpected +
600
+ ", library at " + DYLIB + " reports v" + reportedStr,
601
+ );
602
+ }
603
+ } catch (e) {
604
+ thrown = e;
605
+ }
606
+ const lc = asLifecycleErr(thrown);
607
+ emit({
608
+ ok: true,
609
+ scenario: SCENARIO,
610
+ threwLifecycleError: lc !== null,
611
+ errorCode: lc?.code,
612
+ errorMessage: lc?.message,
613
+ });
614
+ return;
615
+ }
616
+
617
+ emit({ ok: false, scenario: SCENARIO, unexpectedError: "unknown scenario" });
618
+ })().catch((e) => {
619
+ emit({
620
+ ok: false,
621
+ scenario: SCENARIO,
622
+ unexpectedError: e && e.stack ? e.stack : String(e),
623
+ });
624
+ });
625
+ `;
626
+
627
+ const bun = bunOnPath() ?? "bun";
628
+ writeFileSync(scriptPath, script);
629
+ const result = spawnSync(bun, [scriptPath], {
630
+ encoding: "utf8",
631
+ timeout: 30_000,
632
+ });
633
+
634
+ if (result.error) {
635
+ rmSync(tmp, { recursive: true, force: true });
636
+ return {
637
+ ok: false,
638
+ scenario: opts.scenario,
639
+ unexpectedError: `spawn failure: ${result.error.message}`,
640
+ };
641
+ }
642
+ if (result.status !== 0) {
643
+ rmSync(tmp, { recursive: true, force: true });
644
+ return {
645
+ ok: false,
646
+ scenario: opts.scenario,
647
+ unexpectedError: `bun exited ${result.status}\nstdout=${result.stdout}\nstderr=${result.stderr}`,
648
+ };
649
+ }
650
+
651
+ if (existsSync(reportPath)) {
652
+ const report = JSON.parse(
653
+ readFileSync(reportPath, "utf8"),
654
+ ) as HarnessReport;
655
+ rmSync(tmp, { recursive: true, force: true });
656
+ return report;
657
+ }
658
+ rmSync(tmp, { recursive: true, force: true });
659
+
660
+ const lines = result.stdout.split("\n");
661
+ for (const line of lines) {
662
+ if (!line.startsWith("REPORT::")) continue;
663
+ const json = line.slice("REPORT::".length);
664
+ return JSON.parse(json) as HarnessReport;
665
+ }
666
+ return {
667
+ ok: false,
668
+ scenario: opts.scenario,
669
+ unexpectedError: `no REPORT:: line in stdout. stdout=${result.stdout}\nstderr=${result.stderr}`,
670
+ };
671
+ }