@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,475 @@
1
+ /**
2
+ * sections/org.mjs — org enrollment (Cohort knowledge plane, order 75).
3
+ *
4
+ * Cross-workstream seam (design §3.2): registered by dropping this file into
5
+ * sections/ (no core dispatcher edit — runner.mjs globs sections/). This is the
6
+ * step that makes an agent JOIN the org: it captures the Cohort server URL plus
7
+ * an admin-issued org API key (Bearer; hq's documented COHORT_API_KEY),
8
+ * and writes them to config/org.yaml under the canonical
9
+ * `org.cohort.{enabled,base,orgId,token,trustedSignersPem?}` shape that
10
+ * lib/org/client.mjs (isEnabled/configFromAgent) already reads. Once enrolled,
11
+ * the daemon's prompt-builder can consult lib/org/knowledge.recall() so the
12
+ * agent's replies are informed by what the fleet already knows.
13
+ *
14
+ * Order 75 (between recovery-adjacent 70-band and the WS4 recovery/85 + verify/90)
15
+ * so org wiring lands after identity/company/model/comms/tools but is verified
16
+ * before the final verify gate.
17
+ *
18
+ * Every hook is non-blocking under --headless: detect/apply/verify NEVER read the
19
+ * TTY (so doctor/CI/--headless can call them directly); only prompt() touches the
20
+ * TTY and it honours io's headless resolution. apply() is idempotent — it never
21
+ * clobbers an operator-customised config/org.yaml, and verify() is best-effort
22
+ * (a disabled or unreachable server WARNs, it does not hard-fail setup).
23
+ *
24
+ * @module lib/setup/sections/org
25
+ */
26
+
27
+ "use strict";
28
+
29
+ import { join } from "node:path";
30
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
31
+ import * as io from "../io.mjs";
32
+ import { isEnabled, configFromAgent, read, fetchSelfProfile, pairRequest } from "../../org/client.mjs";
33
+ import { ensureDeviceKey } from "../../org/keys.mjs";
34
+ import { mergeAgentJson, readAgentJson } from "../sot.mjs";
35
+ import { cohortPullEnv, pullAndPlan } from "../enroll-from-cohort.mjs";
36
+ import { bootstrapAndPersist } from "../../org/bootstrap-context.mjs";
37
+ import { runGenerator } from "../run-generator.mjs";
38
+
39
+ const CONFIG_REL = join("config", "org.yaml");
40
+
41
+ /** The header comment prepended to every config/org.yaml we write. */
42
+ const ORG_YAML_HEADER =
43
+ "# config/org.yaml — Org enrollment (Cohort shared knowledge plane).\n" +
44
+ "# Written by `maestro setup` (section org). The bearer token authorises\n" +
45
+ "# org-wide recall/remember; the SERVER enforces ACL — this file only points\n" +
46
+ "# the agent at the endpoint. enabled flips true once base + token are set.\n";
47
+
48
+ /** Lazily load js-yaml (a maestro dep) the same way the lib/org modules do. */
49
+ async function loadYaml() {
50
+ const m = await import("js-yaml");
51
+ return m && m.default ? m.default : m;
52
+ }
53
+
54
+ /**
55
+ * Read config/org.yaml into a plain object. Returns {} when the file is absent or
56
+ * unreadable so callers can null-check uniformly. Never throws.
57
+ * @param {string} agentRoot
58
+ * @returns {Promise<object>}
59
+ */
60
+ export async function readOrgConfig(agentRoot) {
61
+ const p = join(agentRoot, CONFIG_REL);
62
+ if (!existsSync(p)) return {};
63
+ try {
64
+ const yaml = await loadYaml();
65
+ const doc = yaml.load(readFileSync(p, "utf8"));
66
+ return doc && typeof doc === "object" ? doc : {};
67
+ } catch {
68
+ return {};
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Build the `org.cohort` config block from the supplied answers, merged over any
74
+ * existing config so we never drop operator-set fields (e.g. trustedSignersPem).
75
+ * `enabled` is true only when we have both a base URL and a credential (the org
76
+ * API key / bearer token).
77
+ *
78
+ * @param {object} existing the existing parsed config/org.yaml ({} when none)
79
+ * @param {{base?:string, orgId?:string, token?:string}} ans
80
+ * @returns {object} the merged config object to serialise
81
+ */
82
+ export function buildOrgConfig(existing, ans = {}) {
83
+ const prev = (existing && existing.org && existing.org.cohort) || {};
84
+ const base = (ans.base != null && String(ans.base).trim()) || prev.base || "";
85
+ const orgId = (ans.orgId != null && String(ans.orgId).trim()) || prev.orgId || "";
86
+ const token = (ans.token != null && String(ans.token).trim()) || prev.token || "";
87
+ const cohort = { ...prev };
88
+
89
+ if (base) cohort.base = base.replace(/\/+$/, ""); else delete cohort.base;
90
+ if (orgId) cohort.orgId = orgId; else if (!cohort.orgId) delete cohort.orgId;
91
+ if (token) cohort.token = token; else if (!cohort.token) delete cohort.token;
92
+
93
+ // The integration is only live once we have an endpoint AND a credential (the
94
+ // bearer token is what makes recall() authoritative). Fail-closed on the
95
+ // credential, fail-open on the network.
96
+ cohort.enabled = !!(cohort.base && (cohort.token || prev.enabled));
97
+
98
+ // Preserve any pinned snapshot signer PEMs the operator configured.
99
+ if (Array.isArray(prev.trustedSignersPem) && prev.trustedSignersPem.length) {
100
+ cohort.trustedSignersPem = prev.trustedSignersPem;
101
+ }
102
+
103
+ return { ...(existing || {}), org: { ...((existing && existing.org) || {}), cohort } };
104
+ }
105
+
106
+ /**
107
+ * Pull this agent's record from Cohort and merge it into config/agent.json when
108
+ * the COHORT_* pull env is present. Fail-open + non-blocking: a missing var or an
109
+ * unreachable server is a no-op (the wizard falls back to the manual prompts).
110
+ * Returns the pull plan ({pulled, patch, changes, ...}) so callers can report it.
111
+ * Shared by this section's apply() and `maestro sync`.
112
+ *
113
+ * @param {string} agentRoot
114
+ * @param {{env?:object, force?:boolean, fetchSelfProfileImpl?:Function}} [opts]
115
+ * @returns {Promise<object>} the pullAndPlan result
116
+ */
117
+ export async function pullProfileIntoAgentConfig(agentRoot, opts = {}) {
118
+ const plan = await pullAndPlan({
119
+ agentRoot,
120
+ env: opts.env || process.env,
121
+ force: !!opts.force,
122
+ fetchSelfProfileImpl: opts.fetchSelfProfileImpl || fetchSelfProfile,
123
+ });
124
+ if (plan.pulled && plan.patch && Object.keys(plan.patch).length) {
125
+ mergeAgentJson(agentRoot, plan.patch);
126
+ }
127
+ return plan;
128
+ }
129
+
130
+ /**
131
+ * Fetch the agent's full authoritative org context from Cohort (self profile,
132
+ * org chart, relationships, strategy, hierarchy, adopted decisions, policy,
133
+ * board) and persist it to `config/org-context.json` so the running agent
134
+ * consumes the org as its source of truth (lib/org/awareness.mjs reads it).
135
+ *
136
+ * Resolves the endpoint + credential from the COHORT_* pull env first (the
137
+ * org-data API path), falling back to the persisted config/org.yaml. Fail-open +
138
+ * non-blocking: when the endpoint/credential/org id are absent it returns
139
+ * `{ skipped:true }`; an unreachable server yields a context full of nulls and a
140
+ * best-effort cache — it NEVER throws. Shared by this section's apply() and the
141
+ * `maestro sync` path.
142
+ *
143
+ * @param {string} agentRoot
144
+ * @param {{env?:object, fetchImpl?:Function}} [opts]
145
+ * @returns {Promise<{ctx:object|null, path:string|null, skipped?:boolean}>}
146
+ */
147
+ export async function bootstrapOrgContextInto(agentRoot, opts = {}) {
148
+ const env = opts.env || process.env;
149
+ const e = cohortPullEnv(env);
150
+ const cfg = await readOrgConfig(agentRoot);
151
+ const c = configFromAgent(cfg);
152
+ // The org-data DTO reads + /v1 reads share the OrgApiKey; prefer the explicit
153
+ // pull env (the canonical provisioning vars), then the persisted org.yaml.
154
+ const base = e.base || c.base || "";
155
+ const apiKey = e.apiKey || c.token || "";
156
+ const orgId = e.orgId || c.orgId || "";
157
+ if (!base || !apiKey || !orgId) return { ctx: null, path: null, skipped: true };
158
+ try {
159
+ return await bootstrapAndPersist(agentRoot, {
160
+ base,
161
+ apiKey,
162
+ orgId,
163
+ agentId: e.agentId || undefined,
164
+ emailHint: String(env.COHORT_AGENT_EMAIL || "").trim() || undefined,
165
+ fetchImpl: opts.fetchImpl,
166
+ });
167
+ } catch {
168
+ return { ctx: null, path: null, skipped: false };
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Would the bootstrapped org context actually SHAPE the backlog? True when it
174
+ * carries strategy streams OR a non-empty ready board — the two inputs
175
+ * `lib/backlog.orgContextStreams` distils into leading WBS streams. A context
176
+ * with neither would only re-write the identical archetype skeleton, so we skip
177
+ * the generate-backlog re-run in that case. Pure; tolerant of a null/partial ctx.
178
+ * @param {object|null} orgCtx the bootstrapContext result
179
+ * @returns {boolean}
180
+ */
181
+ export function orgContextShapesBacklog(orgCtx) {
182
+ if (!orgCtx || typeof orgCtx !== "object") return false;
183
+ const strategy = orgCtx.strategy && typeof orgCtx.strategy === "object" ? orgCtx.strategy : {};
184
+ const hasStreams = Array.isArray(strategy.streams) && strategy.streams.length > 0;
185
+ const hasBoard = Array.isArray(orgCtx.board) && orgCtx.board.length > 0;
186
+ return hasStreams || hasBoard;
187
+ }
188
+
189
+ /** Slugify a free-text value into a stable, wire-safe agent id. Pure. */
190
+ function slugId(v) {
191
+ const s = String(v || "")
192
+ .toLowerCase()
193
+ .normalize("NFKD")
194
+ .replace(/[^a-z0-9]+/g, "-")
195
+ .replace(/^-+|-+$/g, "")
196
+ .slice(0, 60);
197
+ return s;
198
+ }
199
+
200
+ /**
201
+ * Derive this agent's `{ agentId, displayName }` for the pairing handshake from
202
+ * config/agent.json (falling back to the answers bag / COHORT_AGENT_ID). The id
203
+ * is a slug of the explicit agent id, else the email local-part, else the name;
204
+ * the display name is the agent's full name. Pure over its inputs (reads disk via
205
+ * readAgentJson which is itself fail-open). Never throws.
206
+ * @param {string} agentRoot
207
+ * @param {object} [ans] the resolved answers bag (may carry agentId)
208
+ * @returns {{agentId:string, displayName:string}}
209
+ */
210
+ export function deriveAgentIdentity(agentRoot, ans = {}) {
211
+ const id = readAgentJson(agentRoot) || {};
212
+ const explicit = (ans.agentId != null && String(ans.agentId).trim()) || process.env.COHORT_AGENT_ID || "";
213
+ const emailLocal = String(id.email || "").split("@")[0];
214
+ const nameFallback = String(id.fullName || [id.firstName, id.lastName].filter(Boolean).join(" ")).trim();
215
+ const agentId = slugId(explicit || emailLocal || nameFallback) || "agent";
216
+ const displayName = (nameFallback || agentId).trim();
217
+ return { agentId, displayName };
218
+ }
219
+
220
+ /**
221
+ * Run the pre-auth pairing handshake for this agent, then persist the pairing
222
+ * intent into config/org.yaml under `org.cohort.pairing`.
223
+ *
224
+ * Steps: ensure the Ed25519 device keypair (the PUBLIC key is presented in the
225
+ * handshake), derive `{agentId, displayName}` from config/agent.json, POST
226
+ * `pairing.request { code, agentId, displayName, pubkey }` with NO bearer (this
227
+ * is the genuine UNAUTHENTICATED half — the server carves `pairing.request` out
228
+ * of auth), and record `{ code, agentId, status, requestedAt }` so detect()/
229
+ * verify() surface the outstanding handshake and a later re-run knows about it.
230
+ *
231
+ * The config stays DISABLED (no token yet) — enablement waits on an org admin
232
+ * running pairing.approve and minting/handing back a Bearer key. Fail-open: a
233
+ * missing base/short code or an unreachable server returns `{ok:false,error}`
234
+ * and NEVER throws, so it can never block setup.
235
+ *
236
+ * @param {string} agentRoot
237
+ * @param {{base:string, code:string, orgId?:string, answers?:object, fetchImpl?:Function}} opts
238
+ * @returns {Promise<{ok:boolean, frame?:object, agentId?:string, code?:string, status?:string, error?:string}>}
239
+ */
240
+ export async function requestPairing(agentRoot, opts = {}) {
241
+ const base = opts.base ? String(opts.base).replace(/\/+$/, "") : "";
242
+ const code = opts.code != null ? String(opts.code).trim() : "";
243
+ if (!base) return { ok: false, error: "missing base" };
244
+ if (code.length < 4) return { ok: false, error: "pairing code must be at least 4 characters" };
245
+
246
+ const { agentId, displayName } = deriveAgentIdentity(agentRoot, opts.answers || {});
247
+ let pubkey = "";
248
+ try { pubkey = ensureDeviceKey(agentRoot).publicKeyPem; } catch { /* device key unavailable */ }
249
+ if (!pubkey) return { ok: false, agentId, code, error: "could not materialise the device key" };
250
+
251
+ const frame = await pairRequest({ base, agentId, displayName, pubkey, code, fetchImpl: opts.fetchImpl });
252
+ const ok = !!(frame && frame.ok);
253
+ const status = (ok && frame.result && frame.result.status) || (ok ? "pending" : "error");
254
+
255
+ // Persist the pairing intent (survives buildOrgConfig's prev-spread merge, so a
256
+ // subsequent apply keeps it). Best-effort — a write failure is non-fatal.
257
+ try {
258
+ const existing = await readOrgConfig(agentRoot);
259
+ const nextOrg = { ...((existing && existing.org) || {}) };
260
+ const cohort = { ...((nextOrg && nextOrg.cohort) || {}) };
261
+ if (base && !cohort.base) cohort.base = base;
262
+ if (opts.orgId && !cohort.orgId) cohort.orgId = String(opts.orgId);
263
+ cohort.pairing = { code, agentId, status, requestedAt: new Date().toISOString() };
264
+ nextOrg.cohort = cohort;
265
+ const next = { ...(existing || {}), org: nextOrg };
266
+ mkdirSync(join(agentRoot, "config"), { recursive: true });
267
+ const yaml = await loadYaml();
268
+ writeFileSync(join(agentRoot, CONFIG_REL), ORG_YAML_HEADER + yaml.dump(next, { lineWidth: 120, sortKeys: false }));
269
+ } catch { /* best-effort persistence */ }
270
+
271
+ if (!ok) {
272
+ const error = (frame && frame.error && frame.error.message) || "pairing request failed";
273
+ return { ok: false, agentId, code, error };
274
+ }
275
+ return { ok: true, frame, agentId, code, status };
276
+ }
277
+
278
+ /** Resolve the answers bag for this section (answers → env, via io defaults). */
279
+ function resolvedAnswers(ctx) {
280
+ const a = (ctx && ctx.answers) || {};
281
+ return {
282
+ base: a.orgBase != null ? a.orgBase : (process.env.COHORT_BASE || ""),
283
+ orgId: a.orgId != null ? a.orgId : (process.env.COHORT_ORG_ID || ""),
284
+ // Token precedence mirrors configFromAgent / cohortPullEnv: hq's DOCUMENTED
285
+ // agent var is COHORT_API_KEY (README: `Authorization: Bearer <COHORT_API_KEY>`),
286
+ // so the single documented key must enable the plane. The older
287
+ // COHORT_API_TOKEN / COHORT_TOKEN are still accepted for back-compat.
288
+ token: a.orgToken != null
289
+ ? a.orgToken
290
+ : (process.env.COHORT_API_TOKEN || process.env.COHORT_TOKEN || process.env.COHORT_API_KEY || ""),
291
+ // Pairing lane: an operator-chosen handshake code (COHORT_PAIRING_CODE env
292
+ // for headless provisioning) + the chosen auth method (apikey|pairing).
293
+ pairingCode: a.orgPairingCode != null ? a.orgPairingCode : (process.env.COHORT_PAIRING_CODE || ""),
294
+ authMethod: a.orgAuthMethod != null ? a.orgAuthMethod : (process.env.COHORT_AUTH_METHOD || ""),
295
+ };
296
+ }
297
+
298
+ export default {
299
+ id: "org",
300
+ label: "Org enrollment (shared knowledge)",
301
+ order: 75,
302
+
303
+ async detect(ctx) {
304
+ const cfg = await readOrgConfig(ctx.agentRoot);
305
+ if (!existsSync(join(ctx.agentRoot, CONFIG_REL))) {
306
+ return { status: "unconfigured", missing: ["config/org.yaml"] };
307
+ }
308
+ if (isEnabled(cfg)) return { status: "complete", summary: "enrolled in org knowledge plane" };
309
+ const n = (cfg.org && cfg.org.cohort) || {};
310
+ // An outstanding pairing handshake: surface it honestly (awaiting the admin's
311
+ // pairing.approve + an issued Bearer key) rather than as a bare missing token.
312
+ if (n.pairing && n.pairing.code) {
313
+ return {
314
+ status: "partial",
315
+ summary: `pairing ${n.pairing.status || "requested"} (code ${n.pairing.code}) — awaiting admin approval + org key`,
316
+ missing: ["cohort token (approve the pairing, then set org.cohort.token)"],
317
+ };
318
+ }
319
+ const missing = [];
320
+ if (!(n.base || (n.apiUrl && n.orgId))) missing.push("cohort endpoint (base/apiUrl)");
321
+ if (!n.token) missing.push("cohort token (or admin-approved pairing)");
322
+ return { status: "partial", summary: "config/org.yaml present but not enabled", missing };
323
+ },
324
+
325
+ async prompt(ctx) {
326
+ io.note("Enroll this agent in the org's shared knowledge plane (Cohort). Leave blank to skip.", { dim: true });
327
+ const base = await io.text(ctx, {
328
+ message: "Cohort server URL (e.g. https://cohort.internal)",
329
+ key: "orgBase",
330
+ env: "COHORT_BASE",
331
+ default: "",
332
+ });
333
+ ctx.answers.orgBase = base;
334
+ if (!base) return;
335
+ ctx.answers.orgId = await io.text(ctx, { message: "Org id / slug (optional)", key: "orgId", env: "COHORT_ORG_ID", default: "" });
336
+
337
+ // Two honest credential lanes (hq now ships pairing.request/approve):
338
+ // apikey — an admin mints an org API key (Bearer) which you paste here.
339
+ // pairing — you pick a code, the agent opens a pre-auth handshake, and an
340
+ // org admin approves it (pairing.approve); the admin then issues
341
+ // the Bearer key that finishes enabling the plane.
342
+ const method = await io.select(ctx, {
343
+ message: "How should this agent authenticate to the org?",
344
+ key: "orgAuthMethod",
345
+ env: "COHORT_AUTH_METHOD",
346
+ default: "apikey",
347
+ choices: [
348
+ { label: "Org API key (admin-minted Bearer)", value: "apikey" },
349
+ { label: "Pairing code (an org admin approves a handshake)", value: "pairing" },
350
+ ],
351
+ });
352
+ ctx.answers.orgAuthMethod = method;
353
+
354
+ if (method === "pairing") {
355
+ io.note("Pick a pairing code and share it with an org admin — they approve it in Cohort (pairing.approve).", { dim: true });
356
+ ctx.answers.orgPairingCode = await io.text(ctx, { message: "Pairing code (4+ chars)", key: "orgPairingCode", env: "COHORT_PAIRING_CODE", default: "" });
357
+ // An admin may hand back the Bearer key immediately after approving; take it
358
+ // if offered so the plane can enable in one pass. Otherwise pair-then-enable.
359
+ ctx.answers.orgToken = await io.secret(ctx, { message: "Org API key if already issued (optional — leave blank to pair first)", key: "orgToken", env: "COHORT_API_KEY" });
360
+ } else {
361
+ ctx.answers.orgToken = await io.secret(ctx, { message: "Org API key (Bearer — mint in Cohort admin)", key: "orgToken", env: "COHORT_API_KEY" });
362
+ }
363
+ },
364
+
365
+ async apply(ctx) {
366
+ const ans = resolvedAnswers(ctx);
367
+ const existing = await readOrgConfig(ctx.agentRoot);
368
+ const next = buildOrgConfig(existing, ans);
369
+
370
+ // Idempotent: if nothing meaningful was supplied and no file exists yet, write
371
+ // a disabled stub so detect/verify have a stable artefact to read. If a file
372
+ // already exists we merge over it (never dropping operator fields).
373
+ mkdirSync(join(ctx.agentRoot, "config"), { recursive: true });
374
+ const yaml = await loadYaml();
375
+ writeFileSync(join(ctx.agentRoot, CONFIG_REL), ORG_YAML_HEADER + yaml.dump(next, { lineWidth: 120, sortKeys: false }));
376
+
377
+ // Pairing lane (hq ships pairing.request/approve): when the operator chose a
378
+ // pairing code and no Bearer token is available yet, open the pre-auth
379
+ // handshake so an org admin can approve it (pairing.approve). Fail-open +
380
+ // non-blocking: an unreachable server records the intent and moves on; the
381
+ // plane stays DISABLED until a token lands (fail-closed on the credential).
382
+ if (ans.pairingCode && !ans.token) {
383
+ try {
384
+ const res = await requestPairing(ctx.agentRoot, {
385
+ base: ans.base,
386
+ code: ans.pairingCode,
387
+ orgId: ans.orgId,
388
+ answers: (ctx && ctx.answers) || {},
389
+ fetchImpl: ctx && ctx.pairFetchImpl,
390
+ });
391
+ if (ctx && ctx.answers) ctx.answers.pairingRequested = !!res.ok;
392
+ if (res.ok) {
393
+ io.note(`Pairing requested (code ${res.code}, agent ${res.agentId}, status ${res.status}). Ask an org admin to approve it (pairing.approve), then set org.cohort.token to the issued key.`, { dim: true });
394
+ } else {
395
+ io.note(`Pairing request not completed (${res.error}). Re-run \`maestro pair ${ans.pairingCode}\` once the server is reachable.`, { dim: true });
396
+ }
397
+ } catch { /* fail-open — the manual API-key path stands */ }
398
+ }
399
+
400
+ // Cohort-driven setup: when COHORT_API_KEY + COHORT_ORG_ID (and optionally
401
+ // COHORT_AGENT_ID) are present, PULL this agent's record from the org-data API
402
+ // and auto-populate config/agent.json — the identity section then CONFIRMS the
403
+ // pulled values rather than prompting from scratch. Fail-open + non-blocking:
404
+ // a missing var / unreachable server is a no-op and the manual prompts stand.
405
+ try {
406
+ const plan = await pullProfileIntoAgentConfig(ctx.agentRoot, {});
407
+ if (plan && plan.pulled) {
408
+ if (ctx && ctx.answers) ctx.answers.cohortPulled = true;
409
+ const n = plan.changes ? plan.changes.length : 0;
410
+ io.note(`Pulled ${n} field${n === 1 ? "" : "s"} from Cohort into config/agent.json.`, { dim: true });
411
+ }
412
+ } catch { /* fail-open — manual prompts stand */ }
413
+
414
+ // Bootstrap the agent's authoritative org context (chart/relationships/
415
+ // strategy/hierarchy/decisions/policy/board) into config/org-context.json so
416
+ // the running agent consults the org as its source of truth. Fail-open +
417
+ // non-blocking: a missing endpoint/credential or unreachable server is a
418
+ // no-op (the agent runs on its local model).
419
+ try {
420
+ const res = await bootstrapOrgContextInto(ctx.agentRoot, {});
421
+ if (res && res.path) io.note("Cached org context → config/org-context.json (chart, strategy, decisions, policy).", { dim: true });
422
+
423
+ // The operating-model section (order 60) already seeded the backlog from the
424
+ // ARCHETYPE alone — but it ran BEFORE this section wrote config/org-context.json
425
+ // (order 75), so that first backlog could not yet be shaped by the org. Now
426
+ // that the org context is cached, RE-RUN generate-backlog so the fetched org
427
+ // strategy streams + ready-board frontier actually shape the backlog (closes
428
+ // the audit gap "fetched org context shapes nothing: backlog"). Only when the
429
+ // context carries strategy streams or a ready board — otherwise it's a no-op
430
+ // re-write of the identical skeleton. Fail-open + non-blocking.
431
+ if (orgContextShapesBacklog(res && res.ctx)) {
432
+ const g = runGenerator("generate-backlog.mjs", ctx.agentRoot, { maestroRoot: ctx.maestroRoot });
433
+ if (g.ok) io.note("Re-materialised the backlog with org context (strategy streams + ready board lead).", { dim: true });
434
+ else if (ctx.flags && ctx.flags.verbose) io.note(`generate-backlog re-run: ${(g.stderr || "").trim().slice(0, 200)}`, { dim: true });
435
+ }
436
+ } catch { /* fail-open — local model stands */ }
437
+ },
438
+
439
+ async verify(ctx) {
440
+ const checks = [];
441
+ const cfgPresent = existsSync(join(ctx.agentRoot, CONFIG_REL));
442
+ checks.push({ name: "config/org.yaml present", ok: cfgPresent, remedy: cfgPresent ? undefined : "run `maestro setup --only org`" });
443
+
444
+ const cfg = await readOrgConfig(ctx.agentRoot);
445
+ const enabled = isEnabled(cfg);
446
+ if (!enabled) {
447
+ // Org enrollment is OPTIONAL — a not-yet-enrolled agent WARNs (remedy shown)
448
+ // but does not hard-fail setup. ok=true so the agent can run standalone.
449
+ checks.push({ name: "org knowledge plane (not enrolled — optional)", ok: true, remedy: "set org.cohort.base + token in config/org.yaml, or pair with the org admin" });
450
+ return { ok: true, checks };
451
+ }
452
+
453
+ // Best-effort, NON-BLOCKING reachability probe. A GET /v1/snapshot round-trips
454
+ // the configured endpoint; the client is fail-open (transport error → ok:false
455
+ // frame, never a throw). A timeout/unreachable server WARNs, it never blocks
456
+ // setup — the daemon's recall() is itself fail-open at runtime.
457
+ let reachable = false;
458
+ let detail = "no fetch / unreachable";
459
+ try {
460
+ const c = configFromAgent(cfg);
461
+ const r = await read("snapshot", { base: c.base, token: c.token, timeoutMs: 4000 });
462
+ reachable = !!(r && r.ok);
463
+ if (!reachable && r && r.status) detail = `server returned ${r.status}`;
464
+ } catch {
465
+ reachable = false;
466
+ }
467
+ checks.push({
468
+ name: reachable ? "Cohort reachable (snapshot read)" : "Cohort reachability (best-effort)",
469
+ ok: true, // never hard-fail on reachability — recall() is fail-open at runtime
470
+ remedy: reachable ? undefined : `could not reach the org server (${detail}) — recall() will fail open until it is up`,
471
+ });
472
+
473
+ return { ok: checks.every((c) => c.ok), checks };
474
+ },
475
+ };