@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,89 @@
1
+ /**
2
+ * context-pack.test.mjs — deterministic context gathering (lib/setup/context-pack.mjs).
3
+ * Run: `node --test lib/setup/context-pack.test.mjs`
4
+ * @module lib/setup/context-pack.test
5
+ */
6
+
7
+ "use strict";
8
+
9
+ import { test } from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import { mkdtempSync, mkdirSync, writeFileSync, existsSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { buildContextPack, gatherContextPack, readContextPack, CONTEXT_PACK_REL } from "./context-pack.mjs";
15
+
16
+ function tmpRoot() {
17
+ const r = mkdtempSync(join(tmpdir(), "maestro-ctx-"));
18
+ mkdirSync(join(r, "config"), { recursive: true });
19
+ return r;
20
+ }
21
+
22
+ test("buildContextPack merges identity + company + docs + readme", () => {
23
+ const root = tmpRoot();
24
+ writeFileSync(join(root, "config/agent.json"), JSON.stringify({
25
+ firstName: "Jacob", title: "VP Eng", function: "technical-leader", altitude: "vp", company: "Acme",
26
+ }));
27
+ writeFileSync(join(root, "config/company.json"), JSON.stringify({
28
+ name: "Acme", website: "https://acme.com", products: ["Widget"], strategicPriorities: ["Grow"],
29
+ }));
30
+ mkdirSync(join(root, "docs/company-context"), { recursive: true });
31
+ writeFileSync(join(root, "docs/company-context/overview.md"), "# Acme\nWe build widgets.");
32
+ writeFileSync(join(root, "docs/company-context/ignore.png"), "binary");
33
+ writeFileSync(join(root, "README.md"), "Acme agent repo");
34
+
35
+ const pack = buildContextPack(root);
36
+ assert.equal(pack.identity.firstName, "Jacob");
37
+ assert.equal(pack.identity.function, "technical-leader");
38
+ assert.equal(pack.company.name, "Acme");
39
+ assert.deepEqual(pack.company.products, ["Widget"]);
40
+ assert.equal(pack.contextDocs.length, 1, "only .md/.txt docs included");
41
+ assert.match(pack.contextDocs[0].excerpt, /widgets/);
42
+ assert.match(pack.readmeExcerpt, /Acme agent repo/);
43
+ });
44
+
45
+ test("gatherContextPack writes state/setup/context-pack.json", () => {
46
+ const root = tmpRoot();
47
+ writeFileSync(join(root, "config/agent.json"), JSON.stringify({ firstName: "Mara" }));
48
+ const pack = gatherContextPack(root);
49
+ assert.ok(existsSync(join(root, CONTEXT_PACK_REL)));
50
+ const back = readContextPack(root);
51
+ assert.equal(back.identity.firstName, "Mara");
52
+ assert.equal(pack.identity.firstName, "Mara");
53
+ });
54
+
55
+ test("buildContextPack tolerates missing files (no throw)", () => {
56
+ const root = tmpRoot();
57
+ const pack = buildContextPack(root);
58
+ assert.equal(pack.identity.firstName, "");
59
+ assert.deepEqual(pack.contextDocs, []);
60
+ });
61
+
62
+ test("buildContextPack omits orgContext when no org-context cache exists", () => {
63
+ const root = tmpRoot();
64
+ writeFileSync(join(root, "config/agent.json"), JSON.stringify({ firstName: "Solo" }));
65
+ const pack = buildContextPack(root);
66
+ assert.equal("orgContext" in pack, false, "a standalone agent's pack shape is unchanged");
67
+ });
68
+
69
+ test("buildContextPack folds a bounded org-context excerpt in when enrolled (finding: fetched context shapes nothing)", () => {
70
+ const root = tmpRoot();
71
+ writeFileSync(join(root, "config/agent.json"), JSON.stringify({ firstName: "Robin" }));
72
+ writeFileSync(join(root, "config/org-context.json"), JSON.stringify({
73
+ fetchedAt: "2026-07-03T00:00:00Z",
74
+ profile: { slug: "robin", title: "VP Ops", escalationPath: "→ COO", autonomyLevel: "high", reports: [{ member: { slug: "kai", displayName: "Kai" } }] },
75
+ strategy: { northStar: "Own the fund's operating substrate", levers: [{ label: "Automate ops" }], streams: [{ code: "S1", name: "Go-live", mission: "Ship v1" }] },
76
+ decisions: [{ title: "Adopt Postgres" }],
77
+ board: [{ title: "Wire the ready feed" }],
78
+ }));
79
+ const pack = buildContextPack(root);
80
+ assert.ok(pack.orgContext, "orgContext excerpt is present when the cache exists");
81
+ assert.equal(pack.orgContext.self.slug, "robin");
82
+ assert.equal(pack.orgContext.self.escalationPath, "→ COO");
83
+ assert.equal(pack.orgContext.strategy.northStar, "Own the fund's operating substrate");
84
+ assert.deepEqual(pack.orgContext.strategy.levers, ["Automate ops"]);
85
+ assert.equal(pack.orgContext.strategy.streams[0].code, "S1");
86
+ assert.deepEqual(pack.orgContext.reports, ["Kai"]);
87
+ assert.deepEqual(pack.orgContext.adoptedDecisions, ["Adopt Postgres"]);
88
+ assert.deepEqual(pack.orgContext.readyBoard, ["Wire the ready feed"]);
89
+ });
@@ -0,0 +1,277 @@
1
+ /**
2
+ * lib/setup/enrich.mjs — the bounded LLM enrichment function.
3
+ *
4
+ * The deterministic spine generates structurally-complete skeletons with
5
+ * company-specific "slots" left as placeholders (TBD / {{tokens}} / empty prose
6
+ * arrays). `enrichField` fills ONE enumerated prose unit by spawning a single
7
+ * `claude --print` with a field-allowlist contract:
8
+ *
9
+ * system = communication-style rules + "fill ONLY these JSON keys"
10
+ * input = archetype profile + context pack + the skeleton + the placeholder list
11
+ * output = strict JSON containing only the allowlisted keys
12
+ *
13
+ * Output is validated (right keys, non-empty, no residual {{}}, no banned
14
+ * phrases from policies/communication-style.md). On any failure — bad JSON,
15
+ * wrong keys, banned phrase, spawn error — the skeleton is LEFT UNTOUCHED, the
16
+ * field is marked `pending`, and the function returns without throwing
17
+ * (fail-open, per design principle 2).
18
+ *
19
+ * Enumerated enrichable units ONLY (design §4):
20
+ * agent.persona | agent.responsibilities | agent.operatingPrinciples
21
+ * company.description | charter.companySlots | backlog.companySlots
22
+ *
23
+ * The actual `claude --print` spawn is injected via `opts.spawn` so tests stay
24
+ * hermetic; production uses resolveClaudeBin()+daemonClaudeArgs()+execFile.
25
+ *
26
+ * @module lib/setup/enrich
27
+ */
28
+
29
+ "use strict";
30
+
31
+ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
32
+ import { join, dirname } from "node:path";
33
+ import { fileURLToPath } from "node:url";
34
+ import { execFile } from "node:child_process";
35
+ import { resolveClaudeBin, daemonClaudeArgs } from "../claude-bin.mjs";
36
+ import { mergeAgentJson, readAgentJson } from "./sot.mjs";
37
+ import { readContextPack } from "./context-pack.mjs";
38
+ import { markEnrich } from "./state.mjs";
39
+
40
+ const HERE = dirname(fileURLToPath(import.meta.url));
41
+ const MAESTRO_ROOT = join(HERE, "..", "..");
42
+
43
+ /** Banned AI-tell phrases (mirrors policies/communication-style.md §Default tone). */
44
+ export const BANNED_PHRASES = [
45
+ "i'm here to help",
46
+ "let me know if you need anything else",
47
+ "i hope this helps",
48
+ "feel free to ask if you have any questions",
49
+ "as an ai",
50
+ "as a language model",
51
+ "certainly!",
52
+ "absolutely!",
53
+ "great question!",
54
+ "here's a comprehensive overview",
55
+ "i'd be happy to",
56
+ ];
57
+
58
+ /**
59
+ * The allowlist of enrichable units and the agent.json keys each owns.
60
+ * `kind: "agentJson"` units write prose directly into config/agent.json;
61
+ * `kind: "regen"` units enrich a sibling file then re-run a generator (handled
62
+ * by the caller/section — here we only validate + persist the JSON-shaped ones).
63
+ */
64
+ export const ENRICHABLE = {
65
+ "agent.persona": { kind: "agentJson", keys: ["persona"], type: "string" },
66
+ "agent.responsibilities": { kind: "agentJson", keys: ["responsibilities"], type: "string[]" },
67
+ "agent.operatingPrinciples": { kind: "agentJson", keys: ["operatingPrinciples"], type: "string[]" },
68
+ "company.description": { kind: "companyJson", keys: ["description"], type: "string" },
69
+ // Charter company-slots: the model fills the concrete mandate, a KPI-target
70
+ // phrasing, and a default stakeholder-contact phrasing; the enrich section
71
+ // substitutes these into the deterministic charter body (clearing TBD slots).
72
+ "charter.companySlots": { kind: "charter", keys: ["mandate", "kpiTarget", "stakeholderContact"], type: "string" },
73
+ "backlog.companySlots": { kind: "regen", keys: ["focus"], type: "string" },
74
+ };
75
+
76
+ function containsBanned(text) {
77
+ const low = String(text).toLowerCase();
78
+ return BANNED_PHRASES.find((p) => low.includes(p)) || null;
79
+ }
80
+
81
+ function hasResidualToken(text) {
82
+ return /\{\{[^}]+\}\}/.test(String(text));
83
+ }
84
+
85
+ /**
86
+ * Validate a parsed enrichment object against a unit spec. Returns
87
+ * { ok, value?, reason? }. `value` is the cleaned, validated payload for the
88
+ * unit's keys.
89
+ * @param {object} parsed
90
+ * @param {{keys:string[], type:string}} spec
91
+ */
92
+ export function validateEnrichment(parsed, spec) {
93
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
94
+ return { ok: false, reason: "output was not a JSON object" };
95
+ }
96
+ const out = {};
97
+ for (const key of spec.keys) {
98
+ if (!(key in parsed)) return { ok: false, reason: `missing key "${key}"` };
99
+ const v = parsed[key];
100
+ if (spec.type === "string[]") {
101
+ if (!Array.isArray(v) || v.length === 0) return { ok: false, reason: `"${key}" must be a non-empty array` };
102
+ for (const item of v) {
103
+ if (typeof item !== "string" || !item.trim()) return { ok: false, reason: `"${key}" has an empty entry` };
104
+ if (hasResidualToken(item)) return { ok: false, reason: `"${key}" has a residual {{token}}` };
105
+ const banned = containsBanned(item);
106
+ if (banned) return { ok: false, reason: `"${key}" contains banned phrase: ${banned}` };
107
+ }
108
+ out[key] = v.map((s) => s.trim());
109
+ } else {
110
+ if (typeof v !== "string" || !v.trim()) return { ok: false, reason: `"${key}" must be a non-empty string` };
111
+ if (hasResidualToken(v)) return { ok: false, reason: `"${key}" has a residual {{token}}` };
112
+ const banned = containsBanned(v);
113
+ if (banned) return { ok: false, reason: `"${key}" contains banned phrase: ${banned}` };
114
+ out[key] = v.trim();
115
+ }
116
+ }
117
+ return { ok: true, value: out };
118
+ }
119
+
120
+ /** Extract the first balanced JSON object from possibly-noisy model output. */
121
+ export function parseStrictJson(raw) {
122
+ if (typeof raw !== "string") return null;
123
+ const text = raw.trim();
124
+ // Prefer a fenced ```json block if present.
125
+ const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
126
+ const candidate = fence ? fence[1].trim() : text;
127
+ // Try direct parse first.
128
+ try { return JSON.parse(candidate); } catch { /* fall through */ }
129
+ // Otherwise grab the outermost {...}.
130
+ const start = candidate.indexOf("{");
131
+ const end = candidate.lastIndexOf("}");
132
+ if (start >= 0 && end > start) {
133
+ try { return JSON.parse(candidate.slice(start, end + 1)); } catch { /* nope */ }
134
+ }
135
+ return null;
136
+ }
137
+
138
+ /** Read the communication-style policy once for the system prompt. */
139
+ function commStyleRules() {
140
+ const p = join(MAESTRO_ROOT, "policies", "communication-style.md");
141
+ try { return readFileSync(p, "utf-8").slice(0, 4000); } catch { return ""; }
142
+ }
143
+
144
+ /**
145
+ * Build the system + user prompt for a unit. Exposed for testing.
146
+ * @param {string} unit
147
+ * @param {object} pack context pack
148
+ * @param {object} skeleton current agent.json (or relevant slice)
149
+ */
150
+ export function buildPrompt(unit, pack, skeleton) {
151
+ const spec = ENRICHABLE[unit];
152
+ const keys = spec ? spec.keys : [];
153
+ const system = [
154
+ "You write company-specific prose for an autonomous enterprise AI agent.",
155
+ "Voice & tone rules you MUST obey (from policies/communication-style.md):",
156
+ commStyleRules(),
157
+ "",
158
+ `Output a single strict JSON object containing ONLY these keys: ${JSON.stringify(keys)}.`,
159
+ "No prose outside the JSON. No markdown fences. No extra keys.",
160
+ "Do not include any {{template tokens}}. Do not use assistant-y filler.",
161
+ ].join("\n");
162
+ const user = [
163
+ `Enrich the field unit "${unit}" for this agent.`,
164
+ `Archetype: ${pack && pack.identity ? `${pack.identity.function} × ${pack.identity.altitude}` : "(unknown)"}`,
165
+ `Identity: ${JSON.stringify(pack && pack.identity ? pack.identity : {})}`,
166
+ `Company: ${JSON.stringify(pack && pack.company ? pack.company : {})}`,
167
+ pack && pack.contextDocs && pack.contextDocs.length
168
+ ? `Context docs:\n${pack.contextDocs.map((d) => `- ${d.name}: ${d.excerpt.slice(0, 500)}`).join("\n")}`
169
+ : "Context docs: (none)",
170
+ `Current skeleton slice: ${JSON.stringify(skeleton)}`,
171
+ `Return JSON with keys ${JSON.stringify(keys)} only.`,
172
+ ].join("\n\n");
173
+ return { system, user };
174
+ }
175
+
176
+ /**
177
+ * Default spawn: one `claude --print`, returning stdout (or rejecting on error).
178
+ * @param {{system:string, user:string}} prompt
179
+ * @param {object} opts
180
+ * @returns {Promise<string>}
181
+ */
182
+ function defaultSpawn(prompt, opts = {}) {
183
+ return new Promise((resolve, reject) => {
184
+ const bin = resolveClaudeBin();
185
+ const args = [
186
+ "--print",
187
+ ...daemonClaudeArgs(),
188
+ "--model", opts.model || "claude-sonnet-4-6",
189
+ "--append-system-prompt", prompt.system,
190
+ prompt.user,
191
+ ];
192
+ execFile(bin, args, { encoding: "utf-8", timeout: opts.timeoutMs || 60000, maxBuffer: 4 * 1024 * 1024 }, (err, stdout) => {
193
+ if (err) return reject(err);
194
+ resolve(stdout || "");
195
+ });
196
+ });
197
+ }
198
+
199
+ /**
200
+ * Enrich a single field unit. Fail-open: returns a result object, never throws.
201
+ *
202
+ * @param {string} agentRoot
203
+ * @param {string} unit one of ENRICHABLE keys
204
+ * @param {object} [opts]
205
+ * @param {(prompt:object, opts:object)=>Promise<string>} [opts.spawn] injectable spawn (for tests)
206
+ * @param {boolean} [opts.persist=true] write the validated value into agent.json
207
+ * @returns {Promise<{unit:string, status:'enriched'|'pending'|'skipped', reason?:string, value?:object}>}
208
+ */
209
+ export async function enrichField(agentRoot, unit, opts = {}) {
210
+ const spec = ENRICHABLE[unit];
211
+ if (!spec) {
212
+ return { unit, status: "skipped", reason: `unit "${unit}" is not enrichable` };
213
+ }
214
+ const persist = opts.persist !== false;
215
+ const record = (status, reason) => { try { markEnrich(agentRoot, unit, { status, reason }); } catch { /* fail-open */ } };
216
+
217
+ let raw;
218
+ try {
219
+ const pack = readContextPack(agentRoot) || {};
220
+ const skeleton = sliceSkeleton(agentRoot, spec);
221
+ const prompt = buildPrompt(unit, pack, skeleton);
222
+ const spawn = opts.spawn || defaultSpawn;
223
+ raw = await spawn(prompt, opts);
224
+ } catch (e) {
225
+ record("pending", `spawn failed: ${e && e.message ? e.message : e}`);
226
+ return { unit, status: "pending", reason: "spawn failed" };
227
+ }
228
+
229
+ const parsed = parseStrictJson(raw);
230
+ if (!parsed) {
231
+ record("pending", "could not parse strict JSON");
232
+ return { unit, status: "pending", reason: "unparseable output" };
233
+ }
234
+ const v = validateEnrichment(parsed, spec);
235
+ if (!v.ok) {
236
+ record("pending", v.reason);
237
+ return { unit, status: "pending", reason: v.reason };
238
+ }
239
+
240
+ if (persist && spec.kind === "agentJson") {
241
+ try { mergeAgentJson(agentRoot, v.value); }
242
+ catch (e) { record("pending", `persist failed: ${e && e.message ? e.message : e}`); return { unit, status: "pending", reason: "persist failed" }; }
243
+ } else if (persist && spec.kind === "companyJson") {
244
+ try { mergeCompanyJson(agentRoot, v.value); }
245
+ catch (e) { record("pending", `persist failed: ${e && e.message ? e.message : e}`); return { unit, status: "pending", reason: "persist failed" }; }
246
+ }
247
+ // kind:"regen" units return the value; the caller folds it into the relevant
248
+ // generator input + re-runs the generator (the regen path is section-owned).
249
+
250
+ record("enriched");
251
+ return { unit, status: "enriched", value: v.value };
252
+ }
253
+
254
+ /** The slice of the SoT relevant to a unit (keeps the prompt small + focused). */
255
+ function sliceSkeleton(agentRoot, spec) {
256
+ if (spec.kind === "companyJson") {
257
+ try { const p = join(agentRoot, "config", "company.json"); return existsSync(p) ? JSON.parse(readFileSync(p, "utf-8")) : {}; }
258
+ catch { return {}; }
259
+ }
260
+ const id = readAgentJson(agentRoot) || {};
261
+ const out = {};
262
+ for (const k of spec.keys) out[k] = id[k];
263
+ // include identity context the model needs
264
+ out._identity = { firstName: id.firstName, title: id.title, function: id.function, altitude: id.altitude, company: id.company };
265
+ return out;
266
+ }
267
+
268
+ function mergeCompanyJson(agentRoot, patch) {
269
+ const p = join(agentRoot, "config", "company.json");
270
+ const cur = existsSync(p) ? JSON.parse(readFileSync(p, "utf-8")) : {};
271
+ const merged = { ...cur, ...patch };
272
+ mkdirSync(dirname(p), { recursive: true });
273
+ const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
274
+ writeFileSync(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
275
+ renameSync(tmp, p);
276
+ return merged;
277
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * enrich.test.mjs — bounded LLM enrichment (lib/setup/enrich.mjs).
3
+ * The claude spawn is injected (fake) so the test is hermetic — no network.
4
+ * Run: `node --test lib/setup/enrich.test.mjs`
5
+ * @module lib/setup/enrich.test
6
+ */
7
+
8
+ "use strict";
9
+
10
+ import { test } from "node:test";
11
+ import assert from "node:assert/strict";
12
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
13
+ import { tmpdir } from "node:os";
14
+ import { join } from "node:path";
15
+ import {
16
+ enrichField, validateEnrichment, parseStrictJson, buildPrompt, ENRICHABLE, BANNED_PHRASES,
17
+ } from "./enrich.mjs";
18
+ import { gatherContextPack } from "./context-pack.mjs";
19
+ import { readAgentJson } from "./sot.mjs";
20
+ import { loadProgress } from "./state.mjs";
21
+
22
+ function tmpRoot() {
23
+ const r = mkdtempSync(join(tmpdir(), "maestro-enrich-"));
24
+ mkdirSync(join(r, "config"), { recursive: true });
25
+ writeFileSync(join(r, "config/agent.json"), JSON.stringify({
26
+ firstName: "Jacob", title: "VP Eng", function: "technical-leader", altitude: "vp", company: "Acme",
27
+ responsibilities: [], operatingPrinciples: [],
28
+ }, null, 2));
29
+ writeFileSync(join(r, "config/company.json"), JSON.stringify({ name: "Acme", description: "" }, null, 2));
30
+ gatherContextPack(r);
31
+ return r;
32
+ }
33
+
34
+ test("parseStrictJson handles fenced and raw JSON and noisy output", () => {
35
+ assert.deepEqual(parseStrictJson('{"a":1}'), { a: 1 });
36
+ assert.deepEqual(parseStrictJson("```json\n{\"a\":2}\n```"), { a: 2 });
37
+ assert.deepEqual(parseStrictJson("Sure!\n{\"a\":3}\nthanks"), { a: 3 });
38
+ assert.equal(parseStrictJson("no json here"), null);
39
+ });
40
+
41
+ test("validateEnrichment enforces keys, types, tokens, banned phrases", () => {
42
+ const spec = ENRICHABLE["agent.responsibilities"];
43
+ assert.equal(validateEnrichment({ responsibilities: ["Own platform reliability"] }, spec).ok, true);
44
+ assert.equal(validateEnrichment({ wrong: [] }, spec).ok, false);
45
+ assert.equal(validateEnrichment({ responsibilities: [] }, spec).ok, false);
46
+ assert.equal(validateEnrichment({ responsibilities: ["see {{x}}"] }, spec).ok, false);
47
+ const banned = validateEnrichment({ responsibilities: ["As an AI, I will help"] }, spec);
48
+ assert.equal(banned.ok, false);
49
+ assert.match(banned.reason, /banned phrase/);
50
+ });
51
+
52
+ test("buildPrompt requests only the unit's keys", () => {
53
+ const p = buildPrompt("agent.persona", { identity: {}, company: {} }, { persona: "" });
54
+ assert.match(p.system, /\["persona"\]/);
55
+ assert.match(p.user, /agent\.persona/);
56
+ });
57
+
58
+ test("enrichField writes validated prose into agent.json (faked spawn)", async () => {
59
+ const root = tmpRoot();
60
+ const fakeSpawn = async () => JSON.stringify({ responsibilities: ["Run the platform org", "Own delivery confidence"] });
61
+ const res = await enrichField(root, "agent.responsibilities", { spawn: fakeSpawn });
62
+ assert.equal(res.status, "enriched");
63
+ const cfg = readAgentJson(root);
64
+ assert.deepEqual(cfg.responsibilities, ["Run the platform org", "Own delivery confidence"]);
65
+ // other keys untouched
66
+ assert.equal(cfg.firstName, "Jacob");
67
+ // ledger updated
68
+ assert.equal(loadProgress(root).enrich["agent.responsibilities"].status, "enriched");
69
+ });
70
+
71
+ test("enrichField is fail-open on bad JSON — leaves skeleton, marks pending", async () => {
72
+ const root = tmpRoot();
73
+ const before = readAgentJson(root);
74
+ const fakeSpawn = async () => "definitely not json";
75
+ const res = await enrichField(root, "agent.persona", { spawn: fakeSpawn });
76
+ assert.equal(res.status, "pending");
77
+ assert.deepEqual(readAgentJson(root), before, "agent.json unchanged on failure");
78
+ assert.equal(loadProgress(root).enrich["agent.persona"].status, "pending");
79
+ });
80
+
81
+ test("enrichField is fail-open on spawn throw (no network)", async () => {
82
+ const root = tmpRoot();
83
+ const fakeSpawn = async () => { throw new Error("ENOENT claude"); };
84
+ const res = await enrichField(root, "agent.operatingPrinciples", { spawn: fakeSpawn });
85
+ assert.equal(res.status, "pending");
86
+ assert.match(res.reason, /spawn failed/);
87
+ });
88
+
89
+ test("enrichField rejects a banned-phrase response (stays pending)", async () => {
90
+ const root = tmpRoot();
91
+ const fakeSpawn = async () => JSON.stringify({ persona: "I'm here to help with anything you need." });
92
+ const res = await enrichField(root, "agent.persona", { spawn: fakeSpawn });
93
+ assert.equal(res.status, "pending");
94
+ assert.match(res.reason, /banned phrase/);
95
+ });
96
+
97
+ test("enrichField writes company.description for companyJson unit", async () => {
98
+ const root = tmpRoot();
99
+ const fakeSpawn = async () => JSON.stringify({ description: "Acme builds developer tooling for fintechs." });
100
+ const res = await enrichField(root, "company.description", { spawn: fakeSpawn });
101
+ assert.equal(res.status, "enriched");
102
+ const co = JSON.parse(readFileSync(join(root, "config/company.json"), "utf-8"));
103
+ assert.match(co.description, /developer tooling/);
104
+ });
105
+
106
+ test("enrichField skips unknown units", async () => {
107
+ const root = tmpRoot();
108
+ const res = await enrichField(root, "not.a.unit", { spawn: async () => "{}" });
109
+ assert.equal(res.status, "skipped");
110
+ });
111
+
112
+ test("BANNED_PHRASES includes the canonical AI-tells", () => {
113
+ assert.ok(BANNED_PHRASES.includes("as an ai"));
114
+ assert.ok(BANNED_PHRASES.includes("i'd be happy to"));
115
+ });