@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.
- package/agent-bridge.d.ts +5 -5
- package/api/auth/audit.d.ts +1 -1
- package/api/auth/auth-context.d.ts +1 -1
- package/api/auth/bootstrap-token.d.ts +1 -1
- package/api/auth/sessions.d.ts +1 -1
- package/api/auth-bootstrap-routes.d.ts +1 -1
- package/api/auth-pairing-routes.d.ts +1 -1
- package/api/auth-pairing-routes.d.ts.map +1 -1
- package/api/auth-pairing-routes.js +2 -3
- package/api/auth-session-routes.d.ts +2 -2
- package/api/auth.d.ts +1 -1
- package/api/automations-compat-routes.d.ts +1 -1
- package/api/background-tasks-routes.d.ts +1 -1
- package/api/catalog-routes.d.ts +1 -1
- package/api/cloud-voice-routes.d.ts +1 -1
- package/api/compat-route-shared.d.ts +12 -5
- package/api/compat-route-shared.d.ts.map +1 -1
- package/api/compat-route-shared.js +22 -187
- package/api/database-rows-compat-routes.d.ts +1 -1
- package/api/dev-compat-routes.d.ts +1 -1
- package/api/dev-route-catalog.d.ts.map +1 -1
- package/api/dev-route-catalog.js +20 -0
- package/api/drop-status-compat-route.d.ts +3 -0
- package/api/drop-status-compat-route.d.ts.map +1 -0
- package/api/drop-status-compat-route.js +12 -0
- package/api/first-run-routes.d.ts +1 -1
- package/api/internal-routes.d.ts +1 -1
- package/api/runtime-mode-routes.d.ts +1 -1
- package/api/secrets-manager-routes.d.ts.map +1 -1
- package/api/sensitive-request-routes.d.ts +2 -2
- package/api/server-config-filter.d.ts.map +1 -1
- package/api/server-config-filter.js +0 -1
- package/api/server-first-run-helpers.d.ts.map +1 -1
- package/api/server-first-run-helpers.js +0 -3
- package/api/server.d.ts +8 -8
- package/api/server.d.ts.map +1 -1
- package/api/server.js +13 -59
- package/api/training-benchmarks.d.ts +1 -1
- package/api/training-benchmarks.d.ts.map +1 -1
- package/api/training-benchmarks.js +31 -9
- package/api/workbench-compat-routes.d.ts +1 -1
- package/benchmark/server-utils.d.ts +2 -2
- package/browser.d.ts +4 -4
- package/cli/plugins-cli.d.ts.map +1 -1
- package/cli/plugins-cli.js +5 -0
- package/cli/program/register.auth.d.ts +27 -1
- package/cli/program/register.auth.d.ts.map +1 -1
- package/cli/program/register.auth.js +187 -0
- package/cli/program/register.dashboard.d.ts.map +1 -1
- package/cli/program/register.dashboard.js +11 -1
- package/cli/program.d.ts +1 -1
- package/first-run/first-run-config.d.ts +1 -1
- package/index.d.ts +46 -47
- package/index.d.ts.map +1 -1
- package/index.js +3 -7
- package/package.json +42 -36
- package/packaging/PUBLISHING_GUIDE.md +20 -20
- package/platform/elizaos-agent-browser-stub.d.ts +0 -1
- package/platform/elizaos-agent-browser-stub.d.ts.map +1 -1
- package/platform/elizaos-agent-browser-stub.js +0 -1
- package/platform/ios-runtime-bridge.d.ts.map +1 -1
- package/platform/ios-runtime-bridge.js +0 -1
- package/platform/native-plugin-entrypoints.d.ts +2 -2
- package/platforms/android/app/build.gradle +30 -7
- package/platforms/android/app/src/main/elizavoice-jni/elizavoice-jni.cpp +179 -35
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaAgentService.java +60 -32
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaBionicInferenceServer.java +405 -65
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaVoiceNative.java +35 -0
- package/platforms/android/app/src/main/java/ai/elizaos/app/ElizaVoicePlugin.java +13 -6
- package/platforms/android/app/src/main/res/drawable/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png +0 -0
- package/platforms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/platforms/android/app/src/main/res/values/colors.xml +5 -1
- package/platforms/android/app/src/main/res/values/ic_launcher_background.xml +1 -1
- package/platforms/android/app/src/main/res/values/styles.xml +4 -1
- package/platforms/electrobun/electrobun.config.ts +0 -1
- package/platforms/electrobun/native/macos/window-effects.mm +4 -0
- package/platforms/electrobun/package.json +11 -9
- package/platforms/electrobun/remotes/local-model/src/bun/eliza1-catalog.ts +1 -1
- package/platforms/electrobun/remotes/runtime/src/dev/phase2-smoke.ts +10 -5
- package/platforms/electrobun/remotes/runtime/src/dev/phase3-smoke.ts +11 -15
- package/platforms/electrobun/src/application-menu.ts +0 -7
- package/platforms/electrobun/src/desktop-http-request.test.ts +30 -1
- package/platforms/electrobun/src/desktop-http-request.ts +14 -2
- package/platforms/electrobun/src/desktop-test-bridge-server.ts +28 -0
- package/platforms/electrobun/src/fatal-shutdown.test.ts +3 -7
- package/platforms/electrobun/src/index.ts +64 -13
- package/platforms/electrobun/src/lifecycle/api-base-owner.test.ts +83 -0
- package/platforms/electrobun/src/lifecycle/api-base-owner.ts +37 -16
- package/platforms/electrobun/src/native/agent.ts +28 -17
- package/platforms/electrobun/src/native/credentials.ts +6 -1
- package/platforms/electrobun/src/native/desktop-window.test.ts +142 -26
- package/platforms/electrobun/src/native/desktop.ts +8 -4
- package/platforms/electrobun/src/native/index.ts +6 -0
- package/platforms/electrobun/src/native/remote-plugin-host.ts +18 -8
- package/platforms/electrobun/src/preload.js +1 -1
- package/platforms/electrobun/src/voice/voice-live-validation.test.ts +10 -5
- package/platforms/electrobun/src/voice/voice-runtime-adapter.ts +3 -2
- package/platforms/electrobun/src/voice/voice-service.test.ts +26 -0
- package/platforms/electrobun/src/voice/voice-service.ts +57 -17
- package/platforms/electrobun/tsconfig.json +17 -1
- package/platforms/ios/App/App/Base.lproj/LaunchScreen.storyboard +4 -6
- package/registry/index.d.ts +1 -6
- package/registry/index.d.ts.map +1 -1
- package/registry/index.js +9 -101
- package/runtime/android-avf-microdroid-bridge.d.ts +1 -1
- package/runtime/bundled-fused-lib.d.ts +36 -0
- package/runtime/bundled-fused-lib.d.ts.map +1 -0
- package/runtime/bundled-fused-lib.js +77 -0
- package/runtime/channel-plugin-map.d.ts +1 -5
- package/runtime/channel-plugin-map.d.ts.map +1 -1
- package/runtime/channel-plugin-map.js +11 -31
- package/runtime/desktop/index.d.ts +5 -5
- package/runtime/dev-server.js +11 -2
- package/runtime/eliza.d.ts.map +1 -1
- package/runtime/eliza.js +18 -8
- package/runtime/mode/route-mode-guard.d.ts +1 -1
- package/runtime/mode/route-mode-matrix.d.ts +1 -1
- package/runtime/voice-warmup.d.ts +4 -19
- package/runtime/voice-warmup.d.ts.map +1 -1
- package/runtime/voice-warmup.js +4 -14
- package/scripts/aosp/README.md +10 -1
- package/scripts/aosp/compile-libllama-paths.mjs +110 -0
- package/scripts/aosp/compile-libllama-zig-pin.test.mjs +147 -0
- package/scripts/aosp/compile-libllama.mjs +377 -81
- package/scripts/aosp/compile-libllama.test.mjs +133 -0
- package/scripts/aosp/compile-shim.mjs +21 -1
- package/scripts/aosp/deploy-pixel.mjs +1 -1
- package/scripts/aosp/stage-default-models.mjs +24 -31
- package/scripts/aosp/stage-default-models.test.mjs +23 -0
- package/scripts/assert-required-bundled-packages.test.ts +22 -1
- package/scripts/build-helpers/verify-fused-symbols.mjs +33 -0
- package/scripts/build-helpers/verify-fused-symbols.test.mjs +0 -0
- package/scripts/build-image.sh +1 -3
- package/scripts/build-llama-cpp-mtp.mjs +17 -16
- package/scripts/check-i18n.mjs +0 -1
- package/scripts/copy-runtime-node-modules.ts +147 -0
- package/scripts/desktop-build.mjs +317 -4
- package/scripts/docker-ci-smoke.sh +33 -8
- package/scripts/ensure-type-package-aliases.mjs +1 -1
- package/scripts/ios-xcframework/run-physical-device-smoke.mjs +1 -1
- package/scripts/lib/agent-source-watcher.mjs +0 -1
- package/scripts/lib/artifact-staleness.d.mts +33 -0
- package/scripts/lib/artifact-staleness.mjs +122 -0
- package/scripts/lib/artifact-staleness.test.mts +98 -0
- package/scripts/lib/duet-bridge.mjs +1 -1
- package/scripts/lib/ios-plist-url-scheme.mjs +80 -0
- package/scripts/lib/mobile-build-decisions.mjs +97 -0
- package/scripts/lib/renderer-build-manifest.d.mts +65 -0
- package/scripts/lib/renderer-build-manifest.mjs +257 -0
- package/scripts/lib/renderer-build-manifest.test.mts +277 -0
- package/scripts/lib/stage-android-agent.mjs +13 -4
- package/scripts/lib/verify-ondevice-artifact.d.mts +16 -0
- package/scripts/lib/verify-ondevice-artifact.mjs +61 -0
- package/scripts/lib/verify-ondevice-artifact.test.mts +98 -0
- package/scripts/lib/websocket-pending-queue.ts +92 -0
- package/scripts/link-docker-local-app-packages.mjs +6 -0
- package/scripts/playwright-ui-live-stack.ts +430 -144
- package/scripts/playwright-ui-smoke-api-stub.mjs +160 -168
- package/scripts/run-local-plugin-live-smoke.mjs +1 -6
- package/scripts/run-mobile-build-brand-separation.test.mts +33 -0
- package/scripts/run-mobile-build-ios-engine-gate.test.mjs +85 -0
- package/scripts/run-mobile-build-mtp-staleness.test.mts +198 -0
- package/scripts/run-mobile-build.mjs +408 -86
- package/scripts/serve-real-local-agent.ts +72 -0
- package/scripts/stage-desktop-fused-lib.mjs +43 -14
- package/scripts/verify-ondevice-artifact.mjs +111 -0
- package/scripts/voice-attribution-smoke.ts +144 -38
- package/scripts/voice-duet.mjs +6 -6
- package/scripts/voice-e2e-hardware.ts +22 -16
- package/scripts/voice-interactive.mjs +16 -45
- package/security/agent-vault-id.d.ts +1 -1
- package/security/platform-secure-store-node.d.ts +1 -1
- package/services/ambient-audio/consent.d.ts +1 -1
- package/services/ambient-audio/index.d.ts +6 -6
- package/services/ambient-audio/replay-buffer.d.ts +1 -1
- package/services/ambient-audio/response-gate.d.ts +1 -1
- package/services/ambient-audio/service.d.ts +3 -3
- package/services/connector-target-catalog.d.ts +1 -1
- package/services/sensitive-requests/index.d.ts +6 -6
- package/services/steward-sidecar/wallet-setup.d.ts +1 -1
- package/services/steward-sidecar.d.ts +3 -3
- package/services/tunnel-to-mobile/index.d.ts +1 -1
- package/services/vault-bootstrap.d.ts.map +1 -1
- package/services/vault-bootstrap.js +14 -10
- package/services/voice-profiles/diarization-pipeline.d.ts +1 -1
- package/services/voice-profiles/index.d.ts +11 -11
- package/services/voice-profiles/owner-confidence.d.ts +1 -1
- package/services/voice-profiles/private-challenge.d.ts +1 -1
- package/services/voice-profiles/store.d.ts +1 -1
- package/test/helpers/live-provider.test.ts +91 -1
- package/test/helpers/live-provider.ts +195 -51
- package/test/scripts/websocket-pending-queue.test.ts +119 -0
- package/test-support/test-helpers.d.ts +0 -33
- package/test-support/test-helpers.d.ts.map +1 -1
- package/test-support/test-helpers.js +81 -189
- package/ui-compat.d.ts +1 -0
- package/ui-compat.d.ts.map +1 -1
- package/ui-compat.js +4 -0
- package/platforms/electrobun/docs/convergence-audit.json +0 -3505
- package/platforms/electrobun/docs/convergence-audit.md +0 -694
- package/platforms/electrobun/scripts/ensure-whisper-gguf.sh +0 -55
- package/platforms/electrobun/scripts/ensure-whisper-model.sh +0 -35
- package/platforms/electrobun/scripts/generate-convergence-audit.ts +0 -1203
- package/platforms/electrobun/src/native/whisper-env.test.ts +0 -71
- package/platforms/electrobun/src/native/whisper-env.ts +0 -68
- package/registry/app-registry.d.ts +0 -14
- package/registry/app-registry.d.ts.map +0 -1
- package/registry/app-registry.js +0 -29
- package/registry/entries/apps/app-polymarket.json +0 -31
- package/registry/entries/apps/clawville.json +0 -27
- package/registry/entries/apps/companion.json +0 -28
- package/registry/entries/apps/database-viewer.json +0 -27
- package/registry/entries/apps/defense-of-the-agents.json +0 -27
- package/registry/entries/apps/documents.json +0 -30
- package/registry/entries/apps/feed.json +0 -27
- package/registry/entries/apps/hyperliquid.json +0 -31
- package/registry/entries/apps/log-viewer.json +0 -27
- package/registry/entries/apps/memory-viewer.json +0 -27
- package/registry/entries/apps/model-tester.json +0 -31
- package/registry/entries/apps/plugin-viewer.json +0 -27
- package/registry/entries/apps/relationship-viewer.json +0 -27
- package/registry/entries/apps/runtime-debugger.json +0 -27
- package/registry/entries/apps/shopify.json +0 -31
- package/registry/entries/apps/skills-viewer.json +0 -27
- package/registry/entries/apps/steward.json +0 -31
- package/registry/entries/apps/training.json +0 -54
- package/registry/entries/apps/trajectory-viewer.json +0 -27
- package/registry/entries/apps/vincent.json +0 -31
- package/registry/entries/connectors/bluebubbles.json +0 -99
- package/registry/entries/connectors/bluesky.json +0 -173
- package/registry/entries/connectors/discord.json +0 -119
- package/registry/entries/connectors/farcaster.json +0 -174
- package/registry/entries/connectors/feishu.json +0 -79
- package/registry/entries/connectors/google-chat.json +0 -120
- package/registry/entries/connectors/google.json +0 -82
- package/registry/entries/connectors/imessage.json +0 -96
- package/registry/entries/connectors/instagram.json +0 -64
- package/registry/entries/connectors/line.json +0 -86
- package/registry/entries/connectors/matrix.json +0 -94
- package/registry/entries/connectors/mattermost.json +0 -110
- package/registry/entries/connectors/msteams.json +0 -104
- package/registry/entries/connectors/nextcloud-talk.json +0 -104
- package/registry/entries/connectors/nostr.json +0 -70
- package/registry/entries/connectors/signal.json +0 -81
- package/registry/entries/connectors/slack.json +0 -102
- package/registry/entries/connectors/telegram.json +0 -71
- package/registry/entries/connectors/tlon.json +0 -94
- package/registry/entries/connectors/twitch.json +0 -110
- package/registry/entries/connectors/whatsapp.json +0 -113
- package/registry/entries/connectors/x.json +0 -231
- package/registry/entries/connectors/zalo.json +0 -112
- package/registry/entries/connectors/zalouser.json +0 -122
- package/registry/entries/plugins/agent-orchestrator.json +0 -33
- package/registry/entries/plugins/agent-skills.json +0 -72
- package/registry/entries/plugins/anthropic.json +0 -73
- package/registry/entries/plugins/app-control.json +0 -23
- package/registry/entries/plugins/auto-trader.json +0 -203
- package/registry/entries/plugins/background-runner.json +0 -26
- package/registry/entries/plugins/blooio.json +0 -102
- package/registry/entries/plugins/browser.json +0 -75
- package/registry/entries/plugins/cli.json +0 -40
- package/registry/entries/plugins/clipboard.json +0 -44
- package/registry/entries/plugins/coding-tools.json +0 -71
- package/registry/entries/plugins/commands.json +0 -63
- package/registry/entries/plugins/computeruse.json +0 -74
- package/registry/entries/plugins/copilot-proxy.json +0 -93
- package/registry/entries/plugins/directives.json +0 -63
- package/registry/entries/plugins/edge-tts.json +0 -97
- package/registry/entries/plugins/elevenlabs.json +0 -169
- package/registry/entries/plugins/elizacloud.json +0 -208
- package/registry/entries/plugins/evm.json +0 -134
- package/registry/entries/plugins/experience.json +0 -34
- package/registry/entries/plugins/facewear.json +0 -131
- package/registry/entries/plugins/form.json +0 -26
- package/registry/entries/plugins/github.json +0 -93
- package/registry/entries/plugins/gmail-watch.json +0 -25
- package/registry/entries/plugins/goals.json +0 -77
- package/registry/entries/plugins/google-genai.json +0 -106
- package/registry/entries/plugins/groq.json +0 -93
- package/registry/entries/plugins/hedera.json +0 -48
- package/registry/entries/plugins/inmemorydb.json +0 -25
- package/registry/entries/plugins/linear.json +0 -51
- package/registry/entries/plugins/local-inference.json +0 -137
- package/registry/entries/plugins/local-storage.json +0 -36
- package/registry/entries/plugins/localdb.json +0 -25
- package/registry/entries/plugins/mcp.json +0 -44
- package/registry/entries/plugins/memory.json +0 -124
- package/registry/entries/plugins/minecraft.json +0 -79
- package/registry/entries/plugins/moltbook.json +0 -83
- package/registry/entries/plugins/music.json +0 -155
- package/registry/entries/plugins/mysticism.json +0 -48
- package/registry/entries/plugins/nearai.json +0 -82
- package/registry/entries/plugins/ngrok.json +0 -69
- package/registry/entries/plugins/ollama.json +0 -96
- package/registry/entries/plugins/openai.json +0 -189
- package/registry/entries/plugins/openrouter.json +0 -188
- package/registry/entries/plugins/pdf.json +0 -26
- package/registry/entries/plugins/plugin-manager.json +0 -23
- package/registry/entries/plugins/prose.json +0 -48
- package/registry/entries/plugins/rlm.json +0 -26
- package/registry/entries/plugins/roblox.json +0 -88
- package/registry/entries/plugins/rss.json +0 -64
- package/registry/entries/plugins/s3-storage.json +0 -91
- package/registry/entries/plugins/scheduling.json +0 -35
- package/registry/entries/plugins/shell.json +0 -94
- package/registry/entries/plugins/social-alpha.json +0 -72
- package/registry/entries/plugins/tailscale.json +0 -81
- package/registry/entries/plugins/tee.json +0 -53
- package/registry/entries/plugins/todos.json +0 -26
- package/registry/entries/plugins/trajectory-logger.json +0 -33
- package/registry/entries/plugins/trust.json +0 -39
- package/registry/entries/plugins/tts.json +0 -71
- package/registry/entries/plugins/tunnel.json +0 -45
- package/registry/entries/plugins/twilio.json +0 -168
- package/registry/entries/plugins/vercel-ai-gateway.json +0 -128
- package/registry/entries/plugins/video.json +0 -23
- package/registry/entries/plugins/vision.json +0 -43
- package/registry/entries/plugins/webhooks.json +0 -23
- package/registry/entries/plugins/workflow.json +0 -25
- package/registry/entries/plugins/xai.json +0 -75
- package/registry/loader.d.ts +0 -28
- package/registry/loader.d.ts.map +0 -1
- package/registry/loader.js +0 -158
- package/registry/schema.d.ts +0 -1199
- package/registry/schema.d.ts.map +0 -1
- package/registry/schema.js +0 -300
- package/scripts/patches/llama-mobile-kokoro-tts.patch +0 -480
- package/scripts/release-workflow-drift.test.ts +0 -57
- package/test/helpers/i18n.ts +0 -5
- package/test/helpers/loopback.ts +0 -33
- package/test/helpers/react-test.ts +0 -40
- 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 {
|
|
240
|
-
|
|
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(
|
|
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 +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
?
|
|
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
|
-
|
|
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 &&
|
|
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(
|
|
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)
|
|
26
|
-
*
|
|
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 {
|
|
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
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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:
|
|
69
|
-
|
|
70
|
-
|
|
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 (!
|
|
74
|
-
|
|
75
|
-
`
|
|
76
|
-
"
|
|
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
|
-
|
|
87
|
-
"
|
|
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(
|
|
154
|
+
const FFI_CTX = FFI.create(BUNDLE.root);
|
|
94
155
|
if (!FusedSpeakerEncoder.isSupported(FFI)) {
|
|
95
|
-
|
|
96
|
-
`
|
|
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
|
-
|
|
102
|
-
`
|
|
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
|
|
231
|
+
// turn-segmentation stage runs through GgmlSileroVad on the same FFI context.
|
|
172
232
|
if (FUSED_VAD_AVAILABLE) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
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
|
-
|
|
432
|
-
const detector =
|
|
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
|
}
|
package/scripts/voice-duet.mjs
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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 = "
|
|
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
|
|
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 (
|
|
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)".
|