@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,587 @@
1
+ /**
2
+ * Maestro — WhatsApp (Baileys) channel adapter
3
+ *
4
+ * Baileys is the de facto open-source WhatsApp Web reverse-engineering
5
+ * library used by openclaw's `/extensions/whatsapp/`. It supports
6
+ * everything Twilio's API does (text, image, document) plus the
7
+ * features Twilio's API can't reach because they're WhatsApp-Web-only:
8
+ * - Voice notes (PTT) — both inbound and outbound
9
+ * - Quoted-message threading (replies in chat)
10
+ * - Reactions
11
+ * - Group chats with full member roster
12
+ * - Read receipts
13
+ * - Typing indicators
14
+ *
15
+ * Trade-offs vs. Twilio:
16
+ * + Free (no per-message Twilio fee)
17
+ * + Voice notes work natively
18
+ * + Group chats and quoted-reply threading
19
+ * - Requires QR-code login from a real WhatsApp account (no Business API)
20
+ * - Banned by WhatsApp's ToS for production business use — use only
21
+ * for personal-account scenarios (e.g. a sole-proprietor agent
22
+ * speaking from the owner's account)
23
+ * - Auth state lives on disk; lose it and you re-scan the QR
24
+ *
25
+ * This adapter dynamically imports `baileys` so the framework doesn't
26
+ * force the dependency on every install. Operators who opt in add
27
+ * `baileys` to their agent's package.json:
28
+ *
29
+ * npm i baileys @whiskeysockets/baileys
30
+ *
31
+ * Voice note pipeline:
32
+ * inbound voice note → buffer ogg/opus
33
+ * → Whisper / gpt-realtime-whisper transcript
34
+ * → emit MessageEvent with .attachments + .text
35
+ * outbound voice note → caller passes attachment {type: "voice_note", buffer}
36
+ * → adapter encodes to ogg/opus + sends as PTT
37
+ *
38
+ * WS2 hardening (task #30): this adapter now extends BaseAdapter so it inherits
39
+ * the ONE shared outbound send-gate (banned-phrase / AI-disclosure / barrier
40
+ * screening before every send), the bounded-backoff reconnect SUPERVISOR (a
41
+ * dropped socket reconnects instead of the old one-shot setTimeout restart, and
42
+ * a dead channel turns healthCheck() red), and the ownId / peerAgentIds
43
+ * bot-loop guard. Subclasses implement only the platform hooks
44
+ * `_connect/_toEvent/_send/_react/_setTyping`; baileys is still dynamically
45
+ * imported. WhatsApp's typing indicator is presence-based ("composing"/"paused")
46
+ * rather than the base's per-tick `_setTyping`, so this adapter overrides the
47
+ * heartbeat methods to drive `sock.sendPresenceUpdate` directly.
48
+ *
49
+ * @module lib/channels/whatsapp/adapter-baileys
50
+ */
51
+
52
+ import { join, resolve } from "node:path";
53
+ import { mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
54
+ import { CHANNEL_CAPABILITIES } from "../channel.mjs";
55
+ import { definePlatform } from "../contract.mjs";
56
+ import { BaseAdapter } from "../base-adapter.mjs";
57
+
58
+ const BAILEYS_CAPABILITIES = [
59
+ CHANNEL_CAPABILITIES.SEND,
60
+ CHANNEL_CAPABILITIES.REACT,
61
+ CHANNEL_CAPABILITIES.THREAD,
62
+ CHANNEL_CAPABILITIES.MEDIA_IMAGE,
63
+ CHANNEL_CAPABILITIES.MEDIA_AUDIO,
64
+ CHANNEL_CAPABILITIES.MEDIA_DOCUMENT,
65
+ CHANNEL_CAPABILITIES.VOICE_NOTE,
66
+ CHANNEL_CAPABILITIES.GROUP_CHAT,
67
+ CHANNEL_CAPABILITIES.READ_RECEIPTS,
68
+ CHANNEL_CAPABILITIES.TYPING_INDICATOR,
69
+ ];
70
+
71
+ /** Presence-refresh cadence for the WhatsApp typing heartbeat (ms). */
72
+ const TYPING_REFRESH_MS = 8000;
73
+
74
+ let _baileysCache = null;
75
+ async function loadBaileys(agentRoot) {
76
+ if (_baileysCache) return _baileysCache;
77
+ // Resolution strategy:
78
+ // 1. Resolve from the agent's node_modules (baileys is heavy; the
79
+ // framework doesn't bundle it — operators install it in the
80
+ // agent repo).
81
+ // 2. Fall back to framework-relative resolution if the agent path
82
+ // doesn't have it (e.g. when run in a dev checkout).
83
+ const candidates = [];
84
+ if (agentRoot) {
85
+ candidates.push(`file://${agentRoot}/node_modules/baileys/lib/index.js`);
86
+ candidates.push(`file://${agentRoot}/node_modules/baileys/lib/index.mjs`);
87
+ candidates.push(`file://${agentRoot}/node_modules/baileys/index.js`);
88
+ candidates.push(`file://${agentRoot}/node_modules/@whiskeysockets/baileys/lib/index.js`);
89
+ }
90
+ candidates.push("baileys");
91
+ candidates.push("@whiskeysockets/baileys");
92
+
93
+ const errors = [];
94
+ for (const spec of candidates) {
95
+ try {
96
+ const mod = await import(spec);
97
+ _baileysCache = mod;
98
+ return mod;
99
+ } catch (err) {
100
+ errors.push(`${spec}: ${err.message}`);
101
+ }
102
+ }
103
+ throw new Error(
104
+ "WhatsApp(Baileys) requires the `baileys` package installed in the agent repo. " +
105
+ "Run: `npm i baileys` in your agent repo. " +
106
+ `Tried resolutions:\n - ${errors.join("\n - ")}`
107
+ );
108
+ }
109
+
110
+ /**
111
+ * WhatsApp (Baileys) adapter. Extends BaseAdapter so `send()` runs the shared
112
+ * send-gate before `_send`, `_connect` is wrapped by the reconnect supervisor,
113
+ * and inbound from this agent / a peer agent is dropped (bot-loop guard).
114
+ */
115
+ export class BaileysWhatsAppAdapter extends BaseAdapter {
116
+ /**
117
+ * @param {object} opts
118
+ * @param {string} [opts.authDir]
119
+ * @param {(buf:Buffer, meta:object)=>Promise<string>} [opts.transcribeVoiceNote]
120
+ * @param {any} [opts.sock] inject a connected Baileys socket (tests)
121
+ */
122
+ constructor(opts = {}) {
123
+ super({ name: "whatsapp", capabilities: BAILEYS_CAPABILITIES, ...opts });
124
+ this.transport = "baileys";
125
+ this.authDir = opts.authDir || join(this.agentRoot, "state/whatsapp/auth");
126
+ this.transcribe =
127
+ typeof opts.transcribeVoiceNote === "function" ? opts.transcribeVoiceNote : null;
128
+
129
+ /** @type {any} the Baileys WASocket once connected */
130
+ this._sock = opts.sock || null;
131
+
132
+ // Per-jid "composing" presence refresh timers (typing heartbeat). WhatsApp's
133
+ // typing indicator auto-expires (~25s), so we re-assert "composing" on an
134
+ // interval while a session is working, then send "paused" on stop. Keyed by
135
+ // jid so a second start for the same chat doesn't stack timers. This is the
136
+ // presence-based heartbeat WhatsApp needs in place of the base's per-tick
137
+ // `_setTyping`, so the heartbeat methods are overridden below.
138
+ /** @type {Map<string, any>} */
139
+ this._presenceTimers = new Map();
140
+ }
141
+
142
+ // ── Test seams (no-ops in production; never called on the live path) ──
143
+ /** @internal inject a fake sock without connecting (tests only). */
144
+ __setSock(s) { this._sock = s; }
145
+ /** @internal number of active per-jid typing timers (tests only). */
146
+ __typingTimerCount() { return this._presenceTimers.size; }
147
+
148
+ /** Best-effort presence send; a missing/disconnected sock just no-ops. */
149
+ _sendPresence(state, jid) {
150
+ try {
151
+ const sock = this._sock;
152
+ if (sock && typeof sock.sendPresenceUpdate === "function") {
153
+ // sendPresenceUpdate may be async; we don't await (fire-and-forget) and
154
+ // swallow any rejection so a dropped socket never surfaces here.
155
+ Promise.resolve(sock.sendPresenceUpdate(state, jid)).catch(() => {});
156
+ }
157
+ } catch { /* fail-open: disconnected/absent sock */ }
158
+ }
159
+
160
+ /** Resolve a jid from a SessionSource ({chatId}) or a bare chatId string. */
161
+ _jidFromSource(source) {
162
+ const raw = source && typeof source === "object" ? source.chatId : source;
163
+ if (!raw) return "";
164
+ try { return normaliseJid(String(raw)); } catch { return ""; }
165
+ }
166
+
167
+ // -- connect: open the Baileys socket, wire its events to the supervisor ----
168
+
169
+ async _connect({ onRaw, signal }) {
170
+ mkdirSync(this.authDir, { recursive: true });
171
+
172
+ if (!this._sock) {
173
+ const baileys = await loadBaileys(this.agentRoot);
174
+ const makeSocket = baileys.makeWASocket || baileys.default;
175
+ const useMultiFileAuthState = baileys.useMultiFileAuthState;
176
+ const fetchLatestBaileysVersion = baileys.fetchLatestBaileysVersion;
177
+
178
+ const { state, saveCreds } = await useMultiFileAuthState(this.authDir);
179
+ const { version } = fetchLatestBaileysVersion
180
+ ? await fetchLatestBaileysVersion()
181
+ : { version: undefined };
182
+
183
+ this._sock = makeSocket({
184
+ version,
185
+ auth: state,
186
+ printQRInTerminal: false, // we surface the QR code via the event handler
187
+ // Some Baileys versions accept `logger`; we pass a no-op-ish to silence noise.
188
+ logger: {
189
+ level: "warn", info: () => {}, warn: () => {}, error: (...a) => this.log("error", a.join(" ")),
190
+ debug: () => {}, trace: () => {}, child: () => ({ level: "warn", info: () => {}, warn: () => {}, error: () => {}, debug: () => {}, trace: () => {}, child: () => this }),
191
+ },
192
+ });
193
+
194
+ this._sock.ev.on("creds.update", saveCreds);
195
+ }
196
+ const sock = this._sock;
197
+ const DisconnectReason = (await loadBaileysDisconnectReason(this.agentRoot)) || {};
198
+
199
+ sock.ev.on("connection.update", (update) => {
200
+ const { connection, lastDisconnect, qr } = update;
201
+ if (qr) {
202
+ // Persist the QR string so the operator can render it elsewhere
203
+ // (terminal qrcode-terminal, web UI, etc.) without forcing us
204
+ // to pull a QR-rendering dep into the framework.
205
+ try { writeFileSync(join(this.authDir, "pending-qr.txt"), qr); } catch { /* */ }
206
+ this.log("info", `QR code pending — scan it. Saved to ${join(this.authDir, "pending-qr.txt")}`);
207
+ }
208
+ if (connection === "open") {
209
+ this.log("info", "WhatsApp (Baileys) connected");
210
+ } else if (connection === "close") {
211
+ const reason = lastDisconnect?.error?.output?.statusCode;
212
+ // A logged-out socket can't be recovered by reconnecting (the auth state
213
+ // is gone) — let the supervisor give up rather than thrash. Every other
214
+ // drop is reported to the reconnect supervisor, which owns the bounded
215
+ // backoff (replacing the old one-shot setTimeout(start, 5000) that died
216
+ // permanently after a single swallowed failure — gaps P0-6).
217
+ const loggedOut = reason === (DisconnectReason.loggedOut || 401);
218
+ this.log("warn", `connection closed (reason=${reason}); loggedOut=${loggedOut}`);
219
+ if (!loggedOut && !this._stopping && !signal?.aborted) {
220
+ this.notifyConnectionDown(`whatsapp-close:${reason ?? "?"}`);
221
+ }
222
+ }
223
+ });
224
+
225
+ sock.ev.on("messages.upsert", async ({ messages, type }) => {
226
+ if (type !== "notify") return;
227
+ for (const m of messages) {
228
+ if (m.key?.fromMe) continue;
229
+ try {
230
+ const raw = await normaliseInbound(m, { sock, transcribe: this.transcribe, agentRoot: this.agentRoot });
231
+ if (raw) onRaw(raw);
232
+ } catch (err) {
233
+ this.log("error", `failed to normalise inbound: ${err.message}`);
234
+ }
235
+ }
236
+ });
237
+
238
+ if (signal) signal.addEventListener?.("abort", () => { try { sock?.end?.(undefined); } catch { /* */ } });
239
+ }
240
+
241
+ // -- translate: normalised inbound → MessageEvent --------------------------
242
+ //
243
+ // normaliseInbound already produces a MessageEvent-shaped object (with a
244
+ // `source` descriptor) so the platform factory's old start-wrapper is no
245
+ // longer needed; _toEvent simply forwards it, with a defensive null check.
246
+
247
+ _toEvent(raw) {
248
+ if (!raw || typeof raw !== "object") return null;
249
+ return raw;
250
+ }
251
+
252
+ // -- outbound transport (gated by BaseAdapter.send before this runs) --------
253
+
254
+ async _send(out) {
255
+ const sock = this._sock;
256
+ if (!sock) throw new Error("WhatsApp(Baileys) not connected — call start() first");
257
+ const to = out?.to ?? out?.source?.chatId;
258
+ if (!to) throw new Error("WhatsApp._send: `to` (jid like '9715...@s.whatsapp.net' or E.164) required");
259
+ const jid = normaliseJid(to);
260
+
261
+ // Quoted reply
262
+ const threadRef = out.thread_ref ?? out.source?.threadRef;
263
+ const quoted = threadRef ? { key: parseQuotedRef(threadRef) } : undefined;
264
+
265
+ // Voice note path
266
+ if (out.attachments?.length === 1 && out.attachments[0].type === "voice_note") {
267
+ const att = out.attachments[0];
268
+ const audio = att.buffer || (att.url ? await fetchUrl(att.url) : null);
269
+ if (!audio) throw new Error("voice_note attachment requires buffer or url");
270
+ const result = await sock.sendMessage(jid, {
271
+ audio,
272
+ mimetype: att.mime || "audio/ogg; codecs=opus",
273
+ ptt: true,
274
+ }, quoted);
275
+ return { ok: true, message_id: result?.key?.id || "", raw: result };
276
+ }
277
+
278
+ // Media attachments
279
+ if (out.attachments?.length) {
280
+ // Send the first attachment with a caption from out.text; for
281
+ // multi-attachment payloads, send them sequentially (caller
282
+ // gets the id of the last send).
283
+ let last;
284
+ for (let i = 0; i < out.attachments.length; i++) {
285
+ const att = out.attachments[i];
286
+ const buffer = att.buffer || (att.url ? await fetchUrl(att.url) : null);
287
+ if (!buffer) throw new Error(`attachment ${i} requires buffer or url`);
288
+ const payload = mediaPayload(att, buffer, i === 0 ? out.text : undefined);
289
+ last = await sock.sendMessage(jid, payload, quoted);
290
+ }
291
+ return { ok: true, message_id: last?.key?.id || "", raw: last };
292
+ }
293
+
294
+ // Plain text
295
+ const result = await sock.sendMessage(jid, { text: out.text || "" }, quoted);
296
+ return { ok: true, message_id: result?.key?.id || "", raw: result };
297
+ }
298
+
299
+ async _react(out) {
300
+ const sock = this._sock;
301
+ if (!sock) throw new Error("WhatsApp(Baileys) not connected");
302
+ const ref = parseQuotedRef(out.messageRef);
303
+ await sock.sendMessage(ref.remoteJid, {
304
+ react: { text: out.emoji, key: ref },
305
+ });
306
+ return { ok: true };
307
+ }
308
+
309
+ // -- typing: presence-based heartbeat (overrides the base per-tick model) ---
310
+
311
+ /**
312
+ * Start a presence-based "typing…" heartbeat for a conversation. Sends
313
+ * "composing" immediately, then refreshes it on an unref'd interval so the
314
+ * indicator doesn't lapse mid-session. Idempotent per jid. Fail-open: a
315
+ * missing/disconnected sock makes this a no-op.
316
+ * @param {{chatId?:string}|string} source
317
+ * @returns {any} the (existing or new) timer handle
318
+ */
319
+ startTypingHeartbeat(source) {
320
+ try {
321
+ const jid = this._jidFromSource(source);
322
+ if (!jid) return null;
323
+ this._sendPresence("composing", jid);
324
+ const existing = this._presenceTimers.get(jid);
325
+ if (existing) return existing; // already heart-beating this jid — don't stack
326
+ const timer = this._setInterval(() => this._sendPresence("composing", jid), TYPING_REFRESH_MS);
327
+ if (timer && typeof timer.unref === "function") timer.unref();
328
+ this._presenceTimers.set(jid, timer);
329
+ return timer;
330
+ } catch {
331
+ return null; // fail-open
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Stop the heartbeat for a conversation: clear its refresh timer and send a
337
+ * best-effort "paused" presence. Fail-open; stopping an unknown jid is a
338
+ * clean no-op.
339
+ * @param {{chatId?:string}|string} source
340
+ */
341
+ stopTypingHeartbeat(source) {
342
+ try {
343
+ const jid = this._jidFromSource(source);
344
+ if (!jid) return;
345
+ const timer = this._presenceTimers.get(jid);
346
+ if (timer) {
347
+ this._clearInterval(timer);
348
+ this._presenceTimers.delete(jid);
349
+ }
350
+ this._sendPresence("paused", jid);
351
+ } catch { /* fail-open */ }
352
+ }
353
+
354
+ /** Clear every presence heartbeat (called from stop()). */
355
+ stopAllTyping() {
356
+ for (const t of this._presenceTimers.values()) { try { this._clearInterval(t); } catch { /* */ } }
357
+ this._presenceTimers.clear();
358
+ }
359
+
360
+ /**
361
+ * Base hook so any base-class caller of `_setTyping` still drives the right
362
+ * WhatsApp presence (the heartbeat methods above are the live path).
363
+ */
364
+ async _setTyping(source, on) {
365
+ const jid = this._jidFromSource(source);
366
+ if (!jid) return;
367
+ this._sendPresence(on ? "composing" : "paused", jid);
368
+ }
369
+
370
+ // -- health / lifecycle ----------------------------------------------------
371
+
372
+ async healthCheck() {
373
+ // Prefer the supervisor's view when supervised (a dead channel reports RED);
374
+ // otherwise fall back to the socket-identity probe.
375
+ const base = await super.healthCheck();
376
+ const connected = !!this._sock?.user;
377
+ if (this._superviseEnabled && this._supervisor) {
378
+ return {
379
+ ...base,
380
+ detail: connected ? `connected as ${this._sock.user.id}` : base.detail,
381
+ };
382
+ }
383
+ return { ok: connected, detail: connected ? `connected as ${this._sock.user.id}` : "not connected" };
384
+ }
385
+
386
+ async stop() {
387
+ await super.stop();
388
+ this.stopAllTyping();
389
+ try { this._sock?.end?.(undefined); } catch { /* */ }
390
+ this._sock = null;
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Factory matching the channel/platform contract. Returns a BaseAdapter
396
+ * subclass instance; the daemon/registry call `.start({onInbound,signal})`,
397
+ * `.send()`, `.react()`, `.healthCheck()`, `.stop()`, and the typing-registry
398
+ * drives `.startTypingHeartbeat`/`.stopTypingHeartbeat`.
399
+ */
400
+ export function createBaileysWhatsAppChannel(opts = {}) {
401
+ return new BaileysWhatsAppAdapter(opts);
402
+ }
403
+
404
+ // ── Helpers ────────────────────────────────────────────────────────────────
405
+
406
+ /** Best-effort import of Baileys' DisconnectReason map (for loggedOut). */
407
+ async function loadBaileysDisconnectReason(agentRoot) {
408
+ try {
409
+ const baileys = await loadBaileys(agentRoot);
410
+ return baileys.DisconnectReason || null;
411
+ } catch {
412
+ return null;
413
+ }
414
+ }
415
+
416
+ function mediaPayload(att, buffer, caption) {
417
+ switch (att.type) {
418
+ case "image":
419
+ return { image: buffer, mimetype: att.mime || "image/jpeg", caption };
420
+ case "video":
421
+ return { video: buffer, mimetype: att.mime || "video/mp4", caption };
422
+ case "audio":
423
+ return { audio: buffer, mimetype: att.mime || "audio/mpeg", ptt: false };
424
+ case "document":
425
+ return { document: buffer, mimetype: att.mime || "application/pdf", fileName: att.filename, caption };
426
+ default:
427
+ return { document: buffer, mimetype: att.mime || "application/octet-stream", fileName: att.filename, caption };
428
+ }
429
+ }
430
+
431
+ function normaliseJid(to) {
432
+ if (String(to).includes("@")) return String(to);
433
+ const digits = String(to).replace(/[^\d]/g, "");
434
+ return `${digits}@s.whatsapp.net`;
435
+ }
436
+
437
+ function parseQuotedRef(ref) {
438
+ // Accept either an object {remoteJid, id, fromMe} or a serialised
439
+ // string "remoteJid:id:fromMe".
440
+ if (typeof ref === "object" && ref?.id) return ref;
441
+ const [remoteJid, id, fromMe] = String(ref).split(":");
442
+ return { remoteJid, id, fromMe: fromMe === "true" };
443
+ }
444
+
445
+ async function normaliseInbound(m, { sock, transcribe, agentRoot }) {
446
+ const channel = "whatsapp";
447
+ const remoteJid = m.key.remoteJid;
448
+ const id = m.key.id;
449
+ const isGroup = remoteJid?.endsWith("@g.us");
450
+ const senderJid = isGroup ? m.key.participant : remoteJid;
451
+ const ts = m.messageTimestamp;
452
+
453
+ // Identify message body across the many Baileys message shapes.
454
+ let text = "";
455
+ const attachments = [];
456
+
457
+ const inner =
458
+ m.message?.conversation ??
459
+ m.message?.extendedTextMessage?.text ??
460
+ null;
461
+ if (typeof inner === "string") text = inner;
462
+
463
+ // Quoted message reference (thread_id)
464
+ let thread_id = null;
465
+ const ctx = m.message?.extendedTextMessage?.contextInfo;
466
+ if (ctx?.stanzaId) {
467
+ thread_id = `${ctx.remoteJid || remoteJid}:${ctx.stanzaId}:${!!ctx.fromMe}`;
468
+ }
469
+
470
+ let kind = "message";
471
+
472
+ // Voice note → transcribe
473
+ if (m.message?.audioMessage?.ptt) {
474
+ kind = "voice_note";
475
+ const buffer = await downloadMedia(sock, m, "audio", agentRoot);
476
+ let transcript = "";
477
+ if (transcribe && buffer) {
478
+ try { transcript = await transcribe(buffer, { mime: m.message.audioMessage.mimetype }); }
479
+ catch (err) { transcript = `(transcription failed: ${err.message})`; }
480
+ }
481
+ attachments.push({
482
+ type: "voice_note",
483
+ mime: m.message.audioMessage.mimetype || "audio/ogg; codecs=opus",
484
+ size_bytes: buffer?.length || 0,
485
+ transcript,
486
+ });
487
+ if (transcript) text = transcript; // surface as the message body
488
+ }
489
+ // Image, document, etc.
490
+ for (const [key, kindName] of [
491
+ ["imageMessage", "image"],
492
+ ["videoMessage", "video"],
493
+ ["documentMessage", "document"],
494
+ ["audioMessage", "audio"],
495
+ ]) {
496
+ if (m.message?.[key]) {
497
+ if (key === "audioMessage" && m.message.audioMessage.ptt) continue; // handled above
498
+ const mime = m.message[key].mimetype || null;
499
+ const filename = m.message[key].fileName || null;
500
+ attachments.push({ type: kindName, mime, filename });
501
+ const caption = m.message[key].caption;
502
+ if (caption && !text) text = caption;
503
+ }
504
+ }
505
+
506
+ const toId = remoteJid;
507
+ const chatId = String(toId || senderJid || "");
508
+ return {
509
+ channel,
510
+ message_id: id,
511
+ thread_id,
512
+ in_reply_to: ctx?.stanzaId || null,
513
+ from: { id: senderJid, phone: jidToPhone(senderJid) },
514
+ to: { id: remoteJid },
515
+ text,
516
+ attachments,
517
+ kind,
518
+ timestamp: typeof ts === "number" ? new Date(ts * 1000).toISOString() : new Date().toISOString(),
519
+ raw: m,
520
+ // Durable source descriptor so the inbox-item seam + typing registry have
521
+ // the conversation descriptor (previously stamped by the platform wrapper).
522
+ source: {
523
+ channel: "whatsapp",
524
+ chatId,
525
+ threadRef: thread_id || undefined,
526
+ userScope: chatId.endsWith("@g.us") ? "group" : "dm",
527
+ peerId: senderJid,
528
+ },
529
+ };
530
+ }
531
+
532
+ async function downloadMedia(sock, m, type, agentRoot) {
533
+ try {
534
+ if (typeof sock.downloadMediaMessage === "function") {
535
+ return await sock.downloadMediaMessage(m);
536
+ }
537
+ // Newer Baileys: helper function exported on module
538
+ const baileys = await loadBaileys(agentRoot);
539
+ if (typeof baileys.downloadMediaMessage === "function") {
540
+ return await baileys.downloadMediaMessage(m, "buffer", {});
541
+ }
542
+ } catch { /* */ }
543
+ return null;
544
+ }
545
+
546
+ function jidToPhone(jid) {
547
+ if (!jid) return null;
548
+ return "+" + String(jid).split("@")[0].split(":")[0];
549
+ }
550
+
551
+ async function fetchUrl(url) {
552
+ const res = await fetch(url);
553
+ if (!res.ok) throw new Error(`fetch ${url} failed: ${res.status}`);
554
+ return Buffer.from(await res.arrayBuffer());
555
+ }
556
+
557
+ /**
558
+ * Platform factory for the daemon's generic channel-bus loop. Gated by
559
+ * config/whatsapp.yaml; only handles the Baileys transport (Twilio WhatsApp
560
+ * uses its own webhook path, not the bus). Throws a clear skip when the config
561
+ * selects a different transport so the loop logs and moves on.
562
+ *
563
+ * The adapter itself now stamps each inbound MessageEvent with a `source`
564
+ * descriptor (in normaliseInbound), and BaseAdapter.start owns the connect
565
+ * supervisor + send-gate + bot-loop guard, so the old start-wrapper that
566
+ * re-stamped `source` / forced `kind` is no longer needed.
567
+ */
568
+ function createWhatsAppPlatform(opts = {}) {
569
+ const agentRoot = resolve(opts.agentRoot || process.env.AGENT_ROOT || process.env.AGENT_DIR || process.cwd());
570
+ const cfgPath = join(agentRoot, "config/whatsapp.yaml");
571
+ if (existsSync(cfgPath)) {
572
+ const body = readFileSync(cfgPath, "utf-8");
573
+ if (!/^transport:\s*baileys\b/m.test(body)) {
574
+ throw new Error("whatsapp transport is not baileys — handled outside the channel bus");
575
+ }
576
+ }
577
+ return createBaileysWhatsAppChannel(opts);
578
+ }
579
+
580
+ definePlatform({
581
+ name: "whatsapp",
582
+ gateFile: "config/whatsapp.yaml",
583
+ factory: createWhatsAppPlatform,
584
+ capabilities: BAILEYS_CAPABILITIES,
585
+ });
586
+
587
+ export default createBaileysWhatsAppChannel;