@cohortapp/agent-sdk 2.17.0 → 2.18.5

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