@ouro.bot/cli 0.1.0-alpha.56 → 0.1.0-alpha.561
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/README.md +127 -23
- package/RepairGuide.ouro/agent.json +5 -0
- package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
- package/RepairGuide.ouro/psyche/SOUL.md +55 -0
- package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
- package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
- package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +3604 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +837 -26
- package/dist/heart/agent-entry.js +58 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +479 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +322 -0
- package/dist/heart/config.js +114 -118
- package/dist/heart/core.js +913 -246
- package/dist/heart/cross-chat-delivery.js +3 -18
- package/dist/heart/daemon/agent-config-check.js +419 -0
- package/dist/heart/daemon/agent-discovery.js +102 -3
- package/dist/heart/daemon/agent-service.js +522 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +776 -0
- package/dist/heart/daemon/cli-exec.js +7457 -0
- package/dist/heart/daemon/cli-help.js +498 -0
- package/dist/heart/daemon/cli-parse.js +1592 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +763 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/daemon-cli.js +29 -1698
- package/dist/heart/daemon/daemon-entry.js +387 -2
- package/dist/heart/daemon/daemon-health.js +176 -0
- package/dist/heart/daemon/daemon-rollup.js +57 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +796 -71
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +826 -0
- package/dist/heart/daemon/health-monitor.js +122 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +37 -8
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/mcp-canary.js +297 -0
- package/dist/heart/daemon/message-router.js +2 -2
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +14 -12
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/process-manager.js +375 -33
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +365 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +3 -31
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +389 -38
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +158 -11
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +330 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +162 -17
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -1
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-runtime-state.js +100 -0
- package/dist/heart/habits/habit-scheduler.js +372 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +32 -56
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
- package/dist/heart/identity.js +203 -57
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mail-import-discovery.js +353 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +66 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +246 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +31 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +195 -0
- package/dist/heart/mailbox/readers/agent-machine.js +382 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +338 -0
- package/dist/heart/mailbox/readers/mail.js +362 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +651 -0
- package/dist/heart/mailbox/readers/sessions.js +232 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +683 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +267 -0
- package/dist/heart/provider-credentials.js +425 -0
- package/dist/heart/provider-failover.js +301 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-readiness-cache.js +40 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +139 -52
- package/dist/heart/providers/azure.js +97 -13
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +367 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +13 -4
- package/dist/heart/session-activity.js +43 -22
- package/dist/heart/session-events.js +1149 -0
- package/dist/heart/session-playback-cli-main.js +5 -0
- package/dist/heart/session-playback-cli.js +36 -0
- package/dist/heart/session-playback.js +231 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +182 -0
- package/dist/heart/session-transcript.js +243 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +9 -5
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-description.js +139 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +389 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +426 -0
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mailbox-ui/assets/index-B-461hes.js +61 -0
- package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/mailbox-ui/index.html +15 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +674 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +720 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +430 -0
- package/dist/mailroom/mbox-import.js +383 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +233 -0
- package/dist/mailroom/search-cache.js +256 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mailroom/source-state.js +176 -0
- package/dist/mailroom/thread.js +109 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +165 -101
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +62 -75
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +39 -0
- package/dist/mind/friends/resolver.js +54 -2
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/types.js +2 -2
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +4 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1011 -123
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/contract.js +5 -5
- package/dist/nerves/coverage/file-completeness.js +129 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/review/cli-main.js +5 -0
- package/dist/nerves/review/cli.js +156 -0
- package/dist/nerves/review/core.js +152 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +963 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +180 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +178 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +396 -0
- package/dist/repertoire/mcp-client.js +295 -0
- package/dist/repertoire/mcp-manager.js +362 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +31 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +16 -4
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -78
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +47 -1075
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +142 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +381 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-mail.js +1857 -0
- package/dist/repertoire/tools-notes.js +421 -0
- package/dist/repertoire/tools-session.js +750 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +9 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +604 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +108 -100
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +594 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/active-turns.js +216 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
- package/dist/senses/bluebubbles/entry.js +77 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +2305 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/replay.js +137 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +607 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +85 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +520 -209
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +175 -21
- package/dist/senses/inner-dialog.js +330 -27
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +549 -181
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +251 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +387 -98
- package/dist/senses/trust-gate.js +100 -5
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/elevenlabs.js +178 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +26 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +85 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +80 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +146 -0
- package/package.json +38 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +117 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +101 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -351
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -166
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/associative-recall.js +0 -209
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1177
- package/dist/senses/debug-activity.js +0 -148
- package/subagents/README.md +0 -86
- package/subagents/work-doer.md +0 -237
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -390
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -93,6 +93,91 @@ function writeInnerPendingNotice(bundleRoot, noticeContent, nowIso) {
|
|
|
93
93
|
fs.mkdirSync(innerPendingDir, { recursive: true });
|
|
94
94
|
fs.writeFileSync(filePath, JSON.stringify(payload), "utf-8");
|
|
95
95
|
}
|
|
96
|
+
const ACKNOWLEDGED_GROUPS_FILENAME = "acknowledged-auto-groups.json";
|
|
97
|
+
function acknowledgedGroupsPath(bundleRoot) {
|
|
98
|
+
return path.join(bundleRoot, "state", ACKNOWLEDGED_GROUPS_FILENAME);
|
|
99
|
+
}
|
|
100
|
+
function loadAcknowledgedGroupsState(bundleRoot) {
|
|
101
|
+
try {
|
|
102
|
+
const raw = fs.readFileSync(acknowledgedGroupsPath(bundleRoot), "utf-8");
|
|
103
|
+
if (!raw.trim())
|
|
104
|
+
return {};
|
|
105
|
+
const parsed = JSON.parse(raw);
|
|
106
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
107
|
+
return {};
|
|
108
|
+
return parsed;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function persistAcknowledgedGroupsState(bundleRoot, state) {
|
|
115
|
+
const target = acknowledgedGroupsPath(bundleRoot);
|
|
116
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
117
|
+
fs.writeFileSync(target, `${JSON.stringify(state, null, 2)}\n`, "utf-8");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* For BlueBubbles group chats that were auto-created at stranger trust (no
|
|
121
|
+
* explicit operator/agent action ever bound the harness to this group), the
|
|
122
|
+
* gate's family-member bypass would otherwise let messages flow through
|
|
123
|
+
* silently and the agent would accumulate a session it has no mental model
|
|
124
|
+
* for. Surface the relationship as an inner-pending notice exactly once so
|
|
125
|
+
* the agent can categorize / rename / dismiss the group on its next turn.
|
|
126
|
+
*
|
|
127
|
+
* Returns true if a notice was written so callers can emit a telemetry event.
|
|
128
|
+
*/
|
|
129
|
+
function maybeSurfaceAutoCreatedGroup(input, bundleRoot, nowIso) {
|
|
130
|
+
// Caller guarantees isGroupChat = true (only invoked from the family-member
|
|
131
|
+
// bypass branch); skip a redundant guard here.
|
|
132
|
+
if (input.friend.trustLevel !== "stranger")
|
|
133
|
+
return false;
|
|
134
|
+
if (!input.friend.notes?.["autoCreatedGroup"])
|
|
135
|
+
return false;
|
|
136
|
+
// loadAcknowledgedGroupsState is defensive (its own try/catch returns {})
|
|
137
|
+
// so we don't wrap it in another try here.
|
|
138
|
+
const state = loadAcknowledgedGroupsState(bundleRoot);
|
|
139
|
+
if (state[input.friend.id])
|
|
140
|
+
return false;
|
|
141
|
+
const noticeContent = `New BlueBubbles group "${input.friend.name}" became active without explicit acknowledgment. ` +
|
|
142
|
+
`It was auto-created at stranger trust the first time a message routed through it. ` +
|
|
143
|
+
`If you recognize the group, label or rename it (and consider promoting trust); if not, you can leave it as a stranger group or rename it for clarity. ` +
|
|
144
|
+
`external id: ${input.externalId}; friend id: ${input.friend.id}.`;
|
|
145
|
+
try {
|
|
146
|
+
writeInnerPendingNotice(bundleRoot, noticeContent, nowIso);
|
|
147
|
+
persistAcknowledgedGroupsState(bundleRoot, {
|
|
148
|
+
...state,
|
|
149
|
+
[input.friend.id]: { surfacedAt: nowIso },
|
|
150
|
+
});
|
|
151
|
+
(0, runtime_1.emitNervesEvent)({
|
|
152
|
+
level: "info",
|
|
153
|
+
component: "senses",
|
|
154
|
+
event: "senses.trust_gate_group_acknowledgment_surfaced",
|
|
155
|
+
message: "auto-created group surfaced for agent acknowledgment",
|
|
156
|
+
meta: {
|
|
157
|
+
friendId: input.friend.id,
|
|
158
|
+
friendName: input.friend.name,
|
|
159
|
+
externalId: input.externalId,
|
|
160
|
+
provider: input.provider,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
return true;
|
|
164
|
+
/* v8 ignore start -- defensive: surfacing failure must not block the gate decision @preserve */
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
(0, runtime_1.emitNervesEvent)({
|
|
168
|
+
level: "error",
|
|
169
|
+
component: "senses",
|
|
170
|
+
event: "senses.trust_gate_error",
|
|
171
|
+
message: "failed to surface auto-created group for acknowledgment",
|
|
172
|
+
meta: {
|
|
173
|
+
friendId: input.friend.id,
|
|
174
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
/* v8 ignore stop */
|
|
180
|
+
}
|
|
96
181
|
function enforceTrustGate(input) {
|
|
97
182
|
const { senseType } = input;
|
|
98
183
|
// Local (CLI) and internal (inner dialog) — always allow
|
|
@@ -104,6 +189,20 @@ function enforceTrustGate(input) {
|
|
|
104
189
|
return { allowed: true };
|
|
105
190
|
}
|
|
106
191
|
// Open senses (BlueBubbles/iMessage) — enforce trust rules
|
|
192
|
+
// Group chat with a family member present — allow regardless of trust level.
|
|
193
|
+
// BUT if this is an auto-created stranger group (the harness picked it up
|
|
194
|
+
// silently via the family-member shortcut and the agent never explicitly
|
|
195
|
+
// acknowledged it), surface a one-time inner-pending notice so the agent
|
|
196
|
+
// gets a chance to categorize / rename / dismiss the relationship instead
|
|
197
|
+
// of accumulating activity invisibly.
|
|
198
|
+
if (input.isGroupChat && input.groupHasFamilyMember) {
|
|
199
|
+
/* v8 ignore start -- defaults shared with the rest of the gate; tested via the stranger-trust path */
|
|
200
|
+
const bundleRoot = input.bundleRoot ?? (0, identity_1.getAgentRoot)();
|
|
201
|
+
const nowIso = (input.now ?? (() => new Date()))().toISOString();
|
|
202
|
+
/* v8 ignore stop */
|
|
203
|
+
maybeSurfaceAutoCreatedGroup(input, bundleRoot, nowIso);
|
|
204
|
+
return { allowed: true };
|
|
205
|
+
}
|
|
107
206
|
const trustLevel = input.friend.trustLevel ?? "friend";
|
|
108
207
|
// Family and friend — always allow on open
|
|
109
208
|
if ((0, types_1.isTrustedLevel)(trustLevel)) {
|
|
@@ -119,11 +218,7 @@ function enforceTrustGate(input) {
|
|
|
119
218
|
return handleStranger(input, bundleRoot, nowIso);
|
|
120
219
|
}
|
|
121
220
|
function handleAcquaintance(input, bundleRoot, nowIso) {
|
|
122
|
-
const { isGroupChat,
|
|
123
|
-
// Group chat with family member present — allow
|
|
124
|
-
if (isGroupChat && groupHasFamilyMember) {
|
|
125
|
-
return { allowed: true };
|
|
126
|
-
}
|
|
221
|
+
const { isGroupChat, hasExistingGroupWithFamily } = input;
|
|
127
222
|
let result;
|
|
128
223
|
let noticeDetail;
|
|
129
224
|
if (isGroupChat) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNodeVoiceCommandRunner = createNodeVoiceCommandRunner;
|
|
4
|
+
exports.inspectVoiceAudioRouting = inspectVoiceAudioRouting;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
7
|
+
function createNodeVoiceCommandRunner() {
|
|
8
|
+
return (command, args, options) => new Promise((resolve, reject) => {
|
|
9
|
+
const child = (0, child_process_1.spawn)(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
10
|
+
const stdout = [];
|
|
11
|
+
const stderr = [];
|
|
12
|
+
const timer = setTimeout(() => {
|
|
13
|
+
child.kill("SIGTERM");
|
|
14
|
+
reject(new Error(`command timed out after ${options.timeoutMs}ms`));
|
|
15
|
+
}, options.timeoutMs);
|
|
16
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
17
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
18
|
+
child.on("error", (error) => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
reject(error);
|
|
21
|
+
});
|
|
22
|
+
child.on("close", (exitCode) => {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
resolve({
|
|
25
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
26
|
+
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
27
|
+
exitCode: exitCode ?? 0,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function parseDeviceLines(output) {
|
|
33
|
+
return output
|
|
34
|
+
.split(/\r?\n/)
|
|
35
|
+
.map((line) => line.trim())
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
function commandFailureMessage(exitCode, result) {
|
|
39
|
+
const stderr = result.stderr?.trim();
|
|
40
|
+
if (stderr)
|
|
41
|
+
return stderr;
|
|
42
|
+
const stdout = result.stdout?.trim();
|
|
43
|
+
if (stdout)
|
|
44
|
+
return stdout;
|
|
45
|
+
return `exit ${exitCode}`;
|
|
46
|
+
}
|
|
47
|
+
function setupGuidance(missing, currentOutput, outputDeviceName) {
|
|
48
|
+
const guidance = missing.map((device) => `Install or configure the local audio device: ${device}.`);
|
|
49
|
+
if (currentOutput && currentOutput !== outputDeviceName) {
|
|
50
|
+
guidance.push(`Browser meeting audio should be routed through ${outputDeviceName}; current output is ${currentOutput}.`);
|
|
51
|
+
}
|
|
52
|
+
return guidance;
|
|
53
|
+
}
|
|
54
|
+
async function inspectVoiceAudioRouting(options = {}) {
|
|
55
|
+
const commandRunner = options.commandRunner ?? createNodeVoiceCommandRunner();
|
|
56
|
+
const switchAudioSourcePath = options.switchAudioSourcePath ?? "SwitchAudioSource";
|
|
57
|
+
const captureDeviceName = options.captureDeviceName ?? "BlackHole 2ch";
|
|
58
|
+
const outputDeviceName = options.outputDeviceName ?? "Multi-Output Device";
|
|
59
|
+
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
60
|
+
try {
|
|
61
|
+
const devicesResult = await commandRunner(switchAudioSourcePath, ["-a"], { timeoutMs });
|
|
62
|
+
if (typeof devicesResult.exitCode === "number" && devicesResult.exitCode !== 0) {
|
|
63
|
+
throw new Error(commandFailureMessage(devicesResult.exitCode, devicesResult));
|
|
64
|
+
}
|
|
65
|
+
const currentResult = await commandRunner(switchAudioSourcePath, ["-c"], { timeoutMs });
|
|
66
|
+
if (typeof currentResult.exitCode === "number" && currentResult.exitCode !== 0) {
|
|
67
|
+
throw new Error(commandFailureMessage(currentResult.exitCode, currentResult));
|
|
68
|
+
}
|
|
69
|
+
const devices = parseDeviceLines(devicesResult.stdout ?? "");
|
|
70
|
+
const currentOutput = parseDeviceLines(currentResult.stdout ?? "")[0] ?? null;
|
|
71
|
+
const hasCaptureDevice = devices.includes(captureDeviceName);
|
|
72
|
+
const hasOutputDevice = devices.includes(outputDeviceName);
|
|
73
|
+
const missing = [
|
|
74
|
+
...(hasCaptureDevice ? [] : [captureDeviceName]),
|
|
75
|
+
...(hasOutputDevice ? [] : [outputDeviceName]),
|
|
76
|
+
];
|
|
77
|
+
const result = {
|
|
78
|
+
status: missing.length === 0 ? "ready" : "needs_setup",
|
|
79
|
+
hasCaptureDevice,
|
|
80
|
+
hasOutputDevice,
|
|
81
|
+
currentOutput,
|
|
82
|
+
missing,
|
|
83
|
+
guidance: setupGuidance(missing, currentOutput, outputDeviceName),
|
|
84
|
+
};
|
|
85
|
+
(0, runtime_1.emitNervesEvent)({
|
|
86
|
+
component: "senses",
|
|
87
|
+
event: "senses.voice_audio_routing_checked",
|
|
88
|
+
message: "voice audio routing readiness checked",
|
|
89
|
+
meta: {
|
|
90
|
+
status: result.status,
|
|
91
|
+
hasCaptureDevice,
|
|
92
|
+
hasOutputDevice,
|
|
93
|
+
currentOutput,
|
|
94
|
+
missing,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
const result = {
|
|
102
|
+
status: "unknown",
|
|
103
|
+
hasCaptureDevice: false,
|
|
104
|
+
hasOutputDevice: false,
|
|
105
|
+
currentOutput: null,
|
|
106
|
+
missing: [captureDeviceName, outputDeviceName],
|
|
107
|
+
guidance: setupGuidance([captureDeviceName, outputDeviceName], null, outputDeviceName),
|
|
108
|
+
error: message,
|
|
109
|
+
};
|
|
110
|
+
(0, runtime_1.emitNervesEvent)({
|
|
111
|
+
level: "error",
|
|
112
|
+
component: "senses",
|
|
113
|
+
event: "senses.voice_audio_routing_error",
|
|
114
|
+
message: "voice audio routing readiness check failed",
|
|
115
|
+
meta: { error: message, missing: result.missing },
|
|
116
|
+
});
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_ELEVENLABS_MIME_TYPE = exports.DEFAULT_ELEVENLABS_OUTPUT_FORMAT = exports.DEFAULT_ELEVENLABS_MODEL_ID = void 0;
|
|
4
|
+
exports.createNodeElevenLabsSocketFactory = createNodeElevenLabsSocketFactory;
|
|
5
|
+
exports.createElevenLabsTtsClient = createElevenLabsTtsClient;
|
|
6
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
7
|
+
exports.DEFAULT_ELEVENLABS_MODEL_ID = "eleven_flash_v2_5";
|
|
8
|
+
exports.DEFAULT_ELEVENLABS_OUTPUT_FORMAT = "pcm_16000";
|
|
9
|
+
exports.DEFAULT_ELEVENLABS_MIME_TYPE = "audio/pcm;rate=16000";
|
|
10
|
+
function cleanTtsText(text) {
|
|
11
|
+
return text.replace(/\s+/g, " ").trim();
|
|
12
|
+
}
|
|
13
|
+
function elevenLabsStreamUrl(voiceId, modelId, outputFormat) {
|
|
14
|
+
const params = new URLSearchParams({ model_id: modelId, output_format: outputFormat });
|
|
15
|
+
return `wss://api.elevenlabs.io/v1/text-to-speech/${encodeURIComponent(voiceId)}/stream-input?${params.toString()}`;
|
|
16
|
+
}
|
|
17
|
+
function payloadText(payload) {
|
|
18
|
+
if (payload && typeof payload === "object" && "data" in payload) {
|
|
19
|
+
return payloadText(payload.data);
|
|
20
|
+
}
|
|
21
|
+
if (typeof payload === "string")
|
|
22
|
+
return payload;
|
|
23
|
+
if (Buffer.isBuffer(payload))
|
|
24
|
+
return payload.toString("utf8");
|
|
25
|
+
return String(payload ?? "");
|
|
26
|
+
}
|
|
27
|
+
function createNodeElevenLabsSocketFactory(webSocketConstructor) {
|
|
28
|
+
const WebSocketConstructor = webSocketConstructor
|
|
29
|
+
?? globalThis.WebSocket;
|
|
30
|
+
if (!WebSocketConstructor) {
|
|
31
|
+
throw new Error("global WebSocket is unavailable; inject an ElevenLabs socketFactory");
|
|
32
|
+
}
|
|
33
|
+
return (url) => {
|
|
34
|
+
const socket = new WebSocketConstructor(url);
|
|
35
|
+
const handlers = {
|
|
36
|
+
open: [],
|
|
37
|
+
message: [],
|
|
38
|
+
error: [],
|
|
39
|
+
close: [],
|
|
40
|
+
};
|
|
41
|
+
const emit = (event, payload) => {
|
|
42
|
+
for (const handler of handlers[event]) {
|
|
43
|
+
handler(payload);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const attach = (event) => {
|
|
47
|
+
const listener = (payload) => emit(event, payload);
|
|
48
|
+
if (typeof socket.addEventListener === "function") {
|
|
49
|
+
socket.addEventListener(event, listener);
|
|
50
|
+
}
|
|
51
|
+
else if (typeof socket.on === "function") {
|
|
52
|
+
socket.on(event, listener);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
socket[`on${event}`] = listener;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
attach("open");
|
|
59
|
+
attach("message");
|
|
60
|
+
attach("error");
|
|
61
|
+
attach("close");
|
|
62
|
+
return {
|
|
63
|
+
on(event, handler) {
|
|
64
|
+
handlers[event].push(handler);
|
|
65
|
+
},
|
|
66
|
+
send(payload) {
|
|
67
|
+
socket.send(payload);
|
|
68
|
+
},
|
|
69
|
+
close() {
|
|
70
|
+
socket.close();
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function createElevenLabsTtsClient(options) {
|
|
76
|
+
const modelId = options.modelId ?? exports.DEFAULT_ELEVENLABS_MODEL_ID;
|
|
77
|
+
const outputFormat = options.outputFormat ?? exports.DEFAULT_ELEVENLABS_OUTPUT_FORMAT;
|
|
78
|
+
const socketFactory = options.socketFactory ?? createNodeElevenLabsSocketFactory();
|
|
79
|
+
const mimeType = outputFormat === exports.DEFAULT_ELEVENLABS_OUTPUT_FORMAT
|
|
80
|
+
? exports.DEFAULT_ELEVENLABS_MIME_TYPE
|
|
81
|
+
: "audio/mpeg";
|
|
82
|
+
return {
|
|
83
|
+
async synthesize(request) {
|
|
84
|
+
const text = cleanTtsText(request.text);
|
|
85
|
+
if (!text) {
|
|
86
|
+
(0, runtime_1.emitNervesEvent)({
|
|
87
|
+
level: "error",
|
|
88
|
+
component: "senses",
|
|
89
|
+
event: "senses.voice_tts_error",
|
|
90
|
+
message: "voice TTS text is empty",
|
|
91
|
+
meta: { utteranceId: request.utteranceId },
|
|
92
|
+
});
|
|
93
|
+
throw new Error("voice TTS text is empty");
|
|
94
|
+
}
|
|
95
|
+
const url = elevenLabsStreamUrl(options.voiceId, modelId, outputFormat);
|
|
96
|
+
const socket = socketFactory(url);
|
|
97
|
+
const chunks = [];
|
|
98
|
+
(0, runtime_1.emitNervesEvent)({
|
|
99
|
+
component: "senses",
|
|
100
|
+
event: "senses.voice_tts_start",
|
|
101
|
+
message: "starting ElevenLabs TTS",
|
|
102
|
+
meta: { utteranceId: request.utteranceId, modelId, voiceId: options.voiceId },
|
|
103
|
+
});
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
let settled = false;
|
|
106
|
+
const fail = (error) => {
|
|
107
|
+
if (settled)
|
|
108
|
+
return;
|
|
109
|
+
settled = true;
|
|
110
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
111
|
+
(0, runtime_1.emitNervesEvent)({
|
|
112
|
+
level: "error",
|
|
113
|
+
component: "senses",
|
|
114
|
+
event: "senses.voice_tts_error",
|
|
115
|
+
message: "ElevenLabs TTS failed",
|
|
116
|
+
meta: { utteranceId: request.utteranceId, error: message },
|
|
117
|
+
});
|
|
118
|
+
reject(new Error(`ElevenLabs TTS failed: ${message}`));
|
|
119
|
+
};
|
|
120
|
+
const finish = () => {
|
|
121
|
+
if (settled)
|
|
122
|
+
return;
|
|
123
|
+
settled = true;
|
|
124
|
+
const audio = Buffer.concat(chunks);
|
|
125
|
+
(0, runtime_1.emitNervesEvent)({
|
|
126
|
+
component: "senses",
|
|
127
|
+
event: "senses.voice_tts_end",
|
|
128
|
+
message: "finished ElevenLabs TTS",
|
|
129
|
+
meta: { utteranceId: request.utteranceId, chunkCount: chunks.length, byteLength: audio.byteLength },
|
|
130
|
+
});
|
|
131
|
+
socket.close();
|
|
132
|
+
resolve({
|
|
133
|
+
utteranceId: request.utteranceId,
|
|
134
|
+
audio,
|
|
135
|
+
byteLength: audio.byteLength,
|
|
136
|
+
chunkCount: chunks.length,
|
|
137
|
+
modelId,
|
|
138
|
+
voiceId: options.voiceId,
|
|
139
|
+
mimeType,
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
socket.on("open", () => {
|
|
143
|
+
socket.send(JSON.stringify({
|
|
144
|
+
text: " ",
|
|
145
|
+
xi_api_key: options.apiKey,
|
|
146
|
+
voice_settings: {
|
|
147
|
+
stability: 0.5,
|
|
148
|
+
similarity_boost: 0.8,
|
|
149
|
+
use_speaker_boost: true,
|
|
150
|
+
},
|
|
151
|
+
}));
|
|
152
|
+
socket.send(JSON.stringify({ text, try_trigger_generation: true }));
|
|
153
|
+
socket.send(JSON.stringify({ text: "" }));
|
|
154
|
+
});
|
|
155
|
+
socket.on("message", (payload) => {
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(payloadText(payload));
|
|
158
|
+
if (typeof parsed.audio === "string" && parsed.audio.length > 0) {
|
|
159
|
+
chunks.push(Buffer.from(parsed.audio, "base64"));
|
|
160
|
+
}
|
|
161
|
+
if (parsed.isFinal === true) {
|
|
162
|
+
finish();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
fail(error);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
socket.on("error", fail);
|
|
170
|
+
socket.on("close", () => {
|
|
171
|
+
if (!settled) {
|
|
172
|
+
fail(new Error("socket closed before final audio"));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runVoiceGoldenPath = runVoiceGoldenPath;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
const audio_routing_1 = require("./audio-routing");
|
|
6
|
+
const meeting_1 = require("./meeting");
|
|
7
|
+
const playback_1 = require("./playback");
|
|
8
|
+
const turn_1 = require("./turn");
|
|
9
|
+
const defaultVoiceMeetingJoiner = {
|
|
10
|
+
async join(request) {
|
|
11
|
+
const result = {
|
|
12
|
+
status: "manual_required",
|
|
13
|
+
detail: `Open ${request.meeting.redactedUrl} in a browser profile whose meeting audio is routed through Multi-Output Device.`,
|
|
14
|
+
};
|
|
15
|
+
(0, runtime_1.emitNervesEvent)({
|
|
16
|
+
component: "senses",
|
|
17
|
+
event: "senses.voice_meeting_join_manual_required",
|
|
18
|
+
message: "voice meeting join requires browser handoff",
|
|
19
|
+
meta: {
|
|
20
|
+
provider: request.meeting.provider,
|
|
21
|
+
sessionKey: request.meeting.sessionKey,
|
|
22
|
+
routingStatus: request.audioRouting.status,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return result;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
function defaultUtteranceId() {
|
|
29
|
+
return `voice-${Date.now().toString(36)}`;
|
|
30
|
+
}
|
|
31
|
+
function playbackSkipped() {
|
|
32
|
+
(0, runtime_1.emitNervesEvent)({
|
|
33
|
+
component: "senses",
|
|
34
|
+
event: "senses.voice_golden_path_playback_skipped",
|
|
35
|
+
message: "voice golden path skipped playback because TTS failed",
|
|
36
|
+
meta: { reason: "tts_failed" },
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
status: "skipped",
|
|
40
|
+
reason: "tts_failed",
|
|
41
|
+
playbackAttempted: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function runVoiceGoldenPath(options) {
|
|
45
|
+
(0, runtime_1.emitNervesEvent)({
|
|
46
|
+
component: "senses",
|
|
47
|
+
event: "senses.voice_golden_path_start",
|
|
48
|
+
message: "starting voice golden path",
|
|
49
|
+
meta: { agentName: options.agentName, friendId: options.friendId },
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
const meeting = (0, meeting_1.parseVoiceMeetingUrl)(options.meetingUrl);
|
|
53
|
+
const audioRouting = await (options.inspectAudioRouting ?? audio_routing_1.inspectVoiceAudioRouting)();
|
|
54
|
+
const joiner = options.meetingJoiner ?? defaultVoiceMeetingJoiner;
|
|
55
|
+
const join = await joiner.join({ meeting, audioRouting });
|
|
56
|
+
const utteranceId = options.utteranceId ?? defaultUtteranceId();
|
|
57
|
+
const transcript = await options.transcriber.transcribe({
|
|
58
|
+
utteranceId,
|
|
59
|
+
audioPath: options.audioPath,
|
|
60
|
+
language: options.language,
|
|
61
|
+
});
|
|
62
|
+
const sessionKey = options.sessionKey ?? meeting.sessionKey;
|
|
63
|
+
const runSenseTurn = options.runSenseTurn;
|
|
64
|
+
const turn = await (0, turn_1.runVoiceLoopbackTurn)({
|
|
65
|
+
agentName: options.agentName,
|
|
66
|
+
friendId: options.friendId,
|
|
67
|
+
sessionKey,
|
|
68
|
+
transcript,
|
|
69
|
+
tts: options.tts,
|
|
70
|
+
runSenseTurn,
|
|
71
|
+
});
|
|
72
|
+
const writePlaybackArtifact = options.writePlaybackArtifact ?? playback_1.writeVoicePlaybackArtifact;
|
|
73
|
+
const playback = turn.tts.status === "delivered"
|
|
74
|
+
? await writePlaybackArtifact({
|
|
75
|
+
utteranceId,
|
|
76
|
+
delivery: turn.tts,
|
|
77
|
+
outputDir: options.outputDir,
|
|
78
|
+
playAudio: options.playAudio ?? false,
|
|
79
|
+
})
|
|
80
|
+
: playbackSkipped();
|
|
81
|
+
const result = {
|
|
82
|
+
meeting,
|
|
83
|
+
audioRouting,
|
|
84
|
+
join,
|
|
85
|
+
transcript,
|
|
86
|
+
responseText: turn.responseText,
|
|
87
|
+
ponderDeferred: turn.ponderDeferred,
|
|
88
|
+
tts: turn.tts,
|
|
89
|
+
playback,
|
|
90
|
+
sessionKey,
|
|
91
|
+
};
|
|
92
|
+
(0, runtime_1.emitNervesEvent)({
|
|
93
|
+
component: "senses",
|
|
94
|
+
event: "senses.voice_golden_path_end",
|
|
95
|
+
message: "finished voice golden path",
|
|
96
|
+
meta: {
|
|
97
|
+
sessionKey,
|
|
98
|
+
joinStatus: join.status,
|
|
99
|
+
ttsStatus: turn.tts.status,
|
|
100
|
+
playbackStatus: playback.status,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
107
|
+
(0, runtime_1.emitNervesEvent)({
|
|
108
|
+
level: "error",
|
|
109
|
+
component: "senses",
|
|
110
|
+
event: "senses.voice_golden_path_error",
|
|
111
|
+
message: "voice golden path failed",
|
|
112
|
+
meta: { error: message },
|
|
113
|
+
});
|
|
114
|
+
throw new Error(`voice golden path failed: ${message}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/* v8 ignore file -- barrel exports only; executable voice helpers are covered directly. @preserve */
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
__exportStar(require("./transcript"), exports);
|
|
20
|
+
__exportStar(require("./whisper"), exports);
|
|
21
|
+
__exportStar(require("./elevenlabs"), exports);
|
|
22
|
+
__exportStar(require("./turn"), exports);
|
|
23
|
+
__exportStar(require("./meeting"), exports);
|
|
24
|
+
__exportStar(require("./audio-routing"), exports);
|
|
25
|
+
__exportStar(require("./playback"), exports);
|
|
26
|
+
__exportStar(require("./golden-path"), exports);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.redactVoiceMeetingUrl = redactVoiceMeetingUrl;
|
|
4
|
+
exports.parseVoiceMeetingUrl = parseVoiceMeetingUrl;
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
7
|
+
function isRiversideHost(host) {
|
|
8
|
+
return host === "riverside.fm" || host === "riverside.com";
|
|
9
|
+
}
|
|
10
|
+
function stableMeetingHash(provider, url) {
|
|
11
|
+
return (0, crypto_1.createHash)("sha256")
|
|
12
|
+
.update(`${provider}:${url.protocol}//${url.host}${url.pathname}`)
|
|
13
|
+
.digest("hex")
|
|
14
|
+
.slice(0, 12);
|
|
15
|
+
}
|
|
16
|
+
function redactPath(pathname) {
|
|
17
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
18
|
+
if (parts.length === 0)
|
|
19
|
+
return "/";
|
|
20
|
+
if (parts.length === 1)
|
|
21
|
+
return "/:redacted";
|
|
22
|
+
return `/${parts[0]}/:redacted`;
|
|
23
|
+
}
|
|
24
|
+
function redactVoiceMeetingUrl(input) {
|
|
25
|
+
try {
|
|
26
|
+
const url = new URL(input);
|
|
27
|
+
return `${url.protocol}//${url.host}${redactPath(url.pathname)}`;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return ":invalid";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function parseVoiceMeetingUrl(input) {
|
|
34
|
+
const trimmed = input.trim();
|
|
35
|
+
if (!trimmed) {
|
|
36
|
+
(0, runtime_1.emitNervesEvent)({
|
|
37
|
+
level: "error",
|
|
38
|
+
component: "senses",
|
|
39
|
+
event: "senses.voice_meeting_rejected",
|
|
40
|
+
message: "voice meeting URL is empty",
|
|
41
|
+
meta: { reason: "empty" },
|
|
42
|
+
});
|
|
43
|
+
throw new Error("voice meeting URL is empty");
|
|
44
|
+
}
|
|
45
|
+
let url;
|
|
46
|
+
try {
|
|
47
|
+
url = new URL(trimmed);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
(0, runtime_1.emitNervesEvent)({
|
|
51
|
+
level: "error",
|
|
52
|
+
component: "senses",
|
|
53
|
+
event: "senses.voice_meeting_rejected",
|
|
54
|
+
message: "voice meeting URL is invalid",
|
|
55
|
+
meta: { reason: "invalid_url" },
|
|
56
|
+
});
|
|
57
|
+
throw new Error("voice meeting URL is invalid");
|
|
58
|
+
}
|
|
59
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
60
|
+
(0, runtime_1.emitNervesEvent)({
|
|
61
|
+
level: "error",
|
|
62
|
+
component: "senses",
|
|
63
|
+
event: "senses.voice_meeting_rejected",
|
|
64
|
+
message: "voice meeting URL must be http or https",
|
|
65
|
+
meta: { protocol: url.protocol },
|
|
66
|
+
});
|
|
67
|
+
throw new Error("voice meeting URL must be http or https");
|
|
68
|
+
}
|
|
69
|
+
const host = url.hostname.toLowerCase();
|
|
70
|
+
const provider = isRiversideHost(host) ? "riverside" : "generic";
|
|
71
|
+
if (provider === "riverside" && !url.pathname.startsWith("/studio/")) {
|
|
72
|
+
(0, runtime_1.emitNervesEvent)({
|
|
73
|
+
level: "error",
|
|
74
|
+
component: "senses",
|
|
75
|
+
event: "senses.voice_meeting_rejected",
|
|
76
|
+
message: "Riverside voice meeting URLs must use /studio/",
|
|
77
|
+
meta: { host, redactedUrl: redactVoiceMeetingUrl(trimmed) },
|
|
78
|
+
});
|
|
79
|
+
throw new Error("Riverside voice meeting URLs must use /studio/");
|
|
80
|
+
}
|
|
81
|
+
if (provider === "generic" && url.protocol !== "https:") {
|
|
82
|
+
(0, runtime_1.emitNervesEvent)({
|
|
83
|
+
level: "error",
|
|
84
|
+
component: "senses",
|
|
85
|
+
event: "senses.voice_meeting_rejected",
|
|
86
|
+
message: "generic voice meeting URLs must use https",
|
|
87
|
+
meta: { host, redactedUrl: redactVoiceMeetingUrl(trimmed) },
|
|
88
|
+
});
|
|
89
|
+
throw new Error("generic voice meeting URLs must use https");
|
|
90
|
+
}
|
|
91
|
+
const hash = stableMeetingHash(provider, url);
|
|
92
|
+
const meeting = {
|
|
93
|
+
originalUrl: trimmed,
|
|
94
|
+
provider,
|
|
95
|
+
host,
|
|
96
|
+
pathname: url.pathname,
|
|
97
|
+
redactedUrl: redactVoiceMeetingUrl(trimmed),
|
|
98
|
+
sessionKey: `voice-${provider}-${hash}`,
|
|
99
|
+
requiresBrowserJoin: true,
|
|
100
|
+
};
|
|
101
|
+
(0, runtime_1.emitNervesEvent)({
|
|
102
|
+
component: "senses",
|
|
103
|
+
event: "senses.voice_meeting_parsed",
|
|
104
|
+
message: "voice meeting URL parsed",
|
|
105
|
+
meta: {
|
|
106
|
+
provider: meeting.provider,
|
|
107
|
+
host: meeting.host,
|
|
108
|
+
sessionKey: meeting.sessionKey,
|
|
109
|
+
redactedUrl: meeting.redactedUrl,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
return meeting;
|
|
113
|
+
}
|