@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,215 @@
1
+ /**
2
+ * Maestro — Channel contract (one type, one serialization)
3
+ *
4
+ * WS2 (Hermes integration §5) unifies every channel onto a single message
5
+ * type + a self-registering platform registry. This module is the SoT for:
6
+ *
7
+ * - `MessageEvent` — the reconciled, canonical inbound event. It is a
8
+ * superset of the legacy `ChannelMessage` (lib/channels/channel.mjs):
9
+ * same fields, plus `kind`, `source`, and `thread_context`. We export
10
+ * `ChannelMessage` as a back-compat alias so existing imports keep
11
+ * working unchanged.
12
+ * - `SessionSource` — the durable, channel-agnostic descriptor of *where*
13
+ * an event came from, used to reply / set typing / react without the
14
+ * adapter re-deriving channel-specific ids.
15
+ * - `SendResult` — the uniform return shape of every adapter `_send`.
16
+ * - `EventKind` — the closed set of inbound event kinds.
17
+ * - The platform registry: `definePlatform` / `getPlatform` /
18
+ * `listPlatforms`. Each concrete adapter file ends with a
19
+ * `definePlatform({ name, gateFile, factory, capabilities })` call so the
20
+ * daemon can discover channels by *dropping a file* (no core edit), the
21
+ * same pattern WS1 uses for setup sections.
22
+ *
23
+ * This module is pure data + a tiny registry — no runtime IO, no network.
24
+ *
25
+ * @module lib/channels/contract
26
+ */
27
+
28
+ "use strict";
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // EventKind — the closed set of inbound event kinds.
32
+ // ---------------------------------------------------------------------------
33
+
34
+ /**
35
+ * @typedef {"message"|"reaction"|"channel_cc"|"voice_note"|"call"} EventKind
36
+ *
37
+ * - message a normal directed message (DM, @mention, thread reply)
38
+ * - reaction an emoji reaction add/remove (rarely action-required)
39
+ * - channel_cc a non-directed message in a watched/cc channel (observe, don't reply)
40
+ * - voice_note a voice note whose transcript becomes `text` (+ a voice_note attachment)
41
+ * - call a live phone/voice call turn
42
+ */
43
+ export const EVENT_KINDS = Object.freeze([
44
+ "message",
45
+ "reaction",
46
+ "channel_cc",
47
+ "voice_note",
48
+ "call",
49
+ ]);
50
+
51
+ /** The default kind when an event/serialized item does not specify one. */
52
+ export const DEFAULT_EVENT_KIND = "message";
53
+
54
+ /** Normalise an arbitrary kind value to a known EventKind (defaults to "message"). */
55
+ export function normalizeKind(kind) {
56
+ return EVENT_KINDS.includes(kind) ? kind : DEFAULT_EVENT_KIND;
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Typedefs (JSDoc only — no runtime cost).
61
+ // ---------------------------------------------------------------------------
62
+
63
+ /**
64
+ * @typedef {Object} SessionSource
65
+ * @property {string} channel Channel name, e.g. "slack" | "telegram" | "whatsapp"
66
+ * @property {string} chatId The channel-native conversation/chat id (Slack channel id,
67
+ * Telegram chat id, WhatsApp jid, …). Stable per-conversation.
68
+ * @property {string} [threadRef] Thread / topic reference within the chat (Slack thread_ts,
69
+ * Telegram message_thread_id), when threaded.
70
+ * @property {string} [replyToRef] The specific message being replied to (for quoted replies).
71
+ * @property {string} userScope "dm" | "group" | "channel" — coarse audience scope, drives
72
+ * whether pairing (DMs) or membership+directed-gate (group)
73
+ * governs the event.
74
+ */
75
+
76
+ /**
77
+ * @typedef {Object} SendResult
78
+ * @property {boolean} ok Whether the send succeeded.
79
+ * @property {string} [message_id] Provider message id on success.
80
+ * @property {string} [error] Error detail on failure.
81
+ * @property {object} [raw] Untouched provider response for diagnostics.
82
+ */
83
+
84
+ /**
85
+ * MessageEvent — the canonical inbound event. Superset of ChannelMessage.
86
+ *
87
+ * @typedef {Object} MessageEvent
88
+ * @property {string} channel Channel name, e.g. "slack" | "telegram" | "whatsapp"
89
+ * @property {string} message_id Stable per-channel id (Slack ts, Telegram msg id, …)
90
+ * @property {string} [thread_id] Thread / conversation id within the channel
91
+ * @property {import("./channel.mjs").ChannelIdentity} from
92
+ * @property {import("./channel.mjs").ChannelIdentity} [to]
93
+ * @property {string} text Plain-text body (channel-specific formatting stripped)
94
+ * @property {string} [html] Original HTML body if applicable
95
+ * @property {Array<import("./channel.mjs").Attachment>} [attachments]
96
+ * @property {string} timestamp ISO 8601
97
+ * @property {string} [in_reply_to] Parent message id for threaded replies
98
+ * @property {object} [raw] Untouched provider payload for diagnostics
99
+ * @property {EventKind} kind WS2: the event kind (default "message")
100
+ * @property {SessionSource} [source] WS2: durable source descriptor for reply/typing/react
101
+ * @property {string|null} [thread_context] WS2: rendered prior-thread history (or null)
102
+ */
103
+
104
+ /**
105
+ * Build a MessageEvent with the WS2 defaults applied. Accepts a partial and
106
+ * fills `kind` (default "message") + leaves `source`/`thread_context` as
107
+ * provided. Adapters typically return a full event from `_toEvent`; this
108
+ * helper is for tests + callers that want the defaults guaranteed.
109
+ *
110
+ * @param {Partial<MessageEvent>} partial
111
+ * @returns {MessageEvent}
112
+ */
113
+ export function makeMessageEvent(partial = {}) {
114
+ return {
115
+ ...partial,
116
+ kind: normalizeKind(partial.kind),
117
+ };
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Self-registering platform registry.
122
+ // ---------------------------------------------------------------------------
123
+
124
+ /**
125
+ * @typedef {Object} PlatformDefinition
126
+ * @property {string} name The platform name (== channel name), e.g. "telegram".
127
+ * @property {string} gateFile Repo-relative gate file whose presence enables the
128
+ * platform, e.g. "config/telegram.yaml".
129
+ * @property {(opts:object)=>object} factory Factory returning an adapter (BaseAdapter subclass).
130
+ * @property {string[]} [capabilities] Advertised capability strings.
131
+ */
132
+
133
+ /** @type {Map<string, PlatformDefinition>} */
134
+ const PLATFORMS = new Map();
135
+
136
+ /**
137
+ * Register a platform. Called at module-eval time at the end of each adapter
138
+ * file. Idempotent by name: re-defining the same name replaces the prior
139
+ * definition (so a re-import during tests / hot-reload doesn't throw). The
140
+ * registry holds *definitions* (factories), not live instances — the daemon
141
+ * decides which to instantiate based on gate-file presence.
142
+ *
143
+ * @param {PlatformDefinition} def
144
+ * @returns {PlatformDefinition}
145
+ */
146
+ export function definePlatform(def) {
147
+ if (!def || typeof def !== "object") throw new Error("definePlatform: def object required");
148
+ if (!def.name || typeof def.name !== "string") throw new Error("definePlatform: name required");
149
+ if (!def.gateFile || typeof def.gateFile !== "string") throw new Error("definePlatform: gateFile required");
150
+ if (typeof def.factory !== "function") throw new Error("definePlatform: factory must be a function");
151
+ const normalized = {
152
+ name: def.name,
153
+ gateFile: def.gateFile,
154
+ factory: def.factory,
155
+ capabilities: Array.isArray(def.capabilities) ? def.capabilities.slice() : [],
156
+ };
157
+ PLATFORMS.set(def.name, normalized);
158
+ return normalized;
159
+ }
160
+
161
+ /** Look up a platform definition by name (undefined when not registered). */
162
+ export function getPlatform(name) {
163
+ return PLATFORMS.get(name);
164
+ }
165
+
166
+ /** List every registered platform definition (sorted by name for determinism). */
167
+ export function listPlatforms() {
168
+ return Array.from(PLATFORMS.values()).sort((a, b) => a.name.localeCompare(b.name));
169
+ }
170
+
171
+ /** Test/utility helper: drop a registration. */
172
+ export function clearPlatform(name) {
173
+ return PLATFORMS.delete(name);
174
+ }
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // Single-owner rule (H2).
178
+ // ---------------------------------------------------------------------------
179
+ //
180
+ // A platform whose real-time connection is owned by a DEDICATED launchd process
181
+ // must NOT also be started by the maestro daemon's generic channel loop —
182
+ // otherwise the channel is ingested TWICE (every DM answered twice).
183
+ //
184
+ // - "slack" is owned by `ai.maestro.<agent>-slack-socket`
185
+ // (generate-plists.sh → scripts/poller/slack-socket-mode.mjs), which reads
186
+ // config/slack.yaml itself for reactions / cc-channels / thread enrichment.
187
+ //
188
+ // This set is the SoT both the daemon (to skip) and any reviewer can consult.
189
+ // Platforms NOT in this set (telegram / whatsapp / voice) have no dedicated
190
+ // plist and ARE daemon-managed.
191
+ export const DEDICATED_PLIST_PLATFORMS = Object.freeze(new Set(["slack"]));
192
+
193
+ /**
194
+ * Should the maestro daemon's generic channel loop start this platform itself?
195
+ * False for platforms with a dedicated launchd owner (see above). Pure.
196
+ *
197
+ * @param {string} name platform name (== channel name)
198
+ * @returns {boolean}
199
+ */
200
+ export function isDaemonManagedPlatform(name) {
201
+ return !DEDICATED_PLIST_PLATFORMS.has(name);
202
+ }
203
+
204
+ export default {
205
+ EVENT_KINDS,
206
+ DEFAULT_EVENT_KIND,
207
+ normalizeKind,
208
+ makeMessageEvent,
209
+ definePlatform,
210
+ getPlatform,
211
+ listPlatforms,
212
+ clearPlatform,
213
+ DEDICATED_PLIST_PLATFORMS,
214
+ isDaemonManagedPlatform,
215
+ };
@@ -0,0 +1,137 @@
1
+ /**
2
+ * contract.test.mjs — node:test coverage for lib/channels/contract.mjs
3
+ *
4
+ * Covers: EventKind set + normalization, makeMessageEvent defaults, and the
5
+ * self-registering platform registry (definePlatform / getPlatform /
6
+ * listPlatforms / clearPlatform). Pure, hermetic — no IO.
7
+ */
8
+
9
+ import { test } from "node:test";
10
+ import assert from "node:assert/strict";
11
+
12
+ import {
13
+ EVENT_KINDS,
14
+ DEFAULT_EVENT_KIND,
15
+ normalizeKind,
16
+ makeMessageEvent,
17
+ definePlatform,
18
+ getPlatform,
19
+ listPlatforms,
20
+ clearPlatform,
21
+ DEDICATED_PLIST_PLATFORMS,
22
+ isDaemonManagedPlatform,
23
+ } from "./contract.mjs";
24
+
25
+ import { ChannelMessage } from "./index.mjs";
26
+
27
+ test("EVENT_KINDS is the closed set from the design", () => {
28
+ assert.deepEqual([...EVENT_KINDS].sort(), ["call", "channel_cc", "message", "reaction", "voice_note"]);
29
+ assert.equal(DEFAULT_EVENT_KIND, "message");
30
+ });
31
+
32
+ test("normalizeKind passes known kinds and defaults unknown to message", () => {
33
+ assert.equal(normalizeKind("reaction"), "reaction");
34
+ assert.equal(normalizeKind("channel_cc"), "channel_cc");
35
+ assert.equal(normalizeKind("voice_note"), "voice_note");
36
+ assert.equal(normalizeKind(undefined), "message");
37
+ assert.equal(normalizeKind("bogus"), "message");
38
+ assert.equal(normalizeKind(null), "message");
39
+ });
40
+
41
+ test("makeMessageEvent fills kind default and preserves other fields", () => {
42
+ const ev = makeMessageEvent({ channel: "slack", message_id: "1.2", text: "hi" });
43
+ assert.equal(ev.kind, "message");
44
+ assert.equal(ev.channel, "slack");
45
+ assert.equal(ev.text, "hi");
46
+
47
+ const reaction = makeMessageEvent({ channel: "telegram", kind: "reaction", text: "" });
48
+ assert.equal(reaction.kind, "reaction");
49
+ });
50
+
51
+ test("ChannelMessage is exported as a back-compat alias for MessageEvent", () => {
52
+ // The alias must be re-exported from the channels facade so existing
53
+ // `import { ChannelMessage }` sites resolve. It's a typedef carrier — the
54
+ // runtime value is a harmless namespace/sentinel, just assert it exists.
55
+ assert.notEqual(ChannelMessage, undefined, "ChannelMessage alias must be exported");
56
+ });
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Platform registry
60
+ // ---------------------------------------------------------------------------
61
+
62
+ test("definePlatform + getPlatform + listPlatforms round-trip", () => {
63
+ clearPlatform("test-plat-a");
64
+ clearPlatform("test-plat-b");
65
+
66
+ const factoryA = () => ({ name: "test-plat-a" });
67
+ const def = definePlatform({
68
+ name: "test-plat-a",
69
+ gateFile: "config/test-plat-a.yaml",
70
+ factory: factoryA,
71
+ capabilities: ["send", "react"],
72
+ });
73
+ assert.equal(def.name, "test-plat-a");
74
+ assert.equal(def.gateFile, "config/test-plat-a.yaml");
75
+ assert.deepEqual(def.capabilities, ["send", "react"]);
76
+
77
+ const got = getPlatform("test-plat-a");
78
+ assert.equal(got.factory, factoryA);
79
+ assert.deepEqual(got.capabilities, ["send", "react"]);
80
+
81
+ definePlatform({ name: "test-plat-b", gateFile: "config/test-plat-b.yaml", factory: () => ({}) });
82
+ const names = listPlatforms().map((p) => p.name);
83
+ assert.ok(names.includes("test-plat-a"));
84
+ assert.ok(names.includes("test-plat-b"));
85
+ // Sorted deterministically.
86
+ assert.deepEqual([...names].sort(), names);
87
+
88
+ clearPlatform("test-plat-a");
89
+ clearPlatform("test-plat-b");
90
+ });
91
+
92
+ test("definePlatform is idempotent by name (re-import safe)", () => {
93
+ clearPlatform("dup");
94
+ const f1 = () => ({ v: 1 });
95
+ const f2 = () => ({ v: 2 });
96
+ definePlatform({ name: "dup", gateFile: "config/dup.yaml", factory: f1 });
97
+ // Re-defining must NOT throw — it replaces (modules can re-eval in tests).
98
+ assert.doesNotThrow(() => definePlatform({ name: "dup", gateFile: "config/dup.yaml", factory: f2 }));
99
+ assert.equal(getPlatform("dup").factory, f2);
100
+ clearPlatform("dup");
101
+ });
102
+
103
+ test("definePlatform validates required fields", () => {
104
+ assert.throws(() => definePlatform(null), /def object required/);
105
+ assert.throws(() => definePlatform({ gateFile: "x", factory: () => {} }), /name required/);
106
+ assert.throws(() => definePlatform({ name: "x", factory: () => {} }), /gateFile required/);
107
+ assert.throws(() => definePlatform({ name: "x", gateFile: "x" }), /factory must be a function/);
108
+ });
109
+
110
+ test("capabilities default to an empty array and are copied (not aliased)", () => {
111
+ clearPlatform("nocap");
112
+ const src = ["send"];
113
+ const def = definePlatform({ name: "nocap", gateFile: "config/nocap.yaml", factory: () => {}, capabilities: src });
114
+ src.push("mutated");
115
+ assert.deepEqual(def.capabilities, ["send"], "registry must not alias the caller's array");
116
+ clearPlatform("nocap");
117
+ });
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // Single-owner rule (H2) — Slack must NOT be daemon-managed, because its
121
+ // dedicated launchd `slack-socket` process owns the real-time connection.
122
+ // Starting it in BOTH places double-ingests every DM. This invariant is the
123
+ // guard that two Slack connections can't both be live.
124
+ // ---------------------------------------------------------------------------
125
+
126
+ test("H2: Slack is excluded from daemon-managed platforms (dedicated plist owns it)", () => {
127
+ assert.ok(DEDICATED_PLIST_PLATFORMS.has("slack"), "slack has a dedicated launchd owner");
128
+ assert.equal(isDaemonManagedPlatform("slack"), false,
129
+ "the daemon generic loop must NOT start Slack — its slack-socket plist is the single owner");
130
+ });
131
+
132
+ test("H2: daemon-only channels (telegram/whatsapp/voice) remain daemon-managed", () => {
133
+ for (const name of ["telegram", "whatsapp", "voice"]) {
134
+ assert.equal(isDaemonManagedPlatform(name), true, `${name} has no dedicated plist → daemon-managed`);
135
+ assert.equal(DEDICATED_PLIST_PLATFORMS.has(name), false);
136
+ }
137
+ });
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Maestro — Conversation resolver
3
+ *
4
+ * Maps an inbound channel message → { agentId, sessionKey, threadId, channelName }
5
+ * so the dispatcher can deduplicate across channels and route a Slack-DM
6
+ * reply, a Gmail thread, a WhatsApp quoted-message, and a voice call to
7
+ * the same agent session if they belong to the same conversation.
8
+ *
9
+ * The resolver is pure (no IO). It accepts a ChannelMessage and returns
10
+ * a normalised key. Different inputs producing the same key SHOULD be
11
+ * treated as the same conversation by the daemon's session-lock layer.
12
+ *
13
+ * Drawn from the pattern in `openclaw/src/channels/conversation-resolution.ts`
14
+ * (MIT) but simplified to maestro's needs: we don't need a multi-agent
15
+ * routing layer because each maestro instance is single-agent.
16
+ */
17
+
18
+ /**
19
+ * Resolve a conversation key for an inbound message.
20
+ *
21
+ * @param {import("./channel.mjs").ChannelMessage} msg
22
+ * @returns {{ channel: string, sessionKey: string, threadId: string, peerId: string }}
23
+ */
24
+ export function resolveConversation(msg) {
25
+ if (!msg || typeof msg !== "object") {
26
+ throw new Error("resolveConversation: msg required");
27
+ }
28
+ const channel = String(msg.channel || "unknown").trim().toLowerCase();
29
+ // Normalise the identity components feeding the session key so that
30
+ // case-variant or whitespace-padded ids do NOT fork the session.
31
+ // Emails and ids are case-insensitive for routing purposes; phones are
32
+ // only trimmed (case has no meaning, casing them is a no-op but trim
33
+ // strips stray whitespace from upstream adapters).
34
+ const rawId = msg.from?.id;
35
+ const rawEmail = msg.from?.email;
36
+ const rawPhone = msg.from?.phone;
37
+ const peerId =
38
+ (rawId != null && String(rawId).trim().toLowerCase()) ||
39
+ (rawEmail != null && String(rawEmail).trim().toLowerCase()) ||
40
+ (rawPhone != null && String(rawPhone).trim()) ||
41
+ "unknown";
42
+ // threadId logic per channel — preserves existing maestro thread semantics.
43
+ let threadId;
44
+ switch (channel) {
45
+ case "slack":
46
+ // For Slack we treat thread_ts as the conversation; DMs without a
47
+ // thread collapse onto `dm:<channel>` so successive messages in
48
+ // the same DM bind to one session. When the Slack channel id is
49
+ // absent (some adapter paths omit msg.raw.channel) we fall back to
50
+ // the peerId so two different DMs do NOT collide on `dm:unknown`.
51
+ threadId = msg.thread_id || `dm:${msg.raw?.channel || peerId}`;
52
+ break;
53
+ case "gmail":
54
+ threadId = msg.thread_id || msg.in_reply_to || msg.message_id;
55
+ break;
56
+ case "whatsapp":
57
+ threadId = msg.thread_id || msg.in_reply_to || `peer:${peerId}`;
58
+ break;
59
+ case "sms":
60
+ threadId = `peer:${peerId}`;
61
+ break;
62
+ case "voice":
63
+ threadId = msg.thread_id || `call:${msg.message_id}`;
64
+ break;
65
+ case "calendar":
66
+ threadId = msg.thread_id || `event:${msg.message_id}`;
67
+ break;
68
+ default:
69
+ threadId = msg.thread_id || msg.message_id || "unknown";
70
+ }
71
+ const sessionKey = `${channel}:${threadId}`;
72
+ return { channel, sessionKey, threadId, peerId };
73
+ }
74
+
75
+ /**
76
+ * Decide whether two messages belong to the same conversation. Useful
77
+ * for dedup logic in the daemon's inbox processor.
78
+ */
79
+ export function sameConversation(msgA, msgB) {
80
+ const a = resolveConversation(msgA);
81
+ const b = resolveConversation(msgB);
82
+ return a.sessionKey === b.sessionKey;
83
+ }
84
+
85
+ /**
86
+ * Build a deterministic session lock key from a conversation. Mirrors
87
+ * the keys used by scripts/daemon/session-lock.mjs so the channel bus
88
+ * can be wired in without breaking existing locks.
89
+ */
90
+ export function sessionLockKey(msg) {
91
+ const { channel, threadId } = resolveConversation(msg);
92
+ return `${channel}:${threadId}`;
93
+ }
94
+
95
+ export default { resolveConversation, sameConversation, sessionLockKey };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Maestro — Gmail channel adapter
3
+ *
4
+ * Wraps the existing scripts/send-email.sh + Python email helpers in the
5
+ * Channel interface. Ingestion stays with scripts/poller/gmail-poller.mjs.
6
+ */
7
+
8
+ import { execFileSync } from "node:child_process";
9
+ import { existsSync, writeFileSync, mkdirSync } from "node:fs";
10
+ import { join, resolve } from "node:path";
11
+ import { tmpdir } from "node:os";
12
+ import { CHANNEL_CAPABILITIES } from "../channel.mjs";
13
+
14
+ const DEFAULT_CAPABILITIES = [
15
+ CHANNEL_CAPABILITIES.SEND,
16
+ CHANNEL_CAPABILITIES.THREAD,
17
+ CHANNEL_CAPABILITIES.MEDIA_IMAGE,
18
+ CHANNEL_CAPABILITIES.MEDIA_DOCUMENT,
19
+ ];
20
+
21
+ export function createGmailChannel(opts = {}) {
22
+ const agentRoot = resolve(opts.agentRoot || process.env.AGENT_ROOT || process.env.AGENT_DIR || process.cwd());
23
+ const sendScript = opts.sendScript || join(agentRoot, "scripts/send-email.sh");
24
+ const threadedScript = opts.threadedScript || join(agentRoot, "scripts/send-email-threaded.py");
25
+ const log = opts.log || ((lvl, m) => process.stdout.write(`[gmail-channel:${lvl}] ${m}\n`));
26
+
27
+ return {
28
+ name: "gmail",
29
+ capabilities: DEFAULT_CAPABILITIES,
30
+
31
+ async start({ onInbound }) {
32
+ if (typeof onInbound !== "function") {
33
+ throw new Error("Gmail.start: onInbound required");
34
+ }
35
+ const inboxDir = join(agentRoot, "state/inbox/gmail");
36
+ if (!existsSync(inboxDir)) {
37
+ log("warn", `gmail inbox dir ${inboxDir} not present; running in send-only mode`);
38
+ }
39
+ },
40
+
41
+ async send(msg) {
42
+ if (!msg || !msg.to) throw new Error("Gmail.send: msg.to required");
43
+ if (msg.thread_ref && existsSync(threadedScript)) {
44
+ // Use the Python threaded sender that preserves Message-ID / In-Reply-To.
45
+ const args = [
46
+ threadedScript,
47
+ "--to", msg.to,
48
+ "--subject", msg.meta?.subject || "(no subject)",
49
+ "--body", msg.text || "",
50
+ "--in-reply-to", String(msg.thread_ref),
51
+ ];
52
+ execFileSync("python3", args, {
53
+ encoding: "utf-8",
54
+ timeout: 60_000,
55
+ env: { ...process.env, AGENT_ROOT: agentRoot, AGENT_DIR: agentRoot },
56
+ });
57
+ return { message_id: "", raw: { threaded: true } };
58
+ }
59
+ if (!existsSync(sendScript)) {
60
+ throw new Error(`Gmail.send: ${sendScript} not found`);
61
+ }
62
+ const args = [
63
+ msg.to,
64
+ "--subject", msg.meta?.subject || "(no subject)",
65
+ "--body", msg.text || "",
66
+ ];
67
+ if (msg.meta?.cc) args.push("--cc", msg.meta.cc);
68
+ if (msg.meta?.bcc) args.push("--bcc", msg.meta.bcc);
69
+ const out = execFileSync(sendScript, args, {
70
+ encoding: "utf-8",
71
+ timeout: 60_000,
72
+ env: { ...process.env, AGENT_ROOT: agentRoot, AGENT_DIR: agentRoot },
73
+ });
74
+ const idMatch = out.match(/Message-ID:\s*<([^>]+)>/i);
75
+ return { message_id: idMatch?.[1] || "", raw: { stdout: out } };
76
+ },
77
+
78
+ async healthCheck() {
79
+ const present = existsSync(sendScript);
80
+ return { ok: present, detail: present ? "send-email.sh present" : `missing ${sendScript}` };
81
+ },
82
+
83
+ async stop() {},
84
+ };
85
+ }
86
+
87
+ export default createGmailChannel;