@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,290 @@
1
+ /**
2
+ * events.test.mjs — node:test coverage for the diagnostic-event spine.
3
+ *
4
+ * Hermetic: each test writes into an isolated tmpdir and injects a fixed clock,
5
+ * so date-stamped filenames and timestamps are deterministic. We assert the
6
+ * on-disk JSONL is valid, carries trace_id, and that emitEvent NEVER throws —
7
+ * including on an unwritable directory.
8
+ */
9
+
10
+ import { test } from "node:test";
11
+ import assert from "node:assert/strict";
12
+ import { promises as fsp } from "node:fs";
13
+ import { readFileSync, existsSync } from "node:fs";
14
+ import { tmpdir } from "node:os";
15
+ import { join } from "node:path";
16
+
17
+ import {
18
+ emitEvent,
19
+ isValidEventType,
20
+ EVENT_TYPES,
21
+ } from "./events.mjs";
22
+ import { mintTraceId, withTrace } from "./trace.mjs";
23
+
24
+ async function makeDir() {
25
+ const path = join(
26
+ tmpdir(),
27
+ `events-test-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
28
+ );
29
+ await fsp.mkdir(path, { recursive: true });
30
+ return path;
31
+ }
32
+ async function rm(path) {
33
+ try { await fsp.rm(path, { recursive: true, force: true }); } catch { /* */ }
34
+ }
35
+
36
+ const FIXED_MS = Date.UTC(2026, 5, 11, 12, 30, 0); // 2026-06-11T12:30:00Z
37
+ const fixedNow = () => FIXED_MS;
38
+
39
+ function readRows(logDir) {
40
+ const file = join(logDir, "2026-06-11.jsonl");
41
+ const body = readFileSync(file, "utf-8");
42
+ return body
43
+ .trim()
44
+ .split("\n")
45
+ .filter(Boolean)
46
+ .map((l) => JSON.parse(l)); // throws if any line is not valid JSON
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // EVENT_TYPES contract
51
+ // ---------------------------------------------------------------------------
52
+
53
+ test("EVENT_TYPES is the closed 8-type set and is frozen", () => {
54
+ const values = Object.values(EVENT_TYPES).sort();
55
+ assert.deepEqual(values, [
56
+ "classified",
57
+ "dispatched",
58
+ "error",
59
+ "guard_fail",
60
+ "item_received",
61
+ "sent",
62
+ "session_closed",
63
+ "session_opened",
64
+ ]);
65
+ assert.ok(Object.isFrozen(EVENT_TYPES));
66
+ });
67
+
68
+ test("isValidEventType only accepts the contract types", () => {
69
+ assert.equal(isValidEventType("item_received"), true);
70
+ assert.equal(isValidEventType("sent"), true);
71
+ assert.equal(isValidEventType("nope"), false);
72
+ assert.equal(isValidEventType(""), false);
73
+ assert.equal(isValidEventType(null), false);
74
+ assert.equal(isValidEventType(42), false);
75
+ });
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // emitEvent — happy path writes valid JSONL with trace_id
79
+ // ---------------------------------------------------------------------------
80
+
81
+ test("emitEvent writes a valid JSONL row with the full contract shape", async () => {
82
+ const logDir = await makeDir();
83
+ try {
84
+ const traceId = mintTraceId();
85
+ const ok = emitEvent(
86
+ {
87
+ type: EVENT_TYPES.ITEM_RECEIVED,
88
+ trace_id: traceId,
89
+ span: "root",
90
+ attrs: { source: "slack", item_id: "C123.456" },
91
+ },
92
+ { now: fixedNow, logDir }
93
+ );
94
+ assert.equal(ok, true);
95
+
96
+ const rows = readRows(logDir);
97
+ assert.equal(rows.length, 1);
98
+ const row = rows[0];
99
+ assert.equal(row.type, "item_received");
100
+ assert.equal(row.trace_id, traceId);
101
+ assert.equal(row.span, "root");
102
+ assert.equal(row.ts, "2026-06-11T12:30:00.000Z");
103
+ assert.deepEqual(row.attrs, { source: "slack", item_id: "C123.456" });
104
+ } finally {
105
+ await rm(logDir);
106
+ }
107
+ });
108
+
109
+ test("emitEvent appends successive rows to the same date stream", async () => {
110
+ const logDir = await makeDir();
111
+ try {
112
+ const t = mintTraceId();
113
+ emitEvent({ type: "item_received", trace_id: t }, { now: fixedNow, logDir });
114
+ emitEvent({ type: "classified", trace_id: t }, { now: fixedNow, logDir });
115
+ emitEvent({ type: "sent", trace_id: t }, { now: fixedNow, logDir });
116
+ const rows = readRows(logDir);
117
+ assert.equal(rows.length, 3);
118
+ assert.deepEqual(rows.map((r) => r.type), ["item_received", "classified", "sent"]);
119
+ // All correlate to one interaction.
120
+ assert.ok(rows.every((r) => r.trace_id === t));
121
+ } finally {
122
+ await rm(logDir);
123
+ }
124
+ });
125
+
126
+ test("emitEvent names the file by UTC date of the event", async () => {
127
+ const logDir = await makeDir();
128
+ try {
129
+ emitEvent({ type: "sent" }, { now: fixedNow, logDir });
130
+ assert.ok(existsSync(join(logDir, "2026-06-11.jsonl")));
131
+ } finally {
132
+ await rm(logDir);
133
+ }
134
+ });
135
+
136
+ // ---------------------------------------------------------------------------
137
+ // emitEvent — ambient trace context fills trace_id/span
138
+ // ---------------------------------------------------------------------------
139
+
140
+ test("emitEvent inherits trace_id/span from the ambient withTrace context", async () => {
141
+ const logDir = await makeDir();
142
+ try {
143
+ const traceId = mintTraceId();
144
+ withTrace({ trace_id: traceId, span: "root.classify" }, () => {
145
+ emitEvent({ type: "classified", attrs: { category: "action_required" } }, {
146
+ now: fixedNow,
147
+ logDir,
148
+ });
149
+ });
150
+ const [row] = readRows(logDir);
151
+ assert.equal(row.trace_id, traceId);
152
+ assert.equal(row.span, "root.classify");
153
+ } finally {
154
+ await rm(logDir);
155
+ }
156
+ });
157
+
158
+ test("emitEvent trace_id is null when no id supplied and no ambient context", async () => {
159
+ const logDir = await makeDir();
160
+ try {
161
+ emitEvent({ type: "error", attrs: { msg: "boom" } }, { now: fixedNow, logDir });
162
+ const [row] = readRows(logDir);
163
+ assert.equal(row.trace_id, null);
164
+ assert.equal(row.span, "root");
165
+ } finally {
166
+ await rm(logDir);
167
+ }
168
+ });
169
+
170
+ test("an explicit trace_id overrides the ambient context", async () => {
171
+ const logDir = await makeDir();
172
+ try {
173
+ const ambient = mintTraceId();
174
+ const explicit = mintTraceId();
175
+ withTrace(ambient, () => {
176
+ emitEvent({ type: "sent", trace_id: explicit }, { now: fixedNow, logDir });
177
+ });
178
+ const [row] = readRows(logDir);
179
+ assert.equal(row.trace_id, explicit);
180
+ } finally {
181
+ await rm(logDir);
182
+ }
183
+ });
184
+
185
+ // ---------------------------------------------------------------------------
186
+ // emitEvent — closed type set: unknown type is coerced to error
187
+ // ---------------------------------------------------------------------------
188
+
189
+ test("an unknown event type is coerced to error and preserves the original", async () => {
190
+ const logDir = await makeDir();
191
+ try {
192
+ emitEvent({ type: "totally_made_up", attrs: { a: 1 } }, { now: fixedNow, logDir });
193
+ const [row] = readRows(logDir);
194
+ assert.equal(row.type, "error");
195
+ assert.equal(row.attrs._invalid_type, "totally_made_up");
196
+ assert.equal(row.attrs.a, 1);
197
+ } finally {
198
+ await rm(logDir);
199
+ }
200
+ });
201
+
202
+ test("a missing type is coerced to error with _invalid_type null", async () => {
203
+ const logDir = await makeDir();
204
+ try {
205
+ emitEvent({ attrs: {} }, { now: fixedNow, logDir });
206
+ const [row] = readRows(logDir);
207
+ assert.equal(row.type, "error");
208
+ assert.equal(row.attrs._invalid_type, null);
209
+ } finally {
210
+ await rm(logDir);
211
+ }
212
+ });
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // emitEvent — never throws; degrades to false
216
+ // ---------------------------------------------------------------------------
217
+
218
+ test("emitEvent never throws on a bad/unwritable dir (returns false)", async () => {
219
+ const logDir = await makeDir();
220
+ try {
221
+ // Make the diagnostics dir a FILE so mkdir/append fails.
222
+ const blocker = join(logDir, "blocked");
223
+ await fsp.writeFile(blocker, "x");
224
+ // Point logDir at a path under the file → cannot create a directory there.
225
+ const badDir = join(blocker, "diagnostics");
226
+ let ok;
227
+ assert.doesNotThrow(() => {
228
+ ok = emitEvent({ type: "sent", trace_id: "T" }, { now: fixedNow, logDir: badDir });
229
+ });
230
+ assert.equal(ok, false);
231
+ } finally {
232
+ await rm(logDir);
233
+ }
234
+ });
235
+
236
+ test("emitEvent never throws on a null/garbage event argument", () => {
237
+ let ok;
238
+ assert.doesNotThrow(() => {
239
+ ok = emitEvent(null, { append: () => true });
240
+ });
241
+ assert.equal(ok, true); // coerced to {} → error row, write succeeds
242
+ });
243
+
244
+ test("emitEvent returns false when the injected appender fails", () => {
245
+ const ok = emitEvent(
246
+ { type: "sent", trace_id: "T" },
247
+ { append: () => false }
248
+ );
249
+ assert.equal(ok, false);
250
+ });
251
+
252
+ test("emitEvent never throws when the appender itself throws", () => {
253
+ let ok;
254
+ assert.doesNotThrow(() => {
255
+ ok = emitEvent({ type: "sent" }, {
256
+ append: () => { throw new Error("disk gone"); },
257
+ });
258
+ });
259
+ assert.equal(ok, false);
260
+ });
261
+
262
+ test("emitEvent tolerates non-object attrs by wrapping them", async () => {
263
+ const logDir = await makeDir();
264
+ try {
265
+ emitEvent({ type: "sent", trace_id: "T", attrs: "just a string" }, {
266
+ now: fixedNow,
267
+ logDir,
268
+ });
269
+ const [row] = readRows(logDir);
270
+ assert.deepEqual(row.attrs, { value: "just a string" });
271
+ } finally {
272
+ await rm(logDir);
273
+ }
274
+ });
275
+
276
+ test("emitEvent uses the injected appender with the date-stamped path", () => {
277
+ const calls = [];
278
+ const ok = emitEvent(
279
+ { type: "dispatched", trace_id: "T" },
280
+ {
281
+ now: fixedNow,
282
+ logDir: "/somewhere/logs/diagnostics",
283
+ append: (path, record) => { calls.push({ path, record }); return true; },
284
+ }
285
+ );
286
+ assert.equal(ok, true);
287
+ assert.equal(calls.length, 1);
288
+ assert.equal(calls[0].path, "/somewhere/logs/diagnostics/2026-06-11.jsonl");
289
+ assert.equal(calls[0].record.type, "dispatched");
290
+ });
@@ -0,0 +1,237 @@
1
+ /**
2
+ * lib/diagnostics/otel.mjs — Claude Code native OTLP enablement (env builder).
3
+ *
4
+ * For a Claude-Code fleet, ~70% of observability is "turn on the env vars":
5
+ * Claude Code natively emits OpenTelemetry metrics/logs/traces — cost-per-session
6
+ * in USD, `api_error`/`api_refusal`/`tool_decision` events, `TRACEPARENT`
7
+ * inheritance — once `CLAUDE_CODE_ENABLE_TELEMETRY=1` and the `OTEL_*` exporter
8
+ * vars are set (gaps-enterprise-ops G7/G14). This module is the single, pure
9
+ * BUILDER for that env block, plus the `OTEL_RESOURCE_ATTRIBUTES` string that
10
+ * stamps `service.name` and the agent id onto every signal so a fleet collector
11
+ * can attribute traces/metrics to the right Mac mini.
12
+ *
13
+ * It is a pure function over inputs (an env source + agent identity) returning a
14
+ * plain `{KEY: value}` map. It does NOT mutate `process.env`, spawn anything, or
15
+ * add an OpenTelemetry SDK dependency — maestro is near-zero-dep, and Claude Code
16
+ * already ships the SDK. The returned map is what you spread into a spawned
17
+ * sub-session's env, or render into managed `settings.json` `env` for fleet-wide
18
+ * rollout (see docs/guides/observability-otel.md).
19
+ *
20
+ * Disabled by default. The block is produced only when telemetry is explicitly
21
+ * enabled (`MAESTRO_OTEL=1`, or `config.enabled === true`); otherwise an EMPTY
22
+ * map is returned, so an un-configured agent emits nothing and spreading the
23
+ * result is a no-op.
24
+ *
25
+ * Constraints (CLAUDE.md): ESM, Node built-ins only, pure + injectable (env and
26
+ * identity are passed in, never read implicitly beyond the default `process.env`
27
+ * convenience). Never throws.
28
+ *
29
+ * @module lib/diagnostics/otel
30
+ */
31
+
32
+ /** Truthy env strings, mirroring Claude Code's own "1/true/yes/on" convention. */
33
+ const TRUTHY = new Set(["1", "true", "yes", "on"]);
34
+
35
+ /**
36
+ * Interpret a value as a boolean flag the way the rest of maestro / Claude Code
37
+ * does: case-insensitive 1/true/yes/on.
38
+ * @param {unknown} v
39
+ * @returns {boolean}
40
+ */
41
+ function isTruthy(v) {
42
+ return TRUTHY.has(String(v == null ? "" : v).trim().toLowerCase());
43
+ }
44
+
45
+ /**
46
+ * Decide whether telemetry is enabled. Order: explicit `config.enabled`
47
+ * (boolean) wins; otherwise the `MAESTRO_OTEL` env flag.
48
+ * @param {object} opts
49
+ * @param {Record<string, string|undefined>} opts.env
50
+ * @param {{enabled?: boolean}} opts.config
51
+ * @returns {boolean}
52
+ */
53
+ export function otelEnabled({ env = process.env, config = {} } = {}) {
54
+ if (typeof config.enabled === "boolean") return config.enabled;
55
+ return isTruthy(env.MAESTRO_OTEL);
56
+ }
57
+
58
+ /**
59
+ * Sanitize one `OTEL_RESOURCE_ATTRIBUTES` value: it is a comma-separated
60
+ * `k=v,k=v` string, so a `,` or `=` inside a value would corrupt the list. We
61
+ * strip those (and surrounding whitespace) rather than percent-encode, keeping
62
+ * the attribute human-readable in a collector UI. Empty after stripping → "".
63
+ * @param {unknown} v
64
+ * @returns {string}
65
+ */
66
+ function sanitizeAttrValue(v) {
67
+ return String(v == null ? "" : v)
68
+ .replace(/[,=]/g, " ")
69
+ .trim();
70
+ }
71
+
72
+ /**
73
+ * Derive a stable agent id for `service.instance.id` / the `agent.id` resource
74
+ * attribute from an agent identity record (config/agent.json shape). Prefers an
75
+ * explicit id, then the repo slug, then a slugified full name. Returns "" when
76
+ * nothing usable is present (the attribute is then omitted, not blank).
77
+ * @param {object} identity
78
+ * @returns {string}
79
+ */
80
+ export function deriveAgentId(identity = {}) {
81
+ const raw =
82
+ identity.agentId ||
83
+ identity.id ||
84
+ identity.repoSlug ||
85
+ identity.slackMemberId ||
86
+ identity.fullName ||
87
+ identity.firstName ||
88
+ "";
89
+ return String(raw)
90
+ .trim()
91
+ .toLowerCase()
92
+ .replace(/[^a-z0-9._-]+/g, "-")
93
+ .replace(/^-+|-+$/g, "");
94
+ }
95
+
96
+ /**
97
+ * Build the `OTEL_RESOURCE_ATTRIBUTES` value: `service.name`, `service.version`,
98
+ * the agent id (as both `service.instance.id` and a friendly `agent.id`), and
99
+ * any extra caller-supplied attributes. Skips empty values so the list never
100
+ * carries a dangling `k=`.
101
+ * @param {object} opts
102
+ * @param {string} opts.serviceName
103
+ * @param {string} [opts.serviceVersion]
104
+ * @param {string} [opts.agentId]
105
+ * @param {Record<string, string>} [opts.extra]
106
+ * @returns {string}
107
+ */
108
+ export function buildResourceAttributes({
109
+ serviceName,
110
+ serviceVersion,
111
+ agentId,
112
+ extra = {},
113
+ } = {}) {
114
+ const pairs = [];
115
+ const push = (k, v) => {
116
+ const val = sanitizeAttrValue(v);
117
+ if (k && val) pairs.push(`${k}=${val}`);
118
+ };
119
+ push("service.name", serviceName);
120
+ push("service.version", serviceVersion);
121
+ push("service.instance.id", agentId);
122
+ push("agent.id", agentId);
123
+ for (const [k, v] of Object.entries(extra)) push(k, v);
124
+ return pairs.join(",");
125
+ }
126
+
127
+ /**
128
+ * Build the managed-settings env block that enables Claude Code's native OTLP
129
+ * telemetry for THIS agent.
130
+ *
131
+ * Returns an empty object `{}` when telemetry is not enabled (see otelEnabled),
132
+ * so the caller can unconditionally spread it. When enabled, returns the env map:
133
+ *
134
+ * CLAUDE_CODE_ENABLE_TELEMETRY "1"
135
+ * OTEL_METRICS_EXPORTER metrics exporter (default "otlp")
136
+ * OTEL_LOGS_EXPORTER logs/events exporter (default "otlp")
137
+ * OTEL_TRACES_EXPORTER traces exporter (default "otlp")
138
+ * OTEL_EXPORTER_OTLP_PROTOCOL "grpc" | "http/protobuf" | "http/json"
139
+ * OTEL_EXPORTER_OTLP_ENDPOINT collector endpoint (when configured)
140
+ * OTEL_EXPORTER_OTLP_HEADERS auth headers (when configured)
141
+ * OTEL_METRIC_EXPORT_INTERVAL ms between metric exports (when configured)
142
+ * OTEL_LOGS_EXPORT_INTERVAL ms between log exports (when configured)
143
+ * OTEL_RESOURCE_ATTRIBUTES service.name + agent id (+ extras)
144
+ *
145
+ * All exporter knobs are read from `config` first, then `env` (so an operator
146
+ * can set them in managed `settings.json` `env` OR in the shell), then a small
147
+ * set of safe defaults. Endpoint/headers/intervals are emitted only when a value
148
+ * is present — Claude Code's own OTLP defaults apply otherwise.
149
+ *
150
+ * @param {object} [opts]
151
+ * @param {Record<string, string|undefined>} [opts.env] env source (default process.env)
152
+ * @param {object} [opts.config] telemetry config block (config/observability.yaml shape)
153
+ * @param {boolean} [opts.config.enabled]
154
+ * @param {string} [opts.config.endpoint]
155
+ * @param {string} [opts.config.protocol]
156
+ * @param {string} [opts.config.headers]
157
+ * @param {string} [opts.config.metricsExporter]
158
+ * @param {string} [opts.config.logsExporter]
159
+ * @param {string} [opts.config.tracesExporter]
160
+ * @param {number|string} [opts.config.metricExportIntervalMs]
161
+ * @param {number|string} [opts.config.logsExportIntervalMs]
162
+ * @param {Record<string, string>} [opts.config.resourceAttributes] extra attrs
163
+ * @param {object} [opts.identity] agent identity (config/agent.json shape)
164
+ * @param {string} [opts.serviceName] override service.name (default "maestro-agent")
165
+ * @param {string} [opts.serviceVersion] override service.version
166
+ * @returns {Record<string, string>} env map (empty when disabled)
167
+ */
168
+ export function buildOtelEnv(opts = {}) {
169
+ try {
170
+ const env = opts.env || process.env;
171
+ const config = opts.config || {};
172
+ if (!otelEnabled({ env, config })) return {};
173
+
174
+ const identity = opts.identity || {};
175
+ const serviceName =
176
+ opts.serviceName || config.serviceName || env.OTEL_SERVICE_NAME || "maestro-agent";
177
+ const serviceVersion =
178
+ opts.serviceVersion ||
179
+ config.serviceVersion ||
180
+ identity.maestroVersion ||
181
+ identity.version ||
182
+ "";
183
+ const agentId = deriveAgentId(identity);
184
+
185
+ // Resolution helper: config wins, then env, then default.
186
+ const pick = (cfgKey, envKey, dflt) => {
187
+ const c = config[cfgKey];
188
+ if (c != null && String(c).trim() !== "") return String(c).trim();
189
+ const e = env[envKey];
190
+ if (e != null && String(e).trim() !== "") return String(e).trim();
191
+ return dflt;
192
+ };
193
+
194
+ const out = {
195
+ CLAUDE_CODE_ENABLE_TELEMETRY: "1",
196
+ OTEL_METRICS_EXPORTER: pick("metricsExporter", "OTEL_METRICS_EXPORTER", "otlp"),
197
+ OTEL_LOGS_EXPORTER: pick("logsExporter", "OTEL_LOGS_EXPORTER", "otlp"),
198
+ OTEL_TRACES_EXPORTER: pick("tracesExporter", "OTEL_TRACES_EXPORTER", "otlp"),
199
+ OTEL_EXPORTER_OTLP_PROTOCOL: pick(
200
+ "protocol",
201
+ "OTEL_EXPORTER_OTLP_PROTOCOL",
202
+ "http/protobuf"
203
+ ),
204
+ };
205
+
206
+ // Optional knobs: only emit when an actual value exists so Claude Code's own
207
+ // OTLP defaults apply otherwise (don't clobber with a blank).
208
+ const endpoint = pick("endpoint", "OTEL_EXPORTER_OTLP_ENDPOINT", "");
209
+ if (endpoint) out.OTEL_EXPORTER_OTLP_ENDPOINT = endpoint;
210
+
211
+ const headers = pick("headers", "OTEL_EXPORTER_OTLP_HEADERS", "");
212
+ if (headers) out.OTEL_EXPORTER_OTLP_HEADERS = headers;
213
+
214
+ const metricInterval = pick(
215
+ "metricExportIntervalMs",
216
+ "OTEL_METRIC_EXPORT_INTERVAL",
217
+ ""
218
+ );
219
+ if (metricInterval) out.OTEL_METRIC_EXPORT_INTERVAL = String(metricInterval);
220
+
221
+ const logsInterval = pick("logsExportIntervalMs", "OTEL_LOGS_EXPORT_INTERVAL", "");
222
+ if (logsInterval) out.OTEL_LOGS_EXPORT_INTERVAL = String(logsInterval);
223
+
224
+ const resourceAttrs = buildResourceAttributes({
225
+ serviceName,
226
+ serviceVersion,
227
+ agentId,
228
+ extra: config.resourceAttributes || {},
229
+ });
230
+ if (resourceAttrs) out.OTEL_RESOURCE_ATTRIBUTES = resourceAttrs;
231
+
232
+ return out;
233
+ } catch {
234
+ // A pure builder must never break a spawn path; degrade to "no telemetry".
235
+ return {};
236
+ }
237
+ }