@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
@@ -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) => [