@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,81 @@
1
+ /**
2
+ * learning.test.mjs — WS3 setup section (order 80) tests.
3
+ * Run: node --test lib/setup/sections/learning.test.mjs
4
+ */
5
+ "use strict";
6
+
7
+ import { test } from "node:test";
8
+ import assert from "node:assert/strict";
9
+ import { mkdtempSync, existsSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join, dirname } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ import learning from "./learning.mjs";
15
+ import * as io from "../io.mjs";
16
+
17
+ const MAESTRO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
18
+
19
+ function tmpRoot() {
20
+ const root = mkdtempSync(join(tmpdir(), "maestro-learnsec-"));
21
+ mkdirSync(join(root, "config"), { recursive: true });
22
+ return root;
23
+ }
24
+ function ctxFor(root, answers = {}) {
25
+ return { agentRoot: root, maestroRoot: MAESTRO_ROOT, flags: { headless: true }, answers, io };
26
+ }
27
+
28
+ test("section shape: id/order/hooks", () => {
29
+ assert.equal(learning.id, "learning");
30
+ assert.equal(learning.order, 80);
31
+ for (const h of ["detect", "prompt", "apply", "verify"]) assert.equal(typeof learning[h], "function");
32
+ });
33
+
34
+ test("detect: unconfigured before apply, complete after", async () => {
35
+ const root = tmpRoot();
36
+ const before = await learning.detect(ctxFor(root));
37
+ assert.equal(before.status, "unconfigured");
38
+
39
+ await learning.apply(ctxFor(root, { learningEnabled: true }));
40
+ // config + counters now exist; hook is NOT wired on a CI machine → partial.
41
+ const after = await learning.detect(ctxFor(root));
42
+ assert.ok(["complete", "partial"].includes(after.status));
43
+ assert.ok(existsSync(join(root, "config", "learning.yaml")));
44
+ assert.ok(existsSync(join(root, "state", "learning", "counters.json")));
45
+ // Counters seeded to zero state.
46
+ const c = JSON.parse(readFileSync(join(root, "state", "learning", "counters.json"), "utf8"));
47
+ assert.equal(c.tool_uses_since_skill, 0);
48
+ });
49
+
50
+ test("apply: disabled choice writes enabled:false and skips seeding", async () => {
51
+ const root = tmpRoot();
52
+ await learning.apply(ctxFor(root, { learningEnabled: false }));
53
+ const body = readFileSync(join(root, "config", "learning.yaml"), "utf8");
54
+ assert.match(body, /enabled:\s*false/);
55
+ assert.ok(!existsSync(join(root, "state", "learning", "counters.json")), "no counters when disabled");
56
+ const det = await learning.detect(ctxFor(root));
57
+ assert.equal(det.status, "complete");
58
+ assert.match(det.summary, /disabled/);
59
+ });
60
+
61
+ test("verify: config + counters checks; hook is a soft (non-failing) check", async () => {
62
+ const root = tmpRoot();
63
+ await learning.apply(ctxFor(root, { learningEnabled: true }));
64
+ const v = await learning.verify(ctxFor(root));
65
+ const names = v.checks.map((c) => c.name);
66
+ assert.ok(names.some((n) => /config\/learning\.yaml/.test(n)));
67
+ assert.ok(names.some((n) => /counters seeded/.test(n)));
68
+ // The hook check never hard-fails verify (so CI / fresh repos stay green).
69
+ const hookCheck = v.checks.find((c) => /Stop-hook/.test(c.name));
70
+ assert.equal(hookCheck.ok, true);
71
+ assert.equal(v.ok, true);
72
+ });
73
+
74
+ test("apply: idempotent — re-apply does not clobber an existing config", async () => {
75
+ const root = tmpRoot();
76
+ // Operator hand-customised config.
77
+ writeFileSync(join(root, "config", "learning.yaml"), "enabled: true\nminTurns: 9\n# custom\n");
78
+ await learning.apply(ctxFor(root, { learningEnabled: true }));
79
+ const body = readFileSync(join(root, "config", "learning.yaml"), "utf8");
80
+ assert.match(body, /minTurns: 9/, "existing customisation preserved");
81
+ });
@@ -0,0 +1,219 @@
1
+ /**
2
+ * sections/messaging.mjs — org messaging + calling enablement (order 76).
3
+ *
4
+ * SP10 wiring step: makes an agent PARTICIPATE in the org's own human messaging
5
+ * app (post / @mention / react / call ON Cohort). It is a thin follow-on to the
6
+ * org-enrollment section (order 75): it does NOT re-capture the server URL or
7
+ * token (that's org.mjs's job) — it confirms enrollment is present, ensures the
8
+ * messaging.read / messaging.write / calling.write scopes are recorded in the
9
+ * agent's GRANTED scope set, and optionally captures the agent's "home" channels
10
+ * (the channels it watches + posts into by default), writing all of it to
11
+ * config/org.yaml under `org.cohort.{scopes,messaging.homeChannels}`.
12
+ *
13
+ * Order 76 sits immediately after org (75) and before the WS4 recovery/85 +
14
+ * verify/90 gate, so messaging enablement lands once the org endpoint+token are
15
+ * known but is still verified before the final gate.
16
+ *
17
+ * Non-blocking by contract: detect/apply/verify NEVER read the TTY (so doctor /
18
+ * CI / --headless call them directly); only prompt() touches the TTY. apply() is
19
+ * idempotent and ADDITIVE — it merges the messaging scopes into whatever scope
20
+ * set is already configured (never clobbering operator-set fields) and never
21
+ * writes anything destructive. verify() best-effort pings messaging.channels and
22
+ * WARNs (never hard-fails) when the org is disabled / unreachable — messaging is
23
+ * fail-open at runtime.
24
+ *
25
+ * @module lib/setup/sections/messaging
26
+ */
27
+
28
+ "use strict";
29
+
30
+ import { join } from "node:path";
31
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
32
+ import * as io from "../io.mjs";
33
+ import { isEnabled, configFromAgent } from "../../org/client.mjs";
34
+ import { listChannels } from "../../org/messaging.mjs";
35
+
36
+ const CONFIG_REL = join("config", "org.yaml");
37
+
38
+ /** The scopes this section guarantees are present in the granted set. */
39
+ export const MESSAGING_SCOPES = Object.freeze(["messaging.read", "messaging.write", "calling.write"]);
40
+
41
+ /** Lazily load js-yaml (a maestro dep) the same way the lib/org modules do. */
42
+ async function loadYaml() {
43
+ const m = await import("js-yaml");
44
+ return m && m.default ? m.default : m;
45
+ }
46
+
47
+ /**
48
+ * Read config/org.yaml into a plain object. Returns {} when absent/unreadable so
49
+ * callers can null-check uniformly. Never throws.
50
+ * @param {string} agentRoot
51
+ * @returns {Promise<object>}
52
+ */
53
+ export async function readOrgConfig(agentRoot) {
54
+ const p = join(agentRoot, CONFIG_REL);
55
+ if (!existsSync(p)) return {};
56
+ try {
57
+ const yaml = await loadYaml();
58
+ const doc = yaml.load(readFileSync(p, "utf8"));
59
+ return doc && typeof doc === "object" ? doc : {};
60
+ } catch {
61
+ return {};
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Merge the messaging/calling scopes (and optional home channels) into the
67
+ * org.cohort block, preserving everything else. ADDITIVE on scopes — the merged
68
+ * set is the union of any existing `scopes` plus MESSAGING_SCOPES, de-duplicated
69
+ * and stable-sorted so the write is idempotent. Pure.
70
+ *
71
+ * @param {object} existing parsed config/org.yaml ({} when none)
72
+ * @param {{homeChannels?:string[]}} ans
73
+ * @returns {object} the merged config object to serialise
74
+ */
75
+ export function buildMessagingConfig(existing, ans = {}) {
76
+ const prevOrg = (existing && existing.org) || {};
77
+ const prev = prevOrg.cohort || {};
78
+ const cohort = { ...prev };
79
+
80
+ // Union the granted scopes with the messaging ones; de-dupe; stable-sort.
81
+ const prevScopes = Array.isArray(prev.scopes) ? prev.scopes.map(String) : [];
82
+ const merged = Array.from(new Set([...prevScopes, ...MESSAGING_SCOPES]));
83
+ merged.sort();
84
+ cohort.scopes = merged;
85
+
86
+ // Optional home channels — merge into the existing messaging block.
87
+ const home = Array.isArray(ans.homeChannels)
88
+ ? ans.homeChannels.map((s) => String(s).trim()).filter(Boolean)
89
+ : [];
90
+ const prevMsg = (prev.messaging && typeof prev.messaging === "object") ? prev.messaging : {};
91
+ if (home.length || (Array.isArray(prevMsg.homeChannels) && prevMsg.homeChannels.length)) {
92
+ const prevHome = Array.isArray(prevMsg.homeChannels) ? prevMsg.homeChannels.map(String) : [];
93
+ const mergedHome = Array.from(new Set([...prevHome, ...home]));
94
+ cohort.messaging = { ...prevMsg, homeChannels: mergedHome };
95
+ } else if (Object.keys(prevMsg).length) {
96
+ cohort.messaging = prevMsg;
97
+ }
98
+
99
+ return { ...(existing || {}), org: { ...prevOrg, cohort } };
100
+ }
101
+
102
+ /** Resolve the answers bag (answers → env, via io defaults). */
103
+ function resolvedAnswers(ctx) {
104
+ const a = (ctx && ctx.answers) || {};
105
+ let home = a.homeChannels;
106
+ if (home == null && process.env.COHORT_HOME_CHANNELS) home = process.env.COHORT_HOME_CHANNELS;
107
+ if (typeof home === "string") {
108
+ home = home.split(",").map((s) => s.trim()).filter(Boolean);
109
+ }
110
+ return { homeChannels: Array.isArray(home) ? home : [] };
111
+ }
112
+
113
+ /** Do the configured granted scopes already cover all messaging scopes? */
114
+ function hasAllMessagingScopes(cfg) {
115
+ const scopes = (cfg && cfg.org && cfg.org.cohort && cfg.org.cohort.scopes) || [];
116
+ if (!Array.isArray(scopes)) return false;
117
+ const set = new Set(scopes.map(String));
118
+ return MESSAGING_SCOPES.every((s) => set.has(s));
119
+ }
120
+
121
+ export default {
122
+ id: "messaging",
123
+ label: "Org messaging + calling",
124
+ order: 76,
125
+
126
+ async detect(ctx) {
127
+ const cfgPath = join(ctx.agentRoot, CONFIG_REL);
128
+ if (!existsSync(cfgPath)) {
129
+ // Org section hasn't run yet — messaging can't be enabled without org.
130
+ return { status: "unconfigured", missing: ["config/org.yaml (run the org section first)"] };
131
+ }
132
+ const cfg = await readOrgConfig(ctx.agentRoot);
133
+ if (!isEnabled(cfg)) {
134
+ return { status: "partial", summary: "org not enrolled — messaging is dormant", missing: ["org.cohort enabled (base + token)"] };
135
+ }
136
+ if (hasAllMessagingScopes(cfg)) {
137
+ return { status: "complete", summary: "messaging + calling scopes granted" };
138
+ }
139
+ return { status: "partial", summary: "org enrolled but messaging scopes not yet recorded", missing: ["org.cohort.scopes: messaging.read/write + calling.write"] };
140
+ },
141
+
142
+ async prompt(ctx) {
143
+ io.note("Let this agent post / @mention / call ON the org messaging app (Cohort). Requires org enrollment (previous step).", { dim: true });
144
+ // Home channels are OPTIONAL — comma-separated channel ids the agent watches
145
+ // and posts into by default. Blank = no default channels (it still receives
146
+ // @mentions and DMs).
147
+ const home = await io.text(ctx, {
148
+ message: "Home channel id(s) the agent watches/posts into (comma-separated, optional)",
149
+ key: "homeChannels",
150
+ env: "COHORT_HOME_CHANNELS",
151
+ default: "",
152
+ });
153
+ ctx.answers.homeChannels = home
154
+ ? String(home).split(",").map((s) => s.trim()).filter(Boolean)
155
+ : [];
156
+ },
157
+
158
+ async apply(ctx) {
159
+ const ans = resolvedAnswers(ctx);
160
+ const existing = await readOrgConfig(ctx.agentRoot);
161
+
162
+ // If org.yaml doesn't exist yet, do NOTHING destructive — the org section
163
+ // owns creating it. We still write the messaging scope set onto whatever is
164
+ // there so a later org enable inherits it; if there is genuinely no config
165
+ // we write a minimal stub carrying just the scopes so detect/verify have a
166
+ // stable artefact (matches the org section's disabled-stub behaviour).
167
+ const next = buildMessagingConfig(existing, ans);
168
+
169
+ mkdirSync(join(ctx.agentRoot, "config"), { recursive: true });
170
+ const yaml = await loadYaml();
171
+ const header =
172
+ "# config/org.yaml — Org enrollment + messaging (Cohort).\n" +
173
+ "# The `org` setup section owns base/token; the `messaging` section adds the\n" +
174
+ "# messaging.read/write + calling.write scopes and optional home channels so\n" +
175
+ "# the agent can post / @mention / call on the org messaging app. The SERVER\n" +
176
+ "# enforces ACL — these scopes are what the agent REQUESTS at pairing time.\n";
177
+ writeFileSync(join(ctx.agentRoot, CONFIG_REL), header + yaml.dump(next, { lineWidth: 120, sortKeys: false }));
178
+ },
179
+
180
+ async verify(ctx) {
181
+ const checks = [];
182
+ const cfgPresent = existsSync(join(ctx.agentRoot, CONFIG_REL));
183
+ checks.push({ name: "config/org.yaml present", ok: cfgPresent, remedy: cfgPresent ? undefined : "run `maestro setup --only org` then `--only messaging`" });
184
+
185
+ const cfg = await readOrgConfig(ctx.agentRoot);
186
+ const scopesOk = hasAllMessagingScopes(cfg);
187
+ checks.push({
188
+ name: "messaging + calling scopes recorded",
189
+ ok: scopesOk,
190
+ remedy: scopesOk ? undefined : "run `maestro setup --only messaging` to add messaging.read/write + calling.write to org.cohort.scopes",
191
+ });
192
+
193
+ if (!isEnabled(cfg)) {
194
+ // Messaging is OPTIONAL and dormant until the org is enrolled — WARN, do
195
+ // not hard-fail setup (the agent can run standalone).
196
+ checks.push({ name: "org messaging (dormant — not enrolled)", ok: true, remedy: "enroll the org (set org.cohort.base + token) to activate messaging" });
197
+ return { ok: checks.every((c) => c.ok), checks };
198
+ }
199
+
200
+ // Best-effort, NON-BLOCKING reachability probe via messaging.channels. The
201
+ // client is fail-open (transport error → []), so a timeout/unreachable server
202
+ // just yields an empty list; we never hard-fail on reachability — the runtime
203
+ // messaging path is itself fail-open.
204
+ let reachable = false;
205
+ try {
206
+ const channels = await listChannels({ cfg, fetchImpl: ctx.fetchImpl });
207
+ reachable = Array.isArray(channels);
208
+ } catch {
209
+ reachable = false;
210
+ }
211
+ checks.push({
212
+ name: reachable ? "Cohort messaging reachable (channels listed)" : "Cohort messaging reachability (best-effort)",
213
+ ok: true, // never hard-fail — messaging is fail-open at runtime
214
+ remedy: reachable ? undefined : "could not list org channels — messaging will fail open until the server is up + scopes are granted",
215
+ });
216
+
217
+ return { ok: checks.every((c) => c.ok), checks };
218
+ },
219
+ };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * messaging.test.mjs — org messaging+calling setup section (order 76) tests.
3
+ * Non-blocking (headless ctx), additive scope merge, no destructive writes,
4
+ * best-effort verify. Run: node --test lib/setup/sections/messaging.test.mjs
5
+ */
6
+ "use strict";
7
+
8
+ import { test } from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { mkdtempSync, existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { join, dirname } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import yaml from "js-yaml";
15
+
16
+ import messaging, { buildMessagingConfig, readOrgConfig, MESSAGING_SCOPES } from "./messaging.mjs";
17
+ import * as io from "../io.mjs";
18
+
19
+ const MAESTRO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
20
+
21
+ function tmpRoot() {
22
+ const root = mkdtempSync(join(tmpdir(), "maestro-msgsec-"));
23
+ mkdirSync(join(root, "config"), { recursive: true });
24
+ return root;
25
+ }
26
+ function ctxFor(root, answers = {}) {
27
+ return { agentRoot: root, maestroRoot: MAESTRO_ROOT, flags: { headless: true }, answers, io };
28
+ }
29
+ function readCfg(root) {
30
+ return yaml.load(readFileSync(join(root, "config", "org.yaml"), "utf8"));
31
+ }
32
+ function writeOrg(root, cohort) {
33
+ writeFileSync(join(root, "config", "org.yaml"), yaml.dump({ org: { cohort } }));
34
+ }
35
+
36
+ test("section shape: id/order=76/hooks; hooks are functions", () => {
37
+ assert.equal(messaging.id, "messaging");
38
+ assert.equal(messaging.order, 76);
39
+ for (const h of ["detect", "prompt", "apply", "verify"]) assert.equal(typeof messaging[h], "function");
40
+ });
41
+
42
+ test("buildMessagingConfig: ADDITIVE union of existing scopes + messaging scopes, sorted + de-duped", () => {
43
+ const existing = { org: { cohort: { enabled: true, base: "b", token: "t", scopes: ["org.read", "messaging.read"] } } };
44
+ const next = buildMessagingConfig(existing, {});
45
+ const scopes = next.org.cohort.scopes;
46
+ for (const s of MESSAGING_SCOPES) assert.ok(scopes.includes(s), `${s} present`);
47
+ assert.ok(scopes.includes("org.read"), "existing scope preserved");
48
+ // de-duped (messaging.read appeared in both inputs) + sorted (idempotent write)
49
+ assert.equal(new Set(scopes).size, scopes.length, "no duplicates");
50
+ assert.deepEqual(scopes, [...scopes].sort(), "stable-sorted");
51
+ // never drops base/token
52
+ assert.equal(next.org.cohort.base, "b");
53
+ assert.equal(next.org.cohort.token, "t");
54
+ });
55
+
56
+ test("buildMessagingConfig: home channels merge (union) without clobbering", () => {
57
+ const existing = { org: { cohort: { enabled: true, base: "b", messaging: { homeChannels: ["C1"] } } } };
58
+ const next = buildMessagingConfig(existing, { homeChannels: ["C2", "C1"] });
59
+ assert.deepEqual(next.org.cohort.messaging.homeChannels.sort(), ["C1", "C2"]);
60
+ });
61
+
62
+ test("detect: unconfigured when there is no org.yaml at all", async () => {
63
+ const root = tmpRoot();
64
+ const d = await messaging.detect(ctxFor(root));
65
+ assert.equal(d.status, "unconfigured");
66
+ });
67
+
68
+ test("detect: partial when org present but not enrolled", async () => {
69
+ const root = tmpRoot();
70
+ writeOrg(root, { enabled: false });
71
+ const d = await messaging.detect(ctxFor(root));
72
+ assert.equal(d.status, "partial");
73
+ });
74
+
75
+ test("detect: complete when enrolled AND messaging scopes recorded", async () => {
76
+ const root = tmpRoot();
77
+ writeOrg(root, { enabled: true, base: "https://x", token: "t", scopes: [...MESSAGING_SCOPES] });
78
+ const d = await messaging.detect(ctxFor(root));
79
+ assert.equal(d.status, "complete");
80
+ });
81
+
82
+ test("apply: idempotent + additive — records messaging scopes onto an enrolled org without dropping fields", async () => {
83
+ const root = tmpRoot();
84
+ writeOrg(root, { enabled: true, base: "https://cohort.example", token: "tok-1", scopes: ["org.read"] });
85
+ await messaging.apply(ctxFor(root, { homeChannels: ["C-eng"] }));
86
+ const cfg = readCfg(root);
87
+ for (const s of MESSAGING_SCOPES) assert.ok(cfg.org.cohort.scopes.includes(s));
88
+ assert.ok(cfg.org.cohort.scopes.includes("org.read"), "did not clobber existing scope");
89
+ assert.equal(cfg.org.cohort.base, "https://cohort.example", "did not clobber base");
90
+ assert.equal(cfg.org.cohort.token, "tok-1", "did not clobber token");
91
+ assert.deepEqual(cfg.org.cohort.messaging.homeChannels, ["C-eng"]);
92
+ assert.equal(cfg.org.cohort.enabled, true, "did not flip enabled");
93
+
94
+ // Idempotent: a second apply with no new answers leaves the scope set identical.
95
+ const before = JSON.stringify(readCfg(root).org.cohort.scopes);
96
+ await messaging.apply(ctxFor(root, {}));
97
+ assert.equal(JSON.stringify(readCfg(root).org.cohort.scopes), before, "second apply is a no-op on scopes");
98
+ });
99
+
100
+ test("apply: never blocks — runs under headless ctx with no answers, writes a non-destructive stub", async () => {
101
+ const root = tmpRoot();
102
+ // No org.yaml yet — apply must not throw and must not enable anything.
103
+ await messaging.apply(ctxFor(root, {}));
104
+ assert.ok(existsSync(join(root, "config", "org.yaml")));
105
+ const cfg = readCfg(root);
106
+ // scopes recorded, but enrollment NOT fabricated (no base/token, enabled falsy).
107
+ for (const s of MESSAGING_SCOPES) assert.ok(cfg.org.cohort.scopes.includes(s));
108
+ assert.ok(!cfg.org.cohort.enabled, "apply never enables the org by itself");
109
+ });
110
+
111
+ test("verify: org not enrolled → ok:true (optional/dormant), with scopes-recorded check", async () => {
112
+ const root = tmpRoot();
113
+ await messaging.apply(ctxFor(root, {}));
114
+ const v = await messaging.verify(ctxFor(root));
115
+ assert.equal(v.ok, true, "dormant messaging never hard-fails setup");
116
+ assert.ok(v.checks.some((c) => /scopes recorded/.test(c.name) && c.ok));
117
+ });
118
+
119
+ test("verify: enrolled → best-effort channels probe, never hard-fails on unreachable", async () => {
120
+ const root = tmpRoot();
121
+ writeOrg(root, { enabled: true, base: "https://unreachable.invalid", token: "t", scopes: [...MESSAGING_SCOPES] });
122
+ // Injected fetch that errors → listChannels fails open to []; verify still ok.
123
+ const ctx = ctxFor(root);
124
+ ctx.fetchImpl = async () => { throw new Error("unreachable"); };
125
+ const v = await messaging.verify(ctx);
126
+ assert.equal(v.ok, true);
127
+ });
@@ -0,0 +1,102 @@
1
+ /**
2
+ * sections/model.mjs — model / provider selection. (order 30)
3
+ *
4
+ * Records the provider choice: either Anthropic API key (recorded in .env) or
5
+ * the Claude Code subscription (MAESTRO_PREFER_SUBSCRIPTION_AUTH=1). Optionally
6
+ * drops a config/model-routing.yaml from the shipped example for cost routing.
7
+ * Verify probes Claude API reachability (shared with doctor).
8
+ *
9
+ * @module lib/setup/sections/model
10
+ */
11
+
12
+ "use strict";
13
+
14
+ import { join } from "node:path";
15
+ import { existsSync, readFileSync, appendFileSync, writeFileSync, copyFileSync } from "node:fs";
16
+ import * as io from "../io.mjs";
17
+ import { probeClaude } from "../claude-probe.mjs";
18
+
19
+ function readEnv(agentRoot) {
20
+ const p = join(agentRoot, ".env");
21
+ return existsSync(p) ? readFileSync(p, "utf-8") : "";
22
+ }
23
+ function hasKey(body, key) { return new RegExp(`^${key}=.+`, "m").test(body); }
24
+
25
+ function appendEnv(agentRoot, key, value) {
26
+ const p = join(agentRoot, ".env");
27
+ if (!existsSync(p)) { writeFileSync(p, `${key}=${value}\n`); return; }
28
+ const body = readFileSync(p, "utf-8");
29
+ if (new RegExp(`^${key}=`, "m").test(body)) return; // never clobber an existing value
30
+ appendFileSync(p, `${body.endsWith("\n") || body === "" ? "" : "\n"}${key}=${value}\n`);
31
+ }
32
+
33
+ export default {
34
+ id: "model",
35
+ label: "Model & provider",
36
+ order: 30,
37
+
38
+ async detect(ctx) {
39
+ const env = readEnv(ctx.agentRoot);
40
+ const subs = /^MAESTRO_PREFER_SUBSCRIPTION_AUTH=(1|true|yes)/im.test(env);
41
+ const apiKey = hasKey(env, "ANTHROPIC_API_KEY");
42
+ if (subs) return { status: "complete", summary: "Claude Code subscription auth" };
43
+ if (apiKey) return { status: "complete", summary: "Anthropic API key (.env)" };
44
+ return { status: "unconfigured", missing: ["provider auth"] };
45
+ },
46
+
47
+ async prompt(ctx) {
48
+ const provider = await io.select(ctx, {
49
+ message: "How should the agent authenticate to Claude?",
50
+ key: "provider",
51
+ choices: [
52
+ { label: "Claude Code subscription (keychain OAuth — no API key needed)", value: "subscription" },
53
+ { label: "Anthropic API key (paste an sk-ant-… key)", value: "api-key" },
54
+ ],
55
+ default: "subscription",
56
+ });
57
+ let apiKey = "";
58
+ if (provider === "api-key") {
59
+ apiKey = await io.secret(ctx, { message: "Paste ANTHROPIC_API_KEY (sk-ant-…)", key: "anthropicApiKey" });
60
+ }
61
+ const routing = await io.confirm(ctx, {
62
+ message: "Enable cost-optimised model routing (writes config/model-routing.yaml)?",
63
+ key: "modelRouting",
64
+ default: false,
65
+ });
66
+ ctx.answers.model = { provider, apiKey, routing };
67
+ },
68
+
69
+ async apply(ctx) {
70
+ const a = (ctx.answers && ctx.answers.model) || pull(ctx);
71
+ if (!a) return;
72
+ if (a.provider === "subscription") {
73
+ appendEnv(ctx.agentRoot, "MAESTRO_PREFER_SUBSCRIPTION_AUTH", "1");
74
+ } else if (a.provider === "api-key" && a.apiKey) {
75
+ appendEnv(ctx.agentRoot, "ANTHROPIC_API_KEY", a.apiKey);
76
+ }
77
+ if (a.routing) {
78
+ const dst = join(ctx.agentRoot, "config", "model-routing.yaml");
79
+ const example = join(ctx.maestroRoot, "scaffold", "config", "model-routing.yaml.example");
80
+ if (!existsSync(dst) && existsSync(example)) {
81
+ try { copyFileSync(example, dst); } catch { /* fail-open */ }
82
+ }
83
+ }
84
+ },
85
+
86
+ async verify(ctx) {
87
+ const probe = probeClaude(ctx.agentRoot);
88
+ return {
89
+ ok: probe.ok,
90
+ checks: [{ name: probe.name, ok: probe.ok, remedy: probe.remedy }],
91
+ };
92
+ },
93
+ };
94
+
95
+ function pull(ctx) {
96
+ const ans = ctx.answers || {};
97
+ if (ans.model) return ans.model;
98
+ if ("provider" in ans || "anthropicApiKey" in ans) {
99
+ return { provider: ans.provider || (ans.anthropicApiKey ? "api-key" : "subscription"), apiKey: ans.anthropicApiKey || "", routing: !!ans.modelRouting };
100
+ }
101
+ return null;
102
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * sections/operating-model.mjs — generate the full operating model. (order 60)
3
+ *
4
+ * apply() runs the deterministic generators in the design's exact order:
5
+ * init-archetype → generate-company → generate-charter → generate-backlog →
6
+ * generate-capability → generate-comms → generate-autonomy → generate-cadences
7
+ * → generate-plists.sh
8
+ *
9
+ * verify() = the shared completeness gate (lib/setup/completeness.assertComplete).
10
+ * A generator failure is fatal for this section (the operating model is the
11
+ * "gets straight to work" spine); enrichment of company-slots happens later in
12
+ * the enrich section.
13
+ *
14
+ * @module lib/setup/sections/operating-model
15
+ */
16
+
17
+ "use strict";
18
+
19
+ import { join } from "node:path";
20
+ import { runGenerator, runScriptAbs } from "../run-generator.mjs";
21
+ import { assertComplete } from "../completeness.mjs";
22
+ import * as io from "../io.mjs";
23
+
24
+ /** Ordered generator pipeline (design §4 "Operating-model apply() order"). */
25
+ const PIPELINE = [
26
+ "init-archetype.mjs",
27
+ "generate-company.mjs",
28
+ "generate-charter.mjs",
29
+ "generate-backlog.mjs",
30
+ "generate-capability.mjs",
31
+ "generate-comms.mjs",
32
+ "generate-autonomy.mjs",
33
+ "generate-cadences.mjs",
34
+ ];
35
+
36
+ export default {
37
+ id: "operating-model",
38
+ label: "Operating model (charter, backlog, cadences, policies)",
39
+ order: 60,
40
+
41
+ async detect(ctx) {
42
+ const res = await assertComplete(ctx.agentRoot);
43
+ if (res.ok) return { status: "complete", summary: "charter + backlog + roster + cadences + policies present" };
44
+ // Identity/archetype gaps are owned by the identity section, not here.
45
+ const own = res.missing.filter((m) => !m.artifact.startsWith("identity") && m.artifact !== "config/agent.json");
46
+ if (own.length === 0) return { status: "complete" };
47
+ return { status: own.length >= 6 ? "unconfigured" : "partial", missing: own.map((m) => m.artifact) };
48
+ },
49
+
50
+ // No interactive input — the operating model is derived from identity + company.
51
+ async prompt() { /* none */ },
52
+
53
+ async apply(ctx) {
54
+ for (const script of PIPELINE) {
55
+ const r = runGenerator(script, ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
56
+ if (!r.ok) {
57
+ throw new Error(`operating-model: ${script} failed (exit ${r.code}): ${(r.stderr || r.stdout || "").trim().slice(0, 300)}`);
58
+ }
59
+ if (ctx.flags && ctx.flags.verbose) io.note((r.stdout || "").trim(), { dim: true });
60
+ }
61
+ // launchd plists (cadence-bus architecture) live under scripts/local-triggers.
62
+ const plistGen = join(ctx.agentRoot, "scripts", "local-triggers", "generate-plists.sh");
63
+ const pr = runScriptAbs(plistGen, ctx.agentRoot, { bash: true, maestroRoot: ctx.maestroRoot });
64
+ // Plist generation can legitimately no-op on non-macOS / missing HOME; don't
65
+ // hard-fail the operating model on it (the artifacts above are the gate).
66
+ if (!pr.ok && ctx.flags && ctx.flags.verbose) io.note(`generate-plists.sh: ${(pr.stderr || "").trim()}`, { dim: true });
67
+ },
68
+
69
+ async verify(ctx) {
70
+ const res = await assertComplete(ctx.agentRoot);
71
+ const checks = res.ok
72
+ ? [{ name: "operating model complete", ok: true }]
73
+ : res.missing
74
+ .filter((m) => !m.artifact.startsWith("identity") && m.artifact !== "config/agent.json")
75
+ .map((m) => ({ name: m.artifact, ok: false, remedy: m.reason }));
76
+ return { ok: checks.every((c) => c.ok), checks: checks.length ? checks : [{ name: "operating model complete", ok: true }] };
77
+ },
78
+ };