@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,512 @@
1
+ /**
2
+ * lib/telemetry/collect.mjs — build the SHARED STATUS SNAPSHOT for an agent.
3
+ *
4
+ * THE GAP this closes: the org mesh presence-beat already proves an agent is
5
+ * alive, but it carries no picture of WHAT the agent is doing or HOW healthy its
6
+ * machine is. `collectStatus` assembles the one snapshot the whole fleet agrees
7
+ * on — emitted by the client on every presence beat, stored by the server, and
8
+ * read back by `lib/org/activity.mjs` so any agent (or a human) can ask "who is
9
+ * doing what right now". The snapshot shape is the frozen contract:
10
+ *
11
+ * status = {
12
+ * state: "active"|"idle"|"busy"|"error"|"offline",
13
+ * activity: string, // human-readable ("handling inbox" / "session: <taskClass>" / "idle")
14
+ * subAgentsRunning: number, // running claude child processes (best-effort)
15
+ * session: { id?, taskClass?, model? } | null,
16
+ * machine: {
17
+ * loadavg: [n,n,n],
18
+ * memUsedPct: number, // 1 - free/total
19
+ * cpuPct?: number, // best-effort (powermetrics)
20
+ * tempC?: number, // best-effort (powermetrics)
21
+ * diskUsedPct?: number,
22
+ * },
23
+ * claudeAuth: "ok"|"relogin_required"|"unknown",
24
+ * alerts: [{ id, severity, kind, detail }],
25
+ * ts: ISO8601,
26
+ * }
27
+ *
28
+ * It rides `presence.beat` params as `params.status` — NO protocol change (the
29
+ * server handler validates beat params and stores the snapshot).
30
+ *
31
+ * DESIGN: pure-ish + fully injectable. The real I/O surfaces (node:os, the
32
+ * session/presence state on disk, `powermetrics`, `df`, the log files) are all
33
+ * reached through an injectable `deps` bag so tests are hermetic and CI never
34
+ * forks `powermetrics`. Every gather step is independently fail-open: a broken
35
+ * probe degrades that one field (best-effort fields drop out; required fields get
36
+ * a benign default), it NEVER throws and NEVER blocks the beat.
37
+ *
38
+ * G16 (zero-LLM-in-the-coordination-loop): pure os reads + file reads + a guarded
39
+ * `powermetrics`/`df` exec. ZERO LLM calls.
40
+ *
41
+ * Constraints (CLAUDE.md): ESM, execFile (not exec), Node built-ins only,
42
+ * everything injectable.
43
+ *
44
+ * @module lib/telemetry/collect
45
+ */
46
+
47
+ "use strict";
48
+
49
+ import os from "node:os";
50
+ import { execFile } from "node:child_process";
51
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
52
+ import { join, resolve } from "node:path";
53
+
54
+ import { list as listPresence } from "../collective/presence.mjs";
55
+ import { liveClaudeStats } from "../resource-governor.mjs";
56
+
57
+ /** Default temperature probe ceiling — used only as a guard in alerts; here we just report. */
58
+ const VALID_STATES = new Set(["active", "idle", "busy", "error", "offline"]);
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Small helpers
62
+ // ---------------------------------------------------------------------------
63
+
64
+ function nowMsFrom(now) {
65
+ if (typeof now === "function") { const v = Number(now()); return Number.isFinite(v) ? v : Date.now(); }
66
+ if (Number.isFinite(now)) return Number(now);
67
+ return Date.now();
68
+ }
69
+
70
+ function pct(n) {
71
+ const v = Number(n);
72
+ if (!Number.isFinite(v)) return 0;
73
+ return Math.round(Math.max(0, Math.min(100, v)) * 10) / 10;
74
+ }
75
+
76
+ function safeReadJson(path) {
77
+ try { return JSON.parse(readFileSync(path, "utf8")); } catch { return null; }
78
+ }
79
+
80
+ /**
81
+ * Run a command via execFile with a hard timeout, resolving to its stdout string
82
+ * or "" on ANY failure (timeout, missing binary, non-zero exit). Never rejects —
83
+ * a slow/absent powermetrics must never wedge the beat.
84
+ */
85
+ function execFileSafe(execFileImpl, cmd, args, timeoutMs) {
86
+ return new Promise((res) => {
87
+ let done = false;
88
+ const finish = (v) => { if (!done) { done = true; res(v || ""); } };
89
+ try {
90
+ const child = execFileImpl(cmd, args, { timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024 }, (err, stdout) => {
91
+ if (err) return finish("");
92
+ finish(typeof stdout === "string" ? stdout : "");
93
+ });
94
+ // execFile's own timeout kills the child; this belt-and-braces guard
95
+ // resolves us even if the callback never fires.
96
+ const t = setTimeout(() => finish(""), timeoutMs + 250);
97
+ if (t && typeof t.unref === "function") t.unref();
98
+ if (child && typeof child.on === "function") child.on("error", () => finish(""));
99
+ } catch { finish(""); }
100
+ });
101
+ }
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // State + activity (what is the agent doing right now)
105
+ // ---------------------------------------------------------------------------
106
+
107
+ /**
108
+ * Resolve the current session/activity from local daemon + presence state. We
109
+ * look at, in order:
110
+ * 1. an explicit `deps.sessions` array (injected; tests)
111
+ * 2. the live presence registry (state/collective/presence) — daemon + sessions
112
+ * The "primary" session is the most-recently-beating non-daemon session, else the
113
+ * daemon record. We derive:
114
+ * - session: { id?, taskClass?, model? } | null
115
+ * - activity: a human-readable line
116
+ * - state: idle | busy | active
117
+ * Best-effort; never throws.
118
+ *
119
+ * @returns {{ session:object|null, activity:string, state:string }}
120
+ */
121
+ export function resolveActivity(o = {}) {
122
+ const agentRoot = o.agentRoot;
123
+ const nowMs = nowMsFrom(o.now);
124
+ let records = [];
125
+ try {
126
+ if (Array.isArray(o.sessions)) records = o.sessions;
127
+ else if (agentRoot) records = listPresence(agentRoot, { nowMs, reap: false });
128
+ } catch { records = []; }
129
+ records = Array.isArray(records) ? records.filter((r) => r && typeof r === "object") : [];
130
+
131
+ // Split daemon vs work sessions.
132
+ const work = records.filter((r) => (r.kind || "interactive") !== "daemon");
133
+ const daemons = records.filter((r) => (r.kind || "interactive") === "daemon");
134
+
135
+ // Primary = newest-beating work session, else the daemon.
136
+ const byBeat = (a, b) => Date.parse(b.lastBeat || b.startedAt || 0) - Date.parse(a.lastBeat || a.startedAt || 0);
137
+ const primary = work.slice().sort(byBeat)[0] || daemons.slice().sort(byBeat)[0] || null;
138
+
139
+ // Map a presence record's intent → a taskClass label. The daemon writes
140
+ // intent like "session: <taskClass>" / "<cadence>"; we surface the raw intent
141
+ // as the activity line and best-effort split a taskClass out of it.
142
+ let session = null;
143
+ let activity = "idle";
144
+ let state = "idle";
145
+
146
+ if (work.length > 0 && primary) {
147
+ state = "busy";
148
+ const taskClass = deriveTaskClass(primary.intent);
149
+ session = {
150
+ id: primary.sessionId || undefined,
151
+ taskClass: taskClass || undefined,
152
+ model: primary.model || undefined,
153
+ };
154
+ activity = humanActivity(primary.intent, taskClass);
155
+ } else if (daemons.length > 0) {
156
+ // Daemon up but no work session running → the agent is alive and idle.
157
+ state = "idle";
158
+ activity = "idle";
159
+ session = null;
160
+ } else {
161
+ state = "idle";
162
+ activity = "idle";
163
+ session = null;
164
+ }
165
+
166
+ return { session, activity, state };
167
+ }
168
+
169
+ /** Pull a taskClass out of an intent string ("session: inbox" → "inbox"). */
170
+ function deriveTaskClass(intent) {
171
+ const s = String(intent || "").trim();
172
+ if (!s) return "";
173
+ const m = s.match(/(?:session|task|cadence)\s*[:=]\s*([a-z0-9._-]+)/i);
174
+ if (m) return m[1];
175
+ // A bare single-token intent (e.g. "inbox", "morning-brief") is itself the class.
176
+ if (/^[a-z0-9._-]+$/i.test(s)) return s;
177
+ return "";
178
+ }
179
+
180
+ /** Build a human-readable activity line from an intent + derived taskClass. */
181
+ function humanActivity(intent, taskClass) {
182
+ const s = String(intent || "").trim();
183
+ if (s) return s.length > 120 ? `${s.slice(0, 117)}...` : s;
184
+ if (taskClass) return `session: ${taskClass}`;
185
+ return "working";
186
+ }
187
+
188
+ // ---------------------------------------------------------------------------
189
+ // Machine stats
190
+ // ---------------------------------------------------------------------------
191
+
192
+ /**
193
+ * Gather machine stats from node:os plus best-effort `powermetrics` (temp/cpu)
194
+ * and `df` (disk). The exec probes are guarded + injected; on macOS powermetrics
195
+ * needs root so a non-root agent simply gets no temp/cpu (those fields drop out).
196
+ *
197
+ * @returns {Promise<{loadavg:number[], memUsedPct:number, cpuPct?:number, tempC?:number, diskUsedPct?:number}>}
198
+ */
199
+ export async function collectMachine(o = {}) {
200
+ const osImpl = o.os || os;
201
+ const machine = { loadavg: [0, 0, 0], memUsedPct: 0 };
202
+
203
+ // loadavg
204
+ try {
205
+ const la = osImpl.loadavg();
206
+ if (Array.isArray(la) && la.length >= 3) {
207
+ machine.loadavg = [round2(la[0]), round2(la[1]), round2(la[2])];
208
+ }
209
+ } catch { /* keep [0,0,0] */ }
210
+
211
+ // memUsedPct = 1 - free/total
212
+ try {
213
+ const total = Number(osImpl.totalmem());
214
+ const free = Number(osImpl.freemem());
215
+ if (Number.isFinite(total) && total > 0 && Number.isFinite(free)) {
216
+ machine.memUsedPct = pct((1 - free / total) * 100);
217
+ }
218
+ } catch { /* keep 0 */ }
219
+
220
+ // cpuPct — coarse instantaneous estimate from loadavg/cpus when powermetrics
221
+ // is unavailable. powermetrics (if it yields) overrides below.
222
+ try {
223
+ const cpus = osImpl.cpus();
224
+ const n = Array.isArray(cpus) ? cpus.length : 0;
225
+ if (n > 0 && Number.isFinite(machine.loadavg[0])) {
226
+ const est = (machine.loadavg[0] / n) * 100;
227
+ if (Number.isFinite(est)) machine.cpuPct = pct(est);
228
+ }
229
+ } catch { /* no cpuPct */ }
230
+
231
+ // Best-effort temp + cpu via powermetrics (guarded, injected, never blocks).
232
+ if (o.powermetrics !== false) {
233
+ const pm = await collectPowermetrics(o);
234
+ if (Number.isFinite(pm.tempC)) machine.tempC = round1(pm.tempC);
235
+ if (Number.isFinite(pm.cpuPct)) machine.cpuPct = pct(pm.cpuPct);
236
+ }
237
+
238
+ // Best-effort diskUsedPct via df.
239
+ if (o.disk !== false) {
240
+ const disk = await collectDisk(o);
241
+ if (Number.isFinite(disk)) machine.diskUsedPct = pct(disk);
242
+ }
243
+
244
+ return machine;
245
+ }
246
+
247
+ /**
248
+ * Parse `powermetrics` for CPU temperature + active CPU%. Guarded: only attempts
249
+ * the exec on darwin (unless an explicit `deps.execFile` + `deps.platform` say
250
+ * otherwise for tests). Returns {} on any failure. NEVER blocks past timeoutMs.
251
+ *
252
+ * @returns {Promise<{ tempC?:number, cpuPct?:number }>}
253
+ */
254
+ export async function collectPowermetrics(o = {}) {
255
+ const platform = o.platform || (o.os && o.os.platform && o.os.platform()) || process.platform;
256
+ const execFileImpl = o.execFile || execFile;
257
+ if (platform !== "darwin" && !o.execFile) return {}; // only darwin in the wild; tests inject execFile
258
+ const timeoutMs = Number.isFinite(o.pmTimeoutMs) ? o.pmTimeoutMs : 1500;
259
+ const bin = o.powermetricsBin || "/usr/bin/powermetrics";
260
+ if (!o.execFile && !existsSync(bin)) return {};
261
+ const out = await execFileSafe(
262
+ execFileImpl, bin,
263
+ ["-n", "1", "-i", "200", "--samplers", "smc,cpu_power", "--show-process-energy"],
264
+ timeoutMs,
265
+ );
266
+ if (!out) return {};
267
+ return parsePowermetrics(out);
268
+ }
269
+
270
+ /** Pure parser for powermetrics text → { tempC?, cpuPct? }. Tolerant of format drift. */
271
+ export function parsePowermetrics(text) {
272
+ const out = {};
273
+ const s = String(text || "");
274
+ // Temperature: "CPU die temperature: 54.21 C" or "GPU die temperature: ..."
275
+ const temp = s.match(/CPU die temperature:\s*([\d.]+)\s*C/i) || s.match(/die temperature:\s*([\d.]+)\s*C/i);
276
+ if (temp) { const v = Number(temp[1]); if (Number.isFinite(v)) out.tempC = v; }
277
+ // Active CPU%: "CPU Average frequency as fraction of nominal: ..." isn't util;
278
+ // prefer an explicit "... active residency" total or a "CPU usage" line.
279
+ const idle = s.match(/CPU\s+(?:Average\s+)?idle\s+residency:\s*([\d.]+)\s*%/i);
280
+ if (idle) { const v = Number(idle[1]); if (Number.isFinite(v)) out.cpuPct = Math.max(0, 100 - v); }
281
+ return out;
282
+ }
283
+
284
+ /** Best-effort diskUsedPct for the agent root's filesystem via `df -k .`. */
285
+ export async function collectDisk(o = {}) {
286
+ const execFileImpl = o.execFile || execFile;
287
+ const root = o.agentRoot ? resolve(o.agentRoot) : (process.env.AGENT_ROOT || process.cwd());
288
+ const timeoutMs = Number.isFinite(o.dfTimeoutMs) ? o.dfTimeoutMs : 1500;
289
+ const out = await execFileSafe(execFileImpl, "/bin/df", ["-k", root], timeoutMs);
290
+ if (!out) return undefined;
291
+ return parseDf(out);
292
+ }
293
+
294
+ /** Pure parser for `df -k` text → used percent number, or undefined. */
295
+ export function parseDf(text) {
296
+ const lines = String(text || "").trim().split("\n");
297
+ if (lines.length < 2) return undefined;
298
+ // The data row may wrap; scan for a token ending in "%".
299
+ for (let i = lines.length - 1; i >= 1; i--) {
300
+ const cols = lines[i].trim().split(/\s+/);
301
+ const capCol = cols.find((c) => /^\d+%$/.test(c));
302
+ if (capCol) { const v = Number(capCol.replace("%", "")); if (Number.isFinite(v)) return v; }
303
+ }
304
+ return undefined;
305
+ }
306
+
307
+ function round1(n) { return Math.round(Number(n) * 10) / 10; }
308
+ function round2(n) { return Math.round(Number(n) * 100) / 100; }
309
+
310
+ // ---------------------------------------------------------------------------
311
+ // claudeAuth detection
312
+ // ---------------------------------------------------------------------------
313
+
314
+ /** Auth-failure fingerprints we treat as "the agent needs to re-login". */
315
+ const RELOGIN_FINGERPRINTS = [
316
+ "relogin_required",
317
+ "please run /login",
318
+ "please run claude login",
319
+ "run `claude login`",
320
+ "invalid api key",
321
+ "oauth token has expired",
322
+ "authentication_error",
323
+ "credentials have expired",
324
+ "not logged in",
325
+ ];
326
+
327
+ /**
328
+ * Detect whether Claude auth is healthy by scanning recent log tails for a
329
+ * re-login/auth fingerprint. Looks at the most recent file in each of a small set
330
+ * of log dirs (sessions, diagnostics) within a freshness window.
331
+ *
332
+ * Returns:
333
+ * "relogin_required" a recent auth/login failure is present
334
+ * "ok" logs exist and show no recent auth failure
335
+ * "unknown" no logs to judge from
336
+ *
337
+ * `deps.logText` (string) short-circuits the file scan for tests.
338
+ *
339
+ * @returns {"ok"|"relogin_required"|"unknown"}
340
+ */
341
+ export function detectClaudeAuth(o = {}) {
342
+ const nowMs = nowMsFrom(o.now);
343
+ const windowMs = Number.isFinite(o.authWindowMs) ? o.authWindowMs : 6 * 60 * 60 * 1000; // 6h
344
+
345
+ // Test / explicit override.
346
+ if (typeof o.logText === "string") {
347
+ return scanForRelogin(o.logText) ? "relogin_required" : (o.logText ? "ok" : "unknown");
348
+ }
349
+
350
+ const agentRoot = o.agentRoot;
351
+ if (!agentRoot) return "unknown";
352
+
353
+ const dirs = [
354
+ join(agentRoot, "logs", "sessions"),
355
+ join(agentRoot, "logs", "diagnostics"),
356
+ join(agentRoot, "logs"),
357
+ ];
358
+ let sawAny = false;
359
+ let sawRelogin = false;
360
+ for (const dir of dirs) {
361
+ const file = newestFileWithin(dir, nowMs, windowMs);
362
+ if (!file) continue;
363
+ sawAny = true;
364
+ const text = tailFile(file, 64 * 1024);
365
+ if (scanForRelogin(text)) { sawRelogin = true; break; }
366
+ }
367
+ if (sawRelogin) return "relogin_required";
368
+ return sawAny ? "ok" : "unknown";
369
+ }
370
+
371
+ function scanForRelogin(text) {
372
+ const lower = String(text || "").toLowerCase();
373
+ return RELOGIN_FINGERPRINTS.some((f) => lower.includes(f));
374
+ }
375
+
376
+ /** Newest *.jsonl/*.log file in dir modified within windowMs of now, else null. */
377
+ function newestFileWithin(dir, nowMs, windowMs) {
378
+ try {
379
+ if (!existsSync(dir)) return null;
380
+ let best = null;
381
+ for (const name of readdirSync(dir)) {
382
+ if (!/\.(jsonl|log|txt)$/i.test(name)) continue;
383
+ const p = join(dir, name);
384
+ let st;
385
+ try { st = statSync(p); } catch { continue; }
386
+ if (!st.isFile()) continue;
387
+ const age = nowMs - st.mtimeMs;
388
+ if (!Number.isFinite(age) || age > windowMs) continue;
389
+ if (!best || st.mtimeMs > best.mtimeMs) best = { p, mtimeMs: st.mtimeMs };
390
+ }
391
+ return best ? best.p : null;
392
+ } catch { return null; }
393
+ }
394
+
395
+ /** Read the last `bytes` of a file as utf8 (best-effort). */
396
+ function tailFile(path, bytes) {
397
+ try {
398
+ const buf = readFileSync(path);
399
+ if (buf.length <= bytes) return buf.toString("utf8");
400
+ return buf.slice(buf.length - bytes).toString("utf8");
401
+ } catch { return ""; }
402
+ }
403
+
404
+ // ---------------------------------------------------------------------------
405
+ // subAgentsRunning
406
+ // ---------------------------------------------------------------------------
407
+
408
+ /**
409
+ * Count running claude child processes (best-effort). Prefers an injected count;
410
+ * else reuses the resource-governor's `liveClaudeStats()` (one `ps` parse, 2s
411
+ * cached) so the governor and telemetry agree on "live claude". Fail-open → 0.
412
+ */
413
+ export function countSubAgents(o = {}) {
414
+ if (Number.isFinite(o.subAgentsRunning)) return Math.max(0, Math.floor(o.subAgentsRunning));
415
+ if (typeof o.liveClaudeStats === "function") {
416
+ try { const v = o.liveClaudeStats(); return Math.max(0, Math.floor(Number(v && v.count) || 0)); }
417
+ catch { return 0; }
418
+ }
419
+ try { const v = liveClaudeStats(); return Math.max(0, Math.floor(Number(v && v.count) || 0)); }
420
+ catch { return 0; }
421
+ }
422
+
423
+ // ---------------------------------------------------------------------------
424
+ // collectStatus — the public assembler
425
+ // ---------------------------------------------------------------------------
426
+
427
+ /**
428
+ * Build the SHARED STATUS SNAPSHOT. Every step is independently fail-open; the
429
+ * function never throws and always returns a well-formed snapshot.
430
+ *
431
+ * @param {object} o
432
+ * @param {string} [o.agentRoot] agent repo root (for state/log reads)
433
+ * @param {number|Function} [o.now] clock (ms or () => ms); default Date.now
434
+ * @param {object} [o.deps] injectable probes (see below) — also accepted at top level
435
+ * @param {object[]} [o.sessions] presence records override (tests)
436
+ * @param {number} [o.subAgentsRunning] override the child-process count (tests)
437
+ * @param {Function} [o.liveClaudeStats] override the ps-based count (tests)
438
+ * @param {object} [o.os] node:os override (tests)
439
+ * @param {Function}[o.execFile] execFile override (tests; gates powermetrics/df)
440
+ * @param {string} [o.logText] log text override for auth detection (tests)
441
+ * @param {object} [o.thresholds] alert thresholds passed to deriveAlerts
442
+ * @param {boolean} [o.withAlerts=true] derive + attach alerts inline
443
+ * @returns {Promise<object>} the status snapshot
444
+ */
445
+ export async function collectStatus(o = {}) {
446
+ // Allow callers to pass probes under `deps` or at top level (top level wins).
447
+ const deps = (o && typeof o.deps === "object" && o.deps) || {};
448
+ const opt = { ...deps, ...o };
449
+ const nowMs = nowMsFrom(opt.now);
450
+
451
+ // 1. state + activity + session
452
+ let act = { session: null, activity: "idle", state: "idle" };
453
+ try { act = resolveActivity(opt); } catch { /* defaults */ }
454
+
455
+ // 2. subAgentsRunning
456
+ let subAgentsRunning = 0;
457
+ try { subAgentsRunning = countSubAgents(opt); } catch { subAgentsRunning = 0; }
458
+
459
+ // 3. machine
460
+ let machine = { loadavg: [0, 0, 0], memUsedPct: 0 };
461
+ try { machine = await collectMachine(opt); } catch { machine = { loadavg: [0, 0, 0], memUsedPct: 0 }; }
462
+
463
+ // 4. claudeAuth
464
+ let claudeAuth = "unknown";
465
+ try { claudeAuth = detectClaudeAuth(opt); } catch { claudeAuth = "unknown"; }
466
+
467
+ // Promote state to "busy" if there are sub-agents but resolveActivity said idle
468
+ // (a daemon spawning work but its own presence record lags). Honour an explicit
469
+ // override.
470
+ let state = VALID_STATES.has(opt.state) ? opt.state : act.state;
471
+ if (claudeAuth === "relogin_required") state = "error";
472
+ else if (state === "idle" && subAgentsRunning > 0) state = "busy";
473
+
474
+ const status = {
475
+ state,
476
+ activity: act.activity || "idle",
477
+ subAgentsRunning,
478
+ session: act.session || null,
479
+ machine,
480
+ claudeAuth,
481
+ alerts: [],
482
+ ts: new Date(nowMs).toISOString(),
483
+ };
484
+
485
+ // 5. alerts (inline, unless the caller wants to derive them itself)
486
+ if (opt.withAlerts !== false) {
487
+ try {
488
+ const mod = await import("./alerts.mjs");
489
+ status.alerts = mod.deriveAlerts(status, opt.thresholds) || [];
490
+ } catch { status.alerts = []; }
491
+ }
492
+
493
+ return status;
494
+ }
495
+
496
+ export const _internals = {
497
+ resolveActivity,
498
+ deriveTaskClass,
499
+ humanActivity,
500
+ collectMachine,
501
+ collectPowermetrics,
502
+ parsePowermetrics,
503
+ collectDisk,
504
+ parseDf,
505
+ detectClaudeAuth,
506
+ scanForRelogin,
507
+ countSubAgents,
508
+ execFileSafe,
509
+ RELOGIN_FINGERPRINTS,
510
+ };
511
+
512
+ export default { collectStatus, _internals };