@cohortapp/agent-sdk 2.17.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 (526) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +18 -5
  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/setup-wizard.md +1 -1
  9. package/docs/runbooks/fleet-rollout.md +156 -0
  10. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  11. package/lib/action-executor.js +19 -3
  12. package/lib/budget-guard.mjs +279 -3
  13. package/lib/channels/base-adapter.mjs +3 -1
  14. package/lib/channels/contract.mjs +2 -1
  15. package/lib/channels/inbox-item.mjs +8 -0
  16. package/lib/claude-bin.mjs +5 -6
  17. package/lib/cli/doctor-checks.mjs +141 -10
  18. package/lib/cli/global-setup-extras.mjs +5 -1
  19. package/lib/cli/inbox.mjs +100 -15
  20. package/lib/cli/seat-auth.mjs +463 -0
  21. package/lib/cli/session.mjs +80 -12
  22. package/lib/collective/capture.mjs +8 -6
  23. package/lib/collective/global-config.mjs +63 -1
  24. package/lib/collective/presence.mjs +142 -5
  25. package/lib/comms/send-gate.mjs +559 -1
  26. package/lib/diagnostics/alerts.mjs +49 -0
  27. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  28. package/lib/engine/agents/definitions.mjs +343 -0
  29. package/lib/engine/agents/persist.mjs +275 -0
  30. package/lib/engine/agents/runtime.mjs +748 -0
  31. package/lib/engine/agents/usage.mjs +95 -0
  32. package/lib/engine/auth-status.mjs +139 -0
  33. package/lib/engine/budget.mjs +194 -0
  34. package/lib/engine/cli.mjs +1204 -0
  35. package/lib/engine/commands/index.mjs +269 -0
  36. package/lib/engine/context/budget.mjs +219 -0
  37. package/lib/engine/context/cache.mjs +125 -0
  38. package/lib/engine/context/child-env.mjs +215 -0
  39. package/lib/engine/context/compaction.mjs +342 -0
  40. package/lib/engine/context/images.mjs +90 -0
  41. package/lib/engine/context/instructions.mjs +327 -0
  42. package/lib/engine/context/lazy-instructions.mjs +169 -0
  43. package/lib/engine/context/manager.mjs +182 -0
  44. package/lib/engine/context/real-path.mjs +91 -0
  45. package/lib/engine/context/secret-values.mjs +163 -0
  46. package/lib/engine/context/settings.mjs +274 -0
  47. package/lib/engine/context/stream-input.mjs +159 -0
  48. package/lib/engine/guard.mjs +152 -0
  49. package/lib/engine/hooks.mjs +713 -0
  50. package/lib/engine/loop.mjs +560 -0
  51. package/lib/engine/mcp/client.mjs +254 -0
  52. package/lib/engine/mcp/config.mjs +301 -0
  53. package/lib/engine/mcp/http.mjs +201 -0
  54. package/lib/engine/mcp/index.mjs +146 -0
  55. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  56. package/lib/engine/mcp/naming.mjs +66 -0
  57. package/lib/engine/mcp/resources.mjs +89 -0
  58. package/lib/engine/mcp/results.mjs +133 -0
  59. package/lib/engine/mcp/stdio.mjs +137 -0
  60. package/lib/engine/mcp/supervisor.mjs +116 -0
  61. package/lib/engine/messages.mjs +104 -0
  62. package/lib/engine/output/json.mjs +164 -0
  63. package/lib/engine/output/stream-json.mjs +266 -0
  64. package/lib/engine/permissions.mjs +845 -0
  65. package/lib/engine/process-identity.mjs +164 -0
  66. package/lib/engine/process-tree.mjs +551 -0
  67. package/lib/engine/prompt.mjs +60 -0
  68. package/lib/engine/session/store.mjs +299 -0
  69. package/lib/engine/session-runtime/args.mjs +97 -0
  70. package/lib/engine/session-runtime/host.mjs +143 -0
  71. package/lib/engine/session-runtime/inbox.mjs +122 -0
  72. package/lib/engine/session-runtime/notifications.mjs +129 -0
  73. package/lib/engine/session-runtime/registry.mjs +328 -0
  74. package/lib/engine/session-runtime/runner.mjs +344 -0
  75. package/lib/engine/session-runtime/socket.mjs +212 -0
  76. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  77. package/lib/engine/skills/index.mjs +321 -0
  78. package/lib/engine/tools/bash-background.mjs +533 -0
  79. package/lib/engine/tools/bash.mjs +216 -0
  80. package/lib/engine/tools/edit.mjs +97 -0
  81. package/lib/engine/tools/glob.mjs +81 -0
  82. package/lib/engine/tools/grep.mjs +224 -0
  83. package/lib/engine/tools/index.mjs +84 -0
  84. package/lib/engine/tools/list-agents.mjs +32 -0
  85. package/lib/engine/tools/ls.mjs +127 -0
  86. package/lib/engine/tools/monitor.mjs +82 -0
  87. package/lib/engine/tools/notebook-edit.mjs +218 -0
  88. package/lib/engine/tools/read.mjs +103 -0
  89. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  90. package/lib/engine/tools/schema.mjs +144 -0
  91. package/lib/engine/tools/send-message.mjs +77 -0
  92. package/lib/engine/tools/session.mjs +70 -0
  93. package/lib/engine/tools/todo.mjs +144 -0
  94. package/lib/engine/tools/toolsearch.mjs +217 -0
  95. package/lib/engine/tools/walk.mjs +193 -0
  96. package/lib/engine/tools/web-switch.mjs +31 -0
  97. package/lib/engine/tools/webfetch-html.mjs +387 -0
  98. package/lib/engine/tools/webfetch-net.mjs +340 -0
  99. package/lib/engine/tools/webfetch.mjs +198 -0
  100. package/lib/engine/tools/websearch.mjs +91 -0
  101. package/lib/engine/tools/workflow.mjs +95 -0
  102. package/lib/engine/tools/write.mjs +76 -0
  103. package/lib/engine/tui/line-editor.mjs +137 -0
  104. package/lib/engine/tui/render.mjs +86 -0
  105. package/lib/engine/tui/tui.mjs +274 -0
  106. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  107. package/lib/engine/wire/effort.mjs +36 -0
  108. package/lib/engine/wire/errors.mjs +496 -0
  109. package/lib/engine/wire/http.mjs +441 -0
  110. package/lib/engine/wire/index.mjs +76 -0
  111. package/lib/engine/wire/openai-chat.mjs +332 -0
  112. package/lib/engine/wire/prompt-cache.mjs +79 -0
  113. package/lib/engine/wire/search.mjs +140 -0
  114. package/lib/engine/wire/sse.mjs +114 -0
  115. package/lib/engine/wire/stall.mjs +349 -0
  116. package/lib/engine/wire/token-provider.mjs +175 -0
  117. package/lib/engine/wire/usage.mjs +192 -0
  118. package/lib/engine/workflow/host.mjs +524 -0
  119. package/lib/engine/workflow/journal.mjs +188 -0
  120. package/lib/engine/workflow/json-schema.mjs +171 -0
  121. package/lib/engine/workflow/meta.mjs +329 -0
  122. package/lib/engine/workflow/notifications.mjs +52 -0
  123. package/lib/engine/workflow/runtime.mjs +447 -0
  124. package/lib/engine/workflow/sandbox.mjs +534 -0
  125. package/lib/engine/workflow/worker.mjs +141 -0
  126. package/lib/engine/workflow/worktree.mjs +74 -0
  127. package/lib/execution/disposition.mjs +1 -1
  128. package/lib/execution/intake.mjs +10 -0
  129. package/lib/execution/surface-policy.mjs +15 -0
  130. package/lib/learning/curator.mjs +8 -6
  131. package/lib/learning/reflect.mjs +8 -6
  132. package/lib/model-router/catalog/cohort.yaml +137 -0
  133. package/lib/model-router/catalog.mjs +118 -1
  134. package/lib/model-router/failover.mjs +67 -16
  135. package/lib/model-router/llm-task.mjs +39 -3
  136. package/lib/model-router/resolve.mjs +89 -3
  137. package/lib/model-router/spawn.mjs +46 -47
  138. package/lib/model-router/taxonomy.mjs +126 -4
  139. package/lib/org/cost-sync.mjs +141 -11
  140. package/lib/org/inbound/broadcast.mjs +289 -0
  141. package/lib/org/inbound/collective.mjs +375 -0
  142. package/lib/org/inbound/directedness.mjs +96 -8
  143. package/lib/org/inbound/facts.mjs +78 -2
  144. package/lib/org/inbound/project.mjs +22 -0
  145. package/lib/org/inbound/surfaces.mjs +14 -0
  146. package/lib/org/llm-token.mjs +879 -0
  147. package/lib/org/mesh.mjs +61 -0
  148. package/lib/org/messaging.mjs +3 -1
  149. package/lib/org/protocol.checksum +1 -1
  150. package/lib/org/protocol.mjs +15 -0
  151. package/lib/org/quota.mjs +520 -0
  152. package/lib/org/tool-surface.mjs +104 -16
  153. package/lib/org/ui-parity.mjs +16 -1
  154. package/lib/org/work-ledger.mjs +37 -6
  155. package/lib/rate-guard.mjs +114 -1
  156. package/lib/resource-governor.mjs +41 -6
  157. package/lib/runtime/adapter.mjs +823 -0
  158. package/lib/runtime/child-env.mjs +191 -0
  159. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  160. package/lib/runtime/seat-engine.mjs +162 -0
  161. package/lib/session/ask-ledger.mjs +271 -0
  162. package/lib/session/current-work.mjs +676 -0
  163. package/lib/session/feed-core.mjs +40 -3
  164. package/lib/session/launch-args.mjs +56 -4
  165. package/lib/session/status-summary.mjs +26 -9
  166. package/lib/session/upgrade-notice.mjs +42 -0
  167. package/lib/setup/claude-probe.mjs +117 -13
  168. package/lib/setup/enrich.mjs +13 -10
  169. package/lib/setup/sections/model.mjs +39 -13
  170. package/lib/telemetry/collect.mjs +208 -9
  171. package/lib/upgrade/ignored-drift.mjs +105 -0
  172. package/lib/voice/post-call-brief.mjs +30 -17
  173. package/package.json +13 -3
  174. package/plugins/maestro-skills/skills/board-work.md +5 -0
  175. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  176. package/plugins/maestro-skills/skills/main-session.md +18 -7
  177. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  178. package/scripts/ci/run-tests.mjs +47 -19
  179. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  180. package/scripts/collective/hook-runner.mjs +29 -2
  181. package/scripts/continuous-monitor.sh +13 -0
  182. package/scripts/cost/track-claude-usage.mjs +15 -0
  183. package/scripts/daemon/agent-daemon.mjs +408 -20
  184. package/scripts/daemon/assurance.mjs +48 -12
  185. package/scripts/daemon/cadence-consumer.mjs +218 -68
  186. package/scripts/daemon/cadence-handlers.mjs +73 -4
  187. package/scripts/daemon/classifier.mjs +75 -26
  188. package/scripts/daemon/context-compiler.mjs +51 -37
  189. package/scripts/daemon/deliver.mjs +30 -1
  190. package/scripts/daemon/dispatcher.mjs +595 -149
  191. package/scripts/daemon/health.mjs +14 -1
  192. package/scripts/daemon/maestro-daemon.mjs +11 -0
  193. package/scripts/daemon/prompt-builder.mjs +24 -0
  194. package/scripts/daemon/responder.mjs +246 -79
  195. package/scripts/daemon/sdk-version.mjs +98 -16
  196. package/scripts/eval/probe-gateway.mjs +635 -0
  197. package/scripts/eval/replay/extract.mjs +270 -0
  198. package/scripts/eval/replay/grade.mjs +260 -0
  199. package/scripts/eval/replay/lib/config.mjs +50 -0
  200. package/scripts/eval/replay/lib/effects.mjs +65 -0
  201. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  202. package/scripts/eval/replay/lib/judge.mjs +72 -0
  203. package/scripts/eval/replay/lib/redact.mjs +136 -0
  204. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  205. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  206. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  207. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  208. package/scripts/eval/replay/report.mjs +185 -0
  209. package/scripts/eval/replay/run.mjs +404 -0
  210. package/scripts/fleet/rollout.mjs +1094 -0
  211. package/scripts/hooks/pre-send-audit.sh +36 -245
  212. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  213. package/scripts/hooks/validate-state-yaml.sh +190 -0
  214. package/scripts/huddle/huddle-llm.mjs +361 -0
  215. package/scripts/huddle/huddle-server.mjs +46 -121
  216. package/scripts/local-triggers/autoupdate.sh +448 -78
  217. package/scripts/local-triggers/run-trigger.sh +13 -0
  218. package/scripts/maintenance/pin-integrity.mjs +364 -0
  219. package/scripts/poll-slack-events.sh +41 -9
  220. package/scripts/poller/slack-socket-mode.mjs +28 -3
  221. package/scripts/session/supervisor.mjs +80 -13
  222. package/scripts/spawn-session.sh +13 -0
  223. package/bin/maestro.test.mjs +0 -1574
  224. package/lib/action-executor.test.mjs +0 -871
  225. package/lib/archetype.test.mjs +0 -132
  226. package/lib/assurance/plan-note.test.mjs +0 -234
  227. package/lib/assurance/room-budget.test.mjs +0 -486
  228. package/lib/assurance/tier.test.mjs +0 -174
  229. package/lib/autonomy.test.mjs +0 -66
  230. package/lib/backlog.test.mjs +0 -302
  231. package/lib/backup/policy.test.mjs +0 -305
  232. package/lib/budget-escalate.test.mjs +0 -232
  233. package/lib/budget-guard.envelope.test.mjs +0 -476
  234. package/lib/budget-guard.test.mjs +0 -427
  235. package/lib/cadence-bus-requeue.test.mjs +0 -83
  236. package/lib/cadence-bus-schedule.test.mjs +0 -194
  237. package/lib/cadence-bus.test.mjs +0 -720
  238. package/lib/cadences.test.mjs +0 -230
  239. package/lib/capability/inventory.test.mjs +0 -232
  240. package/lib/capability.test.mjs +0 -78
  241. package/lib/channels/base-adapter.test.mjs +0 -590
  242. package/lib/channels/channels.test.mjs +0 -371
  243. package/lib/channels/contract.test.mjs +0 -162
  244. package/lib/channels/inbox-item.test.mjs +0 -368
  245. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  246. package/lib/channels/pairing.test.mjs +0 -270
  247. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  248. package/lib/channels/slack-adapter.test.mjs +0 -212
  249. package/lib/channels/telegram-adapter.test.mjs +0 -306
  250. package/lib/channels/voice/adapter.test.mjs +0 -278
  251. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  252. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  253. package/lib/charter.test.mjs +0 -89
  254. package/lib/claude-bin.test.mjs +0 -131
  255. package/lib/cli/board.test.mjs +0 -227
  256. package/lib/cli/design.test.mjs +0 -270
  257. package/lib/cli/doctor-checks.test.mjs +0 -336
  258. package/lib/cli/global-setup-extras.test.mjs +0 -462
  259. package/lib/cli/inbox.test.mjs +0 -230
  260. package/lib/cli/session-ack.test.mjs +0 -63
  261. package/lib/cli/session.test.mjs +0 -613
  262. package/lib/collective/capture.test.mjs +0 -121
  263. package/lib/collective/cards.test.mjs +0 -114
  264. package/lib/collective/config.test.mjs +0 -123
  265. package/lib/collective/global-config.test.mjs +0 -220
  266. package/lib/collective/global-skills.test.mjs +0 -126
  267. package/lib/collective/presence.test.mjs +0 -95
  268. package/lib/collective/recall.test.mjs +0 -116
  269. package/lib/collective/vendor-skills.test.mjs +0 -306
  270. package/lib/comms/send-gate.test.mjs +0 -770
  271. package/lib/comms.test.mjs +0 -41
  272. package/lib/context/budget.test.mjs +0 -252
  273. package/lib/context/history-scope.test.mjs +0 -79
  274. package/lib/cost/ledger-row.test.mjs +0 -183
  275. package/lib/design/design-md.test.mjs +0 -318
  276. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  277. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  278. package/lib/design/fixtures/foundation.json +0 -133
  279. package/lib/design/refresh-gate.test.mjs +0 -144
  280. package/lib/design/write.test.mjs +0 -241
  281. package/lib/diagnostics/alerts.test.mjs +0 -318
  282. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  283. package/lib/diagnostics/counters.test.mjs +0 -206
  284. package/lib/diagnostics/events.test.mjs +0 -290
  285. package/lib/diagnostics/otel.test.mjs +0 -196
  286. package/lib/diagnostics/trace.test.mjs +0 -251
  287. package/lib/env-compat.test.mjs +0 -104
  288. package/lib/execution/disposition.test.mjs +0 -553
  289. package/lib/execution/drive.test.mjs +0 -270
  290. package/lib/execution/effects.test.mjs +0 -344
  291. package/lib/execution/intake.test.mjs +0 -389
  292. package/lib/execution/journal.test.mjs +0 -261
  293. package/lib/execution/match.test.mjs +0 -235
  294. package/lib/execution/pipeline.test.mjs +0 -392
  295. package/lib/execution/route.test.mjs +0 -186
  296. package/lib/execution/surface-policy.test.mjs +0 -162
  297. package/lib/fs-atomic.test.mjs +0 -72
  298. package/lib/fs-ownership.test.mjs +0 -158
  299. package/lib/goals/admission.test.mjs +0 -164
  300. package/lib/goals/classify.test.mjs +0 -167
  301. package/lib/goals/collaborate.test.mjs +0 -336
  302. package/lib/goals/gaps.test.mjs +0 -284
  303. package/lib/goals/loop.test.mjs +0 -845
  304. package/lib/hooks/bus.test.mjs +0 -387
  305. package/lib/identity/persona.test.mjs +0 -142
  306. package/lib/kpi-sensors.test.mjs +0 -278
  307. package/lib/kpi.test.mjs +0 -244
  308. package/lib/learning/config.test.mjs +0 -75
  309. package/lib/learning/counters.test.mjs +0 -69
  310. package/lib/learning/curator-consolidate.test.mjs +0 -238
  311. package/lib/learning/curator.test.mjs +0 -106
  312. package/lib/learning/reflect.test.mjs +0 -0
  313. package/lib/learning/session-index.test.mjs +0 -125
  314. package/lib/learning/skill-writer.test.mjs +0 -210
  315. package/lib/mandate/audit.test.mjs +0 -195
  316. package/lib/mandate/contract.test.mjs +0 -185
  317. package/lib/mandate/derive.test.mjs +0 -274
  318. package/lib/mandate/model.test.mjs +0 -164
  319. package/lib/mandate/refresh.test.mjs +0 -389
  320. package/lib/mcp/server.test.mjs +0 -426
  321. package/lib/model-router/auth-profiles.test.mjs +0 -580
  322. package/lib/model-router/catalog.test.mjs +0 -385
  323. package/lib/model-router/economics.test.mjs +0 -438
  324. package/lib/model-router/failover.test.mjs +0 -439
  325. package/lib/model-router/health.test.mjs +0 -338
  326. package/lib/model-router/integration-coverage.test.mjs +0 -831
  327. package/lib/model-router/integration.test.mjs +0 -564
  328. package/lib/model-router/ledger.test.mjs +0 -415
  329. package/lib/model-router/llm-task.test.mjs +0 -392
  330. package/lib/model-router/org-credentials.test.mjs +0 -265
  331. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  332. package/lib/model-router/reconcile.test.mjs +0 -316
  333. package/lib/model-router/repair.test.mjs +0 -180
  334. package/lib/model-router/spawn.test.mjs +0 -446
  335. package/lib/model-router/taxonomy.test.mjs +0 -410
  336. package/lib/model-router.test.mjs +0 -1207
  337. package/lib/org/activity.test.mjs +0 -134
  338. package/lib/org/approvals.test.mjs +0 -216
  339. package/lib/org/awareness.test.mjs +0 -159
  340. package/lib/org/board-mine-cache.test.mjs +0 -53
  341. package/lib/org/board.test.mjs +0 -187
  342. package/lib/org/bootstrap-context.test.mjs +0 -153
  343. package/lib/org/client.test.mjs +0 -1206
  344. package/lib/org/cohort-client.test.mjs +0 -126
  345. package/lib/org/cost-sync.test.mjs +0 -153
  346. package/lib/org/doctor.test.mjs +0 -346
  347. package/lib/org/engagement-ledger.test.mjs +0 -112
  348. package/lib/org/engagement.test.mjs +0 -739
  349. package/lib/org/handoff.test.mjs +0 -269
  350. package/lib/org/inbound/directedness.test.mjs +0 -668
  351. package/lib/org/inbound/facts.test.mjs +0 -471
  352. package/lib/org/inbound/hydrate.test.mjs +0 -908
  353. package/lib/org/inbound/index.test.mjs +0 -429
  354. package/lib/org/inbound/project.test.mjs +0 -287
  355. package/lib/org/integration-tools.test.mjs +0 -160
  356. package/lib/org/keys.test.mjs +0 -92
  357. package/lib/org/knowledge.test.mjs +0 -326
  358. package/lib/org/leases.test.mjs +0 -235
  359. package/lib/org/mesh-directives.test.mjs +0 -110
  360. package/lib/org/mesh-integration.test.mjs +0 -127
  361. package/lib/org/mesh.test.mjs +0 -400
  362. package/lib/org/messaging.test.mjs +0 -471
  363. package/lib/org/param-contract.test.mjs +0 -477
  364. package/lib/org/policy.test.mjs +0 -237
  365. package/lib/org/protocol.checksum.test.mjs +0 -90
  366. package/lib/org/protocol.test.mjs +0 -323
  367. package/lib/org/push.test.mjs +0 -792
  368. package/lib/org/registry.test.mjs +0 -100
  369. package/lib/org/resource-tools.test.mjs +0 -361
  370. package/lib/org/tool-access.test.mjs +0 -144
  371. package/lib/org/tool-surface-integration.test.mjs +0 -120
  372. package/lib/org/tool-surface.test.mjs +0 -1268
  373. package/lib/org/typing.test.mjs +0 -291
  374. package/lib/org/ui-parity.test.mjs +0 -560
  375. package/lib/org/verify.test.mjs +0 -194
  376. package/lib/org/work-ledger.test.mjs +0 -273
  377. package/lib/plan/adoption-e2e.test.mjs +0 -366
  378. package/lib/plan/budget-enforcement.test.mjs +0 -400
  379. package/lib/plan/compile.test.mjs +0 -382
  380. package/lib/plan/emit.test.mjs +0 -269
  381. package/lib/plan/explain.test.mjs +0 -188
  382. package/lib/prompts/parallelism.test.mjs +0 -177
  383. package/lib/rag/rag.test.mjs +0 -505
  384. package/lib/rate-guard.test.mjs +0 -272
  385. package/lib/reactive-gate.test.mjs +0 -57
  386. package/lib/render.test.mjs +0 -68
  387. package/lib/resource-governor.test.mjs +0 -488
  388. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  389. package/lib/scheduling/jitter.test.mjs +0 -140
  390. package/lib/secrets/broker.test.mjs +0 -280
  391. package/lib/secrets/providers.test.mjs +0 -274
  392. package/lib/security/audit-engine.test.mjs +0 -424
  393. package/lib/security/coerce-args.test.mjs +0 -281
  394. package/lib/security/dangerous-tools.test.mjs +0 -68
  395. package/lib/security/external-content.test.mjs +0 -84
  396. package/lib/security/redact.test.mjs +0 -441
  397. package/lib/security/secret-equal.test.mjs +0 -55
  398. package/lib/session/config.test.mjs +0 -92
  399. package/lib/session/feed-core.test.mjs +0 -198
  400. package/lib/session/first-run.test.mjs +0 -121
  401. package/lib/session/frontdoor.test.mjs +0 -205
  402. package/lib/session/handoffs.test.mjs +0 -183
  403. package/lib/session/identity.test.mjs +0 -180
  404. package/lib/session/inbox-claims.test.mjs +0 -286
  405. package/lib/session/launch-args.test.mjs +0 -157
  406. package/lib/session/liveness.test.mjs +0 -100
  407. package/lib/session/status-summary.test.mjs +0 -118
  408. package/lib/session-permissions.test.mjs +0 -120
  409. package/lib/setup/claude-probe.test.mjs +0 -187
  410. package/lib/setup/completeness.test.mjs +0 -110
  411. package/lib/setup/context-pack.test.mjs +0 -89
  412. package/lib/setup/enrich.test.mjs +0 -115
  413. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  414. package/lib/setup/integration.test.mjs +0 -162
  415. package/lib/setup/io.test.mjs +0 -77
  416. package/lib/setup/runner.test.mjs +0 -132
  417. package/lib/setup/sections/identity.test.mjs +0 -234
  418. package/lib/setup/sections/inventory.test.mjs +0 -198
  419. package/lib/setup/sections/learning.test.mjs +0 -81
  420. package/lib/setup/sections/mandate.test.mjs +0 -388
  421. package/lib/setup/sections/messaging.test.mjs +0 -127
  422. package/lib/setup/sections/model.test.mjs +0 -240
  423. package/lib/setup/sections/org.test.mjs +0 -346
  424. package/lib/setup/sections/orgmail.test.mjs +0 -118
  425. package/lib/setup/sections/recovery.test.mjs +0 -98
  426. package/lib/setup/sections/subagents.test.mjs +0 -429
  427. package/lib/setup/sections/verify.test.mjs +0 -175
  428. package/lib/setup/sot.test.mjs +0 -81
  429. package/lib/setup/state.test.mjs +0 -115
  430. package/lib/singleton.test.mjs +0 -151
  431. package/lib/subagents/cli.test.mjs +0 -389
  432. package/lib/subagents/client.test.mjs +0 -309
  433. package/lib/subagents/gap.test.mjs +0 -234
  434. package/lib/subagents/lock.test.mjs +0 -248
  435. package/lib/subagents/manifest.test.mjs +0 -175
  436. package/lib/subagents/refs.test.mjs +0 -204
  437. package/lib/subagents/resolve.test.mjs +0 -422
  438. package/lib/subagents/schema.test.mjs +0 -328
  439. package/lib/telemetry/alerts.test.mjs +0 -109
  440. package/lib/telemetry/collect.test.mjs +0 -1274
  441. package/lib/tool-definitions-integration.test.mjs +0 -83
  442. package/lib/tool-definitions.test.mjs +0 -437
  443. package/lib/upgrade/global-refresh.test.mjs +0 -65
  444. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  445. package/lib/upgrade/post-steps.test.mjs +0 -200
  446. package/lib/upgrade/verify.test.mjs +0 -164
  447. package/lib/util/fetch-timeout.test.mjs +0 -202
  448. package/lib/util/reconnect.test.mjs +0 -369
  449. package/lib/util/unhandled.test.mjs +0 -216
  450. package/lib/voice/outbound.test.mjs +0 -69
  451. package/lib/voice/session-rotation.test.mjs +0 -114
  452. package/lib/voice/stt.test.mjs +0 -226
  453. package/lib/voice/voice.test.mjs +0 -990
  454. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  455. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  456. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  457. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  458. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  459. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  460. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  461. package/scripts/ci/check.test.mjs +0 -194
  462. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  463. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  464. package/scripts/collective/hook-runner.test.mjs +0 -173
  465. package/scripts/cost/fleet-digest.test.mjs +0 -207
  466. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  467. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  468. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  469. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  470. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  471. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  472. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  473. package/scripts/daemon/assurance.test.mjs +0 -1791
  474. package/scripts/daemon/board-mirror.test.mjs +0 -165
  475. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  476. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  477. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  478. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  479. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  480. package/scripts/daemon/classifier.test.mjs +0 -266
  481. package/scripts/daemon/classify-kind.test.mjs +0 -40
  482. package/scripts/daemon/context-compiler.test.mjs +0 -406
  483. package/scripts/daemon/deliver.test.mjs +0 -564
  484. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  485. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  486. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  487. package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
  488. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  489. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  490. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  491. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  492. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  493. package/scripts/daemon/integration.test.mjs +0 -149
  494. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  495. package/scripts/daemon/lib/session-router.test.mjs +0 -554
  496. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  497. package/scripts/daemon/prompt-builder.test.mjs +0 -556
  498. package/scripts/daemon/responder-cost.test.mjs +0 -68
  499. package/scripts/daemon/responder-history.test.mjs +0 -221
  500. package/scripts/daemon/sdk-version.test.mjs +0 -31
  501. package/scripts/daemon/session-lock.test.mjs +0 -252
  502. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  503. package/scripts/daemon/typing-registry.test.mjs +0 -102
  504. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  505. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  506. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  507. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  508. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  509. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  510. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  511. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  512. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  513. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  514. package/scripts/poller-launchd/install.test.mjs +0 -243
  515. package/scripts/restore-from-backup.test.mjs +0 -181
  516. package/scripts/session/feed.test.mjs +0 -196
  517. package/scripts/session/supervisor-sh.test.mjs +0 -218
  518. package/scripts/session/supervisor.test.mjs +0 -482
  519. package/scripts/setup/configure-macos.test.mjs +0 -306
  520. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  521. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  522. package/scripts/setup/generate-capability.test.mjs +0 -134
  523. package/scripts/setup/init-agent.test.mjs +0 -370
  524. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  525. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  526. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -22,9 +22,9 @@ This runbook covers the complete setup of a Mac mini as the perpetual operations
22
22
 
23
23
  Before starting, ensure you have credentials for:
24
24
 
25
- - Claude Code Max subscription — the seat runs on a long-lived OAuth token
26
- from `claude setup-token` (see Step 6); an Anthropic API key is the
27
- pay-per-token alternative, not the default
25
+ - A per-agent Claude Console API key in a workspace with a spend cap — the
26
+ fleet posture (see Step 6 and `docs/guides/billing-console-keys.md`). Do not
27
+ provision a seat on Max-subscription OAuth (`claude setup-token`)
28
28
  - GitHub (access to maestro repo)
29
29
  - Google Workspace (the-agent@the-company)
30
30
  - Slack (the company workspace)
@@ -114,14 +114,12 @@ cat > ~/.maestro-env << 'EOF'
114
114
  # the agent operating system Environment Variables
115
115
  # Source this file in your shell profile
116
116
 
117
- # Required — Claude auth. THE FLEET DEFAULT is the subscription OAuth token:
118
- # run `claude setup-token` on any machine where you are logged in, paste the
119
- # result here. (An ANTHROPIC_API_KEY is the pay-per-token alternative; set one
120
- # or the other, never both.) `maestro setup` writes the same two lines into the
121
- # agent's .env; doctor reports what `claude auth status` says (it reports the
122
- # token is present, it does not validate it — `claude -p "ping"` proves it).
123
- export CLAUDE_CODE_OAUTH_TOKEN="paste-the-output-of-claude-setup-token"
124
- export MAESTRO_PREFER_SUBSCRIPTION_AUTH=1
117
+ # Required — Claude auth. THE FLEET POSTURE is a per-agent Console API key in a
118
+ # workspace with a hard spend cap (docs/guides/billing-console-keys.md). Do not
119
+ # set MAESTRO_PREFER_SUBSCRIPTION_AUTH or CLAUDE_CODE_OAUTH_TOKEN. `maestro
120
+ # setup` writes the key into the agent's .env; `maestro doctor` verifies it with
121
+ # a real API call.
122
+ export ANTHROPIC_API_KEY="paste-this-agent's-console-api-key"
125
123
 
126
124
  # Optional but recommended
127
125
  export SLACK_TOKEN="your-slack-token-here"
@@ -145,8 +143,8 @@ source ~/.maestro-env
145
143
  Verify:
146
144
 
147
145
  ```bash
148
- echo $CLAUDE_CODE_OAUTH_TOKEN | head -c 10
149
- # Should print the first 10 characters of the token
146
+ echo $ANTHROPIC_API_KEY | head -c 10
147
+ # Should print the first 10 characters of the key
150
148
  ```
151
149
 
152
150
  **Security note**: Set file permissions to owner-only:
@@ -356,7 +354,7 @@ echo "=============================="
356
354
  - [ ] Git, Node.js, Python, jq, yq installed
357
355
  - [ ] Claude Code CLI installed and verified
358
356
  - [ ] maestro repo cloned to ~/maestro
359
- - [ ] Claude auth: CLAUDE_CODE_OAUTH_TOKEN (+ MAESTRO_PREFER_SUBSCRIPTION_AUTH=1) in the agent's .env
357
+ - [ ] Claude auth: a per-agent Console API key (ANTHROPIC_API_KEY) in the agent's .env; `maestro doctor` shows it verified
360
358
  - [ ] Environment variables configured in ~/.maestro-env
361
359
  - [ ] File permissions set (chmod 600 on secrets)
362
360
  - [ ] MCP servers installed and configured
@@ -21,7 +21,9 @@ import {
21
21
  normalizeAccessLevel,
22
22
  } from "./tool-definitions.js";
23
23
  import { isOrgTool, executeOrgTool } from "./org/tool-surface.mjs";
24
- import { resolveClaudeBin, augmentedPath } from "./claude-bin.mjs";
24
+ import { buildSpawn, DEFAULT_COHORT_TIER_MAP } from "./runtime/adapter.mjs";
25
+ import { resolveSeatSpawn } from "./runtime/seat-engine.mjs";
26
+ import { cohortTierFor } from "./model-router/catalog.mjs";
25
27
 
26
28
  const AGENT_ROOT = process.env.AGENT_ROOT || process.cwd();
27
29
 
@@ -524,12 +526,26 @@ export async function executeLookup(toolName, input, agentRoot, opts = {}) {
524
526
  // unchanged. Fail-open: routing errors yield the historical `haiku` default.
525
527
  const routed = await resolveLookupModel(toolName, agentRoot, opts);
526
528
 
529
+ // The seat's engine, read per lookup (conformance row 36: a huddle's or a
530
+ // voice call's tools land here). A claude seat's buildSpawn input is exactly
531
+ // what it was; an engine-cohort seat runs `cli.mjs run` with its seat token
532
+ // and never falls back to claude. The engine is never handed a third-party
533
+ // model id the router chose for claude: the lane's own slot is used instead.
534
+ const seat = await (opts.resolveSeatSpawn || resolveSeatSpawn)(agentRoot);
535
+ const model = seat.engine === "cohort" && !cohortTierFor(routed.modelFlag, DEFAULT_COHORT_TIER_MAP)
536
+ ? LOOKUP_DEFAULT_MODEL
537
+ : routed.modelFlag;
538
+
527
539
  try {
528
540
  const { stdout } = await new Promise((resolve, reject) => {
529
- execFile(resolveClaudeBin(), ["--print", "--model", routed.modelFlag, "-p", prompt], {
541
+ // argv + env from the runtime adapter (lane "lookup"):
542
+ // --print --model <m> -p <prompt>, TERM=dumb, PATH augmented.
543
+ const spec = (opts.buildSpawn || buildSpawn)({ lane: "lookup", model, prompt, env: process.env, ...seat.fields });
544
+ if (!spec.ok) return reject(new Error(spec.error.message));
545
+ execFile(spec.bin, spec.argv, {
530
546
  timeout: LOOKUP_TIMEOUT_MS,
531
547
  maxBuffer: LOOKUP_MAX_BUFFER,
532
- env: { ...process.env, TERM: "dumb", PATH: augmentedPath() },
548
+ env: spec.env,
533
549
  cwd: agentRoot,
534
550
  }, (err, stdout, stderr) => {
535
551
  if (err) reject(err);
@@ -57,6 +57,144 @@ import { join, resolve, dirname } from "node:path";
57
57
  import { randomBytes } from "node:crypto";
58
58
 
59
59
  import { summariseRows, imputeUnmeasured } from "./cost/ledger-row.mjs";
60
+ import { quotaBand, quotaStatePath, readQuotaSnapshot } from "./org/quota.mjs";
61
+ import { load as yamlLoad } from "js-yaml";
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // cohort-metered seats (Cohort Compute, design §8.3)
65
+ // ---------------------------------------------------------------------------
66
+ //
67
+ // A seat whose model routing uses the cohort provider (or whose runtime engine
68
+ // is cohort) is metered by cohort-llm, not by this ledger. hq owns its limits —
69
+ // the seat band's 5h/7d/30d windows, credits and spend limits — and publishes
70
+ // them at GET /cohort/v1/quota. For such a seat the band comes from THAT
71
+ // reading (lib/org/quota.mjs, persisted by the quota client at
72
+ // state/cohort-llm/quota.json), never from summing local JSONL: the gateway's
73
+ // number is the one that refuses, so it is the one that must pace.
74
+ //
75
+ // utilization ≥ 75 % → band 80 (warn the supervisor)
76
+ // utilization ≥ 100 % → band 100 (degrade: self-directed work stops)
77
+ // canAdmit refused → band 100
78
+ // Spawn-level refusal is the pre-spawn gate's job (lib/runtime/adapter.mjs
79
+ // admitSpawn), which reads the same endpoint.
80
+
81
+ /** A persisted quota reading older than this is not trusted for banding. */
82
+ export const COHORT_QUOTA_MAX_AGE_MS = 10 * 60 * 1000;
83
+
84
+ function agentRootFor(deps) {
85
+ return resolve((deps && deps.agentRoot) || process.env.AGENT_ROOT || process.env.AGENT_DIR || process.cwd());
86
+ }
87
+
88
+ /** The catalog provider name cohort-llm rows carry (`cohort/<tier>`). */
89
+ const COHORT_PROVIDER = "cohort";
90
+
91
+ /** A ledger row billed by cohort-llm is metered by the quota, never summed here. */
92
+ export function isCohortBilledRow(r) {
93
+ return !!(r && typeof r === "object" && r.billed_by === "cohort-llm");
94
+ }
95
+
96
+ /**
97
+ * How much of a routing config's traffic cohort-llm serves. PURE.
98
+ * "none" no routed ref names the cohort provider
99
+ * "whole" every routed ref is cohort AND the Anthropic safety net is off
100
+ * "mixed" anything else that routes at least one ref to cohort
101
+ * Routed refs are every routing_policy chain member (aliases resolved) plus
102
+ * every alias value; the fallback is Anthropic unless fallback_to_anthropic is
103
+ * explicitly false (the router's own rule). An unresolvable member counts as
104
+ * non-cohort — the conservative direction, because "mixed" takes the worse of
105
+ * both bands. Parsed, never regex-scanned: a commented-out ref routes nothing.
106
+ * @param {object|null} cfg a parsed config/model-routing document
107
+ * @returns {"none"|"mixed"|"whole"}
108
+ */
109
+ export function routingCohortMode(cfg) {
110
+ if (!cfg || typeof cfg !== "object") return "none";
111
+ const aliases = cfg.aliases && typeof cfg.aliases === "object" && !Array.isArray(cfg.aliases) ? cfg.aliases : {};
112
+ const refs = [];
113
+ const add = (member, depth = 0) => {
114
+ if (Array.isArray(member)) { for (const m of member) add(m, depth); return; }
115
+ if (typeof member !== "string") return;
116
+ const s = member.trim();
117
+ if (!s || s === "rules") return;
118
+ if (s.includes("/")) { refs.push(s); return; }
119
+ if (depth < 8 && Object.prototype.hasOwnProperty.call(aliases, s)) { add(aliases[s], depth + 1); return; }
120
+ refs.push(`unresolved:${s}`);
121
+ };
122
+ for (const v of Object.values(aliases)) add(v);
123
+ for (const rule of Array.isArray(cfg.routing_policy) ? cfg.routing_policy : []) {
124
+ if (rule && typeof rule === "object") add(rule.chain);
125
+ }
126
+ const isCohort = (ref) => ref.split("/")[0].trim().toLowerCase() === COHORT_PROVIDER;
127
+ const cohortRefs = refs.filter(isCohort).length;
128
+ if (cohortRefs === 0) return "none";
129
+ const everyRefCohort = cohortRefs === refs.length;
130
+ return everyRefCohort && cfg.fallback_to_anthropic === false ? "whole" : "mixed";
131
+ }
132
+
133
+ /** Read and parse the seat's routing config the way lib/model-router.mjs finds it. null on absence or a parse error. */
134
+ function readRoutingConfigDoc(root, deps) {
135
+ if (deps && deps.routingConfig !== undefined) return deps.routingConfig;
136
+ const candidates = [
137
+ deps && deps.routingConfigPath,
138
+ process.env.MAESTRO_ROUTING_CONFIG,
139
+ join(root, "config", "model-routing.yaml"),
140
+ join(root, "config", "model-routing.json"),
141
+ ].filter(Boolean);
142
+ for (const p of candidates) {
143
+ try {
144
+ if (!existsSync(p)) continue;
145
+ const raw = readFileSync(p, "utf-8");
146
+ return p.endsWith(".json") ? JSON.parse(raw) : yamlLoad(raw);
147
+ } catch {
148
+ // The daemon falls back to the stock Anthropic CLI on an invalid config
149
+ // (dispatcher getRoutingConfig), so nothing routes to cohort: "none".
150
+ return null;
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+
156
+ /**
157
+ * How this seat is metered by cohort-llm: {mode, source}. `deps.cohortMetered`
158
+ * (boolean → whole/none) or `deps.cohortMetering` ("none"|"mixed"|"whole")
159
+ * override. Otherwise config/agent.json runtime.engine === "cohort" → whole;
160
+ * else the parsed routing config ({@link routingCohortMode}). Never throws.
161
+ */
162
+ export function cohortMetering(deps) {
163
+ if (deps && typeof deps.cohortMetering === "string" && ["none", "mixed", "whole"].includes(deps.cohortMetering)) {
164
+ return { mode: deps.cohortMetering, source: "override" };
165
+ }
166
+ if (deps && typeof deps.cohortMetered === "boolean") return { mode: deps.cohortMetered ? "whole" : "none", source: "override" };
167
+ const root = agentRootFor(deps);
168
+ try {
169
+ const agentJson = join(root, "config", "agent.json");
170
+ if (existsSync(agentJson)) {
171
+ const cfg = JSON.parse(readFileSync(agentJson, "utf-8"));
172
+ const engine = cfg && cfg.runtime && typeof cfg.runtime.engine === "string" ? cfg.runtime.engine.trim().toLowerCase() : "";
173
+ if (engine === "cohort") return { mode: "whole", source: "runtime.engine" };
174
+ }
175
+ } catch { /* unreadable agent.json → not cohort by this signal */ }
176
+ try {
177
+ return { mode: routingCohortMode(readRoutingConfigDoc(root, deps)), source: "model-routing" };
178
+ } catch {
179
+ return { mode: "none", source: "model-routing" };
180
+ }
181
+ }
182
+
183
+ /** Does cohort-llm serve ANY of this seat's traffic? (whole or mixed) */
184
+ export function isCohortMetered(deps) {
185
+ return cohortMetering(deps).mode !== "none";
186
+ }
187
+
188
+ /** The seat's latest trusted quota reading, or null. `deps.cohortQuota` overrides. */
189
+ function cohortQuotaFor(deps) {
190
+ if (deps && deps.cohortQuota !== undefined) return deps.cohortQuota;
191
+ return readQuotaSnapshot(quotaStatePath(agentRootFor(deps)), clock(deps)(), COHORT_QUOTA_MAX_AGE_MS);
192
+ }
193
+
194
+ const microsToUsd = (m) => {
195
+ const n = Number(m);
196
+ return Number.isFinite(n) ? +(n / 1e6).toFixed(6) : null;
197
+ };
60
198
 
61
199
  // ---------------------------------------------------------------------------
62
200
  // Config
@@ -305,7 +443,7 @@ function sumToday(deps) {
305
443
  let malformed = 0;
306
444
  for (const line of body.split("\n")) {
307
445
  if (!line.trim()) continue;
308
- try { rows.push(JSON.parse(line)); } catch { malformed += 1; }
446
+ try { const r = JSON.parse(line); if (!isCohortBilledRow(r)) rows.push(r); } catch { malformed += 1; }
309
447
  }
310
448
 
311
449
  const summary = summariseRows(rows);
@@ -369,7 +507,7 @@ function monthMeanSessionUsd(deps) {
369
507
  const rows = [];
370
508
  for (const line of body.split("\n")) {
371
509
  if (!line.trim()) continue;
372
- try { rows.push(JSON.parse(line)); } catch { /* skip */ }
510
+ try { const r = JSON.parse(line); if (!isCohortBilledRow(r)) rows.push(r); } catch { /* skip */ }
373
511
  }
374
512
  const s = summariseRows(rows);
375
513
  usd += s.measuredUsd;
@@ -408,7 +546,7 @@ export function monthToDate(deps) {
408
546
  const rows = [];
409
547
  for (const line of body.split("\n")) {
410
548
  if (!line.trim()) continue;
411
- try { rows.push(JSON.parse(line)); } catch { /* skip */ }
549
+ try { const r = JSON.parse(line); if (!isCohortBilledRow(r)) rows.push(r); } catch { /* skip */ }
412
550
  }
413
551
  const summary = summariseRows(rows);
414
552
  spentUSD += summary.measuredUsd + imputeUnmeasured(summary, { fallbackPerSessionUsd }).imputedUsd;
@@ -672,11 +810,47 @@ export function dailyAllowance(deps) {
672
810
  * the log every tick. `deps.subscriptionAuth` overrides for tests.
673
811
  */
674
812
  export function isSubscriptionAuth(deps) {
813
+ // A wholly cohort-served seat HAS a meter (cohort-llm), whatever its Claude
814
+ // auth says: it must never be waved through as a flat-fee subscription. On a
815
+ // MIXED seat this answers for the Anthropic half only (the local ledger); the
816
+ // cohort half is banded from the quota regardless (see dailyStatus).
817
+ if (cohortMetering(deps).mode === "whole") return false;
675
818
  if (deps && typeof deps.subscriptionAuth === "boolean") return deps.subscriptionAuth;
676
819
  return process.env.MAESTRO_PREFER_SUBSCRIPTION_AUTH === "1" && !process.env.ANTHROPIC_API_KEY;
677
820
  }
678
821
 
822
+ /**
823
+ * The seat's budget status. A wholly cohort-served seat is banded from its
824
+ * /cohort/v1/quota reading alone. A MIXED seat (some routes cohort, some — or
825
+ * the safety net — Anthropic) is banded on the WORSE of the local ledger (its
826
+ * Anthropic traffic; cohort-billed rows are excluded) and the cohort quota, so
827
+ * neither meter can silently stop pacing the traffic it owns.
828
+ */
679
829
  export function dailyStatus(deps) {
830
+ const metering = cohortMetering(deps);
831
+ if (metering.mode === "whole") return cohortDailyStatus(deps);
832
+ const local = ledgerDailyStatus(deps);
833
+ if (metering.mode === "mixed") return worseOfLedgerAndCohort(local, cohortDailyStatus(deps));
834
+ return local;
835
+ }
836
+
837
+ /** Merge a mixed seat's two readings: the ledger status, with the cohort band when it is worse. */
838
+ function worseOfLedgerAndCohort(local, cohort) {
839
+ const out = { ...local, cohortMetering: "mixed", cohort: cohort.cohort, bandSource: "ledger" };
840
+ out.degradations = [...(local.degradations || []), ...(cohort.degradations || [])];
841
+ if (cohort.band > local.band) {
842
+ const posture = postureForBand(cohort.band);
843
+ out.band = cohort.band;
844
+ out.mode = posture.mode;
845
+ out.posture = posture;
846
+ out.essentialOnly = cohort.band >= 125;
847
+ out.pct = Math.max(Number(local.pct) || 0, Number(cohort.pct) || 0);
848
+ out.bandSource = "cohort-quota";
849
+ }
850
+ return out;
851
+ }
852
+
853
+ function ledgerDailyStatus(deps) {
680
854
  const subscriptionAuth = isSubscriptionAuth(deps);
681
855
  const iterationCap = cfgIterationCap(deps);
682
856
  const today = sumToday(deps);
@@ -803,6 +977,77 @@ export function dailyStatus(deps) {
803
977
  };
804
978
  }
805
979
 
980
+ /**
981
+ * dailyStatus for a cohort-metered seat: the same shape, banded from the
982
+ * seat's /cohort/v1/quota reading. Fail-open (band 0) with a loud degradation
983
+ * when no fresh reading exists — the pre-spawn gate still refuses a spawn the
984
+ * gateway would refuse.
985
+ */
986
+ function cohortDailyStatus(deps) {
987
+ const quota = cohortQuotaFor(deps);
988
+ const q = quotaBand(quota);
989
+ const now = clock(deps)();
990
+ const degradations = [];
991
+ let band = 0;
992
+ if (q.band === "unknown") {
993
+ degradations.push(
994
+ "COHORT QUOTA UNKNOWN: this seat is metered by cohort-llm but has no fresh /cohort/v1/quota reading " +
995
+ `(state/cohort-llm/quota.json missing or older than ${COHORT_QUOTA_MAX_AGE_MS / 60000} min) — band 0; ` +
996
+ "the pre-spawn quota gate is the barrier until a reading lands."
997
+ );
998
+ } else if (q.band === "at") {
999
+ band = 100;
1000
+ } else if (q.band === "approaching") {
1001
+ band = 80;
1002
+ }
1003
+ for (const d of degradations) {
1004
+ try { console.warn(`[budget-guard] degraded: ${d}`); } catch { /* never throw */ }
1005
+ }
1006
+ const pct = q.utilization === null ? (q.canAdmit === false ? 100 : 0) : q.utilization * 100;
1007
+ const month = quota && Array.isArray(quota.windows) ? quota.windows.find((w) => w.kind === "30d") : null;
1008
+ const posture = postureForBand(band);
1009
+ const capUSD = microsToUsd(q.limitMicros) ?? 0;
1010
+ const env = readSeatEnvelope(deps);
1011
+ return {
1012
+ spentUSD: microsToUsd(q.consumedMicros) ?? 0,
1013
+ capUSD,
1014
+ bandingCapUSD: capUSD,
1015
+ pct: +pct.toFixed(2),
1016
+ dayPct: null,
1017
+ monthPct: month && month.utilization !== null ? +(month.utilization * 100).toFixed(2) : 0,
1018
+ band,
1019
+ mode: posture.mode,
1020
+ posture,
1021
+ essentialOnly: band >= 125,
1022
+ sessions: 0,
1023
+ iterationCap: cfgIterationCap(deps),
1024
+ date: todayUtc(deps),
1025
+ funded: q.band !== "unknown",
1026
+ capSource: "cohort-quota",
1027
+ configuredCapUSD: null,
1028
+ envelope: {
1029
+ seatBudgetCents: null,
1030
+ monthUSD: month ? microsToUsd(month.limitMicros) : null,
1031
+ source: "cohort-quota",
1032
+ supervisorMemberId: env.supervisorMemberId,
1033
+ },
1034
+ month: {
1035
+ spentUSD: month ? (microsToUsd(month.consumedMicros) ?? 0) : 0,
1036
+ pct: month && month.utilization !== null ? +(month.utilization * 100).toFixed(2) : null,
1037
+ daysInMonth: daysInUtcMonth(now),
1038
+ dayOfMonth: new Date(now).getUTCDate(),
1039
+ },
1040
+ supervisorMemberId: env.supervisorMemberId || null,
1041
+ measuredUSD: 0,
1042
+ imputedUSD: 0,
1043
+ unmeasuredSessions: 0,
1044
+ nonLlmRows: 0,
1045
+ blind: false,
1046
+ degradations,
1047
+ cohort: { band: q.band, utilization: q.utilization, window: q.window, resetsAt: q.resetsAt, canAdmit: q.canAdmit, asOf: quota ? quota.asOf || null : null },
1048
+ };
1049
+ }
1050
+
806
1051
  /**
807
1052
  * The lowest binding cap this period has run under, and the envelope it belongs
808
1053
  * to. See the LATCH comment in {@link dailyStatus} for why banding may not use a
@@ -1140,6 +1385,7 @@ export function rollingWindowUsage(deps) {
1140
1385
  if (!line.trim()) continue;
1141
1386
  let r;
1142
1387
  try { r = JSON.parse(line); } catch { continue; }
1388
+ if (isCohortBilledRow(r)) continue; // metered by the cohort quota, not these windows
1143
1389
  const at = r && r.ts ? Date.parse(r.ts) : NaN;
1144
1390
  if (!Number.isFinite(at)) continue;
1145
1391
  const dt = now - at;
@@ -1159,6 +1405,36 @@ export function rollingWindowUsage(deps) {
1159
1405
  * band is the WORSE of the 5h and weekly horizons.
1160
1406
  */
1161
1407
  export function windowBand(deps) {
1408
+ const metering = cohortMetering(deps);
1409
+ if (metering.mode === "none") return ledgerWindowBand(deps);
1410
+ // cohort-llm's own windows (5h/7d/30d) are the pacing signal for its traffic.
1411
+ const q = quotaBand(cohortQuotaFor(deps));
1412
+ const cohort = {
1413
+ band: q.band === "unknown" ? "ok" : q.band,
1414
+ source: "cohort-quota",
1415
+ active: q.band !== "unknown",
1416
+ utilization: q.utilization,
1417
+ window: q.window,
1418
+ resetsAt: q.resetsAt,
1419
+ canAdmit: q.canAdmit,
1420
+ window5hTokens: 0, window5hSessions: 0, week7dTokens: 0, week7dSessions: 0,
1421
+ cap5h: null, capWeek: null,
1422
+ };
1423
+ if (metering.mode === "whole") return cohort;
1424
+ // Mixed: the worse of the local token windows (Anthropic traffic) and the quota.
1425
+ const local = ledgerWindowBand(deps);
1426
+ const rank = { ok: 0, approaching: 1, at: 2 };
1427
+ const worse = rank[cohort.band] > rank[local.band] ? cohort.band : local.band;
1428
+ return {
1429
+ ...local,
1430
+ band: worse,
1431
+ source: rank[cohort.band] > rank[local.band] ? "cohort-quota" : "ledger",
1432
+ active: local.active || cohort.active,
1433
+ cohort: { band: cohort.band, active: cohort.active, utilization: q.utilization, window: q.window, resetsAt: q.resetsAt, canAdmit: q.canAdmit },
1434
+ };
1435
+ }
1436
+
1437
+ function ledgerWindowBand(deps) {
1162
1438
  const u = rollingWindowUsage(deps);
1163
1439
  // Caps come from deps first (tests / a per-seat override), else the env consts.
1164
1440
  const cap5 = deps && deps.cap5h != null ? deps.cap5h : WINDOW_5H_TOKEN_CAP;
@@ -448,7 +448,9 @@ export class BaseAdapter {
448
448
  * Order (gaps-product-quality P0-3/P0-5):
449
449
  * 1. send-gate — the hook-bus `beforeSend` cancellable chain AND the shared
450
450
  * lib/comms/send-gate.screenOutbound. EITHER vetoing blocks the send.
451
- * Now EVERY adapter send passes the gate, not just Gmail.
451
+ * Now EVERY adapter send passes the gate, not just Gmail. screenOutbound
452
+ * runs screenScaffold first, so a raw model turn is caught here too —
453
+ * coverage is real, just not visible at this call site.
452
454
  * 2. pair guard — a per-conversation sliding window so two agents in one
453
455
  * channel can't reply-storm.
454
456
  * 3. _send — the adapter transport.
@@ -33,7 +33,7 @@
33
33
 
34
34
  /**
35
35
  * @typedef {"message"|"reaction"|"channel_cc"|"voice_note"|"call"
36
- * |"mention"|"thread_reply"|"task_assigned"|"task_comment"
36
+ * |"mention"|"broadcast"|"thread_reply"|"task_assigned"|"task_comment"
37
37
  * |"file_comment"|"approval"|"decision"|"escalation"|"handoff"
38
38
  * |"email"} EventKind
39
39
  *
@@ -73,6 +73,7 @@ export const EVENT_KINDS = Object.freeze([
73
73
  "call",
74
74
  // Work surfaces (org inbound).
75
75
  "mention",
76
+ "broadcast",
76
77
  "thread_reply",
77
78
  "task_assigned",
78
79
  "task_comment",
@@ -310,6 +310,14 @@ export function eventToInboxItem(ev) {
310
310
  item.scope_id = sanitizeYamlField(scopeId);
311
311
  }
312
312
 
313
+ // A roll-call's respondent bound: how many AI members the org has, so the
314
+ // composer can be told it is "one of up to N" answering the same message.
315
+ // A number, never interpolated; absent for every other surface.
316
+ const respondents = cohort ? cohort.respondents : null;
317
+ if (Number.isFinite(respondents) && respondents > 0) {
318
+ item.respondents = Math.floor(respondents);
319
+ }
320
+
313
321
  // Carry attachments through when present (voice notes, media). The legacy
314
322
  // YAML writer (scripts/poller/utils.mjs) renders these when shaped as
315
323
  // { id, name, mimetype, size }; adapters that produce contract-style
@@ -7,12 +7,11 @@
7
7
  * fails with ENOENT — even though the operator's interactive shell finds
8
8
  * it without trouble.
9
9
  *
10
- * Caller sites:
11
- * - scripts/daemon/dispatcher.mjs (reactive: replies to Slack/Gmail/etc)
12
- * - scripts/daemon/responder.mjs (quick replies + holding messages)
13
- * - scripts/daemon/cadence-consumer.mjs (scheduled cadence ticks)
14
- * - scripts/poller/intra-session-check.mjs
15
- * - any other module that spawns claude from a non-interactive context
10
+ * Caller: lib/runtime/adapter.mjs, the runtime adapter seam, which owns the
11
+ * binary, argv and env of every JS lane that starts `claude` (it lists the 17
12
+ * lanes). Spawn sites ask the adapter, not this module. The remaining direct
13
+ * readers only resolve the binary without spawning it (the daemon's
14
+ * `claudeAvailable()` presence check, `global-setup`'s CLI probe).
16
15
  */
17
16
 
18
17
  import { existsSync, statSync } from "node:fs";