@cohortapp/agent-sdk 2.16.0 → 2.18.4

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 (529) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +23 -7
  3. package/README.md +1 -0
  4. package/bin/maestro.mjs +62 -0
  5. package/docs/guides/billing-console-keys.md +60 -0
  6. package/docs/guides/front-door-session.md +54 -9
  7. package/docs/guides/mac-mini.md +20 -25
  8. package/docs/guides/poller-daemon-setup.md +4 -1
  9. package/docs/guides/setup-wizard.md +1 -1
  10. package/docs/runbooks/fleet-rollout.md +156 -0
  11. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  12. package/lib/action-executor.js +19 -3
  13. package/lib/budget-guard.mjs +279 -3
  14. package/lib/channels/base-adapter.mjs +3 -1
  15. package/lib/channels/contract.mjs +2 -1
  16. package/lib/channels/inbox-item.mjs +8 -0
  17. package/lib/claude-bin.mjs +5 -6
  18. package/lib/cli/doctor-checks.mjs +141 -10
  19. package/lib/cli/global-setup-extras.mjs +5 -1
  20. package/lib/cli/inbox.mjs +100 -15
  21. package/lib/cli/seat-auth.mjs +463 -0
  22. package/lib/cli/session.mjs +80 -12
  23. package/lib/collective/capture.mjs +8 -6
  24. package/lib/collective/global-config.mjs +63 -1
  25. package/lib/collective/presence.mjs +142 -5
  26. package/lib/comms/send-gate.mjs +559 -1
  27. package/lib/context/budget.mjs +327 -0
  28. package/lib/context/history-scope.mjs +138 -0
  29. package/lib/diagnostics/alerts.mjs +49 -0
  30. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  31. package/lib/engine/agents/definitions.mjs +343 -0
  32. package/lib/engine/agents/persist.mjs +275 -0
  33. package/lib/engine/agents/runtime.mjs +748 -0
  34. package/lib/engine/agents/usage.mjs +95 -0
  35. package/lib/engine/auth-status.mjs +139 -0
  36. package/lib/engine/budget.mjs +194 -0
  37. package/lib/engine/cli.mjs +1204 -0
  38. package/lib/engine/commands/index.mjs +269 -0
  39. package/lib/engine/context/budget.mjs +219 -0
  40. package/lib/engine/context/cache.mjs +125 -0
  41. package/lib/engine/context/child-env.mjs +215 -0
  42. package/lib/engine/context/compaction.mjs +342 -0
  43. package/lib/engine/context/images.mjs +90 -0
  44. package/lib/engine/context/instructions.mjs +327 -0
  45. package/lib/engine/context/lazy-instructions.mjs +169 -0
  46. package/lib/engine/context/manager.mjs +182 -0
  47. package/lib/engine/context/real-path.mjs +91 -0
  48. package/lib/engine/context/secret-values.mjs +163 -0
  49. package/lib/engine/context/settings.mjs +274 -0
  50. package/lib/engine/context/stream-input.mjs +159 -0
  51. package/lib/engine/guard.mjs +152 -0
  52. package/lib/engine/hooks.mjs +713 -0
  53. package/lib/engine/loop.mjs +560 -0
  54. package/lib/engine/mcp/client.mjs +254 -0
  55. package/lib/engine/mcp/config.mjs +301 -0
  56. package/lib/engine/mcp/http.mjs +201 -0
  57. package/lib/engine/mcp/index.mjs +146 -0
  58. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  59. package/lib/engine/mcp/naming.mjs +66 -0
  60. package/lib/engine/mcp/resources.mjs +89 -0
  61. package/lib/engine/mcp/results.mjs +133 -0
  62. package/lib/engine/mcp/stdio.mjs +137 -0
  63. package/lib/engine/mcp/supervisor.mjs +116 -0
  64. package/lib/engine/messages.mjs +104 -0
  65. package/lib/engine/output/json.mjs +164 -0
  66. package/lib/engine/output/stream-json.mjs +266 -0
  67. package/lib/engine/permissions.mjs +845 -0
  68. package/lib/engine/process-identity.mjs +164 -0
  69. package/lib/engine/process-tree.mjs +551 -0
  70. package/lib/engine/prompt.mjs +60 -0
  71. package/lib/engine/session/store.mjs +299 -0
  72. package/lib/engine/session-runtime/args.mjs +97 -0
  73. package/lib/engine/session-runtime/host.mjs +143 -0
  74. package/lib/engine/session-runtime/inbox.mjs +122 -0
  75. package/lib/engine/session-runtime/notifications.mjs +129 -0
  76. package/lib/engine/session-runtime/registry.mjs +328 -0
  77. package/lib/engine/session-runtime/runner.mjs +344 -0
  78. package/lib/engine/session-runtime/socket.mjs +212 -0
  79. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  80. package/lib/engine/skills/index.mjs +321 -0
  81. package/lib/engine/tools/bash-background.mjs +533 -0
  82. package/lib/engine/tools/bash.mjs +216 -0
  83. package/lib/engine/tools/edit.mjs +97 -0
  84. package/lib/engine/tools/glob.mjs +81 -0
  85. package/lib/engine/tools/grep.mjs +224 -0
  86. package/lib/engine/tools/index.mjs +84 -0
  87. package/lib/engine/tools/list-agents.mjs +32 -0
  88. package/lib/engine/tools/ls.mjs +127 -0
  89. package/lib/engine/tools/monitor.mjs +82 -0
  90. package/lib/engine/tools/notebook-edit.mjs +218 -0
  91. package/lib/engine/tools/read.mjs +103 -0
  92. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  93. package/lib/engine/tools/schema.mjs +144 -0
  94. package/lib/engine/tools/send-message.mjs +77 -0
  95. package/lib/engine/tools/session.mjs +70 -0
  96. package/lib/engine/tools/todo.mjs +144 -0
  97. package/lib/engine/tools/toolsearch.mjs +217 -0
  98. package/lib/engine/tools/walk.mjs +193 -0
  99. package/lib/engine/tools/web-switch.mjs +31 -0
  100. package/lib/engine/tools/webfetch-html.mjs +387 -0
  101. package/lib/engine/tools/webfetch-net.mjs +340 -0
  102. package/lib/engine/tools/webfetch.mjs +198 -0
  103. package/lib/engine/tools/websearch.mjs +91 -0
  104. package/lib/engine/tools/workflow.mjs +95 -0
  105. package/lib/engine/tools/write.mjs +76 -0
  106. package/lib/engine/tui/line-editor.mjs +137 -0
  107. package/lib/engine/tui/render.mjs +86 -0
  108. package/lib/engine/tui/tui.mjs +274 -0
  109. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  110. package/lib/engine/wire/effort.mjs +36 -0
  111. package/lib/engine/wire/errors.mjs +496 -0
  112. package/lib/engine/wire/http.mjs +441 -0
  113. package/lib/engine/wire/index.mjs +76 -0
  114. package/lib/engine/wire/openai-chat.mjs +332 -0
  115. package/lib/engine/wire/prompt-cache.mjs +79 -0
  116. package/lib/engine/wire/search.mjs +140 -0
  117. package/lib/engine/wire/sse.mjs +114 -0
  118. package/lib/engine/wire/stall.mjs +349 -0
  119. package/lib/engine/wire/token-provider.mjs +175 -0
  120. package/lib/engine/wire/usage.mjs +192 -0
  121. package/lib/engine/workflow/host.mjs +524 -0
  122. package/lib/engine/workflow/journal.mjs +188 -0
  123. package/lib/engine/workflow/json-schema.mjs +171 -0
  124. package/lib/engine/workflow/meta.mjs +329 -0
  125. package/lib/engine/workflow/notifications.mjs +52 -0
  126. package/lib/engine/workflow/runtime.mjs +447 -0
  127. package/lib/engine/workflow/sandbox.mjs +534 -0
  128. package/lib/engine/workflow/worker.mjs +141 -0
  129. package/lib/engine/workflow/worktree.mjs +74 -0
  130. package/lib/execution/disposition.mjs +1 -1
  131. package/lib/execution/intake.mjs +10 -0
  132. package/lib/execution/surface-policy.mjs +15 -0
  133. package/lib/learning/curator.mjs +8 -6
  134. package/lib/learning/reflect.mjs +8 -6
  135. package/lib/model-router/catalog/cohort.yaml +137 -0
  136. package/lib/model-router/catalog.mjs +118 -1
  137. package/lib/model-router/economics.mjs +9 -0
  138. package/lib/model-router/failover.mjs +67 -16
  139. package/lib/model-router/llm-task.mjs +39 -3
  140. package/lib/model-router/resolve.mjs +95 -3
  141. package/lib/model-router/spawn.mjs +46 -47
  142. package/lib/model-router/taxonomy.mjs +126 -4
  143. package/lib/org/cost-sync.mjs +141 -11
  144. package/lib/org/inbound/broadcast.mjs +289 -0
  145. package/lib/org/inbound/collective.mjs +375 -0
  146. package/lib/org/inbound/directedness.mjs +96 -8
  147. package/lib/org/inbound/facts.mjs +82 -4
  148. package/lib/org/inbound/hydrate.mjs +555 -51
  149. package/lib/org/inbound/project.mjs +22 -0
  150. package/lib/org/inbound/surfaces.mjs +14 -0
  151. package/lib/org/llm-token.mjs +879 -0
  152. package/lib/org/mesh.mjs +61 -0
  153. package/lib/org/messaging.mjs +3 -1
  154. package/lib/org/protocol.checksum +1 -1
  155. package/lib/org/protocol.mjs +15 -0
  156. package/lib/org/quota.mjs +520 -0
  157. package/lib/org/tool-surface.mjs +104 -16
  158. package/lib/org/ui-parity.mjs +16 -1
  159. package/lib/org/work-ledger.mjs +37 -6
  160. package/lib/rate-guard.mjs +114 -1
  161. package/lib/resource-governor.mjs +41 -6
  162. package/lib/runtime/adapter.mjs +823 -0
  163. package/lib/runtime/child-env.mjs +191 -0
  164. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  165. package/lib/runtime/seat-engine.mjs +162 -0
  166. package/lib/session/ask-ledger.mjs +271 -0
  167. package/lib/session/current-work.mjs +676 -0
  168. package/lib/session/feed-core.mjs +40 -3
  169. package/lib/session/launch-args.mjs +56 -4
  170. package/lib/session/status-summary.mjs +26 -9
  171. package/lib/session/upgrade-notice.mjs +42 -0
  172. package/lib/setup/claude-probe.mjs +117 -13
  173. package/lib/setup/enrich.mjs +13 -10
  174. package/lib/setup/sections/model.mjs +39 -13
  175. package/lib/telemetry/collect.mjs +208 -9
  176. package/lib/upgrade/ignored-drift.mjs +105 -0
  177. package/lib/voice/post-call-brief.mjs +30 -17
  178. package/package.json +15 -3
  179. package/plugins/maestro-skills/skills/board-work.md +5 -0
  180. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  181. package/plugins/maestro-skills/skills/main-session.md +18 -7
  182. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  183. package/scripts/ci/run-tests.mjs +47 -19
  184. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  185. package/scripts/collective/hook-runner.mjs +29 -2
  186. package/scripts/continuous-monitor.sh +13 -0
  187. package/scripts/cost/track-claude-usage.mjs +15 -0
  188. package/scripts/daemon/agent-daemon.mjs +408 -20
  189. package/scripts/daemon/assurance.mjs +48 -12
  190. package/scripts/daemon/cadence-consumer.mjs +218 -68
  191. package/scripts/daemon/cadence-handlers.mjs +73 -4
  192. package/scripts/daemon/classifier.mjs +75 -26
  193. package/scripts/daemon/context-compiler.mjs +104 -59
  194. package/scripts/daemon/deliver.mjs +30 -1
  195. package/scripts/daemon/dispatcher.mjs +804 -157
  196. package/scripts/daemon/health.mjs +14 -1
  197. package/scripts/daemon/lib/session-router.mjs +310 -42
  198. package/scripts/daemon/maestro-daemon.mjs +11 -0
  199. package/scripts/daemon/prompt-builder.mjs +121 -12
  200. package/scripts/daemon/responder.mjs +315 -146
  201. package/scripts/daemon/sdk-version.mjs +98 -16
  202. package/scripts/eval/probe-gateway.mjs +635 -0
  203. package/scripts/eval/replay/extract.mjs +270 -0
  204. package/scripts/eval/replay/grade.mjs +260 -0
  205. package/scripts/eval/replay/lib/config.mjs +50 -0
  206. package/scripts/eval/replay/lib/effects.mjs +65 -0
  207. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  208. package/scripts/eval/replay/lib/judge.mjs +72 -0
  209. package/scripts/eval/replay/lib/redact.mjs +136 -0
  210. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  211. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  212. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  213. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  214. package/scripts/eval/replay/report.mjs +185 -0
  215. package/scripts/eval/replay/run.mjs +404 -0
  216. package/scripts/fleet/rollout.mjs +1094 -0
  217. package/scripts/hooks/pre-send-audit.sh +36 -245
  218. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  219. package/scripts/hooks/validate-state-yaml.sh +190 -0
  220. package/scripts/huddle/huddle-llm.mjs +361 -0
  221. package/scripts/huddle/huddle-server.mjs +46 -121
  222. package/scripts/local-triggers/autoupdate.sh +448 -78
  223. package/scripts/local-triggers/run-trigger.sh +13 -0
  224. package/scripts/maintenance/pin-integrity.mjs +364 -0
  225. package/scripts/poll-slack-events.sh +41 -9
  226. package/scripts/poller/slack-socket-mode.mjs +28 -3
  227. package/scripts/session/supervisor.mjs +80 -13
  228. package/scripts/spawn-session.sh +13 -0
  229. package/bin/maestro.test.mjs +0 -1574
  230. package/lib/action-executor.test.mjs +0 -871
  231. package/lib/archetype.test.mjs +0 -132
  232. package/lib/assurance/plan-note.test.mjs +0 -234
  233. package/lib/assurance/room-budget.test.mjs +0 -486
  234. package/lib/assurance/tier.test.mjs +0 -174
  235. package/lib/autonomy.test.mjs +0 -66
  236. package/lib/backlog.test.mjs +0 -302
  237. package/lib/backup/policy.test.mjs +0 -305
  238. package/lib/budget-escalate.test.mjs +0 -232
  239. package/lib/budget-guard.envelope.test.mjs +0 -476
  240. package/lib/budget-guard.test.mjs +0 -427
  241. package/lib/cadence-bus-requeue.test.mjs +0 -83
  242. package/lib/cadence-bus-schedule.test.mjs +0 -194
  243. package/lib/cadence-bus.test.mjs +0 -720
  244. package/lib/cadences.test.mjs +0 -230
  245. package/lib/capability/inventory.test.mjs +0 -232
  246. package/lib/capability.test.mjs +0 -78
  247. package/lib/channels/base-adapter.test.mjs +0 -590
  248. package/lib/channels/channels.test.mjs +0 -371
  249. package/lib/channels/contract.test.mjs +0 -162
  250. package/lib/channels/inbox-item.test.mjs +0 -368
  251. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  252. package/lib/channels/pairing.test.mjs +0 -270
  253. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  254. package/lib/channels/slack-adapter.test.mjs +0 -212
  255. package/lib/channels/telegram-adapter.test.mjs +0 -306
  256. package/lib/channels/voice/adapter.test.mjs +0 -278
  257. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  258. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  259. package/lib/charter.test.mjs +0 -89
  260. package/lib/claude-bin.test.mjs +0 -131
  261. package/lib/cli/board.test.mjs +0 -227
  262. package/lib/cli/design.test.mjs +0 -270
  263. package/lib/cli/doctor-checks.test.mjs +0 -336
  264. package/lib/cli/global-setup-extras.test.mjs +0 -462
  265. package/lib/cli/inbox.test.mjs +0 -230
  266. package/lib/cli/session-ack.test.mjs +0 -63
  267. package/lib/cli/session.test.mjs +0 -613
  268. package/lib/collective/capture.test.mjs +0 -121
  269. package/lib/collective/cards.test.mjs +0 -114
  270. package/lib/collective/config.test.mjs +0 -123
  271. package/lib/collective/global-config.test.mjs +0 -220
  272. package/lib/collective/global-skills.test.mjs +0 -126
  273. package/lib/collective/presence.test.mjs +0 -95
  274. package/lib/collective/recall.test.mjs +0 -116
  275. package/lib/collective/vendor-skills.test.mjs +0 -306
  276. package/lib/comms/send-gate.test.mjs +0 -770
  277. package/lib/comms.test.mjs +0 -41
  278. package/lib/cost/ledger-row.test.mjs +0 -183
  279. package/lib/design/design-md.test.mjs +0 -318
  280. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  281. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  282. package/lib/design/fixtures/foundation.json +0 -133
  283. package/lib/design/refresh-gate.test.mjs +0 -144
  284. package/lib/design/write.test.mjs +0 -241
  285. package/lib/diagnostics/alerts.test.mjs +0 -318
  286. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  287. package/lib/diagnostics/counters.test.mjs +0 -206
  288. package/lib/diagnostics/events.test.mjs +0 -290
  289. package/lib/diagnostics/otel.test.mjs +0 -196
  290. package/lib/diagnostics/trace.test.mjs +0 -251
  291. package/lib/env-compat.test.mjs +0 -104
  292. package/lib/execution/disposition.test.mjs +0 -553
  293. package/lib/execution/drive.test.mjs +0 -270
  294. package/lib/execution/effects.test.mjs +0 -344
  295. package/lib/execution/intake.test.mjs +0 -389
  296. package/lib/execution/journal.test.mjs +0 -261
  297. package/lib/execution/match.test.mjs +0 -235
  298. package/lib/execution/pipeline.test.mjs +0 -392
  299. package/lib/execution/route.test.mjs +0 -186
  300. package/lib/execution/surface-policy.test.mjs +0 -162
  301. package/lib/fs-atomic.test.mjs +0 -72
  302. package/lib/fs-ownership.test.mjs +0 -158
  303. package/lib/goals/admission.test.mjs +0 -164
  304. package/lib/goals/classify.test.mjs +0 -167
  305. package/lib/goals/collaborate.test.mjs +0 -336
  306. package/lib/goals/gaps.test.mjs +0 -284
  307. package/lib/goals/loop.test.mjs +0 -845
  308. package/lib/hooks/bus.test.mjs +0 -387
  309. package/lib/identity/persona.test.mjs +0 -142
  310. package/lib/kpi-sensors.test.mjs +0 -278
  311. package/lib/kpi.test.mjs +0 -244
  312. package/lib/learning/config.test.mjs +0 -75
  313. package/lib/learning/counters.test.mjs +0 -69
  314. package/lib/learning/curator-consolidate.test.mjs +0 -238
  315. package/lib/learning/curator.test.mjs +0 -106
  316. package/lib/learning/reflect.test.mjs +0 -0
  317. package/lib/learning/session-index.test.mjs +0 -125
  318. package/lib/learning/skill-writer.test.mjs +0 -210
  319. package/lib/mandate/audit.test.mjs +0 -195
  320. package/lib/mandate/contract.test.mjs +0 -185
  321. package/lib/mandate/derive.test.mjs +0 -274
  322. package/lib/mandate/model.test.mjs +0 -164
  323. package/lib/mandate/refresh.test.mjs +0 -389
  324. package/lib/mcp/server.test.mjs +0 -426
  325. package/lib/model-router/auth-profiles.test.mjs +0 -580
  326. package/lib/model-router/catalog.test.mjs +0 -385
  327. package/lib/model-router/economics.test.mjs +0 -438
  328. package/lib/model-router/failover.test.mjs +0 -439
  329. package/lib/model-router/health.test.mjs +0 -338
  330. package/lib/model-router/integration-coverage.test.mjs +0 -831
  331. package/lib/model-router/integration.test.mjs +0 -564
  332. package/lib/model-router/ledger.test.mjs +0 -415
  333. package/lib/model-router/llm-task.test.mjs +0 -392
  334. package/lib/model-router/org-credentials.test.mjs +0 -265
  335. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  336. package/lib/model-router/reconcile.test.mjs +0 -316
  337. package/lib/model-router/repair.test.mjs +0 -180
  338. package/lib/model-router/spawn.test.mjs +0 -446
  339. package/lib/model-router/taxonomy.test.mjs +0 -410
  340. package/lib/model-router.test.mjs +0 -1207
  341. package/lib/org/activity.test.mjs +0 -134
  342. package/lib/org/approvals.test.mjs +0 -216
  343. package/lib/org/awareness.test.mjs +0 -159
  344. package/lib/org/board-mine-cache.test.mjs +0 -53
  345. package/lib/org/board.test.mjs +0 -187
  346. package/lib/org/bootstrap-context.test.mjs +0 -153
  347. package/lib/org/client.test.mjs +0 -1206
  348. package/lib/org/cohort-client.test.mjs +0 -126
  349. package/lib/org/cost-sync.test.mjs +0 -153
  350. package/lib/org/doctor.test.mjs +0 -346
  351. package/lib/org/engagement-ledger.test.mjs +0 -112
  352. package/lib/org/engagement.test.mjs +0 -739
  353. package/lib/org/handoff.test.mjs +0 -269
  354. package/lib/org/inbound/directedness.test.mjs +0 -668
  355. package/lib/org/inbound/facts.test.mjs +0 -471
  356. package/lib/org/inbound/hydrate.test.mjs +0 -453
  357. package/lib/org/inbound/index.test.mjs +0 -429
  358. package/lib/org/inbound/project.test.mjs +0 -287
  359. package/lib/org/integration-tools.test.mjs +0 -160
  360. package/lib/org/keys.test.mjs +0 -92
  361. package/lib/org/knowledge.test.mjs +0 -326
  362. package/lib/org/leases.test.mjs +0 -235
  363. package/lib/org/mesh-directives.test.mjs +0 -110
  364. package/lib/org/mesh-integration.test.mjs +0 -127
  365. package/lib/org/mesh.test.mjs +0 -400
  366. package/lib/org/messaging.test.mjs +0 -471
  367. package/lib/org/param-contract.test.mjs +0 -477
  368. package/lib/org/policy.test.mjs +0 -237
  369. package/lib/org/protocol.checksum.test.mjs +0 -90
  370. package/lib/org/protocol.test.mjs +0 -323
  371. package/lib/org/push.test.mjs +0 -792
  372. package/lib/org/registry.test.mjs +0 -100
  373. package/lib/org/resource-tools.test.mjs +0 -361
  374. package/lib/org/tool-access.test.mjs +0 -144
  375. package/lib/org/tool-surface-integration.test.mjs +0 -120
  376. package/lib/org/tool-surface.test.mjs +0 -1268
  377. package/lib/org/typing.test.mjs +0 -291
  378. package/lib/org/ui-parity.test.mjs +0 -560
  379. package/lib/org/verify.test.mjs +0 -194
  380. package/lib/org/work-ledger.test.mjs +0 -273
  381. package/lib/plan/adoption-e2e.test.mjs +0 -366
  382. package/lib/plan/budget-enforcement.test.mjs +0 -400
  383. package/lib/plan/compile.test.mjs +0 -382
  384. package/lib/plan/emit.test.mjs +0 -269
  385. package/lib/plan/explain.test.mjs +0 -188
  386. package/lib/prompts/parallelism.test.mjs +0 -177
  387. package/lib/rag/rag.test.mjs +0 -505
  388. package/lib/rate-guard.test.mjs +0 -272
  389. package/lib/reactive-gate.test.mjs +0 -57
  390. package/lib/render.test.mjs +0 -68
  391. package/lib/resource-governor.test.mjs +0 -488
  392. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  393. package/lib/scheduling/jitter.test.mjs +0 -140
  394. package/lib/secrets/broker.test.mjs +0 -280
  395. package/lib/secrets/providers.test.mjs +0 -274
  396. package/lib/security/audit-engine.test.mjs +0 -424
  397. package/lib/security/coerce-args.test.mjs +0 -281
  398. package/lib/security/dangerous-tools.test.mjs +0 -68
  399. package/lib/security/external-content.test.mjs +0 -84
  400. package/lib/security/redact.test.mjs +0 -441
  401. package/lib/security/secret-equal.test.mjs +0 -55
  402. package/lib/session/config.test.mjs +0 -92
  403. package/lib/session/feed-core.test.mjs +0 -198
  404. package/lib/session/first-run.test.mjs +0 -121
  405. package/lib/session/frontdoor.test.mjs +0 -205
  406. package/lib/session/handoffs.test.mjs +0 -183
  407. package/lib/session/identity.test.mjs +0 -180
  408. package/lib/session/inbox-claims.test.mjs +0 -286
  409. package/lib/session/launch-args.test.mjs +0 -157
  410. package/lib/session/liveness.test.mjs +0 -100
  411. package/lib/session/status-summary.test.mjs +0 -118
  412. package/lib/session-permissions.test.mjs +0 -120
  413. package/lib/setup/claude-probe.test.mjs +0 -187
  414. package/lib/setup/completeness.test.mjs +0 -110
  415. package/lib/setup/context-pack.test.mjs +0 -89
  416. package/lib/setup/enrich.test.mjs +0 -115
  417. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  418. package/lib/setup/integration.test.mjs +0 -162
  419. package/lib/setup/io.test.mjs +0 -77
  420. package/lib/setup/runner.test.mjs +0 -132
  421. package/lib/setup/sections/identity.test.mjs +0 -234
  422. package/lib/setup/sections/inventory.test.mjs +0 -198
  423. package/lib/setup/sections/learning.test.mjs +0 -81
  424. package/lib/setup/sections/mandate.test.mjs +0 -388
  425. package/lib/setup/sections/messaging.test.mjs +0 -127
  426. package/lib/setup/sections/model.test.mjs +0 -240
  427. package/lib/setup/sections/org.test.mjs +0 -346
  428. package/lib/setup/sections/orgmail.test.mjs +0 -118
  429. package/lib/setup/sections/recovery.test.mjs +0 -98
  430. package/lib/setup/sections/subagents.test.mjs +0 -429
  431. package/lib/setup/sections/verify.test.mjs +0 -175
  432. package/lib/setup/sot.test.mjs +0 -81
  433. package/lib/setup/state.test.mjs +0 -115
  434. package/lib/singleton.test.mjs +0 -151
  435. package/lib/subagents/cli.test.mjs +0 -389
  436. package/lib/subagents/client.test.mjs +0 -309
  437. package/lib/subagents/gap.test.mjs +0 -234
  438. package/lib/subagents/lock.test.mjs +0 -248
  439. package/lib/subagents/manifest.test.mjs +0 -175
  440. package/lib/subagents/refs.test.mjs +0 -204
  441. package/lib/subagents/resolve.test.mjs +0 -422
  442. package/lib/subagents/schema.test.mjs +0 -328
  443. package/lib/telemetry/alerts.test.mjs +0 -109
  444. package/lib/telemetry/collect.test.mjs +0 -1274
  445. package/lib/tool-definitions-integration.test.mjs +0 -83
  446. package/lib/tool-definitions.test.mjs +0 -437
  447. package/lib/upgrade/global-refresh.test.mjs +0 -65
  448. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  449. package/lib/upgrade/post-steps.test.mjs +0 -200
  450. package/lib/upgrade/verify.test.mjs +0 -164
  451. package/lib/util/fetch-timeout.test.mjs +0 -202
  452. package/lib/util/reconnect.test.mjs +0 -369
  453. package/lib/util/unhandled.test.mjs +0 -216
  454. package/lib/voice/outbound.test.mjs +0 -69
  455. package/lib/voice/session-rotation.test.mjs +0 -114
  456. package/lib/voice/stt.test.mjs +0 -226
  457. package/lib/voice/voice.test.mjs +0 -990
  458. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  459. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  460. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  461. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  462. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  463. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  464. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  465. package/scripts/ci/check.test.mjs +0 -194
  466. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  467. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  468. package/scripts/collective/hook-runner.test.mjs +0 -173
  469. package/scripts/cost/fleet-digest.test.mjs +0 -207
  470. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  471. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  472. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  473. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  474. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  475. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  476. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  477. package/scripts/daemon/assurance.test.mjs +0 -1791
  478. package/scripts/daemon/board-mirror.test.mjs +0 -165
  479. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  480. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  481. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  482. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  483. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  484. package/scripts/daemon/classifier.test.mjs +0 -266
  485. package/scripts/daemon/classify-kind.test.mjs +0 -40
  486. package/scripts/daemon/context-compiler.test.mjs +0 -300
  487. package/scripts/daemon/deliver.test.mjs +0 -564
  488. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  489. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  490. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  491. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  492. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  493. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  494. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  495. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  496. package/scripts/daemon/integration.test.mjs +0 -149
  497. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  498. package/scripts/daemon/lib/session-router.test.mjs +0 -295
  499. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  500. package/scripts/daemon/prompt-builder.test.mjs +0 -344
  501. package/scripts/daemon/responder-cost.test.mjs +0 -68
  502. package/scripts/daemon/responder-history.test.mjs +0 -185
  503. package/scripts/daemon/sdk-version.test.mjs +0 -31
  504. package/scripts/daemon/session-lock.test.mjs +0 -252
  505. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  506. package/scripts/daemon/typing-registry.test.mjs +0 -102
  507. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  508. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  509. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  510. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  511. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  512. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  513. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  514. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  515. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  516. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  517. package/scripts/poller-launchd/install.test.mjs +0 -243
  518. package/scripts/restore-from-backup.test.mjs +0 -181
  519. package/scripts/session/feed.test.mjs +0 -196
  520. package/scripts/session/supervisor-sh.test.mjs +0 -218
  521. package/scripts/session/supervisor.test.mjs +0 -482
  522. package/scripts/setup/configure-macos.test.mjs +0 -306
  523. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  524. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  525. package/scripts/setup/generate-capability.test.mjs +0 -134
  526. package/scripts/setup/init-agent.test.mjs +0 -370
  527. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  528. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  529. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -1,83 +0,0 @@
1
- /**
2
- * tool-definitions-integration.test.mjs — SP5 native-plane hot-refresh.
3
- *
4
- * The native tool snapshot (getToolsForAccessLevel) must reflect a CHANGED disk
5
- * cache without a restart: a granted integration tool appears at the write tiers
6
- * (ceo + leadership) and never at the read-only default tier. Run:
7
- * node --test lib/tool-definitions-integration.test.mjs
8
- */
9
-
10
- "use strict";
11
-
12
- import { test, before, after, beforeEach } from "node:test";
13
- import assert from "node:assert/strict";
14
- import { mkdtempSync, rmSync } from "node:fs";
15
- import { tmpdir } from "node:os";
16
- import { join } from "node:path";
17
-
18
- import { getToolNamesForAccessLevel } from "./tool-definitions.js";
19
- import { writeIntegrationToolsToDisk } from "./org/integration-tools.mjs";
20
-
21
- const ENV_KEYS = ["COHORT_AGENT_ROOT", "AGENT_ROOT", "AGENT_DIR"];
22
- const saved = {};
23
- let root;
24
-
25
- const TOOL = {
26
- name: "plugin__stripe__list_charges",
27
- title: "list_charges",
28
- description: "stripe: List charges",
29
- input_schema: { type: "object" },
30
- access: "write",
31
- binding: { kind: "rpc", method: "integration.invokeTool" },
32
- integration: { slug: "stripe", tool: "list_charges" },
33
- };
34
-
35
- before(() => {
36
- for (const k of ENV_KEYS) { saved[k] = process.env[k]; delete process.env[k]; }
37
- root = mkdtempSync(join(tmpdir(), "td-integration-"));
38
- process.env.COHORT_AGENT_ROOT = root;
39
- });
40
- after(() => {
41
- for (const k of ENV_KEYS) {
42
- if (saved[k] === undefined) delete process.env[k];
43
- else process.env[k] = saved[k];
44
- }
45
- try { rmSync(root, { recursive: true, force: true }); } catch { /* best-effort */ }
46
- });
47
- beforeEach(() => { process.env.COHORT_AGENT_ROOT = root; });
48
-
49
- test("no disk cache → the integration tool is absent everywhere", () => {
50
- const ceo = getToolNamesForAccessLevel("ceo");
51
- assert.equal(ceo.includes("plugin__stripe__list_charges"), false);
52
- });
53
-
54
- test("after a disk write, the native ceo + leadership tiers expose it (hot-refresh)", () => {
55
- writeIntegrationToolsToDisk(root, { version: 1, tools: [TOOL] });
56
- assert.ok(
57
- getToolNamesForAccessLevel("ceo").includes("plugin__stripe__list_charges"),
58
- "ceo sees the granted tool",
59
- );
60
- assert.ok(
61
- getToolNamesForAccessLevel("leadership").includes("plugin__stripe__list_charges"),
62
- "leadership sees the granted tool",
63
- );
64
- });
65
-
66
- test("the read-only default tier never exposes the write-tier integration tool", () => {
67
- writeIntegrationToolsToDisk(root, { version: 1, tools: [TOOL] });
68
- assert.equal(
69
- getToolNamesForAccessLevel("default").includes("plugin__stripe__list_charges"),
70
- false,
71
- );
72
- });
73
-
74
- test("a bumped disk cache is reflected on the very next call (no restart)", () => {
75
- writeIntegrationToolsToDisk(root, { version: 2, tools: [TOOL] });
76
- assert.ok(getToolNamesForAccessLevel("ceo").includes("plugin__stripe__list_charges"));
77
- // Revoke everything: next call reflects the empty set.
78
- writeIntegrationToolsToDisk(root, { version: 3, tools: [] });
79
- assert.equal(
80
- getToolNamesForAccessLevel("ceo").includes("plugin__stripe__list_charges"),
81
- false,
82
- );
83
- });
@@ -1,437 +0,0 @@
1
- /**
2
- * tool-definitions.test.mjs — access-control / privilege-escalation tests for
3
- * the maestro tool surface (lib/tool-definitions.js).
4
- *
5
- * The tool-definitions module is the SoT for which Claude API tools are exposed
6
- * to a caller given their access level (ceo / leadership / voice / default).
7
- * Because the write tools here can send email, place phone calls, and generate
8
- * outbound documents, the scoping logic is a privilege boundary: a regression
9
- * that leaks a CEO-only write tool into the `default` (read-only) surface is a
10
- * real escalation. These tests pin that boundary plus the structural invariants
11
- * every tool schema must satisfy to be accepted by the Claude API.
12
- *
13
- * Pure assertions over the exported functions; the only dependency is
14
- * node:test + node:assert and the module under test.
15
- */
16
-
17
- import { test } from "node:test";
18
- import assert from "node:assert/strict";
19
-
20
- import {
21
- getToolsForAccessLevel,
22
- getToolNamesForAccessLevel,
23
- getAllToolsByCategory,
24
- normalizeAccessLevel,
25
- KNOWN_ACCESS_LEVELS,
26
- } from "./tool-definitions.js";
27
-
28
- // Access levels the module is documented to recognise.
29
- const KNOWN_LEVELS = ["ceo", "leadership", "voice", "default"];
30
-
31
- // Read-only tools must never confer the ability to act. These are the
32
- // write/side-effecting tools that gate on privilege; none may appear in the
33
- // `default` surface.
34
- const WRITE_TOOL_NAMES = new Set([
35
- "slack_send",
36
- "draft_email",
37
- "whatsapp_send",
38
- "sms_send",
39
- "send_voice_message",
40
- "place_voice_call",
41
- "queue_update",
42
- "create_action_item",
43
- "schedule_meeting",
44
- "create_reminder",
45
- "generate_memo",
46
- "generate_report",
47
- ]);
48
-
49
- // The most sensitive write tools — CEO-only, must NOT leak down to leadership.
50
- const CEO_ONLY_WRITE_NAMES = [
51
- "whatsapp_send",
52
- "sms_send",
53
- "place_voice_call",
54
- "generate_memo",
55
- "generate_report",
56
- ];
57
-
58
- /** Resolve a tool by name within an access level's surface (the module has no
59
- * direct getToolByName export, so this mirrors how a caller would look one up). */
60
- function findToolByName(accessLevel, name) {
61
- return getToolsForAccessLevel(accessLevel).find((t) => t.name === name);
62
- }
63
-
64
- // ---------------------------------------------------------------------------
65
- // Each access level returns a defined, non-empty tool set
66
- // ---------------------------------------------------------------------------
67
-
68
- for (const level of KNOWN_LEVELS) {
69
- test(`getToolsForAccessLevel("${level}") returns a defined, non-empty array`, () => {
70
- const tools = getToolsForAccessLevel(level);
71
- assert.ok(Array.isArray(tools), `${level} should return an array`);
72
- assert.ok(tools.length > 0, `${level} should expose at least one tool`);
73
- });
74
-
75
- test(`getToolNamesForAccessLevel("${level}") matches the tool array`, () => {
76
- const names = getToolNamesForAccessLevel(level);
77
- const tools = getToolsForAccessLevel(level);
78
- assert.ok(Array.isArray(names));
79
- assert.equal(names.length, tools.length);
80
- assert.deepEqual(names, tools.map((t) => t.name));
81
- });
82
-
83
- test(`"${level}" surface has no duplicate tool names`, () => {
84
- const names = getToolNamesForAccessLevel(level);
85
- assert.equal(new Set(names).size, names.length, `${level} has duplicate tools`);
86
- });
87
- }
88
-
89
- // ---------------------------------------------------------------------------
90
- // Unknown access level: least-privilege "default" surface, never empty, no throw
91
- // (audit M4 + the CEO-case footgun — "CEO" used to yield 0 tools silently)
92
- // ---------------------------------------------------------------------------
93
-
94
- test("unknown access level coerces to the default surface (NOT empty), no throw", () => {
95
- let tools;
96
- assert.doesNotThrow(() => {
97
- tools = getToolsForAccessLevel("intern");
98
- });
99
- assert.ok(Array.isArray(tools));
100
- assert.ok(tools.length > 0, "unknown level must not yield an empty tool set");
101
- assert.deepEqual(tools, getToolsForAccessLevel("default"));
102
- });
103
-
104
- test("undefined / null / empty / numeric access levels coerce to default, no throw", () => {
105
- const defaultTools = getToolsForAccessLevel("default");
106
- for (const bad of [undefined, null, "", 42, {}, []]) {
107
- let tools;
108
- assert.doesNotThrow(() => {
109
- tools = getToolsForAccessLevel(bad);
110
- }, `access level ${JSON.stringify(bad)} should not throw`);
111
- assert.ok(Array.isArray(tools));
112
- assert.deepEqual(
113
- tools,
114
- defaultTools,
115
- `access level ${JSON.stringify(bad)} should fall back to default`,
116
- );
117
- }
118
- });
119
-
120
- test("access-level lookups are case/whitespace-insensitive (CEO === ceo)", () => {
121
- // The CEO-case footgun: "CEO" previously yielded 0 tools (silent under-privilege).
122
- const upper = getToolNamesForAccessLevel("CEO");
123
- const lower = getToolNamesForAccessLevel("ceo");
124
- assert.ok(upper.length > 0, "'CEO' must not yield an empty tool set");
125
- assert.deepEqual(upper, lower);
126
- assert.deepEqual(getToolsForAccessLevel(" Ceo "), getToolsForAccessLevel("ceo"));
127
- assert.deepEqual(
128
- getToolNamesForAccessLevel(" Leadership "),
129
- getToolNamesForAccessLevel("leadership"),
130
- );
131
- });
132
-
133
- test("normalizeAccessLevel maps casing/whitespace to known keys", () => {
134
- assert.equal(normalizeAccessLevel("CEO"), "ceo");
135
- assert.equal(normalizeAccessLevel(" Ceo "), "ceo");
136
- assert.equal(normalizeAccessLevel("LEADERSHIP"), "leadership");
137
- assert.equal(normalizeAccessLevel("Voice"), "voice");
138
- assert.equal(normalizeAccessLevel("default"), "default");
139
- });
140
-
141
- test("normalizeAccessLevel coerces unknown/invalid input to least-privilege default", () => {
142
- for (const bad of ["intern", "", null, undefined, 42, {}, []]) {
143
- assert.equal(
144
- normalizeAccessLevel(bad),
145
- "default",
146
- `${JSON.stringify(bad)} should normalise to default`,
147
- );
148
- }
149
- });
150
-
151
- test("known access levels return unchanged tool sets (no privilege change)", () => {
152
- // Lock the privilege boundary: default must never gain a write tool, and the
153
- // ceo surface must remain a superset of leadership.
154
- const ceo = getToolNamesForAccessLevel("ceo");
155
- const leadership = new Set(getToolNamesForAccessLevel("leadership"));
156
- const def = new Set(getToolNamesForAccessLevel("default"));
157
- assert.ok(ceo.includes("slack_send"), "ceo retains communication write tools");
158
- assert.ok(!def.has("slack_send"), "default must not gain a write tool");
159
- for (const name of leadership) {
160
- assert.ok(
161
- new Set(ceo).has(name),
162
- `leadership tool "${name}" must also exist at ceo`,
163
- );
164
- }
165
- });
166
-
167
- test("KNOWN_ACCESS_LEVELS lists exactly the configured levels", () => {
168
- assert.ok(Array.isArray(KNOWN_ACCESS_LEVELS));
169
- for (const level of ["ceo", "leadership", "voice", "default"]) {
170
- assert.ok(
171
- KNOWN_ACCESS_LEVELS.includes(level),
172
- `KNOWN_ACCESS_LEVELS should include ${level}`,
173
- );
174
- }
175
- });
176
-
177
- // ---------------------------------------------------------------------------
178
- // Privilege boundaries — no escalation
179
- // ---------------------------------------------------------------------------
180
-
181
- test("default (read-only) surface contains NO write/side-effecting tools", () => {
182
- const names = new Set(getToolNamesForAccessLevel("default"));
183
- for (const write of WRITE_TOOL_NAMES) {
184
- assert.equal(
185
- names.has(write),
186
- false,
187
- `default access must not expose write tool "${write}"`,
188
- );
189
- }
190
- });
191
-
192
- test("default surface is a strict subset of both leadership and ceo surfaces", () => {
193
- const defaultNames = new Set(getToolNamesForAccessLevel("default"));
194
- const leadershipNames = new Set(getToolNamesForAccessLevel("leadership"));
195
- const ceoNames = new Set(getToolNamesForAccessLevel("ceo"));
196
-
197
- for (const name of defaultNames) {
198
- assert.ok(leadershipNames.has(name), `leadership missing read tool "${name}"`);
199
- assert.ok(ceoNames.has(name), `ceo missing read tool "${name}"`);
200
- }
201
- // Strict subset: higher levels strictly add capability.
202
- assert.ok(leadershipNames.size > defaultNames.size, "leadership should add tools over default");
203
- assert.ok(ceoNames.size > defaultNames.size, "ceo should add tools over default");
204
- });
205
-
206
- test("leadership surface is a subset of ceo (no leadership-only tool exceeds ceo)", () => {
207
- const leadershipNames = getToolNamesForAccessLevel("leadership");
208
- const ceoNames = new Set(getToolNamesForAccessLevel("ceo"));
209
- for (const name of leadershipNames) {
210
- assert.ok(
211
- ceoNames.has(name),
212
- `leadership tool "${name}" is not present in ceo — privilege inversion`,
213
- );
214
- }
215
- });
216
-
217
- test("ceo has strictly more capability than leadership", () => {
218
- const leadershipNames = new Set(getToolNamesForAccessLevel("leadership"));
219
- const ceoNames = new Set(getToolNamesForAccessLevel("ceo"));
220
- assert.ok(ceoNames.size > leadershipNames.size, "ceo should outrank leadership in tool count");
221
- });
222
-
223
- test("sensitive CEO-only write tools never leak into the leadership surface", () => {
224
- const leadershipNames = new Set(getToolNamesForAccessLevel("leadership"));
225
- for (const name of CEO_ONLY_WRITE_NAMES) {
226
- assert.equal(
227
- leadershipNames.has(name),
228
- false,
229
- `CEO-only write tool "${name}" must not appear at leadership level`,
230
- );
231
- // sanity: it really is a CEO tool, otherwise the assertion above is vacuous.
232
- assert.ok(
233
- new Set(getToolNamesForAccessLevel("ceo")).has(name),
234
- `expected "${name}" to be a CEO tool`,
235
- );
236
- }
237
- });
238
-
239
- test("any voice write tool is also granted at ceo level (voice ⊆ ceo for write tools)", () => {
240
- const ceoNames = new Set(getToolNamesForAccessLevel("ceo"));
241
- const voiceTools = getToolsForAccessLevel("voice");
242
- for (const tool of voiceTools) {
243
- if (WRITE_TOOL_NAMES.has(tool.name)) {
244
- assert.ok(
245
- ceoNames.has(tool.name),
246
- `voice write tool "${tool.name}" is not authorised at ceo level — escalation`,
247
- );
248
- }
249
- }
250
- });
251
-
252
- // ---------------------------------------------------------------------------
253
- // Name-based lookup: unknown name resolves to nothing, no throw
254
- // ---------------------------------------------------------------------------
255
-
256
- test("looking up an unknown tool name returns undefined without throwing", () => {
257
- let found;
258
- assert.doesNotThrow(() => {
259
- found = findToolByName("ceo", "definitely_not_a_real_tool");
260
- });
261
- assert.equal(found, undefined);
262
- });
263
-
264
- test("looking up a known write tool at default level resolves to undefined (scoped out)", () => {
265
- assert.equal(findToolByName("default", "place_voice_call"), undefined);
266
- // ...but it does resolve at ceo level.
267
- const atCeo = findToolByName("ceo", "place_voice_call");
268
- assert.ok(atCeo);
269
- assert.equal(atCeo.name, "place_voice_call");
270
- });
271
-
272
- // ---------------------------------------------------------------------------
273
- // Structural invariants — every tool is a valid Claude API tool schema
274
- // ---------------------------------------------------------------------------
275
-
276
- /** Collect every distinct tool object exposed across all surfaces + categories. */
277
- function allDistinctTools() {
278
- const byName = new Map();
279
- for (const level of KNOWN_LEVELS) {
280
- for (const tool of getToolsForAccessLevel(level)) {
281
- byName.set(tool.name, tool);
282
- }
283
- }
284
- for (const tools of Object.values(getAllToolsByCategory())) {
285
- for (const tool of tools) byName.set(tool.name, tool);
286
- }
287
- return [...byName.values()];
288
- }
289
-
290
- test("every tool definition has a non-empty string name", () => {
291
- for (const tool of allDistinctTools()) {
292
- assert.equal(typeof tool.name, "string", `tool name not a string: ${JSON.stringify(tool)}`);
293
- assert.ok(tool.name.length > 0, "tool name is empty");
294
- // Claude API tool names: letters, numbers, underscores, hyphens.
295
- assert.match(tool.name, /^[a-zA-Z0-9_-]+$/, `tool name "${tool.name}" has invalid chars`);
296
- }
297
- });
298
-
299
- test("every tool definition has a non-empty string description", () => {
300
- for (const tool of allDistinctTools()) {
301
- assert.equal(typeof tool.description, "string", `${tool.name} description not a string`);
302
- assert.ok(tool.description.trim().length > 0, `${tool.name} has empty description`);
303
- }
304
- });
305
-
306
- test("every tool definition has an object-typed input_schema with properties", () => {
307
- for (const tool of allDistinctTools()) {
308
- assert.ok(tool.input_schema, `${tool.name} missing input_schema`);
309
- assert.equal(typeof tool.input_schema, "object", `${tool.name} input_schema not an object`);
310
- assert.equal(tool.input_schema.type, "object", `${tool.name} input_schema.type !== "object"`);
311
- assert.equal(
312
- typeof tool.input_schema.properties,
313
- "object",
314
- `${tool.name} input_schema.properties missing`,
315
- );
316
- // Every name in `required` must actually be declared in properties.
317
- if (Array.isArray(tool.input_schema.required)) {
318
- for (const req of tool.input_schema.required) {
319
- assert.ok(
320
- Object.prototype.hasOwnProperty.call(tool.input_schema.properties, req),
321
- `${tool.name} requires "${req}" but it is not declared in properties`,
322
- );
323
- }
324
- }
325
- }
326
- });
327
-
328
- // ---------------------------------------------------------------------------
329
- // getAllToolsByCategory shape
330
- // ---------------------------------------------------------------------------
331
-
332
- test("getAllToolsByCategory returns all seven categories, each a non-empty array", () => {
333
- const cats = getAllToolsByCategory();
334
- const expected = [
335
- "communication",
336
- "search",
337
- "knowledge",
338
- "operations",
339
- "analysis",
340
- "documents",
341
- "org",
342
- ];
343
- assert.deepEqual(Object.keys(cats).sort(), [...expected].sort());
344
- for (const key of expected) {
345
- assert.ok(Array.isArray(cats[key]), `${key} is not an array`);
346
- assert.ok(cats[key].length > 0, `${key} category is empty`);
347
- }
348
- });
349
-
350
- test("every categorised tool is a structurally valid tool definition", () => {
351
- for (const [cat, tools] of Object.entries(getAllToolsByCategory())) {
352
- for (const tool of tools) {
353
- assert.ok(tool.name, `tool in ${cat} missing name`);
354
- assert.ok(tool.description, `${tool.name} in ${cat} missing description`);
355
- assert.equal(tool.input_schema?.type, "object", `${tool.name} in ${cat} bad input_schema`);
356
- }
357
- }
358
- });
359
-
360
- // ---------------------------------------------------------------------------
361
- // Category 7 — Org (Cohort): the curated surface from lib/org/tool-surface.mjs
362
- // ---------------------------------------------------------------------------
363
-
364
- import { getOrgTools, emailFamilyAvailable } from "./org/tool-surface.mjs";
365
-
366
- test("org category mirrors the tool-surface table exactly (one SoT, two planes)", () => {
367
- const org = getAllToolsByCategory().org;
368
- const table = getOrgTools();
369
- assert.deepEqual(org.map((t) => t.name), table.map((t) => t.name), "same tools, same order");
370
- for (const t of org) {
371
- assert.ok(t.name && t.description && t.input_schema?.type === "object");
372
- assert.equal(t.access, undefined, "table-internal fields are stripped from the Claude schema");
373
- assert.equal(t.binding, undefined, "binding never leaks into the Claude schema");
374
- }
375
- });
376
-
377
- test("org access monotonicity: default ⊂ leadership ⊂ ceo on the org subset", () => {
378
- const table = getOrgTools();
379
- const orgNames = new Set(table.map((t) => t.name));
380
- const orgAt = (level) => getToolNamesForAccessLevel(level).filter((n) => orgNames.has(n));
381
- const def = orgAt("default");
382
- const lead = orgAt("leadership");
383
- const ceo = orgAt("ceo");
384
- for (const n of def) assert.ok(lead.includes(n), `default org tool ${n} must exist at leadership`);
385
- for (const n of lead) assert.ok(ceo.includes(n), `leadership org tool ${n} must exist at ceo`);
386
- // default = exactly the reads; leadership adds exactly the writes; ceo adds the hatches
387
- const reads = table.filter((t) => t.access === "read").map((t) => t.name).sort();
388
- const writes = table.filter((t) => t.access === "write").map((t) => t.name).sort();
389
- assert.deepEqual([...def].sort(), reads, "every level's lookup set = the org reads");
390
- assert.deepEqual(lead.filter((n) => !def.includes(n)).sort(), writes, "leadership adds the org writes");
391
- });
392
-
393
- test("escape hatches (org_rpc / org_read) are CEO-only in the native plane", () => {
394
- const def = new Set(getToolNamesForAccessLevel("default"));
395
- const lead = new Set(getToolNamesForAccessLevel("leadership"));
396
- const voice = new Set(getToolNamesForAccessLevel("voice"));
397
- const ceo = new Set(getToolNamesForAccessLevel("ceo"));
398
- for (const hatch of ["org_rpc", "org_read"]) {
399
- assert.ok(ceo.has(hatch), `${hatch} present at ceo`);
400
- assert.ok(!def.has(hatch), `${hatch} must NOT reach default`);
401
- assert.ok(!lead.has(hatch), `${hatch} must NOT reach leadership`);
402
- assert.ok(!voice.has(hatch), `${hatch} must NOT reach voice`);
403
- }
404
- });
405
-
406
- test("org writes gate on leadership+: default/voice never gain messaging_send-class tools", () => {
407
- const def = new Set(getToolNamesForAccessLevel("default"));
408
- for (const w of ["messaging_send", "board_claim", "task_create", "decision_propose", "email_send", "email_mark_read", "escalation_raise"]) {
409
- if (getOrgTools().some((t) => t.name === w)) {
410
- assert.ok(!def.has(w), `org write ${w} must not reach the default (read-only) surface`);
411
- assert.ok(new Set(getToolNamesForAccessLevel("leadership")).has(w), `${w} present at leadership`);
412
- }
413
- }
414
- });
415
-
416
- test("voice curation: conversation-sized org tools only", () => {
417
- const voice = new Set(getToolNamesForAccessLevel("voice"));
418
- for (const n of ["org_whoami", "messaging_history", "messaging_send", "board_ready"]) {
419
- assert.ok(voice.has(n), `${n} curated into voice`);
420
- }
421
- for (const n of ["org_rpc", "org_read", "email_send", "knowledge_append", "decision_propose"]) {
422
- assert.ok(!voice.has(n), `${n} must NOT reach the voice surface`);
423
- }
424
- });
425
-
426
- test("email tools present in the native surface iff the vendored protocol carries the family", () => {
427
- const lead = new Set(getToolNamesForAccessLevel("leadership"));
428
- const def = new Set(getToolNamesForAccessLevel("default"));
429
- if (emailFamilyAvailable()) {
430
- assert.ok(def.has("email_inbox") && def.has("email_message") && def.has("email_thread"), "email reads in every level's lookup set");
431
- assert.ok(lead.has("email_send") && lead.has("email_mark_read"), "email writes at leadership+");
432
- } else {
433
- for (const n of ["email_inbox", "email_send", "email_mark_read"]) {
434
- assert.ok(!def.has(n) && !lead.has(n), `${n} absent while the family is unvendored`);
435
- }
436
- }
437
- });
@@ -1,65 +0,0 @@
1
- /**
2
- * global-refresh.test.mjs — planGlobalRefresh is pure; refreshGlobalInstall
3
- * never reaches a real npm (exec is injected AND MAESTRO_SKIP_GLOBAL_INSTALL=1
4
- * is honoured).
5
- */
6
-
7
- import { test } from "node:test";
8
- import assert from "node:assert/strict";
9
- import { planGlobalRefresh, parseGlobalVersion, refreshGlobalInstall, readGlobalVersion } from "./global-refresh.mjs";
10
-
11
- const ARGV = ["npm", "install", "-g", "@cohortapp/agent-sdk@2.12.0", "--no-audit", "--no-fund"];
12
-
13
- test("planGlobalRefresh: install when absent or behind, none when equal, skip under MAESTRO_SKIP_GLOBAL_INSTALL", () => {
14
- assert.deepEqual(planGlobalRefresh({ version: "2.12.0", globalVersion: "" }), { action: "install", reason: "not installed globally", argv: ARGV });
15
- assert.deepEqual(planGlobalRefresh({ version: "2.12.0", globalVersion: "2.11.17" }), { action: "install", reason: "global install is 2.11.17, agent dir is 2.12.0", argv: ARGV });
16
- assert.equal(planGlobalRefresh({ version: "2.12.0", globalVersion: "2.12.0" }).action, "none");
17
- const s = planGlobalRefresh({ version: "2.12.0", globalVersion: "1.0.0", skip: true });
18
- assert.equal(s.action, "skip");
19
- assert.deepEqual(s.argv, ARGV, "the skipped command is still reported so an operator can run it by hand");
20
- assert.equal(planGlobalRefresh({ version: "", globalVersion: "1.0.0" }).action, "skip");
21
- });
22
-
23
- test("parseGlobalVersion reads npm ls --json, tolerates the tree form and the not-installed shape", () => {
24
- assert.equal(parseGlobalVersion(JSON.stringify({ dependencies: { "@cohortapp/agent-sdk": { version: "2.11.17" } } })), "2.11.17");
25
- assert.equal(parseGlobalVersion(JSON.stringify({ name: "lib", dependencies: {} })), "");
26
- assert.equal(parseGlobalVersion("/opt/homebrew/lib\n└── @cohortapp/agent-sdk@2.12.0\n"), "2.12.0");
27
- assert.equal(parseGlobalVersion(""), "");
28
- assert.equal(parseGlobalVersion("not json"), "");
29
- });
30
-
31
- function recorder(responses) {
32
- const calls = [];
33
- const exec = (cmd, args) => { calls.push([cmd, ...args]); return responses(cmd, args); };
34
- exec.calls = calls;
35
- return exec;
36
- }
37
-
38
- test("refreshGlobalInstall: with MAESTRO_SKIP_GLOBAL_INSTALL=1 nothing is executed and the row says what was skipped", () => {
39
- const exec = recorder(() => ({ status: 0, stdout: "", stderr: "" }));
40
- const r = refreshGlobalInstall({ version: "2.12.0", env: { MAESTRO_SKIP_GLOBAL_INSTALL: "1" }, exec });
41
- assert.equal(r.ok, true);
42
- assert.equal(r.action, "skip");
43
- assert.match(r.detail, /npm install -g @cohortapp\/agent-sdk@2\.12\.0 --no-audit --no-fund/);
44
- assert.deepEqual(exec.calls, [], "no npm call at all under the skip flag");
45
- });
46
-
47
- test("refreshGlobalInstall: runs the pinned install through exec when behind; reports a failure as ok:false with the tail of stderr", () => {
48
- const behind = recorder((cmd, args) => (args[0] === "ls"
49
- ? { status: 1, stdout: JSON.stringify({ dependencies: { "@cohortapp/agent-sdk": { version: "2.11.17" } } }), stderr: "" }
50
- : { status: 0, stdout: "", stderr: "" }));
51
- const r = refreshGlobalInstall({ version: "2.12.0", env: {}, exec: behind });
52
- assert.equal(r.ok, true);
53
- assert.deepEqual(behind.calls[1], ARGV);
54
-
55
- const same = recorder(() => ({ status: 0, stdout: JSON.stringify({ dependencies: { "@cohortapp/agent-sdk": { version: "2.12.0" } } }), stderr: "" }));
56
- const n = refreshGlobalInstall({ version: "2.12.0", env: {}, exec: same });
57
- assert.equal(n.action, "none");
58
- assert.equal(same.calls.length, 1, "only the read");
59
-
60
- const fail = recorder((cmd, args) => (args[0] === "ls" ? { status: 1, stdout: "{}", stderr: "" } : { status: 243, stdout: "", stderr: "npm ERR! code EACCES\nnpm ERR! permission denied\n" }));
61
- const f = refreshGlobalInstall({ version: "2.12.0", env: {}, exec: fail });
62
- assert.equal(f.ok, false);
63
- assert.match(f.detail, /permission denied/);
64
- assert.equal(readGlobalVersion({ exec: fail }), "");
65
- });