@cohortapp/agent-sdk 2.17.0 → 2.18.5

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 (531) 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-slots.mjs +234 -0
  23. package/lib/collective/capture.mjs +8 -6
  24. package/lib/collective/config.mjs +2 -0
  25. package/lib/collective/global-config.mjs +63 -1
  26. package/lib/collective/loop-guard.mjs +155 -0
  27. package/lib/collective/presence.mjs +142 -5
  28. package/lib/comms/send-gate.mjs +559 -1
  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/failover.mjs +67 -16
  138. package/lib/model-router/llm-task.mjs +39 -3
  139. package/lib/model-router/resolve.mjs +89 -3
  140. package/lib/model-router/spawn.mjs +46 -47
  141. package/lib/model-router/taxonomy.mjs +126 -4
  142. package/lib/org/cost-sync.mjs +141 -11
  143. package/lib/org/inbound/broadcast.mjs +289 -0
  144. package/lib/org/inbound/collective.mjs +375 -0
  145. package/lib/org/inbound/directedness.mjs +96 -8
  146. package/lib/org/inbound/facts.mjs +78 -2
  147. package/lib/org/inbound/project.mjs +22 -0
  148. package/lib/org/inbound/surfaces.mjs +14 -0
  149. package/lib/org/llm-token.mjs +879 -0
  150. package/lib/org/mesh.mjs +61 -0
  151. package/lib/org/messaging.mjs +3 -1
  152. package/lib/org/protocol.checksum +1 -1
  153. package/lib/org/protocol.mjs +15 -0
  154. package/lib/org/quota.mjs +520 -0
  155. package/lib/org/tool-surface.mjs +104 -16
  156. package/lib/org/ui-parity.mjs +16 -1
  157. package/lib/org/work-ledger.mjs +37 -6
  158. package/lib/rate-guard.mjs +114 -1
  159. package/lib/resource-governor.mjs +41 -6
  160. package/lib/runtime/adapter.mjs +833 -0
  161. package/lib/runtime/child-env.mjs +191 -0
  162. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  163. package/lib/runtime/seat-engine.mjs +162 -0
  164. package/lib/session/ask-ledger.mjs +271 -0
  165. package/lib/session/current-work.mjs +676 -0
  166. package/lib/session/feed-core.mjs +40 -3
  167. package/lib/session/launch-args.mjs +56 -4
  168. package/lib/session/status-summary.mjs +26 -9
  169. package/lib/session/upgrade-notice.mjs +42 -0
  170. package/lib/setup/claude-probe.mjs +117 -13
  171. package/lib/setup/enrich.mjs +13 -10
  172. package/lib/setup/sections/model.mjs +39 -13
  173. package/lib/telemetry/collect.mjs +229 -11
  174. package/lib/upgrade/ignored-drift.mjs +105 -0
  175. package/lib/voice/post-call-brief.mjs +30 -17
  176. package/package.json +13 -3
  177. package/plugins/maestro-skills/skills/board-work.md +5 -0
  178. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  179. package/plugins/maestro-skills/skills/main-session.md +18 -7
  180. package/scaffold/config/collective.yaml +7 -0
  181. package/scripts/ci/check-durable-write-seam.mjs +3 -1
  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 +142 -19
  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 +51 -37
  194. package/scripts/daemon/deliver.mjs +30 -1
  195. package/scripts/daemon/dispatcher.mjs +595 -149
  196. package/scripts/daemon/health.mjs +14 -1
  197. package/scripts/daemon/maestro-daemon.mjs +11 -0
  198. package/scripts/daemon/prompt-builder.mjs +24 -0
  199. package/scripts/daemon/responder.mjs +246 -79
  200. package/scripts/daemon/sdk-version.mjs +98 -16
  201. package/scripts/eval/probe-gateway.mjs +635 -0
  202. package/scripts/eval/replay/extract.mjs +270 -0
  203. package/scripts/eval/replay/grade.mjs +260 -0
  204. package/scripts/eval/replay/lib/config.mjs +50 -0
  205. package/scripts/eval/replay/lib/effects.mjs +65 -0
  206. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  207. package/scripts/eval/replay/lib/judge.mjs +72 -0
  208. package/scripts/eval/replay/lib/redact.mjs +136 -0
  209. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  210. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  211. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  212. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  213. package/scripts/eval/replay/report.mjs +185 -0
  214. package/scripts/eval/replay/run.mjs +404 -0
  215. package/scripts/fleet/rollout.mjs +1151 -0
  216. package/scripts/hooks/pre-send-audit.sh +36 -245
  217. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  218. package/scripts/hooks/validate-state-yaml.sh +190 -0
  219. package/scripts/huddle/huddle-llm.mjs +361 -0
  220. package/scripts/huddle/huddle-server.mjs +46 -121
  221. package/scripts/local-triggers/autoupdate.sh +465 -81
  222. package/scripts/local-triggers/run-trigger.sh +13 -0
  223. package/scripts/maintenance/pin-integrity.mjs +364 -0
  224. package/scripts/poll-slack-events.sh +41 -9
  225. package/scripts/poller/slack-socket-mode.mjs +28 -3
  226. package/scripts/session/supervisor.mjs +80 -13
  227. package/scripts/spawn-session.sh +13 -0
  228. package/bin/maestro.test.mjs +0 -1574
  229. package/lib/action-executor.test.mjs +0 -871
  230. package/lib/archetype.test.mjs +0 -132
  231. package/lib/assurance/plan-note.test.mjs +0 -234
  232. package/lib/assurance/room-budget.test.mjs +0 -486
  233. package/lib/assurance/tier.test.mjs +0 -174
  234. package/lib/autonomy.test.mjs +0 -66
  235. package/lib/backlog.test.mjs +0 -302
  236. package/lib/backup/policy.test.mjs +0 -305
  237. package/lib/budget-escalate.test.mjs +0 -232
  238. package/lib/budget-guard.envelope.test.mjs +0 -476
  239. package/lib/budget-guard.test.mjs +0 -427
  240. package/lib/cadence-bus-requeue.test.mjs +0 -83
  241. package/lib/cadence-bus-schedule.test.mjs +0 -194
  242. package/lib/cadence-bus.test.mjs +0 -720
  243. package/lib/cadences.test.mjs +0 -230
  244. package/lib/capability/inventory.test.mjs +0 -232
  245. package/lib/capability.test.mjs +0 -78
  246. package/lib/channels/base-adapter.test.mjs +0 -590
  247. package/lib/channels/channels.test.mjs +0 -371
  248. package/lib/channels/contract.test.mjs +0 -162
  249. package/lib/channels/inbox-item.test.mjs +0 -368
  250. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  251. package/lib/channels/pairing.test.mjs +0 -270
  252. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  253. package/lib/channels/slack-adapter.test.mjs +0 -212
  254. package/lib/channels/telegram-adapter.test.mjs +0 -306
  255. package/lib/channels/voice/adapter.test.mjs +0 -278
  256. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  257. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  258. package/lib/charter.test.mjs +0 -89
  259. package/lib/claude-bin.test.mjs +0 -131
  260. package/lib/cli/board.test.mjs +0 -227
  261. package/lib/cli/design.test.mjs +0 -270
  262. package/lib/cli/doctor-checks.test.mjs +0 -336
  263. package/lib/cli/global-setup-extras.test.mjs +0 -462
  264. package/lib/cli/inbox.test.mjs +0 -230
  265. package/lib/cli/session-ack.test.mjs +0 -63
  266. package/lib/cli/session.test.mjs +0 -613
  267. package/lib/collective/capture.test.mjs +0 -121
  268. package/lib/collective/cards.test.mjs +0 -114
  269. package/lib/collective/config.test.mjs +0 -123
  270. package/lib/collective/global-config.test.mjs +0 -220
  271. package/lib/collective/global-skills.test.mjs +0 -126
  272. package/lib/collective/presence.test.mjs +0 -95
  273. package/lib/collective/recall.test.mjs +0 -116
  274. package/lib/collective/vendor-skills.test.mjs +0 -306
  275. package/lib/comms/send-gate.test.mjs +0 -770
  276. package/lib/comms.test.mjs +0 -41
  277. package/lib/context/budget.test.mjs +0 -252
  278. package/lib/context/history-scope.test.mjs +0 -79
  279. package/lib/cost/ledger-row.test.mjs +0 -183
  280. package/lib/design/design-md.test.mjs +0 -318
  281. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  282. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  283. package/lib/design/fixtures/foundation.json +0 -133
  284. package/lib/design/refresh-gate.test.mjs +0 -144
  285. package/lib/design/write.test.mjs +0 -241
  286. package/lib/diagnostics/alerts.test.mjs +0 -318
  287. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  288. package/lib/diagnostics/counters.test.mjs +0 -206
  289. package/lib/diagnostics/events.test.mjs +0 -290
  290. package/lib/diagnostics/otel.test.mjs +0 -196
  291. package/lib/diagnostics/trace.test.mjs +0 -251
  292. package/lib/env-compat.test.mjs +0 -104
  293. package/lib/execution/disposition.test.mjs +0 -553
  294. package/lib/execution/drive.test.mjs +0 -270
  295. package/lib/execution/effects.test.mjs +0 -344
  296. package/lib/execution/intake.test.mjs +0 -389
  297. package/lib/execution/journal.test.mjs +0 -261
  298. package/lib/execution/match.test.mjs +0 -235
  299. package/lib/execution/pipeline.test.mjs +0 -392
  300. package/lib/execution/route.test.mjs +0 -186
  301. package/lib/execution/surface-policy.test.mjs +0 -162
  302. package/lib/fs-atomic.test.mjs +0 -72
  303. package/lib/fs-ownership.test.mjs +0 -158
  304. package/lib/goals/admission.test.mjs +0 -164
  305. package/lib/goals/classify.test.mjs +0 -167
  306. package/lib/goals/collaborate.test.mjs +0 -336
  307. package/lib/goals/gaps.test.mjs +0 -284
  308. package/lib/goals/loop.test.mjs +0 -845
  309. package/lib/hooks/bus.test.mjs +0 -387
  310. package/lib/identity/persona.test.mjs +0 -142
  311. package/lib/kpi-sensors.test.mjs +0 -278
  312. package/lib/kpi.test.mjs +0 -244
  313. package/lib/learning/config.test.mjs +0 -75
  314. package/lib/learning/counters.test.mjs +0 -69
  315. package/lib/learning/curator-consolidate.test.mjs +0 -238
  316. package/lib/learning/curator.test.mjs +0 -106
  317. package/lib/learning/reflect.test.mjs +0 -0
  318. package/lib/learning/session-index.test.mjs +0 -125
  319. package/lib/learning/skill-writer.test.mjs +0 -210
  320. package/lib/mandate/audit.test.mjs +0 -195
  321. package/lib/mandate/contract.test.mjs +0 -185
  322. package/lib/mandate/derive.test.mjs +0 -274
  323. package/lib/mandate/model.test.mjs +0 -164
  324. package/lib/mandate/refresh.test.mjs +0 -389
  325. package/lib/mcp/server.test.mjs +0 -426
  326. package/lib/model-router/auth-profiles.test.mjs +0 -580
  327. package/lib/model-router/catalog.test.mjs +0 -385
  328. package/lib/model-router/economics.test.mjs +0 -438
  329. package/lib/model-router/failover.test.mjs +0 -439
  330. package/lib/model-router/health.test.mjs +0 -338
  331. package/lib/model-router/integration-coverage.test.mjs +0 -831
  332. package/lib/model-router/integration.test.mjs +0 -564
  333. package/lib/model-router/ledger.test.mjs +0 -415
  334. package/lib/model-router/llm-task.test.mjs +0 -392
  335. package/lib/model-router/org-credentials.test.mjs +0 -265
  336. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  337. package/lib/model-router/reconcile.test.mjs +0 -316
  338. package/lib/model-router/repair.test.mjs +0 -180
  339. package/lib/model-router/spawn.test.mjs +0 -446
  340. package/lib/model-router/taxonomy.test.mjs +0 -410
  341. package/lib/model-router.test.mjs +0 -1207
  342. package/lib/org/activity.test.mjs +0 -134
  343. package/lib/org/approvals.test.mjs +0 -216
  344. package/lib/org/awareness.test.mjs +0 -159
  345. package/lib/org/board-mine-cache.test.mjs +0 -53
  346. package/lib/org/board.test.mjs +0 -187
  347. package/lib/org/bootstrap-context.test.mjs +0 -153
  348. package/lib/org/client.test.mjs +0 -1206
  349. package/lib/org/cohort-client.test.mjs +0 -126
  350. package/lib/org/cost-sync.test.mjs +0 -153
  351. package/lib/org/doctor.test.mjs +0 -346
  352. package/lib/org/engagement-ledger.test.mjs +0 -112
  353. package/lib/org/engagement.test.mjs +0 -739
  354. package/lib/org/handoff.test.mjs +0 -269
  355. package/lib/org/inbound/directedness.test.mjs +0 -668
  356. package/lib/org/inbound/facts.test.mjs +0 -471
  357. package/lib/org/inbound/hydrate.test.mjs +0 -908
  358. package/lib/org/inbound/index.test.mjs +0 -429
  359. package/lib/org/inbound/project.test.mjs +0 -287
  360. package/lib/org/integration-tools.test.mjs +0 -160
  361. package/lib/org/keys.test.mjs +0 -92
  362. package/lib/org/knowledge.test.mjs +0 -326
  363. package/lib/org/leases.test.mjs +0 -235
  364. package/lib/org/mesh-directives.test.mjs +0 -110
  365. package/lib/org/mesh-integration.test.mjs +0 -127
  366. package/lib/org/mesh.test.mjs +0 -400
  367. package/lib/org/messaging.test.mjs +0 -471
  368. package/lib/org/param-contract.test.mjs +0 -477
  369. package/lib/org/policy.test.mjs +0 -237
  370. package/lib/org/protocol.checksum.test.mjs +0 -90
  371. package/lib/org/protocol.test.mjs +0 -323
  372. package/lib/org/push.test.mjs +0 -792
  373. package/lib/org/registry.test.mjs +0 -100
  374. package/lib/org/resource-tools.test.mjs +0 -361
  375. package/lib/org/tool-access.test.mjs +0 -144
  376. package/lib/org/tool-surface-integration.test.mjs +0 -120
  377. package/lib/org/tool-surface.test.mjs +0 -1268
  378. package/lib/org/typing.test.mjs +0 -291
  379. package/lib/org/ui-parity.test.mjs +0 -560
  380. package/lib/org/verify.test.mjs +0 -194
  381. package/lib/org/work-ledger.test.mjs +0 -273
  382. package/lib/plan/adoption-e2e.test.mjs +0 -366
  383. package/lib/plan/budget-enforcement.test.mjs +0 -400
  384. package/lib/plan/compile.test.mjs +0 -382
  385. package/lib/plan/emit.test.mjs +0 -269
  386. package/lib/plan/explain.test.mjs +0 -188
  387. package/lib/prompts/parallelism.test.mjs +0 -177
  388. package/lib/rag/rag.test.mjs +0 -505
  389. package/lib/rate-guard.test.mjs +0 -272
  390. package/lib/reactive-gate.test.mjs +0 -57
  391. package/lib/render.test.mjs +0 -68
  392. package/lib/resource-governor.test.mjs +0 -488
  393. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  394. package/lib/scheduling/jitter.test.mjs +0 -140
  395. package/lib/secrets/broker.test.mjs +0 -280
  396. package/lib/secrets/providers.test.mjs +0 -274
  397. package/lib/security/audit-engine.test.mjs +0 -424
  398. package/lib/security/coerce-args.test.mjs +0 -281
  399. package/lib/security/dangerous-tools.test.mjs +0 -68
  400. package/lib/security/external-content.test.mjs +0 -84
  401. package/lib/security/redact.test.mjs +0 -441
  402. package/lib/security/secret-equal.test.mjs +0 -55
  403. package/lib/session/config.test.mjs +0 -92
  404. package/lib/session/feed-core.test.mjs +0 -198
  405. package/lib/session/first-run.test.mjs +0 -121
  406. package/lib/session/frontdoor.test.mjs +0 -205
  407. package/lib/session/handoffs.test.mjs +0 -183
  408. package/lib/session/identity.test.mjs +0 -180
  409. package/lib/session/inbox-claims.test.mjs +0 -286
  410. package/lib/session/launch-args.test.mjs +0 -157
  411. package/lib/session/liveness.test.mjs +0 -100
  412. package/lib/session/status-summary.test.mjs +0 -118
  413. package/lib/session-permissions.test.mjs +0 -120
  414. package/lib/setup/claude-probe.test.mjs +0 -187
  415. package/lib/setup/completeness.test.mjs +0 -110
  416. package/lib/setup/context-pack.test.mjs +0 -89
  417. package/lib/setup/enrich.test.mjs +0 -115
  418. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  419. package/lib/setup/integration.test.mjs +0 -162
  420. package/lib/setup/io.test.mjs +0 -77
  421. package/lib/setup/runner.test.mjs +0 -132
  422. package/lib/setup/sections/identity.test.mjs +0 -234
  423. package/lib/setup/sections/inventory.test.mjs +0 -198
  424. package/lib/setup/sections/learning.test.mjs +0 -81
  425. package/lib/setup/sections/mandate.test.mjs +0 -388
  426. package/lib/setup/sections/messaging.test.mjs +0 -127
  427. package/lib/setup/sections/model.test.mjs +0 -240
  428. package/lib/setup/sections/org.test.mjs +0 -346
  429. package/lib/setup/sections/orgmail.test.mjs +0 -118
  430. package/lib/setup/sections/recovery.test.mjs +0 -98
  431. package/lib/setup/sections/subagents.test.mjs +0 -429
  432. package/lib/setup/sections/verify.test.mjs +0 -175
  433. package/lib/setup/sot.test.mjs +0 -81
  434. package/lib/setup/state.test.mjs +0 -115
  435. package/lib/singleton.test.mjs +0 -151
  436. package/lib/subagents/cli.test.mjs +0 -389
  437. package/lib/subagents/client.test.mjs +0 -309
  438. package/lib/subagents/gap.test.mjs +0 -234
  439. package/lib/subagents/lock.test.mjs +0 -248
  440. package/lib/subagents/manifest.test.mjs +0 -175
  441. package/lib/subagents/refs.test.mjs +0 -204
  442. package/lib/subagents/resolve.test.mjs +0 -422
  443. package/lib/subagents/schema.test.mjs +0 -328
  444. package/lib/telemetry/alerts.test.mjs +0 -109
  445. package/lib/telemetry/collect.test.mjs +0 -1274
  446. package/lib/tool-definitions-integration.test.mjs +0 -83
  447. package/lib/tool-definitions.test.mjs +0 -437
  448. package/lib/upgrade/global-refresh.test.mjs +0 -65
  449. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  450. package/lib/upgrade/post-steps.test.mjs +0 -200
  451. package/lib/upgrade/verify.test.mjs +0 -164
  452. package/lib/util/fetch-timeout.test.mjs +0 -202
  453. package/lib/util/reconnect.test.mjs +0 -369
  454. package/lib/util/unhandled.test.mjs +0 -216
  455. package/lib/voice/outbound.test.mjs +0 -69
  456. package/lib/voice/session-rotation.test.mjs +0 -114
  457. package/lib/voice/stt.test.mjs +0 -226
  458. package/lib/voice/voice.test.mjs +0 -990
  459. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  460. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  461. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  462. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  463. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  464. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  465. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  466. package/scripts/ci/check.test.mjs +0 -194
  467. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  468. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  469. package/scripts/collective/hook-runner.test.mjs +0 -173
  470. package/scripts/cost/fleet-digest.test.mjs +0 -207
  471. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  472. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  473. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  474. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  475. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  476. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  477. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  478. package/scripts/daemon/assurance.test.mjs +0 -1791
  479. package/scripts/daemon/board-mirror.test.mjs +0 -165
  480. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  481. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  482. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  483. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  484. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  485. package/scripts/daemon/classifier.test.mjs +0 -266
  486. package/scripts/daemon/classify-kind.test.mjs +0 -40
  487. package/scripts/daemon/context-compiler.test.mjs +0 -406
  488. package/scripts/daemon/deliver.test.mjs +0 -564
  489. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  490. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  491. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  492. package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
  493. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  494. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  495. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  496. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  497. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  498. package/scripts/daemon/integration.test.mjs +0 -149
  499. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  500. package/scripts/daemon/lib/session-router.test.mjs +0 -554
  501. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  502. package/scripts/daemon/prompt-builder.test.mjs +0 -556
  503. package/scripts/daemon/responder-cost.test.mjs +0 -68
  504. package/scripts/daemon/responder-history.test.mjs +0 -221
  505. package/scripts/daemon/sdk-version.test.mjs +0 -31
  506. package/scripts/daemon/session-lock.test.mjs +0 -252
  507. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  508. package/scripts/daemon/typing-registry.test.mjs +0 -102
  509. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  510. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  511. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  512. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  513. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  514. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  515. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  516. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  517. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  518. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  519. package/scripts/poller-launchd/install.test.mjs +0 -243
  520. package/scripts/restore-from-backup.test.mjs +0 -181
  521. package/scripts/session/feed.test.mjs +0 -196
  522. package/scripts/session/supervisor-sh.test.mjs +0 -218
  523. package/scripts/session/supervisor.test.mjs +0 -482
  524. package/scripts/setup/configure-macos.test.mjs +0 -306
  525. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  526. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  527. package/scripts/setup/generate-capability.test.mjs +0 -134
  528. package/scripts/setup/init-agent.test.mjs +0 -370
  529. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  530. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  531. 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";