@ouro.bot/cli 0.1.0-alpha.6 → 0.1.0-alpha.600

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (440) hide show
  1. package/README.md +229 -183
  2. package/RepairGuide.ouro/agent.json +5 -0
  3. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  4. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  5. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  6. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  7. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  8. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  9. package/SerpentGuide.ouro/agent.json +83 -0
  10. package/SerpentGuide.ouro/psyche/SOUL.md +25 -0
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +2 -2
  12. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  13. package/assets/ouroboros.png +0 -0
  14. package/changelog.json +4182 -0
  15. package/dist/arc/attention-types.js +8 -0
  16. package/dist/arc/cares.js +140 -0
  17. package/dist/arc/episodes.js +117 -0
  18. package/dist/arc/intentions.js +133 -0
  19. package/dist/arc/json-store.js +117 -0
  20. package/dist/arc/obligations.js +254 -0
  21. package/dist/arc/packets.js +193 -0
  22. package/dist/arc/presence.js +185 -0
  23. package/dist/arc/task-lifecycle.js +65 -0
  24. package/dist/heart/active-work.js +989 -0
  25. package/dist/heart/agent-entry.js +69 -3
  26. package/dist/heart/attachments/image-normalize.js +194 -0
  27. package/dist/heart/attachments/materialize.js +97 -0
  28. package/dist/heart/attachments/originals.js +88 -0
  29. package/dist/heart/attachments/render.js +29 -0
  30. package/dist/heart/attachments/sources/adapter.js +2 -0
  31. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  32. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  33. package/dist/heart/attachments/sources/index.js +16 -0
  34. package/dist/heart/attachments/store.js +103 -0
  35. package/dist/heart/attachments/types.js +93 -0
  36. package/dist/heart/auth/auth-flow.js +479 -0
  37. package/dist/heart/awaiting/await-alert.js +146 -0
  38. package/dist/heart/awaiting/await-expiry.js +108 -0
  39. package/dist/heart/awaiting/await-loader.js +91 -0
  40. package/dist/heart/awaiting/await-parser.js +141 -0
  41. package/dist/heart/awaiting/await-runtime-state.js +97 -0
  42. package/dist/heart/awaiting/await-scheduler.js +377 -0
  43. package/dist/heart/background-operations.js +281 -0
  44. package/dist/heart/bridges/manager.js +358 -0
  45. package/dist/heart/bridges/state-machine.js +135 -0
  46. package/dist/heart/bridges/store.js +123 -0
  47. package/dist/heart/bundle-state.js +168 -0
  48. package/dist/heart/commitments.js +142 -0
  49. package/dist/heart/config-registry.js +322 -0
  50. package/dist/heart/config.js +164 -135
  51. package/dist/heart/core.js +1069 -260
  52. package/dist/heart/cross-chat-delivery.js +131 -0
  53. package/dist/heart/daemon/agent-config-check.js +419 -0
  54. package/dist/heart/daemon/agent-discovery.js +180 -0
  55. package/dist/heart/daemon/agent-service.js +522 -0
  56. package/dist/heart/daemon/agentic-repair.js +547 -0
  57. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  58. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  59. package/dist/heart/daemon/cadence.js +70 -0
  60. package/dist/heart/daemon/cli-defaults.js +776 -0
  61. package/dist/heart/daemon/cli-exec.js +7571 -0
  62. package/dist/heart/daemon/cli-help.js +498 -0
  63. package/dist/heart/daemon/cli-parse.js +1599 -0
  64. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  65. package/dist/heart/daemon/cli-render.js +763 -0
  66. package/dist/heart/daemon/cli-types.js +8 -0
  67. package/dist/heart/daemon/connect-bay.js +323 -0
  68. package/dist/heart/daemon/daemon-cli.js +30 -758
  69. package/dist/heart/daemon/daemon-entry.js +540 -8
  70. package/dist/heart/daemon/daemon-health.js +176 -0
  71. package/dist/heart/daemon/daemon-rollup.js +57 -0
  72. package/dist/heart/daemon/daemon-runtime-sync.js +287 -0
  73. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  74. package/dist/heart/daemon/daemon.js +972 -20
  75. package/dist/heart/daemon/dns-workflow.js +394 -0
  76. package/dist/heart/daemon/doctor-types.js +8 -0
  77. package/dist/heart/daemon/doctor.js +873 -0
  78. package/dist/heart/daemon/health-monitor.js +122 -1
  79. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  80. package/dist/heart/daemon/hooks/bundle-meta.js +206 -0
  81. package/dist/heart/daemon/http-health-probe.js +80 -0
  82. package/dist/heart/daemon/human-command-screens.js +234 -0
  83. package/dist/heart/daemon/human-readiness.js +114 -0
  84. package/dist/heart/daemon/inner-status.js +89 -0
  85. package/dist/heart/daemon/interactive-repair.js +394 -0
  86. package/dist/heart/daemon/launchd.js +188 -0
  87. package/dist/heart/daemon/log-tailer.js +82 -12
  88. package/dist/heart/daemon/logs-prune.js +110 -0
  89. package/dist/heart/daemon/mcp-canary.js +297 -0
  90. package/dist/heart/daemon/message-router.js +17 -8
  91. package/dist/heart/daemon/os-cron-deps.js +135 -0
  92. package/dist/heart/daemon/os-cron.js +14 -12
  93. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  94. package/dist/heart/daemon/ouro-entry.js +3 -1
  95. package/dist/heart/daemon/process-manager.js +381 -26
  96. package/dist/heart/daemon/provider-discovery.js +137 -0
  97. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  98. package/dist/heart/daemon/pulse.js +475 -0
  99. package/dist/heart/daemon/readiness-repair.js +365 -0
  100. package/dist/heart/daemon/run-hooks.js +39 -0
  101. package/dist/heart/daemon/runtime-logging.js +67 -16
  102. package/dist/heart/daemon/runtime-metadata.js +191 -0
  103. package/dist/heart/daemon/runtime-mode.js +67 -0
  104. package/dist/heart/daemon/safe-mode.js +161 -0
  105. package/dist/heart/daemon/sense-manager.js +731 -0
  106. package/dist/heart/daemon/session-id-resolver.js +131 -0
  107. package/dist/heart/daemon/skill-management-installer.js +94 -0
  108. package/dist/heart/daemon/socket-client.js +349 -0
  109. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  110. package/dist/heart/daemon/startup-tui.js +330 -0
  111. package/dist/heart/daemon/task-scheduler.js +3 -25
  112. package/dist/heart/daemon/terminal-ui.js +499 -0
  113. package/dist/heart/daemon/thoughts.js +524 -0
  114. package/dist/heart/daemon/up-progress.js +366 -0
  115. package/dist/heart/daemon/vault-items.js +56 -0
  116. package/dist/heart/delegation.js +62 -0
  117. package/dist/heart/habits/habit-migration.js +189 -0
  118. package/dist/heart/habits/habit-parser.js +140 -0
  119. package/dist/heart/habits/habit-runtime-state.js +100 -0
  120. package/dist/heart/habits/habit-scheduler.js +372 -0
  121. package/dist/heart/{daemon → hatch}/hatch-animation.js +10 -3
  122. package/dist/heart/{daemon → hatch}/hatch-flow.js +34 -136
  123. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  124. package/dist/heart/hatch/specialist-orchestrator.js +129 -0
  125. package/dist/heart/hatch/specialist-prompt.js +102 -0
  126. package/dist/heart/hatch/specialist-tools.js +306 -0
  127. package/dist/heart/identity.js +281 -67
  128. package/dist/heart/kept-notes.js +357 -0
  129. package/dist/heart/kicks.js +2 -20
  130. package/dist/heart/machine-identity.js +161 -0
  131. package/dist/heart/mail-import-discovery.js +353 -0
  132. package/dist/heart/mailbox/mailbox-http-hooks.js +66 -0
  133. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  134. package/dist/heart/mailbox/mailbox-http-routes.js +246 -0
  135. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  136. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  137. package/dist/heart/mailbox/mailbox-http.js +99 -0
  138. package/dist/heart/mailbox/mailbox-read.js +31 -0
  139. package/dist/heart/mailbox/mailbox-types.js +27 -0
  140. package/dist/heart/mailbox/mailbox-view.js +195 -0
  141. package/dist/heart/mailbox/readers/agent-machine.js +382 -0
  142. package/dist/heart/mailbox/readers/continuity-readers.js +338 -0
  143. package/dist/heart/mailbox/readers/mail.js +367 -0
  144. package/dist/heart/mailbox/readers/runtime-readers.js +651 -0
  145. package/dist/heart/mailbox/readers/sessions.js +232 -0
  146. package/dist/heart/mailbox/readers/shared.js +111 -0
  147. package/dist/heart/mcp/mcp-server.js +656 -0
  148. package/dist/heart/migrate-config.js +100 -0
  149. package/dist/heart/model-capabilities.js +59 -0
  150. package/dist/heart/platform.js +81 -0
  151. package/dist/heart/progress-story.js +42 -0
  152. package/dist/heart/provider-attempt.js +134 -0
  153. package/dist/heart/provider-binding-resolver.js +267 -0
  154. package/dist/heart/provider-credentials.js +425 -0
  155. package/dist/heart/provider-failover.js +301 -0
  156. package/dist/heart/provider-models.js +81 -0
  157. package/dist/heart/provider-ping.js +262 -0
  158. package/dist/heart/provider-readiness-cache.js +40 -0
  159. package/dist/heart/provider-visibility.js +188 -0
  160. package/dist/heart/providers/anthropic-token.js +131 -0
  161. package/dist/heart/providers/anthropic.js +202 -50
  162. package/dist/heart/providers/azure.js +104 -13
  163. package/dist/heart/providers/error-classification.js +127 -0
  164. package/dist/heart/providers/github-copilot.js +145 -0
  165. package/dist/heart/providers/minimax-vlm.js +189 -0
  166. package/dist/heart/providers/minimax.js +29 -7
  167. package/dist/heart/providers/openai-codex.js +63 -39
  168. package/dist/heart/runtime-capability-check.js +170 -0
  169. package/dist/heart/runtime-credentials.js +367 -0
  170. package/dist/heart/runtime-cwd.js +87 -0
  171. package/dist/heart/sense-truth.js +70 -0
  172. package/dist/heart/session-activity.js +190 -0
  173. package/dist/heart/session-events.js +1149 -0
  174. package/dist/heart/session-playback-cli-main.js +5 -0
  175. package/dist/heart/session-playback-cli.js +36 -0
  176. package/dist/heart/session-playback.js +231 -0
  177. package/dist/heart/session-stats-cli-main.js +5 -0
  178. package/dist/heart/session-stats.js +182 -0
  179. package/dist/heart/session-transcript.js +243 -0
  180. package/dist/heart/start-of-turn-packet.js +345 -0
  181. package/dist/heart/streaming.js +129 -34
  182. package/dist/heart/sync-classification.js +176 -0
  183. package/dist/heart/sync.js +449 -0
  184. package/dist/heart/target-resolution.js +127 -0
  185. package/dist/heart/tempo.js +93 -0
  186. package/dist/heart/temporal-view.js +41 -0
  187. package/dist/heart/timeouts.js +101 -0
  188. package/dist/heart/tool-activity-callbacks.js +59 -0
  189. package/dist/heart/tool-description.js +143 -0
  190. package/dist/heart/tool-friction.js +55 -0
  191. package/dist/heart/tool-loop.js +200 -0
  192. package/dist/heart/turn-context.js +421 -0
  193. package/dist/heart/turn-coordinator.js +28 -0
  194. package/dist/heart/versioning/ouro-bot-global-installer.js +129 -0
  195. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  196. package/dist/heart/versioning/ouro-path-installer.js +426 -0
  197. package/dist/heart/{daemon → versioning}/ouro-uti.js +11 -2
  198. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  199. package/dist/heart/versioning/staged-restart.js +146 -0
  200. package/dist/heart/versioning/update-checker.js +116 -0
  201. package/dist/heart/versioning/update-hooks.js +142 -0
  202. package/dist/heart/versioning/wrapper-publish-guard.js +86 -0
  203. package/dist/mailbox-ui/assets/index-B-461hes.js +61 -0
  204. package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -0
  205. package/dist/mailbox-ui/index.html +15 -0
  206. package/dist/mailroom/attention.js +167 -0
  207. package/dist/mailroom/autonomy.js +209 -0
  208. package/dist/mailroom/blob-store.js +700 -0
  209. package/dist/mailroom/body-cache.js +61 -0
  210. package/dist/mailroom/core.js +788 -0
  211. package/dist/mailroom/entry.js +160 -0
  212. package/dist/mailroom/file-store.js +457 -0
  213. package/dist/mailroom/mbox-import.js +393 -0
  214. package/dist/mailroom/migration.js +164 -0
  215. package/dist/mailroom/outbound.js +380 -0
  216. package/dist/mailroom/policy.js +263 -0
  217. package/dist/mailroom/reader.js +233 -0
  218. package/dist/mailroom/search-cache.js +268 -0
  219. package/dist/mailroom/search-relevance.js +319 -0
  220. package/dist/mailroom/smtp-ingress.js +176 -0
  221. package/dist/mailroom/source-state.js +176 -0
  222. package/dist/mailroom/thread.js +109 -0
  223. package/dist/mailroom/travel-extract.js +89 -0
  224. package/dist/mind/bundle-manifest.js +77 -1
  225. package/dist/mind/context.js +174 -94
  226. package/dist/mind/diary-integrity.js +60 -0
  227. package/dist/mind/{memory.js → diary.js} +84 -96
  228. package/dist/mind/embedding-provider.js +60 -0
  229. package/dist/mind/file-state.js +179 -0
  230. package/dist/mind/first-impressions.js +16 -2
  231. package/dist/mind/friends/channel.js +74 -0
  232. package/dist/mind/friends/group-context.js +144 -0
  233. package/dist/mind/friends/resolver.js +54 -2
  234. package/dist/mind/friends/store-file.js +58 -3
  235. package/dist/mind/friends/trust-explanation.js +74 -0
  236. package/dist/mind/friends/types.js +10 -2
  237. package/dist/mind/journal-index.js +161 -0
  238. package/dist/mind/note-search.js +268 -0
  239. package/dist/mind/obligation-steering.js +221 -0
  240. package/dist/mind/pending.js +76 -9
  241. package/dist/mind/phrases.js +1 -0
  242. package/dist/mind/prompt-refresh.js +3 -2
  243. package/dist/mind/prompt.js +1267 -130
  244. package/dist/mind/provenance-trust.js +26 -0
  245. package/dist/mind/scrutiny.js +173 -0
  246. package/dist/mind/token-estimate.js +8 -12
  247. package/dist/nerves/cli-logging.js +22 -3
  248. package/dist/nerves/coverage/audit-rules.js +15 -6
  249. package/dist/nerves/coverage/audit.js +28 -2
  250. package/dist/nerves/coverage/cli.js +1 -1
  251. package/dist/nerves/coverage/contract.js +5 -5
  252. package/dist/nerves/coverage/file-completeness.js +129 -5
  253. package/dist/nerves/coverage/run-artifacts.js +1 -1
  254. package/dist/nerves/event-buffer.js +111 -0
  255. package/dist/nerves/index.js +224 -4
  256. package/dist/nerves/observation.js +20 -0
  257. package/dist/nerves/redact.js +79 -0
  258. package/dist/nerves/review/cli-main.js +5 -0
  259. package/dist/nerves/review/cli.js +156 -0
  260. package/dist/nerves/review/core.js +152 -0
  261. package/dist/nerves/runtime.js +5 -1
  262. package/dist/repertoire/ado-client.js +17 -56
  263. package/dist/repertoire/ado-semantic.js +11 -10
  264. package/dist/repertoire/api-client.js +97 -0
  265. package/dist/repertoire/bitwarden-store.js +997 -0
  266. package/dist/repertoire/bundle-templates.js +72 -0
  267. package/dist/repertoire/bw-installer.js +180 -0
  268. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  269. package/dist/repertoire/coding/context-pack.js +330 -0
  270. package/dist/repertoire/coding/feedback.js +301 -0
  271. package/dist/repertoire/coding/index.js +4 -1
  272. package/dist/repertoire/coding/manager.js +220 -13
  273. package/dist/repertoire/coding/spawner.js +58 -12
  274. package/dist/repertoire/coding/tools.js +209 -7
  275. package/dist/repertoire/commerce-errors.js +109 -0
  276. package/dist/repertoire/commerce-self-test.js +156 -0
  277. package/dist/repertoire/credential-access.js +178 -0
  278. package/dist/repertoire/data/ado-endpoints.json +188 -0
  279. package/dist/repertoire/duffel-client.js +185 -0
  280. package/dist/repertoire/github-client.js +14 -55
  281. package/dist/repertoire/graph-client.js +11 -52
  282. package/dist/repertoire/guardrails.js +396 -0
  283. package/dist/repertoire/mcp-client.js +295 -0
  284. package/dist/repertoire/mcp-manager.js +362 -0
  285. package/dist/repertoire/mcp-tools.js +63 -0
  286. package/dist/repertoire/shell-sessions.js +133 -0
  287. package/dist/repertoire/skills.js +15 -24
  288. package/dist/repertoire/stripe-client.js +131 -0
  289. package/dist/repertoire/tasks/board.js +43 -5
  290. package/dist/repertoire/tasks/fix.js +182 -0
  291. package/dist/repertoire/tasks/index.js +39 -13
  292. package/dist/repertoire/tasks/lifecycle.js +2 -2
  293. package/dist/repertoire/tasks/parser.js +3 -2
  294. package/dist/repertoire/tasks/scanner.js +194 -37
  295. package/dist/repertoire/tasks/transitions.js +16 -79
  296. package/dist/repertoire/tool-results.js +29 -0
  297. package/dist/repertoire/tools-attachments.js +317 -0
  298. package/dist/repertoire/tools-awaiting.js +360 -0
  299. package/dist/repertoire/tools-base.js +56 -707
  300. package/dist/repertoire/tools-bluebubbles.js +94 -0
  301. package/dist/repertoire/tools-bridge.js +142 -0
  302. package/dist/repertoire/tools-bundle.js +984 -0
  303. package/dist/repertoire/tools-config.js +185 -0
  304. package/dist/repertoire/tools-continuity.js +248 -0
  305. package/dist/repertoire/tools-credential.js +381 -0
  306. package/dist/repertoire/tools-files.js +342 -0
  307. package/dist/repertoire/tools-flight.js +224 -0
  308. package/dist/repertoire/tools-flow.js +119 -0
  309. package/dist/repertoire/tools-github.js +1 -7
  310. package/dist/repertoire/tools-mail.js +1916 -0
  311. package/dist/repertoire/tools-notes.js +421 -0
  312. package/dist/repertoire/tools-obligations.js +142 -0
  313. package/dist/repertoire/tools-runtime.js +61 -0
  314. package/dist/repertoire/tools-session.js +809 -0
  315. package/dist/repertoire/tools-shell.js +120 -0
  316. package/dist/repertoire/tools-stripe.js +180 -0
  317. package/dist/repertoire/tools-surface.js +345 -0
  318. package/dist/repertoire/tools-teams.js +64 -61
  319. package/dist/repertoire/tools-travel.js +125 -0
  320. package/dist/repertoire/tools-trip.js +604 -0
  321. package/dist/repertoire/tools-user-profile.js +144 -0
  322. package/dist/repertoire/tools-vault.js +40 -0
  323. package/dist/repertoire/tools-voice.js +144 -0
  324. package/dist/repertoire/tools.js +154 -98
  325. package/dist/repertoire/travel-api-client.js +360 -0
  326. package/dist/repertoire/user-profile.js +131 -0
  327. package/dist/repertoire/vault-setup.js +246 -0
  328. package/dist/repertoire/vault-unlock.js +594 -0
  329. package/dist/scripts/claude-code-hook.js +41 -0
  330. package/dist/scripts/claude-code-stop-hook.js +47 -0
  331. package/dist/senses/attention-queue.js +116 -0
  332. package/dist/senses/await-turn-message.js +58 -0
  333. package/dist/senses/bluebubbles/active-turns.js +216 -0
  334. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  335. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  336. package/dist/senses/bluebubbles/client.js +685 -0
  337. package/dist/senses/bluebubbles/entry.js +77 -0
  338. package/dist/senses/bluebubbles/inbound-log.js +126 -0
  339. package/dist/senses/bluebubbles/index.js +2548 -0
  340. package/dist/senses/bluebubbles/media.js +389 -0
  341. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +45 -16
  342. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  343. package/dist/senses/bluebubbles/processed-log.js +133 -0
  344. package/dist/senses/bluebubbles/replay.js +137 -0
  345. package/dist/senses/bluebubbles/runtime-state.js +137 -0
  346. package/dist/senses/bluebubbles/session-cleanup.js +72 -0
  347. package/dist/senses/bluebubbles-meta-guard.js +40 -0
  348. package/dist/senses/cli/bracketed-paste.js +82 -0
  349. package/dist/senses/cli/image-paste.js +287 -0
  350. package/dist/senses/cli/image-ref-navigation.js +75 -0
  351. package/dist/senses/cli/ink-app.js +156 -0
  352. package/dist/senses/cli/inline-diff.js +64 -0
  353. package/dist/senses/cli/input-keys.js +174 -0
  354. package/dist/senses/cli/kill-ring.js +86 -0
  355. package/dist/senses/cli/message-list.js +51 -0
  356. package/dist/senses/cli/ouro-tui.js +607 -0
  357. package/dist/senses/cli/spinner-imperative.js +135 -0
  358. package/dist/senses/cli/spinner.js +101 -0
  359. package/dist/senses/cli/status-line.js +60 -0
  360. package/dist/senses/cli/streaming-markdown.js +526 -0
  361. package/dist/senses/cli/tool-display.js +85 -0
  362. package/dist/senses/cli/tool-render.js +85 -0
  363. package/dist/senses/cli/tui-store.js +240 -0
  364. package/dist/senses/cli/virtual-list.js +35 -0
  365. package/dist/senses/cli-entry.js +60 -8
  366. package/dist/senses/cli-layout.js +187 -0
  367. package/dist/senses/cli.js +777 -264
  368. package/dist/senses/commands.js +66 -3
  369. package/dist/senses/continuity.js +94 -0
  370. package/dist/senses/habit-turn-message.js +108 -0
  371. package/dist/senses/inner-dialog-worker.js +209 -16
  372. package/dist/senses/inner-dialog.js +682 -91
  373. package/dist/senses/mail-entry.js +66 -0
  374. package/dist/senses/mail.js +379 -0
  375. package/dist/senses/pipeline.js +751 -0
  376. package/dist/senses/proactive-content-guard.js +51 -0
  377. package/dist/senses/shared-turn.js +392 -0
  378. package/dist/senses/surface-tool.js +70 -0
  379. package/dist/senses/teams-entry.js +60 -8
  380. package/dist/senses/teams.js +925 -195
  381. package/dist/senses/trust-gate.js +207 -2
  382. package/dist/senses/voice/audio-playback.js +237 -0
  383. package/dist/senses/voice/audio-routing.js +119 -0
  384. package/dist/senses/voice/elevenlabs.js +202 -0
  385. package/dist/senses/voice/floor-control.js +431 -0
  386. package/dist/senses/voice/floor-controller.js +115 -0
  387. package/dist/senses/voice/golden-path.js +116 -0
  388. package/dist/senses/voice/index.js +29 -0
  389. package/dist/senses/voice/meeting.js +113 -0
  390. package/dist/senses/voice/outbound.js +190 -0
  391. package/dist/senses/voice/phone.js +33 -0
  392. package/dist/senses/voice/playback.js +139 -0
  393. package/dist/senses/voice/realtime-eval.js +496 -0
  394. package/dist/senses/voice/realtime-trace.js +531 -0
  395. package/dist/senses/voice/transcript.js +70 -0
  396. package/dist/senses/voice/turn.js +191 -0
  397. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  398. package/dist/senses/voice/twilio-phone.js +5077 -0
  399. package/dist/senses/voice/types.js +2 -0
  400. package/dist/senses/voice/whisper.js +161 -0
  401. package/dist/senses/voice-entry.js +81 -0
  402. package/dist/senses/voice-realtime-eval-command.js +99 -0
  403. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  404. package/dist/senses/voice-twilio-entry.js +87 -0
  405. package/dist/trips/core.js +138 -0
  406. package/dist/trips/store.js +265 -0
  407. package/package.json +52 -7
  408. package/skills/agent-commerce.md +106 -0
  409. package/skills/browser-navigation.md +117 -0
  410. package/skills/commerce-setup-guide.md +116 -0
  411. package/skills/commerce-setup.md +84 -0
  412. package/skills/configure-dev-tools.md +99 -0
  413. package/skills/travel-planning.md +138 -0
  414. package/AdoptionSpecialist.ouro/agent.json +0 -20
  415. package/AdoptionSpecialist.ouro/psyche/SOUL.md +0 -22
  416. package/dist/heart/daemon/specialist-orchestrator.js +0 -160
  417. package/dist/heart/daemon/specialist-prompt.js +0 -40
  418. package/dist/heart/daemon/specialist-session.js +0 -142
  419. package/dist/heart/daemon/specialist-tools.js +0 -128
  420. package/dist/heart/daemon/subagent-installer.js +0 -125
  421. package/dist/inner-worker-entry.js +0 -4
  422. package/dist/mind/associative-recall.js +0 -197
  423. package/dist/senses/bluebubbles-client.js +0 -279
  424. package/dist/senses/bluebubbles-entry.js +0 -11
  425. package/dist/senses/bluebubbles.js +0 -332
  426. package/subagents/README.md +0 -73
  427. package/subagents/work-doer.md +0 -233
  428. package/subagents/work-merger.md +0 -624
  429. package/subagents/work-planner.md +0 -373
  430. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  431. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  432. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  433. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  434. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  435. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  436. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  437. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  438. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  439. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  440. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
@@ -39,6 +39,9 @@ const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const identity_1 = require("../heart/identity");
41
41
  const runtime_1 = require("../nerves/runtime");
42
+ const types_1 = require("../mind/friends/types");
43
+ const pending_1 = require("../mind/pending");
44
+ // Canned reply; eventually agents should compose their own first-contact message
42
45
  exports.STRANGER_AUTO_REPLY = "I'm sorry, I'm not allowed to talk to strangers";
43
46
  function buildExternalKey(provider, externalId, tenantId) {
44
47
  return `${provider}:${tenantId ?? ""}:${externalId}`;
@@ -77,16 +80,202 @@ function appendPrimaryNotification(bundleRoot, provider, externalId, tenantId, n
77
80
  fs.mkdirSync(inboxDir, { recursive: true });
78
81
  fs.appendFileSync(notificationsPath, `${JSON.stringify(payload)}\n`, "utf8");
79
82
  }
83
+ function writeInnerPendingNotice(bundleRoot, noticeContent, nowIso) {
84
+ const innerPendingDir = path.join(bundleRoot, "state", "pending", pending_1.INNER_DIALOG_PENDING.friendId, pending_1.INNER_DIALOG_PENDING.channel, pending_1.INNER_DIALOG_PENDING.key);
85
+ const fileName = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
86
+ const filePath = path.join(innerPendingDir, fileName);
87
+ const payload = {
88
+ from: "instinct",
89
+ content: noticeContent,
90
+ timestamp: Date.now(),
91
+ at: nowIso,
92
+ };
93
+ fs.mkdirSync(innerPendingDir, { recursive: true });
94
+ fs.writeFileSync(filePath, JSON.stringify(payload), "utf-8");
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
+ }
80
181
  function enforceTrustGate(input) {
182
+ const { senseType } = input;
183
+ // Local (CLI) and internal (inner dialog) — always allow
184
+ if (senseType === "local" || senseType === "internal") {
185
+ return { allowed: true };
186
+ }
187
+ // Closed senses (Teams) — org already gates access, allow all trust levels
188
+ if (senseType === "closed") {
189
+ return { allowed: true };
190
+ }
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
+ }
81
206
  const trustLevel = input.friend.trustLevel ?? "friend";
82
- if (trustLevel !== "stranger") {
207
+ // Family and friend — always allow on open
208
+ if ((0, types_1.isTrustedLevel)(trustLevel)) {
83
209
  return { allowed: true };
84
210
  }
85
211
  const bundleRoot = input.bundleRoot ?? (0, identity_1.getAgentRoot)();
86
- const repliesPath = path.join(bundleRoot, "stranger-replies.json");
87
212
  const nowIso = (input.now ?? (() => new Date()))().toISOString();
213
+ // Acquaintance rules
214
+ if (trustLevel === "acquaintance") {
215
+ return handleAcquaintance(input, bundleRoot, nowIso);
216
+ }
217
+ // Stranger rules (trustLevel === "stranger")
218
+ return handleStranger(input, bundleRoot, nowIso);
219
+ }
220
+ function handleAcquaintance(input, bundleRoot, nowIso) {
221
+ const { isGroupChat, hasExistingGroupWithFamily } = input;
222
+ let result;
223
+ let noticeDetail;
224
+ if (isGroupChat) {
225
+ // Group chat without family member — reject silently
226
+ result = { allowed: false, reason: "acquaintance_group_no_family" };
227
+ noticeDetail = `acquaintance "${input.friend.name}" messaged in a group chat without a family member present`;
228
+ }
229
+ else if (hasExistingGroupWithFamily) {
230
+ // 1:1 but shares a group with family — redirect
231
+ result = {
232
+ allowed: false,
233
+ reason: "acquaintance_1on1_has_group",
234
+ autoReply: "Hey! Reach me in our group chat instead.",
235
+ };
236
+ noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — redirected to our group chat`;
237
+ }
238
+ else {
239
+ // 1:1, no shared group with family — redirect to any group
240
+ result = {
241
+ allowed: false,
242
+ reason: "acquaintance_1on1_no_group",
243
+ autoReply: "Hey! Reach me in a group chat instead.",
244
+ };
245
+ noticeDetail = `acquaintance "${input.friend.name}" DMed me directly — asked to reach me in a group chat`;
246
+ }
247
+ (0, runtime_1.emitNervesEvent)({
248
+ level: "warn",
249
+ component: "senses",
250
+ event: "senses.trust_gate",
251
+ message: "acquaintance message blocked",
252
+ meta: {
253
+ channel: input.channel,
254
+ provider: input.provider,
255
+ reason: result.reason,
256
+ },
257
+ });
258
+ try {
259
+ writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
260
+ }
261
+ catch (error) {
262
+ (0, runtime_1.emitNervesEvent)({
263
+ level: "error",
264
+ component: "senses",
265
+ event: "senses.trust_gate_error",
266
+ message: "failed to write inner pending notice",
267
+ meta: {
268
+ reason: error instanceof Error ? error.message : String(error),
269
+ },
270
+ });
271
+ }
272
+ return result;
273
+ }
274
+ function handleStranger(input, bundleRoot, nowIso) {
275
+ const repliesPath = path.join(bundleRoot, "stranger-replies.json");
88
276
  const externalKey = buildExternalKey(input.provider, input.externalId, input.tenantId);
89
277
  const state = loadRepliesState(repliesPath);
278
+ // Subsequent contact — silent drop
90
279
  if (state[externalKey]) {
91
280
  (0, runtime_1.emitNervesEvent)({
92
281
  level: "warn",
@@ -103,6 +292,7 @@ function enforceTrustGate(input) {
103
292
  reason: "stranger_silent_drop",
104
293
  };
105
294
  }
295
+ // First contact — auto-reply, persist state, notify agent
106
296
  state[externalKey] = nowIso;
107
297
  try {
108
298
  persistRepliesState(repliesPath, state);
@@ -132,6 +322,21 @@ function enforceTrustGate(input) {
132
322
  },
133
323
  });
134
324
  }
325
+ const noticeDetail = `stranger "${input.friend.name}" tried to reach me via ${input.channel}. Auto-replied once.`;
326
+ try {
327
+ writeInnerPendingNotice(bundleRoot, noticeDetail, nowIso);
328
+ }
329
+ catch (error) {
330
+ (0, runtime_1.emitNervesEvent)({
331
+ level: "error",
332
+ component: "senses",
333
+ event: "senses.trust_gate_error",
334
+ message: "failed to write inner pending notice",
335
+ meta: {
336
+ reason: error instanceof Error ? error.message : String(error),
337
+ },
338
+ });
339
+ }
135
340
  (0, runtime_1.emitNervesEvent)({
136
341
  level: "warn",
137
342
  component: "senses",
@@ -0,0 +1,237 @@
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.prepareVoiceCallAudio = prepareVoiceCallAudio;
37
+ const node_child_process_1 = require("node:child_process");
38
+ const fs = __importStar(require("fs/promises"));
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
41
+ const runtime_1 = require("../../nerves/runtime");
42
+ const SAMPLE_RATE = 8_000;
43
+ const DEFAULT_TONE_MS = 700;
44
+ const DEFAULT_CLIP_MS = 5_000;
45
+ const MAX_AUDIO_MS = 20_000;
46
+ const MAX_AUDIO_BYTES = 10 * 1024 * 1024;
47
+ function clampDuration(value, fallback) {
48
+ if (value === undefined || !Number.isFinite(value))
49
+ return fallback;
50
+ return Math.min(MAX_AUDIO_MS, Math.max(80, Math.round(value)));
51
+ }
52
+ function clampToneHz(value) {
53
+ if (value === undefined || !Number.isFinite(value))
54
+ return 660;
55
+ return Math.min(3_000, Math.max(80, value));
56
+ }
57
+ function pcm16ToMulaw(sample) {
58
+ const BIAS = 0x84;
59
+ const CLIP = 32635;
60
+ let sign = 0;
61
+ let magnitude = Math.round(sample);
62
+ if (magnitude < 0) {
63
+ magnitude = -magnitude;
64
+ sign = 0x80;
65
+ }
66
+ magnitude = Math.min(CLIP, magnitude) + BIAS;
67
+ let exponent = 7;
68
+ for (let mask = 0x4000; exponent > 0 && (magnitude & mask) === 0; mask >>= 1) {
69
+ exponent -= 1;
70
+ }
71
+ const mantissa = (magnitude >> (exponent + 3)) & 0x0f;
72
+ return (~(sign | (exponent << 4) | mantissa)) & 0xff;
73
+ }
74
+ function generateToneMulaw(toneHz, durationMs) {
75
+ const sampleCount = Math.max(1, Math.round((SAMPLE_RATE * durationMs) / 1000));
76
+ const out = new Uint8Array(sampleCount);
77
+ for (let i = 0; i < sampleCount; i += 1) {
78
+ const envelope = Math.min(1, i / 160, (sampleCount - i) / 160);
79
+ const sample = Math.sin((2 * Math.PI * toneHz * i) / SAMPLE_RATE) * 9000 * Math.max(0, envelope);
80
+ out[i] = pcm16ToMulaw(sample);
81
+ }
82
+ return out;
83
+ }
84
+ function execFileText(file, args, timeout = 30_000) {
85
+ return new Promise((resolve, reject) => {
86
+ (0, node_child_process_1.execFile)(file, args, { timeout }, (error, stdout = "", stderr = "") => {
87
+ if (error) {
88
+ const message = stderr.toString().trim() || error.message;
89
+ reject(new Error(message));
90
+ return;
91
+ }
92
+ resolve(stdout.toString());
93
+ });
94
+ });
95
+ }
96
+ async function convertWithFfmpeg(input, durationMs, options) {
97
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "ouro-voice-audio-"));
98
+ const inputPath = path.join(tmpDir, "input.audio");
99
+ const outputPath = path.join(tmpDir, "output.ulaw");
100
+ await fs.writeFile(inputPath, input);
101
+ const args = [
102
+ "-hide_banner",
103
+ "-loglevel",
104
+ "error",
105
+ "-y",
106
+ "-t",
107
+ (durationMs / 1000).toFixed(3),
108
+ "-i",
109
+ inputPath,
110
+ "-ac",
111
+ "1",
112
+ "-ar",
113
+ String(SAMPLE_RATE),
114
+ "-f",
115
+ "mulaw",
116
+ outputPath,
117
+ ];
118
+ const candidates = [
119
+ ...(options.ffmpegCandidates ?? [
120
+ options.ffmpegPath,
121
+ "ffmpeg",
122
+ "/opt/homebrew/bin/ffmpeg",
123
+ "/usr/local/bin/ffmpeg",
124
+ "/usr/bin/ffmpeg",
125
+ ]),
126
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
127
+ let lastError = null;
128
+ try {
129
+ for (const candidate of candidates) {
130
+ try {
131
+ await execFileText(candidate, args);
132
+ return await fs.readFile(outputPath);
133
+ }
134
+ catch (error) {
135
+ /* v8 ignore next -- child_process errors are Error instances in supported Node runtimes @preserve */
136
+ lastError = error instanceof Error ? error : new Error(String(error));
137
+ }
138
+ }
139
+ /* v8 ignore next -- either a candidate error or an empty candidate list is covered; v8 counts the nullish join as a branch @preserve */
140
+ throw lastError ?? new Error("ffmpeg unavailable");
141
+ }
142
+ finally {
143
+ await fs.rm(tmpDir, { recursive: true, force: true });
144
+ }
145
+ }
146
+ async function readUrlAudio(url, fetchImpl) {
147
+ const parsed = new URL(url);
148
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
149
+ throw new Error("voice audio URL must be http(s)");
150
+ }
151
+ const response = await fetchImpl(parsed.toString());
152
+ if (!response.ok)
153
+ throw new Error(`voice audio URL fetch failed: ${response.status}`);
154
+ const contentLength = Number(response.headers.get("content-length") ?? "0");
155
+ if (Number.isFinite(contentLength) && contentLength > MAX_AUDIO_BYTES) {
156
+ throw new Error("voice audio URL is too large");
157
+ }
158
+ const bytes = new Uint8Array(await response.arrayBuffer());
159
+ /* v8 ignore next -- oversized response bodies are covered; v8 under-reports the positive branch for arrayBuffer-backed Response @preserve */
160
+ if (bytes.byteLength > MAX_AUDIO_BYTES)
161
+ throw new Error("voice audio URL is too large");
162
+ return bytes;
163
+ }
164
+ async function readFileAudio(inputPath, agentRoot) {
165
+ const resolved = path.resolve(inputPath);
166
+ const allowedRoots = [
167
+ agentRoot ? path.resolve(agentRoot) : "",
168
+ path.resolve(os.tmpdir()),
169
+ ].filter(Boolean);
170
+ if (!allowedRoots.some((root) => resolved === root || resolved.startsWith(`${root}${path.sep}`))) {
171
+ throw new Error("voice audio files must live under the agent bundle or temp directory");
172
+ }
173
+ const stat = await fs.stat(resolved);
174
+ if (!stat.isFile())
175
+ throw new Error("voice audio path is not a file");
176
+ if (stat.size > MAX_AUDIO_BYTES)
177
+ throw new Error("voice audio file is too large");
178
+ return fs.readFile(resolved);
179
+ }
180
+ async function prepareVoiceCallAudio(request, options = {}) {
181
+ const source = request.source ?? "tone";
182
+ const label = request.label?.trim() || (source === "tone" ? "tone" : "audio clip");
183
+ (0, runtime_1.emitNervesEvent)({
184
+ component: "senses",
185
+ event: "senses.voice_audio_prepare_start",
186
+ message: "preparing voice call audio clip",
187
+ meta: { source, label },
188
+ });
189
+ try {
190
+ if (source === "tone") {
191
+ const durationMs = clampDuration(request.durationMs, DEFAULT_TONE_MS);
192
+ const prepared = {
193
+ label,
194
+ durationMs,
195
+ mimeType: "audio/x-mulaw;rate=8000",
196
+ audio: generateToneMulaw(clampToneHz(request.toneHz), durationMs),
197
+ };
198
+ (0, runtime_1.emitNervesEvent)({
199
+ component: "senses",
200
+ event: "senses.voice_audio_prepare_end",
201
+ message: "prepared voice call audio clip",
202
+ meta: { source, label, durationMs: String(prepared.durationMs), byteLength: String(prepared.audio.byteLength) },
203
+ });
204
+ return prepared;
205
+ }
206
+ const durationMs = clampDuration(request.durationMs, DEFAULT_CLIP_MS);
207
+ /* v8 ignore next 3 -- URL and file branches are both covered; optional missing-field fallbacks are defensive @preserve */
208
+ const input = source === "url"
209
+ ? await readUrlAudio(request.url?.trim() || "", options.fetchImpl ?? fetch)
210
+ : await readFileAudio(request.path?.trim() || "", options.agentRoot);
211
+ const audio = await convertWithFfmpeg(input, durationMs, options);
212
+ const prepared = {
213
+ label,
214
+ durationMs: Math.min(durationMs, Math.round((audio.byteLength / SAMPLE_RATE) * 1000)),
215
+ mimeType: "audio/x-mulaw;rate=8000",
216
+ audio,
217
+ };
218
+ (0, runtime_1.emitNervesEvent)({
219
+ component: "senses",
220
+ event: "senses.voice_audio_prepare_end",
221
+ message: "prepared voice call audio clip",
222
+ meta: { source, label, durationMs: String(prepared.durationMs), byteLength: String(prepared.audio.byteLength) },
223
+ });
224
+ return prepared;
225
+ }
226
+ catch (error) {
227
+ (0, runtime_1.emitNervesEvent)({
228
+ level: "error",
229
+ component: "senses",
230
+ event: "senses.voice_audio_prepare_error",
231
+ message: "failed to prepare voice call audio clip",
232
+ /* v8 ignore next -- catches in this module throw Error objects @preserve */
233
+ meta: { source, label, error: error instanceof Error ? error.message : String(error) },
234
+ });
235
+ throw error;
236
+ }
237
+ }
@@ -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
+ }