@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
@@ -85,6 +85,19 @@
85
85
  * // to sdkVersion without a schema change. `ok` is the attempt's verdict:
86
86
  * // true = healthy on `to`; false = install failed or rolled back to `from`.
87
87
  * upgrade?: { at: ISO8601, from: string, to: string, ok: boolean },
88
+ * // THE BEATING DAEMON — which process is emitting this beat, what code
89
+ * // it is actually running, and the last health-gate verdict on it.
90
+ * // Also on `machine` (open record). `sdkVersion` ABOVE is what is
91
+ * // INSTALLED on the seat; `daemon.sdkVersion` is what the running
92
+ * // process booted on, and the two differ for exactly as long as an
93
+ * // installed upgrade has not been restarted onto. `pid`+`bootAt` make a
94
+ * // crash loop visible org-side (a pid that moves every beat), which is
95
+ * // the failure that hid for six days behind an hourly "up to date".
96
+ * // `dashboardAt` is the stamp of the file these came from, so a reader
97
+ * // can age the claim; it is never a liveness signal on its own.
98
+ * daemon?: { pid?: number, bootAt?: ISO8601, uptimeS?: number,
99
+ * sdkVersion?: string, dashboardAt?: ISO8601,
100
+ * healthy?: boolean, healthReason?: string },
88
101
  * },
89
102
  * claudeAuth: "ok"|"relogin_required"|"unknown",
90
103
  * alerts: [{ id, severity, kind, detail }],
@@ -120,6 +133,7 @@ import { fileURLToPath } from "node:url";
120
133
  import { createRequire } from "node:module";
121
134
  import { writeJsonAtomic } from "../fs-atomic.mjs";
122
135
  import { probeClaude as defaultProbeClaude } from "../setup/claude-probe.mjs";
136
+ import { readSeatEngine } from "../runtime/adapter.mjs";
123
137
 
124
138
  import { list as listPresence } from "../collective/presence.mjs";
125
139
  import { billableUsd } from "../cost/ledger-row.mjs";
@@ -433,18 +447,64 @@ export async function collectMachine(o = {}) {
433
447
  if (Number.isFinite(disk)) machine.diskUsedPct = pct(disk);
434
448
  }
435
449
 
450
+ // IDENTITY (§3.8) — hostname / tailnetIp / sdkVersion: where the seat is and
451
+ // what it runs. Collected FIRST and on its own terms, no longer as a rider on
452
+ // the inventory probe.
453
+ //
454
+ // WHY IT MOVED. It used to be read out of `collectInventory`'s return, inside
455
+ // `if (o.inventory !== false)` and with no try of its own. Three ways that
456
+ // lost the one field a fleet rollout is verified by, none of them about
457
+ // identity: a caller that turned inventory off lost it as a side effect; a
458
+ // `system_profiler` fork that threw took the whole `collectMachine` down with
459
+ // it (`collectStatus` then substitutes a bare `{loadavg, memUsedPct}` floor —
460
+ // the beat still goes, carrying neither the host stats nor the version); and
461
+ // a seat in that state stayed in it, beat after beat, with nothing on the box
462
+ // or in the org saying why. Identity is three cheap, independent reads and it
463
+ // is what an operator is looking for; it should not be able to fail because a
464
+ // hardware probe did. Each field is independently fail-open inside
465
+ // `collectIdentity`, and the try here is the outer guard for the probe seam.
466
+ //
467
+ // The results are handed on to `collectInventory` below so the tailnet probe
468
+ // forks once per beat, not twice.
469
+ let identity = {};
470
+ if (o.identity !== false) {
471
+ try { identity = await collectIdentity(o, osImpl); } catch { identity = {}; }
472
+ if (identity.hostname) machine.hostname = identity.hostname;
473
+ if (identity.tailnetIp) machine.tailnetIp = identity.tailnetIp;
474
+ if (identity.sdkVersion) machine.sdkVersion = identity.sdkVersion;
475
+ }
476
+
436
477
  // INVENTORY — device / ram / cores. Cached on disk (see collectInventory):
437
478
  // the chip and the installed RAM do not change between beats, so the
438
479
  // system_profiler fork happens once per machine, not every 30 seconds.
480
+ //
481
+ // WRAPPED, and the try is not decorative. `collectInventory` is not total:
482
+ // `execFileSafe` swallows a failing FORK, but the reads around it do not all
483
+ // have a try of their own (the platform read at the top of the probe block is
484
+ // the live example), so the function can and does throw. Before identity was
485
+ // hoisted above this, that throw propagated out of `collectMachine` and
486
+ // `collectStatus` substituted its bare `{loadavg, memUsedPct}` floor — the
487
+ // beat went out carrying neither host stats NOR the version, which is how two
488
+ // seats beat for days with no hostname and no sdkVersion. Now it costs its
489
+ // own three fields and nothing else.
439
490
  if (o.inventory !== false) {
440
- const inv = await collectInventory(o);
441
- if (inv.device) machine.device = inv.device;
442
- if (inv.ram) machine.ram = inv.ram;
443
- if (Number.isFinite(inv.cores)) machine.cores = inv.cores;
444
- // IDENTITY (§3.8) — where the seat is and what it runs.
445
- if (inv.hostname) machine.hostname = inv.hostname;
446
- if (inv.tailnetIp) machine.tailnetIp = inv.tailnetIp;
447
- if (inv.sdkVersion) machine.sdkVersion = inv.sdkVersion;
491
+ try {
492
+ const inv = await collectInventory({
493
+ ...o,
494
+ // Hand the identity answers on so the tailnet probe forks ONCE per
495
+ // beat, not twice. An empty string is an answer ("probed, none
496
+ // found") — and when `identity` is switched off entirely it is the
497
+ // right answer to hand on: a caller that asked for no identity should
498
+ // not pay for a tailnet fork inside the inventory probe either.
499
+ // `collectInventory` still returns those two fields; we ignore them —
500
+ // `machine.hostname/tailnetIp/sdkVersion` are set above or not at all.
501
+ tailnetIp: typeof o.tailnetIp === "string" ? o.tailnetIp : String(identity.tailnetIp || ""),
502
+ sdkVersion: typeof o.sdkVersion === "string" ? o.sdkVersion : String(identity.sdkVersion || ""),
503
+ });
504
+ if (inv.device) machine.device = inv.device;
505
+ if (inv.ram) machine.ram = inv.ram;
506
+ if (Number.isFinite(inv.cores)) machine.cores = inv.cores;
507
+ } catch { /* device/ram/cores drop; identity above already landed */ }
448
508
  }
449
509
 
450
510
  // spend24h — the agent's own rolling 24h LLM spend, off the cost ledger.
@@ -1310,6 +1370,123 @@ export function upgradeSummary(last) {
1310
1370
  return { at, from: typeof last.from === "string" ? last.from : "", to, ok: last.ok === true };
1311
1371
  }
1312
1372
 
1373
+ // ---------------------------------------------------------------------------
1374
+ // Daemon — WHICH process is beating, what code it is running, and the gate's
1375
+ // last verdict on it (`machine.daemon`)
1376
+ // ---------------------------------------------------------------------------
1377
+
1378
+ /**
1379
+ * Top-level scalars we lift out of state/dashboards/daemon-health.yaml. The file
1380
+ * is written by scripts/daemon/health.mjs as flat `key: value` lines plus one
1381
+ * indented `dispatcher:` JSON block, so a line regex over the UNINDENTED lines
1382
+ * reads it exactly and cannot be confused by the nested block. We do NOT pull a
1383
+ * YAML parser in for four scalars on a 30-second loop.
1384
+ *
1385
+ * Fail-open → null (no file, unreadable, or no recognisable line).
1386
+ * @param {string} text
1387
+ * @returns {{pid?:number, sdkVersion?:string, installedSdkVersion?:string,
1388
+ * versionsAgree?:boolean, versionDrift?:string, bootAt?:string,
1389
+ * updatedAt?:string, uptimeS?:number}|null}
1390
+ */
1391
+ export function parseDaemonHealthYaml(text) {
1392
+ if (typeof text !== "string" || text === "") return null;
1393
+ const out = {};
1394
+ for (const line of text.split("\n")) {
1395
+ // Unindented `key: value` only — an indented line belongs to `dispatcher:`.
1396
+ const m = /^([a-z_]+):[ \t]+(.+?)[ \t]*$/.exec(line);
1397
+ if (!m) continue;
1398
+ const [, k, v] = m;
1399
+ if (k === "pid") { const n = Number(v); if (Number.isInteger(n) && n > 0) out.pid = n; }
1400
+ else if (k === "sdk_version") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.sdkVersion = s; }
1401
+ // The three the resolver added so a version can be CHECKED and not merely
1402
+ // read. `sdk_version_agrees` is a tri-state written as a word: "unknown"
1403
+ // is not false and must never collapse to one.
1404
+ else if (k === "sdk_version_installed") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.installedSdkVersion = s; }
1405
+ else if (k === "sdk_version_agrees") { const s = v.trim(); if (s === "true" || s === "false") out.versionsAgree = s === "true"; }
1406
+ else if (k === "sdk_version_drift") { const s = v.trim(); if (s && s !== "undefined" && s !== "null") out.versionDrift = s; }
1407
+ else if (k === "daemon_start") { const s = v.trim(); if (!Number.isNaN(Date.parse(s))) out.bootAt = new Date(Date.parse(s)).toISOString(); }
1408
+ else if (k === "updated") { const s = v.trim(); if (!Number.isNaN(Date.parse(s))) out.updatedAt = new Date(Date.parse(s)).toISOString(); }
1409
+ else if (k === "uptime_s") { const n = Number(v); if (Number.isFinite(n) && n >= 0) out.uptimeS = Math.floor(n); }
1410
+ }
1411
+ return Object.keys(out).length > 0 ? out : null;
1412
+ }
1413
+
1414
+ /** state/dashboards/daemon-health.yaml as text, or "" — fail-open. */
1415
+ function readDaemonHealthText(agentRoot) {
1416
+ if (!agentRoot) return "";
1417
+ try {
1418
+ return readFileSync(join(resolve(agentRoot), "state", "dashboards", "daemon-health.yaml"), "utf8");
1419
+ } catch { return ""; }
1420
+ }
1421
+
1422
+ /**
1423
+ * Pure: the beat's `machine.daemon` — WHO is beating and whether the last health
1424
+ * gate believed it.
1425
+ *
1426
+ * WHY THIS FIELD EXISTS. `machine.sdkVersion` is the version INSTALLED on the
1427
+ * seat. It is not the version RUNNING: autoupdate installs into node_modules
1428
+ * while the old daemon process keeps executing the code it booted on, and a
1429
+ * hand-run `maestro upgrade` never restarts the daemon at all. One seat logged
1430
+ * "up to date" hourly for six days while its daemon crash-looped on the
1431
+ * previous version ~25 s at a time; the org saw nothing, because nothing the
1432
+ * beat carried could tell the two versions apart. `pid` + `bootAt` make the
1433
+ * distinction visible org-side: a pid that changes every beat is a crash loop,
1434
+ * and `sdkVersion` here differing from `machine.sdkVersion` is an installed-but-
1435
+ * not-restarted seat. Neither needs an ssh tour to see any more.
1436
+ *
1437
+ * ADDITIVE AND SMALL — six scalars, every one optional. It rides `machine`,
1438
+ * which hq validates as an OPEN record (`presence/beat.ts#statusSchema`), so it
1439
+ * lands the moment the daemon emits it and renders when the reader learns it.
1440
+ * It is NOT put on `session`, whose hq schema is `.strict()` and would refuse
1441
+ * the whole beat.
1442
+ *
1443
+ * FRESHNESS. `pid`/`bootAt`/`sdkVersion` describe the process that wrote the
1444
+ * dashboard, which a DEAD daemon leaves behind verbatim. `dashboardAt` is that
1445
+ * file's own stamp, carried so a reader can age the claim rather than trust it;
1446
+ * the ledger beat's server-stamped `at` remains the only liveness signal.
1447
+ *
1448
+ * @param {object|null} dash parsed daemon-health.yaml scalars, or null
1449
+ * @param {object|null} last state/autoupdate/last.json, or null
1450
+ * THE SEAT'S OWN VERDICT RIDES ALONG. `installedSdkVersion` + `versionsAgree`
1451
+ * are what the daemon's resolver concluded on the box, where both files are
1452
+ * readable; hq compares `machine.sdkVersion` against `machine.daemon.sdkVersion`
1453
+ * and reaches the same conclusion from the outside. Carrying both is not
1454
+ * redundancy — they are read at different instants from different files, and a
1455
+ * reader that sees them disagree has learned something real. `versionsAgree` is
1456
+ * carried ONLY as a real boolean: an inferred running version yields no verdict
1457
+ * at all, and absence must never read as agreement.
1458
+ *
1459
+ * @returns {{pid?:number, bootAt?:string, uptimeS?:number, sdkVersion?:string,
1460
+ * installedSdkVersion?:string, versionsAgree?:boolean,
1461
+ * versionDrift?:string, dashboardAt?:string, healthy?:boolean,
1462
+ * healthReason?:string}|null}
1463
+ */
1464
+ export function daemonSummary(dash, last) {
1465
+ const out = {};
1466
+ if (dash && typeof dash === "object") {
1467
+ if (Number.isInteger(dash.pid) && dash.pid > 0) out.pid = dash.pid;
1468
+ if (typeof dash.bootAt === "string" && dash.bootAt) out.bootAt = dash.bootAt;
1469
+ if (Number.isFinite(dash.uptimeS) && dash.uptimeS >= 0) out.uptimeS = Math.floor(dash.uptimeS);
1470
+ if (typeof dash.sdkVersion === "string" && dash.sdkVersion) out.sdkVersion = dash.sdkVersion;
1471
+ if (typeof dash.installedSdkVersion === "string" && dash.installedSdkVersion) out.installedSdkVersion = dash.installedSdkVersion;
1472
+ if (typeof dash.versionsAgree === "boolean") out.versionsAgree = dash.versionsAgree;
1473
+ if (dash.versionsAgree === false && typeof dash.versionDrift === "string" && dash.versionDrift) out.versionDrift = dash.versionDrift.slice(0, 200);
1474
+ if (typeof dash.updatedAt === "string" && dash.updatedAt) out.dashboardAt = dash.updatedAt;
1475
+ }
1476
+ // The health GATE's verdict, as autoupdate.sh last recorded it
1477
+ // (`{from,to,at,ok,healthy,reason}`). `healthy` is the gate's answer about the
1478
+ // DAEMON — pid stability, no fatal line since boot, a server-acknowledged
1479
+ // beat — which is a different question from `upgrade.ok` (did the last install
1480
+ // land). Only a real boolean is carried: a record written before the gate
1481
+ // existed has no opinion, and absence must not read as "healthy".
1482
+ if (last && typeof last === "object" && typeof last.healthy === "boolean") {
1483
+ out.healthy = last.healthy;
1484
+ const reason = typeof last.reason === "string" ? last.reason.trim() : "";
1485
+ if (!last.healthy && reason) out.healthReason = reason.slice(0, 200);
1486
+ }
1487
+ return Object.keys(out).length > 0 ? out : null;
1488
+ }
1489
+
1313
1490
  /** state/autoupdate/last.json, or null. */
1314
1491
  function readAutoupdateLast(agentRoot) {
1315
1492
  if (!agentRoot) return null;
@@ -1570,7 +1747,12 @@ function subscriptionModeFromEnv(agentRoot, env) {
1570
1747
  function probeAuthForBeat(o, nowMs) {
1571
1748
  if (o.probeClaude === false) return "unknown";
1572
1749
  const agentRoot = resolve(o.agentRoot);
1573
- if (!subscriptionModeFromEnv(agentRoot, o.env || process.env)) return "unknown";
1750
+ // W4 integration (row 25): an engine-cohort seat's probe is the engine's `auth status` — a
1751
+ // real call to the gateway that proves the seat credential — so it is asked whatever the
1752
+ // seat's Claude env says. A claude seat still probes only on the subscription path.
1753
+ let cohortSeat = false;
1754
+ try { cohortSeat = (o.seatEngine || readSeatEngine)(agentRoot) === "cohort"; } catch { cohortSeat = false; /* unreadable config: the default engine */ }
1755
+ if (!cohortSeat && !subscriptionModeFromEnv(agentRoot, o.env || process.env)) return "unknown";
1574
1756
  const injected = typeof o.probeClaude === "function";
1575
1757
  if (!injected) {
1576
1758
  const hit = _authProbeMemo.get(agentRoot);
@@ -1737,6 +1919,21 @@ export async function collectStatus(o = {}) {
1737
1919
  if (up) machine.upgrade = up;
1738
1920
  } catch { /* no upgrade field */ }
1739
1921
 
1922
+ // 4d. WHO is beating — `machine.daemon` {pid, bootAt, uptimeS, sdkVersion,
1923
+ // dashboardAt, healthy?, healthReason?}. This is what makes fleet version
1924
+ // drift a QUERY instead of an ssh tour: `machine.sdkVersion` is what is
1925
+ // INSTALLED, `machine.daemon.sdkVersion` is what is RUNNING, and a pid that
1926
+ // moves every beat is a crash loop. Fail-open in the same way every other
1927
+ // probe here is — a throw drops the field, never the beat.
1928
+ try {
1929
+ const dash = opt.daemonDashboard !== undefined
1930
+ ? opt.daemonDashboard
1931
+ : parseDaemonHealthYaml(readDaemonHealthText(opt.agentRoot));
1932
+ const last = opt.autoupdateLast !== undefined ? opt.autoupdateLast : readAutoupdateLast(opt.agentRoot);
1933
+ const d = daemonSummary(dash, last);
1934
+ if (d) machine.daemon = d;
1935
+ } catch { /* no daemon field */ }
1936
+
1740
1937
  const status = {
1741
1938
  state,
1742
1939
  activity: act.activity || "idle",
@@ -1782,6 +1979,8 @@ export const _internals = {
1782
1979
  sessionJobInstalled,
1783
1980
  readSessionAttention,
1784
1981
  upgradeSummary,
1982
+ parseDaemonHealthYaml,
1983
+ daemonSummary,
1785
1984
  detectClaudeAuth,
1786
1985
  resetAuthProbeCache,
1787
1986
  scanForRelogin,
@@ -0,0 +1,105 @@
1
+ /**
2
+ * lib/upgrade/ignored-drift.mjs — what a `.maestroignore`-protected file is
3
+ * missing from upstream, said per file.
4
+ *
5
+ * `maestro upgrade` never touches a path `.maestroignore` names — that is the
6
+ * file's whole purpose. Until this module, the summary said only "14 ignored
7
+ * (.maestroignore protected)": a count, with no way to tell from the log
8
+ * whether a protected fork was one line behind upstream or a whole release
9
+ * behind. On one seat the protected daemon lacked 24 upstream references to a
10
+ * feature the release notes said had shipped, and nothing in any upgrade log
11
+ * said so. A protected file is a fork the operator owns, and an owner needs a
12
+ * per-file answer to "how far has upstream moved under this?".
13
+ *
14
+ * `classifyIgnored` is PURE over the two texts (either may be null for absent)
15
+ * and answers one of:
16
+ *
17
+ * identical — the protection is currently idle (safe to drop the entry)
18
+ * local-only — the seat has it, upstream ships nothing at that path
19
+ * upstream-only — upstream ships it, the seat has never taken it
20
+ * drifts — both exist and differ: `onlyHere` / `onlyUpstream` are
21
+ * the line counts each side has that the other does not
22
+ *
23
+ * The line counts are a MULTISET difference, not an edit script: cheap, stable
24
+ * across a whole tree, and honest about what they are — a size of drift, not a
25
+ * patch. A moved block counts as zero drift, which is the right answer for
26
+ * "is upstream's fix in here?" and the wrong one for "is this file identical?"
27
+ * (that is `identical`, decided by bytes first).
28
+ *
29
+ * @module lib/upgrade/ignored-drift
30
+ */
31
+
32
+ "use strict";
33
+
34
+ /** Where `maestro upgrade` writes the report, relative to the agent root. */
35
+ export const IGNORED_DRIFT_REL = ".maestro/ignored-drift.json";
36
+
37
+ /**
38
+ * Multiset line difference of two texts.
39
+ * @param {string} a
40
+ * @param {string} b
41
+ * @returns {{onlyA:number, onlyB:number}}
42
+ */
43
+ export function lineDrift(a, b) {
44
+ const count = (text) => {
45
+ const m = new Map();
46
+ for (const line of String(text).split("\n")) m.set(line, (m.get(line) || 0) + 1);
47
+ return m;
48
+ };
49
+ const ma = count(a);
50
+ const mb = count(b);
51
+ let onlyA = 0;
52
+ let onlyB = 0;
53
+ for (const [line, n] of ma) onlyA += Math.max(0, n - (mb.get(line) || 0));
54
+ for (const [line, n] of mb) onlyB += Math.max(0, n - (ma.get(line) || 0));
55
+ return { onlyA, onlyB };
56
+ }
57
+
58
+ /**
59
+ * Classify one protected path.
60
+ * @param {string} path repo-relative path (reported back verbatim)
61
+ * @param {string|null} localText the seat's bytes, or null when absent
62
+ * @param {string|null} upstreamText the package's bytes, or null when absent
63
+ * @returns {{path:string, state:"identical"|"local-only"|"upstream-only"|"drifts", onlyHere?:number, onlyUpstream?:number}}
64
+ */
65
+ export function classifyIgnored(path, localText, upstreamText) {
66
+ const hasLocal = typeof localText === "string";
67
+ const hasUpstream = typeof upstreamText === "string";
68
+ if (hasLocal && !hasUpstream) return { path, state: "local-only" };
69
+ if (!hasLocal && hasUpstream) return { path, state: "upstream-only" };
70
+ if (!hasLocal && !hasUpstream) return { path, state: "local-only" }; // named in .maestroignore, present nowhere — the entry is idle
71
+ if (localText === upstreamText) return { path, state: "identical" };
72
+ const d = lineDrift(localText, upstreamText);
73
+ return { path, state: "drifts", onlyHere: d.onlyA, onlyUpstream: d.onlyB };
74
+ }
75
+
76
+ /**
77
+ * Build the whole report.
78
+ * @param {Array<{path:string, local:string|null, upstream:string|null}>} entries
79
+ * @param {{sdkVersion?:string, at?:string}} [meta]
80
+ */
81
+ export function buildIgnoredDriftReport(entries, meta = {}) {
82
+ const files = entries.map((e) => classifyIgnored(e.path, e.local, e.upstream));
83
+ const counts = { identical: 0, "local-only": 0, "upstream-only": 0, drifts: 0 };
84
+ for (const f of files) counts[f.state] += 1;
85
+ return {
86
+ sdkVersion: meta.sdkVersion || null,
87
+ at: meta.at || null,
88
+ counts,
89
+ files,
90
+ };
91
+ }
92
+
93
+ /**
94
+ * One log line per protected file, for the upgrade summary.
95
+ * @param {{path:string, state:string, onlyHere?:number, onlyUpstream?:number}} f
96
+ */
97
+ export function formatIgnoredLine(f) {
98
+ switch (f.state) {
99
+ case "identical": return ` = ${f.path} (identical to upstream — the entry is idle)`;
100
+ case "local-only": return ` · ${f.path} (upstream ships nothing here)`;
101
+ case "upstream-only": return ` ! ${f.path} (upstream ships it; this seat has never taken it)`;
102
+ case "drifts": return ` ~ ${f.path} (drifts: ${f.onlyUpstream} upstream line${f.onlyUpstream === 1 ? "" : "s"} not here, ${f.onlyHere} local line${f.onlyHere === 1 ? "" : "s"} not upstream)`;
103
+ default: return ` ? ${f.path}`;
104
+ }
105
+ }
@@ -37,6 +37,8 @@ import { spawn } from "node:child_process";
37
37
  import { writeFileSync, readFileSync, mkdirSync, existsSync, appendFileSync } from "node:fs";
38
38
  import { join, resolve, dirname } from "node:path";
39
39
  import { writeJsonAtomic } from "../fs-atomic.mjs";
40
+ import { buildSpawn } from "../runtime/adapter.mjs";
41
+ import { resolveSeatSpawn } from "../runtime/seat-engine.mjs";
40
42
 
41
43
  const BRIEF_PROMPT = `You are an executive operator processing the transcript of a voice call.
42
44
  Produce a structured post-call brief in JSON only — no preamble, no commentary.
@@ -166,25 +168,36 @@ export async function generateBrief(opts = {}) {
166
168
  // Claude --print runner (single-shot, JSON-only output)
167
169
  // ---------------------------------------------------------------------------
168
170
 
169
- function runClaudeBrief({ agentRoot, model, systemPrompt, input }) {
171
+ /**
172
+ * The voice-brief spawn for this seat (conformance row 36). Binary + argv + env
173
+ * from the runtime adapter (lane "voice-brief"), with the seat's engine:
174
+ * claude --print --dangerously-skip-permissions --strict-mcp-config --model <m>
175
+ * <prompt>, env {...env, AGENT_ROOT, AGENT_DIR}; the binary resolved
176
+ * by claude-bin (a safe $CLAUDE_BIN, then the known install paths)
177
+ * instead of a bare `claude`, which ENOENTs under launchd. The
178
+ * buildSpawn input is exactly what it was before the seat was read.
179
+ * cohort `node lib/engine/cli.mjs run -p …` with the seat token and every
180
+ * Anthropic/Claude credential scrubbed; no token → a refused spec
181
+ * (the caller writes the fallback brief), never claude.
182
+ * @param {{agentRoot:string, model:string, prompt:string, env:object}} o
183
+ * @param {{resolveSeatSpawn?:Function, buildSpawn?:Function, adapterDeps?:object}} [deps]
184
+ */
185
+ export async function briefSpawnSpec({ agentRoot, model, prompt, env }, deps = {}) {
186
+ const seat = await (deps.resolveSeatSpawn || resolveSeatSpawn)(agentRoot);
187
+ return (deps.buildSpawn || buildSpawn)({ lane: "voice-brief", model, prompt, mcp: "strict", agentRoot, env, ...seat.fields }, deps.adapterDeps);
188
+ }
189
+
190
+ async function runClaudeBrief({ agentRoot, model, systemPrompt, input }) {
191
+ const fullPrompt = `${systemPrompt}\n\n---\n${input}`;
192
+ const spec = await briefSpawnSpec({ agentRoot, model, prompt: fullPrompt, env: process.env });
170
193
  return new Promise((resolveP) => {
171
- const claudeBin = process.env.CLAUDE_BIN || "claude";
172
- const fullPrompt = `${systemPrompt}\n\n---\n${input}`;
173
- const proc = spawn(claudeBin, [
174
- "--print",
175
- "--dangerously-skip-permissions",
176
- "--strict-mcp-config",
177
- "--model", model,
178
- fullPrompt,
179
- ], {
194
+ if (!spec.ok) {
195
+ resolveP(fallbackBrief(input, spec.error.message));
196
+ return;
197
+ }
198
+ const proc = spawn(spec.bin, spec.argv, {
180
199
  cwd: agentRoot,
181
- env: {
182
- ...process.env,
183
- AGENT_ROOT: agentRoot,
184
- AGENT_DIR: agentRoot,
185
- // Voice briefs are routine summarization; let model router pick
186
- // the cheap backend by signalling it as a "fast" tier task.
187
- },
200
+ env: spec.env,
188
201
  stdio: ["ignore", "pipe", "pipe"],
189
202
  });
190
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cohortapp/agent-sdk",
3
- "version": "2.16.0",
3
+ "version": "2.18.4",
4
4
  "description": "Cohort Agent SDK — autonomous AI colleague runtime. Deploy senior AI colleagues on dedicated Mac minis, wired to the Cohort operating surface.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,8 @@
18
18
  "./singleton": "./lib/singleton.js",
19
19
  "./cadence-bus": "./lib/cadence-bus.mjs",
20
20
  "./fs-atomic": "./lib/fs-atomic.mjs",
21
+ "./context/budget": "./lib/context/budget.mjs",
22
+ "./context/history-scope": "./lib/context/history-scope.mjs",
21
23
  "./org/protocol": "./lib/org/protocol.mjs",
22
24
  "./org/client": "./lib/org/client.mjs",
23
25
  "./org/ui-parity": "./lib/org/ui-parity.mjs",
@@ -112,18 +114,28 @@
112
114
  ".claude/settings.json",
113
115
  ".env.example",
114
116
  "README.md",
115
- "framework-features.json"
117
+ "framework-features.json",
118
+ "!**/*.test.mjs",
119
+ "!**/*.test.js",
120
+ "!**/fixtures/**",
121
+ "!**/test-fixtures/**",
122
+ "!**/__fixtures__/**",
123
+ "!test/**",
124
+ "!scripts/eval/replay/examples/**"
116
125
  ],
117
126
  "publishConfig": {
118
127
  "access": "public",
119
128
  "registry": "https://registry.npmjs.org/"
120
129
  },
121
130
  "scripts": {
131
+ "fleet:rollout": "node scripts/fleet/rollout.mjs",
122
132
  "test": "node scripts/ci/run-tests.mjs",
123
133
  "test:coverage": "node scripts/ci/run-coverage.mjs",
124
- "test:list": "node --test bin/maestro.test.mjs lib/action-executor.test.mjs lib/archetype.test.mjs lib/autonomy.test.mjs lib/backlog.test.mjs lib/budget-guard.test.mjs lib/cadence-bus-schedule.test.mjs lib/cadence-bus.test.mjs lib/cadences.test.mjs lib/capability.test.mjs lib/channels/base-adapter.test.mjs lib/channels/channels.test.mjs lib/channels/contract.test.mjs lib/channels/inbox-item.test.mjs lib/channels/pairing.test.mjs lib/channels/slack-adapter.test.mjs lib/channels/telegram-adapter.test.mjs lib/channels/whatsapp/baileys-typing.test.mjs lib/charter.test.mjs lib/claude-bin.test.mjs lib/collective/capture.test.mjs lib/collective/cards.test.mjs lib/collective/config.test.mjs lib/collective/global-config.test.mjs lib/collective/presence.test.mjs lib/collective/recall.test.mjs lib/comms.test.mjs lib/fs-atomic.test.mjs lib/fs-ownership.test.mjs lib/learning/config.test.mjs lib/learning/counters.test.mjs lib/learning/curator-consolidate.test.mjs lib/learning/curator.test.mjs lib/learning/reflect.test.mjs lib/learning/session-index.test.mjs lib/learning/skill-writer.test.mjs lib/model-router.test.mjs lib/model-router/catalog.test.mjs lib/model-router/economics.test.mjs lib/model-router/failover.test.mjs lib/model-router/health.test.mjs lib/model-router/integration.test.mjs lib/model-router/integration-coverage.test.mjs lib/model-router/ledger.test.mjs lib/model-router/llm-task.test.mjs lib/model-router/org-credentials.test.mjs lib/model-router/reconcile.test.mjs lib/model-router/repair.test.mjs lib/model-router/spawn.test.mjs lib/model-router/taxonomy.test.mjs lib/org/approvals.test.mjs lib/org/board.test.mjs lib/org/client.test.mjs lib/org/cost-sync.test.mjs lib/org/handoff.test.mjs lib/org/keys.test.mjs lib/org/knowledge.test.mjs lib/org/activity.test.mjs lib/org/leases.test.mjs lib/org/mesh.test.mjs lib/org/messaging.test.mjs lib/org/cohort-client.test.mjs lib/org/policy.test.mjs lib/org/registry.test.mjs lib/org/tool-surface.test.mjs lib/org/ui-parity.test.mjs lib/org/doctor.test.mjs lib/org/verify.test.mjs lib/mcp/server.test.mjs lib/channels/orgmail/adapter.test.mjs lib/rag/rag.test.mjs lib/rate-guard.test.mjs lib/render.test.mjs lib/resource-governor.test.mjs lib/security/coerce-args.test.mjs lib/security/dangerous-tools.test.mjs lib/security/external-content.test.mjs lib/security/redact.test.mjs lib/security/secret-equal.test.mjs lib/session-permissions.test.mjs lib/setup/completeness.test.mjs lib/setup/context-pack.test.mjs lib/setup/enrich.test.mjs lib/setup/enroll-from-cohort.test.mjs lib/setup/integration.test.mjs lib/setup/io.test.mjs lib/setup/runner.test.mjs lib/setup/sections/identity.test.mjs lib/setup/sections/learning.test.mjs lib/setup/sections/messaging.test.mjs lib/setup/sections/org.test.mjs lib/setup/sections/orgmail.test.mjs lib/setup/sections/recovery.test.mjs lib/setup/sections/subagents.test.mjs lib/setup/sot.test.mjs lib/setup/state.test.mjs lib/singleton.test.mjs lib/subagents/schema.test.mjs lib/subagents/refs.test.mjs lib/subagents/manifest.test.mjs lib/subagents/lock.test.mjs lib/subagents/resolve.test.mjs lib/subagents/gap.test.mjs lib/subagents/client.test.mjs lib/subagents/cli.test.mjs lib/telemetry/alerts.test.mjs lib/telemetry/collect.test.mjs lib/tool-definitions.test.mjs lib/voice/session-rotation.test.mjs lib/voice/outbound.test.mjs lib/voice/stt.test.mjs lib/voice/voice.test.mjs scripts/cadence/enqueue-cadence-tick.test.mjs scripts/ci/check-durable-write-seam.test.mjs scripts/ci/check-no-build-artifacts.test.mjs scripts/ci/check-no-residual-identity.test.mjs scripts/ci/check.test.mjs scripts/collective/hook-runner.test.mjs scripts/cost/fleet-digest.test.mjs scripts/daemon/cadence-consumer-governance.test.mjs scripts/daemon/cadence-consumer.test.mjs scripts/daemon/cadence-handlers.test.mjs scripts/daemon/classify-kind.test.mjs scripts/daemon/dispatcher-cooldown.test.mjs scripts/daemon/dispatcher-governance.test.mjs scripts/daemon/inbox-deferral.test.mjs scripts/daemon/lib/session-router.test.mjs scripts/daemon/prompt-builder.test.mjs scripts/daemon/typing-registry.test.mjs scripts/hooks/pre-send-audit.test.mjs scripts/local-triggers/generate-plists.test.mjs scripts/poller/inbox-scan-poller.test.mjs scripts/poller/lib/cloud-relay-dedup.test.mjs scripts/poller/slack-socket-mode.test.mjs scripts/setup/gen-subagent-manifest.test.mjs scripts/setup/init-agent.test.mjs services/webhook-relay/server.test.mjs lib/cadence-bus-requeue.test.mjs lib/cli/inbox.test.mjs lib/cli/session-ack.test.mjs lib/org/mesh-directives.test.mjs lib/session/frontdoor.test.mjs lib/session/handoffs.test.mjs lib/session/inbox-claims.test.mjs scripts/daemon/agent-daemon-frontdoor.test.mjs scripts/daemon/cadence-consumer-frontdoor.test.mjs scripts/daemon/dispatcher-resume.test.mjs scripts/daemon/inbox-deferral-session.test.mjs scripts/watchdog/memory-watchdog.test.mjs lib/upgrade/launchd-reconcile.test.mjs lib/upgrade/global-refresh.test.mjs lib/upgrade/verify.test.mjs lib/upgrade/post-steps.test.mjs lib/org/board-mine-cache.test.mjs scripts/daemon/agent-daemon-board-mine.test.mjs scripts/local-triggers/autoupdate.test.mjs scripts/daemon/sdk-version.test.mjs",
134
+ "test:list": "node scripts/ci/run-tests.mjs --list",
125
135
  "test:setup": "node --test lib/setup/io.test.mjs lib/setup/state.test.mjs lib/setup/sot.test.mjs lib/setup/completeness.test.mjs lib/setup/context-pack.test.mjs lib/setup/enrich.test.mjs lib/setup/runner.test.mjs lib/setup/integration.test.mjs lib/setup/sections/subagents.test.mjs",
126
136
  "check": "node scripts/ci/check.mjs",
137
+ "test:engine-conformance": "node test/engine-conformance/run.mjs",
138
+ "eval:replay": "node scripts/eval/replay/run.mjs",
127
139
  "conformance:org": "node scripts/ci/conformance-org-api.mjs",
128
140
  "conformance:org:write": "node scripts/ci/conformance-org-api.mjs --write",
129
141
  "test:router": "node --test lib/model-router.test.mjs lib/model-router/catalog.test.mjs lib/model-router/economics.test.mjs lib/model-router/failover.test.mjs lib/model-router/health.test.mjs lib/model-router/integration.test.mjs lib/model-router/integration-coverage.test.mjs lib/model-router/ledger.test.mjs lib/model-router/llm-task.test.mjs lib/model-router/org-credentials.test.mjs lib/model-router/reconcile.test.mjs lib/model-router/repair.test.mjs lib/model-router/spawn.test.mjs lib/model-router/taxonomy.test.mjs scripts/cost/fleet-digest.test.mjs",
@@ -54,6 +54,11 @@ accepted` (or `maestro board track <inbox-id> --stage accepted --title …
54
54
  the message and dedupes, so a re-delivered ask finds the same row. See
55
55
  `inbound-triage`.
56
56
 
57
+ If you got the ask through the inbox, `maestro inbox claim <id>` has ALREADY
58
+ filed it — and `--title` / `--why` are read only by the step that opens the
59
+ row, so they belong on that claim (`maestro inbox claim <id> --title … --why
60
+ …`). Sending them later, against a row that exists, is accepted and ignored.
61
+
57
62
  ## What not to do
58
63
 
59
64
  - Do not narrate every step on the board; the board is for the state a
@@ -11,26 +11,39 @@ no: see "The one-interim rule" at the foot of this skill.
11
11
 
12
12
  ## The three outcomes
13
13
 
14
+ > **The board row is automatic — you do not open it.** `maestro inbox claim`
15
+ > opens the row and puts it in `running`, `reply` closes it with what you said,
16
+ > `done` closes it if you never replied, and `defer` parks it in `blocked` with
17
+ > your reason. The server's own gate decides whether an ask deserves a row at
18
+ > all, from the text, so a claimed one-line question mints nothing. Use
19
+ > `maestro board track` only to say something the four commands cannot: a
20
+ > `--stage blocked` with `notify` when you need unblocking mid-work, or a
21
+ > `--stage review` when a human should look before it goes out.
22
+
14
23
  1. **Reply now.** The ask is answerable in one message from what you already
15
24
  know or can look up in under a minute (a status, a fact, a yes/no, a link,
16
25
  a short opinion). `maestro inbox claim <id>` → `maestro inbox reply <id>
17
- --text "…"` → `maestro inbox done <id>`. No board row: the ledger on the
18
- server already records that you answered.
26
+ --text "…"` → `maestro inbox done <id>`. No board row: the server's gate
27
+ reads the text as answerable in the turn and mints nothing.
19
28
 
20
29
  2. **File it, work here.** The ask needs real work — reading, drafting,
21
30
  building, several steps — but you can finish it in this session inside an
22
31
  hour or two without blocking the front door. In ONE turn:
23
- - `maestro inbox claim <id>`
24
- - `maestro board track <id> --stage accepted --title "<what you took on>"
25
- --why "<one line: why this is more than a reply>"`
32
+ - `maestro inbox claim <id>` — this opens the row and puts it in `running`.
33
+ If the row deserves a better name than the ask's own first sentence, say
34
+ so ON THE CLAIM: `maestro inbox claim <id> --title "<what you took on>"
35
+ --why "<one line: why this is more than a reply>"`. The claim is the only
36
+ moment either is read — the server writes the title and the reason when
37
+ the row OPENS and protects them afterwards, so a later `maestro board
38
+ track … --title/--why` on the same ask is accepted and changes nothing.
26
39
  - then run the work — a `Workflow` when it has distinct steps, plain tool
27
- use when it does not. Track meaningful changes only: `--stage working`
28
- when the substantive part starts, `--stage blocked` (say what you need,
29
- `notify` who can unblock) when stuck, `--stage review` when a human should
30
- look before it goes out.
31
- - report back in the SAME channel/thread with `maestro inbox reply <id>`
32
- (or `messaging_send` to the thread), then `maestro board track <id>
33
- --stage done` and `maestro inbox done <id>`.
40
+ use when it does not. Track only what the four inbox commands cannot say:
41
+ `--stage blocked` (say what you need, `notify` who can unblock) when
42
+ stuck, `--stage review` when a human should look before it goes out.
43
+ - report back in the SAME channel/thread with `maestro inbox reply <id>`,
44
+ which closes the row and tags the channel, then `maestro inbox done <id>`.
45
+ If you reported with `messaging_send` instead, `maestro inbox done <id>`
46
+ is what closes the row.
34
47
 
35
48
  3. **File it, hand to a peer.** Same as 2, but the work is long (hours), heavy
36
49
  (a repo build, a large research pass), or would block you from answering the
@@ -39,13 +52,14 @@ no: see "The one-interim rule" at the foot of this skill.
39
52
  with a prompt that names the deliverable, the channel and thread to report
40
53
  to, the inbox id, and the instruction to `SendMessage` you a two-line
41
54
  status when done. You stay the one who talks to the human; the peer talks to
42
- you. When it reports back, you send the result and close the row
43
- (`--stage done`) and the item (`maestro inbox done <id>`).
55
+ you. When it reports back, you send the result and close the item
56
+ (`maestro inbox reply` then `maestro inbox done <id>`), which closes the row.
44
57
 
45
58
  ## How to pick
46
59
 
47
60
  - Would a competent colleague answer this from their chair in one message?
48
- → reply now.
61
+ → reply now. A "what are you doing / working on right now?" is this kind:
62
+ answer it from `maestro session status` (`currentWork`), never from memory.
49
63
  - Does it produce an artefact (a doc, a deck, a number that needs checking, a
50
64
  change in a system)? → file it. The server derives the board from the
51
65
  message: a DM's ask lands on that conversation's board, a space's ask on the
@@ -56,6 +70,33 @@ no: see "The one-interim rule" at the foot of this skill.
56
70
  and say in-channel what you need and from whom. That is a message with
57
71
  content in it, so it is always worth sending; do not guess.
58
72
 
73
+ ## Addressed to the whole room (`collective` on the feed line)
74
+
75
+ A message a human sent to everyone carries `collective` on its `inbound` line:
76
+ `roll-call`, `work`, or `ambient`. It is empty for a DM, an @mention or a task.
77
+ Nobody was named, so nobody is more responsible than you: **you answer for your
78
+ own seat, and you never answer for anyone else's.**
79
+
80
+ - `collective: roll-call` — "can everyone give me an update on what they're
81
+ doing right now?" → **outcome 1**. Answer from `maestro session status`
82
+ (`currentWork`) in at most five short lines: what you are on, what is queued,
83
+ what is blocked. No board row — reporting on yourself is not work. Do not
84
+ summarise, guess at or speak for any colleague; they answer for themselves.
85
+ - `collective: work` — "everyone, please review the binder by Friday" → **outcome
86
+ 2 or 3**, exactly as if it had been addressed to you alone. Claim it, `board
87
+ track --stage accepted`, do YOUR slice, and reply with what you will do and by
88
+ when. The board row is yours and covers your slice only; twelve other seats
89
+ are opening twelve other rows for the same message and that is correct.
90
+ - `collective: ambient` — the room was addressed and nothing was asked of you
91
+ ("everyone should take a look sometime", "FYI the binder is up"). **Outcome 1
92
+ at most, and usually silence.** Do not mint work from it. A board that grows a
93
+ row per seat per aside is a board nobody opens by the end of the week.
94
+
95
+ Why it is on the line at all: when your front door is live the daemon does not
96
+ dispatch these items, so there is no other plane that will act on them. If the
97
+ field is missing or empty on a message that plainly addressed the room, treat it
98
+ as `ambient` and use your judgement — it is a hint, not an instruction.
99
+
59
100
  ## Special topics
60
101
 
61
102
  - **Calls** (`topic: call`): answer in the channel — join if you are free now,