@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,182 @@
1
+ /**
2
+ * lib/org/integration-tools.mjs — the daemon's versioned cache of the workspace's
3
+ * GRANTED integration (plugin) tools, and its persistence to disk (SP5).
4
+ *
5
+ * THE GAP this closes: SP1 gave hq the `integration.toolsetVersion` (cheap ETag
6
+ * poll) + `integration.listAgentTools` (the authoritative granted-tool manifest,
7
+ * SCHEMAS ONLY — never plugin code or secrets) reads. This module is the agent
8
+ * half: it polls the version, refetches the manifest only when it changed, maps
9
+ * each granted tool into the canonical tool-surface descriptor, and — so per
10
+ * session `cohort-mcp` subprocesses and the native tool snapshot pick up the
11
+ * current set WITHOUT a daemon restart — persists the versioned set to
12
+ * `state/org/toolset.json`.
13
+ *
14
+ * Both SP1 endpoints are protocol METHODS (POST) not GET reads, so BOTH ride the
15
+ * client's `call(method, params, o)` — which returns a res frame `{ok, result}`.
16
+ * `call` is injected (deps) so tests never touch the network.
17
+ *
18
+ * Tool name uses the SP3/SP4 convention (`plugin__<slug>__<tool>`) so a tool is
19
+ * named IDENTICALLY across the in-app responder, the hq MCP server, and here.
20
+ * Execution proxies back to hq through `integration.invokeTool` (SP5) — the
21
+ * descriptor carries `{integration:{slug,tool}}` so `executeOrgTool` can send the
22
+ * right params.
23
+ *
24
+ * FAIL-OPEN like the rest of the org client: any transport/parse hiccup returns
25
+ * the last-good cache; a refresh never throws past the boundary.
26
+ *
27
+ * Node builtins only. ESM.
28
+ *
29
+ * @module lib/org/integration-tools
30
+ */
31
+
32
+ "use strict";
33
+
34
+ import { existsSync, mkdirSync, writeFileSync, renameSync } from "node:fs";
35
+ import { join, resolve } from "node:path";
36
+
37
+ /** In-memory cache: the current version + mapped tools + when we last fetched. */
38
+ let cache = { version: 0, tools: [], fetchedAt: 0 };
39
+
40
+ /**
41
+ * Resolve the agent root the same env-first way `resolveOrgToolConfig` /
42
+ * lib/agent-root do, so the long-lived daemon and a per-session subprocess agree
43
+ * on the `state/org/toolset.json` location. Fail-open: worst case is cwd.
44
+ * @param {string} [agentRoot]
45
+ * @returns {string}
46
+ */
47
+ function resolveAgentRoot(agentRoot) {
48
+ return resolve(
49
+ agentRoot ||
50
+ process.env.COHORT_AGENT_ROOT ||
51
+ process.env.AGENT_ROOT ||
52
+ process.env.AGENT_DIR ||
53
+ process.cwd(),
54
+ );
55
+ }
56
+
57
+ /**
58
+ * The canonical cross-process cache path: `<agentRoot>/state/org/toolset.json`.
59
+ * The daemon WRITES it on refresh (Task 4); `getOrgTools()` + the native plane
60
+ * READ it (tool-surface.loadIntegrationToolsFromDisk). Shape:
61
+ * `{ version:number, tools:SurfaceTool[], fetchedAt:number }`.
62
+ * @param {string} [agentRoot]
63
+ * @returns {string}
64
+ */
65
+ export function integrationToolsetPath(agentRoot) {
66
+ return join(resolveAgentRoot(agentRoot), "state", "org", "toolset.json");
67
+ }
68
+
69
+ /** Build the SP3/SP4 tool name; sanitized + capped at 64 chars. */
70
+ function mcpName(slug, tool) {
71
+ return `plugin__${String(slug).replace(/[^a-z0-9]/gi, "_")}__${tool}`.slice(
72
+ 0,
73
+ 64,
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Map a `listAgentTools` manifest entry → the canonical tool-surface descriptor
79
+ * (the SAME shape ORG_TOOLS uses), carrying an `integration:{slug,tool}` hint so
80
+ * `executeOrgTool` can proxy to `integration.invokeTool`.
81
+ * @param {object} t
82
+ * @returns {object}
83
+ */
84
+ export function toSurfaceTool(t) {
85
+ return {
86
+ name: mcpName(t.slug, t.name),
87
+ title: t.name,
88
+ description: `${t.slug}: ${t.description ?? ""}`.trim(),
89
+ input_schema:
90
+ t.inputSchema && typeof t.inputSchema === "object"
91
+ ? t.inputSchema
92
+ : { type: "object" },
93
+ access: "write",
94
+ binding: { kind: "rpc", method: "integration.invokeTool" },
95
+ // Carried so executeOrgTool passes {slug, tool} to invokeTool (Task 3).
96
+ integration: { slug: t.slug, tool: t.name },
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Cheap version poll + conditional refetch. `deps` inject `call` (+ optional
102
+ * `agentMemberId`, `now`) for tests. Both SP1 endpoints are protocol METHODS, so
103
+ * both go through `call` (res frame `{ok, result}`); a transport failure or a
104
+ * bad shape returns the last-good cache. Returns the (possibly unchanged) cache.
105
+ *
106
+ * @param {object} deps - { call, agentMemberId?, now? }
107
+ * @returns {Promise<{version:number, tools:object[], fetchedAt:number}>}
108
+ */
109
+ export async function maybeRefresh({ call, agentMemberId, now } = {}) {
110
+ if (typeof call !== "function") return cache;
111
+ const vres = await call("integration.toolsetVersion", {}).catch(() => null);
112
+ const version = vres && vres.result ? vres.result.version : undefined;
113
+ if (typeof version !== "number") return cache;
114
+ // A matching version with a populated set is a no-op (the cheap poll's payoff).
115
+ if (version === cache.version && cache.tools.length) return cache;
116
+
117
+ const tres = await call(
118
+ "integration.listAgentTools",
119
+ agentMemberId ? { agentMemberId } : {},
120
+ ).catch(() => null);
121
+ const tools = tres && tres.result ? tres.result.tools : undefined;
122
+ if (!Array.isArray(tools)) return cache;
123
+
124
+ cache = {
125
+ version,
126
+ tools: tools.map(toSurfaceTool),
127
+ fetchedAt: typeof now === "function" ? now() : Date.now(),
128
+ };
129
+ return cache;
130
+ }
131
+
132
+ /** The current mapped integration tools (surface-shaped). */
133
+ export function getIntegrationTools() {
134
+ return cache.tools;
135
+ }
136
+
137
+ /** The current cached toolset version (0 before the first successful refresh). */
138
+ export function currentToolsetVersion() {
139
+ return cache.version;
140
+ }
141
+
142
+ /**
143
+ * Persist the versioned tool set to `state/org/toolset.json` (atomic write via a
144
+ * temp file + rename) so per-session subprocesses + the native snapshot read the
145
+ * current set. Fail-open: never throws (a persistence hiccup must not break the
146
+ * beat that calls it).
147
+ * @param {string} agentRoot
148
+ * @param {{version:number, tools:object[]}} snapshot
149
+ * @returns {boolean} true when written
150
+ */
151
+ export function writeIntegrationToolsToDisk(agentRoot, snapshot) {
152
+ try {
153
+ const path = integrationToolsetPath(agentRoot);
154
+ const dir = join(resolveAgentRoot(agentRoot), "state", "org");
155
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
156
+ const doc = {
157
+ version: Number(snapshot && snapshot.version) || 0,
158
+ tools: Array.isArray(snapshot && snapshot.tools) ? snapshot.tools : [],
159
+ fetchedAt: Date.now(),
160
+ };
161
+ const tmp = `${path}.${process.pid}.tmp`;
162
+ writeFileSync(tmp, JSON.stringify(doc, null, 2));
163
+ renameSync(tmp, path);
164
+ return true;
165
+ } catch {
166
+ return false;
167
+ }
168
+ }
169
+
170
+ /** Reset the in-memory cache — test-only. */
171
+ export function _resetForTest() {
172
+ cache = { version: 0, tools: [], fetchedAt: 0 };
173
+ }
174
+
175
+ export default {
176
+ maybeRefresh,
177
+ getIntegrationTools,
178
+ currentToolsetVersion,
179
+ writeIntegrationToolsToDisk,
180
+ integrationToolsetPath,
181
+ toSurfaceTool,
182
+ };
@@ -0,0 +1,160 @@
1
+ /**
2
+ * integration-tools.test.mjs — the versioned integration-tools cache (SP5).
3
+ *
4
+ * Hermetic: the hq fetch rides an injected `call` (no network). Disk tests write
5
+ * into a fresh mkdtemp root. Run: node --test lib/org/integration-tools.test.mjs
6
+ */
7
+
8
+ "use strict";
9
+
10
+ import { test, beforeEach, after } from "node:test";
11
+ import assert from "node:assert/strict";
12
+ import { mkdtempSync, rmSync, readFileSync, existsSync } from "node:fs";
13
+ import { tmpdir } from "node:os";
14
+ import { join } from "node:path";
15
+
16
+ import {
17
+ maybeRefresh,
18
+ getIntegrationTools,
19
+ currentToolsetVersion,
20
+ writeIntegrationToolsToDisk,
21
+ integrationToolsetPath,
22
+ _resetForTest,
23
+ } from "./integration-tools.mjs";
24
+
25
+ /** A fake `call` that answers the two SP1 read-methods from a canned set. */
26
+ function makeCall({ version, tools }) {
27
+ const calls = [];
28
+ const call = async (method, params) => {
29
+ calls.push({ method, params });
30
+ if (method === "integration.toolsetVersion") {
31
+ return { ok: true, result: { version, updatedAt: null } };
32
+ }
33
+ if (method === "integration.listAgentTools") {
34
+ return { ok: true, result: { version, tools } };
35
+ }
36
+ return { ok: false, error: { code: "NOT_FOUND", message: "?" } };
37
+ };
38
+ return { call, calls };
39
+ }
40
+
41
+ const TOOLS_V5 = [
42
+ {
43
+ slug: "open-meteo",
44
+ name: "forecast",
45
+ description: "Get the forecast",
46
+ inputSchema: { type: "object", properties: { city: { type: "string" } } },
47
+ outputSchema: { type: "object" },
48
+ },
49
+ ];
50
+
51
+ const roots = [];
52
+ function freshRoot() {
53
+ const r = mkdtempSync(join(tmpdir(), "int-tools-"));
54
+ roots.push(r);
55
+ return r;
56
+ }
57
+
58
+ beforeEach(() => _resetForTest());
59
+ after(() => {
60
+ for (const r of roots) {
61
+ try {
62
+ rmSync(r, { recursive: true, force: true });
63
+ } catch {
64
+ /* best-effort */
65
+ }
66
+ }
67
+ });
68
+
69
+ test("first refresh populates the cache from version + tools", async () => {
70
+ const { call, calls } = makeCall({ version: 5, tools: TOOLS_V5 });
71
+ const cache = await maybeRefresh({ call });
72
+ assert.equal(cache.version, 5);
73
+ assert.equal(currentToolsetVersion(), 5);
74
+ const got = getIntegrationTools();
75
+ assert.equal(got.length, 1);
76
+ const t = got[0];
77
+ assert.equal(t.name, "plugin__open_meteo__forecast");
78
+ assert.equal(t.access, "write");
79
+ assert.equal(t.binding.kind, "rpc");
80
+ assert.equal(t.binding.method, "integration.invokeTool");
81
+ assert.deepEqual(t.integration, { slug: "open-meteo", tool: "forecast" });
82
+ assert.deepEqual(t.input_schema, TOOLS_V5[0].inputSchema);
83
+ // Both reads happened on the first refresh.
84
+ assert.deepEqual(
85
+ calls.map((c) => c.method),
86
+ ["integration.toolsetVersion", "integration.listAgentTools"],
87
+ );
88
+ });
89
+
90
+ test("same version → no refetch of listAgentTools", async () => {
91
+ const first = makeCall({ version: 5, tools: TOOLS_V5 });
92
+ await maybeRefresh({ call: first.call });
93
+ const second = makeCall({ version: 5, tools: TOOLS_V5 });
94
+ await maybeRefresh({ call: second.call });
95
+ assert.deepEqual(
96
+ second.calls.map((c) => c.method),
97
+ ["integration.toolsetVersion"],
98
+ "a matching version polls the cheap version read only",
99
+ );
100
+ });
101
+
102
+ test("a bumped version refetches + swaps in the new tools", async () => {
103
+ await maybeRefresh(makeCall({ version: 5, tools: TOOLS_V5 }));
104
+ const v6Tools = [
105
+ { slug: "stripe", name: "list_charges", description: "List charges", inputSchema: {} },
106
+ ];
107
+ await maybeRefresh(makeCall({ version: 6, tools: v6Tools }));
108
+ assert.equal(currentToolsetVersion(), 6);
109
+ const names = getIntegrationTools().map((t) => t.name);
110
+ assert.deepEqual(names, ["plugin__stripe__list_charges"]);
111
+ });
112
+
113
+ test("passes agentMemberId through to listAgentTools when given", async () => {
114
+ const { call, calls } = makeCall({ version: 1, tools: TOOLS_V5 });
115
+ await maybeRefresh({ call, agentMemberId: "wm_agent" });
116
+ const list = calls.find((c) => c.method === "integration.listAgentTools");
117
+ assert.deepEqual(list.params, { agentMemberId: "wm_agent" });
118
+ });
119
+
120
+ test("a non-numeric version leaves the cache untouched (fail-open)", async () => {
121
+ const call = async () => ({ ok: true, result: {} }); // no version
122
+ const cache = await maybeRefresh({ call });
123
+ assert.equal(cache.version, 0);
124
+ assert.equal(getIntegrationTools().length, 0);
125
+ });
126
+
127
+ test("a throwing call fails open to the existing cache", async () => {
128
+ await maybeRefresh(makeCall({ version: 2, tools: TOOLS_V5 }));
129
+ const boom = async () => {
130
+ throw new Error("network down");
131
+ };
132
+ const cache = await maybeRefresh({ call: boom });
133
+ assert.equal(cache.version, 2, "kept the last-good cache");
134
+ assert.equal(getIntegrationTools().length, 1);
135
+ });
136
+
137
+ test("the mcp name sanitizes non-alnum slug chars and caps at 64", async () => {
138
+ const tools = [
139
+ { slug: "My.Weird-Slug", name: "do_thing", description: "d", inputSchema: {} },
140
+ ];
141
+ await maybeRefresh(makeCall({ version: 1, tools }));
142
+ const [t] = getIntegrationTools();
143
+ assert.equal(t.name, "plugin__My_Weird_Slug__do_thing");
144
+ assert.ok(t.name.length <= 64);
145
+ });
146
+
147
+ test("writeIntegrationToolsToDisk persists {version,tools} atomically", async () => {
148
+ const root = freshRoot();
149
+ await maybeRefresh(makeCall({ version: 9, tools: TOOLS_V5 }));
150
+ writeIntegrationToolsToDisk(root, {
151
+ version: currentToolsetVersion(),
152
+ tools: getIntegrationTools(),
153
+ });
154
+ const p = integrationToolsetPath(root);
155
+ assert.ok(existsSync(p), "toolset.json exists");
156
+ const doc = JSON.parse(readFileSync(p, "utf8"));
157
+ assert.equal(doc.version, 9);
158
+ assert.equal(doc.tools.length, 1);
159
+ assert.equal(doc.tools[0].name, "plugin__open_meteo__forecast");
160
+ });
@@ -0,0 +1,131 @@
1
+ /**
2
+ * lib/org/keys.mjs — device keypair for the Cohort org binding.
3
+ *
4
+ * Every paired agent owns one long-lived Ed25519 device key. The PUBLIC key is
5
+ * presented in the pairing handshake (`pairing.request { pubkey }`) and pinned
6
+ * by peers / the verify layer; the PRIVATE key never leaves the machine and is
7
+ * used to sign outbound material (and, symmetrically, to let the detective layer
8
+ * verify signatures the server makes over snapshots — see lib/org/verify.mjs).
9
+ *
10
+ * The private key is stored 0600 under `state/org/` and loaded-or-created on
11
+ * first use, so a freshly-provisioned agent self-bootstraps a stable identity
12
+ * that survives restarts. Keys are SubjectPublicKeyInfo / PKCS#8 PEM so they are
13
+ * portable across `node:crypto`, OpenSSL, and any Ed25519 verifier.
14
+ *
15
+ * Pure node:crypto + node:fs (atomic write via lib/fs-atomic). Fully testable:
16
+ * `ensureDeviceKey` is the only fs-touching entry point; `verifySignature` and
17
+ * the returned `sign` are pure over their inputs.
18
+ *
19
+ * @module lib/org/keys
20
+ */
21
+
22
+ "use strict";
23
+
24
+ import {
25
+ generateKeyPairSync,
26
+ createPrivateKey,
27
+ createPublicKey,
28
+ sign as edSign,
29
+ verify as edVerify,
30
+ } from "node:crypto";
31
+ import { readFileSync, existsSync, chmodSync } from "node:fs";
32
+ import { join } from "node:path";
33
+
34
+ import { writeFileAtomic } from "../fs-atomic.mjs";
35
+
36
+ /** Relative location of the device private key under the agent root. */
37
+ const KEY_DIR = join("state", "org");
38
+ const PRIV_FILE = "device-key.pem";
39
+
40
+ /** Resolve the on-disk path of the private key PEM for an agent root. */
41
+ function privKeyPath(agentRoot) {
42
+ return join(String(agentRoot || "."), KEY_DIR, PRIV_FILE);
43
+ }
44
+
45
+ /**
46
+ * Derive the public-key PEM (SPKI) from a private-key PEM (PKCS#8). Pure.
47
+ * @param {string} privateKeyPem
48
+ * @returns {string}
49
+ */
50
+ function publicPemFromPrivate(privateKeyPem) {
51
+ const pub = createPublicKey(createPrivateKey(privateKeyPem));
52
+ return pub.export({ type: "spki", format: "pem" }).toString();
53
+ }
54
+
55
+ /**
56
+ * Generate a fresh Ed25519 keypair as PEM strings. Pure.
57
+ * @returns {{publicKeyPem:string, privateKeyPem:string}}
58
+ */
59
+ export function generateDeviceKey() {
60
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
61
+ return {
62
+ publicKeyPem: publicKey.export({ type: "spki", format: "pem" }).toString(),
63
+ privateKeyPem: privateKey.export({ type: "pkcs8", format: "pem" }).toString(),
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Sign a buffer with an Ed25519 private-key PEM. Ed25519 takes no digest
69
+ * algorithm (the second arg to `crypto.sign` is null). Returns base64. Pure.
70
+ * @param {string} privateKeyPem PKCS#8 PEM
71
+ * @param {Buffer|Uint8Array|string} data
72
+ * @returns {string} base64 signature
73
+ */
74
+ export function signWith(privateKeyPem, data) {
75
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
76
+ const key = createPrivateKey(privateKeyPem);
77
+ return edSign(null, buf, key).toString("base64");
78
+ }
79
+
80
+ /**
81
+ * Verify an Ed25519 signature over `data` against a public-key PEM. Never
82
+ * throws — a malformed key/signature returns false (the detective layer treats
83
+ * "can't verify" as "don't trust"). Pure.
84
+ * @param {string} publicKeyPem SPKI PEM
85
+ * @param {Buffer|Uint8Array|string} data
86
+ * @param {string} sigB64 base64 signature
87
+ * @returns {boolean}
88
+ */
89
+ export function verifySignature(publicKeyPem, data, sigB64) {
90
+ try {
91
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
92
+ const sig = Buffer.from(String(sigB64 || ""), "base64");
93
+ if (sig.length === 0) return false;
94
+ const key = createPublicKey(publicKeyPem);
95
+ return edVerify(null, buf, key, sig);
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Load the agent's device key, creating it on first use. The private key is
103
+ * stored 0600 under `state/org/device-key.pem`; only the public key and a bound
104
+ * `sign(buf)->base64` closure are returned (the private PEM never escapes).
105
+ *
106
+ * Idempotent and crash-safe: the create path writes atomically (temp→fsync→
107
+ * rename) with mode 0600, then re-chmods defensively in case a pre-existing key
108
+ * had looser perms.
109
+ *
110
+ * @param {string} agentRoot absolute path to the agent instance root
111
+ * @returns {{publicKeyPem:string, sign:(data:(Buffer|Uint8Array|string))=>string}}
112
+ */
113
+ export function ensureDeviceKey(agentRoot) {
114
+ const path = privKeyPath(agentRoot);
115
+ let privateKeyPem;
116
+ if (existsSync(path)) {
117
+ privateKeyPem = readFileSync(path, "utf8");
118
+ // Defensive: tighten perms if a previous writer left them loose.
119
+ try { chmodSync(path, 0o600); } catch { /* best-effort */ }
120
+ } else {
121
+ const kp = generateDeviceKey();
122
+ privateKeyPem = kp.privateKeyPem;
123
+ writeFileAtomic(path, privateKeyPem, { mode: 0o600 });
124
+ try { chmodSync(path, 0o600); } catch { /* best-effort */ }
125
+ }
126
+ const publicKeyPem = publicPemFromPrivate(privateKeyPem);
127
+ return {
128
+ publicKeyPem,
129
+ sign: (data) => signWith(privateKeyPem, data),
130
+ };
131
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * keys.test.mjs — device keypair crypto round-trips + tamper rejection.
3
+ * Run: node --test lib/org/keys.test.mjs
4
+ */
5
+ "use strict";
6
+
7
+ import { test } from "node:test";
8
+ import assert from "node:assert/strict";
9
+ import { mkdtempSync, existsSync, statSync, rmSync, readFileSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+
13
+ import {
14
+ generateDeviceKey,
15
+ signWith,
16
+ verifySignature,
17
+ ensureDeviceKey,
18
+ } from "./keys.mjs";
19
+
20
+ function tmpRoot() {
21
+ return mkdtempSync(join(tmpdir(), "cohort-keys-"));
22
+ }
23
+
24
+ test("generateDeviceKey: emits SPKI public + PKCS#8 private PEM", () => {
25
+ const { publicKeyPem, privateKeyPem } = generateDeviceKey();
26
+ assert.match(publicKeyPem, /-----BEGIN PUBLIC KEY-----/);
27
+ assert.match(privateKeyPem, /-----BEGIN PRIVATE KEY-----/);
28
+ });
29
+
30
+ test("sign/verify: round-trip succeeds", () => {
31
+ const { publicKeyPem, privateKeyPem } = generateDeviceKey();
32
+ const msg = Buffer.from("the org snapshot bytes");
33
+ const sig = signWith(privateKeyPem, msg);
34
+ assert.equal(typeof sig, "string");
35
+ assert.equal(verifySignature(publicKeyPem, msg, sig), true);
36
+ // string payload accepted too
37
+ assert.equal(verifySignature(publicKeyPem, "the org snapshot bytes", sig), true);
38
+ });
39
+
40
+ test("verify: rejects tampered message", () => {
41
+ const { publicKeyPem, privateKeyPem } = generateDeviceKey();
42
+ const sig = signWith(privateKeyPem, Buffer.from("original"));
43
+ assert.equal(verifySignature(publicKeyPem, Buffer.from("tampered"), sig), false);
44
+ });
45
+
46
+ test("verify: rejects signature from a different key", () => {
47
+ const a = generateDeviceKey();
48
+ const b = generateDeviceKey();
49
+ const msg = Buffer.from("payload");
50
+ const sig = signWith(a.privateKeyPem, msg);
51
+ assert.equal(verifySignature(b.publicKeyPem, msg, sig), false);
52
+ });
53
+
54
+ test("verify: never throws on garbage inputs", () => {
55
+ assert.equal(verifySignature("not a pem", Buffer.from("x"), "AAAA"), false);
56
+ assert.equal(verifySignature(generateDeviceKey().publicKeyPem, Buffer.from("x"), ""), false);
57
+ assert.equal(verifySignature(generateDeviceKey().publicKeyPem, Buffer.from("x"), "!!!notb64"), false);
58
+ });
59
+
60
+ test("ensureDeviceKey: creates 0600 key under state/org and returns signer", () => {
61
+ const root = tmpRoot();
62
+ try {
63
+ const { publicKeyPem, sign } = ensureDeviceKey(root);
64
+ assert.match(publicKeyPem, /BEGIN PUBLIC KEY/);
65
+ const path = join(root, "state", "org", "device-key.pem");
66
+ assert.equal(existsSync(path), true);
67
+ // perms must be owner-only (0600)
68
+ const mode = statSync(path).mode & 0o777;
69
+ assert.equal(mode, 0o600);
70
+ // the returned signer signs verifiably against the returned public key
71
+ const sig = sign(Buffer.from("hello"));
72
+ assert.equal(verifySignature(publicKeyPem, Buffer.from("hello"), sig), true);
73
+ // private PEM is on disk but never returned
74
+ assert.match(readFileSync(path, "utf8"), /BEGIN PRIVATE KEY/);
75
+ } finally {
76
+ rmSync(root, { recursive: true, force: true });
77
+ }
78
+ });
79
+
80
+ test("ensureDeviceKey: idempotent — reload returns the SAME identity", () => {
81
+ const root = tmpRoot();
82
+ try {
83
+ const first = ensureDeviceKey(root);
84
+ const second = ensureDeviceKey(root);
85
+ assert.equal(first.publicKeyPem, second.publicKeyPem);
86
+ // a signature from the first loader verifies under the second's public key
87
+ const sig = first.sign(Buffer.from("cross"));
88
+ assert.equal(verifySignature(second.publicKeyPem, Buffer.from("cross"), sig), true);
89
+ } finally {
90
+ rmSync(root, { recursive: true, force: true });
91
+ }
92
+ });