@ouro.bot/cli 0.1.0-alpha.92 → 0.1.0-alpha.921
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/LICENSE +21 -0
- package/README.md +154 -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/assets/bluebubbles-host +264 -0
- package/assets/sanctuary-host-launcher.sh +16 -0
- package/changelog.json +5826 -16
- package/deploy/unraid/Dockerfile +58 -0
- package/deploy/unraid/README.txt +2997 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/docker-man-template-transaction.mjs +711 -0
- package/deploy/unraid/docker-man-template-xml.cjs +105 -0
- package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
- package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
- package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
- package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
- package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
- package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-authority-installation.json +36 -0
- package/deploy/unraid/sanctuary-authority-service.sh +30 -0
- package/deploy/unraid/sanctuary-butler-upgrade.mjs +1254 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
- package/deploy/unraid/sanctuary-replay-gate.mjs +1564 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2086 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/awaiting/house-care-sweep.md +17 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +2231 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +33 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +27 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +22 -0
- package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +232 -0
- package/deploy/unraid/sanctuary.xml +30 -0
- package/dist/a2a/card.js +57 -0
- package/dist/a2a/client.js +250 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/delegated-command-grants.js +93 -0
- package/dist/a2a/delegated-command.js +197 -0
- package/dist/a2a/delegation-stores.js +56 -0
- package/dist/a2a/did-resolution.js +93 -0
- package/dist/a2a/escalation-grants.js +84 -0
- package/dist/a2a/escalation-wiring.js +60 -0
- package/dist/a2a/identity.js +258 -0
- package/dist/a2a/inbound-share.js +110 -0
- package/dist/a2a/mission-result-wire.js +196 -0
- package/dist/a2a/onboarding.js +133 -0
- package/dist/a2a/operator-trust.js +382 -0
- package/dist/a2a/outbox-client.js +57 -0
- package/dist/a2a/outbox-store.js +188 -0
- package/dist/a2a/outbox-wire.js +95 -0
- package/dist/a2a/pin-store.js +132 -0
- package/dist/a2a/replay-grant-holders.js +33 -0
- package/dist/a2a/replay-harness.js +188 -0
- package/dist/a2a/resolution-proof.js +53 -0
- package/dist/a2a/sealed-chat.js +113 -0
- package/dist/a2a/seen-ledger.js +120 -0
- package/dist/a2a/server.js +771 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/transport.js +45 -0
- package/dist/a2a/trust-commands.js +203 -0
- package/dist/a2a/trusted-files.js +185 -0
- package/dist/a2a/types.js +3 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +360 -0
- package/dist/arc/episodes.js +118 -0
- package/dist/arc/evolution.js +490 -0
- package/dist/arc/flight-recorder.js +1034 -0
- package/dist/arc/intentions.js +134 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +386 -0
- package/dist/arc/packets.js +336 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +57 -0
- package/dist/commerce/store.js +755 -0
- package/dist/commerce/types.js +3 -0
- package/dist/heart/active-work.js +798 -40
- package/dist/heart/agent-entry.js +252 -3
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +960 -0
- 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/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +18 -0
- package/dist/heart/attachments/sources/telegram.js +71 -0
- package/dist/heart/attachments/store.js +132 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +495 -0
- package/dist/heart/autonomy-budget.js +478 -0
- package/dist/heart/awaiting/a2a-await-delivery.js +109 -0
- package/dist/heart/awaiting/await-alert.js +152 -0
- package/dist/heart/awaiting/await-expiry.js +143 -0
- package/dist/heart/awaiting/await-loader.js +91 -0
- package/dist/heart/awaiting/await-parser.js +152 -0
- package/dist/heart/awaiting/await-runtime-state.js +111 -0
- package/dist/heart/awaiting/await-scheduler.js +391 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bluebubbles-host-account.js +78 -0
- package/dist/heart/bridges/manager.js +137 -17
- package/dist/heart/bridges/store.js +14 -2
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +44 -18
- package/dist/heart/config-registry.js +331 -0
- package/dist/heart/config.js +174 -131
- package/dist/heart/context-loss-gauntlet.js +387 -0
- package/dist/heart/context-loss-sentinel.js +1159 -0
- package/dist/heart/core.js +2364 -578
- package/dist/heart/cross-chat-delivery.js +9 -22
- package/dist/heart/daemon/agent-config-check.js +451 -0
- package/dist/heart/daemon/agent-discovery.js +283 -3
- package/dist/heart/daemon/agent-home.js +319 -0
- package/dist/heart/daemon/agent-service.js +542 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
- package/dist/heart/daemon/bluebubbles-host.js +273 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +180 -0
- package/dist/heart/daemon/cli-arg-redaction.js +26 -0
- package/dist/heart/daemon/cli-defaults.js +876 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +9199 -0
- package/dist/heart/daemon/cli-help.js +735 -0
- package/dist/heart/daemon/cli-parse.js +3211 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +819 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
- package/dist/heart/daemon/container-healthcheck.js +112 -0
- package/dist/heart/daemon/container-runtime.js +97 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
- package/dist/heart/daemon/container-spec-auditor.js +235 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
- package/dist/heart/daemon/daemon-cli.js +29 -2350
- package/dist/heart/daemon/daemon-entry.js +953 -11
- package/dist/heart/daemon/daemon-health.js +186 -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 +2084 -105
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +1766 -0
- package/dist/heart/daemon/freshness.js +345 -0
- package/dist/heart/daemon/habit-private-wake.js +61 -0
- package/dist/heart/daemon/health-monitor.js +154 -11
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +135 -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 +78 -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 +79 -10
- package/dist/heart/daemon/logs-prune.js +132 -0
- package/dist/heart/daemon/mcp-canary.js +524 -0
- package/dist/heart/daemon/message-router.js +65 -5
- package/dist/heart/daemon/migrate-to-desk.js +848 -0
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +22 -14
- package/dist/heart/daemon/ouro-bot-entry.js +6 -3
- package/dist/heart/daemon/ouro-entry.js +5 -2
- package/dist/heart/daemon/plugin-cli.js +432 -0
- package/dist/heart/daemon/process-manager.js +760 -61
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/prunable-bundle.js +144 -0
- package/dist/heart/daemon/pulse.js +511 -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 +47 -14
- package/dist/heart/daemon/runtime-metadata.js +2 -30
- package/dist/heart/daemon/runtime-mode.js +13 -2
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2654 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1830 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +955 -0
- package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
- package/dist/heart/daemon/sanctuary-authority-epoch.js +281 -0
- package/dist/heart/daemon/sanctuary-authority-installation.js +161 -0
- package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
- package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +1267 -0
- package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
- package/dist/heart/daemon/sanctuary-bundle-migration.js +730 -0
- package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
- package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +493 -0
- package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
- package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
- package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
- package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
- package/dist/heart/daemon/sanctuary-package-management.js +103 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +475 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-service.js +676 -0
- package/dist/heart/daemon/sense-manager.js +733 -48
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +1 -1
- package/dist/heart/daemon/socket-client.js +180 -12
- package/dist/heart/daemon/stale-bundle-prune.js +113 -0
- package/dist/heart/daemon/startup-tui.js +339 -0
- package/dist/heart/daemon/supercronic-supervisor.js +282 -0
- package/dist/heart/daemon/task-scheduler.js +117 -39
- package/dist/heart/daemon/terminal-ui.js +503 -0
- package/dist/heart/daemon/thoughts.js +138 -54
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -4
- package/dist/heart/external-events/router.js +1516 -0
- package/dist/heart/failure-reports.js +336 -0
- package/dist/heart/file-lock.js +109 -0
- package/dist/heart/frontend-approval-runtime.js +506 -0
- package/dist/heart/frontend-journal.js +215 -0
- package/dist/heart/frontend-session-service.js +237 -0
- package/dist/heart/frontend-socket-client.js +164 -0
- package/dist/heart/frontend-socket.js +400 -0
- package/dist/heart/habits/habit-cancel.js +810 -0
- package/dist/heart/habits/habit-lifecycle.js +1327 -0
- package/dist/heart/habits/habit-migration.js +194 -0
- package/dist/heart/habits/habit-parser.js +278 -0
- package/dist/heart/habits/habit-runtime-state.js +93 -0
- package/dist/heart/habits/habit-scheduler.js +653 -0
- package/dist/heart/habits/habit-session-summary.js +318 -0
- package/dist/heart/habits/habit-session.js +636 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
- 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 +53 -18
- package/dist/heart/identity.js +230 -59
- package/dist/heart/kept-notes.js +289 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +175 -0
- package/dist/heart/mail-import-discovery.js +356 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +272 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +39 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +197 -0
- package/dist/heart/mailbox/readers/agent-machine.js +428 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
- package/dist/heart/mailbox/readers/mail.js +375 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
- package/dist/heart/mailbox/readers/sessions.js +231 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +696 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +83 -16
- package/dist/heart/orientation-frame.js +345 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +158 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +221 -0
- package/dist/heart/private-runtime/policy.js +260 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- package/dist/heart/provider-attempt.js +137 -0
- package/dist/heart/provider-binding-resolver.js +273 -0
- package/dist/heart/provider-credentials.js +449 -0
- package/dist/heart/provider-failover.js +311 -0
- package/dist/heart/provider-models.js +96 -0
- package/dist/heart/provider-ping.js +265 -0
- package/dist/heart/provider-readiness-cache.js +119 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +173 -60
- package/dist/heart/providers/azure.js +23 -11
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +38 -39
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex-token.js +349 -0
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/reply-style.js +52 -0
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +607 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +24 -4
- package/dist/heart/session-activity.js +92 -24
- package/dist/heart/session-events.js +1504 -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-redaction-repair-cli-main.js +31 -0
- package/dist/heart/session-redaction-repair.js +552 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +183 -0
- package/dist/heart/session-transcript.js +133 -0
- package/dist/heart/shelf-claims.js +78 -0
- package/dist/heart/shepherd-config.js +113 -0
- package/dist/heart/source-grounding.js +297 -0
- package/dist/heart/start-of-turn-packet.js +372 -0
- package/dist/heart/steward-policy.js +656 -0
- package/dist/heart/streaming.js +609 -207
- package/dist/heart/structured-output.js +196 -0
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync-health.js +152 -0
- package/dist/heart/sync.js +726 -0
- package/dist/heart/target-resolution.js +11 -7
- 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-approval.js +382 -0
- package/dist/heart/tool-description.js +155 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +206 -0
- package/dist/heart/turn-context.js +496 -0
- package/dist/heart/turn-execution-lease.js +30 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +3 -2
- package/dist/heart/versioning/ouro-path-installer.js +432 -0
- package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
- package/dist/heart/versioning/ouro-version-manager.js +432 -0
- package/dist/heart/versioning/release-sources.js +173 -0
- package/dist/heart/versioning/self-update-defaults.js +294 -0
- package/dist/heart/versioning/self-update.js +196 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/versioning/update-state.js +126 -0
- package/dist/heart/versioning/updater-launchd.js +150 -0
- package/dist/heart/versioning/version-intent.js +114 -0
- package/dist/heart/work-card.js +380 -0
- package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +1 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +16 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +889 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/cache-sync-cli.js +58 -0
- package/dist/mailroom/core.js +803 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +568 -0
- package/dist/mailroom/hosted-cache-sync.js +384 -0
- package/dist/mailroom/mbox-import.js +393 -0
- package/dist/mailroom/migration.js +164 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +292 -0
- package/dist/mailroom/search-cache.js +513 -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 +24 -2
- package/dist/mind/context.js +307 -139
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +68 -77
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friend-link-codes.js +209 -0
- package/dist/mind/friend-merge.js +330 -0
- package/dist/mind/{associative-recall.js → note-search.js} +47 -58
- package/dist/mind/obligation-steering.js +199 -3
- package/dist/mind/pending.js +36 -10
- package/dist/mind/prompt-budget.js +479 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1085 -198
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/record-paths.js +312 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/session-transaction.js +564 -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 +139 -5
- package/dist/nerves/coverage/run-artifacts.js +55 -35
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +293 -9
- 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 +159 -0
- package/dist/nerves/runtime.js +17 -1
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1201 -0
- package/dist/repertoire/bundle-templates.js +71 -0
- package/dist/repertoire/bw-installer.js +231 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +331 -0
- package/dist/repertoire/coding/feedback.js +174 -32
- package/dist/repertoire/coding/index.js +15 -3
- package/dist/repertoire/coding/manager.js +159 -10
- package/dist/repertoire/coding/spawner.js +40 -11
- package/dist/repertoire/coding/tools.js +182 -18
- package/dist/repertoire/coding/workbench-client.js +272 -0
- package/dist/repertoire/coding/workbench-manager.js +346 -0
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +183 -0
- package/dist/repertoire/desk/classifier.js +362 -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 +968 -20
- package/dist/repertoire/house-sweep.js +469 -0
- package/dist/repertoire/mcp-client.js +58 -5
- package/dist/repertoire/mcp-manager.js +427 -139
- package/dist/repertoire/mcp-tools.js +132 -0
- package/dist/repertoire/mcp-write-guard.js +46 -0
- package/dist/repertoire/plugin-mcp.js +175 -0
- package/dist/repertoire/plugins.js +253 -0
- package/dist/repertoire/relationship-authorization.js +322 -0
- package/dist/repertoire/sanctuary-container-policy.js +46 -0
- package/dist/repertoire/shell-sessions.js +134 -0
- package/dist/repertoire/skills.js +48 -4
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tool-arguments.js +182 -0
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-a2a.js +699 -0
- package/dist/repertoire/tools-attachments.js +322 -0
- package/dist/repertoire/tools-awaiting.js +872 -0
- package/dist/repertoire/tools-base.js +83 -1168
- package/dist/repertoire/tools-bluebubbles.js +2 -0
- package/dist/repertoire/tools-bridge.js +144 -0
- package/dist/repertoire/tools-bundle.js +995 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-config.js +186 -0
- package/dist/repertoire/tools-continuity.js +575 -0
- package/dist/repertoire/tools-credential.js +383 -0
- package/dist/repertoire/tools-escalation.js +84 -0
- package/dist/repertoire/tools-evolution.js +527 -0
- package/dist/repertoire/tools-files.js +344 -0
- package/dist/repertoire/tools-flight.js +290 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-friend-link.js +57 -0
- package/dist/repertoire/tools-github.js +3 -8
- package/dist/repertoire/tools-habits.js +103 -0
- package/dist/repertoire/tools-house-care.js +174 -0
- package/dist/repertoire/tools-mail.js +1828 -0
- package/dist/repertoire/tools-notes.js +496 -0
- package/dist/repertoire/tools-obligations.js +182 -0
- package/dist/repertoire/tools-orientation.js +31 -0
- package/dist/repertoire/tools-record.js +482 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/repertoire/tools-runtime.js +238 -0
- package/dist/repertoire/tools-sanctuary-host.js +202 -0
- package/dist/repertoire/tools-session.js +1133 -0
- package/dist/repertoire/tools-shell.js +149 -0
- package/dist/repertoire/tools-shepherd.js +262 -0
- package/dist/repertoire/tools-steward-policy.js +147 -0
- package/dist/repertoire/tools-stripe.js +224 -0
- package/dist/repertoire/tools-surface.js +369 -0
- package/dist/repertoire/tools-teams.js +12 -39
- package/dist/repertoire/tools-telegram-contacts.js +37 -0
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +982 -0
- package/dist/repertoire/tools-unraid.js +556 -0
- package/dist/repertoire/tools-user-profile.js +146 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice-local.js +198 -0
- package/dist/repertoire/tools-voice.js +145 -0
- package/dist/repertoire/tools.js +514 -156
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +339 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +686 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +936 -0
- package/dist/rsvp/config.js +327 -0
- package/dist/rsvp/cutover.js +433 -0
- package/dist/rsvp/diagnostics.js +232 -0
- package/dist/rsvp/diff-renderer.js +176 -0
- package/dist/rsvp/habit-policy.js +110 -0
- package/dist/rsvp/habit-stage.js +123 -0
- package/dist/rsvp/incident-bundle.js +97 -0
- package/dist/rsvp/migration.js +204 -0
- package/dist/rsvp/native-habit-runner.js +630 -0
- package/dist/rsvp/outbound-state.js +628 -0
- package/dist/rsvp/query.js +118 -0
- package/dist/rsvp/replay.js +233 -0
- package/dist/rsvp/snapshot.js +232 -0
- package/dist/rsvp/spend-ledger.js +175 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/a2a-entry.js +132 -0
- package/dist/senses/acp-server.js +386 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +65 -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} +379 -41
- package/dist/senses/bluebubbles/context-packet.js +207 -0
- package/dist/senses/bluebubbles/context-smoke.js +144 -0
- package/dist/senses/bluebubbles/entry.js +90 -0
- package/dist/senses/bluebubbles/host-account-verifier.js +88 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
- package/dist/senses/bluebubbles/index.js +3878 -0
- package/dist/senses/bluebubbles/latest-turn.js +311 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
- package/dist/senses/bluebubbles/outbound-state.js +308 -0
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/reaction-policy.js +47 -0
- package/dist/senses/bluebubbles/replay.js +284 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/bluebubbles/webhook-registration.js +257 -0
- package/dist/senses/bluebubbles-meta-guard.js +39 -0
- 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 +241 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli.js +601 -304
- package/dist/senses/commands.js +66 -3
- package/dist/senses/context-packet-ledger.js +371 -0
- package/dist/senses/context-packets.js +154 -0
- package/dist/senses/habit-lifecycle-guard.js +24 -0
- package/dist/senses/habit-turn-message.js +160 -0
- package/dist/senses/ingress-evidence.js +2 -0
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +524 -0
- package/dist/senses/pipeline.js +1158 -204
- package/dist/senses/private-runtime-worker.js +859 -0
- package/dist/senses/private-runtime.js +1627 -0
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/root-host-approval-port.js +345 -0
- package/dist/senses/root-host-approval-runtime.js +393 -0
- package/dist/senses/sanctuary-authority-resident.js +102 -0
- package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
- package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
- package/dist/senses/sanctuary-health-runner.js +104 -0
- package/dist/senses/sanctuary-health.js +422 -0
- package/dist/senses/sanctuary-install-state-contract.js +169 -0
- package/dist/senses/sanctuary-interactive-control.js +410 -0
- package/dist/senses/sanctuary-media-catalog-contract.js +259 -0
- package/dist/senses/sanctuary-media-optimization.js +514 -0
- package/dist/senses/sanctuary-runtime.js +369 -0
- package/dist/senses/sanctuary-sab.js +85 -0
- package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
- package/dist/senses/shared-turn.js +793 -0
- package/dist/senses/shepherd/client.js +196 -0
- package/dist/senses/shepherd/cmux.js +352 -0
- package/dist/senses/shepherd/herdr.js +315 -0
- package/dist/senses/shepherd/host.js +14 -0
- package/dist/senses/shepherd/judge.js +137 -0
- package/dist/senses/shepherd/owner-notice.js +251 -0
- package/dist/senses/shepherd/redact.js +66 -0
- package/dist/senses/shepherd/returns.js +79 -0
- package/dist/senses/shepherd/sense.js +310 -0
- package/dist/senses/shepherd/terminal-process.js +99 -0
- package/dist/senses/shepherd-entry.js +78 -0
- package/dist/senses/surface-tool.js +109 -0
- package/dist/senses/teams-entry.js +68 -8
- package/dist/senses/teams.js +598 -233
- package/dist/senses/telegram-admission.js +718 -0
- package/dist/senses/telegram-approval-runtime.js +793 -0
- package/dist/senses/telegram-attachments.js +186 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-authority-transport.js +231 -0
- package/dist/senses/telegram-client.js +1749 -0
- package/dist/senses/telegram-effect-adapter.js +900 -0
- package/dist/senses/telegram-entry.js +83 -0
- package/dist/senses/telegram-handoff.js +80 -0
- package/dist/senses/telegram.js +2405 -0
- package/dist/senses/trust-gate.js +104 -9
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/call-auth.js +250 -0
- package/dist/senses/voice/elevenlabs.js +202 -0
- package/dist/senses/voice/floor-control.js +431 -0
- package/dist/senses/voice/floor-controller.js +115 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +29 -0
- package/dist/senses/voice/local-audio-control.js +344 -0
- package/dist/senses/voice/local-audio-devices.js +516 -0
- package/dist/senses/voice/local-audio-disclosure.js +174 -0
- package/dist/senses/voice/local-audio-identity.js +103 -0
- package/dist/senses/voice/local-audio-join.js +325 -0
- package/dist/senses/voice/local-audio-mute.js +199 -0
- package/dist/senses/voice/local-audio-notify.js +121 -0
- package/dist/senses/voice/local-audio-prompts.js +116 -0
- package/dist/senses/voice/local-audio-transport.js +427 -0
- package/dist/senses/voice/local-audio.js +281 -0
- package/dist/senses/voice/local-media-stream.js +145 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/outbound.js +190 -0
- package/dist/senses/voice/phone.js +33 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/process-guards.js +51 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-tool-schema.js +195 -0
- package/dist/senses/voice/realtime-trace.js +531 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +191 -0
- package/dist/senses/voice/twilio-phone-runtime.js +836 -0
- package/dist/senses/voice/twilio-phone.js +5454 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +83 -0
- package/dist/senses/voice-realtime-eval-command.js +99 -0
- package/dist/senses/voice-realtime-eval-entry.js +21 -0
- package/dist/senses/voice-twilio-entry.js +87 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +265 -0
- package/dist/util/frontmatter.js +69 -0
- package/npm-shrinkwrap.json +8100 -0
- package/package.json +74 -13
- package/skills/agent-commerce.md +113 -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 +99 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -430
- package/dist/heart/daemon/ouro-path-installer.js +0 -260
- package/dist/heart/daemon/ouro-version-manager.js +0 -171
- package/dist/heart/daemon/update-hooks.js +0 -138
- package/dist/heart/obligations.js +0 -191
- package/dist/heart/safe-workspace.js +0 -362
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/friends/channel.js +0 -84
- package/dist/mind/friends/group-context.js +0 -144
- package/dist/mind/friends/resolver.js +0 -84
- package/dist/mind/friends/store-file.js +0 -190
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/trust-explanation.js +0 -74
- package/dist/mind/friends/types.js +0 -29
- package/dist/repertoire/tasks/board.js +0 -134
- package/dist/repertoire/tasks/index.js +0 -224
- package/dist/repertoire/tasks/lifecycle.js +0 -80
- package/dist/repertoire/tasks/middleware.js +0 -65
- package/dist/repertoire/tasks/parser.js +0 -173
- package/dist/repertoire/tasks/scanner.js +0 -132
- package/dist/repertoire/tasks/transitions.js +0 -144
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1181
- package/dist/senses/debug-activity.js +0 -154
- package/dist/senses/inner-dialog-worker.js +0 -90
- package/dist/senses/inner-dialog.js +0 -525
- package/subagents/README.md +0 -7
- /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/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +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
|
@@ -0,0 +1,516 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LocalAudioDevices = exports.KILL_GRACE_MS = exports.defaultProcessHost = exports.PS_ENV = exports.PLAYBACK_DEVICE_NAME = exports.CAPTURE_DEVICE_NAME = void 0;
|
|
37
|
+
exports.soxCaptureArgs = soxCaptureArgs;
|
|
38
|
+
exports.ffmpegPlaybackArgs = ffmpegPlaybackArgs;
|
|
39
|
+
exports.audioToolboxListArgs = audioToolboxListArgs;
|
|
40
|
+
exports.findAudioToolboxDeviceIndex = findAudioToolboxDeviceIndex;
|
|
41
|
+
exports.defaultListAudioToolboxDevices = defaultListAudioToolboxDevices;
|
|
42
|
+
exports.fileCaptureArgs = fileCaptureArgs;
|
|
43
|
+
exports.filePlaybackArgs = filePlaybackArgs;
|
|
44
|
+
exports.defaultKillGroup = defaultKillGroup;
|
|
45
|
+
exports.defaultInspectProcess = defaultInspectProcess;
|
|
46
|
+
exports.defaultInspectProcessAsync = defaultInspectProcessAsync;
|
|
47
|
+
exports.checkLocalAudioRouting = checkLocalAudioRouting;
|
|
48
|
+
exports.soxProbeToneArgs = soxProbeToneArgs;
|
|
49
|
+
exports.probeCaptureLoopback = probeCaptureLoopback;
|
|
50
|
+
exports.capturedNothingReason = capturedNothingReason;
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
55
|
+
const local_audio_1 = require("./local-audio");
|
|
56
|
+
const local_audio_mute_1 = require("./local-audio-mute");
|
|
57
|
+
const RAW_MULAW = ["-t", "raw", "-r", "8000", "-e", "mu-law", "-b", "8", "-c", "1"];
|
|
58
|
+
const AUDIO_PROCESS_NAMES = new Set(["sox", "ffmpeg"]);
|
|
59
|
+
exports.CAPTURE_DEVICE_NAME = "BlackHole 16ch";
|
|
60
|
+
exports.PLAYBACK_DEVICE_NAME = "BlackHole 2ch";
|
|
61
|
+
/** Capture a CoreAudio device as mono 8 kHz mu-law on stdout (channels 1 and 2 mixed). */
|
|
62
|
+
function soxCaptureArgs(device) {
|
|
63
|
+
// BlackHole 16ch spreads a stereo call over many channels; `remix 1,2` mixes the first two to mono.
|
|
64
|
+
// `--buffer` sizes sox's writes to stdout (1024 bytes is 128 ms of 8 kHz mu-law), while the larger
|
|
65
|
+
// `--input-buffer` keeps the CoreAudio reads big enough to avoid dropped audio. A single
|
|
66
|
+
// `--buffer 8192` delayed everything the agent heard by about a second.
|
|
67
|
+
return ["-q", "--buffer", "1024", "--input-buffer", "8192", "-t", "coreaudio", device, ...RAW_MULAW, "-", "remix", "1,2"];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Play raw mono 8 kHz mu-law from stdin to an AudioToolbox output device, chosen by index.
|
|
71
|
+
*
|
|
72
|
+
* sox's CoreAudio output can deadlock when the process runs at lowered priority (a zsh background
|
|
73
|
+
* job is niced by default, and so is a launchd Background job): its main thread holds a lock inside
|
|
74
|
+
* `AudioDeviceStart` while the device IO thread waits for that lock, so nothing is ever played.
|
|
75
|
+
* Observed live: 5 of 5 niced joins were silent with sox and 2 of 2 spoke with ffmpeg. The probe
|
|
76
|
+
* flags keep ffmpeg from buffering input before it starts playing.
|
|
77
|
+
*/
|
|
78
|
+
function ffmpegPlaybackArgs(deviceIndex) {
|
|
79
|
+
return [
|
|
80
|
+
"-hide_banner", "-loglevel", "error", "-nostdin",
|
|
81
|
+
"-probesize", "32", "-analyzeduration", "0", "-fflags", "nobuffer",
|
|
82
|
+
"-f", "mulaw", "-ar", "8000", "-ac", "1", "-i", "pipe:0",
|
|
83
|
+
"-f", "audiotoolbox", "-audio_device_index", String(deviceIndex), "-",
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
/** ffmpeg prints the AudioToolbox devices to its log; `-t 0` means nothing is played. */
|
|
87
|
+
function audioToolboxListArgs() {
|
|
88
|
+
return ["-hide_banner", "-f", "lavfi", "-i", "anullsrc", "-t", "0", "-f", "audiotoolbox", "-list_devices", "true", "-"];
|
|
89
|
+
}
|
|
90
|
+
// "[AudioToolbox @ 0x7b514003c0] [1] BlackHole 2ch, BlackHole2ch_UID"
|
|
91
|
+
const AUDIOTOOLBOX_DEVICE_LINE = /\[(\d+)\]\s+(.+),\s*[^,]*$/;
|
|
92
|
+
/** The index ffmpeg uses for a device with exactly this name, or undefined when it is not listed. */
|
|
93
|
+
function findAudioToolboxDeviceIndex(listing, deviceName) {
|
|
94
|
+
for (const line of listing.split("\n")) {
|
|
95
|
+
const match = AUDIOTOOLBOX_DEVICE_LINE.exec(line.trim());
|
|
96
|
+
if (match && match[2].trim() === deviceName)
|
|
97
|
+
return Number(match[1]);
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
/** Runs ffmpeg's device listing and returns its log. Rejects only when ffmpeg itself cannot run. */
|
|
102
|
+
function defaultListAudioToolboxDevices(ffmpegPath = "ffmpeg", run = (file, args, callback) => {
|
|
103
|
+
(0, child_process_1.execFile)(file, args, { encoding: "utf8", timeout: 5000 }, (error, stdout, stderr) => callback(error, stdout, stderr));
|
|
104
|
+
}) {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
run(ffmpegPath, audioToolboxListArgs(), (error, _stdout, stderr) => {
|
|
107
|
+
if (error && typeof error.code !== "number")
|
|
108
|
+
reject(error);
|
|
109
|
+
else
|
|
110
|
+
resolve(stderr);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Decode any audio file to the capture stream format (stdout). The caller paces it. */
|
|
115
|
+
function fileCaptureArgs(inputPath) {
|
|
116
|
+
return ["-q", inputPath, ...RAW_MULAW, "-"];
|
|
117
|
+
}
|
|
118
|
+
/** Write raw mono 8 kHz mu-law from stdin to a WAV file. */
|
|
119
|
+
function filePlaybackArgs(outputPath) {
|
|
120
|
+
return ["-q", ...RAW_MULAW, "-", "-t", "wav", outputPath];
|
|
121
|
+
}
|
|
122
|
+
/** Signals a whole process group. Returns false when the group is already gone. */
|
|
123
|
+
function defaultKillGroup(pid, signal) {
|
|
124
|
+
try {
|
|
125
|
+
process.kill(-pid, signal);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// `ps -o lstart=` prints "Fri Oct 9 11:59:46 2026": weekday, month, day, time, year. The words are not
|
|
133
|
+
// matched by name, so a locale that spells them differently still parses (and LC_ALL=C pins the format).
|
|
134
|
+
const PS_LINE = /^(\S+\s+\S+\s+\d+\s+[\d:]{8}\s+\d{4})\s+(.+)$/;
|
|
135
|
+
exports.PS_ENV = { ...process.env, LC_ALL: "C" };
|
|
136
|
+
const PS_ARGS = (pid) => ["-p", String(pid), "-o", "lstart=", "-o", "comm="];
|
|
137
|
+
function parsePsLine(out) {
|
|
138
|
+
const match = PS_LINE.exec(out.trim());
|
|
139
|
+
return match ? { startTime: match[1], command: match[2] } : null;
|
|
140
|
+
}
|
|
141
|
+
/** Command and start time of a live process, or null when it is gone. Used to avoid killing a reused pid. */
|
|
142
|
+
function defaultInspectProcess(pid, runPs = (args) => (0, child_process_1.execFileSync)("ps", args, { encoding: "utf8", timeout: 2000, env: exports.PS_ENV })) {
|
|
143
|
+
try {
|
|
144
|
+
return parsePsLine(runPs(PS_ARGS(pid)));
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** The same lookup without blocking the event loop (audio is flowing while this runs). */
|
|
151
|
+
function defaultInspectProcessAsync(pid, runPs = (args) => new Promise((resolve, reject) => {
|
|
152
|
+
(0, child_process_1.execFile)("ps", args, { encoding: "utf8", timeout: 2000, env: exports.PS_ENV }, (error, stdout) => (error ? reject(error) : resolve(stdout)));
|
|
153
|
+
})) {
|
|
154
|
+
return runPs(PS_ARGS(pid)).then(parsePsLine, () => null);
|
|
155
|
+
}
|
|
156
|
+
exports.defaultProcessHost = {
|
|
157
|
+
get pid() { return process.pid; },
|
|
158
|
+
once: (event, listener) => process.once(event, listener),
|
|
159
|
+
off: (event, listener) => process.off(event, listener),
|
|
160
|
+
kill: (pid, signal) => process.kill(pid, signal),
|
|
161
|
+
listenerCount: (event) => process.listenerCount(event),
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* sox does not act on SIGTERM while it is blocked in a CoreAudio call or in a stdin read (observed
|
|
165
|
+
* live: a tone process stayed alive for minutes). After this grace, anything left gets SIGKILL.
|
|
166
|
+
*/
|
|
167
|
+
exports.KILL_GRACE_MS = 500;
|
|
168
|
+
/**
|
|
169
|
+
* Owns the capture and playback sox processes: each runs detached in its own process group,
|
|
170
|
+
* pids are recorded in a pid file so a later start can sweep leftovers from a crashed run.
|
|
171
|
+
*/
|
|
172
|
+
class LocalAudioDevices {
|
|
173
|
+
options;
|
|
174
|
+
running = [];
|
|
175
|
+
killGroup;
|
|
176
|
+
inspectProcess;
|
|
177
|
+
inspectProcessAsync;
|
|
178
|
+
host;
|
|
179
|
+
setTimer;
|
|
180
|
+
signalCleanup = null;
|
|
181
|
+
constructor(options) {
|
|
182
|
+
this.options = options;
|
|
183
|
+
this.killGroup = options.killGroup ?? defaultKillGroup;
|
|
184
|
+
this.setTimer = options.setTimer ?? ((cb, ms) => setTimeout(cb, ms));
|
|
185
|
+
this.inspectProcess = options.inspectProcess ?? defaultInspectProcess;
|
|
186
|
+
this.inspectProcessAsync = options.inspectProcessAsync ?? defaultInspectProcessAsync;
|
|
187
|
+
this.host = options.processHost ?? exports.defaultProcessHost;
|
|
188
|
+
}
|
|
189
|
+
start() {
|
|
190
|
+
this.stop();
|
|
191
|
+
const command = this.options.soxPath ?? "sox";
|
|
192
|
+
const capture = this.spawnTracked(command, this.options.captureArgs);
|
|
193
|
+
const playback = this.spawnTracked(this.options.playbackCommand ?? command, this.options.playbackArgs);
|
|
194
|
+
this.writePidFile();
|
|
195
|
+
this.recordStartTimes([...this.running]);
|
|
196
|
+
this.watchParentSignals();
|
|
197
|
+
if (!capture.stdout || !playback.stdin) {
|
|
198
|
+
this.stop();
|
|
199
|
+
throw new Error("sox did not provide capture stdout / playback stdin stdio");
|
|
200
|
+
}
|
|
201
|
+
(0, runtime_1.emitNervesEvent)({
|
|
202
|
+
component: "senses",
|
|
203
|
+
event: "senses.voice_local_audio_devices_started",
|
|
204
|
+
message: "local audio sox processes started",
|
|
205
|
+
meta: { capturePid: capture.pid ?? null, playbackPid: playback.pid ?? null },
|
|
206
|
+
});
|
|
207
|
+
return { capture: capture.stdout, playback: playback.stdin };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* `force` is for paths where no timer will get to run (the parent is exiting or being signalled):
|
|
211
|
+
* SIGKILL straight away. Otherwise SIGTERM now and SIGKILL after a short grace to whatever is
|
|
212
|
+
* still alive.
|
|
213
|
+
*/
|
|
214
|
+
stop(force = false) {
|
|
215
|
+
this.signalCleanup?.();
|
|
216
|
+
const stopping = this.running;
|
|
217
|
+
this.running = [];
|
|
218
|
+
const send = (entry, signal) => {
|
|
219
|
+
if (entry.pid === undefined || !this.killGroup(entry.pid, signal))
|
|
220
|
+
entry.child.kill(signal);
|
|
221
|
+
};
|
|
222
|
+
for (const entry of stopping)
|
|
223
|
+
send(entry, force ? "SIGKILL" : "SIGTERM");
|
|
224
|
+
if (!force && stopping.length > 0) {
|
|
225
|
+
this.setTimer(() => {
|
|
226
|
+
for (const entry of stopping) {
|
|
227
|
+
// A child that exited cleared its pid (the pid may be reused by now): leave it alone.
|
|
228
|
+
if (entry.pid !== undefined)
|
|
229
|
+
send(entry, "SIGKILL");
|
|
230
|
+
}
|
|
231
|
+
}, exports.KILL_GRACE_MS);
|
|
232
|
+
}
|
|
233
|
+
if (stopping.length > 0) {
|
|
234
|
+
fs.rmSync(this.options.pidFile, { force: true });
|
|
235
|
+
(0, runtime_1.emitNervesEvent)({
|
|
236
|
+
component: "senses",
|
|
237
|
+
event: "senses.voice_local_audio_devices_stopped",
|
|
238
|
+
message: "local audio sox processes stopped",
|
|
239
|
+
meta: { processes: stopping.length },
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Graceful end for file output: close the playback stream and let sox finalize the WAV header,
|
|
245
|
+
* then stop everything. Gives up waiting after `timeoutMs`.
|
|
246
|
+
*/
|
|
247
|
+
async finish(timeoutMs = 3000) {
|
|
248
|
+
const playback = this.running[1];
|
|
249
|
+
if (playback && playback.pid !== undefined) {
|
|
250
|
+
await new Promise((resolve) => {
|
|
251
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
252
|
+
playback.child.on("exit", () => {
|
|
253
|
+
clearTimeout(timer);
|
|
254
|
+
resolve();
|
|
255
|
+
});
|
|
256
|
+
playback.child.stdin?.end();
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
this.stop();
|
|
260
|
+
}
|
|
261
|
+
/** Kills sox groups left behind in the pid file by a previous run. Returns how many were killed. */
|
|
262
|
+
sweepStale() {
|
|
263
|
+
let text;
|
|
264
|
+
try {
|
|
265
|
+
text = fs.readFileSync(this.options.pidFile, "utf8");
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
let killed = 0;
|
|
271
|
+
const terminated = [];
|
|
272
|
+
for (const line of text.split("\n")) {
|
|
273
|
+
const [pidText = "", recordedStart = ""] = line.split("\t");
|
|
274
|
+
const pid = Number.parseInt(pidText.trim(), 10);
|
|
275
|
+
if (!Number.isInteger(pid) || pid <= 1)
|
|
276
|
+
continue;
|
|
277
|
+
if (!this.isRecordedSox(pid, recordedStart))
|
|
278
|
+
continue;
|
|
279
|
+
if (this.killGroup(pid, "SIGTERM")) {
|
|
280
|
+
killed++;
|
|
281
|
+
terminated.push({ pid, recordedStart });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (terminated.length > 0) {
|
|
285
|
+
// sox can ignore SIGTERM while blocked in CoreAudio or a stdin read: whatever is still the same sox gets SIGKILL.
|
|
286
|
+
this.setTimer(() => {
|
|
287
|
+
for (const { pid, recordedStart } of terminated) {
|
|
288
|
+
if (this.isRecordedSox(pid, recordedStart))
|
|
289
|
+
this.killGroup(pid, "SIGKILL");
|
|
290
|
+
}
|
|
291
|
+
}, exports.KILL_GRACE_MS);
|
|
292
|
+
}
|
|
293
|
+
fs.rmSync(this.options.pidFile, { force: true });
|
|
294
|
+
(0, runtime_1.emitNervesEvent)({
|
|
295
|
+
component: "senses",
|
|
296
|
+
event: "senses.voice_local_audio_stale_swept",
|
|
297
|
+
message: "stale local audio sox processes swept",
|
|
298
|
+
meta: { killed },
|
|
299
|
+
});
|
|
300
|
+
return killed;
|
|
301
|
+
}
|
|
302
|
+
/** Only a process that is still one of our audio tools AND started when we recorded it is ours to kill (pids get reused). */
|
|
303
|
+
isRecordedSox(pid, recordedStart) {
|
|
304
|
+
const info = this.inspectProcess(pid);
|
|
305
|
+
if (!info || !AUDIO_PROCESS_NAMES.has(path.basename(info.command)))
|
|
306
|
+
return false;
|
|
307
|
+
return recordedStart !== "" && info.startTime === recordedStart;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* sox lives in its own process group, so a Ctrl-C or kill aimed at this process would not reach
|
|
311
|
+
* it. Kill the groups on SIGINT, SIGTERM and exit; for signals, re-raise afterwards so the
|
|
312
|
+
* process still terminates the way the sender intended.
|
|
313
|
+
*/
|
|
314
|
+
watchParentSignals() {
|
|
315
|
+
const handlers = [
|
|
316
|
+
["SIGINT", () => this.onSignal("SIGINT")],
|
|
317
|
+
["SIGTERM", () => this.onSignal("SIGTERM")],
|
|
318
|
+
["exit", () => this.stop(true)],
|
|
319
|
+
];
|
|
320
|
+
for (const [event, listener] of handlers)
|
|
321
|
+
this.host.once(event, listener);
|
|
322
|
+
this.signalCleanup = () => {
|
|
323
|
+
this.signalCleanup = null;
|
|
324
|
+
for (const [event, listener] of handlers)
|
|
325
|
+
this.host.off(event, listener);
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
onSignal(signal) {
|
|
329
|
+
(0, runtime_1.emitNervesEvent)({
|
|
330
|
+
level: "warn",
|
|
331
|
+
component: "senses",
|
|
332
|
+
event: "senses.voice_local_audio_parent_signal",
|
|
333
|
+
message: "local audio parent received a stop signal; killing sox",
|
|
334
|
+
meta: { signal },
|
|
335
|
+
});
|
|
336
|
+
if (this.options.gracefulPlayback) {
|
|
337
|
+
this.winddownFileWriter();
|
|
338
|
+
this.setTimer(() => {
|
|
339
|
+
this.stop(true);
|
|
340
|
+
this.reraise(signal);
|
|
341
|
+
}, exports.KILL_GRACE_MS);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
this.stop(true);
|
|
345
|
+
this.reraise(signal);
|
|
346
|
+
}
|
|
347
|
+
/** The WAV writer finalizes its header on EOF or SIGTERM; everything else is not worth waiting for. */
|
|
348
|
+
winddownFileWriter() {
|
|
349
|
+
this.running.forEach((entry, index) => {
|
|
350
|
+
if (index === 0) {
|
|
351
|
+
this.sendSignal(entry, "SIGKILL");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
entry.child.stdin?.end();
|
|
355
|
+
this.sendSignal(entry, "SIGTERM");
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
sendSignal(entry, signal) {
|
|
359
|
+
if (entry.pid === undefined || !this.killGroup(entry.pid, signal))
|
|
360
|
+
entry.child.kill(signal);
|
|
361
|
+
}
|
|
362
|
+
/** Re-raise so the process still ends the way the sender intended, unless another listener owns that. */
|
|
363
|
+
reraise(signal) {
|
|
364
|
+
if (this.host.listenerCount(signal) === 0)
|
|
365
|
+
this.host.kill(this.host.pid, signal);
|
|
366
|
+
}
|
|
367
|
+
spawnTracked(command, args) {
|
|
368
|
+
const child = this.options.spawner.spawn(command, args, { detached: true });
|
|
369
|
+
const entry = { child, pid: child.pid };
|
|
370
|
+
this.running.push(entry);
|
|
371
|
+
child.on("exit", () => {
|
|
372
|
+
entry.pid = undefined;
|
|
373
|
+
});
|
|
374
|
+
return Object.assign(child, { pid: child.pid });
|
|
375
|
+
}
|
|
376
|
+
writePidFile() {
|
|
377
|
+
const lines = [];
|
|
378
|
+
for (const entry of this.running) {
|
|
379
|
+
if (entry.pid === undefined)
|
|
380
|
+
continue;
|
|
381
|
+
lines.push(`${entry.pid}\t`);
|
|
382
|
+
}
|
|
383
|
+
fs.mkdirSync(path.dirname(this.options.pidFile), { recursive: true });
|
|
384
|
+
fs.writeFileSync(this.options.pidFile, `${lines.join("\n")}\n`);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* The pid file first lists bare pids (a later sweep will not kill a pid without a start time), then
|
|
388
|
+
* gains start times once `ps` answers, so the event loop never waits on `ps` while audio flows.
|
|
389
|
+
*/
|
|
390
|
+
recordStartTimes(entries) {
|
|
391
|
+
const pids = entries.map((entry) => entry.pid);
|
|
392
|
+
void Promise.all(pids.map((pid) => (pid === undefined ? Promise.resolve(null) : this.inspectProcessAsync(pid)))).then((infos) => {
|
|
393
|
+
if (entries.some((entry) => !this.running.includes(entry)))
|
|
394
|
+
return;
|
|
395
|
+
const lines = [];
|
|
396
|
+
entries.forEach((entry, index) => {
|
|
397
|
+
if (entry.pid === pids[index] && entry.pid !== undefined)
|
|
398
|
+
lines.push(`${entry.pid}\t${infos[index]?.startTime ?? ""}`);
|
|
399
|
+
});
|
|
400
|
+
try {
|
|
401
|
+
fs.writeFileSync(this.options.pidFile, `${lines.join("\n")}\n`);
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// The state directory is gone (the run ended): nothing to record.
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
exports.LocalAudioDevices = LocalAudioDevices;
|
|
410
|
+
async function checkLocalAudioRouting(inspect, queryMute) {
|
|
411
|
+
const inspection = await inspect({ captureDeviceName: exports.CAPTURE_DEVICE_NAME, outputDeviceName: exports.PLAYBACK_DEVICE_NAME });
|
|
412
|
+
const steps = [];
|
|
413
|
+
const muteSteps = inspection.missing.length === 0 && queryMute
|
|
414
|
+
? (0, local_audio_mute_1.muteFixSteps)((await queryMute([exports.CAPTURE_DEVICE_NAME, exports.PLAYBACK_DEVICE_NAME])) ?? [])
|
|
415
|
+
: [];
|
|
416
|
+
steps.push(...muteSteps);
|
|
417
|
+
if (inspection.status === "unknown") {
|
|
418
|
+
// The inspection itself failed, so nothing is known about the devices: do not tell the owner to install them.
|
|
419
|
+
const error = inspection.error ?? "unknown error";
|
|
420
|
+
steps.push(/ENOENT|not found/i.test(error)
|
|
421
|
+
? "The device check needs SwitchAudioSource: brew install switchaudio-osx"
|
|
422
|
+
: `Audio device inspection failed: ${error}`);
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
const missing = inspection.missing.map((name) => name.toLowerCase().replace(/\s+/g, "-"));
|
|
426
|
+
if (missing.length > 0) {
|
|
427
|
+
steps.push(`Install the missing audio devices: brew install --cask ${missing.join(" ")} (then restart macOS audio or reboot).`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
steps.push(`In the call app, set the microphone to "${exports.PLAYBACK_DEVICE_NAME}" (the agent speaks into it).`, `In the call app, set the speaker output to "${exports.CAPTURE_DEVICE_NAME}" (the agent listens to it).`, `To hear the call yourself, create a Multi-Output Device in Audio MIDI Setup that includes "${exports.CAPTURE_DEVICE_NAME}" and your speakers, and use it as the call app's speaker output.`);
|
|
431
|
+
return { ok: inspection.status === "ready" && muteSteps.length === 0, steps };
|
|
432
|
+
}
|
|
433
|
+
/** Plays a quiet tone into a CoreAudio device with a separate sox process (stopped as soon as it is heard). */
|
|
434
|
+
function soxProbeToneArgs(device) {
|
|
435
|
+
return ["-q", "-n", "-t", "coreaudio", device, "synth", "3", "sine", "1000", "vol", "0.2"];
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* A quiet room is not a permission failure: BlackHole returns zeros whenever the call app plays
|
|
439
|
+
* nothing. So before the join we play a short probe tone into the capture device and require a
|
|
440
|
+
* capture to hear it. The probe uses its own capture and tone processes, so it never touches the
|
|
441
|
+
* live capture stream: the first seconds of real audio are never consumed or discarded, and the
|
|
442
|
+
* tone never reaches the agent. The tone starts only after the probe capture has delivered its
|
|
443
|
+
* first chunk (proof that it is open and listening), so a slow CoreAudio open cannot miss the tone.
|
|
444
|
+
* Only a missing probe fails the join: either macOS microphone permission is missing for this
|
|
445
|
+
* process, or the device is muted.
|
|
446
|
+
*/
|
|
447
|
+
function probeCaptureLoopback(options) {
|
|
448
|
+
const timeoutMs = options.timeoutMs ?? 6000;
|
|
449
|
+
const openTimeoutMs = options.openTimeoutMs ?? 60_000;
|
|
450
|
+
const device = options.device ?? exports.CAPTURE_DEVICE_NAME;
|
|
451
|
+
const command = options.soxPath ?? "sox";
|
|
452
|
+
const setTimer = options.setTimer ?? ((cb, ms) => setTimeout(cb, ms));
|
|
453
|
+
const clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
454
|
+
const killGroup = options.killGroup ?? defaultKillGroup;
|
|
455
|
+
if (options.signal?.aborted)
|
|
456
|
+
return Promise.resolve({ ok: false, reason: "cancelled" });
|
|
457
|
+
const listener = options.spawner.spawn(command, soxCaptureArgs(device), { detached: true });
|
|
458
|
+
let tone;
|
|
459
|
+
return new Promise((resolve) => {
|
|
460
|
+
let timer;
|
|
461
|
+
let done = false;
|
|
462
|
+
const kill = (child) => {
|
|
463
|
+
const send = (signal) => {
|
|
464
|
+
if (child.pid === undefined || !killGroup(child.pid, signal))
|
|
465
|
+
child.kill(signal);
|
|
466
|
+
};
|
|
467
|
+
let exited = false;
|
|
468
|
+
child.on("exit", () => { exited = true; });
|
|
469
|
+
send("SIGTERM");
|
|
470
|
+
setTimer(() => { if (!exited)
|
|
471
|
+
send("SIGKILL"); }, exports.KILL_GRACE_MS);
|
|
472
|
+
};
|
|
473
|
+
const finish = (result) => {
|
|
474
|
+
if (done)
|
|
475
|
+
return;
|
|
476
|
+
done = true;
|
|
477
|
+
listener.stdout?.off("data", onData);
|
|
478
|
+
clearTimer(timer);
|
|
479
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
480
|
+
kill(listener);
|
|
481
|
+
if (tone)
|
|
482
|
+
kill(tone);
|
|
483
|
+
(0, runtime_1.emitNervesEvent)({
|
|
484
|
+
level: result.ok ? "info" : "error",
|
|
485
|
+
component: "senses",
|
|
486
|
+
event: result.ok ? "senses.voice_local_capture_probe_heard" : "senses.voice_local_capture_probe_missing",
|
|
487
|
+
message: result.ok ? "local capture heard the probe tone" : "local capture did not hear the probe tone",
|
|
488
|
+
meta: { timeoutMs, reason: result.reason ?? null },
|
|
489
|
+
});
|
|
490
|
+
resolve(result);
|
|
491
|
+
};
|
|
492
|
+
const onAbort = () => finish({ ok: false, reason: "cancelled" });
|
|
493
|
+
const onData = (chunk) => {
|
|
494
|
+
if (!tone) {
|
|
495
|
+
// The capture is open and delivering: now it is safe to play the tone.
|
|
496
|
+
clearTimer(timer);
|
|
497
|
+
tone = options.spawner.spawn(command, soxProbeToneArgs(device), { detached: true });
|
|
498
|
+
timer = setTimer(() => {
|
|
499
|
+
finish({ ok: false, reason: capturedNothingReason(device, `capture heard nothing from the probe tone within ${timeoutMs} ms`) });
|
|
500
|
+
}, timeoutMs);
|
|
501
|
+
}
|
|
502
|
+
if (!(0, local_audio_1.isDigitalSilence)([chunk]))
|
|
503
|
+
finish({ ok: true });
|
|
504
|
+
};
|
|
505
|
+
timer = setTimer(() => {
|
|
506
|
+
finish({ ok: false, reason: capturedNothingReason(device, `the probe capture delivered no audio at all within ${openTimeoutMs} ms`) });
|
|
507
|
+
}, openTimeoutMs);
|
|
508
|
+
options.signal?.addEventListener("abort", onAbort);
|
|
509
|
+
listener.on("exit", () => finish({ ok: false, reason: capturedNothingReason(device, "the probe capture process exited before hearing the tone") }));
|
|
510
|
+
listener.stdout?.on("data", onData);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
/** Digital silence has two usual causes on macOS; always name both fixes. */
|
|
514
|
+
function capturedNothingReason(device, what) {
|
|
515
|
+
return `${what} on ${device}. Either the device is muted (open Audio MIDI Setup, select ${device}, and uncheck Mute for Input and Output) or macOS microphone permission is missing for the app running this command (System Settings > Privacy & Security > Microphone).`;
|
|
516
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DisclosureTracker = exports.disclosureTimings = void 0;
|
|
4
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
5
|
+
/** Tunable so tests can run the real state machine quickly. */
|
|
6
|
+
exports.disclosureTimings = {
|
|
7
|
+
/** The join leaves if the disclosure has not been spoken this long after it was first requested. */
|
|
8
|
+
deadlineMs: 20_000,
|
|
9
|
+
/** Pause before asking again after a cancelled or cut attempt. */
|
|
10
|
+
retryDelayMs: 500,
|
|
11
|
+
/** How long to wait for the response to start before checking whether the request was lost. */
|
|
12
|
+
createWaitMs: 4_000,
|
|
13
|
+
};
|
|
14
|
+
/** The spoken notice must actually say it is an AI; an answer to the room instead of the notice does not count. */
|
|
15
|
+
const SAYS_AI = /\b(A\.?I\b\.?|artificial intelligence)/iu;
|
|
16
|
+
/**
|
|
17
|
+
* Makes the spoken disclosure a guarantee instead of a hope. A local audio join asks the model to say
|
|
18
|
+
* the notice, but the floor gate can delay or overwrite that request and a barge-in can cut its audio.
|
|
19
|
+
* The tracker follows the one response that carries the notice and counts it as spoken only when the
|
|
20
|
+
* response completed, its audio transcript is done, no barge-in cut it, and its audio played out.
|
|
21
|
+
* Anything else asks again, until the deadline, when the join gives up and leaves.
|
|
22
|
+
*/
|
|
23
|
+
class DisclosureTracker {
|
|
24
|
+
options;
|
|
25
|
+
state = "idle";
|
|
26
|
+
responseId;
|
|
27
|
+
transcriptSeen = false;
|
|
28
|
+
cut = false;
|
|
29
|
+
sent = false;
|
|
30
|
+
attempts = 0;
|
|
31
|
+
markName = "";
|
|
32
|
+
deadlineTimer;
|
|
33
|
+
createTimer;
|
|
34
|
+
retryTimer;
|
|
35
|
+
now;
|
|
36
|
+
setTimer;
|
|
37
|
+
clearTimer;
|
|
38
|
+
constructor(options) {
|
|
39
|
+
this.options = options;
|
|
40
|
+
this.now = options.now ?? Date.now;
|
|
41
|
+
this.setTimer = options.setTimer ?? ((cb, ms) => {
|
|
42
|
+
const handle = setTimeout(cb, ms);
|
|
43
|
+
handle.unref();
|
|
44
|
+
return handle;
|
|
45
|
+
});
|
|
46
|
+
this.clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
47
|
+
}
|
|
48
|
+
start() {
|
|
49
|
+
this.deadlineTimer = this.setTimer(() => this.fail(), exports.disclosureTimings.deadlineMs);
|
|
50
|
+
this.attempt();
|
|
51
|
+
}
|
|
52
|
+
isSpoken() {
|
|
53
|
+
return this.state === "spoken";
|
|
54
|
+
}
|
|
55
|
+
stop() {
|
|
56
|
+
this.finish("stopped");
|
|
57
|
+
}
|
|
58
|
+
/** The session put a response.create on the wire. */
|
|
59
|
+
noteSent(body) {
|
|
60
|
+
if (body === this.options.body && this.state === "requested")
|
|
61
|
+
this.sent = true;
|
|
62
|
+
}
|
|
63
|
+
handleRealtimeEvent(event) {
|
|
64
|
+
if (this.isTerminal())
|
|
65
|
+
return;
|
|
66
|
+
const type = event.type;
|
|
67
|
+
const response = event.response;
|
|
68
|
+
if (type === "response.created") {
|
|
69
|
+
if (!this.sent || this.responseId || typeof response?.id !== "string")
|
|
70
|
+
return;
|
|
71
|
+
this.responseId = response.id;
|
|
72
|
+
this.sent = false;
|
|
73
|
+
this.state = "speaking";
|
|
74
|
+
this.clear(this.createTimer);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (type === "response.output_audio_transcript.done") {
|
|
78
|
+
if (this.responseId && event.response_id === this.responseId && typeof event.transcript === "string" && SAYS_AI.test(event.transcript)) {
|
|
79
|
+
this.transcriptSeen = true;
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (type === "response.done" && this.responseId && response?.id === this.responseId) {
|
|
84
|
+
if (response.status === "completed" && this.transcriptSeen && !this.cut) {
|
|
85
|
+
this.state = "playing";
|
|
86
|
+
this.markName = `disclosure-${this.attempts}`;
|
|
87
|
+
this.options.host.sendMark(this.markName);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
this.retry(`response ${String(response.status)}${this.cut ? " after a barge-in" : ""}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** The caller's speech cleared the playback queue. */
|
|
95
|
+
noteCleared() {
|
|
96
|
+
if (this.state === "speaking" || this.state === "playing")
|
|
97
|
+
this.cut = true;
|
|
98
|
+
}
|
|
99
|
+
/** A playback mark came back. Returns true when it was the disclosure's own. */
|
|
100
|
+
noteMark(name) {
|
|
101
|
+
if (this.state !== "playing" || name !== this.markName)
|
|
102
|
+
return false;
|
|
103
|
+
if (this.cut) {
|
|
104
|
+
this.retry("audio was cut before it played out");
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
this.finish("spoken");
|
|
108
|
+
const atMs = this.now();
|
|
109
|
+
(0, runtime_1.emitNervesEvent)({
|
|
110
|
+
component: "senses",
|
|
111
|
+
event: "senses.voice_local_disclosure_spoken",
|
|
112
|
+
message: "local audio disclosure notice was spoken and played out",
|
|
113
|
+
meta: { attempts: this.attempts },
|
|
114
|
+
});
|
|
115
|
+
this.options.host.onSpoken(atMs);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
attempt() {
|
|
119
|
+
this.attempts++;
|
|
120
|
+
this.state = "requested";
|
|
121
|
+
this.responseId = undefined;
|
|
122
|
+
this.transcriptSeen = false;
|
|
123
|
+
this.cut = false;
|
|
124
|
+
this.sent = false;
|
|
125
|
+
this.options.host.request(this.options.body);
|
|
126
|
+
this.createTimer = this.setTimer(() => this.checkRequest(), exports.disclosureTimings.createWaitMs);
|
|
127
|
+
}
|
|
128
|
+
/** The response never started: if the session no longer holds the request (the gate overwrote it), ask again. */
|
|
129
|
+
checkRequest() {
|
|
130
|
+
if (this.options.host.isQueued(this.options.body)) {
|
|
131
|
+
this.createTimer = this.setTimer(() => this.checkRequest(), exports.disclosureTimings.createWaitMs);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
this.attempt();
|
|
135
|
+
}
|
|
136
|
+
retry(why) {
|
|
137
|
+
this.state = "retrying";
|
|
138
|
+
(0, runtime_1.emitNervesEvent)({
|
|
139
|
+
level: "warn",
|
|
140
|
+
component: "senses",
|
|
141
|
+
event: "senses.voice_local_disclosure_retry",
|
|
142
|
+
message: "local audio disclosure notice was not spoken; asking again",
|
|
143
|
+
meta: { why, attempts: this.attempts },
|
|
144
|
+
});
|
|
145
|
+
this.retryTimer = this.setTimer(() => this.attempt(), exports.disclosureTimings.retryDelayMs);
|
|
146
|
+
}
|
|
147
|
+
fail() {
|
|
148
|
+
this.finish("failed");
|
|
149
|
+
(0, runtime_1.emitNervesEvent)({
|
|
150
|
+
level: "error",
|
|
151
|
+
component: "senses",
|
|
152
|
+
event: "senses.voice_local_disclosure_failed",
|
|
153
|
+
message: "local audio disclosure notice could not be spoken in time; leaving",
|
|
154
|
+
meta: { attempts: this.attempts, deadlineMs: exports.disclosureTimings.deadlineMs },
|
|
155
|
+
});
|
|
156
|
+
this.options.host.onFailed();
|
|
157
|
+
}
|
|
158
|
+
isTerminal() {
|
|
159
|
+
return this.state === "spoken" || this.state === "failed" || this.state === "stopped";
|
|
160
|
+
}
|
|
161
|
+
finish(state) {
|
|
162
|
+
if (this.isTerminal())
|
|
163
|
+
return;
|
|
164
|
+
this.state = state;
|
|
165
|
+
this.clear(this.deadlineTimer);
|
|
166
|
+
this.clear(this.createTimer);
|
|
167
|
+
this.clear(this.retryTimer);
|
|
168
|
+
}
|
|
169
|
+
clear(handle) {
|
|
170
|
+
if (handle !== undefined)
|
|
171
|
+
this.clearTimer(handle);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
exports.DisclosureTracker = DisclosureTracker;
|