@cohortapp/agent-sdk 2.17.0 → 2.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (526) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +18 -5
  3. package/README.md +1 -0
  4. package/bin/maestro.mjs +62 -0
  5. package/docs/guides/billing-console-keys.md +60 -0
  6. package/docs/guides/front-door-session.md +54 -9
  7. package/docs/guides/mac-mini.md +20 -25
  8. package/docs/guides/setup-wizard.md +1 -1
  9. package/docs/runbooks/fleet-rollout.md +156 -0
  10. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  11. package/lib/action-executor.js +19 -3
  12. package/lib/budget-guard.mjs +279 -3
  13. package/lib/channels/base-adapter.mjs +3 -1
  14. package/lib/channels/contract.mjs +2 -1
  15. package/lib/channels/inbox-item.mjs +8 -0
  16. package/lib/claude-bin.mjs +5 -6
  17. package/lib/cli/doctor-checks.mjs +141 -10
  18. package/lib/cli/global-setup-extras.mjs +5 -1
  19. package/lib/cli/inbox.mjs +100 -15
  20. package/lib/cli/seat-auth.mjs +463 -0
  21. package/lib/cli/session.mjs +80 -12
  22. package/lib/collective/capture.mjs +8 -6
  23. package/lib/collective/global-config.mjs +63 -1
  24. package/lib/collective/presence.mjs +142 -5
  25. package/lib/comms/send-gate.mjs +559 -1
  26. package/lib/diagnostics/alerts.mjs +49 -0
  27. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  28. package/lib/engine/agents/definitions.mjs +343 -0
  29. package/lib/engine/agents/persist.mjs +275 -0
  30. package/lib/engine/agents/runtime.mjs +748 -0
  31. package/lib/engine/agents/usage.mjs +95 -0
  32. package/lib/engine/auth-status.mjs +139 -0
  33. package/lib/engine/budget.mjs +194 -0
  34. package/lib/engine/cli.mjs +1204 -0
  35. package/lib/engine/commands/index.mjs +269 -0
  36. package/lib/engine/context/budget.mjs +219 -0
  37. package/lib/engine/context/cache.mjs +125 -0
  38. package/lib/engine/context/child-env.mjs +215 -0
  39. package/lib/engine/context/compaction.mjs +342 -0
  40. package/lib/engine/context/images.mjs +90 -0
  41. package/lib/engine/context/instructions.mjs +327 -0
  42. package/lib/engine/context/lazy-instructions.mjs +169 -0
  43. package/lib/engine/context/manager.mjs +182 -0
  44. package/lib/engine/context/real-path.mjs +91 -0
  45. package/lib/engine/context/secret-values.mjs +163 -0
  46. package/lib/engine/context/settings.mjs +274 -0
  47. package/lib/engine/context/stream-input.mjs +159 -0
  48. package/lib/engine/guard.mjs +152 -0
  49. package/lib/engine/hooks.mjs +713 -0
  50. package/lib/engine/loop.mjs +560 -0
  51. package/lib/engine/mcp/client.mjs +254 -0
  52. package/lib/engine/mcp/config.mjs +301 -0
  53. package/lib/engine/mcp/http.mjs +201 -0
  54. package/lib/engine/mcp/index.mjs +146 -0
  55. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  56. package/lib/engine/mcp/naming.mjs +66 -0
  57. package/lib/engine/mcp/resources.mjs +89 -0
  58. package/lib/engine/mcp/results.mjs +133 -0
  59. package/lib/engine/mcp/stdio.mjs +137 -0
  60. package/lib/engine/mcp/supervisor.mjs +116 -0
  61. package/lib/engine/messages.mjs +104 -0
  62. package/lib/engine/output/json.mjs +164 -0
  63. package/lib/engine/output/stream-json.mjs +266 -0
  64. package/lib/engine/permissions.mjs +845 -0
  65. package/lib/engine/process-identity.mjs +164 -0
  66. package/lib/engine/process-tree.mjs +551 -0
  67. package/lib/engine/prompt.mjs +60 -0
  68. package/lib/engine/session/store.mjs +299 -0
  69. package/lib/engine/session-runtime/args.mjs +97 -0
  70. package/lib/engine/session-runtime/host.mjs +143 -0
  71. package/lib/engine/session-runtime/inbox.mjs +122 -0
  72. package/lib/engine/session-runtime/notifications.mjs +129 -0
  73. package/lib/engine/session-runtime/registry.mjs +328 -0
  74. package/lib/engine/session-runtime/runner.mjs +344 -0
  75. package/lib/engine/session-runtime/socket.mjs +212 -0
  76. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  77. package/lib/engine/skills/index.mjs +321 -0
  78. package/lib/engine/tools/bash-background.mjs +533 -0
  79. package/lib/engine/tools/bash.mjs +216 -0
  80. package/lib/engine/tools/edit.mjs +97 -0
  81. package/lib/engine/tools/glob.mjs +81 -0
  82. package/lib/engine/tools/grep.mjs +224 -0
  83. package/lib/engine/tools/index.mjs +84 -0
  84. package/lib/engine/tools/list-agents.mjs +32 -0
  85. package/lib/engine/tools/ls.mjs +127 -0
  86. package/lib/engine/tools/monitor.mjs +82 -0
  87. package/lib/engine/tools/notebook-edit.mjs +218 -0
  88. package/lib/engine/tools/read.mjs +103 -0
  89. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  90. package/lib/engine/tools/schema.mjs +144 -0
  91. package/lib/engine/tools/send-message.mjs +77 -0
  92. package/lib/engine/tools/session.mjs +70 -0
  93. package/lib/engine/tools/todo.mjs +144 -0
  94. package/lib/engine/tools/toolsearch.mjs +217 -0
  95. package/lib/engine/tools/walk.mjs +193 -0
  96. package/lib/engine/tools/web-switch.mjs +31 -0
  97. package/lib/engine/tools/webfetch-html.mjs +387 -0
  98. package/lib/engine/tools/webfetch-net.mjs +340 -0
  99. package/lib/engine/tools/webfetch.mjs +198 -0
  100. package/lib/engine/tools/websearch.mjs +91 -0
  101. package/lib/engine/tools/workflow.mjs +95 -0
  102. package/lib/engine/tools/write.mjs +76 -0
  103. package/lib/engine/tui/line-editor.mjs +137 -0
  104. package/lib/engine/tui/render.mjs +86 -0
  105. package/lib/engine/tui/tui.mjs +274 -0
  106. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  107. package/lib/engine/wire/effort.mjs +36 -0
  108. package/lib/engine/wire/errors.mjs +496 -0
  109. package/lib/engine/wire/http.mjs +441 -0
  110. package/lib/engine/wire/index.mjs +76 -0
  111. package/lib/engine/wire/openai-chat.mjs +332 -0
  112. package/lib/engine/wire/prompt-cache.mjs +79 -0
  113. package/lib/engine/wire/search.mjs +140 -0
  114. package/lib/engine/wire/sse.mjs +114 -0
  115. package/lib/engine/wire/stall.mjs +349 -0
  116. package/lib/engine/wire/token-provider.mjs +175 -0
  117. package/lib/engine/wire/usage.mjs +192 -0
  118. package/lib/engine/workflow/host.mjs +524 -0
  119. package/lib/engine/workflow/journal.mjs +188 -0
  120. package/lib/engine/workflow/json-schema.mjs +171 -0
  121. package/lib/engine/workflow/meta.mjs +329 -0
  122. package/lib/engine/workflow/notifications.mjs +52 -0
  123. package/lib/engine/workflow/runtime.mjs +447 -0
  124. package/lib/engine/workflow/sandbox.mjs +534 -0
  125. package/lib/engine/workflow/worker.mjs +141 -0
  126. package/lib/engine/workflow/worktree.mjs +74 -0
  127. package/lib/execution/disposition.mjs +1 -1
  128. package/lib/execution/intake.mjs +10 -0
  129. package/lib/execution/surface-policy.mjs +15 -0
  130. package/lib/learning/curator.mjs +8 -6
  131. package/lib/learning/reflect.mjs +8 -6
  132. package/lib/model-router/catalog/cohort.yaml +137 -0
  133. package/lib/model-router/catalog.mjs +118 -1
  134. package/lib/model-router/failover.mjs +67 -16
  135. package/lib/model-router/llm-task.mjs +39 -3
  136. package/lib/model-router/resolve.mjs +89 -3
  137. package/lib/model-router/spawn.mjs +46 -47
  138. package/lib/model-router/taxonomy.mjs +126 -4
  139. package/lib/org/cost-sync.mjs +141 -11
  140. package/lib/org/inbound/broadcast.mjs +289 -0
  141. package/lib/org/inbound/collective.mjs +375 -0
  142. package/lib/org/inbound/directedness.mjs +96 -8
  143. package/lib/org/inbound/facts.mjs +78 -2
  144. package/lib/org/inbound/project.mjs +22 -0
  145. package/lib/org/inbound/surfaces.mjs +14 -0
  146. package/lib/org/llm-token.mjs +879 -0
  147. package/lib/org/mesh.mjs +61 -0
  148. package/lib/org/messaging.mjs +3 -1
  149. package/lib/org/protocol.checksum +1 -1
  150. package/lib/org/protocol.mjs +15 -0
  151. package/lib/org/quota.mjs +520 -0
  152. package/lib/org/tool-surface.mjs +104 -16
  153. package/lib/org/ui-parity.mjs +16 -1
  154. package/lib/org/work-ledger.mjs +37 -6
  155. package/lib/rate-guard.mjs +114 -1
  156. package/lib/resource-governor.mjs +41 -6
  157. package/lib/runtime/adapter.mjs +823 -0
  158. package/lib/runtime/child-env.mjs +191 -0
  159. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  160. package/lib/runtime/seat-engine.mjs +162 -0
  161. package/lib/session/ask-ledger.mjs +271 -0
  162. package/lib/session/current-work.mjs +676 -0
  163. package/lib/session/feed-core.mjs +40 -3
  164. package/lib/session/launch-args.mjs +56 -4
  165. package/lib/session/status-summary.mjs +26 -9
  166. package/lib/session/upgrade-notice.mjs +42 -0
  167. package/lib/setup/claude-probe.mjs +117 -13
  168. package/lib/setup/enrich.mjs +13 -10
  169. package/lib/setup/sections/model.mjs +39 -13
  170. package/lib/telemetry/collect.mjs +208 -9
  171. package/lib/upgrade/ignored-drift.mjs +105 -0
  172. package/lib/voice/post-call-brief.mjs +30 -17
  173. package/package.json +13 -3
  174. package/plugins/maestro-skills/skills/board-work.md +5 -0
  175. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  176. package/plugins/maestro-skills/skills/main-session.md +18 -7
  177. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  178. package/scripts/ci/run-tests.mjs +47 -19
  179. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  180. package/scripts/collective/hook-runner.mjs +29 -2
  181. package/scripts/continuous-monitor.sh +13 -0
  182. package/scripts/cost/track-claude-usage.mjs +15 -0
  183. package/scripts/daemon/agent-daemon.mjs +408 -20
  184. package/scripts/daemon/assurance.mjs +48 -12
  185. package/scripts/daemon/cadence-consumer.mjs +218 -68
  186. package/scripts/daemon/cadence-handlers.mjs +73 -4
  187. package/scripts/daemon/classifier.mjs +75 -26
  188. package/scripts/daemon/context-compiler.mjs +51 -37
  189. package/scripts/daemon/deliver.mjs +30 -1
  190. package/scripts/daemon/dispatcher.mjs +595 -149
  191. package/scripts/daemon/health.mjs +14 -1
  192. package/scripts/daemon/maestro-daemon.mjs +11 -0
  193. package/scripts/daemon/prompt-builder.mjs +24 -0
  194. package/scripts/daemon/responder.mjs +246 -79
  195. package/scripts/daemon/sdk-version.mjs +98 -16
  196. package/scripts/eval/probe-gateway.mjs +635 -0
  197. package/scripts/eval/replay/extract.mjs +270 -0
  198. package/scripts/eval/replay/grade.mjs +260 -0
  199. package/scripts/eval/replay/lib/config.mjs +50 -0
  200. package/scripts/eval/replay/lib/effects.mjs +65 -0
  201. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  202. package/scripts/eval/replay/lib/judge.mjs +72 -0
  203. package/scripts/eval/replay/lib/redact.mjs +136 -0
  204. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  205. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  206. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  207. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  208. package/scripts/eval/replay/report.mjs +185 -0
  209. package/scripts/eval/replay/run.mjs +404 -0
  210. package/scripts/fleet/rollout.mjs +1094 -0
  211. package/scripts/hooks/pre-send-audit.sh +36 -245
  212. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  213. package/scripts/hooks/validate-state-yaml.sh +190 -0
  214. package/scripts/huddle/huddle-llm.mjs +361 -0
  215. package/scripts/huddle/huddle-server.mjs +46 -121
  216. package/scripts/local-triggers/autoupdate.sh +448 -78
  217. package/scripts/local-triggers/run-trigger.sh +13 -0
  218. package/scripts/maintenance/pin-integrity.mjs +364 -0
  219. package/scripts/poll-slack-events.sh +41 -9
  220. package/scripts/poller/slack-socket-mode.mjs +28 -3
  221. package/scripts/session/supervisor.mjs +80 -13
  222. package/scripts/spawn-session.sh +13 -0
  223. package/bin/maestro.test.mjs +0 -1574
  224. package/lib/action-executor.test.mjs +0 -871
  225. package/lib/archetype.test.mjs +0 -132
  226. package/lib/assurance/plan-note.test.mjs +0 -234
  227. package/lib/assurance/room-budget.test.mjs +0 -486
  228. package/lib/assurance/tier.test.mjs +0 -174
  229. package/lib/autonomy.test.mjs +0 -66
  230. package/lib/backlog.test.mjs +0 -302
  231. package/lib/backup/policy.test.mjs +0 -305
  232. package/lib/budget-escalate.test.mjs +0 -232
  233. package/lib/budget-guard.envelope.test.mjs +0 -476
  234. package/lib/budget-guard.test.mjs +0 -427
  235. package/lib/cadence-bus-requeue.test.mjs +0 -83
  236. package/lib/cadence-bus-schedule.test.mjs +0 -194
  237. package/lib/cadence-bus.test.mjs +0 -720
  238. package/lib/cadences.test.mjs +0 -230
  239. package/lib/capability/inventory.test.mjs +0 -232
  240. package/lib/capability.test.mjs +0 -78
  241. package/lib/channels/base-adapter.test.mjs +0 -590
  242. package/lib/channels/channels.test.mjs +0 -371
  243. package/lib/channels/contract.test.mjs +0 -162
  244. package/lib/channels/inbox-item.test.mjs +0 -368
  245. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  246. package/lib/channels/pairing.test.mjs +0 -270
  247. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  248. package/lib/channels/slack-adapter.test.mjs +0 -212
  249. package/lib/channels/telegram-adapter.test.mjs +0 -306
  250. package/lib/channels/voice/adapter.test.mjs +0 -278
  251. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  252. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  253. package/lib/charter.test.mjs +0 -89
  254. package/lib/claude-bin.test.mjs +0 -131
  255. package/lib/cli/board.test.mjs +0 -227
  256. package/lib/cli/design.test.mjs +0 -270
  257. package/lib/cli/doctor-checks.test.mjs +0 -336
  258. package/lib/cli/global-setup-extras.test.mjs +0 -462
  259. package/lib/cli/inbox.test.mjs +0 -230
  260. package/lib/cli/session-ack.test.mjs +0 -63
  261. package/lib/cli/session.test.mjs +0 -613
  262. package/lib/collective/capture.test.mjs +0 -121
  263. package/lib/collective/cards.test.mjs +0 -114
  264. package/lib/collective/config.test.mjs +0 -123
  265. package/lib/collective/global-config.test.mjs +0 -220
  266. package/lib/collective/global-skills.test.mjs +0 -126
  267. package/lib/collective/presence.test.mjs +0 -95
  268. package/lib/collective/recall.test.mjs +0 -116
  269. package/lib/collective/vendor-skills.test.mjs +0 -306
  270. package/lib/comms/send-gate.test.mjs +0 -770
  271. package/lib/comms.test.mjs +0 -41
  272. package/lib/context/budget.test.mjs +0 -252
  273. package/lib/context/history-scope.test.mjs +0 -79
  274. package/lib/cost/ledger-row.test.mjs +0 -183
  275. package/lib/design/design-md.test.mjs +0 -318
  276. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  277. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  278. package/lib/design/fixtures/foundation.json +0 -133
  279. package/lib/design/refresh-gate.test.mjs +0 -144
  280. package/lib/design/write.test.mjs +0 -241
  281. package/lib/diagnostics/alerts.test.mjs +0 -318
  282. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  283. package/lib/diagnostics/counters.test.mjs +0 -206
  284. package/lib/diagnostics/events.test.mjs +0 -290
  285. package/lib/diagnostics/otel.test.mjs +0 -196
  286. package/lib/diagnostics/trace.test.mjs +0 -251
  287. package/lib/env-compat.test.mjs +0 -104
  288. package/lib/execution/disposition.test.mjs +0 -553
  289. package/lib/execution/drive.test.mjs +0 -270
  290. package/lib/execution/effects.test.mjs +0 -344
  291. package/lib/execution/intake.test.mjs +0 -389
  292. package/lib/execution/journal.test.mjs +0 -261
  293. package/lib/execution/match.test.mjs +0 -235
  294. package/lib/execution/pipeline.test.mjs +0 -392
  295. package/lib/execution/route.test.mjs +0 -186
  296. package/lib/execution/surface-policy.test.mjs +0 -162
  297. package/lib/fs-atomic.test.mjs +0 -72
  298. package/lib/fs-ownership.test.mjs +0 -158
  299. package/lib/goals/admission.test.mjs +0 -164
  300. package/lib/goals/classify.test.mjs +0 -167
  301. package/lib/goals/collaborate.test.mjs +0 -336
  302. package/lib/goals/gaps.test.mjs +0 -284
  303. package/lib/goals/loop.test.mjs +0 -845
  304. package/lib/hooks/bus.test.mjs +0 -387
  305. package/lib/identity/persona.test.mjs +0 -142
  306. package/lib/kpi-sensors.test.mjs +0 -278
  307. package/lib/kpi.test.mjs +0 -244
  308. package/lib/learning/config.test.mjs +0 -75
  309. package/lib/learning/counters.test.mjs +0 -69
  310. package/lib/learning/curator-consolidate.test.mjs +0 -238
  311. package/lib/learning/curator.test.mjs +0 -106
  312. package/lib/learning/reflect.test.mjs +0 -0
  313. package/lib/learning/session-index.test.mjs +0 -125
  314. package/lib/learning/skill-writer.test.mjs +0 -210
  315. package/lib/mandate/audit.test.mjs +0 -195
  316. package/lib/mandate/contract.test.mjs +0 -185
  317. package/lib/mandate/derive.test.mjs +0 -274
  318. package/lib/mandate/model.test.mjs +0 -164
  319. package/lib/mandate/refresh.test.mjs +0 -389
  320. package/lib/mcp/server.test.mjs +0 -426
  321. package/lib/model-router/auth-profiles.test.mjs +0 -580
  322. package/lib/model-router/catalog.test.mjs +0 -385
  323. package/lib/model-router/economics.test.mjs +0 -438
  324. package/lib/model-router/failover.test.mjs +0 -439
  325. package/lib/model-router/health.test.mjs +0 -338
  326. package/lib/model-router/integration-coverage.test.mjs +0 -831
  327. package/lib/model-router/integration.test.mjs +0 -564
  328. package/lib/model-router/ledger.test.mjs +0 -415
  329. package/lib/model-router/llm-task.test.mjs +0 -392
  330. package/lib/model-router/org-credentials.test.mjs +0 -265
  331. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  332. package/lib/model-router/reconcile.test.mjs +0 -316
  333. package/lib/model-router/repair.test.mjs +0 -180
  334. package/lib/model-router/spawn.test.mjs +0 -446
  335. package/lib/model-router/taxonomy.test.mjs +0 -410
  336. package/lib/model-router.test.mjs +0 -1207
  337. package/lib/org/activity.test.mjs +0 -134
  338. package/lib/org/approvals.test.mjs +0 -216
  339. package/lib/org/awareness.test.mjs +0 -159
  340. package/lib/org/board-mine-cache.test.mjs +0 -53
  341. package/lib/org/board.test.mjs +0 -187
  342. package/lib/org/bootstrap-context.test.mjs +0 -153
  343. package/lib/org/client.test.mjs +0 -1206
  344. package/lib/org/cohort-client.test.mjs +0 -126
  345. package/lib/org/cost-sync.test.mjs +0 -153
  346. package/lib/org/doctor.test.mjs +0 -346
  347. package/lib/org/engagement-ledger.test.mjs +0 -112
  348. package/lib/org/engagement.test.mjs +0 -739
  349. package/lib/org/handoff.test.mjs +0 -269
  350. package/lib/org/inbound/directedness.test.mjs +0 -668
  351. package/lib/org/inbound/facts.test.mjs +0 -471
  352. package/lib/org/inbound/hydrate.test.mjs +0 -908
  353. package/lib/org/inbound/index.test.mjs +0 -429
  354. package/lib/org/inbound/project.test.mjs +0 -287
  355. package/lib/org/integration-tools.test.mjs +0 -160
  356. package/lib/org/keys.test.mjs +0 -92
  357. package/lib/org/knowledge.test.mjs +0 -326
  358. package/lib/org/leases.test.mjs +0 -235
  359. package/lib/org/mesh-directives.test.mjs +0 -110
  360. package/lib/org/mesh-integration.test.mjs +0 -127
  361. package/lib/org/mesh.test.mjs +0 -400
  362. package/lib/org/messaging.test.mjs +0 -471
  363. package/lib/org/param-contract.test.mjs +0 -477
  364. package/lib/org/policy.test.mjs +0 -237
  365. package/lib/org/protocol.checksum.test.mjs +0 -90
  366. package/lib/org/protocol.test.mjs +0 -323
  367. package/lib/org/push.test.mjs +0 -792
  368. package/lib/org/registry.test.mjs +0 -100
  369. package/lib/org/resource-tools.test.mjs +0 -361
  370. package/lib/org/tool-access.test.mjs +0 -144
  371. package/lib/org/tool-surface-integration.test.mjs +0 -120
  372. package/lib/org/tool-surface.test.mjs +0 -1268
  373. package/lib/org/typing.test.mjs +0 -291
  374. package/lib/org/ui-parity.test.mjs +0 -560
  375. package/lib/org/verify.test.mjs +0 -194
  376. package/lib/org/work-ledger.test.mjs +0 -273
  377. package/lib/plan/adoption-e2e.test.mjs +0 -366
  378. package/lib/plan/budget-enforcement.test.mjs +0 -400
  379. package/lib/plan/compile.test.mjs +0 -382
  380. package/lib/plan/emit.test.mjs +0 -269
  381. package/lib/plan/explain.test.mjs +0 -188
  382. package/lib/prompts/parallelism.test.mjs +0 -177
  383. package/lib/rag/rag.test.mjs +0 -505
  384. package/lib/rate-guard.test.mjs +0 -272
  385. package/lib/reactive-gate.test.mjs +0 -57
  386. package/lib/render.test.mjs +0 -68
  387. package/lib/resource-governor.test.mjs +0 -488
  388. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  389. package/lib/scheduling/jitter.test.mjs +0 -140
  390. package/lib/secrets/broker.test.mjs +0 -280
  391. package/lib/secrets/providers.test.mjs +0 -274
  392. package/lib/security/audit-engine.test.mjs +0 -424
  393. package/lib/security/coerce-args.test.mjs +0 -281
  394. package/lib/security/dangerous-tools.test.mjs +0 -68
  395. package/lib/security/external-content.test.mjs +0 -84
  396. package/lib/security/redact.test.mjs +0 -441
  397. package/lib/security/secret-equal.test.mjs +0 -55
  398. package/lib/session/config.test.mjs +0 -92
  399. package/lib/session/feed-core.test.mjs +0 -198
  400. package/lib/session/first-run.test.mjs +0 -121
  401. package/lib/session/frontdoor.test.mjs +0 -205
  402. package/lib/session/handoffs.test.mjs +0 -183
  403. package/lib/session/identity.test.mjs +0 -180
  404. package/lib/session/inbox-claims.test.mjs +0 -286
  405. package/lib/session/launch-args.test.mjs +0 -157
  406. package/lib/session/liveness.test.mjs +0 -100
  407. package/lib/session/status-summary.test.mjs +0 -118
  408. package/lib/session-permissions.test.mjs +0 -120
  409. package/lib/setup/claude-probe.test.mjs +0 -187
  410. package/lib/setup/completeness.test.mjs +0 -110
  411. package/lib/setup/context-pack.test.mjs +0 -89
  412. package/lib/setup/enrich.test.mjs +0 -115
  413. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  414. package/lib/setup/integration.test.mjs +0 -162
  415. package/lib/setup/io.test.mjs +0 -77
  416. package/lib/setup/runner.test.mjs +0 -132
  417. package/lib/setup/sections/identity.test.mjs +0 -234
  418. package/lib/setup/sections/inventory.test.mjs +0 -198
  419. package/lib/setup/sections/learning.test.mjs +0 -81
  420. package/lib/setup/sections/mandate.test.mjs +0 -388
  421. package/lib/setup/sections/messaging.test.mjs +0 -127
  422. package/lib/setup/sections/model.test.mjs +0 -240
  423. package/lib/setup/sections/org.test.mjs +0 -346
  424. package/lib/setup/sections/orgmail.test.mjs +0 -118
  425. package/lib/setup/sections/recovery.test.mjs +0 -98
  426. package/lib/setup/sections/subagents.test.mjs +0 -429
  427. package/lib/setup/sections/verify.test.mjs +0 -175
  428. package/lib/setup/sot.test.mjs +0 -81
  429. package/lib/setup/state.test.mjs +0 -115
  430. package/lib/singleton.test.mjs +0 -151
  431. package/lib/subagents/cli.test.mjs +0 -389
  432. package/lib/subagents/client.test.mjs +0 -309
  433. package/lib/subagents/gap.test.mjs +0 -234
  434. package/lib/subagents/lock.test.mjs +0 -248
  435. package/lib/subagents/manifest.test.mjs +0 -175
  436. package/lib/subagents/refs.test.mjs +0 -204
  437. package/lib/subagents/resolve.test.mjs +0 -422
  438. package/lib/subagents/schema.test.mjs +0 -328
  439. package/lib/telemetry/alerts.test.mjs +0 -109
  440. package/lib/telemetry/collect.test.mjs +0 -1274
  441. package/lib/tool-definitions-integration.test.mjs +0 -83
  442. package/lib/tool-definitions.test.mjs +0 -437
  443. package/lib/upgrade/global-refresh.test.mjs +0 -65
  444. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  445. package/lib/upgrade/post-steps.test.mjs +0 -200
  446. package/lib/upgrade/verify.test.mjs +0 -164
  447. package/lib/util/fetch-timeout.test.mjs +0 -202
  448. package/lib/util/reconnect.test.mjs +0 -369
  449. package/lib/util/unhandled.test.mjs +0 -216
  450. package/lib/voice/outbound.test.mjs +0 -69
  451. package/lib/voice/session-rotation.test.mjs +0 -114
  452. package/lib/voice/stt.test.mjs +0 -226
  453. package/lib/voice/voice.test.mjs +0 -990
  454. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  455. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  456. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  457. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  458. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  459. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  460. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  461. package/scripts/ci/check.test.mjs +0 -194
  462. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  463. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  464. package/scripts/collective/hook-runner.test.mjs +0 -173
  465. package/scripts/cost/fleet-digest.test.mjs +0 -207
  466. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  467. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  468. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  469. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  470. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  471. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  472. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  473. package/scripts/daemon/assurance.test.mjs +0 -1791
  474. package/scripts/daemon/board-mirror.test.mjs +0 -165
  475. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  476. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  477. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  478. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  479. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  480. package/scripts/daemon/classifier.test.mjs +0 -266
  481. package/scripts/daemon/classify-kind.test.mjs +0 -40
  482. package/scripts/daemon/context-compiler.test.mjs +0 -406
  483. package/scripts/daemon/deliver.test.mjs +0 -564
  484. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  485. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  486. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  487. package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
  488. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  489. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  490. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  491. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  492. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  493. package/scripts/daemon/integration.test.mjs +0 -149
  494. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  495. package/scripts/daemon/lib/session-router.test.mjs +0 -554
  496. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  497. package/scripts/daemon/prompt-builder.test.mjs +0 -556
  498. package/scripts/daemon/responder-cost.test.mjs +0 -68
  499. package/scripts/daemon/responder-history.test.mjs +0 -221
  500. package/scripts/daemon/sdk-version.test.mjs +0 -31
  501. package/scripts/daemon/session-lock.test.mjs +0 -252
  502. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  503. package/scripts/daemon/typing-registry.test.mjs +0 -102
  504. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  505. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  506. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  507. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  508. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  509. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  510. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  511. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  512. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  513. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  514. package/scripts/poller-launchd/install.test.mjs +0 -243
  515. package/scripts/restore-from-backup.test.mjs +0 -181
  516. package/scripts/session/feed.test.mjs +0 -196
  517. package/scripts/session/supervisor-sh.test.mjs +0 -218
  518. package/scripts/session/supervisor.test.mjs +0 -482
  519. package/scripts/setup/configure-macos.test.mjs +0 -306
  520. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  521. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  522. package/scripts/setup/generate-capability.test.mjs +0 -134
  523. package/scripts/setup/init-agent.test.mjs +0 -370
  524. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  525. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  526. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -54,6 +54,12 @@
54
54
  "use strict";
55
55
 
56
56
  import { SURFACES } from "./surfaces.mjs";
57
+ import { isBroadcast, BROADCAST_KIND, BROADCAST_REASON } from "./broadcast.mjs";
58
+
59
+ // The collective-address vocabulary lives in ONE place (`broadcast.mjs`, with an
60
+ // hq twin at `src/server/llm-responder/broadcast.ts`); re-exported here so the
61
+ // daemon and the tests keep a single import for "is this mine, and why".
62
+ export { isBroadcast, BROADCAST_PATTERNS, BROADCAST_CORPUS } from "./broadcast.mjs";
57
63
 
58
64
  /** Channel kinds where every message is addressed to the room. */
59
65
  export const PRIVATE_ROOM_KINDS = Object.freeze(["DM", "GROUP_DM", "HUDDLE"]);
@@ -68,9 +74,12 @@ export const MEMBERSHIP_REQUIRED_KINDS = Object.freeze([
68
74
  * The typed reasons a verdict can carry. A daemon switches on `reason` for
69
75
  * triage; it is a closed set so a new value is a deliberate, reviewable change.
70
76
  *
71
- * @typedef {"dm"|"mention"|"thread"|"channel"|"participant"|"assignee"
72
- * |"reviewer"|"direct"|"requester"|"approver"|"proposer"|"commenter"
73
- * |"owner"|"shared"|"waiting_on"|"task"|"named"} Reason
77
+ * @typedef {"dm"|"mention"|"collective"|"thread"|"channel"|"participant"
78
+ * |"assignee"|"reviewer"|"direct"|"requester"|"approver"|"proposer"
79
+ * |"commenter"|"owner"|"shared"|"waiting_on"|"task"|"named"} Reason
80
+ *
81
+ * `collective` — a HUMAN addressed the whole room I am in ("everyone",
82
+ * "@here", "roll call"); surface `broadcast`. See broadcast.mjs.
74
83
  */
75
84
 
76
85
  /** Narrow an unknown payload to a readable record (never throws). */
@@ -168,7 +177,7 @@ export function classifyEvent(ev) {
168
177
  return {
169
178
  ...base,
170
179
  topic: "message",
171
- surfaces: ["dm", "mention", "thread_reply"],
180
+ surfaces: ["dm", "mention", "broadcast", "thread_reply"],
172
181
  ids: {
173
182
  channelId,
174
183
  channelKind: String(p.channelKind || p.channel_kind || "").toUpperCase() || undefined,
@@ -179,6 +188,9 @@ export function classifyEvent(ev) {
179
188
  // mention or a thread reply, so it can be dropped WITHOUT paging the
180
189
  // channel at all. That single test is what keeps mention-detection from
181
190
  // costing one `messaging.history` per busy public channel per pull.
191
+ // The ONE exception is a HUMAN-authored space post, which may still be a
192
+ // broadcast ("everyone …") — see `needsSpaceProbe`, which lets exactly
193
+ // those through for hydration and still drops agent-authored ones.
182
194
  hints: {
183
195
  mentionCount: Number.isFinite(Number(p.mentionCount)) ? Number(p.mentionCount) : undefined,
184
196
  threaded: typeof p.threaded === "boolean" ? p.threaded : undefined,
@@ -522,6 +534,11 @@ function resolveCalendar(cand, me, facts, on) {
522
534
  * 2. private room + I am a member → `dm`
523
535
  * 3. private room + I am NOT a member → NOT directed (hard stop)
524
536
  * 4. private SPACE + I am not a member → NOT directed (hard stop)
537
+ * 4b. a HUMAN's un-threaded, un-mentioning post to the WHOLE ROOM
538
+ * ("everyone", "@here", "roll call" …) in a space I am a member of
539
+ * → `broadcast` (reason `collective`)
540
+ * — decided BEFORE the structural count drop, because the drop is
541
+ * exactly what silenced thirteen seats on 2026-09-21.
525
542
  * 5. mentions[] (from history) names me → `mention`
526
543
  * 6. thread root I have spoken in / been named in → `thread_reply`
527
544
  * 7. otherwise → ambient chatter, not directed
@@ -557,12 +574,26 @@ function resolveMessaging(cand, me, facts, on) {
557
574
  // membership-required — the conservative branch.
558
575
  if (!channelKind && !has(members, channelId)) return no("channel_kind_unknown");
559
576
 
577
+ const msg = lookupMessage(facts, channelId, cand.ids.messageId);
578
+
579
+ // (4b) BROADCAST — a human addressed the whole room. This sits ABOVE the
580
+ // structural drop on purpose: the drop reasons from `mentionCount: 0`,
581
+ // and a roll-call has, by construction, zero mentions. It is gated on
582
+ // every condition the vocabulary module documents — human author, a
583
+ // space I am a MEMBER of (a public room I merely see does not address
584
+ // me), not threaded, and no structured mention of anyone (a message that
585
+ // @-names specific people chose them; "everyone" inside it is prose).
586
+ // A DM never reaches this line: (2) returned `dm` above.
587
+ if (msg && isBroadcastFor(cand, msg, channelId, members, facts)) {
588
+ return on(BROADCAST_KIND) ? yes(BROADCAST_KIND, BROADCAST_REASON) : no("surface_disabled");
589
+ }
590
+
560
591
  // Cheap structural drop from the redacted counts, BEFORE any hydration: a
561
- // space message that mentions nobody and is not threaded cannot be mine.
592
+ // space message that mentions nobody and is not threaded cannot be mine —
593
+ // unless a human wrote it, in which case `needsSpaceProbe` answered `null`
594
+ // (page it) and we reach here with the body in hand to say so either way.
562
595
  if (needsSpaceProbe(cand, facts) === false) return no("no_mentions_not_threaded");
563
596
 
564
- const msg = lookupMessage(facts, channelId, cand.ids.messageId);
565
-
566
597
  // (5) An explicit @mention of my member id. The mention list is NOT in the
567
598
  // chain payload; it comes from `messaging.history`, which hq ACLs to rooms
568
599
  // I may read.
@@ -764,8 +795,16 @@ function resolveApproval(cand, me, facts, on) {
764
795
  * Prose-name matching, when the caller enabled it, always needs the body — so a
765
796
  * caller with `myNames` gets `null` rather than `false`.
766
797
  *
798
+ * A HUMAN-authored post with zero mentions and no thread is the shape of a
799
+ * broadcast ("everyone, status?"), and only the body can tell. So when
800
+ * `facts.memberKinds` PROVES the actor is a human the answer is `null` — page
801
+ * it — while an agent-authored one, or one whose author kind is unknown (the
802
+ * `member.list` read failed, or the surface is off so it was never made),
803
+ * keeps today's `false`. That is the cheapest correct thing: the extra history
804
+ * page is spent only on posts a person wrote, never on agent chatter.
805
+ *
767
806
  * @param {Candidate} cand
768
- * @param {{myNames?: string[]}} [facts]
807
+ * @param {{myNames?: string[], memberKinds?: Map<string,string>|null}} [facts]
769
808
  * @returns {boolean|null}
770
809
  */
771
810
  export function needsSpaceProbe(cand, facts = {}) {
@@ -774,9 +813,55 @@ export function needsSpaceProbe(cand, facts = {}) {
774
813
  if (h.mentionCount === undefined || h.threaded === undefined) return null;
775
814
  if (h.mentionCount > 0) return true;
776
815
  if (h.threaded) return true;
816
+ if (authorKindOf(facts, cand && cand.actor) === "HUMAN") return null;
777
817
  return false;
778
818
  }
779
819
 
820
+ /**
821
+ * The org-directory kind ("HUMAN" | "AI_AGENT" | …) of a member id or slug, from
822
+ * the ONE `member.list` read `facts.mjs` makes per pull. `""` when unknown —
823
+ * and unknown is never treated as human.
824
+ *
825
+ * @param {{memberKinds?: Map<string,string>|null}} facts
826
+ * @param {unknown} id
827
+ * @returns {string}
828
+ */
829
+ export function authorKindOf(facts, id) {
830
+ const kinds = facts && facts.memberKinds instanceof Map ? facts.memberKinds : null;
831
+ const key = id == null ? "" : String(id).trim();
832
+ if (!kinds || !key) return "";
833
+ return String(kinds.get(key) || kinds.get(key.toLowerCase()) || "").toUpperCase();
834
+ }
835
+
836
+ /**
837
+ * Conditions 1–3 of the broadcast rule (see broadcast.mjs), joined with the
838
+ * body test. `msg` is the hydrated history row — the body never enters the
839
+ * chain, so this can only ever be asked after paging.
840
+ *
841
+ * @param {Candidate} cand
842
+ * @param {object} msg the hydrated message row
843
+ * @param {string} channelId
844
+ * @param {Set<string>|null} members proven memberships (null == unknown)
845
+ * @param {import("./facts.mjs").Facts} facts
846
+ * @returns {boolean}
847
+ */
848
+ export function isBroadcastFor(cand, msg, channelId, members, facts) {
849
+ if (!msg || !cand) return false;
850
+ // (2) a space I am a MEMBER of. `members` for a PUBLIC room is populated only
851
+ // from hq's own `joined` flag, so visibility alone never qualifies.
852
+ if (!has(members, channelId)) return false;
853
+ // (3) not threaded — a thread reply keeps the thread rule.
854
+ if (msg.threadRootId || msg.thread_root_id) return false;
855
+ if (cand.hints && cand.hints.threaded === true) return false;
856
+ // A message that structurally @-names people is addressed to THEM.
857
+ if (Array.isArray(msg.mentions) && msg.mentions.length > 0) return false;
858
+ // (1) the author is a HUMAN — proven by the directory, never assumed.
859
+ const author = msg.authorId || msg.author || cand.actor;
860
+ if (authorKindOf(facts, author) !== "HUMAN") return false;
861
+ // (4) the body names the whole room.
862
+ return isBroadcast(msg.body ?? msg.text);
863
+ }
864
+
780
865
  /** Find a hydrated message row in `facts.channelPages`. */
781
866
  export function lookupMessage(facts, channelId, messageId) {
782
867
  if (!(facts.channelPages instanceof Map) || !channelId || !messageId) return null;
@@ -847,6 +932,9 @@ export default {
847
932
  coordsOf,
848
933
  asSet,
849
934
  needsSpaceProbe,
935
+ authorKindOf,
936
+ isBroadcastFor,
937
+ isBroadcast,
850
938
  lookupMessage,
851
939
  matchesMyName,
852
940
  taskIdFromFileKey,
@@ -79,6 +79,8 @@ export const DEFAULT_LIMITS = Object.freeze({
79
79
  * @property {Map<string,object>|null} fileAcl fileId → {ownerId, shared, mentionsMe, kind}
80
80
  * @property {Set<string>|null} myEventIds calendar events I own or attend (null == unreadable)
81
81
  * @property {Map<string,object>|null} myEvents eventId → the calendar event DTO
82
+ * @property {Map<string,string>|null} memberKinds member id AND slug → "HUMAN"|"AI_AGENT" (null == not read / unreadable)
83
+ * @property {number|null} aiMemberCount how many AI_AGENT members the org has (the roll-call respondent bound)
82
84
  * @property {string[]} degraded names of facts that could not be read
83
85
  */
84
86
 
@@ -131,6 +133,8 @@ export async function resolveFacts(o = {}) {
131
133
  fileAcl: null,
132
134
  myEventIds: null,
133
135
  myEvents: null,
136
+ memberKinds: null,
137
+ aiMemberCount: null,
134
138
  degraded,
135
139
  };
136
140
  if (!io || candidates.length === 0) return facts;
@@ -185,6 +189,16 @@ export async function resolveFacts(o = {}) {
185
189
  }
186
190
  }
187
191
 
192
+ // ── 1b. Who is a HUMAN. A broadcast is only ever a human's ("everyone,
193
+ // status?" from a peer agent is ambient), and the chain payload carries
194
+ // no author kind — so the directory is the only proof. ONE `member.list`
195
+ // per pull, and only when this pull holds a space post that the counts
196
+ // alone cannot rule out. Unreadable ⇒ `memberKinds` stays null ⇒ nobody
197
+ // is proven human ⇒ today's structural drop stands (logged, not silent).
198
+ if (families.has("messaging") && on("broadcast") && wantsMemberKinds(candidates, facts, me)) {
199
+ await resolveMemberKinds({ facts, io, log, degraded });
200
+ }
201
+
188
202
  // ── 2. Message pages: one per affected, VISIBLE channel. Public-space traffic
189
203
  // that the redacted counts already prove uninteresting is skipped.
190
204
  if (families.has("messaging")) {
@@ -307,6 +321,67 @@ async function resolveCalendarFacts({ candidates, facts, io, log, degraded }) {
307
321
  // Step implementations
308
322
  // ---------------------------------------------------------------------------
309
323
 
324
+ /**
325
+ * Does this batch hold a space post whose author kind could change the verdict?
326
+ * That is: a non-private-room message, in a space I am a MEMBER of, by someone
327
+ * other than me, with counts that would otherwise drop it (0 mentions, not
328
+ * threaded) or counts absent. Anything else never asks the directory.
329
+ *
330
+ * @param {import("./directedness.mjs").Candidate[]} candidates
331
+ * @param {Facts} facts
332
+ * @param {string} me
333
+ * @returns {boolean}
334
+ */
335
+ export function wantsMemberKinds(candidates, facts, me) {
336
+ const members = facts.memberChannelIds;
337
+ if (!members) return false;
338
+ for (const cand of candidates) {
339
+ if (!cand || cand.family !== "messaging") continue;
340
+ const channelId = String((cand.ids && cand.ids.channelId) || "");
341
+ if (!channelId || !members.has(channelId)) continue;
342
+ const chan = facts.channels.get(channelId);
343
+ const kind = String((cand.ids && cand.ids.channelKind) || (chan && chan.kind) || "").toUpperCase();
344
+ if (PRIVATE_ROOM_KINDS.includes(kind)) continue;
345
+ if (me && String(cand.actor || "") === me) continue;
346
+ const h = cand.hints || {};
347
+ if (h.mentionCount > 0 || h.threaded === true) continue; // paged anyway; no kind needed
348
+ return true;
349
+ }
350
+ return false;
351
+ }
352
+
353
+ /**
354
+ * `member.list` → `facts.memberKinds` keyed by BOTH member id and slug (the
355
+ * chain's `actor` is documented as "member slug or api-key id" while history's
356
+ * `authorId` is the cuid; one map answers either) + `facts.aiMemberCount`.
357
+ * Org-scoped, read-only, the same directory every seat may already list.
358
+ */
359
+ async function resolveMemberKinds({ facts, io, log, degraded }) {
360
+ const frame = await io.call("member.list", {});
361
+ if (!frame || frame.ok !== true) {
362
+ degraded.push("members");
363
+ log("warn", "[inbound] member.list unreadable — no author can be proven human this tick, so a human's roll-call in a space WILL be dropped as no_mentions_not_threaded");
364
+ return;
365
+ }
366
+ const kinds = new Map();
367
+ let ai = 0;
368
+ for (const m of arrayOf(resultOf(frame), "members")) {
369
+ if (!m) continue;
370
+ const kind = String(m.kind || "").toUpperCase();
371
+ if (!kind) continue;
372
+ const id = String(m.id || "");
373
+ const slug = String(m.slug || "");
374
+ if (id) kinds.set(id, kind);
375
+ if (slug) {
376
+ kinds.set(slug, kind);
377
+ kinds.set(slug.toLowerCase(), kind);
378
+ }
379
+ if (kind === "AI_AGENT") ai += 1;
380
+ }
381
+ facts.memberKinds = kinds;
382
+ facts.aiMemberCount = ai;
383
+ }
384
+
310
385
  /**
311
386
  * Fetch one `messaging.history` page per affected channel and fold it into
312
387
  * `facts.channelPages` + `facts.myThreadRootIds`.
@@ -347,8 +422,9 @@ async function pageChannels({ candidates, facts, io, limits, log, on }) {
347
422
  if (!members || !members.has(channelId)) continue;
348
423
  if (!on("dm")) continue;
349
424
  } else {
350
- // A space: paged only if it could be a mention or a thread reply.
351
- if (!on("mention") && !on("thread_reply")) continue;
425
+ // A space: paged only if it could be a mention, a thread reply or a
426
+ // human's broadcast to the room.
427
+ if (!on("mention") && !on("thread_reply") && !on("broadcast")) continue;
352
428
  if (needsSpaceProbe(cand, facts) === false) {
353
429
  skippedByCounts += 1;
354
430
  continue;
@@ -123,6 +123,22 @@ export function toMessageEvent(o = {}) {
123
123
  // TRUE only when this really is a direct address, so downstream triage can
124
124
  // trust it. `channel`/`assignee` delivery is directed at the SEAT, not a
125
125
  // literal @mention, and says so.
126
+ //
127
+ // `collective` IS DELIBERATELY ABSENT, and this is the one line most
128
+ // likely to be "fixed" by someone chasing the right bug. A work-shaped
129
+ // broadcast ("Everyone, please review the binder by Friday") DID open no
130
+ // board row, and the shortest-looking cure is to add `collective` here so
131
+ // `work-ledger.mjs` reports `directed: true`. That cure is wrong twice
132
+ // over: this flag also gates the daemon's directed-message rules, so it
133
+ // would change what the seat REPLIES to and not only what it tracks; and
134
+ // it would claim EVERY broadcast is assigned work, including "everyone
135
+ // should take a look sometime", which is thirteen board rows for an
136
+ // aside. The audience travels as its own structural fact instead —
137
+ // `isRollCallItem` reads the verdict reason back off the flattened item,
138
+ // `work-ledger.mjs` sends it as `collective`, and hq's
139
+ // `work/collective.ts` decides whether the ask is work. One address, two
140
+ // questions, and the second one is answered in the one place both planes
141
+ // share.
126
142
  mentions_agent:
127
143
  verdict.reason === "mention" ||
128
144
  verdict.reason === "named" ||
@@ -158,6 +174,12 @@ export function toMessageEvent(o = {}) {
158
174
  scope_id: s(c.ids.taskId || c.ids.decisionId || c.ids.fileId || "") || null,
159
175
  seq: c.seq ?? null,
160
176
  me: s(me),
177
+ // A roll-call is answered by every AI member of the room; the org-wide
178
+ // AI count is the honest upper bound a seat can know (it cannot see the
179
+ // room's roster). Carried only on a broadcast, so nothing else changes.
180
+ ...(verdict.surface === "broadcast" && Number.isFinite(facts.aiMemberCount)
181
+ ? { respondents: facts.aiMemberCount }
182
+ : {}),
161
183
  },
162
184
  };
163
185
 
@@ -55,6 +55,20 @@ export const SURFACES = Object.freeze({
55
55
  scope: "channel",
56
56
  default: true,
57
57
  }),
58
+ /**
59
+ * A HUMAN's message to the whole room ("everyone", "@here", "roll call", …)
60
+ * in a space I am a member of. Every addressed member answers for itself, in
61
+ * ≤ 5 lines, with no ack and no election — see `broadcast.mjs` for the
62
+ * vocabulary and the reply shape. Added 2026-09-21 after a #general
63
+ * "can everyone give me an update" drew zero replies from thirteen seats.
64
+ */
65
+ broadcast: Object.freeze({
66
+ topic: "message",
67
+ kind: "broadcast",
68
+ subject: "Cohort roll-call",
69
+ scope: "channel",
70
+ default: true,
71
+ }),
58
72
  /** A reply in a thread I have spoken in / been named in. */
59
73
  thread_reply: Object.freeze({
60
74
  topic: "message",