@cohortapp/agent-sdk 2.16.0 → 2.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +23 -7
  3. package/README.md +1 -0
  4. package/bin/maestro.mjs +62 -0
  5. package/docs/guides/billing-console-keys.md +60 -0
  6. package/docs/guides/front-door-session.md +54 -9
  7. package/docs/guides/mac-mini.md +20 -25
  8. package/docs/guides/poller-daemon-setup.md +4 -1
  9. package/docs/guides/setup-wizard.md +1 -1
  10. package/docs/runbooks/fleet-rollout.md +156 -0
  11. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  12. package/lib/action-executor.js +19 -3
  13. package/lib/budget-guard.mjs +279 -3
  14. package/lib/channels/base-adapter.mjs +3 -1
  15. package/lib/channels/contract.mjs +2 -1
  16. package/lib/channels/inbox-item.mjs +8 -0
  17. package/lib/claude-bin.mjs +5 -6
  18. package/lib/cli/doctor-checks.mjs +141 -10
  19. package/lib/cli/global-setup-extras.mjs +5 -1
  20. package/lib/cli/inbox.mjs +100 -15
  21. package/lib/cli/seat-auth.mjs +463 -0
  22. package/lib/cli/session.mjs +80 -12
  23. package/lib/collective/capture.mjs +8 -6
  24. package/lib/collective/global-config.mjs +63 -1
  25. package/lib/collective/presence.mjs +142 -5
  26. package/lib/comms/send-gate.mjs +559 -1
  27. package/lib/context/budget.mjs +327 -0
  28. package/lib/context/history-scope.mjs +138 -0
  29. package/lib/diagnostics/alerts.mjs +49 -0
  30. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  31. package/lib/engine/agents/definitions.mjs +343 -0
  32. package/lib/engine/agents/persist.mjs +275 -0
  33. package/lib/engine/agents/runtime.mjs +748 -0
  34. package/lib/engine/agents/usage.mjs +95 -0
  35. package/lib/engine/auth-status.mjs +139 -0
  36. package/lib/engine/budget.mjs +194 -0
  37. package/lib/engine/cli.mjs +1204 -0
  38. package/lib/engine/commands/index.mjs +269 -0
  39. package/lib/engine/context/budget.mjs +219 -0
  40. package/lib/engine/context/cache.mjs +125 -0
  41. package/lib/engine/context/child-env.mjs +215 -0
  42. package/lib/engine/context/compaction.mjs +342 -0
  43. package/lib/engine/context/images.mjs +90 -0
  44. package/lib/engine/context/instructions.mjs +327 -0
  45. package/lib/engine/context/lazy-instructions.mjs +169 -0
  46. package/lib/engine/context/manager.mjs +182 -0
  47. package/lib/engine/context/real-path.mjs +91 -0
  48. package/lib/engine/context/secret-values.mjs +163 -0
  49. package/lib/engine/context/settings.mjs +274 -0
  50. package/lib/engine/context/stream-input.mjs +159 -0
  51. package/lib/engine/guard.mjs +152 -0
  52. package/lib/engine/hooks.mjs +713 -0
  53. package/lib/engine/loop.mjs +560 -0
  54. package/lib/engine/mcp/client.mjs +254 -0
  55. package/lib/engine/mcp/config.mjs +301 -0
  56. package/lib/engine/mcp/http.mjs +201 -0
  57. package/lib/engine/mcp/index.mjs +146 -0
  58. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  59. package/lib/engine/mcp/naming.mjs +66 -0
  60. package/lib/engine/mcp/resources.mjs +89 -0
  61. package/lib/engine/mcp/results.mjs +133 -0
  62. package/lib/engine/mcp/stdio.mjs +137 -0
  63. package/lib/engine/mcp/supervisor.mjs +116 -0
  64. package/lib/engine/messages.mjs +104 -0
  65. package/lib/engine/output/json.mjs +164 -0
  66. package/lib/engine/output/stream-json.mjs +266 -0
  67. package/lib/engine/permissions.mjs +845 -0
  68. package/lib/engine/process-identity.mjs +164 -0
  69. package/lib/engine/process-tree.mjs +551 -0
  70. package/lib/engine/prompt.mjs +60 -0
  71. package/lib/engine/session/store.mjs +299 -0
  72. package/lib/engine/session-runtime/args.mjs +97 -0
  73. package/lib/engine/session-runtime/host.mjs +143 -0
  74. package/lib/engine/session-runtime/inbox.mjs +122 -0
  75. package/lib/engine/session-runtime/notifications.mjs +129 -0
  76. package/lib/engine/session-runtime/registry.mjs +328 -0
  77. package/lib/engine/session-runtime/runner.mjs +344 -0
  78. package/lib/engine/session-runtime/socket.mjs +212 -0
  79. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  80. package/lib/engine/skills/index.mjs +321 -0
  81. package/lib/engine/tools/bash-background.mjs +533 -0
  82. package/lib/engine/tools/bash.mjs +216 -0
  83. package/lib/engine/tools/edit.mjs +97 -0
  84. package/lib/engine/tools/glob.mjs +81 -0
  85. package/lib/engine/tools/grep.mjs +224 -0
  86. package/lib/engine/tools/index.mjs +84 -0
  87. package/lib/engine/tools/list-agents.mjs +32 -0
  88. package/lib/engine/tools/ls.mjs +127 -0
  89. package/lib/engine/tools/monitor.mjs +82 -0
  90. package/lib/engine/tools/notebook-edit.mjs +218 -0
  91. package/lib/engine/tools/read.mjs +103 -0
  92. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  93. package/lib/engine/tools/schema.mjs +144 -0
  94. package/lib/engine/tools/send-message.mjs +77 -0
  95. package/lib/engine/tools/session.mjs +70 -0
  96. package/lib/engine/tools/todo.mjs +144 -0
  97. package/lib/engine/tools/toolsearch.mjs +217 -0
  98. package/lib/engine/tools/walk.mjs +193 -0
  99. package/lib/engine/tools/web-switch.mjs +31 -0
  100. package/lib/engine/tools/webfetch-html.mjs +387 -0
  101. package/lib/engine/tools/webfetch-net.mjs +340 -0
  102. package/lib/engine/tools/webfetch.mjs +198 -0
  103. package/lib/engine/tools/websearch.mjs +91 -0
  104. package/lib/engine/tools/workflow.mjs +95 -0
  105. package/lib/engine/tools/write.mjs +76 -0
  106. package/lib/engine/tui/line-editor.mjs +137 -0
  107. package/lib/engine/tui/render.mjs +86 -0
  108. package/lib/engine/tui/tui.mjs +274 -0
  109. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  110. package/lib/engine/wire/effort.mjs +36 -0
  111. package/lib/engine/wire/errors.mjs +496 -0
  112. package/lib/engine/wire/http.mjs +441 -0
  113. package/lib/engine/wire/index.mjs +76 -0
  114. package/lib/engine/wire/openai-chat.mjs +332 -0
  115. package/lib/engine/wire/prompt-cache.mjs +79 -0
  116. package/lib/engine/wire/search.mjs +140 -0
  117. package/lib/engine/wire/sse.mjs +114 -0
  118. package/lib/engine/wire/stall.mjs +349 -0
  119. package/lib/engine/wire/token-provider.mjs +175 -0
  120. package/lib/engine/wire/usage.mjs +192 -0
  121. package/lib/engine/workflow/host.mjs +524 -0
  122. package/lib/engine/workflow/journal.mjs +188 -0
  123. package/lib/engine/workflow/json-schema.mjs +171 -0
  124. package/lib/engine/workflow/meta.mjs +329 -0
  125. package/lib/engine/workflow/notifications.mjs +52 -0
  126. package/lib/engine/workflow/runtime.mjs +447 -0
  127. package/lib/engine/workflow/sandbox.mjs +534 -0
  128. package/lib/engine/workflow/worker.mjs +141 -0
  129. package/lib/engine/workflow/worktree.mjs +74 -0
  130. package/lib/execution/disposition.mjs +1 -1
  131. package/lib/execution/intake.mjs +10 -0
  132. package/lib/execution/surface-policy.mjs +15 -0
  133. package/lib/learning/curator.mjs +8 -6
  134. package/lib/learning/reflect.mjs +8 -6
  135. package/lib/model-router/catalog/cohort.yaml +137 -0
  136. package/lib/model-router/catalog.mjs +118 -1
  137. package/lib/model-router/economics.mjs +9 -0
  138. package/lib/model-router/failover.mjs +67 -16
  139. package/lib/model-router/llm-task.mjs +39 -3
  140. package/lib/model-router/resolve.mjs +95 -3
  141. package/lib/model-router/spawn.mjs +46 -47
  142. package/lib/model-router/taxonomy.mjs +126 -4
  143. package/lib/org/cost-sync.mjs +141 -11
  144. package/lib/org/inbound/broadcast.mjs +289 -0
  145. package/lib/org/inbound/collective.mjs +375 -0
  146. package/lib/org/inbound/directedness.mjs +96 -8
  147. package/lib/org/inbound/facts.mjs +82 -4
  148. package/lib/org/inbound/hydrate.mjs +555 -51
  149. package/lib/org/inbound/project.mjs +22 -0
  150. package/lib/org/inbound/surfaces.mjs +14 -0
  151. package/lib/org/llm-token.mjs +879 -0
  152. package/lib/org/mesh.mjs +61 -0
  153. package/lib/org/messaging.mjs +3 -1
  154. package/lib/org/protocol.checksum +1 -1
  155. package/lib/org/protocol.mjs +15 -0
  156. package/lib/org/quota.mjs +520 -0
  157. package/lib/org/tool-surface.mjs +104 -16
  158. package/lib/org/ui-parity.mjs +16 -1
  159. package/lib/org/work-ledger.mjs +37 -6
  160. package/lib/rate-guard.mjs +114 -1
  161. package/lib/resource-governor.mjs +41 -6
  162. package/lib/runtime/adapter.mjs +823 -0
  163. package/lib/runtime/child-env.mjs +191 -0
  164. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  165. package/lib/runtime/seat-engine.mjs +162 -0
  166. package/lib/session/ask-ledger.mjs +271 -0
  167. package/lib/session/current-work.mjs +676 -0
  168. package/lib/session/feed-core.mjs +40 -3
  169. package/lib/session/launch-args.mjs +56 -4
  170. package/lib/session/status-summary.mjs +26 -9
  171. package/lib/session/upgrade-notice.mjs +42 -0
  172. package/lib/setup/claude-probe.mjs +117 -13
  173. package/lib/setup/enrich.mjs +13 -10
  174. package/lib/setup/sections/model.mjs +39 -13
  175. package/lib/telemetry/collect.mjs +208 -9
  176. package/lib/upgrade/ignored-drift.mjs +105 -0
  177. package/lib/voice/post-call-brief.mjs +30 -17
  178. package/package.json +15 -3
  179. package/plugins/maestro-skills/skills/board-work.md +5 -0
  180. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  181. package/plugins/maestro-skills/skills/main-session.md +18 -7
  182. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  183. package/scripts/ci/run-tests.mjs +47 -19
  184. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  185. package/scripts/collective/hook-runner.mjs +29 -2
  186. package/scripts/continuous-monitor.sh +13 -0
  187. package/scripts/cost/track-claude-usage.mjs +15 -0
  188. package/scripts/daemon/agent-daemon.mjs +408 -20
  189. package/scripts/daemon/assurance.mjs +48 -12
  190. package/scripts/daemon/cadence-consumer.mjs +218 -68
  191. package/scripts/daemon/cadence-handlers.mjs +73 -4
  192. package/scripts/daemon/classifier.mjs +75 -26
  193. package/scripts/daemon/context-compiler.mjs +104 -59
  194. package/scripts/daemon/deliver.mjs +30 -1
  195. package/scripts/daemon/dispatcher.mjs +804 -157
  196. package/scripts/daemon/health.mjs +14 -1
  197. package/scripts/daemon/lib/session-router.mjs +310 -42
  198. package/scripts/daemon/maestro-daemon.mjs +11 -0
  199. package/scripts/daemon/prompt-builder.mjs +121 -12
  200. package/scripts/daemon/responder.mjs +315 -146
  201. package/scripts/daemon/sdk-version.mjs +98 -16
  202. package/scripts/eval/probe-gateway.mjs +635 -0
  203. package/scripts/eval/replay/extract.mjs +270 -0
  204. package/scripts/eval/replay/grade.mjs +260 -0
  205. package/scripts/eval/replay/lib/config.mjs +50 -0
  206. package/scripts/eval/replay/lib/effects.mjs +65 -0
  207. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  208. package/scripts/eval/replay/lib/judge.mjs +72 -0
  209. package/scripts/eval/replay/lib/redact.mjs +136 -0
  210. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  211. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  212. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  213. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  214. package/scripts/eval/replay/report.mjs +185 -0
  215. package/scripts/eval/replay/run.mjs +404 -0
  216. package/scripts/fleet/rollout.mjs +1094 -0
  217. package/scripts/hooks/pre-send-audit.sh +36 -245
  218. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  219. package/scripts/hooks/validate-state-yaml.sh +190 -0
  220. package/scripts/huddle/huddle-llm.mjs +361 -0
  221. package/scripts/huddle/huddle-server.mjs +46 -121
  222. package/scripts/local-triggers/autoupdate.sh +448 -78
  223. package/scripts/local-triggers/run-trigger.sh +13 -0
  224. package/scripts/maintenance/pin-integrity.mjs +364 -0
  225. package/scripts/poll-slack-events.sh +41 -9
  226. package/scripts/poller/slack-socket-mode.mjs +28 -3
  227. package/scripts/session/supervisor.mjs +80 -13
  228. package/scripts/spawn-session.sh +13 -0
  229. package/bin/maestro.test.mjs +0 -1574
  230. package/lib/action-executor.test.mjs +0 -871
  231. package/lib/archetype.test.mjs +0 -132
  232. package/lib/assurance/plan-note.test.mjs +0 -234
  233. package/lib/assurance/room-budget.test.mjs +0 -486
  234. package/lib/assurance/tier.test.mjs +0 -174
  235. package/lib/autonomy.test.mjs +0 -66
  236. package/lib/backlog.test.mjs +0 -302
  237. package/lib/backup/policy.test.mjs +0 -305
  238. package/lib/budget-escalate.test.mjs +0 -232
  239. package/lib/budget-guard.envelope.test.mjs +0 -476
  240. package/lib/budget-guard.test.mjs +0 -427
  241. package/lib/cadence-bus-requeue.test.mjs +0 -83
  242. package/lib/cadence-bus-schedule.test.mjs +0 -194
  243. package/lib/cadence-bus.test.mjs +0 -720
  244. package/lib/cadences.test.mjs +0 -230
  245. package/lib/capability/inventory.test.mjs +0 -232
  246. package/lib/capability.test.mjs +0 -78
  247. package/lib/channels/base-adapter.test.mjs +0 -590
  248. package/lib/channels/channels.test.mjs +0 -371
  249. package/lib/channels/contract.test.mjs +0 -162
  250. package/lib/channels/inbox-item.test.mjs +0 -368
  251. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  252. package/lib/channels/pairing.test.mjs +0 -270
  253. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  254. package/lib/channels/slack-adapter.test.mjs +0 -212
  255. package/lib/channels/telegram-adapter.test.mjs +0 -306
  256. package/lib/channels/voice/adapter.test.mjs +0 -278
  257. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  258. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  259. package/lib/charter.test.mjs +0 -89
  260. package/lib/claude-bin.test.mjs +0 -131
  261. package/lib/cli/board.test.mjs +0 -227
  262. package/lib/cli/design.test.mjs +0 -270
  263. package/lib/cli/doctor-checks.test.mjs +0 -336
  264. package/lib/cli/global-setup-extras.test.mjs +0 -462
  265. package/lib/cli/inbox.test.mjs +0 -230
  266. package/lib/cli/session-ack.test.mjs +0 -63
  267. package/lib/cli/session.test.mjs +0 -613
  268. package/lib/collective/capture.test.mjs +0 -121
  269. package/lib/collective/cards.test.mjs +0 -114
  270. package/lib/collective/config.test.mjs +0 -123
  271. package/lib/collective/global-config.test.mjs +0 -220
  272. package/lib/collective/global-skills.test.mjs +0 -126
  273. package/lib/collective/presence.test.mjs +0 -95
  274. package/lib/collective/recall.test.mjs +0 -116
  275. package/lib/collective/vendor-skills.test.mjs +0 -306
  276. package/lib/comms/send-gate.test.mjs +0 -770
  277. package/lib/comms.test.mjs +0 -41
  278. package/lib/cost/ledger-row.test.mjs +0 -183
  279. package/lib/design/design-md.test.mjs +0 -318
  280. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  281. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  282. package/lib/design/fixtures/foundation.json +0 -133
  283. package/lib/design/refresh-gate.test.mjs +0 -144
  284. package/lib/design/write.test.mjs +0 -241
  285. package/lib/diagnostics/alerts.test.mjs +0 -318
  286. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  287. package/lib/diagnostics/counters.test.mjs +0 -206
  288. package/lib/diagnostics/events.test.mjs +0 -290
  289. package/lib/diagnostics/otel.test.mjs +0 -196
  290. package/lib/diagnostics/trace.test.mjs +0 -251
  291. package/lib/env-compat.test.mjs +0 -104
  292. package/lib/execution/disposition.test.mjs +0 -553
  293. package/lib/execution/drive.test.mjs +0 -270
  294. package/lib/execution/effects.test.mjs +0 -344
  295. package/lib/execution/intake.test.mjs +0 -389
  296. package/lib/execution/journal.test.mjs +0 -261
  297. package/lib/execution/match.test.mjs +0 -235
  298. package/lib/execution/pipeline.test.mjs +0 -392
  299. package/lib/execution/route.test.mjs +0 -186
  300. package/lib/execution/surface-policy.test.mjs +0 -162
  301. package/lib/fs-atomic.test.mjs +0 -72
  302. package/lib/fs-ownership.test.mjs +0 -158
  303. package/lib/goals/admission.test.mjs +0 -164
  304. package/lib/goals/classify.test.mjs +0 -167
  305. package/lib/goals/collaborate.test.mjs +0 -336
  306. package/lib/goals/gaps.test.mjs +0 -284
  307. package/lib/goals/loop.test.mjs +0 -845
  308. package/lib/hooks/bus.test.mjs +0 -387
  309. package/lib/identity/persona.test.mjs +0 -142
  310. package/lib/kpi-sensors.test.mjs +0 -278
  311. package/lib/kpi.test.mjs +0 -244
  312. package/lib/learning/config.test.mjs +0 -75
  313. package/lib/learning/counters.test.mjs +0 -69
  314. package/lib/learning/curator-consolidate.test.mjs +0 -238
  315. package/lib/learning/curator.test.mjs +0 -106
  316. package/lib/learning/reflect.test.mjs +0 -0
  317. package/lib/learning/session-index.test.mjs +0 -125
  318. package/lib/learning/skill-writer.test.mjs +0 -210
  319. package/lib/mandate/audit.test.mjs +0 -195
  320. package/lib/mandate/contract.test.mjs +0 -185
  321. package/lib/mandate/derive.test.mjs +0 -274
  322. package/lib/mandate/model.test.mjs +0 -164
  323. package/lib/mandate/refresh.test.mjs +0 -389
  324. package/lib/mcp/server.test.mjs +0 -426
  325. package/lib/model-router/auth-profiles.test.mjs +0 -580
  326. package/lib/model-router/catalog.test.mjs +0 -385
  327. package/lib/model-router/economics.test.mjs +0 -438
  328. package/lib/model-router/failover.test.mjs +0 -439
  329. package/lib/model-router/health.test.mjs +0 -338
  330. package/lib/model-router/integration-coverage.test.mjs +0 -831
  331. package/lib/model-router/integration.test.mjs +0 -564
  332. package/lib/model-router/ledger.test.mjs +0 -415
  333. package/lib/model-router/llm-task.test.mjs +0 -392
  334. package/lib/model-router/org-credentials.test.mjs +0 -265
  335. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  336. package/lib/model-router/reconcile.test.mjs +0 -316
  337. package/lib/model-router/repair.test.mjs +0 -180
  338. package/lib/model-router/spawn.test.mjs +0 -446
  339. package/lib/model-router/taxonomy.test.mjs +0 -410
  340. package/lib/model-router.test.mjs +0 -1207
  341. package/lib/org/activity.test.mjs +0 -134
  342. package/lib/org/approvals.test.mjs +0 -216
  343. package/lib/org/awareness.test.mjs +0 -159
  344. package/lib/org/board-mine-cache.test.mjs +0 -53
  345. package/lib/org/board.test.mjs +0 -187
  346. package/lib/org/bootstrap-context.test.mjs +0 -153
  347. package/lib/org/client.test.mjs +0 -1206
  348. package/lib/org/cohort-client.test.mjs +0 -126
  349. package/lib/org/cost-sync.test.mjs +0 -153
  350. package/lib/org/doctor.test.mjs +0 -346
  351. package/lib/org/engagement-ledger.test.mjs +0 -112
  352. package/lib/org/engagement.test.mjs +0 -739
  353. package/lib/org/handoff.test.mjs +0 -269
  354. package/lib/org/inbound/directedness.test.mjs +0 -668
  355. package/lib/org/inbound/facts.test.mjs +0 -471
  356. package/lib/org/inbound/hydrate.test.mjs +0 -453
  357. package/lib/org/inbound/index.test.mjs +0 -429
  358. package/lib/org/inbound/project.test.mjs +0 -287
  359. package/lib/org/integration-tools.test.mjs +0 -160
  360. package/lib/org/keys.test.mjs +0 -92
  361. package/lib/org/knowledge.test.mjs +0 -326
  362. package/lib/org/leases.test.mjs +0 -235
  363. package/lib/org/mesh-directives.test.mjs +0 -110
  364. package/lib/org/mesh-integration.test.mjs +0 -127
  365. package/lib/org/mesh.test.mjs +0 -400
  366. package/lib/org/messaging.test.mjs +0 -471
  367. package/lib/org/param-contract.test.mjs +0 -477
  368. package/lib/org/policy.test.mjs +0 -237
  369. package/lib/org/protocol.checksum.test.mjs +0 -90
  370. package/lib/org/protocol.test.mjs +0 -323
  371. package/lib/org/push.test.mjs +0 -792
  372. package/lib/org/registry.test.mjs +0 -100
  373. package/lib/org/resource-tools.test.mjs +0 -361
  374. package/lib/org/tool-access.test.mjs +0 -144
  375. package/lib/org/tool-surface-integration.test.mjs +0 -120
  376. package/lib/org/tool-surface.test.mjs +0 -1268
  377. package/lib/org/typing.test.mjs +0 -291
  378. package/lib/org/ui-parity.test.mjs +0 -560
  379. package/lib/org/verify.test.mjs +0 -194
  380. package/lib/org/work-ledger.test.mjs +0 -273
  381. package/lib/plan/adoption-e2e.test.mjs +0 -366
  382. package/lib/plan/budget-enforcement.test.mjs +0 -400
  383. package/lib/plan/compile.test.mjs +0 -382
  384. package/lib/plan/emit.test.mjs +0 -269
  385. package/lib/plan/explain.test.mjs +0 -188
  386. package/lib/prompts/parallelism.test.mjs +0 -177
  387. package/lib/rag/rag.test.mjs +0 -505
  388. package/lib/rate-guard.test.mjs +0 -272
  389. package/lib/reactive-gate.test.mjs +0 -57
  390. package/lib/render.test.mjs +0 -68
  391. package/lib/resource-governor.test.mjs +0 -488
  392. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  393. package/lib/scheduling/jitter.test.mjs +0 -140
  394. package/lib/secrets/broker.test.mjs +0 -280
  395. package/lib/secrets/providers.test.mjs +0 -274
  396. package/lib/security/audit-engine.test.mjs +0 -424
  397. package/lib/security/coerce-args.test.mjs +0 -281
  398. package/lib/security/dangerous-tools.test.mjs +0 -68
  399. package/lib/security/external-content.test.mjs +0 -84
  400. package/lib/security/redact.test.mjs +0 -441
  401. package/lib/security/secret-equal.test.mjs +0 -55
  402. package/lib/session/config.test.mjs +0 -92
  403. package/lib/session/feed-core.test.mjs +0 -198
  404. package/lib/session/first-run.test.mjs +0 -121
  405. package/lib/session/frontdoor.test.mjs +0 -205
  406. package/lib/session/handoffs.test.mjs +0 -183
  407. package/lib/session/identity.test.mjs +0 -180
  408. package/lib/session/inbox-claims.test.mjs +0 -286
  409. package/lib/session/launch-args.test.mjs +0 -157
  410. package/lib/session/liveness.test.mjs +0 -100
  411. package/lib/session/status-summary.test.mjs +0 -118
  412. package/lib/session-permissions.test.mjs +0 -120
  413. package/lib/setup/claude-probe.test.mjs +0 -187
  414. package/lib/setup/completeness.test.mjs +0 -110
  415. package/lib/setup/context-pack.test.mjs +0 -89
  416. package/lib/setup/enrich.test.mjs +0 -115
  417. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  418. package/lib/setup/integration.test.mjs +0 -162
  419. package/lib/setup/io.test.mjs +0 -77
  420. package/lib/setup/runner.test.mjs +0 -132
  421. package/lib/setup/sections/identity.test.mjs +0 -234
  422. package/lib/setup/sections/inventory.test.mjs +0 -198
  423. package/lib/setup/sections/learning.test.mjs +0 -81
  424. package/lib/setup/sections/mandate.test.mjs +0 -388
  425. package/lib/setup/sections/messaging.test.mjs +0 -127
  426. package/lib/setup/sections/model.test.mjs +0 -240
  427. package/lib/setup/sections/org.test.mjs +0 -346
  428. package/lib/setup/sections/orgmail.test.mjs +0 -118
  429. package/lib/setup/sections/recovery.test.mjs +0 -98
  430. package/lib/setup/sections/subagents.test.mjs +0 -429
  431. package/lib/setup/sections/verify.test.mjs +0 -175
  432. package/lib/setup/sot.test.mjs +0 -81
  433. package/lib/setup/state.test.mjs +0 -115
  434. package/lib/singleton.test.mjs +0 -151
  435. package/lib/subagents/cli.test.mjs +0 -389
  436. package/lib/subagents/client.test.mjs +0 -309
  437. package/lib/subagents/gap.test.mjs +0 -234
  438. package/lib/subagents/lock.test.mjs +0 -248
  439. package/lib/subagents/manifest.test.mjs +0 -175
  440. package/lib/subagents/refs.test.mjs +0 -204
  441. package/lib/subagents/resolve.test.mjs +0 -422
  442. package/lib/subagents/schema.test.mjs +0 -328
  443. package/lib/telemetry/alerts.test.mjs +0 -109
  444. package/lib/telemetry/collect.test.mjs +0 -1274
  445. package/lib/tool-definitions-integration.test.mjs +0 -83
  446. package/lib/tool-definitions.test.mjs +0 -437
  447. package/lib/upgrade/global-refresh.test.mjs +0 -65
  448. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  449. package/lib/upgrade/post-steps.test.mjs +0 -200
  450. package/lib/upgrade/verify.test.mjs +0 -164
  451. package/lib/util/fetch-timeout.test.mjs +0 -202
  452. package/lib/util/reconnect.test.mjs +0 -369
  453. package/lib/util/unhandled.test.mjs +0 -216
  454. package/lib/voice/outbound.test.mjs +0 -69
  455. package/lib/voice/session-rotation.test.mjs +0 -114
  456. package/lib/voice/stt.test.mjs +0 -226
  457. package/lib/voice/voice.test.mjs +0 -990
  458. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  459. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  460. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  461. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  462. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  463. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  464. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  465. package/scripts/ci/check.test.mjs +0 -194
  466. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  467. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  468. package/scripts/collective/hook-runner.test.mjs +0 -173
  469. package/scripts/cost/fleet-digest.test.mjs +0 -207
  470. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  471. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  472. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  473. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  474. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  475. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  476. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  477. package/scripts/daemon/assurance.test.mjs +0 -1791
  478. package/scripts/daemon/board-mirror.test.mjs +0 -165
  479. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  480. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  481. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  482. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  483. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  484. package/scripts/daemon/classifier.test.mjs +0 -266
  485. package/scripts/daemon/classify-kind.test.mjs +0 -40
  486. package/scripts/daemon/context-compiler.test.mjs +0 -300
  487. package/scripts/daemon/deliver.test.mjs +0 -564
  488. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  489. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  490. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  491. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  492. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  493. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  494. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  495. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  496. package/scripts/daemon/integration.test.mjs +0 -149
  497. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  498. package/scripts/daemon/lib/session-router.test.mjs +0 -295
  499. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  500. package/scripts/daemon/prompt-builder.test.mjs +0 -344
  501. package/scripts/daemon/responder-cost.test.mjs +0 -68
  502. package/scripts/daemon/responder-history.test.mjs +0 -185
  503. package/scripts/daemon/sdk-version.test.mjs +0 -31
  504. package/scripts/daemon/session-lock.test.mjs +0 -252
  505. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  506. package/scripts/daemon/typing-registry.test.mjs +0 -102
  507. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  508. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  509. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  510. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  511. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  512. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  513. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  514. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  515. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  516. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  517. package/scripts/poller-launchd/install.test.mjs +0 -243
  518. package/scripts/restore-from-backup.test.mjs +0 -181
  519. package/scripts/session/feed.test.mjs +0 -196
  520. package/scripts/session/supervisor-sh.test.mjs +0 -218
  521. package/scripts/session/supervisor.test.mjs +0 -482
  522. package/scripts/setup/configure-macos.test.mjs +0 -306
  523. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  524. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  525. package/scripts/setup/generate-capability.test.mjs +0 -134
  526. package/scripts/setup/init-agent.test.mjs +0 -370
  527. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  528. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  529. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -0,0 +1,463 @@
1
+ /**
2
+ * lib/cli/seat-auth.mjs — G4: which Claude billing rail is this seat on, and
3
+ * is that the fleet posture?
4
+ *
5
+ * The fleet posture is a **per-agent Console API key in a workspace with a hard
6
+ * spend cap** (`docs/guides/billing-console-keys.md`). Subscription OAuth — the
7
+ * `MAESTRO_PREFER_SUBSCRIPTION_AUTH` flag, a `CLAUDE_CODE_OAUTH_TOKEN`, or the
8
+ * keychain login behind them — still RUNS, which is exactly why a seat can sit
9
+ * on it for months with nothing complaining. G4 is the gate that ends that, and
10
+ * this module is the tooling half: a verdict a seat can state about itself, an
11
+ * inventory row an operator can paste, and a fleet template to paste it into.
12
+ *
13
+ * Three deliberate constraints, each of which is a test:
14
+ *
15
+ * 1. **Names, never values.** Every verdict reports which variable NAMES are
16
+ * present. No value — masked, truncated or otherwise — ever reaches a
17
+ * message, a row or the JSON. A migration tool that prints a fragment of the
18
+ * credential it is migrating has created a second copy of the problem.
19
+ * 2. **Both-set is its own verdict.** A seat carrying a Console key AND a
20
+ * subscription credential is not "half migrated", it is ambiguous: the
21
+ * runtime adapter blanks `ANTHROPIC_API_KEY` on a seat that
22
+ * `subscriptionAuth()` calls true (`lib/runtime/adapter.mjs`), so the
23
+ * subscription rail wins the lanes that go through it while lanes that pass
24
+ * the environment straight through may use the key. Spend then lands in two
25
+ * places and neither is authoritative. It is called out loudly.
26
+ * 3. **Warn today, fail when told.** Seats are LIVE. A check that fails by
27
+ * default would turn every doctor run red the hour it ships and break the
28
+ * very seats it is trying to move. So `subscription` and `both` are `warn`
29
+ * until `MAESTRO_REQUIRE_CONSOLE_KEY=1` (documented in `.env.example` and the
30
+ * runbook) flips them to `fail`. `neither` is a failure regardless on a
31
+ * `claude` seat — a seat with no credential at all is broken, not lax — and
32
+ * is the EXPECTED state on an `engine=cohort` seat, which holds no Anthropic
33
+ * credential by design.
34
+ *
35
+ * Everything here is pure except `runSeatAuth`, whose every edge (the seat's
36
+ * `.env`, the hostname, the clock, the org directory) is injectable.
37
+ *
38
+ * What this module deliberately does NOT do: reach another machine. A seat's
39
+ * environment lives on the seat's own box; there is no remote inventory
40
+ * mechanism to invent. The fleet report is a TEMPLATE plus paste-able rows,
41
+ * which is the honest shape of the job.
42
+ *
43
+ * @module lib/cli/seat-auth
44
+ */
45
+
46
+ "use strict";
47
+
48
+ import { hostname as osHostname } from "node:os";
49
+
50
+ /** The three variables that decide the rail. Order is the report order. */
51
+ export const SEAT_AUTH_VARS = Object.freeze([
52
+ "ANTHROPIC_API_KEY",
53
+ "MAESTRO_PREFER_SUBSCRIPTION_AUTH",
54
+ "CLAUDE_CODE_OAUTH_TOKEN",
55
+ ]);
56
+
57
+ /** Set this to 1/true/yes to turn the G4 warning into a doctor failure. */
58
+ export const REQUIRE_CONSOLE_KEY_VAR = "MAESTRO_REQUIRE_CONSOLE_KEY";
59
+
60
+ /** The operator runbook every remedy points at. */
61
+ export const RUNBOOK = "docs/guides/billing-console-keys.md";
62
+
63
+ /**
64
+ * The one remedy every auth row gives (CF-13). Defined here — the G4 module —
65
+ * and re-exported by `lib/cli/doctor-checks.mjs` so the two surfaces cannot
66
+ * drift into saying different things.
67
+ */
68
+ export const CONSOLE_KEY_REMEDY =
69
+ "put a per-agent Console API key (a workspace with a spend cap) in .env as ANTHROPIC_API_KEY and remove MAESTRO_PREFER_SUBSCRIPTION_AUTH / CLAUDE_CODE_OAUTH_TOKEN — see docs/guides/billing-console-keys.md";
70
+
71
+ const TRUTHY = /^(1|true|yes)$/i;
72
+
73
+ /** Non-empty after trimming, for any value shape an env bag can hold. */
74
+ function nonEmpty(v) {
75
+ return v !== undefined && v !== null && String(v).trim() !== "";
76
+ }
77
+
78
+ /**
79
+ * Is the G4 guard armed in this environment?
80
+ * @param {Record<string,string>} [env]
81
+ * @returns {boolean}
82
+ */
83
+ export function enforcementEnabled(env = {}) {
84
+ return TRUTHY.test(String((env && env[REQUIRE_CONSOLE_KEY_VAR]) || "").trim());
85
+ }
86
+
87
+ /** `ANTHROPIC_API_KEY (.env)` when the caller knows where it came from. */
88
+ function labelled(name, sources) {
89
+ const src = sources && sources[name];
90
+ return src ? `${name} (${src})` : name;
91
+ }
92
+
93
+ /**
94
+ * Which billing rail is this seat on?
95
+ *
96
+ * @param {Record<string,string>} [env] the seat's effective env (`.env` merged
97
+ * over the ambient env, the way every consumer sees it). VALUES ARE NEVER
98
+ * REPORTED — only whether each name is non-empty.
99
+ * @param {object} [opts]
100
+ * @param {"claude"|"cohort"|string} [opts.seatEngine="claude"] a `cohort` seat
101
+ * talks to cohort-llm and is SUPPOSED to hold no Anthropic credential.
102
+ * @param {Record<string,string>} [opts.sources] optional `{VAR: ".env"|"env"}`
103
+ * provenance, surfaced in the message (a token arriving through launchd's
104
+ * environment looks identical to one in `.env` until you say which).
105
+ * @param {boolean} [opts.enforce] override the env-derived guard state.
106
+ * @returns {{verdict:"api-key"|"subscription"|"both"|"neither", level:"ok"|"warn"|"fail",
107
+ * compliant:boolean, enforced:boolean, seatEngine:string, present:string[],
108
+ * absent:string[], msg:string, remedy:string}}
109
+ */
110
+ export function seatAuthVerdict(env = {}, opts = {}) {
111
+ const e = env && typeof env === "object" ? env : {};
112
+ const sources = (opts && opts.sources) || {};
113
+ const seatEngine = String((opts && opts.seatEngine) || "claude");
114
+ const enforced = opts && opts.enforce !== undefined ? !!opts.enforce : enforcementEnabled(e);
115
+
116
+ const key = nonEmpty(e.ANTHROPIC_API_KEY);
117
+ const token = nonEmpty(e.CLAUDE_CODE_OAUTH_TOKEN);
118
+ const preferPresent = nonEmpty(e.MAESTRO_PREFER_SUBSCRIPTION_AUTH);
119
+ const preferOn = TRUTHY.test(String(e.MAESTRO_PREFER_SUBSCRIPTION_AUTH || "").trim());
120
+ // The same test the runtime adapter applies (lib/runtime/adapter.mjs
121
+ // subscriptionAuth): the prefer flag OR a token puts the seat on the
122
+ // subscription rail, and that is what decides whether a lane blanks the key.
123
+ const subscription = token || preferOn;
124
+
125
+ const present = SEAT_AUTH_VARS.filter((n) => nonEmpty(e[n]));
126
+ const absent = SEAT_AUTH_VARS.filter((n) => !nonEmpty(e[n]));
127
+ const names = present.length ? present.map((n) => labelled(n, sources)).join(", ") : "none";
128
+ const cohortSeat = seatEngine === "cohort";
129
+
130
+ const verdict = key && subscription ? "both" : key ? "api-key" : subscription ? "subscription" : "neither";
131
+ // A flag that is present but not truthy is ignored by every consumer, and
132
+ // "MAESTRO_PREFER_SUBSCRIPTION_AUTH is set" reads as the opposite. Say so.
133
+ const preferNote = preferPresent && !preferOn
134
+ ? ` MAESTRO_PREFER_SUBSCRIPTION_AUTH is present but not 1/true/yes, so every consumer ignores it.`
135
+ : "";
136
+
137
+ const head = `Seat auth (G4): ${verdict} — names present: ${names}`;
138
+ const enforceNote = enforced
139
+ ? ` ${REQUIRE_CONSOLE_KEY_VAR} is set, so this is a failure.`
140
+ : ` Warning only today; set ${REQUIRE_CONSOLE_KEY_VAR}=1 to make it a doctor failure once the fleet has migrated.`;
141
+
142
+ if (verdict === "api-key") {
143
+ const msg = cohortSeat
144
+ ? `${head}. This engine-cohort seat needs no Anthropic credential; the key is harmless but unused by the engine lanes.${preferNote}`
145
+ : `${head}. This is the fleet posture: a Console API key in a capped workspace.${preferNote}`;
146
+ return { verdict, level: "ok", compliant: true, enforced, seatEngine, present, absent, msg, remedy: "" };
147
+ }
148
+
149
+ if (verdict === "both") {
150
+ const msg =
151
+ `${head}. AMBIGUOUS — this seat carries BOTH rails. The runtime adapter blanks ANTHROPIC_API_KEY on a ` +
152
+ `seat that is on subscription auth (lib/runtime/adapter.mjs subscriptionAuth), so the subscription rail ` +
153
+ `wins the lanes that go through it while a lane that passes the environment straight through may use the ` +
154
+ `key: spend lands in two places and neither is authoritative. Migrate, do not stack — ${CONSOLE_KEY_REMEDY}.` +
155
+ preferNote + enforceNote;
156
+ return { verdict, level: enforced ? "fail" : "warn", compliant: false, enforced, seatEngine, present, absent, msg, remedy: CONSOLE_KEY_REMEDY };
157
+ }
158
+
159
+ if (verdict === "subscription") {
160
+ const msg =
161
+ `${head}. Subscription OAuth is NOT the fleet posture${cohortSeat ? " and an engine-cohort seat should hold no Anthropic credential at all" : ""} — ` +
162
+ `it has no per-agent ceiling, no central spend visibility, and it expires headlessly. ${CONSOLE_KEY_REMEDY}.` +
163
+ preferNote + enforceNote;
164
+ return { verdict, level: enforced ? "fail" : "warn", compliant: false, enforced, seatEngine, present, absent, msg, remedy: CONSOLE_KEY_REMEDY };
165
+ }
166
+
167
+ // neither
168
+ if (cohortSeat) {
169
+ return {
170
+ verdict, level: "ok", compliant: true, enforced, seatEngine, present, absent,
171
+ msg: `${head}. Expected on an engine-cohort seat: it reaches models through cohort-llm and holds no Anthropic credential.`,
172
+ remedy: "",
173
+ };
174
+ }
175
+ return {
176
+ verdict, level: "fail", compliant: false, enforced, seatEngine, present, absent,
177
+ msg: `${head}. This seat has NO Claude credential at all — every spawn will fail, not fall back. ${CONSOLE_KEY_REMEDY}.`,
178
+ remedy: CONSOLE_KEY_REMEDY,
179
+ };
180
+ }
181
+
182
+ // ---------------------------------------------------------------------------
183
+ // The fleet inventory: a template, and rows that paste into it
184
+ // ---------------------------------------------------------------------------
185
+
186
+ /**
187
+ * The inventory columns, in order. The measured ones (Seat → Env names
188
+ * present) a seat can fill in about itself; the rest are Console-side facts
189
+ * only a human with Console access knows.
190
+ */
191
+ export const INVENTORY_COLUMNS = Object.freeze([
192
+ "Seat",
193
+ "Agent id",
194
+ "Machine",
195
+ "Auth verdict",
196
+ "Env names present",
197
+ "Checked",
198
+ "Operator",
199
+ "Anthropic account backing it",
200
+ "Console workspace",
201
+ "Monthly cap",
202
+ "Key id (last 4)",
203
+ ".env mode 600",
204
+ "Old token revoked",
205
+ "Verified (who / when)",
206
+ ]);
207
+
208
+ /** Keys of the columns a seat measures about itself, in `inventoryRow` order. */
209
+ const MEASURED = Object.freeze(["seat", "agentId", "machine", "verdict", "names", "checked"]);
210
+
211
+ /** A markdown table cell: never break the table, never invent content. */
212
+ function cell(v) {
213
+ const s = v === undefined || v === null ? "" : String(v);
214
+ return s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ").trim();
215
+ }
216
+
217
+ /**
218
+ * One inventory row. The measured fields are filled; every Console-side field
219
+ * is left blank for the operator — a tool that guesses them teaches people to
220
+ * trust a guess.
221
+ *
222
+ * @param {{seat?:string, agentId?:string, machine?:string, verdict?:string,
223
+ * names?:string[]|string, checked?:string}} [fields]
224
+ * @returns {string} a markdown table row with `INVENTORY_COLUMNS.length` cells
225
+ */
226
+ export function inventoryRow(fields = {}) {
227
+ const f = fields || {};
228
+ const names = Array.isArray(f.names) ? f.names.join(" + ") : f.names || "";
229
+ const measured = { seat: f.seat, agentId: f.agentId, machine: f.machine, verdict: f.verdict, names, checked: f.checked };
230
+ const cells = INVENTORY_COLUMNS.map((_, i) => (i < MEASURED.length ? cell(measured[MEASURED[i]]) : ""));
231
+ return `| ${cells.join(" | ")} |`;
232
+ }
233
+
234
+ /**
235
+ * Turn the org directory into seat stubs for the template. Members that the
236
+ * directory marks human are dropped; anything ambiguous is KEPT, because a
237
+ * missing row is a seat nobody audits while a spurious row is one an operator
238
+ * deletes in a second.
239
+ *
240
+ * @param {object[]} [members]
241
+ * @returns {Array<{seat:string, agentId:string}>}
242
+ */
243
+ export function seatsFromDirectory(members = []) {
244
+ const out = [];
245
+ const seen = new Set();
246
+ for (const m of Array.isArray(members) ? members : []) {
247
+ if (!m || typeof m !== "object") continue;
248
+ const human =
249
+ m.isHuman === true ||
250
+ /^human$/i.test(String(m.memberType || m.kind || m.type || "")) ||
251
+ /^(human|person)$/i.test(String(m.category || ""));
252
+ if (human) continue;
253
+ const agentId = String(m.agentId || m.slug || m.memberId || m.id || "").trim();
254
+ const seat = String(m.name || m.displayName || m.firstName || agentId || "").trim();
255
+ if (!seat && !agentId) continue;
256
+ const dedupe = `${seat.toLowerCase()}::${agentId.toLowerCase()}`;
257
+ if (seen.has(dedupe)) continue;
258
+ seen.add(dedupe);
259
+ out.push({ seat, agentId });
260
+ }
261
+ out.sort((a, b) => (a.seat || a.agentId).localeCompare(b.seat || b.agentId));
262
+ return out;
263
+ }
264
+
265
+ /**
266
+ * The per-machine inventory an operator fills in, with one pre-filled row per
267
+ * known member. Pure: the caller supplies the seats and the timestamp.
268
+ *
269
+ * @param {{seats?:Array<{seat:string, agentId:string}>, generatedAt?:string,
270
+ * source?:string, note?:string}} [o]
271
+ * @returns {string} markdown
272
+ */
273
+ export function fleetInventoryTemplate(o = {}) {
274
+ const seats = Array.isArray(o.seats) ? o.seats : [];
275
+ const generatedAt = o.generatedAt || "";
276
+ const source = o.source || "the org directory";
277
+ const lines = [];
278
+ lines.push("# G4 seat-auth inventory — off subscription OAuth, onto Console API keys");
279
+ lines.push("");
280
+ lines.push(`Generated by \`maestro seat-auth --fleet\`${generatedAt ? ` at ${generatedAt}` : ""} from ${source}.`);
281
+ lines.push("");
282
+ lines.push("**How to fill this in.** A seat's environment lives on the seat's own machine — there is no");
283
+ lines.push("remote inventory and this tool does not pretend otherwise. On EACH machine, in the agent's");
284
+ lines.push("directory, run:");
285
+ lines.push("");
286
+ lines.push("```sh");
287
+ lines.push("maestro seat-auth --row");
288
+ lines.push("```");
289
+ lines.push("");
290
+ lines.push("and paste the line it prints over that seat's row below. It fills the measured columns");
291
+ lines.push("(seat, agent id, machine, verdict, which variable NAMES are present, when it was checked)");
292
+ lines.push("and leaves the Console-side columns blank, because only a human with Console access knows them.");
293
+ lines.push("");
294
+ lines.push("**Never record a key value here** — or in a ticket, a commit, or a chat message. The last four");
295
+ lines.push("characters of a key id identify it; nothing else about a credential belongs in a document.");
296
+ lines.push("");
297
+ lines.push(`Migration steps, per seat: \`${RUNBOOK}\`. When every row reads \`api-key\`, set`);
298
+ lines.push(`\`${REQUIRE_CONSOLE_KEY_VAR}=1\` on the fleet so a seat that regresses fails \`maestro doctor\``);
299
+ lines.push("instead of warning quietly.");
300
+ if (o.note) { lines.push(""); lines.push(String(o.note)); }
301
+ lines.push("");
302
+ lines.push(`| ${INVENTORY_COLUMNS.join(" | ")} |`);
303
+ lines.push(`| ${INVENTORY_COLUMNS.map(() => "---").join(" | ")} |`);
304
+ if (seats.length === 0) {
305
+ lines.push(inventoryRow({}));
306
+ } else {
307
+ for (const s of seats) lines.push(inventoryRow({ seat: s.seat, agentId: s.agentId }));
308
+ }
309
+ lines.push("");
310
+ lines.push("## Record once, for the fleet");
311
+ lines.push("");
312
+ lines.push(`- Total live seats (rows above: **${seats.length}** from ${source} — confirm against the live fleet).`);
313
+ lines.push("- How many distinct Anthropic accounts backed the fleet before migration, and whose. A personal");
314
+ lines.push(" account powering several 24/7 seats is the most exposed shape.");
315
+ lines.push("- The date the last subscription token was revoked. That is the date this gate closes.");
316
+ lines.push(`- The date \`${REQUIRE_CONSOLE_KEY_VAR}=1\` was set fleet-wide, which is what stops it regressing.`);
317
+ lines.push("");
318
+ return lines.join("\n");
319
+ }
320
+
321
+ // ---------------------------------------------------------------------------
322
+ // The edge: `maestro seat-auth [--row] [--fleet] [--json]`
323
+ // ---------------------------------------------------------------------------
324
+
325
+ export const USAGE = `Usage:
326
+ maestro seat-auth This seat's billing rail: verdict, variable NAMES, remediation
327
+ maestro seat-auth --row One markdown row to paste into the G4 inventory
328
+ maestro seat-auth --fleet The per-machine inventory template, one row per org member
329
+ maestro seat-auth --json The same verdict as JSON
330
+
331
+ No value of any credential is ever printed. Exit 1 when the seat's verdict is a
332
+ failure (no credential at all, or a non-compliant rail with ${REQUIRE_CONSOLE_KEY_VAR}=1).
333
+ Runbook: ${RUNBOOK}
334
+ `;
335
+
336
+ /** config/agent.json, or {} — identity for the row, never a credential. */
337
+ function readAgentJson(agentRoot, deps) {
338
+ try {
339
+ const { readFileSync, existsSync } = deps.fs || {};
340
+ const p = `${agentRoot}/config/agent.json`;
341
+ if (existsSync && !existsSync(p)) return {};
342
+ return JSON.parse(String(readFileSync(p, "utf8")));
343
+ } catch {
344
+ return {};
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Run `maestro seat-auth …`. Every edge is injectable; nothing is written.
350
+ *
351
+ * @param {string[]} [argv]
352
+ * @param {{env?:Record<string,string>, agentRoot?:string, hostname?:()=>string,
353
+ * now?:number, fs?:object, readDotEnv?:(root:string)=>Record<string,string>,
354
+ * fetchDirectory?:(o:object)=>Promise<object[]>, orgConfig?:object,
355
+ * seatEngine?:string, stdout?:(s:string)=>void}} [deps]
356
+ * @returns {Promise<number>} exit code
357
+ */
358
+ export async function runSeatAuth(argv = [], deps = {}) {
359
+ const write = deps.stdout || ((s) => process.stdout.write(s));
360
+ const env = deps.env || process.env;
361
+ const agentRoot = deps.agentRoot || env.AGENT_ROOT || env.AGENT_DIR || process.cwd();
362
+ const now = Number.isFinite(deps.now) ? deps.now : Date.now();
363
+ const checked = new Date(now).toISOString().slice(0, 10);
364
+
365
+ let wantRow = false;
366
+ let wantFleet = false;
367
+ let wantJson = false;
368
+ for (const a of argv) {
369
+ if (a === "--row") wantRow = true;
370
+ else if (a === "--fleet") wantFleet = true;
371
+ else if (a === "--json") wantJson = true;
372
+ else if (a === "--help" || a === "-h") { write(USAGE); return 0; }
373
+ else if (a.startsWith("-")) { write(`seat-auth: unknown flag ${a}\n\n${USAGE}`); return 1; }
374
+ }
375
+
376
+ // The seat's effective env, exactly as every consumer sees it: .env wins over
377
+ // the ambient environment (dotenv never overrides an already-set variable is
378
+ // the LOADER's rule; the FILE is the seat's declared intent, which is what an
379
+ // audit is about — doctor merges the same way).
380
+ const readDotEnv = deps.readDotEnv || (async (root) => {
381
+ const { parseDotEnv } = await import("./doctor-checks.mjs");
382
+ const fs = deps.fs || (await import("node:fs"));
383
+ try { return parseDotEnv(fs.readFileSync(`${root}/.env`, "utf8")); } catch { return {}; }
384
+ });
385
+ const dotEnv = await readDotEnv(agentRoot);
386
+ const ambient = {};
387
+ for (const n of [...SEAT_AUTH_VARS, REQUIRE_CONSOLE_KEY_VAR]) if (nonEmpty(env[n])) ambient[n] = String(env[n]);
388
+ const effective = { ...ambient, ...dotEnv };
389
+ const sources = {};
390
+ for (const n of SEAT_AUTH_VARS) {
391
+ if (nonEmpty(dotEnv[n])) sources[n] = ".env";
392
+ else if (nonEmpty(ambient[n])) sources[n] = "env";
393
+ }
394
+
395
+ let seatEngine = deps.seatEngine;
396
+ if (!seatEngine) {
397
+ try {
398
+ const { readSeatEngine } = await import("../runtime/adapter.mjs");
399
+ seatEngine = readSeatEngine(agentRoot);
400
+ } catch { seatEngine = "claude"; }
401
+ }
402
+
403
+ const verdict = seatAuthVerdict(effective, { seatEngine, sources });
404
+ const agentJson = readAgentJson(agentRoot, { fs: deps.fs || (await import("node:fs")) });
405
+ const seat = String(agentJson.firstName || agentJson.name || "").split(/\s+/)[0] || String(agentRoot).split("/").pop();
406
+ const agentId = String(agentJson.agentId || agentJson.memberId || agentJson.slug || env.COHORT_AGENT_ID || "");
407
+ const hostname = (deps.hostname || (() => { try { return osHostname(); } catch { return ""; } }))();
408
+
409
+ if (wantFleet) {
410
+ let members = [];
411
+ let source = "the org directory";
412
+ try {
413
+ const fetchDirectory = deps.fetchDirectory || (await import("../org/client.mjs")).fetchDirectory;
414
+ const { loadOrgConfig } = await import("../org/client.mjs");
415
+ members = await fetchDirectory({ cfg: deps.orgConfig || loadOrgConfig(agentRoot) });
416
+ } catch {
417
+ members = [];
418
+ }
419
+ const seats = seatsFromDirectory(members);
420
+ if (seats.length === 0) source = "the org directory (EMPTY — this seat is not enrolled, or the directory was unreachable; add the rows by hand)";
421
+ const md = fleetInventoryTemplate({ seats, generatedAt: new Date(now).toISOString(), source });
422
+ if (wantJson) { write(JSON.stringify({ seats, generatedAt: new Date(now).toISOString() }, null, 2) + "\n"); return 0; }
423
+ write(md + "\n");
424
+ return 0;
425
+ }
426
+
427
+ if (wantRow) {
428
+ write(inventoryRow({ seat, agentId, machine: hostname, verdict: verdict.verdict, names: verdict.present, checked }) + "\n");
429
+ return verdict.level === "fail" ? 1 : 0;
430
+ }
431
+
432
+ if (wantJson) {
433
+ write(JSON.stringify({
434
+ seat, agentId, machine: hostname, checked,
435
+ verdict: verdict.verdict, level: verdict.level, compliant: verdict.compliant,
436
+ enforced: verdict.enforced, seatEngine: verdict.seatEngine,
437
+ present: verdict.present, absent: verdict.absent,
438
+ remedy: verdict.remedy, runbook: RUNBOOK,
439
+ }, null, 2) + "\n");
440
+ return verdict.level === "fail" ? 1 : 0;
441
+ }
442
+
443
+ const mark = verdict.level === "ok" ? "✓" : verdict.level === "warn" ? "!" : "✗";
444
+ write(`${mark} ${verdict.msg}\n`);
445
+ write(` seat ${seat}${agentId ? ` (${agentId})` : ""} on ${hostname || "this machine"}; engine ${verdict.seatEngine}\n`);
446
+ write(` absent: ${verdict.absent.length ? verdict.absent.join(", ") : "none"}\n`);
447
+ write(` paste into the G4 inventory: maestro seat-auth --row (template: maestro seat-auth --fleet)\n`);
448
+ return verdict.level === "fail" ? 1 : 0;
449
+ }
450
+
451
+ export default {
452
+ SEAT_AUTH_VARS,
453
+ REQUIRE_CONSOLE_KEY_VAR,
454
+ CONSOLE_KEY_REMEDY,
455
+ RUNBOOK,
456
+ INVENTORY_COLUMNS,
457
+ enforcementEnabled,
458
+ seatAuthVerdict,
459
+ inventoryRow,
460
+ seatsFromDirectory,
461
+ fleetInventoryTemplate,
462
+ runSeatAuth,
463
+ };
@@ -13,8 +13,10 @@
13
13
  * Commands:
14
14
  *
15
15
  * status [--brief|--json] lock holder · heartbeat age · main session id ·
16
- * mux session · plist installed/loaded · peers ·
17
- * open handoffs · restart/upgrade flags
16
+ * [--all] mux session · plist installed/loaded · peers ·
17
+ * open handoffs · restart/upgrade flags · the
18
+ * current-work block (lib/session/current-work;
19
+ * `--all` = the operator's unscoped view)
18
20
  * attach print the exact `tmux attach` / `screen -r` line;
19
21
  * exec it when stdin+stdout are a TTY
20
22
  * start bootstrap the plist if not loaded, then kickstart
@@ -55,7 +57,8 @@
55
57
 
56
58
  "use strict";
57
59
 
58
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
60
+ import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
61
+ import { randomUUID } from "node:crypto";
59
62
  import { basename, dirname, join } from "node:path";
60
63
  import { homedir } from "node:os";
61
64
  import { spawnSync } from "node:child_process";
@@ -63,9 +66,12 @@ import { parseArgs } from "node:util";
63
66
  import { pathToFileURL } from "node:url";
64
67
  import { writeJsonAtomic } from "../fs-atomic.mjs";
65
68
  import { resolveAgentRoot } from "../agent-root.mjs";
66
- import { resolveClaudeBin } from "../claude-bin.mjs";
69
+ import { buildSpawn } from "../runtime/adapter.mjs";
70
+ import { resolveSeatSpawn } from "../runtime/seat-engine.mjs";
67
71
  import { sessionPermissionArgs } from "../session-permissions.mjs";
68
72
  import { withParallelism } from "../prompts/parallelism.mjs";
73
+ import { envFileCommand, renderEnvFile } from "../session/launch-args.mjs";
74
+ import { currentWorkBlock } from "../session/current-work.mjs";
69
75
 
70
76
  /** The SDK's own package.json version — what "installed" means for an upgrade notice. */
71
77
  function ownVersion() {
@@ -229,7 +235,32 @@ export function peerPermissionArgs({ env = process.env, allowedTools } = {}) {
229
235
  * @returns {string[]}
230
236
  */
231
237
  export function buildSpawnArgs({ first, slug, prompt, env, allowedTools }) {
232
- return ["--name", `${first}-${slug}`, ...peerPermissionArgs({ env, allowedTools }), withParallelism(prompt)];
238
+ const spec = peerSpawn({ first, slug, prompt, env, allowedTools, bin: "claude" });
239
+ // Only the claude engine is wired for peers, so a failure is a defect.
240
+ if (!spec.ok) throw new Error(`buildSpawnArgs: ${spec.error.message}`);
241
+ return spec.argv;
242
+ }
243
+
244
+ /**
245
+ * The whole peer spawn — binary, argv, env — from the runtime adapter
246
+ * (lib/runtime/adapter.mjs, lane "peer"). `bin` is the caller's injected binary
247
+ * (deps.claudeBin); absent, the adapter resolves it.
248
+ *
249
+ * W4-E1 (row 5): `seat` is the seat's engine resolved once for this spawn
250
+ * (lib/runtime/seat-engine.mjs). A claude seat adds no fields (unchanged); an
251
+ * engine-cohort seat launches `cli.mjs session --name …` with the seat token.
252
+ * @param {{first:string, slug:string, prompt:string, env?:object, allowedTools?:string[], bin?:string, seat?:{fields:object}}} o
253
+ */
254
+ export function peerSpawn({ first, slug, prompt, env, allowedTools, bin, seat = { fields: {} } }) {
255
+ return buildSpawn({
256
+ lane: "peer",
257
+ bin: bin || undefined,
258
+ name: `${first}-${slug}`,
259
+ permissions: peerPermissionArgs({ env, allowedTools }),
260
+ prompt: withParallelism(prompt),
261
+ env,
262
+ ...seat.fields,
263
+ });
233
264
  }
234
265
 
235
266
  /**
@@ -280,6 +311,9 @@ function defaultDeps() {
280
311
  },
281
312
  /** The version the running SDK reports — an upgrade notice for it is already applied. */
282
313
  installedVersion: ownVersion(),
314
+ /** W4-E1: a private (0600) file that must not exist yet — the engine env file. */
315
+ writePrivateFile(path, text) { writeFileSync(path, text, { mode: 0o600, flag: "wx" }); chmodSync(path, 0o600); },
316
+ envFileId: () => randomUUID(),
283
317
  };
284
318
  }
285
319
 
@@ -542,7 +576,7 @@ function ensureLoaded(ctx, say) {
542
576
  export function usage() {
543
577
  return `maestro session <command>
544
578
 
545
- status [--brief|--json] lock holder, heartbeat age, mux session, plist, peers, handoffs
579
+ status [--brief|--json|--all] lock holder, heartbeat age, mux session, plist, peers, handoffs, current work (--all: unscoped)
546
580
  attach print the attach command (tmux attach / screen -r); exec it on a TTY
547
581
  start load the launchd job if needed, then kickstart it
548
582
  stop launchctl bootout the session job
@@ -557,6 +591,7 @@ export function usage() {
557
591
  const FLAGS = {
558
592
  json: { type: "boolean", default: false },
559
593
  brief: { type: "boolean", default: false },
594
+ all: { type: "boolean", default: false },
560
595
  force: { type: "boolean", default: false },
561
596
  name: { type: "string" },
562
597
  cwd: { type: "string" },
@@ -596,9 +631,21 @@ export async function run(argv = [], opts = {}) {
596
631
 
597
632
  case "status": {
598
633
  const s = snapshot(ctx);
634
+ // The "what am I doing right now" block (lib/session/current-work) —
635
+ // the same fact `session_status` returns and the daemon's replies carry.
636
+ // Scoped like every other reader: by default a live session from any
637
+ // conversation is reported as existing only; `--all` is the operator's
638
+ // own view (the files under state/ are theirs to read anyway — the flag
639
+ // only says the printout may carry what they hold, so it is not pasted
640
+ // onward by reflex). Fail-open: an unreadable state dir is an empty
641
+ // block, not a failed status.
642
+ try { s.currentWork = await (d.currentWorkBlock || currentWorkBlock)(agentRoot, { now: d.now(), audience: flags.all ? null : {} }); } catch { s.currentWork = ""; }
599
643
  if (flags.json) say(JSON.stringify(s, null, 2));
600
644
  else if (flags.brief) say(briefLine(s));
601
- else for (const l of humanStatus(s)) say(l);
645
+ else {
646
+ for (const l of humanStatus(s)) say(l);
647
+ if (s.currentWork) for (const l of s.currentWork.split("\n")) say(l);
648
+ }
602
649
  return { ok: true, code: 0, lines, data: s };
603
650
  }
604
651
 
@@ -679,12 +726,33 @@ export async function run(argv = [], opts = {}) {
679
726
 
680
727
  const cfg = await loadCfg(ctx);
681
728
  const kind = chooseMux(d, cfg);
682
- const bin = d.claudeBin || resolveClaudeBin();
683
- const args = buildSpawnArgs({ first, slug, prompt, env: d.env, allowedTools: cfg.allowedTools });
729
+ // W4-E1: the seat's engine, once for this spawn; an engine-cohort peer never falls back to claude.
730
+ let seat = { engine: "claude", fields: {} };
731
+ try { seat = (await (d.seatSpawn || ((root) => resolveSeatSpawn(root)))(agentRoot)) || seat; } catch { seat = { engine: "claude", fields: {} }; }
732
+ const spec = peerSpawn({ first, slug, prompt, env: d.env, allowedTools: cfg.allowedTools, bin: d.claudeBin, seat });
733
+ if (!spec.ok) return fail(`session spawn: ${spec.error.message}`);
734
+ // W4-E1: an engine-cohort peer's env (seat token, Claude credentials
735
+ // scrubbed) rides a 0600 file the session sources and deletes before exec
736
+ // (launch-args ENGINE_ENV_SCRIPT). The mux cannot carry it: a tmux server
737
+ // already running gives a new session its own global env, not the client's.
738
+ let envFile = null;
739
+ if (seat.engine === "cohort") {
740
+ envFile = join(paths.dir, `engine-env-${muxName}-${d.envFileId()}.env`);
741
+ try {
742
+ mkdirSync(paths.dir, { recursive: true });
743
+ d.writePrivateFile(envFile, renderEnvFile(spec.env));
744
+ } catch (e) {
745
+ return fail(`session spawn: could not write the engine env file ${envFile}: ${e && e.message ? e.message : e}`);
746
+ }
747
+ }
748
+ const command = envFile ? envFileCommand({ envFile, bin: spec.bin, args: spec.argv }) : [spec.bin, ...spec.argv];
684
749
  const r = kind === "tmux"
685
- ? d.exec("tmux", ["new-session", "-d", "-s", muxName, "-c", cwd, "--", bin, ...args], { cwd, env: d.env })
686
- : d.exec("screen", ["-dmS", muxName, bin, ...args], { cwd, env: d.env });
687
- if (r.status !== 0) return fail(`session spawn: ${kind} returned ${r.status}${r.stderr ? `: ${r.stderr.trim()}` : ""}`);
750
+ ? d.exec("tmux", ["new-session", "-d", "-s", muxName, "-c", cwd, "--", ...command], { cwd, env: d.env })
751
+ : d.exec("screen", ["-dmS", muxName, ...command], { cwd, env: d.env });
752
+ if (r.status !== 0) {
753
+ if (envFile) { try { unlinkSync(envFile); } catch { /* never written */ } }
754
+ return fail(`session spawn: ${kind} returned ${r.status}${r.stderr ? `: ${r.stderr.trim()}` : ""}`);
755
+ }
688
756
 
689
757
  const startedAt = new Date(d.now()).toISOString();
690
758
  const reg = updatePeers(paths, (cur) => [
@@ -22,7 +22,7 @@
22
22
  import { spawnSync } from "node:child_process";
23
23
  import { normalizeCard, appendCards, repoLabel } from "./cards.mjs";
24
24
  import { isRepoDenied } from "./config.mjs";
25
- import { resolveClaudeBin, daemonClaudeArgs, augmentedPath } from "../claude-bin.mjs";
25
+ import { buildSpawn } from "../runtime/adapter.mjs";
26
26
 
27
27
  const DISTILL_TIMEOUT_MS = 60_000;
28
28
  const MAX_TRANSCRIPT_CHARS = 16_000;
@@ -118,13 +118,15 @@ export function extractCardsFromModelOutput(text) {
118
118
  /** Default distiller: spawn a cheap `claude --print` pass. Fail-open → "". */
119
119
  export function defaultDistiller(prompt, opts = {}) {
120
120
  try {
121
- const model = opts.model || "haiku";
122
- const args = ["--print", "--model", model, ...daemonClaudeArgs()];
123
- const res = spawnSync(resolveClaudeBin(), args, {
124
- input: prompt,
121
+ // argv + env from the runtime adapter (lane "capture"): --print --model <m>
122
+ // <daemon MCP args>, prompt on stdin, PATH augmented.
123
+ const spec = buildSpawn({ lane: "capture", model: opts.model || "haiku", prompt, mcp: {}, env: process.env });
124
+ if (!spec.ok) return "";
125
+ const res = spawnSync(spec.bin, spec.argv, {
126
+ input: spec.stdin,
125
127
  encoding: "utf8",
126
128
  timeout: DISTILL_TIMEOUT_MS,
127
- env: { ...process.env, PATH: augmentedPath() },
129
+ env: spec.env,
128
130
  maxBuffer: 8 * 1024 * 1024,
129
131
  });
130
132
  if (res.status === 0 && res.stdout) return res.stdout;