@elizaos/app-core 2.0.3-beta.2 → 2.0.3-beta.4

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 (361) hide show
  1. package/agent-bridge.d.ts +5 -5
  2. package/api/auth/audit.d.ts +1 -1
  3. package/api/auth/auth-context.d.ts +1 -1
  4. package/api/auth/bootstrap-token.d.ts +1 -1
  5. package/api/auth/sessions.d.ts +1 -1
  6. package/api/auth-bootstrap-routes.d.ts +1 -1
  7. package/api/auth-pairing-routes.d.ts +1 -1
  8. package/api/auth-pairing-routes.d.ts.map +1 -1
  9. package/api/auth-pairing-routes.js +2 -3
  10. package/api/auth-session-routes.d.ts +2 -2
  11. package/api/auth.d.ts +1 -1
  12. package/api/automations-compat-routes.d.ts +1 -1
  13. package/api/background-tasks-routes.d.ts +1 -1
  14. package/api/catalog-routes.d.ts +1 -1
  15. package/api/cloud-voice-routes.d.ts +1 -1
  16. package/api/compat-route-shared.d.ts +12 -5
  17. package/api/compat-route-shared.d.ts.map +1 -1
  18. package/api/compat-route-shared.js +22 -187
  19. package/api/database-rows-compat-routes.d.ts +1 -1
  20. package/api/dev-compat-routes.d.ts +1 -1
  21. package/api/dev-route-catalog.d.ts.map +1 -1
  22. package/api/dev-route-catalog.js +20 -0
  23. package/api/drop-status-compat-route.d.ts +3 -0
  24. package/api/drop-status-compat-route.d.ts.map +1 -0
  25. package/api/drop-status-compat-route.js +12 -0
  26. package/api/first-run-routes.d.ts +1 -1
  27. package/api/internal-routes.d.ts +1 -1
  28. package/api/runtime-mode-routes.d.ts +1 -1
  29. package/api/secrets-manager-routes.d.ts.map +1 -1
  30. package/api/sensitive-request-routes.d.ts +2 -2
  31. package/api/server-config-filter.d.ts.map +1 -1
  32. package/api/server-config-filter.js +0 -1
  33. package/api/server-first-run-helpers.d.ts.map +1 -1
  34. package/api/server-first-run-helpers.js +0 -3
  35. package/api/server.d.ts +8 -8
  36. package/api/server.d.ts.map +1 -1
  37. package/api/server.js +13 -59
  38. package/api/training-benchmarks.d.ts +1 -1
  39. package/api/training-benchmarks.d.ts.map +1 -1
  40. package/api/training-benchmarks.js +31 -9
  41. package/api/workbench-compat-routes.d.ts +1 -1
  42. package/benchmark/server-utils.d.ts +2 -2
  43. package/browser.d.ts +4 -4
  44. package/cli/plugins-cli.d.ts.map +1 -1
  45. package/cli/plugins-cli.js +5 -0
  46. package/cli/program/register.auth.d.ts +27 -1
  47. package/cli/program/register.auth.d.ts.map +1 -1
  48. package/cli/program/register.auth.js +187 -0
  49. package/cli/program/register.dashboard.d.ts.map +1 -1
  50. package/cli/program/register.dashboard.js +11 -1
  51. package/cli/program.d.ts +1 -1
  52. package/first-run/first-run-config.d.ts +1 -1
  53. package/index.d.ts +46 -47
  54. package/index.d.ts.map +1 -1
  55. package/index.js +3 -7
  56. package/package.json +42 -36
  57. package/packaging/PUBLISHING_GUIDE.md +20 -20
  58. package/platform/elizaos-agent-browser-stub.d.ts +0 -1
  59. package/platform/elizaos-agent-browser-stub.d.ts.map +1 -1
  60. package/platform/elizaos-agent-browser-stub.js +0 -1
  61. package/platform/ios-runtime-bridge.d.ts.map +1 -1
  62. package/platform/ios-runtime-bridge.js +0 -1
  63. package/platform/native-plugin-entrypoints.d.ts +2 -2
  64. package/platforms/android/app/build.gradle +30 -7
  65. package/platforms/android/app/src/main/elizavoice-jni/elizavoice-jni.cpp +179 -35
  66. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaAgentService.java +60 -32
  67. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaBionicInferenceServer.java +405 -65
  68. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaVoiceNative.java +35 -0
  69. package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaVoicePlugin.java +13 -6
  70. package/platforms/android/app/src/main/res/drawable/splash.png +0 -0
  71. package/platforms/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
  72. package/platforms/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
  73. package/platforms/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
  74. package/platforms/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
  75. package/platforms/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
  76. package/platforms/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
  77. package/platforms/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
  78. package/platforms/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
  79. package/platforms/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
  80. package/platforms/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
  81. package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  82. package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  83. package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png +0 -0
  84. package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  85. package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  86. package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  87. package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png +0 -0
  88. package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  89. package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  90. package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  91. package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png +0 -0
  92. package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  93. package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  94. package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  95. package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png +0 -0
  96. package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  97. package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  98. package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  99. package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png +0 -0
  100. package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  101. package/platforms/android/app/src/main/res/values/colors.xml +5 -1
  102. package/platforms/android/app/src/main/res/values/ic_launcher_background.xml +1 -1
  103. package/platforms/android/app/src/main/res/values/styles.xml +4 -1
  104. package/platforms/electrobun/electrobun.config.ts +0 -1
  105. package/platforms/electrobun/native/macos/window-effects.mm +4 -0
  106. package/platforms/electrobun/package.json +11 -9
  107. package/platforms/electrobun/remotes/local-model/src/bun/eliza1-catalog.ts +1 -1
  108. package/platforms/electrobun/remotes/runtime/src/dev/phase2-smoke.ts +10 -5
  109. package/platforms/electrobun/remotes/runtime/src/dev/phase3-smoke.ts +11 -15
  110. package/platforms/electrobun/src/application-menu.ts +0 -7
  111. package/platforms/electrobun/src/desktop-http-request.test.ts +30 -1
  112. package/platforms/electrobun/src/desktop-http-request.ts +14 -2
  113. package/platforms/electrobun/src/desktop-test-bridge-server.ts +28 -0
  114. package/platforms/electrobun/src/fatal-shutdown.test.ts +3 -7
  115. package/platforms/electrobun/src/index.ts +64 -13
  116. package/platforms/electrobun/src/lifecycle/api-base-owner.test.ts +83 -0
  117. package/platforms/electrobun/src/lifecycle/api-base-owner.ts +37 -16
  118. package/platforms/electrobun/src/native/agent.ts +28 -17
  119. package/platforms/electrobun/src/native/credentials.ts +6 -1
  120. package/platforms/electrobun/src/native/desktop-window.test.ts +142 -26
  121. package/platforms/electrobun/src/native/desktop.ts +8 -4
  122. package/platforms/electrobun/src/native/index.ts +6 -0
  123. package/platforms/electrobun/src/native/remote-plugin-host.ts +18 -8
  124. package/platforms/electrobun/src/preload.js +1 -1
  125. package/platforms/electrobun/src/voice/voice-live-validation.test.ts +10 -5
  126. package/platforms/electrobun/src/voice/voice-runtime-adapter.ts +3 -2
  127. package/platforms/electrobun/src/voice/voice-service.test.ts +26 -0
  128. package/platforms/electrobun/src/voice/voice-service.ts +57 -17
  129. package/platforms/electrobun/tsconfig.json +17 -1
  130. package/platforms/ios/App/App/Base.lproj/LaunchScreen.storyboard +4 -6
  131. package/registry/index.d.ts +1 -6
  132. package/registry/index.d.ts.map +1 -1
  133. package/registry/index.js +9 -101
  134. package/runtime/android-avf-microdroid-bridge.d.ts +1 -1
  135. package/runtime/bundled-fused-lib.d.ts +36 -0
  136. package/runtime/bundled-fused-lib.d.ts.map +1 -0
  137. package/runtime/bundled-fused-lib.js +77 -0
  138. package/runtime/channel-plugin-map.d.ts +1 -5
  139. package/runtime/channel-plugin-map.d.ts.map +1 -1
  140. package/runtime/channel-plugin-map.js +11 -31
  141. package/runtime/desktop/index.d.ts +5 -5
  142. package/runtime/dev-server.js +11 -2
  143. package/runtime/eliza.d.ts.map +1 -1
  144. package/runtime/eliza.js +18 -8
  145. package/runtime/mode/route-mode-guard.d.ts +1 -1
  146. package/runtime/mode/route-mode-matrix.d.ts +1 -1
  147. package/runtime/voice-warmup.d.ts +4 -19
  148. package/runtime/voice-warmup.d.ts.map +1 -1
  149. package/runtime/voice-warmup.js +4 -14
  150. package/scripts/aosp/README.md +10 -1
  151. package/scripts/aosp/compile-libllama-paths.mjs +110 -0
  152. package/scripts/aosp/compile-libllama-zig-pin.test.mjs +147 -0
  153. package/scripts/aosp/compile-libllama.mjs +377 -81
  154. package/scripts/aosp/compile-libllama.test.mjs +133 -0
  155. package/scripts/aosp/compile-shim.mjs +21 -1
  156. package/scripts/aosp/deploy-pixel.mjs +1 -1
  157. package/scripts/aosp/stage-default-models.mjs +24 -31
  158. package/scripts/aosp/stage-default-models.test.mjs +23 -0
  159. package/scripts/assert-required-bundled-packages.test.ts +22 -1
  160. package/scripts/build-helpers/verify-fused-symbols.mjs +33 -0
  161. package/scripts/build-helpers/verify-fused-symbols.test.mjs +0 -0
  162. package/scripts/build-image.sh +1 -3
  163. package/scripts/build-llama-cpp-mtp.mjs +17 -16
  164. package/scripts/check-i18n.mjs +0 -1
  165. package/scripts/copy-runtime-node-modules.ts +147 -0
  166. package/scripts/desktop-build.mjs +317 -4
  167. package/scripts/docker-ci-smoke.sh +33 -8
  168. package/scripts/ensure-type-package-aliases.mjs +1 -1
  169. package/scripts/ios-xcframework/run-physical-device-smoke.mjs +1 -1
  170. package/scripts/lib/agent-source-watcher.mjs +0 -1
  171. package/scripts/lib/artifact-staleness.d.mts +33 -0
  172. package/scripts/lib/artifact-staleness.mjs +122 -0
  173. package/scripts/lib/artifact-staleness.test.mts +98 -0
  174. package/scripts/lib/duet-bridge.mjs +1 -1
  175. package/scripts/lib/ios-plist-url-scheme.mjs +80 -0
  176. package/scripts/lib/mobile-build-decisions.mjs +97 -0
  177. package/scripts/lib/renderer-build-manifest.d.mts +65 -0
  178. package/scripts/lib/renderer-build-manifest.mjs +257 -0
  179. package/scripts/lib/renderer-build-manifest.test.mts +277 -0
  180. package/scripts/lib/stage-android-agent.mjs +13 -4
  181. package/scripts/lib/verify-ondevice-artifact.d.mts +16 -0
  182. package/scripts/lib/verify-ondevice-artifact.mjs +61 -0
  183. package/scripts/lib/verify-ondevice-artifact.test.mts +98 -0
  184. package/scripts/lib/websocket-pending-queue.ts +92 -0
  185. package/scripts/link-docker-local-app-packages.mjs +6 -0
  186. package/scripts/playwright-ui-live-stack.ts +430 -144
  187. package/scripts/playwright-ui-smoke-api-stub.mjs +160 -168
  188. package/scripts/run-local-plugin-live-smoke.mjs +1 -6
  189. package/scripts/run-mobile-build-brand-separation.test.mts +33 -0
  190. package/scripts/run-mobile-build-ios-engine-gate.test.mjs +85 -0
  191. package/scripts/run-mobile-build-mtp-staleness.test.mts +198 -0
  192. package/scripts/run-mobile-build.mjs +408 -86
  193. package/scripts/serve-real-local-agent.ts +72 -0
  194. package/scripts/stage-desktop-fused-lib.mjs +43 -14
  195. package/scripts/verify-ondevice-artifact.mjs +111 -0
  196. package/scripts/voice-attribution-smoke.ts +144 -38
  197. package/scripts/voice-duet.mjs +6 -6
  198. package/scripts/voice-e2e-hardware.ts +22 -16
  199. package/scripts/voice-interactive.mjs +16 -45
  200. package/security/agent-vault-id.d.ts +1 -1
  201. package/security/platform-secure-store-node.d.ts +1 -1
  202. package/services/ambient-audio/consent.d.ts +1 -1
  203. package/services/ambient-audio/index.d.ts +6 -6
  204. package/services/ambient-audio/replay-buffer.d.ts +1 -1
  205. package/services/ambient-audio/response-gate.d.ts +1 -1
  206. package/services/ambient-audio/service.d.ts +3 -3
  207. package/services/connector-target-catalog.d.ts +1 -1
  208. package/services/sensitive-requests/index.d.ts +6 -6
  209. package/services/steward-sidecar/wallet-setup.d.ts +1 -1
  210. package/services/steward-sidecar.d.ts +3 -3
  211. package/services/tunnel-to-mobile/index.d.ts +1 -1
  212. package/services/vault-bootstrap.d.ts.map +1 -1
  213. package/services/vault-bootstrap.js +14 -10
  214. package/services/voice-profiles/diarization-pipeline.d.ts +1 -1
  215. package/services/voice-profiles/index.d.ts +11 -11
  216. package/services/voice-profiles/owner-confidence.d.ts +1 -1
  217. package/services/voice-profiles/private-challenge.d.ts +1 -1
  218. package/services/voice-profiles/store.d.ts +1 -1
  219. package/test/helpers/live-provider.test.ts +91 -1
  220. package/test/helpers/live-provider.ts +195 -51
  221. package/test/scripts/websocket-pending-queue.test.ts +119 -0
  222. package/test-support/test-helpers.d.ts +0 -33
  223. package/test-support/test-helpers.d.ts.map +1 -1
  224. package/test-support/test-helpers.js +81 -189
  225. package/ui-compat.d.ts +1 -0
  226. package/ui-compat.d.ts.map +1 -1
  227. package/ui-compat.js +4 -0
  228. package/platforms/electrobun/docs/convergence-audit.json +0 -3505
  229. package/platforms/electrobun/docs/convergence-audit.md +0 -694
  230. package/platforms/electrobun/scripts/ensure-whisper-gguf.sh +0 -55
  231. package/platforms/electrobun/scripts/ensure-whisper-model.sh +0 -35
  232. package/platforms/electrobun/scripts/generate-convergence-audit.ts +0 -1203
  233. package/platforms/electrobun/src/native/whisper-env.test.ts +0 -71
  234. package/platforms/electrobun/src/native/whisper-env.ts +0 -68
  235. package/registry/app-registry.d.ts +0 -14
  236. package/registry/app-registry.d.ts.map +0 -1
  237. package/registry/app-registry.js +0 -29
  238. package/registry/entries/apps/app-polymarket.json +0 -31
  239. package/registry/entries/apps/clawville.json +0 -27
  240. package/registry/entries/apps/companion.json +0 -28
  241. package/registry/entries/apps/database-viewer.json +0 -27
  242. package/registry/entries/apps/defense-of-the-agents.json +0 -27
  243. package/registry/entries/apps/documents.json +0 -30
  244. package/registry/entries/apps/feed.json +0 -27
  245. package/registry/entries/apps/hyperliquid.json +0 -31
  246. package/registry/entries/apps/log-viewer.json +0 -27
  247. package/registry/entries/apps/memory-viewer.json +0 -27
  248. package/registry/entries/apps/model-tester.json +0 -31
  249. package/registry/entries/apps/plugin-viewer.json +0 -27
  250. package/registry/entries/apps/relationship-viewer.json +0 -27
  251. package/registry/entries/apps/runtime-debugger.json +0 -27
  252. package/registry/entries/apps/shopify.json +0 -31
  253. package/registry/entries/apps/skills-viewer.json +0 -27
  254. package/registry/entries/apps/steward.json +0 -31
  255. package/registry/entries/apps/training.json +0 -54
  256. package/registry/entries/apps/trajectory-viewer.json +0 -27
  257. package/registry/entries/apps/vincent.json +0 -31
  258. package/registry/entries/connectors/bluebubbles.json +0 -99
  259. package/registry/entries/connectors/bluesky.json +0 -173
  260. package/registry/entries/connectors/discord.json +0 -119
  261. package/registry/entries/connectors/farcaster.json +0 -174
  262. package/registry/entries/connectors/feishu.json +0 -79
  263. package/registry/entries/connectors/google-chat.json +0 -120
  264. package/registry/entries/connectors/google.json +0 -82
  265. package/registry/entries/connectors/imessage.json +0 -96
  266. package/registry/entries/connectors/instagram.json +0 -64
  267. package/registry/entries/connectors/line.json +0 -86
  268. package/registry/entries/connectors/matrix.json +0 -94
  269. package/registry/entries/connectors/mattermost.json +0 -110
  270. package/registry/entries/connectors/msteams.json +0 -104
  271. package/registry/entries/connectors/nextcloud-talk.json +0 -104
  272. package/registry/entries/connectors/nostr.json +0 -70
  273. package/registry/entries/connectors/signal.json +0 -81
  274. package/registry/entries/connectors/slack.json +0 -102
  275. package/registry/entries/connectors/telegram.json +0 -71
  276. package/registry/entries/connectors/tlon.json +0 -94
  277. package/registry/entries/connectors/twitch.json +0 -110
  278. package/registry/entries/connectors/whatsapp.json +0 -113
  279. package/registry/entries/connectors/x.json +0 -231
  280. package/registry/entries/connectors/zalo.json +0 -112
  281. package/registry/entries/connectors/zalouser.json +0 -122
  282. package/registry/entries/plugins/agent-orchestrator.json +0 -33
  283. package/registry/entries/plugins/agent-skills.json +0 -72
  284. package/registry/entries/plugins/anthropic.json +0 -73
  285. package/registry/entries/plugins/app-control.json +0 -23
  286. package/registry/entries/plugins/auto-trader.json +0 -203
  287. package/registry/entries/plugins/background-runner.json +0 -26
  288. package/registry/entries/plugins/blooio.json +0 -102
  289. package/registry/entries/plugins/browser.json +0 -75
  290. package/registry/entries/plugins/cli.json +0 -40
  291. package/registry/entries/plugins/clipboard.json +0 -44
  292. package/registry/entries/plugins/coding-tools.json +0 -71
  293. package/registry/entries/plugins/commands.json +0 -63
  294. package/registry/entries/plugins/computeruse.json +0 -74
  295. package/registry/entries/plugins/copilot-proxy.json +0 -93
  296. package/registry/entries/plugins/directives.json +0 -63
  297. package/registry/entries/plugins/edge-tts.json +0 -97
  298. package/registry/entries/plugins/elevenlabs.json +0 -169
  299. package/registry/entries/plugins/elizacloud.json +0 -208
  300. package/registry/entries/plugins/evm.json +0 -134
  301. package/registry/entries/plugins/experience.json +0 -34
  302. package/registry/entries/plugins/facewear.json +0 -131
  303. package/registry/entries/plugins/form.json +0 -26
  304. package/registry/entries/plugins/github.json +0 -93
  305. package/registry/entries/plugins/gmail-watch.json +0 -25
  306. package/registry/entries/plugins/goals.json +0 -77
  307. package/registry/entries/plugins/google-genai.json +0 -106
  308. package/registry/entries/plugins/groq.json +0 -93
  309. package/registry/entries/plugins/hedera.json +0 -48
  310. package/registry/entries/plugins/inmemorydb.json +0 -25
  311. package/registry/entries/plugins/linear.json +0 -51
  312. package/registry/entries/plugins/local-inference.json +0 -137
  313. package/registry/entries/plugins/local-storage.json +0 -36
  314. package/registry/entries/plugins/localdb.json +0 -25
  315. package/registry/entries/plugins/mcp.json +0 -44
  316. package/registry/entries/plugins/memory.json +0 -124
  317. package/registry/entries/plugins/minecraft.json +0 -79
  318. package/registry/entries/plugins/moltbook.json +0 -83
  319. package/registry/entries/plugins/music.json +0 -155
  320. package/registry/entries/plugins/mysticism.json +0 -48
  321. package/registry/entries/plugins/nearai.json +0 -82
  322. package/registry/entries/plugins/ngrok.json +0 -69
  323. package/registry/entries/plugins/ollama.json +0 -96
  324. package/registry/entries/plugins/openai.json +0 -189
  325. package/registry/entries/plugins/openrouter.json +0 -188
  326. package/registry/entries/plugins/pdf.json +0 -26
  327. package/registry/entries/plugins/plugin-manager.json +0 -23
  328. package/registry/entries/plugins/prose.json +0 -48
  329. package/registry/entries/plugins/rlm.json +0 -26
  330. package/registry/entries/plugins/roblox.json +0 -88
  331. package/registry/entries/plugins/rss.json +0 -64
  332. package/registry/entries/plugins/s3-storage.json +0 -91
  333. package/registry/entries/plugins/scheduling.json +0 -35
  334. package/registry/entries/plugins/shell.json +0 -94
  335. package/registry/entries/plugins/social-alpha.json +0 -72
  336. package/registry/entries/plugins/tailscale.json +0 -81
  337. package/registry/entries/plugins/tee.json +0 -53
  338. package/registry/entries/plugins/todos.json +0 -26
  339. package/registry/entries/plugins/trajectory-logger.json +0 -33
  340. package/registry/entries/plugins/trust.json +0 -39
  341. package/registry/entries/plugins/tts.json +0 -71
  342. package/registry/entries/plugins/tunnel.json +0 -45
  343. package/registry/entries/plugins/twilio.json +0 -168
  344. package/registry/entries/plugins/vercel-ai-gateway.json +0 -128
  345. package/registry/entries/plugins/video.json +0 -23
  346. package/registry/entries/plugins/vision.json +0 -43
  347. package/registry/entries/plugins/webhooks.json +0 -23
  348. package/registry/entries/plugins/workflow.json +0 -25
  349. package/registry/entries/plugins/xai.json +0 -75
  350. package/registry/loader.d.ts +0 -28
  351. package/registry/loader.d.ts.map +0 -1
  352. package/registry/loader.js +0 -158
  353. package/registry/schema.d.ts +0 -1199
  354. package/registry/schema.d.ts.map +0 -1
  355. package/registry/schema.js +0 -300
  356. package/scripts/patches/llama-mobile-kokoro-tts.patch +0 -480
  357. package/scripts/release-workflow-drift.test.ts +0 -57
  358. package/test/helpers/i18n.ts +0 -5
  359. package/test/helpers/loopback.ts +0 -33
  360. package/test/helpers/react-test.ts +0 -40
  361. package/test/helpers/skip-without.ts +0 -53
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Persistent real local agent for device e2e.
3
+ *
4
+ * This is the long-running counterpart to check-real-local-chat.ts: it boots a
5
+ * real AgentRuntime + real app-core HTTP API with a deterministic model plugin,
6
+ * then stays alive until the surrounding workflow sends SIGTERM. Android
7
+ * WebView tests reach it through adb reverse as a "remote" first-run target.
8
+ */
9
+
10
+ import { createDeterministicLlmProxyPlugin } from "../../test/mocks/helpers/llm-proxy-plugin.ts";
11
+ import { startApiServer } from "../src/api/server.ts";
12
+ import { useIsolatedConfigEnv } from "../test/helpers/isolated-config.ts";
13
+ import { createRealTestRuntime } from "../test/helpers/real-runtime.ts";
14
+
15
+ function resolvePort(): number {
16
+ const raw = process.env.ELIZA_API_PORT ?? process.env.ELIZA_PORT ?? "31337";
17
+ const port = Number.parseInt(raw, 10);
18
+ if (!Number.isFinite(port) || port <= 0) {
19
+ throw new Error(`Invalid ELIZA_API_PORT/ELIZA_PORT: ${raw}`);
20
+ }
21
+ return port;
22
+ }
23
+
24
+ async function main(): Promise<void> {
25
+ const t0 = Date.now();
26
+ const port = resolvePort();
27
+
28
+ process.env.ELIZA_PAIRING_DISABLED ??= "1";
29
+
30
+ const configEnv = useIsolatedConfigEnv("eliza-device-e2e-host-agent-");
31
+ const proxy = createDeterministicLlmProxyPlugin({
32
+ failOnUnhandledAction: false,
33
+ });
34
+ const runtimeResult = await createRealTestRuntime({
35
+ characterName: "DeviceE2EHostAgent",
36
+ plugins: [proxy],
37
+ });
38
+ const server = await startApiServer({
39
+ port,
40
+ runtime: runtimeResult.runtime,
41
+ skipDeferredStartupWork: true,
42
+ });
43
+
44
+ console.log(
45
+ `[device-e2e-host-agent] real API up on :${server.port} in ${Date.now() - t0}ms`,
46
+ );
47
+
48
+ let stopping = false;
49
+ const stop = async (signal: string) => {
50
+ if (stopping) return;
51
+ stopping = true;
52
+ console.log(`[device-e2e-host-agent] stopping (${signal})`);
53
+ await server.close().catch(() => undefined);
54
+ await runtimeResult.cleanup().catch(() => undefined);
55
+ await configEnv.restore().catch(() => undefined);
56
+ };
57
+
58
+ for (const signal of ["SIGINT", "SIGTERM"] as const) {
59
+ process.once(signal, () => {
60
+ void stop(signal).finally(() => process.exit(0));
61
+ });
62
+ }
63
+
64
+ await new Promise<never>(() => {});
65
+ }
66
+
67
+ main().catch((error) => {
68
+ console.error(
69
+ `[device-e2e-host-agent] FAILED: ${error instanceof Error ? (error.stack ?? error.message) : String(error)}`,
70
+ );
71
+ process.exit(1);
72
+ });
@@ -236,9 +236,12 @@ function backendCmakeFlags(backend) {
236
236
  }
237
237
  }
238
238
 
239
- const { variant, outDir: outOverride, jobs: jobsArg, force } = parseArgs(
240
- process.argv.slice(2),
241
- );
239
+ const {
240
+ variant,
241
+ outDir: outOverride,
242
+ jobs: jobsArg,
243
+ force,
244
+ } = parseArgs(process.argv.slice(2));
242
245
 
243
246
  if (!existsSync(path.join(forkSrc, "CMakeLists.txt"))) {
244
247
  die(
@@ -249,12 +252,13 @@ if (!have("cmake")) die("cmake not found on PATH");
249
252
 
250
253
  const backend = variant === "auto" ? detectBackend() : variant;
251
254
  log(`host: ${process.platform}/${process.arch}`);
252
- log(`backend: ${backend}${variant === "auto" ? " (autodetected)" : ""} (GGML_CPU always on for fallback)`);
255
+ log(
256
+ `backend: ${backend}${variant === "auto" ? " (autodetected)" : ""} (GGML_CPU always on for fallback)`,
257
+ );
253
258
 
254
259
  const buildDir = path.join(forkSrc, `build-desktop-${backend}`);
255
260
  const outDir =
256
- outOverride ||
257
- path.join(resolveStateDir(), "local-inference", "lib");
261
+ outOverride || path.join(resolveStateDir(), "local-inference", "lib");
258
262
 
259
263
  if (force && existsSync(buildDir)) {
260
264
  rmSync(buildDir, { recursive: true, force: true });
@@ -276,7 +280,7 @@ const jobs =
276
280
  // (so the GPU backend can load the system driver at runtime) and matches the
277
281
  // dlopen()-able sibling set the runtime resolves. LLAMA_BUILD_OMNIVOICE +
278
282
  // LLAMA_BUILD_MTMD + LLAMA_BUILD_KOKORO are required for the fused
279
- // `elizainference` SHARED target (TTS + Qwen3-ASR + Kokoro). GGML_NATIVE=ON tunes
283
+ // `elizainference` SHARED target (TTS + local ASR + Kokoro). GGML_NATIVE=ON tunes
280
284
  // the CPU backend to the build host — correct for a local/dev build; a
281
285
  // redistributable build should pin explicit CPU features instead.
282
286
  run("cmake", [
@@ -310,6 +314,11 @@ run("cmake", [
310
314
  buildDir,
311
315
  "--target",
312
316
  "elizainference",
317
+ // Multi-config generators (MSVC, Xcode) ignore -DCMAKE_BUILD_TYPE and pick
318
+ // Debug unless the build step names the config; single-config generators
319
+ // (Make, Ninja) silently ignore --config, so this is safe everywhere.
320
+ "--config",
321
+ "Release",
313
322
  "-j",
314
323
  String(jobs),
315
324
  ]);
@@ -317,14 +326,23 @@ run("cmake", [
317
326
  // Collect the produced shared libs (the fused lib + its ggml/llama/mtmd
318
327
  // backends) and stage them as one consistent set. Sweep the out dir first so a
319
328
  // backend switch never leaves a stale sibling the loader could pick up.
320
- const binDir = path.join(buildDir, "bin");
329
+ // Multi-config generators (MSVC, Xcode) nest the artifacts under bin/<Config>;
330
+ // single-config generators (Make, Ninja) emit straight into bin/.
331
+ let binDir = path.join(buildDir, "bin");
332
+ const releaseBinDir = path.join(binDir, "Release");
333
+ if (existsSync(releaseBinDir)) binDir = releaseBinDir;
321
334
  const libExt =
322
335
  process.platform === "darwin"
323
336
  ? ".dylib"
324
337
  : process.platform === "win32"
325
338
  ? ".dll"
326
339
  : ".so";
327
- const fusedName = `libelizainference${libExt}`;
340
+ // CMake prepends the `lib` prefix to shared libs on Unix only; MSVC/Windows
341
+ // emits the bare target name (elizainference.dll).
342
+ const fusedName =
343
+ process.platform === "win32"
344
+ ? `elizainference${libExt}`
345
+ : `libelizainference${libExt}`;
328
346
  if (!existsSync(path.join(binDir, fusedName))) {
329
347
  die(`build did not produce ${fusedName} in ${binDir}`);
330
348
  }
@@ -378,7 +396,10 @@ function definedSymbols(libPath) {
378
396
  process.platform === "darwin"
379
397
  ? { cmd: "nm", args: ["-gU", libPath] }
380
398
  : process.platform === "win32"
381
- ? { cmd: "objdump", args: ["-T", libPath] }
399
+ ? // PE exports live in the export-address table shown by `objdump -p`
400
+ // ("[Ordinal/Name Pointer] Table"); `-T` is the ELF dynamic-symbol
401
+ // flag and lists nothing for a .dll, so it would false-fail the verify.
402
+ { cmd: "objdump", args: ["-p", libPath] }
382
403
  : { cmd: "nm", args: ["-D", "--defined-only", libPath] };
383
404
  try {
384
405
  return execFileSync(tool.cmd, tool.args, {
@@ -396,8 +417,14 @@ function verifyFusedSymbols(stagedDir) {
396
417
  log("symbol verify skipped (nm/objdump unavailable)");
397
418
  return;
398
419
  }
399
- // eliza_inference_* and ov_* must be in the fused lib itself.
400
- const inFused = { "eliza_inference_*": /\beliza_inference_/, "ov_*": /\bov_/ };
420
+ // eliza_inference_* and ov_* must be in the fused lib itself. Anchor to the
421
+ // symbol start after nm's type column and allow the Mach-O leading underscore
422
+ // (`_eliza_inference_*` on macOS, `eliza_inference_*` on Linux) — a bare `\b`
423
+ // does NOT match between `_` and a letter, so it false-fails on macOS.
424
+ const inFused = {
425
+ "eliza_inference_*": /(?:^|\s)_?eliza_inference_/m,
426
+ "ov_*": /(?:^|\s)_?ov_/m,
427
+ };
401
428
  const missingFused = Object.entries(inFused)
402
429
  .filter(([, re]) => !re.test(fusedSyms))
403
430
  .map(([n]) => n);
@@ -410,12 +437,14 @@ function verifyFusedSymbols(stagedDir) {
410
437
  // llama_* across the staged set (the sibling libllama in a shared build).
411
438
  const llamaHere = staged.some((n) => {
412
439
  const s = definedSymbols(path.join(stagedDir, n));
413
- return s !== null && /\bllama_/.test(s);
440
+ return s !== null && /(?:^|\s)_?llama_/m.test(s);
414
441
  });
415
442
  if (!llamaHere) {
416
443
  die(`llama_* symbols not found in the staged lib set — incomplete build.`);
417
444
  }
418
- log("symbol verify OK: eliza_inference_* + ov_* in fused lib, llama_* in set");
445
+ log(
446
+ "symbol verify OK: eliza_inference_* + ov_* in fused lib, llama_* in set",
447
+ );
419
448
  }
420
449
 
421
450
  log("");
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI: verify a staged on-device artifact contains the freshly built renderer +
4
+ * required companion files (agent bundle, native libs). A post-build CI gate for
5
+ * issue #9309.
6
+ *
7
+ * Usage:
8
+ * node verify-ondevice-artifact.mjs \
9
+ * --renderer-dir packages/app/ios/App/App/public \
10
+ * --fresh-dist packages/app/dist \
11
+ * --require agent/agent-bundle.js \
12
+ * --label ios
13
+ *
14
+ * # presets resolve the standard paths for a platform:
15
+ * node verify-ondevice-artifact.mjs --platform ios [--local]
16
+ * node verify-ondevice-artifact.mjs --platform desktop
17
+ */
18
+ import path from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+
21
+ import { verifyStagedArtifact } from "./lib/verify-ondevice-artifact.mjs";
22
+
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+ const repoRoot = path.resolve(__dirname, "..", "..", "..");
25
+
26
+ function arg(name) {
27
+ const idx = process.argv.indexOf(`--${name}`);
28
+ return idx >= 0 ? process.argv[idx + 1] : undefined;
29
+ }
30
+ function flag(name) {
31
+ return process.argv.includes(`--${name}`);
32
+ }
33
+ function repeatedArg(name) {
34
+ const out = [];
35
+ for (let i = 0; i < process.argv.length; i += 1) {
36
+ if (process.argv[i] === `--${name}` && process.argv[i + 1]) {
37
+ out.push(process.argv[i + 1]);
38
+ }
39
+ }
40
+ return out;
41
+ }
42
+
43
+ function presetFor(platform, { local }) {
44
+ const appDist = path.join(repoRoot, "packages", "app", "dist");
45
+ if (platform === "ios") {
46
+ return {
47
+ rendererDir: path.join(
48
+ repoRoot,
49
+ "packages",
50
+ "app",
51
+ "ios",
52
+ "App",
53
+ "App",
54
+ "public",
55
+ ),
56
+ freshDistDir: appDist,
57
+ requiredFiles: local ? ["agent/agent-bundle.js"] : [],
58
+ label: "ios",
59
+ };
60
+ }
61
+ if (platform === "android") {
62
+ return {
63
+ rendererDir: path.join(
64
+ repoRoot,
65
+ "packages",
66
+ "app-core",
67
+ "platforms",
68
+ "android",
69
+ "app",
70
+ "src",
71
+ "main",
72
+ "assets",
73
+ "public",
74
+ ),
75
+ freshDistDir: appDist,
76
+ requiredFiles: [],
77
+ label: "android",
78
+ };
79
+ }
80
+ if (platform === "desktop") {
81
+ return {
82
+ rendererDir: appDist,
83
+ freshDistDir: appDist,
84
+ requiredFiles: [],
85
+ label: "desktop",
86
+ };
87
+ }
88
+ throw new Error(`unknown --platform ${platform}`);
89
+ }
90
+
91
+ const platform = arg("platform");
92
+ const config = platform
93
+ ? presetFor(platform, { local: flag("local") })
94
+ : {
95
+ rendererDir: path.resolve(arg("renderer-dir") ?? "."),
96
+ freshDistDir: arg("fresh-dist") ? path.resolve(arg("fresh-dist")) : null,
97
+ requiredFiles: repeatedArg("require"),
98
+ label: arg("label") ?? "artifact",
99
+ };
100
+
101
+ const result = verifyStagedArtifact(config);
102
+ if (!result.ok) {
103
+ console.error(`[verify-ondevice-artifact] FAIL (${config.label}):`);
104
+ for (const problem of result.problems) console.error(` - ${problem}`);
105
+ process.exit(1);
106
+ }
107
+ console.log(
108
+ `[verify-ondevice-artifact] OK (${config.label}): renderer buildId=${
109
+ result.manifest ? String(result.manifest.buildId).slice(0, 12) : "?"
110
+ }, ${config.requiredFiles.length} required file(s) present.`,
111
+ );
@@ -22,13 +22,22 @@
22
22
  * --models /path/to/dir/with/{silero-vad-v5,wespeaker-resnet34-lm,pyannote-segmentation-3.0}.gguf
23
23
  * ELIZA_VOICE_REAL_MODEL_DIR=/path/to/models bun packages/app-core/scripts/voice-attribution-smoke.ts
24
24
  *
25
- * Exit 0 on pass OR when models / the fused lib are absent (skipped); 1 on any
26
- * assertion fail.
25
+ * Exit 0 on pass OR when models / the fused lib are absent (skipped). Pass
26
+ * `--require-real` (or set `ELIZA_VOICE_REAL_REQUIRE=1`) in provisioned CI to
27
+ * convert any skip into a hard failure; the real matrix must produce evidence,
28
+ * not a green skip.
27
29
  */
28
- import { existsSync, mkdtempSync, readFileSync } from "node:fs";
30
+ import {
31
+ copyFileSync,
32
+ existsSync,
33
+ mkdirSync,
34
+ mkdtempSync,
35
+ readFileSync,
36
+ } from "node:fs";
29
37
  import { tmpdir } from "node:os";
30
38
  import path from "node:path";
31
39
  import process from "node:process";
40
+ import { buildVoiceTurnSignal } from "../../../packages/shared/src/voice/respond-gate.ts";
32
41
  import { handleLiveVoiceAttribution } from "../../../plugins/plugin-local-inference/src/runtime/voice-entity-binding.ts";
33
42
  import { resolveFusedLibraryPath } from "../../../plugins/plugin-local-inference/src/services/desktop-fused-ffi-backend-runtime.ts";
34
43
  import {
@@ -40,14 +49,10 @@ import { VoiceProfileStore } from "../../../plugins/plugin-local-inference/src/s
40
49
  import { VoiceAttributionPipeline } from "../../../plugins/plugin-local-inference/src/services/voice/speaker/attribution-pipeline.ts";
41
50
  import { FusedDiarizer } from "../../../plugins/plugin-local-inference/src/services/voice/speaker/diarizer-fused.ts";
42
51
  import { FusedSpeakerEncoder } from "../../../plugins/plugin-local-inference/src/services/voice/speaker/encoder-fused.ts";
43
- import type { VadDetector } from "../../../plugins/plugin-local-inference/src/services/voice/vad.ts";
44
-
45
- // The Silero VAD is exclusively the fused `libelizainference` runtime
46
- // (`eliza_inference_vad_*`, the `GgmlSileroVad` / `silero-ggml` backend). The
47
- // two VAD-driven stages below need a live VAD-driven turn segmenter; the
48
- // AudioFrameConsumer stage is gated as a documented follow-up until this
49
- // harness drives `GgmlSileroVad` off the fused ctx.
50
- const FUSED_VAD_AVAILABLE = false;
52
+ import {
53
+ GgmlSileroVad,
54
+ VadDetector,
55
+ } from "../../../plugins/plugin-local-inference/src/services/voice/vad.ts";
51
56
 
52
57
  const REPO_ROOT = path.resolve(import.meta.dir, "../../..");
53
58
  const WAV = path.join(
@@ -60,49 +65,104 @@ function arg(name: string): string | undefined {
60
65
  return i >= 0 ? process.argv[i + 1] : undefined;
61
66
  }
62
67
 
68
+ const REQUIRE_REAL =
69
+ process.argv.includes("--require-real") ||
70
+ process.env.ELIZA_VOICE_REAL_REQUIRE?.trim() === "1";
71
+
72
+ function skip(message: string): never {
73
+ const prefix = REQUIRE_REAL ? "FAIL" : "SKIP";
74
+ console.log(`[voice-attribution-smoke] ${prefix} — ${message}`);
75
+ process.exit(REQUIRE_REAL ? 1 : 0);
76
+ }
77
+
63
78
  const modelsDir =
64
79
  arg("--models") ??
65
80
  process.env.ELIZA_VOICE_REAL_MODEL_DIR?.trim() ??
66
81
  "/tmp/voice-models";
82
+ function firstExisting(...parts: string[]): string | null {
83
+ for (const part of parts) {
84
+ const p = path.isAbsolute(part) ? part : path.join(modelsDir, part);
85
+ if (existsSync(p)) return p;
86
+ }
87
+ return null;
88
+ }
67
89
  const M = {
68
- vad: path.join(modelsDir, "silero-vad-v5.gguf"),
69
- enc: path.join(modelsDir, "wespeaker-resnet34-lm.gguf"),
70
- dia: path.join(modelsDir, "pyannote-segmentation-3.0.gguf"),
90
+ vad: firstExisting(
91
+ "silero-vad-v5.1.2.ggml.bin",
92
+ "silero-vad-v5.gguf",
93
+ "vad/silero-vad-v5.1.2.ggml.bin",
94
+ "vad/silero-vad-v5.gguf",
95
+ "voice/vad/silero-vad-v5.1.2.ggml.bin",
96
+ "bundles/2b/vad/silero-vad-v5.gguf",
97
+ ),
98
+ enc: firstExisting(
99
+ "wespeaker-resnet34-lm.gguf",
100
+ "speaker/wespeaker-resnet34-lm.gguf",
101
+ "speaker-encoder/wespeaker-resnet34-lm.gguf",
102
+ "voice/speaker-encoder/wespeaker-resnet34-lm.gguf",
103
+ ),
104
+ dia: firstExisting(
105
+ "pyannote-segmentation-3.0.gguf",
106
+ "diariz/pyannote-segmentation-3.0.gguf",
107
+ "diarizer/pyannote-segmentation-3.0.gguf",
108
+ "voice/diarizer/pyannote-segmentation-3.0.gguf",
109
+ ),
71
110
  };
72
111
 
73
- if (!existsSync(M.vad) || !existsSync(M.enc) || !existsSync(M.dia)) {
74
- console.log(
75
- `[voice-attribution-smoke] SKIP — GGUF models not found under ${modelsDir}.\n` +
76
- " Produce them with the converters in packages/native/plugins/<lib>/scripts/ and pass --models <dir>.",
112
+ if (!M.vad || !M.enc || !M.dia) {
113
+ skip(
114
+ `GGUF models not found under ${modelsDir}.\n` +
115
+ " Expected Silero VAD, WeSpeaker, and pyannote assets; pass --models <dir> or set ELIZA_VOICE_REAL_MODEL_DIR.",
77
116
  );
78
- process.exit(0);
79
117
  }
80
118
 
119
+ function stageVoiceBundle(models: { vad: string; enc: string; dia: string }): {
120
+ root: string;
121
+ vad: string;
122
+ enc: string;
123
+ dia: string;
124
+ } {
125
+ const root = mkdtempSync(path.join(tmpdir(), "voice-model-bundle-"));
126
+ const out = {
127
+ root,
128
+ vad: path.join(root, "vad", path.basename(models.vad)),
129
+ enc: path.join(root, "speaker", "wespeaker-resnet34-lm.gguf"),
130
+ dia: path.join(root, "diariz", "pyannote-segmentation-3.0.gguf"),
131
+ };
132
+ mkdirSync(path.dirname(out.vad), { recursive: true });
133
+ mkdirSync(path.dirname(out.enc), { recursive: true });
134
+ mkdirSync(path.dirname(out.dia), { recursive: true });
135
+ copyFileSync(models.vad, out.vad);
136
+ copyFileSync(models.enc, out.enc);
137
+ copyFileSync(models.dia, out.dia);
138
+ return out;
139
+ }
140
+
141
+ const BUNDLE = stageVoiceBundle(M as { vad: string; enc: string; dia: string });
142
+
81
143
  // Resolve the fused libelizainference. The speaker encoder + diarizer run
82
144
  // EXCLUSIVELY through it (the `eliza_inference_speaker_*` / `_diariz_*` ABI off
83
145
  // one handle); there is no standalone runtime to fall back to.
84
146
  const FUSED_LIB = resolveFusedLibraryPath(modelsDir, process.env);
85
147
  if (!FUSED_LIB) {
86
- console.log(
87
- "[voice-attribution-smoke] SKIP — fused libelizainference not found.\n" +
148
+ skip(
149
+ "fused libelizainference not found.\n" +
88
150
  " Set $ELIZA_INFERENCE_LIBRARY (exact) or $ELIZA_INFERENCE_LIB_DIR, or build it via packages/app-core/scripts/build-llama-cpp-mtp.mjs.",
89
151
  );
90
- process.exit(0);
91
152
  }
92
153
  const FFI = loadElizaInferenceFfi(FUSED_LIB);
93
- const FFI_CTX = FFI.create(modelsDir);
154
+ const FFI_CTX = FFI.create(BUNDLE.root);
94
155
  if (!FusedSpeakerEncoder.isSupported(FFI)) {
95
- console.log(
96
- `[voice-attribution-smoke] SKIP — the fused lib at ${FUSED_LIB} (ABI v${FFI.libraryAbiVersion}) lacks the speaker ABI (eliza_inference_speaker_supported() == 0). Rebuild with the WeSpeaker forward graph linked in.`,
156
+ skip(
157
+ `the fused lib at ${FUSED_LIB} (ABI v${FFI.libraryAbiVersion}) lacks the speaker ABI (eliza_inference_speaker_supported() == 0). Rebuild with the WeSpeaker forward graph linked in.`,
97
158
  );
98
- process.exit(0);
99
159
  }
100
160
  if (!FusedDiarizer.isSupported(FFI)) {
101
- console.log(
102
- `[voice-attribution-smoke] SKIP — the fused lib at ${FUSED_LIB} (ABI v${FFI.libraryAbiVersion}) lacks the diarizer ABI (eliza_inference_diariz_supported() == 0). Rebuild with the pyannote forward graph linked in.`,
161
+ skip(
162
+ `the fused lib at ${FUSED_LIB} (ABI v${FFI.libraryAbiVersion}) lacks the diarizer ABI (eliza_inference_diariz_supported() == 0). Rebuild with the pyannote forward graph linked in.`,
103
163
  );
104
- process.exit(0);
105
164
  }
165
+ const FUSED_VAD_AVAILABLE = GgmlSileroVad.isSupported(FFI);
106
166
 
107
167
  /** Decode a PCM16 mono WAV → { pcm, sampleRate }. */
108
168
  function decodeWavMono(file: string): {
@@ -168,13 +228,43 @@ console.log(
168
228
  );
169
229
 
170
230
  // VAD — fused-only; the fused ffi + ctx are booted above, but the VAD-driven
171
- // turn-segmentation stage is not wired in this pass. See FUSED_VAD_AVAILABLE.
231
+ // turn-segmentation stage runs through GgmlSileroVad on the same FFI context.
172
232
  if (FUSED_VAD_AVAILABLE) {
173
- // Re-enable once this harness drives `GgmlSileroVad` off FFI/FFI_CTX.
174
- } else {
175
- console.log(
176
- "[voice-attribution-smoke] SKIP VAD stage — fused libelizainference VAD context not booted in this harness.",
233
+ const vad = await GgmlSileroVad.load({ ffi: FFI, ctx: FFI_CTX });
234
+ const silence = new Float32Array(vad.windowSamples);
235
+ let speechMax = 0;
236
+ for (
237
+ let off = 0;
238
+ off + vad.windowSamples <= Math.min(pcm.length, 16_000 * 4);
239
+ off += vad.windowSamples
240
+ ) {
241
+ const p = await vad.process(pcm.subarray(off, off + vad.windowSamples));
242
+ if (p > speechMax) speechMax = p;
243
+ }
244
+ vad.reset();
245
+ let silenceMax = 0;
246
+ for (let i = 0; i < 8; i += 1) {
247
+ const p = await vad.process(silence);
248
+ if (p > silenceMax) silenceMax = p;
249
+ }
250
+ vad.close();
251
+ ok(
252
+ "Silero VAD: real speech scores above silence",
253
+ speechMax > silenceMax && speechMax > 0.2,
254
+ `speechMax=${speechMax.toFixed(3)} silenceMax=${silenceMax.toFixed(3)}`,
177
255
  );
256
+ } else {
257
+ if (REQUIRE_REAL) {
258
+ ok(
259
+ "Silero VAD: fused lib advertises eliza_inference_vad_* support",
260
+ false,
261
+ "fused libelizainference does not advertise eliza_inference_vad_* support",
262
+ );
263
+ } else {
264
+ console.log(
265
+ "[voice-attribution-smoke] SKIP VAD stage — fused libelizainference does not advertise eliza_inference_vad_* support.",
266
+ );
267
+ }
178
268
  }
179
269
 
180
270
  // Encoder
@@ -204,6 +294,17 @@ if (FUSED_VAD_AVAILABLE) {
204
294
  cos(eA, eB) > 0.78,
205
295
  `cos(A,B)=${cos(eA, eB).toFixed(3)}`,
206
296
  );
297
+ const selfVoiceSimilarity = cos(eA, eA2);
298
+ const selfVoiceSignal = buildVoiceTurnSignal("misheard synthetic echo", {
299
+ agentSpeaking: true,
300
+ selfVoiceSimilarity,
301
+ });
302
+ ok(
303
+ "Live selfVoiceSimilarity cosine suppresses an agent-echo turn",
304
+ selfVoiceSignal.agentShouldSpeak === false &&
305
+ selfVoiceSignal.source === "client-ambient+self-voice",
306
+ `selfVoiceSimilarity=${selfVoiceSimilarity.toFixed(4)} source=${selfVoiceSignal.source}`,
307
+ );
207
308
  await enc.dispose();
208
309
  }
209
310
 
@@ -401,8 +502,7 @@ if (FUSED_VAD_AVAILABLE) {
401
502
  //
402
503
  // Gated on FUSED_VAD_AVAILABLE: AudioFrameConsumer needs a real VAD to segment
403
504
  // turns, and the sole VAD runtime is now the fused libelizainference engine,
404
- // which requires an EngineVoiceBridge (ffi + ctx) this standalone-model harness
405
- // does not boot. Follow-up: stand up the bridge and feed `GgmlSileroVad`.
505
+ // which this standalone-model harness now boots from the same FFI context.
406
506
  if (FUSED_VAD_AVAILABLE) {
407
507
  /** Encode a Float32 [-1,1] window → base64 LE-s16, as the native side does. */
408
508
  function encodeFrame(
@@ -428,8 +528,13 @@ if (FUSED_VAD_AVAILABLE) {
428
528
  };
429
529
  }
430
530
 
431
- // Fused VAD detector (wired once the bridge is stood up).
432
- const detector = null as unknown as VadDetector;
531
+ const fusedVad = await GgmlSileroVad.load({ ffi: FFI, ctx: FFI_CTX });
532
+ const detector = new VadDetector(fusedVad, {
533
+ onsetThreshold: 0.5,
534
+ pauseHangoverMs: 120,
535
+ endHangoverMs: 500,
536
+ minSpeechMs: 250,
537
+ });
433
538
  const encoder = await FusedSpeakerEncoder.load({
434
539
  ffi: FFI,
435
540
  ctx: FFI_CTX,
@@ -526,6 +631,7 @@ if (FUSED_VAD_AVAILABLE) {
526
631
  );
527
632
 
528
633
  await consumer.close();
634
+ fusedVad.close();
529
635
  await encoder.dispose();
530
636
  await diarizer.dispose?.();
531
637
  }
@@ -3,7 +3,7 @@
3
3
  * Two-agents-talking-endlessly voice harness for Eliza-1 (`bun run voice:duet`).
4
4
  *
5
5
  * Agent A and agent B are two `LocalInferenceEngine` instances on the **same
6
- * tier bundle** (`eliza-1-0_8b` by default, then `eliza-1-2b`) but with
6
+ * tier bundle** (`eliza-1-2b` by default the smallest/entry tier) but with
7
7
  * **different characters** — A's `replyText` → A's OmniVoice TTS →
8
8
  * `InMemoryAudioSink`-shaped `DuetSink` (24 kHz → 16 kHz) → a ring →
9
9
  * B's `PushMicSource` → B's VAD + streaming ASR → B's `VoiceTurnController`
@@ -60,7 +60,7 @@ const __filename = fileURLToPath(import.meta.url);
60
60
 
61
61
  function parseArgs(argv) {
62
62
  const out = {
63
- model: "eliza-1-0_8b",
63
+ model: "eliza-1-2b",
64
64
  turns: Infinity,
65
65
  characterA: null,
66
66
  characterB: null,
@@ -126,7 +126,7 @@ function intArg(s) {
126
126
 
127
127
  const USAGE = `Usage: bun run voice:duet [-- <options>]
128
128
 
129
- --model <id> tier bundle (default eliza-1-0_8b; also eliza-1-2b)
129
+ --model <id> tier bundle (default eliza-1-2b; also eliza-1-4b)
130
130
  --turns <N> stop after N round-trips (default: endless)
131
131
  --character-a <path> agent A's Character JSON (default: a baked-in persona)
132
132
  --character-b <path> agent B's Character JSON (default: a baked-in persona)
@@ -789,7 +789,7 @@ async function main() {
789
789
  let perceived = null;
790
790
  if (perceivedRaw) {
791
791
  perceived = asrEmotionToTag(perceivedRaw) ?? null;
792
- emotionPerceiver = "qwen3-asr-emotion";
792
+ emotionPerceiver = "local-asr-emotion";
793
793
  } else {
794
794
  emotionPerceiver = "fallback-classifier:unavailable";
795
795
  }
@@ -1039,7 +1039,7 @@ async function main() {
1039
1039
  },
1040
1040
  notes:
1041
1041
  accuracy == null
1042
- ? "emotionFidelity.accuracy is null — the GGUF-converted Qwen3-ASR did not surface an emotion label in this run and no fallback emotion-from-audio classifier was available; recorded as null per the honesty contract, not fabricated. structured-decode token-savings % is null when the running llama-server's /metrics did not expose the guided-decode counter."
1042
+ ? "emotionFidelity.accuracy is null — the GGUF-converted local ASR did not surface an emotion label in this run and no fallback emotion-from-audio classifier was available; recorded as null per the honesty contract, not fabricated. structured-decode token-savings % is null when the running llama-server's /metrics did not expose the guided-decode counter."
1043
1043
  : `emotionFidelity perceiver: ${emotionPerceiver}.`,
1044
1044
  turns: turnLog,
1045
1045
  };
@@ -1098,7 +1098,7 @@ async function main() {
1098
1098
  }
1099
1099
 
1100
1100
  // ---------------------------------------------------------------------------
1101
- // Best-effort transcript emotion extraction (Qwen3-ASR `<emotion>…</emotion>`
1101
+ // Best-effort transcript emotion extraction (local ASR `<emotion>…</emotion>`
1102
1102
  // or a `[emotion:happy]`-style tag, if the GGUF-ASR surfaces one). Returns the
1103
1103
  // raw label or null. Honest: if the transcript has no emotion marker, null —
1104
1104
  // the fidelity metric records "perceiver: fallback-classifier (unavailable)".