@cohortapp/agent-sdk 2.16.0 → 2.18.4

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