@cohortapp/agent-sdk 2.3.0

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 (731) hide show
  1. package/.claude/commands/init-agent.md +104 -0
  2. package/.claude/commands/init-maestro.md +1187 -0
  3. package/.claude/settings.json +161 -0
  4. package/.env.example +216 -0
  5. package/README.md +632 -0
  6. package/agents/browser-operator/agent.md +52 -0
  7. package/agents/calendar-ops/agent.md +50 -0
  8. package/agents/communications/agent.md +96 -0
  9. package/agents/decision-log/agent.md +65 -0
  10. package/agents/desktop-operator/agent.md +59 -0
  11. package/agents/gmail-operator/agent.md +62 -0
  12. package/agents/inbound-dispatcher/agent.md +66 -0
  13. package/agents/inbox-processor/agent.md +39 -0
  14. package/agents/pmo-execution/agent.md +60 -0
  15. package/agents/session-spawner/agent.md +64 -0
  16. package/agents/slack-operator/agent.md +60 -0
  17. package/agents/whatsapp-operator/agent.md +60 -0
  18. package/agents/workflow-automation/agent.md +61 -0
  19. package/archetypes/altitudes/c-suite.yaml +58 -0
  20. package/archetypes/altitudes/founder.yaml +68 -0
  21. package/archetypes/altitudes/senior-manager.yaml +63 -0
  22. package/archetypes/altitudes/svp.yaml +60 -0
  23. package/archetypes/altitudes/vp.yaml +50 -0
  24. package/archetypes/archetype.schema.json +77 -0
  25. package/archetypes/base.yaml +47 -0
  26. package/archetypes/capabilities/commercial-leader.yaml +159 -0
  27. package/archetypes/capabilities/compliance-officer.yaml +159 -0
  28. package/archetypes/capabilities/executive-operator.yaml +169 -0
  29. package/archetypes/capabilities/finance-leader.yaml +162 -0
  30. package/archetypes/capabilities/operations-leader.yaml +154 -0
  31. package/archetypes/capabilities/product-leader.yaml +148 -0
  32. package/archetypes/capabilities/technical-leader.yaml +146 -0
  33. package/archetypes/functions/commercial-leader.yaml +62 -0
  34. package/archetypes/functions/compliance-officer.yaml +64 -0
  35. package/archetypes/functions/executive-operator.yaml +70 -0
  36. package/archetypes/functions/finance-leader.yaml +70 -0
  37. package/archetypes/functions/operations-leader.yaml +62 -0
  38. package/archetypes/functions/product-leader.yaml +61 -0
  39. package/archetypes/functions/technical-leader.yaml +57 -0
  40. package/bin/cohort-mcp.mjs +81 -0
  41. package/bin/maestro.mjs +3516 -0
  42. package/bin/maestro.test.mjs +1015 -0
  43. package/desktop-control/README.md +56 -0
  44. package/desktop-control/app-profiles/gmail.yaml +120 -0
  45. package/desktop-control/app-profiles/slack.yaml +315 -0
  46. package/desktop-control/app-profiles/whatsapp.yaml +107 -0
  47. package/docs/architecture/agent-topology.md +2239 -0
  48. package/docs/architecture/archetype-agent-factory.md +110 -0
  49. package/docs/architecture/collective-memory-and-org-mesh.md +115 -0
  50. package/docs/architecture/continuous-monitoring.md +221 -0
  51. package/docs/architecture/mcp-capability-map.md +585 -0
  52. package/docs/architecture/system-architecture.md +1272 -0
  53. package/docs/company-context/README.md +40 -0
  54. package/docs/guides/agent-persona-setup.md +600 -0
  55. package/docs/guides/agents-observe-setup.md +64 -0
  56. package/docs/guides/billing-console-keys.md +88 -0
  57. package/docs/guides/ccxray-diagnostics.md +65 -0
  58. package/docs/guides/channel-bus.md +127 -0
  59. package/docs/guides/claude-mem-setup.md +79 -0
  60. package/docs/guides/claude-pace-setup.md +56 -0
  61. package/docs/guides/claudraband-sessions.md +98 -0
  62. package/docs/guides/clawteam-swarm.md +116 -0
  63. package/docs/guides/code-review-graph-setup.md +86 -0
  64. package/docs/guides/email-setup.md +431 -0
  65. package/docs/guides/mac-mini.md +119 -0
  66. package/docs/guides/media-generation-setup.md +349 -0
  67. package/docs/guides/model-routing.md +162 -0
  68. package/docs/guides/observability-otel.md +265 -0
  69. package/docs/guides/org-onboarding.md +132 -0
  70. package/docs/guides/outbound-governance-setup.md +437 -0
  71. package/docs/guides/pdf-generation-setup.md +315 -0
  72. package/docs/guides/poller-daemon-setup.md +563 -0
  73. package/docs/guides/rag-context-setup.md +459 -0
  74. package/docs/guides/self-optimization-pattern.md +82 -0
  75. package/docs/guides/setup-wizard.md +178 -0
  76. package/docs/guides/slack-setup.md +350 -0
  77. package/docs/guides/telegram-setup.md +227 -0
  78. package/docs/guides/twilio-subaccounts-setup.md +223 -0
  79. package/docs/guides/verification.md +128 -0
  80. package/docs/guides/voice-mode.md +188 -0
  81. package/docs/guides/voice-sms-setup.md +698 -0
  82. package/docs/guides/webhook-relay-setup.md +349 -0
  83. package/docs/guides/whatsapp-setup.md +288 -0
  84. package/docs/prompts/board-pack-cover-template.md +36 -0
  85. package/docs/prompts/decision-recommendation-template.md +88 -0
  86. package/docs/prompts/followup-message-template.md +141 -0
  87. package/docs/prompts/investor-letter-template.md +52 -0
  88. package/docs/prompts/morning-brief-template.md +82 -0
  89. package/docs/prompts/presentation-template.md +58 -0
  90. package/docs/prompts/weekly-strategic-memo-template.md +104 -0
  91. package/docs/research/hallucinated-tool-output-investigation.md +151 -0
  92. package/docs/runbooks/backup-restore.md +205 -0
  93. package/docs/runbooks/cohort-cutover.md +129 -0
  94. package/docs/runbooks/fleet-operations.md +200 -0
  95. package/docs/runbooks/incident-response.md +226 -0
  96. package/docs/runbooks/mac-mini-bootstrap.md +431 -0
  97. package/docs/runbooks/perpetual-operations.md +509 -0
  98. package/docs/runbooks/recovery-and-failover.md +260 -0
  99. package/framework-features.json +267 -0
  100. package/ingest/README.md +87 -0
  101. package/lib/action-executor.js +689 -0
  102. package/lib/action-executor.test.mjs +871 -0
  103. package/lib/agent-root.mjs +37 -0
  104. package/lib/archetype.mjs +236 -0
  105. package/lib/archetype.test.mjs +132 -0
  106. package/lib/autonomy.mjs +114 -0
  107. package/lib/autonomy.test.mjs +66 -0
  108. package/lib/backlog.mjs +358 -0
  109. package/lib/backlog.test.mjs +266 -0
  110. package/lib/budget-guard.mjs +279 -0
  111. package/lib/budget-guard.test.mjs +291 -0
  112. package/lib/cadence-bus-schedule.test.mjs +194 -0
  113. package/lib/cadence-bus.mjs +1120 -0
  114. package/lib/cadence-bus.test.mjs +720 -0
  115. package/lib/cadences.mjs +205 -0
  116. package/lib/cadences.test.mjs +125 -0
  117. package/lib/capability.mjs +154 -0
  118. package/lib/capability.test.mjs +78 -0
  119. package/lib/channels/base-adapter.mjs +719 -0
  120. package/lib/channels/base-adapter.test.mjs +590 -0
  121. package/lib/channels/channel.mjs +128 -0
  122. package/lib/channels/channels.test.mjs +371 -0
  123. package/lib/channels/contract.mjs +215 -0
  124. package/lib/channels/contract.test.mjs +137 -0
  125. package/lib/channels/conversation-resolver.mjs +95 -0
  126. package/lib/channels/gmail/adapter.mjs +87 -0
  127. package/lib/channels/inbox-item.mjs +255 -0
  128. package/lib/channels/inbox-item.test.mjs +335 -0
  129. package/lib/channels/index.mjs +94 -0
  130. package/lib/channels/orgmail/adapter.mjs +353 -0
  131. package/lib/channels/orgmail/adapter.test.mjs +311 -0
  132. package/lib/channels/pairing.mjs +363 -0
  133. package/lib/channels/pairing.test.mjs +270 -0
  134. package/lib/channels/registry.mjs +164 -0
  135. package/lib/channels/slack/adapter.mjs +317 -0
  136. package/lib/channels/slack-adapter.test.mjs +212 -0
  137. package/lib/channels/sms/adapter.mjs +43 -0
  138. package/lib/channels/telegram/adapter.mjs +432 -0
  139. package/lib/channels/telegram-adapter.test.mjs +306 -0
  140. package/lib/channels/voice/adapter.mjs +301 -0
  141. package/lib/channels/voice/adapter.test.mjs +278 -0
  142. package/lib/channels/whatsapp/adapter-baileys.mjs +587 -0
  143. package/lib/channels/whatsapp/adapter-baileys.test.mjs +359 -0
  144. package/lib/channels/whatsapp/adapter-twilio.mjs +65 -0
  145. package/lib/channels/whatsapp/baileys-typing.test.mjs +154 -0
  146. package/lib/charter.mjs +256 -0
  147. package/lib/charter.test.mjs +89 -0
  148. package/lib/claude-bin.mjs +134 -0
  149. package/lib/claude-bin.test.mjs +75 -0
  150. package/lib/collective/capture.mjs +185 -0
  151. package/lib/collective/capture.test.mjs +121 -0
  152. package/lib/collective/cards.mjs +201 -0
  153. package/lib/collective/cards.test.mjs +114 -0
  154. package/lib/collective/config.mjs +186 -0
  155. package/lib/collective/config.test.mjs +123 -0
  156. package/lib/collective/global-config.mjs +113 -0
  157. package/lib/collective/global-config.test.mjs +75 -0
  158. package/lib/collective/presence.mjs +201 -0
  159. package/lib/collective/presence.test.mjs +95 -0
  160. package/lib/collective/recall.mjs +215 -0
  161. package/lib/collective/recall.test.mjs +116 -0
  162. package/lib/comms/send-gate.mjs +554 -0
  163. package/lib/comms/send-gate.test.mjs +577 -0
  164. package/lib/comms.mjs +67 -0
  165. package/lib/comms.test.mjs +41 -0
  166. package/lib/diagnostics/alerts.mjs +424 -0
  167. package/lib/diagnostics/alerts.test.mjs +318 -0
  168. package/lib/diagnostics/backup-freshness.mjs +188 -0
  169. package/lib/diagnostics/backup-freshness.test.mjs +185 -0
  170. package/lib/diagnostics/counters.mjs +269 -0
  171. package/lib/diagnostics/counters.test.mjs +206 -0
  172. package/lib/diagnostics/events.mjs +188 -0
  173. package/lib/diagnostics/events.test.mjs +290 -0
  174. package/lib/diagnostics/otel.mjs +237 -0
  175. package/lib/diagnostics/otel.test.mjs +196 -0
  176. package/lib/diagnostics/trace.mjs +216 -0
  177. package/lib/diagnostics/trace.test.mjs +251 -0
  178. package/lib/env-compat.mjs +74 -0
  179. package/lib/env-compat.test.mjs +104 -0
  180. package/lib/feature-init.mjs +331 -0
  181. package/lib/fs-atomic.mjs +112 -0
  182. package/lib/fs-atomic.test.mjs +72 -0
  183. package/lib/fs-ownership.mjs +111 -0
  184. package/lib/fs-ownership.test.mjs +158 -0
  185. package/lib/hooks/bus.mjs +347 -0
  186. package/lib/hooks/bus.test.mjs +387 -0
  187. package/lib/index.js +16 -0
  188. package/lib/learning/config.mjs +106 -0
  189. package/lib/learning/config.test.mjs +75 -0
  190. package/lib/learning/counters.mjs +156 -0
  191. package/lib/learning/counters.test.mjs +69 -0
  192. package/lib/learning/curator-consolidate.test.mjs +238 -0
  193. package/lib/learning/curator.mjs +453 -0
  194. package/lib/learning/curator.test.mjs +106 -0
  195. package/lib/learning/log.mjs +40 -0
  196. package/lib/learning/reflect.mjs +534 -0
  197. package/lib/learning/reflect.test.mjs +0 -0
  198. package/lib/learning/session-index.mjs +352 -0
  199. package/lib/learning/session-index.test.mjs +125 -0
  200. package/lib/learning/skill-writer.mjs +474 -0
  201. package/lib/learning/skill-writer.test.mjs +210 -0
  202. package/lib/mcp/server.mjs +328 -0
  203. package/lib/mcp/server.test.mjs +400 -0
  204. package/lib/model-router/auth-profiles.mjs +758 -0
  205. package/lib/model-router/auth-profiles.test.mjs +580 -0
  206. package/lib/model-router/catalog/anthropic.yaml +153 -0
  207. package/lib/model-router/catalog/deepseek.yaml +86 -0
  208. package/lib/model-router/catalog/moonshot.yaml +81 -0
  209. package/lib/model-router/catalog/qwen.yaml +114 -0
  210. package/lib/model-router/catalog.mjs +925 -0
  211. package/lib/model-router/catalog.test.mjs +385 -0
  212. package/lib/model-router/economics.mjs +564 -0
  213. package/lib/model-router/economics.test.mjs +344 -0
  214. package/lib/model-router/failover.mjs +298 -0
  215. package/lib/model-router/failover.test.mjs +439 -0
  216. package/lib/model-router/health.mjs +453 -0
  217. package/lib/model-router/health.test.mjs +338 -0
  218. package/lib/model-router/integration-coverage.test.mjs +829 -0
  219. package/lib/model-router/integration.test.mjs +564 -0
  220. package/lib/model-router/ledger.mjs +402 -0
  221. package/lib/model-router/ledger.test.mjs +382 -0
  222. package/lib/model-router/llm-task.mjs +515 -0
  223. package/lib/model-router/llm-task.test.mjs +392 -0
  224. package/lib/model-router/org-credentials.mjs +260 -0
  225. package/lib/model-router/org-credentials.test.mjs +265 -0
  226. package/lib/model-router/pricing-refresh.mjs +463 -0
  227. package/lib/model-router/pricing-refresh.test.mjs +286 -0
  228. package/lib/model-router/reconcile.mjs +429 -0
  229. package/lib/model-router/reconcile.test.mjs +316 -0
  230. package/lib/model-router/repair.mjs +471 -0
  231. package/lib/model-router/repair.test.mjs +180 -0
  232. package/lib/model-router/resolve.mjs +1206 -0
  233. package/lib/model-router/spawn.mjs +497 -0
  234. package/lib/model-router/spawn.test.mjs +425 -0
  235. package/lib/model-router/taxonomy.mjs +893 -0
  236. package/lib/model-router/taxonomy.test.mjs +410 -0
  237. package/lib/model-router.mjs +677 -0
  238. package/lib/model-router.test.mjs +907 -0
  239. package/lib/org/activity.mjs +211 -0
  240. package/lib/org/activity.test.mjs +134 -0
  241. package/lib/org/approvals.mjs +448 -0
  242. package/lib/org/approvals.test.mjs +216 -0
  243. package/lib/org/awareness.mjs +222 -0
  244. package/lib/org/awareness.test.mjs +159 -0
  245. package/lib/org/board.mjs +229 -0
  246. package/lib/org/board.test.mjs +177 -0
  247. package/lib/org/bootstrap-context.mjs +169 -0
  248. package/lib/org/bootstrap-context.test.mjs +153 -0
  249. package/lib/org/client.mjs +1628 -0
  250. package/lib/org/client.test.mjs +1107 -0
  251. package/lib/org/cohort-client.mjs +67 -0
  252. package/lib/org/cohort-client.test.mjs +126 -0
  253. package/lib/org/cost-sync.mjs +227 -0
  254. package/lib/org/cost-sync.test.mjs +153 -0
  255. package/lib/org/doctor.mjs +212 -0
  256. package/lib/org/doctor.test.mjs +212 -0
  257. package/lib/org/handoff.mjs +293 -0
  258. package/lib/org/handoff.test.mjs +269 -0
  259. package/lib/org/integration-tools.mjs +182 -0
  260. package/lib/org/integration-tools.test.mjs +160 -0
  261. package/lib/org/keys.mjs +131 -0
  262. package/lib/org/keys.test.mjs +92 -0
  263. package/lib/org/knowledge.mjs +463 -0
  264. package/lib/org/knowledge.test.mjs +319 -0
  265. package/lib/org/leases.mjs +335 -0
  266. package/lib/org/leases.test.mjs +235 -0
  267. package/lib/org/mesh-integration.test.mjs +127 -0
  268. package/lib/org/mesh.mjs +459 -0
  269. package/lib/org/mesh.test.mjs +345 -0
  270. package/lib/org/messaging.mjs +503 -0
  271. package/lib/org/messaging.test.mjs +238 -0
  272. package/lib/org/policy.mjs +345 -0
  273. package/lib/org/policy.test.mjs +237 -0
  274. package/lib/org/protocol.checksum +1 -0
  275. package/lib/org/protocol.checksum.test.mjs +90 -0
  276. package/lib/org/protocol.mjs +967 -0
  277. package/lib/org/protocol.test.mjs +264 -0
  278. package/lib/org/registry.mjs +194 -0
  279. package/lib/org/registry.test.mjs +100 -0
  280. package/lib/org/tool-surface-integration.test.mjs +120 -0
  281. package/lib/org/tool-surface.mjs +2535 -0
  282. package/lib/org/tool-surface.test.mjs +589 -0
  283. package/lib/org/ui-parity.mjs +3236 -0
  284. package/lib/org/ui-parity.test.mjs +348 -0
  285. package/lib/org/verify.mjs +176 -0
  286. package/lib/org/verify.test.mjs +194 -0
  287. package/lib/rag/embed.mjs +188 -0
  288. package/lib/rag/indexer.mjs +425 -0
  289. package/lib/rag/rag.test.mjs +505 -0
  290. package/lib/rag/search.mjs +475 -0
  291. package/lib/rate-guard.mjs +246 -0
  292. package/lib/rate-guard.test.mjs +201 -0
  293. package/lib/render.mjs +112 -0
  294. package/lib/render.test.mjs +68 -0
  295. package/lib/resource-governor.mjs +297 -0
  296. package/lib/resource-governor.test.mjs +262 -0
  297. package/lib/scheduling/dynamic-jobs.mjs +675 -0
  298. package/lib/scheduling/dynamic-jobs.test.mjs +344 -0
  299. package/lib/scheduling/jitter.mjs +0 -0
  300. package/lib/scheduling/jitter.test.mjs +140 -0
  301. package/lib/secrets/broker.mjs +315 -0
  302. package/lib/secrets/broker.test.mjs +280 -0
  303. package/lib/secrets/providers.mjs +461 -0
  304. package/lib/secrets/providers.test.mjs +274 -0
  305. package/lib/security/audit-engine.mjs +684 -0
  306. package/lib/security/audit-engine.test.mjs +389 -0
  307. package/lib/security/coerce-args.mjs +552 -0
  308. package/lib/security/coerce-args.test.mjs +281 -0
  309. package/lib/security/dangerous-tools.mjs +97 -0
  310. package/lib/security/dangerous-tools.test.mjs +68 -0
  311. package/lib/security/external-content.mjs +145 -0
  312. package/lib/security/external-content.test.mjs +67 -0
  313. package/lib/security/redact.mjs +592 -0
  314. package/lib/security/redact.test.mjs +441 -0
  315. package/lib/security/secret-equal.mjs +73 -0
  316. package/lib/security/secret-equal.test.mjs +55 -0
  317. package/lib/session-permissions.mjs +101 -0
  318. package/lib/session-permissions.test.mjs +100 -0
  319. package/lib/setup/claude-probe.mjs +74 -0
  320. package/lib/setup/completeness.mjs +175 -0
  321. package/lib/setup/completeness.test.mjs +110 -0
  322. package/lib/setup/context-pack.mjs +173 -0
  323. package/lib/setup/context-pack.test.mjs +89 -0
  324. package/lib/setup/enrich.mjs +277 -0
  325. package/lib/setup/enrich.test.mjs +115 -0
  326. package/lib/setup/enroll-from-cohort.mjs +441 -0
  327. package/lib/setup/enroll-from-cohort.test.mjs +233 -0
  328. package/lib/setup/integration.test.mjs +162 -0
  329. package/lib/setup/io.mjs +360 -0
  330. package/lib/setup/io.test.mjs +77 -0
  331. package/lib/setup/run-generator.mjs +81 -0
  332. package/lib/setup/runner.mjs +244 -0
  333. package/lib/setup/runner.test.mjs +132 -0
  334. package/lib/setup/sections/comms.mjs +173 -0
  335. package/lib/setup/sections/company.mjs +120 -0
  336. package/lib/setup/sections/enrich.mjs +138 -0
  337. package/lib/setup/sections/identity.mjs +182 -0
  338. package/lib/setup/sections/identity.test.mjs +140 -0
  339. package/lib/setup/sections/learning.mjs +153 -0
  340. package/lib/setup/sections/learning.test.mjs +81 -0
  341. package/lib/setup/sections/messaging.mjs +219 -0
  342. package/lib/setup/sections/messaging.test.mjs +127 -0
  343. package/lib/setup/sections/model.mjs +102 -0
  344. package/lib/setup/sections/operating-model.mjs +78 -0
  345. package/lib/setup/sections/org.mjs +475 -0
  346. package/lib/setup/sections/org.test.mjs +313 -0
  347. package/lib/setup/sections/orgmail.mjs +173 -0
  348. package/lib/setup/sections/orgmail.test.mjs +118 -0
  349. package/lib/setup/sections/recovery.mjs +159 -0
  350. package/lib/setup/sections/recovery.test.mjs +98 -0
  351. package/lib/setup/sections/tools.mjs +132 -0
  352. package/lib/setup/sections/verify.mjs +97 -0
  353. package/lib/setup/sot.mjs +205 -0
  354. package/lib/setup/sot.test.mjs +81 -0
  355. package/lib/setup/state.mjs +151 -0
  356. package/lib/setup/state.test.mjs +92 -0
  357. package/lib/singleton.js +229 -0
  358. package/lib/singleton.test.mjs +135 -0
  359. package/lib/telemetry/alerts.mjs +216 -0
  360. package/lib/telemetry/alerts.test.mjs +109 -0
  361. package/lib/telemetry/collect.mjs +512 -0
  362. package/lib/telemetry/collect.test.mjs +202 -0
  363. package/lib/tool-definitions-integration.test.mjs +83 -0
  364. package/lib/tool-definitions.js +738 -0
  365. package/lib/tool-definitions.test.mjs +437 -0
  366. package/lib/util/fetch-timeout.mjs +136 -0
  367. package/lib/util/fetch-timeout.test.mjs +202 -0
  368. package/lib/util/reconnect.mjs +343 -0
  369. package/lib/util/reconnect.test.mjs +369 -0
  370. package/lib/util/unhandled.mjs +205 -0
  371. package/lib/util/unhandled.test.mjs +216 -0
  372. package/lib/voice/context-loader.mjs +466 -0
  373. package/lib/voice/index.mjs +100 -0
  374. package/lib/voice/openai-realtime.mjs +510 -0
  375. package/lib/voice/outbound.mjs +542 -0
  376. package/lib/voice/outbound.test.mjs +69 -0
  377. package/lib/voice/post-call-brief.mjs +428 -0
  378. package/lib/voice/provider.mjs +52 -0
  379. package/lib/voice/session-rotation.mjs +257 -0
  380. package/lib/voice/stt.mjs +161 -0
  381. package/lib/voice/stt.test.mjs +226 -0
  382. package/lib/voice/tool-bridge.mjs +370 -0
  383. package/lib/voice/tts.mjs +104 -0
  384. package/lib/voice/twilio-sip-bridge.mjs +288 -0
  385. package/lib/voice/voice.test.mjs +990 -0
  386. package/mcp/README.md +80 -0
  387. package/package.json +151 -0
  388. package/plugins/maestro-skills/plugin.json +139 -0
  389. package/plugins/maestro-skills/skills/agents-observe.md +110 -0
  390. package/plugins/maestro-skills/skills/board-deck.md +68 -0
  391. package/plugins/maestro-skills/skills/books-close.md +77 -0
  392. package/plugins/maestro-skills/skills/brand-steward.md +121 -0
  393. package/plugins/maestro-skills/skills/calendar-plan.md +57 -0
  394. package/plugins/maestro-skills/skills/call-working-sessions.md +124 -0
  395. package/plugins/maestro-skills/skills/ccxray-diagnostics.md +91 -0
  396. package/plugins/maestro-skills/skills/claude-pace.md +61 -0
  397. package/plugins/maestro-skills/skills/code-review-graph.md +99 -0
  398. package/plugins/maestro-skills/skills/crm-pipeline.md +65 -0
  399. package/plugins/maestro-skills/skills/decision-brief.md +89 -0
  400. package/plugins/maestro-skills/skills/directory-hygiene.md +125 -0
  401. package/plugins/maestro-skills/skills/draft-comms.md +84 -0
  402. package/plugins/maestro-skills/skills/evening-wrap.md +53 -0
  403. package/plugins/maestro-skills/skills/files-find.md +65 -0
  404. package/plugins/maestro-skills/skills/generative-ui.md +228 -0
  405. package/plugins/maestro-skills/skills/hiring-triage.md +74 -0
  406. package/plugins/maestro-skills/skills/inbox-triage.md +61 -0
  407. package/plugins/maestro-skills/skills/mail-triage.md +86 -0
  408. package/plugins/maestro-skills/skills/morning-brief.md +54 -0
  409. package/plugins/maestro-skills/skills/native-artifacts.md +157 -0
  410. package/plugins/maestro-skills/skills/org-board.md +133 -0
  411. package/plugins/maestro-skills/skills/org-credential.md +68 -0
  412. package/plugins/maestro-skills/skills/org-recall.md +81 -0
  413. package/plugins/maestro-skills/skills/pipeline-review.md +76 -0
  414. package/plugins/maestro-skills/skills/regulatory-status.md +81 -0
  415. package/plugins/maestro-skills/skills/router-why.md +78 -0
  416. package/plugins/maestro-skills/skills/schedule-meeting.md +91 -0
  417. package/plugins/maestro-skills/skills/session-search.md +71 -0
  418. package/plugins/maestro-skills/skills/set-reminder.md +93 -0
  419. package/plugins/maestro-skills/skills/slack-followup.md +64 -0
  420. package/plugins/maestro-skills/skills/team-activity.md +86 -0
  421. package/plugins/maestro-skills/skills/weekly-memo.md +70 -0
  422. package/policies/action-classification.yaml +114 -0
  423. package/policies/ai-disclosure.yaml +294 -0
  424. package/policies/communication-style.md +139 -0
  425. package/policies/information-barriers.yaml +118 -0
  426. package/policies/prompt-injection-defence.yaml +138 -0
  427. package/public/assets/icon-dark.png +0 -0
  428. package/public/assets/icon-dark.svg +9 -0
  429. package/public/assets/icon-light.svg +9 -0
  430. package/public/assets/logo-dark.svg +15 -0
  431. package/public/assets/logo-light.svg +15 -0
  432. package/scaffold/.mcp.json +7 -0
  433. package/scaffold/CLAUDE.md +368 -0
  434. package/scaffold/config/agent.json +55 -0
  435. package/scaffold/config/agent.ts +76 -0
  436. package/scaffold/config/agent.ts.example +89 -0
  437. package/scaffold/config/alerts.yaml +23 -0
  438. package/scaffold/config/allowlist.yaml.example +25 -0
  439. package/scaffold/config/caller-id-map.yaml +46 -0
  440. package/scaffold/config/collective.yaml +49 -0
  441. package/scaffold/config/company.json +20 -0
  442. package/scaffold/config/known-agents.json +6 -0
  443. package/scaffold/config/learning.yaml +55 -0
  444. package/scaffold/config/model-routing.yaml.example +104 -0
  445. package/scaffold/config/org.yaml +25 -0
  446. package/scaffold/config/orgmail.yaml.example +19 -0
  447. package/scaffold/config/recovery.yaml +72 -0
  448. package/scaffold/config/secrets.yaml +27 -0
  449. package/scaffold/config/slack.yaml.example +35 -0
  450. package/scaffold/config/telegram.yaml.example +38 -0
  451. package/scaffold/config/voice.yaml.example +89 -0
  452. package/scaffold/config/whatsapp.yaml.example +39 -0
  453. package/schedules/README.md +49 -0
  454. package/schedules/triggers/backlog-executor.md +102 -0
  455. package/schedules/triggers/brand-steward.md +72 -0
  456. package/schedules/triggers/daily-evening-wrap.md +159 -0
  457. package/schedules/triggers/daily-midday-sweep.md +58 -0
  458. package/schedules/triggers/daily-morning-brief.md +55 -0
  459. package/schedules/triggers/directory-hygiene.md +81 -0
  460. package/schedules/triggers/dynamic-jobs.md +40 -0
  461. package/schedules/triggers/inbox-processor.md +115 -0
  462. package/schedules/triggers/meeting-action-capture.md +60 -0
  463. package/schedules/triggers/meeting-prep.md +69 -0
  464. package/schedules/triggers/messaging-inbound.md +50 -0
  465. package/schedules/triggers/org-pulse.md +24 -0
  466. package/schedules/triggers/quarterly-self-assessment.md +54 -0
  467. package/schedules/triggers/weekly-engineering-health.md +37 -0
  468. package/schedules/triggers/weekly-execution.md +65 -0
  469. package/schedules/triggers/weekly-hiring.md +53 -0
  470. package/schedules/triggers/weekly-priorities.md +38 -0
  471. package/schedules/triggers/weekly-strategic-memo.md +124 -0
  472. package/scripts/archive-email.sh +55 -0
  473. package/scripts/cadence/cadence-status.mjs +36 -0
  474. package/scripts/cadence/enqueue-cadence-tick.mjs +174 -0
  475. package/scripts/cadence/enqueue-cadence-tick.test.mjs +187 -0
  476. package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
  477. package/scripts/cadence/launchd-cloud-relay-wrapper.sh +95 -0
  478. package/scripts/cadence/launchd-socket-mode-wrapper.sh +95 -0
  479. package/scripts/ci/check-docs-accuracy.mjs +493 -0
  480. package/scripts/ci/check-docs-accuracy.test.mjs +409 -0
  481. package/scripts/ci/check-exports-exist.mjs +140 -0
  482. package/scripts/ci/check-files-exist.mjs +107 -0
  483. package/scripts/ci/check-no-build-artifacts.mjs +111 -0
  484. package/scripts/ci/check-no-build-artifacts.test.mjs +71 -0
  485. package/scripts/ci/check-no-confidential.mjs +198 -0
  486. package/scripts/ci/check-no-conflict-markers.mjs +169 -0
  487. package/scripts/ci/check-no-residual-identity.mjs +163 -0
  488. package/scripts/ci/check-no-residual-identity.test.mjs +89 -0
  489. package/scripts/ci/check-tarball-fidelity.mjs +205 -0
  490. package/scripts/ci/check-unresolved-tokens.mjs +83 -0
  491. package/scripts/ci/check.mjs +109 -0
  492. package/scripts/ci/check.test.mjs +194 -0
  493. package/scripts/ci/run-coverage.mjs +82 -0
  494. package/scripts/ci/run-tests.mjs +71 -0
  495. package/scripts/cloud-relay/README.md +59 -0
  496. package/scripts/cloud-relay/index.mjs +233 -0
  497. package/scripts/cloud-relay/package.json +15 -0
  498. package/scripts/cloud-relay/railway.json +13 -0
  499. package/scripts/cloud-relay/voice/README.md +94 -0
  500. package/scripts/cloud-relay/voice/package-lock.json +39 -0
  501. package/scripts/cloud-relay/voice/package.json +16 -0
  502. package/scripts/cloud-relay/voice/railway.json +13 -0
  503. package/scripts/cloud-relay/voice/server.mjs +532 -0
  504. package/scripts/collective/hook-runner.mjs +211 -0
  505. package/scripts/collective/hook-runner.test.mjs +90 -0
  506. package/scripts/collective/org-pulse.mjs +72 -0
  507. package/scripts/collective/org-sync.mjs +61 -0
  508. package/scripts/collective/recall.mjs +45 -0
  509. package/scripts/collective/who.mjs +30 -0
  510. package/scripts/comms-monitor.sh +288 -0
  511. package/scripts/configure-whatsapp-sandbox.sh +201 -0
  512. package/scripts/continuous-monitor.sh +91 -0
  513. package/scripts/cost/fleet-digest.mjs +407 -0
  514. package/scripts/cost/fleet-digest.test.mjs +207 -0
  515. package/scripts/cost/track-claude-usage.mjs +169 -0
  516. package/scripts/daemon/agent-daemon.mjs +989 -0
  517. package/scripts/daemon/agent-daemon.test.mjs +525 -0
  518. package/scripts/daemon/cadence-consumer-governance.test.mjs +220 -0
  519. package/scripts/daemon/cadence-consumer.mjs +1080 -0
  520. package/scripts/daemon/cadence-consumer.test.mjs +770 -0
  521. package/scripts/daemon/cadence-handlers.mjs +1121 -0
  522. package/scripts/daemon/cadence-handlers.test.mjs +617 -0
  523. package/scripts/daemon/classifier.mjs +704 -0
  524. package/scripts/daemon/classifier.test.mjs +238 -0
  525. package/scripts/daemon/classify-kind.mjs +54 -0
  526. package/scripts/daemon/classify-kind.test.mjs +40 -0
  527. package/scripts/daemon/context-compiler.mjs +605 -0
  528. package/scripts/daemon/context-compiler.test.mjs +300 -0
  529. package/scripts/daemon/dispatcher-cooldown.test.mjs +122 -0
  530. package/scripts/daemon/dispatcher-governance.test.mjs +886 -0
  531. package/scripts/daemon/dispatcher.mjs +1516 -0
  532. package/scripts/daemon/health.mjs +72 -0
  533. package/scripts/daemon/inbox-deferral.mjs +210 -0
  534. package/scripts/daemon/inbox-deferral.test.mjs +242 -0
  535. package/scripts/daemon/integration.test.mjs +149 -0
  536. package/scripts/daemon/launchd-wrapper-generic.sh +96 -0
  537. package/scripts/daemon/launchd-wrapper-slack-events.sh +37 -0
  538. package/scripts/daemon/launchd-wrapper.sh +91 -0
  539. package/scripts/daemon/lib/session-router.mjs +274 -0
  540. package/scripts/daemon/lib/session-router.test.mjs +295 -0
  541. package/scripts/daemon/maestro-daemon.mjs +275 -0
  542. package/scripts/daemon/prompt-builder.mjs +685 -0
  543. package/scripts/daemon/prompt-builder.test.mjs +213 -0
  544. package/scripts/daemon/responder.mjs +854 -0
  545. package/scripts/daemon/session-lock.mjs +721 -0
  546. package/scripts/daemon/session-lock.test.mjs +252 -0
  547. package/scripts/daemon/session-outcomes.mjs +640 -0
  548. package/scripts/daemon/session-outcomes.test.mjs +533 -0
  549. package/scripts/daemon/typing-registry.mjs +90 -0
  550. package/scripts/daemon/typing-registry.test.mjs +77 -0
  551. package/scripts/daemon/voice-webhook-server.mjs +804 -0
  552. package/scripts/decisions/capture-decision.mjs +116 -0
  553. package/scripts/disclosure_assessment.py +873 -0
  554. package/scripts/disclosure_boundaries.py +562 -0
  555. package/scripts/email-signature-principal.html +52 -0
  556. package/scripts/email-signature.html +60 -0
  557. package/scripts/email_quote_thread.py +167 -0
  558. package/scripts/email_thread_dedup.py +362 -0
  559. package/scripts/emergency-stop.sh +81 -0
  560. package/scripts/healthcheck.sh +116 -0
  561. package/scripts/hooks/block-mcp-cohort-send.sh +15 -0
  562. package/scripts/hooks/block-mcp-slack-send.sh +7 -0
  563. package/scripts/hooks/post-action-log.sh +126 -0
  564. package/scripts/hooks/pre-send-audit.sh +174 -0
  565. package/scripts/hooks/pre-send-audit.test.mjs +215 -0
  566. package/scripts/hooks/session-end-log.sh +27 -0
  567. package/scripts/hooks/session-start-banner.sh +115 -0
  568. package/scripts/huddle/audio-bridge.mjs +664 -0
  569. package/scripts/huddle/boot-slack-cdp.sh +102 -0
  570. package/scripts/huddle/huddle-controller.mjs +942 -0
  571. package/scripts/huddle/huddle-server.mjs +1229 -0
  572. package/scripts/huddle/launch-slack.sh +232 -0
  573. package/scripts/huddle/openai-realtime-bridge.mjs +462 -0
  574. package/scripts/huddle/package-lock.json +62 -0
  575. package/scripts/huddle/package.json +22 -0
  576. package/scripts/huddle/setup-audio.sh +239 -0
  577. package/scripts/huddle/start-call.mjs +318 -0
  578. package/scripts/huddle/test-pipeline.mjs +263 -0
  579. package/scripts/learning/consolidate-skills.mjs +72 -0
  580. package/scripts/learning/session-search.mjs +125 -0
  581. package/scripts/llm_email_dedup.py +442 -0
  582. package/scripts/local-triggers/generate-plists.sh +432 -0
  583. package/scripts/local-triggers/generate-plists.test.mjs +413 -0
  584. package/scripts/local-triggers/install-all.sh +49 -0
  585. package/scripts/local-triggers/plists/.gitkeep +0 -0
  586. package/scripts/local-triggers/run-trigger.sh +63 -0
  587. package/scripts/local-triggers/templates/rag-reindex.plist.template +47 -0
  588. package/scripts/local-triggers/templates/voice-relay-poller.plist.template +54 -0
  589. package/scripts/local-triggers/templates/voice-tunnel.plist.template +55 -0
  590. package/scripts/local-triggers/templates/voice-webhook.plist.template +51 -0
  591. package/scripts/maintenance/backup-to-cloud.sh +124 -0
  592. package/scripts/maintenance/health-check.sh +377 -0
  593. package/scripts/media-generation/README.md +105 -0
  594. package/scripts/media-generation/gemini-image-client.mjs +173 -0
  595. package/scripts/media-generation/generate-assets.mjs +289 -0
  596. package/scripts/media-generation/veo-video-client.mjs +219 -0
  597. package/scripts/org/send-orgmail.mjs +227 -0
  598. package/scripts/outbound-dedup-cleanup.sh +43 -0
  599. package/scripts/outbound-dedup.sh +477 -0
  600. package/scripts/outbound_dedup.py +115 -0
  601. package/scripts/parse-voice-transcript.mjs +481 -0
  602. package/scripts/pdf-generation/README.md +63 -0
  603. package/scripts/pdf-generation/build-document.mjs +247 -0
  604. package/scripts/pdf-generation/templates/board-pack.latex +136 -0
  605. package/scripts/pdf-generation/templates/corporate-letter.latex +126 -0
  606. package/scripts/pdf-generation/templates/memo.latex +114 -0
  607. package/scripts/poll-slack-events.sh +35 -0
  608. package/scripts/poller/calendar-poller.mjs +12 -0
  609. package/scripts/poller/gmail-poller.mjs +192 -0
  610. package/scripts/poller/imap-client.mjs +289 -0
  611. package/scripts/poller/inbox-scan-poller.mjs +156 -0
  612. package/scripts/poller/inbox-scan-poller.test.mjs +231 -0
  613. package/scripts/poller/index.mjs +73 -0
  614. package/scripts/poller/intra-session-check.mjs +285 -0
  615. package/scripts/poller/lib/cloud-relay-dedup.mjs +88 -0
  616. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +133 -0
  617. package/scripts/poller/lib/slash-command-handlers.mjs +177 -0
  618. package/scripts/poller/secondary-gmail-poller.mjs +132 -0
  619. package/scripts/poller/slack-cloud-relay-client.mjs +368 -0
  620. package/scripts/poller/slack-poller.mjs +854 -0
  621. package/scripts/poller/slack-socket-mode.mjs +917 -0
  622. package/scripts/poller/slack-socket-mode.test.mjs +753 -0
  623. package/scripts/poller/trigger.mjs +75 -0
  624. package/scripts/poller/utils.mjs +371 -0
  625. package/scripts/poller/voice-cloud-relay-client.mjs +179 -0
  626. package/scripts/poller/voice-poller.mjs +236 -0
  627. package/scripts/poller-launchd/install.sh +66 -0
  628. package/scripts/poller-launchd/poller.plist.template +40 -0
  629. package/scripts/poller-launchd/whatsapp-handler.plist.template +39 -0
  630. package/scripts/post-interaction-indexer.py +1598 -0
  631. package/scripts/pre-draft-context.py +994 -0
  632. package/scripts/pre_draft_lookup.py +258 -0
  633. package/scripts/rag/build-index.mjs +47 -0
  634. package/scripts/rag/ingest.mjs +111 -0
  635. package/scripts/rag/search.mjs +119 -0
  636. package/scripts/rag-indexer.py +629 -0
  637. package/scripts/restore-from-backup.sh +248 -0
  638. package/scripts/restore-from-backup.test.mjs +178 -0
  639. package/scripts/resume-operations.sh +80 -0
  640. package/scripts/search-secondary-inbox.py +181 -0
  641. package/scripts/secondary-inbox-poller.py +437 -0
  642. package/scripts/self-optimization/compute-metrics.py +398 -0
  643. package/scripts/send-email-as-principal.py +369 -0
  644. package/scripts/send-email-threaded.py +392 -0
  645. package/scripts/send-email-with-attachment.py +377 -0
  646. package/scripts/send-email.sh +131 -0
  647. package/scripts/send-sms.sh +175 -0
  648. package/scripts/send-whatsapp.sh +292 -0
  649. package/scripts/session-start.sh +106 -0
  650. package/scripts/setup/boot-claude-session.sh +94 -0
  651. package/scripts/setup/configure-macos.sh +674 -0
  652. package/scripts/setup/configure-twilio-sip-trunk.mjs +207 -0
  653. package/scripts/setup/configure-voice-tunnel.mjs +182 -0
  654. package/scripts/setup/generate-agent-env.mjs +92 -0
  655. package/scripts/setup/generate-agent-package-json.mjs +222 -0
  656. package/scripts/setup/generate-agent-package-json.test.mjs +143 -0
  657. package/scripts/setup/generate-autonomy.mjs +60 -0
  658. package/scripts/setup/generate-backlog.mjs +101 -0
  659. package/scripts/setup/generate-cadences.mjs +92 -0
  660. package/scripts/setup/generate-capability.mjs +162 -0
  661. package/scripts/setup/generate-charter.mjs +76 -0
  662. package/scripts/setup/generate-comms.mjs +59 -0
  663. package/scripts/setup/generate-company.mjs +92 -0
  664. package/scripts/setup/init-agent.sh +547 -0
  665. package/scripts/setup/init-agent.test.mjs +151 -0
  666. package/scripts/setup/init-archetype.mjs +74 -0
  667. package/scripts/setup/init-backup.mjs +54 -0
  668. package/scripts/setup/init-cadence-bus.mjs +60 -0
  669. package/scripts/setup/init-channel-bus.mjs +46 -0
  670. package/scripts/setup/init-cost-tracking.mjs +45 -0
  671. package/scripts/setup/init-decision-capture.mjs +66 -0
  672. package/scripts/setup/init-known-agents.mjs +57 -0
  673. package/scripts/setup/init-learning.mjs +70 -0
  674. package/scripts/setup/init-memory-executive.mjs +45 -0
  675. package/scripts/setup/init-model-router.mjs +124 -0
  676. package/scripts/setup/init-rag.mjs +174 -0
  677. package/scripts/setup/init-session-router.mjs +38 -0
  678. package/scripts/setup/init-slack-socket-mode.mjs +260 -0
  679. package/scripts/setup/init-telegram.mjs +165 -0
  680. package/scripts/setup/init-voice-realtime.mjs +204 -0
  681. package/scripts/setup/init-whatsapp-baileys.mjs +77 -0
  682. package/scripts/setup/install-dev-tools.sh +150 -0
  683. package/scripts/setup/lib/install-plist.mjs +95 -0
  684. package/scripts/setup/migrate-agent-to-sot.mjs +192 -0
  685. package/scripts/setup/render-environment-yaml.mjs +133 -0
  686. package/scripts/slack-events-ctl.sh +177 -0
  687. package/scripts/slack-events-server.mjs +1045 -0
  688. package/scripts/slack-react.mjs +89 -0
  689. package/scripts/slack-responded.sh +232 -0
  690. package/scripts/slack-send.sh +287 -0
  691. package/scripts/slack-typing.mjs +196 -0
  692. package/scripts/slack-upload-v2.py +95 -0
  693. package/scripts/sms-handler.mjs +450 -0
  694. package/scripts/spawn-session.sh +120 -0
  695. package/scripts/sync-protocol.mjs +217 -0
  696. package/scripts/system-verify.sh +184 -0
  697. package/scripts/test-email-thread-dedup.py +239 -0
  698. package/scripts/test-information-barriers.py +484 -0
  699. package/scripts/test-llm-email-dedup.py +251 -0
  700. package/scripts/test-pre-draft-integration.py +203 -0
  701. package/scripts/test-rag-phase2.sh +442 -0
  702. package/scripts/test-rag-search.sh +251 -0
  703. package/scripts/test-voice-parser.mjs +316 -0
  704. package/scripts/user-context-search.py +659 -0
  705. package/scripts/validate_outbound.py +1504 -0
  706. package/scripts/watchdog/ai.maestro.memory-watchdog.plist +41 -0
  707. package/scripts/watchdog/force-reboot.sh +157 -0
  708. package/scripts/watchdog/memory-watchdog.sh +473 -0
  709. package/scripts/whatsapp-handler.mjs +538 -0
  710. package/teams/desktop-operations.yaml +34 -0
  711. package/teams/executive-office.yaml +27 -0
  712. package/teams/legal-and-regulatory.yaml +24 -0
  713. package/teams/platform-and-engineering.yaml +23 -0
  714. package/teams/strategy-and-growth.yaml +29 -0
  715. package/workflows/continuous/backlog-executor.yaml +141 -0
  716. package/workflows/continuous/inbound-monitor.yaml +168 -0
  717. package/workflows/daily/applicant-triage.yaml +197 -0
  718. package/workflows/daily/comms-triage.yaml +80 -0
  719. package/workflows/daily/evening-wrap.yaml +105 -0
  720. package/workflows/daily/morning-brief.yaml +164 -0
  721. package/workflows/daily/slack-followup-sweep.yaml +87 -0
  722. package/workflows/event-driven/README.md +50 -0
  723. package/workflows/event-driven/agent-failure-investigation.yaml +137 -0
  724. package/workflows/event-driven/pr-review.yaml +107 -0
  725. package/workflows/monthly/board-readiness.yaml +76 -0
  726. package/workflows/quarterly/strategic-scenario-analysis.yaml +85 -0
  727. package/workflows/session-protocol.md +171 -0
  728. package/workflows/weekly/engineering-health.yaml +154 -0
  729. package/workflows/weekly/hiring-review.yaml +169 -0
  730. package/workflows/weekly/rollup-pipeline-review.yaml +76 -0
  731. package/workflows/weekly/strategic-memo.yaml +79 -0
@@ -0,0 +1,664 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Maestro Huddle — Audio Bridge
4
+ *
5
+ * Bidirectional audio pipeline between Slack huddles and the agent's voice AI:
6
+ *
7
+ * INBOUND (what others say):
8
+ * BlackHole 2ch → sox capture → PCM stream → Deepgram STT → transcript
9
+ *
10
+ * OUTBOUND (what the agent says):
11
+ * Response text → ElevenLabs TTS → MP3/PCM → sox playback → BlackHole 16ch
12
+ *
13
+ * The two paths use separate virtual audio devices to prevent feedback loops.
14
+ *
15
+ * Dependencies:
16
+ * - sox (brew install sox) — audio capture and playback
17
+ * - BlackHole 2ch — Slack speaker output (capture device)
18
+ * - BlackHole 16ch — Slack microphone input (playback device)
19
+ * - Deepgram API key (real-time STT)
20
+ * - ElevenLabs API key (TTS)
21
+ */
22
+
23
+ import { EventEmitter } from "node:events";
24
+ import { spawn, execFileSync } from "node:child_process";
25
+ import { WebSocket } from "ws";
26
+ import { WebSocketServer } from "ws";
27
+ import { writeFile, unlink } from "node:fs/promises";
28
+ import { tmpdir } from "node:os";
29
+ import { join } from "node:path";
30
+ import { createServer } from "node:http";
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Configuration
34
+ // ---------------------------------------------------------------------------
35
+
36
+ const CAPTURE_DEVICE = process.env.HUDDLE_CAPTURE_DEVICE || "BlackHole 2ch";
37
+ const PLAYBACK_DEVICE = process.env.HUDDLE_PLAYBACK_DEVICE || "BlackHole 16ch";
38
+
39
+ // Capture mode: "webaudio" bypasses BlackHole for inbound audio (fixes echo),
40
+ // "sox" uses the original BlackHole 2ch pipeline.
41
+ const CAPTURE_MODE = process.env.HUDDLE_CAPTURE_MODE || "webaudio";
42
+ const CAPTURE_WS_PORT = parseInt(process.env.HUDDLE_CAPTURE_WS_PORT || "3201", 10);
43
+
44
+ const DEEPGRAM_API_KEY = process.env.DEEPGRAM_API_KEY || "";
45
+ const DEEPGRAM_WS_URL = "wss://api.deepgram.com/v1/listen";
46
+ const DEEPGRAM_PARAMS = new URLSearchParams({
47
+ model: "nova-2",
48
+ language: "en",
49
+ smart_format: "true",
50
+ punctuate: "true",
51
+ interim_results: "true",
52
+ utterance_end_ms: "1500", // 1.5s silence = utterance boundary
53
+ vad_events: "true", // Voice activity detection
54
+ endpointing: "800", // 800ms endpoint detection
55
+ encoding: "linear16",
56
+ sample_rate: "16000",
57
+ channels: "1",
58
+ });
59
+
60
+ const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY || "";
61
+ const ELEVENLABS_VOICE_ID = process.env.ELEVENLABS_VOICE_ID || "EXAVITQu4vr4xnSDxMaL"; // Sarah
62
+ const ELEVENLABS_MODEL = process.env.ELEVENLABS_MODEL || "eleven_flash_v2_5";
63
+ const ELEVENLABS_API_URL = "https://api.elevenlabs.io/v1/text-to-speech";
64
+
65
+ const SAMPLE_RATE = 16000;
66
+ const CHANNELS = 1;
67
+ const BIT_DEPTH = 16;
68
+
69
+ // Silence detection: how many ms of silence before we consider speech done
70
+ const SPEECH_TIMEOUT_MS = 1500;
71
+
72
+ /**
73
+ * AudioBridge manages bidirectional audio for huddle voice participation.
74
+ *
75
+ * Events:
76
+ * - 'transcript' — Final transcript ready { text, isFinal }
77
+ * - 'transcript:interim' — Interim transcript { text }
78
+ * - 'speech:start' — Voice activity detected (someone started talking)
79
+ * - 'speech:end' — Voice activity ended (silence detected)
80
+ * - 'tts:start' — TTS playback started { text }
81
+ * - 'tts:end' — TTS playback finished
82
+ * - 'error' — Error occurred { component, message }
83
+ * - 'capture:started' — Audio capture started
84
+ * - 'capture:stopped' — Audio capture stopped
85
+ */
86
+ export class AudioBridge extends EventEmitter {
87
+ constructor(options = {}) {
88
+ super();
89
+ this.captureDevice = options.captureDevice || CAPTURE_DEVICE;
90
+ this.playbackDevice = options.playbackDevice || PLAYBACK_DEVICE;
91
+ this.deepgramKey = options.deepgramKey || DEEPGRAM_API_KEY;
92
+ this.elevenLabsKey = options.elevenLabsKey || ELEVENLABS_API_KEY;
93
+ this.voiceId = options.voiceId || ELEVENLABS_VOICE_ID;
94
+
95
+ // State
96
+ this.captureProcess = null;
97
+ this.deepgramWs = null;
98
+ this.isCapturing = false;
99
+ this.isSpeaking = false; // the agent is currently speaking (TTS playing)
100
+ this.speechTimer = null;
101
+ this.currentUtterance = ""; // Accumulated speech from current speaker
102
+ this.ttsQueue = []; // Queue of texts waiting to be spoken
103
+ this.ttsProcessing = false;
104
+ this.captureMode = options.captureMode || CAPTURE_MODE;
105
+ this.captureWsServer = null; // WebSocket server for Web Audio capture
106
+ this.captureWsClient = null; // Connected Web Audio client
107
+ }
108
+
109
+ // -------------------------------------------------------------------------
110
+ // Lifecycle
111
+ // -------------------------------------------------------------------------
112
+
113
+ /**
114
+ * Start the audio bridge — begin capturing from BlackHole and streaming
115
+ * to Deepgram for real-time transcription.
116
+ */
117
+ async start() {
118
+ if (this.isCapturing) return;
119
+
120
+ if (!this.deepgramKey) {
121
+ throw new Error("DEEPGRAM_API_KEY required for real-time STT");
122
+ }
123
+ if (!this.elevenLabsKey) {
124
+ throw new Error("ELEVENLABS_API_KEY required for TTS");
125
+ }
126
+
127
+ // Start Deepgram WebSocket first
128
+ await this._connectDeepgram();
129
+
130
+ // Start audio capture
131
+ if (this.captureMode === "webaudio") {
132
+ await this._startWebAudioCapture();
133
+ } else {
134
+ this._startCapture();
135
+ }
136
+
137
+ this.isCapturing = true;
138
+ this.emit("capture:started");
139
+ }
140
+
141
+ /**
142
+ * Stop the audio bridge — kill capture process and close Deepgram connection.
143
+ */
144
+ async stop() {
145
+ this.isCapturing = false;
146
+
147
+ // Stop capture
148
+ if (this.captureProcess) {
149
+ this.captureProcess.kill("SIGTERM");
150
+ this.captureProcess = null;
151
+ }
152
+
153
+ // Stop Web Audio capture server
154
+ if (this.captureWsServer) {
155
+ if (this.captureWsClient) {
156
+ this.captureWsClient.close();
157
+ this.captureWsClient = null;
158
+ }
159
+ this.captureWsServer.close();
160
+ this.captureWsServer = null;
161
+ }
162
+
163
+ // Close Deepgram
164
+ if (this.deepgramWs) {
165
+ // Send close frame to flush final transcript
166
+ try {
167
+ this.deepgramWs.send(JSON.stringify({ type: "CloseStream" }));
168
+ } catch { /* ignore */ }
169
+ this.deepgramWs.close();
170
+ this.deepgramWs = null;
171
+ }
172
+
173
+ if (this.speechTimer) {
174
+ clearTimeout(this.speechTimer);
175
+ this.speechTimer = null;
176
+ }
177
+
178
+ this.emit("capture:stopped");
179
+ }
180
+
181
+ // -------------------------------------------------------------------------
182
+ // Inbound: Audio Capture → Deepgram STT
183
+ // -------------------------------------------------------------------------
184
+
185
+ _startCapture() {
186
+ // Use sox to capture from BlackHole 2ch and output raw PCM to stdout
187
+ this.captureProcess = spawn("sox", [
188
+ "-t", "coreaudio", this.captureDevice, // Input: BlackHole 2ch
189
+ "-t", "raw", // Output: raw PCM
190
+ "-r", String(SAMPLE_RATE), // 16kHz
191
+ "-e", "signed-integer", // Signed int
192
+ "-b", String(BIT_DEPTH), // 16-bit
193
+ "-c", String(CHANNELS), // Mono
194
+ "-", // stdout
195
+ ], {
196
+ stdio: ["ignore", "pipe", "pipe"],
197
+ });
198
+
199
+ this.captureProcess.stdout.on("data", (chunk) => {
200
+ // Don't send audio to Deepgram while the agent is speaking
201
+ // This prevents the STT from transcribing the agent's own voice
202
+ if (this.isSpeaking) return;
203
+
204
+ if (this.deepgramWs && this.deepgramWs.readyState === WebSocket.OPEN) {
205
+ this.deepgramWs.send(chunk);
206
+ }
207
+ });
208
+
209
+ this.captureProcess.stderr.on("data", (data) => {
210
+ const msg = data.toString().trim();
211
+ // sox prints informational messages to stderr — only log errors
212
+ if (msg && !msg.startsWith("sox WARN")) {
213
+ console.error(`[CAPTURE] ${msg}`);
214
+ }
215
+ });
216
+
217
+ this.captureProcess.on("exit", (code) => {
218
+ if (this.isCapturing) {
219
+ // Unexpected exit — restart
220
+ console.error(`[CAPTURE] sox exited with code ${code}, restarting...`);
221
+ setTimeout(() => {
222
+ if (this.isCapturing) this._startCapture();
223
+ }, 1000);
224
+ }
225
+ });
226
+ }
227
+
228
+ // -------------------------------------------------------------------------
229
+ // Inbound: Web Audio API capture (echo-free alternative to sox/BlackHole)
230
+ //
231
+ // Architecture:
232
+ // 1. A local WebSocket server receives PCM audio from the browser
233
+ // 2. A ScriptProcessorNode injected into Slack's renderer via CDP
234
+ // captures the incoming WebRTC audio stream (before AEC)
235
+ // 3. PCM chunks are forwarded to Deepgram STT
236
+ //
237
+ // This bypasses Slack's speaker output entirely, eliminating the AEC
238
+ // echo artifacts that occur with virtual audio devices.
239
+ // -------------------------------------------------------------------------
240
+
241
+ async _startWebAudioCapture() {
242
+ // Step 1: Start a local WebSocket server to receive PCM from the browser
243
+ await this._startCaptureWsServer();
244
+
245
+ // Step 2: Inject the Web Audio capture script into Slack's renderer via CDP
246
+ await this._injectWebAudioCapture();
247
+
248
+ console.log(`[CAPTURE] Web Audio mode — listening on ws://127.0.0.1:${CAPTURE_WS_PORT}`);
249
+ }
250
+
251
+ _startCaptureWsServer() {
252
+ return new Promise((resolve) => {
253
+ const httpServer = createServer();
254
+ this.captureWsServer = new WebSocketServer({ server: httpServer });
255
+
256
+ this.captureWsServer.on("connection", (ws) => {
257
+ console.log("[CAPTURE] Browser audio stream connected");
258
+ this.captureWsClient = ws;
259
+
260
+ ws.on("message", (data) => {
261
+ // Don't forward to Deepgram while the agent is speaking
262
+ if (this.isSpeaking) return;
263
+
264
+ if (this.deepgramWs && this.deepgramWs.readyState === WebSocket.OPEN) {
265
+ this.deepgramWs.send(data);
266
+ }
267
+ });
268
+
269
+ ws.on("close", () => {
270
+ console.log("[CAPTURE] Browser audio stream disconnected");
271
+ this.captureWsClient = null;
272
+ });
273
+ });
274
+
275
+ httpServer.listen(CAPTURE_WS_PORT, "127.0.0.1", () => {
276
+ resolve();
277
+ });
278
+ });
279
+ }
280
+
281
+ /**
282
+ * Inject a Web Audio capture script into Slack's main renderer via CDP.
283
+ * The script finds the huddle audio element, taps its MediaStream with
284
+ * a ScriptProcessorNode, and streams PCM to our local WebSocket server.
285
+ */
286
+ async _injectWebAudioCapture() {
287
+ // Find the Slack main page CDP target
288
+ const resp = await fetch("http://localhost:9222/json");
289
+ const targets = await resp.json();
290
+ const mainTarget = targets.find(
291
+ (t) => t.type === "page" && t.url?.includes("app.slack.com/client")
292
+ );
293
+ if (!mainTarget) {
294
+ console.error("[CAPTURE] No Slack CDP target found for Web Audio injection");
295
+ return;
296
+ }
297
+
298
+ const captureScript = `
299
+ (async () => {
300
+ if (window._agentAudioCapture) {
301
+ console.log("[AGENT-CAPTURE] Already injected");
302
+ return "already-injected";
303
+ }
304
+
305
+ // Find the audio element carrying huddle audio
306
+ const findAudioStream = () => {
307
+ const audioEls = document.querySelectorAll("audio");
308
+ for (const el of audioEls) {
309
+ if (el.srcObject && el.srcObject.getAudioTracks().length > 0) {
310
+ return el.srcObject;
311
+ }
312
+ }
313
+ // Fallback: capture from the element itself
314
+ for (const el of audioEls) {
315
+ if (!el.paused && el.captureStream) {
316
+ return el.captureStream();
317
+ }
318
+ }
319
+ return null;
320
+ };
321
+
322
+ // Retry until we find an active audio stream
323
+ let stream = null;
324
+ for (let i = 0; i < 30; i++) {
325
+ stream = findAudioStream();
326
+ if (stream) break;
327
+ await new Promise(r => setTimeout(r, 1000));
328
+ }
329
+
330
+ if (!stream) {
331
+ console.error("[AGENT-CAPTURE] No audio stream found after 30s");
332
+ return "no-stream";
333
+ }
334
+
335
+ console.log("[AGENT-CAPTURE] Found audio stream with",
336
+ stream.getAudioTracks().length, "tracks");
337
+
338
+ // Connect to the agent's capture WebSocket server
339
+ const ws = new WebSocket("ws://127.0.0.1:${CAPTURE_WS_PORT}");
340
+
341
+ ws.onopen = () => {
342
+ console.log("[AGENT-CAPTURE] Connected to capture server");
343
+
344
+ // Create AudioContext and ScriptProcessor to capture PCM
345
+ const ctx = new AudioContext({ sampleRate: 16000 });
346
+ const source = ctx.createMediaStreamSource(stream);
347
+
348
+ // ScriptProcessorNode: 4096 buffer, 1 input channel, 1 output channel
349
+ const processor = ctx.createScriptProcessor(4096, 1, 1);
350
+
351
+ processor.onaudioprocess = (e) => {
352
+ if (ws.readyState !== WebSocket.OPEN) return;
353
+
354
+ const float32 = e.inputBuffer.getChannelData(0);
355
+
356
+ // Convert Float32 to Int16 PCM (what Deepgram expects)
357
+ const int16 = new Int16Array(float32.length);
358
+ for (let i = 0; i < float32.length; i++) {
359
+ const s = Math.max(-1, Math.min(1, float32[i]));
360
+ int16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
361
+ }
362
+
363
+ ws.send(int16.buffer);
364
+ };
365
+
366
+ source.connect(processor);
367
+ processor.connect(ctx.destination); // Required for processor to work
368
+
369
+ window._agentAudioCapture = { ctx, source, processor, ws };
370
+ console.log("[AGENT-CAPTURE] Audio capture pipeline active (16kHz PCM)");
371
+ };
372
+
373
+ ws.onerror = (e) => {
374
+ console.error("[AGENT-CAPTURE] WebSocket error:", e.message || e);
375
+ };
376
+
377
+ ws.onclose = () => {
378
+ console.log("[AGENT-CAPTURE] WebSocket closed — cleaning up");
379
+ if (window._agentAudioCapture) {
380
+ window._agentAudioCapture.processor.disconnect();
381
+ window._agentAudioCapture.source.disconnect();
382
+ window._agentAudioCapture.ctx.close();
383
+ window._agentAudioCapture = null;
384
+ }
385
+ };
386
+
387
+ return "injected";
388
+ })()
389
+ `;
390
+
391
+ // Inject via CDP
392
+ const { WebSocket: WS } = await import("ws");
393
+ return new Promise((resolve) => {
394
+ const ws = new WS(mainTarget.webSocketDebuggerUrl);
395
+ ws.on("error", () => resolve());
396
+ ws.on("open", () => {
397
+ ws.send(JSON.stringify({
398
+ id: 1,
399
+ method: "Runtime.evaluate",
400
+ params: { expression: captureScript, returnByValue: true, awaitPromise: true },
401
+ }));
402
+ });
403
+ ws.on("message", (data) => {
404
+ const msg = JSON.parse(data.toString());
405
+ if (msg.id === 1) {
406
+ const result = msg.result?.result?.value || "unknown";
407
+ console.log(`[CAPTURE] CDP injection result: ${result}`);
408
+ ws.close();
409
+ resolve();
410
+ }
411
+ });
412
+ setTimeout(() => { ws.close(); resolve(); }, 35000);
413
+ });
414
+ }
415
+
416
+ async _connectDeepgram() {
417
+ const url = `${DEEPGRAM_WS_URL}?${DEEPGRAM_PARAMS.toString()}`;
418
+
419
+ return new Promise((resolve, reject) => {
420
+ this.deepgramWs = new WebSocket(url, {
421
+ headers: {
422
+ Authorization: `Token ${this.deepgramKey}`,
423
+ },
424
+ });
425
+
426
+ const timeout = setTimeout(() => {
427
+ reject(new Error("Deepgram connection timed out"));
428
+ }, 10000);
429
+
430
+ this.deepgramWs.on("open", () => {
431
+ clearTimeout(timeout);
432
+ console.log("[DEEPGRAM] Connected — real-time STT active");
433
+ resolve();
434
+ });
435
+
436
+ this.deepgramWs.on("message", (raw) => {
437
+ try {
438
+ const msg = JSON.parse(raw.toString());
439
+ this._handleDeepgramMessage(msg);
440
+ } catch (err) {
441
+ console.error("[DEEPGRAM] Parse error:", err.message);
442
+ }
443
+ });
444
+
445
+ this.deepgramWs.on("error", (err) => {
446
+ console.error("[DEEPGRAM] WebSocket error:", err.message);
447
+ this.emit("error", { component: "deepgram", message: err.message });
448
+ });
449
+
450
+ this.deepgramWs.on("close", (code, reason) => {
451
+ console.log(`[DEEPGRAM] Closed: ${code} ${reason}`);
452
+ // Reconnect if bridge is still active
453
+ if (this.isCapturing) {
454
+ setTimeout(() => this._connectDeepgram().catch(() => {}), 2000);
455
+ }
456
+ });
457
+ });
458
+ }
459
+
460
+ _handleDeepgramMessage(msg) {
461
+ // Voice Activity Detection events
462
+ if (msg.type === "SpeechStarted") {
463
+ this.emit("speech:start");
464
+ if (this.speechTimer) {
465
+ clearTimeout(this.speechTimer);
466
+ this.speechTimer = null;
467
+ }
468
+ return;
469
+ }
470
+
471
+ if (msg.type === "UtteranceEnd") {
472
+ // Utterance boundary — finalize accumulated text
473
+ if (this.currentUtterance.trim()) {
474
+ this.emit("transcript", {
475
+ text: this.currentUtterance.trim(),
476
+ isFinal: true,
477
+ });
478
+ this.currentUtterance = "";
479
+ }
480
+ this.emit("speech:end");
481
+ return;
482
+ }
483
+
484
+ // Transcript results
485
+ if (msg.type === "Results" && msg.channel) {
486
+ const alt = msg.channel.alternatives?.[0];
487
+ if (!alt) return;
488
+
489
+ const transcript = alt.transcript;
490
+ if (!transcript) return;
491
+
492
+ if (msg.is_final) {
493
+ // Final result for this segment
494
+ this.currentUtterance += (this.currentUtterance ? " " : "") + transcript;
495
+
496
+ // Reset speech timeout
497
+ if (this.speechTimer) clearTimeout(this.speechTimer);
498
+ this.speechTimer = setTimeout(() => {
499
+ if (this.currentUtterance.trim()) {
500
+ this.emit("transcript", {
501
+ text: this.currentUtterance.trim(),
502
+ isFinal: true,
503
+ });
504
+ this.currentUtterance = "";
505
+ this.emit("speech:end");
506
+ }
507
+ }, SPEECH_TIMEOUT_MS);
508
+ } else {
509
+ // Interim result — show in real-time but don't accumulate
510
+ const interim = this.currentUtterance
511
+ ? this.currentUtterance + " " + transcript
512
+ : transcript;
513
+ this.emit("transcript:interim", { text: interim });
514
+ }
515
+ }
516
+ }
517
+
518
+ // -------------------------------------------------------------------------
519
+ // Outbound: Text → ElevenLabs TTS → BlackHole 16ch
520
+ // -------------------------------------------------------------------------
521
+
522
+ /**
523
+ * Speak text through the huddle. Queues if already speaking.
524
+ * @param {string} text - The text for the agent to say
525
+ * @returns {Promise<void>} Resolves when speech playback completes
526
+ */
527
+ async speak(text) {
528
+ if (!text || !text.trim()) return;
529
+
530
+ return new Promise((resolve) => {
531
+ this.ttsQueue.push({ text: text.trim(), resolve });
532
+ this._processTtsQueue();
533
+ });
534
+ }
535
+
536
+ async _processTtsQueue() {
537
+ if (this.ttsProcessing || this.ttsQueue.length === 0) return;
538
+ this.ttsProcessing = true;
539
+
540
+ while (this.ttsQueue.length > 0) {
541
+ const { text, resolve } = this.ttsQueue.shift();
542
+
543
+ try {
544
+ this.isSpeaking = true;
545
+ this.emit("tts:start", { text });
546
+
547
+ // Generate TTS audio
548
+ const audioBuffer = await this._generateTts(text);
549
+
550
+ // Play audio to BlackHole 16ch (Slack's mic input)
551
+ await this._playAudio(audioBuffer);
552
+
553
+ this.emit("tts:end");
554
+ } catch (err) {
555
+ console.error(`[TTS] Error: ${err.message}`);
556
+ this.emit("error", { component: "tts", message: err.message });
557
+ } finally {
558
+ this.isSpeaking = false;
559
+ resolve();
560
+ }
561
+ }
562
+
563
+ this.ttsProcessing = false;
564
+ }
565
+
566
+ /**
567
+ * Generate speech audio from ElevenLabs API.
568
+ * @param {string} text
569
+ * @returns {Promise<Buffer>} Raw audio data (MP3)
570
+ */
571
+ async _generateTts(text) {
572
+ const url = `${ELEVENLABS_API_URL}/${this.voiceId}`;
573
+
574
+ const response = await fetch(url, {
575
+ method: "POST",
576
+ headers: {
577
+ "xi-api-key": this.elevenLabsKey,
578
+ "Content-Type": "application/json",
579
+ Accept: "audio/mpeg",
580
+ },
581
+ body: JSON.stringify({
582
+ text,
583
+ model_id: ELEVENLABS_MODEL,
584
+ voice_settings: {
585
+ stability: 0.5,
586
+ similarity_boost: 0.8,
587
+ speed: 1.0,
588
+ },
589
+ }),
590
+ });
591
+
592
+ if (!response.ok) {
593
+ const errText = await response.text().catch(() => "");
594
+ throw new Error(`ElevenLabs API error ${response.status}: ${errText}`);
595
+ }
596
+
597
+ const arrayBuffer = await response.arrayBuffer();
598
+ return Buffer.from(arrayBuffer);
599
+ }
600
+
601
+ /**
602
+ * Play audio buffer to the playback device (BlackHole 16ch).
603
+ * Uses sox to decode MP3 and output to the CoreAudio device.
604
+ * @param {Buffer} audioBuffer - MP3 audio data
605
+ * @returns {Promise<void>}
606
+ */
607
+ async _playAudio(audioBuffer) {
608
+ // Write MP3 to a temp file (sox needs seekable input for MP3)
609
+ const tmpFile = join(tmpdir(), `agent-tts-${Date.now()}.mp3`);
610
+ await writeFile(tmpFile, audioBuffer);
611
+
612
+ return new Promise((resolve, reject) => {
613
+ const player = spawn("sox", [
614
+ tmpFile, // Input: MP3 file
615
+ "-t", "coreaudio", this.playbackDevice, // Output: BlackHole 16ch
616
+ ], {
617
+ stdio: ["ignore", "ignore", "pipe"],
618
+ });
619
+
620
+ let stderrBuf = "";
621
+ player.stderr.on("data", (d) => { stderrBuf += d.toString(); });
622
+
623
+ player.on("exit", async (code) => {
624
+ // Clean up temp file
625
+ await unlink(tmpFile).catch(() => {});
626
+
627
+ if (code === 0) {
628
+ resolve();
629
+ } else {
630
+ reject(new Error(`sox playback exited ${code}: ${stderrBuf.trim()}`));
631
+ }
632
+ });
633
+
634
+ player.on("error", async (err) => {
635
+ await unlink(tmpFile).catch(() => {});
636
+ reject(err);
637
+ });
638
+ });
639
+ }
640
+
641
+ // -------------------------------------------------------------------------
642
+ // Utilities
643
+ // -------------------------------------------------------------------------
644
+
645
+ /**
646
+ * Check if the audio devices are available and working.
647
+ * @returns {Promise<{capture: boolean, playback: boolean}>}
648
+ */
649
+ async checkDevices() {
650
+ const result = { capture: false, playback: false };
651
+
652
+ try {
653
+ const devices = execFileSync("SwitchAudioSource", ["-a"], { encoding: "utf8" });
654
+ result.capture = devices.includes(this.captureDevice);
655
+ result.playback = devices.includes(this.playbackDevice);
656
+ } catch {
657
+ // SwitchAudioSource not available
658
+ }
659
+
660
+ return result;
661
+ }
662
+ }
663
+
664
+ export default AudioBridge;